commit 9311e0af8002c3cf4f8e1c199c1a99d8e190febb
parent 80ab141c6154b79ce45b0cc433405c39b58408be
Author: Jack Sangdahl <jack@pngu.org>
Date: Wed, 19 Nov 2025 11:55:51 -0700
Q2. Second Largest
Diffstat:
4 files changed, 127 insertions(+), 0 deletions(-)
diff --git a/q2_second_largest/Makefile b/q2_second_largest/Makefile
@@ -0,0 +1,8 @@
+SIM = verilator
+TOPLEVEL_LANG = verilog
+VERILOG_SOURCES = $(PWD)/second_largest.v
+COCOTB_TOPLEVEL = model
+COCOTB_TEST_MODULES = second_largest_test
+WAVES = 1
+
+include $(shell cocotb-config --makefiles)/Makefile.sim
diff --git a/q2_second_largest/readme.md b/q2_second_largest/readme.md
@@ -0,0 +1,45 @@
+# Second Largest
+
+## Prompt
+Given a clocked sequence of unsigned values, output the second-largest value seen so far in the sequence. If only one value is seen, then the output (`dout`) should equal 0. Note that the values are treated as separate candidates for being the second largest value.
+
+When the reset-low signal (`resetn`) goes low, all previous values seen in the input sequence should no longer be considered for thee calculation of the second largest value, and the output `dout` should restart from 0 on the next cycle.
+
+## Input & Output Signals
+- `clk`: Clock signal.
+- `resetn`: Synchronous reset-low signal.
+- `din`: Input data sequence.
+- `dout`: Second-largest value seen so far.
+
+## Output signals during reset
+- `dout`: 0 when `resetn` is active.
+
+## Example
+- Data width: 32
+- Clock cycle 1
+ - `resetn`: `h0`
+ - `din`: `h2`
+- Clock cycle 2
+ - `resetn`: `h1`
+ - `din`: `h2`
+- Clock cycle 3
+ - `resetn`: `h1`
+ - `din`: `h6`
+- Clock cycle 4
+ - `resetn`: `h1`
+ - `din`: `h0`
+- Clock cycle 5
+ - `resetn`: `h1`
+ - `din`: `he`
+- Clock cycle 6
+ - `resetn`: `h1`
+ - `din`: `hc`
+- Clock cycle 7
+ - `resetn`: `h0`
+ - `din`: `h0`
+- Clock cycle 8
+ - `resetn`: `h1`
+ - `din`: `h1`
+- Clock cycle 9
+ - `resetn`: `h1`
+ - `din`: `h2`
diff --git a/q2_second_largest/second_largest.v b/q2_second_largest/second_largest.v
@@ -0,0 +1,24 @@
+module model #(
+ parameter DATA_WIDTH = 32
+) (
+ input clk,
+ input resetn,
+ input [DATA_WIDTH-1:0] din,
+ output logic [DATA_WIDTH-1:0] dout
+);
+
+ logic [DATA_WIDTH-1:0] largest, second_largest;
+ always @(posedge clk) begin
+ if (~resetn) begin
+ largest <= '0;
+ second_largest <= '0;
+ end else if ((din > largest && din > second_largest)) begin
+ largest <= din;
+ second_largest <= largest;
+ end else if (din > second_largest) begin
+ second_largest <= din;
+ end
+ end
+ assign dout = second_largest;
+
+endmodule
diff --git a/q2_second_largest/second_largest_test.py b/q2_second_largest/second_largest_test.py
@@ -0,0 +1,50 @@
+import cocotb
+from cocotb.clock import Clock
+from cocotb.triggers import RisingEdge, Timer
+
+
+@cocotb.test()
+async def test_second_largest(dut):
+ clock = Clock(dut.clk, 10, unit="ns")
+ cocotb.start_soon(clock.start())
+
+ dut.resetn.value = 0
+ await RisingEdge(dut.clk)
+ dut.resetn.value = 1
+ await RisingEdge(dut.clk)
+
+ din1 = [0x2, 0x6, 0x0, 0xE, 0xC]
+ dout1 = [0x0, 0x2, 0x2, 0x6, 0xC]
+
+ for i, (din_val, expected) in enumerate(zip(din1, dout1)):
+ dut.din.value = din_val
+ await RisingEdge(dut.clk)
+ await Timer(1, unit="ns")
+
+ actual = dut.dout.value.to_unsigned()
+ dut._log.info(
+ f"pre-reset step {i + 1}: din = 0x{din_val:x}, dout = 0x{actual:x}, expected = 0x{expected:x}"
+ )
+ assert actual == expected, (
+ f"mismatch at step {i + 1}: got 0x{actual:x}, expected 0x{expected:x}"
+ )
+
+ dut.resetn.value = 0
+ await RisingEdge(dut.clk)
+ dut.resetn.value = 1
+
+ din2 = [0x0, 0x1, 0x2]
+ dout2 = [0x0, 0x0, 0x1]
+
+ for i, (din_val, expected) in enumerate(zip(din2, dout2)):
+ dut.din.value = din_val
+ await RisingEdge(dut.clk)
+ await Timer(1, unit="ns")
+
+ actual = dut.dout.value.to_unsigned()
+ dut._log.info(
+ f"post-reset step {i + 1}: din = 0x{din_val:x}, dout = 0x{actual:x}, expected = 0x{expected:x}"
+ )
+ assert actual == expected, (
+ f"mismatch at step {i + 1}: got 0x{actual:x}, expected 0x{expected:x}"
+ )