diff --git a/simavr/cores/sim_mega128.c b/simavr/cores/sim_mega128.c index 9c8a42730..76c5e423a 100644 --- a/simavr/cores/sim_mega128.c +++ b/simavr/cores/sim_mega128.c @@ -391,7 +391,7 @@ const struct mcu_t { .vector = TIMER3_CAPT_vect, }, }, - AVR_SPI_DECLARE(0, 0), + AVR_SPI_DECLARE(0, 0, 'B', 1, 3, 2, 0), .twi = { .r_twcr = TWCR, diff --git a/simavr/cores/sim_mega1280.c b/simavr/cores/sim_mega1280.c index 316bfd67a..376a98f6d 100644 --- a/simavr/cores/sim_mega1280.c +++ b/simavr/cores/sim_mega1280.c @@ -616,7 +616,7 @@ const struct mcu_t { }, }, - AVR_SPI_DECLARE(PRR0, PRSPI), + AVR_SPI_DECLARE(PRR0, PRSPI, 'B', 1, 3, 2, 0), .twi = { .r_twcr = TWCR, diff --git a/simavr/cores/sim_mega1281.c b/simavr/cores/sim_mega1281.c index 222743c60..abd70bb64 100644 --- a/simavr/cores/sim_mega1281.c +++ b/simavr/cores/sim_mega1281.c @@ -431,7 +431,7 @@ const struct mcu_t { .vector = TIMER3_CAPT_vect, }, }, - AVR_SPI_DECLARE(PRR0, PRSPI), + AVR_SPI_DECLARE(PRR0, PRSPI, 'B', 1, 3, 2, 0), .twi = { .r_twcr = TWCR, diff --git a/simavr/cores/sim_mega128rfr2.c b/simavr/cores/sim_mega128rfr2.c index 5ffbab960..89aef9b36 100644 --- a/simavr/cores/sim_mega128rfr2.c +++ b/simavr/cores/sim_mega128rfr2.c @@ -469,7 +469,7 @@ const struct mcu_t { .vector = TIMER3_CAPT_vect, }, }, - AVR_SPI_DECLARE(PRR0, PRSPI), + AVR_SPI_DECLARE(PRR0, PRSPI, 'B', 1, 3, 2, 0), .twi = { .r_twcr = TWCR, diff --git a/simavr/cores/sim_mega169.c b/simavr/cores/sim_mega169.c index 209ff2ff9..08b813720 100644 --- a/simavr/cores/sim_mega169.c +++ b/simavr/cores/sim_mega169.c @@ -319,7 +319,7 @@ const struct mcu_t { }, }, }, - AVR_SPI_DECLARE(PRR, PRSPI), + AVR_SPI_DECLARE(PRR, PRSPI, 'B', 1, 3, 2, 0), }; static avr_t * make() diff --git a/simavr/cores/sim_mega2560.c b/simavr/cores/sim_mega2560.c index 14ab65215..d713f6e28 100644 --- a/simavr/cores/sim_mega2560.c +++ b/simavr/cores/sim_mega2560.c @@ -618,7 +618,7 @@ const struct mcu_t { }, }, - AVR_SPI_DECLARE(PRR0, PRSPI), + AVR_SPI_DECLARE(PRR0, PRSPI, 'B', 1, 3, 2, 0), .twi = { .r_twcr = TWCR, diff --git a/simavr/cores/sim_megax.h b/simavr/cores/sim_megax.h index 20363947e..d1a41541b 100644 --- a/simavr/cores/sim_megax.h +++ b/simavr/cores/sim_megax.h @@ -301,7 +301,7 @@ const struct mcu_t SIM_CORENAME = { }, }, }, - AVR_SPI_DECLARE(0, 0), + AVR_SPI_DECLARE(0, 0, 'B', 7, 6, 5, 4), .twi = { .r_twcr = TWCR, diff --git a/simavr/cores/sim_megax4.h b/simavr/cores/sim_megax4.h index 10384b7e8..9c567e482 100644 --- a/simavr/cores/sim_megax4.h +++ b/simavr/cores/sim_megax4.h @@ -432,9 +432,9 @@ const struct mcu_t SIM_CORENAME = { }, #endif #ifdef MSTR0 /* xx4a and xx4pa series */ - AVR_SPIX_DECLARE(0, PRR0, PRSPI), + AVR_SPIX_DECLARE(0, PRR0, PRSPI, 'B', 7, 6, 5, 4), #else - AVR_SPI_DECLARE(PRR0, PRSPI), + AVR_SPI_DECLARE(PRR0, PRSPI, 'B', 7, 6, 5, 4), #endif .twi = { .disabled = AVR_IO_REGBIT(PRR0,PRTWI), diff --git a/simavr/cores/sim_megax8.h b/simavr/cores/sim_megax8.h index 3bd65cbee..ac025597b 100644 --- a/simavr/cores/sim_megax8.h +++ b/simavr/cores/sim_megax8.h @@ -345,7 +345,7 @@ const struct mcu_t SIM_CORENAME = { } } }, - AVR_SPI_DECLARE(PRR, PRSPI), + AVR_SPI_DECLARE(PRR, PRSPI, 'B', 5, 4, 3, 2), .twi = { .disabled = AVR_IO_REGBIT(PRR,PRTWI), diff --git a/simavr/cores/sim_megaxm1.h b/simavr/cores/sim_megaxm1.h index 6af2c1cde..3e75e0858 100644 --- a/simavr/cores/sim_megaxm1.h +++ b/simavr/cores/sim_megaxm1.h @@ -311,7 +311,7 @@ const struct mcu_t SIM_CORENAME = { }, }, }, - AVR_SPI_DECLARE(PRR, PRSPI), + AVR_SPI_DECLARE(PRR, PRSPI, 'C', 7, 0, 1, 1), }; #endif /* SIM_CORENAME */ diff --git a/simavr/cores/sim_usb162.c b/simavr/cores/sim_usb162.c index 0ed9874eb..516fa0c1d 100644 --- a/simavr/cores/sim_usb162.c +++ b/simavr/cores/sim_usb162.c @@ -202,7 +202,7 @@ const struct mcu_t { }, }, }, - AVR_SPI_DECLARE(0, 0), + AVR_SPI_DECLARE(PRR0, PRSPI, 'B', 7, 6, 5, 4), .usb = { .name='1', .disabled=AVR_IO_REGBIT(PRR1, PRUSB),// bit in the PRR diff --git a/simavr/sim/avr/avr_mcu_section.h b/simavr/sim/avr/avr_mcu_section.h index ebeceb0a3..6357b0091 100644 --- a/simavr/sim/avr/avr_mcu_section.h +++ b/simavr/sim/avr/avr_mcu_section.h @@ -63,6 +63,7 @@ enum { AVR_MMCU_TAG_VCD_PORTPIN, AVR_MMCU_TAG_VCD_IRQ, AVR_MMCU_TAG_PORT_EXTERNAL_PULL, + AVR_MMCU_TAG_BITBANG, }; enum { @@ -72,6 +73,18 @@ enum { SIMAVR_CMD_UART_LOOPBACK, }; +#define BITBANG_ON_SPI(_name) \ + ((uint64_t)(_name >= '0' ? ((1 << (_name - '0'))&0xFF) : (1 << 8)) << 0) + +#define BITBANG_ON_UART(_name) \ + ((uint64_t)((1 << (_name - '0'))&0xFF) << 16) + +#define BITBANG_ON_TWI(_name) \ + ((uint64_t)(1) << 24) + +#define BITBANG_ON_LIN(_name) \ + ((uint64_t)(1) << 32) + #if __AVR__ /* * WARNING. Due to newer GCC being stupid, they introduced a bug that @@ -87,6 +100,12 @@ struct avr_mmcu_long_t { uint32_t val; } __attribute__((__packed__)); +struct avr_mmcu_longlong_t { + uint8_t tag; + uint8_t len; + uint64_t val; +} __attribute__((__packed__)); + struct avr_mmcu_string_t { uint8_t tag; uint8_t len; @@ -121,6 +140,13 @@ struct avr_mmcu_vcd_trace_t { #define DO_CONCAT2(_a, _b) _a##_b #define DO_CONCAT(_a, _b) DO_CONCAT2(_a,_b) +#define AVR_MCU_LONGLONG(_tag, _val) \ + const struct avr_mmcu_longlong_t DO_CONCAT(DO_CONCAT(_, _tag), __LINE__) _MMCU_ = {\ + .tag = _tag,\ + .len = sizeof(struct avr_mmcu_longlong_t) - 2,\ + .val = _val,\ + } + #define AVR_MCU_LONG(_tag, _val) \ const struct avr_mmcu_long_t DO_CONCAT(DO_CONCAT(_, _tag), __LINE__) _MMCU_ = {\ .tag = _tag,\ @@ -131,6 +157,18 @@ struct avr_mmcu_vcd_trace_t { #define AVR_MCU_BYTE(_tag, _val) \ const uint8_t _##_tag _MMCU_ = { _tag, 1, _val } +/*! + * This Macro allows you turn on or off a bitbang feature for + * peripherals, which support it (plese look code of the corresponding + * module to ensure it's implemented). + * By default bitbang is off for every peripheral. + * You can turn it on using bitmask for specific peripheral instance, + * e.g. the following turns it on for SPI0 and for USART1 in SPI mode: + * AVR_MCU_BITBANG(BITBANG_ON_SPI(0) | BITBANG_ON_SPI('1')); + */ +#define AVR_MCU_BITBANG(_peripheral_on_mask) \ + AVR_MCU_LONGLONG(AVR_MMCU_TAG_BITBANG, _peripheral_on_mask) + /*! * This Macro allows you to specify traces for the VCD file output * engine. This specifies a default header, and let you fill in the diff --git a/simavr/sim/avr_bitbang.c b/simavr/sim/avr_bitbang.c index e34aab23c..95127c95f 100644 --- a/simavr/sim/avr_bitbang.c +++ b/simavr/sim/avr_bitbang.c @@ -29,6 +29,7 @@ extern "C" { #include #include +#include #include "avr_bitbang.h" @@ -99,7 +100,12 @@ static void avr_bitbang_write_bit(avr_bitbang_t *p) // output to HW pin if ( p->p_out.port ) { - avr_raise_irq(avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_out.port ), p->p_out.pin), bit); + avr_raise_irq(p->p_out.irq, bit); + uint16_t addr = p->p_out.ioport->r_port; + uint8_t value = (p->avr->data[addr] & ~(1 << p->p_out.pin)) | (!!bit << p->p_out.pin); + // at least avr->data[addr] update is required, otherwise port state is broken + // also triggering vcd signal would be nice here + avr_core_watch_write(p->avr, addr, value); } // module callback @@ -130,7 +136,12 @@ static void avr_bitbang_clk_edge(avr_bitbang_t *p) // generate clock output on HW pin if ( p->clk_generate && p->p_clk.port ) { - avr_raise_irq(avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_clk.port ), p->p_clk.pin), clk); + avr_raise_irq(p->p_clk.irq, clk); + uint16_t addr = p->p_clk.ioport->r_port; + uint8_t value = (p->avr->data[addr] & ~(1 << p->p_clk.pin)) | (!!clk << p->p_clk.pin); + // at least avr->data[addr] update is required, otherwise port state is broken + // also triggering vcd signal would be nice here + avr_core_watch_write(p->avr, addr, value); } if ( phase ) { @@ -175,6 +186,36 @@ static void avr_bitbang_clk_hook(struct avr_irq_t * irq, uint32_t value, void * avr_bitbang_clk_edge(p); } +/** + * define bitbang pins + * + * @param p bitbang structure + * @param clk clock pin + * @param in incoming data pin + * @param out outgoing data pin + */ +void avr_bitbang_defpins(avr_bitbang_t * p, avr_iopin_t *clk, avr_iopin_t *in, avr_iopin_t *out) +{ + if (clk) { + p->p_clk.port = clk->port; + p->p_clk.pin = clk->pin; + p->p_clk.ioport = (avr_ioport_t *)avr_io_findinstance(p->avr, "port", clk->port); + p->p_clk.irq = avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_clk.port ), p->p_clk.pin); + } + if (in) { + p->p_in.port = in->port; + p->p_in.pin = in->pin; + p->p_in.ioport = (avr_ioport_t *)avr_io_findinstance(p->avr, "port", in->port); + p->p_in.irq = avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_in.port ), p->p_in.pin); + } + if (out) { + p->p_out.port = out->port; + p->p_out.pin = out->pin; + p->p_out.ioport = (avr_ioport_t *)avr_io_findinstance(p->avr, "port", out->port); + p->p_out.irq = avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_out.port ), p->p_out.pin); + } +} + /** * reset bitbang sub-module * @@ -220,7 +261,7 @@ void avr_bitbang_start(avr_bitbang_t * p) } else { // slave mode -> attach clock function to clock pin ///@todo test - avr_irq_register_notify( avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_clk.port ), p->p_clk.pin), avr_bitbang_clk_hook, p); + avr_irq_register_notify( p->p_clk.irq, avr_bitbang_clk_hook, p); } } @@ -238,7 +279,7 @@ void avr_bitbang_stop(avr_bitbang_t * p) p->enabled = 0; avr_cycle_timer_cancel(p->avr, avr_bitbang_clk_timer, p); - avr_irq_unregister_notify( avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_clk.port ), p->p_clk.pin), avr_bitbang_clk_hook, p); + avr_irq_unregister_notify( p->p_clk.irq, avr_bitbang_clk_hook, p); } #ifdef __cplusplus diff --git a/simavr/sim/avr_bitbang.h b/simavr/sim/avr_bitbang.h index 3d8832f3a..b4a1dffc2 100644 --- a/simavr/sim/avr_bitbang.h +++ b/simavr/sim/avr_bitbang.h @@ -48,6 +48,15 @@ #include "avr_ioport.h" +/** + * pin structure + */ +typedef struct avr_bitbang_iopin_t { + uint16_t port : 8; ///< port e.g. 'B' + uint16_t pin : 8; ///< pin number + avr_ioport_t * ioport; + avr_irq_t * irq; +} avr_bitbang_iopin_t; /// SPI Module initialization and state structure @@ -73,9 +82,9 @@ typedef struct avr_bitbang_t { uint32_t (*callback_transfer_finished)(uint32_t data, void *param); ///< callback function to notify about a complete transfer /// (read received data and write new output data) - avr_iopin_t p_clk; ///< clock pin (optional) - avr_iopin_t p_in; ///< data in pin - avr_iopin_t p_out; ///< data out pin + avr_bitbang_iopin_t p_clk; ///< clock pin (optional) + avr_bitbang_iopin_t p_in; ///< data in pin + avr_bitbang_iopin_t p_out; ///< data out pin // private data uint32_t data; ///< data buffer @@ -92,6 +101,16 @@ typedef struct avr_bitbang_t { */ void avr_bitbang_reset(avr_t *avr, avr_bitbang_t * p); +/** + * define bitbang pins + * + * @param p bitbang structure + * @param clk clock pin + * @param in incoming data pin + * @param out outgoing data pin + */ +void avr_bitbang_defpins(avr_bitbang_t * p, avr_iopin_t *clk, avr_iopin_t *in, avr_iopin_t *out); + /** * start bitbang transfer * diff --git a/simavr/sim/avr_spi.c b/simavr/sim/avr_spi.c index e97d8246c..2d78b6685 100644 --- a/simavr/sim/avr_spi.c +++ b/simavr/sim/avr_spi.c @@ -21,26 +21,82 @@ #include #include "avr_spi.h" +#include "sim_gdb.h" static avr_cycle_count_t avr_spi_raise(struct avr_t * avr, avr_cycle_count_t when, void * param) { avr_spi_t * p = (avr_spi_t *)param; - + if (avr_regbit_get(avr, p->spe)) { - // in master mode, any byte is sent as it comes.. + avr_raise_interrupt(avr, &p->spi); if (avr_regbit_get(avr, p->mstr)) { - avr_raise_interrupt(avr, &p->spi); - avr_raise_irq(p->io.irq + SPI_IRQ_OUTPUT, avr->data[p->r_spdr]); + avr_raise_irq(p->io.irq + SPI_IRQ_OUTPUT, p->output_data_register); } + // the last received byte will be echoed back the next time, unless user overrides it + p->output_data_register = p->input_data_register; + if (p->flags & AVR_SPI_FLAG_BITBANG_ON) + p->bit_bang.data = p->output_data_register; } return 0; } +static uint32_t avr_spi_transfer_finished(uint32_t data, void *param) +{ + avr_spi_t * p = (avr_spi_t *)param; + if (p->bit_bang.clk_generate) + avr_bitbang_stop(&(p->bit_bang)); + p->input_data_register = data & 0xFF; + p->bit_bang.avr->data[p->r_spdr] = p->input_data_register; // not necessary, but for fullness.. + avr_spi_raise(p->bit_bang.avr, 0, p); + return data; +} + +static void avr_spi_bitbang_switch_mode(avr_spi_t * p, uint8_t master) +{ + if (!(p->flags & AVR_SPI_FLAG_IOPIN_DIR_CTRL)) + return; + if (master) { + //p->bit_bang.p_in = p->p_miso; + //p->bit_bang.p_out = p->p_mosi; + avr_bitbang_defpins(&(p->bit_bang), &(p->p_sck), &(p->p_miso), &(p->p_mosi)); + avr_irq_t *irq; + uint32_t irq_val; + irq = avr_io_getirq( p->bit_bang.avr, + AVR_IOCTL_IOPORT_GETIRQ(p->p_miso.port), + IOPORT_IRQ_DIRECTION_ALL ); + irq_val = irq->value & ~(1 << p->p_miso.pin); + avr_raise_irq(irq, irq_val); + p->bit_bang.clk_generate = 1; + } else { + //p->bit_bang.p_in = p->p_mosi; + //p->bit_bang.p_out = p->p_miso; + avr_bitbang_defpins(&(p->bit_bang), &(p->p_sck), &(p->p_mosi), &(p->p_miso)); + avr_irq_t *irq; + uint32_t irq_val; + irq = avr_io_getirq( p->bit_bang.avr, + AVR_IOCTL_IOPORT_GETIRQ(p->bit_bang.p_clk.port), + IOPORT_IRQ_DIRECTION_ALL ); + irq_val = irq->value & ~(1 << p->bit_bang.p_clk.pin); + avr_raise_irq(irq, irq_val); + irq = avr_io_getirq( p->bit_bang.avr, + AVR_IOCTL_IOPORT_GETIRQ(p->p_mosi.port), + IOPORT_IRQ_DIRECTION_ALL ); + irq_val = irq->value & ~(1 << p->p_mosi.pin); + avr_raise_irq(irq, irq_val); + irq = avr_io_getirq( p->bit_bang.avr, + AVR_IOCTL_IOPORT_GETIRQ(p->p_ss.port), + IOPORT_IRQ_DIRECTION_ALL ); + irq_val = irq->value & ~(1 << p->p_ss.pin); + avr_raise_irq(irq, irq_val); + p->bit_bang.clk_generate = 0; + } +} + static uint8_t avr_spi_read(struct avr_t * avr, avr_io_addr_t addr, void * param) { avr_spi_t * p = (avr_spi_t *)param; uint8_t v = p->input_data_register; - p->input_data_register = 0; + p->input_data_register = 0; // this seems is not correct avr_regbit_clear(avr, p->spi.raised); // printf("avr_spi_read = %02x\n", v); return v; @@ -54,8 +110,82 @@ static void avr_spi_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, voi /* Clear the SPIF bit. See ATmega164/324/644 manual, Section 18.5.2. */ avr_regbit_clear(avr, p->spi.raised); + // The byte to be sent should NOT be written there, + // the value written could never be read back. + //avr_core_watch_write(avr, addr, v); + p->output_data_register = v; + if (avr->gdb) { + avr_gdb_handle_watchpoints(avr, addr, AVR_GDB_WATCH_WRITE); + } + if (avr_regbit_get(avr, p->spe)) { + if (p->flags & AVR_SPI_FLAG_BITBANG_ON) { + avr_bitbang_stop(&(p->bit_bang)); + avr_bitbang_reset(avr, &(p->bit_bang)); + p->bit_bang.data = v; + if (avr_regbit_get(avr, p->mstr)) { + p->bit_bang.clk_generate = 1; + avr_bitbang_start(&(p->bit_bang)); + } else { + p->bit_bang.clk_generate = 0; + } + } else { + if (avr_regbit_get(avr, p->mstr)) + avr_cycle_timer_register(avr, p->bit_bang.clk_cycles, avr_spi_raise, p); + } + } + } else if ((addr == p->r_spcr) || (addr == p->r_spsr)) { avr_core_watch_write(avr, addr, v); - avr_cycle_timer_register_usec(avr, 100, avr_spi_raise, p); // should be speed dependent + avr_bitbang_stop(&(p->bit_bang)); + avr_bitbang_reset(avr, &(p->bit_bang)); + p->bit_bang.data = p->output_data_register; + if (avr_regbit_get(avr, p->spe)) { + p->bit_bang.clk_phase = avr_regbit_get(avr, p->cpha); + p->bit_bang.clk_pol = avr_regbit_get(avr, p->cpol); + p->bit_bang.data_order = avr_regbit_get(avr, p->dord); + if (avr_regbit_get(avr, p->mstr)) { + int clock_divider_ix = (avr_regbit_get(avr, p->spr[0]) | avr_regbit_get(avr, p->spr[1])); + static const char clock_div_values[] = { 4,16,64,128 }; + int clock_divider = clock_div_values[clock_divider_ix]; + if (avr_regbit_get(avr, p->spr[2])) + clock_divider /= 2; + p->bit_bang.clk_cycles = clock_divider; + avr_spi_bitbang_switch_mode(p, 1); + + } else { + avr_spi_bitbang_switch_mode(p, 0); + } + } + } +} + +static void avr_spi_ss_hook(struct avr_irq_t * irq, uint32_t value, void * param) +{ + avr_spi_t * p = (avr_spi_t *)param; + + if (avr_regbit_get(p->bit_bang.avr, p->mstr)) { // master mode + avr_ioport_state_t iostate; + uint8_t dir = 0; + + avr_ioctl(p->bit_bang.avr, AVR_IOCTL_IOPORT_GETSTATE( p->p_ss.port ), &iostate); + dir = ( iostate.ddr >> p->p_ss.pin ) & 1; + if (!dir) { + if (!value) { + // other master is active, reset to slave mode + avr_bitbang_stop(&(p->bit_bang)); + avr_bitbang_reset(p->bit_bang.avr, &(p->bit_bang)); + avr_regbit_setto(p->bit_bang.avr, p->mstr, 0); + avr_spi_bitbang_switch_mode(p, 0); + avr_spi_raise(p->bit_bang.avr, 0, p); + } + } + } else { // slave mode + if (value) { + avr_bitbang_stop(&(p->bit_bang)); + avr_bitbang_reset(p->bit_bang.avr, &(p->bit_bang)); + } else { + if (avr_regbit_get(p->bit_bang.avr, p->spe)) + avr_bitbang_start(&(p->bit_bang)); + } } } @@ -70,21 +200,59 @@ static void avr_spi_irq_input(struct avr_irq_t * irq, uint32_t value, void * par // double buffer the input.. ? p->input_data_register = value; + avr->data[p->r_spdr] = p->input_data_register; // not necessary, but for fullness.. avr_raise_interrupt(avr, &p->spi); // if in slave mode, // 'output' the byte only when we received one... if (!avr_regbit_get(avr, p->mstr)) { - avr_raise_irq(p->io.irq + SPI_IRQ_OUTPUT, avr->data[p->r_spdr]); + avr_raise_irq(p->io.irq + SPI_IRQ_OUTPUT, p->output_data_register); // avr->data[p->r_spdr]); } + // the last received byte will be echoed next time, unless user overrides it + p->output_data_register = p->input_data_register; } void avr_spi_reset(struct avr_io_t *io) { avr_spi_t * p = (avr_spi_t *)io; + avr_bitbang_reset(p->bit_bang.avr, &(p->bit_bang)); avr_irq_register_notify(p->io.irq + SPI_IRQ_INPUT, avr_spi_irq_input, p); } +static int +avr_spi_ioctl( + struct avr_io_t * port, + uint32_t ctl, + void * io_param) +{ + avr_spi_t * p = (avr_spi_t *)port; + int res = -1; + + if (!io_param) + return res; + + if (ctl == AVR_IOCTL_SPI_SET_FLAGS(p->name)) { + p->flags = *(uint32_t*)io_param; + if (p->flags & AVR_SPI_FLAG_BITBANG_ON) { + avr_irq_register_notify( avr_io_getirq(p->bit_bang.avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_ss.port ), p->p_ss.pin), + avr_spi_ss_hook, p); + p->flags |= AVR_SPI_FLAG_IOPIN_DIR_CTRL | AVR_SPI_FLAG_SSPIN_HANDLE; + } else { + p->bit_bang.clk_generate = 0; + if (!(p->flags & AVR_SPI_FLAG_SSPIN_HANDLE)) + avr_irq_unregister_notify( avr_io_getirq(p->bit_bang.avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_ss.port ), p->p_ss.pin), + avr_spi_ss_hook, p); + } + res = 0; + } + if (ctl == AVR_IOCTL_SPI_GET_FLAGS(p->name)) { + *(uint32_t*)io_param = p->flags; + res = 0; + } + + return res; +} + static const char * irq_names[SPI_IRQ_COUNT] = { [SPI_IRQ_INPUT] = "8io = _io; + p->bit_bang.avr = avr; + p->bit_bang.callback_transfer_finished = avr_spi_transfer_finished; + p->bit_bang.callback_param = p; + p->bit_bang.buffer_size = 8; + p->bit_bang.clk_cycles = 4; + p->flags = 0; + avr_register_io(avr, &p->io); avr_register_vector(avr, &p->spi); // allocate this module's IRQ avr_io_setirqs(&p->io, AVR_IOCTL_SPI_GETIRQ(p->name), SPI_IRQ_COUNT, NULL); avr_register_io_write(avr, p->r_spdr, avr_spi_write, p); + avr_register_io_write(avr, p->r_spcr, avr_spi_write, p); + avr_register_io_write(avr, p->r_spsr, avr_spi_write, p); avr_register_io_read(avr, p->r_spdr, avr_spi_read, p); + } diff --git a/simavr/sim/avr_spi.h b/simavr/sim/avr_spi.h index 23ff78f2a..99b4d589c 100644 --- a/simavr/sim/avr_spi.h +++ b/simavr/sim/avr_spi.h @@ -27,6 +27,7 @@ extern "C" { #endif #include "sim_avr.h" +#include "avr_bitbang.h" enum { SPI_IRQ_INPUT = 0, @@ -34,32 +35,50 @@ enum { SPI_IRQ_COUNT }; + +#define AVR_SPI_FLAG_BITBANG_ON 0x01 +#define AVR_SPI_FLAG_IOPIN_DIR_CTRL 0x02 +#define AVR_SPI_FLAG_SSPIN_HANDLE 0x04 + // add port number to get the real IRQ #define AVR_IOCTL_SPI_GETIRQ(_name) AVR_IOCTL_DEF('s','p','i',(_name)) +#define AVR_IOCTL_SPI_SET_FLAGS(_name) AVR_IOCTL_DEF('s','p','s',(_name)) +#define AVR_IOCTL_SPI_GET_FLAGS(_name) AVR_IOCTL_DEF('s','p','g',(_name)) typedef struct avr_spi_t { avr_io_t io; char name; avr_regbit_t disabled; // bit in the PRR - avr_io_addr_t r_spdr; // data register - avr_io_addr_t r_spcr; // control register - avr_io_addr_t r_spsr; // status register + avr_io_addr_t r_spdr; // data register + avr_io_addr_t r_spcr; // control register + avr_io_addr_t r_spsr; // status register - avr_regbit_t spe; // spi enable - avr_regbit_t mstr; // master/slave - avr_regbit_t spr[4]; // clock divider - - avr_int_vector_t spi; // spi interrupt + avr_regbit_t spe; // spi enable + avr_regbit_t dord; // if zero, the MSB of the data word is transmitted first + avr_regbit_t mstr; // master/slave + avr_regbit_t cpol; // if zero, SCK is low when idle + avr_regbit_t cpha; // if zero, sampling on the SCK leading edge + avr_regbit_t spr[4]; // clock divider + + avr_iopin_t p_sck; // clock in/out pin + avr_iopin_t p_miso; // data in/out pin + avr_iopin_t p_mosi; // data in/out pin + avr_bitbang_t bit_bang; + avr_iopin_t p_ss; // slave select pin + + avr_int_vector_t spi; // spi interrupt uint8_t input_data_register; + uint8_t output_data_register; + uint32_t flags; } avr_spi_t; void avr_spi_init(avr_t * avr, avr_spi_t * port); -#define AVR_SPIX_DECLARE(_name, _prr, _prspi) \ +#define AVR_SPIX_DECLARE(_name, _prr, _prspi, _ss_pin_port, _p_sck, _p_miso, _p_mosi, _p_ss) \ .spi = { \ - .name = '0' + _name,\ + .name = _name, \ .disabled = AVR_IO_REGBIT(_prr, _prspi), \ \ .r_spdr = SPDR ## _name, \ @@ -67,7 +86,15 @@ void avr_spi_init(avr_t * avr, avr_spi_t * port); .r_spsr = SPSR ## _name, \ \ .spe = AVR_IO_REGBIT(SPCR ## _name, SPE ## _name), \ + .dord = AVR_IO_REGBIT(SPCR ## _name, DORD ## _name), \ .mstr = AVR_IO_REGBIT(SPCR ## _name, MSTR ## _name), \ + .cpol = AVR_IO_REGBIT(SPCR ## _name, CPOL ## _name), \ + .cpha = AVR_IO_REGBIT(SPCR ## _name, CPHA ## _name), \ + \ + .p_sck = AVR_IOPIN('B', _p_sck), \ + .p_miso = AVR_IOPIN('B', _p_miso), \ + .p_mosi = AVR_IOPIN('B', _p_mosi), \ + .p_ss = AVR_IOPIN(_ss_pin_port, _p_ss), \ \ .spr = { AVR_IO_REGBIT(SPCR ## _name, SPR0 ## _name), \ AVR_IO_REGBIT(SPCR ## _name, SPR1 ## _name), \ @@ -79,9 +106,9 @@ void avr_spi_init(avr_t * avr, avr_spi_t * port); }, \ } - -#define AVR_SPI_DECLARE(_prr, _prspi) \ +#define AVR_SPI_DECLARE(_prr, _prspi, _ss_pin_port, _p_sck, _p_miso, _p_mosi, _p_ss) \ .spi = { \ + .name = 0, \ .disabled = AVR_IO_REGBIT(_prr, _prspi), \ \ .r_spdr = SPDR, \ @@ -89,7 +116,15 @@ void avr_spi_init(avr_t * avr, avr_spi_t * port); .r_spsr = SPSR, \ \ .spe = AVR_IO_REGBIT(SPCR, SPE), \ + .dord = AVR_IO_REGBIT(SPCR, DORD), \ .mstr = AVR_IO_REGBIT(SPCR, MSTR), \ + .cpol = AVR_IO_REGBIT(SPCR, CPOL), \ + .cpha = AVR_IO_REGBIT(SPCR, CPHA), \ + \ + .p_sck = AVR_IOPIN('B', _p_sck), \ + .p_miso = AVR_IOPIN('B', _p_miso), \ + .p_mosi = AVR_IOPIN('B', _p_mosi), \ + .p_ss = AVR_IOPIN(_ss_pin_port, _p_ss), \ \ .spr = { AVR_IO_REGBIT(SPCR, SPR0), AVR_IO_REGBIT(SPCR, SPR1), AVR_IO_REGBIT(SPSR, SPI2X) }, \ .spi = { \ diff --git a/simavr/sim/sim_elf.c b/simavr/sim/sim_elf.c index f2f1a154b..9287e56b0 100644 --- a/simavr/sim/sim_elf.c +++ b/simavr/sim/sim_elf.c @@ -37,6 +37,11 @@ #include "avr_eeprom.h" #include "avr_ioport.h" +#include "avr_spi.h" +#include "avr_uart.h" +#include "avr_twi.h" +#include "avr_lin.h" + #ifndef O_BINARY #define O_BINARY 0 #endif @@ -102,6 +107,31 @@ avr_load_firmware( avr_set_command_register(avr, firmware->command_register_addr); avr_set_console_register(avr, firmware->console_register_addr); + // initialize bitbang mode for multi-instance peripherals + for (char name = '0'; name < '9'; name++) { + if ((firmware->bitbang_on_mask & BITBANG_ON_SPI(name)) != 0) { + uint32_t f = 0; + avr_ioctl(avr, AVR_IOCTL_SPI_GET_FLAGS(name), &f); + f |= AVR_SPI_FLAG_BITBANG_ON; + avr_ioctl(avr, AVR_IOCTL_SPI_SET_FLAGS(name), &f); + } + if ((firmware->bitbang_on_mask & BITBANG_ON_SPI(name - '0')) != 0) { + uint32_t f = 0; + avr_ioctl(avr, AVR_IOCTL_SPI_GET_FLAGS(name - '0'), &f); + f |= AVR_SPI_FLAG_BITBANG_ON; + avr_ioctl(avr, AVR_IOCTL_SPI_SET_FLAGS(name - '0'), &f); + } + if ((firmware->bitbang_on_mask & BITBANG_ON_UART(name)) != 0) { + //TODO: implement bitbang mode for UART modeule + } + if ((firmware->bitbang_on_mask & BITBANG_ON_TWI(name)) != 0) { + //TODO: implement bitbang mode for TWI modeule + } + if ((firmware->bitbang_on_mask & BITBANG_ON_LIN(name)) != 0) { + //TODO: implement bitbang mode for LIN modeule + } + } + // rest is initialization of the VCD file if (firmware->tracecount == 0) return; @@ -267,6 +297,10 @@ elf_parse_mmcu_section( case AVR_MMCU_TAG_SIMAVR_CONSOLE: { firmware->console_register_addr = src[0] | (src[1] << 8); } break; + case AVR_MMCU_TAG_BITBANG: { + firmware->bitbang_on_mask = + src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); + } break; } size -= next; src += next - 2; // already incremented diff --git a/simavr/sim/sim_elf.h b/simavr/sim/sim_elf.h index 174a44027..f06fe8da9 100644 --- a/simavr/sim/sim_elf.h +++ b/simavr/sim/sim_elf.h @@ -60,6 +60,9 @@ typedef struct elf_firmware_t { uint8_t mask, value; } external_state[8]; + // Bitbang turn on for peripheral instance mask, like BITBANG_ON_SPI('0') | BITBANG_ON_UART('1') + uint64_t bitbang_on_mask; + // register to listen to for commands from the firmware uint16_t command_register_addr; uint16_t console_register_addr; diff --git a/simavr/sim/sim_io.c b/simavr/sim/sim_io.c index 548f03061..0b263f55d 100644 --- a/simavr/sim/sim_io.c +++ b/simavr/sim/sim_io.c @@ -296,3 +296,31 @@ avr_deallocate_ios( } avr->io_port = NULL; } + +avr_io_t * +avr_io_findinstance( + avr_t * avr, + const char * kind, + char name) +{ + avr_io_t * io = avr->io_port; + while (io) { + if (!strcmp(io->kind, kind)) + { + if (!strcmp(kind, "port") || + !strcmp(kind, "uart") || + !strcmp(kind, "usb") || + !strcmp(kind, "spi") || + !strcmp(kind, "twi") || + !strcmp(kind, "timer")) { + avr_io_instance_t * io_inst = (avr_io_instance_t *)io; + if (io_inst->name == name) + return io; + } else { + return io; + } + } + io = io->next; + } + return NULL; +} diff --git a/simavr/sim/sim_io.h b/simavr/sim/sim_io.h index cfd2fdbb0..685e0d178 100644 --- a/simavr/sim/sim_io.h +++ b/simavr/sim/sim_io.h @@ -104,6 +104,18 @@ avr_io_getirq( avr_t * avr, uint32_t ctl, int index); +typedef struct avr_io_instance_t { + avr_io_t io; + char name; +} avr_io_instance_t; +// finds an IO module instance by its kind (e.g. "port" for ioport, "uart" for usart and so on) +// and (if applicable) symbolic name (e.g. 'B' for ioport, '0' for uart) +avr_io_t * +avr_io_findinstance( + avr_t * avr, + const char * kind, + char name); + // get the IRQ for an absolute IO address // this allows any code to hook an IRQ in any io address, for example