From 452e2e5cd9d7eb91087238d903978a81907fa9de Mon Sep 17 00:00:00 2001 From: Phil Hord Date: Thu, 24 Oct 2013 10:27:43 -0400 Subject: [PATCH] Restore simulation build target. This code was accidentally removed long ago in a botched merge. This patch recovers it and makes it build again. I've done minimal testing and some necessary cleanup. It compiles and runs, but it probably still has a few dust bunnies here and there. I added registers and pin definitions to simulator.h and simulator/simulator.c which I needed to match my Gen7-based config. Other configs or non-AVR ports will need to define more or different registers. Some registers are 16-bits, some are 8-bit, and some are just constant values (enums). A more clever solution would read in the chip-specific header and produce saner definitions which covered all GPIOs. But this commit just takes the quick and easy path to support my own hardware. Most of this code originated in these commits: commit cbf41dd4adbdac8bd1ce0288b94266342fb62609 Author: Stephan Walter Date: Mon Oct 18 20:28:08 2010 +0200 document simulation commit 3028b297f31fa805db4c6f5077e1c3edb0ca5099 Author: Stephan Walter Date: Mon Oct 18 20:15:59 2010 +0200 Add simulation code: use "make sim" Additional tweaks: Revert va_args processing for AVR, but keep 'int' generalization for simulation. gcc wasn't lying. The sim really aborts without this. Remove delay(us) from simulator (obsolete). Improve the README.sim to demonstrate working pronterface connection to sim. Also fix the build instructions. Appease all stock configs. Stub out intercom and shush usb_serial when building simulator. Pretend to be all chip-types for config appeasement. Replace sim_timer with AVR-simulator timer: The original sim_timer and sim_clock provided direct replacements for timer/clock.c in the main code. But when the main code changed, simcode did not. The main clock.c was dropped and merged into timer.c. Also, the timer.c now has movement calculation code in it in some cases (ACCELERATION_TEMPORAL) and it would be wrong to teach the simulator to do the same thing. Instead, teach the simulator to emulate the AVR Timer1 functionality, reacting to values written to OCR1A and OCR1B timer comparison registers. Whenever OCR1A/B are changed, the sim_setTimer function needs to be called. It is called automatically after a timer event, so changes within the timer ISRs do not need to bother with this. A C++ class could make this requirement go away by noticing the assignment. On the other hand, a chip-agnostic timer.c would help make the main code more portable. The latter cleanup is probably better for us in the long run. --- Makefile-AVR | 32 +++++++ README.sim | 28 ++++++ analog.h | 2 + arduino.h | 7 +- crc.c | 29 ++++--- dda.c | 2 + dda_lookahead.c | 2 + dda_queue.c | 2 + gcode_process.c | 2 + heater.h | 1 + intercom.c | 2 + intercom.h | 2 + memory_barrier.h | 7 ++ mendel.c | 12 +++ pinio.h | 4 + serial.h | 3 + sersendf.c | 29 ++++--- sersendf.h | 3 + simulator.h | 165 ++++++++++++++++++++++++++++++++++++ simulator/analog_sim.c | 15 ++++ simulator/delay_sim.c | 12 +++ simulator/heater_sim.c | 27 ++++++ simulator/intercom_sim.c | 35 ++++++++ simulator/serial_sim.c | 102 ++++++++++++++++++++++ simulator/simulator.c | 144 +++++++++++++++++++++++++++++++ simulator/timer_ext.c | 178 +++++++++++++++++++++++++++++++++++++++ temp.c | 7 ++ timer.c | 14 +++ timer.h | 3 + usb_serial.c | 1 + 30 files changed, 847 insertions(+), 25 deletions(-) create mode 100644 README.sim create mode 100644 simulator.h create mode 100644 simulator/analog_sim.c create mode 100644 simulator/delay_sim.c create mode 100644 simulator/heater_sim.c create mode 100644 simulator/intercom_sim.c create mode 100644 simulator/serial_sim.c create mode 100644 simulator/simulator.c create mode 100644 simulator/timer_ext.c diff --git a/Makefile-AVR b/Makefile-AVR index 81d136d..8fea054 100644 --- a/Makefile-AVR +++ b/Makefile-AVR @@ -142,3 +142,35 @@ size: $(BUILDDIR)/$(PROGRAM).elf @$(OBJDUMP) -h $^ | perl -MPOSIX -ne '/.(text)\s+([0-9a-f]+)/ && do { $$a += eval "0x$$2" }; END { printf " FLASH : %5d bytes %3d%% %3d%% %3d%% %3d%%\n", $$a, ceil($$a * 100 / (14 * 1024)), ceil($$a * 100 / (30 * 1024)),ceil($$a * 100 / (62 * 1024)), ceil($$a * 100 / (126 * 1024)) }' @$(OBJDUMP) -h $^ | perl -MPOSIX -ne '/.(data|bss)\s+([0-9a-f]+)/ && do { $$a += eval "0x$$2" }; END { printf " RAM : %5d bytes %3d%% %3d%% %3d%% %3d%%\n", $$a, ceil($$a * 100 / (1 * 1024)), ceil($$a * 100 / (2 * 1024)),ceil($$a * 100 / (4 * 1024)), ceil($$a * 100 / (8 * 1024)) }' @$(OBJDUMP) -h $^ | perl -MPOSIX -ne '/.(eeprom)\s+([0-9a-f]+)/ && do { $$a += eval "0x$$2" }; END { printf " EEPROM: %5d bytes %3d%% %3d%% %3d%% %3d%%\n", $$a, ceil($$a * 100 / (1 * 1024)), ceil($$a * 100 / (2 * 1024)), ceil($$a * 100 / (2 * 1024)), ceil($$a * 100 / (4 * 1024)) }' + + +############################################################################## +# # +# Simulator # +# # +############################################################################## + +SIM_PATH = simulator +SIM_SOURCES := $(subst $(SIM_PATH)/,,$(wildcard $(SIM_PATH)/*.c)) +SIM_SOURCES += $(filter-out $(subst _sim.c,.c,$(SIM_SOURCES)),$(SOURCES)) +SIM_HEADERS = config.h serial.h dda.h timer.h clock.h temp.h sermsg.h +SIM_HEADERS += dda_queue.h debug.h sersendf.h heater.h analog.h delay.h +SIM_HEADERS += simulator.h + +SIM_OBJ = $(patsubst %.c,$(BUILDDIR)/%.sim.o,$(SIM_SOURCES)) +SIM_CFLAGS = -g -Wall -Wstrict-prototypes -Wno-format -Os $(DEFS) -std=gnu99 +SIM_CFLAGS += -funsigned-char -funsigned-bitfields -fshort-enums -I.. -I. + +# Satisfy all current config chip targets +SIM_CFLAGS += -DSIMULATOR -D__AVR_ATmega644P__ -D__AVR_ATmega1280__ +SIM_CFLAGS +=-D__AVR_ATmega2560__ -D__AVR_ATmega32U4__ -D__AVR_AT90USB1286__ + +$(BUILDDIR)/%.sim.o: %.c $(SIM_HEADERS) + @echo " CC $@" + @cc -DDEBUG -c $(SIM_CFLAGS) -o $@ $< + +sim: $(SIM_OBJ) + @echo " LINK $@" + @cc $(SIM_CFLAGS) -o $@ $^ + +vpath %.c $(SIM_PATH) diff --git a/README.sim b/README.sim new file mode 100644 index 0000000..8124560 --- /dev/null +++ b/README.sim @@ -0,0 +1,28 @@ +############################################################################## +# # +# Simulator of AVR-targetted code on a PC # +# # +############################################################################## + +To compile the simulation code, use + + $ make -f Makefile-AVR sim + +The simulator will open a serial port for communication. If you don't want to +connect a null modem cable, you can use 'socat' to connect two serial ports: + + socat -d -d PTY,raw,echo=0 PTY,raw,echo=0 + 2013/10/24 17:42:04 socat[3375] N PTY is /dev/pts/12 + 2013/10/24 17:42:04 socat[3375] N PTY is /dev/pts/13 + 2013/10/24 17:42:04 socat[3375] N starting data transfer loop with FDs [3,3] and [5,5] + +On the first line of output, socat will tell you the name of the virtual serial +port. Pass this as an argument to the simulator (in another terminal): + + $ ./sim /dev/pts/12 + +Give the other virtual serial port to an application like pronterface. + +Now you can send G-codes from the socat terminal. The simulation code will +print any data sent via the firmware's serial interface. Stepper positions +will be shown in green, counting a rising slope on the pin as one step. diff --git a/analog.h b/analog.h index d75a7b6..1c0083b 100644 --- a/analog.h +++ b/analog.h @@ -3,6 +3,7 @@ #include +#ifndef SIMULATOR #define REFERENCE_AREF 0 #define REFERENCE_AVCC 64 #if defined (__AVR_ATmega168__) || defined (__AVR_ATmega328__) || defined (__AVR_ATmega328P__) @@ -24,6 +25,7 @@ #warning in your config.h #error REFERENCE undefined #endif +#endif /* SIMULATOR */ void analog_init(void); diff --git a/arduino.h b/arduino.h index e261908..fe4e357 100644 --- a/arduino.h +++ b/arduino.h @@ -8,7 +8,9 @@ #ifndef _ARDUINO_H #define _ARDUINO_H +#ifndef SIMULATOR #include +#endif /* utility functions @@ -66,7 +68,9 @@ added as necessary or if I feel like it- not a comprehensive list! */ - +#ifdef SIMULATOR + #include "simulator.h" +#else #if defined (__AVR_ATmega168__) || defined (__AVR_ATmega328__) || \ defined (__AVR_ATmega328P__) #include "arduino_168_328p.h" @@ -93,6 +97,7 @@ #if defined (__AVR_ATmega32U4__) #include "arduino_32U4.h" #endif +#endif /* SIMULATOR */ #ifndef DIO0_PIN #error pins for this chip not defined in arduino.h! If you write an appropriate pin definition and have this firmware work on your chip, please tell us via the forum thread diff --git a/crc.c b/crc.c index b592b40..b3ecf8f 100644 --- a/crc.c +++ b/crc.c @@ -4,22 +4,23 @@ \brief crc16 routine */ +#ifndef SIMULATOR #include +#else -// avr-libc's _crc16_update is equivalent to the following: -// -// uint16_t _crc16_update(uint16_t crc, uint8_t a) { -// int i; -// crc ^= a; -// for (i = 0; i < 8; ++i) -// { -// if (crc & 1) -// crc = (crc >> 1) ^ 0xA001; -// else -// crc = (crc >> 1); -// } -// return crc; -// } +// Equivalent to avr-libc's _crc16_update. +uint16_t _crc16_update(uint16_t crc, uint8_t a) { + int i; + crc ^= a; + for (i = 0; i < 8; ++i) { + if (crc & 1) + crc = (crc >> 1) ^ 0xA001; + else + crc = (crc >> 1); + } + return crc; +} +#endif /** block-at-once CRC16 calculator \param *data data to find crc16 for diff --git a/dda.c b/dda.c index 4039785..beeb39b 100644 --- a/dda.c +++ b/dda.c @@ -7,7 +7,9 @@ #include #include #include +#ifndef SIMULATOR #include +#endif #include "dda_maths.h" #include "dda_lookahead.h" diff --git a/dda_lookahead.c b/dda_lookahead.c index 00283b8..4318c7c 100644 --- a/dda_lookahead.c +++ b/dda_lookahead.c @@ -11,7 +11,9 @@ #include #include #include +#ifndef SIMULATOR #include +#endif #include "dda_maths.h" #include "dda.h" diff --git a/dda_queue.c b/dda_queue.c index 0e58e0f..6e3c73b 100644 --- a/dda_queue.c +++ b/dda_queue.c @@ -5,7 +5,9 @@ */ #include +#ifndef SIMULATOR #include +#endif #include "config.h" #include "timer.h" diff --git a/gcode_process.c b/gcode_process.c index 0d4e556..fccd366 100644 --- a/gcode_process.c +++ b/gcode_process.c @@ -5,7 +5,9 @@ */ #include +#ifndef SIMULATOR #include +#endif #include "gcode_parse.h" diff --git a/heater.h b/heater.h index 5b9c181..cf60645 100644 --- a/heater.h +++ b/heater.h @@ -3,6 +3,7 @@ #include "config.h" #include +#include "simulator.h" #include "temp.h" #undef DEFINE_HEATER diff --git a/intercom.c b/intercom.c index 4235750..be4494f 100644 --- a/intercom.c +++ b/intercom.c @@ -4,8 +4,10 @@ \brief motherboard <-> extruder board protocol */ +#ifndef SIMULATOR #include #include +#endif #include "memory_barrier.h" #include "config.h" diff --git a/intercom.h b/intercom.h index d4dffc3..6dab15a 100644 --- a/intercom.h +++ b/intercom.h @@ -4,6 +4,7 @@ #include #include "config.h" +#ifndef enable_transmit #ifdef MOTHERBOARD #define enable_transmit() do { WRITE(TX_ENABLE_PIN,1); UCSR1B &=~MASK(RXEN1); } while(0) #define disable_transmit() do { WRITE(TX_ENABLE_PIN,0); UCSR1B &= ~(MASK(TXCIE1) | MASK(UDRIE1)); UCSR1B |= MASK(RXEN1); } while(0) @@ -11,6 +12,7 @@ #define enable_transmit() do { WRITE(TX_ENABLE_PIN,1); UCSR0B &= ~MASK(RXEN0); } while(0) #define disable_transmit() do { WRITE(TX_ENABLE_PIN,0); UCSR0B &= ~(MASK(TXCIE0) | MASK(UDRIE0)); UCSR0B |= MASK(RXEN0); } while(0) #endif +#endif /// list of error codes, not many so far... enum { diff --git a/memory_barrier.h b/memory_barrier.h index 8aab3f6..fa7fc4f 100644 --- a/memory_barrier.h +++ b/memory_barrier.h @@ -1,6 +1,12 @@ #ifndef _MEMORY_BARRIER_H_ #define _MEMORY_BARRIER_H_ +#ifdef SIMULATOR + #define CLI_SEI_BUG_MEMORY_BARRIER() + #define MEMORY_BARRIER() + #define ATOMIC_START + #define ATOMIC_END +#else #include #include @@ -38,3 +44,4 @@ } #endif +#endif diff --git a/mendel.c b/mendel.c index 67cccf0..4fc1d41 100644 --- a/mendel.c +++ b/mendel.c @@ -24,8 +24,10 @@ ctrl+d \endcode */ +#ifndef SIMULATOR #include #include +#endif #include "config.h" #include "fuses.h" @@ -46,6 +48,7 @@ #include "arduino.h" #include "clock.h" #include "intercom.h" +#include "simulator.h" /// initialise all I/O - set pins as input or output, turn off unused subsystems, etc void io_init(void) { @@ -228,8 +231,17 @@ void init(void) { /// this is where it all starts, and ends /// /// just run init(), then run an endless loop where we pass characters from the serial RX buffer to gcode_parse_char() and check the clocks +#ifdef SIMULATOR +int g_argc; +char** g_argv; +int main (int argc, char** argv) +{ + g_argc = argc; + g_argv = argv; +#else int main (void) { +#endif init(); // main loop diff --git a/pinio.h b/pinio.h index ef45106..2bcbef4 100644 --- a/pinio.h +++ b/pinio.h @@ -7,6 +7,10 @@ #include "config.h" +#ifdef SIMULATOR + #include "simulator.h" +#endif + /* Power */ diff --git a/serial.h b/serial.h index b2e25ff..87ef694 100644 --- a/serial.h +++ b/serial.h @@ -3,8 +3,11 @@ #include "config.h" #include +#ifndef SIMULATOR #include #include +#endif +#include "simulator.h" #ifdef USB_SERIAL #include "usb_serial.h" diff --git a/sersendf.c b/sersendf.c index 10c7650..010cb53 100644 --- a/sersendf.c +++ b/sersendf.c @@ -5,7 +5,9 @@ */ #include +#ifndef SIMULATOR #include +#endif #include "serial.h" #include "sermsg.h" @@ -94,6 +96,13 @@ \code sersendf_P(PSTR("X:%ld Y:%ld temp:%u.%d flags:%sx Q%su/%su%c\n"), target.X, target.Y, current_temp >> 2, (current_temp & 3) * 25, dda.allflags, mb_head, mb_tail, (queue_full()?'F':(queue_empty()?'E':' '))) \endcode */ + +#ifdef SIMULATOR + #define GET_ARG(T) (va_arg(args, int)) +#else + #define GET_ARG(T) (va_arg(args, T)) +#endif + void sersendf_P(PGM_P format, ...) { va_list args; va_start(args, format); @@ -111,36 +120,36 @@ void sersendf_P(PGM_P format, ...) { break; case 'u': if (j == 4) - serwrite_uint32(va_arg(args, uint32_t)); + serwrite_uint32(GET_ARG(uint32_t)); else - serwrite_uint16(va_arg(args, uint16_t)); + serwrite_uint16(GET_ARG(uint16_t)); j = 0; break; case 'd': if (j == 4) - serwrite_int32(va_arg(args, int32_t)); + serwrite_int32(GET_ARG(uint32_t)); else - serwrite_int16(va_arg(args, int16_t)); + serwrite_int16(GET_ARG(uint16_t)); j = 0; break; case 'c': - serial_writechar(va_arg(args, uint16_t)); + serial_writechar(GET_ARG(uint16_t)); j = 0; break; case 'x': serial_writestr_P(PSTR("0x")); if (j == 4) - serwrite_hex32(va_arg(args, uint32_t)); + serwrite_hex32(GET_ARG(uint32_t)); else if (j == 1) - serwrite_hex8(va_arg(args, uint16_t)); + serwrite_hex8(GET_ARG(uint16_t)); else - serwrite_hex16(va_arg(args, uint16_t)); + serwrite_hex16(GET_ARG(uint16_t)); j = 0; break; /* case 'p': - serwrite_hex16(va_arg(args, uint16_t));*/ + serwrite_hex16(GET_ARG(uint16_t));*/ case 'q': - serwrite_int32_vf(va_arg(args, int32_t), 3); + serwrite_int32_vf(GET_ARG(uint32_t), 3); j = 0; break; default: diff --git a/sersendf.h b/sersendf.h index 22aa592..b2b3007 100644 --- a/sersendf.h +++ b/sersendf.h @@ -1,7 +1,10 @@ #ifndef _SERSENDF_H #define _SERSENDF_H +#ifndef SIMULATOR #include +#endif +#include "simulator.h" void sersendf(char *format, ...) __attribute__ ((format (printf, 1, 2))); void sersendf_P(PGM_P format, ...) __attribute__ ((format (printf, 1, 2))); diff --git a/simulator.h b/simulator.h new file mode 100644 index 0000000..cd93801 --- /dev/null +++ b/simulator.h @@ -0,0 +1,165 @@ +#ifdef SIMULATOR + +#undef X_STEP_PIN +#undef X_DIR_PIN +#undef X_MIN_PIN +#undef X_ENABLE_PIN +#undef Y_STEP_PIN +#undef Y_DIR_PIN +#undef Y_MIN_PIN +#undef Y_ENABLE_PIN +#undef Z_STEP_PIN +#undef Z_DIR_PIN +#undef Z_MIN_PIN +#undef Z_ENABLE_PIN +#undef E_STEP_PIN +#undef E_DIR_PIN +#undef E_ENABLE_PIN +#undef STEPPER_ENABLE_PIN + +#undef PS_MOSFET_PIN +#undef PS_ON_PIN +#undef RX_ENABLE_PIN +#undef TX_ENABLE_PIN +#undef X_MAX_PIN +#undef Y_MAX_PIN +#undef Z_MAX_PIN + +#undef READ +#undef WRITE +#undef TOGGLE +#undef SET_INPUT +#undef SET_OUTPUT +#undef GET_INPUT +#undef GET_OUTPUT + +// Compiler appeasement +#undef disable_transmit +#undef enable_transmit +#define disable_transmit() +#define enable_transmit() +#undef USB_SERIAL + +#ifndef _SIMULATOR_H +#define _SIMULATOR_H + +#include +#include + +#define PROGMEM +#define PGM_P const char * +#define PSTR(x) (x) +#define pgm_read_byte(x) (*((uint8_t *)(x))) +#define pgm_read_word(x) (*((uint16_t *)(x))) + +#define MASK(PIN) (1 << PIN) +#define ACD 7 +#define OCIE1A 1 + +// TODO: Implement simulated EEMEM persistence +#define EEMEM +#define eeprom_read_dword(ptr32) (*(ptr32)) +#define eeprom_read_word(ptr16) (*(ptr16)) +#define eeprom_write_dword(ptr32, i32) (*(ptr32)=i32) +#define eeprom_write_word(ptr16, i16) (*(ptr16)=i16) + +typedef enum { + // Define pins used + X_STEP_PIN, + X_DIR_PIN, + X_MIN_PIN, + X_ENABLE_PIN, + Y_STEP_PIN, + Y_DIR_PIN, + Y_MIN_PIN, + Y_ENABLE_PIN, + Z_STEP_PIN, + Z_DIR_PIN, + Z_MIN_PIN, + Z_ENABLE_PIN, + E_STEP_PIN, + E_DIR_PIN, + E_ENABLE_PIN, + + STEPPER_ENABLE_PIN, + + SCK, + MOSI, + MISO, + SS, + + RX_ENABLE_PIN, + TX_ENABLE_PIN, +/* + * Not used in the simulator. Add them to this list to enable them if needed. + PS_MOSFET_PIN, + PS_ON_PIN, + X_MAX_PIN, + Y_MAX_PIN, + Z_MAX_PIN, +*/ + PIN_NB /* End of PINS marker; Put all new pins before this one */ +} pin_t; + +// AVR stand-ins +typedef enum { + WGM00 = 0, + WGM01, + WGM20, + WGM21, + CS00 = 0, + CS02, + CS20, + CS21, + CS22, +} masks_t; + +#undef TEMP_PIN_CHANNEL +#define TEMP_PIN_CHANNEL 0 + +extern uint8_t ACSR; +extern uint8_t TIMSK1; +extern volatile bool sim_interrupts; + +bool READ(pin_t pin); +void WRITE(pin_t pin, bool on); +void SET_OUTPUT(pin_t pin); +void SET_INPUT(pin_t pin); + +// Simulate AVR interrupts. +#define ISR(fn) void fn (void) +void TIMER1_COMPA_vect(void); +void TIMER1_COMPB_vect(void); + +// Compare-timers for next interrupts. +extern uint16_t OCR1A, OCR1B; + +// Interrupt control registers. +extern uint16_t TCCR1A, TCCR1B; +enum { CS10 = 1 , OCIE1B = 3 }; + +#define TCNT1 (sim_tick_counter()) +void sei(void); + +#ifdef USE_WATCHDOG +#define wd_init() +#define wd_reset() +#endif + +void sim_start(int argc, char ** argv); +void sim_info(const char fmt[], ...); +void sim_debug(const char fmt[], ...); +void sim_error(const char msg[]); +void sim_assert(bool cond, const char msg[]); +void sim_timer_init(void); +void sim_timer_stop(void); +void sim_setTimer(void); +uint16_t sim_tick_counter(void); + +inline void cli(void); +inline void cli() { } + +#define DIO0_PIN "proof of life" + +#endif /* _SIMULATOR_H */ +#endif /* SIMULATOR */ diff --git a/simulator/analog_sim.c b/simulator/analog_sim.c new file mode 100644 index 0000000..7cef911 --- /dev/null +++ b/simulator/analog_sim.c @@ -0,0 +1,15 @@ +#include "analog.h" +#include "simulator.h" + +static bool analog_initialised = false; + +void analog_init(void) { + sim_info("analog_init: not implemented in simulator"); + analog_initialised = true; +} + +uint16_t analog_read(uint8_t channel) { + sim_assert(analog_initialised, "analog_init() was not called before analog_read()"); + sim_assert(sim_interrupts, "interrupts disabled"); + return 0; +} diff --git a/simulator/delay_sim.c b/simulator/delay_sim.c new file mode 100644 index 0000000..499b73c --- /dev/null +++ b/simulator/delay_sim.c @@ -0,0 +1,12 @@ +#include + +#include "delay.h" +#include "simulator.h" + +void delay_ms(uint32_t ms) { + usleep(ms * 1000); +} + +void delay_us(uint16_t us) { + usleep(us); +} diff --git a/simulator/heater_sim.c b/simulator/heater_sim.c new file mode 100644 index 0000000..fb69ce7 --- /dev/null +++ b/simulator/heater_sim.c @@ -0,0 +1,27 @@ + +#include "heater.h" + +void heater_init(void) { +} + +void heater_set(heater_t index, uint8_t value) { + sim_info("heater not implemented in simulator."); +} + +void heater_tick(heater_t h, temp_type_t type, uint16_t current_temp, uint16_t target_temp) { +} + +uint8_t heaters_all_zero(void) { + return 1; +} + +#ifdef EECONFIG +void pid_set_p(heater_t index, int32_t p) {} +void pid_set_i(heater_t index, int32_t i) {} +void pid_set_d(heater_t index, int32_t d) {} +void pid_set_i_limit(heater_t index, int32_t i_limit) {} +void heater_save_settings(void) {} +#endif /* EECONFIG */ + +void heater_print(uint16_t i) { +} diff --git a/simulator/intercom_sim.c b/simulator/intercom_sim.c new file mode 100644 index 0000000..7ae2c0a --- /dev/null +++ b/simulator/intercom_sim.c @@ -0,0 +1,35 @@ +#include "intercom.h" + +/* Intercom stubs here to appease compiler for build testing. + * Functionally moot. + */ + +void intercom_init(void) { + sim_error("simulated intercom is not supported"); +} + +void send_temperature(uint8_t index, uint16_t temperature) { +} + +uint16_t read_temperature(uint8_t index) { + return 0; +} + +#ifdef MOTHERBOARD +void set_dio(uint8_t index, uint8_t value) { +} +#else +uint8_t get_dio(uint8_t index) { + return 0; +} +#endif + +void set_err(uint8_t err) { +} + +uint8_t get_err() { + return 0; +} + +void start_send(void) { +} diff --git a/simulator/serial_sim.c b/simulator/serial_sim.c new file mode 100644 index 0000000..2285ffc --- /dev/null +++ b/simulator/serial_sim.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include +#include + +#include "serial.h" +#include "simulator.h" + +static int serial_fd; +static bool serial_initialised = false; + +extern int g_argc; +extern char ** g_argv; + +void serial_init(void) { + struct termios options; + + int argc = g_argc; + char **argv = g_argv; + + sim_assert(argc >= 2, "please specify a serial port device name"); + + sim_info("opening serial port %s", argv[1]); + serial_fd = open(argv[1], O_RDWR | O_NOCTTY | O_NDELAY); + sim_assert(serial_fd != -1, "couldn't open serial port"); + sim_assert(isatty(serial_fd), "not a TTY"); + + sim_info("configuring port"); + // Get the current options for the port + if (tcgetattr(serial_fd, &options) != 0) { + sim_error("tcgetattr"); + } + + // Set the baud rates + cfsetispeed(&options, B115200); + cfsetospeed(&options, B115200); + + // Enable the receiver and set local mode + options.c_cflag |= (CLOCAL | CREAD); + + options.c_cflag &= ~PARENB; + options.c_cflag &= ~CSTOPB; + options.c_cflag &= ~CSIZE; + options.c_cflag |= CS8; + + // Set the new options for the port + if (tcsetattr(serial_fd, TCSANOW, &options) != 0) { + sim_error("tcsetattr"); + } + + // flush tx and rx buffers + tcflush(serial_fd, TCIOFLUSH); + + serial_initialised = true; +} + +// return number of characters in the receive buffer +uint8_t serial_rxchars(void) { + int rx_chars_nb; + + sim_assert(serial_initialised, "serial interface not initialised"); + ioctl(serial_fd, FIONREAD, &rx_chars_nb); + return rx_chars_nb; +} + +// read one character +uint8_t serial_popchar(void) { + uint8_t c; + ssize_t count; + + sim_assert(serial_initialised, "serial interface not initialised"); + sim_assert(serial_rxchars() > 0, "no chars to read"); + count = read(serial_fd, &c, 1); + sim_assert(count == 1, "no character in serial RX buffer"); + return c; +} + +// send one character +void serial_writechar(uint8_t data) { + ssize_t count; + sim_assert(serial_initialised, "serial interface not initialised"); + putchar(data); + count = write(serial_fd, &data, 1); + sim_assert(count == 1, "could not write to serial port"); +} + +// read/write many characters +void serial_writestr(uint8_t *data) { + ssize_t count; + const char *str = (char *)data; + sim_assert(serial_initialised, "serial interface not initialised"); + puts(str); + count = write(serial_fd, str, strlen(str)); + sim_assert(count == strlen(str), "could not write to serial port"); +} + +// write from flash +void serial_writestr_P(PGM_P data) { + serial_writestr((uint8_t *)data); +} diff --git a/simulator/simulator.c b/simulator/simulator.c new file mode 100644 index 0000000..971b172 --- /dev/null +++ b/simulator/simulator.c @@ -0,0 +1,144 @@ +#include +#include +#include + +#include "simulator.h" + +uint8_t ACSR; +uint8_t TIMSK1; +uint16_t + TCCR0A, + TCCR0B, + TCCR1A, + TCCR1B, + TCCR2A, + TCCR2B, + OCR0B, + OCR1A, + OCR1B, + OCR2A, + OCR2B, + TIMSK0, + TIMSK2; + +volatile uint8_t + DIO1_WPORT, + DIO2_WPORT, + DIO3_WPORT, + DIO4_WPORT; + + +/* -- debugging ------------------------------------------------------------ */ + +static void fgreen(void) { fputs("\033[0;32m" , stdout); } +static void fred(void) { fputs("\033[0;31m" , stdout); } +static void fbreset(void) { fputs("\033[m" , stdout); } + + +void sim_info(const char fmt[], ...) { + va_list ap; + fgreen(); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + fputc('\n', stdout); + fbreset(); +} + +void sim_debug(const char fmt[], ...) { +#ifdef SIM_DEBUG + va_list ap; + fcyan(); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + fputc('\n', stdout); + fbreset(); +#endif +} + +void sim_error(const char msg[]) { + fred(); + printf("ERROR: %s\n", msg); + fputc('\n', stdout); + fbreset(); + exit(-1); +} + +void sim_assert(bool cond, const char msg[]) { + if (!cond) { + sim_error(msg); + } +} + + +/* -- interrupts ----------------------------------------------------------- */ + +volatile bool sim_interrupts = false; + +void sei(void) { + sim_interrupts = true; +} + + +/* -- PIN I/O ------------------------------------------------------------ */ + +#define out true +#define in false + +static int x = 0, y = 0, z = 0, e = 0; + +static bool direction[PIN_NB]; +static bool state[PIN_NB]; + +static void print_pos(void) { + printf("print_pos: %d, %d, %d, %d\n", x, y, z, e); + sim_info("x:%5d y:%5d z:%5d e:%5d", x, y, z, e); +} + +bool READ(pin_t pin) { + sim_assert(pin < PIN_NB, "READ: Pin number out of range"); + // Add any necessary reactive pin-handlers here. + return state[pin]; +} + +void WRITE(pin_t pin, bool s) { + bool old_state = state[pin]; + sim_assert(pin < PIN_NB, "WRITE: Pin number out of range"); + + if (direction[pin] == out) { + state[pin] = s; + } + if (s && !old_state) { /* rising edge */ + switch (pin) { + case X_STEP_PIN: + x += state[X_DIR_PIN] ? 1 : -1; + print_pos(); + break; + case Y_STEP_PIN: + y += state[Y_DIR_PIN] ? 1 : -1; + print_pos(); + break; + case Z_STEP_PIN: + z += state[Z_DIR_PIN] ? 1 : -1; + print_pos(); + break; + case E_STEP_PIN: + e += state[E_DIR_PIN] ? 1 : -1; + print_pos(); + break; + default: + break; + } + } +} + +void SET_OUTPUT(pin_t pin) { + sim_assert(pin < PIN_NB, "Pin number out of range"); + direction[pin] = out; +} + +void SET_INPUT(pin_t pin) { + sim_assert(pin < PIN_NB, "Pin number out of range"); + direction[pin] = in; +} diff --git a/simulator/timer_ext.c b/simulator/timer_ext.c new file mode 100644 index 0000000..ee11ef8 --- /dev/null +++ b/simulator/timer_ext.c @@ -0,0 +1,178 @@ +#include +#include +#include + +#include "dda_queue.h" +#include "timer.h" +#include "simulator.h" +#include +#include // printf +#include // usleep + +#define TIME_SLOW_FACTOR 1 + +static void schedule_timer(uint32_t useconds); + +static bool timer_initialised = false; + +enum { + // One bit per timer + TIMER_OCR1A = 0x1 , + TIMER_OCR1B = 0x2 , +}; + +static volatile uint8_t timer_reason; // Who scheduled this timer + +static uint64_t now_us(void) { + struct timespec tv; + + int n = clock_gettime(CLOCK_MONOTONIC, &tv); + sim_assert(n == 0, "clock_gettime failed"); + + // Convert tv to nanoseconds. + uint64_t nsec = tv.tv_sec; + nsec *= 1000 * 1000 * 1000; + nsec += tv.tv_nsec; + + // nanoseconds to microseconds + return nsec / 1000; +} + +uint16_t sim_tick_counter(void) { + // microseconds to 16-bit clock ticks + return (now_us() / TIME_SLOW_FACTOR) US; +} + +extern uint8_t clock_counter_10ms, clock_counter_250ms, clock_counter_1s; +static uint64_t begin; +static uint64_t then; +void sim_timer_init(void) { + then = begin = now_us(); + sim_info("timer_init"); + timer_initialised = true; + + sim_setTimer(); +} + +void sim_timer_stop(void) { + sim_info("timer_stop"); + timer_reason = 0; // Cancel pending timer; +} + +static void timer1_isr(int cause, siginfo_t *HowCome, void *ucontext) { + if ( ! sim_interrupts) { + // Interrupts disabled. Schedule another callback in 10us. + schedule_timer(10); + return; + } + + sim_interrupts = false; + + #ifdef SIM_DEBUG + uint64_t now = now_us(); + static unsigned int cc_1s = 0, prev_1s = 0; + + if ( ! clock_counter_1s && prev_1s) ++cc_1s; + prev_1s = clock_counter_1s; + + //uint16_t now = sim_tick_counter(); + uint64_t real = now-begin; + uint64_t avr = cc_1s * 4 + clock_counter_1s; + avr *= 250; + avr += clock_counter_250ms * 10; + avr += clock_counter_10ms; + avr *= 1000 ; + printf("test: Real: %us %u.%03ums AVR: %us %u.%03ums Real/AVR: %u\n", + real / 1000000 , (real % 1000000)/1000 , real % 1000 , + avr / 1000000 , (avr % 1000000)/1000 , avr % 1000 , + real / (avr?avr:1) ); + printf("test: 10ms=%u 250ms=%u 1s=%u total=%lu actual=%lu\n", + clock_counter_10ms, clock_counter_250ms, clock_counter_1s, + now - begin , now - then); + //printf(" timer1_isr tick_time=%04X now=%04X delta=%u total=%u\n", + // TICK_TIME , now, now_us() - then, (now_us() - begin)/1000000 ) ; + then = now; + #endif + + if (timer_reason & TIMER_OCR1A) TIMER1_COMPA_vect(); + if (timer_reason & TIMER_OCR1B) TIMER1_COMPB_vect(); + timer_reason = 0; + + sim_interrupts = true; + + // Setup next timer + sim_setTimer(); +} + +// TODO: Remove 'delay' value and use AVR regs instead +void sim_setTimer() { + // Set callbacks for COMPA and COMPB timers + uint32_t nextA = 0, nextB = 0; + uint16_t now = sim_tick_counter(); + + sim_assert(timer_initialised, "timer not initialised"); + + //-- Calculate time in clock ticks until next timer events + if (TIMSK1 & MASK(OCIE1A)) { + sim_debug("Timer1 Interrupt A: Enabled"); + nextA = OCR1A - now; + // 0 = No timer; 1-0x10000 = time until next occurrence + if ( ! nextA) nextA = 0x10000; + } + if (TIMSK1 & MASK(OCIE1B)) { + sim_debug("Timer1 Interrupt B: Enabled"); + nextB = OCR1B - now; + // 0 = No timer; 1-0x10000 = time until next occurrence + if ( ! nextB) nextB = 0x10000; + } + + //-- Find the nearest event + uint32_t next = nextA; + if (nextB && ( ! next || (nextB < next))) { + next = nextB; + timer_reason = TIMER_OCR1B; + } + + //-- Flag the reasons for the next event + timer_reason = 0; + if (next == nextA) timer_reason |= TIMER_OCR1A; + if (next == nextB) timer_reason |= TIMER_OCR1B; + + // FIXME: We will miss events if they occur like this: + // nextA = 0x1000 + // nextB = 0x1001 + // timer_reason = TIMER_OCR1A + // ISR is triggered and finishes at 0x1002 + // => Next trigger for B will not occur until NEXT 0x1001 comes around + // Need some way to notice a missed trigger. + // Maybe store 32-bit tick value for next trigger time for each timer. + + //-- Convert ticks to microseconds + long actual = ((unsigned long)next) * TIME_SLOW_FACTOR / (1 US); + if (next) { + sim_debug("OCR1A:%04X OCR1B:%04X now=%04X", OCR1A, OCR1B, now ); + sim_debug(" next=%u real=%u", next, actual); + } + + //-- Schedule the event + schedule_timer(actual); +} + +// Schedule Timer1 callback useconds from now. +// Zero cancels any pending timer. +static void schedule_timer(uint32_t useconds) { + struct itimerval itimer; + struct sigaction sa; + + sa.sa_sigaction = timer1_isr; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + if (sigaction(SIGALRM, &sa, 0)) { + sim_error("sigaction"); + } + itimer.it_interval.tv_sec = 0; + itimer.it_interval.tv_usec = 0; // If signal occurs , trigger again in 10us + itimer.it_value.tv_sec = useconds / 1000000; + itimer.it_value.tv_usec = useconds % 1000000; + setitimer(ITIMER_REAL, &itimer, NULL); +} diff --git a/temp.c b/temp.c index 3554201..82eb960 100644 --- a/temp.c +++ b/temp.c @@ -7,8 +7,11 @@ */ #include +#ifndef SIMULATOR #include #include +#endif +#include "simulator.h" #include "arduino.h" #include "debug.h" @@ -47,7 +50,11 @@ typedef struct { #undef DEFINE_TEMP_SENSOR /// help build list of sensors from entries in config.h +#ifndef SIMULATOR #define DEFINE_TEMP_SENSOR(name, type, pin, additional) { (type), (pin ## _ADC), (HEATER_ ## name), (additional) }, +#else +#define DEFINE_TEMP_SENSOR(name, type, pin, additional) { (type), (TEMP_SENSOR_ ## name), (HEATER_ ## name), (additional) }, +#endif static const temp_sensor_definition_t temp_sensors[NUM_TEMP_SENSORS] = { #include "config.h" diff --git a/timer.c b/timer.c index 052fc01..4daa690 100644 --- a/timer.c +++ b/timer.c @@ -10,7 +10,9 @@ Teacup has tried numerous timer management methods, and this is the best so far. */ +#ifndef SIMULATOR #include +#endif #include "memory_barrier.h" #include "arduino.h" @@ -119,6 +121,10 @@ void timer_init() OCR1B = TICK_TIME & 0xFFFF; // enable interrupt TIMSK1 = MASK(OCIE1B); + #ifdef SIMULATOR + // Tell simulator + sim_timer_init(); + #endif } #ifdef MOTHERBOARD @@ -201,11 +207,19 @@ void setTimer(uint32_t delay) // timer1a interrupt to the far side of the return, protecting the // stack from recursively clobbering memory. TIMSK1 |= MASK(OCIE1A); + #ifdef SIMULATOR + // Tell simulator + sim_setTimer(); + #endif } /// stop timers - emergency stop void timer_stop() { // disable all interrupts TIMSK1 = 0; + #ifdef SIMULATOR + // Tell simulator + sim_timer_stop(); + #endif } #endif /* ifdef MOTHERBOARD */ diff --git a/timer.h b/timer.h index b28f9b0..9bfd487 100644 --- a/timer.h +++ b/timer.h @@ -2,7 +2,10 @@ #define _TIMER_H #include +#ifndef SIMULATOR #include +#endif +#include "simulator.h" // time-related constants #define US * (F_CPU / 1000000) diff --git a/usb_serial.c b/usb_serial.c index 66f76c4..53ccef8 100644 --- a/usb_serial.c +++ b/usb_serial.c @@ -32,6 +32,7 @@ // 2012-10-17: Import to Teacup firmware #include "config.h" +#include "simulator.h" /* protect this file from Arduino IDE */ #ifdef USB_SERIAL