Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
// Copyright 2018 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this 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.
////////////////////////////////////////////////////////////////////////////////
// Engineer: Matthias Baer - baermatt@student.ethz.ch //
// //
// Additional contributions by: //
// Igor Loi - igor.loi@unibo.it //
// Andreas Traber - atraber@student.ethz.ch //
// Sven Stucki - svstucki@student.ethz.ch //
// Michael Gautschi - gautschi@iis.ee.ethz.ch //
// Davide Schiavone - pschiavo@iis.ee.ethz.ch //
// Robert Balas - balasr@iis.ee.ethz.ch //
// Andrea Bettati - andrea.bettati@studenti.unipr.it //
// //
// Design Name: Main controller //
// Project Name: RI5CY //
// Language: SystemVerilog //
// //
// Description: Main CPU controller of the processor //
// //
////////////////////////////////////////////////////////////////////////////////
module cv32e40p_controller import cv32e40p_pkg::*;
#(
parameter COREV_CLUSTER = 0,
parameter COREV_PULP = 0,
parameter FPU = 0
)
(
input logic clk, // Gated clock
input logic clk_ungated_i, // Ungated clock
input logic rst_n,
input logic fetch_enable_i, // Start the decoding
output logic ctrl_busy_o, // Core is busy processing instructions
output logic is_decoding_o, // Core is in decoding state
input logic is_fetch_failed_i,
// decoder related signals
output logic deassert_we_o, // deassert write enable for next instruction
input logic illegal_insn_i, // decoder encountered an invalid instruction
input logic ecall_insn_i, // decoder encountered an ecall instruction
input logic mret_insn_i, // decoder encountered an mret instruction
input logic uret_insn_i, // decoder encountered an uret instruction
input logic dret_insn_i, // decoder encountered an dret instruction
input logic mret_dec_i,
input logic uret_dec_i,
input logic dret_dec_i,
input logic wfi_i, // decoder wants to execute a WFI
input logic ebrk_insn_i, // decoder encountered an ebreak instruction
input logic fencei_insn_i, // decoder encountered an fence.i instruction
input logic csr_status_i, // decoder encountered an csr status instruction
output logic hwlp_mask_o, // prevent writes on the hwloop instructions in case interrupt are taken
// from IF/ID pipeline
input logic instr_valid_i, // instruction coming from IF/ID pipeline is valid
// from prefetcher
output logic instr_req_o, // Start fetching instructions
// to prefetcher
output logic pc_set_o, // jump to address set by pc_mux
output logic [3:0] pc_mux_o, // Selector in the Fetch stage to select the rigth PC (normal, jump ...)
output logic [2:0] exc_pc_mux_o, // Selects target PC for exception
output logic [1:0] trap_addr_mux_o, // Selects trap address base
// HWLoop signls
input logic [31:0] pc_id_i,
// from hwloop_regs
input logic [1:0] [31:0] hwlp_start_addr_i,
input logic [1:0] [31:0] hwlp_end_addr_i,
input logic [1:0] [31:0] hwlp_counter_i,
// to hwloop_regs
output logic [1:0] hwlp_dec_cnt_o,
output logic hwlp_jump_o,
output logic [31:0] hwlp_targ_addr_o,
// LSU
input logic data_req_ex_i, // data memory access is currently performed in EX stage
input logic data_we_ex_i,
input logic data_misaligned_i,
input logic data_load_event_i,
input logic data_err_i,
output logic data_err_ack_o,
// from ALU
input logic mult_multicycle_i, // multiplier is taken multiple cycles and uses op c as storage
// APU dependency checks
input logic apu_en_i,
input logic apu_read_dep_i,
input logic apu_read_dep_for_jalr_i,
input logic apu_write_dep_i,
output logic apu_stall_o,
// jump/branch signals
input logic branch_taken_ex_i, // branch taken signal from EX ALU
input logic [1:0] ctrl_transfer_insn_in_id_i, // jump is being calculated in ALU
input logic [1:0] ctrl_transfer_insn_in_dec_i, // jump is being calculated in ALU
// Interrupt Controller Signals
input logic irq_req_ctrl_i,
input logic irq_sec_ctrl_i,
input logic [4:0] irq_id_ctrl_i,
input logic irq_wu_ctrl_i,
input PrivLvl_t current_priv_lvl_i,
output logic irq_ack_o,
output logic [4:0] irq_id_o,
output logic [4:0] exc_cause_o,
// Debug Signal
output logic debug_mode_o,
output logic [2:0] debug_cause_o,
output logic debug_csr_save_o,
input logic debug_req_i,
input logic debug_single_step_i,
input logic debug_ebreakm_i,
input logic debug_ebreaku_i,
input logic trigger_match_i,
output logic debug_p_elw_no_sleep_o,
output logic debug_wfi_no_sleep_o,
output logic debug_havereset_o,
output logic debug_running_o,
output logic debug_halted_o,
// Wakeup Signal
output logic wake_from_sleep_o,
output logic csr_save_if_o,
output logic csr_save_id_o,
output logic csr_save_ex_o,
output logic [5:0] csr_cause_o,
output logic csr_irq_sec_o,
output logic csr_restore_mret_id_o,
output logic csr_restore_uret_id_o,
output logic csr_restore_dret_id_o,
output logic csr_save_cause_o,
// Regfile target
input logic regfile_we_id_i, // currently decoded we enable
input logic [5:0] regfile_alu_waddr_id_i, // currently decoded target address
// Forwarding signals from regfile
input logic regfile_we_ex_i, // FW: write enable from EX stage
input logic [5:0] regfile_waddr_ex_i, // FW: write address from EX stage
input logic regfile_we_wb_i, // FW: write enable from WB stage
input logic regfile_alu_we_fw_i, // FW: ALU/MUL write enable from EX stage
// forwarding signals
output logic [1:0] operand_a_fw_mux_sel_o, // regfile ra data selector form ID stage
output logic [1:0] operand_b_fw_mux_sel_o, // regfile rb data selector form ID stage
output logic [1:0] operand_c_fw_mux_sel_o, // regfile rc data selector form ID stage
// forwarding detection signals
input logic reg_d_ex_is_reg_a_i,
input logic reg_d_ex_is_reg_b_i,
input logic reg_d_ex_is_reg_c_i,
input logic reg_d_wb_is_reg_a_i,
input logic reg_d_wb_is_reg_b_i,
input logic reg_d_wb_is_reg_c_i,
input logic reg_d_alu_is_reg_a_i,
input logic reg_d_alu_is_reg_b_i,
input logic reg_d_alu_is_reg_c_i,
// stall signals
output logic halt_if_o,
output logic halt_id_o,
output logic misaligned_stall_o,
output logic jr_stall_o,
output logic load_stall_o,
input logic id_ready_i, // ID stage is ready
input logic id_valid_i, // ID stage is valid
input logic ex_valid_i, // EX stage is done
input logic wb_ready_i, // WB stage is ready
// Performance Counters
output logic perf_pipeline_stall_o // stall due to cv.elw extra cycles
);
// FSM state encoding
ctrl_state_e ctrl_fsm_cs, ctrl_fsm_ns;
// Debug state
debug_state_e debug_fsm_cs, debug_fsm_ns;
logic jump_done, jump_done_q, jump_in_dec, branch_in_id;
logic data_err_q;
logic debug_mode_q, debug_mode_n;
logic ebrk_force_debug_mode;
logic is_hwlp_body;
logic illegal_insn_q, illegal_insn_n;
logic debug_req_entry_q, debug_req_entry_n;
logic debug_force_wakeup_q, debug_force_wakeup_n;
logic hwlp_end0_eq_pc;
logic hwlp_end1_eq_pc;
logic hwlp_counter0_gt_1;
logic hwlp_counter1_gt_1;
logic hwlp_counter0_eq_1;
logic hwlp_counter1_eq_1;
logic hwlp_counter0_eq_0;
logic hwlp_counter1_eq_0;
logic hwlp_end0_eq_pc_plus4;
logic hwlp_end1_eq_pc_plus4;
logic hwlp_start0_leq_pc;
logic hwlp_start1_leq_pc;
logic hwlp_end0_geq_pc;
logic hwlp_end1_geq_pc;
// Auxiliary signals to make hwlp_jump_o last only one cycle (converting it into a pulse)
logic hwlp_end_4_id_d, hwlp_end_4_id_q;
logic debug_req_q;
logic debug_req_pending;
// qualify wfi vs nosleep locally
logic wfi_active;
////////////////////////////////////////////////////////////////////////////////////////////
// ____ ___ ____ _____ ____ ___ _ _ _____ ____ ___ _ _ _____ ____ //
// / ___/ _ \| _ \| ____| / ___/ _ \| \ | |_ _| _ \ / _ \| | | | | ____| _ \ //
// | | | | | | |_) | _| | | | | | | \| | | | | |_) | | | | | | | | _| | |_) | //
// | |__| |_| | _ <| |___ | |__| |_| | |\ | | | | _ <| |_| | |___| |___| |___| _ < //
// \____\___/|_| \_\_____| \____\___/|_| \_| |_| |_| \_\\___/|_____|_____|_____|_| \_\ //
// //
////////////////////////////////////////////////////////////////////////////////////////////
always_comb
begin
// Default values
instr_req_o = 1'b1;
data_err_ack_o = 1'b0;
csr_save_if_o = 1'b0;
csr_save_id_o = 1'b0;
csr_save_ex_o = 1'b0;
csr_restore_mret_id_o = 1'b0;
csr_restore_uret_id_o = 1'b0;
csr_restore_dret_id_o = 1'b0;
csr_save_cause_o = 1'b0;
exc_cause_o = '0;
exc_pc_mux_o = EXC_PC_IRQ;
trap_addr_mux_o = TRAP_MACHINE;
csr_cause_o = '0;
csr_irq_sec_o = 1'b0;
pc_mux_o = PC_BOOT;
pc_set_o = 1'b0;
jump_done = jump_done_q;
ctrl_fsm_ns = ctrl_fsm_cs;
ctrl_busy_o = 1'b1;
halt_if_o = 1'b0;
halt_id_o = 1'b0;
is_decoding_o = 1'b0;
irq_ack_o = 1'b0;
irq_id_o = 5'b0;
jump_in_dec = ctrl_transfer_insn_in_dec_i == BRANCH_JALR || ctrl_transfer_insn_in_dec_i == BRANCH_JAL;
branch_in_id = ctrl_transfer_insn_in_id_i == BRANCH_COND;
ebrk_force_debug_mode = (debug_ebreakm_i && current_priv_lvl_i == PRIV_LVL_M) ||
(debug_ebreaku_i && current_priv_lvl_i == PRIV_LVL_U);
debug_csr_save_o = 1'b0;
debug_cause_o = DBG_CAUSE_EBREAK;
debug_mode_n = debug_mode_q;
illegal_insn_n = illegal_insn_q;
// a trap towards the debug unit is generated when one of the
// following conditions are true:
// - ebreak instruction encountered
// - single-stepping mode enabled
// - illegal instruction exception and IIE bit is set
// - IRQ and INTE bit is set and no exception is currently running
// - Debuger requests halt
debug_req_entry_n = debug_req_entry_q;
debug_force_wakeup_n = debug_force_wakeup_q;
perf_pipeline_stall_o = 1'b0;
hwlp_mask_o = 1'b0;
hwlp_dec_cnt_o = '0;
hwlp_end_4_id_d = 1'b0;
// When the controller tells to hwlp-jump, the prefetcher does not always jump immediately,
// but the aligner immediately modifies pc_id to HWLP_BEGIN. This condition on hwlp_targ_addr_o
// ensures that the target is kept constant even if pc_id is no more HWLP_END
hwlp_targ_addr_o = ((hwlp_start1_leq_pc && hwlp_end1_geq_pc) && !(hwlp_start0_leq_pc && hwlp_end0_geq_pc)) ? hwlp_start_addr_i[1] : hwlp_start_addr_i[0];
unique case (ctrl_fsm_cs)
// We were just reset, wait for fetch_enable
RESET:
begin
is_decoding_o = 1'b0;
instr_req_o = 1'b0;
if (fetch_enable_i == 1'b1)
begin
ctrl_fsm_ns = BOOT_SET;
end
end
// copy boot address to instr fetch address
BOOT_SET:
begin
is_decoding_o = 1'b0;
instr_req_o = 1'b1;
pc_mux_o = PC_BOOT;
pc_set_o = 1'b1;
if (debug_req_pending) begin
ctrl_fsm_ns = DBG_TAKEN_IF;
debug_force_wakeup_n = 1'b1;
end else begin
ctrl_fsm_ns = FIRST_FETCH;
end
end
WAIT_SLEEP:
begin
is_decoding_o = 1'b0;
ctrl_busy_o = 1'b0;
instr_req_o = 1'b0;
halt_if_o = 1'b1;
halt_id_o = 1'b1;
ctrl_fsm_ns = SLEEP;
end
// instruction in if_stage is already valid
SLEEP:
begin
// we begin execution when an
// interrupt has arrived
is_decoding_o = 1'b0;
instr_req_o = 1'b0;
halt_if_o = 1'b1;
halt_id_o = 1'b1;
// normal execution flow
// in debug mode or single step mode we leave immediately (wfi=nop)
if (wake_from_sleep_o) begin
if (debug_req_pending) begin
ctrl_fsm_ns = DBG_TAKEN_IF;
debug_force_wakeup_n = 1'b1;
end else begin
ctrl_fsm_ns = FIRST_FETCH;
end
end else begin
ctrl_busy_o = 1'b0;
end
end
FIRST_FETCH:
begin
is_decoding_o = 1'b0;
// ID stage is always ready
ctrl_fsm_ns = DECODE;
// handle interrupts
if (irq_req_ctrl_i && ~(debug_req_pending || debug_mode_q)) begin
// This assumes that the pipeline is always flushed before
// going to sleep.
// Debug mode takes precedence over irq (see DECODE:)
// Taken IRQ
halt_if_o = 1'b1;
halt_id_o = 1'b1;
pc_set_o = 1'b1;
pc_mux_o = PC_EXCEPTION;
exc_pc_mux_o = EXC_PC_IRQ;
exc_cause_o = irq_id_ctrl_i;
csr_irq_sec_o = irq_sec_ctrl_i;
// IRQ interface
irq_ack_o = 1'b1;
irq_id_o = irq_id_ctrl_i;
if (irq_sec_ctrl_i)
trap_addr_mux_o = TRAP_MACHINE;
else
trap_addr_mux_o = current_priv_lvl_i == PRIV_LVL_U ? TRAP_USER : TRAP_MACHINE;
csr_save_cause_o = 1'b1;
csr_cause_o = {1'b1,irq_id_ctrl_i};
csr_save_if_o = 1'b1;
end
end
DECODE:
begin
if (branch_taken_ex_i)
begin //taken branch
// there is a branch in the EX stage that is taken
is_decoding_o = 1'b0;
pc_mux_o = PC_BRANCH;
pc_set_o = 1'b1;
// if we want to debug, flush the pipeline
// the current_pc_if will take the value of the next instruction to
// be executed (NPC)
end //taken branch
else if (data_err_i)
begin //data error
// the current LW or SW have been blocked by the PMP
is_decoding_o = 1'b0;
halt_if_o = 1'b1;
halt_id_o = 1'b1;
csr_save_ex_o = 1'b1;
csr_save_cause_o = 1'b1;
data_err_ack_o = 1'b1;
//no jump in this stage as we have to wait one cycle to go to Machine Mode
csr_cause_o = {1'b0, data_we_ex_i ? EXC_CAUSE_STORE_FAULT : EXC_CAUSE_LOAD_FAULT};
ctrl_fsm_ns = FLUSH_WB;
end //data error
else if (is_fetch_failed_i)
begin
// the current instruction has been blocked by the PMP
is_decoding_o = 1'b0;
halt_id_o = 1'b1;
halt_if_o = 1'b1;
csr_save_if_o = 1'b1;
csr_save_cause_o = !debug_mode_q;
//no jump in this stage as we have to wait one cycle to go to Machine Mode
csr_cause_o = {1'b0, EXC_CAUSE_INSTR_FAULT};
ctrl_fsm_ns = FLUSH_WB;
end
// decode and execute instructions only if the current conditional
// branch in the EX stage is either not taken, or there is no
// conditional branch in the EX stage
else if (instr_valid_i) //valid block
begin: blk_decode_level1 // now analyze the current instruction in the ID stage
is_decoding_o = 1'b1;
illegal_insn_n = 1'b0;
if ( (debug_req_pending || trigger_match_i) & ~debug_mode_q )
begin
//Serving the debug
is_decoding_o = COREV_PULP ? 1'b0 : 1'b1;
halt_if_o = 1'b1;
halt_id_o = 1'b1;
ctrl_fsm_ns = DBG_FLUSH;
debug_req_entry_n = 1'b1;
end
else if (irq_req_ctrl_i && ~debug_mode_q)
begin
// Taken IRQ
hwlp_mask_o = COREV_PULP ? 1'b1 : 1'b0;
is_decoding_o = 1'b0;
halt_if_o = 1'b1;
halt_id_o = 1'b1;
pc_set_o = 1'b1;
pc_mux_o = PC_EXCEPTION;
exc_pc_mux_o = EXC_PC_IRQ;
exc_cause_o = irq_id_ctrl_i;
csr_irq_sec_o = irq_sec_ctrl_i;
// IRQ interface
irq_ack_o = 1'b1;
irq_id_o = irq_id_ctrl_i;
if (irq_sec_ctrl_i)
trap_addr_mux_o = TRAP_MACHINE;
else
trap_addr_mux_o = current_priv_lvl_i == PRIV_LVL_U ? TRAP_USER : TRAP_MACHINE;
csr_save_cause_o = 1'b1;
csr_cause_o = {1'b1,irq_id_ctrl_i};
csr_save_id_o = 1'b1;
end
else
begin
if (illegal_insn_i) begin
halt_if_o = 1'b1;
halt_id_o = 1'b0;
ctrl_fsm_ns = id_ready_i ? FLUSH_EX : DECODE;
illegal_insn_n = 1'b1;
end else begin
//decoding block
unique case (1'b1)
jump_in_dec: begin
// handle unconditional jumps
// we can jump directly since we know the address already
// we don't need to worry about conditional branches here as they
// will be evaluated in the EX stage
pc_mux_o = PC_JUMP;
// if there is a jr stall, wait for it to be gone
if ((~jr_stall_o) && (~jump_done_q)) begin
pc_set_o = 1'b1;
jump_done = 1'b1;
end
end
ebrk_insn_i: begin
halt_if_o = 1'b1;
halt_id_o = 1'b0;
if (debug_mode_q)
// we got back to the park loop in the debug rom
ctrl_fsm_ns = DBG_FLUSH;
else if (ebrk_force_debug_mode) begin
// debug module commands us to enter debug mode anyway
ctrl_fsm_ns = DBG_FLUSH;
end else begin
// otherwise just a normal ebreak exception
ctrl_fsm_ns = id_ready_i ? FLUSH_EX : DECODE;
end
end
wfi_active: begin
halt_if_o = 1'b1;
halt_id_o = 1'b0;
ctrl_fsm_ns = id_ready_i ? FLUSH_EX : DECODE;
end
ecall_insn_i: begin
halt_if_o = 1'b1;
halt_id_o = 1'b0;
ctrl_fsm_ns = id_ready_i ? FLUSH_EX : DECODE;
end
fencei_insn_i: begin
halt_if_o = 1'b1;
halt_id_o = 1'b0;
ctrl_fsm_ns = id_ready_i ? FLUSH_EX : DECODE;
end
mret_insn_i | uret_insn_i | dret_insn_i: begin
halt_if_o = 1'b1;
halt_id_o = 1'b0;
ctrl_fsm_ns = id_ready_i ? FLUSH_EX : DECODE;
end
csr_status_i: begin
halt_if_o = 1'b1;
if (~id_ready_i) begin
ctrl_fsm_ns = DECODE;
end else begin
ctrl_fsm_ns = FLUSH_EX;
if (hwlp_end0_eq_pc) begin
hwlp_dec_cnt_o[0] = 1'b1;
end
if (hwlp_end1_eq_pc) begin
hwlp_dec_cnt_o[1] = 1'b1;
end
end
end
data_load_event_i: begin
ctrl_fsm_ns = id_ready_i ? ELW_EXE : DECODE;
halt_if_o = 1'b1;
end
default: begin
if(is_hwlp_body) begin
//we are at the inside of an HWloop, thus change state
//We stay here in case we returned from the second last instruction, otherwise the next cycle
//in DECODE_HWLOOP we miss to jump, we jump at PC_END.
//This way looses a cycle but it's a corner case of returning from exceptions or interrupts
ctrl_fsm_ns = hwlp_end0_eq_pc_plus4 || hwlp_end1_eq_pc_plus4 ? DECODE : DECODE_HWLOOP;
// we can be at the end of HWloop due to a return from interrupt or ecall or ebreak or exceptions
if (hwlp_end0_eq_pc && hwlp_counter0_gt_1) begin
pc_mux_o = PC_HWLOOP;
if (~jump_done_q) begin
pc_set_o = 1'b1;
// Keep the instruction and the related address in the Aligner if
// ID is stalled during a jump
jump_done = 1'b1;
hwlp_dec_cnt_o[0] = 1'b1;
end
end
if (hwlp_end1_eq_pc && hwlp_counter1_gt_1) begin
pc_mux_o = PC_HWLOOP;
if (~jump_done_q) begin
pc_set_o = 1'b1;
// Keep the instruction and the related address in the Aligner if
// ID is stalled during a jump
jump_done = 1'b1;
hwlp_dec_cnt_o[1] = 1'b1;
end
end
end
if (hwlp_end0_eq_pc && hwlp_counter0_eq_1) begin
hwlp_dec_cnt_o[0] = 1'b1;
end
if (hwlp_end1_eq_pc && hwlp_counter1_eq_1) begin
hwlp_dec_cnt_o[1] = 1'b1;
end
end
endcase // unique case (1'b1)
end
if (debug_single_step_i & ~debug_mode_q) begin
// prevent any more instructions from executing
halt_if_o = 1'b1;
// we don't handle dret here because its should be illegal
// anyway in this context
// illegal, ecall, ebrk and xrettransition to later to a DBG
// state since we need the return address which is
// determined later
if (id_ready_i) begin
// make sure the current instruction has been executed
unique case(1'b1)
illegal_insn_i | ecall_insn_i:
begin
ctrl_fsm_ns = FLUSH_EX;
end
(~ebrk_force_debug_mode & ebrk_insn_i):
begin
ctrl_fsm_ns = FLUSH_EX;
end
mret_insn_i | uret_insn_i:
begin
ctrl_fsm_ns = FLUSH_EX;
end
branch_in_id:
begin
ctrl_fsm_ns = DBG_WAIT_BRANCH;
end
default:
// regular instruction or ebrk force debug
ctrl_fsm_ns = DBG_FLUSH;
endcase // unique case (1'b1)
end
end
end // else: !if (irq_req_ctrl_i && ~debug_mode_q)
end //valid block
else begin
is_decoding_o = 1'b0;
perf_pipeline_stall_o = data_load_event_i;
end
end
DECODE_HWLOOP:
begin
if (COREV_PULP) begin
if (instr_valid_i) // valid block
begin // now analyze the current instruction in the ID stage
is_decoding_o = 1'b1;
if ( (debug_req_pending || trigger_match_i) & ~debug_mode_q )
begin
//Serving the debug
is_decoding_o = COREV_PULP ? 1'b0 : 1'b1;
halt_if_o = 1'b1;
halt_id_o = 1'b1;
ctrl_fsm_ns = DBG_FLUSH;
debug_req_entry_n = 1'b1;
end
else if (irq_req_ctrl_i && ~debug_mode_q)
begin
// Taken IRQ
hwlp_mask_o = COREV_PULP ? 1'b1 : 1'b0;
is_decoding_o = 1'b0;
halt_if_o = 1'b1;
halt_id_o = 1'b1;
pc_set_o = 1'b1;
pc_mux_o = PC_EXCEPTION;
exc_pc_mux_o = EXC_PC_IRQ;
exc_cause_o = irq_id_ctrl_i;
csr_irq_sec_o = irq_sec_ctrl_i;
// IRQ interface
irq_ack_o = 1'b1;
irq_id_o = irq_id_ctrl_i;
if (irq_sec_ctrl_i)
trap_addr_mux_o = TRAP_MACHINE;
else
trap_addr_mux_o = current_priv_lvl_i == PRIV_LVL_U ? TRAP_USER : TRAP_MACHINE;
csr_save_cause_o = 1'b1;
csr_cause_o = {1'b1,irq_id_ctrl_i};
csr_save_id_o = 1'b1;
ctrl_fsm_ns = DECODE;
end
else
begin
if (illegal_insn_i) begin
halt_if_o = 1'b1;
halt_id_o = 1'b1;
ctrl_fsm_ns = FLUSH_EX;
illegal_insn_n = 1'b1;
end else begin
//decoding block
unique case (1'b1)
ebrk_insn_i: begin
halt_if_o = 1'b1;
halt_id_o = 1'b0;
if (debug_mode_q)
// we got back to the park loop in the debug rom
ctrl_fsm_ns = DBG_FLUSH;
else if (ebrk_force_debug_mode)
// debug module commands us to enter debug mode anyway
ctrl_fsm_ns = DBG_FLUSH;
else begin
// otherwise just a normal ebreak exception
ctrl_fsm_ns = id_ready_i ? FLUSH_EX : DECODE_HWLOOP;
end
end
ecall_insn_i: begin
halt_if_o = 1'b1;
halt_id_o = 1'b0;
ctrl_fsm_ns = id_ready_i ? FLUSH_EX : DECODE_HWLOOP;
end
csr_status_i: begin
halt_if_o = 1'b1;
if (~id_ready_i) begin
ctrl_fsm_ns = DECODE_HWLOOP;
end else begin
ctrl_fsm_ns = FLUSH_EX;
if (hwlp_end0_eq_pc) begin
hwlp_dec_cnt_o[0] = 1'b1;
end
if (hwlp_end1_eq_pc) begin
hwlp_dec_cnt_o[1] = 1'b1;
end
end
end
data_load_event_i: begin
ctrl_fsm_ns = id_ready_i ? ELW_EXE : DECODE_HWLOOP;
halt_if_o = 1'b1;
end
default: begin
// we can be at the end of HWloop due to a return from interrupt or ecall or ebreak or exceptions
if(hwlp_end1_eq_pc_plus4) begin
if(hwlp_counter1_gt_1) begin
hwlp_end_4_id_d = 1'b1;
hwlp_targ_addr_o = hwlp_start_addr_i[1];
ctrl_fsm_ns = DECODE_HWLOOP;
end else
ctrl_fsm_ns = is_hwlp_body ? DECODE_HWLOOP : DECODE;
end
if(hwlp_end0_eq_pc_plus4) begin
if(hwlp_counter0_gt_1) begin
hwlp_end_4_id_d = 1'b1;
hwlp_targ_addr_o = hwlp_start_addr_i[0];
ctrl_fsm_ns = DECODE_HWLOOP;
end else
ctrl_fsm_ns = is_hwlp_body ? DECODE_HWLOOP : DECODE;
end
hwlp_dec_cnt_o[0] = hwlp_end0_eq_pc && !hwlp_counter0_eq_0;
hwlp_dec_cnt_o[1] = hwlp_end1_eq_pc && !hwlp_counter1_eq_0;
end
endcase // unique case (1'b1)
end // else: !if(illegal_insn_i)
if (debug_single_step_i & ~debug_mode_q) begin
// prevent any more instructions from executing
halt_if_o = 1'b1;
// we don't handle dret here because its should be illegal
// anyway in this context
// illegal, ecall, ebrk and xrettransition to later to a DBG
// state since we need the return address which is
// determined later
if (id_ready_i) begin
// make sure the current instruction has been executed
unique case(1'b1)
illegal_insn_i | ecall_insn_i:
begin
ctrl_fsm_ns = FLUSH_EX;
end
(~ebrk_force_debug_mode & ebrk_insn_i):
begin
ctrl_fsm_ns = FLUSH_EX;
end
mret_insn_i | uret_insn_i:
begin
ctrl_fsm_ns = FLUSH_EX;
end
branch_in_id:
begin
ctrl_fsm_ns = DBG_WAIT_BRANCH;
end
default:
// regular instruction or ebrk force debug
ctrl_fsm_ns = DBG_FLUSH;
endcase // unique case (1'b1)
end
end // if (debug_single_step_i & ~debug_mode_q)
end // else: !if (irq_req_ctrl_i && ~debug_mode_q)
end // block: blk_decode_level1 : valid block
else begin
is_decoding_o = 1'b0;
perf_pipeline_stall_o = data_load_event_i;
end
end
end
// flush the pipeline, insert NOP into EX stage
FLUSH_EX:
begin
is_decoding_o = 1'b0;
halt_if_o = 1'b1;
halt_id_o = 1'b1;
if (data_err_i)
begin //data error
// the current LW or SW have been blocked by the PMP
csr_save_ex_o = 1'b1;
csr_save_cause_o = 1'b1;
data_err_ack_o = 1'b1;
//no jump in this stage as we have to wait one cycle to go to Machine Mode
csr_cause_o = {1'b0, data_we_ex_i ? EXC_CAUSE_STORE_FAULT : EXC_CAUSE_LOAD_FAULT};
ctrl_fsm_ns = FLUSH_WB;
//putting illegal to 0 as if it was 1, the core is going to jump to the exception of the EX stage,
//so the illegal was never executed
illegal_insn_n = 1'b0;
end //data erro
else if (ex_valid_i) begin
//check done to prevent data harzard in the CSR registers
ctrl_fsm_ns = FLUSH_WB;
if(illegal_insn_q) begin
csr_save_id_o = 1'b1;
csr_save_cause_o = !debug_mode_q;
csr_cause_o = {1'b0, EXC_CAUSE_ILLEGAL_INSN};
end else begin
unique case (1'b1)
ebrk_insn_i: begin
csr_save_id_o = 1'b1;
csr_save_cause_o = 1'b1;
csr_cause_o = {1'b0, EXC_CAUSE_BREAKPOINT};
end
ecall_insn_i: begin
csr_save_id_o = 1'b1;
csr_save_cause_o = !debug_mode_q;
csr_cause_o = {1'b0, current_priv_lvl_i == PRIV_LVL_U ? EXC_CAUSE_ECALL_UMODE : EXC_CAUSE_ECALL_MMODE};
end
default:;
endcase // unique case (1'b1)
end
end
end
IRQ_FLUSH_ELW:
begin
if (COREV_CLUSTER == 1'b1) begin
is_decoding_o = 1'b0;
halt_if_o = 1'b1;
halt_id_o = 1'b1;
ctrl_fsm_ns = DECODE;
perf_pipeline_stall_o = data_load_event_i;
if (irq_req_ctrl_i && ~(debug_req_pending || debug_mode_q)) begin
// Taken IRQ
is_decoding_o = 1'b0;
halt_if_o = 1'b1;
halt_id_o = 1'b1;
pc_set_o = 1'b1;
pc_mux_o = PC_EXCEPTION;
exc_pc_mux_o = EXC_PC_IRQ;
exc_cause_o = irq_id_ctrl_i;
csr_irq_sec_o = irq_sec_ctrl_i;
// IRQ interface
irq_ack_o = 1'b1;
irq_id_o = irq_id_ctrl_i;
if (irq_sec_ctrl_i)
trap_addr_mux_o = TRAP_MACHINE;
else
trap_addr_mux_o = current_priv_lvl_i == PRIV_LVL_U ? TRAP_USER : TRAP_MACHINE;
csr_save_cause_o = 1'b1;
csr_cause_o = {1'b1,irq_id_ctrl_i};
csr_save_id_o = 1'b1;
end
end
end
ELW_EXE:
begin
if (COREV_CLUSTER == 1'b1) begin
is_decoding_o = 1'b0;
halt_if_o = 1'b1;
halt_id_o = 1'b1;
//if we are here, a cv.elw is executing now in the EX stage
//or if an interrupt has been received
//the ID stage contains the PC_ID of the cv.elw, therefore halt_id is set to invalid the instruction
//If an interrupt occurs, we replay the ELW
//No needs to check irq_int_req_i since in the EX stage there is only the cv.elw, no CSR pendings
if(id_ready_i)
ctrl_fsm_ns = ((debug_req_pending || trigger_match_i) & ~debug_mode_q) ? DBG_FLUSH : IRQ_FLUSH_ELW;
// if from the ELW EXE we go to IRQ_FLUSH_ELW, it is assumed that if there was an IRQ req together with the grant and IE was valid, then
// there must be no hazard due to xIE
else
ctrl_fsm_ns = ELW_EXE;
perf_pipeline_stall_o = data_load_event_i;
end
end
// flush the pipeline, insert NOP into EX and WB stage
FLUSH_WB:
begin
is_decoding_o = 1'b0;
halt_if_o = 1'b1;
halt_id_o = 1'b1;
ctrl_fsm_ns = DECODE;
if(data_err_q) begin
//PMP data_error
pc_mux_o = PC_EXCEPTION;
pc_set_o = 1'b1;
trap_addr_mux_o = TRAP_MACHINE;
//little hack during testing
exc_pc_mux_o = EXC_PC_EXCEPTION;
exc_cause_o = data_we_ex_i ? EXC_CAUSE_LOAD_FAULT : EXC_CAUSE_STORE_FAULT;
end
else if (is_fetch_failed_i) begin
//instruction fetch error
pc_mux_o = PC_EXCEPTION;
pc_set_o = 1'b1;
trap_addr_mux_o = TRAP_MACHINE;
exc_pc_mux_o = debug_mode_q ? EXC_PC_DBE : EXC_PC_EXCEPTION;
exc_cause_o = EXC_CAUSE_INSTR_FAULT;
end
else begin
if(illegal_insn_q) begin
//exceptions
pc_mux_o = PC_EXCEPTION;
pc_set_o = 1'b1;
trap_addr_mux_o = TRAP_MACHINE;
exc_pc_mux_o = debug_mode_q ? EXC_PC_DBE : EXC_PC_EXCEPTION;
illegal_insn_n = 1'b0;
if (debug_single_step_i && ~debug_mode_q)
ctrl_fsm_ns = DBG_TAKEN_IF;
end else begin
unique case(1'b1)
ebrk_insn_i: begin
//ebreak
pc_mux_o = PC_EXCEPTION;
pc_set_o = 1'b1;
trap_addr_mux_o = TRAP_MACHINE;
exc_pc_mux_o = EXC_PC_EXCEPTION;
if (debug_single_step_i && ~debug_mode_q)
ctrl_fsm_ns = DBG_TAKEN_IF;
end
ecall_insn_i: begin
//ecall
pc_mux_o = PC_EXCEPTION;
pc_set_o = 1'b1;
trap_addr_mux_o = TRAP_MACHINE;
exc_pc_mux_o = debug_mode_q ? EXC_PC_DBE : EXC_PC_EXCEPTION;
if (debug_single_step_i && ~debug_mode_q)
ctrl_fsm_ns = DBG_TAKEN_IF;
end
mret_insn_i: begin
csr_restore_mret_id_o = !debug_mode_q;
ctrl_fsm_ns = XRET_JUMP;
end
uret_insn_i: begin
csr_restore_uret_id_o = !debug_mode_q;
ctrl_fsm_ns = XRET_JUMP;
end
dret_insn_i: begin
csr_restore_dret_id_o = 1'b1;
ctrl_fsm_ns = XRET_JUMP;
end
csr_status_i: begin
if ((hwlp_end0_eq_pc && !hwlp_counter0_eq_0) ||
(hwlp_end1_eq_pc && !hwlp_counter1_eq_0)) begin
pc_mux_o = PC_HWLOOP;
pc_set_o = 1'b1;
end
end
wfi_i: begin
if ( debug_req_pending) begin
ctrl_fsm_ns = DBG_TAKEN_IF;
debug_force_wakeup_n = 1'b1;
end else begin
ctrl_fsm_ns = WAIT_SLEEP;
end
end
fencei_insn_i: begin
// we just jump to instruction after the fence.i since that
// forces the instruction cache to refetch
pc_mux_o = PC_FENCEI;
pc_set_o = 1'b1;
end
default:;
endcase
end
end
end
XRET_JUMP:
begin
is_decoding_o = 1'b0;
ctrl_fsm_ns = DECODE;
unique case(1'b1)
mret_dec_i: begin
//mret
pc_mux_o = debug_mode_q ? PC_EXCEPTION : PC_MRET;
pc_set_o = 1'b1;
exc_pc_mux_o = EXC_PC_DBE; // only used if in debug_mode
end
uret_dec_i: begin
//uret
pc_mux_o = debug_mode_q ? PC_EXCEPTION : PC_URET;
pc_set_o = 1'b1;
exc_pc_mux_o = EXC_PC_DBE; // only used if in debug_mode
end
dret_dec_i: begin
//dret
// this case is only reachable while in debug_mode
pc_mux_o = PC_DRET;
pc_set_o = 1'b1;
debug_mode_n = 1'b0;
end
default:;
endcase
if (debug_single_step_i && ~debug_mode_q) begin
ctrl_fsm_ns = DBG_TAKEN_IF;
end
end
// a branch was in ID when trying to go to debug rom. Wait until we can
// determine branch target address (for saving into dpc) before proceeding
DBG_WAIT_BRANCH:
begin
is_decoding_o = 1'b0;
halt_if_o = 1'b1;
if (branch_taken_ex_i) begin
// there is a branch in the EX stage that is taken
pc_mux_o = PC_BRANCH;
pc_set_o = 1'b1;
end
ctrl_fsm_ns = DBG_FLUSH;
end
// We enter this state when we encounter
// 1. ebreak during debug mode
// 2. trigger match
// 3. ebreak with forced entry into debug mode (ebreakm or ebreaku set).
// 4. halt request during decode
// Regular ebreak's go through FLUSH_EX and FLUSH_WB.
// For 1. we don't update dcsr and dpc while for 2., 3., & 4. we do
// dpc is set to the address of ebreak and trigger match
// not to the next instruction's (which is why we save the pc in id).
DBG_TAKEN_ID:
begin
is_decoding_o = 1'b0;
pc_set_o = 1'b1;
pc_mux_o = PC_EXCEPTION;
exc_pc_mux_o = EXC_PC_DBD;
// If not in debug mode then save cause and dpc csrs
// else it was an ebreak in debug mode, so don't update csrs
if (~debug_mode_q) begin
csr_save_cause_o = 1'b1;
csr_save_id_o = 1'b1;
debug_csr_save_o = 1'b1;
if (trigger_match_i)
debug_cause_o = DBG_CAUSE_TRIGGER; // pri 4 (highest)
else if (ebrk_force_debug_mode & ebrk_insn_i)
debug_cause_o = DBG_CAUSE_EBREAK; // pri 3
else if (debug_req_entry_q)
debug_cause_o = DBG_CAUSE_HALTREQ;// pri 2 and 1
end
debug_req_entry_n = 1'b0;
ctrl_fsm_ns = DECODE;
debug_mode_n = 1'b1;
end
// We enter this state for single stepping
// DPC is set the next instruction to be executed/fetched
DBG_TAKEN_IF:
begin
is_decoding_o = 1'b0;
pc_set_o = 1'b1;
pc_mux_o = PC_EXCEPTION;
exc_pc_mux_o = EXC_PC_DBD;
csr_save_cause_o = 1'b1;
debug_csr_save_o = 1'b1;
if (debug_force_wakeup_q)
debug_cause_o = DBG_CAUSE_HALTREQ;
else if (debug_single_step_i)
debug_cause_o = DBG_CAUSE_STEP; // pri 0
csr_save_if_o = 1'b1;
ctrl_fsm_ns = DECODE;
debug_mode_n = 1'b1;
debug_force_wakeup_n = 1'b0;
end
DBG_FLUSH:
begin
is_decoding_o = 1'b0;
halt_if_o = 1'b1;
halt_id_o = 1'b1;
perf_pipeline_stall_o = data_load_event_i;
if (data_err_i)
begin //data error
// the current LW or SW have been blocked by the PMP
csr_save_ex_o = 1'b1;
csr_save_cause_o = 1'b1;
data_err_ack_o = 1'b1;
//no jump in this stage as we have to wait one cycle to go to Machine Mode
csr_cause_o = {1'b0, data_we_ex_i ? EXC_CAUSE_STORE_FAULT : EXC_CAUSE_LOAD_FAULT};
ctrl_fsm_ns = FLUSH_WB;
end //data error
else begin
if(debug_mode_q |
trigger_match_i |
(ebrk_force_debug_mode & ebrk_insn_i) |
data_load_event_i |
debug_req_entry_q )
begin
ctrl_fsm_ns = DBG_TAKEN_ID;
end else
begin
// else must be debug_single_step_i
ctrl_fsm_ns = DBG_TAKEN_IF;
end
end
end
// Debug end
default: begin
is_decoding_o = 1'b0;
instr_req_o = 1'b0;
ctrl_fsm_ns = RESET;
end
endcase
end
generate
if(COREV_PULP) begin : gen_hwlp
//////////////////////////////////////////////////////////////////////////////
// Convert hwlp_jump_o to a pulse
//////////////////////////////////////////////////////////////////////////////
// hwlp_jump_o should last one cycle only, as the prefetcher
// reacts immediately. If it last more cycles, the prefetcher
// goes on requesting HWLP_BEGIN more than one time (wrong!).
// This signal is not controlled by id_ready because otherwise,
// in case of stall, the jump would happen at the end of the stall.
// Make hwlp_jump_o last only one cycle
assign hwlp_jump_o = (hwlp_end_4_id_d && !hwlp_end_4_id_q) ? 1'b1 : 1'b0;
always_ff @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
hwlp_end_4_id_q <= 1'b0;
end else begin
hwlp_end_4_id_q <= hwlp_end_4_id_d;
end
end
assign hwlp_end0_eq_pc = hwlp_end_addr_i[0] == pc_id_i + 4; // Equivalent to hwlp_end_addr_i[0] - 4 == pc_id_i
assign hwlp_end1_eq_pc = hwlp_end_addr_i[1] == pc_id_i + 4; // Equivalent to hwlp_end_addr_i[1] - 4 == pc_id_i
assign hwlp_counter0_gt_1 = hwlp_counter_i[0] > 1;
assign hwlp_counter1_gt_1 = hwlp_counter_i[1] > 1;
assign hwlp_counter0_eq_1 = hwlp_counter_i[0] == 1;
assign hwlp_counter1_eq_1 = hwlp_counter_i[1] == 1;
assign hwlp_counter0_eq_0 = hwlp_counter_i[0] == 0;
assign hwlp_counter1_eq_0 = hwlp_counter_i[1] == 0;
assign hwlp_end0_eq_pc_plus4 = hwlp_end_addr_i[0] == pc_id_i + 8; // Equivalent to hwlp_end_addr_i[0] - 4 == pc_id_i + 4
assign hwlp_end1_eq_pc_plus4 = hwlp_end_addr_i[1] == pc_id_i + 8; // Equivalent to hwlp_end_addr_i[1] - 4 == pc_id_i + 4
assign hwlp_start0_leq_pc = hwlp_start_addr_i[0] <= pc_id_i;
assign hwlp_start1_leq_pc = hwlp_start_addr_i[1] <= pc_id_i;
assign hwlp_end0_geq_pc = hwlp_end_addr_i[0] >= pc_id_i + 4; // Equivalent to hwlp_end_addr_i[0] - 4 >= pc_id_i
assign hwlp_end1_geq_pc = hwlp_end_addr_i[1] >= pc_id_i + 4; // Equivalent to hwlp_end_addr_i[1] - 4 >= pc_id_i;
assign is_hwlp_body = ((hwlp_start0_leq_pc && hwlp_end0_geq_pc) && hwlp_counter0_gt_1) || ((hwlp_start1_leq_pc && hwlp_end1_geq_pc) && hwlp_counter1_gt_1);
end else begin : gen_no_hwlp
assign hwlp_jump_o = 1'b0;
assign hwlp_end_4_id_q = 1'b0;
assign hwlp_end0_eq_pc = 1'b0;
assign hwlp_end1_eq_pc = 1'b0;
assign hwlp_counter0_gt_1 = 1'b0;
assign hwlp_counter1_gt_1 = 1'b0;
assign hwlp_counter0_eq_1 = 1'b0;
assign hwlp_counter1_eq_1 = 1'b0;
assign hwlp_counter0_eq_0 = 1'b0;
assign hwlp_counter1_eq_0 = 1'b0;
assign hwlp_end0_eq_pc_plus4 = 1'b0;
assign hwlp_end1_eq_pc_plus4 = 1'b0;
assign hwlp_start0_leq_pc = 1'b0;
assign hwlp_start1_leq_pc = 1'b0;
assign hwlp_end0_geq_pc = 1'b0;
assign hwlp_end1_geq_pc = 1'b0;
assign is_hwlp_body = 1'b0;
end
endgenerate
/////////////////////////////////////////////////////////////
// ____ _ _ _ ____ _ _ //
// / ___|| |_ __ _| | | / ___|___ _ __ | |_ _ __ ___ | | //
// \___ \| __/ _` | | | | | / _ \| '_ \| __| '__/ _ \| | //
// ___) | || (_| | | | | |__| (_) | | | | |_| | | (_) | | //
// |____/ \__\__,_|_|_| \____\___/|_| |_|\__|_| \___/|_| //
// //
/////////////////////////////////////////////////////////////
always_comb
begin
load_stall_o = 1'b0;
deassert_we_o = 1'b0;
// deassert WE when the core is not decoding instructions
if (~is_decoding_o)
deassert_we_o = 1'b1;
// deassert WE in case of illegal instruction
if (illegal_insn_i)
deassert_we_o = 1'b1;
// Stall because of load operation
if (
( (data_req_ex_i == 1'b1) && (regfile_we_ex_i == 1'b1) ||
(wb_ready_i == 1'b0) && (regfile_we_wb_i == 1'b1)
) &&
( (reg_d_ex_is_reg_a_i == 1'b1) || (reg_d_ex_is_reg_b_i == 1'b1) || (reg_d_ex_is_reg_c_i == 1'b1) ||
(is_decoding_o && (regfile_we_id_i && !data_misaligned_i) && (regfile_waddr_ex_i == regfile_alu_waddr_id_i)) )
)
begin
deassert_we_o = 1'b1;
load_stall_o = 1'b1;
end
// Stall because of jr path
// - always stall if a result is to be forwarded to the PC
// we don't care about in which state the ctrl_fsm is as we deassert_we
// anyway when we are not in DECODE
if ((ctrl_transfer_insn_in_dec_i == BRANCH_JALR) &&
(((regfile_we_wb_i == 1'b1) && (reg_d_wb_is_reg_a_i == 1'b1)) ||
((regfile_we_ex_i == 1'b1) && (reg_d_ex_is_reg_a_i == 1'b1)) ||
((regfile_alu_we_fw_i == 1'b1) && (reg_d_alu_is_reg_a_i == 1'b1)) ||
(FPU && (apu_read_dep_for_jalr_i == 1'b1))
)
)
begin
jr_stall_o = 1'b1;
deassert_we_o = 1'b1;
end
else
begin
jr_stall_o = 1'b0;
end
end
// stall because of misaligned data access
assign misaligned_stall_o = data_misaligned_i;
// APU dependency stalls (data hazards)
assign apu_stall_o = apu_read_dep_i | (apu_write_dep_i & ~apu_en_i);
// Forwarding control unit
always_comb
begin
// default assignements
operand_a_fw_mux_sel_o = SEL_REGFILE;
operand_b_fw_mux_sel_o = SEL_REGFILE;
operand_c_fw_mux_sel_o = SEL_REGFILE;
// Forwarding WB -> ID
if (regfile_we_wb_i == 1'b1)
begin
if (reg_d_wb_is_reg_a_i == 1'b1)
operand_a_fw_mux_sel_o = SEL_FW_WB;
if (reg_d_wb_is_reg_b_i == 1'b1)
operand_b_fw_mux_sel_o = SEL_FW_WB;
if (reg_d_wb_is_reg_c_i == 1'b1)
operand_c_fw_mux_sel_o = SEL_FW_WB;
end
// Forwarding EX -> ID
if (regfile_alu_we_fw_i == 1'b1)
begin
if (reg_d_alu_is_reg_a_i == 1'b1)
operand_a_fw_mux_sel_o = SEL_FW_EX;
if (reg_d_alu_is_reg_b_i == 1'b1)
operand_b_fw_mux_sel_o = SEL_FW_EX;
if (reg_d_alu_is_reg_c_i == 1'b1)
operand_c_fw_mux_sel_o = SEL_FW_EX;
end
// for misaligned memory accesses
if (data_misaligned_i)
begin
operand_a_fw_mux_sel_o = SEL_FW_EX;
operand_b_fw_mux_sel_o = SEL_REGFILE;
end else if (mult_multicycle_i) begin
operand_c_fw_mux_sel_o = SEL_FW_EX;
end
end
// update registers
always_ff @(posedge clk , negedge rst_n)
begin : UPDATE_REGS
if ( rst_n == 1'b0 )
begin
ctrl_fsm_cs <= RESET;
jump_done_q <= 1'b0;
data_err_q <= 1'b0;
debug_mode_q <= 1'b0;
illegal_insn_q <= 1'b0;
debug_req_entry_q <= 1'b0;
debug_force_wakeup_q <= 1'b0;
end
else
begin
ctrl_fsm_cs <= ctrl_fsm_ns;
// clear when id is valid (no instruction incoming)
jump_done_q <= jump_done & (~id_ready_i);
data_err_q <= data_err_i;
debug_mode_q <= debug_mode_n;
illegal_insn_q <= illegal_insn_n;
debug_req_entry_q <= debug_req_entry_n;
debug_force_wakeup_q <= debug_force_wakeup_n;
end
end
// wakeup from sleep conditions
assign wake_from_sleep_o = irq_wu_ctrl_i || debug_req_pending || debug_mode_q;
// debug mode
assign debug_mode_o = debug_mode_q;
assign debug_req_pending = debug_req_i || debug_req_q;
// Do not let cv.elw cause core_sleep_o during debug
assign debug_p_elw_no_sleep_o = debug_mode_q || debug_req_q || debug_single_step_i || trigger_match_i;
// Do not let WFI cause core_sleep_o (but treat as NOP):
//
// - During debug
// - For PULP Cluster (only cv.elw can trigger sleep)
assign debug_wfi_no_sleep_o = debug_mode_q || debug_req_pending || debug_single_step_i || trigger_match_i || COREV_CLUSTER;
// Gate off wfi
assign wfi_active = wfi_i & ~debug_wfi_no_sleep_o;
// sticky version of debug_req (must be on clk_ungated_i such that incoming pulse before core is enabled is not missed)
always_ff @(posedge clk_ungated_i, negedge rst_n)
if ( !rst_n )
debug_req_q <= 1'b0;
else
if( debug_req_i )
debug_req_q <= 1'b1;
else if( debug_mode_q )
debug_req_q <= 1'b0;
// Debug state FSM
always_ff @(posedge clk , negedge rst_n)
begin
if ( rst_n == 1'b0 )
begin
debug_fsm_cs <= HAVERESET;
end
else
begin
debug_fsm_cs <= debug_fsm_ns;
end
end
always_comb
begin
debug_fsm_ns = debug_fsm_cs;
case (debug_fsm_cs)
HAVERESET:
begin
if (debug_mode_n || (ctrl_fsm_ns == FIRST_FETCH)) begin
if (debug_mode_n) begin
debug_fsm_ns = HALTED;
end else begin
debug_fsm_ns = RUNNING;
end
end
end
RUNNING:
begin
if (debug_mode_n) begin
debug_fsm_ns = HALTED;
end
end
HALTED:
begin
if (!debug_mode_n) begin
debug_fsm_ns = RUNNING;
end
end
default: begin
debug_fsm_ns = HAVERESET;
end
endcase
end
assign debug_havereset_o = debug_fsm_cs[HAVERESET_INDEX];
assign debug_running_o = debug_fsm_cs[RUNNING_INDEX];
assign debug_halted_o = debug_fsm_cs[HALTED_INDEX];
//----------------------------------------------------------------------------
// Assertions
//----------------------------------------------------------------------------
`ifdef CV32E40P_ASSERT_ON
// make sure that taken branches do not happen back-to-back, as this is not
// possible without branch prediction in the IF stage
assert property (
@(posedge clk) (branch_taken_ex_i) |=> (~branch_taken_ex_i) ) else $warning("Two branches back-to-back are taken");
// ELW_EXE and IRQ_FLUSH_ELW states are only used for COREV_CLUSTER = 1
property p_pulp_cluster_only_states;
@(posedge clk) (1'b1) |-> ( !((COREV_CLUSTER == 1'b0) && ((ctrl_fsm_cs == ELW_EXE) || (ctrl_fsm_cs == IRQ_FLUSH_ELW))) );
endproperty
a_pulp_cluster_only_states : assert property(p_pulp_cluster_only_states);
// WAIT_SLEEP and SLEEP states are never used for COREV_CLUSTER = 1
property p_pulp_cluster_excluded_states;
@(posedge clk) (1'b1) |-> ( !((COREV_CLUSTER == 1'b1) && ((ctrl_fsm_cs == SLEEP) || (ctrl_fsm_cs == WAIT_SLEEP))) );
endproperty
a_pulp_cluster_excluded_states : assert property(p_pulp_cluster_excluded_states);
generate
if (COREV_PULP) begin : gen_pulp_xpulp_assertions
// HWLoop 0 and 1 having target address constraints
property p_hwlp_same_target_address;
@(posedge clk) (hwlp_counter_i[1] > 1 && hwlp_counter_i[0] > 1 && pc_id_i >= hwlp_start_addr_i[0] && pc_id_i <= hwlp_end_addr_i[0] - 4) |-> ( hwlp_end_addr_i[1] - 4 >= hwlp_end_addr_i[0] - 4 + 8 );
endproperty
a_hwlp_same_target_address : assert property(p_hwlp_same_target_address) else $warning("%t, HWLoops target address do not respect constraints", $time);
end else begin : gen_no_pulp_xpulp_assertions
property p_no_hwlp;
@(posedge clk) (1'b1) |-> ((pc_mux_o != PC_HWLOOP) && (ctrl_fsm_cs != DECODE_HWLOOP) &&
(hwlp_mask_o == 1'b0) && (is_hwlp_body == 'b0) &&
(hwlp_start_addr_i == 'b0) && (hwlp_end_addr_i == 'b0) && (hwlp_counter_i[1] == 32'b0) && (hwlp_counter_i[0] == 32'b0) &&
(hwlp_dec_cnt_o == 2'b0) && (hwlp_jump_o == 1'b0) && (hwlp_targ_addr_o == 32'b0) &&
(hwlp_end0_eq_pc == 1'b0) && (hwlp_end1_eq_pc == 1'b0) && (hwlp_counter0_gt_1 == 1'b0) && (hwlp_counter1_gt_1 == 1'b0) &&
(hwlp_counter0_eq_1 == 1'b0) && (hwlp_counter1_eq_1 == 1'b0) &&
(hwlp_end0_eq_pc_plus4 == 1'b0) && (hwlp_end1_eq_pc_plus4 == 1'b0) && (hwlp_start0_leq_pc == 0) && (hwlp_start1_leq_pc == 0) &&
(hwlp_end0_geq_pc == 1'b0) && (hwlp_end1_geq_pc == 1'b0) && (hwlp_end_4_id_d == 1'b0) && (hwlp_end_4_id_q == 1'b0));
endproperty
a_no_hwlp : assert property(p_no_hwlp);
end
endgenerate
// Ensure DBG_TAKEN_IF can only be enterred if in single step mode or woken
// up from sleep by debug_req_i
a_single_step_dbg_taken_if : assert property (@(posedge clk) disable iff (!rst_n) (ctrl_fsm_ns==DBG_TAKEN_IF) |-> ((~debug_mode_q && debug_single_step_i) || debug_force_wakeup_n));
// Ensure DBG_FLUSH state is only one cycle. This implies that cause is either trigger, debug_req_entry, or ebreak
a_dbg_flush : assert property (@(posedge clk) disable iff (!rst_n) (ctrl_fsm_cs==DBG_FLUSH) |-> (ctrl_fsm_ns!=DBG_FLUSH) );
// Ensure that debug state outputs are one-hot
a_debug_state_onehot : assert property (@(posedge clk) $onehot({debug_havereset_o, debug_running_o, debug_halted_o}));
// Ensure that debug_halted_o equals debug_mode_q
a_debug_halted_equals_debug_mode : assert property (@(posedge clk) disable iff (!rst_n) (1'b1) |-> (debug_mode_q == debug_halted_o));
// Ensure ID always ready in FIRST_FETCH state
a_first_fetch_id_ready : assert property (@(posedge clk) disable iff (!rst_n) (ctrl_fsm_cs == FIRST_FETCH) |-> (id_ready_i == 1'b1));
// Ensure that the only way to get to DBG_TAKEN_IF from DBG_FLUSH is if debug_single_step_i is asserted
a_dbg_flush_to_taken_if : assert property (@(posedge clk) disable iff (!rst_n) (ctrl_fsm_cs == DBG_FLUSH) && (ctrl_fsm_ns == DBG_TAKEN_IF) |-> debug_single_step_i);
`endif
endmodule // cv32e40p_controller
dd1f8e446This file is pinned to the selected release. Download the original to open it in your design tool.