Kathryn ships 39 worked examples under test/model/, named tc1 … tc40
(the numbering has one gap — there is currently no tc36).
Every file is self-contained : it describes the model, provides a build()
function that emits Verilog, and carries its own cocotb simulation that
asserts the intended behaviour end-to-end through a real simulator. They are
the ground truth for how each feature behaves — when a tutorial page and your
intuition disagree, run the example.
Each entry links to the tutorial page that covers its feature.
# Example What it shows Tutorial 1 tc1_seq_simpleA single sequential block: x <= simple_val, then y <= x. Seq & Par 2 tc2_parParallel auto-sync: x and y assigned in two branches, both settle the same cycle. Seq & Par 15 tc15_reset_defaultReg reset values and wire defaults, fed as direct int literals (one wider than 64 bits). Reset & Defaults 26 tc26_int_operand_autowrapInt literals as operands/sources auto-wrap into width-matched vals; every overloaded operator exercised. Expressions 27 tc27_asm_resizeAssignment-source auto-resize: narrower sources zero-extend, wider sources drop MSBs (with warnings). Conversion & Resize 37 tc37_mem_blkmem_blk + mem_ele: gated clocked write port, same-cycle combinational read, and hierarchical preload of the memory array from the testbench.Signals
# Example What it shows Tutorial 3 tc3_sifSequential if: x <= 42 only when cond_in is high (condition sampled sequentially). Conditionals 4 tc4_cifCombinational if: same as tc3 but the condition costs zero extra cycles, so x latches earlier. Conditionals 5 tc5_zifZero-cycle if: wires driven combinationally; outputs reflect sources the very cycle conditions go high. Conditionals 6 tc6_cloopCounter loop: two explicit resets then x incremented 4 times via cloop(3). Loops 7 tc7_cwhileCombinational while: x incremented each iteration while x < 3, zero-cycle condition checks. Loops 8 tc8_swhileSequential while: like tc7 but the condition costs one extra clock per iteration. Loops 9 tc9_cdowhileDo-while: the body runs at least once, then repeats while x < 3. Loops 10 tc10_zswitchZero-cycle switch: an out wire driven combinationally from sel across three cases. State Machines 11 tc11_waitWait blocks: sywait (fixed cycles) and scwait (condition) delaying the next assignment. Waits 14 tc14_zif_chain_same_regzif/zelif/zelse chain writing one reg three values — lowers to a single clocked priority mux. Conditionals 22 tc22_pickPick block: a multi-way select that is NOT mutex-chained — every matching pif fires; pidef is the default. Pick 38 tc38_forever_scwaitThe “processor loop” pattern: an endless cwhile handshaking with the outside world through scwait, plus always-on bare comb logic beside the loop. Waits
# Example What it shows Tutorial 12 tc12_par_same_priorityThree parallel writes to the same reg at the SAME priority: stable sort keeps program order, the last wins. Write Priority 13 tc13_par_diff_prioritySame three writes at three DIFFERENT priorities: the highest-priority write wins even when declared first. Write Priority
# Example What it shows Tutorial 16 tc16_pip_zync_baseline3-stage pip/zync pipeline chained through shared arbiters, free-running with no stall, flush, or guard. Pip/Zync Basics 17 tc17_pip_zync_cond_stallA stage-2 conditional one-shot stall: a cif guard fires sywait(5) once, stalling the pipe 5 cycles. Stalls & Bubbles 18 tc18_pip_zync_stall_bubbleA one-cycle stall bubble: arb.stall() pulsed once punches a single bubble into the pipeline. Stalls & Bubbles 19 tc19_pip_zync_flush_deadlockA one-shot arb.flush() holds the arbiter’s reset high, jamming the pipe permanently at (5, 4, 4). Flush & Hazards 20 tc20_pip_zync_multi_assign_orderThe same register assigned twice in one clocked block — probes which write wins (last-write override). Multi-Assign Ordering 21 tc21_pip_zync_multi_assign_prioritytc20’s double write wrapped in priority(...) — the higher-priority (first-declared) write wins instead. Multi-Assign Ordering 23 tc23_zync_fanoutOne producer fires TWO consumer pipelines in lockstep via a multi-arb zync with mode="all". Fanout 24 tc24_zync_parity_fanoutConditional fan-out: per-bind conditions route the producer to a different consumer by parity (mode="any"). Fanout
The tc16 baseline: three stages chained through shared arbiters, free-running:
flowchart LR
S1["stage 1"] --> A1["arbiter"]
A1 --> S2["stage 2"]
S2 --> A2["arbiter"]
A2 --> S3["stage 3"]
And the tc23 fanout: one producer driving two consumer pipelines in lockstep
via a multi-arb zync with mode="all":
flowchart LR
P["producer"] --> Z["zync<br/>(mode=all)"]
Z --> C1["consumer pipeline 1"]
Z --> C2["consumer pipeline 2"]
Caution
The headers of tc16, tc23, and tc24 flag a known limitation: a one-cycle
handshake bootstrap race can keep downstream stages from arming, so these
testbenches encode the intended behaviour and stay red until the fix lands.
# Example What it shows Tutorial 25 tc25_karray_regfileA 4-entry Karray as a tiny register file: field-wise writes and whole-element {field: source} writes. Conversion & Resize 28 tc28_karray_to_karrayKarray-to-karray region copy: fields paired by name+width; non-matching fields skipped with a warning. Conversion & Resize 29 tc29_karray_dynamic_indexDynamic element reads: binary addresses at all four indices, plus a one-hot oh(...) select. Indexing 30 tc30_karray_reduceCallback-driven reduce: plain max, and a valid-gated max proving the callback consumes multiple fields. Reduce 31 tc31_karray_reduce_advancedAdvanced reduce: nested per-dimension select fns, request_index winner coordinates, and carried extras. Reduce 32 tc32_karray_dynamic_assignDynamic element writes: binary, one-hot, and whole-element map — each landing on exactly one element. Dynamic Writes 33 tc33_karray_cus_dynamic_assigncus_dynamic_assign: custom write-enables built from view.coord, including a multi-element range write.Dynamic Writes
The tc25 register file: a 4-entry Karray (Hardware Aggregator, Table and
Slot) addressed by index, each entry holding named fields:
flowchart TB
K["Karray regfile<br/>(4 entries)"] --> E0["entry 0"]
K --> E1["entry 1"]
K --> E2["entry 2"]
K --> E3["entry 3"]
E0 --> F["fields<br/>(per-field or whole-element write)"]
# Example What it shows Tutorial 34 tc34_lib_bitsAll kathryn.lib bit helpers — zext, sext, cat, replicate, or_reduce/and_reduce, mux — driven and checked in the same cycle. Bit Utilities 35 tc35_lib_bundle_handshakeBundle + Decoupled: consumer/producer IO marking, fire(), a connect_from relay chain, and backpressure.Bundles & Handshake
# Example What it shows Tutorial 39 tc39_hier_basicTop + one child: cross-module routing both ways, plus implicit clk/mrst forwarding into the child’s clocked flow. Modules 40 tc40_hier_deep_siblingDeep hierarchy Top { ChildA { GrandChild }, ChildB }: 2-level input/output chains, sibling routing through the LCA, and IoWire reuse. Modules
Each file registers itself into a shared cocotb pool; cocotb_pool.run_all()
drives every registered example (there is no per-test Makefile). Each build()
follows the standard pipeline — reset(), construct the module,
build_model(...), emit_verilog(...) — described in
Building & Emitting , and the
emitted Verilog for each example lands in test/.model_output/<tcname>/top.v.