From 851faa78515548a15eb2e05947ee76a00831e3ed Mon Sep 17 00:00:00 2001 From: Florian Zaruba Date: Sun, 14 May 2017 20:11:39 +0200 Subject: [PATCH] :white_check_mark: Add fetch fifo testbench stub --- Makefile | 2 +- src/branch_engine.sv | 38 +++---- src/btb.sv | 2 +- src/fetch_fifo.sv | 127 ++++++++++++----------- src/if_stage.sv | 14 +-- src/pcgen.sv | 4 +- src/prefetch_buffer.sv | 48 +++++---- tb/agents/fetch_fifo_if/fetch_fifo_if.sv | 53 ++++++++++ tb/fetch_fifo_tb.sv | 75 +++++++++++++ tb/test/fetch_fifo/fetch_fifo_pkg.sv | 23 ++++ 10 files changed, 280 insertions(+), 106 deletions(-) create mode 100755 tb/agents/fetch_fifo_if/fetch_fifo_if.sv create mode 100755 tb/fetch_fifo_tb.sv create mode 100755 tb/test/fetch_fifo/fetch_fifo_pkg.sv diff --git a/Makefile b/Makefile index 91b0f148a..e8d4e763b 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/src/branch_engine.sv b/src/branch_engine.sv index 465ffb6ca..a896b2b66 100644 --- a/src/branch_engine.sv +++ b/src/branch_engine.sv @@ -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 diff --git a/src/btb.sv b/src/btb.sv index 2ece9e2c1..75364a8a2 100644 --- a/src/btb.sv +++ b/src/btb.sv @@ -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 ) ( diff --git a/src/fetch_fifo.sv b/src/fetch_fifo.sv index 6377de336..884e5c7d8 100644 --- a/src/fetch_fifo.sv +++ b/src/fetch_fifo.sv @@ -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 \ No newline at end of file diff --git a/src/if_stage.sv b/src/if_stage.sv index c5e99f773..7b97f7b37 100644 --- a/src/if_stage.sv +++ b/src/if_stage.sv @@ -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; diff --git a/src/pcgen.sv b/src/pcgen.sv index f969b82b8..d20d8c4a2 100644 --- a/src/pcgen.sv +++ b/src/pcgen.sv @@ -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 diff --git a/src/prefetch_buffer.sv b/src/prefetch_buffer.sv index 4af831a73..e1969f468 100644 --- a/src/prefetch_buffer.sv +++ b/src/prefetch_buffer.sv @@ -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; diff --git a/tb/agents/fetch_fifo_if/fetch_fifo_if.sv b/tb/agents/fetch_fifo_if/fetch_fifo_if.sv new file mode 100755 index 000000000..97c1d6a67 --- /dev/null +++ b/tb/agents/fetch_fifo_if/fetch_fifo_if.sv @@ -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 \ No newline at end of file diff --git a/tb/fetch_fifo_tb.sv b/tb/fetch_fifo_tb.sv new file mode 100755 index 000000000..5eef448fe --- /dev/null +++ b/tb/fetch_fifo_tb.sv @@ -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 \ No newline at end of file diff --git a/tb/test/fetch_fifo/fetch_fifo_pkg.sv b/tb/test/fetch_fifo/fetch_fifo_pkg.sv new file mode 100755 index 000000000..d08e8bb3e --- /dev/null +++ b/tb/test/fetch_fifo/fetch_fifo_pkg.sv @@ -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 \ No newline at end of file