🐛 Fix FIFO wrap around bug

This commit is contained in:
Florian Zaruba 2017-05-15 00:37:31 +02:00
parent a6c81e7cab
commit 650f514bb2
7 changed files with 191 additions and 139 deletions

View file

@ -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

View file

@ -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
//-------------

View file

@ -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
);

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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