From a69c6da93a2eb6f4c182789b64ee73f50b2577a0 Mon Sep 17 00:00:00 2001 From: Michel Pollet Date: Thu, 24 Dec 2009 16:56:27 +0000 Subject: [PATCH] SPM: Added Self Programming Instruction & Support Added the SPM support to the core, and to the x8 devices. Tested with Arduino's bootloader. Signed-off-by: Michel Pollet --- simavr/cores/sim_megax8.c | 1 + simavr/cores/sim_megax8.h | 3 ++ simavr/sim/avr_flash.c | 103 ++++++++++++++++++++++++++++++++++++++ simavr/sim/avr_flash.h | 64 +++++++++++++++++++++++ simavr/sim/sim_core.c | 12 +++-- 5 files changed, 179 insertions(+), 4 deletions(-) create mode 100644 simavr/sim/avr_flash.c create mode 100644 simavr/sim/avr_flash.h diff --git a/simavr/cores/sim_megax8.c b/simavr/cores/sim_megax8.c index 0a9f782..970aa50 100644 --- a/simavr/cores/sim_megax8.c +++ b/simavr/cores/sim_megax8.c @@ -30,6 +30,7 @@ void mx8_init(struct avr_t * avr) printf("%s init\n", avr->mmcu); avr_eeprom_init(avr, &mcu->eeprom); + avr_flash_init(avr, &mcu->selfprog); avr_extint_init(avr, &mcu->extint); avr_ioport_init(avr, &mcu->portb); avr_ioport_init(avr, &mcu->portc); diff --git a/simavr/cores/sim_megax8.h b/simavr/cores/sim_megax8.h index ed2ac02..b0036fc 100644 --- a/simavr/cores/sim_megax8.h +++ b/simavr/cores/sim_megax8.h @@ -25,6 +25,7 @@ #include "sim_core_declare.h" #include "avr_eeprom.h" +#include "avr_flash.h" #include "avr_extint.h" #include "avr_ioport.h" #include "avr_uart.h" @@ -42,6 +43,7 @@ void mx8_reset(struct avr_t * avr); struct mcu_t { avr_t core; avr_eeprom_t eeprom; + avr_flash_t selfprog; avr_extint_t extint; avr_ioport_t portb,portc,portd; avr_uart_t uart; @@ -69,6 +71,7 @@ struct mcu_t SIM_CORENAME = { .reset = mx8_reset, }, AVR_EEPROM_DECLARE(EE_READY_vect), + AVR_SELFPROG_DECLARE(SPMCSR, SPM_READY_vect), .extint = { AVR_EXTINT_DECLARE(0, 'D', 2), AVR_EXTINT_DECLARE(1, 'D', 3), diff --git a/simavr/sim/avr_flash.c b/simavr/sim/avr_flash.c new file mode 100644 index 0000000..05f3e87 --- /dev/null +++ b/simavr/sim/avr_flash.c @@ -0,0 +1,103 @@ +/* + avr_flash.c + + Copyright 2008, 2009 Michel Pollet + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + +#include +#include +#include +#include "avr_flash.h" + +static avr_cycle_count_t avr_progen_clear(struct avr_t * avr, avr_cycle_count_t when, void * param) +{ + avr_flash_t * p = (avr_flash_t *)param; + avr_regbit_clear(p->io.avr, p->selfprgen); + printf("avr_progen_clear - SPM not received, clearing PRGEN bit\n"); + return 0; +} + +static uint8_t avr_flash_read(struct avr_t * avr, avr_io_addr_t addr, void * param) +{ + avr_flash_t * p = (avr_flash_t *)param; + uint8_t v = avr_core_watch_read(avr, addr); + printf("avr_flash_read %02x\n", v); + return v; +} + +static void avr_flash_write(avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param) +{ + avr_flash_t * p = (avr_flash_t *)param; + + avr_core_watch_write(avr, addr, v); + +// printf("** avr_flash_write %02x\n", v); + + if (avr_regbit_get(avr, p->selfprgen)) + avr_cycle_timer_register(avr, 4, avr_progen_clear, p); // 4 cycles is very little! +} + +static int avr_flash_ioctl(struct avr_io_t * port, uint32_t ctl, void * io_param) +{ + if (ctl != AVR_IOCTL_FLASH_SPM) + return -1; + + avr_flash_t * p = (avr_flash_t *)port; + avr_t * avr = p->io.avr; + + uint16_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8); + uint16_t r01 = avr->data[0] | (avr->data[1] << 8); + +// printf("AVR_IOCTL_FLASH_SPM %02x Z:%04x R01:%04x\n", avr->data[p->r_spm], z,r01); + avr_cycle_timer_cancel(avr, avr_progen_clear, p); + avr_regbit_clear(avr, p->selfprgen); + if (avr_regbit_get(avr, p->pgers)) { + z &= ~1; + printf("Erasing page %04x (%d)\n", (z / p->spm_pagesize), p->spm_pagesize); + for (int i = 0; i < p->spm_pagesize; i++) + avr->flash[z++] = 0xff; + } else if (avr_regbit_get(avr, p->pgwrt)) { + z &= ~1; + printf("Writing page %04x (%d)\n", (z / p->spm_pagesize), p->spm_pagesize); + } else if (avr_regbit_get(avr, p->blbset)) { + printf("Settting lock bits (ignored)\n"); + } else { + z &= ~1; + avr->flash[z++] = r01; + avr->flash[z++] = r01 >> 8; + } + return 0; +} + +static avr_io_t _io = { + .kind = "flash", + .ioctl = avr_flash_ioctl, +}; + +void avr_flash_init(avr_t * avr, avr_flash_t * p) +{ + p->io = _io; + printf("%s init SPM %04x\n", __FUNCTION__, p->r_spm); + + avr_register_io(avr, &p->io); + avr_register_vector(avr, &p->flash); + + avr_register_io_write(avr, p->r_spm, avr_flash_write, p); +// avr_register_io_read(avr, p->r_spm, avr_flash_read, p); +} + diff --git a/simavr/sim/avr_flash.h b/simavr/sim/avr_flash.h new file mode 100644 index 0000000..30201dc --- /dev/null +++ b/simavr/sim/avr_flash.h @@ -0,0 +1,64 @@ +/* + avr_flash.h + + Copyright 2008, 2009 Michel Pollet + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + + +#ifndef __AVR_FLASH_H___ +#define __AVR_FLASH_H___ + +#include "sim_avr.h" + +/* + * Handles self-programming subsystem if the core + * supports it. + */ +typedef struct avr_flash_t { + avr_io_t io; + + uint16_t spm_pagesize; + uint8_t r_spm; + avr_regbit_t selfprgen; + avr_regbit_t pgers; // page erase + avr_regbit_t pgwrt; // page write + avr_regbit_t blbset; // lock bit set + + avr_int_vector_t flash; // Interrupt vector +} avr_flash_t; + +void avr_flash_init(avr_t * avr, avr_flash_t * p); + + +#define AVR_IOCTL_FLASH_SPM AVR_IOCTL_DEF('f','s','p','m') + +#define AVR_SELFPROG_DECLARE(_spmr, _vector) \ + .selfprog = {\ + .r_spm = _spmr,\ + .spm_pagesize = SPM_PAGESIZE,\ + .selfprgen = AVR_IO_REGBIT(_spmr, SELFPRGEN),\ + .pgers = AVR_IO_REGBIT(_spmr, PGERS),\ + .pgwrt = AVR_IO_REGBIT(_spmr, PGWRT),\ + .blbset = AVR_IO_REGBIT(_spmr, BLBSET),\ + .flash = {\ + .enable = AVR_IO_REGBIT(_spmr, SPMIE),\ + .vector = _vector,\ + },\ + } + +#endif /* __AVR_FLASH_H___ */ diff --git a/simavr/sim/sim_core.c b/simavr/sim/sim_core.c index 51d8c33..e7e1f6f 100644 --- a/simavr/sim/sim_core.c +++ b/simavr/sim/sim_core.c @@ -25,6 +25,7 @@ #include #include "sim_avr.h" #include "sim_core.h" +#include "avr_flash.h" // SREG bit names const char * _sreg_bit_name = "cznvshti"; @@ -388,6 +389,7 @@ static inline int _avr_is_instruction_32_bits(avr_t * avr, uint32_t pc) */ uint16_t avr_run_one(avr_t * avr) { +#if CONFIG_SIMAVR_TRACE /* * this traces spurious reset or bad jumps */ @@ -396,15 +398,13 @@ uint16_t avr_run_one(avr_t * avr) STATE("RESET\n"); CRASH(); } + avr->touched[0] = avr->touched[1] = avr->touched[2] = 0; +#endif uint32_t opcode = (avr->flash[avr->pc + 1] << 8) | avr->flash[avr->pc]; uint32_t new_pc = avr->pc + 2; // future "default" pc int cycle = 1; -#if CONFIG_SIMAVR_TRACE - avr->touched[0] = avr->touched[1] = avr->touched[2] = 0; -#endif - switch (opcode & 0xf000) { case 0x0000: { switch (opcode) { @@ -764,6 +764,10 @@ uint16_t avr_run_one(avr_t * avr) case 0x95a8: { // WDR STATE("wdr\n"); } break; + case 0x95e8: { // SPM + STATE("spm\n"); + avr_ioctl(avr, AVR_IOCTL_FLASH_SPM, 0); + } break; case 0x9409: { // IJMP Indirect jump uint16_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8); STATE("ijmp Z[%04x]\n", z << 1); -- 2.39.5