diff --git a/configure.ac b/configure.ac index 4cd85cb..068ecf7 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 subdir-objects]) +AM_INIT_AUTOMAKE([1.10 -Wall foreign subdir-objects]) AC_PREREQ([2.61]) AC_CONFIG_MACRO_DIR([m4]) AM_MAINTAINER_MODE diff --git a/examples/simple_serial/Makefile.am b/examples/simple_serial/Makefile.am index bebf7a7..75158dc 100644 --- a/examples/simple_serial/Makefile.am +++ b/examples/simple_serial/Makefile.am @@ -10,6 +10,7 @@ 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 diff --git a/examples/simple_serial/main.c b/examples/simple_serial/main.c index fd6ec66..a62ca84 100644 --- a/examples/simple_serial/main.c +++ b/examples/simple_serial/main.c @@ -36,6 +36,28 @@ #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) { diff --git a/src/atmega1284abase.cpp b/src/atmega1284abase.cpp index aa8611c..6db193d 100644 --- a/src/atmega1284abase.cpp +++ b/src/atmega1284abase.cpp @@ -40,6 +40,11 @@ AVR_REGISTER(atmega324, AvrDevice_atmega324A) AVR_REGISTER(atmega644, AvrDevice_atmega644A) AVR_REGISTER(atmega1284, AvrDevice_atmega1284A) +AVR_REGISTER(atmega164p, AvrDevice_atmega164A) +AVR_REGISTER(atmega324p, AvrDevice_atmega324A) +AVR_REGISTER(atmega644p, AvrDevice_atmega644A) +AVR_REGISTER(atmega1284p, AvrDevice_atmega1284A) + AvrDevice_atmega1284Abase::~AvrDevice_atmega1284Abase() { delete usart1; delete usart0; @@ -203,6 +208,7 @@ AvrDevice_atmega1284Abase::AvrDevice_atmega1284Abase(unsigned ram_bytes, 21, // (22) UDRE vector 22); // (23) TX complete vector + // TODO: ATmega644P has USART1, but ATmega644 (maybe others) has not. usart1 = new HWUsart(this, irqSystem, PinAtPort(&portd, 3), // TXD1 diff --git a/src/avrdevice.h b/src/avrdevice.h index 73e7d45..7586cd6 100644 --- a/src/avrdevice.h +++ b/src/avrdevice.h @@ -222,7 +222,7 @@ class AvrDevice: public SimulationMember, public TraceValueRegister { //! When a call/jump/cond-jump instruction was executed. For debugging. void DebugOnJump(); - friend void ELFLoad(const AvrDevice * core); + friend void ELFLoad(AvrDevice * core); }; diff --git a/src/avrfactory.cpp b/src/avrfactory.cpp index 785a1e9..b6592fd 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 2fe7dc5..a411473 100644 --- a/src/avrreadelf.cpp +++ b/src/avrreadelf.cpp @@ -31,10 +31,15 @@ #include "elfio/elfio.hpp" #include +#include #include #include #include "avrdevice_impl.h" +#include "avrsignature.h" +#include "serialrx.h" +#include "serialtx.h" +#include "simulavr_info.h" #include "avrreadelf.h" @@ -87,19 +92,19 @@ typedef struct { Elf32_Word p_align; /* memory/file alignment */ } Elf32_Phdr; -void ELFLoad(const AvrDevice * core) { - FILE * f = fopen(core->actualFilename.c_str(), "rb"); +void ELFLoad(AvrDevice * core) { + 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); @@ -114,7 +119,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); @@ -127,24 +132,23 @@ void ELFLoad(const AvrDevice * core) { fclose(f); } -unsigned int ELFGetSignature(const char *filename) { +unsigned int ELFGetDeviceNameAndSignature(const char *filename, char *devicename) { return std::numeric_limits::max(); } #endif #ifndef _MSC_VER - -void ELFLoad(const AvrDevice * core) { +void ELFLoad(AvrDevice * core) { ELFIO::elfio reader; - if(!reader.load(core->actualFilename)) + if(!reader.load(core->GetFname())) avr_error("File '%s' not found or isn't a elf object", - core->actualFilename.c_str()); + core->GetFname().c_str()); if(reader.get_machine() != EM_AVR) avr_error("ELF file '%s' is not for Atmel AVR architecture (%d)", - core->actualFilename.c_str(), + core->GetFname().c_str(), reader.get_machine()); // over all symbols ... @@ -204,13 +208,74 @@ void ELFLoad(const AvrDevice * core) { /* lock bits starting from 0x830000, do nothing */; } else if(value >= 0x840000 && value < 0x840400) { /* signature space starting from 0x840000, do nothing */; + } else if(!strncmp("siminfo" , name.c_str(), 7)) { + /* SIMINFO symbol, do nothing */ } else - avr_warning("Unknown symbol address range found! (symbol='%s', address=0x%llx)", + avr_warning("Unknown symbol address range found! (symbol='%s', address=0x%lx)", name.c_str(), value); } } + if(psec->get_name() == ".siminfo") { + /* + * You wonder why SIMINFO is read here, ignoring symbols? + * 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. + */ + ELFIO::Elf_Xword filesize = psec->get_size(); + const char *data = psec->get_data(); + const char *data_ptr = data, *data_end = data + filesize; + + 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; + } + } } // load program, data and - if available - eeprom, fuses and signature @@ -248,14 +313,15 @@ void ELFLoad(const AvrDevice * core) { } else if(vma >= 0x840000 && vma < 0x840400) { // read and check signature, if available, space from 0x840000 to 0x840400 if(filesize != 3) - avr_error("wrong device signature size in elf file, expected=3, given=%llu", + avr_error("wrong device signature size in elf file, expected=3, given=%lu", filesize); else { unsigned int sig = (((data[2] << 8) + data[1]) << 8) + data[0]; - if(core->devSignature != std::numeric_limits::max() && sig != core->devSignature) + if(core->GetDeviceSignature() != std::numeric_limits::max() && + sig != core->GetDeviceSignature()) avr_error("wrong device signature, expected=0x%x, given=0x%x", - core->devSignature, + core->GetDeviceSignature(), sig); } } @@ -263,8 +329,9 @@ void ELFLoad(const AvrDevice * core) { } } -unsigned int ELFGetSignature(const char *filename) { - unsigned int signature = std::numeric_limits::max(); +unsigned int ELFGetDeviceNameAndSignature(const char *filename, char *devicename) { + unsigned int signature = 0, sig_cli = 0, sig_elf = 0, sig_siminfo = 0; + char siminfo_name[128]; ELFIO::elfio reader; if(!reader.load(filename)) @@ -275,6 +342,18 @@ unsigned int ELFGetSignature(const char *filename) { filename, reader.get_machine()); + // Search command line for signature. + if(strcmp(devicename, "unknown")) { + std::map::iterator cur = + AvrNameToSignatureMap.find(devicename); + if(cur != AvrNameToSignatureMap.end()) { + sig_cli = cur->second; + } else { + avr_warning("signature for device '%s' not found", devicename); + } + } + + // Search ELF binary for signature. ELFIO::Elf_Half seg_num = reader.segments.size(); for(ELFIO::Elf_Half i = 0; i < seg_num; i++) { @@ -290,18 +369,97 @@ unsigned int ELFGetSignature(const char *filename) { if(vma >= 0x840000 && vma < 0x840400) { // read and check signature, if available, space from 0x840000 to 0x840400 if(filesize != 3) - avr_error("wrong device signature size in elf file, expected=3, given=%llu", - filesize); + avr_error("wrong device signature size in elf file, " + "expected=3, given=%lu", filesize); else { const unsigned char* data = (const unsigned char*)pseg->get_data(); - signature = (((data[2] << 8) + data[1]) << 8) + data[0]; + sig_elf = (((data[2] << 8) + data[1]) << 8) + data[0]; + + std::map::iterator cur = + AvrSignatureToNameMap.find(sig_elf); + if(cur == AvrSignatureToNameMap.end()) { + avr_warning("unknown signature in ELF file: 0x%x", + sig_elf); + sig_elf = 0; + } break; } } } } + // Search SIMINFO for device name. + for(ELFIO::Elf_Half i = 0; i < seg_num; i++) { + ELFIO::section* psec = reader.sections[i]; + + if(psec->get_name() == ".siminfo") { + ELFIO::Elf_Xword filesize = psec->get_size(); + const char *data = psec->get_data(); + const char *data_ptr = data, *data_end = data + filesize; + + 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(siminfo_name, + ((siminfo_string_t *)data_ptr)->string, 128); + siminfo_name[127] = '\0'; // safety + + std::map::iterator cur = + AvrNameToSignatureMap.find(siminfo_name); + if(cur != AvrNameToSignatureMap.end()) { + sig_siminfo = cur->second; + } + else { + avr_warning("signature for device '%s' not found", + siminfo_name); + } + break; + } + // Everything else handled in ELFLoad(). + data_ptr += length; + } + } + } + + // Now we have searched all possible sources, take the one with the highest + // precedence: command line over ELF over SIMINFO. + if(sig_cli) { + signature = sig_cli; + if(sig_elf && sig_elf != sig_cli) { + avr_warning("Command line device name '%s' taking precedence over " + "device signature 0x%x in ELF file.", + devicename, sig_elf); + } + if(sig_siminfo && sig_siminfo != sig_cli) { + avr_warning("Command line device name '%s' taking precedence over " + "SIMINFO device name '%s'.", devicename, siminfo_name); + } + } + else if(sig_elf) { + signature = sig_elf; + if(sig_siminfo && sig_siminfo != sig_elf) { + avr_warning("Device signature 0x%x in ELF file taking precedence " + "over SIMINFO device name '%s'.", + sig_elf, siminfo_name); + } + } + else if(sig_siminfo) { + signature = sig_siminfo; + } + + // Done. + std::map::iterator cur = + AvrSignatureToNameMap.find(signature); + if(cur != AvrSignatureToNameMap.end()) { + strncpy(devicename, cur->second.c_str(), 1024); + } + avr_message("Device name is %s, signature 0x%x.", devicename, signature); + return signature; } diff --git a/src/avrreadelf.h b/src/avrreadelf.h index ef09c2c..e42f070 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/gdbserver.cpp b/src/cmd/gdbserver.cpp index 438dfb9..417537a 100644 --- a/src/cmd/gdbserver.cpp +++ b/src/cmd/gdbserver.cpp @@ -270,7 +270,7 @@ bool GdbServerSocketUnix::Connect(void) { /* If we got this far, we now have a client connected and can start processing. */ - fprintf(stderr, "Connection opened by host %s, port %hd.\n", + fprintf(stderr, "Connection opened by host %s, port %hu.\n", inet_ntoa(address->sin_addr), ntohs(address->sin_port)); return true; diff --git a/src/cmd/main.cpp b/src/cmd/main.cpp index c866714..1b4bb94 100644 --- a/src/cmd/main.cpp +++ b/src/cmd/main.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include using namespace std; @@ -147,7 +148,7 @@ int main(int argc, char *argv[]) { int global_gdb_debug = 0; bool globalWaitForGdbConnection = true; //please wait for gdb connection int userinterface_flag = 0; - unsigned long long fcpu = 4000000; + unsigned long long fcpu = 0; unsigned long long maxRunTime = 0; unsigned long long linestotrace = 1000000; UserInterface *ui; @@ -240,9 +241,6 @@ int main(int argc, char *argv[]) { cerr << "frequency is zero" << endl; exit(1); } - if(global_verbose_on) - printf("Running with CPU frequency: %1.4f MHz (%lld Hz)\n", - fcpu/1000000.0, fcpu); break; case 'l': @@ -275,7 +273,6 @@ int main(int argc, char *argv[]) { break; case 'd': - avr_message("Device to simulate: %s", optarg); devicename = optarg; break; @@ -350,35 +347,17 @@ int main(int argc, char *argv[]) { /* check, if devicename is given or get it out from elf file, if given */ unsigned int sig; - if(devicename == "unknown") { - // option -d | --device not given - if(filename != "unknown") { - // filename given, try to get signature - sig = ELFGetSignature(filename.c_str()); - if(sig != numeric_limits::max()) { - // signature in elf found, try to get devicename - std::map::iterator cur = AvrSignatureToNameMap.find(sig); - if(cur != AvrSignatureToNameMap.end()) { - // devicename found - devicename = cur->second; - } else { - avr_warning("unknown signature in elf file '%s': 0x%x", filename.c_str(), sig); - } - } - } + char *new_devicename = (char *)malloc(1024); // can't be static + + strncpy(new_devicename, devicename.c_str(), 1024); + if(filename != "unknown") { + sig = ELFGetDeviceNameAndSignature(filename.c_str(), new_devicename); } /* now we create the device and set device name and signature */ - AvrDevice *dev1 = AvrFactory::instance().makeDevice(devicename.c_str()); - std::map::iterator cur = AvrNameToSignatureMap.find(devicename); - if(cur != AvrNameToSignatureMap.end()) { - // signature found - sig = cur->second; - } else { - avr_warning("signature for device '%s' not found", devicename.c_str()); - sig = -1; - } - dev1->SetDeviceNameAndSignature(devicename, sig); + AvrDevice *dev1 = AvrFactory::instance().makeDevice(new_devicename); + dev1->SetDeviceNameAndSignature(new_devicename, sig); + free(new_devicename); /* We had to wait with dumping the available tracing values until the device has been created! */ @@ -435,8 +414,20 @@ int main(int argc, char *argv[]) { //if not gdb, the ui will be master controller :-) ui = (userinterface_flag == 1) ? new UserInterface(7777) : NULL; - dev1->SetClockFreq(1000000000 / fcpu); // time base is 1ns! - + if(fcpu != 0) + dev1->SetClockFreq((SystemClockOffset)1000000000 / fcpu); // time base is 1ns! + + if(dev1->GetClockFreq() == 0) { + avr_warning("Clock frequency not given, defaulting to 4000000. " + "Use -F | --cpufrequency FREQ or insert a " + "SIMINFO_CPUFREQUENCY(freq) macro into your source %s", + "to specify it."); + dev1->SetClockFreq((SystemClockOffset)1000000000 / 4000000); + } + avr_message("Running with CPU frequency: %1.3lf MHz (%lld Hz)\n", + (double)1000 / dev1->GetClockFreq(), + (unsigned long long)1000000000 / dev1->GetClockFreq()); + if(sysConHandler.GetTraceState()) dev1->trace_on = 1; @@ -451,9 +442,8 @@ int main(int argc, char *argv[]) { << "number of cpu cycles simulated: " << dec << steps << endl; } else { // limited steps = SystemClock::Instance().Run(maxRunTime); - cout << "Ran too long. Terminated after " << dec << maxRunTime - << " ns (simulated) and " << endl - << dec << steps << " cpu cycles" << endl; + cout << "Ran too long. Terminated after " + << dec << steps << " cpu cycles simulated." << endl; } Application::GetInstance()->PrintResults(); } else { // gdb should be activated diff --git a/src/simulavr_info.h b/src/simulavr_info.h new file mode 100644 index 0000000..52e392e --- /dev/null +++ b/src/simulavr_info.h @@ -0,0 +1,170 @@ +/* + **************************************************************************** + * + * simulavr - A simulator for the Atmel AVR family of microcontrollers. + * Copyright (C) 2013 Markus Hitter + * ELF storage strategy inspired by simavr by Michel Pollet. + * + * 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$ + * + * This header provides macros to embed simulator setup information into + * a compiled ELF binary. + * + * Example: + * + * Add this somewhere at the root level of your AVR code: + * + * #include "simulavr_info.h" + * SIMINFO_DEVICE("atmega644"); + * SIMINFO_CPUFREQUENCY(F_CPU); + * + * Then link as usual, but add these linker flags to avr-gcc to prohibit + * the linker from removing the info sections at the link stage: + * + * -Wl,--section-start=.siminfo=0x900000 + * + * The value choosen here to be 0x900000 can be choosen freely, but must + * be above 0x840400, else it can conflict with program / eeprom / fuses / + * lockbits / signature data, see ELFLoad() in src/avrreadelf.cpp, line 215ff. + * + * You also have to avoid the -Wl,--gc-sections flag, which unfortunately + * increases binary size if you have unused functions. + * + * Having this done, running the ELF binary in the simulator will + * automatically inform simulavr for which AVR variant and CPU frequency + * the binary was built, making the corresponding command line parameters + * obsolete. In case you give both, in-binary and CLI parameters, CLI + * parameters take precedence. + * + * The really nice thing about this mechanism is, it doesn't alter the + * executed binary at all. You can upload and run this on real hardware, + * not a single byte of Flash memory or a single CPU cycle at runtime wasted. + */ + +#ifndef __SIMULAVR_INFO_H__ +#define __SIMULAVR_INFO_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#define SIMINFO_SECTION __attribute__((section(".siminfo"))) + +enum { + SIMINFO_TAG_NOTAG = 0, // keep this unused as a protection against empty data + SIMINFO_TAG_DEVICE, + SIMINFO_TAG_CPUFREQUENCY, + SIMINFO_TAG_SERIAL_OUT, + SIMINFO_TAG_SERIAL_IN, +}; + +struct siminfo_long_t { + uint8_t tag; + uint8_t length; + uint32_t value; +} __attribute__((__packed__)); + +struct siminfo_string_t { + uint8_t tag; + uint8_t length; + char string[]; +} __attribute__((__packed__)); + +struct siminfo_serial_t { + uint8_t tag; + uint8_t length; + char pin[3]; + uint32_t baudrate; + char filename[]; +} __attribute__((__packed__)); + + +/* + * This gives the device type, like "attiny45", "atmega128", "atmega644", etc. + */ +#define SIMINFO_DEVICE(name) \ + const struct siminfo_string_t siminfo_device SIMINFO_SECTION = { \ + SIMINFO_TAG_DEVICE, \ + /* We could use sizeof(siminfo_device) here, but avr-gcc has \ + been seen to set length to 0 (zero), then. */ \ + sizeof(name) + 2, \ + name \ + } + +/* + * This gives the cpu frequency, like 8000000UL or 16000000UL. + */ +#define SIMINFO_CPUFREQUENCY(value) \ + const struct siminfo_long_t siminfo_cpufrequency SIMINFO_SECTION = { \ + SIMINFO_TAG_CPUFREQUENCY, \ + sizeof(uint32_t) + 2, \ + value \ + } + +/* + * Create a serial in (Rx, to AVR) component. The the sent characters/bytes + * will be taken from the given file. This component can be connected to the + * same file as a serial out, if it's a special file like a real serial device + * or a pipe. Connecting both to the same regular file will mess things up. + * + * Using "-" as file name means connecting to the console (stdin/stdout). + * + * The pin to connect is named by a 2-character string, where "E2" means + * pin 2 on port E. + * + * Why a baud rate? Well, the component doesn't just write to the UART receive + * register, but synthesizes actual serial signals on the pin, which in turn + * should be interpreted by your AVR code. If your code sets a baud rate not + * matching the one given here, serial communications won't work. Just like a + * real serial device configured to work at 19200 baud won't work on a real + * serial port set to something else. + * + * Other parameters are fixed to 8N1, which means 8 bits, no parity, 1 stop bit. + */ +#define SIMINFO_SERIAL_IN(pin, filename, baudrate) \ + const struct siminfo_serial_t siminfo_serial_in SIMINFO_SECTION = { \ + SIMINFO_TAG_SERIAL_IN, \ + sizeof(char[3]) + sizeof(uint32_t) + sizeof(filename) + 2, \ + pin, \ + baudrate, \ + filename \ + } + +/* + * Create a serial out (Tx, from AVR) component. Same as above, but the + * other direction. The serial port pin is continuously read and interpreted. + */ +#define SIMINFO_SERIAL_OUT(pin, filename, baudrate) \ + const struct siminfo_serial_t siminfo_serial_out SIMINFO_SECTION = { \ + SIMINFO_TAG_SERIAL_OUT, \ + sizeof(char[3]) + sizeof(uint32_t) + sizeof(filename) + 2, \ + pin, \ + baudrate, \ + filename \ + } + + +#ifdef __cplusplus +}; +#endif + +#endif /* __SIMULAVR_INFO_H__ */ diff --git a/src/systemclock.cpp b/src/systemclock.cpp index ecd69b0..960f5ce 100644 --- a/src/systemclock.cpp +++ b/src/systemclock.cpp @@ -241,8 +241,18 @@ long SystemClock::Run(SystemClockOffset maxRunTime) { (SystemClock::Instance().GetCurrentTime() < maxRunTime)) { steps++; bool untilCoreStepFinished = false; - if (Step(untilCoreStepFinished)) - break; + // This breaks at least ATemga644, core->Step() in SystemClock::Step() + // occasionally returns 1 in normal program flow even without the use + // of Sleep, Break or whatever causes AvrDevice to return 1. + // + //if (Step(untilCoreStepFinished)) + // break; + // + // Breaking commit was d6e3b58358cce6aa35eaf5fcc62c1ff1e139bf06, + // "patch #7766 Make Step stoppable, print less when used as a library" + // + // Let's take the old code (this one line): + Step(untilCoreStepFinished); } return steps; diff --git a/src/traceval.cpp b/src/traceval.cpp index be2e136..133cfd0 100644 --- a/src/traceval.cpp +++ b/src/traceval.cpp @@ -202,7 +202,7 @@ void TraceValueRegister::_tvr_insertTraceValuesToSet(TraceSet &t) { void TraceValueRegister::RegisterTraceValue(TraceValue *t) { // check for duplicate names and the right prefix string p = t->name(); - unsigned int idx = _tvr_scopeprefix.length(); + size_t idx = _tvr_scopeprefix.length(); if((p.length() <= idx) || (p.substr(0, idx) != _tvr_scopeprefix)) avr_error("add TraceValue denied: wrong prefix: '%s', scope is '%s'", p.c_str(), _tvr_scopeprefix.c_str()); @@ -220,7 +220,7 @@ void TraceValueRegister::RegisterTraceValue(TraceValue *t) { } void TraceValueRegister::UnregisterTraceValue(TraceValue *t) { - int idx = _tvr_scopeprefix.length(); + size_t idx = _tvr_scopeprefix.length(); string n = t->name().substr(idx); for (valmap_t::iterator i = _tvr_values.begin(); i != _tvr_values.end(); i++) { if(n == *(i->first)) { @@ -247,8 +247,8 @@ TraceValue* TraceValueRegister::GetTraceValueByName(const std::string &name) { } TraceValueRegister* TraceValueRegister::FindScopeGroupByName(const std::string &name) { - int idx = name.find('.'); - if(idx > 0) { + size_t idx = name.find('.'); + if(idx != 0 && idx != string::npos) { TraceValueRegister *r = GetScopeGroupByName(name.substr(0, idx)); if(r == NULL) return NULL; @@ -259,8 +259,8 @@ TraceValueRegister* TraceValueRegister::FindScopeGroupByName(const std::string & } TraceValue* TraceValueRegister::FindTraceValueByName(const std::string &name) { - int idx = name.find('.'); - if(idx > 0) { + size_t idx = name.find('.'); + if(idx != 0 && idx != string::npos) { TraceValueRegister *r = GetScopeGroupByName(name.substr(0, idx)); if(r == NULL) return NULL; @@ -311,8 +311,8 @@ void TraceValueCoreRegister::RegisterTraceSetValue(TraceValue *t, const std::str TraceValue* TraceValueCoreRegister::GetTraceValueByName(const std::string &name) { TraceValue *res = TraceValueRegister::GetTraceValueByName(name); if(res == NULL) { - int idx = _tvr_numberindex(name); - if(idx != -1) { + size_t idx = _tvr_numberindex(name); + if(idx != string::npos) { // name + number found, check name and index value string n = name.substr(0, idx); int v = atoi(name.substr(idx).c_str()); @@ -352,11 +352,10 @@ void TraceValueCoreRegister::_tvr_insertTraceValuesToSet(TraceSet &t) { } } -int TraceValueCoreRegister::_tvr_numberindex(const std::string &str) { - int l = str.size(); - int i = l - 1; +size_t TraceValueCoreRegister::_tvr_numberindex(const std::string &str) { + size_t l = str.size(), i; // start from end of string to the beginning ... - for(; i >= 0; i--) { + for(i = l - 1; i >= 0; i--) { char c = str[i]; // check, if number sign if(c < '0' || c > '9') { @@ -365,7 +364,7 @@ int TraceValueCoreRegister::_tvr_numberindex(const std::string &str) { } } if(i == l) - i = -1; + i = string::npos; return i; } @@ -598,8 +597,8 @@ TraceValue* DumpManager::seekValueByName(const std::string &name) { return NULL; return devices[0]->FindTraceValueByName(name); } else { - int idx = name.find('.'); - if(idx <= 0) + size_t idx = name.find('.'); + if(idx == 0 || idx == string::npos) return NULL; for(vector::iterator i = devices.begin(); i != devices.end(); i++) { if((*i)->GetScopeName() == name.substr(0, idx)) { diff --git a/src/traceval.h b/src/traceval.h index d7356b9..81745bd 100644 --- a/src/traceval.h +++ b/src/traceval.h @@ -510,7 +510,7 @@ class TraceValueCoreRegister: public TraceValueRegister { setmap_t _tvr_valset; //!< the registered TraceValue's //! helper function to split up into name an number tail - int _tvr_numberindex(const std::string &str); + size_t _tvr_numberindex(const std::string &str); protected: //! Get the count of all TraceValues, that are registered here and descending diff --git a/src/ui/serialrx.cpp b/src/ui/serialrx.cpp index c24f6c1..1af84bb 100644 --- a/src/ui/serialrx.cpp +++ b/src/ui/serialrx.cpp @@ -26,6 +26,7 @@ #include "serialrx.h" #include "systemclock.h" #include "systemclocktypes.h" +#include using namespace std; @@ -150,6 +151,35 @@ long SerialRxBuffered::Size(){ // =========================================================================== +SerialRxFile::SerialRxFile(const char *filename) { + if (std::string(filename) == "-") + stream.std::ostream::rdbuf(std::cout.rdbuf()); + else + stream.open(filename); + // Enable automatic flushing, hitting ctrl-c to + // end a simulation doesn't call destructors. + stream << std::unitbuf; +} + +SerialRxFile::~SerialRxFile() { + if (stream.std::ostream::rdbuf() != std::cout.rdbuf()) + stream.close(); +} + +void SerialRxFile::CharReceived(unsigned char c){ + if (sendInHex) { + stream << "0x" << std::hex << (unsigned int)c << " "; + } else { + stream << c; + } +} + + +// =========================================================================== +// =========================================================================== +// =========================================================================== + + SerialRx::SerialRx(UserInterface *_ui, const char *_name, const char *baseWindow): ui(_ui), name(_name) { rx.RegisterCallback(this); diff --git a/src/ui/serialrx.h b/src/ui/serialrx.h index 0cb4253..f91ddb3 100644 --- a/src/ui/serialrx.h +++ b/src/ui/serialrx.h @@ -26,6 +26,7 @@ #ifndef SERIALRX_H_INCLUDED #define SERIALRX_H_INCLUDED +#include #include "systemclocktypes.h" #include "ui.h" #include "pinnotify.h" @@ -81,6 +82,18 @@ class SerialRxBuffered: public SerialRxBasic{ }; +/** Reads bits from device pins, reconstructs UART bytes and sends them to + * a file, a special file/com port or the console. */ +class SerialRxFile: public SerialRxBasic { + private: + std::ofstream stream; + protected: + virtual void CharReceived(unsigned char c); + public: + SerialRxFile(const char *filename); + ~SerialRxFile(); + }; + /** Reads bits from device pins, reconstructs UART bytes and sends them to UI. */ class SerialRx: public SerialRxBasic, public ExternalType{ protected: diff --git a/src/ui/serialtx.cpp b/src/ui/serialtx.cpp index a987578..4663526 100644 --- a/src/ui/serialtx.cpp +++ b/src/ui/serialtx.cpp @@ -107,7 +107,7 @@ void SerialTxBuffered::Send(unsigned char data) { inputBuffer.push_back(data); //write new char to input buffer - cerr << "TX: " << hex << data << " "; + // cerr << "TX: " << hex << data << endl; //if we not active, activate tx machine now if (txState==TX_DISABLED) { txState=TX_SEND_STARTBIT; @@ -115,6 +115,10 @@ void SerialTxBuffered::Send(unsigned char data) } } +bool SerialTxBuffered::Sending(void) { + return (txState != TX_DISABLED); +} + void SerialTxBuffered::SetBaudRate(SystemClockOffset baud){ baudrate = baud; } @@ -124,6 +128,60 @@ void SerialTxBuffered::SetHexInput(bool newValue){ } +// =========================================================================== +// =========================================================================== +// =========================================================================== + +#include +#include +#include +#include + +SerialTxFile::SerialTxFile(const char *filename) { + if (std::string(filename) == "-") + fd = fileno(stdin); + else + fd = open(filename, O_RDONLY, O_CREAT); + if (fd < 0) + avr_error("open input file failed"); + Reset(); + + SystemClock::Instance().Add(this); +} + +SerialTxFile::~SerialTxFile() { + if (fd != fileno(stdin)) + close(fd); +} + +int SerialTxFile::Step(bool &trueHwStep, + SystemClockOffset *timeToNextStepIn_ns) { + + if (Sending()) { + SerialTxBuffered::Step(trueHwStep, timeToNextStepIn_ns); + } + +#ifndef WIN32 + pollfd cinfd[1]; + cinfd[0].fd = fd; + cinfd[0].events = POLLIN; + if (poll(cinfd, 1, 0) > 0) { +#else + HANDLE h = GetStdHandle(STD_INPUT_HANDLE); + if (WaitForSingleObject(h, 0) == WAIT_OBJECT_0) { +#endif + unsigned char c; + if (read(fd, &c, 1)) { + Send((unsigned char)c); + } + } + + if ( ! Sending()) // may have changed with Step() or Send() + // Polling for new data every millisecond should be enough. If there's + // more than one byte waiting, they're accepted at every serial clock + // tick, which means, ten times faster than they can be sent. + *timeToNextStepIn_ns = (SystemClockOffset)1000000; +} // =========================================================================== // =========================================================================== diff --git a/src/ui/serialtx.h b/src/ui/serialtx.h index de40a74..4565892 100644 --- a/src/ui/serialtx.h +++ b/src/ui/serialtx.h @@ -61,10 +61,27 @@ class SerialTxBuffered: public SimulationMember { virtual int Step(bool &trueHwStep, SystemClockOffset *timeToNextStepIn_ns=0); /// Add byte from UI to be sent to device's UART. virtual void Send(unsigned char data); + bool Sending(void); virtual void SetBaudRate(SystemClockOffset baud); virtual Pin* GetPin(const char *name); }; +#include +#include + +/** Reads bytes from a file, a special file/com port or the console + * and sends them to the device's UART. */ +class SerialTxFile: public SerialTxBuffered { + private: + // Use a classic file descriptor to have better + // control over buffers (and their avoidance). + int fd; + public: + SerialTxFile(const char *filename); + ~SerialTxFile(); + virtual int Step(bool &trueHwStep, SystemClockOffset *timeToNextStepIn_ns=0); +}; + /** Buffers byte from UI to be sent to device's UART. */ class SerialTx: public SerialTxBuffered, public ExternalType {