mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-23 21:57:11 -04:00
Restructuring tb, more ready for UVM port
This commit is contained in:
parent
7ee4add5b6
commit
f3f136a382
2 changed files with 199 additions and 120 deletions
56
tb/models/scoreboard.sv
Executable file
56
tb/models/scoreboard.sv
Executable file
|
@ -0,0 +1,56 @@
|
|||
class Scoreboard;
|
||||
|
||||
scoreboard_entry decoded_instructions[$];
|
||||
scoreboard_entry issued_instructions[$];
|
||||
static integer unsigned pc = 0;
|
||||
|
||||
// utility function to get randomized input data
|
||||
static function automatic scoreboard_entry randomize_scoreboard();
|
||||
exception exception = { 64'h55, 63'h0, 1'b0};
|
||||
scoreboard_entry entry = {
|
||||
pc, ALU, ADD, 5'h5, 5'h5, 5'h5, 64'h0, 1'b0, 1'b0, exception
|
||||
};
|
||||
pc++;
|
||||
return entry;
|
||||
endfunction : randomize_scoreboard
|
||||
|
||||
// just allow one operation
|
||||
function void submit_instruction(scoreboard_entry entry);
|
||||
decoded_instructions.push_back(entry);
|
||||
endfunction : submit_instruction
|
||||
|
||||
// get the current issue instruction
|
||||
function scoreboard_entry get_issue();
|
||||
scoreboard_entry issue = decoded_instructions.pop_front();
|
||||
issued_instructions.push_back(issue);
|
||||
return issue;
|
||||
endfunction : get_issue
|
||||
|
||||
// write back to scoreboard
|
||||
function void write_back(logic [63:0] pc, logic [63:0] value);
|
||||
for (int i = 0; i < $size(issued_instructions); i++) begin
|
||||
if (issued_instructions[i].pc == pc) begin
|
||||
issued_instructions[i].valid = 1'b1;
|
||||
issued_instructions[i].result = value;
|
||||
end
|
||||
end
|
||||
endfunction : write_back
|
||||
|
||||
// // commit the instruction, e.g.: delete it from the entries
|
||||
function scoreboard_entry commit();
|
||||
return issued_instructions.pop_front();
|
||||
endfunction : commit
|
||||
|
||||
// return the clobbered registers
|
||||
function logic [31:0][$bits(fu_t)-1:0] get_clobber();
|
||||
logic [31:0][$bits(fu_t)-1:0] result;
|
||||
for (int i = 0; i < $size(issued_instructions); i++) begin
|
||||
if (issued_instructions[i].rd != 5'h0) begin
|
||||
result[issued_instructions[i].rd] = issued_instructions[i].fu;
|
||||
end
|
||||
end
|
||||
return result;
|
||||
endfunction : get_clobber
|
||||
|
||||
|
||||
endclass : Scoreboard
|
|
@ -7,58 +7,40 @@
|
|||
// All rights reserved.
|
||||
//
|
||||
// TODO, test register read and clobber interface, make a proper TB out of it
|
||||
module scoreboard_tb;
|
||||
|
||||
import uvm_pkg::*;
|
||||
import ariane_pkg::*;
|
||||
|
||||
logic rst_ni, clk;
|
||||
scoreboard_entry scoreboard_queue[$];
|
||||
scoreboard_entry temp_scoreboard_entry;
|
||||
scoreboard_entry comp;
|
||||
semaphore wb_lock = new(1);
|
||||
module scoreboard_tb;
|
||||
|
||||
integer unsigned pc = 0;
|
||||
`include "models/scoreboard.sv"
|
||||
|
||||
scoreboard_if scoreboard_if (clk);
|
||||
logic rst_ni, clk;
|
||||
scoreboard_if scoreboard_if (clk);
|
||||
|
||||
scoreboard dut (
|
||||
.clk_i ( clk ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.full_o ( scoreboard_if.full ),
|
||||
.flush_i ( scoreboard_if.flush ),
|
||||
.rd_clobber_o ( scoreboard_if.rd_clobber ),
|
||||
.rs1_i ( scoreboard_if.rs1_address ),
|
||||
.rs1_o ( scoreboard_if.rs1 ),
|
||||
.rs1_valid_o ( scoreboard_if.rs1_valid ),
|
||||
.rs2_i ( scoreboard_if.rs2_address ),
|
||||
.rs2_o ( scoreboard_if.rs2 ),
|
||||
.rs2_valid_o ( scoreboard_if.rs2_valid ),
|
||||
.commit_instr_o ( scoreboard_if.commit_instr ),
|
||||
.commit_ack_i ( scoreboard_if.commit_ack ),
|
||||
.decoded_instr_i ( scoreboard_if.decoded_instr ),
|
||||
.decoded_instr_valid_i( scoreboard_if.decoded_instr_valid ),
|
||||
.issue_instr_o ( scoreboard_if.issue_instr ),
|
||||
.issue_instr_valid_o ( scoreboard_if.issue_instr_valid ),
|
||||
.issue_ack_i ( scoreboard_if.issue_ack ),
|
||||
.pc_i ( scoreboard_if.pc ),
|
||||
.wdata_i ( scoreboard_if.wdata ),
|
||||
.wb_valid_i ( scoreboard_if.wb_valid )
|
||||
);
|
||||
|
||||
function automatic scoreboard_entry randomize_scoreboard();
|
||||
exception exception = { 64'h55, 63'h0, 1'b0};
|
||||
scoreboard_entry entry = {
|
||||
pc, ALU, ADD, 5'h5, 5'h5, 5'h5, 64'h0, 1'b0, 1'b0, exception
|
||||
};
|
||||
pc++;
|
||||
return entry;
|
||||
endfunction : randomize_scoreboard
|
||||
|
||||
initial begin
|
||||
// register the scoreboard interface
|
||||
// uvm_config_db #(virtual scoreboard_if)::set(null, "uvm_test_top", "scoreboard_vif", scoreboard_if);
|
||||
end
|
||||
scoreboard dut (
|
||||
.clk_i ( clk ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.full_o ( scoreboard_if.full ),
|
||||
.flush_i ( scoreboard_if.flush ),
|
||||
.rd_clobber_o ( scoreboard_if.rd_clobber ),
|
||||
.rs1_i ( scoreboard_if.rs1_address ),
|
||||
.rs1_o ( scoreboard_if.rs1 ),
|
||||
.rs1_valid_o ( scoreboard_if.rs1_valid ),
|
||||
.rs2_i ( scoreboard_if.rs2_address ),
|
||||
.rs2_o ( scoreboard_if.rs2 ),
|
||||
.rs2_valid_o ( scoreboard_if.rs2_valid ),
|
||||
.commit_instr_o ( scoreboard_if.commit_instr ),
|
||||
.commit_ack_i ( scoreboard_if.commit_ack ),
|
||||
.decoded_instr_i ( scoreboard_if.decoded_instr ),
|
||||
.decoded_instr_valid_i( scoreboard_if.decoded_instr_valid ),
|
||||
.issue_instr_o ( scoreboard_if.issue_instr ),
|
||||
.issue_instr_valid_o ( scoreboard_if.issue_instr_valid ),
|
||||
.issue_ack_i ( scoreboard_if.issue_ack ),
|
||||
.pc_i ( scoreboard_if.pc ),
|
||||
.wdata_i ( scoreboard_if.wdata ),
|
||||
.wb_valid_i ( scoreboard_if.wb_valid )
|
||||
);
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
|
@ -71,88 +53,129 @@ scoreboard dut (
|
|||
#10ns clk = ~clk;
|
||||
end
|
||||
|
||||
// push new decoded instructions
|
||||
initial begin
|
||||
wait(rst_ni == 1'b1);
|
||||
// load the scoreboard until it is full
|
||||
forever begin
|
||||
program testbench (scoreboard_if scoreboard_if);
|
||||
// variable declarations
|
||||
Scoreboard sb = new;
|
||||
semaphore wb_lock = new(1);
|
||||
|
||||
@(scoreboard_if.mck);
|
||||
// if we are not full load another instruction
|
||||
if (scoreboard_if.full == 1'b0) begin
|
||||
temp_scoreboard_entry = randomize_scoreboard();
|
||||
scoreboard_queue.push_back(temp_scoreboard_entry);
|
||||
|
||||
scoreboard_if.mck.decoded_instr <= temp_scoreboard_entry;
|
||||
scoreboard_if.mck.decoded_instr_valid <= 1'b1;
|
||||
end else begin
|
||||
scoreboard_if.mck.decoded_instr_valid <= 1'b0;
|
||||
end
|
||||
assign scoreboard_if.flush = 1'b0;
|
||||
|
||||
initial begin
|
||||
// register the scoreboard interface
|
||||
// uvm_config_db #(virtual scoreboard_if)::set(null, "uvm_test_top", "scoreboard_vif", scoreboard_if);
|
||||
end
|
||||
end
|
||||
scoreboard_entry issue_instruction;
|
||||
// pull e.g. issue instructions
|
||||
initial begin
|
||||
wait(rst_ni == 1'b1);
|
||||
forever begin
|
||||
|
||||
@(scoreboard_if.mck);
|
||||
// push new decoded instructions
|
||||
initial begin
|
||||
scoreboard_if.mck.decoded_instr_valid <= 1'b0;
|
||||
wait(rst_ni == 1'b1);
|
||||
// load the scoreboard until it is full
|
||||
forever begin
|
||||
|
||||
// if we are not full then load another instruction
|
||||
if (scoreboard_if.issue_instr_valid == 1'b1) begin
|
||||
scoreboard_if.mck.issue_ack <= 1'b1;
|
||||
issue_instruction <= scoreboard_if.mck.issue_instr;
|
||||
$display("Time: %t, Issuing: %0h, Valid: %h", $time, scoreboard_if.mck.issue_instr.pc, scoreboard_if.issue_instr_valid);
|
||||
@(scoreboard_if.mck)
|
||||
scoreboard_if.mck.issue_ack <= 1'b0;
|
||||
|
||||
// generate a delay between 0 and 3 cycles for WB, write-back out of order
|
||||
fork
|
||||
write_back: begin
|
||||
automatic scoreboard_entry thread_copy = issue_instruction;
|
||||
repeat ($urandom_range(0,20)) @(scoreboard_if.mck);
|
||||
wb_lock.get(1);
|
||||
$display("Time: %t, Writing Back: %0h", $time, thread_copy.pc);
|
||||
scoreboard_if.mck.pc <= thread_copy.pc;
|
||||
scoreboard_if.mck.wdata <= 64'h7777;
|
||||
scoreboard_if.mck.wb_valid <= 1'b1;
|
||||
@(scoreboard_if.mck);
|
||||
scoreboard_if.mck.wb_valid <= 1'b0;
|
||||
wb_lock.put(1);
|
||||
end
|
||||
join_none
|
||||
end else begin
|
||||
scoreboard_if.mck.issue_ack <= 1'b0;
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
// commit instructions
|
||||
initial begin
|
||||
wait(rst_ni == 1'b1);
|
||||
forever begin
|
||||
repeat ($urandom_range(1,3)) @(scoreboard_if.mck);
|
||||
if (scoreboard_if.mck.commit_instr.valid == 1'b1) begin
|
||||
$display("Time: %t, Commiting: %0h", $time, scoreboard_if.mck.commit_instr.pc);
|
||||
scoreboard_if.mck.commit_ack <= 1'b1;
|
||||
@(scoreboard_if.mck);
|
||||
scoreboard_if.mck.commit_ack <= 1'b0;
|
||||
repeat ($urandom_range(0,3)) @(scoreboard_if.mck);
|
||||
end else
|
||||
scoreboard_if.mck.commit_ack <= 1'b0;
|
||||
end
|
||||
end
|
||||
// commit checker e.g.: monitor
|
||||
initial begin
|
||||
wait(rst_ni == 1'b1);
|
||||
// if we are not full load another instruction
|
||||
if (scoreboard_if.full == 1'b0) begin
|
||||
scoreboard_if.mck.decoded_instr <= Scoreboard::randomize_scoreboard();
|
||||
scoreboard_if.mck.decoded_instr_valid <= 1'b1;
|
||||
end else begin
|
||||
scoreboard_if.mck.decoded_instr_valid <= 1'b0;
|
||||
end
|
||||
|
||||
forever begin
|
||||
@(scoreboard_if.pck);
|
||||
if (scoreboard_if.pck.commit_ack == 1'b1) begin
|
||||
comp = scoreboard_queue.pop_front();
|
||||
assert (comp.pc === scoreboard_if.pck.commit_instr.pc) else $error($sformatf("Mismatch: Expected: %0h Got: %0h", comp.pc, scoreboard_if.pck.commit_instr.pc));
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
scoreboard_entry issue_instruction;
|
||||
// pull e.g. issue instructions
|
||||
initial begin
|
||||
wait(rst_ni == 1'b1);
|
||||
forever begin
|
||||
|
||||
@(scoreboard_if.mck);
|
||||
|
||||
// if we are not full then load another instruction
|
||||
if (scoreboard_if.issue_instr_valid == 1'b1) begin
|
||||
scoreboard_if.mck.issue_ack <= 1'b1;
|
||||
issue_instruction <= scoreboard_if.mck.issue_instr;
|
||||
// $display("Time: %t, Issuing: %0h, Valid: %h", $time, scoreboard_if.mck.issue_instr.pc, scoreboard_if.issue_instr_valid);
|
||||
@(scoreboard_if.mck)
|
||||
scoreboard_if.mck.issue_ack <= 1'b0;
|
||||
// generate a delay between 0 and 3 cycles for WB, write-back out of order
|
||||
fork
|
||||
write_back: begin
|
||||
automatic scoreboard_entry thread_copy = issue_instruction;
|
||||
automatic logic [63:0] rand_data = $urandom_range(0, 2**31);
|
||||
repeat ($urandom_range(1,20)) @(scoreboard_if.mck);
|
||||
wb_lock.get(1);
|
||||
// $display("Time: %t, Writing Back: %0h", $time, thread_copy.pc);
|
||||
scoreboard_if.mck.pc <= thread_copy.pc;
|
||||
scoreboard_if.mck.wdata <= rand_data;
|
||||
scoreboard_if.mck.wb_valid <= 1'b1;
|
||||
sb.write_back(thread_copy.pc, rand_data);
|
||||
@(scoreboard_if.mck);
|
||||
scoreboard_if.mck.wb_valid <= 1'b0;
|
||||
wb_lock.put(1);
|
||||
end
|
||||
join_none
|
||||
end else begin
|
||||
scoreboard_if.mck.issue_ack <= 1'b0;
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
// commit instructions
|
||||
initial begin
|
||||
wait(rst_ni == 1'b1);
|
||||
forever begin
|
||||
repeat ($urandom_range(1,3)) @(scoreboard_if.mck);
|
||||
if (scoreboard_if.mck.commit_instr.valid == 1'b1) begin
|
||||
// $display("Time: %t, Commiting: %0h", $time, scoreboard_if.mck.commit_instr.pc);
|
||||
scoreboard_if.mck.commit_ack <= 1'b1;
|
||||
@(scoreboard_if.mck);
|
||||
scoreboard_if.mck.commit_ack <= 1'b0;
|
||||
repeat ($urandom_range(0,3)) @(scoreboard_if.mck);
|
||||
end else
|
||||
scoreboard_if.mck.commit_ack <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
// ------------------
|
||||
// Monitor + Checker
|
||||
// ------------------
|
||||
initial begin
|
||||
wait(rst_ni == 1'b1);
|
||||
forever begin
|
||||
@(scoreboard_if.pck);
|
||||
if (scoreboard_if.pck.decoded_instr_valid == 1'b1) begin
|
||||
sb.submit_instruction(scoreboard_if.pck.decoded_instr);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
scoreboard_entry tmp_sbe;
|
||||
initial begin
|
||||
wait(rst_ni == 1'b1);
|
||||
forever begin
|
||||
@(scoreboard_if.pck);
|
||||
if (scoreboard_if.pck.issue_instr_valid == 1'b1 && scoreboard_if.pck.issue_ack) begin
|
||||
tmp_sbe = sb.get_issue();
|
||||
assert (tmp_sbe.pc == issue_instruction.pc) else $error("Issue instruction mismatch. Expected: %0h Got: %0h", tmp_sbe, issue_instruction);
|
||||
end
|
||||
end
|
||||
end
|
||||
// commit checker
|
||||
scoreboard_entry comp;
|
||||
initial begin
|
||||
wait(rst_ni == 1'b1);
|
||||
|
||||
forever begin
|
||||
@(scoreboard_if.pck);
|
||||
if (scoreboard_if.pck.commit_ack == 1'b1) begin
|
||||
comp = sb.commit();
|
||||
assert (comp === scoreboard_if.pck.commit_instr) else $error($sformatf("Mismatch: Expected: %0h Got: %0h", comp, scoreboard_if.pck.commit_instr));
|
||||
end
|
||||
end
|
||||
end
|
||||
endprogram
|
||||
|
||||
testbench tb(scoreboard_if);
|
||||
endmodule
|
Loading…
Add table
Add a link
Reference in a new issue