simulator: Make time-scale=0 a cmdline option

Teach the simulator to run with no delays when time-scale=0.

Let the user specify the time-scale on the command line.
This commit is contained in:
Phil Hord 2013-11-26 19:24:25 -05:00 committed by Markus Hitter
parent c7b43782ce
commit 96495fb9bb
7 changed files with 135 additions and 61 deletions

View File

@ -80,6 +80,9 @@ void clock() {
ifclock(clock_flag_10ms) { ifclock(clock_flag_10ms) {
clock_10ms(); clock_10ms();
} }
#ifdef SIMULATOR
sim_time_warp();
#endif
} }

View File

@ -156,11 +156,18 @@ void sim_assert(bool cond, const char msg[]);
void sim_gcode_ch(char ch); void sim_gcode_ch(char ch);
void sim_gcode(const char msg[]); 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_timer_stop(void);
void sim_setTimer(void); void sim_setTimer(void);
uint16_t sim_tick_counter(void); uint16_t sim_tick_counter(void);
uint64_t sim_runtime_ns(void); ///< Simulated run-time in nanoseconds 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" #define DIO0_PIN "proof of life"

View File

@ -74,10 +74,7 @@ void record_pin(int pin, int32_t state, uint64_t t) {
sim_assert(pin < MAX_PINS, "pin number invalid"); sim_assert(pin < MAX_PINS, "pin number invalid");
// Record previous state when new state value appears emit_log_data();
if ( t != prev_t ) {
emit_log_data();
}
prev_t = t; prev_t = t;
values[pin] = state; values[pin] = state;
} }
@ -87,3 +84,9 @@ void record_comment(const char * msg) {
fprintf(file, "# %s\n", msg); fprintf(file, "# %s\n", msg);
fflush(file); fflush(file);
} }
void record_raw(const char * msg) {
if (!file) return;
fprintf(file, "%s", msg);
fflush(file);
}

View File

@ -9,6 +9,7 @@ void recorder_init(const char* filename);
void record_pin(int pin, int32_t state, uint64_t time); void record_pin(int pin, int32_t state, uint64_t time);
void add_trace_var(const char* name, int pin); void add_trace_var(const char* name, int pin);
void record_comment(const char * msg); void record_comment(const char * msg);
void record_raw(const char * msg);
void record_comment_stream(char ch); void record_comment_stream(char ch);
#endif /* DATA_RECORDER_H_ */ #endif /* DATA_RECORDER_H_ */

View File

@ -4,6 +4,9 @@
#include <ctype.h> #include <ctype.h>
#include <getopt.h> #include <getopt.h>
// If no time scale specified, use 1/10th real-time for simulator
#define DEFAULT_TIME_SCALE 10
#include "simulator.h" #include "simulator.h"
#include "data_recorder.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_gcode = 0; ///< show gcode on the console
int trace_pos = 0; ///< show print head position 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[] = { struct option opts[] = {
{ "quiet", no_argument, &verbose , 0 }, { "quiet", no_argument, &verbose , 0 },
{ "verbose", no_argument, NULL, 'v' }, { "verbose", no_argument, NULL, 'v' },
{ "gcode", no_argument, NULL, 'g' }, { "gcode", no_argument, NULL, 'g' },
{ "pos", no_argument, NULL, 'p' }, { "pos", no_argument, NULL, 'p' },
{ "time-scale", required_argument, NULL, 't' },
{ "tracefile", optional_argument, NULL, 'o' } { "tracefile", optional_argument, NULL, 'o' }
}; };
static void usage(const char *name) { 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("\n\n");
printf(" -g || --gcode show gcode on console as it is processed\n"); printf(" -q || --quiet show less output\n");
printf(" -p || --pos show head position on console\n"); printf(" -v || --verbose show more output\n");
printf(" -o || --tracefile write simulator pin trace to 'outfile' (default filename=datalog.out)\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"); printf("\n");
exit(1); exit(1);
} }
@ -64,8 +71,9 @@ char** g_argv;
void sim_start(int argc, char** argv) { void sim_start(int argc, char** argv) {
int c; int c;
int index; 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) { switch (c) {
case 'q': case 'q':
verbose = 0; verbose = 0;
@ -79,6 +87,9 @@ void sim_start(int argc, char** argv) {
case 'v': case 'v':
verbose += 1; verbose += 1;
break; break;
case 't':
time_scale = (uint8_t) atoi(optarg);
break;
case 'o': case 'o':
recorder_init(optarg ? optarg : "datalog.out"); recorder_init(optarg ? optarg : "datalog.out");
break; break;
@ -86,12 +97,23 @@ void sim_start(int argc, char** argv) {
sim_error("Unexpected result in getopt_long handler"); 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 // Store remaining arguments list for serial sim
g_argc = argc - optind + 1; g_argc = argc - optind + 1;
g_argv = argv + optind - 1; g_argv = argv + optind - 1;
if (argc < 2) usage(argv[0]); if (argc < 2) usage(argv[0]);
// Initialize timer
sim_timer_init(time_scale);
// Record pin names in datalog // Record pin names in datalog
#define NAME_PIN_AXES(x) \ #define NAME_PIN_AXES(x) \
add_trace_var(#x "_X" , TRACE_##x + 0); \ add_trace_var(#x "_X" , TRACE_##x + 0); \

View File

@ -9,9 +9,12 @@
#include <stdio.h> // printf #include <stdio.h> // printf
#include <unistd.h> // usleep #include <unistd.h> // 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 schedule_timer(uint32_t useconds);
static void timer1_isr(void);
static bool timer_initialised = false; static bool timer_initialised = false;
@ -22,6 +25,8 @@ enum {
}; };
static volatile uint8_t timer_reason; // Who scheduled this timer static volatile uint8_t timer_reason; // Who scheduled this timer
static uint64_t ticks;
static uint32_t warpTarget;
static uint64_t now_ns(void) { static uint64_t now_ns(void) {
struct timespec tv; struct timespec tv;
@ -41,20 +46,40 @@ static uint64_t now_us(void) {
return now_ns() / 1000; return now_ns() / 1000;
} }
uint16_t sim_tick_counter(void) { void sim_time_warp(void) {
// microseconds to 16-bit clock ticks if (time_scale || timer_reason == 0)
return (now_us() / TIME_SLOW_FACTOR) US; 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; extern uint8_t clock_counter_10ms, clock_counter_250ms, clock_counter_1s;
#endif
static uint64_t begin; static uint64_t begin;
static uint64_t then; static uint64_t then;
void sim_timer_init(void) { void sim_timer_init(uint8_t scale) {
time_scale = scale;
then = begin = now_ns(); 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; timer_initialised = true;
sim_setTimer();
} }
void sim_timer_stop(void) { void sim_timer_stop(void) {
@ -63,15 +88,23 @@ void sim_timer_stop(void) {
} }
uint64_t sim_runtime_ns(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) { if ( ! sim_interrupts) {
// Interrupts disabled. Schedule another callback in 10us. // Interrupts disabled. Schedule another callback in 10us.
schedule_timer(10); schedule_timer(10);
return; return;
} }
timer_reason = 0;
cli(); cli();
@ -101,9 +134,8 @@ static void timer1_isr(int cause, siginfo_t *HowCome, void *ucontext) {
then = now; then = now;
#endif #endif
if (timer_reason & TIMER_OCR1A) TIMER1_COMPA_vect(); if (tr & TIMER_OCR1A) TIMER1_COMPA_vect();
if (timer_reason & TIMER_OCR1B) TIMER1_COMPB_vect(); if (tr & TIMER_OCR1B) TIMER1_COMPB_vect();
timer_reason = 0;
sei(); sei();
@ -125,6 +157,7 @@ void sim_setTimer() {
// 0 = No timer; 1-0x10000 = time until next occurrence // 0 = No timer; 1-0x10000 = time until next occurrence
if ( ! nextA) nextA = 0x10000; if ( ! nextA) nextA = 0x10000;
} }
if (TIMSK1 & MASK(OCIE1B)) { if (TIMSK1 & MASK(OCIE1B)) {
sim_debug("Timer1 Interrupt B: Enabled"); sim_debug("Timer1 Interrupt B: Enabled");
nextB = (OCR1B - now) & 0xFFFF; nextB = (OCR1B - now) & 0xFFFF;
@ -134,37 +167,40 @@ void sim_setTimer() {
//-- Find the nearest event //-- Find the nearest event
uint32_t next = nextA; uint32_t next = nextA;
if (nextB && ( ! next || (nextB < next))) { if (nextB && ( ! next || (nextB < next)))
next = nextB; next = nextB;
timer_reason = TIMER_OCR1B;
}
//-- Flag the reasons for the next event //-- Flag the reasons for the next event
timer_reason = 0; timer_reason = 0;
if (next == nextA) timer_reason |= TIMER_OCR1A; if (next && next == nextA) timer_reason |= TIMER_OCR1A;
if (next == nextB) timer_reason |= TIMER_OCR1B; if (next && next == nextB) timer_reason |= TIMER_OCR1B;
// FIXME: We will miss events if they occur like this: warpTarget = next ;
// 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 if (time_scale) {
long actual = ((unsigned long)next) * TIME_SLOW_FACTOR / (1 US); // FIXME: We will miss events if they occur like this:
if ( next && !actual) // nextA = 0x1000
actual++; // 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) { //-- Convert ticks to microseconds
sim_debug("OCR1A:%04X OCR1B:%04X now=%04X", OCR1A, OCR1B, now ); long actual = ((unsigned long)next) * time_scale / (1 US);
sim_debug(" next=%u real=%u", next, actual); 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. // Schedule Timer1 callback useconds from now.
@ -173,15 +209,17 @@ static void schedule_timer(uint32_t useconds) {
struct itimerval itimer; struct itimerval itimer;
struct sigaction sa; struct sigaction sa;
sa.sa_sigaction = timer1_isr; if (time_scale) {
sigemptyset(&sa.sa_mask); sa.sa_sigaction = timer1_callback;
sa.sa_flags = SA_SIGINFO; sigemptyset(&sa.sa_mask);
if (sigaction(SIGALRM, &sa, 0)) { sa.sa_flags = SA_SIGINFO;
sim_error("sigaction"); 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);
} }

View File

@ -121,10 +121,10 @@ void timer_init()
OCR1B = TICK_TIME & 0xFFFF; OCR1B = TICK_TIME & 0xFFFF;
// enable interrupt // enable interrupt
TIMSK1 = MASK(OCIE1B); TIMSK1 = MASK(OCIE1B);
#ifdef SIMULATOR #ifdef SIMULATOR
// Tell simulator // Tell simulator
sim_timer_init(); sim_setTimer();
#endif #endif
} }
#ifdef MOTHERBOARD #ifdef MOTHERBOARD