Add fetch fifo testbench stub

This commit is contained in:
Florian Zaruba 2017-05-14 20:11:39 +02:00
parent 5fe47f0786
commit 851faa7851
10 changed files with 280 additions and 106 deletions

View file

@ -8,7 +8,7 @@ library = work
top_level = core_tb
test_top_level = core_tb
# test targets
tests = alu scoreboard fifo mem_arbiter store_queue lsu core
tests = alu scoreboard fifo mem_arbiter store_queue lsu core fetch_fifo
# UVM agents
agents = include/ariane_pkg.svh $(wildcard tb/agents/*/*.sv)
# path to interfaces

View file

@ -66,29 +66,31 @@ module branch_engine (
resolved_branch_o.is_taken = 1'b0;
resolved_branch_o.valid = valid_i;
resolved_branch_o.is_mispredict = 1'b0;
resolved_branch_o.is_lower_16 = 1'b0;
// calculate next PC, depending on whether the instruction is compressed or not this may be different
next_pc = pc_i + ((is_compressed_instr_i) ? 64'h2 : 64'h4);
// calculate target address simple 64 bit addition
target_address = $unsigned($signed(operand_c_i) + $signed(imm_i));
// save PC - we need this to get the target row in the branch target buffer
// we play this trick with the branch instruction which wraps a byte boundary:
// |---------- Place the prediction on this PC
// \/
// ____________________________________________________
// |branch [15:0] | branch[31:16] | compressed 1[15:0] |
// |____________________________________________________
// This will relief the prefetcher to re-fetch partially fetched unaligned branch instructions e.g.:
// we don't have a back arch between prefetcher and decoder/instruction FIFO.
resolved_branch_o.pc = (is_compressed_instr_i || pc_i[1] == 1'b0) ? pc_i : ({pc_i[63:2], 2'b0} + 64'h4);
// save if the branch instruction was in the lower 16 bit of the instruction word
// the first case is a compressed instruction which is in slot 0
// the other case is a misaligned uncompressed instruction which we only predict in the next cycle (see notes above)
resolved_branch_o.is_lower_16 = (is_compressed_instr_i && pc_i[1] == 1'b0) || (!is_compressed_instr_i && pc_i[1] == 1'b1);
// write target address which goes to pc gen
resolved_branch_o.target_address = (comparison_result) ? target_address : next_pc;
resolved_branch_o.is_taken = comparison_result;
// we've detected a branch in ID with the following parameters
if (valid_i) begin
// save PC - we need this to get the target row in the branch target buffer
// we play this trick with the branch instruction which wraps a byte boundary:
// |---------- Place the prediction on this PC
// \/
// ____________________________________________________
// |branch [15:0] | branch[31:16] | compressed 1[15:0] |
// |____________________________________________________
// This will relief the prefetcher to re-fetch partially fetched unaligned branch instructions e.g.:
// we don't have a back arch between prefetcher and decoder/instruction FIFO.
resolved_branch_o.pc = (is_compressed_instr_i || pc_i[1] == 1'b0) ? pc_i : ({pc_i[63:2], 2'b0} + 64'h4);
// save if the branch instruction was in the lower 16 bit of the instruction word
// the first case is a compressed instruction which is in slot 0
// the other case is a misaligned uncompressed instruction which we only predict in the next cycle (see notes above)
resolved_branch_o.is_lower_16 = (is_compressed_instr_i && pc_i[1] == 1'b0) || (!is_compressed_instr_i && pc_i[1] == 1'b1);
// write target address which goes to pc gen
resolved_branch_o.target_address = (comparison_result) ? target_address : next_pc;
resolved_branch_o.is_taken = comparison_result;
// we've detected a branch in ID with the following parameters
// we mis-predicted e.g.: the predicted address is unequal to the actual address
if (target_address[0] == 1'b0) begin
// TODO in case of branch which is not taken it is not necessary to check for the address

View file

@ -19,7 +19,7 @@
import ariane_pkg::*;
module btb #(
parameter int NR_ENTRIES = 64,
parameter int NR_ENTRIES = 1024,
parameter int BITS_SATURATION_COUNTER = 2
)
(

View file

@ -1,36 +1,30 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 ETH Zurich, University of Bologna //
// All rights reserved. //
// //
// This code is under development and not yet released to the public. //
// Until it is released, the code is under the copyright of ETH Zurich //
// and the University of Bologna, and may contain unpublished work. //
// Any reuse/redistribution should only be under explicit permission. //
// //
// Bug fixes and contributions will eventually be released under the //
// SolderPad open hardware license and under the copyright of ETH Zurich //
// and the University of Bologna. //
// //
// Engineer: Andreas Traber - atraber@iis.ee.ethz.ch //
// //
// Design Name: Fetch Fifo for 32 bit memory interface //
// Project Name: zero-riscy //
// Language: SystemVerilog //
// //
// Description: Fetch fifo //
////////////////////////////////////////////////////////////////////////////////
// Author: Florian Zaruba, ETH Zurich
// Date: 14.05.2017
// Description: Dual Port fetch FIFO with instruction aligner and support for compressed instructions
//
// Copyright (C) 2017 ETH Zurich, University of Bologna
// All rights reserved.
//
// This code is under development and not yet released to the public.
// Until it is released, the code is under the copyright of ETH Zurich and
// the University of Bologna, and may contain confidential and/or unpublished
// work. Any reuse/redistribution is strictly forbidden without written
// permission from ETH Zurich.
//
// Bug fixes and contributions will eventually be released under the
// SolderPad open hardware license in the context of the PULP platform
// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the
// University of Bologna.
//
import ariane_pkg::*;
// input port: send address one cycle before the data
// clear_i clears the FIFO for the following cycle.
module fetch_fifo
(
input logic clk_i,
input logic rst_ni,
// control signals
input logic clear_i, // clears the contents of the fifo
// input port
input logic flush_i, // clears the contents of the FIFO -> quasi reset
// branch prediction at in_addr_i address, as this is an address and not PC it can be the case
// that we have two compressed instruction (or one compressed instruction and one unaligned instruction) so we need
// keep two prediction inputs: [c1|c0] <- prediction for c1 and c0
@ -68,7 +62,7 @@ module fetch_fifo
logic [$clog2(DEPTH)-1:0] status_cnt_n, status_cnt_q; // this integer will be truncated by the synthesis tool
// status signals
logic full, empty, one_left;
logic full, empty;
// the last instruction was unaligned
logic unaligned_n, unaligned_q;
// save the unaligned part of the instruction to this ff
@ -79,8 +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 one_left = (status_cnt_q == DEPTH - 1); // two spaces are left
assign full = (status_cnt_q >= DEPTH - 2);
assign empty = (status_cnt_q == 0);
/* verilator lint_on WIDTH */
// the output is valid if we are either empty or just got a valid
@ -106,7 +99,7 @@ module fetch_fifo
branch_predict_n = branch_predict_i;
end
// flush the input registers
if (clear_i) begin
if (flush_i) begin
in_valid_n = 1'b0;
end
end
@ -251,17 +244,17 @@ module fetch_fifo
out_rdata_o = {in_rdata_q[15:0], unaligned_instr_q};
end
// there is currently no valid instruction in the pipeline register push this instruction
if (out_ready_i || !pipelein_register_valid_q) begin
pipelein_register_valid_n = 1'b1;
read_pointer_n = read_pointer_q + 1;
status_cnt--;
end
// if (out_ready_i) begin
// pipelein_register_valid_n = 1'b1;
// read_pointer_n = read_pointer_q + 1;
// status_cnt--;
// end
// regular read but do not issue if we are already empty
// this can happen since we have an output latch in the IF stage and the ID stage will only know a cycle
// later that we do not have any valid instructions anymore
end
if (out_ready_i && !empty) begin
if (out_ready_i) begin
read_pointer_n = read_pointer_q + 1;
status_cnt--;
end
@ -273,39 +266,55 @@ module fetch_fifo
write_pointer_n = write_pointer;
status_cnt_n = status_cnt;
if (clear_i)
status_cnt_n = '0;
if (flush_i) begin
status_cnt_n = '0;
write_pointer_n = 'b0;
read_pointer_n = 'b0;
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (~rst_ni) begin
status_cnt_q <= '{default: 0};
mem_q <= '{default: 0};
read_pointer_q <= '{default: 0};
write_pointer_q <= '{default: 0};
unaligned_q <= 1'b0;
unaligned_instr_q <= 16'b0;
unaligned_address_q <= 64'b0;
status_cnt_q <= '{default: 0};
mem_q <= '{default: 0};
read_pointer_q <= '{default: 0};
write_pointer_q <= '{default: 0};
unaligned_q <= 1'b0;
unaligned_instr_q <= 16'b0;
unaligned_address_q <= 64'b0;
// input registers
in_addr_q <= 64'b0;
in_rdata_q <= 32'b0;
in_valid_q <= 1'b0;
branch_predict_q <= '{default: 0};
in_addr_q <= 64'b0;
in_rdata_q <= 32'b0;
in_valid_q <= 1'b0;
branch_predict_q <= '{default: 0};
pipelein_register_valid_q <= 1'b0;
end else begin
status_cnt_q <= status_cnt_n;
mem_q <= mem_n;
read_pointer_q <= read_pointer_n;
write_pointer_q <= write_pointer_n;
unaligned_q <= unaligned_n;
unaligned_instr_q <= unaligned_instr_n;
unaligned_address_q <= unaligned_address_n;
status_cnt_q <= status_cnt_n;
mem_q <= mem_n;
read_pointer_q <= read_pointer_n;
write_pointer_q <= write_pointer_n;
unaligned_q <= unaligned_n;
unaligned_instr_q <= unaligned_instr_n;
unaligned_address_q <= unaligned_address_n;
// input registers
in_addr_q <= in_addr_n;
in_rdata_q <= in_rdata_n;
in_valid_q <= in_valid_n;
branch_predict_q <= branch_predict_n;
in_addr_q <= in_addr_n;
in_rdata_q <= in_rdata_n;
in_valid_q <= in_valid_n;
branch_predict_q <= branch_predict_n;
pipelein_register_valid_q <= pipelein_register_valid_n;
end
end
//-------------
// Assertions
//-------------
`ifndef SYNTHESIS
`ifndef VERILATOR
// since this is a dual port queue the status count of the queue should never change more than two
assert property (@(posedge clk_i) ((status_cnt_n - status_cnt_q) < 3 || (status_cnt_n - status_cnt_q) > 3)) else $error("FIFO underflowed or overflowed");
// assert property (
// @(posedge clk_i) (instr_gnt_i) |-> (instr_req_o) )
// else $warning("There was a grant without a request");
`endif
`endif
endmodule

View file

@ -66,12 +66,12 @@ module if_stage (
// ---------------------
// IF <-> ID Registers
// ---------------------
logic [63:0] pc_n, pc_q;
logic instr_valid_n, instr_valid_q;
logic [31:0] instr_rdata_n, instr_rdata_q;
logic instr_is_compressed_n, instr_is_compressed_q;
logic [63:0] pc_n, pc_q;
logic instr_valid_n, instr_valid_q;
logic [31:0] instr_rdata_n, instr_rdata_q;
logic instr_is_compressed_n, instr_is_compressed_q;
// branch predict registers
logic branch_predict_n, branch_predict_q;
branchpredict_sbe branch_predict_n, branch_predict_q;
// compressed instruction decoding, or more precisely compressed instruction expander
// since it does not matter where we decompress instructions, we do it here to ease timing closure
@ -116,7 +116,7 @@ module if_stage (
if (flush_i) begin
instr_valid_n = 1'b0;
end
// exception forwarding in here
// TODO: exception forwarding in here
end
// --------------------------------------------------------------
@ -125,7 +125,7 @@ module if_stage (
always_ff @(posedge clk_i, negedge rst_ni) begin : IF_ID_PIPE_REGISTERS
if (~rst_ni) begin
ex_o <= '{default: 0};
branch_predict_q <= '{default: 0};
branch_predict_q <= '0;
pc_q <= 64'b0;
instr_valid_q <= 1'b0;
instr_rdata_q <= 32'b0;

View file

@ -42,10 +42,10 @@ module pcgen (
logic [63:0] npc_n, npc_q;
branchpredict_sbe branch_predict_btb;
assign pc_if_o = npc_q;
assign fetch_address_o = npc_q;
btb #(
.NR_ENTRIES(64),
.NR_ENTRIES(1024),
.BITS_SATURATION_COUNTER(2)
)
btb_i

View file

@ -64,18 +64,13 @@ module prefetch_buffer
//---------------------------------
// we are busy if we are either waiting for a grant
// or if the fifo is full
assign busy_o = (CS inside {WAIT_GNT, WAIT_ABORTED}) || !fifo_ready;
assign busy_o = (CS inside {WAIT_GNT, WAIT_ABORTED} && !instr_req_o) || !fifo_ready;
//---------------------------------
// Fetch FIFO
// consumes addresses and rdata
//---------------------------------
fetch_fifo fifo_i (
.clk_i ( clk_i ),
.rst_ni ( rst_n_i ),
.clear_i ( flush_i ),
.branch_predict_i ( branchpredict_q ),
.in_addr_i ( instr_addr_q ),
.in_rdata_i ( instr_rdata_i ),
@ -86,7 +81,8 @@ module prefetch_buffer
.out_valid_o ( valid_o ),
.out_ready_i ( ready_i ),
.out_rdata_o ( rdata_o ),
.out_addr_o ( addr_o )
.out_addr_o ( addr_o ),
.*
);
//--------------------------------------------------
@ -113,7 +109,11 @@ module prefetch_buffer
if(instr_gnt_i) //~> granted request
NS = WAIT_RVALID;
// 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
@ -126,7 +126,11 @@ module prefetch_buffer
instr_req_o = 1'b1;
if(instr_gnt_i)
NS = WAIT_RVALID;
// 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
@ -137,15 +141,18 @@ module prefetch_buffer
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
NS = WAIT_RVALID;
// we have one outstanding rvalid: wait for it
if (flush_i)
NS = WAIT_ABORTED;
else
NS = WAIT_RVALID;
end else begin
NS = WAIT_GNT;
end
@ -153,12 +160,13 @@ module prefetch_buffer
// 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;
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
@ -169,33 +177,37 @@ module prefetch_buffer
// there was no new request sent yet
// we assume that req_i is set to high
WAIT_ABORTED: begin
instr_addr_o = instr_addr_q;
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
NS = WAIT_RVALID;
// we have one outstanding rvalid
if (flush_i)
NS = WAIT_ABORTED;
else
NS = WAIT_RVALID;
end else begin
NS = WAIT_GNT;
end
end
end
default:
begin
default: begin
NS = IDLE;
instr_req_o = 1'b0;
end
endcase
end
//-------------
// Registers
//-------------
always_ff @(posedge clk_i, negedge rst_n_i)
always_ff @(posedge clk_i, negedge rst_ni)
begin
if (~rst_ni) begin
CS <= IDLE;

View file

@ -0,0 +1,53 @@
// Author: Florian Zaruba, ETH Zurich
// Date: 14.5.2017
// Description: Fetch FIFO interface
//
//
// Copyright (C) 2017 ETH Zurich, University of Bologna
// All rights reserved.
//
// This code is under development and not yet released to the public.
// Until it is released, the code is under the copyright of ETH Zurich and
// the University of Bologna, and may contain confidential and/or unpublished
// work. Any reuse/redistribution is strictly forbidden without written
// permission from ETH Zurich.
//
// Bug fixes and contributions will eventually be released under the
// SolderPad open hardware license in the context of the PULP platform
// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the
// University of Bologna.
//
`ifndef FETCH_FIFO_IF_SV
`define FETCH_FIFO_IF_SV
import ariane_pkg::*;
interface fetch_fifo_if #(
parameter type dtype = logic[7:0]
)(
input clk
);
wire flush;
wire [$bits(branchpredict_sbe)-1:0] in_branch_predict;
wire [63:0] in_addr;
wire [31:0] in_rdata;
wire in_valid;
wire in_ready;
wire [$bits(branchpredict_sbe)-1:0] out_branch_predict;
wire [63:0] out_addr;
wire [31:0] out_rdata;
wire out_valid;
wire out_ready;
clocking mck @(posedge clk);
input in_ready, out_branch_predict, out_addr, out_rdata, out_valid;
output flush, in_branch_predict, in_addr, in_rdata, in_valid, out_ready;
endclocking
clocking pck @(posedge clk);
input in_ready, out_branch_predict, out_addr, out_rdata, out_valid,
flush, in_branch_predict, in_addr, in_rdata, in_valid, out_ready;
endclocking
endinterface
`endif

75
tb/fetch_fifo_tb.sv Executable file
View file

@ -0,0 +1,75 @@
// Author: Florian Zaruba, ETH Zurich
// Date: 14.5.2017
// Description: Fetch FIFO testbench
//
//
// Copyright (C) 2017 ETH Zurich, University of Bologna
// All rights reserved.
//
// This code is under development and not yet released to the public.
// Until it is released, the code is under the copyright of ETH Zurich and
// the University of Bologna, and may contain confidential and/or unpublished
// work. Any reuse/redistribution is strictly forbidden without written
// permission from ETH Zurich.
//
// Bug fixes and contributions will eventually be released under the
// SolderPad open hardware license in the context of the PULP platform
// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the
// University of Bologna.
//
module fetch_fifo_tb;
logic rst_ni, clk_i;
fetch_fifo_if fetch_fifo_if (clk);
fetch_fifo
dut (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( fetch_fifo_if.flush ),
.branch_predict_i ( fetch_fifo_if.in_branch_predict ),
.in_addr_i ( fetch_fifo_if.in_addr ),
.in_rdata_i ( fetch_fifo_if.in_rdata ),
.in_valid_i ( fetch_fifo_if.in_valid ),
.in_ready_o ( fetch_fifo_if.in_ready ),
.branch_predict_o ( fetch_fifo_if.out_branch_predict ),
.out_addr_o ( fetch_fifo_if.out_addr ),
.out_rdata_o ( fetch_fifo_if.out_rdata ),
.out_valid_o ( fetch_fifo_if.out_valid ),
.out_ready_i ( fetch_fifo_if.out_ready )
);
initial begin
clk_i = 1'b0;
rst_ni = 1'b0;
repeat(8)
#10ns clk_i = ~clk_i;
rst_ni = 1'b1;
forever
#10ns clk_i = ~clk_i;
end
// simulator stopper, this is suboptimal better go for coverage
initial begin
#10000000ns
$stop;
end
program testbench (fetch_fifo_if fetch_fifo_if);
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;
end
endprogram
testbench tb(fetch_fifo_if);
endmodule

View file

@ -0,0 +1,23 @@
// Author: Florian Zaruba, ETH Zurich
// Date: 14.5.2017
// Description: Fetch FIFO Pkg
//
//
// Copyright (C) 2017 ETH Zurich, University of Bologna
// All rights reserved.
//
// This code is under development and not yet released to the public.
// Until it is released, the code is under the copyright of ETH Zurich and
// the University of Bologna, and may contain confidential and/or unpublished
// work. Any reuse/redistribution is strictly forbidden without written
// permission from ETH Zurich.
//
// Bug fixes and contributions will eventually be released under the
// SolderPad open hardware license in the context of the PULP platform
// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the
// University of Bologna.
//
package fetch_fifo_pkg;
endpackage