feat: add lab-rv32i-c-gpio-edges-debounce card
This commit is contained in:
+12
@@ -0,0 +1,12 @@
|
|||||||
|
/build/
|
||||||
|
/doc/generated/*
|
||||||
|
!/doc/generated/.gitkeep
|
||||||
|
!/doc/generated/main.tex
|
||||||
|
*.aux
|
||||||
|
*.fdb_latexmk
|
||||||
|
*.fls
|
||||||
|
*.log
|
||||||
|
*.out
|
||||||
|
*.upa
|
||||||
|
*.upb
|
||||||
|
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
RISCV_PREFIX ?= riscv64-unknown-elf-
|
||||||
|
CC := $(RISCV_PREFIX)gcc
|
||||||
|
OBJCOPY := $(RISCV_PREFIX)objcopy
|
||||||
|
OBJDUMP := $(RISCV_PREFIX)objdump
|
||||||
|
SIZE := $(RISCV_PREFIX)size
|
||||||
|
|
||||||
|
RV_ENV_ROOT ?= /home/user/dev/edu/rv32i-hazard3-student-env
|
||||||
|
H3_COMMON ?= $(RV_ENV_ROOT)/vendor/Hazard3/test/sim/common
|
||||||
|
H3_INIT ?= $(H3_COMMON)/init.S
|
||||||
|
H3_IRQ_DISPATCH ?= $(H3_COMMON)/irq_dispatch.S
|
||||||
|
LDSCRIPT ?= $(H3_COMMON)/link_hazard3.ld
|
||||||
|
MEMOPS ?= $(RV_ENV_ROOT)/vendor/lab-runtime/memops.c
|
||||||
|
|
||||||
|
BUILD_ROOT ?= build
|
||||||
|
TASKS := task01_gpio_polling task02_gpio_rising_irq task03_gpio_quiet_window task04_raw_c_superloop
|
||||||
|
ARCH ?= rv32i_zicsr_zifencei
|
||||||
|
ABI ?= ilp32
|
||||||
|
CFLAGS := -std=gnu11 -march=$(ARCH) -mabi=$(ABI) -nostdlib -nostartfiles \
|
||||||
|
-ffreestanding -fno-builtin -fno-stack-protector -fno-pic -mno-relax \
|
||||||
|
-O0 -g3 -fno-omit-frame-pointer -Wall -Wextra \
|
||||||
|
-Isrc/tasks -isystem $(H3_COMMON)
|
||||||
|
LDFLAGS := -Wl,--no-relax -T$(LDSCRIPT)
|
||||||
|
LIBS := -lgcc
|
||||||
|
|
||||||
|
.PHONY: all tasks test card-test clean check-support task1 task2 task3 task4
|
||||||
|
all: tasks
|
||||||
|
tasks: $(TASKS)
|
||||||
|
task1: $(word 1,$(TASKS))
|
||||||
|
task2: $(word 2,$(TASKS))
|
||||||
|
task3: $(word 3,$(TASKS))
|
||||||
|
task4: $(word 4,$(TASKS))
|
||||||
|
test: tasks
|
||||||
|
./tests/test_hazard3.sh
|
||||||
|
card-test:
|
||||||
|
./tests/test_card.sh
|
||||||
|
|
||||||
|
check-support:
|
||||||
|
@test -f "$(H3_INIT)"
|
||||||
|
@test -f "$(H3_IRQ_DISPATCH)"
|
||||||
|
@test -f "$(H3_COMMON)/tb_gpio_io.h"
|
||||||
|
@test -f "$(H3_COMMON)/tb_uart_io.h"
|
||||||
|
@test -f "$(LDSCRIPT)"
|
||||||
|
@test -f "$(MEMOPS)"
|
||||||
|
|
||||||
|
define TASK_RULES
|
||||||
|
$(1): $(BUILD_ROOT)/$(1)/prog.bin $(BUILD_ROOT)/$(1)/prog.lst
|
||||||
|
|
||||||
|
$(BUILD_ROOT)/$(1)/prog.elf: crt0.S src/tasks/$(1).c src/tasks/c13_common.h $(H3_INIT) $(H3_IRQ_DISPATCH) $(LDSCRIPT) $(MEMOPS) | check-support
|
||||||
|
mkdir -p $$(@D)
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) -Wl,-Map,$(BUILD_ROOT)/$(1)/prog.map \
|
||||||
|
-o $$@ $(H3_INIT) $(H3_IRQ_DISPATCH) crt0.S $(MEMOPS) src/tasks/$(1).c $(LIBS)
|
||||||
|
$(SIZE) -A -x $$@
|
||||||
|
|
||||||
|
$(BUILD_ROOT)/$(1)/prog.bin: $(BUILD_ROOT)/$(1)/prog.elf
|
||||||
|
$(OBJCOPY) -O binary $$< $$@
|
||||||
|
|
||||||
|
$(BUILD_ROOT)/$(1)/prog.lst: $(BUILD_ROOT)/$(1)/prog.elf
|
||||||
|
$(OBJDUMP) -d -S -M no-aliases,numeric $$< > $$@
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(foreach task,$(TASKS),$(eval $(call TASK_RULES,$(task))))
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(BUILD_ROOT)
|
||||||
|
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# C13 — GPIO, zbocza i debounce na RV32I/Hazard3
|
||||||
|
|
||||||
|
Karta zamyka serię raw C czterema wykonywalnymi przykładami:
|
||||||
|
|
||||||
|
1. polling `DIR`, `OUT` i `IN` z kontrolowanym `TEST_IN`;
|
||||||
|
2. rising edge → GPIO `PENDING` → external IRQ18 → dispatcher → W1C;
|
||||||
|
3. bounce `1 → 0 → 1` i akceptacja dopiero po quiet window mierzonym `mtime`;
|
||||||
|
4. superloop łączący machine timer, UART RX IRQ16 i GPIO IRQ18.
|
||||||
|
|
||||||
|
Programy są freestanding RV32I+Zicsr+Zifencei i wykonują się w RTL
|
||||||
|
Hazard3/Verilator. Nie ma modelu hostowego, RTOS, kolejki ani semafora.
|
||||||
|
|
||||||
|
## Rejestry modelu
|
||||||
|
|
||||||
|
GPIO zaczyna się pod `0xc0000300` i udostępnia `OUT`, `OUT_SET`, `OUT_CLR`,
|
||||||
|
`IN`, `TEST_IN`, `RISE_EN`, `FALL_EN`, `PENDING` W1C oraz `DIR`. GPIO używa
|
||||||
|
external IRQ18. UART0 używa external IRQ16, a machine timer standardowego
|
||||||
|
machine timer interrupt o `mcause=0x80000007`.
|
||||||
|
|
||||||
|
`TEST_IN` i UART `TEST_RX` są wyłącznie bodźcami testbencha. Symulują zmianę
|
||||||
|
otoczenia bez TCP lub fizycznego pinu. Nie są rejestrami produkcyjnego GPIO,
|
||||||
|
nie opisują RP2350 i nie mogą znaleźć się w sterowniku fizycznej płytki.
|
||||||
|
|
||||||
|
## Budowanie i test
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make tasks
|
||||||
|
./tests/test_hazard3.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Oracle sprawdza kod wyjścia, timeout, nieobsłużone pułapki i raportowane
|
||||||
|
rejestry. Dodatkowo niezależnie przelicza odstępy bounce, granicę quiet window
|
||||||
|
i obecność `mret` w handlerze machine timer.
|
||||||
|
|
||||||
|
Bieżący pomiar RTL:
|
||||||
|
|
||||||
|
- Task01: `DIR=1`, wejście zewnętrzne `2`, po ustawieniu wyjścia `IN=3`,
|
||||||
|
po wyzerowaniu `IN=0`;
|
||||||
|
- Task02: jeden IRQ18, `PENDING=0x4` w ISR, W1C usuwa źródło i ponowny wysoki
|
||||||
|
poziom nie tworzy nowego zbocza;
|
||||||
|
- Task03: czasy low `0x591`, `0x8ea`, `0xc43`, odstępy po `0x359`, quiet
|
||||||
|
`0x7d0`, akceptacja po `0x878`, bez wczesnej akceptacji;
|
||||||
|
- Task04: po jednym timer IRQ, UART16 i GPIO18, bajt `U`, trzy działania main,
|
||||||
|
`OUT=0x7`, widoczne `IN=0x17`.
|
||||||
|
|
||||||
|
Wartości czasowe zależą od bieżącego RTL i buildu. Kontraktem są relacje:
|
||||||
|
dwa odstępy bounce są dodatnie i krótsze od quiet window, a akceptacja następuje
|
||||||
|
nie wcześniej niż quiet po ostatnim zboczu.
|
||||||
|
|
||||||
|
## Podział odpowiedzialności
|
||||||
|
|
||||||
|
W Task02–Task04 ISR potwierdza źródło i publikuje minimalny stan. Debounce oraz
|
||||||
|
aktualizacja wyjść należą do kodu normalnego. Task04 używa prostych flag
|
||||||
|
jednozdarzeniowych; nie jest odporną na dowolne przeciążenie kolejką zdarzeń.
|
||||||
|
To świadoma granica między raw C a kolejną serią FreeRTOS.
|
||||||
|
|
||||||
|
## Karta
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./scripts/render_card_layouts.sh
|
||||||
|
./scripts/render_new_pdf.sh
|
||||||
|
./tests/test_card.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Każdy przykład ma dokładnie A1–A8. Tylko Task01.A3 jest N/D, ponieważ polling
|
||||||
|
nie ma mechanizmu dispatchu.
|
||||||
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
.text
|
||||||
|
.global _start
|
||||||
|
.type _start, @function
|
||||||
|
|
||||||
|
_start:
|
||||||
|
.option push
|
||||||
|
.option norelax
|
||||||
|
la gp, __global_pointer$
|
||||||
|
.option pop
|
||||||
|
|
||||||
|
# common/init.S installed sp and mtvec before entering this startup.
|
||||||
|
la a0, __bss_start
|
||||||
|
la a1, __bss_end
|
||||||
|
1:
|
||||||
|
bgeu a0, a1, 2f
|
||||||
|
sb zero, 0(a0)
|
||||||
|
addi a0, a0, 1
|
||||||
|
j 1b
|
||||||
|
2:
|
||||||
|
call main
|
||||||
|
tail _exit
|
||||||
|
|
||||||
|
.size _start, .-_start
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1,553 @@
|
|||||||
|
% Generated from json/card_source.json by tools/render_card.py.
|
||||||
|
% Do not edit manually; update the JSON and regenerate.
|
||||||
|
|
||||||
|
\documentclass[12pt,a4paper]{article}
|
||||||
|
|
||||||
|
\usepackage[T1]{fontenc}
|
||||||
|
\usepackage{lmodern}
|
||||||
|
\usepackage[utf8]{inputenc}
|
||||||
|
\usepackage[polish]{babel}
|
||||||
|
\usepackage{csquotes}
|
||||||
|
\usepackage{amsmath}
|
||||||
|
\usepackage{amssymb}
|
||||||
|
\usepackage{xcolor}
|
||||||
|
\usepackage[a4paper,left=2.15cm,right=2.15cm,top=0.5cm,bottom=0.5cm,headheight=24mm,headsep=3mm,includehead]{geometry}
|
||||||
|
\usepackage{eso-pic}
|
||||||
|
\usepackage{marginnote}
|
||||||
|
\usepackage{array}
|
||||||
|
\usepackage{tabularx}
|
||||||
|
\usepackage{graphicx}
|
||||||
|
\usepackage{pdflscape}
|
||||||
|
\usepackage{float}
|
||||||
|
\usepackage{silence}
|
||||||
|
\WarningFilter{soulutf8}{This package is obsolete}
|
||||||
|
\usepackage{pdfcomment}
|
||||||
|
\usepackage{enumitem}
|
||||||
|
\usepackage{needspace}
|
||||||
|
\usepackage{fancyhdr}
|
||||||
|
\usepackage{lastpage}
|
||||||
|
\usepackage{microtype}
|
||||||
|
\usepackage[most]{tcolorbox}
|
||||||
|
\usepackage{qrcode}
|
||||||
|
\IfFileExists{references.bib}{%
|
||||||
|
\usepackage[backend=biber,style=numeric,sorting=none]{biblatex}%
|
||||||
|
\addbibresource{references.bib}%
|
||||||
|
}{}
|
||||||
|
|
||||||
|
\hypersetup{hidelinks}
|
||||||
|
|
||||||
|
\IfFileExists{build-meta.tex}{%
|
||||||
|
\input{build-meta.tex}%
|
||||||
|
}{%
|
||||||
|
\newcommand{\BuildCommit}{lokalna}%
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\DocumentAuthor}{M. Pabiszczak}
|
||||||
|
\newcommand{\DocumentYear}{2026}
|
||||||
|
\newcommand{\CardSeries}{c}
|
||||||
|
\newcommand{\CardNumber}{13}
|
||||||
|
\newcommand{\CardCount}{13}
|
||||||
|
\newcommand{\CardSlug}{gpio-edges-debounce}
|
||||||
|
\newcommand{\CardVersion}{v00.01}
|
||||||
|
\newcommand{\DocumentUUID}{72e2cbba-4658-5de2-a0dc-8b600ee3f7b0}
|
||||||
|
|
||||||
|
\newcommand{\EmptyCheck}{\(\square\)}
|
||||||
|
\newcommand{\EscAnswerLines}[1][3]{\par\noindent\dotfill\par\noindent\dotfill\par\noindent\dotfill}
|
||||||
|
\newcommand{\EscWriteRow}[1][2.8em]{\rule{0pt}{#1}}
|
||||||
|
\setlength{\parindent}{0pt}
|
||||||
|
\setlength{\parskip}{0.45em}
|
||||||
|
\setlength{\headheight}{24mm}
|
||||||
|
\setlength{\headsep}{3mm}
|
||||||
|
\setlength{\footskip}{8mm}
|
||||||
|
\setlength{\marginparwidth}{1.5cm}
|
||||||
|
\setlength{\marginparsep}{0.25cm}
|
||||||
|
\renewcommand{\arraystretch}{1.22}
|
||||||
|
% Margin separator lines disabled for layout trial.
|
||||||
|
|
||||||
|
\newcommand{\ESCPageResourceHeader}{%
|
||||||
|
\begingroup%
|
||||||
|
\setlength{\parskip}{0pt}%
|
||||||
|
\setlength{\fboxsep}{0pt}%
|
||||||
|
\setlength{\fboxrule}{0.35pt}%
|
||||||
|
\setlength{\arrayrulewidth}{0.35pt}%
|
||||||
|
\noindent\fbox{%
|
||||||
|
\begin{minipage}[c][23.5mm][c]{\dimexpr\textwidth-2\fboxrule\relax}%
|
||||||
|
\begin{minipage}[c][23mm][c]{24mm}\centering%
|
||||||
|
\href{https://zsl-gitea.mpabi.pl/edu-inf/lab-rv32i-c-gpio-edges-debounce}{\qrcode[level=L,height=22mm]{https://zsl-gitea.mpabi.pl/edu-inf/lab-rv32i-c-gpio-edges-debounce}}%
|
||||||
|
\end{minipage}%
|
||||||
|
\vrule width0.35pt%
|
||||||
|
\begin{minipage}[c][23mm][c]{\dimexpr\linewidth-48mm-0.7pt\relax}%
|
||||||
|
\setlength{\tabcolsep}{0pt}%
|
||||||
|
\renewcommand{\arraystretch}{0}%
|
||||||
|
\begin{tabularx}{\linewidth}{@{}p{\dimexpr0.50000000\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.15126050\linewidth-\arrayrulewidth\relax}|X@{}}%
|
||||||
|
\parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{3.2}{3.4}\selectfont\ttfamily\bfseries TITLE}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{7.7}{7.9}\selectfont\ttfamily\bfseries GPIO, zbocza i quiet-window debounce}\hspace{0.45mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{3.2}{3.4}\selectfont\ttfamily\bfseries VER.}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{6.6}{6.8}\selectfont\ttfamily\bfseries v00.01}\hspace{0.45mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{3.2}{3.4}\selectfont\ttfamily\bfseries DATETIME}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{6.4}{6.6}\selectfont\ttfamily\bfseries 2026-07-20T00:00:00+02:00}\hspace{0.45mm}}\par} \\%
|
||||||
|
\end{tabularx}%
|
||||||
|
\par\nointerlineskip%
|
||||||
|
\hrule height0.35pt%
|
||||||
|
\nointerlineskip%
|
||||||
|
\begin{tabularx}{\linewidth}{@{}p{\dimexpr0.50000000\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.25210084\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.14285714\linewidth-\arrayrulewidth\relax}|X@{}}%
|
||||||
|
\parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{3.2}{3.4}\selectfont\ttfamily\bfseries PROJECT}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{7.7}{7.9}\selectfont\ttfamily\bfseries Freestanding C na RV32I}\hspace{0.45mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{3.2}{3.4}\selectfont\ttfamily\bfseries SERIES}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{6.4}{6.6}\selectfont\ttfamily\bfseries C · Freestanding RV32I and K\&R}\hspace{0.45mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{3.2}{3.4}\selectfont\ttfamily\bfseries CARD}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{6.4}{6.6}\selectfont\ttfamily\bfseries 13/13}\hspace{0.45mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{3.2}{3.4}\selectfont\ttfamily\bfseries SHEET}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{6.4}{6.6}\selectfont\ttfamily\bfseries \thepage/\pageref{LastPage}}\hspace{0.45mm}}\par} \\%
|
||||||
|
\end{tabularx}%
|
||||||
|
\par\nointerlineskip%
|
||||||
|
\hrule height0.35pt%
|
||||||
|
\nointerlineskip%
|
||||||
|
\begin{tabularx}{\linewidth}{@{}p{\dimexpr0.05882353\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.05042017\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.14705882\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.09243697\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.07563025\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.07563025\linewidth-\arrayrulewidth\relax}|X@{}}%
|
||||||
|
\parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.15mm}{\fontsize{2.85}{3}\selectfont\ttfamily\bfseries SUBJ.}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{4.45}{4.65}\selectfont\ttfamily\bfseries Inf.}\hspace{0.15mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.15mm}{\fontsize{2.85}{3}\selectfont\ttfamily\bfseries PROG.}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{4.45}{4.65}\selectfont\ttfamily\bfseries —}\hspace{0.15mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.15mm}{\fontsize{2.85}{3}\selectfont\ttfamily\bfseries CORE}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{4.45}{4.65}\selectfont\ttfamily\bfseries —}\hspace{0.15mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.15mm}{\fontsize{2.85}{3}\selectfont\ttfamily\bfseries SCOPE}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{4.45}{4.65}\selectfont\ttfamily\bfseries —}\hspace{0.15mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.15mm}{\fontsize{2.85}{3}\selectfont\ttfamily\bfseries LEVEL}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{4.45}{4.65}\selectfont\ttfamily\bfseries —}\hspace{0.15mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.15mm}{\fontsize{2.85}{3}\selectfont\ttfamily\bfseries POS.}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{4.45}{4.65}\selectfont\ttfamily\bfseries —}\hspace{0.15mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hbox to\linewidth{\hspace{0.35mm}{\fontsize{3.4}{3.75}\selectfont\ttfamily\bfseries GITEA UUID}\hfill{\fontsize{3.4}{3.75}\selectfont\ttfamily 72e2cbba-4658-5de2-a0dc-8b600ee3f7b0}\hspace{0.35mm}}\par\nointerlineskip\hbox to\linewidth{\hspace{0.35mm}{\fontsize{3.4}{3.75}\selectfont\ttfamily\bfseries CARD UUID} {\fontsize{3.4}{3.75}\selectfont\ttfamily 29e6021a-d033-5fbe-a5fc-ec03442ac68a}\hfill{\fontsize{3.4}{3.75}\selectfont\ttfamily\bfseries AUTHOR M. Pabiszczak}\hspace{0.35mm}}} \\%
|
||||||
|
\end{tabularx}%
|
||||||
|
\par\nointerlineskip%
|
||||||
|
\hrule height0.35pt%
|
||||||
|
\nointerlineskip%
|
||||||
|
\begin{tabularx}{\linewidth}{@{}X@{}}%
|
||||||
|
\parbox[c][3.46276mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{4.5}{4.85}\selectfont\ttfamily\bfseries GITEA} {\fontsize{4.5}{4.85}\selectfont\ttfamily\href{https://zsl-gitea.mpabi.pl/edu-inf/lab-rv32i-c-gpio-edges-debounce}{\nolinkurl{https://zsl-gitea.mpabi.pl/edu-inf/lab-rv32i-c-gpio-edges-debounce}}}} \\%
|
||||||
|
\end{tabularx}%
|
||||||
|
\par\nointerlineskip%
|
||||||
|
\hrule height0.35pt%
|
||||||
|
\nointerlineskip%
|
||||||
|
\begin{tabularx}{\linewidth}{@{}X@{}}%
|
||||||
|
\parbox[c][3.46276mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{4.5}{4.85}\selectfont\ttfamily\bfseries HTML} {\fontsize{4.5}{4.85}\selectfont\ttfamily\href{https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/29e6021a-d033-5fbe-a5fc-ec03442ac68a}{\nolinkurl{https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/29e6021a-d033-5fbe-a5fc-ec03442ac68a}}}} \\%
|
||||||
|
\end{tabularx}%
|
||||||
|
\end{minipage}%
|
||||||
|
\vrule width0.35pt%
|
||||||
|
\begin{minipage}[c][23mm][c]{24mm}\centering%
|
||||||
|
\href{https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/29e6021a-d033-5fbe-a5fc-ec03442ac68a}{\qrcode[level=L,height=22mm]{https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/29e6021a-d033-5fbe-a5fc-ec03442ac68a}}%
|
||||||
|
\end{minipage}%
|
||||||
|
\end{minipage}%
|
||||||
|
}%
|
||||||
|
\endgroup%
|
||||||
|
}
|
||||||
|
|
||||||
|
\pagestyle{fancy}
|
||||||
|
\fancyhf{}
|
||||||
|
\fancyhead[C]{\ESCPageResourceHeader}
|
||||||
|
\renewcommand{\headrulewidth}{0pt}
|
||||||
|
\renewcommand{\footrulewidth}{0pt}
|
||||||
|
|
||||||
|
\newcommand{\ESCMarginTag}[4]{%
|
||||||
|
\hspace*{#1}\textcolor{#2}{#3~#4}%
|
||||||
|
}
|
||||||
|
\newcommand{\ESCSectionBlockStart}{%
|
||||||
|
\Needspace{8\baselineskip}%
|
||||||
|
\par\vspace{0.35em}%
|
||||||
|
\noindent\textcolor{black!28}{\rule{\textwidth}{0.35pt}}%
|
||||||
|
\par\vspace{0.15em}%
|
||||||
|
}
|
||||||
|
\newcommand{\ESCSectionBlockEnd}{%
|
||||||
|
\par\vspace{0.45em}%
|
||||||
|
}
|
||||||
|
\newcommand{\ESCTinyStepSeparator}{%
|
||||||
|
\par\vspace{0.10em}%
|
||||||
|
\noindent{\color{black!18}\leaders\hbox{\rule{0.60em}{0.22pt}\hspace{0.38em}}\hfill\kern0pt}%
|
||||||
|
\par\vspace{0.10em}%
|
||||||
|
}
|
||||||
|
\newtcolorbox{ESCTaskFrame}[1]{enhanced,breakable,arc=0pt,boxrule=0.35pt,colback=white,colframe=black,boxsep=0pt,left=1.4mm,right=1.4mm,top=0.8mm,bottom=0.8mm,colbacktitle=black!7,coltitle=black,title={\ttfamily\bfseries TASK\quad #1}}
|
||||||
|
\newtcolorbox{ESCBlockFrame}[1]{enhanced,breakable,arc=0pt,boxrule=0.35pt,colback=white,colframe=black!55,boxsep=0pt,left=1.0mm,right=1.0mm,top=0.6mm,bottom=0.6mm,colbacktitle=black!4,coltitle=black,title={\ttfamily\bfseries BLOCK\quad #1}}
|
||||||
|
\newtcolorbox{ESCStepFrame}[1]{enhanced,breakable,arc=0pt,boxrule=0.35pt,colback=white,colframe=black!28,boxsep=0pt,left=0.8mm,right=0.8mm,top=0.45mm,bottom=0.45mm,colbacktitle=black!2,coltitle=black,title={\ttfamily STEP\quad #1}}
|
||||||
|
\newtcolorbox{ESCConclusionFrame}{enhanced,breakable,arc=0pt,boxrule=0.45pt,colback=blue!2,colframe=blue!45!black,boxsep=0pt,left=1.2mm,right=1.2mm,top=0.7mm,bottom=0.7mm,colbacktitle=blue!7,coltitle=black,title={\ttfamily\bfseries WNIOSEK}}
|
||||||
|
|
||||||
|
\newcommand{\ESCLearningTreeWidget}{%
|
||||||
|
\reversemarginpar
|
||||||
|
\marginnote[%
|
||||||
|
\begin{minipage}{\marginparwidth}%
|
||||||
|
\raggedright
|
||||||
|
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||||
|
\begin{minipage}[t]{\marginparwidth}%
|
||||||
|
\raggedright
|
||||||
|
{\bfseries\textcolor{black!65}{TECH}\par}%
|
||||||
|
\vspace{0.08em}%
|
||||||
|
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||||
|
\hspace*{0.28em}\textcolor{orange!85!black}{EK~LOCAL~C}\textcolor{black!48}{\pdftooltip[width=\textwidth]{.01}{Uczeń uruchamia obrazy RV32I z limitem cykli, odczytuje rejestry MMIO i kontrolera przerwań,\textCR przelicza czasy bounce i sprawdza, że main, a nie ISR, wykonuje politykę debounce oraz\textCR aktualizuje wyjścia.}}\par%
|
||||||
|
\hspace*{0.54em}\textcolor{blue!70!black}{KW~LOCAL~C}\textcolor{black!48}{\pdftooltip[width=\textwidth]{.01}{Kod, przewidywanie i pomiar tworzą jeden dowód.}}\par%
|
||||||
|
\end{minipage}%
|
||||||
|
}%
|
||||||
|
\end{minipage}%
|
||||||
|
]{}
|
||||||
|
\normalmarginpar
|
||||||
|
|
||||||
|
\marginnote{%
|
||||||
|
\begin{minipage}{\marginparwidth}%
|
||||||
|
\raggedright
|
||||||
|
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||||
|
\hspace*{0.06cm}%
|
||||||
|
\begin{minipage}[t]{\dimexpr\marginparwidth-0.06cm\relax}%
|
||||||
|
\raggedright
|
||||||
|
{\bfseries\textcolor{black!65}{OG}\par}%
|
||||||
|
\vspace{0.08em}%
|
||||||
|
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||||
|
\hspace*{0.28em}\textcolor{green!50!black}{EN~LOCAL~EN~C}\textcolor{black!48}{\pdftooltip[width=\textwidth]{.01}{Uczeń dla każdego zdarzenia wskazuje osobno source, pending, enable, decyzję dispatchera,\textCR minimalny efekt ISR i późniejszy efekt normalnego kodu.}}\par%
|
||||||
|
\hspace*{0.54em}\textcolor{blue!70!black}{KW~LOCAL~KW~C}\textcolor{black!48}{\pdftooltip[width=\textwidth]{.01}{Kod, przewidywanie i pomiar tworzą jeden dowód.}}\par%
|
||||||
|
\end{minipage}%
|
||||||
|
}%
|
||||||
|
\end{minipage}%
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
\sloppy
|
||||||
|
|
||||||
|
\vspace{1.0em}
|
||||||
|
\noindent{\Large\bfseries Cel karty}\par
|
||||||
|
\vspace{0.35em}
|
||||||
|
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.
|
||||||
|
|
||||||
|
\vspace{0.8em}
|
||||||
|
\noindent\textcolor{black!25}{\rule{\textwidth}{0.35pt}}
|
||||||
|
\vspace{0.7em}
|
||||||
|
\noindent{\Large\bfseries Zakres i zachowane przykłady}\par
|
||||||
|
\vspace{0.35em}
|
||||||
|
Cztery przykłady wykonują się na centralnym modelu RTL Hazard3. TEST\_IN i UART TEST\_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.\par\textbf{Układ każdego przykładu:} pełny profil A1--A8; widok N/D ma jawny powód, a diagram nie jest wymagany, gdy tekst daje lepszy dowód.
|
||||||
|
\vspace{0.55em}
|
||||||
|
\small\begin{tabularx}{\textwidth}{@{}>{\ttfamily\raggedright\arraybackslash}p{1.10cm}>{\ttfamily\raggedright\arraybackslash}p{1.45cm}X>{\raggedright\arraybackslash}p{2.50cm}@{}}
|
||||||
|
\textbf{Typ} & \textbf{Task} & \textbf{Idea} & \textbf{Waga} \\ \hline
|
||||||
|
\hline
|
||||||
|
\textbf{\texttt{POLL}} & \textbf{\texttt{Task01}} & \textbf{kierunek, latch wyjścia i testbench-only bodziec wejścia} & \textbf{kluczowe} \\ \hline
|
||||||
|
\hline
|
||||||
|
\textbf{\texttt{IRQ18}} & \textbf{\texttt{Task02}} & \textbf{zbocze, pending peryferium, kontroler i resume} & \textbf{kluczowe} \\ \hline
|
||||||
|
\hline
|
||||||
|
\textbf{\texttt{DBNC}} & \textbf{\texttt{Task03}} & \textbf{krótki ISR, mtime i decyzja stabilności w main} & \textbf{kluczowe} \\ \hline
|
||||||
|
\hline
|
||||||
|
\textbf{\texttt{LOOP}} & \textbf{\texttt{Task04}} & \textbf{trzy źródła, dwa dispatchery, flagi i efekty main} & \textbf{kluczowe} \\ \hline
|
||||||
|
\end{tabularx}
|
||||||
|
\normalsize
|
||||||
|
|
||||||
|
|
||||||
|
\vspace{0.8em}
|
||||||
|
\noindent\textcolor{black!25}{\rule{\textwidth}{0.35pt}}
|
||||||
|
|
||||||
|
\clearpage
|
||||||
|
|
||||||
|
\ESCSectionBlockStart
|
||||||
|
\section{1 — Rejestr modelu nie jest fizycznym pinem}
|
||||||
|
\reversemarginpar
|
||||||
|
\marginnote[%
|
||||||
|
\begin{minipage}{\marginparwidth}%
|
||||||
|
\raggedright
|
||||||
|
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||||
|
\begin{minipage}[t]{\marginparwidth}%
|
||||||
|
\raggedright
|
||||||
|
{\bfseries\textcolor{black!65}{TECH}\par}%
|
||||||
|
\vspace{0.08em}%
|
||||||
|
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||||
|
\end{minipage}%
|
||||||
|
}%
|
||||||
|
\end{minipage}%
|
||||||
|
]{}[-3.1em]
|
||||||
|
\normalmarginpar
|
||||||
|
|
||||||
|
\marginnote{%
|
||||||
|
\begin{minipage}{\marginparwidth}%
|
||||||
|
\raggedright
|
||||||
|
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||||
|
\hspace*{0.06cm}%
|
||||||
|
\begin{minipage}[t]{\dimexpr\marginparwidth-0.06cm\relax}%
|
||||||
|
\raggedright
|
||||||
|
{\bfseries\textcolor{black!65}{OG}\par}%
|
||||||
|
\vspace{0.08em}%
|
||||||
|
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||||
|
\end{minipage}%
|
||||||
|
}%
|
||||||
|
\end{minipage}%
|
||||||
|
}[-3.1em]
|
||||||
|
|
||||||
|
|
||||||
|
GPIO modelu zaczyna się pod adresem \texttt{0xc0000300}. \texttt{DIR} wybiera kierunek, \texttt{OUT/OUT\_SET/OUT\_CLR} sterują zatrzaśnięciem wyjściowym, a \texttt{IN} pokazuje wyjścia dla pinów ustawionych jako output i bodziec zewnętrzny dla pinów input. \texttt{RISE\_EN} i \texttt{FALL\_EN} wybierają wykrywane zbocza, a \texttt{PENDING} zatrzaskuje je do zapisu W1C.
|
||||||
|
|
||||||
|
\texttt{TEST\_IN} istnieje tylko po stronie laboratorium. Pozwala programowi testowemu wywołać zmianę otoczenia bez fizycznego przewodu. Produkcyjny sterownik czyta \texttt{IN}; nie powinien zawierać zapisu do takiego fixture'u.
|
||||||
|
\ESCSectionBlockEnd
|
||||||
|
|
||||||
|
\ESCSectionBlockStart
|
||||||
|
\section{2 — Source, pending i enable to trzy różne fakty}
|
||||||
|
\reversemarginpar
|
||||||
|
\marginnote[%
|
||||||
|
\begin{minipage}{\marginparwidth}%
|
||||||
|
\raggedright
|
||||||
|
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||||
|
\begin{minipage}[t]{\marginparwidth}%
|
||||||
|
\raggedright
|
||||||
|
{\bfseries\textcolor{black!65}{TECH}\par}%
|
||||||
|
\vspace{0.08em}%
|
||||||
|
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||||
|
\end{minipage}%
|
||||||
|
}%
|
||||||
|
\end{minipage}%
|
||||||
|
]{}[-3.1em]
|
||||||
|
\normalmarginpar
|
||||||
|
|
||||||
|
\marginnote{%
|
||||||
|
\begin{minipage}{\marginparwidth}%
|
||||||
|
\raggedright
|
||||||
|
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||||
|
\hspace*{0.06cm}%
|
||||||
|
\begin{minipage}[t]{\dimexpr\marginparwidth-0.06cm\relax}%
|
||||||
|
\raggedright
|
||||||
|
{\bfseries\textcolor{black!65}{OG}\par}%
|
||||||
|
\vspace{0.08em}%
|
||||||
|
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||||
|
\end{minipage}%
|
||||||
|
}%
|
||||||
|
\end{minipage}%
|
||||||
|
}[-3.1em]
|
||||||
|
|
||||||
|
|
||||||
|
Zmiana 0 na 1 jest source dla detektora rising edge. Włączony bit \texttt{RISE\_EN} pozwala zatrzasnąć bit w \texttt{PENDING}; ten bit utrzymuje poziomową linię GPIO external IRQ18. Kontroler Hazard3 osobno ma per-line enable, priorytet oraz globalne bramki \texttt{mie.MEIE} i \texttt{mstatus.MIE}. Dispatcher odczytuje aktywny numer i wywołuje handler z tablicy.
|
||||||
|
|
||||||
|
W1C usuwa źródło poziomowego żądania po stronie peryferium. Sam powrót z handlera, odczyt \texttt{PENDING} ani zamknięcie maski nie potwierdzają zdarzenia.
|
||||||
|
\ESCSectionBlockEnd
|
||||||
|
|
||||||
|
\ESCSectionBlockStart
|
||||||
|
\section{3 — Debounce jest decyzją czasu w main}
|
||||||
|
\reversemarginpar
|
||||||
|
\marginnote[%
|
||||||
|
\begin{minipage}{\marginparwidth}%
|
||||||
|
\raggedright
|
||||||
|
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||||
|
\begin{minipage}[t]{\marginparwidth}%
|
||||||
|
\raggedright
|
||||||
|
{\bfseries\textcolor{black!65}{TECH}\par}%
|
||||||
|
\vspace{0.08em}%
|
||||||
|
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||||
|
\end{minipage}%
|
||||||
|
}%
|
||||||
|
\end{minipage}%
|
||||||
|
]{}[-3.1em]
|
||||||
|
\normalmarginpar
|
||||||
|
|
||||||
|
\marginnote{%
|
||||||
|
\begin{minipage}{\marginparwidth}%
|
||||||
|
\raggedright
|
||||||
|
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||||
|
\hspace*{0.06cm}%
|
||||||
|
\begin{minipage}[t]{\dimexpr\marginparwidth-0.06cm\relax}%
|
||||||
|
\raggedright
|
||||||
|
{\bfseries\textcolor{black!65}{OG}\par}%
|
||||||
|
\vspace{0.08em}%
|
||||||
|
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||||
|
\end{minipage}%
|
||||||
|
}%
|
||||||
|
\end{minipage}%
|
||||||
|
}[-3.1em]
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Sekwencja \texttt{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ń.
|
||||||
|
\ESCSectionBlockEnd
|
||||||
|
|
||||||
|
\ESCSectionBlockStart
|
||||||
|
\section{4 — Superloop jest granicą serii raw C}
|
||||||
|
\reversemarginpar
|
||||||
|
\marginnote[%
|
||||||
|
\begin{minipage}{\marginparwidth}%
|
||||||
|
\raggedright
|
||||||
|
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||||
|
\begin{minipage}[t]{\marginparwidth}%
|
||||||
|
\raggedright
|
||||||
|
{\bfseries\textcolor{black!65}{TECH}\par}%
|
||||||
|
\vspace{0.08em}%
|
||||||
|
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||||
|
\end{minipage}%
|
||||||
|
}%
|
||||||
|
\end{minipage}%
|
||||||
|
]{}[-3.1em]
|
||||||
|
\normalmarginpar
|
||||||
|
|
||||||
|
\marginnote{%
|
||||||
|
\begin{minipage}{\marginparwidth}%
|
||||||
|
\raggedright
|
||||||
|
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||||
|
\hspace*{0.06cm}%
|
||||||
|
\begin{minipage}[t]{\dimexpr\marginparwidth-0.06cm\relax}%
|
||||||
|
\raggedright
|
||||||
|
{\bfseries\textcolor{black!65}{OG}\par}%
|
||||||
|
\vspace{0.08em}%
|
||||||
|
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||||
|
\end{minipage}%
|
||||||
|
}%
|
||||||
|
\end{minipage}%
|
||||||
|
}[-3.1em]
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
\ESCSectionBlockEnd
|
||||||
|
|
||||||
|
\clearpage
|
||||||
|
\ESCSectionBlockStart
|
||||||
|
\section{Zadania — zachowane przykłady w profilu A1–A8}
|
||||||
|
\reversemarginpar
|
||||||
|
\marginnote[%
|
||||||
|
\begin{minipage}{\marginparwidth}%
|
||||||
|
\raggedright
|
||||||
|
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||||
|
\begin{minipage}[t]{\marginparwidth}%
|
||||||
|
\raggedright
|
||||||
|
{\bfseries\textcolor{black!65}{TECH}\par}%
|
||||||
|
\vspace{0.08em}%
|
||||||
|
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||||
|
\end{minipage}%
|
||||||
|
}%
|
||||||
|
\end{minipage}%
|
||||||
|
]{}[-3.1em]
|
||||||
|
\normalmarginpar
|
||||||
|
|
||||||
|
\marginnote{%
|
||||||
|
\begin{minipage}{\marginparwidth}%
|
||||||
|
\raggedright
|
||||||
|
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||||
|
\hspace*{0.06cm}%
|
||||||
|
\begin{minipage}[t]{\dimexpr\marginparwidth-0.06cm\relax}%
|
||||||
|
\raggedright
|
||||||
|
{\bfseries\textcolor{black!65}{OG}\par}%
|
||||||
|
\vspace{0.08em}%
|
||||||
|
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||||
|
\end{minipage}%
|
||||||
|
}%
|
||||||
|
\end{minipage}%
|
||||||
|
}[-3.1em]
|
||||||
|
|
||||||
|
|
||||||
|
\begin{ESCTaskFrame}{TASK01 · Polling DIR, OUT i IN}
|
||||||
|
{\scriptsize\ttfamily 223fada6-43e4-532a-9c9b-e17445857214}\par
|
||||||
|
\begin{ESCBlockFrame}{A1 CONTEXT · AKTYWNE — dwa źródła widocznego IN}
|
||||||
|
\textbf{Zachowany przykład:} \nolinkurl{src/tasks/task01_gpio_polling.c}.\par Dla bitu z \texttt{DIR=1} widoczne \texttt{IN} pochodzi z latcha \texttt{OUT}; dla \texttt{DIR=0} pochodzi z modelowanego wejścia. \texttt{TEST\_IN} zmienia tylko tę drugą część i jest bodźcem laboratorium, nie pinem RP2350.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A2 STRUCTURE · AKTYWNE — pin0 output, pin1 input}
|
||||||
|
Maska 0x1 wybiera pin wyjściowy, a 0x2 wejściowy. Rejestry SET i CLR modyfikują latch bez read--modify--write. Jeden odczyt \texttt{IN} składa oba widoki, dlatego przy obu stanach wysokich wynosi 0x3.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A3 DISPATCH · N/D}
|
||||||
|
\textbf{N/D.} Task01 wyłącza detektory zboczy i przerwania; każdy krok jest bezpośrednim pollingiem MMIO, bez ISR, callbacku ani wyboru handlera.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A4 APPLICATION · AKTYWNE — kontrolowany round trip poziomów}
|
||||||
|
Po external high program widzi 0x2. SET pinu0 daje OUT=0x1 i IN=0x3. CLR pinu0 oraz TEST\_IN=0 kończą sekwencję IN=0. Te liczby są bitowymi relacjami modelu, nie numerami pinów konkretnej płytki.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A5 FLOW · AKTYWNE — configure, stimulate, sample, clear}
|
||||||
|
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.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A6 STATE · AKTYWNE — 0x2 → 0x3 → 0x0}
|
||||||
|
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.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A7 RUNTIME · AKTYWNE — rzeczywiste transakcje pod 0xc0000300}
|
||||||
|
Log RTL raportuje 1,2,1,3,0 i pass=1. Listing powinien pokazać osobne zapisy do offsetów DIR, TEST\_IN, OUT\_SET i OUT\_CLR oraz odczyty IN; nie ma wejścia do wspólnego dispatchera external IRQ.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A8 PATTERNS · AKTYWNE — polling z jawną granicą fixture}
|
||||||
|
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.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{Ćwiczenie · Przewidywanie → wykonanie → wniosek}
|
||||||
|
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\_IN jest dozwolony tylko w fixture testbencha.
|
||||||
|
\par\textbf{Evidence:} Tabela DIR/OUT/IN przed uruchomieniem, log wykonania RTL Hazard3 z kodem wyjścia, odczyt rejestrów MMIO i wniosek oddzielający fixture TEST\_IN od produkcyjnego API GPIO.
|
||||||
|
\par\textbf{Acceptance:} 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.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{TASK01 · Zapis dowodu ucznia}
|
||||||
|
\textbf{1. Przewidywanie przed uruchomieniem}\par\noindent\dotfill\par\noindent\dotfill\par\textbf{2. Log RTL: DIR, OUT i kolejne wartości IN}\par\noindent\dotfill\par\noindent\dotfill\par\textbf{3. Granica modelu: TEST\_IN kontra fizyczny pin}\par\noindent\dotfill\par\noindent\dotfill\par\textbf{4. Wniosek: reguła języka lub kontrakt targetu potwierdzony przez pomiar}\par\noindent\dotfill\par\noindent\dotfill
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\end{ESCTaskFrame}
|
||||||
|
\clearpage
|
||||||
|
\begin{ESCTaskFrame}{TASK02 · Rising edge, IRQ18 i W1C}
|
||||||
|
{\scriptsize\ttfamily 93fb66db-d0ac-5fc2-811e-5b28e072a346}\par
|
||||||
|
\begin{ESCBlockFrame}{A1 CONTEXT · AKTYWNE — zbocze tworzy zatrzaśnięty fakt}
|
||||||
|
\textbf{Zachowany przykład:} \nolinkurl{src/tasks/task02_gpio_rising_irq.c}.\par Bodziec TEST\_IN zmienia pin2 z 0 na 1. Włączony bit RISE\_EN tworzy bit 0x4 w GPIO PENDING, a ten utrzymuje external IRQ18. TEST\_IN jest sztuczny, lecz dalsza ścieżka przez kontroler i rdzeń jest rzeczywistą ścieżką modelu RTL.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A2 STRUCTURE · AKTYWNE — dwa pending i tablica handlerów}
|
||||||
|
Pierwszy pending leży w peryferium jako maska pinów. Drugi jest widokiem aktywnej linii 18 w kontrolerze Hazard3. Tablica \texttt{\_external\_irq\_table[18]} przechowuje adres zwykłej funkcji handlera.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A3 DISPATCH · AKTYWNE — external vector wybiera linię 18}
|
||||||
|
\texttt{mtvec} wybiera wspólny vector machine external interrupt. \texttt{irq\_dispatch.S} odczytuje aktywną linię z \texttt{meinext}, indeksuje tablicę i wykonuje pośredni \texttt{jalr} do handlera GPIO. Handler potwierdza, że bieżący numer to 18.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A4 APPLICATION · AKTYWNE — jedno zbocze, jedno wznowienie}
|
||||||
|
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.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A5 FLOW · AKTYWNE — source → pending → enable → dispatch → W1C}
|
||||||
|
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.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A6 STATE · AKTYWNE — IDLE, LATCHED, HANDLING, ACKED, RESUMED}
|
||||||
|
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.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A7 RUNTIME · AKTYWNE — 0x4 w GPIO i 18 w kontrolerze}
|
||||||
|
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 \texttt{mret}.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A8 PATTERNS · AKTYWNE — acknowledge peripheral before publish}
|
||||||
|
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.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{Ćwiczenie · Przewidywanie → wykonanie → wniosek}
|
||||||
|
Rozpisz ścieżkę od zmiany TEST\_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.
|
||||||
|
\par\textbf{Evidence:} 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.
|
||||||
|
\par\textbf{Acceptance:} 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.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{TASK02 · Zapis dowodu ucznia}
|
||||||
|
\textbf{1. Przewidywanie przed uruchomieniem}\par\noindent\dotfill\par\noindent\dotfill\par\textbf{2. Log RTL: pending GPIO i kontrolera przed/po ISR}\par\noindent\dotfill\par\noindent\dotfill\par\textbf{3. Dowód dispatchu IRQ18 i zapisu W1C}\par\noindent\dotfill\par\noindent\dotfill\par\textbf{4. Wniosek: reguła języka lub kontrakt targetu potwierdzony przez pomiar}\par\noindent\dotfill\par\noindent\dotfill
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\end{ESCTaskFrame}
|
||||||
|
\clearpage
|
||||||
|
\begin{ESCTaskFrame}{TASK03 · Quiet-window debounce po trzech zboczach}
|
||||||
|
{\scriptsize\ttfamily a2e3b023-04f1-5863-a4e0-360b5ec69b89}\par
|
||||||
|
\begin{ESCBlockFrame}{A1 CONTEXT · AKTYWNE — IRQ rejestruje zmianę, nie intencję użytkownika}
|
||||||
|
\textbf{Zachowany przykład:} \nolinkurl{src/tasks/task03_gpio_quiet_window.c}.\par Trzy przejścia TEST\_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.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A2 STRUCTURE · AKTYWNE — historia ISR i kandydat main}
|
||||||
|
ISR zapisuje trzy pary \texttt{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.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A3 DISPATCH · AKTYWNE — każde włączone zbocze wybiera GPIO18}
|
||||||
|
RISE\_EN i FALL\_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\_IN.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A4 APPLICATION · AKTYWNE — restart okna po każdym bounce}
|
||||||
|
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.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A5 FLOW · AKTYWNE — ISR publish, main observe, wait, validate}
|
||||||
|
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.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A6 STATE · AKTYWNE — HIGH?, LOW?, HIGH?, STABLE\_HIGH}
|
||||||
|
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\_HIGH powstaje dopiero po pełnym oknie bez kolejnego IRQ.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A7 RUNTIME · AKTYWNE — dwa bounce gapy krótsze od 0x7d0}
|
||||||
|
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\_after>=quiet.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A8 PATTERNS · AKTYWNE — timestamp in ISR, policy in consumer}
|
||||||
|
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ń.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{Ćwiczenie · Przewidywanie → wykonanie → wniosek}
|
||||||
|
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.
|
||||||
|
\par\textbf{Evidence:} Przewidywana sekwencja kandydatów, log RTL z trzema czasami i poziomami, niezależne przeliczenie gapów oraz accepted\_after i wniosek przypisujący politykę debounce do main.
|
||||||
|
\par\textbf{Acceptance:} 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.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{TASK03 · Zapis dowodu ucznia}
|
||||||
|
\textbf{1. Przewidywanie przed uruchomieniem}\par\noindent\dotfill\par\noindent\dotfill\par\textbf{2. Log RTL: trzy poziomy, czasy i gapy}\par\noindent\dotfill\par\noindent\dotfill\par\textbf{3. Dowód quiet window i miejsca decyzji w main}\par\noindent\dotfill\par\noindent\dotfill\par\textbf{4. Wniosek: reguła języka lub kontrakt targetu potwierdzony przez pomiar}\par\noindent\dotfill\par\noindent\dotfill
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\end{ESCTaskFrame}
|
||||||
|
\clearpage
|
||||||
|
\begin{ESCTaskFrame}{TASK04 · Timer + UART16 + GPIO18 bez RTOS}
|
||||||
|
{\scriptsize\ttfamily ebb18b6a-cf31-5595-9901-ac2ac870296f}\par
|
||||||
|
\begin{ESCBlockFrame}{A1 CONTEXT · AKTYWNE — trzy usługi, wspólny czas CPU}
|
||||||
|
\textbf{Zachowany przykład:} \nolinkurl{src/tasks/task04_raw_c_superloop.c}.\par 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\_RX i TEST\_IN tworzą tylko bodźce; logika timer/UART/GPIO po nich jest rzeczywistym modelem RTL.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A2 STRUCTURE · AKTYWNE — trzy publikacje i trzy bity efektu}
|
||||||
|
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.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A3 DISPATCH · AKTYWNE — slot 7 oraz external 16/18}
|
||||||
|
Machine timer cause 7 trafia bezpośrednio do \texttt{isr\_machine\_timer} i wraca przez \texttt{mret}. Machine external vector trafia do wspólnego dispatchera; \texttt{meinext} wybiera wpis 16 dla UART albo 18 dla GPIO i wykonuje \texttt{jalr}.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A4 APPLICATION · AKTYWNE — efekt dopiero po konsumpcji flagi}
|
||||||
|
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.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A5 FLOW · AKTYWNE — source → pending → dispatch → ISR → main}
|
||||||
|
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.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A6 STATE · AKTYWNE — trzy niezależne PRODUCED/CONSUMED}
|
||||||
|
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.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A7 RUNTIME · AKTYWNE — cause 7, linie 16/18 i OUT 0x7}
|
||||||
|
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.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{A8 PATTERNS · AKTYWNE — ISR-to-main flags with an explicit ceiling}
|
||||||
|
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ą.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{Ćwiczenie · Przewidywanie → wykonanie → wniosek}
|
||||||
|
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.
|
||||||
|
\par\textbf{Evidence:} 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.
|
||||||
|
\par\textbf{Acceptance:} 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.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{TASK04 · Zapis dowodu ucznia}
|
||||||
|
\textbf{1. Przewidywanie przed uruchomieniem}\par\noindent\dotfill\par\noindent\dotfill\par\textbf{2. Log RTL: timer, UART16, GPIO18 i efekty main}\par\noindent\dotfill\par\noindent\dotfill\par\textbf{3. Dowód dwóch dispatcherów i końcowego OUT/IN}\par\noindent\dotfill\par\noindent\dotfill\par\textbf{4. Wniosek: reguła języka lub kontrakt targetu potwierdzony przez pomiar}\par\noindent\dotfill\par\noindent\dotfill
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\end{ESCTaskFrame}
|
||||||
|
\ESCSectionBlockEnd
|
||||||
|
|
||||||
|
\end{document}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1,823 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../../../tools/card-layouts/schemas/card-source.schema.json",
|
||||||
|
"schema": "esc-card-source.v1",
|
||||||
|
"card": {
|
||||||
|
"id": "mpabi-inf-c-13-gpio-edges-debounce",
|
||||||
|
"series": "c",
|
||||||
|
"series_title": "C · Freestanding RV32I and K&R",
|
||||||
|
"number": "13",
|
||||||
|
"count": "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",
|
||||||
|
"project": "Freestanding C na RV32I",
|
||||||
|
"subject": "Informatyka",
|
||||||
|
"level": "Rok 1 · C13 · RV32I/Hazard3",
|
||||||
|
"revision_date": "2026-07-20T00:00:00+02:00",
|
||||||
|
"status": "Gotowa",
|
||||||
|
"version": "v00.01",
|
||||||
|
"uuid": "72e2cbba-4658-5de2-a0dc-8b600ee3f7b0",
|
||||||
|
"author": "M. Pabiszczak",
|
||||||
|
"year": "2026"
|
||||||
|
},
|
||||||
|
"generated": {
|
||||||
|
"tex": "doc/generated/main.tex",
|
||||||
|
"html": "web/index.html",
|
||||||
|
"html_css": "web/style.css",
|
||||||
|
"html_tree_inspector": false,
|
||||||
|
"react_app": true
|
||||||
|
},
|
||||||
|
"render_dictionary": false,
|
||||||
|
"template": "templates/karta-klasyczna.json",
|
||||||
|
"title_block": {
|
||||||
|
"category": "KARTA PRACY · INFORMATYKA",
|
||||||
|
"prepared_by": "M. Pabiszczak",
|
||||||
|
"prepared_on": "2026-07-20T00:00:00+02:00",
|
||||||
|
"title": "GPIO, zbocza i quiet-window debounce",
|
||||||
|
"url": "https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/29e6021a-d033-5fbe-a5fc-ec03442ac68a",
|
||||||
|
"repository_url": "https://zsl-gitea.mpabi.pl/edu-inf/lab-rv32i-c-gpio-edges-debounce",
|
||||||
|
"url_host_uuid": "dce7fb9d-7b2f-5d49-96a2-3a30d3070b84",
|
||||||
|
"url_domain": "mpabi.pl",
|
||||||
|
"doc_uuid": "29e6021a-d033-5fbe-a5fc-ec03442ac68a",
|
||||||
|
"revision": "v00.01",
|
||||||
|
"issued_on": "2026-07-20T00:00:00+02:00",
|
||||||
|
"series": "C-13",
|
||||||
|
"document_type": "karta pracy",
|
||||||
|
"tool": "card-layouts",
|
||||||
|
"show_qr": true,
|
||||||
|
"show_repository_qr": true,
|
||||||
|
"height_cm": 2.6,
|
||||||
|
"repeat_on_every_page": true,
|
||||||
|
"replace_front_matter": true
|
||||||
|
},
|
||||||
|
"front_page_break": true,
|
||||||
|
"front_page_scope": {
|
||||||
|
"title": "Cel karty",
|
||||||
|
"content_tex": "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_title": "Zakres i zachowane przykłady",
|
||||||
|
"scope_content_tex": "Cztery przykłady wykonują się na centralnym modelu RTL Hazard3. TEST\\_IN i UART TEST\\_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.\\par\\textbf{Układ każdego przykładu:} pełny profil A1--A8; widok N/D ma jawny powód, a diagram nie jest wymagany, gdy tekst daje lepszy dowód.",
|
||||||
|
"scope_table": {
|
||||||
|
"headers": [
|
||||||
|
"Typ",
|
||||||
|
"Task",
|
||||||
|
"Idea",
|
||||||
|
"Waga"
|
||||||
|
],
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"chapter": "POLL",
|
||||||
|
"task": "Task01",
|
||||||
|
"idea_tex": "kierunek, latch wyjścia i testbench-only bodziec wejścia",
|
||||||
|
"priority": "kluczowe",
|
||||||
|
"key": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chapter": "IRQ18",
|
||||||
|
"task": "Task02",
|
||||||
|
"idea_tex": "zbocze, pending peryferium, kontroler i resume",
|
||||||
|
"priority": "kluczowe",
|
||||||
|
"key": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chapter": "DBNC",
|
||||||
|
"task": "Task03",
|
||||||
|
"idea_tex": "krótki ISR, mtime i decyzja stabilności w main",
|
||||||
|
"priority": "kluczowe",
|
||||||
|
"key": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chapter": "LOOP",
|
||||||
|
"task": "Task04",
|
||||||
|
"idea_tex": "trzy źródła, dwa dispatchery, flagi i efekty main",
|
||||||
|
"priority": "kluczowe",
|
||||||
|
"key": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"side_margin_tree_layout": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"id": "zawodowe",
|
||||||
|
"label": "TECH",
|
||||||
|
"side": "left",
|
||||||
|
"tree": "WE -> EK -> KW",
|
||||||
|
"description": "Kod C, ABI i obserwacja RV32I."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ogolne",
|
||||||
|
"label": "OG",
|
||||||
|
"side": "right",
|
||||||
|
"tree": "WE -> EN -> KW",
|
||||||
|
"description": "Przewidywanie, pomiar i wniosek."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"learning_effects": {
|
||||||
|
"C13.EN01": {
|
||||||
|
"bloom_level": "Analiza",
|
||||||
|
"label": "Od poziomu wejścia do efektu aplikacji",
|
||||||
|
"text": "Uczeń dla każdego zdarzenia wskazuje osobno source, pending, enable, decyzję dispatchera, minimalny efekt ISR i późniejszy efekt normalnego kodu.",
|
||||||
|
"assessment_criteria": [
|
||||||
|
"C13.KW01"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"C13.EK01": {
|
||||||
|
"bloom_level": "Zastosowanie",
|
||||||
|
"label": "Dowód na GPIO/UART/timer RTL",
|
||||||
|
"text": "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.",
|
||||||
|
"assessment_criteria": [
|
||||||
|
"C13.KW01"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"assessment_criteria": {
|
||||||
|
"C13.KW01": {
|
||||||
|
"text": "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.",
|
||||||
|
"learning_effects": [
|
||||||
|
"C13.EN01",
|
||||||
|
"C13.EK01"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"educational_requirements": {
|
||||||
|
"C13.WE01": {
|
||||||
|
"text": "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.",
|
||||||
|
"label": "RV32I/Hazard3: polling GPIO, external IRQ18, debounce i końcowy superloop raw C",
|
||||||
|
"learning_effects": [
|
||||||
|
"C13.EN01",
|
||||||
|
"C13.EK01"
|
||||||
|
],
|
||||||
|
"learning_tree": {
|
||||||
|
"schema": "we-learning-tree.v1",
|
||||||
|
"policy": "Najpierw przewidywanie, następnie wykonanie i odczyt dowodu.",
|
||||||
|
"ogolne": [
|
||||||
|
{
|
||||||
|
"effect_ref": "C13.EN01",
|
||||||
|
"display": "EN C13 01",
|
||||||
|
"source": "LOCAL",
|
||||||
|
"official": "C",
|
||||||
|
"local": "01",
|
||||||
|
"kind": "EN",
|
||||||
|
"tree_id": "C13.WE01.OG.LOCAL.C.01",
|
||||||
|
"text": "Uczeń dla każdego zdarzenia wskazuje osobno source, pending, enable, decyzję dispatchera, minimalny efekt ISR i późniejszy efekt normalnego kodu.",
|
||||||
|
"kw": [
|
||||||
|
{
|
||||||
|
"criterion_ref": "C13.KW01",
|
||||||
|
"display": "KW C13 01",
|
||||||
|
"source": "LOCAL",
|
||||||
|
"kind": "KW",
|
||||||
|
"official": "C",
|
||||||
|
"local": "01",
|
||||||
|
"text": "Kod, przewidywanie i pomiar tworzą jeden dowód."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"zawodowe": [
|
||||||
|
{
|
||||||
|
"effect_ref": "C13.EK01",
|
||||||
|
"display": "EK C13 01",
|
||||||
|
"source": "LOCAL",
|
||||||
|
"official": "C",
|
||||||
|
"local": "01",
|
||||||
|
"kind": "EK",
|
||||||
|
"tree_id": "C13.WE01.TECH.LOCAL.C.01",
|
||||||
|
"text": "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.",
|
||||||
|
"kw": [
|
||||||
|
{
|
||||||
|
"criterion_ref": "C13.KW01",
|
||||||
|
"display": "KW C13 01",
|
||||||
|
"source": "LOCAL",
|
||||||
|
"kind": "KW",
|
||||||
|
"official": "C",
|
||||||
|
"local": "01",
|
||||||
|
"text": "Kod, przewidywanie i pomiar tworzą jeden dowód."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"title": "1 — Rejestr modelu nie jest fizycznym pinem",
|
||||||
|
"order": 10,
|
||||||
|
"content_kind": "prose",
|
||||||
|
"page_orientation": "portrait",
|
||||||
|
"content_tex": "GPIO modelu zaczyna się pod adresem \\texttt{0xc0000300}. \\texttt{DIR} wybiera kierunek, \\texttt{OUT/OUT\\_SET/OUT\\_CLR} sterują zatrzaśnięciem wyjściowym, a \\texttt{IN} pokazuje wyjścia dla pinów ustawionych jako output i bodziec zewnętrzny dla pinów input. \\texttt{RISE\\_EN} i \\texttt{FALL\\_EN} wybierają wykrywane zbocza, a \\texttt{PENDING} zatrzaskuje je do zapisu W1C.\n\n\\texttt{TEST\\_IN} istnieje tylko po stronie laboratorium. Pozwala programowi testowemu wywołać zmianę otoczenia bez fizycznego przewodu. Produkcyjny sterownik czyta \\texttt{IN}; nie powinien zawierać zapisu do takiego fixture'u.",
|
||||||
|
"educational_requirement_refs": [
|
||||||
|
"C13.WE01"
|
||||||
|
],
|
||||||
|
"learning_effect_refs": [
|
||||||
|
"C13.EN01",
|
||||||
|
"C13.EK01"
|
||||||
|
],
|
||||||
|
"assessment_criterion_refs": [
|
||||||
|
"C13.KW01"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "2 — Source, pending i enable to trzy różne fakty",
|
||||||
|
"order": 20,
|
||||||
|
"content_kind": "prose",
|
||||||
|
"page_orientation": "portrait",
|
||||||
|
"content_tex": "Zmiana 0 na 1 jest source dla detektora rising edge. Włączony bit \\texttt{RISE\\_EN} pozwala zatrzasnąć bit w \\texttt{PENDING}; ten bit utrzymuje poziomową linię GPIO external IRQ18. Kontroler Hazard3 osobno ma per-line enable, priorytet oraz globalne bramki \\texttt{mie.MEIE} i \\texttt{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 \\texttt{PENDING} ani zamknięcie maski nie potwierdzają zdarzenia.",
|
||||||
|
"educational_requirement_refs": [
|
||||||
|
"C13.WE01"
|
||||||
|
],
|
||||||
|
"learning_effect_refs": [
|
||||||
|
"C13.EN01",
|
||||||
|
"C13.EK01"
|
||||||
|
],
|
||||||
|
"assessment_criterion_refs": [
|
||||||
|
"C13.KW01"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "3 — Debounce jest decyzją czasu w main",
|
||||||
|
"order": 30,
|
||||||
|
"content_kind": "prose",
|
||||||
|
"page_orientation": "portrait",
|
||||||
|
"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 \\texttt{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ń.",
|
||||||
|
"educational_requirement_refs": [
|
||||||
|
"C13.WE01"
|
||||||
|
],
|
||||||
|
"learning_effect_refs": [
|
||||||
|
"C13.EN01",
|
||||||
|
"C13.EK01"
|
||||||
|
],
|
||||||
|
"assessment_criterion_refs": [
|
||||||
|
"C13.KW01"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "4 — Superloop jest granicą serii raw C",
|
||||||
|
"order": 40,
|
||||||
|
"content_kind": "prose",
|
||||||
|
"page_orientation": "portrait",
|
||||||
|
"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.",
|
||||||
|
"educational_requirement_refs": [
|
||||||
|
"C13.WE01"
|
||||||
|
],
|
||||||
|
"learning_effect_refs": [
|
||||||
|
"C13.EN01",
|
||||||
|
"C13.EK01"
|
||||||
|
],
|
||||||
|
"assessment_criterion_refs": [
|
||||||
|
"C13.KW01"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Zadania — zachowane przykłady w profilu A1–A8",
|
||||||
|
"order": 50,
|
||||||
|
"content_kind": "tasks",
|
||||||
|
"page_orientation": "portrait",
|
||||||
|
"task_refs": [
|
||||||
|
"task01",
|
||||||
|
"task02",
|
||||||
|
"task03",
|
||||||
|
"task04"
|
||||||
|
],
|
||||||
|
"educational_requirement_refs": [
|
||||||
|
"C13.WE01"
|
||||||
|
],
|
||||||
|
"learning_effect_refs": [
|
||||||
|
"C13.EN01",
|
||||||
|
"C13.EK01"
|
||||||
|
],
|
||||||
|
"assessment_criterion_refs": [
|
||||||
|
"C13.KW01"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tasks": {
|
||||||
|
"task01": {
|
||||||
|
"title": "Polling DIR, OUT i IN",
|
||||||
|
"uuid": "223fada6-43e4-532a-9c9b-e17445857214",
|
||||||
|
"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\\_IN jest dozwolony tylko w fixture testbencha.",
|
||||||
|
"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.",
|
||||||
|
"conclusion_tex": "",
|
||||||
|
"render_task_acceptance": false,
|
||||||
|
"viewpoints": [
|
||||||
|
{
|
||||||
|
"id": "A1",
|
||||||
|
"status": "enabled",
|
||||||
|
"title": "CONTEXT — dwa źródła widocznego IN",
|
||||||
|
"content_tex": "Dla bitu z \\texttt{DIR=1} widoczne \\texttt{IN} pochodzi z latcha \\texttt{OUT}; dla \\texttt{DIR=0} pochodzi z modelowanego wejścia. \\texttt{TEST\\_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 \\texttt{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\\_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\\_IN, OUT\\_SET i OUT\\_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."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"flow": [
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task01.a1",
|
||||||
|
"title": "A1 CONTEXT · AKTYWNE — dwa źródła widocznego IN",
|
||||||
|
"content_tex": "\\textbf{Zachowany przykład:} \\nolinkurl{src/tasks/task01_gpio_polling.c}.\\par Dla bitu z \\texttt{DIR=1} widoczne \\texttt{IN} pochodzi z latcha \\texttt{OUT}; dla \\texttt{DIR=0} pochodzi z modelowanego wejścia. \\texttt{TEST\\_IN} zmienia tylko tę drugą część i jest bodźcem laboratorium, nie pinem RP2350."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task01.a2",
|
||||||
|
"title": "A2 STRUCTURE · AKTYWNE — 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 \\texttt{IN} składa oba widoki, dlatego przy obu stanach wysokich wynosi 0x3."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task01.a3",
|
||||||
|
"title": "A3 DISPATCH · N/D",
|
||||||
|
"content_tex": "\\textbf{N/D.} Task01 wyłącza detektory zboczy i przerwania; każdy krok jest bezpośrednim pollingiem MMIO, bez ISR, callbacku ani wyboru handlera."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task01.a4",
|
||||||
|
"title": "A4 APPLICATION · AKTYWNE — 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\\_IN=0 kończą sekwencję IN=0. Te liczby są bitowymi relacjami modelu, nie numerami pinów konkretnej płytki."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task01.a5",
|
||||||
|
"title": "A5 FLOW · AKTYWNE — 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."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task01.a6",
|
||||||
|
"title": "A6 STATE · AKTYWNE — 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."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task01.a7",
|
||||||
|
"title": "A7 RUNTIME · AKTYWNE — 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\\_IN, OUT\\_SET i OUT\\_CLR oraz odczyty IN; nie ma wejścia do wspólnego dispatchera external IRQ."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task01.a8",
|
||||||
|
"title": "A8 PATTERNS · AKTYWNE — 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."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "exercise",
|
||||||
|
"id": "task01.proof",
|
||||||
|
"title": "Przewidywanie → wykonanie → wniosek",
|
||||||
|
"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\\_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\\_IN od produkcyjnego API GPIO.",
|
||||||
|
"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."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task01.worksheet",
|
||||||
|
"title": "TASK01 · Zapis dowodu ucznia",
|
||||||
|
"content_tex": "\\textbf{1. Przewidywanie przed uruchomieniem}\\par\\noindent\\dotfill\\par\\noindent\\dotfill\\par\\textbf{2. Log RTL: DIR, OUT i kolejne wartości IN}\\par\\noindent\\dotfill\\par\\noindent\\dotfill\\par\\textbf{3. Granica modelu: TEST\\_IN kontra fizyczny pin}\\par\\noindent\\dotfill\\par\\noindent\\dotfill\\par\\textbf{4. Wniosek: reguła języka lub kontrakt targetu potwierdzony przez pomiar}\\par\\noindent\\dotfill\\par\\noindent\\dotfill"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"educational_requirement_refs": [
|
||||||
|
"C13.WE01"
|
||||||
|
],
|
||||||
|
"learning_effect_refs": [
|
||||||
|
"C13.EN01",
|
||||||
|
"C13.EK01"
|
||||||
|
],
|
||||||
|
"assessment_criterion_ref": "C13.KW01"
|
||||||
|
},
|
||||||
|
"task02": {
|
||||||
|
"title": "Rising edge, IRQ18 i W1C",
|
||||||
|
"uuid": "93fb66db-d0ac-5fc2-811e-5b28e072a346",
|
||||||
|
"prompt_tex": "Rozpisz ścieżkę od zmiany TEST\\_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.",
|
||||||
|
"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.",
|
||||||
|
"conclusion_tex": "",
|
||||||
|
"render_task_acceptance": false,
|
||||||
|
"viewpoints": [
|
||||||
|
{
|
||||||
|
"id": "A1",
|
||||||
|
"status": "enabled",
|
||||||
|
"title": "CONTEXT — zbocze tworzy zatrzaśnięty fakt",
|
||||||
|
"content_tex": "Bodziec TEST\\_IN zmienia pin2 z 0 na 1. Włączony bit RISE\\_EN tworzy bit 0x4 w GPIO PENDING, a ten utrzymuje external IRQ18. TEST\\_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 \\texttt{\\_external\\_irq\\_table[18]} przechowuje adres zwykłej funkcji handlera."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "A3",
|
||||||
|
"status": "enabled",
|
||||||
|
"title": "DISPATCH — external vector wybiera linię 18",
|
||||||
|
"content_tex": "\\texttt{mtvec} wybiera wspólny vector machine external interrupt. \\texttt{irq\\_dispatch.S} odczytuje aktywną linię z \\texttt{meinext}, indeksuje tablicę i wykonuje pośredni \\texttt{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 \\texttt{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."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"flow": [
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task02.a1",
|
||||||
|
"title": "A1 CONTEXT · AKTYWNE — zbocze tworzy zatrzaśnięty fakt",
|
||||||
|
"content_tex": "\\textbf{Zachowany przykład:} \\nolinkurl{src/tasks/task02_gpio_rising_irq.c}.\\par Bodziec TEST\\_IN zmienia pin2 z 0 na 1. Włączony bit RISE\\_EN tworzy bit 0x4 w GPIO PENDING, a ten utrzymuje external IRQ18. TEST\\_IN jest sztuczny, lecz dalsza ścieżka przez kontroler i rdzeń jest rzeczywistą ścieżką modelu RTL."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task02.a2",
|
||||||
|
"title": "A2 STRUCTURE · AKTYWNE — 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 \\texttt{\\_external\\_irq\\_table[18]} przechowuje adres zwykłej funkcji handlera."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task02.a3",
|
||||||
|
"title": "A3 DISPATCH · AKTYWNE — external vector wybiera linię 18",
|
||||||
|
"content_tex": "\\texttt{mtvec} wybiera wspólny vector machine external interrupt. \\texttt{irq\\_dispatch.S} odczytuje aktywną linię z \\texttt{meinext}, indeksuje tablicę i wykonuje pośredni \\texttt{jalr} do handlera GPIO. Handler potwierdza, że bieżący numer to 18."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task02.a4",
|
||||||
|
"title": "A4 APPLICATION · AKTYWNE — 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."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task02.a5",
|
||||||
|
"title": "A5 FLOW · AKTYWNE — 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."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task02.a6",
|
||||||
|
"title": "A6 STATE · AKTYWNE — 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."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task02.a7",
|
||||||
|
"title": "A7 RUNTIME · AKTYWNE — 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 \\texttt{mret}."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task02.a8",
|
||||||
|
"title": "A8 PATTERNS · AKTYWNE — 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."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "exercise",
|
||||||
|
"id": "task02.proof",
|
||||||
|
"title": "Przewidywanie → wykonanie → wniosek",
|
||||||
|
"prompt_tex": "Rozpisz ścieżkę od zmiany TEST\\_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.",
|
||||||
|
"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."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task02.worksheet",
|
||||||
|
"title": "TASK02 · Zapis dowodu ucznia",
|
||||||
|
"content_tex": "\\textbf{1. Przewidywanie przed uruchomieniem}\\par\\noindent\\dotfill\\par\\noindent\\dotfill\\par\\textbf{2. Log RTL: pending GPIO i kontrolera przed/po ISR}\\par\\noindent\\dotfill\\par\\noindent\\dotfill\\par\\textbf{3. Dowód dispatchu IRQ18 i zapisu W1C}\\par\\noindent\\dotfill\\par\\noindent\\dotfill\\par\\textbf{4. Wniosek: reguła języka lub kontrakt targetu potwierdzony przez pomiar}\\par\\noindent\\dotfill\\par\\noindent\\dotfill"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"educational_requirement_refs": [
|
||||||
|
"C13.WE01"
|
||||||
|
],
|
||||||
|
"learning_effect_refs": [
|
||||||
|
"C13.EN01",
|
||||||
|
"C13.EK01"
|
||||||
|
],
|
||||||
|
"assessment_criterion_ref": "C13.KW01"
|
||||||
|
},
|
||||||
|
"task03": {
|
||||||
|
"title": "Quiet-window debounce po trzech zboczach",
|
||||||
|
"uuid": "a2e3b023-04f1-5863-a4e0-360b5ec69b89",
|
||||||
|
"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.",
|
||||||
|
"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.",
|
||||||
|
"conclusion_tex": "",
|
||||||
|
"render_task_acceptance": false,
|
||||||
|
"viewpoints": [
|
||||||
|
{
|
||||||
|
"id": "A1",
|
||||||
|
"status": "enabled",
|
||||||
|
"title": "CONTEXT — IRQ rejestruje zmianę, nie intencję użytkownika",
|
||||||
|
"content_tex": "Trzy przejścia TEST\\_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 \\texttt{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\\_EN i FALL\\_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\\_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\\_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\\_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ń."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"flow": [
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task03.a1",
|
||||||
|
"title": "A1 CONTEXT · AKTYWNE — IRQ rejestruje zmianę, nie intencję użytkownika",
|
||||||
|
"content_tex": "\\textbf{Zachowany przykład:} \\nolinkurl{src/tasks/task03_gpio_quiet_window.c}.\\par Trzy przejścia TEST\\_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."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task03.a2",
|
||||||
|
"title": "A2 STRUCTURE · AKTYWNE — historia ISR i kandydat main",
|
||||||
|
"content_tex": "ISR zapisuje trzy pary \\texttt{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."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task03.a3",
|
||||||
|
"title": "A3 DISPATCH · AKTYWNE — każde włączone zbocze wybiera GPIO18",
|
||||||
|
"content_tex": "RISE\\_EN i FALL\\_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\\_IN."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task03.a4",
|
||||||
|
"title": "A4 APPLICATION · AKTYWNE — 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."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task03.a5",
|
||||||
|
"title": "A5 FLOW · AKTYWNE — 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."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task03.a6",
|
||||||
|
"title": "A6 STATE · AKTYWNE — 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\\_HIGH powstaje dopiero po pełnym oknie bez kolejnego IRQ."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task03.a7",
|
||||||
|
"title": "A7 RUNTIME · AKTYWNE — 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\\_after>=quiet."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task03.a8",
|
||||||
|
"title": "A8 PATTERNS · AKTYWNE — 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ń."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "exercise",
|
||||||
|
"id": "task03.proof",
|
||||||
|
"title": "Przewidywanie → wykonanie → wniosek",
|
||||||
|
"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\\_after i wniosek przypisujący politykę debounce do 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."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task03.worksheet",
|
||||||
|
"title": "TASK03 · Zapis dowodu ucznia",
|
||||||
|
"content_tex": "\\textbf{1. Przewidywanie przed uruchomieniem}\\par\\noindent\\dotfill\\par\\noindent\\dotfill\\par\\textbf{2. Log RTL: trzy poziomy, czasy i gapy}\\par\\noindent\\dotfill\\par\\noindent\\dotfill\\par\\textbf{3. Dowód quiet window i miejsca decyzji w main}\\par\\noindent\\dotfill\\par\\noindent\\dotfill\\par\\textbf{4. Wniosek: reguła języka lub kontrakt targetu potwierdzony przez pomiar}\\par\\noindent\\dotfill\\par\\noindent\\dotfill"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"educational_requirement_refs": [
|
||||||
|
"C13.WE01"
|
||||||
|
],
|
||||||
|
"learning_effect_refs": [
|
||||||
|
"C13.EN01",
|
||||||
|
"C13.EK01"
|
||||||
|
],
|
||||||
|
"assessment_criterion_ref": "C13.KW01"
|
||||||
|
},
|
||||||
|
"task04": {
|
||||||
|
"title": "Timer + UART16 + GPIO18 bez RTOS",
|
||||||
|
"uuid": "ebb18b6a-cf31-5595-9901-ac2ac870296f",
|
||||||
|
"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.",
|
||||||
|
"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.",
|
||||||
|
"conclusion_tex": "",
|
||||||
|
"render_task_acceptance": false,
|
||||||
|
"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\\_RX i TEST\\_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 \\texttt{isr\\_machine\\_timer} i wraca przez \\texttt{mret}. Machine external vector trafia do wspólnego dispatchera; \\texttt{meinext} wybiera wpis 16 dla UART albo 18 dla GPIO i wykonuje \\texttt{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ą."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"flow": [
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task04.a1",
|
||||||
|
"title": "A1 CONTEXT · AKTYWNE — trzy usługi, wspólny czas CPU",
|
||||||
|
"content_tex": "\\textbf{Zachowany przykład:} \\nolinkurl{src/tasks/task04_raw_c_superloop.c}.\\par 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\\_RX i TEST\\_IN tworzą tylko bodźce; logika timer/UART/GPIO po nich jest rzeczywistym modelem RTL."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task04.a2",
|
||||||
|
"title": "A2 STRUCTURE · AKTYWNE — 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."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task04.a3",
|
||||||
|
"title": "A3 DISPATCH · AKTYWNE — slot 7 oraz external 16/18",
|
||||||
|
"content_tex": "Machine timer cause 7 trafia bezpośrednio do \\texttt{isr\\_machine\\_timer} i wraca przez \\texttt{mret}. Machine external vector trafia do wspólnego dispatchera; \\texttt{meinext} wybiera wpis 16 dla UART albo 18 dla GPIO i wykonuje \\texttt{jalr}."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task04.a4",
|
||||||
|
"title": "A4 APPLICATION · AKTYWNE — 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."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task04.a5",
|
||||||
|
"title": "A5 FLOW · AKTYWNE — 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."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task04.a6",
|
||||||
|
"title": "A6 STATE · AKTYWNE — 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."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task04.a7",
|
||||||
|
"title": "A7 RUNTIME · AKTYWNE — 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."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task04.a8",
|
||||||
|
"title": "A8 PATTERNS · AKTYWNE — 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ą."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "exercise",
|
||||||
|
"id": "task04.proof",
|
||||||
|
"title": "Przewidywanie → wykonanie → wniosek",
|
||||||
|
"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.",
|
||||||
|
"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."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "task04.worksheet",
|
||||||
|
"title": "TASK04 · Zapis dowodu ucznia",
|
||||||
|
"content_tex": "\\textbf{1. Przewidywanie przed uruchomieniem}\\par\\noindent\\dotfill\\par\\noindent\\dotfill\\par\\textbf{2. Log RTL: timer, UART16, GPIO18 i efekty main}\\par\\noindent\\dotfill\\par\\noindent\\dotfill\\par\\textbf{3. Dowód dwóch dispatcherów i końcowego OUT/IN}\\par\\noindent\\dotfill\\par\\noindent\\dotfill\\par\\textbf{4. Wniosek: reguła języka lub kontrakt targetu potwierdzony przez pomiar}\\par\\noindent\\dotfill\\par\\noindent\\dotfill"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"educational_requirement_refs": [
|
||||||
|
"C13.WE01"
|
||||||
|
],
|
||||||
|
"learning_effect_refs": [
|
||||||
|
"C13.EN01",
|
||||||
|
"C13.EK01"
|
||||||
|
],
|
||||||
|
"assessment_criterion_ref": "C13.KW01"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tasks_order": [
|
||||||
|
"task01",
|
||||||
|
"task02",
|
||||||
|
"task03",
|
||||||
|
"task04"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,302 @@
|
|||||||
|
{
|
||||||
|
"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 A1–A8. 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ą."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Executable
+10
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
script_dir=$(CDPATH= cd "$(dirname "$0")" && pwd)
|
||||||
|
repo_root=$(CDPATH= cd "$script_dir/.." && pwd)
|
||||||
|
layouts_root=$repo_root/../../../tools/card-layouts
|
||||||
|
|
||||||
|
python3 "$layouts_root/tools/build_kr_c_card.py" "$repo_root"
|
||||||
|
exec python3 "$layouts_root/tools/render_card.py" "$repo_root" "$@"
|
||||||
|
|
||||||
Executable
+13
@@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
script_dir=$(CDPATH= cd "$(dirname "$0")" && pwd)
|
||||||
|
repo_root=$(CDPATH= cd "$script_dir/.." && pwd)
|
||||||
|
layouts_root=$repo_root/../../../tools/card-layouts
|
||||||
|
|
||||||
|
if git -C "$repo_root" rev-parse --verify HEAD >/dev/null 2>&1; then
|
||||||
|
exec "$layouts_root/tools/render_card_pdf_preserving.sh" "$repo_root"
|
||||||
|
fi
|
||||||
|
GITEA_SHA=0000000000000000000000000000000000000000 \
|
||||||
|
exec "$layouts_root/tools/render_card_pdf_preserving.sh" "$repo_root"
|
||||||
|
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
#ifndef C13_COMMON_H
|
||||||
|
#define C13_COMMON_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "hazard3_irq.h"
|
||||||
|
#include "tb_cxxrtl_io.h"
|
||||||
|
#include "tb_gpio_io.h"
|
||||||
|
#include "tb_uart_io.h"
|
||||||
|
|
||||||
|
#define C13_UART_INDEX 0u
|
||||||
|
#define C13_UART_IRQ (TB_UART_EXTERNAL_IRQ_BASE + C13_UART_INDEX)
|
||||||
|
#define C13_GPIO_IRQ TB_GPIO_EXTERNAL_IRQ
|
||||||
|
#define C13_MACHINE_TIMER_CAUSE UINT32_C(0x80000007)
|
||||||
|
|
||||||
|
static inline __attribute__((always_inline)) uint64_t c13_timer_read64(void)
|
||||||
|
{
|
||||||
|
uint32_t high_before;
|
||||||
|
uint32_t low;
|
||||||
|
uint32_t high_after;
|
||||||
|
|
||||||
|
do {
|
||||||
|
high_before = mm_timer->mtimeh;
|
||||||
|
low = mm_timer->mtime;
|
||||||
|
high_after = mm_timer->mtimeh;
|
||||||
|
} while (high_before != high_after);
|
||||||
|
|
||||||
|
return ((uint64_t)high_after << 32) | low;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline __attribute__((always_inline)) void c13_timer_set_compare(uint64_t value)
|
||||||
|
{
|
||||||
|
mm_timer->mtimecmph = UINT32_MAX;
|
||||||
|
mm_timer->mtimecmp = (uint32_t)value;
|
||||||
|
mm_timer->mtimecmph = (uint32_t)(value >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void c13_wait_ticks(uint64_t ticks)
|
||||||
|
{
|
||||||
|
const uint64_t deadline = c13_timer_read64() + ticks;
|
||||||
|
while (c13_timer_read64() < deadline) {
|
||||||
|
__asm__ volatile("nop");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool c13_wait_for(volatile uint32_t *value, uint32_t expected)
|
||||||
|
{
|
||||||
|
for (uint32_t spin = 0; spin < 100000u; ++spin) {
|
||||||
|
if (*value == expected) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
__asm__ volatile("nop");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void c13_report_u32(const char *label, uint32_t value)
|
||||||
|
{
|
||||||
|
tb_puts(label);
|
||||||
|
tb_put_u32(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void c13_external_disable(void)
|
||||||
|
{
|
||||||
|
global_irq_enable(false);
|
||||||
|
external_irq_enable(false);
|
||||||
|
h3irq_enable(C13_UART_IRQ, false);
|
||||||
|
h3irq_enable(C13_GPIO_IRQ, false);
|
||||||
|
tb_uart_rx_irq_enable(C13_UART_INDEX, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
#include "c13_common.h"
|
||||||
|
|
||||||
|
#define TASK01_OUTPUT_PIN (UINT32_C(1) << 0)
|
||||||
|
#define TASK01_INPUT_PIN (UINT32_C(1) << 1)
|
||||||
|
|
||||||
|
volatile uint32_t g_task01_dir;
|
||||||
|
volatile uint32_t g_task01_out_after_set;
|
||||||
|
volatile uint32_t g_task01_in_external_high;
|
||||||
|
volatile uint32_t g_task01_in_both_high;
|
||||||
|
volatile uint32_t g_task01_in_final;
|
||||||
|
volatile uint32_t g_task01_pass;
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
c13_external_disable();
|
||||||
|
tb_gpio_write(TB_GPIO_RISE_EN, 0u);
|
||||||
|
tb_gpio_write(TB_GPIO_FALL_EN, 0u);
|
||||||
|
tb_gpio_write(TB_GPIO_PENDING, UINT32_MAX);
|
||||||
|
|
||||||
|
tb_gpio_write(TB_GPIO_DIR, TASK01_OUTPUT_PIN);
|
||||||
|
tb_gpio_write(TB_GPIO_OUT_CLR, TASK01_OUTPUT_PIN);
|
||||||
|
tb_gpio_test_set_input(TASK01_INPUT_PIN);
|
||||||
|
|
||||||
|
g_task01_dir = tb_gpio_read(TB_GPIO_DIR);
|
||||||
|
g_task01_in_external_high = tb_gpio_read(TB_GPIO_IN);
|
||||||
|
|
||||||
|
tb_gpio_write(TB_GPIO_OUT_SET, TASK01_OUTPUT_PIN);
|
||||||
|
g_task01_out_after_set = tb_gpio_read(TB_GPIO_OUT);
|
||||||
|
g_task01_in_both_high = tb_gpio_read(TB_GPIO_IN);
|
||||||
|
|
||||||
|
tb_gpio_write(TB_GPIO_OUT_CLR, TASK01_OUTPUT_PIN);
|
||||||
|
tb_gpio_test_set_input(0u);
|
||||||
|
g_task01_in_final = tb_gpio_read(TB_GPIO_IN);
|
||||||
|
|
||||||
|
g_task01_pass =
|
||||||
|
g_task01_dir == TASK01_OUTPUT_PIN &&
|
||||||
|
g_task01_in_external_high == TASK01_INPUT_PIN &&
|
||||||
|
(g_task01_out_after_set & TASK01_OUTPUT_PIN) != 0u &&
|
||||||
|
g_task01_in_both_high == (TASK01_OUTPUT_PIN | TASK01_INPUT_PIN) &&
|
||||||
|
g_task01_in_final == 0u &&
|
||||||
|
tb_gpio_read(TB_GPIO_PENDING) == 0u;
|
||||||
|
|
||||||
|
c13_report_u32("T01 dir=", g_task01_dir);
|
||||||
|
c13_report_u32("T01 external_high=", g_task01_in_external_high);
|
||||||
|
c13_report_u32("T01 out_after_set=", g_task01_out_after_set);
|
||||||
|
c13_report_u32("T01 both_high=", g_task01_in_both_high);
|
||||||
|
c13_report_u32("T01 final_in=", g_task01_in_final);
|
||||||
|
c13_report_u32("T01 pass=", g_task01_pass);
|
||||||
|
return g_task01_pass ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
#include "c13_common.h"
|
||||||
|
|
||||||
|
#define TASK02_INPUT_PIN (UINT32_C(1) << 2)
|
||||||
|
|
||||||
|
volatile uint32_t g_task02_irq_count;
|
||||||
|
volatile uint32_t g_task02_irq_number;
|
||||||
|
volatile uint32_t g_task02_pending_in_handler;
|
||||||
|
volatile uint32_t g_task02_level_in_handler;
|
||||||
|
volatile uint32_t g_task02_gpio_pending_before;
|
||||||
|
volatile uint32_t g_task02_controller_pending_before;
|
||||||
|
volatile uint32_t g_task02_gpio_pending_after;
|
||||||
|
volatile uint32_t g_task02_controller_pending_after;
|
||||||
|
volatile uint32_t g_task02_same_level_pending;
|
||||||
|
volatile uint32_t g_task02_resumed;
|
||||||
|
volatile uint32_t g_task02_pass;
|
||||||
|
|
||||||
|
void task02_gpio_handler(void)
|
||||||
|
{
|
||||||
|
const uint32_t pending = tb_gpio_read(TB_GPIO_PENDING);
|
||||||
|
|
||||||
|
g_task02_irq_number = (uint32_t)h3irq_get_current_irq();
|
||||||
|
g_task02_pending_in_handler = pending;
|
||||||
|
g_task02_level_in_handler = tb_gpio_read(TB_GPIO_IN) & TASK02_INPUT_PIN;
|
||||||
|
tb_gpio_write(TB_GPIO_PENDING, pending); /* W1C: acknowledge the source. */
|
||||||
|
g_task02_irq_count = g_task02_irq_count + 1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
c13_external_disable();
|
||||||
|
tb_gpio_write(TB_GPIO_DIR, 0u);
|
||||||
|
tb_gpio_write(TB_GPIO_RISE_EN, 0u);
|
||||||
|
tb_gpio_write(TB_GPIO_FALL_EN, 0u);
|
||||||
|
tb_gpio_write(TB_GPIO_PENDING, UINT32_MAX);
|
||||||
|
tb_gpio_test_set_input(0u);
|
||||||
|
|
||||||
|
h3irq_set_handler(C13_GPIO_IRQ, task02_gpio_handler);
|
||||||
|
h3irq_set_priority(C13_GPIO_IRQ, 2u);
|
||||||
|
h3irq_enable(C13_GPIO_IRQ, true);
|
||||||
|
external_irq_enable(true);
|
||||||
|
tb_gpio_write(TB_GPIO_RISE_EN, TASK02_INPUT_PIN);
|
||||||
|
|
||||||
|
/* TEST_IN is laboratory stimulus; the resulting IRQ18 path is real in RTL. */
|
||||||
|
tb_gpio_test_set_input(TASK02_INPUT_PIN);
|
||||||
|
c13_wait_ticks(32u);
|
||||||
|
g_task02_gpio_pending_before =
|
||||||
|
(tb_gpio_read(TB_GPIO_PENDING) & TASK02_INPUT_PIN) != 0u;
|
||||||
|
g_task02_controller_pending_before = h3irq_pending(C13_GPIO_IRQ) ? 1u : 0u;
|
||||||
|
|
||||||
|
global_irq_enable(true);
|
||||||
|
const bool handled = c13_wait_for(&g_task02_irq_count, 1u);
|
||||||
|
g_task02_resumed = handled ? 1u : 0u;
|
||||||
|
global_irq_enable(false);
|
||||||
|
c13_wait_ticks(32u);
|
||||||
|
|
||||||
|
g_task02_gpio_pending_after =
|
||||||
|
(tb_gpio_read(TB_GPIO_PENDING) & TASK02_INPUT_PIN) != 0u;
|
||||||
|
g_task02_controller_pending_after = h3irq_pending(C13_GPIO_IRQ) ? 1u : 0u;
|
||||||
|
|
||||||
|
/* Repeating the same high level is not a second rising edge. */
|
||||||
|
tb_gpio_test_set_input(TASK02_INPUT_PIN);
|
||||||
|
c13_wait_ticks(32u);
|
||||||
|
g_task02_same_level_pending =
|
||||||
|
(tb_gpio_read(TB_GPIO_PENDING) & TASK02_INPUT_PIN) != 0u;
|
||||||
|
|
||||||
|
g_task02_pass = handled &&
|
||||||
|
g_task02_irq_count == 1u &&
|
||||||
|
g_task02_irq_number == C13_GPIO_IRQ &&
|
||||||
|
g_task02_pending_in_handler == TASK02_INPUT_PIN &&
|
||||||
|
g_task02_level_in_handler == TASK02_INPUT_PIN &&
|
||||||
|
g_task02_gpio_pending_before == 1u &&
|
||||||
|
g_task02_controller_pending_before == 1u &&
|
||||||
|
g_task02_gpio_pending_after == 0u &&
|
||||||
|
g_task02_controller_pending_after == 0u &&
|
||||||
|
g_task02_same_level_pending == 0u &&
|
||||||
|
g_task02_resumed == 1u;
|
||||||
|
|
||||||
|
c13_external_disable();
|
||||||
|
tb_gpio_write(TB_GPIO_RISE_EN, 0u);
|
||||||
|
c13_report_u32("T02 count=", g_task02_irq_count);
|
||||||
|
c13_report_u32("T02 irq=", g_task02_irq_number);
|
||||||
|
c13_report_u32("T02 source_before=", g_task02_gpio_pending_before);
|
||||||
|
c13_report_u32("T02 controller_before=", g_task02_controller_pending_before);
|
||||||
|
c13_report_u32("T02 pending_in_handler=", g_task02_pending_in_handler);
|
||||||
|
c13_report_u32("T02 source_after=", g_task02_gpio_pending_after);
|
||||||
|
c13_report_u32("T02 controller_after=", g_task02_controller_pending_after);
|
||||||
|
c13_report_u32("T02 same_level_pending=", g_task02_same_level_pending);
|
||||||
|
c13_report_u32("T02 resumed=", g_task02_resumed);
|
||||||
|
c13_report_u32("T02 pass=", g_task02_pass);
|
||||||
|
return g_task02_pass ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
#include "c13_common.h"
|
||||||
|
|
||||||
|
#define TASK03_INPUT_PIN (UINT32_C(1) << 3)
|
||||||
|
#define TASK03_EDGE_COUNT 3u
|
||||||
|
#define TASK03_BOUNCE_GAP UINT64_C(160)
|
||||||
|
#define TASK03_QUIET UINT64_C(2000)
|
||||||
|
|
||||||
|
volatile uint32_t g_task03_irq_count;
|
||||||
|
volatile uint32_t g_task03_irq_number;
|
||||||
|
volatile uint32_t g_task03_edge_level[TASK03_EDGE_COUNT];
|
||||||
|
volatile uint64_t g_task03_edge_time[TASK03_EDGE_COUNT];
|
||||||
|
volatile uint32_t g_task03_last_level;
|
||||||
|
volatile uint64_t g_task03_last_edge_time;
|
||||||
|
volatile uint32_t g_task03_early_accepts;
|
||||||
|
volatile uint32_t g_task03_accepted_count;
|
||||||
|
volatile uint32_t g_task03_accepted_level;
|
||||||
|
volatile uint64_t g_task03_accept_time;
|
||||||
|
volatile uint32_t g_task03_pass;
|
||||||
|
|
||||||
|
static uint32_t g_seen_irq_count;
|
||||||
|
static uint32_t g_candidate_level;
|
||||||
|
static uint64_t g_candidate_time;
|
||||||
|
|
||||||
|
void task03_gpio_handler(void)
|
||||||
|
{
|
||||||
|
const uint32_t pending = tb_gpio_read(TB_GPIO_PENDING);
|
||||||
|
const uint32_t level = (tb_gpio_read(TB_GPIO_IN) & TASK03_INPUT_PIN) != 0u;
|
||||||
|
const uint64_t timestamp = c13_timer_read64();
|
||||||
|
const uint32_t index = g_task03_irq_count;
|
||||||
|
|
||||||
|
g_task03_irq_number = (uint32_t)h3irq_get_current_irq();
|
||||||
|
if (index < TASK03_EDGE_COUNT) {
|
||||||
|
g_task03_edge_level[index] = level;
|
||||||
|
g_task03_edge_time[index] = timestamp;
|
||||||
|
}
|
||||||
|
g_task03_last_level = level;
|
||||||
|
g_task03_last_edge_time = timestamp;
|
||||||
|
tb_gpio_write(TB_GPIO_PENDING, pending); /* Short ISR: W1C and publish. */
|
||||||
|
g_task03_irq_count = index + 1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool debounce_try_accept(void)
|
||||||
|
{
|
||||||
|
uint32_t irq_count;
|
||||||
|
uint32_t level;
|
||||||
|
uint64_t edge_time;
|
||||||
|
uint64_t now;
|
||||||
|
|
||||||
|
/* Snapshot the ISR publication without pretending a volatile uint64_t is atomic. */
|
||||||
|
global_irq_enable(false);
|
||||||
|
irq_count = g_task03_irq_count;
|
||||||
|
level = g_task03_last_level;
|
||||||
|
edge_time = g_task03_last_edge_time;
|
||||||
|
global_irq_enable(true);
|
||||||
|
|
||||||
|
if (irq_count != g_seen_irq_count) {
|
||||||
|
g_seen_irq_count = irq_count;
|
||||||
|
g_candidate_level = level;
|
||||||
|
g_candidate_time = edge_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
now = c13_timer_read64();
|
||||||
|
if (g_seen_irq_count != 0u &&
|
||||||
|
g_task03_accepted_count == 0u &&
|
||||||
|
now - g_candidate_time >= TASK03_QUIET &&
|
||||||
|
(((tb_gpio_read(TB_GPIO_IN) & TASK03_INPUT_PIN) != 0u) ==
|
||||||
|
(g_candidate_level != 0u))) {
|
||||||
|
g_task03_accepted_level = g_candidate_level;
|
||||||
|
g_task03_accept_time = now;
|
||||||
|
g_task03_accepted_count = 1u;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
bool edge1;
|
||||||
|
bool edge2;
|
||||||
|
bool edge3;
|
||||||
|
|
||||||
|
c13_external_disable();
|
||||||
|
tb_gpio_write(TB_GPIO_DIR, 0u);
|
||||||
|
tb_gpio_write(TB_GPIO_RISE_EN, 0u);
|
||||||
|
tb_gpio_write(TB_GPIO_FALL_EN, 0u);
|
||||||
|
tb_gpio_write(TB_GPIO_PENDING, UINT32_MAX);
|
||||||
|
tb_gpio_test_set_input(0u);
|
||||||
|
|
||||||
|
h3irq_set_handler(C13_GPIO_IRQ, task03_gpio_handler);
|
||||||
|
h3irq_set_priority(C13_GPIO_IRQ, 2u);
|
||||||
|
h3irq_enable(C13_GPIO_IRQ, true);
|
||||||
|
external_irq_enable(true);
|
||||||
|
tb_gpio_write(TB_GPIO_RISE_EN, TASK03_INPUT_PIN);
|
||||||
|
tb_gpio_write(TB_GPIO_FALL_EN, TASK03_INPUT_PIN);
|
||||||
|
global_irq_enable(true);
|
||||||
|
|
||||||
|
tb_gpio_test_set_input(TASK03_INPUT_PIN); /* press */
|
||||||
|
edge1 = c13_wait_for(&g_task03_irq_count, 1u);
|
||||||
|
g_task03_early_accepts += debounce_try_accept() ? 1u : 0u;
|
||||||
|
c13_wait_ticks(TASK03_BOUNCE_GAP);
|
||||||
|
|
||||||
|
tb_gpio_test_set_input(0u); /* bounce low */
|
||||||
|
edge2 = c13_wait_for(&g_task03_irq_count, 2u);
|
||||||
|
g_task03_early_accepts += debounce_try_accept() ? 1u : 0u;
|
||||||
|
c13_wait_ticks(TASK03_BOUNCE_GAP);
|
||||||
|
|
||||||
|
tb_gpio_test_set_input(TASK03_INPUT_PIN); /* stable high */
|
||||||
|
edge3 = c13_wait_for(&g_task03_irq_count, 3u);
|
||||||
|
g_task03_early_accepts += debounce_try_accept() ? 1u : 0u;
|
||||||
|
|
||||||
|
while (c13_timer_read64() - g_task03_last_edge_time < TASK03_QUIET) {
|
||||||
|
__asm__ volatile("nop");
|
||||||
|
}
|
||||||
|
(void)debounce_try_accept();
|
||||||
|
global_irq_enable(false);
|
||||||
|
|
||||||
|
g_task03_pass = edge1 && edge2 && edge3 &&
|
||||||
|
g_task03_irq_count == TASK03_EDGE_COUNT &&
|
||||||
|
g_task03_irq_number == C13_GPIO_IRQ &&
|
||||||
|
g_task03_edge_level[0] == 1u &&
|
||||||
|
g_task03_edge_level[1] == 0u &&
|
||||||
|
g_task03_edge_level[2] == 1u &&
|
||||||
|
g_task03_edge_time[0] < g_task03_edge_time[1] &&
|
||||||
|
g_task03_edge_time[1] < g_task03_edge_time[2] &&
|
||||||
|
g_task03_edge_time[1] - g_task03_edge_time[0] < TASK03_QUIET &&
|
||||||
|
g_task03_edge_time[2] - g_task03_edge_time[1] < TASK03_QUIET &&
|
||||||
|
g_task03_early_accepts == 0u &&
|
||||||
|
g_task03_accepted_count == 1u &&
|
||||||
|
g_task03_accepted_level == 1u &&
|
||||||
|
g_task03_accept_time - g_task03_edge_time[2] >= TASK03_QUIET &&
|
||||||
|
(tb_gpio_read(TB_GPIO_PENDING) & TASK03_INPUT_PIN) == 0u;
|
||||||
|
|
||||||
|
c13_external_disable();
|
||||||
|
tb_gpio_write(TB_GPIO_RISE_EN, 0u);
|
||||||
|
tb_gpio_write(TB_GPIO_FALL_EN, 0u);
|
||||||
|
c13_report_u32("T03 count=", g_task03_irq_count);
|
||||||
|
c13_report_u32("T03 level0=", g_task03_edge_level[0]);
|
||||||
|
c13_report_u32("T03 level1=", g_task03_edge_level[1]);
|
||||||
|
c13_report_u32("T03 level2=", g_task03_edge_level[2]);
|
||||||
|
c13_report_u32("T03 time0_low=", (uint32_t)g_task03_edge_time[0]);
|
||||||
|
c13_report_u32("T03 time1_low=", (uint32_t)g_task03_edge_time[1]);
|
||||||
|
c13_report_u32("T03 time2_low=", (uint32_t)g_task03_edge_time[2]);
|
||||||
|
c13_report_u32("T03 gap01=", (uint32_t)(g_task03_edge_time[1] - g_task03_edge_time[0]));
|
||||||
|
c13_report_u32("T03 gap12=", (uint32_t)(g_task03_edge_time[2] - g_task03_edge_time[1]));
|
||||||
|
c13_report_u32("T03 quiet=", (uint32_t)TASK03_QUIET);
|
||||||
|
c13_report_u32("T03 accepted_after=", (uint32_t)(g_task03_accept_time - g_task03_edge_time[2]));
|
||||||
|
c13_report_u32("T03 early=", g_task03_early_accepts);
|
||||||
|
c13_report_u32("T03 accepted=", g_task03_accepted_count);
|
||||||
|
c13_report_u32("T03 pass=", g_task03_pass);
|
||||||
|
return g_task03_pass ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
#include "c13_common.h"
|
||||||
|
|
||||||
|
#define TASK04_GPIO_INPUT (UINT32_C(1) << 4)
|
||||||
|
#define TASK04_GPIO_EFFECT (UINT32_C(1) << 0)
|
||||||
|
#define TASK04_UART_EFFECT (UINT32_C(1) << 1)
|
||||||
|
#define TASK04_TIMER_EFFECT (UINT32_C(1) << 2)
|
||||||
|
#define TASK04_TIMER_PERIOD UINT64_C(10000)
|
||||||
|
|
||||||
|
volatile uint32_t g_task04_timer_irq_count;
|
||||||
|
volatile uint32_t g_task04_timer_flag;
|
||||||
|
volatile uint32_t g_task04_timer_mcause;
|
||||||
|
volatile uint64_t g_task04_timer_deadline;
|
||||||
|
volatile uint64_t g_task04_timer_observed;
|
||||||
|
|
||||||
|
volatile uint32_t g_task04_uart_irq_count;
|
||||||
|
volatile uint32_t g_task04_uart_irq_number;
|
||||||
|
volatile uint32_t g_task04_uart_byte;
|
||||||
|
volatile uint32_t g_task04_uart_flag;
|
||||||
|
|
||||||
|
volatile uint32_t g_task04_gpio_irq_count;
|
||||||
|
volatile uint32_t g_task04_gpio_irq_number;
|
||||||
|
volatile uint32_t g_task04_gpio_level;
|
||||||
|
volatile uint32_t g_task04_gpio_flag;
|
||||||
|
|
||||||
|
volatile uint32_t g_task04_main_timer_actions;
|
||||||
|
volatile uint32_t g_task04_main_uart_actions;
|
||||||
|
volatile uint32_t g_task04_main_gpio_actions;
|
||||||
|
volatile uint32_t g_task04_final_out;
|
||||||
|
volatile uint32_t g_task04_final_in;
|
||||||
|
volatile uint32_t g_task04_pass;
|
||||||
|
|
||||||
|
void __attribute__((interrupt("machine"))) isr_machine_timer(void)
|
||||||
|
{
|
||||||
|
const uint64_t observed = c13_timer_read64();
|
||||||
|
|
||||||
|
g_task04_timer_mcause = read_csr(mcause);
|
||||||
|
g_task04_timer_observed = observed;
|
||||||
|
g_task04_timer_deadline += TASK04_TIMER_PERIOD;
|
||||||
|
c13_timer_set_compare(g_task04_timer_deadline);
|
||||||
|
g_task04_timer_flag = 1u;
|
||||||
|
g_task04_timer_irq_count = g_task04_timer_irq_count + 1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void task04_uart_handler(void)
|
||||||
|
{
|
||||||
|
g_task04_uart_irq_number = (uint32_t)h3irq_get_current_irq();
|
||||||
|
g_task04_uart_byte = *tb_uart_reg(C13_UART_INDEX, TB_UART_DATA) & 0xffu;
|
||||||
|
g_task04_uart_flag = 1u;
|
||||||
|
g_task04_uart_irq_count = g_task04_uart_irq_count + 1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void task04_gpio_handler(void)
|
||||||
|
{
|
||||||
|
const uint32_t pending = tb_gpio_read(TB_GPIO_PENDING);
|
||||||
|
|
||||||
|
g_task04_gpio_irq_number = (uint32_t)h3irq_get_current_irq();
|
||||||
|
g_task04_gpio_level =
|
||||||
|
(tb_gpio_read(TB_GPIO_IN) & TASK04_GPIO_INPUT) != 0u;
|
||||||
|
tb_gpio_write(TB_GPIO_PENDING, pending);
|
||||||
|
g_task04_gpio_flag = 1u;
|
||||||
|
g_task04_gpio_irq_count = g_task04_gpio_irq_count + 1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t take_flag(volatile uint32_t *flag)
|
||||||
|
{
|
||||||
|
uint32_t value;
|
||||||
|
|
||||||
|
global_irq_enable(false);
|
||||||
|
value = *flag;
|
||||||
|
*flag = 0u;
|
||||||
|
global_irq_enable(true);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
const uint32_t output_mask =
|
||||||
|
TASK04_GPIO_EFFECT | TASK04_UART_EFFECT | TASK04_TIMER_EFFECT;
|
||||||
|
|
||||||
|
global_irq_enable(false);
|
||||||
|
timer_irq_enable(false);
|
||||||
|
external_irq_enable(false);
|
||||||
|
tb_uart_rx_irq_enable(C13_UART_INDEX, false);
|
||||||
|
h3irq_enable(C13_UART_IRQ, false);
|
||||||
|
h3irq_enable(C13_GPIO_IRQ, false);
|
||||||
|
c13_timer_set_compare(UINT64_MAX);
|
||||||
|
|
||||||
|
tb_gpio_write(TB_GPIO_DIR, output_mask);
|
||||||
|
tb_gpio_write(TB_GPIO_OUT_CLR, output_mask);
|
||||||
|
tb_gpio_write(TB_GPIO_RISE_EN, 0u);
|
||||||
|
tb_gpio_write(TB_GPIO_FALL_EN, 0u);
|
||||||
|
tb_gpio_write(TB_GPIO_PENDING, UINT32_MAX);
|
||||||
|
tb_gpio_test_set_input(0u);
|
||||||
|
|
||||||
|
h3irq_set_handler(C13_UART_IRQ, task04_uart_handler);
|
||||||
|
h3irq_set_handler(C13_GPIO_IRQ, task04_gpio_handler);
|
||||||
|
h3irq_set_priority(C13_UART_IRQ, 1u);
|
||||||
|
h3irq_set_priority(C13_GPIO_IRQ, 2u);
|
||||||
|
h3irq_enable(C13_UART_IRQ, true);
|
||||||
|
h3irq_enable(C13_GPIO_IRQ, true);
|
||||||
|
external_irq_enable(true);
|
||||||
|
tb_uart_rx_irq_enable(C13_UART_INDEX, true);
|
||||||
|
tb_gpio_write(TB_GPIO_RISE_EN, TASK04_GPIO_INPUT);
|
||||||
|
|
||||||
|
g_task04_timer_deadline = c13_timer_read64() + TASK04_TIMER_PERIOD;
|
||||||
|
c13_timer_set_compare(g_task04_timer_deadline);
|
||||||
|
timer_irq_enable(true);
|
||||||
|
global_irq_enable(true);
|
||||||
|
|
||||||
|
/* Both writes are testbench stimuli, not production peripheral registers. */
|
||||||
|
tb_uart_test_inject_rx(C13_UART_INDEX, (uint8_t)'U');
|
||||||
|
tb_gpio_test_set_input(TASK04_GPIO_INPUT);
|
||||||
|
|
||||||
|
while (g_task04_main_timer_actions == 0u ||
|
||||||
|
g_task04_main_uart_actions == 0u ||
|
||||||
|
g_task04_main_gpio_actions == 0u) {
|
||||||
|
if (take_flag(&g_task04_uart_flag) != 0u) {
|
||||||
|
if (g_task04_uart_byte == (uint32_t)'U') {
|
||||||
|
tb_gpio_write(TB_GPIO_OUT_SET, TASK04_UART_EFFECT);
|
||||||
|
}
|
||||||
|
g_task04_main_uart_actions++;
|
||||||
|
}
|
||||||
|
if (take_flag(&g_task04_gpio_flag) != 0u) {
|
||||||
|
if (g_task04_gpio_level != 0u) {
|
||||||
|
tb_gpio_write(TB_GPIO_OUT_SET, TASK04_GPIO_EFFECT);
|
||||||
|
}
|
||||||
|
g_task04_main_gpio_actions++;
|
||||||
|
}
|
||||||
|
if (take_flag(&g_task04_timer_flag) != 0u) {
|
||||||
|
tb_gpio_write(TB_GPIO_OUT_SET, TASK04_TIMER_EFFECT);
|
||||||
|
g_task04_main_timer_actions++;
|
||||||
|
}
|
||||||
|
if (g_task04_main_timer_actions == 0u ||
|
||||||
|
g_task04_main_uart_actions == 0u ||
|
||||||
|
g_task04_main_gpio_actions == 0u) {
|
||||||
|
__asm__ volatile("wfi" ::: "memory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
global_irq_enable(false);
|
||||||
|
timer_irq_enable(false);
|
||||||
|
c13_timer_set_compare(UINT64_MAX);
|
||||||
|
c13_external_disable();
|
||||||
|
tb_gpio_write(TB_GPIO_RISE_EN, 0u);
|
||||||
|
|
||||||
|
g_task04_final_out = tb_gpio_read(TB_GPIO_OUT) & output_mask;
|
||||||
|
g_task04_final_in = tb_gpio_read(TB_GPIO_IN) &
|
||||||
|
(output_mask | TASK04_GPIO_INPUT);
|
||||||
|
g_task04_pass =
|
||||||
|
g_task04_timer_irq_count == 1u &&
|
||||||
|
g_task04_timer_mcause == C13_MACHINE_TIMER_CAUSE &&
|
||||||
|
g_task04_timer_observed >= g_task04_timer_deadline - TASK04_TIMER_PERIOD &&
|
||||||
|
g_task04_uart_irq_count == 1u &&
|
||||||
|
g_task04_uart_irq_number == C13_UART_IRQ &&
|
||||||
|
g_task04_uart_byte == (uint32_t)'U' &&
|
||||||
|
g_task04_gpio_irq_count == 1u &&
|
||||||
|
g_task04_gpio_irq_number == C13_GPIO_IRQ &&
|
||||||
|
g_task04_gpio_level == 1u &&
|
||||||
|
g_task04_main_timer_actions == 1u &&
|
||||||
|
g_task04_main_uart_actions == 1u &&
|
||||||
|
g_task04_main_gpio_actions == 1u &&
|
||||||
|
g_task04_final_out == output_mask &&
|
||||||
|
g_task04_final_in == (output_mask | TASK04_GPIO_INPUT);
|
||||||
|
|
||||||
|
c13_report_u32("T04 timer_irq=", g_task04_timer_irq_count);
|
||||||
|
c13_report_u32("T04 timer_mcause=", g_task04_timer_mcause);
|
||||||
|
c13_report_u32("T04 uart_irq=", g_task04_uart_irq_count);
|
||||||
|
c13_report_u32("T04 uart_number=", g_task04_uart_irq_number);
|
||||||
|
c13_report_u32("T04 uart_byte=", g_task04_uart_byte);
|
||||||
|
c13_report_u32("T04 gpio_irq=", g_task04_gpio_irq_count);
|
||||||
|
c13_report_u32("T04 gpio_number=", g_task04_gpio_irq_number);
|
||||||
|
c13_report_u32("T04 gpio_level=", g_task04_gpio_level);
|
||||||
|
c13_report_u32("T04 main_timer=", g_task04_main_timer_actions);
|
||||||
|
c13_report_u32("T04 main_uart=", g_task04_main_uart_actions);
|
||||||
|
c13_report_u32("T04 main_gpio=", g_task04_main_gpio_actions);
|
||||||
|
c13_report_u32("T04 final_out=", g_task04_final_out);
|
||||||
|
c13_report_u32("T04 final_in=", g_task04_final_in);
|
||||||
|
c13_report_u32("T04 pass=", g_task04_pass);
|
||||||
|
return g_task04_pass ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
Executable
+38
@@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
tmp_dir="$(mktemp -d /tmp/c13-card.XXXXXX)"
|
||||||
|
cleanup() {
|
||||||
|
rm -rf -- "$tmp_dir"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT HUP INT TERM
|
||||||
|
|
||||||
|
"$root/scripts/render_card_layouts.sh" >/dev/null
|
||||||
|
"$root/scripts/render_new_pdf.sh" >/dev/null
|
||||||
|
|
||||||
|
pdf="$(find "$root/doc/pdf" -maxdepth 1 -type f \
|
||||||
|
-name 'mpabi-inf-c-13-gpio-edges-debounce-*.pdf' -print -quit)"
|
||||||
|
test -n "$pdf"
|
||||||
|
qpdf --check "$pdf" >/dev/null
|
||||||
|
pdftotext -layout "$pdf" "$tmp_dir/card.txt"
|
||||||
|
|
||||||
|
blocks="$(rg -o 'A[1-8] (CONTEXT|STRUCTURE|DISPATCH|APPLICATION|FLOW|STATE|RUNTIME|PATTERNS)' \
|
||||||
|
"$tmp_dir/card.txt" | wc -l)"
|
||||||
|
test "$blocks" -eq 32
|
||||||
|
|
||||||
|
awk 'BEGIN { RS="\f" } { compact=$0; gsub(/[[:space:]]/, "", compact); if (length(compact) < 100) exit 1 }' \
|
||||||
|
"$tmp_dir/card.txt" || { echo "FAIL: empty PDF page" >&2; exit 1; }
|
||||||
|
awk 'BEGIN { RS="\f" } \
|
||||||
|
/A[1-8] (CONTEXT|STRUCTURE|DISPATCH|APPLICATION|FLOW|STATE|RUNTIME|PATTERNS)/ && !/TASK0[1-4]/ { exit 1 }' \
|
||||||
|
"$tmp_dir/card.txt" || { echo "FAIL: anonymous A1-A8 page" >&2; exit 1; }
|
||||||
|
|
||||||
|
jq -e '
|
||||||
|
.tasks_order == ["task01", "task02", "task03", "task04"] and
|
||||||
|
all(.tasks[];
|
||||||
|
[.viewpoints[].id] == ["A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8"] and
|
||||||
|
all(.viewpoints[]; .status == "enabled" or .status == "unavailable"))
|
||||||
|
' "$root/json/card_source.json" >/dev/null
|
||||||
|
node --check "$root/web/app.js"
|
||||||
|
printf 'PASS card: 4 tasks, 32 A blocks, valid PDF, no empty/anonymous page\n'
|
||||||
|
|
||||||
Executable
+109
@@ -0,0 +1,109 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
env_root="${RV_ENV_ROOT:-/home/user/dev/edu/rv32i-hazard3-student-env}"
|
||||||
|
tb="${HAZARD3_TB:-$env_root/vendor/Hazard3/test/sim/tb_verilator/tb}"
|
||||||
|
objdump="${RISCV_PREFIX:-riscv64-unknown-elf-}objdump"
|
||||||
|
|
||||||
|
test -x "$tb"
|
||||||
|
make -C "$root" -s tasks "RV_ENV_ROOT=$env_root"
|
||||||
|
|
||||||
|
run_task() {
|
||||||
|
local task="$1"
|
||||||
|
shift
|
||||||
|
local log="$root/build/$task/oracle.log"
|
||||||
|
"$tb" --bin "$root/build/$task/prog.bin" --cycles 1000000 \
|
||||||
|
--cpuret --logfile "$log"
|
||||||
|
if grep -Eq 'Unhandled trap|Max cycles reached' "$log"; then
|
||||||
|
printf 'FAIL %s: trap or timeout\n' "$task" >&2
|
||||||
|
sed -n '1,180p' "$log" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
local expected
|
||||||
|
for expected in "$@"; do
|
||||||
|
grep -Fqx "$expected" "$log" || {
|
||||||
|
printf 'FAIL %s: missing <%s>\n' "$task" "$expected" >&2
|
||||||
|
sed -n '1,180p' "$log" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
done
|
||||||
|
printf 'PASS Hazard3 oracle: %s\n' "$task"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_task task01_gpio_polling \
|
||||||
|
'T01 dir=00000001' \
|
||||||
|
'T01 external_high=00000002' \
|
||||||
|
'T01 out_after_set=00000001' \
|
||||||
|
'T01 both_high=00000003' \
|
||||||
|
'T01 final_in=00000000' \
|
||||||
|
'T01 pass=00000001'
|
||||||
|
|
||||||
|
run_task task02_gpio_rising_irq \
|
||||||
|
'T02 count=00000001' \
|
||||||
|
'T02 irq=00000012' \
|
||||||
|
'T02 source_before=00000001' \
|
||||||
|
'T02 controller_before=00000001' \
|
||||||
|
'T02 pending_in_handler=00000004' \
|
||||||
|
'T02 source_after=00000000' \
|
||||||
|
'T02 controller_after=00000000' \
|
||||||
|
'T02 same_level_pending=00000000' \
|
||||||
|
'T02 resumed=00000001' \
|
||||||
|
'T02 pass=00000001'
|
||||||
|
|
||||||
|
run_task task03_gpio_quiet_window \
|
||||||
|
'T03 count=00000003' \
|
||||||
|
'T03 level0=00000001' \
|
||||||
|
'T03 level1=00000000' \
|
||||||
|
'T03 level2=00000001' \
|
||||||
|
'T03 quiet=000007d0' \
|
||||||
|
'T03 early=00000000' \
|
||||||
|
'T03 accepted=00000001' \
|
||||||
|
'T03 pass=00000001'
|
||||||
|
|
||||||
|
run_task task04_raw_c_superloop \
|
||||||
|
'T04 timer_irq=00000001' \
|
||||||
|
'T04 timer_mcause=80000007' \
|
||||||
|
'T04 uart_irq=00000001' \
|
||||||
|
'T04 uart_number=00000010' \
|
||||||
|
'T04 uart_byte=00000055' \
|
||||||
|
'T04 gpio_irq=00000001' \
|
||||||
|
'T04 gpio_number=00000012' \
|
||||||
|
'T04 gpio_level=00000001' \
|
||||||
|
'T04 main_timer=00000001' \
|
||||||
|
'T04 main_uart=00000001' \
|
||||||
|
'T04 main_gpio=00000001' \
|
||||||
|
'T04 final_out=00000007' \
|
||||||
|
'T04 final_in=00000017' \
|
||||||
|
'T04 pass=00000001'
|
||||||
|
|
||||||
|
# Independent arithmetic oracle for debounce; do not trust only program pass.
|
||||||
|
t03_log="$root/build/task03_gpio_quiet_window/oracle.log"
|
||||||
|
mapfile -t times < <(sed -n 's/^T03 time[0-2]_low=//p' "$t03_log")
|
||||||
|
mapfile -t gaps < <(sed -n 's/^T03 gap[01][12]=//p' "$t03_log")
|
||||||
|
quiet_hex="$(sed -n 's/^T03 quiet=//p' "$t03_log")"
|
||||||
|
accepted_hex="$(sed -n 's/^T03 accepted_after=//p' "$t03_log")"
|
||||||
|
[[ ${#times[@]} -eq 3 && ${#gaps[@]} -eq 2 ]]
|
||||||
|
quiet=$((16#$quiet_hex))
|
||||||
|
accepted_after=$((16#$accepted_hex))
|
||||||
|
for i in 0 1; do
|
||||||
|
first=$((16#${times[$i]}))
|
||||||
|
second=$((16#${times[$((i + 1))]}))
|
||||||
|
gap=$((16#${gaps[$i]}))
|
||||||
|
(( second > first && second - first == gap && gap < quiet )) || {
|
||||||
|
echo "FAIL task03: invalid bounce interval $i" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
done
|
||||||
|
(( accepted_after >= quiet )) || {
|
||||||
|
echo "FAIL task03: accepted before the quiet window" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# The machine-timer handler must return from a trap, not as a normal function.
|
||||||
|
"$objdump" -d "$root/build/task04_raw_c_superloop/prog.elf" |
|
||||||
|
sed -n '/<isr_machine_timer>:/,/^$/p' |
|
||||||
|
grep -Eq '[[:space:]]mret$'
|
||||||
|
|
||||||
|
printf 'PASS independent oracle: edge/W1C relations, debounce timing, ISR mret\n'
|
||||||
|
|
||||||
File diff suppressed because one or more lines are too long
+61
File diff suppressed because one or more lines are too long
+2706
File diff suppressed because one or more lines are too long
@@ -0,0 +1,15 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="pl">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="card-renderer" content="react">
|
||||||
|
<title>Karta pracy</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<link rel="stylesheet" href="app.css">
|
||||||
|
</head>
|
||||||
|
<body class="resource-header-enabled">
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user