commit 80ab141c6154b79ce45b0cc433405c39b58408be
parent 5b971562baea51a4683195d482f0141717f93738
Author: Jack Sangdahl <jack@pngu.org>
Date: Wed, 19 Nov 2025 11:55:08 -0700
Q1. Simple Router
Diffstat:
4 files changed, 91 insertions(+), 0 deletions(-)
diff --git a/q1_router/Makefile b/q1_router/Makefile
@@ -0,0 +1,8 @@
+SIM = verilator
+TOPLEVEL_LANG = verilog
+VERILOG_SOURCES = $(PWD)/router.v
+COCOTB_TOPLEVEL = model
+COCOTB_TEST_MODULES = router_test
+WAVES = 1
+
+include $(shell cocotb-config --makefiles)/Makefile.sim
diff --git a/q1_router/readme.md b/q1_router/readme.md
@@ -0,0 +1,32 @@
+# Simple Router
+
+## Prompt
+Build a router circuit which forwards data from the input (`din`) to one of four outputs (`dout0`, `dout1`, `dout2`, `dout3`), specified by the address input (`addr`)
+
+The address is a two bit value whose decimal representation determines which output value to use. Append to `dout` the decimal representation of `addr` to get the output signal name `dout{address decimal value}`. For example, if `addr=b11` then the decimal representation of `addr` is 3, so the output signal name is `dout3`.
+
+The input has an enable signal `(din_en)`, which allows the input to be forwarded to an output when enabled. If an output is not currently being driven to, then it should be set to 0.
+
+## Input & Output Signals
+- `din`: Input data.
+- `din_en`: Enable signal for `din`. Forwards data from input to an output if 1, does not forward otherwise.
+- `addr`: Two bit destination address. For example `addr = b11 = 3` indicates `din` should be forwarded to output value 3 (`dout3`).
+- `dout0`: Output 0. Corresponds to `addr = b00`.
+- `dout1`: Output 1. Corresponds to `addr = b01`.
+- `dout2`: Output 2. Corresponds to `addr = b10`.
+- `dout3`: Output 3. Corresponds to `addr = b11`.
+
+## Example:
+- Data width: 32
+- Input 1
+ - `din = hbee`
+ - `din_en`: True
+ - `addr = h1`
+- Input 2
+ - `din = hbee`
+ - `din_en`: True
+ - `addr = h3`
+- Input 3
+ - `din = hbee`
+ - `din_en`: False
+ - `addr = h3`
diff --git a/q1_router/router.v b/q1_router/router.v
@@ -0,0 +1,18 @@
+module model #(
+ parameter DATA_WIDTH = 32
+) (
+ input [DATA_WIDTH-1:0] din,
+ input din_en,
+ input [1:0] addr,
+ output logic [DATA_WIDTH-1:0] dout0,
+ output logic [DATA_WIDTH-1:0] dout1,
+ output logic [DATA_WIDTH-1:0] dout2,
+ output logic [DATA_WIDTH-1:0] dout3
+);
+
+ assign dout0 = (din_en && addr == 0) ? din : 0;
+ assign dout1 = (din_en && addr == 1) ? din : 0;
+ assign dout2 = (din_en && addr == 2) ? din : 0;
+ assign dout3 = (din_en && addr == 3) ? din : 0;
+
+endmodule
diff --git a/q1_router/router_test.py b/q1_router/router_test.py
@@ -0,0 +1,33 @@
+import cocotb
+from cocotb.triggers import Timer
+
+
+@cocotb.test()
+async def test_router(dut):
+ def check_outputs(expected_values, step_desc):
+ dut._log.info(step_desc)
+ for i in range(4):
+ expected = expected_values[i]
+ actual = getattr(dut, f"dout{i}").value.to_unsigned()
+ assert actual == expected, (
+ f"dout{i}: got 0x{actual:x}, expected 0x{expected:x}"
+ )
+
+ dut.addr.value = 0x1
+ dut.din_en.value = 1
+ dut.din.value = 0xBEE
+ dout1 = [0x0, 0xBEE, 0x0, 0x0]
+ await Timer(1, unit="ns")
+ check_outputs(dout1, "state 1: addr = 1, din_en = 1, din = 0xBEE, dout1 = 0xBEE")
+
+ dut.addr.value = 0x3
+ dout2 = [0x0, 0x0, 0x0, 0xBEE]
+ await Timer(1, unit="ns")
+ check_outputs(dout2, "state 2: addr = 3, din_en = 1, din = 0xBEE, dout3 = 0xBEE")
+
+ dut.din_en.value = 0
+ dout3 = [0x0, 0x0, 0x0, 0x0]
+ await Timer(1, unit="ns")
+ check_outputs(
+ dout3, "state 3: addr = 3, din_en = 0, din = 0xBEE, dout{0,1,2,3} = 0"
+ )