mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-24 22:27:10 -04:00
✅ Added UVM testbench for mem arbiter
This commit is contained in:
parent
1b343e7b11
commit
79d7859d65
13 changed files with 221 additions and 77 deletions
2
Makefile
2
Makefile
|
@ -71,7 +71,7 @@ $(tests):
|
||||||
# Optimize top level
|
# Optimize top level
|
||||||
vopt${questa_version} ${compile_flag} $@_tb -o $@_tb_optimized +acc -check_synthesis
|
vopt${questa_version} ${compile_flag} $@_tb -o $@_tb_optimized +acc -check_synthesis
|
||||||
# vsim${questa_version} $@_tb_optimized
|
# vsim${questa_version} $@_tb_optimized
|
||||||
# vsim${questa_version} +UVM_TESTNAME=$@_test -coverage -do "coverage save -onexit $@.ucdb; run -a; quit -code [coverage attribute -name TESTSTATUS -concise]" $@_tb_optimized
|
vsim${questa_version} +UVM_TESTNAME=$@_test -c -coverage -classdebug -do "coverage save -onexit $@.ucdb; run -a; quit -code [coverage attribute -name TESTSTATUS -concise]" $@_tb_optimized
|
||||||
# User Verilator to lint the target
|
# User Verilator to lint the target
|
||||||
lint:
|
lint:
|
||||||
verilator ${src} --lint-only \
|
verilator ${src} --lint-only \
|
||||||
|
|
2
fifo.sv
2
fifo.sv
|
@ -42,7 +42,7 @@ module fifo #(
|
||||||
// actual memory
|
// actual memory
|
||||||
dtype [DEPTH-1:0] mem_n, mem_q;
|
dtype [DEPTH-1:0] mem_n, mem_q;
|
||||||
|
|
||||||
assign full_o = (status_cnt_q == DEPTH);
|
assign full_o = (status_cnt_q == DEPTH-1);
|
||||||
assign empty_o = (status_cnt_q == 0);
|
assign empty_o = (status_cnt_q == 0);
|
||||||
assign single_element_o = (status_cnt_q == 1);
|
assign single_element_o = (status_cnt_q == 1);
|
||||||
// read and write queue logic
|
// read and write queue logic
|
||||||
|
|
|
@ -32,20 +32,18 @@ interface mem_if
|
||||||
// as an active or passive device
|
// as an active or passive device
|
||||||
// only helpful thread so far:
|
// only helpful thread so far:
|
||||||
// https://verificationacademy.com/forums/uvm/getting-multiply-driven-warnings-vsim-passive-agent
|
// https://verificationacademy.com/forums/uvm/getting-multiply-driven-warnings-vsim-passive-agent
|
||||||
logic data_gnt_driver = 'z;
|
|
||||||
assign data_gnt = data_gnt_driver;
|
|
||||||
|
|
||||||
// Memory interface configured as master
|
// Memory interface configured as master
|
||||||
clocking mck @(posedge clk);
|
clocking mck @(posedge clk);
|
||||||
// default input #1ns output #1ns;
|
default input #1ns output #1ns;
|
||||||
input address, data_wdata, data_req, data_we, data_be;
|
input address, data_wdata, data_we, data_req, data_be;
|
||||||
output data_gnt, data_rvalid, data_rdata;
|
output data_rvalid, data_rdata, data_gnt;
|
||||||
endclocking
|
endclocking
|
||||||
// Memory interface configured as slave
|
// Memory interface configured as slave
|
||||||
clocking sck @(posedge clk);
|
clocking sck @(posedge clk);
|
||||||
// default input #1ns output #1ns;
|
default input #1ns output #1ns;
|
||||||
output address, data_wdata, data_req, data_we, data_be;
|
output address, data_wdata, data_we, data_req, data_be;
|
||||||
input data_gnt, data_rvalid, data_rdata;
|
input data_rvalid, data_rdata, data_gnt;
|
||||||
endclocking
|
endclocking
|
||||||
|
|
||||||
clocking pck @(posedge clk);
|
clocking pck @(posedge clk);
|
||||||
|
@ -65,5 +63,6 @@ interface mem_if
|
||||||
input data_gnt, data_rvalid, data_rdata
|
input data_gnt, data_rvalid, data_rdata
|
||||||
);
|
);
|
||||||
// modport Passive (clocking pck);
|
// modport Passive (clocking pck);
|
||||||
|
|
||||||
endinterface
|
endinterface
|
||||||
`endif
|
`endif
|
|
@ -130,4 +130,31 @@ module mem_arbiter #(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
// ------------
|
||||||
|
// Assertions
|
||||||
|
// ------------
|
||||||
|
|
||||||
|
`ifndef synthesis
|
||||||
|
`ifndef VERILATOR
|
||||||
|
// make sure that we eventually get an rvalid after we received a grant
|
||||||
|
assert property (@(posedge clk_i) data_gnt_i |-> ##[1:$] data_rvalid_i )
|
||||||
|
else begin $error("There was a grant without a rvalid"); $stop(); end
|
||||||
|
// assert that there is no grant without a request
|
||||||
|
assert property (@(negedge clk_i) data_gnt_i |-> data_req_o)
|
||||||
|
else begin $error("There was a grant without a request."); $stop(); end
|
||||||
|
// assert that the address does not contain X when request is sent
|
||||||
|
assert property ( @(posedge clk_i) (data_req_o) |-> (!$isunknown(address_o)) )
|
||||||
|
else begin $error("address contains X when request is set"); $stop(); end
|
||||||
|
|
||||||
|
// there should be no rvalid when we are in IDLE
|
||||||
|
// assert property (
|
||||||
|
// @(posedge clk) (CS == IDLE) |-> (data_rvalid_i == 1'b0) )
|
||||||
|
// else begin $error("Received rvalid while in IDLE state"); $stop(); end
|
||||||
|
|
||||||
|
// assert that errors are only sent at the same time as grant or rvalid
|
||||||
|
// assert property ( @(posedge clk) (data_err_i) |-> (data_gnt_i || data_rvalid_i) )
|
||||||
|
// else begin $error("Error without data grant or rvalid"); $stop(); end
|
||||||
|
|
||||||
|
`endif
|
||||||
|
`endif
|
||||||
endmodule
|
endmodule
|
|
@ -44,20 +44,27 @@ class mem_if_driver extends uvm_driver #(mem_if_seq_item);
|
||||||
logic [63:0] address [$];
|
logic [63:0] address [$];
|
||||||
logic [63:0] addr;
|
logic [63:0] addr;
|
||||||
logic slave_data_gnt;
|
logic slave_data_gnt;
|
||||||
|
semaphore lock = new(1);
|
||||||
|
slave_data_gnt = 1'b1;
|
||||||
// grant process is combinatorial
|
// grant process is combinatorial
|
||||||
fork
|
fork
|
||||||
slave_gnt: begin
|
slave_gnt: begin
|
||||||
|
fu.mck.data_gnt <= 1'b1;
|
||||||
forever begin
|
forever begin
|
||||||
slave_data_gnt = 1'b0;
|
|
||||||
fu.data_gnt_driver = slave_data_gnt;
|
// @(fu.mck);
|
||||||
// wait until we got a valid request
|
// wait until we got a valid request
|
||||||
wait (fu.data_req);
|
|
||||||
// randomize grant delay - the grant may come in the same clock cycle
|
// randomize grant delay - the grant may come in the same clock cycle
|
||||||
repeat ($urandom_range(0,3)) @(fu.mck);
|
repeat ($urandom_range(0,3)) @(fu.mck);
|
||||||
|
fu.mck.data_gnt <= 1'b1;
|
||||||
|
repeat ($urandom_range(0,3)) @(fu.mck);
|
||||||
|
fu.mck.data_gnt <= 1'b0;
|
||||||
// now set the grant to one
|
// now set the grant to one
|
||||||
slave_data_gnt = 1'b1;
|
// if the request is still here go for it
|
||||||
fu.data_gnt_driver = slave_data_gnt;
|
// end else begin
|
||||||
@(fu.mck);
|
// fu.mck.data_gnt <= 1'b0;
|
||||||
|
// slave_data_gnt = 1'b0;
|
||||||
|
// end
|
||||||
// do we have another request?
|
// do we have another request?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -70,17 +77,18 @@ class mem_if_driver extends uvm_driver #(mem_if_seq_item);
|
||||||
fork
|
fork
|
||||||
// replay interface
|
// replay interface
|
||||||
imem_read: begin
|
imem_read: begin
|
||||||
@(fu.mck);
|
if (fu.pck.data_gnt & fu.mck.data_req) begin
|
||||||
if (slave_data_gnt) begin
|
|
||||||
// $display("Time: %t, Pushing", $time);
|
// $display("Time: %t, Pushing", $time);
|
||||||
address.push_back(fu.mck.address);
|
address.push_back(fu.mck.address);
|
||||||
if (address.size() != 0) begin
|
if (address.size() != 0) begin
|
||||||
// we an wait a couple of cycles here
|
// we an wait a couple of cycles here
|
||||||
// but at least one
|
// but at least one
|
||||||
|
lock.get(1);
|
||||||
repeat ($urandom_range(1,3)) @(fu.mck);
|
repeat ($urandom_range(1,3)) @(fu.mck);
|
||||||
fu.mck.data_rvalid <= 1'b1;
|
fu.mck.data_rvalid <= 1'b1;
|
||||||
addr = address.pop_front();
|
addr = address.pop_front();
|
||||||
fu.mck.data_rdata <= addr;
|
fu.mck.data_rdata <= addr;
|
||||||
|
lock.put(1);
|
||||||
end else
|
end else
|
||||||
fu.mck.data_rvalid <= 1'b0;
|
fu.mck.data_rvalid <= 1'b0;
|
||||||
end
|
end
|
||||||
|
@ -101,24 +109,25 @@ class mem_if_driver extends uvm_driver #(mem_if_seq_item);
|
||||||
// --------------
|
// --------------
|
||||||
// request a read
|
// request a read
|
||||||
// initial statements, sane resets
|
// initial statements, sane resets
|
||||||
fu.sck.data_req <= 1'b0;
|
fu.sck.data_req <= 1'b0;
|
||||||
fu.sck.address <= 64'b0;
|
fu.sck.address <= 64'b0;
|
||||||
fu.sck.data_be <= 7'b0;
|
fu.sck.data_be <= 7'b0;
|
||||||
fu.sck.data_we <= 1'b0;
|
fu.sck.data_we <= 1'b0;
|
||||||
fu.sck.data_wdata <= 64'b0;
|
fu.sck.data_wdata <= 64'b0;
|
||||||
// request read or write
|
// request read or write
|
||||||
// we don't care about results at this point
|
// we don't care about results at this point
|
||||||
forever begin
|
forever begin
|
||||||
seq_item_port.get_next_item(cmd);
|
seq_item_port.get_next_item(cmd);
|
||||||
do begin
|
// do begin
|
||||||
fu.sck.data_req <= 1'b1;
|
fu.sck.data_req <= 1'b1;
|
||||||
fu.sck.address <= cmd.address;
|
fu.sck.address <= cmd.address;
|
||||||
fu.sck.data_be <= cmd.be;
|
fu.sck.data_be <= cmd.be;
|
||||||
fu.sck.data_we <= (cmd.mode == READ) ? 1'b0 : 1'b1;
|
fu.sck.data_we <= (cmd.mode == READ) ? 1'b0 : 1'b1;
|
||||||
fu.sck.data_wdata <= cmd.data;
|
fu.sck.data_wdata <= cmd.data;
|
||||||
@(fu.sck);
|
|
||||||
end while (~fu.sck.data_gnt);
|
@(fu.sck iff fu.sck.data_gnt);
|
||||||
fu.sck.data_req <= 1'b0;
|
fu.sck.data_req <= 1'b0;
|
||||||
|
|
||||||
// delay the next request
|
// delay the next request
|
||||||
repeat(cmd.requestDelay) @(fu.sck);
|
repeat(cmd.requestDelay) @(fu.sck);
|
||||||
seq_item_port.item_done();
|
seq_item_port.item_done();
|
||||||
|
|
|
@ -50,19 +50,42 @@ class mem_if_monitor extends uvm_component;
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
task run_phase(uvm_phase phase);
|
task run_phase(uvm_phase phase);
|
||||||
|
logic[63:0] address [$];
|
||||||
|
logic[7:0] be [$];
|
||||||
mem_if_seq_item cmd = mem_if_seq_item::type_id::create("cmd");
|
mem_if_seq_item cmd = mem_if_seq_item::type_id::create("cmd");
|
||||||
mem_if_seq_item cloned_item;
|
|
||||||
|
|
||||||
// Monitor
|
// Monitor
|
||||||
// we should also distinguish between slave and master here
|
// we should also distinguish between slave and master here
|
||||||
forever begin
|
fork
|
||||||
@(fu.pck iff fu.pck.data_rvalid);
|
// detect a request
|
||||||
`uvm_info("MEM IF MONITOR", "Got rvalid", UVM_MEDIUM);
|
forever begin
|
||||||
end
|
// wait until detecting a valid request -> store be and address
|
||||||
|
@(fu.pck iff (fu.pck.data_gnt & fu.pck.data_req));
|
||||||
|
// if (m_cfg.mem_if_config == MASTER)
|
||||||
|
// $display("Pushing Address: %0h", fu.pck.address);
|
||||||
|
address.push_back(fu.pck.address);
|
||||||
|
be.push_back(fu.pck.data_be);
|
||||||
|
|
||||||
$cast(cloned_item, cmd.clone());
|
end
|
||||||
m_ap.write(cloned_item);
|
// request finished send it to the monitor
|
||||||
|
forever begin
|
||||||
|
mem_if_seq_item cloned_item;
|
||||||
|
automatic logic [63:0] addr;
|
||||||
|
// wait for the rvalid minimum a cycle later
|
||||||
|
@(fu.pck iff fu.pck.data_rvalid);
|
||||||
|
addr = address.pop_front();
|
||||||
|
// if (m_cfg.mem_if_config == MASTER)
|
||||||
|
// $display("Popping Address: %0h", addr);
|
||||||
|
cmd.address = addr;
|
||||||
|
cmd.be = be.pop_front();
|
||||||
|
cmd.data = fu.pck.data_rdata;
|
||||||
|
// was this from a master or slave agent monitor?
|
||||||
|
cmd.isSlaveAnswer = (m_cfg.mem_if_config == SLAVE) ? 1'b1 : 1'b0;
|
||||||
|
// export the item via the analysis port
|
||||||
|
$cast(cloned_item, cmd.clone());
|
||||||
|
m_ap.write(cloned_item);
|
||||||
|
end
|
||||||
|
|
||||||
|
join_none
|
||||||
|
|
||||||
endtask : run_phase
|
endtask : run_phase
|
||||||
endclass : mem_if_monitor
|
endclass : mem_if_monitor
|
||||||
|
|
|
@ -27,6 +27,7 @@ class mem_if_seq_item extends uvm_sequence_item;
|
||||||
rand logic [7:0] be;
|
rand logic [7:0] be;
|
||||||
rand int requestDelay;
|
rand int requestDelay;
|
||||||
mode_t mode;
|
mode_t mode;
|
||||||
|
logic isSlaveAnswer;
|
||||||
|
|
||||||
constraint delay_bounds {
|
constraint delay_bounds {
|
||||||
requestDelay inside {[0:10]};
|
requestDelay inside {[0:10]};
|
||||||
|
@ -53,6 +54,7 @@ class mem_if_seq_item extends uvm_sequence_item;
|
||||||
data = rhs_.data;
|
data = rhs_.data;
|
||||||
be = rhs_.be;
|
be = rhs_.be;
|
||||||
mode = rhs_.mode;
|
mode = rhs_.mode;
|
||||||
|
isSlaveAnswer = rhs_.isSlaveAnswer;
|
||||||
endfunction:do_copy
|
endfunction:do_copy
|
||||||
|
|
||||||
function bit do_compare(uvm_object rhs, uvm_comparer comparer);
|
function bit do_compare(uvm_object rhs, uvm_comparer comparer);
|
||||||
|
@ -75,7 +77,7 @@ class mem_if_seq_item extends uvm_sequence_item;
|
||||||
string s;
|
string s;
|
||||||
|
|
||||||
$sformat(s, "%s\n", super.convert2string());
|
$sformat(s, "%s\n", super.convert2string());
|
||||||
$sformat(s, "%s\n Mode: %s\n Address: %0h \nData: %0h \nBE: %0h \n", s, mode.name, address, data, be);
|
$sformat(s, "%s\nMode: %s\nAddress: %0h\nData: %0h\nBE: %0h \nisSlaveAnswer: %h", s, mode.name, address, data, be, isSlaveAnswer);
|
||||||
return s;
|
return s;
|
||||||
|
|
||||||
endfunction:convert2string
|
endfunction:convert2string
|
||||||
|
|
41
tb/env/mem_arbiter/mem_arbiter_env.svh
vendored
41
tb/env/mem_arbiter/mem_arbiter_env.svh
vendored
|
@ -27,6 +27,8 @@ class mem_arbiter_env extends uvm_env;
|
||||||
|
|
||||||
mem_if_sequencer m_mem_if_sequencers[3];
|
mem_if_sequencer m_mem_if_sequencers[3];
|
||||||
mem_arbiter_env_config m_cfg;
|
mem_arbiter_env_config m_cfg;
|
||||||
|
|
||||||
|
mem_arbiter_scoreboard m_scoreboard;
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
// Methods
|
// Methods
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
|
@ -46,35 +48,30 @@ class mem_arbiter_env extends uvm_env;
|
||||||
"mem_if_agent_config",
|
"mem_if_agent_config",
|
||||||
m_cfg.m_mem_if_slave_agent);
|
m_cfg.m_mem_if_slave_agent);
|
||||||
|
|
||||||
uvm_config_db #(mem_if_agent_config)::set(this, "m_mem_if_master0*",
|
|
||||||
"mem_if_agent_config",
|
|
||||||
m_cfg.m_mem_if_master_agents[0]);
|
|
||||||
|
|
||||||
uvm_config_db #(mem_if_agent_config)::set(this, "m_mem_if_master1*",
|
|
||||||
"mem_if_agent_config",
|
|
||||||
m_cfg.m_mem_if_master_agents[1]);
|
|
||||||
|
|
||||||
uvm_config_db #(mem_if_agent_config)::set(this, "m_mem_if_master2*",
|
|
||||||
"mem_if_agent_config",
|
|
||||||
m_cfg.m_mem_if_master_agents[2]);
|
|
||||||
|
|
||||||
m_mem_if_slave_agent = mem_if_agent::type_id::create("m_mem_if_slave_agent", this);
|
m_mem_if_slave_agent = mem_if_agent::type_id::create("m_mem_if_slave_agent", this);
|
||||||
|
|
||||||
// create 3 master memory interfaces
|
// create 3 master memory interfaces
|
||||||
m_mem_if_master_agents[0] = mem_if_agent::type_id::create("m_mem_if_master0_agent", this);
|
for (int i = 0; i < 3; i++) begin
|
||||||
m_mem_if_master_agents[1] = mem_if_agent::type_id::create("m_mem_if_master1_agent", this);
|
uvm_config_db #(mem_if_agent_config)::set(this, {"m_mem_if_master", i, "*"},
|
||||||
m_mem_if_master_agents[2] = mem_if_agent::type_id::create("m_mem_if_master2_agent", this);
|
"mem_if_agent_config",
|
||||||
|
m_cfg.m_mem_if_master_agents[i]);
|
||||||
// Get 3 sequencers
|
|
||||||
m_mem_if_sequencers[0] = mem_if_sequencer::type_id::create("m_mem_if_sequencer0", this);
|
|
||||||
m_mem_if_sequencers[1] = mem_if_sequencer::type_id::create("m_mem_if_sequencer1", this);
|
|
||||||
m_mem_if_sequencers[2] = mem_if_sequencer::type_id::create("m_mem_if_sequencer2", this);
|
|
||||||
|
|
||||||
|
m_mem_if_master_agents[i] = mem_if_agent::type_id::create({"m_mem_if_master", i, "_agent"}, this);
|
||||||
|
// Get 3 sequencers
|
||||||
|
m_mem_if_sequencers[i] = mem_if_sequencer::type_id::create({"m_mem_if_sequencer", i}, this);
|
||||||
|
end
|
||||||
|
// instantiate the scoreboard
|
||||||
|
m_scoreboard = mem_arbiter_scoreboard::type_id::create("m_scoreboard", this);
|
||||||
endfunction:build_phase
|
endfunction:build_phase
|
||||||
|
|
||||||
function void connect_phase(uvm_phase phase);
|
function void connect_phase(uvm_phase phase);
|
||||||
m_mem_if_sequencers[0] = m_mem_if_master_agents[0].m_sequencer;
|
// connect the sequencers and monitor to the master agents
|
||||||
m_mem_if_sequencers[1] = m_mem_if_master_agents[1].m_sequencer;
|
for (int i = 0; i < 3; i++) begin
|
||||||
m_mem_if_sequencers[2] = m_mem_if_master_agents[2].m_sequencer;
|
m_mem_if_sequencers[i] = m_mem_if_master_agents[i].m_sequencer;
|
||||||
|
m_mem_if_master_agents[i].m_monitor.m_ap.connect(m_scoreboard.item_export);
|
||||||
|
end
|
||||||
|
// connect the slave monitor to the scoreboard
|
||||||
|
m_mem_if_slave_agent.m_monitor.m_ap.connect(m_scoreboard.item_export);
|
||||||
endfunction: connect_phase
|
endfunction: connect_phase
|
||||||
endclass : mem_arbiter_env
|
endclass : mem_arbiter_env
|
||||||
|
|
3
tb/env/mem_arbiter/mem_arbiter_env_pkg.sv
vendored
3
tb/env/mem_arbiter/mem_arbiter_env_pkg.sv
vendored
|
@ -19,8 +19,11 @@ package mem_arbiter_env_pkg;
|
||||||
`include "uvm_macros.svh"
|
`include "uvm_macros.svh"
|
||||||
// Testbench related imports
|
// Testbench related imports
|
||||||
import mem_if_agent_pkg::*;
|
import mem_if_agent_pkg::*;
|
||||||
|
// Include the scoreboard
|
||||||
|
`include "mem_arbiter_scoreboard.svh"
|
||||||
// Includes for the config for the environment
|
// Includes for the config for the environment
|
||||||
`include "mem_arbiter_env_config.svh"
|
`include "mem_arbiter_env_config.svh"
|
||||||
// Includes the environment
|
// Includes the environment
|
||||||
`include "mem_arbiter_env.svh"
|
`include "mem_arbiter_env.svh"
|
||||||
|
|
||||||
endpackage
|
endpackage
|
||||||
|
|
58
tb/env/mem_arbiter/mem_arbiter_scoreboard.svh
vendored
Executable file
58
tb/env/mem_arbiter/mem_arbiter_scoreboard.svh
vendored
Executable file
|
@ -0,0 +1,58 @@
|
||||||
|
// Author: Florian Zaruba, ETH Zurich
|
||||||
|
// Date: 01.05.2017
|
||||||
|
// Description: Memory Arbiter scoreboard
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
class mem_arbiter_scoreboard extends uvm_scoreboard;
|
||||||
|
|
||||||
|
`uvm_component_utils(mem_arbiter_scoreboard);
|
||||||
|
int slave_answer_cnt, master_answer_cnt;
|
||||||
|
|
||||||
|
uvm_analysis_imp #(mem_if_seq_item, mem_arbiter_scoreboard) item_export;
|
||||||
|
|
||||||
|
function new(string name, uvm_component parent);
|
||||||
|
super.new(name, parent);
|
||||||
|
endfunction : new
|
||||||
|
|
||||||
|
function void build_phase(uvm_phase phase);
|
||||||
|
super.build_phase(phase);
|
||||||
|
item_export = new("item_slave_export", this);
|
||||||
|
slave_answer_cnt = 0;
|
||||||
|
master_answer_cnt = 0;
|
||||||
|
endfunction : build_phase
|
||||||
|
|
||||||
|
virtual function void write (mem_if_seq_item seq_item);
|
||||||
|
// $display("%s", seq_item.convert2string());
|
||||||
|
// the answer is from a master
|
||||||
|
// the address and data should be the same when we use the replay system
|
||||||
|
if (~seq_item.isSlaveAnswer) begin
|
||||||
|
if (seq_item.address !== seq_item.data) begin
|
||||||
|
`uvm_error( "Mem Arbiter Scoreboard", $sformatf("Got: %0h Expected: %0h", seq_item.address, seq_item.data) )
|
||||||
|
end
|
||||||
|
master_answer_cnt++;
|
||||||
|
end else begin
|
||||||
|
slave_answer_cnt++;
|
||||||
|
end
|
||||||
|
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
// check here for the total amount of transactions
|
||||||
|
virtual function void extract_phase( uvm_phase phase );
|
||||||
|
super.extract_phase(phase);
|
||||||
|
if (master_answer_cnt !== slave_answer_cnt) begin
|
||||||
|
`uvm_error("Mem Arbiter Scoreboard", $sformatf("Mismatch in overall result count. Expected: %d Got: %d", slave_answer_cnt, master_answer_cnt))
|
||||||
|
end else
|
||||||
|
`uvm_info("Mem Arbiter Scoreboard", $sformatf("Overall result count: Expected: %d Got: %d", slave_answer_cnt, master_answer_cnt), UVM_LOW)
|
||||||
|
endfunction : extract_phase
|
||||||
|
|
||||||
|
endclass : mem_arbiter_scoreboard
|
|
@ -28,20 +28,28 @@ module mem_arbiter_tb;
|
||||||
|
|
||||||
mem_if master[3](clk);
|
mem_if master[3](clk);
|
||||||
mem_if slave(clk);
|
mem_if slave(clk);
|
||||||
|
// super hack in assigning the wire a value
|
||||||
|
// we need to keep all interface signals as wire as
|
||||||
|
// the simulator does not now if this interface will be used
|
||||||
|
// as an active or passive device
|
||||||
|
// only helpful thread so far:
|
||||||
|
// https://verificationacademy.com/forums/uvm/getting-multiply-driven-warnings-vsim-passive-agent
|
||||||
|
// logic data_gnt_driver = 'z;
|
||||||
|
// assign data_gnt = data_gnt_driver & data_req;
|
||||||
|
|
||||||
mem_arbiter dut (
|
mem_arbiter dut (
|
||||||
.clk_i ( clk ),
|
.clk_i ( clk ),
|
||||||
.rst_ni ( rst_ni ),
|
.rst_ni ( rst_ni ),
|
||||||
.flush_ready_o ( flush_ready_o ),
|
.flush_ready_o ( flush_ready_o ),
|
||||||
|
|
||||||
.address_o ( slave.address ),
|
.address_o ( slave.address ),
|
||||||
.data_wdata_o ( slave.data_wdata ),
|
.data_wdata_o ( slave.data_wdata ),
|
||||||
.data_req_o ( slave.data_req ),
|
.data_req_o ( slave.data_req ),
|
||||||
.data_we_o ( slave.data_we ),
|
.data_we_o ( slave.data_we ),
|
||||||
.data_be_o ( slave.data_be ),
|
.data_be_o ( slave.data_be ),
|
||||||
.data_gnt_i ( slave.data_gnt ),
|
.data_gnt_i ( slave.data_req & slave.data_gnt ),
|
||||||
.data_rvalid_i ( slave.data_rvalid ),
|
.data_rvalid_i ( slave.data_rvalid ),
|
||||||
.data_rdata_i ( slave.data_rdata ),
|
.data_rdata_i ( slave.data_rdata ),
|
||||||
|
|
||||||
.address_i ( {master[2].address, master[1].address, master[0].address} ),
|
.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_wdata_i ( {master[2].data_wdata, master[1].data_wdata, master[0].data_wdata} ),
|
||||||
|
|
|
@ -1,3 +1,19 @@
|
||||||
|
// Author: Florian Zaruba, ETH Zurich
|
||||||
|
// Date: 01.05.2017
|
||||||
|
// Description: Randomized test sequence
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
class mem_arbiter_sequence extends mem_if_sequence;
|
class mem_arbiter_sequence extends mem_if_sequence;
|
||||||
|
|
||||||
`uvm_object_utils(mem_arbiter_sequence);
|
`uvm_object_utils(mem_arbiter_sequence);
|
||||||
|
|
|
@ -38,9 +38,9 @@ class mem_arbiter_test extends mem_arbiter_test_base;
|
||||||
endtask
|
endtask
|
||||||
|
|
||||||
task run_phase(uvm_phase phase);
|
task run_phase(uvm_phase phase);
|
||||||
|
uvm_objection objection;
|
||||||
phase.raise_objection(this, "mem_arbiter_test");
|
phase.raise_objection(this, "mem_arbiter_test");
|
||||||
#200ns;
|
#200ns;
|
||||||
//fibonacci_sequence fibonacci;
|
|
||||||
super.run_phase(phase);
|
super.run_phase(phase);
|
||||||
// fork three sequencers and wait for all of them to finish
|
// fork three sequencers and wait for all of them to finish
|
||||||
// until dropping the objection again
|
// until dropping the objection again
|
||||||
|
@ -50,7 +50,9 @@ class mem_arbiter_test extends mem_arbiter_test_base;
|
||||||
start_sequence(2);
|
start_sequence(2);
|
||||||
join
|
join
|
||||||
// Testlogic goes here
|
// Testlogic goes here
|
||||||
#100ns;
|
// drain time until the objection gets dropped
|
||||||
|
objection = phase.get_objection();
|
||||||
|
objection.set_drain_time(this, 100ns );
|
||||||
phase.drop_objection(this, "mem_arbiter_test");
|
phase.drop_objection(this, "mem_arbiter_test");
|
||||||
endtask
|
endtask
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue