Files

303 lines
22 KiB
JSON
Raw Permalink 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": "13",
"slug": "gpio-edges-debounce",
"title": "GPIO, zbocza i quiet-window debounce",
"topic": "RV32I/Hazard3: polling GPIO, external IRQ18, debounce i końcowy superloop raw C",
"status": "Gotowa",
"version": "v00.01",
"revision_date": "2026-07-20T00:00:00+02:00",
"level": "Rok 1 · C13 · RV32I/Hazard3"
},
"front": {
"goal": "Uczeń rozdziela fizyczny lub modelowany poziom wejścia, detekcję zbocza, zatrzaśnięty pending, maskowanie kontrolera, wybór handlera i efekt wykonywany później przez main. Potrafi zrealizować quiet-window debounce oraz połączyć timer, UART i GPIO w końcowym superloop bez RTOS.",
"scope": "Cztery przykłady wykonują się na centralnym modelu RTL Hazard3. TEST\u005c_IN i UART TEST\u005c_RX są jawnie wyłącznie bodźcami testbencha. Karta nie udaje modelu RP2350 ani fizycznego pinu. Task01 dowodzi DIR/OUT/IN przez polling, Task02 pełnej ścieżki rising-edge IRQ18 z W1C, Task03 krótkiego ISR i decyzji debounce w main, a Task04 współpracy machine timer, UART IRQ16 i GPIO IRQ18.",
"scope_note": "Każdy przykład ma dokładnie A1A8. Widok N/D występuje tylko wtedy, gdy dany mechanizm naprawdę nie istnieje."
},
"scope_headers": ["Typ", "Task", "Idea", "Waga"],
"learning": {
"reasoning_label": "Od poziomu wejścia do efektu aplikacji",
"reasoning": "Uczeń dla każdego zdarzenia wskazuje osobno source, pending, enable, decyzję dispatchera, minimalny efekt ISR i późniejszy efekt normalnego kodu.",
"practice_label": "Dowód na GPIO/UART/timer RTL",
"practice": "Uczeń uruchamia obrazy RV32I z limitem cykli, odczytuje rejestry MMIO i kontrolera przerwań, przelicza czasy bounce i sprawdza, że main, a nie ISR, wykonuje politykę debounce oraz aktualizuje wyjścia.",
"criterion": "Cztery programy kończą się kodem 0 bez timeoutu i nieobsłużonej pułapki. Task01 daje DIR=1 oraz sekwencję IN 2,3,0. Task02 ma jeden IRQ18, PENDING=0x4 w handlerze, source i controller pending 1 przed obsługą oraz 0 po W1C. Task03 zapisuje poziomy 1,0,1, oba odstępy dodatnie i krótsze od quiet=2000, zero wczesnych akceptacji i jedną akceptację po co najmniej quiet. Task04 ma po jednym timer IRQ, UART16 i GPIO18, trzy efekty main, OUT=0x7 oraz IN=0x17.",
"requirement": "Obsługa GPIO i zdarzeń w raw C z jawną granicą bodźca testbench, krótkim ISR, W1C, synchronizacją z main i bez przypisywania modelowi właściwości RP2350."
},
"sections": [
{
"title": "1 — Rejestr modelu nie jest fizycznym pinem",
"content_tex": "GPIO modelu zaczyna się pod adresem \u005ctexttt{0xc0000300}. \u005ctexttt{DIR} wybiera kierunek, \u005ctexttt{OUT/OUT\u005c_SET/OUT\u005c_CLR} sterują zatrzaśnięciem wyjściowym, a \u005ctexttt{IN} pokazuje wyjścia dla pinów ustawionych jako output i bodziec zewnętrzny dla pinów input. \u005ctexttt{RISE\u005c_EN} i \u005ctexttt{FALL\u005c_EN} wybierają wykrywane zbocza, a \u005ctexttt{PENDING} zatrzaskuje je do zapisu W1C.\n\n\u005ctexttt{TEST\u005c_IN} istnieje tylko po stronie laboratorium. Pozwala programowi testowemu wywołać zmianę otoczenia bez fizycznego przewodu. Produkcyjny sterownik czyta \u005ctexttt{IN}; nie powinien zawierać zapisu do takiego fixture'u."
},
{
"title": "2 — Source, pending i enable to trzy różne fakty",
"content_tex": "Zmiana 0 na 1 jest source dla detektora rising edge. Włączony bit \u005ctexttt{RISE\u005c_EN} pozwala zatrzasnąć bit w \u005ctexttt{PENDING}; ten bit utrzymuje poziomową linię GPIO external IRQ18. Kontroler Hazard3 osobno ma per-line enable, priorytet oraz globalne bramki \u005ctexttt{mie.MEIE} i \u005ctexttt{mstatus.MIE}. Dispatcher odczytuje aktywny numer i wywołuje handler z tablicy.\n\nW1C usuwa źródło poziomowego żądania po stronie peryferium. Sam powrót z handlera, odczyt \u005ctexttt{PENDING} ani zamknięcie maski nie potwierdzają zdarzenia."
},
{
"title": "3 — Debounce jest decyzją czasu w main",
"content_tex": "ISR Task03 robi tylko trzy rzeczy: zapisuje czas i poziom, potwierdza pending przez W1C oraz na końcu publikuje licznik. Main obserwuje kandydata i akceptuje go dopiero wtedy, gdy od ostatniego zbocza minęło quiet window oraz bieżący poziom nadal odpowiada kandydatowi. Każde nowe zbocze przesuwa początek okna.\n\nSekwencja \u005ctexttt{1--0--1} jest kontrolowanym bounce. Oba odstępy są krótsze od 2000 taktów, dlatego żaden z trzech bezpośrednich odczytów nie może zaakceptować stanu. To polityka aplikacji, nie funkcja kontrolera przerwań."
},
{
"title": "4 — Superloop jest granicą serii raw C",
"content_tex": "Task04 łączy machine timer, UART0 RX external IRQ16 i GPIO external IRQ18. ISR-y publikują flagi, a main konsumuje je i ustawia trzy różne bity wyjściowe. Flaga może skleić wiele produkcji, więc nie jest kolejką; zachowanie liczby i kolejności zdarzeń należy do następnej serii FreeRTOS."
}
],
"tasks": [
{
"id": "task01",
"source": "src/tasks/task01_gpio_polling.c",
"chapter": "POLL",
"title": "Polling DIR, OUT i IN",
"idea_tex": "kierunek, latch wyjścia i testbench-only bodziec wejścia",
"priority": "kluczowe",
"key": true,
"prompt_tex": "Przewidź cztery wartości: IN po ustawieniu zewnętrznego pinu 1, OUT po ustawieniu pinu 0, IN przy obu wysokich i IN po wyzerowaniu. Wyjaśnij, dlaczego zapis TEST\u005c_IN jest dozwolony tylko w fixture testbencha.",
"evidence_tex": "Tabela DIR/OUT/IN przed uruchomieniem, log wykonania RTL Hazard3 z kodem wyjścia, odczyt rejestrów MMIO i wniosek oddzielający fixture TEST\u005c_IN od produkcyjnego API GPIO.",
"worksheet_step2": "Log RTL: DIR, OUT i kolejne wartości IN",
"worksheet_step3": "Granica modelu: TEST\u005c_IN kontra fizyczny pin",
"criterion": "Oracle: dir=1, external_high=2, out_after_set=1, both_high=3, final_in=0, pass=1 i kod wyjścia 0. PENDING pozostaje 0, ponieważ detektory zboczy są wyłączone.",
"viewpoints": [
{
"id": "A1",
"status": "enabled",
"title": "CONTEXT — dwa źródła widocznego IN",
"content_tex": "Dla bitu z \u005ctexttt{DIR=1} widoczne \u005ctexttt{IN} pochodzi z latcha \u005ctexttt{OUT}; dla \u005ctexttt{DIR=0} pochodzi z modelowanego wejścia. \u005ctexttt{TEST\u005c_IN} zmienia tylko tę drugą część i jest bodźcem laboratorium, nie pinem RP2350."
},
{
"id": "A2",
"status": "enabled",
"title": "STRUCTURE — pin0 output, pin1 input",
"content_tex": "Maska 0x1 wybiera pin wyjściowy, a 0x2 wejściowy. Rejestry SET i CLR modyfikują latch bez read--modify--write. Jeden odczyt \u005ctexttt{IN} składa oba widoki, dlatego przy obu stanach wysokich wynosi 0x3."
},
{
"id": "A3",
"status": "unavailable",
"title": "DISPATCH",
"reason": "Task01 wyłącza detektory zboczy i przerwania; każdy krok jest bezpośrednim pollingiem MMIO, bez ISR, callbacku ani wyboru handlera."
},
{
"id": "A4",
"status": "enabled",
"title": "APPLICATION — kontrolowany round trip poziomów",
"content_tex": "Po external high program widzi 0x2. SET pinu0 daje OUT=0x1 i IN=0x3. CLR pinu0 oraz TEST\u005c_IN=0 kończą sekwencję IN=0. Te liczby są bitowymi relacjami modelu, nie numerami pinów konkretnej płytki."
},
{
"id": "A5",
"status": "enabled",
"title": "FLOW — configure, stimulate, sample, clear",
"content_tex": "Program wyłącza edge enable, czyści pending, ustawia kierunek i stan początkowy. Następnie podaje bodziec wejściowy, próbkuje, ustawia output, próbkuje ponownie i zeruje oba źródła. Każdy pomiar następuje po odpowiadającym mu zapisie MMIO."
},
{
"id": "A6",
"status": "enabled",
"title": "STATE — 0x2 → 0x3 → 0x0",
"content_tex": "Widoczny stan portu przechodzi kolejno przez tylko input high, oba bity high i oba low. Kierunek pozostaje 0x1 przez cały eksperyment, a pending pozostaje pusty."
},
{
"id": "A7",
"status": "enabled",
"title": "RUNTIME — rzeczywiste transakcje pod 0xc0000300",
"content_tex": "Log RTL raportuje 1,2,1,3,0 i pass=1. Listing powinien pokazać osobne zapisy do offsetów DIR, TEST\u005c_IN, OUT\u005c_SET i OUT\u005c_CLR oraz odczyty IN; nie ma wejścia do wspólnego dispatchera external IRQ."
},
{
"id": "A8",
"status": "enabled",
"title": "PATTERNS — polling z jawną granicą fixture",
"content_tex": "Wzorzec produkcyjny konfiguruje kierunek i czyta wejście lub steruje wyjściem. Warstwa testowa może dostarczyć osobny mechanizm wstrzyknięcia, ale jego nazwa, dokumentacja i lokalizacja muszą uniemożliwiać pomylenie z rejestrem sprzętu."
}
]
},
{
"id": "task02",
"source": "src/tasks/task02_gpio_rising_irq.c",
"chapter": "IRQ18",
"title": "Rising edge, IRQ18 i W1C",
"idea_tex": "zbocze, pending peryferium, kontroler i resume",
"priority": "kluczowe",
"key": true,
"prompt_tex": "Rozpisz ścieżkę od zmiany TEST\u005c_IN 0→1 do wznowienia main. Zaznacz GPIO PENDING, pending linii 18 w kontrolerze, dwa poziomy enable, decyzję dispatchera, zapis W1C i efekt ponownego podania tego samego poziomu.",
"evidence_tex": "Przewidywany łańcuch source--pending--enable--dispatch--effect, log RTL z IRQ18 i stanami przed/po, listing wspólnego dispatchera oraz wniosek wyjaśniający W1C i brak drugiego zbocza dla high→high.",
"worksheet_step2": "Log RTL: pending GPIO i kontrolera przed/po ISR",
"worksheet_step3": "Dowód dispatchu IRQ18 i zapisu W1C",
"criterion": "Jeden handler IRQ18 widzi PENDING=0x4 i poziom high. GPIO source oraz controller pending są 1 przed globalnym enable i 0 po W1C; high→high nie tworzy nowego pending, resume=1, pass=1.",
"viewpoints": [
{
"id": "A1",
"status": "enabled",
"title": "CONTEXT — zbocze tworzy zatrzaśnięty fakt",
"content_tex": "Bodziec TEST\u005c_IN zmienia pin2 z 0 na 1. Włączony bit RISE\u005c_EN tworzy bit 0x4 w GPIO PENDING, a ten utrzymuje external IRQ18. TEST\u005c_IN jest sztuczny, lecz dalsza ścieżka przez kontroler i rdzeń jest rzeczywistą ścieżką modelu RTL."
},
{
"id": "A2",
"status": "enabled",
"title": "STRUCTURE — dwa pending i tablica handlerów",
"content_tex": "Pierwszy pending leży w peryferium jako maska pinów. Drugi jest widokiem aktywnej linii 18 w kontrolerze Hazard3. Tablica \u005ctexttt{\u005c_external\u005c_irq\u005c_table[18]} przechowuje adres zwykłej funkcji handlera."
},
{
"id": "A3",
"status": "enabled",
"title": "DISPATCH — external vector wybiera linię 18",
"content_tex": "\u005ctexttt{mtvec} wybiera wspólny vector machine external interrupt. \u005ctexttt{irq\u005c_dispatch.S} odczytuje aktywną linię z \u005ctexttt{meinext}, indeksuje tablicę i wykonuje pośredni \u005ctexttt{jalr} do handlera GPIO. Handler potwierdza, że bieżący numer to 18."
},
{
"id": "A4",
"status": "enabled",
"title": "APPLICATION — jedno zbocze, jedno wznowienie",
"content_tex": "Program celowo generuje edge przy zamkniętym globalnym MIE. Dzięki temu może najpierw zmierzyć oba pending jako 1. Po otwarciu MIE oczekuje dokładnie jednego handlera, W1C, obu pending równych 0 i wykonania kodu resume."
},
{
"id": "A5",
"status": "enabled",
"title": "FLOW — source → pending → enable → dispatch → W1C",
"content_tex": "0→1 ustawia GPIO PENDING. Włączona linia18, MEIE i później MIE dopuszczają trap. Dispatcher wybiera handler18; ISR próbkuje maskę i poziom, zapisuje tę maskę do PENDING jako W1C, a dopiero potem publikuje count. Main wraca i mierzy stan 0."
},
{
"id": "A6",
"status": "enabled",
"title": "STATE — IDLE, LATCHED, HANDLING, ACKED, RESUMED",
"content_tex": "IDLE ma input low i pending 0. Rising edge tworzy LATCHED. Otwarcie MIE prowadzi do HANDLING. Zapis W1C tworzy ACKED, a powrót dispatchera RESUMED. Ponowne high pozostaje ACKED, ponieważ nie zaszła zmiana 0→1."
},
{
"id": "A7",
"status": "enabled",
"title": "RUNTIME — 0x4 w GPIO i 18 w kontrolerze",
"content_tex": "Raport daje count=1, irq=0x12, source/controller przed 1, pending w ISR 0x4, oba pending po 0 i same-level 0. Listing ma zawierać handler GPIO oraz wspólny dispatcher zakończony \u005ctexttt{mret}."
},
{
"id": "A8",
"status": "enabled",
"title": "PATTERNS — acknowledge peripheral before publish",
"content_tex": "Dla poziomowego IRQ najpierw ustal i usuń przyczynę po stronie peryferium, a dopiero potem publikuj zakończenie dla main. Zamknięcie maski może odłożyć obsługę, ale nie jest potwierdzeniem zdarzenia."
}
]
},
{
"id": "task03",
"source": "src/tasks/task03_gpio_quiet_window.c",
"chapter": "DBNC",
"title": "Quiet-window debounce po trzech zboczach",
"idea_tex": "krótki ISR, mtime i decyzja stabilności w main",
"priority": "kluczowe",
"key": true,
"prompt_tex": "Dla sekwencji poziomów 1,0,1 oblicz oba odstępy i porównaj z quiet=2000. Wyjaśnij, dlaczego ISR publikuje czas, lecz nie akceptuje przycisku, oraz jak nowe zbocze restartuje okno ciszy.",
"evidence_tex": "Przewidywana sekwencja kandydatów, log RTL z trzema czasami i poziomami, niezależne przeliczenie gapów oraz accepted\u005c_after i wniosek przypisujący politykę debounce do main.",
"worksheet_step2": "Log RTL: trzy poziomy, czasy i gapy",
"worksheet_step3": "Dowód quiet window i miejsca decyzji w main",
"criterion": "Poziomy 1,0,1, count=3 i IRQ18. Oba gapy są dodatnie i mniejsze od quiet=2000; early=0, accepted=1, zaakceptowany poziom high, accepted_after >= quiet i pass=1.",
"viewpoints": [
{
"id": "A1",
"status": "enabled",
"title": "CONTEXT — IRQ rejestruje zmianę, nie intencję użytkownika",
"content_tex": "Trzy przejścia TEST\u005c_IN modelują bounce, nie trzy świadome naciśnięcia. Kontroler potrafi zgłosić rising i falling edge, lecz nie zna quiet window ani znaczenia stabilnego przycisku. Ta semantyka należy do kodu normalnego."
},
{
"id": "A2",
"status": "enabled",
"title": "STRUCTURE — historia ISR i kandydat main",
"content_tex": "ISR zapisuje trzy pary \u005ctexttt{level,time}, a osobno publikuje ostatni poziom, ostatni czas i count. Main utrzymuje seen count, candidate level oraz candidate time. Krytyczna sekcja chroni snapshot 64-bitowego czasu na RV32."
},
{
"id": "A3",
"status": "enabled",
"title": "DISPATCH — każde włączone zbocze wybiera GPIO18",
"content_tex": "RISE\u005c_EN i FALL\u005c_EN pozwalają trzem przejściom utworzyć pending. External dispatcher trzy razy wybiera wpis 18 i wywołuje ten sam krótki handler. Numer ostatniego handlera oraz count=3 są dowodem decyzji, nie sam fakt zapisu TEST\u005c_IN."
},
{
"id": "A4",
"status": "enabled",
"title": "APPLICATION — restart okna po każdym bounce",
"content_tex": "Po każdym nowym count main zastępuje candidate time czasem ostatniego zbocza. Natychmiastowe próby dają early=0. Dopiero stabilny high utrzymany przez co najmniej 2000 taktów daje accepted=1."
},
{
"id": "A5",
"status": "enabled",
"title": "FLOW — ISR publish, main observe, wait, validate",
"content_tex": "ISR czyta pending, poziom i mtime, zapisuje rekord, wykonuje W1C i na końcu zwiększa count. Main atomowo pobiera publikację, aktualizuje kandydata, mierzy wiek oraz porównuje bieżący IN. Nowe zbocze wraca do początku okna."
},
{
"id": "A6",
"status": "enabled",
"title": "STATE — HIGH?, LOW?, HIGH?, STABLE_HIGH",
"content_tex": "Pierwsze high jest kandydatem, low unieważnia go, a drugie high tworzy nowego kandydata. Żaden stan z wiekiem mniejszym niż quiet nie jest zaakceptowany. STABLE\u005c_HIGH powstaje dopiero po pełnym oknie bez kolejnego IRQ."
},
{
"id": "A7",
"status": "enabled",
"title": "RUNTIME — dwa bounce gapy krótsze od 0x7d0",
"content_tex": "Bieżący RTL raportuje czasy low 0x591, 0x8ea, 0xc43, czyli oba gapy 0x359. Akceptacja następuje po 0x878 od ostatniego edge. Konkretne czasy zależą od buildu; oracle niezależnie sprawdza tylko relacje gap<quiet i accepted\u005c_after>=quiet."
},
{
"id": "A8",
"status": "enabled",
"title": "PATTERNS — timestamp in ISR, policy in consumer",
"content_tex": "ISR powinien szybko potwierdzić sprzęt i opublikować minimalne dane. Konsument może wtedy zmieniać quiet window, politykę akceptacji lub raportowanie bez wydłużania czasu blokowania innych przerwań."
}
]
},
{
"id": "task04",
"source": "src/tasks/task04_raw_c_superloop.c",
"chapter": "LOOP",
"title": "Timer + UART16 + GPIO18 bez RTOS",
"idea_tex": "trzy źródła, dwa dispatchery, flagi i efekty main",
"priority": "kluczowe",
"key": true,
"prompt_tex": "Dla timer tick, bajtu U i rising edge narysuj trzy osobne ścieżki source--pending--enable--dispatcher--ISR--flag--main effect. Wyjaśnij, dlaczego trzy flagi nie są kolejką i jakie ograniczenie przechodzi do serii FreeRTOS.",
"evidence_tex": "Trzy przewidywane ścieżki zdarzeń, log RTL z numerami przerwań i licznikami, listing machine-timer oraz external dispatchera, końcowe OUT/IN i wniosek o granicy flag jednozdarzeniowych.",
"worksheet_step2": "Log RTL: timer, UART16, GPIO18 i efekty main",
"worksheet_step3": "Dowód dwóch dispatcherów i końcowego OUT/IN",
"criterion": "Po jednym timer IRQ z mcause=0x80000007, UART IRQ16 z bajtem 0x55 i GPIO IRQ18 z high. Main konsumuje każdą flagę raz, ustawia trzy bity, OUT=0x7, IN=0x17, pass=1.",
"viewpoints": [
{
"id": "A1",
"status": "enabled",
"title": "CONTEXT — trzy usługi, wspólny czas CPU",
"content_tex": "Machine timer pochodzi z mtime>=mtimecmp, UART16 z niepustego RX FIFO przy włączonym RX IRQ, a GPIO18 z rising edge zatrzaśniętego w PENDING. TEST\u005c_RX i TEST\u005c_IN tworzą tylko bodźce; logika timer/UART/GPIO po nich jest rzeczywistym modelem RTL."
},
{
"id": "A2",
"status": "enabled",
"title": "STRUCTURE — trzy publikacje i trzy bity efektu",
"content_tex": "Każda usługa ma licznik ISR, payload i flagę. Main ma osobny licznik konsumpcji. Bity OUT 0,1,2 reprezentują odpowiednio efekt GPIO, UART i timera, więc końcowe 0x7 dowodzi wykonania trzech gałęzi bez ukrywania ich w jednej sumie."
},
{
"id": "A3",
"status": "enabled",
"title": "DISPATCH — slot 7 oraz external 16/18",
"content_tex": "Machine timer cause 7 trafia bezpośrednio do \u005ctexttt{isr\u005c_machine\u005c_timer} i wraca przez \u005ctexttt{mret}. Machine external vector trafia do wspólnego dispatchera; \u005ctexttt{meinext} wybiera wpis 16 dla UART albo 18 dla GPIO i wykonuje \u005ctexttt{jalr}."
},
{
"id": "A4",
"status": "enabled",
"title": "APPLICATION — efekt dopiero po konsumpcji flagi",
"content_tex": "UART handler publikuje U, GPIO handler high, a timer handler tick. Żaden ISR nie ustawia aplikacyjnego bitu OUT. Superloop atomowo zabiera flagę i dopiero jego trzy gałęzie ustawiają 0x2, 0x1 i 0x4."
},
{
"id": "A5",
"status": "enabled",
"title": "FLOW — source → pending → dispatch → ISR → main",
"content_tex": "Timer: comparator→MTIP→slot7→tick flag→bit2. UART: RX FIFO→IRQ16→external dispatcher→read DATA i flag→bit1. GPIO: edge→PENDING→IRQ18→dispatcher→W1C i flag→bit0. Main używa WFI tylko wtedy, gdy nadal brakuje któregoś efektu."
},
{
"id": "A6",
"status": "enabled",
"title": "STATE — trzy niezależne PRODUCED/CONSUMED",
"content_tex": "Każda usługa przechodzi IDLE→PRODUCED w ISR i PRODUCED→CONSUMED w main. Stan końcowy wymaga trzech liczników konsumpcji równych 1. Flaga wraca do 0, ale licznik zachowuje dowód przejścia."
},
{
"id": "A7",
"status": "enabled",
"title": "RUNTIME — cause 7, linie 16/18 i OUT 0x7",
"content_tex": "Raport daje timer count 1 i mcause 0x80000007, UART count 1/irq 0x10/byte 0x55, GPIO count 1/irq 0x12/high oraz trzy main actions po 1. OUT=0x7, a IN=0x17 dodaje widoczny wysoki pin wejściowy 0x10."
},
{
"id": "A8",
"status": "enabled",
"title": "PATTERNS — ISR-to-main flags with an explicit ceiling",
"content_tex": "Jednokomórkowa flaga jest poprawna tylko wtedy, gdy wystarcza informacja co najmniej jedno zdarzenie. Nie zachowuje liczby ani kolejności wielu produkcji. Ten sufit jest powodem przejścia do kolejek i tasków FreeRTOS, a nie pretekstem do nazywania flagi kolejką."
}
]
}
]
}