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:
parent
c7b43782ce
commit
96495fb9bb
3
clock.c
3
clock.c
|
|
@ -80,6 +80,9 @@ void clock() {
|
||||||
ifclock(clock_flag_10ms) {
|
ifclock(clock_flag_10ms) {
|
||||||
clock_10ms();
|
clock_10ms();
|
||||||
}
|
}
|
||||||
|
#ifdef SIMULATOR
|
||||||
|
sim_time_warp();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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_ */
|
||||||
|
|
|
||||||
|
|
@ -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); \
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
8
timer.c
8
timer.c
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue