run_avr et al. New HEX format loader
authorMichel Pollet <buserror@gmail.com>
Wed, 23 Dec 2009 22:22:23 +0000 (22:22 +0000)
committerMichel Pollet <buserror@gmail.com>
Wed, 23 Dec 2009 22:22:23 +0000 (22:22 +0000)
simavr can now load .hex files directly, It is obviously a lot
more primitive than the ELF loader, but it works.
You have to specify the MCU and the AVR frequency on the command
line to run a .hex, otherwise simavr has no clue what it's suposed
to do.

Also reworked run_avr to get rid of getopt, moved the
read_hex_string function into the new sim_hex.[ch], and now
understand that the base addresd of code is not always zero.

This allows loading of a bootloader (tada!)

Signed-off-by: Michel Pollet <buserror@gmail.com>
simavr/sim/run_avr.c
simavr/sim/sim_gdb.c
simavr/sim/sim_hex.c [new file with mode: 0644]
simavr/sim/sim_hex.h [new file with mode: 0644]

index f39fb3b..aa202f3 100644 (file)
 
 #include <stdlib.h>
 #include <stdio.h>
-#include <getopt.h>
+#include <libgen.h>
 #include <string.h>
 #include "sim_avr.h"
 #include "sim_elf.h"
 #include "sim_core.h"
 #include "sim_gdb.h"
-#include "avr_uart.h"
+#include "sim_hex.h"
 
-void hdump(const char *w, uint8_t *b, size_t l)
-{
-       uint32_t i;
-       if (l < 16) {
-               printf("%s: ",w);
-               for (i = 0; i < l; i++) printf("%02x",b[i]);
-       } else {
-               printf("%s:\n",w);
-               for (i = 0; i < l; i++) {
-                       if (!(i & 0x1f)) printf("    ");
-                       printf("%02x",b[i]);
-                       if ((i & 0x1f) == 0x1f) {
-                               printf(" ");
-                               printf("\n");
-                       }
-               }
-       }
-       printf("\n");
-}
+extern avr_kind_t * avr_kind[];
 
-
-void display_usage()
+void display_usage(char * app)
 {
-       printf("usage: simavr [-t] [-g] [-m <device>] [-f <frequency>] firmware\n");
+       printf("usage: %s [-t] [-g] [-m <device>] [-f <frequency>] firmware\n", app);
        printf("       -t: run full scale decoder trace\n");
        printf("       -g: listen for gdb connection on port 1234\n");
+       printf("   Supported AVR cores:\n");
+       for (int i = 0; avr_kind[i]; i++) {
+               printf("       ");
+               for (int ti = 0; ti < 4 && avr_kind[i]->names[ti]; ti++)
+                       printf("%s ", avr_kind[i]->names[ti]);
+               printf("\n");
+       }
        exit(1);
 }
 
 int main(int argc, char *argv[])
 {
-       elf_firmware_t f;
+       elf_firmware_t f = {0};
        long f_cpu = 0;
        int trace = 0;
        int gdb = 0;
        char name[16] = "";
-       int option_count;
-       int option_index = 0;
-
-       struct option long_options[] = {
-               {"help", no_argument, 0, 'h'},
-               {"mcu", required_argument, 0, 'm'},
-               {"freq", required_argument, 0, 'f'},
-               {"trace", no_argument, 0, 't'},
-               {"gdb", no_argument, 0, 'g'},
-               {0, 0, 0, 0}
-       };
 
        if (argc == 1)
-               display_usage();
-
-       while ((option_count = getopt_long(argc, argv, "tghm:f:", long_options, &option_index)) != -1) {
-               switch (option_count) {
-                       case 'h':
-                               display_usage();
-                               break;
-                       case 'm':
-                               strcpy(name, optarg);
+               display_usage(basename(argv[0]));
+
+       for (int pi = 1; pi < argc; pi++) {
+               if (!strcmp(argv[pi], "-h") || !strcmp(argv[pi], "-help")) {
+                               display_usage(basename(argv[0]));
+               } else if (!strcmp(argv[pi], "-m") || !strcmp(argv[pi], "-mcu")) {
+                       if (pi < argc-1)
+                               strcpy(name, argv[++pi]);
+                       else
+                               display_usage(basename(argv[0]));
+               } else if (!strcmp(argv[pi], "-f") || !strcmp(argv[pi], "-freq")) {
+                       if (pi < argc-1)
+                               f_cpu = atoi(argv[++pi]);
+                       else
+                               display_usage(basename(argv[0]));
                                break;
-                       case 'f':
-                               f_cpu = atoi(optarg);
-                               break;
-                       case 't':
+               } else if (!strcmp(argv[pi], "-t") || !strcmp(argv[pi], "-trace")) {
                                trace++;
-                               break;
-                       case 'g':
-                               gdb++;
-                               break;
+               } else if (!strcmp(argv[pi], "-g") || !strcmp(argv[pi], "-gdb")) {
+                       gdb++;
                }
        }
 
-       elf_read_firmware(argv[argc-1], &f);
+       char * filename = argv[argc-1];
+       char * suffix = strrchr(filename, '.');
+       if (suffix && !strcasecmp(suffix, ".hex")) {
+               if (!name[0] || !f_cpu) {
+                       fprintf(stderr, "%s: -mcu and -freq are mandatory to load .hex files\n", argv[0]);
+                       exit(1);
+               }
+               f.flash = read_ihex_file(filename, &f.flashsize, &f.flashbase);
+       } else {
+               elf_read_firmware(filename, &f);
+       }
 
        if (strlen(name))
                strcpy(f.mmcu, name);
@@ -116,17 +104,12 @@ int main(int argc, char *argv[])
        }
        avr_init(avr);
        avr_load_firmware(avr, &f);
+       if (f.flashbase) {
+               printf("Attempted to load a booloader at %04x\n", f.flashbase);
+               avr->pc = f.flashbase;
+       }
        avr->trace = trace;
 
-       // try to enable "local echo" on the first uart, for testing purposes
-       {
-               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:%s activating uart local echo IRQ src %p dst %p\n", __FILE__, __FUNCTION__, src, dst);
-                       avr_connect_irq(src, dst);
-               }
-       }
        // even if not setup at startup, activate gdb if crashing
        avr->gdb_port = 1234;
        if (gdb) {
index 43225c8..a90bb69 100644 (file)
@@ -32,6 +32,7 @@
 #include <poll.h>
 #include <pthread.h>
 #include "sim_avr.h"
+#include "sim_hex.h"
 #include "avr_eeprom.h"
 
 #define DBG(w)
@@ -49,33 +50,6 @@ typedef struct avr_gdb_t {
        } watch[32];
 } avr_gdb_t;
 
-    // decode line text hex to binary
-int read_hex_string(const char * src, uint8_t * buffer, int maxlen)
-{
-    uint8_t * dst = buffer;
-    int ls = 0;
-    uint8_t b = 0;
-    while (*src && maxlen--) {
-        char c = *src++;
-        switch (c) {
-            case 'a' ... 'f':   b = (b << 4) | (c - 'a' + 0xa); break;
-            case 'A' ... 'F':   b = (b << 4) | (c - 'A' + 0xa); break;
-            case '0' ... '9':   b = (b << 4) | (c - '0'); break;
-            default:
-                if (c > ' ') {
-                    fprintf(stderr, "%s: huh '%c' (%s)\n", __FUNCTION__, c, src);
-                    return -1;
-                }
-                continue;
-        }
-        if (ls & 1) {
-            *dst++ = b; b = 0;
-        }
-        ls++;
-    }
-
-    return dst - buffer;
-}
 
 static void gdb_send_reply(avr_gdb_t * g, char * cmd)
 {
diff --git a/simavr/sim/sim_hex.c b/simavr/sim/sim_hex.c
new file mode 100644 (file)
index 0000000..6280320
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+       sim_hex.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 "sim_hex.h"
+
+// friendly hex dump
+void hdump(const char *w, uint8_t *b, size_t l)
+{
+       uint32_t i;
+       if (l < 16) {
+               printf("%s: ",w);
+               for (i = 0; i < l; i++) printf("%02x",b[i]);
+       } else {
+               printf("%s:\n",w);
+               for (i = 0; i < l; i++) {
+                       if (!(i & 0x1f)) printf("    ");
+                       printf("%02x",b[i]);
+                       if ((i & 0x1f) == 0x1f) {
+                               printf(" ");
+                               printf("\n");
+                       }
+               }
+       }
+       printf("\n");
+}
+
+    // decode line text hex to binary
+int read_hex_string(const char * src, uint8_t * buffer, int maxlen)
+{
+    uint8_t * dst = buffer;
+    int ls = 0;
+    uint8_t b = 0;
+    while (*src && maxlen--) {
+        char c = *src++;
+        switch (c) {
+            case 'a' ... 'f':   b = (b << 4) | (c - 'a' + 0xa); break;
+            case 'A' ... 'F':   b = (b << 4) | (c - 'A' + 0xa); break;
+            case '0' ... '9':   b = (b << 4) | (c - '0'); break;
+            default:
+                if (c > ' ') {
+                    fprintf(stderr, "%s: huh '%c' (%s)\n", __FUNCTION__, c, src);
+                    return -1;
+                }
+                continue;
+        }
+        if (ls & 1) {
+            *dst++ = b; b = 0;
+        }
+        ls++;
+    }
+
+    return dst - buffer;
+}
+
+uint8_t * read_ihex_file(const char * fname, uint32_t * dsize, uint32_t * start)
+{
+       if (!fname || !dsize)
+               return NULL;
+       FILE * f = fopen(fname, "r");
+       if (!f) {
+               perror(fname);
+               return NULL;
+       }
+       uint8_t * res = NULL;
+       uint32_t size = 0;
+       uint32_t base = ~0;
+
+       while (!feof(f)) {
+               char line[128];
+               if (!fgets(line, sizeof(line)-1, f))
+                       continue;
+               if (line[0] != ':') {
+                       fprintf(stderr, "AVR: '%s' invalid ihex format (%.4s)\n", fname, line);
+                       break;
+               }
+               uint8_t bline[64];
+
+               int len = read_hex_string(line + 1, bline, sizeof(bline));
+               if (len <= 0)
+                       continue;
+
+               uint8_t chk = 0;
+               {       // calculate checksum
+                       uint8_t * src = bline;
+                       int tlen = len-1;
+                       while (tlen--)
+                               chk += *src++;
+                       chk = 0x100 - chk;
+               }
+               if (chk != bline[len-1]) {
+                       fprintf(stderr, "%s: %s, invalid checksum %02x/%02x\n", __FUNCTION__, fname, chk, bline[len-1]);
+                       break;
+               }
+               if (bline[3] != 0) {
+                       if (bline[3] != 1) {
+                               fprintf(stderr, "%s: %s, unsupported check type %02x\n", __FUNCTION__, fname, bline[3]);
+                               break;
+                       }
+                       continue;
+               }
+               uint16_t addr = (bline[1] << 8) | bline[2];
+               if (base == ~0) {
+                       base = addr;    // stadt address
+               }
+               if (addr != base + size) {
+                       fprintf(stderr, "%s: %s, offset out of bounds %04x expected %04x\n", __FUNCTION__, fname, addr, base+size);
+                       break;
+               }
+               res = realloc(res, size + bline[0]);
+               memcpy(res + size, bline + 4, bline[0]);
+               size += bline[0];
+       }
+       *dsize = size;
+       if (start)
+               *start = base;
+       fclose(f);
+       return res;
+}
diff --git a/simavr/sim/sim_hex.h b/simavr/sim/sim_hex.h
new file mode 100644 (file)
index 0000000..caf0afc
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+       sim_hex.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 __SIM_HEX_H___
+#define __SIM_HEX_H___
+
+#include <stdint.h>
+
+// parses a hex text string 'src' of at max 'maxlen' characters, decodes it into 'buffer'
+int read_hex_string(const char * src, uint8_t * buffer, int maxlen);
+
+// reads IHEX file 'fname', puts it's decoded size in *'dsize' and returns
+// a newly allocated buffer with the binary data (or NULL, if error)
+uint8_t * read_ihex_file(const char * fname, uint32_t * dsize, uint32_t * start);
+
+// hex dump from pointer 'b' for 'l' bytes with string prefix 'w'
+void hdump(const char *w, uint8_t *b, size_t l);
+
+#endif /* __SIM_HEX_H___ */