From 0bc442b05f79c08a394909f14510da98954000eb Mon Sep 17 00:00:00 2001 From: Stefan Krupop Date: Sun, 28 Oct 2018 00:35:38 +0200 Subject: [PATCH] Add shadow register for UBRRH to keep it separate from UCSRC In e.g. ATmega8 UBRRH and UCSRC registers share the same I/O location. Bit URSEL decides in which physical register written data should go. As there are now more registers then I/O locations, "shadow" versions of certain regbit functions that store to a pointer ("shadow register") instead of the main registers in avr->data[] were introduced. UBRRH was moved into a shadow register, while UCSRC stays a regular register. Therefore, writes to UBRRH/UCSRC with URSEL bit set do not set the baudrate falsely anymore. --- simavr/sim/avr_uart.c | 26 ++++++++++++++++++++- simavr/sim/avr_uart.h | 1 + simavr/sim/sim_regbit.h | 51 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/simavr/sim/avr_uart.c b/simavr/sim/avr_uart.c index 4e3db86c6..9b560194d 100644 --- a/simavr/sim/avr_uart.c +++ b/simavr/sim/avr_uart.c @@ -239,8 +239,27 @@ avr_uart_baud_write( void * param) { avr_uart_t * p = (avr_uart_t *)param; + if (addr == p->ubrrh.reg) { + if (p->ubrrh.reg == p->r_ucsrc) { + // UBRRH and UCSRC registers can share the same I/O location. + // URSEL bit decides which register gets written. If it's UBRRH, + // store to shadow register, else to regular register + if ((v & (1 << 7)) == 0) { // URSEL + avr_regbit_setto_shadow(avr, p->ubrrh, v, &p->ubrrh_shadow); + } else { + avr_core_watch_write(avr, addr, v); + } + } else { + // If UBRRH have different I/O locations, update shadow register and + // regular register + avr_core_watch_write(avr, addr, v); + avr_regbit_setto_shadow(avr, p->ubrrh, v, &p->ubrrh_shadow); + } + return; + } avr_core_watch_write(avr, addr, v); - uint32_t val = avr_regbit_get(avr,p->ubrrl) | (avr_regbit_get(avr,p->ubrrh) << 8); + uint32_t val = avr_regbit_get(avr,p->ubrrl) | + (avr_regbit_get_shadow(avr, p->ubrrh, &p->ubrrh_shadow) << 8); const int databits[] = { 5,6,7,8, /* 'reserved', assume 8 */8,8,8, 9 }; int db = databits[avr_regbit_get(avr, p->ucsz) | (avr_regbit_get(avr, p->ucsz2) << 2)]; @@ -468,6 +487,9 @@ avr_uart_reset( uart_fifo_reset(&p->input); p->tx_cnt = 0; + avr_regbit_clear(avr, p->ubrrl); + avr_regbit_clear_shadow(avr, p->ubrrh, &p->ubrrh_shadow); + avr_regbit_set(avr, p->ucsz); avr_uart_regbit_clear(avr, p->ucsz2); @@ -550,6 +572,8 @@ avr_uart_init( avr_register_io_write(avr, p->r_ucsra, avr_uart_write, p); if (p->ubrrl.reg) avr_register_io_write(avr, p->ubrrl.reg, avr_uart_baud_write, p); + if (p->ubrrh.reg) + avr_register_io_write(avr, p->ubrrh.reg, avr_uart_baud_write, p); avr_register_io_write(avr, p->rxen.reg, avr_uart_write, p); } diff --git a/simavr/sim/avr_uart.h b/simavr/sim/avr_uart.h index 107359e3a..5bb82c7d1 100644 --- a/simavr/sim/avr_uart.h +++ b/simavr/sim/avr_uart.h @@ -113,6 +113,7 @@ typedef struct avr_uart_t { avr_regbit_t ubrrl; avr_regbit_t ubrrh; + uint8_t ubrrh_shadow; avr_int_vector_t rxc; avr_int_vector_t txc; diff --git a/simavr/sim/sim_regbit.h b/simavr/sim/sim_regbit.h index 256519512..ec000e13b 100644 --- a/simavr/sim/sim_regbit.h +++ b/simavr/sim/sim_regbit.h @@ -23,6 +23,7 @@ #define __SIM_REGBIT_H__ #include "sim_avr.h" +#include "sim_gdb.h" #ifdef __cplusplus extern "C" { @@ -52,6 +53,22 @@ static inline uint8_t avr_regbit_set(avr_t * avr, avr_regbit_t rb) return (avr->data[a] >> rb.bit) & rb.mask; } +static inline uint8_t avr_regbit_set_shadow(avr_t * avr, avr_regbit_t rb, uint8_t * shadow) +{ + uint16_t a = rb.reg; + uint8_t m; + + if (!a) + return 0; + m = rb.mask << rb.bit; + if (avr->gdb) { + avr_gdb_handle_watchpoints(avr, a, AVR_GDB_WATCH_WRITE); + } + *shadow = *shadow | m; + avr_core_watch_write(avr, a, avr->data[a] | m); + return (*shadow >> rb.bit) & rb.mask; +} + static inline uint8_t avr_regbit_setto(avr_t * avr, avr_regbit_t rb, uint8_t v) { uint16_t a = rb.reg; @@ -64,6 +81,21 @@ static inline uint8_t avr_regbit_setto(avr_t * avr, avr_regbit_t rb, uint8_t v) return (avr->data[a] >> rb.bit) & rb.mask; } +static inline uint8_t avr_regbit_setto_shadow(avr_t * avr, avr_regbit_t rb, uint8_t v, uint8_t * shadow) +{ + uint16_t a = rb.reg; + uint8_t m; + + if (!a) + return 0; + m = rb.mask << rb.bit; + if (avr->gdb) { + avr_gdb_handle_watchpoints(avr, a, AVR_GDB_WATCH_WRITE); + } + *shadow = (*shadow & ~(m)) | ((v << rb.bit) & m); + return (*shadow >> rb.bit) & rb.mask; +} + /* * Set the 'raw' bits, if 'v' is the unshifted value of the bits */ @@ -88,6 +120,15 @@ static inline uint8_t avr_regbit_get(avr_t * avr, avr_regbit_t rb) return (avr->data[a] >> rb.bit) & rb.mask; } +static inline uint8_t avr_regbit_get_shadow(avr_t * avr, avr_regbit_t rb, uint8_t * shadow) +{ + uint16_t a = rb.reg; + if (!a) + return 0; + //uint8_t m = rb.mask << rb.bit; + return (*shadow >> rb.bit) & rb.mask; +} + /* * Using regbit from value eliminates some of the * set to test then clear register operations. @@ -124,6 +165,16 @@ static inline uint8_t avr_regbit_clear(avr_t * avr, avr_regbit_t rb) return avr->data[a]; } +static inline uint8_t avr_regbit_clear_shadow(avr_t * avr, avr_regbit_t rb, uint8_t * shadow) +{ + uint16_t a = rb.reg; + uint8_t m = rb.mask << rb.bit; + if (avr->gdb) { + avr_gdb_handle_watchpoints(avr, a, AVR_GDB_WATCH_WRITE); + } + *shadow = *shadow & ~m; + return *shadow; +} /* * This reads the bits for an array of avr_regbit_t, make up a "byte" with them.