Skip to content

Commit 65e7e1e

Browse files
author
ga
committed
Add an IRQ for execution of an undefined opcode and a test to
show how it can be used by simulation-aware firmware to request services from the host program. Also add new IRQ function avr_get_memory_irq() and use it in sim_elf.c.
1 parent 5d42c8d commit 65e7e1e

File tree

6 files changed

+332
-33
lines changed

6 files changed

+332
-33
lines changed

simavr/sim/sim_avr.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ int
9797
avr_init(
9898
avr_t * avr)
9999
{
100+
static const char *names[] = { ">avr.core.bad_opcode", }; // IRQs
101+
100102
avr->flash = malloc(avr->flashend + 4);
101103
memset(avr->flash, 0xff, avr->flashend + 1);
102104
*((uint16_t*)&avr->flash[avr->flashend + 1]) = AVR_OVERFLOW_OPCODE;
@@ -126,6 +128,7 @@ avr_init(
126128
avr->state = cpu_Limbo;
127129
avr->frequency = 1000000; // can be overridden via avr_mcu_section
128130
avr->irq_pool.avr = avr;
131+
avr->irq = avr_alloc_irq(&avr->irq_pool, 0, AVR_CORE_IRQ_COUNT, names);
129132
avr_cmd_init(avr);
130133
avr_interrupt_init(avr);
131134
if (avr->custom.init)
@@ -214,6 +217,47 @@ avr_sadly_crashed(
214217
avr->state = cpu_Crashed;
215218
}
216219

220+
/* Get a pointer to a core IRQ. */
221+
222+
avr_irq_t *
223+
avr_get_core_irq(
224+
avr_t * avr,
225+
int irq_no)
226+
{
227+
return avr->irq + irq_no;
228+
}
229+
230+
/* Get a pointer to a memory IRQ. */
231+
232+
avr_irq_t *avr_get_memory_irq(avr_t * avr, uint16_t addr, int is16)
233+
{
234+
avr_irq_t *irq;
235+
int width;
236+
char name[32];
237+
const char *names[1] = {name};
238+
239+
if (addr <= 31 || addr > avr->ramend) {
240+
AVR_LOG(avr, LOG_ERROR,
241+
"Address %#04x out of range for SRAM trace.\n", addr);
242+
return NULL;
243+
}
244+
if (avr->sram_tracepoint_count >= ARRAY_SIZE(avr->sram_tracepoint)) {
245+
AVR_LOG(avr, LOG_ERROR, "Too many SRAM traces (limit = %d)\n",
246+
ARRAY_SIZE(avr->sram_tracepoint));
247+
return NULL;
248+
}
249+
250+
width = is16 ? 16 : 8;
251+
sprintf(name, ">%dSRAM_tracepoint_%d", width, avr->sram_tracepoint_count);
252+
irq = avr_alloc_irq(&avr->irq_pool, 0, 1, names);
253+
if (!irq)
254+
return NULL;
255+
avr->sram_tracepoint[avr->sram_tracepoint_count].irq = irq;
256+
avr->sram_tracepoint[avr->sram_tracepoint_count].width = width;
257+
avr->sram_tracepoint[avr->sram_tracepoint_count++].addr = addr;
258+
return irq;
259+
}
260+
217261
void
218262
avr_set_command_register(
219263
avr_t * avr,

simavr/sim/sim_avr.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@ enum {
9696
#define AVR_TRACE(avr, ... ) \
9797
AVR_LOG(avr, LOG_TRACE, __VA_ARGS__)
9898

99+
/* The core's own IRQS. */
100+
101+
enum {
102+
AVR_CORE_BAD_OPCODE = 0, // Execution of unknown instruction.
103+
AVR_CORE_IRQ_COUNT,
104+
};
105+
99106
/*
100107
* Core states.
101108
*/
@@ -328,6 +335,10 @@ typedef struct avr_t {
328335
// queue of io modules
329336
struct avr_io_t * io_port;
330337

338+
// Core IRQs
339+
340+
avr_irq_t * irq;
341+
331342
// Builtin and user-defined commands
332343
avr_cmd_table_t commands;
333344
// cycle timers tracking & delivery
@@ -401,6 +412,21 @@ avr_core_allocate(
401412
void
402413
avr_reset(
403414
avr_t * avr);
415+
416+
// Get a pointer to a core IRQ.
417+
418+
avr_irq_t *
419+
avr_get_core_irq(
420+
avr_t * avr,
421+
int irq_no);
422+
423+
/* Get a pointer to a memory IRQ. */
424+
425+
avr_irq_t *avr_get_memory_irq(
426+
avr_t * avr,
427+
uint16_t addr,
428+
int is16);
429+
404430
// run one cycle of the AVR, sleep if necessary
405431
int
406432
avr_run(

simavr/sim/sim_core.c

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -441,10 +441,28 @@ const char * avr_regname(avr_t * avr, unsigned int reg)
441441
}
442442

443443
/*
444-
* Called when an invalid opcode is decoded
444+
* Called when an invalid opcode is decoded. Updating avr->pc will work.
445445
*/
446446
static void _avr_invalid_opcode(avr_t * avr)
447447
{
448+
/* This could be an attempt by simulation-aware firmware to summon a demon,
449+
* or other assistance. Raise an IRQ and if the CPU state is changed by
450+
* the call, do not show an error messgae. In that case a new PC may be
451+
* supplied.
452+
*/
453+
454+
avr_raise_irq(avr->irq + AVR_CORE_BAD_OPCODE,
455+
_avr_flash_read16le(avr, avr->pc));
456+
if (avr->state != cpu_Running) {
457+
/* The IRQ may sleep or finish simulation. But if it performed
458+
* a service and now wants to resume, just suppress the error message.
459+
*/
460+
461+
if (avr->state == cpu_StepDone) // Re-use special value for GDB.
462+
avr->state = cpu_Running;
463+
return;
464+
}
465+
avr->pc += 2; // Step over.
448466
#if CONFIG_SIMAVR_TRACE
449467
printf( FONT_RED "*** %04x: %-25s Invalid Opcode SP=%04x O=%04x \n" FONT_DEFAULT,
450468
avr->pc,
@@ -841,7 +859,10 @@ avr_flashaddr_t avr_run_one(avr_t * avr)
841859
avr->sreg[S_Z] = res == 0;
842860
SREG();
843861
} break;
844-
default: _avr_invalid_opcode(avr);
862+
default:
863+
_avr_invalid_opcode(avr);
864+
new_pc = avr->pc;
865+
break;
845866
}
846867
}
847868
}
@@ -889,7 +910,7 @@ avr_flashaddr_t avr_run_one(avr_t * avr)
889910
_avr_flags_add_zns(avr, res, vd, vr);
890911
SREG();
891912
} break;
892-
default: _avr_invalid_opcode(avr);
913+
default: _avr_invalid_opcode(avr); new_pc = avr->pc;
893914
}
894915
} break;
895916

@@ -933,7 +954,7 @@ avr_flashaddr_t avr_run_one(avr_t * avr)
933954
STATE("mov %s, %s[%02x] = %02x\n", AVR_REGNAME(d), AVR_REGNAME(r), vr, res);
934955
_avr_set_r(avr, d, res);
935956
} break;
936-
default: _avr_invalid_opcode(avr);
957+
default: _avr_invalid_opcode(avr); new_pc = avr->pc;
937958
}
938959
} break;
939960

@@ -1022,7 +1043,7 @@ avr_flashaddr_t avr_run_one(avr_t * avr)
10221043
}
10231044
cycle += 1; // 2 cycles, 3 for tinyavr
10241045
} break;
1025-
default: _avr_invalid_opcode(avr);
1046+
default: _avr_invalid_opcode(avr); new_pc = avr->pc;
10261047
}
10271048
} break;
10281049

@@ -1065,8 +1086,11 @@ avr_flashaddr_t avr_run_one(avr_t * avr)
10651086
case 0x9519: { // EICALL -- Indirect Call to Subroutine -- 1001 0101 0001 1001 bit 8 is "push pc"
10661087
int e = opcode & 0x10;
10671088
int p = opcode & 0x100;
1068-
if (e && !avr->eind)
1089+
if (e && !avr->eind) {
10691090
_avr_invalid_opcode(avr);
1091+
new_pc = avr->pc;
1092+
}
1093+
10701094
uint32_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8);
10711095
if (e)
10721096
z |= avr->data[avr->eind] << 16;
@@ -1098,9 +1122,14 @@ avr_flashaddr_t avr_run_one(avr_t * avr)
10981122
cycle += 2; // 3 cycles
10991123
} break;
11001124
case 0x95d8: { // ELPM -- Load Program Memory R0 <- (Z) -- 1001 0101 1101 1000
1101-
if (!avr->rampz)
1125+
if (!avr->rampz) {
11021126
_avr_invalid_opcode(avr);
1103-
uint32_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8) | (avr->data[avr->rampz] << 16);
1127+
new_pc = avr->pc;
1128+
}
1129+
1130+
uint32_t z;
1131+
z = avr->data[R_ZL] | (avr->data[R_ZH] << 8) |
1132+
(avr->data[avr->rampz] << 16);
11041133
STATE("elpm %s, (Z[%02x:%04x] \t%s)\n",
11051134
AVR_REGNAME(0), z >> 16,
11061135
z & 0xffff, FAS(z));
@@ -1138,9 +1167,14 @@ avr_flashaddr_t avr_run_one(avr_t * avr)
11381167
} break;
11391168
case 0x9006:
11401169
case 0x9007: { // ELPM -- Extended Load Program Memory -- 1001 000d dddd 01oo
1141-
if (!avr->rampz)
1170+
if (!avr->rampz) {
11421171
_avr_invalid_opcode(avr);
1143-
uint32_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8) | (avr->data[avr->rampz] << 16);
1172+
new_pc = avr->pc;
1173+
}
1174+
1175+
uint32_t z;
1176+
z = avr->data[R_ZL] | (avr->data[R_ZH] << 8) |
1177+
(avr->data[avr->rampz] << 16);
11441178
get_d5(opcode);
11451179
int op = opcode & 1;
11461180
STATE("elpm %s, (Z[%02x:%04x]%s)\t\t%s\n",
@@ -1442,6 +1476,8 @@ avr_flashaddr_t avr_run_one(avr_t * avr)
14421476
SREG();
14431477
} break;
14441478
default: _avr_invalid_opcode(avr);
1479+
new_pc = avr->pc;
1480+
break;
14451481
}
14461482
}
14471483
} break;
@@ -1462,7 +1498,7 @@ avr_flashaddr_t avr_run_one(avr_t * avr)
14621498
STATE("in %s, %s[%02x]\n", AVR_REGNAME(d), AVR_REGNAME(A), avr->data[A]);
14631499
_avr_set_r(avr, d, _avr_get_ram(avr, A));
14641500
} break;
1465-
default: _avr_invalid_opcode(avr);
1501+
default: _avr_invalid_opcode(avr); new_pc = avr->pc;
14661502
}
14671503
} break;
14681504

@@ -1551,11 +1587,11 @@ avr_flashaddr_t avr_run_one(avr_t * avr)
15511587
}
15521588
}
15531589
} break;
1554-
default: _avr_invalid_opcode(avr);
1590+
default: _avr_invalid_opcode(avr); new_pc = avr->pc;
15551591
}
15561592
} break;
15571593

1558-
default: _avr_invalid_opcode(avr);
1594+
default: _avr_invalid_opcode(avr); new_pc = avr->pc;
15591595

15601596
}
15611597
avr->cycle += cycle;

simavr/sim/sim_elf.c

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -336,27 +336,29 @@ avr_load_firmware(
336336
avr_vcd_add_signal(avr->vcd, irq, 8, firmware->trace[ti].name);
337337
} else if ( (firmware->trace[ti].kind == AVR_MMCU_TAG_VCD_SRAM_8) ||
338338
(firmware->trace[ti].kind == AVR_MMCU_TAG_VCD_SRAM_16) ) {
339-
if ((firmware->trace[ti].addr <= 31) || (firmware->trace[ti].addr > avr->ramend)) {
340-
AVR_LOG(avr, LOG_ERROR, "ELF: *** Invalid SRAM trace address (0x20 < 0x%04x < 0x%04x )\n", firmware->trace[ti].addr, avr->ramend);
341-
} else if (avr->sram_tracepoint_count >= ARRAY_SIZE(avr->sram_tracepoint)) {
342-
AVR_LOG(avr, LOG_ERROR, "ELF: *** Too many SRAM traces (limit = %d)\n", ARRAY_SIZE(avr->sram_tracepoint));
339+
avr_irq_t *irq;
340+
341+
irq = avr_get_memory_irq(
342+
avr, firmware->trace[ti].addr,
343+
firmware->trace[ti].kind == AVR_MMCU_TAG_VCD_SRAM_16);
344+
if (!irq) {
345+
AVR_LOG(avr, LOG_ERROR,
346+
"ELF: *** Invalid SRAM trace address "
347+
"(0x20 < 0x%04x < 0x%04x) or trace table overflow.\n",
348+
firmware->trace[ti].addr, avr->ramend);
343349
} else {
344-
char name[20];
345-
sprintf(name, "sram_tracepoint_%d", avr->sram_tracepoint_count);
346-
const char *names[1] = {name};
347-
avr_irq_t *irq = avr_alloc_irq(&avr->irq_pool, 0, 1, names);
348-
if (irq) {
349-
AVR_LOG(avr, LOG_OUTPUT, "ELF: SRAM tracepoint added at '0x%04x' (%s, %d bits)\n", firmware->trace[ti].addr, firmware->trace[ti].name, firmware->trace[ti].kind == AVR_MMCU_TAG_VCD_SRAM_8 ? 8 : 16);
350-
avr->sram_tracepoint[avr->sram_tracepoint_count].irq = irq;
351-
avr->sram_tracepoint[avr->sram_tracepoint_count].width = firmware->trace[ti].kind == AVR_MMCU_TAG_VCD_SRAM_8 ? 8 : 16;
352-
avr->sram_tracepoint[avr->sram_tracepoint_count].addr = firmware->trace[ti].addr;
353-
avr_vcd_add_signal(avr->vcd,
354-
irq,
355-
avr->sram_tracepoint[avr->sram_tracepoint_count].width,
356-
firmware->trace[ti].name[0] ? firmware->trace[ti].name : names[0]
357-
);
358-
avr->sram_tracepoint_count++;
359-
}
350+
AVR_LOG(avr, LOG_OUTPUT,
351+
"ELF: SRAM tracepoint added at '0x%04x' "
352+
"(%s, %d bits)\n",
353+
firmware->trace[ti].addr, firmware->trace[ti].name,
354+
firmware->trace[ti].kind == AVR_MMCU_TAG_VCD_SRAM_8 ?
355+
8 : 16);
356+
avr_vcd_add_signal(
357+
avr->vcd,
358+
irq,
359+
avr->sram_tracepoint[avr->sram_tracepoint_count].width,
360+
firmware->trace[ti].name[0] ?
361+
firmware->trace[ti].name : irq->name);
360362
}
361363
} else if (firmware->trace[ti].mask == 0xff ||
362364
firmware->trace[ti].mask == 0) {

tests/atmega328pb_jsys.c

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* atmega328pb_jsys.c: a small demonstaration of using an illegal
3+
* instruction code to raise an IRQ and invoke a service from the
4+
* surrounding application. In this case a simple sleep/wake.
5+
* The technique allows low-overhead simulation of busy-waiting.
6+
*
7+
* Derived from atmega48_enabled.c.
8+
*/
9+
10+
#include <avr/io.h>
11+
#include <avr/interrupt.h>
12+
#include <avr/sleep.h>
13+
14+
#include "avr_mcu_section.h"
15+
AVR_MCU(F_CPU, "atmega328pb");
16+
17+
uint8_t count, ext_count;
18+
19+
int main(void)
20+
{
21+
// Set up timer0 - do not start yet
22+
23+
TCCR0A |= (1 << WGM01); // Configure timer 0 for CTC mode
24+
OCR0A = 0xAA; // CTC compare value
25+
26+
TCCR0B |= (1 << CS00) | (1 << CS01); // Start timer: clk/64
27+
28+
sei(); // But none are enabled!
29+
do {
30+
while ((TIFR0 & (1 << OCF0A)) == 0) {
31+
++count;
32+
asm volatile ("\t.word 0xff\n\t" // JSYS requesting sleep.
33+
".byte 1, 0\n");
34+
}
35+
TIFR0 |= (1 << OCF0A); // Clear it
36+
++ext_count;
37+
} while (count < 20);
38+
cli();
39+
40+
/* Try an output JSYS. It will not normally be seen as tests.c suppresses
41+
* stdout, but may be enabled there.
42+
*
43+
* This might be extended to mimic printf(), but with no stdio in the AVR.
44+
* The format strings might be placed in the .mmcu section,
45+
* further reducing flash use.
46+
*
47+
* Gcc is extremely sensitive about guessing the size of the ASM output.
48+
*/
49+
50+
asm volatile ("\t.word 0xff\n\t" // JSYS requesting output.
51+
".byte 2, 'H'\n\t" // Stupid, but .asciz fails.
52+
".byte 'e', 'l'\n\t"
53+
".byte 'l', 'o'\n\t"
54+
".byte ' ', 'f'\n\t"
55+
".byte 'r', 'o'\n\t"
56+
".byte 'm', ' '\n\t"
57+
".byte 'A', 'V'\n\t"
58+
".byte 'R', '.'\n\t"
59+
".byte '\n', 0\n\t"
60+
".byte 0, 0\n");
61+
sleep_cpu();
62+
}

0 commit comments

Comments
 (0)