diff --git a/configure.ac b/configure.ac index 4a81bb1..03950c6 100644 --- a/configure.ac +++ b/configure.ac @@ -6,7 +6,7 @@ # common initialisation of autotools #### AC_INIT([SimulAVR], [1.1dev], [simulavr-devel@nongnu.org]) -AM_INIT_AUTOMAKE([1.10 -Wall -Werror foreign]) +AM_INIT_AUTOMAKE([1.10 -Wall foreign]) AC_PREREQ([2.61]) AC_CONFIG_MACRO_DIR([m4]) AM_MAINTAINER_MODE @@ -215,6 +215,7 @@ AC_CONFIG_FILES([ examples/atmega48/Makefile examples/atmega128_timer/Makefile examples/atmel_key/Makefile examples/feedback/Makefile examples/simple_ex1/Makefile examples/spi/Makefile examples/stdiodemo/Makefile examples/python/Makefile + examples/simple_serial/Makefile ]) ## Certain files should only be generated if Tcl is available and enabled diff --git a/doc/mdate-sh b/doc/mdate-sh index 83d2700..c3dfb1b 100755 --- a/doc/mdate-sh +++ b/doc/mdate-sh @@ -45,7 +45,7 @@ Report bugs to . EOF exit $? ;; - -v | --v*) + -V | --v*) echo "mdate-sh $scriptversion" exit $? ;; diff --git a/doc/simulavr.texi b/doc/simulavr.texi index 5f95962..117941b 100644 --- a/doc/simulavr.texi +++ b/doc/simulavr.texi @@ -179,9 +179,9 @@ no matter if it is ever reached or not. run with user interface for external pin handling at port 7777. This does not open any graphics but activates the interface to communicate with the environment simulation. -@item -v --version +@item -V --version show the software version of simulavr -@item -V --verbose +@item -v --verbose output some hints to console @item -W --writetopipe , add a special pipe register to device at IO-Offset and opens for writing diff --git a/doc/usage.rst b/doc/usage.rst index b7b7aef..a9bc38f 100644 --- a/doc/usage.rst +++ b/doc/usage.rst @@ -8,10 +8,10 @@ Invoke simulavr:: Common options -------------- -``-v, --version`` +``-V, --version`` show the software version of simulavr -``-V, --verbose`` +``-v, --verbose`` output some hints to console ``-h, --help`` diff --git a/examples/Makefile.am b/examples/Makefile.am index 556cb18..f98a40c 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -5,7 +5,7 @@ examplesdir = $(docdir)/examples -SUBDIRS = simple_ex1 +SUBDIRS = simple_ex1 simple_serial if USE_TCL SUBDIRS += anacomp atmega48 spi stdiodemo feedback atmega128_timer atmel_key endif diff --git a/examples/simple_serial/.gitignore b/examples/simple_serial/.gitignore new file mode 100644 index 0000000..fde070e --- /dev/null +++ b/examples/simple_serial/.gitignore @@ -0,0 +1,3 @@ +*.elf +*.hex + diff --git a/examples/simple_serial/Makefile.am b/examples/simple_serial/Makefile.am new file mode 100644 index 0000000..75158dc --- /dev/null +++ b/examples/simple_serial/Makefile.am @@ -0,0 +1,43 @@ +# +# $Id$ +# + +examplesdir = $(docdir)/examples/simple_serial + +EXAMPLE = simple_serial + +AVR_GCC = @AVR_GCC@ +AVR_CPU = atmega644 +AVR_FREQUENCY = 20000000 +AVR_CFLAGS = -g -O2 -mmcu=$(AVR_CPU) -DF_CPU=$(AVR_FREQUENCY) +AVR_CFLAGS += -Wl,--section-start=.siminfo=0x900000 +AVR_OBJS = main.o serial.o +SIMULAVR_ARGS = -d $(AVR_CPU) -f $(EXAMPLE).elf + +EXTRA_DIST = main.c serial.c serial.h + +examples_DATA = $(EXAMPLE).hex $(EXAMPLE).elf $(EXTRA_DIST) README + +CLEANFILES = $(EXAMPLE).hex $(EXAMPLE).elf $(AVR_OBJS) + + +do: $(EXAMPLE).elf + ../../src/simulavr $(SIMULAVR_ARGS) + +program: $(EXAMPLE).hex + avrdude -c stk500v2 -b 115200 -p $(AVR_CPU) -P $(SERIAL_AVR) \ + -U flash:w:$< + + +$(EXAMPLE).hex: $(EXAMPLE).elf + avr-objcopy -j .text -j .data -O ihex -R .eeprom -R .fuse -R .lock $< $@ + +$(EXAMPLE).elf: $(AVR_OBJS) + $(AVR_GCC) $(AVR_CFLAGS) -o $@ $^ + +main.o: $(srcdir)/main.c + $(AVR_GCC) $(AVR_CFLAGS) -c -o $@ $< + +serial.o: $(srcdir)/serial.c $(srcdir)/serial.h + $(AVR_GCC) $(AVR_CFLAGS) -c -o $@ $< + diff --git a/examples/simple_serial/README b/examples/simple_serial/README new file mode 100644 index 0000000..9456914 --- /dev/null +++ b/examples/simple_serial/README @@ -0,0 +1,48 @@ +# +# $Id$ +# +# +# Copyright (C) 2013 Markus Hitter + + +This example illustrates how one can communicate with the simulated AVR +using its USART. The other end of the communications line is stdin/stdout, +so you can simply interact with the simulated AVR by typing into the +terminal you used to start it with. + +The firmware provided can be uploaded to real hardware as-is and should do +just the same when accessed through a serial terminal. This is an important +point, because in main.c you can see how few tweaks to the original code +are required to allow interacting with a simulated run. All of these tweaks +don't alter the binary actually executed at all. + +Building and running the example: + + - Have SimulAVR built, default configuration options are fine. + + - Go to examples/simple_serial (here). + + - Build the example: + + make + + - Run the example: + + make do + + - Upload the example to and run it on real hardware (this isn't exactly + flexible, yet, it might require some tweaks to the Makefile to match + /your/ hardware): + + export SERIAL_AVR=/dev/ + make program + gtkterm -p $SERIAL_AVR -s 19200 + +Interaction: + +Well, extremely simple. It's a code example, after all. Type characters and +the characters will be sent back with some extra text. That's all, I hope +this fuels your imagination. + + + diff --git a/examples/simple_serial/main.c b/examples/simple_serial/main.c new file mode 100644 index 0000000..9de9f5f --- /dev/null +++ b/examples/simple_serial/main.c @@ -0,0 +1,78 @@ + /* + **************************************************************************** + * + * simple_serial - A demo for the SimulAVR simulator. + * Copyright (C) 2013 Markus Hitter + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + **************************************************************************** + * + * $Id$ + * + * Super trivial example exercising the UART serial communications line. + * + * It's purpose is to show how SimulAVR can redirect serial communications + * in a way useful for running in a simulator, while requiring NO code + * modifications which would change it's behaviour compared to running + * on real hardware or disallowing to run the very same compiled binary + * on that hardware. The compiled binary should work in the simulator just + * as fine as on hardware. + */ + +#include "serial.h" + +#include + +/* + * Here we tell SimulAVR for which device and which frequency we build this + * code. The nice thing is, these macros add a section to the ELF file, so + * they can be read by SimulAVR, but they're also ignored when converted to + * an ihex file for hardware upload. Accordingly, the executed binary for + * SimulAVR and real hardware is the same, no need to recompile for one or + * another. The more important result of this is, behaviour is also exactly + * the same, no extra cycles for serving simulator matters. + * + * One small caveat: To stop the linker from stripping this section, avr-gcc + * needs an extra parameter when linking the binary (see Makefile.am): + * + * -Wl,--section-start=.siminfo=0x900000 + * + * For details on these macros, see the comments in simulavr_info.h. + */ +#include "../../src/simulavr_info.h" +SIMINFO_DEVICE("atmega644"); +SIMINFO_CPUFREQUENCY(F_CPU); +SIMINFO_SERIAL_IN("D0", "-", 19200); +SIMINFO_SERIAL_OUT("D1", "-", 19200); + + +// This is all we need: +int main (void) { + + serial_init(); + sei(); + + serial_writestr_P(PSTR("Hello, world!\n\nNow, please type:\n")); + + for (;;) { + if (serial_rxchars() != 0) { + uint8_t c = serial_popchar(); + serial_writestr_P(PSTR("received: <")); + serial_writechar(c); + serial_writestr_P(PSTR(">\n")); + } + } +} diff --git a/examples/simple_serial/serial.c b/examples/simple_serial/serial.c new file mode 100644 index 0000000..0c9724c --- /dev/null +++ b/examples/simple_serial/serial.c @@ -0,0 +1,193 @@ +/* + * $Id$ + * + * Serial subsystem + * + * Copyright (C) 2009 Michael Moon + * 2010 Markus Hitter + * + * This is a pretty lean, fast and powerful serial subsystem, taken from + * RepRaps' Teacup firmware: https://github.com/Traumflug/Teacup_Firmware . + * For even more simplification, support for XON/XOFF flow control was stripped. + * + * The system uses ringbuffers for both transmit and receive, and + * intelligently decides whether to wait or drop transmitted characters if + * the buffer is full. It's tested on many versions of the ATmega. + */ +#include "serial.h" + +#include + +// Baud rate. +#define BAUD 19200 + +// Size of TX and RX buffers. MUST be a power of 2 and 256 or less. +#define BUFSIZE 64 + +// rx buffer head pointer. Points to next available space. +volatile uint8_t rxhead = 0; +// rx buffer tail pointer. Points to last character in buffer. +volatile uint8_t rxtail = 0; +// rx buffer +volatile uint8_t rxbuf[BUFSIZE]; + +// tx buffer head pointer. Points to next available space. +volatile uint8_t txhead = 0; +// tx buffer tail pointer. Points to last character in buffer. +volatile uint8_t txtail = 0; +// tx buffer +volatile uint8_t txbuf[BUFSIZE]; + +// check if we can read from this buffer +#define buf_canread(buffer) ((buffer ## head - buffer ## tail) & (BUFSIZE - 1)) +// read from buffer +#define buf_pop(buffer, data) do { data = buffer ## buf[buffer ## tail]; buffer ## tail = (buffer ## tail + 1) & (BUFSIZE - 1); } while (0) + +// check if we can write to this buffer +#define buf_canwrite(buffer) ((buffer ## tail - buffer ## head - 1) & (BUFSIZE - 1)) +// write to buffer +#define buf_push(buffer, data) do { buffer ## buf[buffer ## head] = data; buffer ## head = (buffer ## head + 1) & (BUFSIZE - 1); } while (0) + +/* + * ringbuffer logic: + * head = written data pointer + * tail = read data pointer + * + * when head == tail, buffer is empty + * when head + 1 == tail, buffer is full + * thus, number of available spaces in buffer is (tail - head) & bufsize + * + * can write: + * (tail - head - 1) & (BUFSIZE - 1) + * + * write to buffer: + * buf[head++] = data; head &= (BUFSIZE - 1); + * + * can read: + * (head - tail) & (BUFSIZE - 1) + * + * read from buffer: + * data = buf[tail++]; tail &= (BUFSIZE - 1); + */ + +// initialise serial subsystem +void serial_init() { + // set up baud generator and interrupts, clear buffers + + #if BAUD > 38401 + UCSR0A = MASK(U2X0); + UBRR0 = (((F_CPU / 8) / BAUD) - 0.5); + #else + UCSR0A = 0; + UBRR0 = (((F_CPU / 16) / BAUD) - 0.5); + #endif + + UCSR0B = MASK(RXEN0) | MASK(TXEN0); + UCSR0C = MASK(UCSZ01) | MASK(UCSZ00); + + UCSR0B |= MASK(RXCIE0) | MASK(UDRIE0); +} + +// receive interrupt +// Using the pragma inside the function is incompatible with Arduinos' gcc. +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#ifdef USART_RX_vect +ISR(USART_RX_vect) { +#else +ISR(USART0_RX_vect) { +#endif + // we have received a character, stuff it in the rx buffer + // if we can, or drop it if we can't + if (buf_canwrite(rx)) + buf_push(rx, UDR0); + else { + // Not reading the character makes the interrupt logic to swamp us with + // retries, so better read it and throw it away. + // #pragma GCC diagnostic ignored "-Wunused-but-set-variable" + uint8_t trash; + // #pragma GCC diagnostic pop + + trash = UDR0; + } +} +#pragma GCC diagnostic pop + +// transmit buffer ready interrupt +#ifdef USART_UDRE_vect +ISR(USART_UDRE_vect) { +#else +ISR(USART0_UDRE_vect) { +#endif + // provide the next character to transmit if we can, + // otherwise disable this interrupt + if (buf_canread(tx)) + buf_pop(tx, UDR0); + else + UCSR0B &= ~MASK(UDRIE0); +} + +// check how many characters can be read +uint8_t serial_rxchars() { + return buf_canread(rx); +} + +// read one character +uint8_t serial_popchar() { + uint8_t c = 0; + + // it's imperative that we check, because if the buffer is empty + // and we pop, we'll go through the whole buffer again + if (buf_canread(rx)) + buf_pop(rx, c); + + return c; +} + +// send one character +void serial_writechar(uint8_t data) { + // check if interrupts are enabled + if (SREG & MASK(SREG_I)) { + // if they are, we should be ok to block since the + // tx buffer is emptied from an interrupt + for ( ; buf_canwrite(tx) == 0; ); + buf_push(tx, data); + } + else { + // interrupts are disabled- maybe we're in one? + // anyway, instead of blocking, only write if we have room + if (buf_canwrite(tx)) + buf_push(tx, data); + } + // enable TX interrupt so we can send this character + UCSR0B |= MASK(UDRIE0); +} + +// send a string +void serial_writestr(uint8_t *data) { + uint8_t i = 0, r; + + // Yes, this is *supposed* to be assignment rather than comparison, so we + // break when r is assigned zero. + while ((r = data[i++])) + serial_writechar(r); +} + +/* + * Write from FLASH + * + * Extensions to output flash memory pointers. This prevents the data to + * become part of the .data segment instead of the .code segment. That means + * less memory is consumed for multi-character writes. + * + * For single character writes (i.e. '\n' instead of "\n"), using + * serial_writechar() directly is the better choice. +*/ +void serial_writestr_P(PGM_P data) { + uint8_t r, i = 0; + + // Yes, this is *supposed* to be assignment rather than comparison, so we + // break when r is assigned zero. + while ((r = pgm_read_byte(&data[i++]))) + serial_writechar(r); +} + diff --git a/examples/simple_serial/serial.h b/examples/simple_serial/serial.h new file mode 100644 index 0000000..0b980e5 --- /dev/null +++ b/examples/simple_serial/serial.h @@ -0,0 +1,47 @@ +/* + * $Id$ + * + * Serial subsystem + * + * Copyright (C) 2009 Michael Moon + * 2010 Markus Hitter + * + * This is a pretty lean, fast and powerful serial subsystem, taken from + * RepRaps' Teacup firmware: https://github.com/Traumflug/Teacup_Firmware . + * For even more simplification, support for XON/XOFF flow control was stripped. + * + * The system uses ringbuffers for both transmit and receive, and + * intelligently decides whether to wait or drop transmitted characters if + * the buffer is full. It's tested on many versions of the ATmega. + */ + +#ifndef _SERIAL_H +#define _SERIAL_H + +#include +#include +#include + +#define MASK(PIN) (1 << PIN) + +// initialise serial subsystem +void serial_init(void); + +// return number of characters in the receive buffer, +// and number of spaces in the send buffer +uint8_t serial_rxchars(void); + +// read one character +uint8_t serial_popchar(void); + +// send one character +void serial_writechar(uint8_t data); + +// read/write many characters +void serial_writestr(uint8_t *data); + +// write from flash +void serial_writestr_P(PGM_P data); + +#endif /* _SERIAL_H */ + diff --git a/examples/stdiodemo/README b/examples/stdiodemo/README index 6fcce03..820c258 100644 --- a/examples/stdiodemo/README +++ b/examples/stdiodemo/README @@ -4,15 +4,11 @@ Author: Knut Schwichtenberg +Build the example (after configuring and building in the main directory): -Joel Sherrill Notes -=================== + make -3 March 2009 -Temporarily, the Makefile.am is a real Makefile. The rest of the -contents of this directory appear to work with the autotools -tree but the Makefile Knut provided has not yet been converted to -a Makefile.am properly. Until this is complete +Run the example: + + ../../src/simulavr -d atmega128 --file stdiodemo.elf -make -f Makefile.notauto -make -f Makefile.notauto clean diff --git a/src/avrdevice.cpp b/src/avrdevice.cpp index 37b236d..aff75a7 100644 --- a/src/avrdevice.cpp +++ b/src/avrdevice.cpp @@ -275,8 +275,7 @@ int AvrDevice::Step(bool &untilCoreStepFinished, SystemClockOffset *nextStepIn_n } if(EP.end() != find(EP.begin(), EP.end(), PC)) { - if(global_verbose_on) - cout << "Simulation finished!" << endl; + avr_message("Simulation finished!"); SystemClock::Instance().stop(); dump_manager->cycle(); return 0; diff --git a/src/avrerror.cpp b/src/avrerror.cpp index 1c26b47..4156160 100644 --- a/src/avrerror.cpp +++ b/src/avrerror.cpp @@ -127,15 +127,21 @@ void SystemConsoleHandler::TraceNextLine(void) { } } -void SystemConsoleHandler::vfmessage(const char *file, int line, const char *fmt, ...) { +void SystemConsoleHandler::vfmessage(const char *fmt, ...) { + if ( ! global_verbose_on) + return; + va_list ap; - char *mfmt = getFormatString("MESSAGE", file, line, fmt); + snprintf(formatStringBuffer, sizeof(formatStringBuffer), + "MESSAGE %s", fmt); va_start(ap, fmt); - vsnprintf(messageStringBuffer, sizeof(messageStringBuffer), mfmt, ap); + vsnprintf(messageStringBuffer, sizeof(messageStringBuffer), + formatStringBuffer, ap); va_end(ap); - if(fmt[strlen(fmt) - 1] != '\n') - *wrnStream << std::endl; *msgStream << messageStringBuffer; + if(fmt[strlen(fmt) - 1] != '\n') + *msgStream << std::endl; + msgStream->flush(); } void SystemConsoleHandler::vfwarning(const char *file, int line, const char *fmt, ...) { diff --git a/src/avrerror.h b/src/avrerror.h index 0a8d7b0..9e752f9 100644 --- a/src/avrerror.h +++ b/src/avrerror.h @@ -32,8 +32,12 @@ #define ATTRIBUTE_NORETURN __declspec(noreturn) #define ATTRIBUTE_PRINTF(string_arg, first_arg) #elif defined(__GNUC__) +#ifndef ATTRIBUTE_NORETURN #define ATTRIBUTE_NORETURN __attribute__((noreturn)) +#endif +#ifndef ATTRIBUTE_PRINTF #define ATTRIBUTE_PRINTF(string_arg, first_arg) __attribute__ ((format (printf, string_arg, first_arg))) +#endif #else #define ATTRIBUTE_NORETURN #define ATTRIBUTE_PRINTF(string_arg, first_arg) @@ -70,8 +74,8 @@ class SystemConsoleHandler { void TraceNextLine(void); //! Format and send a message to message stream (default stdout) - void vfmessage(const char *file, int line, const char *fmt, ...) - ATTRIBUTE_PRINTF(4, 5); + void vfmessage(const char *fmt, ...) + ATTRIBUTE_PRINTF(2, 3); //! Format and send a warning message to warning stream (default stderr) void vfwarning(const char *file, int line, const char *fmt, ...) ATTRIBUTE_PRINTF(4, 5); @@ -123,7 +127,7 @@ extern int global_verbose_on; //! Helper function for writing trace (trace IO access) void trioaccess(const char *t, unsigned char val); -#define avr_message(fmt, ...) sysConHandler.vfmessage(__FILE__, __LINE__, fmt, ## __VA_ARGS__) +#define avr_message(fmt, ...) sysConHandler.vfmessage(fmt, ## __VA_ARGS__) #define avr_warning(fmt, ...) sysConHandler.vfwarning(__FILE__, __LINE__, fmt, ## __VA_ARGS__) #define avr_failure(fmt, ...) sysConHandler.vferror(__FILE__, __LINE__, fmt, ## __VA_ARGS__) #define avr_error(fmt, ...) sysConHandler.vffatal(__FILE__, __LINE__, fmt, ## __VA_ARGS__) diff --git a/src/avrfactory.cpp b/src/avrfactory.cpp index 77bd154..c37b565 100644 --- a/src/avrfactory.cpp +++ b/src/avrfactory.cpp @@ -52,7 +52,9 @@ AvrDevice* AvrFactory::makeDevice(const char *in) { for(unsigned int i = 0; i < devname.size(); i++) devname[i] = tolower(devname[i]); if(devname == "unknown") - avr_error("Device type not specified, use -d | --device TYPE or insert '#insert ' into your source to specify device signature"); + avr_error("Device type not specified, use -d | --device TYPE or " + "insert a SIMINFO_DEVICE(name) macro into your source to " + "specify the device name"); AVRDeviceMap::iterator i = devmap.find(devname); if(i == devmap.end()) avr_error("Invalid device specification: %s", in); diff --git a/src/avrreadelf.cpp b/src/avrreadelf.cpp index 2828bdc..4d17130 100644 --- a/src/avrreadelf.cpp +++ b/src/avrreadelf.cpp @@ -30,9 +30,14 @@ #endif #include +#include #include #include "avrdevice_impl.h" +#include "avrsignature.h" +#include "serialrx.h" +#include "serialtx.h" +#include "simulavr_info.h" #include "avrreadelf.h" @@ -86,18 +91,18 @@ typedef struct { } Elf32_Phdr; void ELFLoad(const AvrDevice * core) { - FILE * f = fopen(core->actualFilename.c_str(), "rb"); + FILE * f = fopen(core->GetFname().c_str(), "rb"); if(f == NULL) - avr_error("Could not open file: %s", core->actualFilename.c_str()); + avr_error("Could not open file: %s", core->GetFname().c_str()); Elf32_Ehdr header; fread(&header, sizeof(header), 1, f); if(header.e_ident[0] != 0x7F || header.e_ident[1] != 'E' || header.e_ident[2] != 'L' || header.e_ident[3] != 'F') - avr_error("File '%s' is not an ELF file", core->actualFilename.c_str()); + avr_error("File '%s' is not an ELF file", core->GetFname().c_str()); // TODO: fix endianity in header if(header.e_machine != 83) - avr_error("ELF file '%s' is not for Atmel AVR architecture (%d)", core->actualFilename.c_str(), header.e_machine); + avr_error("ELF file '%s' is not for Atmel AVR architecture (%d)", core->GetFname().c_str(), header.e_machine); for(int i = 0; i < header.e_phnum; i++) { fseek(f, header.e_phoff + i * header.e_phentsize, SEEK_SET); @@ -112,7 +117,7 @@ void ELFLoad(const AvrDevice * core) { continue; // not into a Flash if(progHeader.p_filesz != progHeader.p_memsz) { avr_error("Segment sizes 0x%x and 0x%x in ELF file '%s' must be the same", - progHeader.p_filesz, progHeader.p_memsz, core->actualFilename.c_str()); + progHeader.p_filesz, progHeader.p_memsz, core->GetFname().c_str()); } unsigned char * tmp = new unsigned char[progHeader.p_filesz]; fseek(f, progHeader.p_offset, SEEK_SET); @@ -125,26 +130,26 @@ void ELFLoad(const AvrDevice * core) { fclose(f); } -unsigned int ELFGetSignature(const char *filename) { +unsigned int ELFGetDeviceNameAndSignature(const char *filename, char * devicename) { return -1; } #endif #ifndef _MSC_VER -void ELFLoad(const AvrDevice * core) { +void ELFLoad(AvrDevice * core) { bfd *abfd; asection *sec; // open file bfd_init(); - abfd = bfd_openr(core->actualFilename.c_str(), NULL); + abfd = bfd_openr(core->GetFname().c_str(), NULL); if(abfd == NULL) - avr_error("Could not open file: %s", core->actualFilename.c_str()); + avr_error("Could not open file: %s", core->GetFname().c_str()); // check format if(bfd_check_format(abfd, bfd_object) == FALSE) - avr_error("File '%s' isn't a elf object", core->actualFilename.c_str()); + avr_error("File '%s' isn't a elf object", core->GetFname().c_str()); // reading out the symbols long storage_needed; @@ -193,17 +198,19 @@ void ELFLoad(const AvrDevice * core) { } else if(vma >= 0x840000 && vma < 0x840400) { /* signature space starting from 0x840000, do nothing */; } else - avr_warning("Unknown symbol address range found! (symbol='%s', address=0x%x)", symbol_table[i]->name, vma); + if ( ! strcmp(symbol_table[i]->name, ".siminfo") && + ! strcmp(symbol_table[i]->name, "siminfo")) + avr_warning("Unknown symbol address range found! (symbol='%s', address=0x%x)", symbol_table[i]->name, vma); } free(symbol_table); } else - avr_warning("Elf file '%s' has no symbol table!", core->actualFilename.c_str()); + avr_warning("Elf file '%s' has no symbol table!", core->GetFname().c_str()); // load program, data and - if available - eeprom, fuses and signature sec = abfd->sections; while(sec != 0) { // only loadable sections - if(sec->flags & SEC_LOAD) { + if(sec->flags & SEC_LOAD && strcmp(sec->name, ".siminfo")) { int size; size = sec->size; @@ -237,9 +244,9 @@ void ELFLoad(const AvrDevice * core) { avr_error("wrong device signature size in elf file, expected=3, given=%d", size); } else { unsigned int sig = (((tmp[2] << 8) + tmp[1]) << 8) + tmp[0]; - if(core->devSignature != -1 && sig != core->devSignature) { + if(core->GetDeviceSignature() != -1 && sig != core->GetDeviceSignature()) { free(tmp); // free memory before abort program - avr_error("wrong device signature, expected=0x%x, given=0x%x", core->devSignature, sig); + avr_error("wrong device signature, expected=0x%x, given=0x%x", core->GetDeviceSignature(), sig); } } } @@ -247,6 +254,73 @@ void ELFLoad(const AvrDevice * core) { // free allocated space free(tmp); } + else if ( ! strcmp(sec->name, ".siminfo")) { + /* + * You wonder why the .siminfo section is read here, while this + * stuff also shows up as symbols, which are read above already? + * Well, doing things this way is pretty independent from ELF + * internals, other than finding the .siminfo section start pointer. + * Accordingly, we can add pretty much anything, as long as the + * interpretation here matches what's given in simulavr_info.h. + */ + int size = sec->size; + unsigned char *data, *data_ptr, *data_end; + + data = (unsigned char *)malloc(size); + if (data == NULL) + break; + bfd_get_section_contents(abfd, sec, data, 0, size); + + data_ptr = data; + data_end = data + size; + while (data_ptr < data_end) { + char tag = *data_ptr; + char length = *(data_ptr + 1); + // Length check already done in ELFGetDeviceNameAndSignature(). + + switch (tag) { + case SIMINFO_TAG_DEVICE: + // Device name. Handled in ELFGetDeviceNameAndSignature(). + break; + case SIMINFO_TAG_CPUFREQUENCY: + core->SetClockFreq((SystemClockOffset)1000000000 / + ((siminfo_long_t *)data_ptr)->value); + break; + case SIMINFO_TAG_SERIAL_IN: + avr_message("Connecting file %s as serial in to pin %s at %d baud.", + ((siminfo_serial_t *)data_ptr)->filename, + ((siminfo_serial_t *)data_ptr)->pin, + ((siminfo_serial_t *)data_ptr)->baudrate); + { + Net *net = new Net(); + SerialTxFile *serial = + new SerialTxFile(((siminfo_serial_t *)data_ptr)->filename); + serial->SetBaudRate(((siminfo_serial_t *)data_ptr)->baudrate); + net->Add(core->GetPin(((siminfo_serial_t *)data_ptr)->pin)); + net->Add(serial->GetPin("tx")); + } + break; + case SIMINFO_TAG_SERIAL_OUT: + avr_message("Connecting pin %s as serial out to file %s at %d baud.", + ((siminfo_serial_t *)data_ptr)->pin, + ((siminfo_serial_t *)data_ptr)->filename, + ((siminfo_serial_t *)data_ptr)->baudrate); + { + Net *net = new Net(); + SerialRxFile *serial = + new SerialRxFile(((siminfo_serial_t *)data_ptr)->filename); + serial->SetBaudRate(((siminfo_serial_t *)data_ptr)->baudrate); + net->Add(core->GetPin(((siminfo_serial_t *)data_ptr)->pin)); + net->Add(serial->GetPin("rx")); + } + break; + default: + avr_warning("Unknown tag in ELF .siminfo section: %hu", tag); + } + data_ptr += length; + } + free(data); + } sec = sec->next; } @@ -254,41 +328,107 @@ void ELFLoad(const AvrDevice * core) { bfd_close(abfd); } -unsigned int ELFGetSignature(const char *filename) { - bfd *abfd; - asection *sec; - unsigned int signature = -1; +unsigned int ELFGetDeviceNameAndSignature(const char *filename, char *devicename) { + unsigned int signature = 0, new_sig = 0; - bfd_init(); - abfd = bfd_openr(filename, NULL); + // Command line takes precedence. + if ( ! strcmp(devicename, "unknown")) { + bfd *abfd; + asection *sec; + + bfd_init(); + abfd = bfd_openr(filename, NULL); - if((abfd != NULL) && (bfd_check_format(abfd, bfd_object) == TRUE)) { - sec = abfd->sections; - while(sec != 0) { - if(sec->flags & SEC_LOAD) { + if((abfd != NULL) && (bfd_check_format(abfd, bfd_object) == TRUE)) { + sec = abfd->sections; + while(sec != 0) { int size = sec->size; - unsigned char *tmp = (unsigned char *)malloc(size); + char *tmp = (char *)malloc(size); - bfd_get_section_contents(abfd, sec, tmp, 0, size); + if(sec->flags & SEC_LOAD) { - if(sec->vma >= 0x840000 && sec->vma < 0x840400) { - // read and check signature, if available, space from 0x840000 to 0x840400 - if(size != 3) { - free(tmp); // free memory before abort program - avr_error("wrong device signature size in elf file, expected=3, given=%d", size); - } else - signature = (((tmp[2] << 8) + tmp[1]) << 8) + tmp[0]; + bfd_get_section_contents(abfd, sec, tmp, 0, size); + + if(sec->vma >= 0x840000 && sec->vma < 0x840400) { + // read and check signature, if available, space from 0x840000 to 0x840400 + if(size != 3) { + free(tmp); // free memory before abort program + avr_error("wrong device signature size in elf file, expected=3, given=%d", size); + } else + signature = (((tmp[2] << 8) + tmp[1]) << 8) + tmp[0]; + } } + if ( ! strcmp(sec->name, ".siminfo")) { + char *data_ptr, *data_end; + + data_ptr = tmp; + data_end = tmp + size; + while (data_ptr < data_end) { + char tag = *data_ptr; + char length = *(data_ptr + 1); + if (length == 0) + avr_error("Field of zero length in .siminfo" + "section in ELF file found."); + if (tag == SIMINFO_TAG_DEVICE) { + strncpy(devicename, + ((siminfo_string_t *)data_ptr)->string, 1024); + devicename[1023] = '\0'; // safety + break; + } + // Everything else handled in ELFLoad(). + data_ptr += length; + } + } free(tmp); + sec = sec->next; } + bfd_close(abfd); + } - sec = sec->next; + if (strcmp(devicename, "unknown")) { + // We found a device name, find the signature of _this_ one. + std::map::iterator cur = + AvrNameToSignatureMap.find(devicename); + if (cur != AvrNameToSignatureMap.end()) { + new_sig = cur->second; + } else { + avr_warning("signature for device '%s' not found", devicename); + } } - bfd_close(abfd); + if (signature != 0) { + if (new_sig != 0 && signature != new_sig) { + avr_warning("ELF signature 0x%x taking precedence over ELF siminfo device name %s", + signature, devicename); + } + } else + signature = new_sig; + } + else { + // We have a device name from the command line. + std::map::iterator cur = + AvrNameToSignatureMap.find(devicename); + if (cur != AvrNameToSignatureMap.end()) { + signature = cur->second; + } else { + avr_warning("signature for device '%s' not found", devicename); + } + } + + // If we've found _anything_, we've a signature now. + if (signature != 0) { + std::map::iterator cur = + AvrSignatureToNameMap.find(signature); + if (cur != AvrSignatureToNameMap.end()) { + strncpy(devicename, cur->second.c_str(), 1024); + } else { + // Assume signatures found by device name never get here. + avr_warning("unknown signature in ELF file: 0x%x", signature); + } } + avr_message("Device name is %s", devicename); return signature; } #endif diff --git a/src/avrreadelf.h b/src/avrreadelf.h index d16195d..9695826 100644 --- a/src/avrreadelf.h +++ b/src/avrreadelf.h @@ -28,7 +28,7 @@ #include "avrdevice.h" -unsigned int ELFGetSignature(const char *filename); -void ELFLoad(const AvrDevice * core); +unsigned int ELFGetDeviceNameAndSignature(const char *filename, char *devicename); +void ELFLoad(AvrDevice * core); #endif diff --git a/src/cmd/main.cpp b/src/cmd/main.cpp index a4dca69..e3771eb 100644 --- a/src/cmd/main.cpp +++ b/src/cmd/main.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include using namespace std; @@ -59,6 +60,9 @@ using namespace std; #include "dumpargs.h" +#include "serialrx.h" +#include "serialtx.h" + const char *SplitOffsetFile(const char *arg, const char *name, int base, @@ -120,7 +124,7 @@ const char Usage[] = "-e --writetoexit \n" " add a special register at IO-offset\n" " which exits simulator run\n" - "-V --verbose output some hints to console\n" + "-v --verbose output some hints to console\n" "-T --terminate