From 90d3defe5aae8bbc85d9f71c4b788f98d6666f77 Mon Sep 17 00:00:00 2001 From: Michel Pollet Date: Wed, 23 Dec 2009 22:27:31 +0000 Subject: [PATCH] UART: Implement a system of flow control + The uart now signals (using IRQs) when it's fifo is full and empty. This allow controling code to send new bytes, or to pause. + The uart also now understand the Baud rate and prints it. + Added new IOCTL to get/set the UART emulation flags A new mode of the UART was made specialy for code that spinloops waiting to get/send a byte. Now if the uart detects that, it will insert a usleep() when the uart is idle, this will prevent code from eating 100% cpu doing nothing. Signed-off-by: Michel Pollet --- simavr/sim/avr_uart.c | 75 +++++++++++++++++++++++++++++++++++++++++++ simavr/sim/avr_uart.h | 16 +++++++++ 2 files changed, 91 insertions(+) diff --git a/simavr/sim/avr_uart.c b/simavr/sim/avr_uart.c index e11679c..235cbe6 100644 --- a/simavr/sim/avr_uart.c +++ b/simavr/sim/avr_uart.c @@ -24,7 +24,9 @@ */ #include +#include #include "avr_uart.h" +#include "sim_hex.h" DEFINE_FIFO(uint8_t, uart_fifo, 128); @@ -47,6 +49,33 @@ static avr_cycle_count_t avr_uart_rxc_raise(struct avr_t * avr, avr_cycle_count_ return 0; } +static uint8_t avr_uart_rxc_read(struct avr_t * avr, avr_io_addr_t addr, void * param) +{ + avr_uart_t * p = (avr_uart_t *)param; + uint8_t v = avr_core_watch_read(avr, addr); + + // + // if RX is enabled, and there is nothing to read, and + // the AVR core is reading this register, it's probably + // to pool the RXC TXC flag and spinloop + // so here we introduce a usleep to make it a bit lighter + // on CPU and let data arrive + // + uint8_t ri = !avr_regbit_get(avr, p->rxen) || !avr_regbit_get(avr, p->rxc.raised); + uint8_t ti = !avr_regbit_get(avr, p->txen) || !avr_regbit_get(avr, p->txc.raised); + + if (p->flags & AVR_UART_FLAG_POOL_SLEEP) { + + if (ri && ti) + usleep(1); + } + // if reception is idle and the fifo is empty, tell whomever there is room + if (avr_regbit_get(avr, p->rxen) && uart_fifo_isempty(&p->input)) + avr_raise_irq(p->io.irq + UART_IRQ_OUT_XON, 1); + + return v; +} + static uint8_t avr_uart_read(struct avr_t * avr, avr_io_addr_t addr, void * param) { avr_uart_t * p = (avr_uart_t *)param; @@ -69,6 +98,19 @@ static uint8_t avr_uart_read(struct avr_t * avr, avr_io_addr_t addr, void * para return v; } +static void avr_uart_baud_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param) +{ + avr_uart_t * p = (avr_uart_t *)param; + avr_core_watch_write(avr, addr, v); + uint32_t val = avr->data[p->r_ubrrl] | (avr->data[p->r_ubrrh] << 8); + uint32_t baud = avr->frequency / (val+1); + if (avr_regbit_get(avr, p->u2x)) + baud /= 8; + else + baud /= 16; + printf("UART-%c configured to %04x = %d baud\n", p->name, val, baud); +} + static void avr_uart_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param) { avr_uart_t * p = (avr_uart_t *)param; @@ -118,6 +160,9 @@ static void avr_uart_irq_input(struct avr_irq_t * irq, uint32_t value, void * pa if (uart_fifo_isempty(&p->input)) avr_cycle_timer_register_usec(avr, 100, avr_uart_rxc_raise, p); // should be uart speed dependent uart_fifo_write(&p->input, value); // add to fifo + + if (uart_fifo_isfull(&p->input)) + avr_raise_irq(p->io.irq + UART_IRQ_OUT_XOFF, 1); } @@ -136,9 +181,34 @@ void avr_uart_reset(struct avr_io_t *io) } +#define AVR_IOCTL_UART_SET_FLAGS(_name) AVR_IOCTL_DEF('u','a','s',(_name)) +#define AVR_IOCTL_UART_GET_FLAGS(_name) AVR_IOCTL_DEF('u','a','g',(_name)) + +static int avr_uart_ioctl(struct avr_io_t * port, uint32_t ctl, void * io_param) +{ + avr_uart_t * p = (avr_uart_t *)port; + int res = -1; + + if (!io_param) + return res; + + if (ctl == AVR_IOCTL_UART_SET_FLAGS(p->name)) { + p->flags = *(uint32_t*)io_param; + res = 0; + } + if (ctl == AVR_IOCTL_UART_GET_FLAGS(p->name)) { + *(uint32_t*)io_param = p->flags; + res = 0; + } + + return res; +} + + static avr_io_t _io = { .kind = "uart", .reset = avr_uart_reset, + .ioctl = avr_uart_ioctl, }; void avr_uart_init(avr_t * avr, avr_uart_t * p) @@ -152,6 +222,8 @@ void avr_uart_init(avr_t * avr, avr_uart_t * p) p->io.irq = avr_alloc_irq(0, p->io.irq_count); p->io.irq_ioctl_get = AVR_IOCTL_UART_GETIRQ(p->name); + p->flags = AVR_UART_FLAG_POOL_SLEEP; + avr_register_io(avr, &p->io); avr_register_vector(avr, &p->rxc); avr_register_vector(avr, &p->txc); @@ -159,7 +231,10 @@ void avr_uart_init(avr_t * avr, avr_uart_t * p) avr_register_io_write(avr, p->r_udr, avr_uart_write, p); avr_register_io_read(avr, p->r_udr, avr_uart_read, p); + // monitor code that reads the rxc flag, and delay it a bit + avr_register_io_read(avr, p->rxc.raised.reg, avr_uart_rxc_read, p); avr_register_io_write(avr, p->r_ucsra, avr_uart_write, p); + avr_register_io_write(avr, p->r_ubrrl, avr_uart_baud_write, p); } diff --git a/simavr/sim/avr_uart.h b/simavr/sim/avr_uart.h index 86fa850..cf3a510 100644 --- a/simavr/sim/avr_uart.h +++ b/simavr/sim/avr_uart.h @@ -31,12 +31,21 @@ DECLARE_FIFO(uint8_t, uart_fifo, 128); enum { UART_IRQ_INPUT = 0, UART_IRQ_OUTPUT, + UART_IRQ_OUT_XON, // signaled when input fifo is not full + UART_IRQ_OUT_XOFF, // signaled when input fifo IS full UART_IRQ_COUNT }; // add port number to get the real IRQ #define AVR_IOCTL_UART_GETIRQ(_name) AVR_IOCTL_DEF('u','a','r',(_name)) +enum { + // the uart code monitors for firmware that pool on + // reception registers, and can do an atomic usleep() + // if it's detected, this helps regulating CPU + AVR_UART_FLAG_POOL_SLEEP = (1 << 0), +}; + typedef struct avr_uart_t { avr_io_t io; char name; @@ -49,6 +58,7 @@ typedef struct avr_uart_t { avr_regbit_t rxen; // receive enabled avr_regbit_t txen; // transmit enable + avr_regbit_t u2x; // double UART speed avr_io_addr_t r_ubrrl,r_ubrrh; @@ -57,8 +67,14 @@ typedef struct avr_uart_t { avr_int_vector_t udrc; uart_fifo_t input; + + uint32_t flags; } avr_uart_t; +/* takes a uint32_t* as parameter */ +#define AVR_IOCTL_UART_SET_FLAGS(_name) AVR_IOCTL_DEF('u','a','s',(_name)) +#define AVR_IOCTL_UART_GET_FLAGS(_name) AVR_IOCTL_DEF('u','a','g',(_name)) + void avr_uart_init(avr_t * avr, avr_uart_t * port); #endif /* AVR_UART_H_ */ -- 2.39.5