Files
lab-rv32i-freertos-c-isr-se…/json/card_spec.json
T

83 lines
20 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"card":{"number":"07","slug":"isr-semaphore","title":"Przerwanie zewnętrzne i semaphore FromISR","topic":"Hazard3 MEI, xISRStack, xSemaphoreGiveFromISR i yield","status":"Gotowa","version":"v00.01"},
"front":{"goal":"Uczeń rozdziela task context od ISR context, używa API FromISR i dowodzi natychmiastowego przełączenia do wyższego taska.","scope":"Prawdziwe wejście IRQ5 testbencha wywołuje machine external interrupt. Port zapisuje pełny kontekst i przechodzi na xISRStack. ISR czyści źródło, daje binary semaphore i wywołuje portYIELD_FROM_ISR. Waiter priority 3 działa przed powrotem do stimulus priority 2."},
"viewpoints":[
{"id":"A1","label":"CONTEXT","status":"enabled","subtitle":"Testbench → Hazard3 → port → task"},
{"id":"A2","label":"STRUCTURE","status":"unavailable","subtitle":"Struktura","reason":"Obiekty runtime i ich relacje są pełniej pokazane w A7."},
{"id":"A3","label":"DISPATCH","status":"enabled","subtitle":"Vector, trap, FromISR i context switch"},
{"id":"A4","label":"APPLICATION","status":"unavailable","subtitle":"Eksperyment","reason":"Konkretny handoff jest kompletny w A5."},
{"id":"A5","label":"FLOW","status":"enabled","subtitle":"E01E09: trigger do PASS"},
{"id":"A6","label":"STATE","status":"enabled","subtitle":"Semaphore empty/given/taken i task state"},
{"id":"A7","label":"RUNTIME","status":"enabled","subtitle":"Waiter, stimulus i xISRStack"},
{"id":"A8","label":"PATTERNS","status":"unavailable","subtitle":"Wzorce","reason":"Deferred interrupt handoff jest tu mechanizmem wykonania A3/A5, nie osobnym wzorcem aplikacyjnym."}
],
"artifact":{"source":"src/tasks/task01_isr_semaphore.c","elf":"build/task01_isr_semaphore/prog.elf","image":"build/task01_isr_semaphore/prog.bin"},
"strategies":[
{"id":"code.irq-contract","kind":"code","prerequisites":["źródło FC07"],"layout":["vector bridge","FreeRTOS trap","application ISR","config"],"commands":["rg -n 'isr_external_irq|application_interrupt|GiveFromISR|SET_IRQ' src include"],"expected_observations":["external vector trafia do pełnego trap path","ISR API ma osobny suffix"],"assertions":["source clear poprzedza give","port ma 256-word ISR stack"],"evidence_fields":["code_ref","symbol","ABI"]},
{"id":"run.trap","kind":"run","prerequisites":["Hazard3 online","IRQ5 enabled","czysta RAM"],"layout":["mcause","mepc","xISRStack","source register"],"commands":["print/x g_fc07.mcause","print/x g_fc07.isr_sp","print g_fc07.irq_source_after_clear"],"expected_observations":["mcause=0x8000000b","ISR SP różny od task SP","source=0"],"assertions":["real MEI","full context saved"],"evidence_fields":["mcause","SP","PC","IRQ register","timestamp"]},
{"id":"run.handoff","kind":"run","prerequisites":["waiter Blocked na binary semaphore"],"layout":["semaphore","xHPW","waiter/stimulus TCB","event order"],"commands":["print g_fc07.higher_priority_task_woken","print g_fc07_events"],"expected_observations":["xHPW=pdTRUE","E07 poprzedza E08"],"assertions":["waiter value widzi stimulus_after=0","stimulus po resume widzi waiter_ran=1"],"evidence_fields":["state","actor","value","PC/SP"]},
{"id":"run.pass","kind":"run","prerequisites":["E01E08 committed"],"layout":["nine records","stack separation","digest"],"commands":["print g_fc07_pass","print g_fc07.digest"],"expected_observations":["PASS=1","nine sequential markers"],"assertions":["three distinct stack contexts","digest stable"],"evidence_fields":["PASS","digest","source blob","ELF hash","timestamp"]}
],
"checkpoints":{
"task01.config":{"event_id":"E01","stop":{"symbol":"fc07_checkpoint_committed","offset":0,"condition":"$a0 == 1"},"verify":{"expressions":[{"expr":"g_fc07_event_count","equals":1},{"expr":"g_fc07.semaphore == 0","equals":1}]}},
"task01.waiter-block":{"event_id":"E02","stop":{"symbol":"fc07_checkpoint_committed","offset":0,"condition":"$a0 == 2"},"verify":{"expressions":[{"expr":"g_fc07.waiter_sp != 0","equals":1},{"expr":"g_fc07.waiter_ran","equals":0}]}},
"task01.before-trigger":{"event_id":"E03","stop":{"symbol":"fc07_checkpoint_committed","offset":0,"condition":"$a0 == 3"},"verify":{"expressions":[{"expr":"g_fc07.stimulus_sp != 0","equals":1},{"expr":"g_fc07.stimulus_after","equals":0}]}},
"task01.isr-entry":{"event_id":"E04","stop":{"symbol":"fc07_checkpoint_committed","offset":0,"condition":"$a0 == 4"},"verify":{"expressions":[{"expr":"g_fc07.mcause","equals":2147483659},{"expr":"g_fc07.isr_sp != g_fc07.stimulus_sp","equals":1}]}},
"task01.source-clear":{"event_id":"E05","stop":{"symbol":"fc07_checkpoint_committed","offset":0,"condition":"$a0 == 5"},"verify":{"expressions":[{"expr":"g_fc07.irq_source_after_clear","equals":0},{"expr":"g_fc07.waiter_ran","equals":0}]}},
"task01.give-yield":{"event_id":"E06","stop":{"symbol":"fc07_checkpoint_committed","offset":0,"condition":"$a0 == 6"},"verify":{"expressions":[{"expr":"g_fc07.higher_priority_task_woken","equals":1},{"expr":"g_fc07.stimulus_after","equals":0}]}},
"task01.waiter-runs":{"event_id":"E07","stop":{"symbol":"fc07_checkpoint_committed","offset":0,"condition":"$a0 == 7"},"verify":{"expressions":[{"expr":"g_fc07.waiter_ran","equals":1},{"expr":"g_fc07.stimulus_after","equals":0}]}},
"task01.stimulus-resumes":{"event_id":"E08","stop":{"symbol":"fc07_checkpoint_committed","offset":0,"condition":"$a0 == 8"},"verify":{"expressions":[{"expr":"g_fc07.waiter_ran","equals":1},{"expr":"g_fc07.stimulus_after","equals":1}]}},
"task01.pass":{"event_id":"E09","stop":{"symbol":"fc07_checkpoint_committed","offset":0,"condition":"$a0 == 9"},"verify":{"expressions":[{"expr":"g_fc07_pass","equals":1},{"expr":"g_fc07_event_count","equals":9},{"expr":"g_fc07_events[6].actor","equals":2}]}}
},
"sections":[
{"id":"A1","label":"CONTEXT","title":"Context","order":10,"orientation":"landscape","description":"Od fizycznej linii IRQ do odblokowanego taska.","content_tex":"Źródło testbencha, kontroler Hazard3, port FreeRTOS, ISR aplikacji i waiter są osobnymi warstwami odpowiedzialności.","assets":[{"stem":"a1-context","title":"A1 · granice interrupt path","caption":"A1 CONTEXT — IRQ5 do high-priority waitera.","label":"fig:a1-context","alt":"Warstwy realnego external IRQ.","phases":[{"id":"context","label":"SOURCE / CPU / PORT / ISR / TASK","steps":[
{"id":"source","number":1,"label":"testbench drives a real IRQ line","mode":"RUN","event_id":"E03","strategy_ref":"run.trap","svg_label":"01","code_ref":"src/tasks/task01_isr_semaphore.c:156","snapshot_ref":"task01.before-trigger","description":"MMIO store ustawia bit wejścia CPU.","evidence":"IRQ mask bit 5."},
{"id":"hazard","number":2,"label":"Hazard3 raises machine external interrupt","mode":"RUN","event_id":"E04","strategy_ref":"run.trap","svg_label":"02","code_ref":"src/tasks/task01_isr_semaphore.c:103","snapshot_ref":"task01.isr-entry","description":"To nie jest tick hook.","evidence":"mcause=MEI."},
{"id":"port","number":3,"label":"port saves full task context","mode":"CODE","strategy_ref":"code.irq-contract","svg_label":"03","code_ref":"src/common/hazard3_freertos_traps.S:24","description":"Vector skacze do official trap handler.","evidence":"xISRStack."},
{"id":"isr","number":4,"label":"application ISR clears and gives","mode":"RUN","event_id":"E06","strategy_ref":"run.handoff","svg_label":"04","code_ref":"src/tasks/task01_isr_semaphore.c:99","snapshot_ref":"task01.give-yield","description":"API FromISR ustawia xHPW.","evidence":"source=0, xHPW=1."},
{"id":"waiter","number":5,"label":"higher waiter consumes semaphore","mode":"RUN","event_id":"E07","strategy_ref":"run.handoff","svg_label":"05","code_ref":"src/tasks/task01_isr_semaphore.c:135","snapshot_ref":"task01.waiter-runs","description":"Context restore wybiera waiter TCB.","evidence":"stimulus_after=0."}
]}]}]},
{"id":"A3","label":"DISPATCH","title":"Dispatch","order":30,"orientation":"landscape","description":"Pełny context save umożliwia yield z ISR.","content_tex":"Samo vTaskSwitchContext w zwykłym handlerze nie wystarcza. Port musi najpierw zapisać pełny kontekst przerwanego taska, przejść na ISR stack i dopiero potem odtworzyć kontekst wybranego taska.","assets":[{"stem":"a3-dispatch","title":"A3 · external interrupt dispatch","caption":"A3 DISPATCH — vector, trap, FromISR i restore.","label":"fig:a3-dispatch","alt":"Dispatch external IRQ w porcie FreeRTOS.","phases":[{"id":"dispatch","label":"VECTOR / SAVE / ISR / RESTORE","steps":[
{"id":"vector","number":1,"label":"external vector enters port","mode":"CODE","strategy_ref":"code.irq-contract","svg_label":"01","code_ref":"src/common/hazard3_freertos_traps.S:24","description":"Strong symbol zastępuje weak handler testbencha.","evidence":"isr_external_irq."},
{"id":"trap","number":2,"label":"trap saves full context","mode":"RUN","event_id":"E04","strategy_ref":"run.trap","svg_label":"02","code_ref":"vendor/FreeRTOS-Kernel/portable/GCC/RISC-V/portASM.S:356","snapshot_ref":"task01.isr-entry","description":"Interrupted SP jest zapisany w TCB.","evidence":"mepc/mstatus/registers."},
{"id":"stack","number":3,"label":"handler runs on xISRStack","mode":"RUN","event_id":"E04","strategy_ref":"run.trap","svg_label":"03","code_ref":"src/tasks/task01_isr_semaphore.c:104","snapshot_ref":"task01.isr-entry","description":"ISR SP różni się od obu tasków.","evidence":"isr_sp."},
{"id":"app","number":4,"label":"application handler owns device protocol","mode":"RUN","event_id":"E05","strategy_ref":"run.trap","svg_label":"04","code_ref":"src/tasks/task01_isr_semaphore.c:107","snapshot_ref":"task01.source-clear","description":"Najpierw clear source.","evidence":"register=0."},
{"id":"give","number":5,"label":"FromISR unblocks waiter","mode":"RUN","event_id":"E06","strategy_ref":"run.handoff","svg_label":"05","code_ref":"src/tasks/task01_isr_semaphore.c:111","snapshot_ref":"task01.give-yield","description":"Binary semaphore staje się available.","evidence":"xHPW=1."},
{"id":"yield","number":6,"label":"yield selects waiter TCB","mode":"RUN","event_id":"E06","strategy_ref":"run.handoff","svg_label":"06","code_ref":"src/tasks/task01_isr_semaphore.c:118","snapshot_ref":"task01.give-yield","description":"Switch następuje przed mret.","evidence":"vTaskSwitchContext."},
{"id":"restore","number":7,"label":"port restores selected task","mode":"RUN","event_id":"E07","strategy_ref":"run.handoff","svg_label":"07","code_ref":"src/tasks/task01_isr_semaphore.c:135","snapshot_ref":"task01.waiter-runs","description":"CPU nie wraca od razu do stimulus.","evidence":"E07 before E08."}
]}]}]},
{"id":"A5","label":"FLOW","title":"Flow","order":40,"orientation":"landscape","description":"Jeden realny handoff E01E09.","content_tex":"Sekwencja dowodzi source-clear-before-give oraz waiter-before-interrupted-task.","assets":[{"stem":"a5-flow","diagram_kind":"sequence","title":"A5 · IRQ handoff replay","caption":"A5 FLOW — od Blocked przez ISR do natychmiastowego waitera.","label":"fig:a5-flow","alt":"Sekwencja ISR semaphore handoff.","phases":[{"id":"flow","label":"TRIGGER / ISR / YIELD / PASS","steps":[
{"id":"config","number":1,"label":"IRQ contract configured","mode":"RUN","event_id":"E01","strategy_ref":"run.trap","svg_label":"01","code_ref":"src/tasks/task01_isr_semaphore.c:238","snapshot_ref":"task01.config","description":"IRQ5 i priority są jawne.","evidence":"event count 1."},
{"id":"wait","number":2,"label":"waiter reaches empty take","mode":"RUN","event_id":"E02","strategy_ref":"run.handoff","svg_label":"02","code_ref":"src/tasks/task01_isr_semaphore.c:130","snapshot_ref":"task01.waiter-block","description":"Następnie przechodzi do Blocked.","evidence":"waiter_ran=0."},
{"id":"trigger","number":3,"label":"stimulus commits before trigger","mode":"RUN","event_id":"E03","strategy_ref":"run.trap","svg_label":"03","code_ref":"src/tasks/task01_isr_semaphore.c:155","snapshot_ref":"task01.before-trigger","description":"Kontrolowany punkt przed MMIO.","evidence":"stimulus_after=0."},
{"id":"entry","number":4,"label":"MEI enters xISRStack","mode":"RUN","event_id":"E04","strategy_ref":"run.trap","svg_label":"04","code_ref":"src/tasks/task01_isr_semaphore.c:103","snapshot_ref":"task01.isr-entry","description":"Prawdziwy async trap.","evidence":"mcause and SP."},
{"id":"clear","number":5,"label":"ISR clears hardware source","mode":"RUN","event_id":"E05","strategy_ref":"run.trap","svg_label":"05","code_ref":"src/tasks/task01_isr_semaphore.c:107","snapshot_ref":"task01.source-clear","description":"Zapobiega retrigger.","evidence":"source=0."},
{"id":"give","number":6,"label":"give requests a switch","mode":"RUN","event_id":"E06","strategy_ref":"run.handoff","svg_label":"06","code_ref":"src/tasks/task01_isr_semaphore.c:111","snapshot_ref":"task01.give-yield","description":"Wyższy task stał się Ready.","evidence":"xHPW=pdTRUE."},
{"id":"waiter","number":7,"label":"waiter runs before stimulus","mode":"RUN","event_id":"E07","strategy_ref":"run.handoff","svg_label":"07","code_ref":"src/tasks/task01_isr_semaphore.c:135","snapshot_ref":"task01.waiter-runs","description":"Natychmiastowy efekt yield.","evidence":"stimulus_after=0."},
{"id":"resume","number":8,"label":"interrupted stimulus resumes","mode":"RUN","event_id":"E08","strategy_ref":"run.handoff","svg_label":"08","code_ref":"src/tasks/task01_isr_semaphore.c:158","snapshot_ref":"task01.stimulus-resumes","description":"Dopiero po zakończeniu waitera.","evidence":"waiter_ran=1."},
{"id":"pass","number":9,"label":"verifier commits PASS","mode":"RUN","event_id":"E09","strategy_ref":"run.pass","svg_label":"09","code_ref":"src/tasks/task01_isr_semaphore.c:186","snapshot_ref":"task01.pass","description":"Sprawdza order, stacks i cause.","evidence":"PASS=1, digest."}
]}]}]},
{"id":"A6","label":"STATE","title":"State","order":50,"orientation":"portrait","description":"Semaphore state i scheduler decision.","content_tex":"GiveFromISR zmienia obiekt synchronizacyjny i stan waitera, lecz natychmiastowe wykonanie wymaga dodatkowego yield request.","assets":[{"stem":"a6-state","title":"A6 · ISR handoff state","caption":"A6 STATE — empty, given, switch i taken.","label":"fig:a6-state","alt":"Maszyna stanu semaphore handoff.","phases":[{"id":"state","label":"SEMAPHORE / TASK / SWITCH","steps":[
{"id":"empty","number":1,"label":"empty semaphore blocks waiter","mode":"RUN","event_id":"E02","strategy_ref":"run.handoff","svg_label":"01","code_ref":"src/tasks/task01_isr_semaphore.c:131","snapshot_ref":"task01.waiter-block","description":"Waiter trafia na wait list.","evidence":"Blocked."},
{"id":"pending","number":2,"label":"IRQ interrupts stimulus","mode":"RUN","event_id":"E04","strategy_ref":"run.trap","svg_label":"02","code_ref":"src/tasks/task01_isr_semaphore.c:99","snapshot_ref":"task01.isr-entry","description":"Stimulus context jest zachowany.","evidence":"mepc and SP."},
{"id":"given","number":3,"label":"give makes waiter Ready","mode":"RUN","event_id":"E06","strategy_ref":"run.handoff","svg_label":"03","code_ref":"src/tasks/task01_isr_semaphore.c:111","snapshot_ref":"task01.give-yield","description":"Semaphore nie pozostaje full.","evidence":"xHPW=true."},
{"id":"switch","number":4,"label":"yield requests context switch","mode":"RUN","event_id":"E06","strategy_ref":"run.handoff","svg_label":"04","code_ref":"src/tasks/task01_isr_semaphore.c:118","snapshot_ref":"task01.give-yield","description":"pxCurrentTCB wybiera waitera.","evidence":"vTaskSwitchContext."},
{"id":"taken","number":5,"label":"waiter consumes token","mode":"RUN","event_id":"E07","strategy_ref":"run.handoff","svg_label":"05","code_ref":"src/tasks/task01_isr_semaphore.c:131","snapshot_ref":"task01.waiter-runs","description":"Take wraca pdPASS.","evidence":"waiter Running."},
{"id":"resumed","number":6,"label":"stimulus eventually resumes","mode":"RUN","event_id":"E08","strategy_ref":"run.handoff","svg_label":"06","code_ref":"src/tasks/task01_isr_semaphore.c:158","snapshot_ref":"task01.stimulus-resumes","description":"Oryginalny mepc nie ginie.","evidence":"E08 after E07."}
]}]}]},
{"id":"A7","label":"RUNTIME","title":"Runtime","order":60,"orientation":"portrait","description":"Trzy stosy i dwa TCB w jednym trap replay.","content_tex":"ISR stack nie należy do żadnego taska. Port zapisuje interrupted task stack, wykonuje ISR na xISRStack i odtwarza stack wybranego waitera.","assets":[{"stem":"a7-runtime","title":"A7 · stack and context evidence","caption":"A7 RUNTIME — stimulus TCB, xISRStack i waiter TCB.","label":"fig:a7-runtime","alt":"Runtime trzech kontekstów stosu.","phases":[{"id":"runtime","label":"TCB / ISR STACK / RESTORE","steps":[
{"id":"waiter","number":1,"label":"waiter owns priority-3 stack","mode":"RUN","event_id":"E02","strategy_ref":"run.handoff","svg_label":"01","code_ref":"src/tasks/task01_isr_semaphore.c:129","snapshot_ref":"task01.waiter-block","description":"SP zapisany przed take.","evidence":"waiter_sp."},
{"id":"stimulus","number":2,"label":"stimulus owns interrupted stack","mode":"RUN","event_id":"E03","strategy_ref":"run.trap","svg_label":"02","code_ref":"src/tasks/task01_isr_semaphore.c:153","snapshot_ref":"task01.before-trigger","description":"Priority 2.","evidence":"stimulus_sp."},
{"id":"isr","number":3,"label":"trap switches to xISRStack","mode":"RUN","event_id":"E04","strategy_ref":"run.trap","svg_label":"03","code_ref":"src/tasks/task01_isr_semaphore.c:104","snapshot_ref":"task01.isr-entry","description":"ISR nie zużywa task stack.","evidence":"isr_sp distinct."},
{"id":"sem","number":4,"label":"semaphore links ISR and waiter","mode":"RUN","event_id":"E06","strategy_ref":"run.handoff","svg_label":"04","code_ref":"src/tasks/task01_isr_semaphore.c:111","snapshot_ref":"task01.give-yield","description":"Only FromISR API in ISR.","evidence":"xHPW."},
{"id":"evidence","number":5,"label":"event order proves selected return","mode":"RUN","event_id":"E09","strategy_ref":"run.pass","svg_label":"05","code_ref":"src/tasks/task01_isr_semaphore.c:195","snapshot_ref":"task01.pass","description":"E07 waiter, E08 stimulus.","evidence":"nine committed records."}
]}]}]}
],
"learning":{"model_label":"Model ISR handoff","model":"Uczeń rozdziela hardware source, trap port, xISRStack, FromISR API i task scheduling.","replay_label":"Replay realnego MEI","replay":"Uczeń odtwarza E01E09 i wiąże mcause, source clear, xHPW, trzy SP oraz kolejność context restore.","criterion":"PASS; mcause=MEI; source=0 przed give; xHPW=1; waiter E07 przed stimulus E08; trzy różne stosy; deterministyczny digest.","requirement":"Zaprojektowanie i zbadanie bezpiecznego handoff z prawdziwego przerwania do taska FreeRTOS."},
"task":{"short_label":"ISR semaphore","title":"External IRQ → FromISR semaphore → high-priority task","prompt_tex":"Przejdź po A1, A3, A5, A6 i A7. Odtwórz E01E09 i udowodnij source-clear-before-give oraz waiter-before-stimulus.","conclusion_tex":"ISR działa na osobnym stacku i używa FromISR API. xHigherPriorityTaskWoken jest informacją dla portu; dopiero portYIELD_FROM_ISR powoduje restore kontekstu wyższego taska przed powrotem do przerwanego kodu.","flow":[
{"kind":"block","id":"contract","title":"A — kontrakt przerwania","content_tex":"Rozdziel source, vector, port, ISR i task.","steps":[{"id":"order","title":"Ustal kolejność ISR.","content_tex":"Najpierw clear, potem give, na końcu yield."},{"id":"predict","title":"Przewidź context restore.","content_tex":"Wskaż, czy pierwszy wykona się waiter czy stimulus."}]},
{"kind":"block","id":"replay","title":"B — replay Hazard3","content_tex":"Każdy RUN zaczyna się od czystej RAM i wyłączonego źródła.","steps":[{"id":"trap","title":"Zbadaj E03E05.","content_tex":"Zapisz mcause, mepc, isr_sp i source state."},{"id":"handoff","title":"Zbadaj E06E08.","content_tex":"Porównaj xHPW i kolejność tasków."},{"id":"pass","title":"Zbadaj E09.","content_tex":"Sprawdź trzy stosy, markers i digest."}]},
{"kind":"exercise","id":"no-yield","title":"Ćwiczenie — brak yield request","prompt_tex":"W kontrolowanym wariancie pomiń portYIELD_FROM_ISR, zachowując GiveFromISR. Zmierz, kiedy waiter zacznie działać i wskaż następny legalny scheduling point.","evidence_tex":"Dwa timestampowane ślady E07/E08 z yield i bez yield oraz opis różnicy.","criterion":"Uczeń nie myli odblokowania taska z natychmiastowym wykonaniem i przywraca poprawny yield w wersji finalnej.","based_on":["contract","replay"]}
]}
}