summaryrefslogtreecommitdiffstats
path: root/baseline/source/extra.h
diff options
context:
space:
mode:
Diffstat (limited to 'baseline/source/extra.h')
-rw-r--r--baseline/source/extra.h350
1 files changed, 0 insertions, 350 deletions
diff --git a/baseline/source/extra.h b/baseline/source/extra.h
deleted file mode 100644
index e8f3d18..0000000
--- a/baseline/source/extra.h
+++ /dev/null
@@ -1,350 +0,0 @@
1/**
2 * Copyright 2019 Sims Hill Osborne and Joshua Bakita
3 *
4 * This header provides facilities by which to separably run and time TACLeBench
5 **/
6#define _GNU_SOURCE
7#include <time.h>
8#include <sys/mman.h>
9#include <stdlib.h>
10#include <stdio.h>
11#include <string.h>
12#include <signal.h>
13#include <limits.h>
14#include <fcntl.h>
15#include <stdint.h>
16#include <sched.h>
17
18// This is only visible if _GNU_SOURCE is defined, and that define does not
19// come along to places where this file is included. Address this by manually
20// forcing it into the global namespace.
21extern int sched_getcpu();
22
23// These constants correspond to the imx6q-sabredb platform
24#define LINE_SIZE 32
25#define L2_SIZE 16*2048*32
26
27#if __arm__
28#include <unistd.h>
29#include <sys/syscall.h>
30#endif
31
32#define LITMUS 0
33#define MC2 0
34#define MMDC_PROF 0
35
36#if LITMUS
37#include <litmus.h>
38#endif
39
40#if MMDC_PROF
41#include "/media/speedy/litmus/tools/mmdc/mmdc.h"
42#endif
43
44#if LITMUS
45#define SET_UP LOAD_PARAMS SETUP_LITMUS
46#else
47#define SET_UP LOAD_PARAMS
48#endif
49
50#if MMDC_PROF
51#define LOAD_PARAMS LOAD_PARAMS_ITRL SETUP_MMDC
52#else
53#define LOAD_PARAMS LOAD_PARAMS_ITRL
54#endif
55
56// Store state globally so that the job can be outside main()
57// Arrays use float as a comprimise between overflow and size
58float *_rt_exec_time;
59#if MMDC_PERF
60float *_rt_mmdc_read;
61float *_rt_mmdc_write;
62#endif
63long _rt_jobs_complete;
64long _rt_max_jobs;
65int _rt_core;
66int _rt_will_output;
67struct timespec _rt_start, _rt_end;
68
69char *_rt_run_id;
70char *_rt_our_prog_name;
71#define _RT_FILENAME_LEN 64
72
73#define LOAD_PARAMS_ITRL \
74 if (argc != 6) { \
75 fprintf(stderr, "Usage: %s <name> <loops> <my core> <runID> <save results?>\n", argv[0]);\
76 fprintf(stderr, " <loops> integer number of iterations. -1 for infitite.\n");\
77 fprintf(stderr, " <save results?> 1 to save results, 0 to discard.\n");\
78 fprintf(stderr, " <my core> UNUSED. Core is now auto-detected.\n");\
79 exit(1);\
80 }\
81 _rt_our_prog_name = argv[1];\
82 _rt_max_jobs = atol(argv[2]);\
83 _rt_core = sched_getcpu();\
84 _rt_run_id = argv[4];\
85 _rt_will_output = atoi(argv[5]);\
86 if (_rt_max_jobs < 0 && _rt_will_output != 0) {\
87 fprintf(stderr, "Infinite loops only supported when _rt_will_output is disabled!\n");\
88 exit(1);\
89 }\
90 if (strlen(_rt_run_id) + 5 > _RT_FILENAME_LEN) {\
91 fprintf(stderr, "Run ID is too large! Keep it to less than %d characters.\n", _RT_FILENAME_LEN);\
92 exit(1);\
93 }\
94 _rt_exec_time = calloc(_rt_max_jobs * _rt_will_output, sizeof(float));\
95 if (!_rt_exec_time) {\
96 perror("Unable to allocate buffer for execution times");\
97 exit(1);\
98 }\
99 _rt_jobs_complete = 0;\
100 mlockall(MCL_CURRENT || MCL_FUTURE);
101
102#define SETUP_MMDC \
103 _rt_mmdc_read = calloc(_rt_max_jobs * _rt_will_output, sizeof(float));\
104 _rt_mmdc_write = calloc(_rt_max_jobs * _rt_will_output, sizeof(float));\
105 if (!_rt_mmdc_read || !_rt_mmdc_write) {\
106 perror("Unable to allocate buffer for MMDC data");\
107 exit(1);\
108 }\
109 MMDC_PROFILE_RES_t mmdc_res;\
110 memset(&mmdc_res, 0, sizeof(MMDC_PROFILE_RES_t));\
111 int fd = open("/dev/mem", O_RDWR, 0);\
112 if (fd < 0) {\
113 perror("Unable to open /dev/mem");\
114 exit(1);\
115 }\
116 pMMDC_t mmdc = mmap(NULL, 0x4000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, MMDC_P0_IPS_BASE_ADDR);\
117 if (mmdc == MAP_FAILED) {\
118 perror("Unable to map MMDC address space");\
119 exit(1);\
120 }\
121 mmdc->madpcr1 = axi_arm1;\
122 msync(&(mmdc->madpcr1),4,MS_SYNC);
123
124#define SETUP_LITMUS \
125 unsigned int wait = 0; \
126 if (be_migrate_to_domain(_rt_core) < 0) { \
127 perror("Unable to migrate to specified CPU"); \
128 exit(1); \
129 } \
130 struct reservation_config res; \
131 res.id = gettid(); \
132 res.cpu = cpu; \
133 res.priority = LITMUS_HIGHEST_PRIORITY; \
134 /* we take over half the CPU time (these are ns) */ \
135 res.polling_params.budget = ms2ns(3000); \
136 res.polling_params.period = ms2ns(3000); \
137 res.polling_params.offset = 0; \
138 res.polling_params.relative_deadline = ms2ns(3000); \
139 /* Not 100% sure that we should use periodic polling */ \
140 if (reservation_create(PERIODIC_POLLING, &res) < 0) { \
141 perror("Unable to create reservation"); \
142 exit(1); \
143 } \
144 struct rt_task rt_param; \
145 init_rt_task_param(&rt_param); \
146 /* Supposedly the next two parameters are irrelevant when reservations are enabled, but I'm leaving them anyway... */ \
147 rt_param.exec_cost = ms2ns(999); \
148 rt_param.period = ms2ns(1000); \
149 rt_param.priority = LITMUS_HIGHEST_PRIORITY; \
150 rt_param.cls = RT_CLASS_HARD; \
151 rt_param.release_policy = TASK_PERIODIC; \
152 rt_param.budget_policy = NO_ENFORCEMENT; \
153 rt_param.cpu = cpu; \
154 if (set_rt_task_param(gettid(), &rt_param) < 0) { \
155 perror("Unable to set real-time parameters"); \
156 exit(1); \
157 } \
158 if (init_litmus() != 0) { \
159 perror("init_litmus failed"); \
160 exit(1); \
161 } \
162 MC2_SETUP \
163 if (task_mode(LITMUS_RT_TASK) != 0) { \
164 perror("Unable to become real-time task"); \
165 exit(1); \
166 } \
167 if (wait && wait_for_ts_release() != 0) { \
168 perror("Unable to wait for taskset release"); \
169 exit(1); \
170 }
171
172#if MC2
173#define MC2_SETUP \
174 struct mc2_task mc2_param; \
175 mc2_param.res_id = gettid(); \
176 mc2_param.crit = CRIT_LEVEL_A; \
177 if (set_mc2_task_param(gettid(), &mc2_param) < 0) { \
178 perror("Unable to set MC^2 task params"); \
179 exit(1); \
180 } \
181 set_page_color(rt_param.cpu);
182#else
183#define MC2_SETUP
184#endif
185
186#define CLEANUP_LITMUS \
187 if (task_mode(BACKGROUND_TASK) != 0) { \
188 perror("Unable to become a real-time task"); \
189 exit(1); \
190 } \
191 reservation_destroy(gettid(), rt_param.cpu);
192
193#if __arm__
194// On ARM, manually flush the cache
195#define FLUSH_CACHES \
196 volatile uint8_t buffer[L2_SIZE * 4]; \
197 for (uint32_t j = 0; j < 4; j++) \
198 for (uint32_t i = 0; i < L2_SIZE * 4; i += LINE_SIZE) \
199 buffer[i]++;
200#else
201// On x86 call the wbinvld instruction (it's in a kernel module due to it being ring-0)
202#define FLUSH_CACHES \
203 FILE *fp = fopen("/proc/wbinvd", "r");\
204 if (fp == NULL) {\
205 perror("Cache flush module interface cannot be opened");\
206 exit(1);\
207 }\
208 char dummy;\
209 if (fread(&dummy, 1, 1, fp) == 0) {\
210 perror("Unable to access cache flush module interface");\
211 exit(1);\
212 }\
213 fclose(fp);
214#endif
215
216// Buffer timing result from a single job
217static void _rt_save_job_result() {
218 if (_rt_jobs_complete >= _rt_max_jobs) {
219 fprintf(stderr, "Max jobs setting too small! Trying to record job #%ld when we only have space for %ld jobs. Exiting...\n", _rt_jobs_complete, _rt_max_jobs);
220 exit(1);
221 }
222 if (_rt_jobs_complete > -1 && _rt_will_output) {
223 _rt_exec_time[_rt_jobs_complete] = _rt_end.tv_sec - _rt_start.tv_sec;
224 _rt_exec_time[_rt_jobs_complete] *= 1e9;
225 _rt_exec_time[_rt_jobs_complete] += _rt_end.tv_nsec - _rt_start.tv_nsec;
226#if MMDC_PROF
227 _rt_mmdc_read[_rt_jobs_complete] = mmdc_res.read_bytes;
228 _rt_mmdc_write[_rt_jobs_complete] = mmdc_res.write_bytes;
229#endif
230 }
231}
232
233// Save all buffered timing results to disk
234static void _rt_write_to_file() {
235 char fileName[_RT_FILENAME_LEN];
236 FILE *fp;
237 if (!_rt_will_output)
238 return;
239 munlockall();
240 strcpy(fileName, _rt_run_id);
241 strcat(fileName, ".txt");
242 fp = fopen(fileName, "a");
243 if (fp == NULL) {
244 perror("Unable to open _rt_will_output file");
245 exit(1);
246 }
247 // Same format as the paired results with "none" for unused fields
248 for (int i = 0; i < _rt_jobs_complete; i++){
249 fprintf(fp, "%s none %u none %ld %.f %s %d %.f %.f \n",
250 _rt_our_prog_name, _rt_core, _rt_max_jobs,
251 _rt_exec_time[i], _rt_run_id, i,
252#if MMDC_PROF
253 _rt_mmdc_read[i], _rt_mmdc_write[i]);
254#else
255 0.0, 0.0);
256#endif
257 }
258 fclose(fp);
259#if LITMUS
260 CLEANUP_LITMUS
261#endif
262}
263
264// Start a job
265static void _rt_start_loop() {
266#if LITMUS
267 if (sleep_next_period() != 0) {
268 perror("Unable to sleep for next period");
269 }
270#else
271 sched_yield();
272#endif
273 FLUSH_CACHES
274#if MMDC_PROF
275 /* This disables profiling, resets the counters, clears the overflow bit, and enables profiling */
276 start_mmdc_profiling(mmdc);
277#endif
278 clock_gettime(CLOCK_MONOTONIC, &_rt_start);
279}
280
281// Complete a job
282static void _rt_stop_loop() {
283 clock_gettime(CLOCK_MONOTONIC, &_rt_end);
284#if MMDC_PROF
285 /* This freezes the profiling and makes results available */
286 pause_mmdc_profiling(mmdc);
287 get_mmdc_profiling_results(mmdc, &mmdc_res);
288#endif
289 _rt_save_job_result();
290 _rt_jobs_complete++;
291}
292
293/****** New API ******
294 * Intended structure:
295 *
296 * |int main(int argc, char **argv) {
297 * | SET_UP
298 * | ...
299 * | for_each_job {
300 * | tacleInit();
301 * | tacleMain();
302 * | }
303 * | WRITE_TO_FILE
304 * |}
305 *
306 * The main() function must call its parameters argc and argv for SET_UP to be
307 * able to read them.
308 * Only SET_UP necessarily has to be in main().
309 *
310 * We use some niche C features, here's a quick explaination:
311 * 1. The && operator doesn't evaluate the right-hand side of the expression
312 * unless the left side evaluated to true. We use this to only execute
313 * _rt_start_loop() when the loop will actually run.
314 * 2. The comma operator executes the first expression and then throws away the
315 * result. We use this to call our void function from inside a comparison.
316 */
317#define for_each_job \
318 for (; _rt_jobs_complete < _rt_max_jobs && (_rt_start_loop(),1); \
319 _rt_stop_loop())
320
321/****** Legacy API ******
322 * Intended structure:
323 *
324 * |int main(int argc, char **argv) {
325 * | SET_UP
326 * | for (jobsComplete=0; jobsComplete<maxJobs; jobsComplete++){
327 * | START_LOOP
328 * | tacleInit();
329 * | tacleMain();
330 * | STOP_LOOP
331 * | }
332 * | WRITE_TO_FILE
333 * | tacleReturn
334 * |}
335 *
336 * The main() function must call its parameters argc and argv for SET_UP to be
337 * able to read them.
338 */
339static int jobsComplete = 0;
340#define START_LOOP _rt_start_loop();
341#define STOP_LOOP _rt_stop_loop();
342#define WRITE_TO_FILE _rt_write_to_file();
343#define maxJobs _rt_max_jobs
344// Has been part of STOP_LOOP for quite some time
345#define SAVE_RESULTS \
346 #warning "The SAVE_RESULTS macro is deprecated and will soon be removed!";
347// Unclear if SLEEP is used anywhere.
348#define SLEEP \
349 #warning "The SLEEP macro is deprecated and may be removed!" \
350 nanosleep((const struct timespec[]){{0, 1000000}}, NULL);