SPM: Added Self Programming Instruction & Support
authorMichel Pollet <buserror@gmail.com>
Thu, 24 Dec 2009 16:56:27 +0000 (16:56 +0000)
committerMichel Pollet <buserror@gmail.com>
Thu, 24 Dec 2009 16:56:27 +0000 (16:56 +0000)
Added the SPM support to the core, and to the x8 devices.
Tested with Arduino's bootloader.

Signed-off-by: Michel Pollet <buserror@gmail.com>
simavr/cores/sim_megax8.c
simavr/cores/sim_megax8.h
simavr/sim/avr_flash.c [new file with mode: 0644]
simavr/sim/avr_flash.h [new file with mode: 0644]
simavr/sim/sim_core.c

index 0a9f782..970aa50 100644 (file)
@@ -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);
index ed2ac02..b0036fc 100644 (file)
@@ -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 (file)
index 0000000..05f3e87
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+       avr_flash.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#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 (file)
index 0000000..30201dc
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+       avr_flash.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+
+       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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#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___ */
index 51d8c33..e7e1f6f 100644 (file)
@@ -25,6 +25,7 @@
 #include <ctype.h>
 #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);