feat: publish FreeRTOS C FC11 card
This commit is contained in:
Executable
+27
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
nm=$(printenv RISCV_NM || true)
|
||||
readelf=$(printenv RISCV_READELF || true)
|
||||
test -n "$nm" || nm=riscv64-unknown-elf-nm
|
||||
test -n "$readelf" || readelf=riscv64-unknown-elf-readelf
|
||||
elf=build/task01_static_allocation/prog.elf
|
||||
|
||||
if "$nm" --defined-only "$elf" | awk '{print $3}' | grep -Eq '^_Z|^__cxa_|^_Unwind_'; then
|
||||
echo "unexpected C++ or unwind symbol in FC11" >&2
|
||||
exit 1
|
||||
fi
|
||||
if "$readelf" -S "$elf" | grep -Eq '\.init_array|\.eh_frame|\.gcc_except_table'; then
|
||||
echo "unexpected C++ initialization or unwind section in FC11" >&2
|
||||
exit 1
|
||||
fi
|
||||
for symbol in fc11_timer_callback fc11_dynamic_task_entry fc11_static_task_entry \
|
||||
fc11_verifier_entry fc11_checkpoint_committed xTaskCreate xTaskCreateStatic \
|
||||
xQueueGenericCreate xQueueGenericCreateStatic xTimerCreate xTimerCreateStatic \
|
||||
vApplicationGetIdleTaskMemory vApplicationGetTimerTaskMemory; do
|
||||
"$nm" --defined-only "$elf" | grep -Eq "[[:space:]]$symbol$" || {
|
||||
echo "required symbol is missing: $symbol" >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
echo "PASS ABI: FC11 links dynamic and static task, queue and timer APIs"
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
tb=${1:?usage: check_determinism.sh /path/to/tb [build-dir]}
|
||||
build_dir=${2:-build}
|
||||
artifact="$build_dir/task01_static_allocation/prog.bin"
|
||||
reference="$build_dir/determinism-1.log"
|
||||
|
||||
run_once() {
|
||||
output=$1
|
||||
"$tb" --bin "$artifact" --cycles 3000000 --cpuret >"$output"
|
||||
}
|
||||
|
||||
run_once "$reference"
|
||||
run_once "$build_dir/determinism-2.log"
|
||||
run_once "$build_dir/determinism-3.log"
|
||||
cmp "$reference" "$build_dir/determinism-2.log"
|
||||
cmp "$reference" "$build_dir/determinism-3.log"
|
||||
echo "PASS determinism: three clean Hazard3 processes produced identical evidence"
|
||||
@@ -0,0 +1,309 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build the FC11 card source from the shared FreeRTOS C card vocabulary."""
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import subprocess
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
BASE = ROOT.parent / "lab-rv32i-freertos-c-notifications" / "json" / "card_source.json"
|
||||
|
||||
|
||||
def sha256(path: Path) -> str:
|
||||
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def code_step(step_id, number, label, code_ref, evidence, strategy="code.storage"):
|
||||
return {
|
||||
"id": step_id, "number": number, "label": label, "mode": "CODE",
|
||||
"strategy_ref": strategy, "svg_label": f"{number:02d}",
|
||||
"code_ref": code_ref, "description": label, "evidence": evidence,
|
||||
}
|
||||
|
||||
|
||||
def run_step(step_id, number, label, event, code_ref, evidence, strategy="run.heap"):
|
||||
snapshot = {
|
||||
1: "h0", 2: "h1", 3: "h2", 4: "dynamic-run",
|
||||
5: "static-run", 6: "dynamic-callback", 7: "static-callback",
|
||||
8: "pass",
|
||||
}[event]
|
||||
return {
|
||||
"id": step_id, "number": number, "label": label, "mode": "RUN",
|
||||
"event_id": f"E{event:02d}", "strategy_ref": strategy,
|
||||
"svg_label": f"{number:02d}", "code_ref": code_ref,
|
||||
"snapshot_ref": f"task01.{snapshot}", "description": label,
|
||||
"evidence": evidence,
|
||||
}
|
||||
|
||||
|
||||
def section(aid, label, title, content, asset, orientation, steps):
|
||||
width, height = (940, 560) if orientation == "landscape" else (660, 760)
|
||||
return {
|
||||
"title": f"{aid} — {label.title()}",
|
||||
"order": int(aid[1:]) * 10,
|
||||
"content_kind": "prose",
|
||||
"asset_page_mode": "one-per-page",
|
||||
"page_orientation": orientation,
|
||||
"content_tex": content,
|
||||
"assets": [{
|
||||
"path": f"assets/{asset}.png",
|
||||
"html_path": f"assets/{asset}.svg",
|
||||
"source_path": f"assets/{asset}.puml",
|
||||
"caption": f"{aid} {label} — {title}.",
|
||||
"label": f"fig:{asset}",
|
||||
"alt": title,
|
||||
"kind": "diagram",
|
||||
"width": 1.0,
|
||||
"page_grid": {
|
||||
"columns": 1, "rows": 1, "page_width": width,
|
||||
"page_height": height, "overview": True,
|
||||
"step_tiles": {s["id"]: 1 for s in steps},
|
||||
},
|
||||
"interactive": {
|
||||
"kind": "uml-class",
|
||||
"storage_key": f"fc11-{asset}",
|
||||
"title": f"{aid} · {title}",
|
||||
"task": {"id": "task01", "label": "Task01 · two storage policies"},
|
||||
"block": {"id": aid.lower(), "label": f"{aid} {label}", "description": content},
|
||||
"phases": [{"id": label.lower(), "label": title.upper(), "steps": steps}],
|
||||
},
|
||||
}],
|
||||
}
|
||||
|
||||
|
||||
data = json.loads(BASE.read_text())
|
||||
card_uuid = str(uuid.uuid5(uuid.NAMESPACE_URL, "stem:freertos-c:fc11:static-allocation"))
|
||||
task_uuid = str(uuid.uuid5(uuid.NAMESPACE_URL, "stem:freertos-c:fc11:task01"))
|
||||
issued = "2026-07-19T00:00:00+02:00"
|
||||
repo_url = "http://77.90.8.171:3001/edu-inf/lab-rv32i-freertos-c-static-allocation"
|
||||
html_url = f"https://{card_uuid}/{task_uuid}"
|
||||
|
||||
data["card"].update({
|
||||
"id": "mpabi-freertos-c-11-static-allocation",
|
||||
"number": "11", "count": "15", "slug": "static-allocation",
|
||||
"title": "Static and dynamic allocation",
|
||||
"topic": "task, queue and timer with two storage policies",
|
||||
"level": "Rok 2 · L11 · RV32I/Hazard3", "revision_date": issued,
|
||||
"version": "v00.01", "uuid": card_uuid,
|
||||
})
|
||||
data["title_block"].update({
|
||||
"title": "Static and dynamic allocation", "url": html_url,
|
||||
"repository_url": repo_url, "url_host_uuid": card_uuid,
|
||||
"doc_uuid": task_uuid, "issued_on": issued, "series": "FREERTOS-C-11",
|
||||
})
|
||||
data["front_page_scope"] = {
|
||||
"title": "Cel karty",
|
||||
"content_tex": "Uczeń rozdziela semantykę obiektu FreeRTOS od polityki przechowywania i dowodzi jej przez adresy oraz cztery pomiary wolnego heap.",
|
||||
"scope_title": "Zakres karty",
|
||||
"scope_content_tex": "Dynamiczny i statyczny task, queue oraz timer wykonują ten sam eksperyment. H1 jest mniejsze od H0, H2 równa się H1, a H3 równa się H2. Pamięć Idle i timer-service dostarcza aplikacja.",
|
||||
"scope_table": {"headers": ["Lekcja", "Task", "Idea", "Priorytet", "Status", "Version"], "rows": [{
|
||||
"chapter": "FC11", "task": "Task01", "idea_tex": "behavior parity plus storage provenance",
|
||||
"priority": "główny", "status": "ready", "version": "v00.01", "key": True,
|
||||
}]},
|
||||
}
|
||||
data["viewpoints"] = [
|
||||
{"id": "A1", "label": "CONTEXT", "subtitle": "ownership boundaries", "status": "enabled", "target_label": "fig:a1-context"},
|
||||
{"id": "A2", "label": "STRUCTURE", "subtitle": "dynamic/static object pairs", "status": "enabled", "target_label": "fig:a2-structure"},
|
||||
{"id": "A3", "label": "DISPATCH", "subtitle": "same kernel dispatch", "status": "unavailable", "reason": "Storage policy does not change callback dispatch."},
|
||||
{"id": "A4", "label": "APPLICATION", "subtitle": "behavior parity", "status": "enabled", "target_label": "fig:a4-application"},
|
||||
{"id": "A5", "label": "FLOW", "subtitle": "H0/H1/H2/H3", "status": "enabled", "target_label": "fig:a5-flow"},
|
||||
{"id": "A6", "label": "STATE", "subtitle": "covered by flow", "status": "unavailable", "reason": "No new lifecycle state machine; the card compares storage."},
|
||||
{"id": "A7", "label": "RUNTIME", "subtitle": "address provenance", "status": "enabled", "target_label": "fig:a7-runtime"},
|
||||
{"id": "A8", "label": "PATTERNS", "subtitle": "policy comparison", "status": "unavailable", "reason": "No wrapper pattern is introduced in this C card."},
|
||||
]
|
||||
data["debug_strategies"] = {
|
||||
"code.storage": {
|
||||
"id": "code.storage", "kind": "code", "action": "open",
|
||||
"expected_observations": ["persistent Static* buffers", "application static Idle/timer task memory"],
|
||||
"assertions": ["no local-lifetime storage", "both allocation modes enabled"],
|
||||
"evidence_fields": ["symbol", "section", "code_ref"], "prerequisites": ["FC11 source"],
|
||||
"layout": ["buffers", "create APIs", "FreeRTOSConfig"],
|
||||
"commands": ["rg -n 'x.*CreateStatic|vApplicationGet.*TaskMemory|Static(Task|Queue|Timer)_t' include src"],
|
||||
},
|
||||
"run.heap": {
|
||||
"id": "run.heap", "kind": "run", "action": "replay",
|
||||
"expected_observations": ["H1 < H0", "H2 == H1", "H3 == H2"],
|
||||
"assertions": ["objects alive at H3", "no exact delta assumption"],
|
||||
"evidence_fields": ["H0", "H1", "H2", "H3", "timestamp"],
|
||||
"prerequisites": ["clean Hazard3 RAM"], "layout": ["heap values", "event order"],
|
||||
"commands": ["print g_fc11.heap_h0", "print g_fc11.heap_h1", "print g_fc11.heap_h2", "print g_fc11.heap_h3"],
|
||||
},
|
||||
"run.parity": {
|
||||
"id": "run.parity", "kind": "run", "action": "replay",
|
||||
"expected_observations": ["both receive 0xCAFE", "both timers callback once"],
|
||||
"assertions": ["distinct stacks", "PASS and deterministic digest"],
|
||||
"evidence_fields": ["received", "timer calls", "SP", "digest"],
|
||||
"prerequisites": ["scheduler running"], "layout": ["two branches", "verifier"],
|
||||
"commands": ["print/x g_fc11.dynamic_received", "print/x g_fc11.static_received", "print g_fc11_pass"],
|
||||
},
|
||||
}
|
||||
|
||||
a1 = [
|
||||
code_step("application", 1, "application owns experiment", "src/tasks/task01_static_allocation.c:20", "global experiment"),
|
||||
code_step("dynamic", 2, "dynamic APIs request storage", "src/tasks/task01_static_allocation.c:218", "xTaskCreate/xQueueCreate/xTimerCreate"),
|
||||
code_step("static", 3, "static APIs receive storage", "src/tasks/task01_static_allocation.c:239", "named buffers"),
|
||||
code_step("kernel", 4, "kernel owns object semantics", "vendor/FreeRTOS-Kernel/tasks.c:1", "same scheduler"),
|
||||
code_step("ram", 5, "both policies coexist in RAM", "src/tasks/task01_static_allocation.c:26", "ucHeap plus static buffers"),
|
||||
]
|
||||
a2 = [
|
||||
run_step("dynamic-task", 1, "dynamic task handle is in heap", 2, "src/tasks/task01_static_allocation.c:218", "heap address"),
|
||||
run_step("static-task", 2, "static task uses TCB and stack buffers", 3, "src/tasks/task01_static_allocation.c:239", "outside heap"),
|
||||
run_step("dynamic-queue", 3, "dynamic queue object is in heap", 2, "src/tasks/task01_static_allocation.c:228", "heap address"),
|
||||
run_step("static-queue", 4, "static queue uses supplied storage", 3, "src/tasks/task01_static_allocation.c:247", "outside heap"),
|
||||
run_step("dynamic-timer", 5, "dynamic timer object is in heap", 2, "src/tasks/task01_static_allocation.c:229", "heap address"),
|
||||
run_step("static-timer", 6, "static timer uses StaticTimer_t", 3, "src/tasks/task01_static_allocation.c:250", "outside heap"),
|
||||
code_step("services", 7, "Idle and timer-service storage is static", "src/tasks/task01_static_allocation.c:45", "two application callbacks"),
|
||||
]
|
||||
a4 = [
|
||||
run_step("dynamic-worker", 1, "dynamic branch receives message", 4, "src/tasks/task01_static_allocation.c:121", "0xCAFE", "run.parity"),
|
||||
run_step("static-worker", 2, "static branch receives message", 5, "src/tasks/task01_static_allocation.c:139", "0xCAFE", "run.parity"),
|
||||
run_step("daemon", 3, "daemon executes both callbacks", 7, "src/tasks/task01_static_allocation.c:99", "1 + 1", "run.parity"),
|
||||
run_step("verifier", 4, "verifier checks behavior parity", 8, "src/tasks/task01_static_allocation.c:157", "PASS", "run.parity"),
|
||||
]
|
||||
a5 = [
|
||||
run_step("h0", 1, "H0 baseline", 1, "src/tasks/task01_static_allocation.c:214", "baseline"),
|
||||
run_step("h1", 2, "dynamic objects make H1 smaller", 2, "src/tasks/task01_static_allocation.c:236", "H1 < H0"),
|
||||
run_step("h2", 3, "static objects leave heap unchanged", 3, "src/tasks/task01_static_allocation.c:263", "H2 = H1"),
|
||||
run_step("dynamic-run", 4, "dynamic worker runs", 4, "src/tasks/task01_static_allocation.c:121", "message" , "run.parity"),
|
||||
run_step("static-run", 5, "static worker runs", 5, "src/tasks/task01_static_allocation.c:139", "message", "run.parity"),
|
||||
run_step("dynamic-callback", 6, "dynamic timer callback", 6, "src/tasks/task01_static_allocation.c:99", "count 1", "run.parity"),
|
||||
run_step("static-callback", 7, "static timer callback", 7, "src/tasks/task01_static_allocation.c:99", "count 1", "run.parity"),
|
||||
run_step("pass", 8, "H3 unchanged and verifier passes", 8, "src/tasks/task01_static_allocation.c:157", "H3 = H2", "run.parity"),
|
||||
]
|
||||
a7 = [
|
||||
run_step("heap-arena", 1, "dynamic handles lie in ucHeap", 8, "src/tasks/task01_static_allocation.c:174", "three true predicates"),
|
||||
run_step("named-buffers", 2, "static handles lie outside ucHeap", 8, "src/tasks/task01_static_allocation.c:177", "three false predicates"),
|
||||
code_step("service-buffers", 3, "kernel-service buffers are named", "src/tasks/task01_static_allocation.c:34", "Idle and daemon buffers"),
|
||||
run_step("heap-relation", 4, "four checkpoints satisfy relation", 8, "include/task01_static_allocation_model.h:34", "H1<H0; H2=H1; H3=H2"),
|
||||
run_step("evidence", 5, "commit-last evidence is deterministic", 8, "src/tasks/task01_static_allocation.c:72", "cdf44a0a", "run.parity"),
|
||||
]
|
||||
data["sections"] = [
|
||||
section("A1", "CONTEXT", "ownership boundaries", "Application, kernel and RAM responsibilities are explicit.", "a1-context", "portrait", a1),
|
||||
section("A2", "STRUCTURE", "object pairs", "Task, queue and timer are compared pairwise.", "a2-structure", "landscape", a2),
|
||||
section("A4", "APPLICATION", "behavior parity", "Storage differs while application behavior remains equal.", "a4-application", "portrait", a4),
|
||||
section("A5", "FLOW", "four heap checkpoints", "H0/H1/H2/H3 bracket creation and scheduler startup.", "a5-flow", "landscape", a5),
|
||||
section("A7", "RUNTIME", "address provenance", "Debugger evidence connects handles to heap or named buffers.", "a7-runtime", "portrait", a7),
|
||||
{"title": "Task01 — two storage policies", "order": 90, "content_kind": "tasks", "task_refs": ["task01"]},
|
||||
]
|
||||
data["tasks_order"] = ["task01"]
|
||||
data["tasks"] = {"task01": {
|
||||
"title": "Same behavior, two storage policies", "uuid": task_uuid,
|
||||
"prompt_tex": "Przejdź po A1, A2, A4, A5 i A7. Odtwórz E01–E08 oraz udowodnij relacje H1<H0, H2=H1 i H3=H2.",
|
||||
"criterion": "PASS; address provenance; message and callback parity; deterministic digest.",
|
||||
"conclusion_tex": "Statyczne API zmienia własność pamięci, ale nie semantykę taska, kolejki ani timera.",
|
||||
"flow": [
|
||||
{"kind": "block", "id": "predict", "title": "A — przewidywanie", "content_tex": "Przewidź cztery pomiary heap.", "steps": [
|
||||
{"id": "relation", "title": "Zapisz relacje H0–H3.", "content_tex": "Nie zakładaj dokładnej liczby bajtów."},
|
||||
{"id": "ownership", "title": "Wskaż właściciela pamięci.", "content_tex": "Rozdziel ucHeap i named buffers."},
|
||||
]},
|
||||
{"kind": "block", "id": "replay", "title": "B — replay Hazard3", "content_tex": "Wykonaj E01–E08 z clean RAM.", "steps": [
|
||||
{"id": "heap", "title": "Zbadaj H0/H1/H2/H3.", "content_tex": "Zapisz wartości i relacje."},
|
||||
{"id": "parity", "title": "Zbadaj obie gałęzie.", "content_tex": "Porównaj message, timer callback i stack."},
|
||||
{"id": "pass", "title": "Zbadaj E08.", "content_tex": "Zapisz PASS, digest i timestamp."},
|
||||
]},
|
||||
{"kind": "exercise", "id": "choose-policy", "title": "Ćwiczenie — wybór polityki", "prompt_tex": "Dla taska okresowego, kolejki telemetrycznej i timera bezpieczeństwa wybierz dynamiczne albo statyczne tworzenie. Uzasadnij lifetime i ownership.", "evidence_tex": "Tabela obiekt→API→storage→lifetime oraz ślad H0–H3.", "criterion": "Każdy bufor ma trwałość co najmniej tak długą jak obiekt kernelowy.", "based_on": ["predict", "replay"]},
|
||||
],
|
||||
"educational_requirement_refs": ["FC11.WE01"], "learning_effect_refs": ["FC11.EN01", "FC11.EK01"], "assessment_criterion_ref": "FC11.KW01",
|
||||
}}
|
||||
|
||||
elf = ROOT / "build/task01_static_allocation/prog.elf"
|
||||
image = ROOT / "build/task01_static_allocation/prog.bin"
|
||||
blob = subprocess.check_output(["git", "hash-object", "src/tasks/task01_static_allocation.c"], cwd=ROOT, text=True).strip()
|
||||
items = {}
|
||||
checks = {
|
||||
1: ("h0", ["g_fc11_event_count", 1]),
|
||||
2: ("h1", ["g_fc11.heap_h1 < g_fc11.heap_h0", 1]),
|
||||
3: ("h2", ["g_fc11.heap_h2 == g_fc11.heap_h1", 1]),
|
||||
4: ("dynamic-run", ["g_fc11.dynamic_received", 0xCAFE]),
|
||||
5: ("static-run", ["g_fc11.static_received", 0xCAFE]),
|
||||
6: ("dynamic-callback", ["g_fc11.dynamic_timer_calls", 1]),
|
||||
7: ("static-callback", ["g_fc11.static_timer_calls", 1]),
|
||||
8: ("pass", ["g_fc11_pass", 1]),
|
||||
}
|
||||
for event, (name, check) in checks.items():
|
||||
items[f"task01.{name}"] = {
|
||||
"event_id": f"E{event:02d}",
|
||||
"stop": {"symbol": "fc11_checkpoint_committed", "offset": 0, "condition": f"$a0 == {event}"},
|
||||
"verify": {"expressions": [{"expr": check[0], "equals": check[1]}]},
|
||||
}
|
||||
data["debug_checkpoints"] = {
|
||||
"schema": "stem-debug-checkpoints.v1", "semantics": "deterministic-replay",
|
||||
"artifact": {"source": "src/tasks/task01_static_allocation.c", "source_git_blob": blob,
|
||||
"elf": "build/task01_static_allocation/prog.elf", "hazard3_elf_sha256": sha256(elf),
|
||||
"hazard3_image": "build/task01_static_allocation/prog.bin", "hazard3_image_sha256": sha256(image)},
|
||||
"targets": {"hazard3-sim": {"adapter": "hazard3-reset-load-replay.v1", "baseline": "restart-backend", "clean_ram": True}},
|
||||
"items": items,
|
||||
}
|
||||
data["educational_requirements"] = {"FC11.WE01": {
|
||||
"text": "Porównanie dynamicznej i statycznej polityki pamięci obiektów FreeRTOS.", "label": "storage policy and behavior parity",
|
||||
"learning_effects": ["FC11.EN01", "FC11.EK01"], "learning_tree": {
|
||||
"schema": "we-learning-tree.v1", "policy": "Każda aktywna kotwica wskazuje kod lub checkpoint.",
|
||||
"ogolne": [{"effect_ref": "FC11.EN01", "display": "EN LOCAL RTOS.11", "source": "LOCAL", "official": "RTOS", "local": "11", "kind": "EN", "tree_id": "FC11.WE01.OG.LOCAL.RTOS.11", "text": "Uczeń rozdziela semantykę obiektu i politykę przechowywania.", "kw": [{"criterion_ref": "FC11.KW01", "display": "KW LOCAL RTOS.11", "source": "LOCAL", "kind": "KW", "official": "RTOS", "local": "11", "text": "Łączy kod, diagram i dowód runtime."}]}],
|
||||
"zawodowe": [{"effect_ref": "FC11.EK01", "display": "EK LOCAL RTOS.11", "source": "LOCAL", "official": "RTOS", "local": "11", "kind": "EK", "tree_id": "FC11.WE01.TECH.LOCAL.RTOS.11", "text": "Uczeń odtwarza E01–E08 i dowodzi H1<H0, H2=H1, H3=H2.", "kw": [{"criterion_ref": "FC11.KW01", "display": "KW LOCAL RTOS.11", "source": "LOCAL", "kind": "KW", "official": "RTOS", "local": "11", "text": "Łączy kod, diagram i dowód runtime."}]}],
|
||||
},
|
||||
}}
|
||||
data["learning_effects"] = {
|
||||
"FC11.EN01": {"bloom_level": "Analiza", "label": "Storage ownership", "text": "Uczeń rozdziela semantykę obiektu i politykę przechowywania.", "assessment_criteria": ["FC11.KW01"]},
|
||||
"FC11.EK01": {"bloom_level": "Zastosowanie", "label": "Heap replay", "text": "Uczeń odtwarza E01–E08 i dowodzi H1<H0, H2=H1, H3=H2.", "assessment_criteria": ["FC11.KW01"]},
|
||||
}
|
||||
data["assessment_criteria"] = {"FC11.KW01": {"text": "PASS; address provenance; message and callback parity; deterministic digest.", "learning_effects": ["FC11.EN01", "FC11.EK01"]}}
|
||||
|
||||
spec_sections = []
|
||||
for expanded in data["sections"]:
|
||||
if expanded.get("content_kind") == "tasks":
|
||||
continue
|
||||
aid = expanded["title"].split()[0]
|
||||
asset = expanded["assets"][0]
|
||||
interactive = asset["interactive"]
|
||||
stem = Path(asset["path"]).stem
|
||||
spec_asset = {
|
||||
"stem": stem,
|
||||
"title": interactive["title"],
|
||||
"caption": asset["caption"],
|
||||
"label": asset["label"],
|
||||
"alt": asset["alt"],
|
||||
"phases": interactive["phases"],
|
||||
}
|
||||
if aid == "A5":
|
||||
spec_asset["diagram_kind"] = "sequence"
|
||||
spec_sections.append({
|
||||
"id": aid,
|
||||
"label": interactive["block"]["label"].split(maxsplit=1)[1],
|
||||
"title": expanded["title"].split("—", 1)[1].strip(),
|
||||
"order": expanded["order"],
|
||||
"orientation": expanded["page_orientation"],
|
||||
"description": interactive["block"]["description"],
|
||||
"content_tex": expanded["content_tex"],
|
||||
"assets": [spec_asset],
|
||||
})
|
||||
|
||||
task = data["tasks"]["task01"]
|
||||
spec = {
|
||||
"card": {key: data["card"][key] for key in ("number", "slug", "title", "topic", "status", "version")},
|
||||
"front": {"goal": data["front_page_scope"]["content_tex"], "scope": data["front_page_scope"]["scope_content_tex"]},
|
||||
"viewpoints": [{key: value[key] for key in ("id", "label", "status", "subtitle", "reason") if key in value} for value in data["viewpoints"]],
|
||||
"artifact": {"source": "src/tasks/task01_static_allocation.c", "elf": "build/task01_static_allocation/prog.elf", "image": "build/task01_static_allocation/prog.bin"},
|
||||
"strategies": list(data["debug_strategies"].values()),
|
||||
"checkpoints": data["debug_checkpoints"]["items"],
|
||||
"sections": spec_sections,
|
||||
"learning": {
|
||||
"model_label": data["learning_effects"]["FC11.EN01"]["label"],
|
||||
"model": data["learning_effects"]["FC11.EN01"]["text"],
|
||||
"replay_label": data["learning_effects"]["FC11.EK01"]["label"],
|
||||
"replay": data["learning_effects"]["FC11.EK01"]["text"],
|
||||
"criterion": data["assessment_criteria"]["FC11.KW01"]["text"],
|
||||
"requirement": data["educational_requirements"]["FC11.WE01"]["text"],
|
||||
},
|
||||
"task": {
|
||||
"short_label": "two storage policies",
|
||||
"title": task["title"], "prompt_tex": task["prompt_tex"],
|
||||
"conclusion_tex": task["conclusion_tex"], "flow": task["flow"],
|
||||
},
|
||||
}
|
||||
|
||||
out = ROOT / "json/card_spec.json"
|
||||
out.write_text(json.dumps(spec, ensure_ascii=False, indent=2) + "\n")
|
||||
print(out)
|
||||
Executable
+52
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
script_dir=$(CDPATH= cd "$(dirname "$0")" && pwd)
|
||||
repo_root=$(CDPATH= cd "$script_dir/.." && pwd)
|
||||
tex_path="$repo_root/doc/generated/main.tex"
|
||||
|
||||
read_tex_command() {
|
||||
sed -n "s/^\\\\newcommand{\\\\$1}{\\([^}]*\\)}$/\\1/p" "$tex_path" | head -n 1
|
||||
}
|
||||
|
||||
publisher=mpabi
|
||||
area=inf
|
||||
series=$(read_tex_command CardSeries)
|
||||
number=$(read_tex_command CardNumber)
|
||||
slug=$(read_tex_command CardSlug)
|
||||
version=$(read_tex_command CardVersion)
|
||||
uuid=$(read_tex_command DocumentUUID)
|
||||
|
||||
if [ -z "$publisher" ] || [ -z "$area" ] || [ -z "$series" ] || \
|
||||
[ -z "$number" ] || [ -z "$slug" ] || [ -z "$version" ] || [ -z "$uuid" ]; then
|
||||
echo "missing card metadata in $tex_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
commit=${GITEA_SHA:-${GITHUB_SHA:-}}
|
||||
if [ -z "$commit" ]; then
|
||||
git_root=$(git -C "$repo_root" rev-parse --show-toplevel 2>/dev/null || true)
|
||||
if [ "$git_root" = "$repo_root" ]; then
|
||||
commit=$(git -C "$repo_root" rev-parse HEAD)
|
||||
else
|
||||
commit=local
|
||||
fi
|
||||
fi
|
||||
short_commit=$(printf '%s' "$commit" | cut -c1-12)
|
||||
meta="$repo_root/doc/build-meta.tex"
|
||||
trap 'rm -f "$meta"' EXIT HUP INT TERM
|
||||
printf '\\newcommand{\\BuildCommit}{%s}\n' "$short_commit" > "$meta"
|
||||
|
||||
out="$repo_root/doc/pdf"
|
||||
mkdir -p "$out"
|
||||
find "$out" -maxdepth 1 -type f -name '*.pdf' -delete
|
||||
(
|
||||
cd "$repo_root/doc/generated"
|
||||
latexmk -pdf -interaction=nonstopmode -halt-on-error -outdir="$out" main.tex
|
||||
)
|
||||
|
||||
target="$out/$publisher-$area-$series-$number-$slug-$version-$uuid.pdf"
|
||||
mv "$out/main.pdf" "$target"
|
||||
find "$out" -maxdepth 1 -type f \( -name '*.aux' -o -name '*.log' -o \
|
||||
-name '*.out' -o -name '*.fls' -o -name '*.fdb_latexmk' \) -delete
|
||||
printf '%s\n' "$target"
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
tb=${1:?usage: run_sim.sh /path/to/tb [build-dir]}
|
||||
build_dir=${2:-build}
|
||||
|
||||
echo "== task01_static_allocation =="
|
||||
"$tb" --bin "$build_dir/task01_static_allocation/prog.bin" --cycles 3000000 --cpuret
|
||||
Reference in New Issue
Block a user