First TB for scoreboard implementation

This commit is contained in:
Florian Zaruba 2017-04-11 18:32:48 +02:00
parent 390182d0c4
commit e66a683e8d
7 changed files with 329 additions and 66 deletions

View file

@ -112,27 +112,27 @@ The field functional unit can be of the following types:
#### Interface
| **Signal** | **Direction** | **Description** | **Category** |
|-----------------------|---------------|----------------------------------------------------------------------------------|------------------------|
| flush_i | Input | Flush Scoreboard | Control |
| full_o | Output | Scoreboard is full | Control |
| rd_clobber_o | Output | Used destination registers, includes the FU that is going to write this register | To issue/read operands |
| rs1_i | Input | Check the scoreboard for a valid register at that address | From read operands |
| rs2_i | Input | Check the scoreboard for a valid register at that address | From read operands |
| rs1_o | Output | Data for rs1 | To read operands |
| rs1_valid_o | Output | Data for rs1 is valid | To read operands |
| rs2_o | Output | Data for rs2 | To read operands |
| rs2_valid_o | Output | Data for rs2 is valid | To read operands |
| commit_instr_o | Output | Instruction to commit | To WB stage |
| commit_ready_i | Input | Commit unit is ready | To WB stage |
| decoded_instr_i | Input | Decoded instruction entering scoreboard | From ID |
| decoded_instr_valid_i | Input | Decoded instruction entering scoreboard is valid | From ID |
| issue_instr_o | Output | Instruction to issue stage | To Issue |
| issue_instr_valid_o | Output | Instruction to issue stage is valid | To Issue |
| issue_ready_i | Input | Issue stage is ready to accept a new instruction | From Issue |
| pc_i | Input | PC at which to write back the data | From WB |
| wdata_i | Input | Write data from WB | From WB |
| wb_valid_i | Input | Data from WB stage is valid | From WB |
| **Signal** | **Direction** | **Description** | **Category** |
|-----------------------|---------------|-----------------------------------------------------------------------------------|------------------------|
| flush_i | Input | Flush Scoreboard | Control |
| full_o | Output | Scoreboard is full | Control |
| rd_clobber_o | Output | Used destination registers, includes the FU that is going to write this register | To issue/read operands |
| rs1_i | Input | Check the scoreboard for a valid register at that address | From read operands |
| rs2_i | Input | Check the scoreboard for a valid register at that address | From read operands |
| rs1_o | Output | Data for rs1 | To read operands |
| rs1_valid_o | Output | Data for rs1 is valid | To read operands |
| rs2_o | Output | Data for rs2 | To read operands |
| rs2_valid_o | Output | Data for rs2 is valid | To read operands |
| commit_instr_o | Output | Instruction to commit | To WB stage |
| commit_ack_i | Input | Commit unit acknowledges instruction, it mus immediately begin with processing it | To WB stage |
| decoded_instr_i | Input | Decoded instruction entering scoreboard | From ID |
| decoded_instr_valid_i | Input | Decoded instruction entering scoreboard is valid | From ID |
| issue_instr_o | Output | Instruction to issue stage | To Issue |
| issue_instr_valid_o | Output | Instruction to issue stage is valid | To Issue |
| issue_ack_i | Input | Issue stage is acknowledging instruction, it must immediately begin processing | From Issue |
| pc_i | Input | PC at which to write back the data | From WB |
| wdata_i | Input | Write data from WB | From WB |
| wb_valid_i | Input | Data from WB stage is valid | From WB |
### Issue

View file

@ -12,3 +12,14 @@ Synthesized @ 0.4 ns, typical case, UMC65:
| **Total** | **45568 (~32 kGE)** | **32441 (~23 kGE)** | **16088 (~ 11 kGE)** |
## ALU
| **Type** | **1.6 ns, worst case, UMC65** |
|---------------|-------------------------------|
| Sequential | 0 |
| Combinatorial | 9019 |
| Buffer | 582 |
| **Total** | **9019 (~6.4 kGE)** |
w

View file

@ -30,7 +30,7 @@ module scoreboard #(
// advertise instruction to commit stage, if ready_i is asserted advance the commit pointer
output dtype commit_instr_o,
input logic commit_ready_i,
input logic commit_ack_i,
// instruction to put on top of scoreboard e.g.: top pointer
// we can always put this instruction to the top unless we signal with asserted full_o
@ -40,7 +40,7 @@ module scoreboard #(
// instruction to issue logic, if issue_instr_valid and issue_ready is asserted, advance the issue pointer
output dtype issue_instr_o,
output logic issue_instr_valid_o,
input logic issue_ready_i,
input logic issue_ack_i,
// write-back port
input logic[63:0] pc_i, // PC at which to write the result back
@ -51,16 +51,18 @@ localparam BITS_ENTRIES = $clog2(NR_ENTRIES);
dtype [NR_ENTRIES-1:0] mem;
logic [BITS_ENTRIES-1:0] issue_pointer_n, issue_pointer_q, // points to the instruction currently in issue
commit_pointer_n, commit_pointer_q, // points to the instruction currently in commit
top_pointer_n, top_pointer_q; // points to the top of the scoreboard, an empty slot
commit_pointer_n, commit_pointer_q, commit_pointer_qq, // points to the instruction currently in commit
top_pointer_n, top_pointer_q, top_pointer_qq; // points to the top of the scoreboard, an empty slot, top pointer two cycles ago
logic pointer_overflow;
logic empty;
logic reset_condition;
// full and empty signaling: signal that we are not able to take new instructions
// track pointer overflow
assign pointer_overflow = (commit_pointer_q <= top_pointer_q) ? 1'b0 : 1'b1;
assign full_o = (pointer_overflow) ? (commit_pointer_q == top_pointer_q) : 1'b0;
// && top_pointer_q >= top_pointer_qq
assign reset_condition = top_pointer_q == top_pointer_qq;
assign pointer_overflow = (top_pointer_q <= commit_pointer_q && ~reset_condition) ? 1'b1 : 1'b0;
assign full_o = (reset_condition) ? 1'b0 : (commit_pointer_q == top_pointer_q);
assign empty = (pointer_overflow) ? 1'b0 : (commit_pointer_q == top_pointer_q);
// rd_clobber output: output currently clobbered destination registers
@ -169,6 +171,7 @@ end
// issue instruction: advance the issue pointer
always_comb begin : issue_instruction
// provide a combinatorial path in case the scoreboard is empty
if (empty) begin
issue_instr_o = decoded_instr_i;
@ -176,12 +179,27 @@ always_comb begin : issue_instruction
// if not empty go to scoreboard and get the instruction at the issue pointer
end else begin
issue_instr_o = mem[$unsigned(issue_pointer_q)];
issue_instr_valid_o = 1'b1;
// we have not reached the top of the buffer
// issue pointer has overflowed
if (issue_pointer_q < commit_pointer_q) begin
if (issue_pointer_q <= top_pointer_q)
issue_instr_valid_o = 1'b1;
else
issue_instr_valid_o = 1'b0;
end else begin // issue pointer has not overflowed
if (pointer_overflow)
issue_instr_valid_o = 1'b1;
else if (issue_pointer_q <= top_pointer_q)
issue_instr_valid_o = 1'b1;
else
issue_instr_valid_o = 1'b0;
end
end
// default assignment: issue didn't read
issue_pointer_n = issue_pointer_q;
// advance pointer if the issue logic was ready
if (issue_ready_i) begin
if (issue_ack_i) begin
issue_pointer_n = issue_pointer_q + 1;
end
@ -193,7 +211,7 @@ always_comb begin: commit_instruction
// we can always safely output the instruction at which the commit pointer points
// since the scoreboard entry has a valid bit which the commit stage needs to check anyway
commit_instr_o = mem[commit_pointer_q];
if (commit_ready_i) begin
if (commit_ack_i) begin
commit_pointer_n = commit_pointer_q + 1;
end
end
@ -201,20 +219,80 @@ end
// sequential process
always_ff @(posedge clk_i or negedge rst_ni) begin : sequential
if(~rst_ni) begin
issue_pointer_q <= '{default: 0};
commit_pointer_q <= '{default: 0};
top_pointer_q <= '{default: 0};
issue_pointer_q <= '{default: 0};
commit_pointer_q <= '{default: 0};
top_pointer_q <= '{default: 0};
top_pointer_qq <= '{default: 0};
end else if (flush_i) begin // reset pointers on flush
issue_pointer_q <= '{default: 0};
commit_pointer_q <= '{default: 0};
top_pointer_q <= '{default: 0};
top_pointer_qq <= '{default: 0};
end else begin
issue_pointer_q <= issue_pointer_n;
commit_pointer_q <= commit_pointer_n;
top_pointer_q <= top_pointer_n;
issue_pointer_q <= issue_pointer_n;
commit_pointer_q <= commit_pointer_n;
top_pointer_q <= top_pointer_n;
if (decoded_instr_valid_i) // only advance if we decoded instruction
top_pointer_qq <= top_pointer_q;
end
end
`ifndef SYNTHESIS
`ifndef verilator
assert (NR_ENTRIES == 2**$clog2(NR_ENTRIES)) else $error("Scoreboard size needs to be a power of two.");
// there should never be more than one instruction writing the same destination register (except x0)
initial begin
assert (NR_ENTRIES == 2**$clog2(NR_ENTRIES)) else $fatal("Scoreboard size needs to be a power of two.");
end
// assert that zero is never set
assert property (
@(posedge clk_i) rst_ni |-> (rd_clobber_o[0] == NONE))
else $error ("RD 0 should not bet set");
// assert that we never acknowledge a commit if the instruction is not valid
assert property (
@(posedge clk_i) (rst_ni && commit_ack_i |-> commit_instr_o.valid))
else $error ("Commit acknowledged but instruction is not valid");
// assert that we never give an issue ack signal if the instruction is not valid
assert property (
@(posedge clk_i) (rst_ni && issue_ack_i |-> issue_instr_valid_o))
else $error ("Issue acknowledged but instruction is not valid");
// there should never be more than one instruction writing the same destination register (except x0)
// assert strict pointer ordering
// print scoreboard
// initial begin
// automatic string pointer = "";
// static integer f = $fopen("scoreboard.txt", "w");
// forever begin
// wait(rst_ni == 1'b1);
// @(posedge clk_i)
// $fwrite(f, $time);
// $fwrite(f, "\n");
// $fwrite(f, "._________________________.\n");
// for (int i = 0; i < NR_ENTRIES; i++) begin
// if (i == commit_pointer_q && i == issue_pointer_q && i == top_pointer_q)
// pointer = " <- top, issue, commit pointer";
// else if (i == commit_pointer_q && i == issue_pointer_q)
// pointer = " <- issue, commit pointer";
// else if (i == top_pointer_q && i == issue_pointer_q)
// pointer = " <- top, issue pointer";
// else if (i == top_pointer_q && i == commit_pointer_q)
// pointer = " <- top, commit pointer";
// else if (i == top_pointer_q)
// pointer = " <- top pointer";
// else if (i == commit_pointer_q)
// pointer = " <- commit pointer";
// else if (i == issue_pointer_q)
// pointer = " <- issue pointer";
// else
// pointer = "";
// $fwrite(f, "|_________________________| %s\n", pointer);
// end
// $fwrite(f, "\n");
// end
// $fclose(f);
// end
`endif
`endif
endmodule
endmodule

View file

@ -9,37 +9,39 @@
// Guard statement proposed by "Easier UVM" (doulos)
`ifndef SCOREBOARD_IF__SV
`define SCOREBOARD_IF_SV
import ariane_pkg::*;
interface scoreboard_if (input clk);
wire rst;
wire full;
wire flush;
wire [31:0][7:0] rd_clobber;
wire [31:0][$bits(fu_t)-1:0] rd_clobber;
wire [4:0] rs1_address;
wire [63:0] rs1;
wire rs1_valid;
wire [4:0] rs2_address;
wire [63:0] rs2;
wire rs2_valid;
wire commitnstr;
wire commit_ready;
wire decodednstr;
wire decodednstr_valid;
wire issuenstr;
wire issuenstr_valid;
wire issue_ready;
scoreboard_entry commit_instr;
wire commit_ack;
scoreboard_entry decoded_instr;
wire decoded_instr_valid;
scoreboard_entry issue_instr;
wire issue_instr_valid;
wire issue_ack;
wire [63:0] pc;
wire [63:0] wdata;
wire wb_valid;
// Scoreboard interface configured as master
clocking mck @(posedge clk);
output rst, flush, rs1_address, rs2_address, commit_ready, decodednstr, decodednstr_valid, issue_ready, pc, wdata, wb_valid;
input full, rd_clobber, rs1, rs1_valid, rs2, rs2_valid, commitnstr, issuenstr, issuenstr_valid;
output flush, rs1_address, rs2_address, commit_ack, decoded_instr, decoded_instr_valid, issue_ack, pc, wdata, wb_valid;
input full, rd_clobber, rs1, rs1_valid, rs2, rs2_valid, commit_instr, issue_instr, issue_instr_valid;
endclocking
// Scoreboard interface configured in passive mode (-> monitor)
clocking pck @(posedge clk);
input rst, flush, rs1_address, rs2_address, commit_ready, decodednstr, decodednstr_valid, issue_ready, pc, wdata, wb_valid,
full, rd_clobber, rs1, rs1_valid, rs2, rs2_valid, commitnstr, issuenstr, issuenstr_valid;
input flush, rs1_address, rs2_address, commit_ack, decoded_instr, decoded_instr_valid, issue_ack, pc, wdata, wb_valid,
full, rd_clobber, rs1, rs1_valid, rs2, rs2_valid, commit_instr, issue_instr, issue_instr_valid;
endclocking
modport master (clocking mck);

View file

@ -34,32 +34,32 @@ module alu_tb;
.is_equal_result_o ( ),
.adder_result_ext_o ( ),
.adder_result_o ( ),
.multdiv_en_i ( 1'b0 ),
.multdiv_en_i ( 1'b0 ),
.multdiv_operand_b_i ( 65'b0 ),
.multdiv_operand_a_i ( 65'b0 )
.multdiv_operand_a_i ( 65'b0 )
);
initial begin
// register the alu itnerface
uvm_config_db #(virtual fu_if)::set(null, "uvm_test_top", "fu_vif", alu_if);
// register the ALU interface
uvm_config_db #(virtual fu_if)::set(null, "uvm_test_top", "fu_vif", alu_if);
end
initial begin
clk = 1'b0;
rstn_i = 1'b0;
repeat(8) begin
#10ns clk = ~clk;
end
rstn_i = 1'b1;
forever begin
#10ns clk = ~clk;
end
rstn_i = 1'b0;
repeat(8)
#10ns clk = ~clk;
rstn_i = 1'b1;
forever
#10ns clk = ~clk;
end
initial begin
// print the topology
uvm_top.enable_print_topology = 1;
// Start UVM test
// Start UVM test
run_test();
end
endmodule

145
tb/scoreboard_tb.sv Executable file
View file

@ -0,0 +1,145 @@
// Author: Florian Zaruba, ETH Zurich
// Date: 10/04/2017
// Description: Top level testbench module. Instantiates the top level DUT, configures
// the virtual interfaces and starts the test passed by +UVM_TEST+
//
// Copyright (C) 2017 ETH Zurich, University of Bologna
// All rights reserved.
//
// TODO, test register read and clobber interface
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;
integer unsigned pc = 0;
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
initial begin
clk = 1'b0;
rst_ni = 1'b0;
repeat(8)
#10ns clk = ~clk;
rst_ni = 1'b1;
forever
#10ns clk = ~clk;
end
// push new decoded instructions
initial begin
wait(rst_ni == 1'b1);
// load the scoreboard until it is full
forever begin
@(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
end
end
scoreboard_entry issue_instruction;
// pull e.g. issue instructions
initial begin
wait(rst_ni == 1'b1);
forever begin
@(scoreboard_if.mck);
scoreboard_if.mck.wb_valid <= 1'b0;
// 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;
@(scoreboard_if.mck)
scoreboard_if.mck.issue_ack <= 1'b0;
// generate a delay between 1 and 3 cycles for WB
repeat ($urandom_range(0,3)) @(scoreboard_if.mck);
scoreboard_if.mck.pc <= issue_instruction.pc;
scoreboard_if.mck.wdata <= 64'h7777;
scoreboard_if.mck.wb_valid <= 1'b1;
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
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);
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
endmodule

27
tb/test/scoreboard_lib_pkg.sv Executable file
View file

@ -0,0 +1,27 @@
// Author: Florian Zaruba, ETH Zurich
// Date: 12/20/2016
// Description: This package contains all test related functionality.
//
// Copyright (C) 2017 ETH Zurich, University of Bologna
// All rights reserved.
package scoreboard_lib_pkg;
// Standard UVM import & include:
import uvm_pkg::*;
`include "uvm_macros.svh"
// Import the memory interface agent
import scoreboard_if_agent_pkg::*;
// import alu_env_pkg::*;
// import alu_sequence_pkg::*;
// Test based includes like base test class and specializations of it
// ----------------
// Base test class
// ----------------
// `include "alu_test_base.svh"
// -------------------
// Child test classes
// -------------------
// plain randomized test
// `include "alu_test.svh"
endpackage