From b918c11733f5d7f99def7ea889c3e702cd3b4874 Mon Sep 17 00:00:00 2001 From: AdaMahdavi Date: Fri, 8 May 2026 19:15:51 -1000 Subject: [PATCH] datapath verified and debugged --- src/bus_arbiter.v | 22 +-- src/comp_queue.v | 38 ++--- src/control_top.v | 4 +- src/req_queue.v | 48 +++---- src/serializer.v | 3 +- test/control_top_tb.v | 169 +++++++++++++++++++++++ test/handshakes/aes_arb_tb.v | 245 +++++++++++++++++++++++++++++++++ test/handshakes/compq_ser_tb.v | 156 +++++++++++++++++++++ test/handshakes/more_top_tb.v | 171 +++++++++++++++++++++++ 9 files changed, 792 insertions(+), 64 deletions(-) create mode 100644 test/control_top_tb.v create mode 100644 test/handshakes/aes_arb_tb.v create mode 100644 test/handshakes/compq_ser_tb.v create mode 100644 test/handshakes/more_top_tb.v diff --git a/src/bus_arbiter.v b/src/bus_arbiter.v index bb74bbe..229ca28 100644 --- a/src/bus_arbiter.v +++ b/src/bus_arbiter.v @@ -86,16 +86,20 @@ always @(posedge clk or negedge rst_n) begin if (bus_ready) counter <= counter + 1; end else begin // Counter should always be 0 when curr_mode == 2'b00 - if (sha_req && aes_req) begin - curr_mode <= last_serviced ? 2'b10 : 2'b01; - end else if (aes_req) begin - curr_mode <= 2'b01; - end else if (sha_req) begin - curr_mode <= 2'b10; - end else begin - curr_mode <= 2'b00; - counter <= 2'b00; + + if (bus_ready) begin + if (sha_req && aes_req) begin + curr_mode <= last_serviced ? 2'b10 : 2'b01; + end else if (aes_req) begin + curr_mode <= 2'b01; + end else if (sha_req) begin + curr_mode <= 2'b10; + end else begin + curr_mode <= 2'b00; + counter <= 2'b00; + end end + end if (counter == 2'b11) begin diff --git a/src/comp_queue.v b/src/comp_queue.v index fdaf680..64cba17 100644 --- a/src/comp_queue.v +++ b/src/comp_queue.v @@ -25,23 +25,11 @@ module comp_queue #( // Internal FIFO reg [ADDRW-1:0] mem [0:QDEPTH-1]; - // Calculate index and count widths based on QDEPTH - // Handles edge cases like QDEPTH <= 1, force min width to be 1 - localparam integer IDXW = (QDEPTH <= 1) ? 1 : $clog2(QDEPTH); - localparam integer COUNTW = (QDEPTH <= 1) ? 1 : $clog2(QDEPTH + 1); - localparam [IDXW-1:0] LAST_IDX = IDXW'(QDEPTH - 1); - localparam [COUNTW-1:0] COUNT_MAX = QDEPTH; - - function [IDXW-1:0] increment_ptr; - input [IDXW-1:0] val; - increment_ptr = (val == LAST_IDX) ? {IDXW{1'b0}} : val + 1'b1; - endfunction - - reg [IDXW-1:0] head, tail; - reg [COUNTW-1:0] count; - - wire full = (count == COUNT_MAX); - wire empty = (count == {COUNTW{1'b0}}); // zero width + reg [$clog2(QDEPTH)-1:0] head, tail; + reg [$clog2(QDEPTH+1)-1:0] count; + + wire full = (count == QDEPTH); + wire empty = (count == 0); // Round-robin selector: 0 = AES, 1 = SHA reg rr_select; @@ -69,25 +57,23 @@ module comp_queue #( always @(posedge clk or negedge rst_n) begin if (!rst_n) begin - head <= {IDXW{1'b0}}; - tail <= {IDXW{1'b0}}; - count <= {COUNTW{1'b0}}; + head <= 0; + tail <= 0; + count <= 0; rr_select <= 0; valid_out <= 0; data_out <= 0; end else begin - // Debug output around failing cycle (simulation only) -`ifndef SYNTHESIS + // Debug output around failing cycle if ($time >= 2250000 && $time <= 2290000) begin $display("[%0t] rr_select=%0b | valid_in_aes=%b valid_in_sha=%b | aes_sel=%b sha_sel=%b | enq_valid=%b enq_ready=%b | count=%0d | tail=%0d", $time, rr_select, valid_in_aes, valid_in_sha, aes_sel, sha_sel, enq_valid, enq_ready, count, tail); end -`endif // Enqueue logic if (do_enq) begin mem[tail] <= enq_data; - tail <= increment_ptr(tail); + tail <= (tail + 1); end // Toggle round-robin if both inputs are valid — regardless of enqueue @@ -102,7 +88,7 @@ module comp_queue #( // Dequeue on handshake if (do_deq) begin - head <= increment_ptr(head); + head <= (head + 1); valid_out <= 0; end @@ -116,4 +102,4 @@ module comp_queue #( end end -endmodule +endmodule \ No newline at end of file diff --git a/src/control_top.v b/src/control_top.v index e9a5a5a..088cfcd 100644 --- a/src/control_top.v +++ b/src/control_top.v @@ -60,5 +60,7 @@ module control_top #( wire compq_ready_in; comp_queue #(.ADDRW(ADDRW), .QDEPTH(COMP_QDEPTH)) comp_queue_inst (.clk(clk), .rst_n(rst_n), .valid_in_aes(compq_aes_valid), .valid_in_sha(compq_sha_valid), .dest_addr_aes(compq_aes_data), .dest_addr_sha(compq_sha_data), .ready_out_aes(compq_ready_aes), .ready_out_sha(compq_ready_sha), .data_out(compq_data), .valid_out(compq_valid_out), .ready_in(compq_ready_in)); - serializer #(.ADDRW(ADDRW)) serializer_inst(.clk(clk), .rst_n(rst_n), .n_cs(cs_n), .spi_clk(spi_clk), .valid_in(compq_valid_out), .addr(compq_data), .miso(miso), .ready_out(compq_ready_in), .err()); +wire ser_ready_out; +assign compq_ready_in = ser_ready_out & ~cs_n; +serializer #(.ADDRW(ADDRW)) serializer_inst(.clk(clk), .rst_n(rst_n), .n_cs(cs_n), .spi_clk(spi_clk), .valid_in(compq_valid_out), .addr(compq_data), .miso(miso), .ready_out(ser_ready_out), .err()); endmodule diff --git a/src/req_queue.v b/src/req_queue.v index 512d29d..1af6ac4 100644 --- a/src/req_queue.v +++ b/src/req_queue.v @@ -22,6 +22,16 @@ module req_queue #( output wire ready_out_sha ); + function integer clog2; + input integer value; + integer v, i; + begin + v = value - 1; + for (i = 0; v > 0; i = i + 1) v = v >> 1; + clog2 = (value <= 1) ? 1 : i; + end + endfunction + // integer i; integer j, k; @@ -34,10 +44,7 @@ module req_queue #( localparam integer SHA_INSTRW = 2 * ADDRW + OPCODEW; localparam integer AES_INSTRW = 3 * ADDRW + OPCODEW; - // Calculate index and count widths based on QDEPTH - // Handles edge cases like QDEPTH <= 1, force min width to be 1 - localparam integer IDXW = (QDEPTH <= 1) ? 1 : $clog2(QDEPTH); - localparam [IDXW - 1:0] LAST_IDX = IDXW'(QDEPTH - 1); + localparam integer IDXW = clog2(QDEPTH); reg [AES_INSTRW - 1:0] aesQueue [QDEPTH - 1:0]; reg [IDXW - 1:0] aesReadIdx; @@ -70,41 +77,28 @@ module req_queue #( if (ready_out_aes) begin if (opcode[0] == 0) begin aesQueue[aesWriteIdx] <= {opcode, key_addr, text_addr, dest_addr}; - // compute next index without using % to avoid width warnings - if (aesWriteIdx == LAST_IDX) begin - if (aesReadIdx == {IDXW{1'b0}}) aesFull <= 1; - aesWriteIdx <= {IDXW{1'b0}}; - end else begin - if (aesReadIdx == aesWriteIdx + 1) aesFull <= 1; - aesWriteIdx <= aesWriteIdx + 1; + aesWriteIdx <= (aesWriteIdx + 1); + if (aesReadIdx == (aesWriteIdx + 1)) begin + aesFull <= 1; end end end if (ready_out_sha) begin if (opcode[0] == 1) begin shaQueue[shaWriteIdx] <= {opcode, text_addr, dest_addr}; - if (shaWriteIdx == LAST_IDX) begin - if (shaReadIdx == {IDXW{1'b0}}) shaFull <= 1; - shaWriteIdx <= {IDXW{1'b0}}; - end else begin - if (shaReadIdx == shaWriteIdx + 1) shaFull <= 1; - shaWriteIdx <= shaWriteIdx + 1; + shaWriteIdx <= (shaWriteIdx + 1); + if (shaReadIdx == (shaWriteIdx + 1)) begin + shaFull <= 1; end end end end - if (ready_in_aes) begin - if (aesReadIdx == LAST_IDX) - aesReadIdx <= {IDXW{1'b0}}; - else - aesReadIdx <= aesReadIdx + 1; + if (ready_in_aes && valid_out_aes) begin + aesReadIdx <= aesReadIdx + 1; aesFull <= 0; end - if (ready_in_sha) begin - if (shaReadIdx == LAST_IDX) - shaReadIdx <= {IDXW{1'b0}}; - else - shaReadIdx <= shaReadIdx + 1; + if (ready_in_sha && valid_out_sha) begin + shaReadIdx <= shaReadIdx + 1; shaFull <= 0; end end diff --git a/src/serializer.v b/src/serializer.v index e88be10..a72555c 100644 --- a/src/serializer.v +++ b/src/serializer.v @@ -81,7 +81,7 @@ module serializer #( err <= 1'b0; end else if (~valid_ncs) begin - if (valid_in && ready_out == 1 && negedgeSPI) begin + if (valid_in && ready_out) begin PISOreg <= {1'b1 , addr}; ready_out <= 0; cnt <= CNT_INIT; @@ -107,6 +107,7 @@ module serializer #( miso <= 1'b0; end else begin err <= 1'b0; + //ready_out <= 0; end end diff --git a/test/control_top_tb.v b/test/control_top_tb.v new file mode 100644 index 0000000..9871d2c --- /dev/null +++ b/test/control_top_tb.v @@ -0,0 +1,169 @@ +`timescale 1ns/1ps + +module control_top_tb; + + parameter ADDRW = 24; + parameter OPCODEW = 2; + parameter REQ_QDEPTH = 4; + parameter COMP_QDEPTH = 4; + + reg clk, rst_n; + reg spi_clk, mosi, cs_n; + reg [2:0] ack_in; + reg bus_ready; + reg ena; + + wire miso; + wire [7:0] data_bus_out; + wire data_bus_valid; + + + control_top #( + .ADDRW(ADDRW), .OPCODEW(OPCODEW), + .REQ_QDEPTH(REQ_QDEPTH), .COMP_QDEPTH(COMP_QDEPTH) + ) dut ( + .clk(clk), .rst_n(rst_n), .ena(ena), + .spi_clk(spi_clk), .mosi(mosi), .cs_n(cs_n), + .miso(miso), + .ack_in(ack_in), .bus_ready(bus_ready), + .data_bus_out(data_bus_out), .data_bus_valid(data_bus_valid) + ); + + always #5 clk = ~clk; + + initial begin + $dumpfile("control_top_tb.vcd"); + $dumpvars(0, control_top_tb); + end + + task spi_bit(input b); + begin + mosi = b; + spi_clk = 0; #25; + spi_clk = 1; #25; + end + endtask + + task spi_frame( + input v, input ed, input as, + input [ADDRW-1:0] key, + input [ADDRW-1:0] text, + input [ADDRW-1:0] dest + ); + integer i; + begin + cs_n = 0; + #500; + spi_bit(v); spi_bit(ed); spi_bit(as); + for (i = ADDRW-1; i >= 0; i = i-1) spi_bit(key[i]); + for (i = ADDRW-1; i >= 0; i = i-1) spi_bit(text[i]); + for (i = ADDRW-1; i >= 0; i = i-1) spi_bit(dest[i]); + spi_clk = 0; + #50; + cs_n = 1; + #100; + end + endtask + + task arb_transaction(input [2:0] ack_val); + begin + wait (dut.bus_arbiter_inst.curr_mode != 2'b00); + wait (dut.bus_arbiter_inst.counter == 2'b11); + @(posedge clk); + wait (dut.bus_arbiter_inst.curr_mode == 2'b00); + @(posedge clk); + ack_in = ack_val; + @(posedge clk); + ack_in = 0; + repeat (2) @(posedge clk); + end + endtask + + task drive_aes_full; + begin + arb_transaction(3'b100); + arb_transaction(3'b100); + arb_transaction(3'b110); + arb_transaction(3'b100); + end + endtask + + task do_reset; + begin + rst_n = 0; cs_n = 1; mosi = 0; spi_clk = 0; + ack_in = 0; bus_ready = 0; + #20 rst_n = 1; + repeat (4) @(posedge clk); + end + endtask + + initial begin + clk = 0; rst_n = 0; spi_clk = 0; mosi = 0; cs_n = 1; + ack_in = 0; bus_ready = 0; ena = 1; + #20 rst_n = 1; #50; + + // ============================================= + // TEST 1: single AES end-to-end + // ============================================= + spi_frame(1, 0, 0, 24'hAA_0001, 24'hBB_0001, 24'hCC_0001); + + bus_ready = 1; + drive_aes_full; + + repeat (10) @(posedge clk); + + // read SPI output + cs_n = 0; #500; + repeat (300) @(posedge clk); + cs_n = 1; #100; + + repeat (20) @(posedge clk); + + do_reset; + + // ============================================= + // TEST 2: back-to-back AES requests + // ============================================= + spi_frame(1, 0, 0, 24'hAA_0002, 24'hBB_0002, 24'hCC_0002); + spi_frame(1, 0, 0, 24'hAA_0003, 24'hBB_0003, 24'hCC_0003); + + bus_ready = 1; + drive_aes_full; + drive_aes_full; + + repeat (10) @(posedge clk); + + cs_n = 0; #500; + repeat (700) @(posedge clk); + cs_n = 1; #100; + + repeat (20) @(posedge clk); + + do_reset; + + // ============================================= + // TEST 3: pipeline stress — 4 AES requests + // ============================================= + spi_frame(1, 0, 0, 24'hAA_0010, 24'hBB_0010, 24'hCC_0010); + spi_frame(1, 0, 0, 24'hAA_0020, 24'hBB_0020, 24'hCC_0020); + spi_frame(1, 0, 0, 24'hAA_0030, 24'hBB_0030, 24'hCC_0030); + spi_frame(1, 0, 0, 24'hAA_0040, 24'hBB_0040, 24'hCC_0040); + + bus_ready = 1; + drive_aes_full; + drive_aes_full; + drive_aes_full; + drive_aes_full; + + repeat (10) @(posedge clk); + + cs_n = 0; #500; + repeat (1500) @(posedge clk); + cs_n = 1; #100; + + repeat (20) @(posedge clk); + + $finish; + end + +endmodule diff --git a/test/handshakes/aes_arb_tb.v b/test/handshakes/aes_arb_tb.v new file mode 100644 index 0000000..d1410b8 --- /dev/null +++ b/test/handshakes/aes_arb_tb.v @@ -0,0 +1,245 @@ +`timescale 1ns/1ps + +module aes_arb_tb; + + parameter ADDRW = 24; + + reg clk, rst_n; + reg bus_ready; + reg [2:0] ack_in; + reg req_valid; + reg [3*ADDRW+1:0] req_data; + reg compq_ready_in; + + wire arb_req; + wire [ADDRW+7:0] fsm_data_out; + wire aes_grant; + wire sha_grant; + wire [7:0] data_bus_out; + wire data_bus_valid; + wire ready_req_out; + wire valid_compq_out; + wire [ADDRW-1:0] compq_data_out; + + + aes_fsm #( + .ADDRW(ADDRW) + ) fsm_inst ( + .clk(clk), .rst_n(rst_n), + .req_valid(req_valid), .req_data(req_data), + .ready_req_out(ready_req_out), + .compq_ready_in(compq_ready_in), + .compq_data_out(compq_data_out), + .valid_compq_out(valid_compq_out), + .arb_req(arb_req), + .arb_grant(aes_grant), + .ack_in(ack_in), + .data_out(fsm_data_out) + ); + + bus_arbiter #( + .ADDRW(ADDRW) + ) arb_inst ( + .clk(clk), .rst_n(rst_n), + .aes_req(arb_req), + .sha_req(1'b0), + .aes_data_in(fsm_data_out), + .sha_data_in({(ADDRW+8){1'b0}}), + .bus_ready(bus_ready), + .data_out(data_bus_out), + .valid_out(data_bus_valid), + .aes_grant(aes_grant), + .sha_grant(sha_grant) + ); + + always #5 clk = ~clk; + + initial begin + $dumpfile("aes_arb_tb.vcd"); + $dumpvars(0, aes_arb_tb); + end + + task do_reset; + begin + rst_n = 0; bus_ready = 0; ack_in = 0; + req_valid = 0; req_data = 0; compq_ready_in = 0; + #20 rst_n = 1; + repeat (4) @(posedge clk); + end + endtask + + initial begin + clk = 0; rst_n = 0; + bus_ready = 0; ack_in = 0; + req_valid = 0; req_data = 0; + compq_ready_in = 0; + #20 rst_n = 1; #50; + + // ============================================= + // TEST 1: single AES op, grant level vs pulse + // ============================================= + // feed request + @(posedge clk); + req_valid = 1; + req_data = {2'b00, 24'hAA_0001, 24'hBB_0001, 24'hCC_0001}; + @(posedge clk); + req_valid = 0; + + // FSM enters RDKEY, arb_req goes high + // arbiter sets curr_mode = AES, aes_grant goes high + // FSM should move to WAIT_RDKEY + + // hold bus_ready high so arbiter counter advances + bus_ready = 1; + + // wait for arbiter to complete 4 beats + repeat (10) @(posedge clk); + + // send mem ack for RDKEY + ack_in = 3'b100; + @(posedge clk); + ack_in = 0; + + // FSM should go to RDTEXT + // KEY QUESTION: does aes_grant drop between transactions? + // if not, FSM skips straight to WAIT_RDTXT without new arbiter transaction + repeat (10) @(posedge clk); + + // send mem ack for RDTEXT + ack_in = 3'b100; + @(posedge clk); + ack_in = 0; + + repeat (10) @(posedge clk); + + // send accel ack for HASHOP + ack_in = 3'b110; + @(posedge clk); + ack_in = 0; + + repeat (10) @(posedge clk); + + // send mem ack for MEMWR + ack_in = 3'b100; + @(posedge clk); + ack_in = 0; + + repeat (5) @(posedge clk); + + // complete + compq_ready_in = 1; + @(posedge clk); + compq_ready_in = 0; + + repeat (10) @(posedge clk); + + do_reset; + + // ============================================= + // TEST 2: verify arbiter counter and grant per transaction + // ============================================= + // same as test 1 but watch counter and curr_mode carefully + // bus_ready pulsed instead of held to control beat timing + + @(posedge clk); + req_valid = 1; + req_data = {2'b00, 24'hAA_0002, 24'hBB_0002, 24'hCC_0002}; + @(posedge clk); + req_valid = 0; + + // RDKEY: manually clock 4 beats + repeat (4) begin + @(posedge clk); bus_ready = 1; + @(posedge clk); bus_ready = 0; + end + // arbiter should return to idle after counter wraps + repeat (3) @(posedge clk); + // send ack + ack_in = 3'b100; + @(posedge clk); + ack_in = 0; + + repeat (5) @(posedge clk); + + // RDTEXT: manually clock 4 beats + repeat (4) begin + @(posedge clk); bus_ready = 1; + @(posedge clk); bus_ready = 0; + end + repeat (3) @(posedge clk); + ack_in = 3'b100; + @(posedge clk); + ack_in = 0; + + repeat (5) @(posedge clk); + + // HASHOP: manually clock 4 beats + repeat (4) begin + @(posedge clk); bus_ready = 1; + @(posedge clk); bus_ready = 0; + end + repeat (3) @(posedge clk); + ack_in = 3'b110; + @(posedge clk); + ack_in = 0; + + repeat (5) @(posedge clk); + + // MEMWR: manually clock 4 beats + repeat (4) begin + @(posedge clk); bus_ready = 1; + @(posedge clk); bus_ready = 0; + end + repeat (3) @(posedge clk); + ack_in = 3'b100; + @(posedge clk); + ack_in = 0; + + repeat (5) @(posedge clk); + + compq_ready_in = 1; + @(posedge clk); + compq_ready_in = 0; + + repeat (10) @(posedge clk); + + do_reset; + + // ============================================= + // TEST 3: bus_ready held high, check for state skipping + // ============================================= + // bus_ready = 1 the whole time + // if grant stays high between transactions, FSM may skip states + + bus_ready = 1; + + @(posedge clk); + req_valid = 1; + req_data = {2'b00, 24'hAA_0003, 24'hBB_0003, 24'hCC_0003}; + @(posedge clk); + req_valid = 0; + + // just watch — does the FSM blow through all states + // before any ack arrives? + repeat (30) @(posedge clk); + + // now try sending acks and see what state the FSM is in + ack_in = 3'b100; @(posedge clk); ack_in = 0; + repeat (10) @(posedge clk); + ack_in = 3'b100; @(posedge clk); ack_in = 0; + repeat (10) @(posedge clk); + ack_in = 3'b110; @(posedge clk); ack_in = 0; + repeat (10) @(posedge clk); + ack_in = 3'b100; @(posedge clk); ack_in = 0; + repeat (10) @(posedge clk); + + compq_ready_in = 1; + @(posedge clk); + compq_ready_in = 0; + + repeat (10) @(posedge clk); + + $finish; + end + +endmodule \ No newline at end of file diff --git a/test/handshakes/compq_ser_tb.v b/test/handshakes/compq_ser_tb.v new file mode 100644 index 0000000..0ec4f63 --- /dev/null +++ b/test/handshakes/compq_ser_tb.v @@ -0,0 +1,156 @@ +`timescale 1ns/1ps + +module compq_ser_tb; + + parameter ADDRW = 24; + parameter QDEPTH = 32; + + reg clk, rst_n; + reg spi_clk, n_cs; + + reg valid_in_aes; + reg [ADDRW-1:0] dest_addr_aes; + + wire [ADDRW-1:0] compq_data_out; + wire compq_valid_out; + wire ser_ready_out; + wire compq_ready_aes; + wire compq_ready_sha; + + wire miso; + wire ser_err; + + + comp_queue #( + .ADDRW(ADDRW), .QDEPTH(QDEPTH) + ) compq_inst ( + .clk(clk), .rst_n(rst_n), + .valid_in_aes(valid_in_aes), .valid_in_sha(1'b0), + .dest_addr_aes(dest_addr_aes), .dest_addr_sha({ADDRW{1'b0}}), + .ready_out_aes(compq_ready_aes), .ready_out_sha(compq_ready_sha), + .data_out(compq_data_out), .valid_out(compq_valid_out), + .ready_in(ser_ready_out) + ); + + serializer #( + .ADDRW(ADDRW) + ) ser_inst ( + .clk(clk), .rst_n(rst_n), + .n_cs(n_cs), .spi_clk(spi_clk), + .valid_in(compq_valid_out), .addr(compq_data_out), + .miso(miso), .ready_out(ser_ready_out), .err(ser_err) + ); + + always #5 clk = ~clk; + always #25 spi_clk = ~spi_clk; + + initial begin + $dumpfile("compq_ser_tb.vcd"); + $dumpvars(0, compq_ser_tb); + end + + task do_reset; + begin + rst_n = 0; n_cs = 1; + valid_in_aes = 0; dest_addr_aes = 0; + #20 rst_n = 1; + repeat (4) @(posedge clk); + end + endtask + + initial begin + clk = 0; rst_n = 0; spi_clk = 0; n_cs = 1; + valid_in_aes = 0; dest_addr_aes = 0; + #20 rst_n = 1; #50; + + // ============================================= + // TEST 1: single entry, serializer loads immediately + // ============================================= + n_cs = 0; + #500; + + @(posedge clk); + valid_in_aes = 1; + dest_addr_aes = 24'hAA_0001; + @(posedge clk); + valid_in_aes = 0; + + repeat (300) @(posedge clk); + + do_reset; + + // ============================================= + // TEST 2: back-to-back entries, second loads after first + // ============================================= + n_cs = 0; + #500; + + @(posedge clk); + valid_in_aes = 1; + dest_addr_aes = 24'hBB_0001; + @(posedge clk); + dest_addr_aes = 24'hBB_0002; + @(posedge clk); + valid_in_aes = 0; + + repeat (700) @(posedge clk); + + do_reset; + + // ============================================= + // TEST 3: n_cs yanked mid-shift + // ============================================= + n_cs = 0; + #500; + + @(posedge clk); + valid_in_aes = 1; + dest_addr_aes = 24'hCC_0001; + @(posedge clk); + valid_in_aes = 0; + + repeat (80) @(posedge clk); + n_cs = 1; + + repeat (30) @(posedge clk); + + do_reset; + + // ============================================= + // TEST 4: three entries drain sequentially + // ============================================= + n_cs = 0; + #500; + + @(posedge clk); + valid_in_aes = 1; + dest_addr_aes = 24'hDD_0001; + @(posedge clk); + dest_addr_aes = 24'hDD_0002; + @(posedge clk); + dest_addr_aes = 24'hDD_0003; + @(posedge clk); + valid_in_aes = 0; + + repeat (1000) @(posedge clk); + + do_reset; + + // ============================================= + // TEST 5: data integrity — capture miso bits + // ============================================= + n_cs = 0; + #500; + + @(posedge clk); + valid_in_aes = 1; + dest_addr_aes = 24'hDE_ADBE; + @(posedge clk); + valid_in_aes = 0; + + repeat (400) @(posedge clk); + + $finish; + end + +endmodule \ No newline at end of file diff --git a/test/handshakes/more_top_tb.v b/test/handshakes/more_top_tb.v new file mode 100644 index 0000000..7af442a --- /dev/null +++ b/test/handshakes/more_top_tb.v @@ -0,0 +1,171 @@ +`timescale 1ns/1ps + +module more_top_tb; + + parameter ADDRW = 24; + parameter OPCODEW = 2; + parameter REQ_QDEPTH = 4; + parameter COMP_QDEPTH = 4; + + reg clk, rst_n; + reg spi_clk, mosi, cs_n; + reg [2:0] ack_in; + reg bus_ready; + reg ena; + + wire miso; + wire [7:0] data_bus_out; + wire data_bus_valid; + + control_top #( + .ADDRW(ADDRW), .OPCODEW(OPCODEW), + .REQ_QDEPTH(REQ_QDEPTH), .COMP_QDEPTH(COMP_QDEPTH) + ) dut ( + .clk(clk), .rst_n(rst_n), .ena(ena), + .spi_clk(spi_clk), .mosi(mosi), .cs_n(cs_n), + .miso(miso), + .ack_in(ack_in), .bus_ready(bus_ready), + .data_bus_out(data_bus_out), .data_bus_valid(data_bus_valid) + ); + + always #5 clk = ~clk; + + initial begin + $dumpfile("more_top_tb.vcd"); + $dumpvars(0, more_top_tb); + end + + // ========================================= + // SPI tasks + // ========================================= + task spi_bit(input b); + begin + mosi = b; + spi_clk = 0; #25; + spi_clk = 1; #25; + end + endtask + + task spi_frame( + input v, input ed, input as, + input [ADDRW-1:0] key, + input [ADDRW-1:0] text, + input [ADDRW-1:0] dest + ); + integer i; + begin + cs_n = 0; + #500; + spi_bit(v); spi_bit(ed); spi_bit(as); + for (i = ADDRW-1; i >= 0; i = i-1) spi_bit(key[i]); + for (i = ADDRW-1; i >= 0; i = i-1) spi_bit(text[i]); + for (i = ADDRW-1; i >= 0; i = i-1) spi_bit(dest[i]); + spi_clk = 0; + #50; + cs_n = 1; + #100; + end + endtask + + // ========================================= + // Wait for FSM to reach a specific WAIT_* state, + // then send the appropriate ack. + // FSM states: + // WAIT_RDKEY = 0010 -> ack 3'b100 (mem read done) + // WAIT_RDTXT = 0100 -> ack 3'b100 (mem read done) + // WAIT_HASHOP = 0110 -> ack 3'b110 (accel done) + // WAIT_MEMWR = 1000 -> ack 3'b100 (mem write done) + // ========================================= + task wait_and_ack(input [3:0] wait_state, input [2:0] ack_val); + begin + $display("[%0t] wait_and_ack: waiting for state=%0d", $time, wait_state); + wait (dut.aes_fsm_inst.state == wait_state); + $display("[%0t] wait_and_ack: state=%0d reached, sending ack=%b", $time, wait_state, ack_val); + @(posedge clk); + ack_in = ack_val; + @(posedge clk); + ack_in = 0; + repeat (3) @(posedge clk); + $display("[%0t] wait_and_ack: done, FSM now in state=%0d", $time, dut.aes_fsm_inst.state); + end + endtask + + task drive_aes_full; + begin + wait_and_ack(4'b0010, 3'b100); // WAIT_RDKEY -> mem ack + wait_and_ack(4'b0100, 3'b100); // WAIT_RDTXT -> mem ack + wait_and_ack(4'b0110, 3'b110); // WAIT_HASHOP -> accel ack + wait_and_ack(4'b1000, 3'b100); // WAIT_MEMWR -> mem ack + // COMPLETE -> READY happens in 1 cycle (compq not full), + // so by the time repeat(3) finishes, FSM is already back in READY. + end + endtask + + // ========================================= + // Main test + // ========================================= + initial begin + clk = 0; rst_n = 0; spi_clk = 0; mosi = 0; cs_n = 1; + ack_in = 0; bus_ready = 0; ena = 1; + #20 rst_n = 1; #50; + + // ---- PHASE 1: SPI input (bus_ready=0) ---- + // FSM may get granted by arbiter, but counter won't advance + // and no acks will come, so FSM stalls in WAIT states. + spi_frame(1, 0, 0, 24'hAA_0010, 24'hBB_0010, 24'hCC_0010); + spi_frame(1, 0, 0, 24'hAA_0020, 24'hBB_0020, 24'hCC_0020); + spi_frame(1, 0, 0, 24'hAA_0030, 24'hBB_0030, 24'hCC_0030); + spi_frame(1, 0, 0, 24'hAA_0040, 24'hBB_0040, 24'hCC_0040); + $display("[%0t] All 4 SPI frames sent", $time); + + // Let everything settle — cs_n is high, SPI done + repeat (10) @(posedge clk); + + // ---- PHASE 2: FSM processing (cs_n stays high) ---- + // bus_ready=1 lets arbiter counter advance. + // cs_n is high so compq_ready_in is gated — entries stay in comp_queue. + bus_ready = 1; + + drive_aes_full; + $display("[%0t] AES request 1 complete, comp_queue count=%0d", $time, dut.comp_queue_inst.count); + drive_aes_full; + $display("[%0t] AES request 2 complete, comp_queue count=%0d", $time, dut.comp_queue_inst.count); + drive_aes_full; + $display("[%0t] AES request 3 complete, comp_queue count=%0d", $time, dut.comp_queue_inst.count); + drive_aes_full; + $display("[%0t] AES request 4 complete, comp_queue count=%0d", $time, dut.comp_queue_inst.count); + + bus_ready = 0; + repeat (10) @(posedge clk); + + // ---- PHASE 3: readout (cs_n low, spi_clk toggling) ---- + $display("[%0t] Starting readout, comp_queue count=%0d", $time, dut.comp_queue_inst.count); + cs_n = 0; + #500; + // Toggle SPI clock so serializer can shift out data + // and valid_ncs can update through the debounce logic. + // 4 entries × 25 bits × 50ns/bit = 5000ns, add margin + repeat (200) begin + spi_clk = 0; #25; + spi_clk = 1; #25; + end + spi_clk = 0; + #50; + cs_n = 1; + #100; + + repeat (20) @(posedge clk); + + $display("[%0t] Done. comp_queue count=%0d", $time, dut.comp_queue_inst.count); + $finish; + end + + // Watchdog + initial begin + #10_000_000; + $display("WATCHDOG TIMEOUT"); + $finish; + end + +endmodule +