aboutsummaryrefslogblamecommitdiffstats
path: root/bin/rtspin.c
blob: 0995d01696880642bd8e1ca132a8334bc00562af (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11





                     
                 



                   
 









                                                          






                                               


                                              







                                                                                                                                               





                                
                      











                                     

                                             


                                   








                                                       



































                                                        






                                                 


                                  
                           




















                                                                                            














































                                                                              
                                                             




















































                                                                                     


                                


                    
                                  



                        
                          
                            
                        
                                 


                                           











                                                    

                           













                                                             


                                             


                                      
                         


                                                                       

                                        


                                    










































                                                                                              









                                                   
 

                                 





                                   
                              
                                            




                                           








                                                                
 



































                                                                                             




                                                                          
 
                                               
                                        
                                              


                                                                     
 
                    



                                                           
 
                      


                                        
                                                     





                                                          
 

                         










                                                                         



                 
#include <sys/time.h>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <math.h>

#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;
}