Duck Server
Duck Server is a heterogeneous streaming SoC that offloads heavy compute (scalar CPU, vector GPU/AIPU, and neural NPU workloads) from client devices, delivering results wirelessly to TVs, laptops, and phones.
Network Stream --> SDU --> CPU / GPU / NPU --> Wireless Driver --> Remote Displays
All processing units are implemented in synthesisable SystemVerilog (RV32IM ISA) and verified with cocotb + Icarus Verilog.
Status
| Unit | Status |
|---|---|
| RISC-V CPU (RV32IM) | ✅ Done — 5-stage pipeline, ALU, register file, L1 cache |
| Server Dispatch Unit | ✅ Done — 1-cycle opcode routing to CPU / GPU / NPU |
| NPU v2 (16×16 INT8, 0.512 TOPS) | ✅ Done — 256 parallel MACs, LOAD_W/LOAD_A/COMPUTE/COMPUTE_ACC, tiled GEMM |
| GPU / AIPU v1 | ✅ Done — 16-lane INT32 vector ALU (ADD/SUB/AND/XOR), see docs/gpu.md |
| Wireless Peripheral Driver | Interface defined — implementation pending |
Simulation: tb_server_dispatch_unit TESTS=15 PASS=15 FAIL=0 ✓ · tb_moesi_integration TESTS=3 PASS=1 FAIL=2 (BUG-006 open) — see docs/verification.md
Running Simulations
Requires Docker (pulls riscv_sim image with iverilog 11 + cocotb v2 pre-installed).
# ── Full test suite (default CPU program: cpu_peak_tests.asm) ──────────────
source scripts/run
# ── Run a specific assembly program ────────────────────────────────────────
TEST_TARGET=tb/extended_cpu_test.asm source scripts/run
TEST_TARGET=tb/example_program.asm source scripts/run
# ── Run a single named test ────────────────────────────────────────────────
COCOTB_TESTCASE=run_npu_gemv source scripts/run
COCOTB_TESTCASE=run_npu_tiled_gemm source scripts/run
COCOTB_TESTCASE=run_npu_random_signed source scripts/run
COCOTB_TESTCASE=run_riscv_program source scripts/run
# ── Run without Docker (if iverilog + cocotb installed locally) ─────────────
make results.xml
make results.xml ASM_TEST=tb/extended_cpu_test.asm
# ── View latest results ─────────────────────────────────────────────────────
cat output.log # full log (overwritten on every run)
grep -E "PASS|FAIL|TESTS=" output.log # summary line only
All 9 available tests:
| Test name | What it exercises |
|---|---|
run_npu_gemv |
Basic 16×16 GEMV — W[r][c]=r+1, A=1 |
run_npu_identity |
Identity weight matrix — O should equal A |
run_npu_all_ones |
W=1, A=1 — all lanes output 16 |
run_npu_random_signed |
Random signed INT8, Python scoreboard cross-check |
run_npu_negative |
Negative weights — signed arithmetic coverage |
run_npu_max_values |
W=127, A=127 — max INT8, no overflow |
run_npu_zero_act |
A=0 — all outputs must be 0 |
run_npu_tiled_gemm |
K=32 tiled GEMM via COMPUTE + COMPUTE_ACC |
run_riscv_program |
RV32IM CPU — MUL chain, 5 register assertions |
Repository Layout
rtl/
system_top.sv - Top-level wrapper; SRAM, L1 cache, module glue
server_dispatch_unit.sv - Opcode router (SDU): CPU / GPU / NPU dispatch
riscv_core.sv - RV32IM scalar CPU
alu.sv - Integer ALU
imm_decode.sv - Immediate field extractor
register_file.sv - 32 x 32-bit register file
npu.sv - 16x16 INT8 parallel MAC array (NPU)
pe.sv - Weight-stationary INT8 MAC processing element
tb/
tb_server_dispatch_unit.py - UVM-style cocotb testbench (9 tests: 8 NPU + 1 CPU)
cpu_peak_tests.asm - RV32IM peak-performance test program
extended_cpu_test.asm - Extended 104-instruction RV32IM test (all instruction groups)
example_program.asm - Example CPU program
docs/
index.md - Wiki home + full file tree
architecture.md - System block diagram, data flow, module map
cpu.md - 5-stage pipeline, ALU, forwarding, hazards
npu.md - NPU v2: parallel MACs, FSM, tiled GEMM
sdu.md - Server Dispatch Unit: routing, backpressure
system.md - system_top: SRAM, L1 cache, halt detection
verification.md - Testbench, 9 tests, how to run
reference.md - Opcode table, encodings, signal glossary
todo.md - Roadmap
Makefile - cocotb simulation build (SIM=icarus)
Dockerfile - Simulation container
scripts/run - One-shot test runner
NPU v2 at a Glance
The NPU v2 implements O[16] = W[16×16] × A[16] (INT8 GEMV) and tiled K>16 GEMM using a fully-parallel 256-MAC array dispatched via RISC-V custom-1 opcode 0x6B. All 256 multiplications are combinational — results are registered in a single clock cycle.
Peak throughput: 0.512 TOPS @ 1 GHz
GEMV usage:
1. 256 × LOAD_W (cmd=000) — fill W[16][16]
2. 16 × LOAD_A (cmd=001) — fill A[16]
3. 1 × COMPUTE (cmd=010) — acc = W·A (1 cycle)
4. Read 16 × INT32 results via result_valid/result_index/result_data
Tiled GEMM (K=32): repeat tiles with COMPUTE_ACC (cmd=011) to accumulate partial sums without losing prior results.
Instruction encoding (bits [9:7] = cmd, [13:10] = row, [17:14] = col, [25:18] = data INT8)
See docs/architecture.md for full instruction encoding and state machine details.
Related Open-Source Chips
| Project | What we borrowed |
|---|---|
| Ibex (lowRISC) | RV32IM pipeline structure |
| Rocket Chip (UC Berkeley) | SDU/RoCC-style heterogeneous dispatch |
| Gemmini (UC Berkeley) | Weight-stationary systolic array + custom instruction interface |
| VTA (Apache TVM) | LOAD_W / LOAD_A / COMPUTE instruction model |
| NVDLA (NVIDIA) | Full DLA reference for future NPU expansion |
| CVA6 (OpenHW) | Pipeline staging reference |