SCALETTA LEZIONE SERT E11 -- 13 dicembre 2012 *** Funzione delay basata sul tick 1 Funzione delay() basata sul tick periodico 1.1 Scrittura funzione static inline in comm.h +------------------------------------------+ |static inline void delay(unsigned long d) | |{ | | unsigned long expire = ticks + d; | | while (ticks < expire) | | /* do nothing */ ; | |} | +------------------------------------------+ 1.2 Modificare main.c per rimuovere la vecchia delay() e impostare un ritardo di 1 secondo (HZ=64 tick) 1.3 Problema: cosa accade quando ticks o ticks+d vanno in overflow? Il funzionamento non e' corretto! 1.3.1 Il contatore di 32 bit andra' in overflow dopo 2^32/(64*60*60*24*365)=4294967296/2018304000 ~ 2 anni 1.3.2 Verificare il bug inizializzando ticks con il valore 0xffffffff-10*HZ 1.3.2 Se il sistema embedded e' progettato per funzionare ininterrottamente e' necessario considerare questa possibilita' 1.4 Macro per gestire l'overflow del tick: +---------------------------------------------------------+ |#define time_after(u, k) ((long)(k)-((long)(u))<0) | |#define time_before_eq(u, k) ((long)(k)-((long)(u))>=0) | |#define time_before(u, k) ((long)(u)-((long)(k))<0) | |#define time_after_eq(u, k) ((long)(u)-((long)(k))>=0) | |[...] | | while (time_before(ticks, expire)) | | /* do nothing */ ; | +---------------------------------------------------------+ 1.4.1 Verificare che il bug e' stato corretto 1.5 Problema: come verificare che delay(HZ) sia un ritardo di un secondo? 1.5.1 Soluzione migliore: verificare con uno strumento di misura esterno (ad esempio, oscilloscopio) 1.5.2 Soluziona adatta per la verifica del software: utilizzare un "orologio" alternativo, ad esempio il debug timer a 40 bit *** Scheduler per task periodici a priorita' fissa non interrompibili 2 Definizione delle caratteristiche dello scheduler 2.1 Task periodici caratterizzati da 2.1.1 Periodo 2.1.2 Priorita' 2.1.3 Job (funzione da eseguire ad ogni rilascio) 2.1.4 Nome (per debug) 2.2 Scheduler a priorita' fissa dei task 2.3 Numero massimo di task prefissato (32 task) 2.4 Scheduler basato sul tick 3 Definizione della struttura che descrive un task (in comm.h) +-------------------------------+ |typedef void (*job_t)(void *); | |struct task { | | int valid; | | unsigned long releasetime; | | int released; | | int period; | | int priority; | | job_t job; | | void *arg; | | const char *name; | |}; | +-------------------------------+ 4 Implementazione del task nel file tasks.c 4.1 Dichiarazione di un vettore di descrittori di task +--------------------------------------+ |#define MAX_NUM_TASKS 32 // in comm.h | |struct task taskset[MAX_NUM_TASKS]; | |int num_tasks; | +--------------------------------------+ 4.2 Inizializzazione del vettore di descrittori di task: +-------------------------------------+ |void init_taskset(void) | |{ | | int i; | | num_tasks = 0; | | for (i=0; ijob = job; | | t->arg = arg; | | t->name = name; | | t->period = period; | | t->priority = priority; | | t->releasetime = ticks+delay; | | t->released = 0; | | ++num_tasks; | | __memory_barrier(); | | t->valid = 1; | | puts("Task "); | | puts(name); | | puts(" created, TID="); | | putu(i); | | puts("\n"); | | return i; | |} | +--------------------------------------------------------------+ 5 Implementazione dello scheduler nel file sched.c 5.1 Definizione della funzione schedule(): +-----------------------------------------------------+ |volatile unsigned long globalreleases = 0; | |void schedule(void) | |{ | | unsigned long now = ticks; | | struct task *f; | | int i; | | for (i=0, f=taskset; ivalid) | | continue; | | if (time_after_eq(now, f->releasetime)) { | | ++f->released; | | f->releasetime += f->period; | | ++globalreleases; | | } | | ++i; | | } | |} | +-----------------------------------------------------+ 5.2 Definizione della funzione run_periodic_tasks(): +------------------------------------------------------------+ |void run_periodic_tasks(void) | |{ | | int maxprio, i; | | struct task *best, *f; | | unsigned long state; | | for (;;) { | | maxprio = MININT; | | best = NULL; | | state = globalreleases; | | for (i=0, f=taskset; ivalid) | | continue; | | ++i; | | if (f->released > 0 && f->priority > maxprio) { | | maxprio = f->priority; | | best = f; | | } | | } | | if (best != NULL && state == globalreleases) { | | best->job(best->arg); | | best->released--; | | } | | } | |} | +------------------------------------------------------------+ 5.4 Invocazione di schedule() ad ogni tick: +-----------------------------+ |static void isr_tick(void) | |{ | | iomem[TEOI] = 1UL; | | ++ticks; | | schedule(); | |} | +-----------------------------+ 6 Inizializzazione dello scheduler in _init(): +------------------+ |[...] | |init_ticks(); | |init_taskset(); | |led_green_off(); | |[...] | +------------------+ 7 Verifica del funzionamento dello scheduler 7.1 Modificare main.c per utilizzare lo scheduler +--------------------------------------------------------------------------+ |void led_cycle(void *arg __attribute__((unused))) { | |{ | | static int lv = 1; | | lv = 1 - lv; | | _led_toggle(2*lv+1); | |} | |void main(void) | |{ | | banner(); | | if (create_task(led_cycle, NULL, HZ, 5, -HZ, "led_cycle") == -1) { | | puts("ERROR: cannot create task led_cycle\n"); | | _greenpanic(); | | } | | run_periodic_tasks(); | |} | +--------------------------------------------------------------------------+ =======