feat: publish FreeRTOS C FC06 card
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
{
|
||||
"card":{"number":"06","slug":"software-timers","title":"Software timers i timer daemon","topic":"one-shot, auto-reload, command queue i callback","status":"Gotowa","version":"v00.01"},
|
||||
"front":{
|
||||
"goal":"Uczeń rozdziela przyjęcie komendy timera od jej wykonania przez timer daemon oraz bada callback na osobnym stosie taska systemowego.",
|
||||
"scope":"Controller uruchamia one-shot i auto-reload, resetuje one-shot przed pierwotnym terminem, po dwóch callbackach zmienia okres auto z 2 na 3 ticki i zatrzymuje go po trzecim. Callback rozpoznaje timer przez pvTimerID i zapisuje wyłącznie bounded evidence."
|
||||
},
|
||||
"viewpoints":[
|
||||
{"id":"A1","label":"CONTEXT","status":"enabled","subtitle":"Application → command queue → daemon"},
|
||||
{"id":"A2","label":"STRUCTURE","status":"unavailable","subtitle":"Struktura","reason":"Timer_t i TimerTag są pokazane dokładniej w A3 i A7."},
|
||||
{"id":"A3","label":"DISPATCH","status":"enabled","subtitle":"TimerHandle_t, pvTimerID i callback"},
|
||||
{"id":"A4","label":"APPLICATION","status":"unavailable","subtitle":"Eksperyment","reason":"One-shot i auto-reload są kompletne w A5."},
|
||||
{"id":"A5","label":"FLOW","status":"enabled","subtitle":"E01–E10: commands i expiry"},
|
||||
{"id":"A6","label":"STATE","status":"enabled","subtitle":"Inactive, active, callback, reload"},
|
||||
{"id":"A7","label":"RUNTIME","status":"enabled","subtitle":"Daemon TCB, callback stack i tick"},
|
||||
{"id":"A8","label":"PATTERNS","status":"unavailable","subtitle":"Wzorce","reason":"Wzorzec callback adapter jest już jawny w A3; karta skupia się na semantyce daemon task."}
|
||||
],
|
||||
"artifact":{"source":"src/tasks/task01_software_timers.c","elf":"build/task01_software_timers/prog.elf","image":"build/task01_software_timers/prog.bin"},
|
||||
"strategies":[
|
||||
{"id":"code.timer-contract","kind":"code","prerequisites":["źródło FC06"],"layout":["FreeRTOSConfig","TimerTag","callback"],"commands":["rg -n 'configTIMER|TimerTag|fc06_timer_callback' include src"],"expected_observations":["daemon ma priorytet 3 i własny stack","jeden callback rozróżnia dwa IDs"],"assertions":["callback nie blokuje","TimerHandle_t nie jest application task handle"],"evidence_fields":["code_ref","config","symbol"]},
|
||||
{"id":"run.commands","kind":"run","prerequisites":["Hazard3 online","czysta RAM"],"layout":["controller","timer queue","daemon","active lists"],"commands":["print g_fc06.start_accepted","print g_fc06.reset_accepted","print g_fc06.change_accepted"],"expected_observations":["pdPASS oznacza enqueue","callback następuje później"],"assertions":["zero block time","queue length 8"],"evidence_fields":["event","accepted","tick","PC","timestamp"]},
|
||||
{"id":"run.callback","kind":"run","prerequisites":["timer active"],"layout":["callback source","pvTimerID","daemon TCB","SP"],"commands":["print g_fc06.daemon_seen","print g_fc06.callback_sp","print g_fc06.auto_tick"],"expected_observations":["callback wykonuje daemon","ID wskazuje TimerTag"],"assertions":["daemon != controller","callback SP != controller SP"],"evidence_fields":["timer kind","daemon handle","SP","tick"]},
|
||||
{"id":"run.pass","kind":"run","prerequisites":["controller_done=1"],"layout":["timeline","counts","digest"],"commands":["print g_fc06","print g_fc06_pass"],"expected_observations":["auto 2/4/7","one-shot 6 raz"],"assertions":["10 records","digest stabilny"],"evidence_fields":["PASS","timeline","digest","ELF hash","timestamp"]}
|
||||
],
|
||||
"checkpoints":{
|
||||
"task01.config":{"event_id":"E01","stop":{"symbol":"fc06_checkpoint_committed","offset":0,"condition":"$a0 == 1"},"verify":{"expressions":[{"expr":"g_fc06_event_count","equals":1},{"expr":"g_fc06.one_shot == 0","equals":1}]}},
|
||||
"task01.started":{"event_id":"E02","stop":{"symbol":"fc06_checkpoint_committed","offset":0,"condition":"$a0 == 2"},"verify":{"expressions":[{"expr":"g_fc06.start_accepted","equals":1},{"expr":"g_fc06.auto_count","equals":0}]}},
|
||||
"task01.reset":{"event_id":"E03","stop":{"symbol":"fc06_checkpoint_committed","offset":0,"condition":"$a0 == 3"},"verify":{"expressions":[{"expr":"g_fc06.reset_accepted","equals":1},{"expr":"g_fc06.one_shot_count","equals":0}]}},
|
||||
"task01.auto-1":{"event_id":"E04","stop":{"symbol":"fc06_checkpoint_committed","offset":0,"condition":"$a0 == 4"},"verify":{"expressions":[{"expr":"g_fc06.auto_count","equals":1},{"expr":"g_fc06.auto_tick[0]","equals":2}]}},
|
||||
"task01.auto-2":{"event_id":"E05","stop":{"symbol":"fc06_checkpoint_committed","offset":0,"condition":"$a0 == 5"},"verify":{"expressions":[{"expr":"g_fc06.auto_count","equals":2},{"expr":"g_fc06.auto_tick[1]","equals":4}]}},
|
||||
"task01.period-change":{"event_id":"E06","stop":{"symbol":"fc06_checkpoint_committed","offset":0,"condition":"$a0 == 6"},"verify":{"expressions":[{"expr":"g_fc06.change_accepted","equals":1},{"expr":"g_fc06.auto_count","equals":2}]}},
|
||||
"task01.one-shot":{"event_id":"E07","stop":{"symbol":"fc06_checkpoint_committed","offset":0,"condition":"$a0 == 7"},"verify":{"expressions":[{"expr":"g_fc06.one_shot_count","equals":1},{"expr":"g_fc06.one_shot_tick","equals":6}]}},
|
||||
"task01.auto-3":{"event_id":"E08","stop":{"symbol":"fc06_checkpoint_committed","offset":0,"condition":"$a0 == 8"},"verify":{"expressions":[{"expr":"g_fc06.auto_count","equals":3},{"expr":"g_fc06.auto_tick[2]","equals":7}]}},
|
||||
"task01.stopped":{"event_id":"E09","stop":{"symbol":"fc06_checkpoint_committed","offset":0,"condition":"$a0 == 9"},"verify":{"expressions":[{"expr":"g_fc06.stop_accepted","equals":1},{"expr":"g_fc06.controller_done","equals":1}]}},
|
||||
"task01.pass":{"event_id":"E10","stop":{"symbol":"fc06_checkpoint_committed","offset":0,"condition":"$a0 == 10"},"verify":{"expressions":[{"expr":"g_fc06_pass","equals":1},{"expr":"g_fc06_event_count","equals":10},{"expr":"g_fc06.daemon_seen != g_fc06.controller","equals":1}]}}
|
||||
},
|
||||
"sections":[
|
||||
{"id":"A1","label":"CONTEXT","title":"Context","order":10,"orientation":"landscape","description":"Dwie płaszczyzny: application command i daemon execution.","content_tex":"Task aplikacyjny nie wywołuje callbacku. Wysyła komendę do kolejki, a timer daemon aktualizuje listy i później uruchamia callback.","assets":[{"stem":"a1-context","title":"A1 · granice timer service","caption":"A1 CONTEXT — controller, command queue, daemon i callback.","label":"fig:a1-context","alt":"Warstwy software timer service.","phases":[{"id":"context","label":"COMMAND / DAEMON / CALLBACK","steps":[
|
||||
{"id":"app","number":1,"label":"application submits commands","mode":"RUN","event_id":"E02","strategy_ref":"run.commands","svg_label":"01","code_ref":"src/tasks/task01_software_timers.c:122","snapshot_ref":"task01.started","description":"API zwraca stan wysłania komendy.","evidence":"start_accepted=1."},
|
||||
{"id":"queue","number":2,"label":"timer queue decouples execution","mode":"CODE","strategy_ref":"code.timer-contract","svg_label":"02","code_ref":"include/FreeRTOSConfig.h:29","description":"Długość 8 ogranicza pending commands.","evidence":"configTIMER_QUEUE_LENGTH."},
|
||||
{"id":"daemon","number":3,"label":"daemon owns command processing","mode":"RUN","event_id":"E04","strategy_ref":"run.callback","svg_label":"03","code_ref":"src/tasks/task01_software_timers.c:77","snapshot_ref":"task01.auto-1","description":"Callback widzi daemon handle.","evidence":"daemon priority 3."},
|
||||
{"id":"lists","number":4,"label":"active lists hold expiry","mode":"RUN","event_id":"E03","strategy_ref":"run.commands","svg_label":"04","code_ref":"src/tasks/task01_software_timers.c:131","snapshot_ref":"task01.reset","description":"Reset przesuwa one-shot.","evidence":"brak callbacku w tick 5."},
|
||||
{"id":"callback","number":5,"label":"callback records only","mode":"RUN","event_id":"E07","strategy_ref":"run.callback","svg_label":"05","code_ref":"src/tasks/task01_software_timers.c:68","snapshot_ref":"task01.one-shot","description":"Krótka funkcja na daemon stack.","evidence":"one_shot_count=1."}
|
||||
]}]}]},
|
||||
{"id":"A3","label":"DISPATCH","title":"Dispatch","order":30,"orientation":"landscape","description":"Handle → Timer_t → callback → TimerTag.","content_tex":"Callback otrzymuje nieprzezroczysty TimerHandle_t. pvTimerGetTimerID odzyskuje jawny kontekst aplikacyjny bez globalnej tablicy rozgałęzień po adresach.","assets":[{"stem":"a3-dispatch","title":"A3 · callback dispatch","caption":"A3 DISPATCH — jeden callback i dwa typowane IDs.","label":"fig:a3-dispatch","alt":"Mechanizm dispatch software timera.","phases":[{"id":"dispatch","label":"HANDLE / ID / CALLBACK","steps":[
|
||||
{"id":"handle","number":1,"label":"opaque handle identifies Timer_t","mode":"CODE","strategy_ref":"code.timer-contract","svg_label":"01","code_ref":"include/task01_software_timers.h:37","description":"Aplikacja nie czyta pól Timer_t.","evidence":"TimerHandle_t."},
|
||||
{"id":"timer","number":2,"label":"kernel stores callback and ID","mode":"CODE","strategy_ref":"code.timer-contract","svg_label":"02","code_ref":"src/tasks/task01_software_timers.c:249","description":"xTimerCreate wiąże funkcję z TimerTag.","evidence":"one-shot i auto."},
|
||||
{"id":"callback","number":3,"label":"daemon invokes shared callback","mode":"RUN","event_id":"E04","strategy_ref":"run.callback","svg_label":"03","code_ref":"src/tasks/task01_software_timers.c:68","snapshot_ref":"task01.auto-1","description":"Wywołanie jest synchroniczne w daemon task.","evidence":"PC w callback, daemon SP."},
|
||||
{"id":"tag","number":4,"label":"pvTimerID restores kind","mode":"RUN","event_id":"E07","strategy_ref":"run.callback","svg_label":"04","code_ref":"src/tasks/task01_software_timers.c:70","snapshot_ref":"task01.one-shot","description":"Marker chroni kontrakt context.","evidence":"ONE_SHOT versus AUTO."},
|
||||
{"id":"evidence","number":5,"label":"callback commits bounded record","mode":"RUN","event_id":"E08","strategy_ref":"run.callback","svg_label":"05","code_ref":"src/tasks/task01_software_timers.c:89","snapshot_ref":"task01.auto-3","description":"Payload to expiry tick.","evidence":"E08 tick 7."}
|
||||
]}]}]},
|
||||
{"id":"A5","label":"FLOW","title":"Flow","order":40,"orientation":"landscape","description":"E01–E10: enqueue, process, expiry i PASS.","content_tex":"Komenda i callback są osobnymi momentami. Oś 2/4/6/7 wynika z okresów, resetu i change period.","assets":[{"stem":"a5-flow","diagram_kind":"sequence","title":"A5 · timer replay","caption":"A5 FLOW — one-shot reset i auto period change.","label":"fig:a5-flow","alt":"Sekwencja controller, daemon i timers.","phases":[{"id":"flow","label":"COMMANDS / EXPIRY / PASS","steps":[
|
||||
{"id":"config","number":1,"label":"configuration committed","mode":"RUN","event_id":"E01","strategy_ref":"run.commands","svg_label":"01","code_ref":"src/tasks/task01_software_timers.c:247","snapshot_ref":"task01.config","description":"Timer objects jeszcze nie istnieją.","evidence":"event count 1."},
|
||||
{"id":"start","number":2,"label":"two starts accepted","mode":"RUN","event_id":"E02","strategy_ref":"run.commands","svg_label":"02","code_ref":"src/tasks/task01_software_timers.c:122","snapshot_ref":"task01.started","description":"Daemon przetwarza dwa commands.","evidence":"start_accepted=1."},
|
||||
{"id":"reset","number":3,"label":"one-shot reset at tick 1","mode":"RUN","event_id":"E03","strategy_ref":"run.commands","svg_label":"03","code_ref":"src/tasks/task01_software_timers.c:131","snapshot_ref":"task01.reset","description":"Expiry przesuwa się z 5 do 6.","evidence":"reset_accepted=1."},
|
||||
{"id":"auto1","number":4,"label":"auto callback at tick 2","mode":"RUN","event_id":"E04","strategy_ref":"run.callback","svg_label":"04","code_ref":"src/tasks/task01_software_timers.c:87","snapshot_ref":"task01.auto-1","description":"Pierwszy period=2.","evidence":"auto_count=1."},
|
||||
{"id":"auto2","number":5,"label":"auto callback at tick 4","mode":"RUN","event_id":"E05","strategy_ref":"run.callback","svg_label":"05","code_ref":"src/tasks/task01_software_timers.c:87","snapshot_ref":"task01.auto-2","description":"Drugi period=2.","evidence":"auto_count=2."},
|
||||
{"id":"change","number":6,"label":"period changed to 3","mode":"RUN","event_id":"E06","strategy_ref":"run.commands","svg_label":"06","code_ref":"src/tasks/task01_software_timers.c:147","snapshot_ref":"task01.period-change","description":"Nowy termin liczony od change command.","evidence":"change_accepted=1."},
|
||||
{"id":"one","number":7,"label":"one-shot callback at tick 6","mode":"RUN","event_id":"E07","strategy_ref":"run.callback","svg_label":"07","code_ref":"src/tasks/task01_software_timers.c:96","snapshot_ref":"task01.one-shot","description":"Fires exactly once.","evidence":"count=1, tick=6."},
|
||||
{"id":"auto3","number":8,"label":"auto callback at tick 7","mode":"RUN","event_id":"E08","strategy_ref":"run.callback","svg_label":"08","code_ref":"src/tasks/task01_software_timers.c:87","snapshot_ref":"task01.auto-3","description":"Nowy okres wynosi 3.","evidence":"4→7."},
|
||||
{"id":"stop","number":9,"label":"auto stop accepted","mode":"RUN","event_id":"E09","strategy_ref":"run.commands","svg_label":"09","code_ref":"src/tasks/task01_software_timers.c:163","snapshot_ref":"task01.stopped","description":"Controller kończy po trzech callbackach.","evidence":"controller_done=1."},
|
||||
{"id":"pass","number":10,"label":"verifier commits PASS","mode":"RUN","event_id":"E10","strategy_ref":"run.pass","svg_label":"10","code_ref":"src/tasks/task01_software_timers.c:194","snapshot_ref":"task01.pass","description":"Timeline, handles, stacks i digest.","evidence":"PASS=1."}
|
||||
]}]}]},
|
||||
{"id":"A6","label":"STATE","title":"State","order":50,"orientation":"landscape","description":"Stan logiczny timera i pending command.","content_tex":"pdPASS oznacza pending command, nie natychmiastową zmianę active list. Auto-reload wraca do ACTIVE, one-shot do INACTIVE.","assets":[{"stem":"a6-state","title":"A6 · timer lifecycle","caption":"A6 STATE — command pending, active, callback i reload.","label":"fig:a6-state","alt":"Maszyna stanów software timera.","phases":[{"id":"state","label":"INACTIVE / ACTIVE / CALLBACK","steps":[
|
||||
{"id":"inactive","number":1,"label":"new timer is inactive","mode":"CODE","strategy_ref":"code.timer-contract","svg_label":"01","code_ref":"src/tasks/task01_software_timers.c:249","description":"Create nie startuje timera.","evidence":"Brak expiry przed command."},
|
||||
{"id":"active","number":2,"label":"processed start makes active","mode":"RUN","event_id":"E02","strategy_ref":"run.commands","svg_label":"02","code_ref":"src/tasks/task01_software_timers.c:122","snapshot_ref":"task01.started","description":"Daemon dodaje timer do active list.","evidence":"Commands accepted."},
|
||||
{"id":"pending","number":3,"label":"reset/change travel through queue","mode":"RUN","event_id":"E06","strategy_ref":"run.commands","svg_label":"03","code_ref":"src/tasks/task01_software_timers.c:147","snapshot_ref":"task01.period-change","description":"Stan przejściowy jest asynchroniczny.","evidence":"pdPASS przed kolejnym expiry."},
|
||||
{"id":"callback","number":4,"label":"expiry invokes callback","mode":"RUN","event_id":"E07","strategy_ref":"run.callback","svg_label":"04","code_ref":"src/tasks/task01_software_timers.c:68","snapshot_ref":"task01.one-shot","description":"Callback działa w daemon task.","evidence":"tick 6."},
|
||||
{"id":"reload","number":5,"label":"auto timer reloads","mode":"RUN","event_id":"E08","strategy_ref":"run.callback","svg_label":"05","code_ref":"src/tasks/task01_software_timers.c:80","snapshot_ref":"task01.auto-3","description":"One-shot nie wraca do active list.","evidence":"auto_count=3."},
|
||||
{"id":"stopped","number":6,"label":"stop removes active timer","mode":"RUN","event_id":"E09","strategy_ref":"run.commands","svg_label":"06","code_ref":"src/tasks/task01_software_timers.c:163","snapshot_ref":"task01.stopped","description":"Brak czwartego callbacku.","evidence":"stop_accepted=1."}
|
||||
]}]}]},
|
||||
{"id":"A7","label":"RUNTIME","title":"Runtime","order":60,"orientation":"landscape","description":"Controller i callback mają różne TCB oraz stosy.","content_tex":"A7 dowodzi, że callback nie jest kontynuacją controller task. Jego PC i SP należą do timer daemon, a TimerTag pozostaje application context.","assets":[{"stem":"a7-runtime","title":"A7 · daemon runtime","caption":"A7 RUNTIME — timeline, Timer_t, daemon handle i stos.","label":"fig:a7-runtime","alt":"Runtime software timer callback.","phases":[{"id":"runtime","label":"TCB / STACK / TIMER / TICK","steps":[
|
||||
{"id":"exp","number":1,"label":"experiment owns bounded results","mode":"RUN","event_id":"E10","strategy_ref":"run.pass","svg_label":"01","code_ref":"src/tasks/task01_software_timers.c:194","snapshot_ref":"task01.pass","description":"Wynik nie zależy od prywatnych list.","evidence":"timeline i counts."},
|
||||
{"id":"controller","number":2,"label":"controller submits commands","mode":"RUN","event_id":"E06","strategy_ref":"run.commands","svg_label":"02","code_ref":"src/tasks/task01_software_timers.c:110","snapshot_ref":"task01.period-change","description":"Priority 2 i controller_sp.","evidence":"current TCB=controller."},
|
||||
{"id":"daemon","number":3,"label":"daemon executes callbacks","mode":"RUN","event_id":"E07","strategy_ref":"run.callback","svg_label":"03","code_ref":"src/tasks/task01_software_timers.c:77","snapshot_ref":"task01.one-shot","description":"Priority 3 i callback_sp.","evidence":"daemon_seen != controller."},
|
||||
{"id":"one","number":4,"label":"one-shot expires once","mode":"RUN","event_id":"E07","strategy_ref":"run.callback","svg_label":"04","code_ref":"src/tasks/task01_software_timers.c:94","snapshot_ref":"task01.one-shot","description":"Reset ustala tick 6.","evidence":"count=1."},
|
||||
{"id":"auto","number":5,"label":"auto timer owns changing period","mode":"RUN","event_id":"E08","strategy_ref":"run.callback","svg_label":"05","code_ref":"src/tasks/task01_software_timers.c:80","snapshot_ref":"task01.auto-3","description":"Period change zachowuje ten sam handle.","evidence":"2/4/7."},
|
||||
{"id":"events","number":6,"label":"ten records form digest","mode":"RUN","event_id":"E10","strategy_ref":"run.pass","svg_label":"06","code_ref":"src/tasks/task01_software_timers.c:208","snapshot_ref":"task01.pass","description":"Marker każdego rekordu jest zapisywany na końcu.","evidence":"stable digest."}
|
||||
]}]}]}
|
||||
],
|
||||
"learning":{"model_label":"Model timer service","model":"Uczeń rozdziela command queue, active list, daemon task i callback.","replay_label":"Replay one-shot i auto-reload","replay":"Uczeń odtwarza E01–E10 i wiąże command acceptance, expiry tick, TimerTag, daemon TCB i stack.","criterion":"Program kończy się PASS; one-shot=6 raz; auto=2/4/7; komendy są accepted; daemon i controller mają różne handles/SP; digest jest deterministyczny.","requirement":"Zaprojektowanie i zbadanie deterministycznego eksperymentu software timers."},
|
||||
"task":{"short_label":"software timers","title":"One-shot, auto-reload i timer daemon","prompt_tex":"Przejdź po A1, A3, A5, A6 i A7. Odtwórz E01–E10 i rozdziel moment enqueue command od callback execution.","conclusion_tex":"Software timer jest obsługiwany przez daemon task. API wysyła komendę, aktywna lista przechowuje expiry, a callback działa później na stosie daemon task.","flow":[
|
||||
{"kind":"block","id":"model","title":"A — model timer service","content_tex":"Przewidź oś czasu i właściciela każdego wywołania.","steps":[{"id":"commands","title":"Rozdziel command i callback.","content_tex":"Wskaż dwa różne momenty dowodowe."},{"id":"timeline","title":"Przewidź 2/4/6/7.","content_tex":"Uwzględnij reset i change period."}]},
|
||||
{"kind":"block","id":"replay","title":"B — replay Hazard3","content_tex":"Zbadaj controller i daemon w osobnych layoutach.","steps":[{"id":"enqueue","title":"Zbadaj E02/E03/E06.","content_tex":"Sprawdź accepted flags i command source."},{"id":"callbacks","title":"Zbadaj E04/E05/E07/E08.","content_tex":"Porównaj TimerTag, PC, SP i tick."},{"id":"pass","title":"Zbadaj E09/E10.","content_tex":"Sprawdź brak czwartego callbacku i digest."}]},
|
||||
{"kind":"exercise","id":"late-command","title":"Ćwiczenie — command queue pressure","prompt_tex":"Zmniejsz configTIMER_QUEUE_LENGTH do 1 i przygotuj kontrolowany burst dwóch komend z block time 0. Obsłuż pdFAIL bez utraty poprzedniego stanu timera.","evidence_tex":"Snapshot queue pressure, jawny expected failure i deterministyczny final state.","criterion":"Uczeń nie interpretuje pdFAIL jako wykonanej komendy i nie blokuje callbacku.","based_on":["model","replay"]}
|
||||
]}
|
||||
}
|
||||
Reference in New Issue
Block a user