Add memory arbiter test

This commit is contained in:
Florian Zaruba 2017-04-27 20:51:52 +02:00
parent 639e8bea50
commit 2341487f77
11 changed files with 397 additions and 40 deletions

View file

@ -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:

View file

@ -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 \

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

276
tb/mem_arbiter_tb.sv Executable file
View file

@ -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

View file

@ -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

View file

@ -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);

63
tb/wave_mem_arbiter.do Normal file
View file

@ -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}