feat: add lab-rv32i-c-machine-timer card

This commit is contained in:
user
2026-07-21 19:14:20 +02:00
commit a0e116f24e
23 changed files with 4720 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
/build/
/doc/generated/*
!/doc/generated/.gitkeep
!/doc/generated/main.tex
*.aux
*.fdb_latexmk
*.fls
*.log
*.out
*.upa
*.upb
+59
View File
@@ -0,0 +1,59 @@
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_ROOT ?= $(RV_ENV_ROOT)/vendor/Hazard3
H3_COMMON ?= $(H3_ROOT)/test/sim/common
H3_INIT ?= $(H3_COMMON)/init.S
LDSCRIPT ?= $(H3_COMMON)/link_hazard3.ld
MEMOPS ?= $(RV_ENV_ROOT)/vendor/lab-runtime/memops.c
BUILD_ROOT ?= build
TASKS := task01_mtime_consistent_read task02_oneshot_timer_irq task03_periodic_absolute_deadline
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 -Wall -Wextra -Isrc/tasks -isystem $(H3_COMMON)
LDFLAGS := -Wl,--no-relax -T $(LDSCRIPT)
LIBS := -lgcc
.PHONY: all tasks clean check test card-test task1 task2 task3
all: tasks
tasks: $(TASKS)
task1: $(word 1,$(TASKS))
task2: $(word 2,$(TASKS))
task3: $(word 3,$(TASKS))
test: tasks
./tests/test_hazard3.sh
card-test:
./tests/test_card.sh
check:
@test -f "$(H3_INIT)" || { echo "Missing $(H3_INIT)" >&2; exit 1; }
@test -f "$(LDSCRIPT)" || { echo "Missing $(LDSCRIPT)" >&2; exit 1; }
@test -f "$(MEMOPS)" || { echo "Missing $(MEMOPS)" >&2; exit 1; }
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/machine_timer.h $(H3_INIT) $(LDSCRIPT) $(MEMOPS) | check
mkdir -p $$(@D)
$(CC) $(CFLAGS) $(LDFLAGS) -Wl,-Map,$(BUILD_ROOT)/$(1)/prog.map \
-o $$@ $(H3_INIT) 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)
+65
View File
@@ -0,0 +1,65 @@
# C11 — machine timer na RV32I/Hazard3
Karta pokazuje trzy osobne, wykonywalne przykłady obsługi 64-bitowego timera
z 32-bitowego rdzenia:
1. spójny odczyt `mtime` sekwencją highlowhigh, również przez rollover;
2. jednorazowe przerwanie machine timer z `mtimecmp`, `mie.MTIE`,
`mstatus.MIE`, wektorem 7 i powrotem `mret`;
3. okresowe deadliney `deadline += period`, z zapisem czasu planowanego,
obserwowanego i spóźnienia każdego ticka.
To nie jest model hostowy. Programy są kompilowane jako freestanding
RV32I+Zicsr+Zifencei i wykonywane przez RTL Hazard3/Verilator. Testbench
inkrementuje `mtime` w każdym kroku oraz wystawia poziomowe żądanie timera,
gdy `mtime >= mtimecmp[hart]`.
## Budowanie i dowód wykonania
```sh
make tasks
./tests/test_hazard3.sh
```
Test zewnętrzny wymaga kodu wyjścia 0, braku nieobsłużonej pułapki i braku
timeoutu, a następnie sprawdza raportowane relacje. Przy bieżącym RTL uzyskano:
- Task01: wybrany offset `0x13`, 2 próby, poprawny high po rolloverze;
- Task02: dokładnie 1 ISR, `mcause=0x80000007`, pending 0 przed i po,
wznowienie po `wfi`;
- Task03: 4 ISR, okres `0x640` (1600), deadliney oddalone dokładnie o okres,
obserwowane spóźnienie `0x28` (40) dla każdego ticka.
Wartość 40 nie jest kontraktem architektury — zależy od RTL, magistrali,
prologu wygenerowanego przez kompilator i chwili próbkowania. Kontraktem karty
jest `observed >= scheduled`, poprawna arytmetyka spóźnienia i stały krok
deadline’ów.
## Granice i ważne rozróżnienia
- Zapis do `mtime` w Task01 jest wyłącznie fixtureem dostępnego testbencha;
kod produkcyjny nie powinien zakładać, że licznik czasu jest zapisywalny.
- `mtime` i `mtimecmp` są urządzeniami 64-bitowymi widzianymi przez cztery
rejestry 32-bitowe. Zwykłe C `uint64_t` nie daje atomowej transakcji MMIO.
- Źródłem zdarzenia jest porównanie `mtime >= mtimecmp`. Bity MTIE i MIE tylko
przepuszczają pending do rdzenia; nie tworzą zdarzenia i go nie kasują.
- Machine timer jest poziomowy. Handler przesuwa `mtimecmp`; samo wejście do
ISR ani odczyt `mcause` nie czyści źródła.
- `common/init.S` ustawia `mtvec` w tryb wektorowy. Slot 7 wybiera symbol
`isr_machine_timer`; atrybut GCC `interrupt("machine")` generuje `mret`.
- Model `deadline += period` zachowuje fazę. `now + period` użyte jako jedyna
reguła przenosi każde spóźnienie na kolejne wyzwolenie i powoduje dryf.
- Karta nie obejmuje jeszcze sterownika RP2350, ticka RTOS, współdzielenia
danych z wieloma hartami ani polityki nadrabiania wielu pominiętych okresów.
## Materiały karty
```sh
./scripts/render_card_layouts.sh
./scripts/render_new_pdf.sh
./tests/test_card.sh
```
Każdy z trzech przykładów ma dokładnie widoki A1A8. Widok A3 jest N/D tylko
w Task01; w Task02 i Task03 dowodem dispatchu jest faktyczny wybór slotu
`mtvec` na podstawie przyczyny przerwania.
+25
View File
@@ -0,0 +1,25 @@
.text
.global _start
.type _start, @function
_start:
.option push
.option norelax
la gp, __global_pointer$
.option pop
# common/init.S has already installed sp and the vectored mtvec table.
# Clear BSS before C code observes ISR-owned globals.
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
+1
View File
@@ -0,0 +1 @@
+478
View File
@@ -0,0 +1,478 @@
% 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}{11}
\newcommand{\CardCount}{13}
\newcommand{\CardSlug}{machine-timer}
\newcommand{\CardVersion}{v00.01}
\newcommand{\DocumentUUID}{45f57fde-fc7b-5f41-a3af-f153202dff0f}
\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-machine-timer}{\qrcode[level=L,height=22mm]{https://zsl-gitea.mpabi.pl/edu-inf/lab-rv32i-c-machine-timer}}%
\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 Machine timer: od mtime do okresowego deadline'u}\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 11/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 45f57fde-fc7b-5f41-a3af-f153202dff0f}\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 f37170be-e988-5f5d-b3cf-6ea179a9cd54}\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-machine-timer}{\nolinkurl{https://zsl-gitea.mpabi.pl/edu-inf/lab-rv32i-c-machine-timer}}}} \\%
\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/f37170be-e988-5f5d-b3cf-6ea179a9cd54}{\nolinkurl{https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/f37170be-e988-5f5d-b3cf-6ea179a9cd54}}}} \\%
\end{tabularx}%
\end{minipage}%
\vrule width0.35pt%
\begin{minipage}[c][23mm][c]{24mm}\centering%
\href{https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/f37170be-e988-5f5d-b3cf-6ea179a9cd54}{\qrcode[level=L,height=22mm]{https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/f37170be-e988-5f5d-b3cf-6ea179a9cd54}}%
\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ń buduje obrazy RV32I, uruchamia je z limitem cykli i sprawdza raport MMIO, mcause,\textCR liczbę ISR, powrót z pułapki oraz arytmetykę deadline'ów.}}\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 zachowania wskazuje osobno source, pending, enabled, decyzję dispatchera i\textCR efekt handlera, zamiast nazywać cały łańcuch jednym słowem timer.}}\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 źródło czasu, warunek pending, dwa poziomy enable, wybór handlera i jego efekt. Potrafi spójnie odczytać 64-bitowy licznik na RV32, uruchomić jednorazowe przerwanie oraz utrzymać fazę okresowego zegara przez deadline += period.
\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}
Trzy przykłady wykonują się na rzeczywistym modelu RTL Hazard3. Task01 wymusza rollover dolnego słowa mtime. Task02 dowodzi wejścia przez slot 7 wektora, skasowania poziomowego źródła przez przesunięcie mtimecmp i wznowienia po mret. Task03 zapisuje cztery planowane i obserwowane czasy oraz spóźnienia. Host nie jest używany jako substytut urządzenia MMIO i przerwań.\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{MMIO}} & \textbf{\texttt{Task01}} & \textbf{RV32 split-register MMIO i high--low--high} & \textbf{kluczowe} \\ \hline
\hline
\textbf{\texttt{IRQ}} & \textbf{\texttt{Task02}} & \textbf{mtimecmp, pending, MTIE/MIE, mtvec, mret i rearm} & \textbf{kluczowe} \\ \hline
\hline
\textbf{\texttt{TICK}} & \textbf{\texttt{Task03}} & \textbf{deadline += period, tick sequence i lateness} & \textbf{kluczowe} \\ \hline
\end{tabularx}
\normalsize
\vspace{0.8em}
\noindent\textcolor{black!25}{\rule{\textwidth}{0.35pt}}
\clearpage
\ESCSectionBlockStart
\section{1 — Jeden timer, pięć różnych ról}
\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]
\texttt{mtime} jest źródłem rosnącego czasu, a relacja \texttt{mtime >= mtimecmp} wytwarza poziomowy stan pending. \texttt{mie.MTIE} dopuszcza tę klasę przerwań lokalnie, \texttt{mstatus.MIE} dopuszcza przerwania globalnie, a \texttt{mtvec} wybiera kod według przyczyny. Dopiero handler zmienia stan programu i przesuwa komparator. Żaden z bitów enable nie kasuje źródła.
W testbenchu Hazard3 \texttt{mtime} rośnie raz na krok, a linia timera jest aktywna tak długo, jak licznik nie jest mniejszy od komparatora. Dlatego powrót bez zapisu przyszłego \texttt{mtimecmp} wywołałby następną pułapkę niemal natychmiast.
\ESCSectionBlockEnd
\ESCSectionBlockStart
\section{2 — 64 bity urządzenia na 32-bitowym rdzeniu}
\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]
Dwa odczyty 32-bitowe nie tworzą automatycznie spójnego odczytu 64-bitowego MMIO. Sekwencja high--low--high akceptuje parę dopiero wtedy, gdy oba odczyty high są równe; zmiana high oznacza rollover między transakcjami i wymusza powtórzenie. Przy zapisie komparatora kolejność high=UINT32\_MAX, low, docelowe high usuwa niebezpieczne okno z przejściowo zbyt małym \texttt{mtimecmp}.
Task01 zapisuje \texttt{mtime} tylko po to, aby fixture testbencha deterministycznie doprowadził do rolloveru. Nie jest to obietnica, że licznik czasu będzie zapisywalny w innym SoC.
\ESCSectionBlockEnd
\ESCSectionBlockStart
\section{3 — Okres to oś czasu, nie odstęp od spóźnionego now}
\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]
Dla periodycznego zegara handler wykonuje \texttt{deadline += period}. Wtedy planowana sekwencja zachowuje fazę nawet wtedy, gdy wejście do ISR następuje później niż deadline. Reguła \texttt{now + period} użyta jako jedyny model przenosi aktualne spóźnienie do następnego terminu i tworzy dryf.
Karta mierzy \texttt{lateness = observed - scheduled}; nie obiecuje stałej wartości opóźnienia. Bieżący RTL i build dają 40 taktów, lecz wynik zależy od magistrali, prologu ISR i punktu próbkowania. Przykład nie definiuje jeszcze polityki nadrabiania wielu całkowicie pominiętych okresów.
\ESCSectionBlockEnd
\clearpage
\ESCSectionBlockStart
\section{Zadania — zachowane przykłady w profilu A1A8}
\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 · Spójny odczyt mtime przez rollover}
{\scriptsize\ttfamily c33548e4-07c8-599c-9457-8ba457701659}\par
\begin{ESCBlockFrame}{A1 CONTEXT · AKTYWNE — atomowość C nie jest atomowością MMIO}
\textbf{Zachowany przykład:} \nolinkurl{src/tasks/task01_mtime_consistent_read.c}.\par Urządzenie ma 64-bitowy licznik, lecz RV32 wykonuje trzy jawne odczyty 32-bitowych rejestrów. Typ \texttt{uint64\_t} służy do złożenia zaakceptowanych połówek; nie zamienia dwóch transakcji magistrali w jeden atomowy odczyt.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{A2 STRUCTURE · AKTYWNE — dwa słowa jednego licznika}
\texttt{timer\_hw\_t} mapuje kolejno \texttt{mtime}, \texttt{mtimeh}, \texttt{mtimecmp} i \texttt{mtimecmph} od adresu 0xc0000100. \texttt{volatile} wymusza obserwowalne dostępy C, ale regułę spójności dostarcza dopiero algorytm.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{A3 DISPATCH · N/D}
\textbf{N/D.} Task01 ma wyłączone przerwania i wykonuje bezpośredni odczyt MMIO; nie ma ISR, callbacku, tablicy funkcji ani wyboru odbiorcy.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{A4 APPLICATION · AKTYWNE — kontrolowane przejście przez 0xffffffff}
Fixture ustawia high=0 i low blisko \texttt{UINT32\_MAX}, a następnie wyszukuje małe okno, w którym high zmieni się między pierwszym i drugim odczytem. Zaakceptowany wynik musi pochodzić już z epoki high=1; kolejny odczyt nie może być mniejszy.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{A5 FLOW · AKTYWNE — read, validate, retry}
Jedna próba czyta \texttt{high\_before}, \texttt{low}, \texttt{high\_after}. Równość high kończy pętlę i pozwala złożyć wynik. Nierówność odrzuca low należące do niejednoznacznej granicy epok i powtarza wszystkie trzy transakcje.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{A6 STATE · AKTYWNE — epoka 0, rollover, epoka 1}
Stan urządzenia przechodzi z high=0 i low blisko maksimum przez rollover do high=1 i małego low. Pierwsza para high opisuje zmianę stanu, więc nie wolno do niej przypisać jednego snapshotu; druga próba stabilizuje się w epoce 1.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{A7 RUNTIME · AKTYWNE — dwa dostępy high są widocznym dowodem}
Oracle raportuje offset, liczbę prób, retry, high wyniku i monotoniczność. Bieżący RTL daje offset 0x13 i attempts=2. Listing ma pokazać dwa osobne \texttt{lw} z rejestru high, rozdzielone odczytem low, oraz gałąź powrotną przy nierówności.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{A8 PATTERNS · AKTYWNE — stabilny snapshot z licznika split-register}
Wzorzec high--low--high stosuj, gdy dokumentacja urządzenia gwarantuje monotoniczny licznik i zgodne zachowanie połówek. Fixture zapisujący czas pozostaje poza produkcyjnym API odczytu.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{Ćwiczenie · Przewidywanie → wykonanie → wniosek}
Wyjaśnij, jaki błędny 64-bitowy wynik może utworzyć pojedyncze low--high podczas rolloveru. Następnie wskaż w raporcie próbę odrzuconą przez high--low--high i rozdziel kod produkcyjnego odczytu od zapisywalnego fixture'u testbencha.
\par\textbf{Evidence:} Przewidywany błąd na granicy rolloveru, log z RTL Hazard3 z kodem wyjścia i limitem cykli, fragment listingu z dwoma odczytami high oraz wniosek o spójności zaakceptowanego snapshotu.
\par\textbf{Acceptance:} Oracle Hazard3: retry=1, attempts > 1, value\_high=1, monotonic=1, pass=1 i kod wyjścia 0. W bieżącym buildzie okno znaleziono dla offsetu 0x13 i po dwóch próbach; sam offset nie jest kontraktem architektury.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{TASK01 · Zapis dowodu ucznia}
\textbf{1. Przewidywanie przed uruchomieniem}\par\noindent\dotfill\par\noindent\dotfill\par\textbf{2. Log wykonania RTL Hazard3 i kod wyjścia}\par\noindent\dotfill\par\noindent\dotfill\par\textbf{3. Dowód w listingu: high--low--high i retry}\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 · Jednorazowe przerwanie machine timer}
{\scriptsize\ttfamily dc7f8696-19db-55b6-8fd9-cb676b399ec7}\par
\begin{ESCBlockFrame}{A1 CONTEXT · AKTYWNE — enable nie jest źródłem}
\textbf{Zachowany przykład:} \nolinkurl{src/tasks/task02_oneshot_timer_irq.c}.\par Źródłem jest relacja \texttt{mtime >= mtimecmp}; \texttt{mip.MTIP} reprezentuje pending. \texttt{mie.MTIE} i \texttt{mstatus.MIE} są dwiema bramkami. Program najpierw ustawia przyszły komparator, a dopiero potem otwiera obie bramki.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{A2 STRUCTURE · AKTYWNE — wspólny driver i stan dowodowy}
Wspólny nagłówek zawiera dostęp MMIO, bezpieczny zapis komparatora i operacje CSR. Task przechowuje osobno deadline, czas zaobserwowany, mcause, liczbę ISR, pending przed i po oraz znacznik wznowienia.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{A3 DISPATCH · AKTYWNE — przyczyna 7 wybiera handler timera}
Startup zapisuje do \texttt{mtvec} adres tablicy z bitem trybu vectored. Machine timer ma cause=7, więc rdzeń wybiera slot 7, którego skok wiąże się z silnym symbolem \texttt{isr\_machine\_timer} zamiast słabego handlera domyślnego. Atrybut GCC kończy funkcję instrukcją \texttt{mret}.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{A4 APPLICATION · AKTYWNE — one-shot z jawnym kryterium zakończenia}
Main planuje deadline 1200 taktów po spójnym odczycie. Sukces wymaga dokładnie jednego wejścia, poprawnego mcause, obserwacji nie wcześniejszej niż deadline, skasowanego pending po rearmie i wykonania instrukcji po \texttt{wfi}.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{A5 FLOW · AKTYWNE — source → pending → enable → handler → effect}
Rosnące mtime osiąga comparator i ustawia pending. Otwarty MTIE oraz MIE pozwalają wejść do wektora. Handler zapisuje obserwację i mcause, przesuwa comparator na \texttt{UINT64\_MAX}, publikuje count, a \texttt{mret} przywraca przerwany przepływ. Main ustawia resumed i zamyka maski.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{A6 STATE · AKTYWNE — DISARMED, ARMED, PENDING, HANDLED, RESUMED}
Komparator maksimum reprezentuje DISARMED. Przyszły deadline tworzy ARMED. Po osiągnięciu terminu urządzenie jest PENDING, handler ponownie zapisuje maksimum i publikuje HANDLED, a kod po pętli \texttt{wfi} potwierdza RESUMED.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{A7 RUNTIME · AKTYWNE — mcause, mip i mret}
Raport z RTL ma: count=1, mcause=0x80000007, pending 0/0, resumed=1 i deadline\_reached=1. Objdump ma pokazać zapis trzech połówek mtimecmp w handlerze oraz końcowe \texttt{mret}; zwykłe \texttt{ret} nie byłoby poprawnym powrotem z pułapki.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{A8 PATTERNS · AKTYWNE — arm before unmask, clear source before publish}
Najpierw ustaw bezpieczny stan urządzenia, potem włącz źródło w \texttt{mie}, na końcu globalne MIE. W ISR usuń poziomowe źródło przed opublikowaniem zakończenia; inaczej powrót może natychmiast wejść ponownie.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{Ćwiczenie · Przewidywanie → wykonanie → wniosek}
Ułóż w poprawnej kolejności: wyłącz maski, ustaw przyszły mtimecmp, sprawdź pending, włącz MTIE i MIE, czekaj w wfi, przesuń komparator w ISR, wróć przez mret. Dla każdego kroku nazwij source, pending, enable, decyzję dispatchera albo efekt.
\par\textbf{Evidence:} Przewidywana sekwencja source--pending--enable--dispatch--effect, log RTL Hazard3 z mcause i kodem wyjścia, fragment listingu z wektorem oraz mret i wniosek o skasowaniu poziomowego źródła.
\par\textbf{Acceptance:} Oracle Hazard3: count=1, mcause=0x80000007, pending\_before=0, pending\_after=0, resumed=1, deadline\_reached=1, pass=1 i kod wyjścia 0. Listing ISR kończy się mret.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{TASK02 · Zapis dowodu ucznia}
\textbf{1. Przewidywanie przed uruchomieniem}\par\noindent\dotfill\par\noindent\dotfill\par\textbf{2. Log wykonania RTL: mcause, pending i resume}\par\noindent\dotfill\par\noindent\dotfill\par\textbf{3. Dowód w CSR i listingu: slot 7 oraz mret}\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 · Okresowe deadline'y bez dryfu fazy}
{\scriptsize\ttfamily 6c330959-1a78-5068-8c59-6b18f712122c}\par
\begin{ESCBlockFrame}{A1 CONTEXT · AKTYWNE — harmonogram i wykonanie to dwie osie}
\textbf{Zachowany przykład:} \nolinkurl{src/tasks/task03_periodic_absolute_deadline.c}.\par Scheduled opisuje idealną oś czasu usługi, observed chwilę rzeczywistego wejścia do pomiaru w ISR, a lateness ich różnicę. Okres ma aktualizować harmonogram, nie kopiować opóźnienia wykonania do przyszłej fazy.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{A2 STRUCTURE · AKTYWNE — cztery rekordy ticków}
Trzy tablice po cztery elementy zapisują deadline, obserwację i lateness. \texttt{g\_task03\_next\_deadline} jest stanem planisty, a count publikuje liczbę kompletnych rekordów. Stała period wynosi 1600 taktów modelu.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{A3 DISPATCH · AKTYWNE — każdy MTIP trafia przez slot 7}
Dla każdego z czterech terminów rdzeń koduje interrupt bit i cause=7 w mcause, a tryb vectored wybiera ten sam \texttt{isr\_machine\_timer}. Handler sprawdza mcause przy każdym wejściu; licznik causes=4 dowodzi czterech decyzji dispatchera.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{A4 APPLICATION · AKTYWNE — stała faza i zmierzone spóźnienie}
Dla i>0 musi zachodzić \texttt{scheduled[i]-scheduled[i-1]=1600}. Dla każdego rekordu observed nie może być wcześniejsze od scheduled, a lateness musi być dokładnie ich różnicą. Po czwartym ticku comparator wraca do maksimum.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{A5 FLOW · AKTYWNE — sample, record, advance, rearm, publish}
ISR najpierw pobiera indeks i aktualny deadline, potem czyta czas oraz zapisuje rekord. Następnie oblicza deadline+period, uzbraja ten bezwzględny termin albo rozbraja po ostatnim ticku, a count publikuje dopiero na końcu. Main czeka w \texttt{wfi} do czterech rekordów.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{A6 STATE · AKTYWNE — TICK0 → TICK4 i niezmienna faza}
Stan count przechodzi 0,1,2,3,4; razem z każdym przejściem next\_deadline rośnie dokładnie o period. Po count=4 urządzenie jest DISARMED. Tablice pozostają historią czterech zakończonych przejść i są analizowane dopiero po zamknięciu masek.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{A7 RUNTIME · AKTYWNE — cztery terminy i 40 taktów bieżącego RTL}
Bieżący raport podaje deadline low 0x96b, 0xfab, 0x15eb, 0x1c2b; obserwacje są o 0x28 późniejsze. Ważne są relacje step\_ok, ordered i late\_math, ponieważ absolutne wartości oraz 40 taktów mogą się zmienić po zmianie RTL, optymalizacji lub prologu ISR.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{A8 PATTERNS · AKTYWNE — absolute periodic deadline}
\texttt{deadline += period} zachowuje fazę. Gdyby następny termin powstał wyłącznie jako \texttt{observed + period}, jego przesunięcie względem osi absolutnej byłoby równe bieżącemu lateness; powtarzanie tej reguły kumuluje dryf. Osobna polityka musi zdecydować, czy po dużym opóźnieniu nadrabiać, pomijać, czy zgłaszać błąd.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{Ćwiczenie · Przewidywanie → wykonanie → wniosek}
Dla czterech ticków porównaj scheduled, observed i lateness. Udowodnij stały krok 1600 oraz wyprowadź, o ile przesunąłby następną fazę model now+period. Wyjaśnij granicę: przykład nie nadrabia dowolnej liczby całkowicie pominiętych okresów.
\par\textbf{Evidence:} Przewidywana sekwencja czterech deadline'ów, log RTL Hazard3 z planowanymi i obserwowanymi czasami, kontrola różnic i kodu wyjścia oraz wniosek porównujący deadline+period z now+period.
\par\textbf{Acceptance:} Oracle Hazard3: count=causes=4, period=1600, step\_ok=ordered=late\_math=naive\_shift\_ok=pass=1. Bieżący pomiar daje cztery lateness po 40 taktów, ale test nie uznaje liczby 40 za kontrakt sprzętowy.
\end{ESCBlockFrame}
\begin{ESCBlockFrame}{TASK03 · Zapis dowodu ucznia}
\textbf{1. Przewidywanie przed uruchomieniem}\par\noindent\dotfill\par\noindent\dotfill\par\textbf{2. Log RTL: deadline, observed i lateness}\par\noindent\dotfill\par\noindent\dotfill\par\textbf{3. Dowód stałego kroku i braku dryfu fazy}\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}
+1
View File
@@ -0,0 +1 @@
+667
View File
@@ -0,0 +1,667 @@
{
"$schema": "../../../tools/card-layouts/schemas/card-source.schema.json",
"schema": "esc-card-source.v1",
"card": {
"id": "mpabi-inf-c-11-machine-timer",
"series": "c",
"series_title": "C · Freestanding RV32I and K&R",
"number": "11",
"count": "13",
"slug": "machine-timer",
"title": "Machine timer: od mtime do okresowego deadline'u",
"topic": "RV32I/Hazard3: spójny odczyt mtime, mtimecmp i przerwania machine timer",
"project": "Freestanding C na RV32I",
"subject": "Informatyka",
"level": "Rok 1 · C11 · RV32I/Hazard3",
"revision_date": "2026-07-20T00:00:00+02:00",
"status": "Gotowa",
"version": "v00.01",
"uuid": "45f57fde-fc7b-5f41-a3af-f153202dff0f",
"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": "Machine timer: od mtime do okresowego deadline'u",
"url": "https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/f37170be-e988-5f5d-b3cf-6ea179a9cd54",
"repository_url": "https://zsl-gitea.mpabi.pl/edu-inf/lab-rv32i-c-machine-timer",
"url_host_uuid": "dce7fb9d-7b2f-5d49-96a2-3a30d3070b84",
"url_domain": "mpabi.pl",
"doc_uuid": "f37170be-e988-5f5d-b3cf-6ea179a9cd54",
"revision": "v00.01",
"issued_on": "2026-07-20T00:00:00+02:00",
"series": "C-11",
"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 źródło czasu, warunek pending, dwa poziomy enable, wybór handlera i jego efekt. Potrafi spójnie odczytać 64-bitowy licznik na RV32, uruchomić jednorazowe przerwanie oraz utrzymać fazę okresowego zegara przez deadline += period.",
"scope_title": "Zakres i zachowane przykłady",
"scope_content_tex": "Trzy przykłady wykonują się na rzeczywistym modelu RTL Hazard3. Task01 wymusza rollover dolnego słowa mtime. Task02 dowodzi wejścia przez slot 7 wektora, skasowania poziomowego źródła przez przesunięcie mtimecmp i wznowienia po mret. Task03 zapisuje cztery planowane i obserwowane czasy oraz spóźnienia. Host nie jest używany jako substytut urządzenia MMIO i przerwań.\\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": "MMIO",
"task": "Task01",
"idea_tex": "RV32 split-register MMIO i high--low--high",
"priority": "kluczowe",
"key": true
},
{
"chapter": "IRQ",
"task": "Task02",
"idea_tex": "mtimecmp, pending, MTIE/MIE, mtvec, mret i rearm",
"priority": "kluczowe",
"key": true
},
{
"chapter": "TICK",
"task": "Task03",
"idea_tex": "deadline += period, tick sequence i lateness",
"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": {
"C11.EN01": {
"bloom_level": "Analiza",
"label": "Od źródła do efektu ISR",
"text": "Uczeń dla każdego zachowania wskazuje osobno source, pending, enabled, decyzję dispatchera i efekt handlera, zamiast nazywać cały łańcuch jednym słowem timer.",
"assessment_criteria": [
"C11.KW01"
]
},
"C11.EK01": {
"bloom_level": "Zastosowanie",
"label": "Dowód na RTL Hazard3",
"text": "Uczeń buduje obrazy RV32I, uruchamia je z limitem cykli i sprawdza raport MMIO, mcause, liczbę ISR, powrót z pułapki oraz arytmetykę deadline'ów.",
"assessment_criteria": [
"C11.KW01"
]
}
},
"assessment_criteria": {
"C11.KW01": {
"text": "Wszystkie trzy programy kończą się kodem 0 bez nieobsłużonej pułapki i timeoutu. Task01 wykazuje co najmniej dwie próby przez rollover, high=1 i monotoniczny drugi odczyt. Task02 ma dokładnie jeden ISR, mcause=0x80000007, pending=0 przed uzbrojeniem i po obsłudze oraz resume=1. Task03 ma cztery ISR, planowane czasy różniące się dokładnie o 1600, observed >= scheduled, lateness=observed-scheduled i fazowe przesunięcie modelu now+period równe bieżącemu spóźnieniu.",
"learning_effects": [
"C11.EN01",
"C11.EK01"
]
}
},
"educational_requirements": {
"C11.WE01": {
"text": "Obsługa split-register MMIO i machine timer interrupt na RV32I z jawnym kontraktem poziomowego źródła, maskowania i bezwzględnego deadline'u.",
"label": "RV32I/Hazard3: spójny odczyt mtime, mtimecmp i przerwania machine timer",
"learning_effects": [
"C11.EN01",
"C11.EK01"
],
"learning_tree": {
"schema": "we-learning-tree.v1",
"policy": "Najpierw przewidywanie, następnie wykonanie i odczyt dowodu.",
"ogolne": [
{
"effect_ref": "C11.EN01",
"display": "EN C11 01",
"source": "LOCAL",
"official": "C",
"local": "01",
"kind": "EN",
"tree_id": "C11.WE01.OG.LOCAL.C.01",
"text": "Uczeń dla każdego zachowania wskazuje osobno source, pending, enabled, decyzję dispatchera i efekt handlera, zamiast nazywać cały łańcuch jednym słowem timer.",
"kw": [
{
"criterion_ref": "C11.KW01",
"display": "KW C11 01",
"source": "LOCAL",
"kind": "KW",
"official": "C",
"local": "01",
"text": "Kod, przewidywanie i pomiar tworzą jeden dowód."
}
]
}
],
"zawodowe": [
{
"effect_ref": "C11.EK01",
"display": "EK C11 01",
"source": "LOCAL",
"official": "C",
"local": "01",
"kind": "EK",
"tree_id": "C11.WE01.TECH.LOCAL.C.01",
"text": "Uczeń buduje obrazy RV32I, uruchamia je z limitem cykli i sprawdza raport MMIO, mcause, liczbę ISR, powrót z pułapki oraz arytmetykę deadline'ów.",
"kw": [
{
"criterion_ref": "C11.KW01",
"display": "KW C11 01",
"source": "LOCAL",
"kind": "KW",
"official": "C",
"local": "01",
"text": "Kod, przewidywanie i pomiar tworzą jeden dowód."
}
]
}
]
}
}
},
"sections": [
{
"title": "1 — Jeden timer, pięć różnych ról",
"order": 10,
"content_kind": "prose",
"page_orientation": "portrait",
"content_tex": "\\texttt{mtime} jest źródłem rosnącego czasu, a relacja \\texttt{mtime >= mtimecmp} wytwarza poziomowy stan pending. \\texttt{mie.MTIE} dopuszcza tę klasę przerwań lokalnie, \\texttt{mstatus.MIE} dopuszcza przerwania globalnie, a \\texttt{mtvec} wybiera kod według przyczyny. Dopiero handler zmienia stan programu i przesuwa komparator. Żaden z bitów enable nie kasuje źródła.\n\nW testbenchu Hazard3 \\texttt{mtime} rośnie raz na krok, a linia timera jest aktywna tak długo, jak licznik nie jest mniejszy od komparatora. Dlatego powrót bez zapisu przyszłego \\texttt{mtimecmp} wywołałby następną pułapkę niemal natychmiast.",
"educational_requirement_refs": [
"C11.WE01"
],
"learning_effect_refs": [
"C11.EN01",
"C11.EK01"
],
"assessment_criterion_refs": [
"C11.KW01"
]
},
{
"title": "2 — 64 bity urządzenia na 32-bitowym rdzeniu",
"order": 20,
"content_kind": "prose",
"page_orientation": "portrait",
"content_tex": "Dwa odczyty 32-bitowe nie tworzą automatycznie spójnego odczytu 64-bitowego MMIO. Sekwencja high--low--high akceptuje parę dopiero wtedy, gdy oba odczyty high są równe; zmiana high oznacza rollover między transakcjami i wymusza powtórzenie. Przy zapisie komparatora kolejność high=UINT32\\_MAX, low, docelowe high usuwa niebezpieczne okno z przejściowo zbyt małym \\texttt{mtimecmp}.\n\nTask01 zapisuje \\texttt{mtime} tylko po to, aby fixture testbencha deterministycznie doprowadził do rolloveru. Nie jest to obietnica, że licznik czasu będzie zapisywalny w innym SoC.",
"educational_requirement_refs": [
"C11.WE01"
],
"learning_effect_refs": [
"C11.EN01",
"C11.EK01"
],
"assessment_criterion_refs": [
"C11.KW01"
]
},
{
"title": "3 — Okres to oś czasu, nie odstęp od spóźnionego now",
"order": 30,
"content_kind": "prose",
"page_orientation": "portrait",
"content_tex": "Dla periodycznego zegara handler wykonuje \\texttt{deadline += period}. Wtedy planowana sekwencja zachowuje fazę nawet wtedy, gdy wejście do ISR następuje później niż deadline. Reguła \\texttt{now + period} użyta jako jedyny model przenosi aktualne spóźnienie do następnego terminu i tworzy dryf.\n\nKarta mierzy \\texttt{lateness = observed - scheduled}; nie obiecuje stałej wartości opóźnienia. Bieżący RTL i build dają 40 taktów, lecz wynik zależy od magistrali, prologu ISR i punktu próbkowania. Przykład nie definiuje jeszcze polityki nadrabiania wielu całkowicie pominiętych okresów.",
"educational_requirement_refs": [
"C11.WE01"
],
"learning_effect_refs": [
"C11.EN01",
"C11.EK01"
],
"assessment_criterion_refs": [
"C11.KW01"
]
},
{
"title": "Zadania — zachowane przykłady w profilu A1A8",
"order": 40,
"content_kind": "tasks",
"page_orientation": "portrait",
"task_refs": [
"task01",
"task02",
"task03"
],
"educational_requirement_refs": [
"C11.WE01"
],
"learning_effect_refs": [
"C11.EN01",
"C11.EK01"
],
"assessment_criterion_refs": [
"C11.KW01"
]
}
],
"tasks": {
"task01": {
"title": "Spójny odczyt mtime przez rollover",
"uuid": "c33548e4-07c8-599c-9457-8ba457701659",
"prompt_tex": "Wyjaśnij, jaki błędny 64-bitowy wynik może utworzyć pojedyncze low--high podczas rolloveru. Następnie wskaż w raporcie próbę odrzuconą przez high--low--high i rozdziel kod produkcyjnego odczytu od zapisywalnego fixture'u testbencha.",
"criterion": "Oracle Hazard3: retry=1, attempts > 1, value_high=1, monotonic=1, pass=1 i kod wyjścia 0. W bieżącym buildzie okno znaleziono dla offsetu 0x13 i po dwóch próbach; sam offset nie jest kontraktem architektury.",
"conclusion_tex": "",
"render_task_acceptance": false,
"viewpoints": [
{
"id": "A1",
"status": "enabled",
"title": "CONTEXT — atomowość C nie jest atomowością MMIO",
"content_tex": "Urządzenie ma 64-bitowy licznik, lecz RV32 wykonuje trzy jawne odczyty 32-bitowych rejestrów. Typ \\texttt{uint64\\_t} służy do złożenia zaakceptowanych połówek; nie zamienia dwóch transakcji magistrali w jeden atomowy odczyt."
},
{
"id": "A2",
"status": "enabled",
"title": "STRUCTURE — dwa słowa jednego licznika",
"content_tex": "\\texttt{timer\\_hw\\_t} mapuje kolejno \\texttt{mtime}, \\texttt{mtimeh}, \\texttt{mtimecmp} i \\texttt{mtimecmph} od adresu 0xc0000100. \\texttt{volatile} wymusza obserwowalne dostępy C, ale regułę spójności dostarcza dopiero algorytm."
},
{
"id": "A3",
"status": "unavailable",
"title": "DISPATCH",
"reason": "Task01 ma wyłączone przerwania i wykonuje bezpośredni odczyt MMIO; nie ma ISR, callbacku, tablicy funkcji ani wyboru odbiorcy."
},
{
"id": "A4",
"status": "enabled",
"title": "APPLICATION — kontrolowane przejście przez 0xffffffff",
"content_tex": "Fixture ustawia high=0 i low blisko \\texttt{UINT32\\_MAX}, a następnie wyszukuje małe okno, w którym high zmieni się między pierwszym i drugim odczytem. Zaakceptowany wynik musi pochodzić już z epoki high=1; kolejny odczyt nie może być mniejszy."
},
{
"id": "A5",
"status": "enabled",
"title": "FLOW — read, validate, retry",
"content_tex": "Jedna próba czyta \\texttt{high\\_before}, \\texttt{low}, \\texttt{high\\_after}. Równość high kończy pętlę i pozwala złożyć wynik. Nierówność odrzuca low należące do niejednoznacznej granicy epok i powtarza wszystkie trzy transakcje."
},
{
"id": "A6",
"status": "enabled",
"title": "STATE — epoka 0, rollover, epoka 1",
"content_tex": "Stan urządzenia przechodzi z high=0 i low blisko maksimum przez rollover do high=1 i małego low. Pierwsza para high opisuje zmianę stanu, więc nie wolno do niej przypisać jednego snapshotu; druga próba stabilizuje się w epoce 1."
},
{
"id": "A7",
"status": "enabled",
"title": "RUNTIME — dwa dostępy high są widocznym dowodem",
"content_tex": "Oracle raportuje offset, liczbę prób, retry, high wyniku i monotoniczność. Bieżący RTL daje offset 0x13 i attempts=2. Listing ma pokazać dwa osobne \\texttt{lw} z rejestru high, rozdzielone odczytem low, oraz gałąź powrotną przy nierówności."
},
{
"id": "A8",
"status": "enabled",
"title": "PATTERNS — stabilny snapshot z licznika split-register",
"content_tex": "Wzorzec high--low--high stosuj, gdy dokumentacja urządzenia gwarantuje monotoniczny licznik i zgodne zachowanie połówek. Fixture zapisujący czas pozostaje poza produkcyjnym API odczytu."
}
],
"flow": [
{
"kind": "block",
"id": "task01.a1",
"title": "A1 CONTEXT · AKTYWNE — atomowość C nie jest atomowością MMIO",
"content_tex": "\\textbf{Zachowany przykład:} \\nolinkurl{src/tasks/task01_mtime_consistent_read.c}.\\par Urządzenie ma 64-bitowy licznik, lecz RV32 wykonuje trzy jawne odczyty 32-bitowych rejestrów. Typ \\texttt{uint64\\_t} służy do złożenia zaakceptowanych połówek; nie zamienia dwóch transakcji magistrali w jeden atomowy odczyt."
},
{
"kind": "block",
"id": "task01.a2",
"title": "A2 STRUCTURE · AKTYWNE — dwa słowa jednego licznika",
"content_tex": "\\texttt{timer\\_hw\\_t} mapuje kolejno \\texttt{mtime}, \\texttt{mtimeh}, \\texttt{mtimecmp} i \\texttt{mtimecmph} od adresu 0xc0000100. \\texttt{volatile} wymusza obserwowalne dostępy C, ale regułę spójności dostarcza dopiero algorytm."
},
{
"kind": "block",
"id": "task01.a3",
"title": "A3 DISPATCH · N/D",
"content_tex": "\\textbf{N/D.} Task01 ma wyłączone przerwania i wykonuje bezpośredni odczyt MMIO; nie ma ISR, callbacku, tablicy funkcji ani wyboru odbiorcy."
},
{
"kind": "block",
"id": "task01.a4",
"title": "A4 APPLICATION · AKTYWNE — kontrolowane przejście przez 0xffffffff",
"content_tex": "Fixture ustawia high=0 i low blisko \\texttt{UINT32\\_MAX}, a następnie wyszukuje małe okno, w którym high zmieni się między pierwszym i drugim odczytem. Zaakceptowany wynik musi pochodzić już z epoki high=1; kolejny odczyt nie może być mniejszy."
},
{
"kind": "block",
"id": "task01.a5",
"title": "A5 FLOW · AKTYWNE — read, validate, retry",
"content_tex": "Jedna próba czyta \\texttt{high\\_before}, \\texttt{low}, \\texttt{high\\_after}. Równość high kończy pętlę i pozwala złożyć wynik. Nierówność odrzuca low należące do niejednoznacznej granicy epok i powtarza wszystkie trzy transakcje."
},
{
"kind": "block",
"id": "task01.a6",
"title": "A6 STATE · AKTYWNE — epoka 0, rollover, epoka 1",
"content_tex": "Stan urządzenia przechodzi z high=0 i low blisko maksimum przez rollover do high=1 i małego low. Pierwsza para high opisuje zmianę stanu, więc nie wolno do niej przypisać jednego snapshotu; druga próba stabilizuje się w epoce 1."
},
{
"kind": "block",
"id": "task01.a7",
"title": "A7 RUNTIME · AKTYWNE — dwa dostępy high są widocznym dowodem",
"content_tex": "Oracle raportuje offset, liczbę prób, retry, high wyniku i monotoniczność. Bieżący RTL daje offset 0x13 i attempts=2. Listing ma pokazać dwa osobne \\texttt{lw} z rejestru high, rozdzielone odczytem low, oraz gałąź powrotną przy nierówności."
},
{
"kind": "block",
"id": "task01.a8",
"title": "A8 PATTERNS · AKTYWNE — stabilny snapshot z licznika split-register",
"content_tex": "Wzorzec high--low--high stosuj, gdy dokumentacja urządzenia gwarantuje monotoniczny licznik i zgodne zachowanie połówek. Fixture zapisujący czas pozostaje poza produkcyjnym API odczytu."
},
{
"kind": "exercise",
"id": "task01.proof",
"title": "Przewidywanie → wykonanie → wniosek",
"prompt_tex": "Wyjaśnij, jaki błędny 64-bitowy wynik może utworzyć pojedyncze low--high podczas rolloveru. Następnie wskaż w raporcie próbę odrzuconą przez high--low--high i rozdziel kod produkcyjnego odczytu od zapisywalnego fixture'u testbencha.",
"evidence_tex": "Przewidywany błąd na granicy rolloveru, log z RTL Hazard3 z kodem wyjścia i limitem cykli, fragment listingu z dwoma odczytami high oraz wniosek o spójności zaakceptowanego snapshotu.",
"criterion": "Oracle Hazard3: retry=1, attempts > 1, value_high=1, monotonic=1, pass=1 i kod wyjścia 0. W bieżącym buildzie okno znaleziono dla offsetu 0x13 i po dwóch próbach; sam offset nie jest kontraktem architektury."
},
{
"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 wykonania RTL Hazard3 i kod wyjścia}\\par\\noindent\\dotfill\\par\\noindent\\dotfill\\par\\textbf{3. Dowód w listingu: high--low--high i retry}\\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": [
"C11.WE01"
],
"learning_effect_refs": [
"C11.EN01",
"C11.EK01"
],
"assessment_criterion_ref": "C11.KW01"
},
"task02": {
"title": "Jednorazowe przerwanie machine timer",
"uuid": "dc7f8696-19db-55b6-8fd9-cb676b399ec7",
"prompt_tex": "Ułóż w poprawnej kolejności: wyłącz maski, ustaw przyszły mtimecmp, sprawdź pending, włącz MTIE i MIE, czekaj w wfi, przesuń komparator w ISR, wróć przez mret. Dla każdego kroku nazwij source, pending, enable, decyzję dispatchera albo efekt.",
"criterion": "Oracle Hazard3: count=1, mcause=0x80000007, pending_before=0, pending_after=0, resumed=1, deadline_reached=1, pass=1 i kod wyjścia 0. Listing ISR kończy się mret.",
"conclusion_tex": "",
"render_task_acceptance": false,
"viewpoints": [
{
"id": "A1",
"status": "enabled",
"title": "CONTEXT — enable nie jest źródłem",
"content_tex": "Źródłem jest relacja \\texttt{mtime >= mtimecmp}; \\texttt{mip.MTIP} reprezentuje pending. \\texttt{mie.MTIE} i \\texttt{mstatus.MIE} są dwiema bramkami. Program najpierw ustawia przyszły komparator, a dopiero potem otwiera obie bramki."
},
{
"id": "A2",
"status": "enabled",
"title": "STRUCTURE — wspólny driver i stan dowodowy",
"content_tex": "Wspólny nagłówek zawiera dostęp MMIO, bezpieczny zapis komparatora i operacje CSR. Task przechowuje osobno deadline, czas zaobserwowany, mcause, liczbę ISR, pending przed i po oraz znacznik wznowienia."
},
{
"id": "A3",
"status": "enabled",
"title": "DISPATCH — przyczyna 7 wybiera handler timera",
"content_tex": "Startup zapisuje do \\texttt{mtvec} adres tablicy z bitem trybu vectored. Machine timer ma cause=7, więc rdzeń wybiera slot 7, którego skok wiąże się z silnym symbolem \\texttt{isr\\_machine\\_timer} zamiast słabego handlera domyślnego. Atrybut GCC kończy funkcję instrukcją \\texttt{mret}."
},
{
"id": "A4",
"status": "enabled",
"title": "APPLICATION — one-shot z jawnym kryterium zakończenia",
"content_tex": "Main planuje deadline 1200 taktów po spójnym odczycie. Sukces wymaga dokładnie jednego wejścia, poprawnego mcause, obserwacji nie wcześniejszej niż deadline, skasowanego pending po rearmie i wykonania instrukcji po \\texttt{wfi}."
},
{
"id": "A5",
"status": "enabled",
"title": "FLOW — source → pending → enable → handler → effect",
"content_tex": "Rosnące mtime osiąga comparator i ustawia pending. Otwarty MTIE oraz MIE pozwalają wejść do wektora. Handler zapisuje obserwację i mcause, przesuwa comparator na \\texttt{UINT64\\_MAX}, publikuje count, a \\texttt{mret} przywraca przerwany przepływ. Main ustawia resumed i zamyka maski."
},
{
"id": "A6",
"status": "enabled",
"title": "STATE — DISARMED, ARMED, PENDING, HANDLED, RESUMED",
"content_tex": "Komparator maksimum reprezentuje DISARMED. Przyszły deadline tworzy ARMED. Po osiągnięciu terminu urządzenie jest PENDING, handler ponownie zapisuje maksimum i publikuje HANDLED, a kod po pętli \\texttt{wfi} potwierdza RESUMED."
},
{
"id": "A7",
"status": "enabled",
"title": "RUNTIME — mcause, mip i mret",
"content_tex": "Raport z RTL ma: count=1, mcause=0x80000007, pending 0/0, resumed=1 i deadline\\_reached=1. Objdump ma pokazać zapis trzech połówek mtimecmp w handlerze oraz końcowe \\texttt{mret}; zwykłe \\texttt{ret} nie byłoby poprawnym powrotem z pułapki."
},
{
"id": "A8",
"status": "enabled",
"title": "PATTERNS — arm before unmask, clear source before publish",
"content_tex": "Najpierw ustaw bezpieczny stan urządzenia, potem włącz źródło w \\texttt{mie}, na końcu globalne MIE. W ISR usuń poziomowe źródło przed opublikowaniem zakończenia; inaczej powrót może natychmiast wejść ponownie."
}
],
"flow": [
{
"kind": "block",
"id": "task02.a1",
"title": "A1 CONTEXT · AKTYWNE — enable nie jest źródłem",
"content_tex": "\\textbf{Zachowany przykład:} \\nolinkurl{src/tasks/task02_oneshot_timer_irq.c}.\\par Źródłem jest relacja \\texttt{mtime >= mtimecmp}; \\texttt{mip.MTIP} reprezentuje pending. \\texttt{mie.MTIE} i \\texttt{mstatus.MIE} są dwiema bramkami. Program najpierw ustawia przyszły komparator, a dopiero potem otwiera obie bramki."
},
{
"kind": "block",
"id": "task02.a2",
"title": "A2 STRUCTURE · AKTYWNE — wspólny driver i stan dowodowy",
"content_tex": "Wspólny nagłówek zawiera dostęp MMIO, bezpieczny zapis komparatora i operacje CSR. Task przechowuje osobno deadline, czas zaobserwowany, mcause, liczbę ISR, pending przed i po oraz znacznik wznowienia."
},
{
"kind": "block",
"id": "task02.a3",
"title": "A3 DISPATCH · AKTYWNE — przyczyna 7 wybiera handler timera",
"content_tex": "Startup zapisuje do \\texttt{mtvec} adres tablicy z bitem trybu vectored. Machine timer ma cause=7, więc rdzeń wybiera slot 7, którego skok wiąże się z silnym symbolem \\texttt{isr\\_machine\\_timer} zamiast słabego handlera domyślnego. Atrybut GCC kończy funkcję instrukcją \\texttt{mret}."
},
{
"kind": "block",
"id": "task02.a4",
"title": "A4 APPLICATION · AKTYWNE — one-shot z jawnym kryterium zakończenia",
"content_tex": "Main planuje deadline 1200 taktów po spójnym odczycie. Sukces wymaga dokładnie jednego wejścia, poprawnego mcause, obserwacji nie wcześniejszej niż deadline, skasowanego pending po rearmie i wykonania instrukcji po \\texttt{wfi}."
},
{
"kind": "block",
"id": "task02.a5",
"title": "A5 FLOW · AKTYWNE — source → pending → enable → handler → effect",
"content_tex": "Rosnące mtime osiąga comparator i ustawia pending. Otwarty MTIE oraz MIE pozwalają wejść do wektora. Handler zapisuje obserwację i mcause, przesuwa comparator na \\texttt{UINT64\\_MAX}, publikuje count, a \\texttt{mret} przywraca przerwany przepływ. Main ustawia resumed i zamyka maski."
},
{
"kind": "block",
"id": "task02.a6",
"title": "A6 STATE · AKTYWNE — DISARMED, ARMED, PENDING, HANDLED, RESUMED",
"content_tex": "Komparator maksimum reprezentuje DISARMED. Przyszły deadline tworzy ARMED. Po osiągnięciu terminu urządzenie jest PENDING, handler ponownie zapisuje maksimum i publikuje HANDLED, a kod po pętli \\texttt{wfi} potwierdza RESUMED."
},
{
"kind": "block",
"id": "task02.a7",
"title": "A7 RUNTIME · AKTYWNE — mcause, mip i mret",
"content_tex": "Raport z RTL ma: count=1, mcause=0x80000007, pending 0/0, resumed=1 i deadline\\_reached=1. Objdump ma pokazać zapis trzech połówek mtimecmp w handlerze oraz końcowe \\texttt{mret}; zwykłe \\texttt{ret} nie byłoby poprawnym powrotem z pułapki."
},
{
"kind": "block",
"id": "task02.a8",
"title": "A8 PATTERNS · AKTYWNE — arm before unmask, clear source before publish",
"content_tex": "Najpierw ustaw bezpieczny stan urządzenia, potem włącz źródło w \\texttt{mie}, na końcu globalne MIE. W ISR usuń poziomowe źródło przed opublikowaniem zakończenia; inaczej powrót może natychmiast wejść ponownie."
},
{
"kind": "exercise",
"id": "task02.proof",
"title": "Przewidywanie → wykonanie → wniosek",
"prompt_tex": "Ułóż w poprawnej kolejności: wyłącz maski, ustaw przyszły mtimecmp, sprawdź pending, włącz MTIE i MIE, czekaj w wfi, przesuń komparator w ISR, wróć przez mret. Dla każdego kroku nazwij source, pending, enable, decyzję dispatchera albo efekt.",
"evidence_tex": "Przewidywana sekwencja source--pending--enable--dispatch--effect, log RTL Hazard3 z mcause i kodem wyjścia, fragment listingu z wektorem oraz mret i wniosek o skasowaniu poziomowego źródła.",
"criterion": "Oracle Hazard3: count=1, mcause=0x80000007, pending_before=0, pending_after=0, resumed=1, deadline_reached=1, pass=1 i kod wyjścia 0. Listing ISR kończy się mret."
},
{
"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 wykonania RTL: mcause, pending i resume}\\par\\noindent\\dotfill\\par\\noindent\\dotfill\\par\\textbf{3. Dowód w CSR i listingu: slot 7 oraz mret}\\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": [
"C11.WE01"
],
"learning_effect_refs": [
"C11.EN01",
"C11.EK01"
],
"assessment_criterion_ref": "C11.KW01"
},
"task03": {
"title": "Okresowe deadline'y bez dryfu fazy",
"uuid": "6c330959-1a78-5068-8c59-6b18f712122c",
"prompt_tex": "Dla czterech ticków porównaj scheduled, observed i lateness. Udowodnij stały krok 1600 oraz wyprowadź, o ile przesunąłby następną fazę model now+period. Wyjaśnij granicę: przykład nie nadrabia dowolnej liczby całkowicie pominiętych okresów.",
"criterion": "Oracle Hazard3: count=causes=4, period=1600, step_ok=ordered=late_math=naive_shift_ok=pass=1. Bieżący pomiar daje cztery lateness po 40 taktów, ale test nie uznaje liczby 40 za kontrakt sprzętowy.",
"conclusion_tex": "",
"render_task_acceptance": false,
"viewpoints": [
{
"id": "A1",
"status": "enabled",
"title": "CONTEXT — harmonogram i wykonanie to dwie osie",
"content_tex": "Scheduled opisuje idealną oś czasu usługi, observed chwilę rzeczywistego wejścia do pomiaru w ISR, a lateness ich różnicę. Okres ma aktualizować harmonogram, nie kopiować opóźnienia wykonania do przyszłej fazy."
},
{
"id": "A2",
"status": "enabled",
"title": "STRUCTURE — cztery rekordy ticków",
"content_tex": "Trzy tablice po cztery elementy zapisują deadline, obserwację i lateness. \\texttt{g\\_task03\\_next\\_deadline} jest stanem planisty, a count publikuje liczbę kompletnych rekordów. Stała period wynosi 1600 taktów modelu."
},
{
"id": "A3",
"status": "enabled",
"title": "DISPATCH — każdy MTIP trafia przez slot 7",
"content_tex": "Dla każdego z czterech terminów rdzeń koduje interrupt bit i cause=7 w mcause, a tryb vectored wybiera ten sam \\texttt{isr\\_machine\\_timer}. Handler sprawdza mcause przy każdym wejściu; licznik causes=4 dowodzi czterech decyzji dispatchera."
},
{
"id": "A4",
"status": "enabled",
"title": "APPLICATION — stała faza i zmierzone spóźnienie",
"content_tex": "Dla i>0 musi zachodzić \\texttt{scheduled[i]-scheduled[i-1]=1600}. Dla każdego rekordu observed nie może być wcześniejsze od scheduled, a lateness musi być dokładnie ich różnicą. Po czwartym ticku comparator wraca do maksimum."
},
{
"id": "A5",
"status": "enabled",
"title": "FLOW — sample, record, advance, rearm, publish",
"content_tex": "ISR najpierw pobiera indeks i aktualny deadline, potem czyta czas oraz zapisuje rekord. Następnie oblicza deadline+period, uzbraja ten bezwzględny termin albo rozbraja po ostatnim ticku, a count publikuje dopiero na końcu. Main czeka w \\texttt{wfi} do czterech rekordów."
},
{
"id": "A6",
"status": "enabled",
"title": "STATE — TICK0 → TICK4 i niezmienna faza",
"content_tex": "Stan count przechodzi 0,1,2,3,4; razem z każdym przejściem next\\_deadline rośnie dokładnie o period. Po count=4 urządzenie jest DISARMED. Tablice pozostają historią czterech zakończonych przejść i są analizowane dopiero po zamknięciu masek."
},
{
"id": "A7",
"status": "enabled",
"title": "RUNTIME — cztery terminy i 40 taktów bieżącego RTL",
"content_tex": "Bieżący raport podaje deadline low 0x96b, 0xfab, 0x15eb, 0x1c2b; obserwacje są o 0x28 późniejsze. Ważne są relacje step\\_ok, ordered i late\\_math, ponieważ absolutne wartości oraz 40 taktów mogą się zmienić po zmianie RTL, optymalizacji lub prologu ISR."
},
{
"id": "A8",
"status": "enabled",
"title": "PATTERNS — absolute periodic deadline",
"content_tex": "\\texttt{deadline += period} zachowuje fazę. Gdyby następny termin powstał wyłącznie jako \\texttt{observed + period}, jego przesunięcie względem osi absolutnej byłoby równe bieżącemu lateness; powtarzanie tej reguły kumuluje dryf. Osobna polityka musi zdecydować, czy po dużym opóźnieniu nadrabiać, pomijać, czy zgłaszać błąd."
}
],
"flow": [
{
"kind": "block",
"id": "task03.a1",
"title": "A1 CONTEXT · AKTYWNE — harmonogram i wykonanie to dwie osie",
"content_tex": "\\textbf{Zachowany przykład:} \\nolinkurl{src/tasks/task03_periodic_absolute_deadline.c}.\\par Scheduled opisuje idealną oś czasu usługi, observed chwilę rzeczywistego wejścia do pomiaru w ISR, a lateness ich różnicę. Okres ma aktualizować harmonogram, nie kopiować opóźnienia wykonania do przyszłej fazy."
},
{
"kind": "block",
"id": "task03.a2",
"title": "A2 STRUCTURE · AKTYWNE — cztery rekordy ticków",
"content_tex": "Trzy tablice po cztery elementy zapisują deadline, obserwację i lateness. \\texttt{g\\_task03\\_next\\_deadline} jest stanem planisty, a count publikuje liczbę kompletnych rekordów. Stała period wynosi 1600 taktów modelu."
},
{
"kind": "block",
"id": "task03.a3",
"title": "A3 DISPATCH · AKTYWNE — każdy MTIP trafia przez slot 7",
"content_tex": "Dla każdego z czterech terminów rdzeń koduje interrupt bit i cause=7 w mcause, a tryb vectored wybiera ten sam \\texttt{isr\\_machine\\_timer}. Handler sprawdza mcause przy każdym wejściu; licznik causes=4 dowodzi czterech decyzji dispatchera."
},
{
"kind": "block",
"id": "task03.a4",
"title": "A4 APPLICATION · AKTYWNE — stała faza i zmierzone spóźnienie",
"content_tex": "Dla i>0 musi zachodzić \\texttt{scheduled[i]-scheduled[i-1]=1600}. Dla każdego rekordu observed nie może być wcześniejsze od scheduled, a lateness musi być dokładnie ich różnicą. Po czwartym ticku comparator wraca do maksimum."
},
{
"kind": "block",
"id": "task03.a5",
"title": "A5 FLOW · AKTYWNE — sample, record, advance, rearm, publish",
"content_tex": "ISR najpierw pobiera indeks i aktualny deadline, potem czyta czas oraz zapisuje rekord. Następnie oblicza deadline+period, uzbraja ten bezwzględny termin albo rozbraja po ostatnim ticku, a count publikuje dopiero na końcu. Main czeka w \\texttt{wfi} do czterech rekordów."
},
{
"kind": "block",
"id": "task03.a6",
"title": "A6 STATE · AKTYWNE — TICK0 → TICK4 i niezmienna faza",
"content_tex": "Stan count przechodzi 0,1,2,3,4; razem z każdym przejściem next\\_deadline rośnie dokładnie o period. Po count=4 urządzenie jest DISARMED. Tablice pozostają historią czterech zakończonych przejść i są analizowane dopiero po zamknięciu masek."
},
{
"kind": "block",
"id": "task03.a7",
"title": "A7 RUNTIME · AKTYWNE — cztery terminy i 40 taktów bieżącego RTL",
"content_tex": "Bieżący raport podaje deadline low 0x96b, 0xfab, 0x15eb, 0x1c2b; obserwacje są o 0x28 późniejsze. Ważne są relacje step\\_ok, ordered i late\\_math, ponieważ absolutne wartości oraz 40 taktów mogą się zmienić po zmianie RTL, optymalizacji lub prologu ISR."
},
{
"kind": "block",
"id": "task03.a8",
"title": "A8 PATTERNS · AKTYWNE — absolute periodic deadline",
"content_tex": "\\texttt{deadline += period} zachowuje fazę. Gdyby następny termin powstał wyłącznie jako \\texttt{observed + period}, jego przesunięcie względem osi absolutnej byłoby równe bieżącemu lateness; powtarzanie tej reguły kumuluje dryf. Osobna polityka musi zdecydować, czy po dużym opóźnieniu nadrabiać, pomijać, czy zgłaszać błąd."
},
{
"kind": "exercise",
"id": "task03.proof",
"title": "Przewidywanie → wykonanie → wniosek",
"prompt_tex": "Dla czterech ticków porównaj scheduled, observed i lateness. Udowodnij stały krok 1600 oraz wyprowadź, o ile przesunąłby następną fazę model now+period. Wyjaśnij granicę: przykład nie nadrabia dowolnej liczby całkowicie pominiętych okresów.",
"evidence_tex": "Przewidywana sekwencja czterech deadline'ów, log RTL Hazard3 z planowanymi i obserwowanymi czasami, kontrola różnic i kodu wyjścia oraz wniosek porównujący deadline+period z now+period.",
"criterion": "Oracle Hazard3: count=causes=4, period=1600, step_ok=ordered=late_math=naive_shift_ok=pass=1. Bieżący pomiar daje cztery lateness po 40 taktów, ale test nie uznaje liczby 40 za kontrakt sprzętowy."
},
{
"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: deadline, observed i lateness}\\par\\noindent\\dotfill\\par\\noindent\\dotfill\\par\\textbf{3. Dowód stałego kroku i braku dryfu fazy}\\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": [
"C11.WE01"
],
"learning_effect_refs": [
"C11.EN01",
"C11.EK01"
],
"assessment_criterion_ref": "C11.KW01"
}
},
"tasks_order": [
"task01",
"task02",
"task03"
]
}
+233
View File
@@ -0,0 +1,233 @@
{
"card": {
"number": "11",
"slug": "machine-timer",
"title": "Machine timer: od mtime do okresowego deadline'u",
"topic": "RV32I/Hazard3: spójny odczyt mtime, mtimecmp i przerwania machine timer",
"status": "Gotowa",
"version": "v00.01",
"revision_date": "2026-07-20T00:00:00+02:00",
"level": "Rok 1 · C11 · RV32I/Hazard3"
},
"front": {
"goal": "Uczeń rozdziela źródło czasu, warunek pending, dwa poziomy enable, wybór handlera i jego efekt. Potrafi spójnie odczytać 64-bitowy licznik na RV32, uruchomić jednorazowe przerwanie oraz utrzymać fazę okresowego zegara przez deadline += period.",
"scope": "Trzy przykłady wykonują się na rzeczywistym modelu RTL Hazard3. Task01 wymusza rollover dolnego słowa mtime. Task02 dowodzi wejścia przez slot 7 wektora, skasowania poziomowego źródła przez przesunięcie mtimecmp i wznowienia po mret. Task03 zapisuje cztery planowane i obserwowane czasy oraz spóźnienia. Host nie jest używany jako substytut urządzenia MMIO i przerwań."
},
"scope_headers": ["Typ", "Task", "Idea", "Waga"],
"learning": {
"reasoning_label": "Od źródła do efektu ISR",
"reasoning": "Uczeń dla każdego zachowania wskazuje osobno source, pending, enabled, decyzję dispatchera i efekt handlera, zamiast nazywać cały łańcuch jednym słowem timer.",
"practice_label": "Dowód na RTL Hazard3",
"practice": "Uczeń buduje obrazy RV32I, uruchamia je z limitem cykli i sprawdza raport MMIO, mcause, liczbę ISR, powrót z pułapki oraz arytmetykę deadline'ów.",
"criterion": "Wszystkie trzy programy kończą się kodem 0 bez nieobsłużonej pułapki i timeoutu. Task01 wykazuje co najmniej dwie próby przez rollover, high=1 i monotoniczny drugi odczyt. Task02 ma dokładnie jeden ISR, mcause=0x80000007, pending=0 przed uzbrojeniem i po obsłudze oraz resume=1. Task03 ma cztery ISR, planowane czasy różniące się dokładnie o 1600, observed >= scheduled, lateness=observed-scheduled i fazowe przesunięcie modelu now+period równe bieżącemu spóźnieniu.",
"requirement": "Obsługa split-register MMIO i machine timer interrupt na RV32I z jawnym kontraktem poziomowego źródła, maskowania i bezwzględnego deadline'u."
},
"sections": [
{
"title": "1 — Jeden timer, pięć różnych ról",
"content_tex": "\u005ctexttt{mtime} jest źródłem rosnącego czasu, a relacja \u005ctexttt{mtime >= mtimecmp} wytwarza poziomowy stan pending. \u005ctexttt{mie.MTIE} dopuszcza tę klasę przerwań lokalnie, \u005ctexttt{mstatus.MIE} dopuszcza przerwania globalnie, a \u005ctexttt{mtvec} wybiera kod według przyczyny. Dopiero handler zmienia stan programu i przesuwa komparator. Żaden z bitów enable nie kasuje źródła.\n\nW testbenchu Hazard3 \u005ctexttt{mtime} rośnie raz na krok, a linia timera jest aktywna tak długo, jak licznik nie jest mniejszy od komparatora. Dlatego powrót bez zapisu przyszłego \u005ctexttt{mtimecmp} wywołałby następną pułapkę niemal natychmiast."
},
{
"title": "2 — 64 bity urządzenia na 32-bitowym rdzeniu",
"content_tex": "Dwa odczyty 32-bitowe nie tworzą automatycznie spójnego odczytu 64-bitowego MMIO. Sekwencja high--low--high akceptuje parę dopiero wtedy, gdy oba odczyty high są równe; zmiana high oznacza rollover między transakcjami i wymusza powtórzenie. Przy zapisie komparatora kolejność high=UINT32\\_MAX, low, docelowe high usuwa niebezpieczne okno z przejściowo zbyt małym \u005ctexttt{mtimecmp}.\n\nTask01 zapisuje \u005ctexttt{mtime} tylko po to, aby fixture testbencha deterministycznie doprowadził do rolloveru. Nie jest to obietnica, że licznik czasu będzie zapisywalny w innym SoC."
},
{
"title": "3 — Okres to oś czasu, nie odstęp od spóźnionego now",
"content_tex": "Dla periodycznego zegara handler wykonuje \u005ctexttt{deadline += period}. Wtedy planowana sekwencja zachowuje fazę nawet wtedy, gdy wejście do ISR następuje później niż deadline. Reguła \u005ctexttt{now + period} użyta jako jedyny model przenosi aktualne spóźnienie do następnego terminu i tworzy dryf.\n\nKarta mierzy \u005ctexttt{lateness = observed - scheduled}; nie obiecuje stałej wartości opóźnienia. Bieżący RTL i build dają 40 taktów, lecz wynik zależy od magistrali, prologu ISR i punktu próbkowania. Przykład nie definiuje jeszcze polityki nadrabiania wielu całkowicie pominiętych okresów."
}
],
"tasks": [
{
"id": "task01",
"source": "src/tasks/task01_mtime_consistent_read.c",
"chapter": "MMIO",
"title": "Spójny odczyt mtime przez rollover",
"idea_tex": "RV32 split-register MMIO i high--low--high",
"priority": "kluczowe",
"key": true,
"prompt_tex": "Wyjaśnij, jaki błędny 64-bitowy wynik może utworzyć pojedyncze low--high podczas rolloveru. Następnie wskaż w raporcie próbę odrzuconą przez high--low--high i rozdziel kod produkcyjnego odczytu od zapisywalnego fixture'u testbencha.",
"evidence_tex": "Przewidywany błąd na granicy rolloveru, log z RTL Hazard3 z kodem wyjścia i limitem cykli, fragment listingu z dwoma odczytami high oraz wniosek o spójności zaakceptowanego snapshotu.",
"worksheet_step2": "Log wykonania RTL Hazard3 i kod wyjścia",
"worksheet_step3": "Dowód w listingu: high--low--high i retry",
"criterion": "Oracle Hazard3: retry=1, attempts > 1, value_high=1, monotonic=1, pass=1 i kod wyjścia 0. W bieżącym buildzie okno znaleziono dla offsetu 0x13 i po dwóch próbach; sam offset nie jest kontraktem architektury.",
"viewpoints": [
{
"id": "A1",
"status": "enabled",
"title": "CONTEXT — atomowość C nie jest atomowością MMIO",
"content_tex": "Urządzenie ma 64-bitowy licznik, lecz RV32 wykonuje trzy jawne odczyty 32-bitowych rejestrów. Typ \u005ctexttt{uint64\\_t} służy do złożenia zaakceptowanych połówek; nie zamienia dwóch transakcji magistrali w jeden atomowy odczyt."
},
{
"id": "A2",
"status": "enabled",
"title": "STRUCTURE — dwa słowa jednego licznika",
"content_tex": "\u005ctexttt{timer\\_hw\\_t} mapuje kolejno \u005ctexttt{mtime}, \u005ctexttt{mtimeh}, \u005ctexttt{mtimecmp} i \u005ctexttt{mtimecmph} od adresu 0xc0000100. \u005ctexttt{volatile} wymusza obserwowalne dostępy C, ale regułę spójności dostarcza dopiero algorytm."
},
{
"id": "A3",
"status": "unavailable",
"title": "DISPATCH",
"reason": "Task01 ma wyłączone przerwania i wykonuje bezpośredni odczyt MMIO; nie ma ISR, callbacku, tablicy funkcji ani wyboru odbiorcy."
},
{
"id": "A4",
"status": "enabled",
"title": "APPLICATION — kontrolowane przejście przez 0xffffffff",
"content_tex": "Fixture ustawia high=0 i low blisko \u005ctexttt{UINT32\\_MAX}, a następnie wyszukuje małe okno, w którym high zmieni się między pierwszym i drugim odczytem. Zaakceptowany wynik musi pochodzić już z epoki high=1; kolejny odczyt nie może być mniejszy."
},
{
"id": "A5",
"status": "enabled",
"title": "FLOW — read, validate, retry",
"content_tex": "Jedna próba czyta \u005ctexttt{high\\_before}, \u005ctexttt{low}, \u005ctexttt{high\\_after}. Równość high kończy pętlę i pozwala złożyć wynik. Nierówność odrzuca low należące do niejednoznacznej granicy epok i powtarza wszystkie trzy transakcje."
},
{
"id": "A6",
"status": "enabled",
"title": "STATE — epoka 0, rollover, epoka 1",
"content_tex": "Stan urządzenia przechodzi z high=0 i low blisko maksimum przez rollover do high=1 i małego low. Pierwsza para high opisuje zmianę stanu, więc nie wolno do niej przypisać jednego snapshotu; druga próba stabilizuje się w epoce 1."
},
{
"id": "A7",
"status": "enabled",
"title": "RUNTIME — dwa dostępy high są widocznym dowodem",
"content_tex": "Oracle raportuje offset, liczbę prób, retry, high wyniku i monotoniczność. Bieżący RTL daje offset 0x13 i attempts=2. Listing ma pokazać dwa osobne \u005ctexttt{lw} z rejestru high, rozdzielone odczytem low, oraz gałąź powrotną przy nierówności."
},
{
"id": "A8",
"status": "enabled",
"title": "PATTERNS — stabilny snapshot z licznika split-register",
"content_tex": "Wzorzec high--low--high stosuj, gdy dokumentacja urządzenia gwarantuje monotoniczny licznik i zgodne zachowanie połówek. Fixture zapisujący czas pozostaje poza produkcyjnym API odczytu."
}
]
},
{
"id": "task02",
"source": "src/tasks/task02_oneshot_timer_irq.c",
"chapter": "IRQ",
"title": "Jednorazowe przerwanie machine timer",
"idea_tex": "mtimecmp, pending, MTIE/MIE, mtvec, mret i rearm",
"priority": "kluczowe",
"key": true,
"prompt_tex": "Ułóż w poprawnej kolejności: wyłącz maski, ustaw przyszły mtimecmp, sprawdź pending, włącz MTIE i MIE, czekaj w wfi, przesuń komparator w ISR, wróć przez mret. Dla każdego kroku nazwij source, pending, enable, decyzję dispatchera albo efekt.",
"evidence_tex": "Przewidywana sekwencja source--pending--enable--dispatch--effect, log RTL Hazard3 z mcause i kodem wyjścia, fragment listingu z wektorem oraz mret i wniosek o skasowaniu poziomowego źródła.",
"worksheet_step2": "Log wykonania RTL: mcause, pending i resume",
"worksheet_step3": "Dowód w CSR i listingu: slot 7 oraz mret",
"criterion": "Oracle Hazard3: count=1, mcause=0x80000007, pending_before=0, pending_after=0, resumed=1, deadline_reached=1, pass=1 i kod wyjścia 0. Listing ISR kończy się mret.",
"viewpoints": [
{
"id": "A1",
"status": "enabled",
"title": "CONTEXT — enable nie jest źródłem",
"content_tex": "Źródłem jest relacja \u005ctexttt{mtime >= mtimecmp}; \u005ctexttt{mip.MTIP} reprezentuje pending. \u005ctexttt{mie.MTIE} i \u005ctexttt{mstatus.MIE} są dwiema bramkami. Program najpierw ustawia przyszły komparator, a dopiero potem otwiera obie bramki."
},
{
"id": "A2",
"status": "enabled",
"title": "STRUCTURE — wspólny driver i stan dowodowy",
"content_tex": "Wspólny nagłówek zawiera dostęp MMIO, bezpieczny zapis komparatora i operacje CSR. Task przechowuje osobno deadline, czas zaobserwowany, mcause, liczbę ISR, pending przed i po oraz znacznik wznowienia."
},
{
"id": "A3",
"status": "enabled",
"title": "DISPATCH — przyczyna 7 wybiera handler timera",
"content_tex": "Startup zapisuje do \u005ctexttt{mtvec} adres tablicy z bitem trybu vectored. Machine timer ma cause=7, więc rdzeń wybiera slot 7, którego skok wiąże się z silnym symbolem \u005ctexttt{isr\\_machine\\_timer} zamiast słabego handlera domyślnego. Atrybut GCC kończy funkcję instrukcją \u005ctexttt{mret}."
},
{
"id": "A4",
"status": "enabled",
"title": "APPLICATION — one-shot z jawnym kryterium zakończenia",
"content_tex": "Main planuje deadline 1200 taktów po spójnym odczycie. Sukces wymaga dokładnie jednego wejścia, poprawnego mcause, obserwacji nie wcześniejszej niż deadline, skasowanego pending po rearmie i wykonania instrukcji po \u005ctexttt{wfi}."
},
{
"id": "A5",
"status": "enabled",
"title": "FLOW — source → pending → enable → handler → effect",
"content_tex": "Rosnące mtime osiąga comparator i ustawia pending. Otwarty MTIE oraz MIE pozwalają wejść do wektora. Handler zapisuje obserwację i mcause, przesuwa comparator na \u005ctexttt{UINT64\\_MAX}, publikuje count, a \u005ctexttt{mret} przywraca przerwany przepływ. Main ustawia resumed i zamyka maski."
},
{
"id": "A6",
"status": "enabled",
"title": "STATE — DISARMED, ARMED, PENDING, HANDLED, RESUMED",
"content_tex": "Komparator maksimum reprezentuje DISARMED. Przyszły deadline tworzy ARMED. Po osiągnięciu terminu urządzenie jest PENDING, handler ponownie zapisuje maksimum i publikuje HANDLED, a kod po pętli \u005ctexttt{wfi} potwierdza RESUMED."
},
{
"id": "A7",
"status": "enabled",
"title": "RUNTIME — mcause, mip i mret",
"content_tex": "Raport z RTL ma: count=1, mcause=0x80000007, pending 0/0, resumed=1 i deadline\\_reached=1. Objdump ma pokazać zapis trzech połówek mtimecmp w handlerze oraz końcowe \u005ctexttt{mret}; zwykłe \u005ctexttt{ret} nie byłoby poprawnym powrotem z pułapki."
},
{
"id": "A8",
"status": "enabled",
"title": "PATTERNS — arm before unmask, clear source before publish",
"content_tex": "Najpierw ustaw bezpieczny stan urządzenia, potem włącz źródło w \u005ctexttt{mie}, na końcu globalne MIE. W ISR usuń poziomowe źródło przed opublikowaniem zakończenia; inaczej powrót może natychmiast wejść ponownie."
}
]
},
{
"id": "task03",
"source": "src/tasks/task03_periodic_absolute_deadline.c",
"chapter": "TICK",
"title": "Okresowe deadline'y bez dryfu fazy",
"idea_tex": "deadline += period, tick sequence i lateness",
"priority": "kluczowe",
"key": true,
"prompt_tex": "Dla czterech ticków porównaj scheduled, observed i lateness. Udowodnij stały krok 1600 oraz wyprowadź, o ile przesunąłby następną fazę model now+period. Wyjaśnij granicę: przykład nie nadrabia dowolnej liczby całkowicie pominiętych okresów.",
"evidence_tex": "Przewidywana sekwencja czterech deadline'ów, log RTL Hazard3 z planowanymi i obserwowanymi czasami, kontrola różnic i kodu wyjścia oraz wniosek porównujący deadline+period z now+period.",
"worksheet_step2": "Log RTL: deadline, observed i lateness",
"worksheet_step3": "Dowód stałego kroku i braku dryfu fazy",
"criterion": "Oracle Hazard3: count=causes=4, period=1600, step_ok=ordered=late_math=naive_shift_ok=pass=1. Bieżący pomiar daje cztery lateness po 40 taktów, ale test nie uznaje liczby 40 za kontrakt sprzętowy.",
"viewpoints": [
{
"id": "A1",
"status": "enabled",
"title": "CONTEXT — harmonogram i wykonanie to dwie osie",
"content_tex": "Scheduled opisuje idealną oś czasu usługi, observed chwilę rzeczywistego wejścia do pomiaru w ISR, a lateness ich różnicę. Okres ma aktualizować harmonogram, nie kopiować opóźnienia wykonania do przyszłej fazy."
},
{
"id": "A2",
"status": "enabled",
"title": "STRUCTURE — cztery rekordy ticków",
"content_tex": "Trzy tablice po cztery elementy zapisują deadline, obserwację i lateness. \u005ctexttt{g\\_task03\\_next\\_deadline} jest stanem planisty, a count publikuje liczbę kompletnych rekordów. Stała period wynosi 1600 taktów modelu."
},
{
"id": "A3",
"status": "enabled",
"title": "DISPATCH — każdy MTIP trafia przez slot 7",
"content_tex": "Dla każdego z czterech terminów rdzeń koduje interrupt bit i cause=7 w mcause, a tryb vectored wybiera ten sam \u005ctexttt{isr\\_machine\\_timer}. Handler sprawdza mcause przy każdym wejściu; licznik causes=4 dowodzi czterech decyzji dispatchera."
},
{
"id": "A4",
"status": "enabled",
"title": "APPLICATION — stała faza i zmierzone spóźnienie",
"content_tex": "Dla i>0 musi zachodzić \u005ctexttt{scheduled[i]-scheduled[i-1]=1600}. Dla każdego rekordu observed nie może być wcześniejsze od scheduled, a lateness musi być dokładnie ich różnicą. Po czwartym ticku comparator wraca do maksimum."
},
{
"id": "A5",
"status": "enabled",
"title": "FLOW — sample, record, advance, rearm, publish",
"content_tex": "ISR najpierw pobiera indeks i aktualny deadline, potem czyta czas oraz zapisuje rekord. Następnie oblicza deadline+period, uzbraja ten bezwzględny termin albo rozbraja po ostatnim ticku, a count publikuje dopiero na końcu. Main czeka w \u005ctexttt{wfi} do czterech rekordów."
},
{
"id": "A6",
"status": "enabled",
"title": "STATE — TICK0 → TICK4 i niezmienna faza",
"content_tex": "Stan count przechodzi 0,1,2,3,4; razem z każdym przejściem next\\_deadline rośnie dokładnie o period. Po count=4 urządzenie jest DISARMED. Tablice pozostają historią czterech zakończonych przejść i są analizowane dopiero po zamknięciu masek."
},
{
"id": "A7",
"status": "enabled",
"title": "RUNTIME — cztery terminy i 40 taktów bieżącego RTL",
"content_tex": "Bieżący raport podaje deadline low 0x96b, 0xfab, 0x15eb, 0x1c2b; obserwacje są o 0x28 późniejsze. Ważne są relacje step\\_ok, ordered i late\\_math, ponieważ absolutne wartości oraz 40 taktów mogą się zmienić po zmianie RTL, optymalizacji lub prologu ISR."
},
{
"id": "A8",
"status": "enabled",
"title": "PATTERNS — absolute periodic deadline",
"content_tex": "\u005ctexttt{deadline += period} zachowuje fazę. Gdyby następny termin powstał wyłącznie jako \u005ctexttt{observed + period}, jego przesunięcie względem osi absolutnej byłoby równe bieżącemu lateness; powtarzanie tej reguły kumuluje dryf. Osobna polityka musi zdecydować, czy po dużym opóźnieniu nadrabiać, pomijać, czy zgłaszać błąd."
}
]
}
]
}
+10
View File
@@ -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" "$@"
+14
View File
@@ -0,0 +1,14 @@
#!/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"
+85
View File
@@ -0,0 +1,85 @@
#ifndef MACHINE_TIMER_H
#define MACHINE_TIMER_H
#include <stdbool.h>
#include <stdint.h>
#include "hazard3_csr.h"
#include "tb_cxxrtl_io.h"
#define MTIP_MASK UINT32_C(0x00000080)
#define MACHINE_TIMER_CAUSE UINT32_C(0x80000007)
static inline __attribute__((always_inline)) void global_irq_enable(bool enable)
{
if (enable) {
set_csr(mstatus, UINT32_C(0x8));
} else {
clear_csr(mstatus, UINT32_C(0x8));
}
}
static inline __attribute__((always_inline)) void timer_irq_enable(bool enable)
{
if (enable) {
set_csr(mie, MTIP_MASK);
} else {
clear_csr(mie, MTIP_MASK);
}
}
/*
* The Hazard3 teaching testbench maps one 64-bit mtime and one comparator per
* hart as four 32-bit MMIO words at 0xc0000100. On RV32, a C uint64_t load
* is not one atomic device transaction, so the access order is part of the
* driver contract.
*/
static inline __attribute__((always_inline)) uint64_t 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;
}
/* Avoid a transient low comparator while replacing its two RV32 halves. */
static inline __attribute__((always_inline)) void timer_set_compare(uint64_t value)
{
mm_timer->mtimecmph = UINT32_MAX;
mm_timer->mtimecmp = (uint32_t)value;
mm_timer->mtimecmph = (uint32_t)(value >> 32);
}
/* Writable mtime is a testbench fixture, not a portable production API. */
static inline __attribute__((always_inline)) void timer_fixture_set_time(uint32_t high,
uint32_t low)
{
mm_timer->mtimeh = high;
mm_timer->mtime = low;
}
static inline __attribute__((always_inline)) void timer_disable_all_irqs(void)
{
global_irq_enable(false);
timer_irq_enable(false);
}
static inline __attribute__((always_inline)) void cpu_wait_for_interrupt(void)
{
__asm__ volatile("wfi" ::: "memory");
}
static inline void report_u32(const char *label, uint32_t value)
{
tb_puts(label);
tb_put_u32(value);
}
#endif
+66
View File
@@ -0,0 +1,66 @@
#include "machine_timer.h"
volatile uint32_t g_task01_attempts;
volatile uint32_t g_task01_selected_offset;
volatile uint32_t g_task01_retry_seen;
volatile uint32_t g_task01_value_high;
volatile uint32_t g_task01_monotonic;
volatile uint32_t g_task01_pass;
/* noinline keeps the three MMIO reads visible as one inspectable operation. */
__attribute__((noinline)) static uint64_t timer_read64_counted(void)
{
uint32_t high_before;
uint32_t low;
uint32_t high_after;
do {
++g_task01_attempts;
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;
}
int main(void)
{
uint64_t value = 0;
uint64_t after;
uint32_t offset;
timer_disable_all_irqs();
timer_set_compare(UINT64_MAX);
/*
* Search a narrow deterministic testbench window around the 32-bit wrap.
* A real application must never write mtime just to test a reader.
*/
for (offset = 2U; offset <= 96U; ++offset) {
g_task01_attempts = 0U;
timer_fixture_set_time(0U, UINT32_MAX - offset);
value = timer_read64_counted();
if (g_task01_attempts > 1U) {
g_task01_selected_offset = offset;
g_task01_retry_seen = 1U;
break;
}
}
after = timer_read64();
g_task01_value_high = (uint32_t)(value >> 32);
g_task01_monotonic = after >= value;
g_task01_pass = g_task01_retry_seen != 0U &&
g_task01_value_high == 1U &&
g_task01_monotonic != 0U;
report_u32("T01 offset=", g_task01_selected_offset);
report_u32("T01 attempts=", g_task01_attempts);
report_u32("T01 retry=", g_task01_retry_seen);
report_u32("T01 value_high=", g_task01_value_high);
report_u32("T01 monotonic=", g_task01_monotonic);
report_u32("T01 pass=", g_task01_pass);
return g_task01_pass ? 0 : 1;
}
+66
View File
@@ -0,0 +1,66 @@
#include "machine_timer.h"
volatile uint32_t g_task02_isr_count;
volatile uint32_t g_task02_mcause;
volatile uint32_t g_task02_pending_before;
volatile uint32_t g_task02_pending_after;
volatile uint32_t g_task02_resumed;
volatile uint32_t g_task02_deadline_reached;
volatile uint32_t g_task02_pass;
volatile uint64_t g_task02_deadline;
volatile uint64_t g_task02_observed;
/* common/init.S vector 7 selects this symbol; GCC emits mret for this ABI. */
void __attribute__((interrupt("machine"))) isr_machine_timer(void)
{
uint64_t observed = timer_read64();
g_task02_mcause = read_csr(mcause);
g_task02_observed = observed;
/* The source is level-sensitive. Move mtimecmp before publishing count. */
timer_set_compare(UINT64_MAX);
g_task02_isr_count = g_task02_isr_count + 1U;
}
int main(void)
{
uint32_t settle;
timer_disable_all_irqs();
timer_set_compare(UINT64_MAX);
g_task02_deadline = timer_read64() + UINT64_C(1200);
timer_set_compare(g_task02_deadline);
g_task02_pending_before = (read_csr(mip) & MTIP_MASK) != 0U;
timer_irq_enable(true);
global_irq_enable(true);
while (g_task02_isr_count == 0U) {
cpu_wait_for_interrupt();
}
g_task02_resumed = 1U;
timer_disable_all_irqs();
for (settle = 0U; settle < 16U; ++settle) {
__asm__ volatile("nop");
}
g_task02_pending_after = (read_csr(mip) & MTIP_MASK) != 0U;
g_task02_deadline_reached = g_task02_observed >= g_task02_deadline;
g_task02_pass = g_task02_isr_count == 1U &&
g_task02_mcause == MACHINE_TIMER_CAUSE &&
g_task02_pending_before == 0U &&
g_task02_pending_after == 0U &&
g_task02_resumed == 1U &&
g_task02_deadline_reached == 1U;
report_u32("T02 count=", g_task02_isr_count);
report_u32("T02 mcause=", g_task02_mcause);
report_u32("T02 pending_before=", g_task02_pending_before);
report_u32("T02 pending_after=", g_task02_pending_after);
report_u32("T02 resumed=", g_task02_resumed);
report_u32("T02 deadline_reached=", g_task02_deadline_reached);
report_u32("T02 pass=", g_task02_pass);
return g_task02_pass ? 0 : 1;
}
@@ -0,0 +1,105 @@
#include "machine_timer.h"
#define TASK03_TICKS 4U
#define TASK03_PERIOD UINT64_C(1600)
volatile uint32_t g_task03_count;
volatile uint32_t g_task03_mcause_ok;
volatile uint32_t g_task03_step_ok;
volatile uint32_t g_task03_ordered;
volatile uint32_t g_task03_late_math_ok;
volatile uint32_t g_task03_naive_shift_ok;
volatile uint32_t g_task03_pass;
volatile uint64_t g_task03_next_deadline;
volatile uint64_t g_task03_scheduled[TASK03_TICKS];
volatile uint64_t g_task03_observed[TASK03_TICKS];
volatile uint64_t g_task03_lateness[TASK03_TICKS];
volatile uint64_t g_task03_naive_phase_shift;
void __attribute__((interrupt("machine"))) isr_machine_timer(void)
{
uint32_t index = g_task03_count;
uint64_t scheduled = g_task03_next_deadline;
uint64_t observed = timer_read64();
if (read_csr(mcause) == MACHINE_TIMER_CAUSE) {
g_task03_mcause_ok = g_task03_mcause_ok + 1U;
}
if (index < TASK03_TICKS) {
g_task03_scheduled[index] = scheduled;
g_task03_observed[index] = observed;
g_task03_lateness[index] = observed - scheduled;
}
/* Preserve phase: advance the previous deadline, not the observation. */
g_task03_next_deadline = scheduled + TASK03_PERIOD;
if (index + 1U < TASK03_TICKS) {
timer_set_compare(g_task03_next_deadline);
} else {
timer_set_compare(UINT64_MAX);
}
g_task03_count = index + 1U;
}
int main(void)
{
uint32_t i;
timer_disable_all_irqs();
timer_set_compare(UINT64_MAX);
g_task03_next_deadline = timer_read64() + TASK03_PERIOD;
timer_set_compare(g_task03_next_deadline);
timer_irq_enable(true);
global_irq_enable(true);
while (g_task03_count < TASK03_TICKS) {
cpu_wait_for_interrupt();
}
timer_disable_all_irqs();
g_task03_step_ok = 1U;
g_task03_ordered = 1U;
g_task03_late_math_ok = 1U;
for (i = 0U; i < TASK03_TICKS; ++i) {
if (i != 0U &&
g_task03_scheduled[i] - g_task03_scheduled[i - 1U] != TASK03_PERIOD) {
g_task03_step_ok = 0U;
}
if (g_task03_observed[i] < g_task03_scheduled[i]) {
g_task03_ordered = 0U;
}
if (g_task03_observed[i] - g_task03_scheduled[i] != g_task03_lateness[i]) {
g_task03_late_math_ok = 0U;
}
}
/* now + period would copy the last lateness into the next phase. */
g_task03_naive_phase_shift =
(g_task03_observed[TASK03_TICKS - 1U] + TASK03_PERIOD) -
(g_task03_scheduled[TASK03_TICKS - 1U] + TASK03_PERIOD);
g_task03_naive_shift_ok =
g_task03_naive_phase_shift == g_task03_lateness[TASK03_TICKS - 1U];
g_task03_pass = g_task03_count == TASK03_TICKS &&
g_task03_mcause_ok == TASK03_TICKS &&
g_task03_step_ok == 1U &&
g_task03_ordered == 1U &&
g_task03_late_math_ok == 1U &&
g_task03_naive_shift_ok == 1U;
report_u32("T03 count=", g_task03_count);
report_u32("T03 causes=", g_task03_mcause_ok);
report_u32("T03 period_low=", (uint32_t)TASK03_PERIOD);
for (i = 0U; i < TASK03_TICKS; ++i) {
report_u32("T03 deadline_low=", (uint32_t)g_task03_scheduled[i]);
report_u32("T03 observed_low=", (uint32_t)g_task03_observed[i]);
report_u32("T03 lateness_low=", (uint32_t)g_task03_lateness[i]);
}
report_u32("T03 step_ok=", g_task03_step_ok);
report_u32("T03 ordered=", g_task03_ordered);
report_u32("T03 late_math=", g_task03_late_math_ok);
report_u32("T03 naive_shift_ok=", g_task03_naive_shift_ok);
report_u32("T03 pass=", g_task03_pass);
return g_task03_pass ? 0 : 1;
}
+49
View File
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
set -euo pipefail
root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
tmp_dir="$(mktemp -d /tmp/c11-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-11-machine-timer-*.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 24
if awk 'BEGIN { RS="\f" } { compact=$0; gsub(/[[:space:]]/, "", compact); if (length(compact) < 100) exit 1 }' \
"$tmp_dir/card.txt"; then
:
else
echo "FAIL: empty or nearly empty PDF page" >&2
exit 1
fi
if awk 'BEGIN { RS="\f" } \
/A[1-8] (CONTEXT|STRUCTURE|DISPATCH|APPLICATION|FLOW|STATE|RUNTIME|PATTERNS)/ && !/TASK0[1-3]/ { exit 1 }' \
"$tmp_dir/card.txt"; then
:
else
echo "FAIL: A1-A8 block on a page without its TASK heading" >&2
exit 1
fi
jq -e '
.tasks_order == ["task01", "task02", "task03"] 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: 3 tasks, 24 A blocks, valid PDF, no empty/anonymous page\n'
+102
View File
@@ -0,0 +1,102 @@
#!/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"
if [[ ! -x "$tb" ]]; then
make -C "$(dirname -- "$tb")" tb >/dev/null
fi
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 500000 --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,160p' "$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,160p' "$log" >&2
return 1
}
done
printf 'PASS Hazard3 oracle: %s\n' "$task"
}
run_task task01_mtime_consistent_read \
'T01 retry=00000001' \
'T01 value_high=00000001' \
'T01 monotonic=00000001' \
'T01 pass=00000001'
run_task task02_oneshot_timer_irq \
'T02 count=00000001' \
'T02 mcause=80000007' \
'T02 pending_before=00000000' \
'T02 pending_after=00000000' \
'T02 resumed=00000001' \
'T02 deadline_reached=00000001' \
'T02 pass=00000001'
run_task task03_periodic_absolute_deadline \
'T03 count=00000004' \
'T03 causes=00000004' \
'T03 period_low=00000640' \
'T03 step_ok=00000001' \
'T03 ordered=00000001' \
'T03 late_math=00000001' \
'T03 naive_shift_ok=00000001' \
'T03 pass=00000001'
# Independent relations: do not accept a program's pass flag as the only oracle.
t01_attempts_hex="$(sed -n 's/^T01 attempts=//p' \
"$root/build/task01_mtime_consistent_read/oracle.log")"
(( 16#$t01_attempts_hex > 1 )) || {
echo "FAIL task01: rollover did not force a retry" >&2
exit 1
}
mapfile -t t03_deadline < <(sed -n 's/^T03 deadline_low=//p' \
"$root/build/task03_periodic_absolute_deadline/oracle.log")
mapfile -t t03_observed < <(sed -n 's/^T03 observed_low=//p' \
"$root/build/task03_periodic_absolute_deadline/oracle.log")
mapfile -t t03_lateness < <(sed -n 's/^T03 lateness_low=//p' \
"$root/build/task03_periodic_absolute_deadline/oracle.log")
[[ ${#t03_deadline[@]} -eq 4 && ${#t03_observed[@]} -eq 4 && ${#t03_lateness[@]} -eq 4 ]]
for i in 0 1 2 3; do
d=$((16#${t03_deadline[$i]}))
o=$((16#${t03_observed[$i]}))
l=$((16#${t03_lateness[$i]}))
(( o >= d && o - d == l )) || {
echo "FAIL task03: invalid observed/deadline/lateness relation at tick $i" >&2
exit 1
}
if (( i > 0 )); then
previous=$((16#${t03_deadline[$((i - 1))]}))
(( d - previous == 1600 )) || {
echo "FAIL task03: deadline step is not 1600 at tick $i" >&2
exit 1
}
fi
done
for task in task02_oneshot_timer_irq task03_periodic_absolute_deadline; do
"$objdump" -d "$root/build/$task/prog.elf" |
sed -n '/<isr_machine_timer>:/,/^$/p' |
grep -Eq '[[:space:]]mret$' || {
echo "FAIL $task: ISR does not end in mret" >&2
exit 1
}
done
printf 'PASS independent oracle: rollover retry, periodic arithmetic, ISR mret\n'
+1
View File
File diff suppressed because one or more lines are too long
+61
View File
File diff suppressed because one or more lines are too long
+2567
View File
File diff suppressed because one or more lines are too long
+15
View File
@@ -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>
+39
View File
File diff suppressed because one or more lines are too long