#include #include #include #include #include #include #include "litmus.h" #include "common.h" static double cputime() { struct timespec ts; int err; err = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); if (err != 0) perror("clock_gettime"); return (ts.tv_sec + 1E-9 * ts.tv_nsec); } static double wctime() { struct timeval tv; gettimeofday(&tv, NULL); return (tv.tv_sec + 1E-6 * tv.tv_usec); } void usage(char *error) { fprintf(stderr, "Error: %s\n", error); fprintf(stderr, "Usage: rt_spin [-w] [-p PARTITION] [-c CLASS] [-A fp# -B fp# -W wave# -M max_wcet -P #jobs [-D #jobs]] WCET PERIOD DURATION\n" " rt_spin -l\n" " -A: Feedback control 'A' parameter\n" " -B: Feedback control 'B' parameter\n" " -W: 0 = flat; 1 = sine; 2 = triangle; 3 = box; 4 = sawtooth; 5 = reverse sawtooth\n" " -M: max wcet (WCET is assumed min wcet)\n" " -P: period of execution behavior wave\n" " -D: Number of jobs wave should hold at extrema points (default = 0)\n"); exit(1); } #define NUMS 4096 static int num[NUMS]; static double loop_length = 1.0; static char* progname; static int loop_once(void) { int i, j = 0; for (i = 0; i < NUMS; i++) j += num[i]++; return j; } static int loop_for(double exec_time) { double t = 0; int tmp = 0; /* while (t + loop_length < exec_time) { tmp += loop_once(); t += loop_length; } */ double start = cputime(); double now = cputime(); while (now + loop_length < start + exec_time) { tmp += loop_once(); t += loop_length; now = cputime(); } return tmp; } static void fine_tune(double interval) { double start, end, delta; start = wctime(); loop_for(interval); end = wctime(); delta = (end - start - interval) / interval; if (delta != 0) loop_length = loop_length / (1 - delta); } static void configure_loop(void) { double start; /* prime cache */ loop_once(); loop_once(); loop_once(); /* measure */ start = wctime(); loop_once(); /* hope we didn't get preempted */ loop_length = wctime(); loop_length -= start; /* fine tune */ fine_tune(0.1); fine_tune(0.1); fine_tune(0.1); } static void show_loop_length(void) { printf("%s/%d: loop_length=%f (%ldus)\n", progname, getpid(), loop_length, (long) (loop_length * 1000000)); } static void debug_delay_loop(void) { double start, end, delay; show_loop_length(); while (1) { for (delay = 0.5; delay > 0.01; delay -= 0.01) { start = wctime(); loop_for(delay); end = wctime(); printf("%6.4fs: looped for %10.8fs, delta=%11.8fs, error=%7.4f%%\n", delay, end - start, end - start - delay, 100 * (end - start - delay) / delay); } } } static int job(double exec_time) { loop_for(exec_time); sleep_next_period(); return 0; } fp_t float_to_frac(double f) { fpbuf_t num; fpbuf_t denom = 1000; /* express in 1000ths */ num = (fpbuf_t)f * denom; if(f != floor(f)) { num += (fpbuf_t)(f - floor(f)) * denom; } return(_frac(num, denom)); } #define OPTSTR "p:c:wld:veA:B:W:M:P:D:" struct wave_data; typedef double(*wave_func)(int, struct wave_data*); struct wave_data { double min_wcet_ms; double max_wcet_ms; double amplitude; int period; int extrema_delay; int min_point; int max_point; wave_func f; }; double flat_wave(int count, struct wave_data* w) { return(w->min_wcet_ms); } double sin_wave(int count, struct wave_data* w) { double step = count % w->period; double exe = w->min_wcet_ms + w->amplitude * sin(M_PI*step/w->period); return(exe); } double tri_wave(int count, struct wave_data* w) { double step = count % w->period; double v = 2.0 * (step/w->period - floor(step/w->period + 0.5)); double exe = w->min_wcet_ms + w->amplitude * fabs(v); return(exe); } double box_wave(int count, struct wave_data* w) { double step = count % w->period; double exe = w->min_wcet_ms + w->amplitude *((step/w->period < 0.5) ? 0 : 1); return(exe); } double saw_wave(int count, struct wave_data* w) { double step = (count % w->period)/2.0; double v = 2.0 * (step/w->period - floor(step/w->period + 0.5)); double exe; if(v < 0) v += 1.0; exe = w->min_wcet_ms + w->amplitude * v; return(exe); } double rsaw_wave(int count, struct wave_data* w) { double step = (count % w->period)/2.0; double v = 2.0 * (step/w->period - floor(step/w->period + 0.5)); double exe; if(v < 0) v += 1.0; exe = w->max_wcet_ms - w->amplitude * v; return(exe); } double get_execution_time(int count, struct wave_data* w) { static int skips = 0; static int delay_count = 0; if(w->extrema_delay) { int at_max = (((count - skips) % w->period) == w->max_point); int at_min = (((count - skips) % w->period) == w->min_point); if((at_max || at_min) && delay_count++ < w->extrema_delay) { ++skips; return((at_max) ? w->max_wcet_ms : w->min_wcet_ms); } else { delay_count = 0; } } return(w->f(count - skips, w)); } int main(int argc, char** argv) { int ret; lt_t wcet; lt_t period; double wcet_ms, period_ms; int migrate = 0; int cpu = 0; int opt; int wait = 0; int test_loop = 0; int skip_config = 0; int verbose = 0; int want_enforcement = 0; double duration, start; task_class_t class = RT_CLASS_HARD; fp_t A = LITMUS_FP_ZERO, B = LITMUS_FP_ZERO; struct wave_data wave = { .min_wcet_ms = 0.0, .max_wcet_ms = 0.0, .amplitude = 0.0, .period = 0, .extrema_delay = 0, .max_point = 0, .min_point = 0, .f = NULL }; progname = argv[0]; while ((opt = getopt(argc, argv, OPTSTR)) != -1) { switch (opt) { case 'w': wait = 1; break; case 'p': cpu = atoi(optarg); migrate = 1; break; case 'c': class = str2class(optarg); if (class == -1) usage("Unknown task class."); break; case 'e': want_enforcement = 1; break; case 'l': test_loop = 1; break; case 'd': /* manually configure delay per loop iteration * unit: microseconds */ loop_length = atof(optarg) / 1000000; skip_config = 1; break; case 'v': verbose = 1; break; case 'A': A = float_to_frac(atof(optarg)); break; case 'B': B = float_to_frac(atof(optarg)); break; case 'W': switch(atoi(optarg)) { case 0: wave.f = flat_wave; break; case 1: wave.f = sin_wave; break; case 2: wave.f = tri_wave; break; case 3: wave.f = box_wave; break; case 4: wave.f = saw_wave; break; case 5: wave.f = rsaw_wave; break; default: usage("Bad wave identifier."); break; } break; case 'M': wave.max_wcet_ms = atof(optarg); break; case 'P': wave.period = atoi(optarg); if(wave.period <= 0) usage("Execution behavior period must be a positive number."); break; case 'D': wave.extrema_delay = atoi(optarg); break; case ':': usage("Argument missing."); break; case '?': default: usage("Bad argument."); break; } } if (!skip_config) configure_loop(); if (test_loop) { debug_delay_loop(); return 0; } if (argc - optind < 3) usage("Arguments missing."); wcet_ms = atof(argv[optind + 0]); period_ms = atof(argv[optind + 1]); duration = atof(argv[optind + 2]); wcet = wcet_ms * __NS_PER_MS; period = period_ms * __NS_PER_MS; if (wcet <= 0) usage("The worst-case execution time must be a " "positive number."); if (period <= 0) usage("The period must be a positive number."); if (wcet > period) { usage("The worst-case execution time must not " "exceed the period."); } if(wave.f != NULL) { wave.min_wcet_ms = wcet_ms; wave.amplitude = wave.max_wcet_ms - wave.min_wcet_ms; if(wave.amplitude < 0.0) { usage("The max wcet must be greater than min wcet."); } if(wave.f == flat_wave) { wave.max_point = 0; wave.min_point = 0; } else if((wave.f == sin_wave) || (wave.f == tri_wave) || (wave.f == box_wave)) { wave.max_point = wave.period/2; wave.min_point = 0; } else if(wave.f == saw_wave) { wave.max_point = wave.period-1; wave.min_point = 0; } else if(wave.f == rsaw_wave) { wave.max_point = 0; wave.min_point = wave.period-1; } if((wave.f == flat_wave) || (wave.f == box_wave)) { wave.extrema_delay = 0; } } if (migrate) { ret = be_migrate_to(cpu); if (ret < 0) bail_out("could not migrate to target partition"); } ret = sporadic_task_ns(wcet, period, 0, A, B, cpu, class, want_enforcement ? PRECISE_ENFORCEMENT : NO_ENFORCEMENT, migrate); if (ret < 0) bail_out("could not setup rt task params"); if (verbose) show_loop_length(); init_litmus(); ret = task_mode(LITMUS_RT_TASK); if (ret != 0) bail_out("could not become RT task"); if (wait) { ret = wait_for_ts_release(); if (ret != 0) bail_out("wait_for_ts_release()"); } start = wctime(); int count = 0; while (start + duration > wctime()) { if(wave.f == NULL) { job(wcet_ms * 0.0009); /* 90% wcet, in seconds */ } else { job(get_execution_time(count++, &wave) * 0.0009); } } return 0; }