diff --git a/clock.c b/clock.c index bcb4853..3935420 100644 --- a/clock.c +++ b/clock.c @@ -80,6 +80,9 @@ void clock() { ifclock(clock_flag_10ms) { clock_10ms(); } +#ifdef SIMULATOR + sim_time_warp(); +#endif } diff --git a/simulator.h b/simulator.h index b241523..277d5cc 100644 --- a/simulator.h +++ b/simulator.h @@ -156,11 +156,18 @@ void sim_assert(bool cond, const char msg[]); void sim_gcode_ch(char ch); void sim_gcode(const char msg[]); -void sim_timer_init(void); +/** + * Initialize simulator timer and set time scale. + * + * @param scale time slow-down factor; 0=warp-speed, 1=real-time, 2-half-time, etc. + */ +void sim_timer_init(uint8_t scale); + void sim_timer_stop(void); void sim_setTimer(void); uint16_t sim_tick_counter(void); uint64_t sim_runtime_ns(void); ///< Simulated run-time in nanoseconds +void sim_time_warp(void); ///< skip ahead to next timer interrupt, when time_scale==0 #define DIO0_PIN "proof of life" diff --git a/simulator/data_recorder.c b/simulator/data_recorder.c index 461fd7a..0daeafd 100644 --- a/simulator/data_recorder.c +++ b/simulator/data_recorder.c @@ -74,10 +74,7 @@ void record_pin(int pin, int32_t state, uint64_t t) { sim_assert(pin < MAX_PINS, "pin number invalid"); - // Record previous state when new state value appears - if ( t != prev_t ) { - emit_log_data(); - } + emit_log_data(); prev_t = t; values[pin] = state; } @@ -87,3 +84,9 @@ void record_comment(const char * msg) { fprintf(file, "# %s\n", msg); fflush(file); } + +void record_raw(const char * msg) { + if (!file) return; + fprintf(file, "%s", msg); + fflush(file); +} diff --git a/simulator/data_recorder.h b/simulator/data_recorder.h index e2cccff..7f36d0b 100644 --- a/simulator/data_recorder.h +++ b/simulator/data_recorder.h @@ -9,6 +9,7 @@ void recorder_init(const char* filename); void record_pin(int pin, int32_t state, uint64_t time); void add_trace_var(const char* name, int pin); void record_comment(const char * msg); +void record_raw(const char * msg); void record_comment_stream(char ch); #endif /* DATA_RECORDER_H_ */ diff --git a/simulator/simulator.c b/simulator/simulator.c index e694918..2388708 100644 --- a/simulator/simulator.c +++ b/simulator/simulator.c @@ -4,6 +4,9 @@ #include #include +// If no time scale specified, use 1/10th real-time for simulator +#define DEFAULT_TIME_SCALE 10 + #include "simulator.h" #include "data_recorder.h" @@ -40,21 +43,25 @@ int verbose = 1; ///< 0=quiet, 1=normal, 2=noisy, 3=debug, etc. int trace_gcode = 0; ///< show gcode on the console int trace_pos = 0; ///< show print head position on the console -const char * shortopts = "qvo::"; +const char * shortopts = "qgpvt:o::"; struct option opts[] = { { "quiet", no_argument, &verbose , 0 }, { "verbose", no_argument, NULL, 'v' }, { "gcode", no_argument, NULL, 'g' }, { "pos", no_argument, NULL, 'p' }, + { "time-scale", required_argument, NULL, 't' }, { "tracefile", optional_argument, NULL, 'o' } }; static void usage(const char *name) { - printf("Usage: %s [-q || --quiet] [-v [-v] || --verbose[=n]] [-g || --gcode] [-p || --pos] [-o[outfile] || --tracefile[=outfile]] gcode-file || UART-tty\n", name); + printf("Usage: %s [options] [gcode_file || uart_device_name]\n", name); printf("\n\n"); - printf(" -g || --gcode show gcode on console as it is processed\n"); - printf(" -p || --pos show head position on console\n"); - printf(" -o || --tracefile write simulator pin trace to 'outfile' (default filename=datalog.out)\n"); + printf(" -q || --quiet show less output\n"); + printf(" -v || --verbose show more output\n"); + printf(" -g || --gcode show gcode on console as it is processed\n"); + printf(" -p || --pos show head position on console\n"); + printf(" -t || --time-scale=n set time-scale; 0=warp-speed, 1=real-time, 2=half-time, etc.\n"); + printf(" -o || --tracefile[=filename] write simulator pin trace to 'outfile' (default filename=datalog.out)\n"); printf("\n"); exit(1); } @@ -64,8 +71,9 @@ char** g_argv; void sim_start(int argc, char** argv) { int c; int index; + uint8_t time_scale = DEFAULT_TIME_SCALE; - while ((c = getopt_long (argc, argv, "qgpvo::", opts, &index)) != -1) + while ((c = getopt_long (argc, argv, shortopts, opts, &index)) != -1) switch (c) { case 'q': verbose = 0; @@ -79,6 +87,9 @@ void sim_start(int argc, char** argv) { case 'v': verbose += 1; break; + case 't': + time_scale = (uint8_t) atoi(optarg); + break; case 'o': recorder_init(optarg ? optarg : "datalog.out"); break; @@ -86,12 +97,23 @@ void sim_start(int argc, char** argv) { sim_error("Unexpected result in getopt_long handler"); } + // Record the command line arguments to the datalog, if active + record_raw("# commandline: "); + for (index = 0; index < argc; index++) { + record_raw(argv[index]); + record_raw(" ") ; + } + record_raw("\n"); + // Store remaining arguments list for serial sim g_argc = argc - optind + 1; g_argv = argv + optind - 1; if (argc < 2) usage(argv[0]); + // Initialize timer + sim_timer_init(time_scale); + // Record pin names in datalog #define NAME_PIN_AXES(x) \ add_trace_var(#x "_X" , TRACE_##x + 0); \ diff --git a/simulator/timer_ext.c b/simulator/timer_ext.c index 3ff4d9b..25858b3 100644 --- a/simulator/timer_ext.c +++ b/simulator/timer_ext.c @@ -9,9 +9,12 @@ #include // printf #include // usleep -#define TIME_SLOW_FACTOR 10 +#define TICKS_TO_US(t) (t / (F_CPU / 1000000)) + +static uint16_t time_scale = 1; static void schedule_timer(uint32_t useconds); +static void timer1_isr(void); static bool timer_initialised = false; @@ -22,6 +25,8 @@ enum { }; static volatile uint8_t timer_reason; // Who scheduled this timer +static uint64_t ticks; +static uint32_t warpTarget; static uint64_t now_ns(void) { struct timespec tv; @@ -41,20 +46,40 @@ static uint64_t now_us(void) { return now_ns() / 1000; } -uint16_t sim_tick_counter(void) { - // microseconds to 16-bit clock ticks - return (now_us() / TIME_SLOW_FACTOR) US; +void sim_time_warp(void) { + if (time_scale || timer_reason == 0) + return; + + ticks += warpTarget; + warpTarget = 0; + + timer1_isr(); } +uint16_t sim_tick_counter(void) { + if (time_scale) { + // microseconds to 16-bit clock ticks + return (now_us() / time_scale) US; + } + return (uint16_t)(ticks % 0xFFFF); +} + +#ifdef SIM_DEBUG extern uint8_t clock_counter_10ms, clock_counter_250ms, clock_counter_1s; +#endif + static uint64_t begin; static uint64_t then; -void sim_timer_init(void) { +void sim_timer_init(uint8_t scale) { + time_scale = scale; then = begin = now_ns(); - sim_info("timer_init"); + if (scale==0) + sim_info("timer_init: warp-speed"); + else if (scale==1) + sim_info("timer_init: real-time"); + else + sim_info("timer_init: 1/%u time", scale); timer_initialised = true; - - sim_setTimer(); } void sim_timer_stop(void) { @@ -63,15 +88,23 @@ void sim_timer_stop(void) { } uint64_t sim_runtime_ns(void) { - return (now_ns() - begin) / TIME_SLOW_FACTOR; + if (time_scale) + return (now_ns() - begin) / time_scale; + return TICKS_TO_US(ticks) * 1000 ; } -static void timer1_isr(int cause, siginfo_t *HowCome, void *ucontext) { +static void timer1_callback(int cause, siginfo_t *HowCome, void *ucontext) { + timer1_isr(); +} + +static void timer1_isr(void) { + const uint8_t tr = timer_reason; if ( ! sim_interrupts) { // Interrupts disabled. Schedule another callback in 10us. schedule_timer(10); return; } + timer_reason = 0; cli(); @@ -101,9 +134,8 @@ static void timer1_isr(int cause, siginfo_t *HowCome, void *ucontext) { then = now; #endif - if (timer_reason & TIMER_OCR1A) TIMER1_COMPA_vect(); - if (timer_reason & TIMER_OCR1B) TIMER1_COMPB_vect(); - timer_reason = 0; + if (tr & TIMER_OCR1A) TIMER1_COMPA_vect(); + if (tr & TIMER_OCR1B) TIMER1_COMPB_vect(); sei(); @@ -125,6 +157,7 @@ void sim_setTimer() { // 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) & 0xFFFF; @@ -134,37 +167,40 @@ void sim_setTimer() { //-- Find the nearest event uint32_t next = nextA; - if (nextB && ( ! next || (nextB < next))) { + 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; + if (next && next == nextA) timer_reason |= TIMER_OCR1A; + if (next && 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. + warpTarget = next ; - //-- Convert ticks to microseconds - long actual = ((unsigned long)next) * TIME_SLOW_FACTOR / (1 US); - if ( next && !actual) - actual++; + if (time_scale) { + // 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. - if (next) { - sim_debug("OCR1A:%04X OCR1B:%04X now=%04X", OCR1A, OCR1B, now ); - sim_debug(" next=%u real=%u", next, actual); + //-- Convert ticks to microseconds + long actual = ((unsigned long)next) * time_scale / (1 US); + if ( next && !actual) + actual++; + + + 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 the event - schedule_timer(actual); } // Schedule Timer1 callback useconds from now. @@ -173,15 +209,17 @@ 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"); + if (time_scale) { + sa.sa_sigaction = timer1_callback; + 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); } - 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/timer.c b/timer.c index ac59ba0..72d5932 100644 --- a/timer.c +++ b/timer.c @@ -121,10 +121,10 @@ void timer_init() OCR1B = TICK_TIME & 0xFFFF; // enable interrupt TIMSK1 = MASK(OCIE1B); - #ifdef SIMULATOR - // Tell simulator - sim_timer_init(); - #endif +#ifdef SIMULATOR + // Tell simulator + sim_setTimer(); +#endif } #ifdef MOTHERBOARD