Skip to content

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:

  1. Credit-based flow controltx_ready reflects 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 costs LATENCY cycles. Deliberate simplification, documented in chiplets.md.
  2. Fixed crossing latency — a LATENCY-deep shift-register delay line stands in for SerDes + interposer propagation, applied only forward (tx → rx).
  3. 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.