* these should probably be allocated dynamically in init()..
*/
struct {
- void * param;
- avr_io_read_t r;
- } ior[MAX_IOs];
- struct {
- void * param;
- avr_io_write_t w;
- } iow[MAX_IOs];
+ struct avr_irq_t * irq; // optional, used only if asked for with avr_iomem_getirq()
+ struct {
+ void * param;
+ avr_io_read_t c;
+ } r;
+ struct {
+ void * param;
+ avr_io_write_t c;
+ } w;
+ } io[MAX_IOs];
// flash memory (initialized to 0xff, and code loaded into it)
uint8_t * flash;
uint32_t touched[256 / 32]; // debug
#endif
+ // this is the VCD file that gets allocated if the
+ // firmware that is loaded explicitely asks for a trace
+ // to be generated, and allocates it's own symbols
+ // using AVR_MMCU_TAG_VCD_TRACE (see avr_mcu_section.h)
+ struct avr_vcd_t * vcd;
+
// gdb hooking structure. Only present when gdb server is active
struct avr_gdb_t * gdb;
// if non-zero, the gdb server will be started when the core
}
if (r > 31) {
uint8_t io = AVR_DATA_TO_IO(r);
- if (avr->iow[io].w)
- avr->iow[io].w(avr, r, v, avr->iow[io].param);
+ if (avr->io[io].w.c)
+ avr->io[io].w.c(avr, r, v, avr->io[io].w.param);
else
avr->data[r] = v;
+ if (avr->io[io].irq) {
+ avr_raise_irq(avr->io[io].irq + AVR_IOMEM_IRQ_ALL, v);
+ for (int i = 0; i < 8; i++)
+ avr_raise_irq(avr->io[io].irq + i, (v >> i) & 1);
+ }
} else
avr->data[r] = v;
}
{
if (addr > 31 && addr < 256) {
uint8_t io = AVR_DATA_TO_IO(addr);
- if (avr->ior[io].r)
- avr->data[addr] = avr->ior[io].r(avr, addr, avr->ior[io].param);
+ if (avr->io[io].r.c)
+ avr->data[addr] = avr->io[io].r.c(avr, addr, avr->io[io].r.param);
+
+ if (avr->io[io].irq) {
+ uint8_t v = avr->data[addr];
+ avr_raise_irq(avr->io[io].irq + AVR_IOMEM_IRQ_ALL, v);
+ for (int i = 0; i < 8; i++)
+ avr_raise_irq(avr->io[io].irq + i, (v >> i) & 1);
+ }
}
return avr_core_watch_read(avr, addr);
}
void avr_register_io_read(avr_t *avr, avr_io_addr_t addr, avr_io_read_t readp, void * param)
{
- avr->ior[AVR_DATA_TO_IO(addr)].param = param;
- avr->ior[AVR_DATA_TO_IO(addr)].r = readp;
+ avr_io_addr_t a = AVR_DATA_TO_IO(addr);
+ avr->io[a].r.param = param;
+ avr->io[a].r.c = readp;
}
void avr_register_io_write(avr_t *avr, avr_io_addr_t addr, avr_io_write_t writep, void * param)
{
- avr->iow[AVR_DATA_TO_IO(addr)].param = param;
- avr->iow[AVR_DATA_TO_IO(addr)].w = writep;
+ avr_io_addr_t a = AVR_DATA_TO_IO(addr);
+ avr->io[a].w.param = param;
+ avr->io[a].w.c = writep;
}
struct avr_irq_t * avr_io_getirq(avr_t * avr, uint32_t ctl, int index)
return NULL;
}
+
+
+avr_irq_t * avr_iomem_getirq(avr_t * avr, avr_io_addr_t addr, int index)
+{
+ avr_io_addr_t a = AVR_DATA_TO_IO(addr);
+ if (avr->io[a].irq == NULL) {
+ avr->io[a].irq = avr_alloc_irq(0, 9);
+ // mark the pin ones as filtered, so they only are raised when changing
+ for (int i = 0; i < 8; i++)
+ avr->io[a].irq[i].flags |= IRQ_FLAG_FILTERED;
+ }
+ return index < 9 ? avr->io[a].irq + index : NULL;
+}
+
// get the specific irq for a module, check AVR_IOCTL_IOPORT_GETIRQ for example
struct avr_irq_t * avr_io_getirq(avr_t * avr, uint32_t ctl, int index);
+// get the IRQ for an absolute IO address
+// this allows any code to hook an IRQ in any io address, for example
+// tracing changes of values into a register
+// Note that the values do not "magicaly" change, they change only
+// when the AVR code attempt to read and write at that address
+//
+// the "index" is a bit number, or ALL bits if index == 8
+#define AVR_IOMEM_IRQ_ALL 8
+struct avr_irq_t * avr_iomem_getirq(avr_t * avr, avr_io_addr_t addr, int index);
+
#endif /* __SIM_IO_H__ */