mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-23 13:47:13 -04:00
🐛 Fix FIFO wrap around bug
This commit is contained in:
parent
a6c81e7cab
commit
650f514bb2
7 changed files with 191 additions and 139 deletions
|
@ -73,7 +73,7 @@ module fetch_fifo
|
|||
// we always need two empty places
|
||||
// as it could happen that we get two compressed instructions/cycle
|
||||
/* verilator lint_off WIDTH */
|
||||
assign full = (status_cnt_q >= DEPTH - 2);
|
||||
assign full = (status_cnt_q >= DEPTH - 3);
|
||||
assign empty = (status_cnt_q == 0);
|
||||
/* verilator lint_on WIDTH */
|
||||
// the output is valid if we are either empty or just got a valid
|
||||
|
@ -141,27 +141,25 @@ module fetch_fifo
|
|||
// check if the lower compressed instruction was no branch otherwise we will need to squash this instruction
|
||||
// but only if we predicted it to be taken, the predict was on the lower 16 bit compressed instruction
|
||||
if (in_rdata_q[17:16] != 2'b11 && !(branch_predict_q.valid && branch_predict_q.predict_taken && branch_predict_q.is_lower_16)) begin
|
||||
mem_n[write_pointer_q + 1].branch_predict = branch_predict_q;
|
||||
mem_n[write_pointer_q + 1].address = {in_addr_q[63:2], 2'b10};
|
||||
mem_n[write_pointer_q + 1].instruction = {16'b0, in_rdata_q[31:16]};
|
||||
mem_n[(write_pointer_q + 1) % DEPTH].branch_predict = branch_predict_q;
|
||||
mem_n[(write_pointer_q + 1) % DEPTH].address = {in_addr_q[63:2], 2'b10};
|
||||
mem_n[(write_pointer_q + 1) % DEPTH].instruction = {16'b0, in_rdata_q[31:16]};
|
||||
|
||||
status_cnt++;
|
||||
write_pointer++;
|
||||
$display("Instruction: [ c | c ] @ %t", $time);
|
||||
// or is it an unaligned 32 bit instruction like
|
||||
// ____________________________________________________
|
||||
// |instr [15:0] | instr [31:16] | compressed 1[15:0] |
|
||||
// |____________________________________________________
|
||||
end else begin
|
||||
// we've got an unaligned 32 bit instruction
|
||||
// check if the previous instruction was no predicted taken branch
|
||||
if (!(branch_predict_q.valid && branch_predict_q.predict_taken && branch_predict_q.is_lower_16)) begin
|
||||
// save the lower 16 bit
|
||||
unaligned_instr_n = in_rdata_q[31:16];
|
||||
// and that it was unaligned
|
||||
unaligned_n = 1'b1;
|
||||
// save the address as well
|
||||
unaligned_address_n = {in_addr_q[63:2], 2'b10};
|
||||
end
|
||||
end else if (!(branch_predict_q.valid && branch_predict_q.predict_taken && branch_predict_q.is_lower_16)) begin
|
||||
// save the lower 16 bit
|
||||
unaligned_instr_n = in_rdata_q[31:16];
|
||||
// and that it was unaligned
|
||||
unaligned_n = 1'b1;
|
||||
// save the address as well
|
||||
unaligned_address_n = {in_addr_q[63:2], 2'b10};
|
||||
$display("Instruction: [ i0 | c ] @ %t", $time);
|
||||
// this does not consume space in the FIFO
|
||||
end
|
||||
end else begin
|
||||
|
@ -174,6 +172,7 @@ module fetch_fifo
|
|||
mem_n[write_pointer_q].instruction = in_rdata_q;
|
||||
status_cnt++;
|
||||
write_pointer++;
|
||||
$display("Instruction: [ i ] @ %t", $time);
|
||||
end
|
||||
end
|
||||
// we have an outstanding unaligned instruction
|
||||
|
@ -191,28 +190,26 @@ module fetch_fifo
|
|||
// check if the lower compressed instruction was no branch otherwise we will need to squash this instruction
|
||||
// but only if we predicted it to be taken, the predict was on the lower 16 bit compressed instruction
|
||||
if (in_rdata_q[17:16] != 2'b11 && !(branch_predict_q.valid && branch_predict_q.predict_taken && branch_predict_q.is_lower_16)) begin
|
||||
mem_n[write_pointer_q + 1].branch_predict = branch_predict_q;
|
||||
mem_n[write_pointer_q + 1].address = {in_addr_q[63:2], 2'b10};
|
||||
mem_n[write_pointer_q + 1].instruction = {16'b0, in_rdata_q[31:16]};
|
||||
mem_n[(write_pointer_q + 1) % DEPTH].branch_predict = branch_predict_q;
|
||||
mem_n[(write_pointer_q + 1) % DEPTH].address = {in_addr_q[63:2], 2'b10};
|
||||
mem_n[(write_pointer_q + 1) % DEPTH].instruction = {16'b0, in_rdata_q[31:16]};
|
||||
status_cnt++;
|
||||
write_pointer++;
|
||||
// unaligned access served
|
||||
unaligned_n = 1'b0;
|
||||
$display("Instruction: [ c | i1 ] @ %t", $time);
|
||||
// or is it an unaligned 32 bit instruction like
|
||||
// ____________________________________________________
|
||||
// |instr [15:0] | instr [31:16] | compressed 1[15:0] |
|
||||
// |____________________________________________________
|
||||
end else begin
|
||||
// we've got an unaligned 32 bit instruction
|
||||
// check if the previous instruction was no predicted taken branch
|
||||
if (!(branch_predict_q.valid && branch_predict_q.predict_taken && branch_predict_q.is_lower_16)) begin
|
||||
// save the lower 16 bit
|
||||
unaligned_instr_n = in_rdata_q[31:16];
|
||||
// and that it was unaligned
|
||||
unaligned_n = 1'b1;
|
||||
// save the address as well
|
||||
unaligned_address_n = {in_addr_q[63:2], 2'b10};
|
||||
end
|
||||
end else if (!(branch_predict_q.valid && branch_predict_q.predict_taken && branch_predict_q.is_lower_16)) begin
|
||||
// save the lower 16 bit
|
||||
unaligned_instr_n = in_rdata_q[31:16];
|
||||
// and that it was unaligned
|
||||
unaligned_n = 1'b1;
|
||||
// save the address as well
|
||||
unaligned_address_n = {in_addr_q[63:2], 2'b10};
|
||||
$display("Instruction: [ i0 | i1 ] @ %t", $time);
|
||||
// this does not consume space in the FIFO
|
||||
end
|
||||
end
|
||||
|
|
|
@ -98,109 +98,107 @@ module prefetch_buffer
|
|||
NS = CS;
|
||||
|
||||
unique case(CS)
|
||||
// default state, not waiting for requested data
|
||||
IDLE: begin
|
||||
instr_addr_o = fetch_address_i;
|
||||
instr_req_o = 1'b0;
|
||||
// default state, not waiting for requested data
|
||||
IDLE: begin
|
||||
instr_addr_o = fetch_address_i;
|
||||
instr_req_o = 1'b0;
|
||||
|
||||
if (fifo_ready && fetch_valid_i) begin
|
||||
instr_req_o = 1'b1;
|
||||
addr_valid = 1'b1;
|
||||
// make a new request
|
||||
if (fifo_ready && fetch_valid_i) begin
|
||||
instr_req_o = 1'b1;
|
||||
addr_valid = 1'b1;
|
||||
|
||||
|
||||
if(instr_gnt_i) //~> granted request
|
||||
if(instr_gnt_i) //~> granted request
|
||||
// we have one outstanding rvalid: wait for it
|
||||
if (flush_i)
|
||||
NS = WAIT_ABORTED;
|
||||
else
|
||||
NS = WAIT_RVALID;
|
||||
else begin //~> got a request but no grant
|
||||
NS = WAIT_GNT;
|
||||
end
|
||||
end
|
||||
end // case: IDLE
|
||||
|
||||
// we sent a request but did not yet get a grant
|
||||
WAIT_GNT: begin
|
||||
instr_addr_o = instr_addr_q;
|
||||
instr_req_o = 1'b1;
|
||||
|
||||
if(instr_gnt_i)
|
||||
// we have one outstanding rvalid: wait for it
|
||||
if (flush_i)
|
||||
NS = WAIT_ABORTED;
|
||||
else
|
||||
NS = WAIT_RVALID;
|
||||
else begin //~> got a request but no grant
|
||||
NS = WAIT_GNT;
|
||||
end
|
||||
end
|
||||
end // case: IDLE
|
||||
|
||||
// we sent a request but did not yet get a grant
|
||||
WAIT_GNT: begin
|
||||
instr_addr_o = instr_addr_q;
|
||||
instr_req_o = 1'b1;
|
||||
|
||||
if(instr_gnt_i)
|
||||
// we have one outstanding rvalid: wait for it
|
||||
if (flush_i)
|
||||
NS = WAIT_ABORTED;
|
||||
else
|
||||
NS = WAIT_RVALID;
|
||||
else
|
||||
NS = WAIT_GNT;
|
||||
end // case: WAIT_GNT
|
||||
NS = WAIT_GNT;
|
||||
end // case: WAIT_GNT
|
||||
|
||||
// we wait for rvalid, after that we are ready to serve a new request
|
||||
WAIT_RVALID: begin
|
||||
instr_addr_o = fetch_address_i;
|
||||
// we wait for rvalid, after that we are ready to serve a new request
|
||||
WAIT_RVALID: begin
|
||||
instr_addr_o = fetch_address_i;
|
||||
// prepare for next request
|
||||
if (fifo_ready && fetch_valid_i) begin
|
||||
// wait for the valid signal
|
||||
if (instr_rvalid_i) begin
|
||||
instr_req_o = 1'b1;
|
||||
fifo_valid = 1'b1;
|
||||
addr_valid = 1'b1;
|
||||
|
||||
if (fifo_ready) begin
|
||||
// prepare for next request
|
||||
if (fifo_ready && fetch_valid_i) begin
|
||||
instr_req_o = 1'b1;
|
||||
// if we are receiving a data item during a flush ignore it
|
||||
fifo_valid = 1'b1;
|
||||
addr_valid = 1'b1;
|
||||
|
||||
if (instr_gnt_i) begin
|
||||
// we have one outstanding rvalid: wait for it
|
||||
if (flush_i)
|
||||
NS = WAIT_ABORTED;
|
||||
else
|
||||
NS = WAIT_RVALID;
|
||||
if (instr_gnt_i) begin
|
||||
// we have one outstanding rvalid: wait for it
|
||||
// if we are receiving a data item during a flush ignore it
|
||||
if (flush_i)
|
||||
NS = WAIT_ABORTED;
|
||||
else
|
||||
NS = WAIT_RVALID;
|
||||
end else begin
|
||||
NS = WAIT_GNT;
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
NS = WAIT_GNT;
|
||||
end
|
||||
end else begin
|
||||
// we are requested to abort our current request
|
||||
// we didn't get an rvalid yet, so wait for it
|
||||
if (flush_i) begin
|
||||
NS = WAIT_ABORTED;
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
// just wait for rvalid and go back to IDLE, no new request
|
||||
if (instr_rvalid_i) begin
|
||||
// if we are receiving a data item during a flush ignore it
|
||||
fifo_valid = 1'b1;
|
||||
NS = IDLE;
|
||||
end
|
||||
end
|
||||
end // case: WAIT_RVALID
|
||||
|
||||
// our last request was aborted, but we didn't yet get a rvalid and
|
||||
// there was no new request sent yet
|
||||
// we assume that req_i is set to high
|
||||
WAIT_ABORTED: begin
|
||||
instr_addr_o = fetch_address_i;
|
||||
|
||||
if (instr_rvalid_i) begin
|
||||
instr_req_o = 1'b1;
|
||||
// no need to send address, already done in WAIT_RVALID
|
||||
|
||||
if (instr_gnt_i) begin
|
||||
// we have one outstanding rvalid
|
||||
if (flush_i)
|
||||
// we are requested to abort our current request
|
||||
// we didn't get an rvalid yet, so wait for it
|
||||
if (flush_i) begin
|
||||
NS = WAIT_ABORTED;
|
||||
else
|
||||
NS = WAIT_RVALID;
|
||||
end else begin
|
||||
NS = WAIT_GNT;
|
||||
end
|
||||
end
|
||||
// just wait for rvalid and go back to IDLE, no new request
|
||||
if (instr_rvalid_i) begin
|
||||
// if we are receiving a data item during a flush ignore it
|
||||
fifo_valid = 1'b1;
|
||||
NS = IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
end // case: WAIT_RVALID
|
||||
|
||||
// our last request was aborted, but we didn't yet get a rvalid and
|
||||
// there was no new request sent yet we assume that req_i is set to high
|
||||
WAIT_ABORTED: begin
|
||||
instr_addr_o = fetch_address_i;
|
||||
|
||||
if (instr_rvalid_i) begin
|
||||
instr_req_o = 1'b1;
|
||||
|
||||
if (instr_gnt_i) begin
|
||||
// we have one outstanding rvalid
|
||||
if (flush_i)
|
||||
NS = WAIT_ABORTED;
|
||||
else
|
||||
NS = WAIT_RVALID;
|
||||
end else begin
|
||||
NS = WAIT_GNT;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
default: begin
|
||||
NS = IDLE;
|
||||
instr_req_o = 1'b0;
|
||||
end
|
||||
default: begin
|
||||
NS = IDLE;
|
||||
instr_req_o = 1'b0;
|
||||
end
|
||||
endcase
|
||||
|
||||
end
|
||||
|
||||
//-------------
|
||||
|
|
|
@ -21,9 +21,7 @@
|
|||
`define FETCH_FIFO_IF_SV
|
||||
import ariane_pkg::*;
|
||||
|
||||
interface fetch_fifo_if #(
|
||||
parameter type dtype = logic[7:0]
|
||||
)(
|
||||
interface fetch_fifo_if (
|
||||
input clk
|
||||
);
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ import fetch_fifo_pkg::*;
|
|||
module fetch_fifo_tb;
|
||||
|
||||
logic rst_ni, clk_i;
|
||||
fetch_fifo_if fetch_fifo_if (clk);
|
||||
fetch_fifo_if fetch_fifo_if (clk_i);
|
||||
|
||||
fetch_fifo
|
||||
dut (
|
||||
|
@ -57,25 +57,38 @@ module fetch_fifo_tb;
|
|||
// simulator stopper, this is suboptimal better go for coverage
|
||||
initial begin
|
||||
#10000000ns
|
||||
$stop;
|
||||
$finish;
|
||||
end
|
||||
|
||||
program testbench (fetch_fifo_if fetch_fifo_if);
|
||||
|
||||
instruction_stream is = new;
|
||||
fetch_fifo_model model = new;
|
||||
instruction_queue_entry_t iqe;
|
||||
|
||||
initial begin
|
||||
|
||||
fetch_fifo_if.mck.flush <= 1'b0;
|
||||
fetch_fifo_if.mck.in_branch_predict <= 'b0;
|
||||
fetch_fifo_if.mck.in_addr <= 'b0;
|
||||
fetch_fifo_if.mck.in_rdata <= 'b0;
|
||||
fetch_fifo_if.mck.in_valid <= 'b0;
|
||||
fetch_fifo_if.mck.out_ready <= 'b0;
|
||||
wait(rst_ni == 1'b1);
|
||||
|
||||
// Driver
|
||||
forever begin
|
||||
is.get_instruction();
|
||||
// @(fetch_fifo_if.mck);
|
||||
@(fetch_fifo_if.mck iff fetch_fifo_if.in_ready);
|
||||
|
||||
do begin
|
||||
iqe = is.get_instruction();
|
||||
fetch_fifo_if.mck.in_addr <= iqe.address;
|
||||
fetch_fifo_if.mck.in_rdata <= iqe.instr;
|
||||
fetch_fifo_if.mck.in_branch_predict <= iqe.bp;
|
||||
fetch_fifo_if.mck.in_valid <= 1'b1;
|
||||
@(fetch_fifo_if.mck);
|
||||
end while (fetch_fifo_if.mck.in_ready);
|
||||
fetch_fifo_if.mck.in_valid <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -19,44 +19,79 @@
|
|||
//
|
||||
|
||||
// Read 32 bit instruction, separate and re-align them
|
||||
typedef struct {
|
||||
logic [63:0] address;
|
||||
logic [31:0] instr;
|
||||
branchpredict_sbe bp;
|
||||
} instruction_queue_entry_t;
|
||||
|
||||
class fetch_fifo_model;
|
||||
|
||||
logic [15:0] unaligned_part;
|
||||
int is_unaligned = 0;
|
||||
logic [63:0] unaligend_address;
|
||||
|
||||
logic [31:0] instruction_queue[$];
|
||||
instruction_queue_entry_t instruction_queue[$];
|
||||
|
||||
function void put(logic [31:0] instr);
|
||||
function void put(logic [63:0] address, logic [31:0] instr, branchpredict_sbe bp);
|
||||
instruction_queue_entry_t param;
|
||||
|
||||
if (is_unaligned == 0) begin
|
||||
// we've generated a compressed instruction so generate another one
|
||||
// we've got a compressed instruction
|
||||
if (instr[1:0] != 2'b11) begin
|
||||
instruction_queue.push_back({16'b0, instr[15:0]});
|
||||
param.address = address;
|
||||
param.instr = {16'b0, instr[15:0]};
|
||||
param.bp = bp;
|
||||
|
||||
instruction_queue.push_back(param);
|
||||
// the upper part is a unaligned 32 bit instruction
|
||||
if (instr[17:16] == 2'b11) begin
|
||||
is_unaligned = 1;
|
||||
unaligned_part = instr[31:16];
|
||||
unaligend_address = {address[63:2], 2'b10};
|
||||
is_unaligned = 1;
|
||||
unaligned_part = instr[31:16];
|
||||
// there is another compressed instruction
|
||||
// don't include if branch prediction predicted a compressed
|
||||
// branch in the first instruction part
|
||||
end else if (!(bp.predict_taken && bp.valid && bp.is_lower_16)) begin
|
||||
param.address = {address[63:2], 2'b10};
|
||||
param.instr = instr[31:16];
|
||||
param.bp = bp;
|
||||
instruction_queue.push_back(param);
|
||||
end
|
||||
// normal instruction
|
||||
end else begin
|
||||
instruction_queue.push_back(instr);
|
||||
param.address = address;
|
||||
param.instr = instr;
|
||||
param.bp = bp;
|
||||
instruction_queue.push_back(param);
|
||||
end
|
||||
// the last generation iteration produced an outstanding instruction
|
||||
end else begin
|
||||
instruction_queue.push_back({instr[15:0], unaligned_part});
|
||||
|
||||
param.address = unaligend_address;
|
||||
param.instr = {instr[15:0], unaligned_part};
|
||||
param.bp = bp;
|
||||
instruction_queue.push_back(param);
|
||||
// there is another compressed instruction
|
||||
// don't include if branch prediction predicted a compressed
|
||||
// branch in the first instruction part
|
||||
if (instr[17:16] != 2'b11) begin
|
||||
instruction_queue.push_back({16'b0, instr[31:16]});
|
||||
if (!(bp.predict_taken && bp.valid && bp.is_lower_16)) begin
|
||||
param.address = {address[63:2], 2'b10};
|
||||
param.instr = instr[31:16];
|
||||
param.bp = bp;
|
||||
instruction_queue.push_back(param);
|
||||
end
|
||||
is_unaligned = 0;
|
||||
end else begin
|
||||
// again we have an unaligned instruction
|
||||
param.address = {address[63:2], 2'b10};
|
||||
is_unaligned = 1;
|
||||
unaligned_part = instr[31:16];
|
||||
end
|
||||
end
|
||||
endfunction : put
|
||||
|
||||
function logic [31:0] pull();
|
||||
function instruction_queue_entry_t pull();
|
||||
return instruction_queue.pop_front();
|
||||
endfunction : pull
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
//
|
||||
|
||||
package fetch_fifo_pkg;
|
||||
`include "instruction_stream.svh"
|
||||
import ariane_pkg::*;
|
||||
`include "fetch_fifo_model.svh"
|
||||
`include "instruction_stream.svh"
|
||||
endpackage
|
|
@ -43,11 +43,16 @@ endclass : instruction
|
|||
|
||||
class instruction_stream;
|
||||
|
||||
logic [63:0] address = 0;
|
||||
instruction instr;
|
||||
logic [15:0] unaligned_part;
|
||||
int is_unaligned = 0;
|
||||
// get an instruction stream of consecutive data
|
||||
function logic [31:0] get_instruction();
|
||||
function instruction_queue_entry_t get_instruction();
|
||||
|
||||
branchpredict_sbe bp = '0;
|
||||
instruction_queue_entry_t return_entry;
|
||||
|
||||
logic [31:0] return_instruction;
|
||||
// generate a new instruction
|
||||
if (is_unaligned == 0) begin
|
||||
|
@ -59,7 +64,7 @@ class instruction_stream;
|
|||
// get a new instruction
|
||||
instr = new;
|
||||
void'(randomize(instr));
|
||||
return_instruction[31:0] = instr.instruction[15:0];
|
||||
return_instruction[31:16] = instr.instruction[15:0];
|
||||
// $display("Instruction: [ c | c ]");
|
||||
// was this a compressed instruction as well?
|
||||
// if not than store that this was an unaligned access
|
||||
|
@ -79,9 +84,9 @@ class instruction_stream;
|
|||
// generate a new isntruction
|
||||
instr = new;
|
||||
void'(randomize(instr));
|
||||
return_instruction [31:16] = instr.instruction[15:0];
|
||||
// was it compressed?
|
||||
if (instr.is_compressed) begin
|
||||
return_instruction [31:16] = instr.instruction[15:0];
|
||||
is_unaligned = 0;
|
||||
// $display("Instruction: [ c | i1 ]");
|
||||
end else begin
|
||||
|
@ -90,8 +95,13 @@ class instruction_stream;
|
|||
// $display("Instruction: [ i0 | i1 ]");
|
||||
end
|
||||
end
|
||||
return_entry.instr = return_instruction;
|
||||
return_entry.bp = bp;
|
||||
return_entry.address = address;
|
||||
|
||||
return return_instruction;
|
||||
address = address + 4;
|
||||
|
||||
return return_entry;
|
||||
endfunction : get_instruction
|
||||
|
||||
endclass : instruction_stream
|
Loading…
Add table
Add a link
Reference in a new issue