main.c

00001 /*
00002  * $Id: main.c,v 1.40 2004/04/17 00:03:51 troth Exp $
00003  *
00004  ****************************************************************************
00005  *
00006  * simulavr - A simulator for the Atmel AVR family of microcontrollers.
00007  * Copyright (C) 2001, 2002, 2003, 2004  Theodore A. Roth
00008  *
00009  * This program is free software; you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation; either version 2 of the License, or
00012  * (at your option) any later version.
00013  *
00014  * This program is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  * GNU General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022  *
00023  ****************************************************************************
00024  */
00025 
00026 #include <config.h>
00027 
00028 #include <stdio.h>
00029 #include <stdlib.h>
00030 #include <string.h>
00031 #include <errno.h>
00032 
00033 #include "avrerror.h"
00034 #include "avrmalloc.h"
00035 #include "avrclass.h"
00036 #include "utils.h"
00037 #include "callback.h"
00038 #include "op_names.h"
00039 
00040 #include "storage.h"
00041 #include "flash.h"
00042 
00043 #include "vdevs.h"
00044 #include "memory.h"
00045 #include "stack.h"
00046 #include "register.h"
00047 #include "sram.h"
00048 #include "eeprom.h"
00049 #include "timers.h"
00050 #include "ports.h"
00051 
00052 #include "avrcore.h"
00053 
00054 #include "devsupp.h"
00055 #include "display.h"
00056 
00057 #include "gdb.h"
00058 #include "gnu_getopt.h"
00059 
00060 /****************************************************************************\
00061  *
00062  * global variables (keep them to a minimum)
00063  *
00064 \****************************************************************************/
00065 
00066 static char *global_device_type = NULL;
00067 
00068 static int global_eeprom_image_type = FFMT_BIN;
00069 static char *global_eeprom_image_file = NULL;
00070 
00071 static int global_flash_image_type = FFMT_BIN;
00072 static char *global_flash_image_file = NULL;
00073 
00074 static int global_gdbserver_mode = 0;
00075 static int global_gdbserver_port = 1212; /* default port number */
00076 static int global_gdb_debug = 0;
00077 
00078 static char *global_disp_prog = NULL;
00079 static int global_disp_without_xterm = 0;
00080 
00081 static int global_dump_core = 0;
00082 
00083 static int global_clock_freq = 8000000; /* Default is 8 MHz. */
00084 
00085 /* If the user needs more than LEN_BREAK_LIST on the command line, they've got
00086    bigger problems. */
00087 
00088 #define LEN_BREAK_LIST  50
00089 static int global_break_count = 0;
00090 static int global_break_list[LEN_BREAK_LIST];
00091 
00092 static AvrCore *global_core = NULL;
00093 
00094 /* *INDENT-OFF* */
00095 static GdbComm_T global_gdb_comm[1] = {{
00096     .user_data = NULL,          /* user_data: will be global_core later */
00097     
00098     .read_reg = (CommFuncReadReg) avr_core_gpwr_get,
00099     .write_reg = (CommFuncWriteReg) avr_core_gpwr_set,
00100     
00101     .read_sreg = (CommFuncReadSREG) avr_core_sreg_get,
00102     .write_sreg = (CommFuncWriteSREG) avr_core_sreg_set,
00103     
00104     .read_pc = (CommFuncReadPC) avr_core_PC_get,
00105     .write_pc = (CommFuncWritePC) avr_core_PC_set,
00106     .max_pc = (CommFuncMaxPC) avr_core_PC_max,
00107     
00108     .read_sram = (CommFuncReadSRAM) avr_core_mem_read,
00109     .write_sram = (CommFuncWriteSRAM) avr_core_mem_write,
00110     
00111     .read_flash = (CommFuncReadFlash) avr_core_flash_read,
00112     .write_flash = (CommFuncWriteFlash) avr_core_flash_write,
00113     .write_flash_lo8 = (CommFuncWriteFlashLo8) avr_core_flash_write_lo8,
00114     .write_flash_hi8 = (CommFuncWriteFlashHi8) avr_core_flash_write_hi8,
00115     
00116     .insert_break = (CommFuncInsertBreak) avr_core_insert_breakpoint,
00117     .remove_break = (CommFuncRemoveBreak) avr_core_remove_breakpoint,
00118     .enable_breakpts = (CommFuncEnableBrkpts) avr_core_enable_breakpoints,
00119     .disable_breakpts = (CommFuncDisableBrkpts) avr_core_disable_breakpoints,
00120     
00121     .step = (CommFuncStep) avr_core_step,
00122     .reset = (CommFuncReset) avr_core_reset,
00123     
00124     .io_fetch = (CommFuncIORegFetch) avr_core_io_fetch,
00125     
00126     .irq_raise = (CommFuncIrqRaise) avr_core_irq_raise,
00127 }};
00128 
00129 static char *usage_fmt_str =
00130 "\nUsage: %s [OPTIONS]... [flash_image]\n" "\n"
00131 "Simulate an avr device. The optional flash_image file is loaded\n"
00132 "into the flash program memory space of the device.\n" "\n" "Options:\n"
00133 "  -h, --help                : Show this message\n"
00134 "  -D, --debug               : Debug instruction output\n"
00135 "  -v, --version             : Print out the version number and exit\n"
00136 "  -g, --gdbserver           : Run as a gdbserver process\n"
00137 "  -G, --gdb-debug           : Print out debug messages for gdbserver\n"
00138 "  -p, --port <port>         : Listen for gdb connection on TCP port\n"
00139 "  -d, --device <dev>        : Specify device type\n"
00140 "  -e, --eeprom-image <img>  : Specify an eeprom image file\n"
00141 "  -E, --eeprom-type <type>  : Specify the type of the eeprom image file\n"
00142 "  -F, --flash-type <type>   : Specify the type of the flash image file\n"
00143 "  -L, --list-devices        : Print supported devices to stdout and exit\n"
00144 "  -P, --disp-prog <prog>    : Display register and memory info with prog\n"
00145 "  -X, --without-xterm       : Don't start disp prog in an xterm\n"
00146 "  -C, --core-dump           : Dump a core memory image to file on exit\n"
00147 "  -c, --clock-freq <freq>   : Set the simulated mcu clock freqency (in Hz)\n"
00148 "  -B, --breakpoint <addr>   : Set a breakpoint (address is a byte address)\n"
00149 "\n" "If the image file types for eeprom or flash images are not given,\n"
00150 "the default file type is binary.\n" "\n"
00151 "If you wish to run the simulator in gdbserver mode, you do not\n"
00152 "have to specify a flash-image file since the program can be loaded\n"
00153 "from gdb via the `load` command.\n" "\n"
00154 "If '--port' option is given, and '--gdbserver' is not, port is ignored\n"
00155 "\n" "If running in gdbserver mode and port is not specified, a default\n"
00156 "port of 1212 is used.\n" "\n"
00157 "If using the '--breakpoint' option, note the simulator will terminate when\n"
00158 "the address is hit if you are not running in gdbserver mode. This feature\n"
00159 "not intended for use in gdbserver mode. It is really intended for testing\n"
00160 "the simulator itself, but may be useful for testing avr programs too.\n"
00161 "\n" "Currently available device types:\n";
00162 
00163 /* *INDENT-ON* */
00164 
00165 /*
00166  * Print usage message.
00167  */
00168 static void
00169 usage (char *prog)
00170 {
00171     fprintf (stdout, usage_fmt_str, prog);
00172     dev_supp_list_devices (stdout);
00173     fprintf (stdout, "\n");
00174 
00175     exit (1);
00176 }
00177 
00178 /* *INDENT-OFF* */
00179 static struct option long_opts[] = {
00180     /* name,             has_arg, flag,   val */
00181     { "help",            0,       0,     'h' },
00182     { "debug",           0,       0,     'D' },
00183     { "version",         0,       0,     'v' },
00184     { "gdbserver",       0,       0,     'g' },
00185     { "gdb-debug",       0,       0,     'G' },
00186     { "port",            1,       0,     'p' },
00187     { "device",          1,       0,     'd' },
00188     { "eeprom-type",     1,       0,     'E' },
00189     { "eeprom-image",    1,       0,     'e' },
00190     { "flash-type",      1,       0,     'F' },
00191     { "list-devices",    0,       0,     'L' },
00192     { "disp-prog",       1,       0,     'P' },
00193     { "without-xterm",   1,       0,     'X' },
00194     { "core-dump",       0,       0,     'C' },
00195     { "clock-freq",      1,       0,     'c' },
00196     { "breakpoint",      1,       0,     'B' },
00197     { NULL,              0,       0,      0  }
00198 };
00199 /* *INDENT-ON* */
00200 
00201 /*
00202  * Parse the command line arguments.
00203  */
00204 static void
00205 parse_cmd_line (int argc, char **argv)
00206 {
00207     int c;
00208     char *prog = argv[0];
00209     char *basename;
00210     int option_index;
00211     char dummy_char;
00212     int break_addr;
00213 
00214     opterr = 0;                 /* disable default error message */
00215 
00216     while (1)
00217     {
00218         c = getopt_long (argc, argv, "hgGvDLd:e:E:F:p:P:XCc:B:", long_opts,
00219                          &option_index);
00220         if (c == -1)
00221             break;              /* no more options */
00222 
00223         switch (c)
00224         {
00225             case 'h':
00226             case '?':
00227                 usage (prog);
00228             case 'g':
00229                 global_gdbserver_mode = 1;
00230                 break;
00231             case 'G':
00232                 global_gdb_debug = 1;
00233                 break;
00234             case 'p':
00235                 global_gdbserver_port = atoi (optarg);
00236                 break;
00237             case 'v':
00238                 printf ("\n%s version %s\n", PACKAGE, VERSION);
00239                 printf ("Copyright 2001, 2002, 2003, 2004"
00240                         "  Theodore A. Roth.\n");
00241                 printf ("\n%s is free software, covered by the GNU General "
00242                         "Public License,\n", PACKAGE);
00243                 printf ("and you are welcome to change it and/or distribute "
00244                         "copies of it under\n");
00245                 printf ("the conditions of the GNU General Public License."
00246                         "\n\n");
00247                 exit (0);
00248             case 'D':
00249                 global_debug_inst_output = 1;
00250                 break;
00251             case 'd':
00252                 global_device_type = optarg;
00253                 break;
00254             case 'e':
00255                 global_eeprom_image_file = avr_strdup (optarg);
00256                 break;
00257             case 'E':
00258                 break;
00259                 global_eeprom_image_type = str2ffmt (optarg);
00260             case 'F':
00261                 global_flash_image_type = str2ffmt (optarg);
00262                 break;
00263             case 'L':
00264                 dev_supp_list_devices (stdout);
00265                 exit (0);
00266             case 'P':
00267                 global_disp_prog = avr_strdup (optarg);
00268                 break;
00269             case 'X':
00270                 global_disp_without_xterm = 1;
00271                 break;
00272             case 'C':
00273                 global_dump_core = 1;
00274                 break;
00275             case 'c':
00276                 if (sscanf (optarg, "%d%c", &global_clock_freq, &dummy_char)
00277                     != 1)
00278                 {
00279                     avr_error ("Invalid clock value: %s", optarg);
00280                 }
00281                 avr_warning ("Clock frequency option is not yet "
00282                              "implemented.\n");
00283                 break;
00284             case 'B':
00285                 if (sscanf (optarg, "%i%c", &break_addr, &dummy_char) != 1)
00286                 {
00287                     avr_error ("Ignoring invalid break addres: %s", optarg);
00288                 }
00289 
00290                 if (global_break_count < LEN_BREAK_LIST)
00291                 {
00292                     global_break_list[global_break_count] = break_addr;
00293                     global_break_count++;
00294                 }
00295                 else
00296                 {
00297                     avr_warning ("Too many break points: igoring %s\n",
00298                                  optarg);
00299                 }
00300 
00301                 break;
00302             default:
00303                 avr_error ("getop() did something screwey");
00304         }
00305     }
00306 
00307     if ((optind + 1) == argc)
00308         global_flash_image_file = argv[optind];
00309     else if (optind != argc)
00310         usage (prog);
00311 
00312     /* FIXME: Issue a warning and bail out if user selects a file format type
00313        we haven't implemented yet. */
00314 
00315     if ((global_eeprom_image_type != FFMT_BIN)
00316         || (global_flash_image_type != FFMT_BIN))
00317     {
00318         fprintf (stderr,
00319                  "Only the bin file format is currently "
00320                  "implemented. Sorry.\n");
00321         exit (1);
00322     }
00323 
00324     /* If user didn't specify a device type, see if it can be gleaned from the
00325        name of the program. */
00326 
00327     if (global_device_type == NULL)
00328     {
00329         /* find the last '/' in dev_name */
00330         basename = strrchr (prog, '/');
00331         if (basename == NULL)
00332             /* no slash in dev_name */
00333             global_device_type = prog;
00334         else
00335             global_device_type = ++basename;
00336     }
00337 }
00338 
00339 uint8_t
00340 ext_port_rd (int addr)
00341 {
00342     int data;
00343     char line[80];
00344 
00345     while (1)
00346     {
00347         fprintf (stderr, "\nEnter a byte of data to read into 0x%04x: ",
00348                  addr);
00349 
00350         /* try to read in a line of input */
00351         if (fgets (line, sizeof (line), stdin) == NULL)
00352             continue;
00353 
00354         /* try to parse the line for a byte of data */
00355         if (sscanf (line, "%i\n", &data) != 1)
00356             continue;
00357 
00358         break;
00359     }
00360     return (uint8_t) (data & 0xff);
00361 }
00362 
00363 void
00364 ext_port_wr (int addr, uint8_t val)
00365 {
00366     fprintf (stderr, "writing 0x%02x to 0x%04x\n", val, addr);
00367     fflush (stderr);
00368 }
00369 
00370 /* This is called whenever the program terminates via a call to exit(). */
00371 
00372 void
00373 atexit_cleanup (void)
00374 {
00375     FILE *dump;
00376 
00377     if (global_dump_core)
00378     {
00379         if ((dump = fopen ("core_avr_dump.core", "w")) == NULL)
00380         {
00381             /* can't call avr_error here since it could have called us */
00382             fprintf (stderr, "fopen failed: core_avr_dump.core: %s\n",
00383                      strerror (errno));
00384         }
00385         else
00386         {
00387             avr_core_dump_core (global_core, dump);
00388             fclose (dump);
00389         }
00390     }
00391 
00392     class_unref ((AvrClass *)global_core);
00393 }
00394 
00395 /*
00396  * Symlinks should be created for each supported device to the
00397  * simulavr program.
00398  */
00399 int
00400 main (int argc, char **argv)
00401 {
00402     int i;
00403     int flash_sz = 0, sram_sz = 0, eeprom_sz = 0;
00404     int sram_start = 0;
00405 
00406     parse_cmd_line (argc, argv);
00407 
00408     global_core = avr_core_new (global_device_type);
00409     if (global_core == NULL)
00410     {
00411         avr_warning ("Device not supported: %s\n", global_device_type);
00412         exit (1);
00413     }
00414 
00415     avr_message ("Simulating clock frequency of %d Hz\n", global_clock_freq);
00416 
00417     avr_core_get_sizes (global_core, &flash_sz, &sram_sz, &sram_start,
00418                         &eeprom_sz);
00419     display_open (global_disp_prog, global_disp_without_xterm, flash_sz,
00420                   sram_sz, sram_start, eeprom_sz);
00421     avr_core_io_display_names (global_core);
00422 
00423     /* Send initial clock cycles to display */
00424     display_clock (0);
00425 
00426     /* install my_atexit to be called when exit() is called */
00427     if (atexit (atexit_cleanup))
00428         avr_error ("Failed to install exit handler");
00429 
00430 #if 0
00431     /* Add external device hooks to ports */
00432     avr_core_add_ext_rd_wr (global_core, PORT_B_BASE, ext_port_rd,
00433                             ext_port_wr);
00434     avr_core_add_ext_rd_wr (global_core, PORT_C_BASE, ext_port_rd,
00435                             ext_port_wr);
00436     avr_core_add_ext_rd_wr (global_core, PORT_D_BASE, ext_port_rd,
00437                             ext_port_wr);
00438 #endif
00439 
00440     /* Load program into flash */
00441     if (global_flash_image_file)
00442         avr_core_load_program (global_core, global_flash_image_file,
00443                                global_flash_image_type);
00444 
00445     /* Load eeprom data image into eeprom */
00446     if (global_eeprom_image_file)
00447         avr_core_load_eeprom (global_core, global_eeprom_image_file,
00448                               global_eeprom_image_type);
00449 
00450     for (i = 0; i < global_break_count; i++)
00451     {
00452         /* Note that we interpret the break address from the user as a byte
00453            address instead of a word address. This makes it easier on the user
00454            since binutils, gcc and gdb all work in terms of byte addresses. */
00455 
00456         avr_message ("Setting breakpoint at 0x%x.\n", global_break_list[i]);
00457         avr_core_insert_breakpoint (global_core, global_break_list[i] / 2);
00458     }
00459 
00460     if (global_gdbserver_mode == 1)
00461     {
00462         global_gdb_comm->user_data = global_core;
00463         gdb_interact (global_gdb_comm, global_gdbserver_port,
00464                       global_gdb_debug);
00465     }
00466     else
00467     {
00468         if (global_flash_image_file)
00469             /* Run the program */
00470             avr_core_run (global_core);
00471         else
00472             fprintf (stderr, "No program was specified to be run.\n");
00473     }
00474 
00475     display_close ();           /* close down the display coprocess */
00476 
00477     exit (0);
00478     return 0;
00479 }