blob: 83e2ef12e6ffc0c1ce9176d3e73b9a9b3cebcfb5 [file] [log] [blame]
//////////////////////////////////////////////////////////////////////////////
// SPDX-FileCopyrightText: Syntacore LLC © 2016-2021
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileContributor: Syntacore LLC
// //////////////////////////////////////////////////////////////////////////
/// @file <scr1_pipe_ifu.sv>
/// @brief Instruction Fetch Unit (IFU)
///
//------------------------------------------------------------------------------
//
// Functionality:
// - Controls instruction fetching process:
// - Fetches instructions either from IMEM or from Program Buffer, supporting
// pending IMEM instructions handling
// - Handles new PC misalignment and constructs the correct instruction (supports
// RVI and RVC instructions)
// - Either stores instructions in the instruction queue or bypasses to the
// IDU if the corresponding option is used
// - Flushes instruction queue if requested
//
// Structure:
// - Instruction queue
// - IFU FSM
// - IFU <-> IMEM i/f
// - IFU <-> IDU i/f
// - IFU <-> HDU i/f
//
//------------------------------------------------------------------------------
`include "scr1_memif.svh"
`include "scr1_arch_description.svh"
`ifdef SCR1_DBG_EN
`include "scr1_hdu.svh"
`endif // SCR1_DBG_EN
module scr1_pipe_ifu
(
// Control signals
input logic rst_n, // IFU reset
input logic clk, // IFU clock
input logic pipe2ifu_stop_fetch_i, // Stop instruction fetch
// IFU <-> IMEM interface
input logic imem2ifu_req_ack_i, // Instruction memory request acknowledgement
output logic ifu2imem_req_o, // Instruction memory request
output logic ifu2imem_cmd_o, // Instruction memory command (READ/WRITE)
output logic [`SCR1_IMEM_AWIDTH-1:0] ifu2imem_addr_o, // Instruction memory address
input logic [`SCR1_IMEM_DWIDTH-1:0] imem2ifu_rdata_i, // Instruction memory read data
input logic [1:0] imem2ifu_resp_i, // Instruction memory response
// IFU <-> EXU New PC interface
input logic exu2ifu_pc_new_req_i, // New PC request (jumps, branches, traps etc)
input logic [`SCR1_XLEN-1:0] exu2ifu_pc_new_i, // New PC
`ifdef SCR1_DBG_EN
// IFU <-> HDU Program Buffer interface
input logic hdu2ifu_pbuf_fetch_i, // Fetch instructions provided by Program Buffer
output logic ifu2hdu_pbuf_rdy_o, // Program Buffer Instruction i/f ready
input logic hdu2ifu_pbuf_vd_i, // Program Buffer Instruction valid
input logic hdu2ifu_pbuf_err_i, // Program Buffer Instruction i/f error
input logic [SCR1_HDU_CORE_INSTR_WIDTH-1:0] hdu2ifu_pbuf_instr_i, // Program Buffer Instruction itself
`endif // SCR1_DBG_EN
`ifdef SCR1_CLKCTRL_EN
output logic ifu2pipe_imem_txns_pnd_o, // There are pending imem transactions
`endif // SCR1_CLKCTRL_EN
// IFU <-> IDU interface
input logic idu2ifu_rdy_i, // IDU ready for new data
output logic [`SCR1_IMEM_DWIDTH-1:0] ifu2idu_instr_o, // IFU instruction
output logic ifu2idu_imem_err_o, // Instruction access fault exception
output logic ifu2idu_err_rvi_hi_o, // 1 - imem fault when trying to fetch second half of an unaligned RVI instruction
output logic ifu2idu_vd_o // IFU request
);
//------------------------------------------------------------------------------
// Local parameters declaration
//------------------------------------------------------------------------------
localparam SCR1_IFU_Q_SIZE_WORD = 2;
localparam SCR1_IFU_Q_SIZE_HALF = SCR1_IFU_Q_SIZE_WORD * 2;
localparam SCR1_TXN_CNT_W = 3;
localparam SCR1_IFU_QUEUE_ADR_W = $clog2(SCR1_IFU_Q_SIZE_HALF);
localparam SCR1_IFU_QUEUE_PTR_W = SCR1_IFU_QUEUE_ADR_W + 1;
localparam SCR1_IFU_Q_FREE_H_W = $clog2(SCR1_IFU_Q_SIZE_HALF + 1);
localparam SCR1_IFU_Q_FREE_W_W = $clog2(SCR1_IFU_Q_SIZE_WORD + 1);
//------------------------------------------------------------------------------
// Local types declaration
//------------------------------------------------------------------------------
//typedef enum logic {
parameter SCR1_IFU_FSM_IDLE = 1'b0;
parameter SCR1_IFU_FSM_FETCH = 1'b1;
//} type_scr1_ifu_fsm_e;
//typedef enum logic[1:0] {
parameter SCR1_IFU_QUEUE_WR_NONE = 2'b00; // No write to queue
parameter SCR1_IFU_QUEUE_WR_FULL = 2'b01; // Write 32 rdata bits to queue
parameter SCR1_IFU_QUEUE_WR_HI = 2'b10; // Write 16 upper rdata bits to queue
//} type_scr1_ifu_queue_wr_e;
//typedef enum logic[1:0] {
parameter SCR1_IFU_QUEUE_RD_NONE = 2'b00; // No queue read
parameter SCR1_IFU_QUEUE_RD_HWORD = 2'b01; // Read halfword
parameter SCR1_IFU_QUEUE_RD_WORD = 2'b10; // Read word
//} type_scr1_ifu_queue_rd_e;
`ifdef SCR1_NO_DEC_STAGE
typedef enum logic[1:0] {
SCR1_BYPASS_NONE, // No bypass
SCR1_BYPASS_RVC, // Bypass RVC
SCR1_BYPASS_RVI_RDATA_QUEUE, // Bypass RVI, rdata+queue
SCR1_BYPASS_RVI_RDATA // Bypass RVI, rdata only
} type_scr1_bypass_e;
`endif // SCR1_NO_DEC_STAGE
//typedef enum logic [2:0] {
// SCR1_IFU_INSTR_<UPPER_16_BITS>_<LOWER_16_BITS>
parameter SCR1_IFU_INSTR_NONE = 3'b000 ; // No valid instruction
parameter SCR1_IFU_INSTR_RVI_HI_RVI_LO = 3'b001 ; // Full RV32I instruction
parameter SCR1_IFU_INSTR_RVC_RVC = 3'b010 ;
parameter SCR1_IFU_INSTR_RVI_LO_RVC = 3'b011 ;
parameter SCR1_IFU_INSTR_RVC_RVI_HI = 3'b100 ;
parameter SCR1_IFU_INSTR_RVI_LO_RVI_HI = 3'b101 ;
parameter SCR1_IFU_INSTR_RVC_NV = 3'b110 ; // Instruction after unaligned new_pc
parameter SCR1_IFU_INSTR_RVI_LO_NV = 3'b111 ; // Instruction after unaligned new_pc
//} type_scr1_ifu_instr_e;
//------------------------------------------------------------------------------
// Local signals declaration
//------------------------------------------------------------------------------
// Instruction queue signals
//------------------------------------------------------------------------------
// New PC unaligned flag register
logic new_pc_unaligned_ff;
logic new_pc_unaligned_next;
logic new_pc_unaligned_upd;
// IMEM instruction type decoder
logic instr_hi_is_rvi;
logic instr_lo_is_rvi;
logic [2:0] instr_type;
// Register to store if the previous IMEM instruction had low part of RVI instruction
// in its high part
logic instr_hi_rvi_lo_ff;
logic instr_hi_rvi_lo_next;
// Queue read/write size decoders
logic [1:0] q_rd_size;
logic q_rd_vd;
logic q_rd_none;
logic q_rd_hword;
logic [1:0] q_wr_size;
logic q_wr_none;
logic q_wr_full;
// Write/read pointer registers
logic [SCR1_IFU_QUEUE_PTR_W-1:0] q_rptr;
logic [SCR1_IFU_QUEUE_PTR_W-1:0] q_rptr_next;
logic q_rptr_upd;
logic [SCR1_IFU_QUEUE_PTR_W-1:0] q_wptr;
logic [SCR1_IFU_QUEUE_PTR_W-1:0] q_wptr_next;
logic q_wptr_upd;
// Instruction queue control signals
logic q_wr_en;
logic q_flush_req;
// Queue data registers
logic [`SCR1_IMEM_DWIDTH/2-1:0] q_data [SCR1_IFU_Q_SIZE_HALF];
logic [`SCR1_IMEM_DWIDTH/2-1:0] q_data_head;
logic [`SCR1_IMEM_DWIDTH/2-1:0] q_data_next;
// Queue error flags registers
logic q_err [SCR1_IFU_Q_SIZE_HALF];
logic q_err_head;
logic q_err_next;
// Instruction queue status signals
logic q_is_empty;
logic q_has_free_slots;
logic q_has_1_ocpd_hw;
logic q_head_is_rvc;
logic q_head_is_rvi;
logic [SCR1_IFU_Q_FREE_H_W-1:0] q_ocpd_h;
logic [SCR1_IFU_Q_FREE_H_W-1:0] q_free_h_next;
logic [SCR1_IFU_Q_FREE_W_W-1:0] q_free_w_next;
// IFU FSM signals
//------------------------------------------------------------------------------
// IFU FSM control signals
logic ifu_fetch_req;
logic ifu_stop_req;
logic ifu_fsm_curr;
logic ifu_fsm_next;
logic ifu_fsm_fetch;
// IMEM signals
//------------------------------------------------------------------------------
// IMEM response signals
logic imem_resp_ok;
logic imem_resp_er;
logic imem_resp_er_discard_pnd;
logic imem_resp_discard_req;
logic imem_resp_received;
logic imem_resp_vd;
logic imem_handshake_done;
logic [15:0] imem_rdata_lo;
logic [31:16] imem_rdata_hi;
// IMEM address signals
logic imem_addr_upd;
logic [`SCR1_XLEN-1:2] imem_addr_ff;
logic [`SCR1_XLEN-1:2] imem_addr_next;
// IMEM pending transactions counter
logic imem_pnd_txns_cnt_upd;
logic [SCR1_TXN_CNT_W-1:0] imem_pnd_txns_cnt;
logic [SCR1_TXN_CNT_W-1:0] imem_pnd_txns_cnt_next;
logic [SCR1_TXN_CNT_W-1:0] imem_vd_pnd_txns_cnt;
logic imem_pnd_txns_q_full;
// IMEM responses discard counter
logic imem_resp_discard_cnt_upd;
logic [SCR1_TXN_CNT_W-1:0] imem_resp_discard_cnt;
logic [SCR1_TXN_CNT_W-1:0] imem_resp_discard_cnt_next;
`ifdef SCR1_NEW_PC_REG
logic new_pc_req_ff;
`endif // SCR1_NEW_PC_REG
// Instruction bypass signals
`ifdef SCR1_NO_DEC_STAGE
type_scr1_bypass_e instr_bypass_type;
logic instr_bypass_vd;
`endif // SCR1_NO_DEC_STAGE
//------------------------------------------------------------------------------
// Instruction queue
//------------------------------------------------------------------------------
//
// Instruction queue consists of the following functional units:
// - New PC unaligned flag register
// - Instruction type decoder, including register to store if the previous
// IMEM instruction had low part of RVI instruction in its high part
// - Read/write size decoders
// - Read/write pointer registers
// - Data and error flag registers
// - Status logic
//
// New PC unaligned flag register
//------------------------------------------------------------------------------
assign new_pc_unaligned_upd = exu2ifu_pc_new_req_i | imem_resp_vd;
always_ff @(posedge clk, negedge rst_n) begin
if (~rst_n) begin
new_pc_unaligned_ff <= 1'b0;
end else if (new_pc_unaligned_upd) begin
new_pc_unaligned_ff <= new_pc_unaligned_next;
end
end
assign new_pc_unaligned_next = exu2ifu_pc_new_req_i ? exu2ifu_pc_new_i[1]
: ~imem_resp_vd ? new_pc_unaligned_ff
: 1'b0;
// Instruction type decoder
//------------------------------------------------------------------------------
assign instr_hi_is_rvi = &imem2ifu_rdata_i[17:16];
assign instr_lo_is_rvi = &imem2ifu_rdata_i[1:0];
always_comb begin
instr_type = SCR1_IFU_INSTR_NONE;
if (imem_resp_ok & ~imem_resp_discard_req) begin
if (new_pc_unaligned_ff) begin
instr_type = instr_hi_is_rvi ? SCR1_IFU_INSTR_RVI_LO_NV
: SCR1_IFU_INSTR_RVC_NV;
end else begin // ~new_pc_unaligned_ff
if (instr_hi_rvi_lo_ff) begin
instr_type = instr_hi_is_rvi ? SCR1_IFU_INSTR_RVI_LO_RVI_HI
: SCR1_IFU_INSTR_RVC_RVI_HI;
end else begin // SCR1_OTHER
casez ({instr_hi_is_rvi, instr_lo_is_rvi})
2'b?1 : instr_type = SCR1_IFU_INSTR_RVI_HI_RVI_LO;
2'b00 : instr_type = SCR1_IFU_INSTR_RVC_RVC;
2'b10 : instr_type = SCR1_IFU_INSTR_RVI_LO_RVC;
endcase
end
end
end
end
// Register to store if the previous IMEM instruction had low part of RVI
// instruction in its high part
//------------------------------------------------------------------------------
always_ff @(posedge clk, negedge rst_n) begin
if (~rst_n) begin
instr_hi_rvi_lo_ff <= 1'b0;
end else begin
if (exu2ifu_pc_new_req_i) begin
instr_hi_rvi_lo_ff <= 1'b0;
end else if (imem_resp_vd) begin
instr_hi_rvi_lo_ff <= instr_hi_rvi_lo_next;
end
end
end
assign instr_hi_rvi_lo_next = (instr_type == SCR1_IFU_INSTR_RVI_LO_NV)
| (instr_type == SCR1_IFU_INSTR_RVI_LO_RVI_HI)
| (instr_type == SCR1_IFU_INSTR_RVI_LO_RVC);
// Queue write/read size decoders
//------------------------------------------------------------------------------
// Queue read size decoder
assign q_rd_vd = ~q_is_empty & ifu2idu_vd_o & idu2ifu_rdy_i;
assign q_rd_hword = q_head_is_rvc | q_err_head
`ifdef SCR1_NO_DEC_STAGE
| (q_head_is_rvi & instr_bypass_vd)
`endif // SCR1_NO_DEC_STAGE
;
assign q_rd_size = ~q_rd_vd ? SCR1_IFU_QUEUE_RD_NONE
: q_rd_hword ? SCR1_IFU_QUEUE_RD_HWORD
: SCR1_IFU_QUEUE_RD_WORD;
assign q_rd_none = (q_rd_size == SCR1_IFU_QUEUE_RD_NONE);
// Queue write size decoder
always_comb begin
q_wr_size = SCR1_IFU_QUEUE_WR_NONE;
if (~imem_resp_discard_req) begin
if (imem_resp_ok) begin
`ifdef SCR1_NO_DEC_STAGE
case (instr_type)
SCR1_IFU_INSTR_NONE : q_wr_size = SCR1_IFU_QUEUE_WR_NONE;
SCR1_IFU_INSTR_RVI_LO_NV : q_wr_size = SCR1_IFU_QUEUE_WR_HI;
SCR1_IFU_INSTR_RVC_NV : q_wr_size = (instr_bypass_vd & idu2ifu_rdy_i)
? SCR1_IFU_QUEUE_WR_NONE
: SCR1_IFU_QUEUE_WR_HI;
SCR1_IFU_INSTR_RVI_HI_RVI_LO: q_wr_size = (instr_bypass_vd & idu2ifu_rdy_i)
? SCR1_IFU_QUEUE_WR_NONE
: SCR1_IFU_QUEUE_WR_FULL;
SCR1_IFU_INSTR_RVC_RVC,
SCR1_IFU_INSTR_RVI_LO_RVC,
SCR1_IFU_INSTR_RVC_RVI_HI,
SCR1_IFU_INSTR_RVI_LO_RVI_HI: q_wr_size = (instr_bypass_vd & idu2ifu_rdy_i)
? SCR1_IFU_QUEUE_WR_HI
: SCR1_IFU_QUEUE_WR_FULL;
endcase // instr_type
`else // SCR1_NO_DEC_STAGE
case (instr_type)
SCR1_IFU_INSTR_NONE : q_wr_size = SCR1_IFU_QUEUE_WR_NONE;
SCR1_IFU_INSTR_RVC_NV,
SCR1_IFU_INSTR_RVI_LO_NV : q_wr_size = SCR1_IFU_QUEUE_WR_HI;
default : q_wr_size = SCR1_IFU_QUEUE_WR_FULL;
endcase // instr_type
`endif // SCR1_NO_DEC_STAGE
end else if (imem_resp_er) begin
q_wr_size = SCR1_IFU_QUEUE_WR_FULL;
end // imem_resp_er
end // ~imem_resp_discard_req
end
assign q_wr_none = (q_wr_size == SCR1_IFU_QUEUE_WR_NONE);
assign q_wr_full = (q_wr_size == SCR1_IFU_QUEUE_WR_FULL);
// Write/read pointer registers
//------------------------------------------------------------------------------
assign q_flush_req = exu2ifu_pc_new_req_i | pipe2ifu_stop_fetch_i;
// Queue write pointer register
assign q_wptr_upd = q_flush_req | ~q_wr_none;
always_ff @(posedge clk, negedge rst_n) begin
if (~rst_n) begin
q_wptr <= '0;
end else if (q_wptr_upd) begin
q_wptr <= q_wptr_next;
end
end
assign q_wptr_next = q_flush_req ? '0
: ~q_wr_none ? q_wptr + (q_wr_full ? 2'd2 : 1'b1)
: q_wptr;
// Queue read pointer register
assign q_rptr_upd = q_flush_req | ~q_rd_none;
always_ff @(posedge clk, negedge rst_n) begin
if (~rst_n) begin
q_rptr <= '0;
end else if (q_rptr_upd) begin
q_rptr <= q_rptr_next;
end
end
assign q_rptr_next = q_flush_req ? '0
: ~q_rd_none ? q_rptr + (q_rd_hword ? 1'b1 : 2'd2)
: q_rptr;
// Queue data and error flag registers
//------------------------------------------------------------------------------
assign imem_rdata_hi = imem2ifu_rdata_i[31:16];
assign imem_rdata_lo = imem2ifu_rdata_i[15:0];
assign q_wr_en = imem_resp_vd & ~q_flush_req;
always_ff @(posedge clk, negedge rst_n) begin
if (~rst_n) begin
`ifdef SCR1_MPRF_RST_EN // Two dimensional array init not allowed in YOSYS - cp.13
q_data <= '{SCR1_IFU_Q_SIZE_HALF{'0}};
q_err <= '{SCR1_IFU_Q_SIZE_HALF{1'b0}};
`endif
end else if (q_wr_en) begin
case (q_wr_size)
SCR1_IFU_QUEUE_WR_HI : begin
q_data[SCR1_IFU_QUEUE_ADR_W'(q_wptr)] <= imem_rdata_hi;
q_err [SCR1_IFU_QUEUE_ADR_W'(q_wptr)] <= imem_resp_er;
end
SCR1_IFU_QUEUE_WR_FULL : begin
q_data[SCR1_IFU_QUEUE_ADR_W'(q_wptr)] <= imem_rdata_lo;
q_err [SCR1_IFU_QUEUE_ADR_W'(q_wptr)] <= imem_resp_er;
q_data[SCR1_IFU_QUEUE_ADR_W'(q_wptr + 1'b1)] <= imem_rdata_hi;
q_err [SCR1_IFU_QUEUE_ADR_W'(q_wptr + 1'b1)] <= imem_resp_er;
end
endcase
end
end
assign q_data_head = q_data [SCR1_IFU_QUEUE_ADR_W'(q_rptr)];
assign q_data_next = q_data [SCR1_IFU_QUEUE_ADR_W'(q_rptr + 1'b1)];
assign q_err_head = q_err [SCR1_IFU_QUEUE_ADR_W'(q_rptr)];
assign q_err_next = q_err [SCR1_IFU_QUEUE_ADR_W'(q_rptr + 1'b1)];
// Queue status logic
//------------------------------------------------------------------------------
assign q_ocpd_h = SCR1_IFU_Q_FREE_H_W'(q_wptr - q_rptr);
assign q_free_h_next = SCR1_IFU_Q_FREE_H_W'(SCR1_IFU_Q_SIZE_HALF - (q_wptr - q_rptr_next));
assign q_free_w_next = SCR1_IFU_Q_FREE_W_W'(q_free_h_next >> 1'b1);
assign q_is_empty = (q_rptr == q_wptr);
assign q_has_free_slots = (SCR1_TXN_CNT_W'(q_free_w_next) > imem_vd_pnd_txns_cnt);
assign q_has_1_ocpd_hw = (q_ocpd_h == SCR1_IFU_Q_FREE_H_W'(1));
assign q_head_is_rvi = &(q_data_head[1:0]);
assign q_head_is_rvc = ~q_head_is_rvi;
//------------------------------------------------------------------------------
// IFU FSM
//------------------------------------------------------------------------------
// IFU FSM control signals
assign ifu_fetch_req = exu2ifu_pc_new_req_i & ~pipe2ifu_stop_fetch_i;
assign ifu_stop_req = pipe2ifu_stop_fetch_i
| (imem_resp_er_discard_pnd & ~exu2ifu_pc_new_req_i);
always_ff @(posedge clk, negedge rst_n) begin
if (~rst_n) begin
ifu_fsm_curr <= SCR1_IFU_FSM_IDLE;
end else begin
ifu_fsm_curr <= ifu_fsm_next;
end
end
always_comb begin
case (ifu_fsm_curr)
SCR1_IFU_FSM_IDLE : begin
ifu_fsm_next = ifu_fetch_req ? SCR1_IFU_FSM_FETCH
: SCR1_IFU_FSM_IDLE;
end
SCR1_IFU_FSM_FETCH : begin
ifu_fsm_next = ifu_stop_req ? SCR1_IFU_FSM_IDLE
: SCR1_IFU_FSM_FETCH;
end
endcase
end
assign ifu_fsm_fetch = (ifu_fsm_curr == SCR1_IFU_FSM_FETCH);
//------------------------------------------------------------------------------
// IFU <-> IMEM interface
//------------------------------------------------------------------------------
//
// IFU <-> IMEM interface consists of the following functional units:
// - IMEM response logic
// - IMEM address register
// - Pending IMEM transactions counter
// - IMEM discard responses counter
// - IFU <-> IMEM interface output signals
//
// IMEM response logic
//------------------------------------------------------------------------------
assign imem_resp_er = (imem2ifu_resp_i == SCR1_MEM_RESP_RDY_ER);
assign imem_resp_ok = (imem2ifu_resp_i == SCR1_MEM_RESP_RDY_OK);
assign imem_resp_received = imem_resp_ok | imem_resp_er;
assign imem_resp_vd = imem_resp_received & ~imem_resp_discard_req;
assign imem_resp_er_discard_pnd = imem_resp_er & ~imem_resp_discard_req;
assign imem_handshake_done = ifu2imem_req_o & imem2ifu_req_ack_i;
// IMEM address register
//------------------------------------------------------------------------------
assign imem_addr_upd = imem_handshake_done | exu2ifu_pc_new_req_i;
always_ff @(posedge clk, negedge rst_n) begin
if (~rst_n) begin
imem_addr_ff <= '0;
end else if (imem_addr_upd) begin
imem_addr_ff <= imem_addr_next;
end
end
`ifndef SCR1_NEW_PC_REG
assign imem_addr_next = exu2ifu_pc_new_req_i ? exu2ifu_pc_new_i[`SCR1_XLEN-1:2] + imem_handshake_done
: &imem_addr_ff[5:2] ? imem_addr_ff + imem_handshake_done
: {imem_addr_ff[`SCR1_XLEN-1:6], imem_addr_ff[5:2] + imem_handshake_done};
`else // SCR1_NEW_PC_REG
assign imem_addr_next = exu2ifu_pc_new_req_i ? exu2ifu_pc_new_i[`SCR1_XLEN-1:2]
: &imem_addr_ff[5:2] ? imem_addr_ff + imem_handshake_done
: {imem_addr_ff[`SCR1_XLEN-1:6], imem_addr_ff[5:2] + imem_handshake_done};
`endif // SCR1_NEW_PC_REG
// Pending IMEM transactions counter
//------------------------------------------------------------------------------
// Pending IMEM transactions occur if IFU request has been acknowledged, but
// response comes in the next cycle or later
assign imem_pnd_txns_cnt_upd = imem_handshake_done ^ imem_resp_received;
always_ff @(posedge clk, negedge rst_n) begin
if (~rst_n) begin
imem_pnd_txns_cnt <= '0;
end else if (imem_pnd_txns_cnt_upd) begin
imem_pnd_txns_cnt <= imem_pnd_txns_cnt_next;
end
end
assign imem_pnd_txns_cnt_next = imem_pnd_txns_cnt + (imem_handshake_done - imem_resp_received);
assign imem_pnd_txns_q_full = &imem_pnd_txns_cnt;
// IMEM discard responses counter
//------------------------------------------------------------------------------
// IMEM instructions should be discarded in the following 2 cases:
// 1. New PC is requested by jump, branch, mret or other instruction
// 2. IMEM response was erroneous and not discarded
//
// In both cases the number of instructions to be discarded equals to the number
// of pending instructions.
// In the 1st case we don't need all the instructions that haven't been fetched
// yet, since the PC has changed.
// In the 2nd case, since the IMEM responce was erroneous there is no guarantee
// that subsequent IMEM instructions would be valid.
assign imem_resp_discard_cnt_upd = exu2ifu_pc_new_req_i | imem_resp_er
| (imem_resp_ok & imem_resp_discard_req);
always_ff @(posedge clk, negedge rst_n) begin
if (~rst_n) begin
imem_resp_discard_cnt <= '0;
end else if (imem_resp_discard_cnt_upd) begin
imem_resp_discard_cnt <= imem_resp_discard_cnt_next;
end
end
`ifndef SCR1_NEW_PC_REG
assign imem_resp_discard_cnt_next = exu2ifu_pc_new_req_i ? imem_pnd_txns_cnt_next - imem_handshake_done
: imem_resp_er_discard_pnd ? imem_pnd_txns_cnt_next
: imem_resp_discard_cnt - 1'b1;
`else // SCR1_NEW_PC_REG
assign imem_resp_discard_cnt_next = exu2ifu_pc_new_req_i | imem_resp_er_discard_pnd
? imem_pnd_txns_cnt_next
: imem_resp_discard_cnt - 1'b1;
`endif // SCR1_NEW_PC_REG
assign imem_vd_pnd_txns_cnt = imem_pnd_txns_cnt - imem_resp_discard_cnt;
assign imem_resp_discard_req = |imem_resp_discard_cnt;
// IFU <-> IMEM interface output signals
//------------------------------------------------------------------------------
`ifndef SCR1_NEW_PC_REG
assign ifu2imem_req_o = (exu2ifu_pc_new_req_i & ~imem_pnd_txns_q_full & ~pipe2ifu_stop_fetch_i)
| (ifu_fsm_fetch & ~imem_pnd_txns_q_full & q_has_free_slots);
assign ifu2imem_addr_o = exu2ifu_pc_new_req_i
? {exu2ifu_pc_new_i[`SCR1_XLEN-1:2], 2'b00}
: {imem_addr_ff, 2'b00};
`else // SCR1_NEW_PC_REG
assign ifu2imem_req_o = ifu_fsm_fetch & ~imem_pnd_txns_q_full & q_has_free_slots;
assign ifu2imem_addr_o = {imem_addr_ff, 2'b00};
`endif // SCR1_NEW_PC_REG
assign ifu2imem_cmd_o = SCR1_MEM_CMD_RD;
`ifdef SCR1_CLKCTRL_EN
assign ifu2pipe_imem_txns_pnd_o = |imem_pnd_txns_cnt;
`endif // SCR1_CLKCTRL_EN
//------------------------------------------------------------------------------
// IFU <-> IDU interface
//------------------------------------------------------------------------------
//
// IFU <-> IDU interface consists of the following functional units:
// - Instruction bypass type decoder
// - IFU <-> IDU status signals
// - Output instruction multiplexer
//
`ifdef SCR1_NO_DEC_STAGE
// Instruction bypass type decoder
//------------------------------------------------------------------------------
assign instr_bypass_vd = (instr_bypass_type != SCR1_BYPASS_NONE);
always_comb begin
instr_bypass_type = SCR1_BYPASS_NONE;
if (imem_resp_vd) begin
if (q_is_empty) begin
case (instr_type)
SCR1_IFU_INSTR_RVC_NV,
SCR1_IFU_INSTR_RVC_RVC,
SCR1_IFU_INSTR_RVI_LO_RVC : begin
instr_bypass_type = SCR1_BYPASS_RVC;
end
SCR1_IFU_INSTR_RVI_HI_RVI_LO : begin
instr_bypass_type = SCR1_BYPASS_RVI_RDATA;
end
default : begin end
endcase // instr_type
end else if (q_has_1_ocpd_hw & q_head_is_rvi) begin
if (instr_hi_rvi_lo_ff) begin
instr_bypass_type = SCR1_BYPASS_RVI_RDATA_QUEUE;
end
end
end // imem_resp_vd
end
// IFU <-> IDU interface status signals
//------------------------------------------------------------------------------
always_comb begin
ifu2idu_vd_o = 1'b0;
ifu2idu_imem_err_o = 1'b0;
ifu2idu_err_rvi_hi_o = 1'b0;
if (ifu_fsm_fetch | ~q_is_empty) begin
if (instr_bypass_vd) begin
ifu2idu_vd_o = 1'b1;
ifu2idu_imem_err_o = (instr_bypass_type == SCR1_BYPASS_RVI_RDATA_QUEUE)
? (imem_resp_er | q_err_head)
: imem_resp_er;
ifu2idu_err_rvi_hi_o = (instr_bypass_type == SCR1_BYPASS_RVI_RDATA_QUEUE) & imem_resp_er;
end else if (~q_is_empty) begin
if (q_has_1_ocpd_hw) begin
ifu2idu_vd_o = q_head_is_rvc | q_err_head;
ifu2idu_imem_err_o = q_err_head;
ifu2idu_err_rvi_hi_o = ~q_err_head & q_head_is_rvi & q_err_next;
end else begin
ifu2idu_vd_o = 1'b1;
ifu2idu_imem_err_o = q_err_head ? 1'b1 : (q_head_is_rvi & q_err_next);
end
end // ~q_is_empty
end
`ifdef SCR1_DBG_EN
if (hdu2ifu_pbuf_fetch_i) begin
ifu2idu_vd_o = hdu2ifu_pbuf_vd_i;
ifu2idu_imem_err_o = hdu2ifu_pbuf_err_i;
end
`endif // SCR1_DBG_EN
end
// Output instruction multiplexer
//------------------------------------------------------------------------------
always_comb begin
case (instr_bypass_type)
SCR1_BYPASS_RVC : begin
ifu2idu_instr_o = `SCR1_IMEM_DWIDTH'(new_pc_unaligned_ff ? imem_rdata_hi
: imem_rdata_lo);
end
SCR1_BYPASS_RVI_RDATA : begin
ifu2idu_instr_o = imem2ifu_rdata_i;
end
SCR1_BYPASS_RVI_RDATA_QUEUE: begin
ifu2idu_instr_o = {imem_rdata_lo, q_data_head};
end
default : begin
ifu2idu_instr_o = `SCR1_IMEM_DWIDTH'(q_head_is_rvc ? q_data_head
: {q_data_next, q_data_head});
end
endcase // instr_bypass_type
`ifdef SCR1_DBG_EN
if (hdu2ifu_pbuf_fetch_i) begin
ifu2idu_instr_o = `SCR1_IMEM_DWIDTH'({'0, hdu2ifu_pbuf_instr_i});
end
`endif // SCR1_DBG_EN
end
`else // SCR1_NO_DEC_STAGE
// IFU <-> IDU interface status signals
//------------------------------------------------------------------------------
always_comb begin
ifu2idu_vd_o = 1'b0;
ifu2idu_imem_err_o = 1'b0;
ifu2idu_err_rvi_hi_o = 1'b0;
if (~q_is_empty) begin
if (q_has_1_ocpd_hw) begin
ifu2idu_vd_o = q_head_is_rvc | q_err_head;
ifu2idu_imem_err_o = q_err_head;
end else begin
ifu2idu_vd_o = 1'b1;
ifu2idu_imem_err_o = q_err_head ? 1'b1 : (q_head_is_rvi & q_err_next);
ifu2idu_err_rvi_hi_o = ~q_err_head & q_head_is_rvi & q_err_next;
end
end // ~q_is_empty
`ifdef SCR1_DBG_EN
if (hdu2ifu_pbuf_fetch_i) begin
ifu2idu_vd_o = hdu2ifu_pbuf_vd_i;
ifu2idu_imem_err_o = hdu2ifu_pbuf_err_i;
end
`endif // SCR1_DBG_EN
end
// Output instruction multiplexer
//------------------------------------------------------------------------------
always_comb begin
ifu2idu_instr_o = q_head_is_rvc ? `SCR1_IMEM_DWIDTH'(q_data_head)
: {q_data_next, q_data_head};
`ifdef SCR1_DBG_EN
if (hdu2ifu_pbuf_fetch_i) begin
ifu2idu_instr_o = `SCR1_IMEM_DWIDTH'({'0, hdu2ifu_pbuf_instr_i});
end
`endif // SCR1_DBG_EN
end
`endif // SCR1_NO_DEC_STAGE
`ifdef SCR1_DBG_EN
assign ifu2hdu_pbuf_rdy_o = idu2ifu_rdy_i;
`endif // SCR1_DBG_EN
`ifdef SCR1_TRGT_SIMULATION
//------------------------------------------------------------------------------
// Assertions
//------------------------------------------------------------------------------
// X checks
SCR1_SVA_IFU_XCHECK : assert property (
@(negedge clk) disable iff (~rst_n)
!$isunknown({imem2ifu_req_ack_i, idu2ifu_rdy_i, exu2ifu_pc_new_req_i})
) else $error("IFU Error: unknown values");
SCR1_SVA_IFU_XCHECK_REQ : assert property (
@(negedge clk) disable iff (~rst_n)
ifu2imem_req_o |-> !$isunknown({ifu2imem_addr_o, ifu2imem_cmd_o})
) else $error("IFU Error: unknown {ifu2imem_addr_o, ifu2imem_cmd_o}");
// Behavior checks
`ifndef VERILATOR
SCR1_SVA_IFU_DRC_UNDERFLOW : assert property (
@(negedge clk) disable iff (~rst_n)
~imem_resp_discard_req |=> ~(imem_resp_discard_cnt == SCR1_TXN_CNT_W'('1))
) else $error("IFU Error: imem_resp_discard_cnt underflow");
`endif // VERILATOR
SCR1_SVA_IFU_DRC_RANGE : assert property (
@(negedge clk) disable iff (~rst_n)
(imem_resp_discard_cnt >= 0) & (imem_resp_discard_cnt <= imem_pnd_txns_cnt)
) else $error("IFU Error: imem_resp_discard_cnt out of range");
SCR1_SVA_IFU_QUEUE_OVF : assert property (
@(negedge clk) disable iff (~rst_n)
(q_ocpd_h >= SCR1_IFU_Q_FREE_H_W'(SCR1_IFU_Q_SIZE_HALF-1)) |->
((q_ocpd_h == SCR1_IFU_Q_FREE_H_W'(SCR1_IFU_Q_SIZE_HALF-1)) ? (q_wr_size != SCR1_IFU_QUEUE_WR_FULL)
: (q_wr_size == SCR1_IFU_QUEUE_WR_NONE))
) else $error("IFU Error: queue overflow");
`ifndef VERILATOR
SCR1_SVA_IFU_IMEM_ERR_BEH : assert property (
@(negedge clk) disable iff (~rst_n)
(imem_resp_er & ~imem_resp_discard_req & ~exu2ifu_pc_new_req_i) |=>
(ifu_fsm_curr == SCR1_IFU_FSM_IDLE) & (imem_resp_discard_cnt == imem_pnd_txns_cnt)
) else $error("IFU Error: incorrect behavior after memory error");
SCR1_SVA_IFU_NEW_PC_REQ_BEH : assert property (
@(negedge clk) disable iff (~rst_n)
exu2ifu_pc_new_req_i |=> q_is_empty
) else $error("IFU Error: incorrect behavior after exu2ifu_pc_new_req_i");
`endif // VERILATOR
SCR1_SVA_IFU_IMEM_ADDR_ALIGNED : assert property (
@(negedge clk) disable iff (~rst_n)
ifu2imem_req_o |-> ~|ifu2imem_addr_o[1:0]
) else $error("IFU Error: unaligned IMEM access");
`ifndef VERILATOR
SCR1_SVA_IFU_STOP_FETCH : assert property (
@(negedge clk) disable iff (~rst_n)
pipe2ifu_stop_fetch_i |=> (ifu_fsm_curr == SCR1_IFU_FSM_IDLE)
) else $error("IFU Error: fetch not stopped");
`endif // VERILATOR
SCR1_SVA_IFU_IMEM_FAULT_RVI_HI : assert property (
@(negedge clk) disable iff (~rst_n)
ifu2idu_err_rvi_hi_o |-> ifu2idu_imem_err_o
) else $error("IFU Error: ifu2idu_imem_err_o == 0");
`endif // SCR1_TRGT_SIMULATION
endmodule : scr1_pipe_ifu