Skip to content

Flow Control Introduction

Kathryn’s flow blocks are the concrete, in-Python form of the Hybrid Design Flow (HDF) — one of the three abstractions Kathryn is built around. HDF is Kathryn’s headline feature: it expresses cycle-accurate control through high-level syntax — no hand-built state machines, no manual synchronization logic — without giving cycle accuracy up. Every flow block below still compiles to an explicit, cycle-by-cycle state machine; you simply stop writing that state machine by hand.

Every flow block follows one of a few naming prefixes that tell you how the construct treats the clock:

PrefixMeaningExamples
c-condition checked combinationally — the check itself costs no extra cycle (the body still takes its own cycles)cif, cselif, cselse, cwhile, cdowhile
s-condition sampled sequentially — one extra clock is spent registering the checksif, swhile, scwait
z-zero-cycle — the whole construct is pure gating logic and consumes no cycles at allzif, zelif, zelse, zstate, zcase
p-pick family — gated multi-way selection, no chainingpick, pif, pidef

Two names sit slightly outside the pattern: cloop is a counter loop, and sywait is a fixed cycle wait rather than a condition check. pip and zync, the pipeline halves, follow their own convention — see Pipelines.

Once you know the prefix, you can usually guess a construct’s cycle cost before reading its page.

ConstructFamilyExampleCycle costWhat it does
seqskeletonwith seq(): ...1 cycle per direct statementRuns its contents in order, one step per clock edge
par / par_autoskeletonwith par(): ...max of its branches; exit auto-synchronizedRuns its contents concurrently, all on the same edge
par_no_syncskeletonwith par_no_sync(): ...branches independent; exit is the OR of branch exitsConcurrent, but doesn’t wait for the slowest branch
cif / cselif / cselsec-with cif(cond): ...0 extra (body still takes its own cycles)Combinational-condition if — the branch is chosen the cycle it’s reached
sif (+ cselif/cselse)s-with sif(cond): ...+1 cycle to sample the conditionSequential-condition if — trades a cycle for timing slack
zif / zelif / zelsez-with zif(cond): ...0 — pure gating, no stateZero-cycle priority mux on wires or a guarded register write
pick / pif / pidefp-with pick(): with pif(cond): ...same as whichever arm firesIndependent gated branches — no chaining, no priority order
zstate / zcasez-with zstate(sig): with zcase(v): ...0Zero-cycle switch on an encoded value — compiles to a Verilog case
cloop(n)counterwith cloop(n): ...n × body cyclesFixed, build-time-known iteration count via a hardware counter
cwhile(cond)c-with cwhile(cond): ...1 cycle per iterationCombinational-condition while loop
swhile(cond)s-with swhile(cond): ...2 cycles per iterationSequential-condition while loop
cdowhile(cond)c-with cdowhile(cond): ...1 cycle per iteration, runs at least onceCombinational do-while loop
sywait(n)cycle waitsywait(n)n cyclesLeaf block: stalls a fixed number of cycles
scwait(cond)s-scwait(cond)until cond reads highLeaf block: stalls until a condition is met
pippipelinewith pip(meta): ...1 cycle per grantGranter half of a handshaked pipeline stage; its body may hold any flow block — seq, par, zync, conditionals, loops (see Pipeline Basics)
zyncpipelinewith zync(meta): ...1 cycle per acknowledgeRequester half of a handshaked pipeline stage
flowchart TB
    HDF["Hybrid Design Flow"]
    HDF --> SK["Skeletons<br/>seq, par / par_auto, par_no_sync"]
    HDF --> C["c- combinational check<br/>cif, cwhile, cdowhile"]
    HDF --> S["s- sequential check<br/>sif, swhile, scwait"]
    HDF --> Z["z- zero-cycle gating<br/>zif, zstate / zcase"]
    HDF --> P["p- gated multi-way<br/>pick, pif, pidef"]
    HDF --> PIPE["pipeline halves<br/>pip, zync"]