diff --git a/include/vhex/timer/interface.h b/include/vhex/timer/interface.h index c72d9e9..86d7c4b 100644 --- a/include/vhex/timer/interface.h +++ b/include/vhex/timer/interface.h @@ -5,7 +5,9 @@ #include /* timer_drv_interface - driver interface */ -struct timer_drv_interface { +struct timer_drv_interface +{ + /* timer API */ tid_t (*timer_configure)(uint64_t delay_us, timer_call_t callback); int (*timer_start)(tid_t timer); int (*timer_pause)(tid_t timer); @@ -13,6 +15,14 @@ struct timer_drv_interface { int (*timer_wait)(tid_t timer); int (*timer_spinwait)(tid_t timer); int (*timer_reload)(tid_t timer, uint64_t delay); + /* profiling */ + int (*timer_prof_init)(timer_prof_t *prof); + void (*timer_prof_enter)(timer_prof_t *prof); + void (*timer_prof_enter_rec)(timer_prof_t *prof); + void (*timer_prof_leave_rec)(timer_prof_t *prof); + void (*timer_prof_leave)(timer_prof_t *prof); + uint32_t (*timer_prof_time)(timer_prof_t *prof); + int (*timer_prof_quit)(timer_prof_t *prof); }; #endif /* __VHEX_TIMER_INTERFACE__ */ diff --git a/include/vhex/timer/profiling.h b/include/vhex/timer/profiling.h new file mode 100644 index 0000000..47846e7 --- /dev/null +++ b/include/vhex/timer/profiling.h @@ -0,0 +1,58 @@ +#ifndef __VHEX_PROFILING__ +# define __VHEX_PROFILING__ + +#include + + +//--- +// Runtime time measurement API +//--- + +/* timer_prof_init(): Create a new context object */ +extern int timer_prof_init(timer_prof_t *prof); + +/* timer_prof_enter(): Start counting time for a function + This fucntion should be called at the start of the context scope */ +extern void timer_prof_enter(timer_prof_t *prof); + +/* timer_prof_leave(): Stop counting time for a function + This should be called at the end of the context scope. */ +extern void timer_prof_leave(timer_prof_t *prof); + +/* Variant of timer_prof_enter()/timer_prof_leave() for recursive contexts */ +extern void timer_prof_enter_rec(timer_prof_t *prof); +extern void timer_prof_leave_rec(timer_prof_t *prof); + +/* timer_prof_exec(): Measure a single block of (non-recurive) code + This operation can be used when profiling is not required, and instead + its is used to measure the performance of a single bit of code. Use it + like this: + + uint32_t elasped_us = prof_exec({ + exec_code(); + }); */ +#define timer_prof_exec(code) ({ \ + timer_prof_t prof; \ + timer_prof_init(&prof); \ + timer_prof_enter(&prof); \ + code; \ + timer_prof_leave(&prof); \ + uint32_t time = timer_prof_time(&prof); \ + timer_prof_quit(&prof); \ + time; \ +}) + +/* timer_prof_quit() : uninit timer_prof object */ +extern int timer_prof_quit(timer_prof_t *prof); + + +//--- +// Post-measurement analysis +//--- + +/* timer_prof_time(): Time spent in a given context, in microseconds + Should only be called when the context is not currently executing. */ +extern uint32_t timer_prof_time(timer_prof_t *prof); + + +#endif /* __VHEX_PROFILING__ */ diff --git a/include/vhex/timer/types.h b/include/vhex/timer/types.h index fd62636..423f6d0 100644 --- a/include/vhex/timer/types.h +++ b/include/vhex/timer/types.h @@ -7,4 +7,15 @@ /* timer ID */ typedef int tid_t; +/* Context object, has an elasped delay and a recursion level. This object can + be created on the stack of a function that measures its execution time, + except if the function is recursive, in which case it should be either + static or global. */ +typedef struct timer_prof +{ + uint32_t rec; + uint32_t elapsed; + +} VPACKED(4) timer_prof_t; + #endif /* __VHEX_TIMER_TYPES__ */ diff --git a/src/drivers/mpu/sh/sh7305/tmu/tmu.c b/src/drivers/mpu/sh/sh7305/tmu/tmu.c index 371cf80..e5d99a9 100644 --- a/src/drivers/mpu/sh/sh7305/tmu/tmu.c +++ b/src/drivers/mpu/sh/sh7305/tmu/tmu.c @@ -257,8 +257,99 @@ int sh7305_tmu_spinwait(tid_t id) return (0); } + + //--- -// hardware primitives +// Public profiling API +//--- + +static uint32_t volatile *tmu_prof_tcnt = NULL; +static int tmu_prof_counter = 0; +static int tmu_prof_timer = -1; + +/* sh7305_tmu_prof_make() : initialise a new */ +int sh7305_tmu_prof_init(timer_prof_t *prof) +{ + if (tmu_prof_tcnt == NULL) { + tmu_prof_timer = -1; + for(int t = 0; t < 2; t++) + { + if (!available(t)) + continue; + tmu_prof_timer = t; + tmu_prof_tcnt = &TMU[t].TCNT; + + TMU[t].TCOR = 0xffffffff; + TMU[t].TCNT = 0xffffffff; + TMU[t].TCR.TPSC = TIMER_Pphi_4; + set(TMU[t].TCR.UNF, 0); + TMU[t].TCR.UNIE = 1; + TMU[t].TCR.CKEG = 0; + + *TSTR = *TSTR | (1 << t); + + break; + } + if (tmu_prof_timer < 0) + return (-1); + } + + prof->rec = 0; + prof->elapsed = 0; + + tmu_prof_counter += 1; + return (0); +} + +/* sh7305_tmu_prof_enter(): Start counting time for a function */ +void sh7305_tmu_prof_enter(timer_prof_t *prof) +{ + prof->elapsed += *tmu_prof_tcnt; +} + +/* sh7305_tmu_prof_enter_rec(): Start counting time for a recursive function */ +void sh7305_tmu_prof_enter_rec(timer_prof_t *prof) +{ + if (!prof->rec++) + prof->elapsed += *tmu_prof_tcnt; +} + +/* sh7305_tmu_prof_leave_rec(): Start counting time for a recursive function */ +void sh7305_tmu_prof_leave_rec(timer_prof_t *prof) +{ + if (!--prof->rec) + prof->elapsed -= *tmu_prof_tcnt; +} + +/* sh7305_tmu_prof_leave(): Stop counting time for a function */ +void sh7305_tmu_prof_leave(timer_prof_t *prof) +{ + prof->elapsed -= *tmu_prof_tcnt; +} + + +/* sh7305_prof_time(): Time spent in a given context, in microseconds */ +uint32_t sh7305_tmu_prof_time(timer_prof_t *prof) +{ + struct cpg_clock_frequency cpg_freq; + + cpg_clock_freq(&cpg_freq); + return ((uint64_t)prof->elapsed * 4 * 1000000) / cpg_freq.Pphi_f; +} + +int sh7305_tmu_prof_quit(timer_prof_t *prof) +{ + (void)prof; + if (--tmu_prof_counter) + return (0); + if(tmu_prof_timer >= 0) + sh7305_tmu_stop(tmu_prof_timer); + tmu_prof_timer = -1; + tmu_prof_tcnt = NULL; +} + +//--- +// hardware specification //--- /* __tmu_configure() : configure the SH7305 TMU/ETMU module */ @@ -399,13 +490,22 @@ struct vhex_driver drv_tmu = { .UNUSED = 0 }, .module_data = &(struct timer_drv_interface){ - .timer_configure = &sh7305_tmu_configure, - .timer_start = &sh7305_tmu_start, - .timer_pause = &sh7305_tmu_pause, - .timer_stop = &sh7305_tmu_stop, - .timer_wait = &sh7305_tmu_wait, - .timer_spinwait = &sh7305_tmu_spinwait, - .timer_reload = &sh7305_tmu_reload + /* timer API */ + .timer_configure = &sh7305_tmu_configure, + .timer_start = &sh7305_tmu_start, + .timer_pause = &sh7305_tmu_pause, + .timer_stop = &sh7305_tmu_stop, + .timer_wait = &sh7305_tmu_wait, + .timer_spinwait = &sh7305_tmu_spinwait, + .timer_reload = &sh7305_tmu_reload, + /* profiling API*/ + .timer_prof_init = &sh7305_tmu_prof_init, + .timer_prof_enter = &sh7305_tmu_prof_enter, + .timer_prof_enter_rec = &sh7305_tmu_prof_enter_rec, + .timer_prof_leave_rec = &sh7305_tmu_prof_leave_rec, + .timer_prof_leave = &sh7305_tmu_prof_leave, + .timer_prof_time = &sh7305_tmu_prof_time, + .timer_prof_quit = &sh7305_tmu_prof_quit, } }; VHEX_DECLARE_DRIVER(03, drv_tmu); diff --git a/src/modules/timer/timer.c b/src/modules/timer/timer.c index 772e86a..9439334 100644 --- a/src/modules/timer/timer.c +++ b/src/modules/timer/timer.c @@ -12,7 +12,7 @@ struct { } timer_info; //--- -// user-level API +// user-level timer API //--- /* timer_configure(): Reserve and configure a timer */ @@ -72,6 +72,65 @@ int timer_reload(tid_t timer, uint64_t delay_us) } +//--- +// user-level profiling API +//--- + +/* timer_prof_init(): Create a new context object */ +int timer_prof_init(timer_prof_t *prof) +{ + if (timer_info.driver.timer_prof_init != NULL) + return (timer_info.driver.timer_prof_init(prof)); + return (-1); +} + +/* timer_prof_enter(): Start counting time for a function */ +void timer_prof_enter(timer_prof_t *prof) +{ + if (timer_info.driver.timer_prof_enter != NULL) + timer_info.driver.timer_prof_enter(prof); +} + +/* timer_prof_leave(): Stop counting time for a function */ +void timer_prof_leave(timer_prof_t *prof) +{ + if (timer_info.driver.timer_prof_leave != NULL) + timer_info.driver.timer_prof_leave(prof); +} + +/* timer_prof_enter_rec(): Start counting time for a recursive function */ +void timer_prof_enter_rec(timer_prof_t *prof) +{ + if (timer_info.driver.timer_prof_enter_rec != NULL) + timer_info.driver.timer_prof_enter_rec(prof); +} + +/* timer_prof_leave_rec(): Stop counting time for a recursive function */ +void timer_prof_leave_rec(timer_prof_t *prof) +{ + if (timer_info.driver.timer_prof_leave_rec != NULL) + timer_info.driver.timer_prof_leave_rec(prof); +} + +/* timer_prof_quit() : uninit timer_prof object */ +int timer_prof_quit(timer_prof_t *prof) +{ + if (timer_info.driver.timer_prof_quit != NULL) + return (timer_info.driver.timer_prof_quit(prof)); + return (-1); +} + +/* timer_prof_time(): Time spent in a given context, in microseconds */ +uint32_t timer_prof_time(timer_prof_t *prof) +{ + if (timer_info.driver.timer_prof_time != NULL) + return (timer_info.driver.timer_prof_time(prof)); + return (-1); +} + + + + //--- // Kernel module information //---