From 2341487f777f86cc519f12c23ffbee5961a2801d Mon Sep 17 00:00:00 2001 From: Florian Zaruba Date: Thu, 27 Apr 2017 20:51:52 +0200 Subject: [PATCH] :white_check_mark: Add memory arbiter test --- .gitlab-ci.yml | 17 ++- Makefile | 8 +- fifo.sv | 7 +- include/mem_if.svh | 4 +- mem_arbiter.sv | 34 ++--- tb/core_tb.sv | 2 +- tb/fifo_tb.sv | 22 ++-- tb/mem_arbiter_tb.sv | 276 ++++++++++++++++++++++++++++++++++++++++ tb/models/scoreboard.sv | 2 +- tb/scoreboard_tb.sv | 2 +- tb/wave_mem_arbiter.do | 63 +++++++++ 11 files changed, 397 insertions(+), 40 deletions(-) create mode 100755 tb/mem_arbiter_tb.sv create mode 100644 tb/wave_mem_arbiter.do diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1485cb316..f5cd2be53 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,7 +2,7 @@ stages: - test - deploy -testALU: +test_alu: stage: test script: - make build @@ -13,7 +13,7 @@ testALU: paths: - covhtmlreport -testFIFO: +test_fifo: stage: test script: - make build @@ -24,7 +24,7 @@ testFIFO: paths: - covhtmlreport -testScoreboard: +test_scoreboard: stage: test script: - make build @@ -35,6 +35,17 @@ testScoreboard: paths: - covhtmlreport +test_mem_arbiter: + stage: test + script: + - make build + - make mem_arbiter + - vcover report mem_arbiter.ucdb + - vcover report -html mem_arbiter.ucdb + artifacts: + paths: + - covhtmlreport + pages: stage: deploy dependencies: diff --git a/Makefile b/Makefile index 0186019dc..513061148 100644 --- a/Makefile +++ b/Makefile @@ -7,10 +7,10 @@ library = work # Top level module to compile top_level = core_tb test_top_level = core_tb -tests = alu scoreboard fifo +tests = alu scoreboard fifo mem_arbiter # path to agents agents = tb/agents/fu_if/fu_if.sv tb/agents/fu_if/fu_if_agent_pkg.sv \ - include/ariane_pkg.svh tb/agents/scoreboard_if/scoreboard_if.sv tb/agents/scoreboard_if/scoreboard_if_agent_pkg.sv + include/ariane_pkg.svh tb/agents/scoreboard_if/scoreboard_if.sv tb/agents/scoreboard_if/scoreboard_if_agent_pkg.sv tb/common/eth_tb_pkg.sv interfaces = include/debug_if.svh include/mem_if.svh tb/agents/fifo_if/fifo_if.sv # this list contains the standalone components @@ -19,7 +19,7 @@ src = alu.sv tb/sequences/alu_sequence_pkg.sv tb/env/alu_env_pkg.sv tb/test/alu_ if_stage.sv compressed_decoder.sv fetch_fifo.sv commit_stage.sv prefetch_buffer.sv \ mmu.sv lsu.sv fifo.sv tb/fifo_tb.sv mem_arbiter.sv \ scoreboard.sv issue_read_operands.sv decoder.sv id_stage.sv util/cluster_clock_gating.sv regfile.sv ex_stage.sv ariane.sv \ - tb/core_tb.sv + tb/mem_arbiter_tb.sv tb/core_tb.sv # Search here for include files (e.g.: non-standalone components) incdir = ./includes @@ -55,7 +55,7 @@ sim: $(tests): # Optimize top level vopt${questa_version} ${compile_flag} $@_tb -o $@_tb_optimized +acc -check_synthesis - vsim${questa_version} -c +UVM_TESTNAME=$@_test -coverage -do "coverage save -onexit $@.ucdb; run -a; exit" $@_tb_optimized + vsim${questa_version} -c +UVM_TESTNAME=$@_test -coverage -do "coverage save -onexit $@.ucdb; run -a; quit -code [coverage attribute -name TESTSTATUS -concise]" $@_tb_optimized # User Verilator to lint the target lint: verilator ${src} --lint-only \ diff --git a/fifo.sv b/fifo.sv index 9d6ca43a3..7810465c4 100644 --- a/fifo.sv +++ b/fifo.sv @@ -27,6 +27,7 @@ module fifo #( // status flags output logic full_o, // queue is full output logic empty_o, // queue is empty + output logic single_element_o, // there is just a single element in the queue // as long as the queue is not full we can push new data input dtype data_i, // data to push into the queue input logic push_i, // data is valid and can be pushed to the queue @@ -41,9 +42,9 @@ module fifo #( // actual memory dtype [DEPTH-1:0] mem_n, mem_q; - assign full_o = (status_cnt_q == DEPTH - 1); - assign empty_o = (status_cnt_q == 0); - + assign full_o = (status_cnt_q == DEPTH); + assign empty_o = (status_cnt_q == 0); + assign single_element_o = (status_cnt_q == 1); // read and write queue logic always_comb begin : read_write_comb // default assignment diff --git a/include/mem_if.svh b/include/mem_if.svh index fdcd2540e..4cf363eba 100644 --- a/include/mem_if.svh +++ b/include/mem_if.svh @@ -40,12 +40,12 @@ interface mem_if #(parameter int ADDRESS_SIZE = 64, data_gnt, data_rvalid, data_rdata; endclocking - modport Master ( + modport master ( clocking mck, input address, data_wdata, data_req, data_we, data_be, output data_gnt, data_rvalid, data_rdata ); - modport Slave ( + modport slave ( clocking sck, output address, data_wdata, data_req, data_we, data_be, input data_gnt, data_rvalid, data_rdata diff --git a/mem_arbiter.sv b/mem_arbiter.sv index 4c4666aca..4c52e580c 100644 --- a/mem_arbiter.sv +++ b/mem_arbiter.sv @@ -51,30 +51,34 @@ module mem_arbiter #( logic push_i; logic [NR_PORTS-1:0] data_o; logic pop_i; - + logic single_element_o; // essentially wait for the queue to be empty - assign flush_ready_o = empty_o; + // or we just got a grant -> this means we issued a memory request in this cycle + // although we are ready if we only got a single element in the queue and an rvalid + // which means we are getting this element back in this cycle + assign flush_ready_o = (empty_o & ~(|data_gnt_i)) | (single_element_o & data_rvalid_i); fifo #( - .dtype ( logic [NR_PORTS-1:0] ), - .DEPTH ( 4 ) + .dtype ( logic [NR_PORTS-1:0] ), + .DEPTH ( 4 ) ) fifo_i ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .single_element_o ( single_element_o ), // the flush is accomplished implicitly by waiting for the flush ready signal - .flush_i ( 1'b0 ), - .full_o ( full_o ), - .empty_o ( empty_o ), - .data_i ( data_i ), - .push_i ( push_i ), - .data_o ( data_o ), - .pop_i ( pop_i ) + .flush_i ( 1'b0 ), + .full_o ( full_o ), + .empty_o ( empty_o ), + .data_i ( data_i ), + .push_i ( push_i ), + .data_o ( data_o ), + .pop_i ( pop_i ) ); // addressing read and full write always_comb begin : read_req_write // default assignment - data_req_o = data_req_i[0]; + data_req_o = 1'b0; address_o = address_i[0]; data_wdata_o = data_wdata_i[0]; data_be_o = data_be_i[0]; @@ -84,7 +88,7 @@ module mem_arbiter #( for (int i = 0; i < NR_PORTS; i++) data_gnt_o[i] = 1'b0; - // only go for a new request if we can wait for the valid + // only go for a new request if we can wait for the valid e.g.: we have enough space in the buffer if (~full_o) begin for (int i = 0; i < NR_PORTS; i++) begin if (data_req_i[i] == 1'b1) begin diff --git a/tb/core_tb.sv b/tb/core_tb.sv index 2865ed5a3..f97aaf564 100755 --- a/tb/core_tb.sv +++ b/tb/core_tb.sv @@ -98,7 +98,7 @@ module core_tb; fork imem_read: begin // instr_if.mck.data_rvalid <= 1'b0; - if (instr_if.mck.data_req) begin + if (instr_if.data_req) begin address.push_back(instr_if.mck.address); end end diff --git a/tb/fifo_tb.sv b/tb/fifo_tb.sv index 9fd404498..7e5f14138 100755 --- a/tb/fifo_tb.sv +++ b/tb/fifo_tb.sv @@ -32,14 +32,16 @@ module fifo_tb; #(.dtype ( logic[7:0] )) dut ( - .clk_i ( clk ), - .rst_ni ( rst_ni ), - .full_o ( fifo_if.full ), - .empty_o ( fifo_if.empty ), - .data_i ( fifo_if.wdata ), - .push_i ( fifo_if.push ), - .data_o ( fifo_if.rdata ), - .pop_i ( fifo_if.pop ) + .clk_i ( clk ), + .rst_ni ( rst_ni ), + .flush_i ( 1'b0 ), + .full_o ( fifo_if.full ), + .empty_o ( fifo_if.empty ), + .single_element_o ( ), + .data_i ( fifo_if.wdata ), + .push_i ( fifo_if.push ), + .data_o ( fifo_if.rdata ), + .pop_i ( fifo_if.pop ) ); initial begin @@ -56,7 +58,7 @@ module fifo_tb; // simulator stopper, this is suboptimal better go for coverage initial begin #10000000ns - $finish; + $stop; end program testbench (fifo_if fifo_if, output logic push, output logic pop); @@ -113,7 +115,7 @@ module fifo_tb; if (fifo_if.pck.pop) begin data = queue.pop_front(); // $display("Time: %t, Expected: %0h Got %0h", $time, data, fifo_if.pck.rdata); - assert(data == fifo_if.mck.rdata) else $error("Mismatch, Expected: %0h Got %0h", data, fifo_if.pck.rdata); + assert(data == fifo_if.pck.rdata) else $error("Mismatch, Expected: %0h Got %0h", data, fifo_if.pck.rdata); end end diff --git a/tb/mem_arbiter_tb.sv b/tb/mem_arbiter_tb.sv new file mode 100755 index 000000000..8e6af8b53 --- /dev/null +++ b/tb/mem_arbiter_tb.sv @@ -0,0 +1,276 @@ +// Author: Florian Zaruba, ETH Zurich +// Date: 24.4.2017 +// Description: Memory Arbiter Testbench +// +// +// Copyright (C) 2017 ETH Zurich, University of Bologna +// All rights reserved. +// +// This code is under development and not yet released to the public. +// Until it is released, the code is under the copyright of ETH Zurich and +// the University of Bologna, and may contain confidential and/or unpublished +// work. Any reuse/redistribution is strictly forbidden without written +// permission from ETH Zurich. +// +// Bug fixes and contributions will eventually be released under the +// SolderPad open hardware license in the context of the PULP platform +// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the +// University of Bologna. +// + +module mem_arbiter_tb; + logic rst_ni, clk; + logic end_test; + + logic flush_ready_o; + logic slave_data_gnt, slave_data_req; + + mem_if master[3](clk); + mem_if slave(clk); + + mem_arbiter dut ( + .clk_i ( clk ), + .rst_ni ( rst_ni ), + .flush_ready_o ( flush_ready_o ), + + .address_o ( slave.address ), + .data_wdata_o ( slave.data_wdata ), + .data_req_o ( slave.data_req ), + .data_we_o ( slave.data_we ), + .data_be_o ( slave.data_be ), + .data_gnt_i ( slave.data_gnt ), + .data_rvalid_i ( slave.data_rvalid ), + .data_rdata_i ( slave.data_rdata ), + + .address_i ( {master[2].address, master[1].address, master[0].address} ), + .data_wdata_i ( {master[2].data_wdata, master[1].data_wdata, master[0].data_wdata} ), + .data_req_i ( {master[2].data_req, master[1].data_req, master[0].data_req} ), + .data_we_i ( {master[2].data_we, master[1].data_we, master[0].data_we} ), + .data_be_i ( {master[2].data_be, master[1].data_be, master[0].data_be} ), + .data_gnt_o ( {master[2].data_gnt, master[1].data_gnt, master[0].data_gnt} ), + .data_rvalid_o ( {master[2].data_rvalid, master[1].data_rvalid, master[0].data_rvalid} ), + .data_rdata_o ( {master[2].data_rdata, master[1].data_rdata, master[0].data_rdata} ) + ); + + initial begin + clk = 1'b0; + rst_ni = 1'b0; + repeat(8) + #10ns clk = ~clk; + + rst_ni = 1'b1; + forever + #10ns clk = ~clk; + end + + initial begin + end_test = 1'b0; + + #1000000ns; + + end_test = 1'b1; + + end + + + assign slave.data_gnt = slave_data_gnt; + + program testbench (mem_if master[3], mem_if slave, input flush_ready); + // -------------- + // Slave Port + // -------------- + logic [7:0] imem [400]; + logic [63:0] address [$]; + logic [63:0] addr; + // grant process + initial begin + + forever begin + slave_data_gnt = 1'b0; + wait (slave.data_req); + // randomize grant delay + repeat ($urandom_range(0,4)) @(slave.mck); + slave_data_gnt = 1'b1; + wait (~slave.data_req); + end + end + // instruction memory + initial begin + // read mem file + $readmemh("add_test.v", imem, 64'b0); + $display("Read instruction memory file"); + slave.mck.data_rdata <= 32'b0; + // apply stimuli for instruction interface + forever begin + @(slave.mck) + slave.mck.data_rvalid <= 1'b0; + fork + imem_read: begin + @(slave.mck); + if (slave_data_gnt) begin + // $display("Time: %t, Pushing", $time); + address.push_back(slave.mck.address); + if (address.size() != 0) begin + // we an wait a couple of cycles here + repeat (3) @(slave.mck); + slave.mck.data_rvalid <= 1'b1; + addr = address.pop_front(); + slave.mck.data_rdata <= addr; + // { + // imem[$unsigned(addr + 3)], + // imem[$unsigned(addr + 2)], + // imem[$unsigned(addr + 1)], + // imem[$unsigned(addr + 0)] + // }; + // $display("Address: %0h, Data: %0h", addr, { + // imem[$unsigned(addr + 3)], + // imem[$unsigned(addr + 2)], + // imem[$unsigned(addr + 1)], + // imem[$unsigned(addr + 0)] + // }); + end else + slave.mck.data_rvalid <= 1'b0; + end + end + imem_write: begin + + end + join_none + end + end + + // -------------- + // Master Ports + // -------------- + // request a read + initial begin + // initial statements, sane resets + master[0].sck.data_req <= 1'b0; + master[0].sck.address <= 64'b0; + master[0].sck.data_be <= 7'b0; + master[0].sck.data_we <= 1'b0; + master[0].sck.data_wdata <= 64'b0; + + master[1].sck.data_req <= 1'b0; + master[1].sck.address <= 64'b0; + master[1].sck.data_be <= 7'b0; + master[1].sck.data_we <= 1'b0; + master[1].sck.data_wdata <= 64'b0; + + master[2].sck.data_req <= 1'b0; + master[2].sck.address <= 64'b0; + master[2].sck.data_be <= 7'b0; + master[2].sck.data_we <= 1'b0; + master[2].sck.data_wdata <= 64'b0; + + wait (rst_ni); + + forever begin + if (end_test) + break; + fork + master0: begin + // read request master 0 + do begin + master[0].sck.data_req <= 1'b1; + master[0].sck.address <= 64'b1; + master[0].sck.data_be <= 7'b1011; + master[0].sck.data_we <= 1'b0; + master[0].sck.data_wdata <= 64'b0; + + @(master[0].sck); + end while (~master[0].data_gnt); + master[0].sck.data_req <= 1'b0; + // randomize response + repeat ($urandom_range(0,10)) @(master[0].sck); + end + master1: begin + // read request master 1 + do begin + master[1].sck.data_req <= 1'b1; + master[1].sck.address <= 64'h8; + master[1].sck.data_be <= 7'b1011; + master[1].sck.data_we <= 1'b0; + master[1].sck.data_wdata <= 64'b0; + + @(master[1].sck); + end while (~master[1].data_gnt); + master[1].sck.data_req <= 1'b0; + // randomize response + repeat ($urandom_range(0,10)) @(master[1].sck); + end + master2: begin + // read request master 2 + do begin + master[2].sck.data_req <= 1'b1; + master[2].sck.address <= 64'hF; + master[2].sck.data_be <= 7'b1011; + master[2].sck.data_we <= 1'b0; + master[2].sck.data_wdata <= 64'b0; + + @(master[2].sck); + end while (~master[2].data_gnt); + master[2].sck.data_req <= 1'b0; + // randomize response + repeat ($urandom_range(0,10)) @(master[2].sck); + end + join_any + end + end + + // ---------------------- + // Monitor & Scoreboard + // ---------------------- + + initial begin + + automatic int slave_count = 0; + automatic int master_count [3] = {0, 0, 0}; + + fork + slave_scoreboard: begin + forever begin + @(slave.pck iff slave.pck.data_rvalid); + slave_count++; + end + end + + master0_scoreboard: begin + forever begin + @(master[0].pck iff master[0].pck.data_rvalid); + master_count[0]++; + assert (master[0].pck.data_rdata == 64'h1) else $error("Mismatch @%t, expected: %0h got: %0h", $time, 64'h1, master[0].pck.data_rdata); + end + end + + master1_scoreboard: begin + forever begin + @(master[1].pck iff master[1].pck.data_rvalid); + master_count[1]++; + assert (master[1].pck.data_rdata == 64'h8) else $error("Mismatch @%t, expected: %0h got: %0h", $time, 64'h8, master[1].pck.data_rdata); + end + end + + master2_scoreboard: begin + forever begin + @(master[2].pck iff master[2].pck.data_rvalid); + master_count[2]++; + assert (master[2].pck.data_rdata == 64'hF) else $error("Mismatch @%t, expected: %0h got: %0h", $time, 64'hF, master[2].pck.data_rdata); + end + end + + control_block: begin + // Wait here for the end of test signal + wait(end_test); + // wait an additional time to be sure that all results got propagated + wait(flush_ready); + // check the result count + assert(slave_count === master_count[0] + master_count[1] + master_count[2]) else $error("Mismatch in expected result count!"); + $stop; + end + join + end + endprogram + + testbench tb (master, slave, flush_ready_o); +endmodule \ No newline at end of file diff --git a/tb/models/scoreboard.sv b/tb/models/scoreboard.sv index 7afec75dd..3271d1852 100755 --- a/tb/models/scoreboard.sv +++ b/tb/models/scoreboard.sv @@ -8,7 +8,7 @@ class Scoreboard; static function scoreboard_entry randomize_scoreboard(); exception exception = { 63'h0, 63'h0, 1'b0}; scoreboard_entry entry = { - i, ALU, ADD, 5'h5, 5'h5, 5'h5, 64'h0, 1'b0, 1'b0, exception + i, ALU, ADD, 5'h5, 5'h5, 5'h5, 64'h0, 1'b0, 1'b0, exception, 1'b0 }; return entry; endfunction : randomize_scoreboard diff --git a/tb/scoreboard_tb.sv b/tb/scoreboard_tb.sv index 83abc7386..3c75069d5 100755 --- a/tb/scoreboard_tb.sv +++ b/tb/scoreboard_tb.sv @@ -60,7 +60,7 @@ module scoreboard_tb; // simulator stopper, this is suboptimal better go for coverage initial begin #10000000ns - $finish; + $stop; end program testbench (scoreboard_if scoreboard_if); diff --git a/tb/wave_mem_arbiter.do b/tb/wave_mem_arbiter.do new file mode 100644 index 000000000..fb3394628 --- /dev/null +++ b/tb/wave_mem_arbiter.do @@ -0,0 +1,63 @@ +onerror {resume} +quietly WaveActivateNextPane {} 0 +add wave -noupdate /mem_arbiter_tb/dut/clk_i +add wave -noupdate /mem_arbiter_tb/dut/rst_ni +add wave -noupdate /mem_arbiter_tb/dut/flush_ready_o +add wave -noupdate -expand -group Slave /mem_arbiter_tb/dut/address_o +add wave -noupdate -expand -group Slave /mem_arbiter_tb/dut/data_wdata_o +add wave -noupdate -expand -group Slave /mem_arbiter_tb/dut/data_req_o +add wave -noupdate -expand -group Slave /mem_arbiter_tb/dut/data_we_o +add wave -noupdate -expand -group Slave /mem_arbiter_tb/dut/data_be_o +add wave -noupdate -expand -group Slave /mem_arbiter_tb/dut/data_gnt_i +add wave -noupdate -expand -group Slave /mem_arbiter_tb/dut/data_rvalid_i +add wave -noupdate -expand -group Slave /mem_arbiter_tb/dut/data_rdata_i +add wave -noupdate -expand -group Master /mem_arbiter_tb/dut/address_i +add wave -noupdate -expand -group Master /mem_arbiter_tb/dut/data_wdata_i +add wave -noupdate -expand -group Master -expand /mem_arbiter_tb/dut/data_req_i +add wave -noupdate -expand -group Master /mem_arbiter_tb/dut/data_we_i +add wave -noupdate -expand -group Master /mem_arbiter_tb/dut/data_be_i +add wave -noupdate -expand -group Master -expand /mem_arbiter_tb/dut/data_gnt_o +add wave -noupdate -expand -group Master -expand /mem_arbiter_tb/dut/data_rvalid_o +add wave -noupdate -expand -group Master -expand /mem_arbiter_tb/dut/data_rdata_o +add wave -noupdate /mem_arbiter_tb/dut/full_o +add wave -noupdate /mem_arbiter_tb/dut/empty_o +add wave -noupdate /mem_arbiter_tb/dut/data_i +add wave -noupdate /mem_arbiter_tb/dut/push_i +add wave -noupdate /mem_arbiter_tb/dut/data_o +add wave -noupdate /mem_arbiter_tb/dut/pop_i +add wave -noupdate -expand -group FIFO /mem_arbiter_tb/dut/fifo_i/clk_i +add wave -noupdate -expand -group FIFO /mem_arbiter_tb/dut/fifo_i/rst_ni +add wave -noupdate -expand -group FIFO /mem_arbiter_tb/dut/fifo_i/flush_i +add wave -noupdate -expand -group FIFO /mem_arbiter_tb/dut/fifo_i/full_o +add wave -noupdate -expand -group FIFO /mem_arbiter_tb/dut/fifo_i/empty_o +add wave -noupdate -expand -group FIFO /mem_arbiter_tb/dut/fifo_i/single_element_o +add wave -noupdate -expand -group FIFO /mem_arbiter_tb/dut/fifo_i/data_i +add wave -noupdate -expand -group FIFO /mem_arbiter_tb/dut/fifo_i/push_i +add wave -noupdate -expand -group FIFO /mem_arbiter_tb/dut/fifo_i/data_o +add wave -noupdate -expand -group FIFO /mem_arbiter_tb/dut/fifo_i/pop_i +add wave -noupdate -expand -group FIFO /mem_arbiter_tb/dut/fifo_i/read_pointer_n +add wave -noupdate -expand -group FIFO /mem_arbiter_tb/dut/fifo_i/read_pointer_q +add wave -noupdate -expand -group FIFO /mem_arbiter_tb/dut/fifo_i/write_pointer_n +add wave -noupdate -expand -group FIFO /mem_arbiter_tb/dut/fifo_i/write_pointer_q +add wave -noupdate -expand -group FIFO /mem_arbiter_tb/dut/fifo_i/status_cnt_n +add wave -noupdate -expand -group FIFO /mem_arbiter_tb/dut/fifo_i/status_cnt_q +add wave -noupdate -expand -group FIFO /mem_arbiter_tb/dut/fifo_i/mem_n +add wave -noupdate -expand -group FIFO /mem_arbiter_tb/dut/fifo_i/mem_q +TreeUpdate [SetDefaultTree] +WaveRestoreCursors {{Cursor 1} {421 ns} 0} +quietly wave cursor active 1 +configure wave -namecolwidth 150 +configure wave -valuecolwidth 100 +configure wave -justifyvalue left +configure wave -signalnamewidth 1 +configure wave -snapdistance 10 +configure wave -datasetprefix 0 +configure wave -rowmargin 4 +configure wave -childrowmargin 2 +configure wave -gridoffset 0 +configure wave -gridperiod 1 +configure wave -griddelta 40 +configure wave -timeline 0 +configure wave -timelineunits ns +update +WaveRestoreZoom {0 ns} {1050 ns}