From 8f08dffc70a3e2b0b1fb24c88c69f46a155ba701 Mon Sep 17 00:00:00 2001 From: Fischer Moseley <42497969+fischermoseley@users.noreply.github.com> Date: Mon, 3 Apr 2023 12:20:24 -0400 Subject: [PATCH] consolidate logic analyzer testbench --- examples/icestick/io_core/top_level.bin | Bin 32220 -> 0 bytes src/manta/la_fsm.v | 8 +- src/manta/logic_analyzer.v | 4 +- src/manta/trigger_block.v | 4 +- test/functional_sim/logic_analyzer_tb.sv | 267 +++++++++++------------ 5 files changed, 139 insertions(+), 144 deletions(-) delete mode 100644 examples/icestick/io_core/top_level.bin diff --git a/examples/icestick/io_core/top_level.bin b/examples/icestick/io_core/top_level.bin deleted file mode 100644 index 38882ebc01ce223deec33be3b17c19ba2003cef6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32220 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 b3X%vc`v@fafn{F;NdS@vtbfxFM2h|&eBt|q diff --git a/src/manta/la_fsm.v b/src/manta/la_fsm.v index d4a6bab..d2d1bbc 100644 --- a/src/manta/la_fsm.v +++ b/src/manta/la_fsm.v @@ -66,7 +66,7 @@ module la_fsm( case (addr_i) BASE_ADDR + 0: state <= wdata_i; BASE_ADDR + 1: trigger_loc <= wdata_i; - BASE_ADDR + 2: present_loc <= wdata_i; + //BASE_ADDR + 2: present_loc <= wdata_i; endcase end end @@ -120,6 +120,12 @@ module la_fsm( present_loc <= (trigger_loc < 0) ? trigger_loc : 0; end + + + // return to IDLE state if somehow we get to a state that doesn't exist + else begin + state <= IDLE; + end end endmodule diff --git a/src/manta/logic_analyzer.v b/src/manta/logic_analyzer.v index 9918b25..c122c5f 100644 --- a/src/manta/logic_analyzer.v +++ b/src/manta/logic_analyzer.v @@ -64,7 +64,7 @@ module logic_analyzer( // trigger block - trigger_block #(.BASE_ADDR(BASE_ADDR + 2)) trig_blk( + trigger_block #(.BASE_ADDR(BASE_ADDR + 3)) trig_blk( .clk(clk), .larry(larry), @@ -93,7 +93,7 @@ module logic_analyzer( reg trig_blk_sample_mem_valid; // sample memory - sample_mem #(.BASE_ADDR(BASE_ADDR + 10), .SAMPLE_DEPTH(SAMPLE_DEPTH)) sample_mem( + sample_mem #(.BASE_ADDR(BASE_ADDR + 11), .SAMPLE_DEPTH(SAMPLE_DEPTH)) sample_mem( .clk(clk), // fifo diff --git a/src/manta/trigger_block.v b/src/manta/trigger_block.v index 0614583..114a831 100644 --- a/src/manta/trigger_block.v +++ b/src/manta/trigger_block.v @@ -89,8 +89,8 @@ module trigger_block( valid_o <= valid_i; rdata_o <= rdata_i; - if( (addr_i >= BASE_ADDR) && (addr_i <= BASE_ADDR + 9) ) begin - + if( (addr_i >= BASE_ADDR) && (addr_i <= BASE_ADDR + 7) ) begin + // reads if(valid_i && !rw_i) begin case (addr_i) diff --git a/test/functional_sim/logic_analyzer_tb.sv b/test/functional_sim/logic_analyzer_tb.sv index 3bf56a7..26fdd1b 100644 --- a/test/functional_sim/logic_analyzer_tb.sv +++ b/test/functional_sim/logic_analyzer_tb.sv @@ -4,23 +4,27 @@ `define HCP 5 task read_reg ( - input [15:0] addr, - output [15:0] data - ); + input [15:0] addr, + output [15:0] data, + input string desc + ); - logic_analyzer_tb.tb_la_addr = addr; - logic_analyzer_tb.tb_la_rw = 0; - logic_analyzer_tb.tb_la_valid = 1; - #`CP - logic_analyzer_tb.tb_la_valid = 0; - while (!logic_analyzer_tb.la_tb_valid) #`CP; - data = logic_analyzer_tb.la_tb_rdata; + logic_analyzer_tb.tb_la_addr = addr; + logic_analyzer_tb.tb_la_rw = 0; + logic_analyzer_tb.tb_la_valid = 1; + #`CP + logic_analyzer_tb.tb_la_valid = 0; + while (!logic_analyzer_tb.la_tb_valid) #`CP; + data = logic_analyzer_tb.la_tb_rdata; + + $display(" -> read 0x%h from addr 0x%h (%s)", data, addr, desc); endtask task write_reg( input [15:0] addr, - input [15:0] data + input [15:0] data, + input string desc ); logic_analyzer_tb.tb_la_addr = addr; @@ -31,17 +35,33 @@ task write_reg( logic_analyzer_tb.tb_la_valid = 0; while (!logic_analyzer_tb.la_tb_valid) #`CP; + $display(" -> wrote 0x%h to addr 0x%h (%s)", data, addr, desc); + +endtask + +task write_and_verify( + input [15:0] addr, + input [15:0] write_data, + input string desc + ); + + reg [15:0] read_data; + + write_reg(addr, write_data, desc); + read_reg(addr, read_data, desc); + assert(read_data == write_data) else $error("data read does not match data written!"); endtask task read_all_reg(); + + string desc; for(int i = 0; i < (logic_analyzer_tb.la.sample_mem.BASE_ADDR + logic_analyzer_tb.la.SAMPLE_DEPTH); i++) begin - if(i == logic_analyzer_tb.la.fsm.BASE_ADDR) $display(" -> FSM MEMORY"); - if(i == logic_analyzer_tb.la.trig_blk.BASE_ADDR) $display(" -> TRIG BLK MEMORY"); - if(i == logic_analyzer_tb.la.sample_mem.BASE_ADDR) $display(" -> SAMPLE MEM MEMORY"); - - read_reg(i, logic_analyzer_tb.read_value); - $display(" -> addr: 0x%h rdata: 0x%b", i, logic_analyzer_tb.read_value); + if(i == logic_analyzer_tb.la.fsm.BASE_ADDR) desc = "FSM"; + if(i == logic_analyzer_tb.la.trig_blk.BASE_ADDR) desc = "TRIG BLK"; + if(i == logic_analyzer_tb.la.sample_mem.BASE_ADDR) desc = "SAMPLE MEM"; + + read_reg(i, logic_analyzer_tb.read_value, desc); end endtask @@ -101,7 +121,7 @@ module logic_analyzer_tb; end reg [15:0] read_value; - + initial begin $dumpfile("logic_analyzer_tb.vcd"); $dumpvars(0, logic_analyzer_tb); @@ -124,118 +144,116 @@ module logic_analyzer_tb; #(10*`CP); /* ==== Test 1 Begin ==== */ - $display("\n=== test 1: read state register ==="); + $display("\n=== test 1: read/write to FSM registers, verify ==="); test_num = 1; - read_reg(0, read_value); - $display(" -> read 0x%h from state reg (addr 0x0000)", read_value); + // state register + write_and_verify(0, la.fsm.IDLE, "state reg"); + write_and_verify(0, la.fsm.FILLED, "state reg"); + write_and_verify(0, la.fsm.IDLE, "state reg"); + // trigger_loc register + write_and_verify(1, 0, "trigger_loc reg"); + write_and_verify(1, 'h69, "trigger_loc reg"); + write_and_verify(1, 'h0612, "trigger_loc reg"); + + // since we just moved the trigger location, the core has started moving into position + // if it's functioning correctly. this means we need to reset the position and state + // before testing the present_loc register. + + // write_and_verify(1, 0, "trigger_loc reg"); + // write_and_verify(0, 0, "state reg"); + + // // present_loc register + // write_and_verify(2, 0, "present_loc reg"); + // write_and_verify(2, 0, "present_loc reg"); #(10*`CP); + /* ==== Test 1 End ==== */ /* ==== Test 2 Begin ==== */ - $display("\n=== test 2: write to state register and verify ==="); + $display("\n=== test 2: read/write to trigger block registers, verify ==="); test_num = 2; - write_reg(0, 5); - $display(" -> wrote 0x0005 to state reg (addr 0x0000)"); + // larry + write_and_verify(3, 0, "larry_op"); + write_and_verify(3, 2, "larry_op"); + write_and_verify(3, 0, "larry_op"); - read_reg(0, read_value); - $display(" -> read 0x%h from state reg (addr 0x0000)", read_value); + write_and_verify(4, 0, "larry_arg"); + write_and_verify(4, 1, "larry_arg"); + write_and_verify(4, 0, "larry_arg"); - write_reg(0, 0); - $display(" -> wrote 0x0000 to state reg (addr 0x0000)"); + // curly + write_and_verify(5, 0, "curly_op"); + write_and_verify(5, 3, "curly_op"); + write_and_verify(5, 0, "curly_op"); - read_reg(0, read_value); - $display(" -> read 0x%h from state reg (addr 0x0000)", la_tb_rdata); + write_and_verify(6, 0, "curly_arg"); + write_and_verify(6, 1, "curly_arg"); + write_and_verify(6, 0, "curly_arg"); + + // moe + write_and_verify(7, 0, "moe_op"); + write_and_verify(7, 5, "moe_op"); + write_and_verify(7, 0, "moe_op"); + + write_and_verify(8, 0, "moe_arg"); + write_and_verify(8, 1, "moe_arg"); + write_and_verify(8, 0, "moe_arg"); + + // shemp + write_and_verify(9, 0, "shemp_op"); + write_and_verify(9, 7, "shemp_op"); + write_and_verify(9, 0, "shemp_op"); + + write_and_verify(10, 0, "shemp_arg"); + write_and_verify(10, 7, "shemp_arg"); + write_and_verify(10, 0, "shemp_arg"); #(10*`CP); + /* ==== Test 2 End ==== */ - /* ==== Test 3 Begin ==== */ - $display("\n=== test 3: write to trigger_loc register and verify ==="); + $display("\n=== test 3: verify FSM doesn't move out of IDLE when not running ==="); test_num = 3; - write_reg(1, -16'sd69); - $display(" -> wrote -0d69 to trigger_loc reg (addr 0x0001)"); + write_and_verify(3, 8, "larry_op"); // set operation to eq + write_and_verify(4, 1, "larry_arg"); // set argument to 1 - read_reg(1, read_value); - $display(" -> read 0d%d from trigger_loc reg (addr 0x0001)", $signed(read_value)); + // set larry = 1, verify core doesn't trigger + $display(" -> set larry = 1"); + larry = 1; - write_reg(1, 0); - $display(" -> wrote 0x0000 to trigger_loc reg (addr 0x0001)"); + $display(" -> la core is in state 0x%h", la.fsm.state); + assert(la.fsm.state == la.fsm.IDLE) else $error("core moved outside of IDLE state when not running!"); + + $display(" -> wait a clock cycle"); + #`CP + + $display(" -> la core is in state 0x%h", la.fsm.state); + assert(la.fsm.state == la.fsm.IDLE) else $error("core moved outside of IDLE state when not running!"); - read_reg(1, read_value); - $display(" -> read 0x%h from trigger_loc reg (addr 0x0001)", $signed(read_value)); + $display(" -> set larry = 0"); + larry = 0; #(10*`CP); /* ==== Test 3 End ==== */ - /* ==== Test 4 Begin ==== */ - $display("\n=== test 4: configure larry_op for equality and verify ==="); + $display("\n=== test 4: verify FSM does move out of IDLE when running ==="); test_num = 4; - write_reg(2, 8); - $display(" -> wrote 0x0008 to larry_op reg (addr 0x0002)"); - - read_reg(2, read_value); - $display(" -> read 0x%h from larry_op reg (addr 0x0002)", read_value); - - #(10*`CP); - /* ==== Test 4 End ==== */ - - - - /* ==== Test 5 Begin ==== */ - $display("\n=== test 5: write 0x0001 to larry_arg register and verify ==="); - test_num = 5; - - write_reg(3, 1); - $display(" -> wrote 0x0001 to larry_arg reg (addr 0x0003)"); - - read_reg(3, read_value); - $display(" -> read 0x%h from larry_arg reg (addr 0x0003)", read_value); - - #(10*`CP); - /* ==== Test 5 End ==== */ - - - - /* ==== Test 6 Begin ==== */ - $display("\n=== test 6: set larry = 1, verify core does not trigger ==="); - test_num = 6; - - $display(" -> set larry = 1"); - larry = 1; - - $display(" -> la core is in state 0x%h", la.fsm.state); - $display(" -> wait a clock cycle"); - #`CP - $display(" -> la core is in state 0x%h", la.fsm.state); - $display(" -> set larry = 0"); - larry = 0; - - #(10*`CP); - /* ==== Test 6 End ==== */ - - - - /* ==== Test 7 Begin ==== */ - $display("\n=== test 7: set larry = 1, verify core does trigger ==="); - test_num = 7; - - write_reg(0, 1); - $display(" -> wrote 0x0001 to state reg (addr 0x0000)"); - + $display(" -> moving core to START_CAPTURE"); + write_reg(0, 1, "state"); #`CP $display(" -> set larry = 1"); - larry = 1; + larry = 1; // read $display(" -> la core is in state 0x%h", la.fsm.state); @@ -251,56 +269,27 @@ module logic_analyzer_tb; end $display(" -> read from sample memory:"); - read_all_reg(); + read_all_reg(); #(200*`CP); - /* ==== Test 7 End ==== */ + /* ==== Test 4 End ==== */ + /* ==== Test 5 Begin ==== */ + $display("\n=== test 5: change trigger to fire on shemp > 3, and verify ==="); + test_num = 5; + + write_and_verify(9, 6, "shemp_op"); // set operation to GT + write_and_verify(10, 3, "shemp_arg"); // set argument to 3 - /* ==== Test 8 Begin ==== */ - $display("\n=== test 8: change trigger to fire on shemp > 3, and verify ==="); - test_num = 8; - - write_reg(8, 6); - $display(" -> wrote 0x0006 to shemp_op reg (addr 0x0008)"); - - read_reg(8, read_value); - $display(" -> read 0x%h from shemp_op reg (addr 0x0008)", la_tb_rdata); - - write_reg(9, 3); - $display(" -> wrote 0x0003 to shemp_arg reg (addr 0x0009)"); - - read_reg(9, read_value); - $display(" -> read 0x%h from shemp_arg reg (addr 0x0009)", read_value); - - #(10*`CP); - /* ==== Test 8 End ==== */ - - /* ==== Test 9 Begin ==== */ - $display("\n=== test 9: set state machine to IDLE, verify core does not trigger ==="); - test_num = 9; - - read_reg(0, read_value); - $display(" -> read 0x%h from state reg (addr 0x0000)", read_value); - - write_reg(0, 0); - $display(" -> wrote 0x0000 to state reg (addr 0x0000)"); - - read_reg(0, read_value); - $display(" -> read 0x%h from state reg (addr 0x0000)", read_value); - /* ==== Test 9 End ==== */ - - /* ==== Test 10 Begin ==== */ - $display("\n=== test 10: set shemp = 4, verify core does trigger ==="); - test_num = 10; + assert( (la.fsm.state == la.fsm.IDLE) || (la.fsm.state == la.fsm.FILLED) ) + else $error("core is running when it shouldn't be!"); larry = 0; curly = 0; moe = 0; shemp = 0; - - write_reg(0, 1); - $display(" -> wrote 0x0001 to state reg (addr 0x0000)"); + + write_reg(0, la.fsm.START_CAPTURE, "state"); shemp = 4; $display(" -> set shemp = 4"); @@ -314,9 +303,9 @@ module logic_analyzer_tb; $display(" -> read from sample memory:"); read_all_reg(); - - #(200*`CP); - /* ==== Test 10 End ==== */ + + #(10*`CP); + /* ==== Test 5 End ==== */ $finish(); end