diff --git a/docs/block_details.md b/docs/block_details.md index 0bf2f35f7..d74812b1f 100644 --- a/docs/block_details.md +++ b/docs/block_details.md @@ -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 diff --git a/docs/synthesis.md b/docs/synthesis.md index fc7aef627..485a05a43 100644 --- a/docs/synthesis.md +++ b/docs/synthesis.md @@ -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 \ No newline at end of file diff --git a/scoreboard.sv b/scoreboard.sv index 26cdeca78..5d7c7edef 100644 --- a/scoreboard.sv +++ b/scoreboard.sv @@ -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 \ No newline at end of file +endmodule diff --git a/tb/agents/scoreboard_if/scoreboard_if.sv b/tb/agents/scoreboard_if/scoreboard_if.sv index 26b79621c..f537aee00 100644 --- a/tb/agents/scoreboard_if/scoreboard_if.sv +++ b/tb/agents/scoreboard_if/scoreboard_if.sv @@ -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); diff --git a/tb/alu_tb.sv b/tb/alu_tb.sv index 503297133..bc6de641a 100644 --- a/tb/alu_tb.sv +++ b/tb/alu_tb.sv @@ -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 diff --git a/tb/scoreboard_tb.sv b/tb/scoreboard_tb.sv new file mode 100755 index 000000000..246a1c63b --- /dev/null +++ b/tb/scoreboard_tb.sv @@ -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 \ No newline at end of file diff --git a/tb/test/scoreboard_lib_pkg.sv b/tb/test/scoreboard_lib_pkg.sv new file mode 100755 index 000000000..1c9b62662 --- /dev/null +++ b/tb/test/scoreboard_lib_pkg.sv @@ -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