feat: add lab-rv32i-c-interrupts card

This commit is contained in:
user
2026-07-21 19:14:20 +02:00
commit 8820c2a22e
23 changed files with 4433 additions and 0 deletions
+109
View File
@@ -0,0 +1,109 @@
{
"card": {
"number": "10",
"slug": "traps-interrupt-control",
"title": "Pułapki i sterowanie przerwaniami RV32I",
"topic": "ecall, mepc, mcause, machine software IRQ oraz bramki mie/mstatus",
"status": "Gotowa",
"version": "v00.01",
"revision_date": "2026-07-20T00:00:00+02:00"
},
"scope_headers": ["Typ", "Task", "Idea", "Waga"],
"front": {
"goal": "Uczeń odróżnia synchroniczny wyjątek od asynchronicznego przerwania, wyprowadza adres wznowienia z mepc oraz dowodzi, że samo pending nie wystarcza bez indywidualnej i globalnej bramki enable.",
"scope": "Trzy obrazy RV32I działają na rzeczywistym modelu Hazard3. Task01 wykonuje \\texttt{ecall}; Task02 używa kontrolowanego machine software interrupt; Task03 mierzy macierz pending, \\texttt{mie.MSIE} i \\texttt{mstatus.MIE}. Peryferia pojawiają się dopiero w C12 i C13."
},
"learning": {
"reasoning_label": "Cause, gate i resume",
"reasoning": "Uczeń osobno zapisuje źródło zdarzenia, mcause, mepc, stan pending, obie bramki enable i efekt po mret.",
"practice_label": "Pułapka widoczna w listingu",
"practice": "Uczeń uruchamia trzy obrazy Hazard3 i wiąże raporty runtime z instrukcjami ecall, CSR, wektorami i mret.",
"criterion": "Task01: jeden ecall daje mcause=11, mepc jest zwiększone o 4 i kod normalny wznawia pracę. Task02: software IRQ ma mcause=0x80000003, pending 1 przed i w ISR, 0 po clear oraz jedno wznowienie. Task03: pending pozostaje zablokowane osobno bez MSIE i bez MIE, a jest dostarczone dokładnie raz dopiero z obiema bramkami.",
"requirement": "Konfiguracja i analiza machine-mode trap z jawnym kontraktem źródła, pending, enable, handlera i adresu wznowienia."
},
"sections": [
{
"title": "1 — Wyjątek synchroniczny wskazuje instrukcję",
"content_tex": "\\texttt{ecall} jest wykonywany przez bieżący strumień instrukcji, więc \\texttt{mcause=11} nie ma ustawionego bitu interrupt, a \\texttt{mepc} wskazuje samo \\texttt{ecall}. Handler Task01 świadomie zapisuje \\texttt{mepc+4}; bez tego \\texttt{mret} wykonałby ponownie tę samą instrukcję. Taka polityka jest poprawna dla tego kontrolowanego ECALL, ale nie wolno automatycznie pomijać instrukcji przy każdym błędzie."
},
{
"title": "2 — Software IRQ jest asynchronicznym źródłem poziomowym",
"content_tex": "Testbench ustawia MSIP hart0, a rdzeń widzi \\texttt{mip.MSIP}. Dostarczenie wymaga dodatkowo \\texttt{mie.MSIE=1} i \\texttt{mstatus.MIE=1}. Wektor 3 prowadzi do \\texttt{isr\\_machine\\_softirq}; handler odczytuje \\texttt{mcause=0x80000003}, usuwa źródło i dopiero potem publikuje licznik. \\texttt{mret} wraca do przerwanego kodu, a nie do instrukcji wybranej ręcznie przez aplikację."
},
{
"title": "3 — Pending i enable odpowiadają na inne pytania",
"content_tex": "Pending mówi, że źródło żąda obsługi. \\texttt{mie.MSIE} dopuszcza konkretną klasę, a \\texttt{mstatus.MIE} otwiera globalną bramkę machine mode. Task03 utrzymuje to samo pending i kolejno sprawdza dwa stany z jedną zamkniętą bramką oraz stan z obiema otwartymi. Dzięki temu kod 0 dowodzi całej macierzy, a nie tylko tego, że ISR kiedyś się wykonał."
}
],
"tasks": [
{
"id": "task01",
"chapter": "ECALL",
"title": "ECALL i wznowienie pod mepc+4",
"idea_tex": "synchroniczny cause 11, mepc i mret",
"priority": "kluczowe",
"key": true,
"prompt_tex": "Przed uruchomieniem przewidź bit interrupt i kod mcause, wskaż wartość mepc oraz adres wznowienia. Wyjaśnij, co stałoby się bez zapisu mepc+4.",
"criterion": "Hazard3: count=1, mcause=0x0000000b, resume_pc-mepc=4, resumed=1, pass=1 i kod 0.",
"evidence_tex": "Adres ecall i dwa adresy mepc z raportu/listingu, cause 11, instrukcja mret oraz kod wyjścia Hazard3.",
"worksheet_step2": "mcause, mepc i adres wznowienia",
"worksheet_step3": "ecall/mret w listingu i kod wyjścia",
"viewpoints": [
{"id":"A1","status":"enabled","title":"CONTEXT — kontrolowany wyjątek M-mode","content_tex":"Granica zaczyna się na instrukcji \\texttt{ecall} w main i kończy po powrocie do kolejnej instrukcji. Nie ma zewnętrznego peryferium ani pending IRQ; rdzeń sam tworzy synchroniczną pułapkę."},
{"id":"A2","status":"enabled","title":"STRUCTURE — mcause, mepc i mtvec","content_tex":"\\texttt{mcause} zapisuje klasę 11 bez bitu interrupt, \\texttt{mepc} zapisuje adres ECALL, a baza \\texttt{mtvec} prowadzi wszystkie synchroniczne wyjątki do \\texttt{handle\\_exception}. Handler publikuje oryginał i osobny adres wznowienia."},
{"id":"A3","status":"enabled","title":"DISPATCH — synchroniczny slot mtvec","content_tex":"Sprzęt wybiera bazowy slot wektora, ponieważ zdarzenie jest wyjątkiem, nie IRQ. \\texttt{handle\\_exception} odczytuje mcause i obsługuje jedyny dopuszczony przypadek ECALL. To granica trap dispatch, choć nie ma tablicy callbacków."},
{"id":"A4","status":"enabled","title":"APPLICATION — cause 11 i stały krok 4","content_tex":"ECALL w RV32I jest instrukcją 32-bitową, dlatego dla tego przykładu poprawny resume ma wartość \\texttt{mepc+4}. Gate wymaga jednego wejścia, cause 11, delty 4 i ustawienia resumed dopiero po mret."},
{"id":"A5","status":"enabled","title":"FLOW — ecall, save, patch, mret","content_tex":"Normalny kod wykonuje ECALL. Rdzeń zapisuje mepc/mcause, skacze do wektora, handler odczytuje oba CSR i zapisuje nowe mepc. Epilog \\texttt{interrupt(\"machine\")} kończy się mret; dopiero potem main zapisuje resumed."},
{"id":"A6","status":"enabled","title":"STATE — RUNNING, TRAP, RESUMED","content_tex":"Stan przechodzi \\texttt{RUNNING -> TRAP(mepc=ecall,cause=11) -> RESUMED(pc=mepc+4)}. Licznik 1 odróżnia pojedyncze wznowienie od pętli ponownego wykonywania ECALL."},
{"id":"A7","status":"enabled","title":"RUNTIME — ecall i mret w ELF","content_tex":"Oracle porównuje raportowane adresy i niezależnie wymaga różnicy 4. Objdump musi pokazać \\texttt{ecall} w main oraz \\texttt{mret} w handlerze. Segmenty LOAD pozostają rozdzielone R-X/R-W."},
{"id":"A8","status":"enabled","title":"PATTERNS — explicit resume policy","content_tex":"Wzorzec handlera wyjątku obejmuje jawną decyzję: retry, skip albo terminate. Task01 wybiera skip tylko dla znanego ECALL. Dla faultu pamięci bezwarunkowe mepc+4 mogłoby ukryć błąd i jest niedozwolone."}
]
},
{
"id": "task02",
"chapter": "MSIP",
"title": "Machine software IRQ i clear źródła",
"idea_tex": "MSIP, MSIE, MIE, vector 3 i mret",
"priority": "kluczowe",
"key": true,
"prompt_tex": "Zapisz stan pending przed globalnym enable, mcause w ISR i pending po clear. Rozdziel źródło MSIP od obu bramek oraz wskaż, dlaczego handler musi usunąć źródło przed mret.",
"criterion": "Hazard3: count=1, mcause=0x80000003, pending_before=1, pending_in_handler=1, pending_after=0, resumed=1, pass=1 i kod 0.",
"evidence_tex": "Ślad źródło→pending→MSIE/MIE→vector 3→ISR→clear→mret, raport globali i kod wyjścia Hazard3.",
"worksheet_step2": "Pending i bramki przed wejściem",
"worksheet_step3": "mcause, clear, mret i wznowienie",
"viewpoints": [
{"id":"A1","status":"enabled","title":"CONTEXT — kontrolowane źródło bez peryferium","content_tex":"\\texttt{tb\\_set\\_softirq(0)} podnosi machine software interrupt hart0. Testbench jest źródłem, CSR są kontrolą rdzenia, ISR usuwa żądanie, a main ocenia wznowienie. UART/GPIO nie uczestniczą."},
{"id":"A2","status":"enabled","title":"STRUCTURE — trzy bity i jeden wektor","content_tex":"Źródło jest widoczne jako \\texttt{mip.MSIP}; indywidualna bramka to \\texttt{mie.MSIE}, globalna to \\texttt{mstatus.MIE}. Cause 3 w trybie vectored wybiera slot 3 i symbol \\texttt{isr\\_machine\\_softirq}."},
{"id":"A3","status":"enabled","title":"DISPATCH — sprzęt wybiera vector 3","content_tex":"Bit interrupt w mcause i kod 3 powodują sprzętowy wybór slotu machine software IRQ. Nie jest to statyczne jal z main. Po wejściu symbol handlera jest już określony przez wektor, a mret odtwarza przerwany kontekst."},
{"id":"A4","status":"enabled","title":"APPLICATION — pending 1 do 0","content_tex":"Przy MSIE=1 i MIE=0 źródło jest już pending, lecz licznik wynosi 0. Po otwarciu MIE handler widzi \\texttt{0x80000003} i pending=1, wykonuje clear, zwiększa licznik, a po mret main mierzy pending=0."},
{"id":"A5","status":"enabled","title":"FLOW — configure, assert, observe, enable","content_tex":"Kolejność eliminuje wyścig pomiaru: najpierw wyłącz wszystko, ustaw MSIE, podnieś źródło, zmierz pending, dopiero potem ustaw MIE. Handler odczytuje cause/pending przed clear i publikuje licznik po clear."},
{"id":"A6","status":"enabled","title":"STATE — MASKED_PENDING, ACTIVE, CLEARED","content_tex":"Źródło przechodzi do \\texttt{MASKED\\_PENDING}, bo globalna bramka jest zamknięta. Po MIE staje się ACTIVE; clear w ISR tworzy CLEARED. Resumed=1 jest osobnym stanem normalnego kodu po mret."},
{"id":"A7","status":"enabled","title":"RUNTIME — cause z bitem 31","content_tex":"Wartość \\texttt{0x80000003} łączy bit interrupt i kod 3. Listing ma pokazać CSR dla mie/mstatus/mip, dostęp do testbench clear oraz mret. Oracle wymaga całego śladu, nie tylko pass."},
{"id":"A8","status":"enabled","title":"PATTERNS — clear level source before publish","content_tex":"Źródło poziomowe usuwa się przed opublikowaniem ukończenia ISR. Gdyby licznik wzrósł bez clear, po mret żądanie pozostałoby aktywne i handler mógłby wejść ponownie."}
]
},
{
"id": "task03",
"chapter": "GATE",
"title": "Macierz pending, MSIE i MIE",
"idea_tex": "to samo źródło, dwie niezależne bramki",
"priority": "kluczowe",
"key": true,
"prompt_tex": "Wypełnij trzy wiersze: pending=1 z samym MIE, pending=1 z samym MSIE oraz pending=1 z obiema bramkami. Przewidź licznik ISR i końcowy pending.",
"criterion": "Hazard3: pending=1, blocked_no_msie=1, blocked_no_mie=1, delivered_both=1, count=1, mcause=0x80000003, pending_after=0, pass=1 i kod 0.",
"evidence_tex": "Tabela prawdy dwóch bramek dla jednego utrzymywanego pending, licznik ISR, końcowy clear, mret i kod wyjścia Hazard3.",
"worksheet_step2": "Macierz pending/MSIE/MIE",
"worksheet_step3": "Dostarczenie, clear i kod wyjścia",
"viewpoints": [
{"id":"A1","status":"enabled","title":"CONTEXT — jedna zmienna, dwie kontrolki","content_tex":"Bodziec MSIP pozostaje ten sam podczas dwóch prób blokowanych i próby dostarczonej. Przykład zmienia wyłącznie bramki rdzenia, więc nie myli nowego zdarzenia z nową konfiguracją."},
{"id":"A2","status":"enabled","title":"STRUCTURE — macierz 1×2×2","content_tex":"Oś danych to pending=1. Dwie osie sterujące to MSIE i MIE. Cztery kombinacje mają jeden stan dostarczalny: oba bity 1. Przykład mierzy trzy istotne wiersze przy utrzymanym pending."},
{"id":"A3","status":"enabled","title":"DISPATCH — wejście dopiero dla 1/1","content_tex":"Sprzętowy dispatch do vector 3 nie zachodzi przy żadnej pojedynczej otwartej bramce. Dopiero MSIE=1 i MIE=1 pozwalają utworzyć trap, zapisać cause i wejść do ISR kończącego się mret."},
{"id":"A4","status":"enabled","title":"APPLICATION — dwa razy blocked, raz delivered","content_tex":"Przy MIE=1/MSIE=0 licznik zostaje 0. Przy MIE=0/MSIE=1 również zostaje 0. Przy obu równych 1 rośnie dokładnie do 1, mcause ma wartość 0x80000003, a clear zeruje pending."},
{"id":"A5","status":"enabled","title":"FLOW — nie zmieniaj źródła między próbami","content_tex":"Program podnosi MSIP raz, następnie testuje global-only, individual-only i both. Globalny bit jest zamykany przed zmianą MSIE, aby dostarczenie nie zaszło między pomiarem a zapisaniem wyniku."},
{"id":"A6","status":"enabled","title":"STATE — P10, P01, P11, CLEAR","content_tex":"Skrót \\texttt{Pxy} oznacza pending=1, MSIE=x, MIE=y. Ślad to \\texttt{P01(blocked) -> P10(blocked) -> P11(active) -> CLEAR}. Licznik zmienia się tylko na trzeciej krawędzi."},
{"id":"A7","status":"enabled","title":"RUNTIME — odczyt mip, zapisy mie/mstatus","content_tex":"Raport utrwala trzy decyzje i cause. Listing rozdziela \\texttt{csrs/csrc mie}, \\texttt{csrs/csrc mstatus} oraz odczyt mip. Objdump niezależnie potwierdza mret w ISR."},
{"id":"A8","status":"enabled","title":"PATTERNS — source, individual, global gate","content_tex":"Trzywarstwowy model zapobiega typowemu błędowi: pending nie znaczy delivered. Ten sam wzorzec wróci dla MTIP/MTIE oraz external IRQ z enable peryferium, kontrolera i globalnym MIE."}
]
}
]
}