feat: add lab-rv32i-c-machine-timer card
This commit is contained in:
+11
@@ -0,0 +1,11 @@
|
||||
/build/
|
||||
/doc/generated/*
|
||||
!/doc/generated/.gitkeep
|
||||
!/doc/generated/main.tex
|
||||
*.aux
|
||||
*.fdb_latexmk
|
||||
*.fls
|
||||
*.log
|
||||
*.out
|
||||
*.upa
|
||||
*.upb
|
||||
@@ -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)
|
||||
@@ -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ą high–low–high, również przez rollover;
|
||||
2. jednorazowe przerwanie machine timer z `mtimecmp`, `mie.MTIE`,
|
||||
`mstatus.MIE`, wektorem 7 i powrotem `mret`;
|
||||
3. okresowe deadline’y `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), deadline’y 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 fixture’em 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 A1–A8. Widok A3 jest N/D tylko
|
||||
w Task01; w Task02 i Task03 dowodem dispatchu jest faktyczny wybór slotu
|
||||
`mtvec` na podstawie przyczyny przerwania.
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -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 A1–A8}
|
||||
\reversemarginpar
|
||||
\marginnote[%
|
||||
\begin{minipage}{\marginparwidth}%
|
||||
\raggedright
|
||||
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||
\begin{minipage}[t]{\marginparwidth}%
|
||||
\raggedright
|
||||
{\bfseries\textcolor{black!65}{TECH}\par}%
|
||||
\vspace{0.08em}%
|
||||
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||
\end{minipage}%
|
||||
}%
|
||||
\end{minipage}%
|
||||
]{}[-3.1em]
|
||||
\normalmarginpar
|
||||
|
||||
\marginnote{%
|
||||
\begin{minipage}{\marginparwidth}%
|
||||
\raggedright
|
||||
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||
\hspace*{0.06cm}%
|
||||
\begin{minipage}[t]{\dimexpr\marginparwidth-0.06cm\relax}%
|
||||
\raggedright
|
||||
{\bfseries\textcolor{black!65}{OG}\par}%
|
||||
\vspace{0.08em}%
|
||||
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||
\end{minipage}%
|
||||
}%
|
||||
\end{minipage}%
|
||||
}[-3.1em]
|
||||
|
||||
|
||||
\begin{ESCTaskFrame}{TASK01 · 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}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
Binary file not shown.
@@ -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 A1–A8",
|
||||
"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"
|
||||
]
|
||||
}
|
||||
@@ -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."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
script_dir=$(CDPATH= cd "$(dirname "$0")" && pwd)
|
||||
repo_root=$(CDPATH= cd "$script_dir/.." && pwd)
|
||||
layouts_root=$repo_root/../../../tools/card-layouts
|
||||
|
||||
python3 "$layouts_root/tools/build_kr_c_card.py" "$repo_root"
|
||||
exec python3 "$layouts_root/tools/render_card.py" "$repo_root" "$@"
|
||||
|
||||
Executable
+14
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Executable
+49
@@ -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'
|
||||
Executable
+102
@@ -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'
|
||||
File diff suppressed because one or more lines are too long
+61
File diff suppressed because one or more lines are too long
+2567
File diff suppressed because one or more lines are too long
@@ -0,0 +1,15 @@
|
||||
<!doctype html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="card-renderer" content="react">
|
||||
<title>Karta pracy</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<link rel="stylesheet" href="app.css">
|
||||
</head>
|
||||
<body class="resource-header-enabled">
|
||||
<div id="root"></div>
|
||||
<script type="module" src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user