Architecture
Duck Server is a heterogeneous streaming SoC. A 32-bit instruction stream arrives from a network fabric, is classified by the NoC router, executed by the CPU or NPU, and results are delivered wirelessly to remote displays. A MOESI coherency controller (v4) mediates shared memory between the CPU and NPU DMA.
The block diagram and module map below describe the logical dataflow, unchanged since v4. As of v5, the router/CPU/NPU/MOESI blocks are physically split across three dies (base die, CPU chiplet, NPU chiplet) connected by modeled UCIe links — see chiplets.md for the package-level view, link training, and flit channels.
System Block Diagram
┌────────────────────────────────────────────────────────────────┐
│ NETWORK DATA STREAM │
│ (32-bit instruction words, net_valid/ready) │
└───────────────────────────┬────────────────────────────────────┘
│
▼
┌──────────────────────────┐
│ NoC ROUTER │
│ classify by opcode[6:0] │
│ cpu_accept single-pulse │
└────────┬─────────────────┘
│ │
cpu │ npu │
▼ ▼
┌─────────────────────┐ ┌──────────────────────┐
│ RV32IM CPU │ │ NPU v3 │
│ 5-stage pipeline │ │ 16×16 MAC array │
│ MUL (3 cy) │ │ INT8 / INT16 │
│ DIV (34 cy) │ │ Ping-pong weights │
│ Forwarding + flush │ │ COMPUTE_ACC (tiling) │
└──────────┬──────────┘ └──────────────────────┘
│ cpu_read/write │ npu_dma_read/addr
│ cpu_stall ◄────────┐ │
▼ │ ▼
┌──────────────────────────────────────────────────┐
│ MOESI COHERENCY CONTROLLER │
│ 4-way set-assoc L2, 16 sets, 8-word lines (2KB) │
│ Write-back + write-allocate, round-robin LRU │
│ CPU master: read/write, stall on miss │
│ NPU DMA master: read-only, M→S flush on dirty │
└──────────────────────────┬───────────────────────┘
│ dram_read/write/ack
▼
┌─────────────────────┐
│ External DRAM │
│ (ack-based, 1-cy) │
└─────────────────────┘
│
NPU result bus ────────────┘
(result_valid/index/data)
▼
┌─────────────────────────────────┐
│ Wireless Peripheral Driver │
│ (stub — interface defined) │
└─────────────────────────────────┘
Module Map
| RTL File | Role | Version |
|---|---|---|
system_top.sv |
Chip boundary; package assembly of the three dies below | v5 |
base_die_top.sv |
Base/I-O die: NoC router, MOESI L2, external I/O | new in v5 |
cpu_chiplet_top.sv |
CPU chiplet: riscv_core + imem + halt detect |
new in v5 |
npu_chiplet_top.sv |
NPU chiplet: npu + result buffering |
new in v5 |
ucie_chan.sv |
Generic UCIe mainband channel: credit flow control + crossing latency | new in v5 |
ucie_link_ctrl.sv |
UCIe link training FSM (functional stub) | new in v5 |
duck_ucie_pkg.sv |
Flit field/opcode reference (docs only, not imported) | new in v5 |
duck_coherency_ctrl.sv |
MOESI L2 cache + 2-master coherency FSM (9 states) | new in v4 |
noc_router.sv |
Opcode classifier, cpu_accept single-pulse, NPU dispatch | v3 |
riscv_core.sv |
5-stage RV32IM pipeline, stall/flush, mem_stall input | v3+ |
alu.sv |
ADD/SUB/logic/SHL/SHR, MUL (3-cy), DIV/DIVU/REM/REMU (34-cy) | v2+ |
npu.sv |
16×16 MAC array, INT8/INT16, ping-pong banks, COMPUTE_ACC | v3 |
pe.sv |
Single weight-stationary INT8 MAC cell | v1 |
imm_decode.sv |
RV32 immediate extractor (I/S/B/U/J) | v1 |
register_file.sv |
32×32-bit regfile, x0 always 0 | v1 |
See chiplets.md for how the top four rows fit together.
Key Design Decisions
Why write-back L2 (not write-through)? Write-back reduces DRAM bandwidth by absorbing repeated writes to the same cache line — important for weight update loops and accumulation patterns in the NPU. The tradeoff is the need for a dirty bit and a flush path (now handled by the MOESI controller).
Why a 34-cycle iterative divider (not pipelined)? A pipelined divider needs 32 pipeline stages or a complex radix-4/SRT design. The iterative approach uses one adder-subtractor per cycle and takes 34 cycles. For the target workload (matrix indexing, not hot-path integer divide), this is the right area-vs-performance tradeoff.
Why MOESI (not MESI or MOSI)? The "O" (Owned) state is not implemented here — the 2-master system simplifies to M→S (flush dirty line, then serve NPU). The protocol is called MOESI in the codebase because it follows the MOESI class of coherency protocols at the conceptual level and is designed to extend to the full protocol when more masters are added.
Why a single-cycle dram_ack model? The simulation DRAM model acks one cycle after the request. Real DRAM latency would be 100–200 cycles; the 1-cycle model lets the cocotb testbench run fast while still exercising the full fill/flush/ack protocol. The coherency controller FSM is correct regardless of ack latency — it simply waits in its fill/flush states until ack arrives.
Comparison with Related Open-Source Projects
PicoRV32 (Claire Wolf)
What it is: Minimal RV32IMC soft-core in ~2500 lines of Verilog. Single-cycle or two-stage pipeline, no cache.
Similarities: Both target embedded/SoC use cases with a clean, readable RTL style. riscv_core.sv is in the same spirit — compact and simulation-friendly.
Differences: Duck Server adds a 5-stage pipeline with forwarding, a 4-way L2 cache, and a co-processor (NPU) tightly coupled to the same memory system. PicoRV32 has no data cache or co-processor port.
Reference: https://github.com/YosysHQ/picorv32
VexRiscv (SpinalHDL)
What it is: Fully configurable RV32 pipeline written in SpinalHDL (Scala DSL). Plugins for MMU, FPU, cache, etc.
Similarities: Both implement 5-stage pipelines with optional extensions.
Differences: VexRiscv is a framework where the pipeline is assembled from plugins; duck_server is monolithic and purpose-built for the streaming SoC use case. VexRiscv has no equivalent of the NPU DMA coherency interface.
Reference: https://github.com/SpinalHDL/VexRiscv
Ibex (lowRISC)
What it is: 2-stage RV32IMC pipeline, formally verified, used in OpenTitan.
Similarities: Both target real verification (cocotb testbench here, FPV in Ibex).
Differences: Ibex prioritises security (side-channel hardening, formal proofs). Duck Server prioritises throughput (wide NPU MAC array, ping-pong weight banks, coherent DMA). Ibex has no data cache or NPU.
Reference: https://github.com/lowRISC/ibex
NVDLA (NVIDIA)
What it is: Full deep learning accelerator RTL (conv engine, pooling, activation, normalise) open-sourced in 2017.
Similarities: Both use a weight-stationary MAC array and a multi-master coherency mechanism between the CPU and the accelerator.
Differences: NVDLA is orders of magnitude more complex (millions of gates, full AXI interconnect, compiler toolchain). Duck Server's NPU is a 16×16 MAC slice — the same conceptual building block NVDLA uses for its "atomic" unit, but without the surrounding convolution buffer, CBUF, and CDMA infrastructure.
Reference: https://github.com/nvdla/hw
Rocket Chip (UC Berkeley)
What it is: Full RISC-V SoC generator in Chisel. Includes L1/L2 caches, TileLink coherency fabric, and extensible co-processor interface (RoCC).
Similarities: The MOESI coherency controller in duck_server serves the same role as the Rocket L2 + TileLink coherency fabric — mediating between CPU and accelerator DMA.
Differences: Rocket's coherency is a full directory protocol over TileLink with multiple agents. Duck Server's is a 2-master simplified FSM optimised for readability and simulation speed. Rocket targets production silicon; duck_server targets teaching and rapid verification.
Reference: https://github.com/chipsalliance/rocket-chip
CVA6 / Ariane (ETH Zürich)
What it is: 6-stage RV64GC out-of-order core with MMU, 32KB L1I/D caches, AXI interface.
Similarities: Both implement write-back caches with dirty bits and writeback on replacement.
Differences: CVA6 targets high-performance (OOO, 64-bit, Linux-capable). Duck Server's CPU is a simple in-order 5-stage for area efficiency; the compute heavy-lifting is done by the NPU, not the CPU.
Reference: https://github.com/openhwgroup/cva6
Summary Table
| Project | Pipeline | Cache | Coherency | Co-processor | Focus |
|---|---|---|---|---|---|
| Duck Server | 5-stage RV32IM | 4-way L2 write-back 2KB | 2-master MOESI | 16×16 NPU INT8/INT16 | Streaming SoC, teaching |
| PicoRV32 | 1–2 stage RV32I(M)(C) | None | None | None | Minimal footprint |
| VexRiscv | 5-stage RV32(I/M/C/F) | Optional | None | None | Configurable framework |
| Ibex | 2-stage RV32IMC | None | None | None | Security, formal proof |
| Rocket Chip | 5-stage+ RV64GC | L1+L2 | TileLink MESI | RoCC interface | Production SoC |
| CVA6 | 6-stage OOO RV64GC | L1 I+D, optional L2 | AXI coherency | None | High-performance |
| NVDLA | N/A | CBUF scratchpad | AXI + SDP | Full DLA pipeline | DNN inference |