core: Added a new ELF tag with AVR->simavr command path
authorMichel Pollet <buserror@gmail.com>
Wed, 23 Dec 2009 22:17:46 +0000 (22:17 +0000)
committerMichel Pollet <buserror@gmail.com>
Wed, 23 Dec 2009 22:17:46 +0000 (22:17 +0000)
This new mode allow the AVR firmware to specify an (unused)
AVR IO register as a "command path" to send commands back to
simavr.
It allows for example the firmware to start/stop the VCD trace
dump, exactly where it should from the ooint of view of the
firmware being ran.

See atmega88_uart_echo.c for an example.

Signed-off-by: Michel Pollet <buserror@gmail.com>
include/avr_mcu_section.h
simavr/sim/sim_avr.c
simavr/sim/sim_avr.h
simavr/sim/sim_elf.c
simavr/sim/sim_elf.h
tests/atmega88_uart_echo.c

index eb36512..01118ca 100644 (file)
@@ -48,11 +48,19 @@ enum {
        AVR_MMCU_TAG_HFUSE,
        AVR_MMCU_TAG_EFUSE,
        AVR_MMCU_TAG_SIGNATURE,
+       AVR_MMCU_TAG_SIMAVR_COMMAND,
        AVR_MMCU_TAG_VCD_FILENAME,
        AVR_MMCU_TAG_VCD_PERIOD,        
        AVR_MMCU_TAG_VCD_TRACE,
 };
 
+enum {
+       SIMAVR_CMD_NONE = 0,
+       SIMAVR_CMD_VCD_START_TRACE,
+       SIMAVR_CMD_VCD_STOP_TRACE,
+       SIMAVR_CMD_UART_LOOPBACK,
+};
+
 #if __AVR__
 
 #define _MMCU_ __attribute__((section(".mmcu")))
@@ -68,6 +76,12 @@ struct avr_mmcu_string_t {
        char string[]; 
 } __attribute__((__packed__));
 
+struct avr_mmcu_addr_t {
+       uint8_t tag;
+       uint8_t len;
+       void * what;
+} __attribute__((__packed__));
+
 struct avr_mmcu_vcd_trace_t {
        uint8_t tag;
        uint8_t len;
@@ -98,13 +112,25 @@ const uint8_t _##_tag _MMCU_ = { _tag, 1, _val }
        .len = sizeof(struct avr_mmcu_vcd_trace_t) - 2 + sizeof(_name),\
        .name = _name
 
-// specified the nane and wanted period (usec) for a VCD file
+// specified the name and wanted period (usec) for a VCD file
 // thid is not mandatory, a default one will be created if
 // symbols are declared themselves
 #define AVR_MCU_VCD_FILE(_name, _period) \
        AVR_MCU_STRING(AVR_MMCU_TAG_VCD_FILENAME, _name);\
        AVR_MCU_LONG(AVR_MMCU_TAG_VCD_PERIOD, _period)
 
+// It is possible to send "commands" to simavr from the
+// firmware itself. For this to work you need to specify
+// an IO register that is to be used for a write-only
+// bridge. A favourite is one of the usual "GPIO register"
+// that most (all ?) AVR have
+#define AVR_MCU_SIMAVR_COMMAND(_register) \
+       const struct avr_mmcu_addr_t _simavr_command_register _MMCU_ = {\
+               .tag = AVR_MMCU_TAG_SIMAVR_COMMAND,\
+               .len = sizeof(void *),\
+               .what = (void*)_register, \
+       }
+
 /*
  * This the has to be used if you want to add other tags to the .mmcu section
  * the _mmcu symbol is used as an anchor to make sure it stays linked in.
index 1af351e..1682c2e 100644 (file)
@@ -26,7 +26,9 @@
 #include "sim_avr.h"
 #include "sim_core.h"
 #include "sim_gdb.h"
+#include "avr_uart.h"
 #include "sim_vcd_file.h"
+#include "avr_mcu_section.h"
 
 
 int avr_init(avr_t * avr)
@@ -73,6 +75,7 @@ void avr_reset(avr_t * avr)
 
 void avr_sadly_crashed(avr_t *avr, uint8_t signal)
 {
+       printf("%s\n", __FUNCTION__);
        avr->state = cpu_Stopped;
        if (avr->gdb_port) {
                // enable gdb server, and wait
@@ -83,6 +86,36 @@ void avr_sadly_crashed(avr_t *avr, uint8_t signal)
                exit(1); // no gdb ?
 }
 
+static void _avr_io_command_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
+{
+       printf("%s %02x\n", __FUNCTION__, v);
+       switch (v) {
+               case SIMAVR_CMD_VCD_START_TRACE:
+                       if (avr->vcd)
+                               avr_vcd_start(avr->vcd);
+                       break;
+               case SIMAVR_CMD_VCD_STOP_TRACE:
+                       if (avr->vcd)
+                               avr_vcd_stop(avr->vcd);
+                       break;
+               case SIMAVR_CMD_UART_LOOPBACK: {
+                       avr_irq_t * src = avr_io_getirq(avr, AVR_IOCTL_UART_GETIRQ('0'), UART_IRQ_OUTPUT);
+                       avr_irq_t * dst = avr_io_getirq(avr, AVR_IOCTL_UART_GETIRQ('0'), UART_IRQ_INPUT);
+                       if (src && dst) {
+                               printf("%s activating uart local echo IRQ src %p dst %p\n", __FUNCTION__, src, dst);
+                               avr_connect_irq(src, dst);
+                       }
+               }       break;
+
+       }
+}
+
+void avr_set_command_register(avr_t * avr, avr_io_addr_t addr)
+{
+       if (addr)
+               avr_register_io_write(avr, addr, _avr_io_command_write, NULL);
+}
+
 void avr_loadcode(avr_t * avr, uint8_t * code, uint32_t size, uint32_t address)
 {
        memcpy(avr->flash + address, code, size);
index e763f04..5a96a19 100644 (file)
@@ -229,6 +229,9 @@ int avr_run(avr_t * avr);
 // finish any pending operations 
 void avr_terminate(avr_t * avr);
 
+// set an IO register to receive commands from the AVR firmware
+// it's optional, and uses the ELF tags
+void avr_set_command_register(avr_t * avr, avr_io_addr_t addr);
 // load code in the "flash"
 void avr_loadcode(avr_t * avr, uint8_t * code, uint32_t size, uint32_t address);
 
index 92785e0..723cda9 100644 (file)
@@ -42,13 +42,13 @@ void avr_load_firmware(avr_t * avr, elf_firmware_t * firmware)
 #if CONFIG_SIMAVR_TRACE
        avr->codeline = firmware->codeline;
 #endif
-       avr_loadcode(avr, firmware->flash, firmware->flashsize, 0);
-       avr->codeend = firmware->flashsize - firmware->datasize;
+       avr_loadcode(avr, firmware->flash, firmware->flashsize, firmware->flashbase);
+       avr->codeend = firmware->flashsize + firmware->flashbase - firmware->datasize;
        if (firmware->eeprom && firmware->eesize) {
                avr_eeprom_desc_t d = { .ee = firmware->eeprom, .offset = 0, .size = firmware->eesize };
                avr_ioctl(avr, AVR_IOCTL_EEPROM_SET, &d);
        }
-
+       avr_set_command_register(avr, firmware->command_register_addr);
        if (firmware->tracecount == 0)
                return;
        avr->vcd = malloc(sizeof(*avr->vcd));
@@ -93,7 +93,10 @@ void avr_load_firmware(avr_t * avr, elf_firmware_t * firmware)
                                }
                }
        }
-       avr_vcd_start(avr->vcd);
+       // if the firmware has specified a command register, do NOT start the trace here
+       // the firmware probably knows best when to start/stop it
+       if (!firmware->command_register_addr)
+               avr_vcd_start(avr->vcd);
 }
 
 static void elf_parse_mmcu_section(elf_firmware_t * firmware, uint8_t * src, uint32_t size)
@@ -129,6 +132,9 @@ static void elf_parse_mmcu_section(elf_firmware_t * firmware, uint8_t * src, uin
                                firmware->traceperiod =
                                        src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
                        }       break;
+                       case AVR_MMCU_TAG_SIMAVR_COMMAND: {
+                               firmware->command_register_addr = src[0] | (src[1] << 8);
+                       }       break;
                }
                size -= next;
                src += next - 2; // already incremented
index f12c8f7..172b1c5 100644 (file)
@@ -43,13 +43,17 @@ typedef struct elf_firmware_t {
                char    name[64];
        } trace[32];
        
-       uint8_t * flash;
-       uint32_t flashsize;
-       uint32_t datasize;
-       uint32_t bsssize;
+       // register to listen to for commands from the firmware
+       uint16_t        command_register_addr;
+
+       uint32_t        flashbase;      // base address
+       uint8_t *       flash;
+       uint32_t        flashsize;
+       uint32_t        datasize;
+       uint32_t        bsssize;
        // read the .eeprom section of the elf, too
-       uint8_t * eeprom;
-       uint32_t eesize;
+       uint8_t *       eeprom;
+       uint32_t        eesize;
 
 #if ELF_SYMBOLS
        avr_symbol_t **  codeline;
index c2b4ba3..c904013 100644 (file)
@@ -21,7 +21,8 @@
  */
 #include "avr_mcu_section.h"
 AVR_MCU(F_CPU, "atmega88");
-
+// tell simavr to listen to commands written in this (unused) register
+AVR_MCU_SIMAVR_COMMAND(&GPIOR0);
 
 static int uart_putchar(char c, FILE *stream) {
   if (c == '\n')
@@ -49,6 +50,9 @@ ISR(USART_RX_vect)
 
 int main()
 {
+       // this tell simavr to put the UART in loopback mode
+       GPIOR0 = SIMAVR_CMD_UART_LOOPBACK;
+
        stdout = &mystdout;
 
        // enable receiver