rtl/ucie_chan.sv — Generic Unidirectional UCIe Mainband Channel
One instance = one "virtual channel" carrying a single flit type in a single direction between two chiplets. Five instances wire up the two physical links in system_top.sv — see duck_ucie_pkg.md for the flit field reference and chiplets.md for the full channel table.
Where it fits
Five instances live directly in system_top.sv (not inside base_die_top/cpu_chiplet_top/npu_chiplet_top): chan_instr_load, chan_mem_req, chan_mem_rsp (CPU link), chan_npu_disp, chan_npu_result (NPU link).
Parameters
| Parameter | Default | Meaning |
|---|---|---|
DEPTH |
4 | Receiver credit / buffer depth |
LATENCY |
2 | Die-to-die crossing latency, in cycles |
Ports
| Direction | Name | Width | Description |
|---|---|---|---|
| in | clk, rst_n, link_up |
1 each | Clock/reset; gates all activity when low |
| in | tx_valid |
1 | Sender has a flit to send |
| out | tx_ready |
1 | link_up && credit_cnt != 0 |
| in | tx_type/tx_addr/tx_data/tx_byte_en/tx_tag |
4/32/32/4/4 | Flit payload fields (see duck_ucie_pkg.sv) |
| out | rx_valid |
1 | Receive FIFO non-empty |
| in | rx_ready |
1 | Receiver ready to pop |
| out | rx_type/rx_addr/rx_data/rx_byte_en/rx_tag |
4/32/32/4/4 | Payload of the FIFO head |
Functionality
Models three things a real UCIe die-to-die link provides:
- Credit-based flow control —
tx_readyreflects remaining receiver buffer credit (DEPTH), not a per-cycle round-trip handshake. Credit is consumed at send time, returned when the far side pops. Credit return is idealized as instantaneous (no modeled latency on the return path) — only the forward data path costsLATENCYcycles. Deliberate simplification, documented in chiplets.md. - Fixed crossing latency — a
LATENCY-deep shift-register delay line stands in for SerDes + interposer propagation, applied only forward (tx → rx). - A receive-side buffer (depth
DEPTH) so the consumer can drain at its own pace once flits arrive.
link_up (from ucie_link_ctrl) gates everything: while low, tx_ready=0, rx_valid=0, and all internal state (credit counter, FIFO pointers, delay line) is held in reset.
Pointer width is a fixed 9 bits (covers DEPTH up to 511) rather than $clog2(DEPTH)-derived, matching this repo's documented avoidance of $clog2 under Icarus Verilog 11.
Example
chan_npu_disp (DEPTH=1, LATENCY=2) — the tightest channel in the design, since it mirrors the original single-outstanding npu_valid/npu_ready handshake:
cycle 0: base die: disp_tx_valid=1, credit_cnt=1 -> tx_ready=1 -> fire_tx, credit_cnt=0
cycle 0-1: flit shifts through the 2-deep delay line
cycle 2: dl_out_valid=1 -> pushed into the 1-deep receive FIFO -> rx_valid=1
cycle 2: NPU chiplet's disp_rx_ready (= npu's own npu_ready) pops it -> credit_cnt back to 1
If the NPU is still busy (not IDLE) when the flit arrives, rx_ready=0 and the FIFO holds the single buffered flit — tx_ready stays low (no credit) until it's popped, exactly reproducing the original same-die npu_valid/npu_ready backpressure, just with 2 cycles of added latency.
Related
- duck_ucie_pkg.md — flit opcode / channel-depth reference (docs-only package)
- ucie_link_ctrl.md — supplies the
link_upgate - chiplets.md — full channel table and deliberate-simplifications list