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")))
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;
.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.
#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)
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
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);
// 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);
#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));
}
}
}
- 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)
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
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;
*/
#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')
int main()
{
+ // this tell simavr to put the UART in loopback mode
+ GPIOR0 = SIMAVR_CMD_UART_LOOPBACK;
+
stdout = &mystdout;
// enable receiver