Skip to content

Structural RTL fallback: z-blocks

The HDBs express a broad range of control flow, but not everything expressible in Verilog. For the rest, Kathryn provides a structural RTL (Verilog-style) fallback built from z-prefixed blocks. These are zero-time, combinationally-resolved blocks: they describe the logic that drives a resource within a single evaluation, and they integrate with the higher-level HDBs so structural RTL and cycle-accurate control flow coexist in one model.

zif is the combinational conditional; zelif and zelse chain onto it (from cond/zif.h and cond/zelif.h):

#define zif(expr) for(auto kathrynBlock = new FlowBlockZIF(expr); kathrynBlock->doPrePostFunction(); kathrynBlock->step())
#define zelif(expr) for(auto kathrynBlock = new FlowBlockZELIF(expr); kathrynBlock->doPrePostFunction(); kathrynBlock->step())
#define zelse for(auto kathrynBlock = new FlowBlockZELIF(); kathrynBlock->doPrePostFunction(); kathrynBlock->step())

From autoSim simAutoTest11, zif/zelif/zelse nesting inside a cwhile:

cwhile(cond){
zif(a > b){
a <<= a + one;
}
zelif(a < b){
a <<= a + two;
zif(a > b){
b <<= b - one;
}zelse{
b <<= b - two;
}
}
}

ztate(sel) is the structural selector — a Verilog case — with zcase(v) branches and a zcasedef default (from state/ztate.h and state/zcase.h):

#define ztate(identState) for(auto kathrynBlock = new FlowBlockZtate(identState); kathrynBlock->doPrePostFunction(); kathrynBlock->step())
#define zcase(caseValue) for(auto kathrynBlock = new FlowBlockZCase(caseValue); kathrynBlock->doPrePostFunction(); kathrynBlock->step())
#define zcasedef for(auto kathrynBlock = new FlowBlockZCase(); kathrynBlock->doPrePostFunction(); kathrynBlock->step())

From autoSim simAutoTest62, a ztate selecting on switchVal, with a nested zif inside a case:

ztate(switchVal){
zcase(0b100){
a <<= 9;
b <<= 24;
zif(subCheck){ b <<= 48; }
}
zcase(0b001){
a <<= 10;
b <<= 107;
}
zcasedef{
b <<= 404;
}
}
flowchart TD
    subgraph K["Kathryn z-blocks"]
        ZIF["zif / zelif / zelse"]
        ZT["ztate / zcase / zcasedef"]
    end
    subgraph V["generated Verilog"]
        ALW["always block<br/>(single clock edge)"]
        IFE["if / else if / else"]
        CASE["case / default"]
    end
    ZIF --> IFE
    ZT --> CASE
    IFE --> ALW
    CASE --> ALW

The structural-RTL fallback is deliberately restricted. Three constraints favor modeling simplicity and simulation performance:

  1. A resource is sensitive to a single clock edge (registers, memory) or to all dependent sources (wires).
  2. Clock-edge-sensitive elements use non-blocking semantics.
  3. Advanced constructs — multiple clock domains, latches, and tri-state ports — are unsupported.

Use z-blocks when you need exact Verilog-style structure or zero cycle cost for a piece of combinational logic, and keep the cycle-accurate control flow in the HDBs around them.