Issuing write back out of order, fixed bug in comb

This commit is contained in:
Florian Zaruba 2017-04-12 15:19:08 +02:00
parent e66a683e8d
commit 7ee4add5b6
3 changed files with 51 additions and 33 deletions

View file

@ -49,9 +49,9 @@ module scoreboard #(
);
localparam BITS_ENTRIES = $clog2(NR_ENTRIES);
dtype [NR_ENTRIES-1:0] mem;
dtype [NR_ENTRIES-1:0] mem_q, mem_n;
logic [BITS_ENTRIES-1:0] issue_pointer_n, issue_pointer_q, // points to the instruction currently in issue
commit_pointer_n, commit_pointer_q, commit_pointer_qq, // points to the instruction currently in commit
commit_pointer_n, commit_pointer_q, // 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;
@ -84,12 +84,12 @@ always_comb begin : clobber_output
for (int unsigned i = 0; i < NR_ENTRIES; i++) begin
// non overflowed case, depicted on the left
if (i[BITS_ENTRIES-1:0] >= commit_pointer_q && i[BITS_ENTRIES-1:0] < issue_pointer_q)
rd_clobber_o[mem[i].rd] = mem[i].fu;
rd_clobber_o[mem_q[i].rd] = mem_q[i].fu;
end
end else begin // the issue pointer has overflowed, invert logic, depicted on the right
for (int unsigned i = 0; i < NR_ENTRIES; i++) begin
if (i[BITS_ENTRIES-1:0] >= commit_pointer_q || i[BITS_ENTRIES-1:0] < issue_pointer_q)
rd_clobber_o[mem[i].rd] = mem[i].fu;
rd_clobber_o[mem_q[i].rd] = mem_q[i].fu;
end
end
// the zero register is always free
@ -108,13 +108,13 @@ always_comb begin : read_operands
if (i[BITS_ENTRIES-1:0] >= commit_pointer_q && i[BITS_ENTRIES-1:0] < issue_pointer_q) begin
// look at the appropriate fields and look whether there was an
// instruction that wrote the rd field before, first for RS1 and then for RS2
if (mem[i[BITS_ENTRIES-1:0]].rd == rs1_i) begin
rs1_o = mem[i].result;
rs1_valid_o = mem[i].valid;
if (mem_q[i[BITS_ENTRIES-1:0]].rd == rs1_i) begin
rs1_o = mem_q[i].result;
rs1_valid_o = mem_q[i].valid;
// do the same for rs2
end else if (mem[i].rd == rs2_i) begin
rs2_o = mem[i].result;
rs2_valid_o = mem[i].valid;
end else if (mem_q[i].rd == rs2_i) begin
rs2_o = mem_q[i].result;
rs2_valid_o = mem_q[i].valid;
end
end
end
@ -122,13 +122,13 @@ always_comb begin : read_operands
for (int unsigned i = 0; i < NR_ENTRIES; i++) begin
if (i[BITS_ENTRIES-1:0] >= commit_pointer_q || i[BITS_ENTRIES-1:0] < issue_pointer_q) begin
// same as above but for the overflowed pointer case
if (mem[i[BITS_ENTRIES-1:0]].rd == rs1_i) begin
rs1_o = mem[i].result;
rs1_valid_o = mem[i].valid;
if (mem_q[i[BITS_ENTRIES-1:0]].rd == rs1_i) begin
rs1_o = mem_q[i].result;
rs1_valid_o = mem_q[i].valid;
// do the same for rs2
end else if (mem[i[BITS_ENTRIES-1:0]].rd == rs2_i) begin
rs2_o = mem[i].result;
rs2_valid_o = mem[i].valid;
end else if (mem_q[i[BITS_ENTRIES-1:0]].rd == rs2_i) begin
rs2_o = mem_q[i].result;
rs2_valid_o = mem_q[i].valid;
end
end
end
@ -144,9 +144,10 @@ end
always_latch begin : push_instruction_and_wb
// default assignment
top_pointer_n = top_pointer_q;
mem_n = mem_q;
// if we are not full we can push a new instruction
if (~full_o && decoded_instr_valid_i) begin
mem[$unsigned(top_pointer_q)] = decoded_instr_i;
mem_n[$unsigned(top_pointer_q)] = decoded_instr_i;
top_pointer_n = top_pointer_q + 1;
end
@ -155,16 +156,16 @@ always_latch begin : push_instruction_and_wb
// also set the valid bit
if (wb_valid_i) begin
for (int unsigned i = 0; i < NR_ENTRIES; i++) begin
if (mem[i].pc == pc_i) begin
mem[i].valid = 1'b1;
mem[i].result = wdata_i;
if (mem_q[i].pc == pc_i) begin
mem_n[i].valid = 1'b1;
mem_n[i].result = wdata_i;
end
end
end
// flush signal
if (flush_i)
mem = '{default: 0};
mem_n = '{default: 0};
end
@ -173,16 +174,16 @@ always_comb begin : issue_instruction
// provide a combinatorial path in case the scoreboard is empty
if (empty) begin
if (top_pointer_q == issue_pointer_q) begin
issue_instr_o = decoded_instr_i;
issue_instr_valid_o = decoded_instr_valid_i;
// 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_o = mem_q[$unsigned(issue_pointer_q)];
// 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)
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;
@ -210,7 +211,7 @@ always_comb begin: commit_instruction
commit_pointer_n = commit_pointer_q;
// 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];
commit_instr_o = mem_q[commit_pointer_q];
if (commit_ack_i) begin
commit_pointer_n = commit_pointer_q + 1;
end
@ -223,15 +224,18 @@ always_ff @(posedge clk_i or negedge rst_ni) begin : sequential
commit_pointer_q <= '{default: 0};
top_pointer_q <= '{default: 0};
top_pointer_qq <= '{default: 0};
mem_q <= '{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};
mem_q <= '{default: 0};
end else begin
issue_pointer_q <= issue_pointer_n;
commit_pointer_q <= commit_pointer_n;
top_pointer_q <= top_pointer_n;
mem_q <= mem_n;
if (decoded_instr_valid_i) // only advance if we decoded instruction
top_pointer_qq <= top_pointer_q;
end

View file

@ -35,6 +35,7 @@ interface scoreboard_if (input clk);
// Scoreboard interface configured as master
clocking mck @(posedge clk);
default input #1 output #5; // save timing
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

View file

@ -6,7 +6,7 @@
// Copyright (C) 2017 ETH Zurich, University of Bologna
// All rights reserved.
//
// TODO, test register read and clobber interface
// TODO, test register read and clobber interface, make a proper TB out of it
module scoreboard_tb;
import uvm_pkg::*;
@ -16,6 +16,7 @@ logic rst_ni, clk;
scoreboard_entry scoreboard_queue[$];
scoreboard_entry temp_scoreboard_entry;
scoreboard_entry comp;
semaphore wb_lock = new(1);
integer unsigned pc = 0;
@ -97,19 +98,30 @@ scoreboard dut (
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;
$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 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;
// 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
@ -122,6 +134,7 @@ scoreboard dut (
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;