feat: add lab-rv32i-c-gpio-edges-debounce card

This commit is contained in:
user
2026-07-21 19:14:20 +02:00
commit 6af8fa6da8
24 changed files with 5390 additions and 0 deletions
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -euo pipefail
root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
tmp_dir="$(mktemp -d /tmp/c13-card.XXXXXX)"
cleanup() {
rm -rf -- "$tmp_dir"
}
trap cleanup EXIT HUP INT TERM
"$root/scripts/render_card_layouts.sh" >/dev/null
"$root/scripts/render_new_pdf.sh" >/dev/null
pdf="$(find "$root/doc/pdf" -maxdepth 1 -type f \
-name 'mpabi-inf-c-13-gpio-edges-debounce-*.pdf' -print -quit)"
test -n "$pdf"
qpdf --check "$pdf" >/dev/null
pdftotext -layout "$pdf" "$tmp_dir/card.txt"
blocks="$(rg -o 'A[1-8] (CONTEXT|STRUCTURE|DISPATCH|APPLICATION|FLOW|STATE|RUNTIME|PATTERNS)' \
"$tmp_dir/card.txt" | wc -l)"
test "$blocks" -eq 32
awk 'BEGIN { RS="\f" } { compact=$0; gsub(/[[:space:]]/, "", compact); if (length(compact) < 100) exit 1 }' \
"$tmp_dir/card.txt" || { echo "FAIL: empty PDF page" >&2; exit 1; }
awk 'BEGIN { RS="\f" } \
/A[1-8] (CONTEXT|STRUCTURE|DISPATCH|APPLICATION|FLOW|STATE|RUNTIME|PATTERNS)/ && !/TASK0[1-4]/ { exit 1 }' \
"$tmp_dir/card.txt" || { echo "FAIL: anonymous A1-A8 page" >&2; exit 1; }
jq -e '
.tasks_order == ["task01", "task02", "task03", "task04"] and
all(.tasks[];
[.viewpoints[].id] == ["A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8"] and
all(.viewpoints[]; .status == "enabled" or .status == "unavailable"))
' "$root/json/card_source.json" >/dev/null
node --check "$root/web/app.js"
printf 'PASS card: 4 tasks, 32 A blocks, valid PDF, no empty/anonymous page\n'
+109
View File
@@ -0,0 +1,109 @@
#!/usr/bin/env bash
set -euo pipefail
root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
env_root="${RV_ENV_ROOT:-/home/user/dev/edu/rv32i-hazard3-student-env}"
tb="${HAZARD3_TB:-$env_root/vendor/Hazard3/test/sim/tb_verilator/tb}"
objdump="${RISCV_PREFIX:-riscv64-unknown-elf-}objdump"
test -x "$tb"
make -C "$root" -s tasks "RV_ENV_ROOT=$env_root"
run_task() {
local task="$1"
shift
local log="$root/build/$task/oracle.log"
"$tb" --bin "$root/build/$task/prog.bin" --cycles 1000000 \
--cpuret --logfile "$log"
if grep -Eq 'Unhandled trap|Max cycles reached' "$log"; then
printf 'FAIL %s: trap or timeout\n' "$task" >&2
sed -n '1,180p' "$log" >&2
return 1
fi
local expected
for expected in "$@"; do
grep -Fqx "$expected" "$log" || {
printf 'FAIL %s: missing <%s>\n' "$task" "$expected" >&2
sed -n '1,180p' "$log" >&2
return 1
}
done
printf 'PASS Hazard3 oracle: %s\n' "$task"
}
run_task task01_gpio_polling \
'T01 dir=00000001' \
'T01 external_high=00000002' \
'T01 out_after_set=00000001' \
'T01 both_high=00000003' \
'T01 final_in=00000000' \
'T01 pass=00000001'
run_task task02_gpio_rising_irq \
'T02 count=00000001' \
'T02 irq=00000012' \
'T02 source_before=00000001' \
'T02 controller_before=00000001' \
'T02 pending_in_handler=00000004' \
'T02 source_after=00000000' \
'T02 controller_after=00000000' \
'T02 same_level_pending=00000000' \
'T02 resumed=00000001' \
'T02 pass=00000001'
run_task task03_gpio_quiet_window \
'T03 count=00000003' \
'T03 level0=00000001' \
'T03 level1=00000000' \
'T03 level2=00000001' \
'T03 quiet=000007d0' \
'T03 early=00000000' \
'T03 accepted=00000001' \
'T03 pass=00000001'
run_task task04_raw_c_superloop \
'T04 timer_irq=00000001' \
'T04 timer_mcause=80000007' \
'T04 uart_irq=00000001' \
'T04 uart_number=00000010' \
'T04 uart_byte=00000055' \
'T04 gpio_irq=00000001' \
'T04 gpio_number=00000012' \
'T04 gpio_level=00000001' \
'T04 main_timer=00000001' \
'T04 main_uart=00000001' \
'T04 main_gpio=00000001' \
'T04 final_out=00000007' \
'T04 final_in=00000017' \
'T04 pass=00000001'
# Independent arithmetic oracle for debounce; do not trust only program pass.
t03_log="$root/build/task03_gpio_quiet_window/oracle.log"
mapfile -t times < <(sed -n 's/^T03 time[0-2]_low=//p' "$t03_log")
mapfile -t gaps < <(sed -n 's/^T03 gap[01][12]=//p' "$t03_log")
quiet_hex="$(sed -n 's/^T03 quiet=//p' "$t03_log")"
accepted_hex="$(sed -n 's/^T03 accepted_after=//p' "$t03_log")"
[[ ${#times[@]} -eq 3 && ${#gaps[@]} -eq 2 ]]
quiet=$((16#$quiet_hex))
accepted_after=$((16#$accepted_hex))
for i in 0 1; do
first=$((16#${times[$i]}))
second=$((16#${times[$((i + 1))]}))
gap=$((16#${gaps[$i]}))
(( second > first && second - first == gap && gap < quiet )) || {
echo "FAIL task03: invalid bounce interval $i" >&2
exit 1
}
done
(( accepted_after >= quiet )) || {
echo "FAIL task03: accepted before the quiet window" >&2
exit 1
}
# The machine-timer handler must return from a trap, not as a normal function.
"$objdump" -d "$root/build/task04_raw_c_superloop/prog.elf" |
sed -n '/<isr_machine_timer>:/,/^$/p' |
grep -Eq '[[:space:]]mret$'
printf 'PASS independent oracle: edge/W1C relations, debounce timing, ISR mret\n'