mirror of
https://github.com/openhwgroup/cve2.git
synced 2025-04-22 21:17:59 -04:00
[rtl] Add fixed time execution of branches
- A new parameter and a run-time control bit (DataIndTiming and data_ind_timing) enabling different behaviour for running security critical code sections. - In the new mode, all branches act as if taken, with not-taken branches executing as a branch to the next instruction. - This should give similar execution time/power characteristics regardless of the branch condition. - Note that with the BranchTargetALU, branches stall an extra cycle in secure mode to avoid factoring the branch-taken decision into the branch target address mux. Signed-off-by: Tom Roberts <tomroberts@lowrisc.org>
This commit is contained in:
parent
58c0e33334
commit
97a50d7f12
11 changed files with 137 additions and 41 deletions
|
@ -500,6 +500,11 @@ Other bit fields read as zero.
|
|||
+-------+------+------------------------------------------------------------------+
|
||||
| Bit# | R/W | Description |
|
||||
+-------+------+------------------------------------------------------------------+
|
||||
| 1 | WARL | **data_ind_timing:** Enable (1) or disable (0) data-independent |
|
||||
| | | timing features. If the core has not been configured with |
|
||||
| | | security features (SecureIbex parameter == 0), this field will |
|
||||
| | | always read as zero. |
|
||||
+-------+------+------------------------------------------------------------------+
|
||||
| 0 | WARL | **icache_enable:** Enable (1) or disable (0) the instruction |
|
||||
| | | cache. If the instruction cache has not been configured (ICache |
|
||||
| | | parameter == 0), this field will always read as zero. |
|
||||
|
|
|
@ -23,6 +23,7 @@ Instantiation Template
|
|||
.MultiplierImplementation ( "fast" ),
|
||||
.ICache ( 0 ),
|
||||
.ICacheECC ( 0 ),
|
||||
.SecureIbex ( 0 ),
|
||||
.DbgTriggerEn ( 0 ),
|
||||
.DmHaltAddr ( 32'h1A110800 ),
|
||||
.DmExceptionAddr ( 32'h1A110808 )
|
||||
|
@ -110,6 +111,9 @@ Parameters
|
|||
| ``ICacheECC`` | bit | 0 | *EXPERIMENTAL* Enable SECDED ECC protection in ICache (if |
|
||||
| | | | ICache == 1) |
|
||||
+------------------------------+-------------+------------+-----------------------------------------------------------------+
|
||||
| ``SecureIbex`` | bit | 0 | *EXPERIMENTAL* Enable various additional features targeting |
|
||||
| | | | secure code execution. |
|
||||
+------------------------------+-------------+------------+-----------------------------------------------------------------+
|
||||
| ``DbgTriggerEn`` | bit | 0 | Enable debug trigger support (one trigger only) |
|
||||
+------------------------------+-------------+------------+-----------------------------------------------------------------+
|
||||
| ``DmHaltAddr`` | int | 0x1A110800 | Address to jump to when entering Debug Mode |
|
||||
|
|
|
@ -71,6 +71,7 @@ module tb_cs_registers #(
|
|||
logic [31:0] pc_id_i;
|
||||
logic [31:0] pc_wb_i;
|
||||
|
||||
logic data_ind_timing_o;
|
||||
logic icache_enable_o;
|
||||
|
||||
logic csr_save_if_i;
|
||||
|
|
|
@ -100,6 +100,12 @@ parameters:
|
|||
default: false
|
||||
description: "Enables third pipeline stage (EXPERIMENTAL)"
|
||||
|
||||
SecureIbex:
|
||||
datatype: bool
|
||||
paramtype: vlogparam
|
||||
default: false
|
||||
description: "Enables security hardening features (EXPERIMENTAL)"
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
|
|
|
@ -77,6 +77,12 @@ parameters:
|
|||
default: false
|
||||
description: "Enables third pipeline stage (EXPERIMENTAL) [0/1]"
|
||||
|
||||
SecureIbex:
|
||||
datatype: bool
|
||||
paramtype: vlogparam
|
||||
default: false
|
||||
description: "Enables security hardening features (EXPERIMENTAL) [0/1]"
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
|
@ -100,6 +106,7 @@ targets:
|
|||
- BranchTargetALU
|
||||
- WritebackStage
|
||||
- MultiplierImplementation
|
||||
- SecureIbex
|
||||
default_tool: verilator
|
||||
toplevel: ibex_core_tracing
|
||||
tools:
|
||||
|
|
|
@ -18,6 +18,7 @@ lint_off -rule WIDTH -file "*/rtl/ibex_core_tracing.sv" -match "*'RV32M'*"
|
|||
lint_off -rule WIDTH -file "*/rtl/ibex_core_tracing.sv" -match "*'RV32E'*"
|
||||
lint_off -rule WIDTH -file "*/rtl/ibex_core_tracing.sv" -match "*'BranchTargetALU'*"
|
||||
lint_off -rule WIDTH -file "*/rtl/ibex_core_tracing.sv" -match "*'WritebackStage'*"
|
||||
lint_off -rule WIDTH -file "*/rtl/ibex_core_tracing.sv" -match "*'SecureIbex'*"
|
||||
|
||||
// Filename 'ibex_register_file_ff' does not match MODULE name: ibex_register_file
|
||||
// ibex_register_file_ff and ibex_register_file_latch provide two
|
||||
|
|
|
@ -25,6 +25,7 @@ module ibex_core #(
|
|||
parameter bit ICache = 1'b0,
|
||||
parameter bit ICacheECC = 1'b0,
|
||||
parameter bit DbgTriggerEn = 1'b0,
|
||||
parameter bit SecureIbex = 1'b0,
|
||||
parameter int unsigned DmHaltAddr = 32'h1A110800,
|
||||
parameter int unsigned DmExceptionAddr = 32'h1A110808
|
||||
) (
|
||||
|
@ -99,7 +100,8 @@ module ibex_core #(
|
|||
|
||||
import ibex_pkg::*;
|
||||
|
||||
localparam int unsigned PMP_NUM_CHAN = 2;
|
||||
localparam int unsigned PMP_NUM_CHAN = 2;
|
||||
localparam bit DataIndTiming = SecureIbex;
|
||||
|
||||
// IF/ID signals
|
||||
logic instr_valid_id;
|
||||
|
@ -116,6 +118,7 @@ module ibex_core #(
|
|||
logic [31:0] pc_id; // Program counter in ID stage
|
||||
logic [31:0] pc_wb; // Program counter in WB stage
|
||||
|
||||
logic data_ind_timing;
|
||||
logic icache_enable;
|
||||
logic icache_inval;
|
||||
|
||||
|
@ -418,6 +421,7 @@ module ibex_core #(
|
|||
.RV32M ( RV32M ),
|
||||
.RV32B ( RV32B ),
|
||||
.BranchTargetALU ( BranchTargetALU ),
|
||||
.DataIndTiming ( DataIndTiming ),
|
||||
.WritebackStage ( WritebackStage )
|
||||
) id_stage_i (
|
||||
.clk_i ( clk ),
|
||||
|
@ -491,6 +495,7 @@ module ibex_core #(
|
|||
.priv_mode_i ( priv_mode_id ),
|
||||
.csr_mstatus_tw_i ( csr_mstatus_tw ),
|
||||
.illegal_csr_insn_i ( illegal_csr_insn_id ),
|
||||
.data_ind_timing_i ( data_ind_timing ),
|
||||
|
||||
// LSU
|
||||
.lsu_req_o ( lsu_req ), // to load store unit
|
||||
|
@ -767,6 +772,7 @@ module ibex_core #(
|
|||
|
||||
ibex_cs_registers #(
|
||||
.DbgTriggerEn ( DbgTriggerEn ),
|
||||
.DataIndTiming ( DataIndTiming ),
|
||||
.ICache ( ICache ),
|
||||
.MHPMCounterNum ( MHPMCounterNum ),
|
||||
.MHPMCounterWidth ( MHPMCounterWidth ),
|
||||
|
@ -828,6 +834,7 @@ module ibex_core #(
|
|||
.pc_id_i ( pc_id ),
|
||||
.pc_wb_i ( pc_wb ),
|
||||
|
||||
.data_ind_timing_o ( data_ind_timing ),
|
||||
.icache_enable_o ( icache_enable ),
|
||||
|
||||
.csr_save_if_i ( csr_save_if ),
|
||||
|
|
|
@ -21,6 +21,7 @@ module ibex_core_tracing #(
|
|||
parameter bit ICache = 1'b0,
|
||||
parameter bit ICacheECC = 1'b0,
|
||||
parameter bit DbgTriggerEn = 1'b0,
|
||||
parameter bit SecureIbex = 1'b0,
|
||||
parameter int unsigned DmHaltAddr = 32'h1A110800,
|
||||
parameter int unsigned DmExceptionAddr = 32'h1A110808
|
||||
) (
|
||||
|
@ -111,6 +112,7 @@ module ibex_core_tracing #(
|
|||
.ICacheECC ( ICacheECC ),
|
||||
.DbgTriggerEn ( DbgTriggerEn ),
|
||||
.WritebackStage ( WritebackStage ),
|
||||
.SecureIbex ( SecureIbex ),
|
||||
.DmHaltAddr ( DmHaltAddr ),
|
||||
.DmExceptionAddr ( DmExceptionAddr )
|
||||
) u_ibex_core (
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
module ibex_cs_registers #(
|
||||
parameter bit DbgTriggerEn = 0,
|
||||
parameter bit DataIndTiming = 1'b0,
|
||||
parameter bit ICache = 1'b0,
|
||||
parameter int unsigned MHPMCounterNum = 10,
|
||||
parameter int unsigned MHPMCounterWidth = 40,
|
||||
|
@ -79,6 +80,7 @@ module ibex_cs_registers #(
|
|||
input logic [31:0] pc_wb_i,
|
||||
|
||||
// CPU control bits
|
||||
output logic data_ind_timing_o,
|
||||
output logic icache_enable_o,
|
||||
|
||||
// Exception save/restore
|
||||
|
@ -158,7 +160,8 @@ module ibex_cs_registers #(
|
|||
|
||||
// CPU control register fields
|
||||
typedef struct packed {
|
||||
logic [30:0] unused_ctrl;
|
||||
logic [31:2] unused_ctrl;
|
||||
logic data_ind_timing;
|
||||
logic icache_enable;
|
||||
} CpuCtrl_t;
|
||||
|
||||
|
@ -1037,6 +1040,34 @@ module ibex_cs_registers #(
|
|||
// Cast register write data
|
||||
assign cpuctrl_wdata = CpuCtrl_t'(csr_wdata_int);
|
||||
|
||||
// Generate fixed time execution bit
|
||||
if (DataIndTiming) begin : gen_dit
|
||||
logic data_ind_timing_d, data_ind_timing_q;
|
||||
|
||||
assign data_ind_timing_d = (csr_we_int && (csr_addr == CSR_CPUCTRL)) ?
|
||||
cpuctrl_wdata.data_ind_timing : data_ind_timing_q;
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
data_ind_timing_q <= 1'b0; // disabled on reset
|
||||
end else begin
|
||||
data_ind_timing_q <= data_ind_timing_d;
|
||||
end
|
||||
end
|
||||
|
||||
assign cpuctrl_rdata.data_ind_timing = data_ind_timing_q;
|
||||
|
||||
end else begin : gen_no_dit
|
||||
// tieoff for the unused bit
|
||||
logic unused_dit;
|
||||
assign unused_dit = cpuctrl_wdata.data_ind_timing;
|
||||
|
||||
// field will always read as zero if not configured
|
||||
assign cpuctrl_rdata.data_ind_timing = 1'b0;
|
||||
end
|
||||
|
||||
assign data_ind_timing_o = cpuctrl_rdata.data_ind_timing;
|
||||
|
||||
// Generate icache enable bit
|
||||
if (ICache) begin : gen_icache_enable
|
||||
logic icache_enable_d, icache_enable_q;
|
||||
|
@ -1064,12 +1095,13 @@ module ibex_cs_registers #(
|
|||
assign cpuctrl_rdata.icache_enable = 1'b0;
|
||||
end
|
||||
|
||||
// tieoff for the currently unused bits of cpuctrl
|
||||
logic [31:1] unused_cpuctrl;
|
||||
assign unused_cpuctrl = {cpuctrl_wdata[31:1]};
|
||||
|
||||
assign icache_enable_o = cpuctrl_rdata.icache_enable;
|
||||
|
||||
// tieoff for the currently unused bits of cpuctrl
|
||||
logic [31:2] unused_cpuctrl;
|
||||
assign unused_cpuctrl = {cpuctrl_wdata[31:2]};
|
||||
|
||||
|
||||
////////////////
|
||||
// Assertions //
|
||||
////////////////
|
||||
|
|
|
@ -35,6 +35,7 @@ module ibex_decoder #(
|
|||
output logic ecall_insn_o, // syscall instr encountered
|
||||
output logic wfi_insn_o, // wait for interrupt instr encountered
|
||||
output logic jump_set_o, // jump taken set signal
|
||||
input logic branch_taken_i, // registered branch decision
|
||||
output logic icache_inval_o,
|
||||
|
||||
// from IF-ID pipeline register
|
||||
|
@ -655,26 +656,24 @@ module ibex_decoder #(
|
|||
endcase
|
||||
|
||||
if (BranchTargetALU) begin
|
||||
// With branch target ALU the main ALU evaluates the branch condition and the branch
|
||||
// target ALU calculates the target (which is controlled in a seperate block below)
|
||||
bt_a_mux_sel_o = OP_A_CURRPC;
|
||||
// Not-taken branch will jump to next instruction (used in secure mode)
|
||||
bt_b_mux_sel_o = branch_taken_i ? IMM_B_B : IMM_B_INCR_PC;
|
||||
end
|
||||
|
||||
// Without branch target ALU, a branch is a two-stage operation using the Main ALU in both
|
||||
// stages
|
||||
if (instr_first_cycle_i) begin
|
||||
// First evaluate the branch condition
|
||||
alu_op_a_mux_sel_o = OP_A_REG_A;
|
||||
alu_op_b_mux_sel_o = OP_B_REG_B;
|
||||
bt_a_mux_sel_o = OP_A_CURRPC;
|
||||
bt_b_mux_sel_o = IMM_B_B;
|
||||
end else begin
|
||||
// Without branch target ALU, a branch is a two-stage operation using the Main ALU in both
|
||||
// stages
|
||||
if (instr_first_cycle_i) begin
|
||||
// First evaluate the branch condition
|
||||
alu_op_a_mux_sel_o = OP_A_REG_A;
|
||||
alu_op_b_mux_sel_o = OP_B_REG_B;
|
||||
end else begin
|
||||
// Then calculate jump target
|
||||
alu_op_a_mux_sel_o = OP_A_CURRPC;
|
||||
alu_op_b_mux_sel_o = OP_B_IMM;
|
||||
imm_b_mux_sel_o = IMM_B_B;
|
||||
alu_operator_o = ALU_ADD;
|
||||
end
|
||||
// Then calculate jump target
|
||||
alu_op_a_mux_sel_o = OP_A_CURRPC;
|
||||
alu_op_b_mux_sel_o = OP_B_IMM;
|
||||
// Not-taken branch will jump to next instruction (used in secure mode)
|
||||
imm_b_mux_sel_o = branch_taken_i ? IMM_B_B : IMM_B_INCR_PC;
|
||||
alu_operator_o = ALU_ADD;
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ module ibex_id_stage #(
|
|||
parameter bit RV32E = 0,
|
||||
parameter bit RV32M = 1,
|
||||
parameter bit RV32B = 0,
|
||||
parameter bit DataIndTiming = 1'b0,
|
||||
parameter bit BranchTargetALU = 0,
|
||||
parameter bit WritebackStage = 0
|
||||
) (
|
||||
|
@ -95,6 +96,7 @@ module ibex_id_stage #(
|
|||
input ibex_pkg::priv_lvl_e priv_mode_i,
|
||||
input logic csr_mstatus_tw_i,
|
||||
input logic illegal_csr_insn_i,
|
||||
input logic data_ind_timing_i,
|
||||
|
||||
// Interface to load store unit
|
||||
output logic lsu_req_o,
|
||||
|
@ -183,6 +185,7 @@ module ibex_id_stage #(
|
|||
|
||||
logic branch_in_dec;
|
||||
logic branch_set, branch_set_d;
|
||||
logic branch_taken;
|
||||
logic jump_in_dec;
|
||||
logic jump_set;
|
||||
|
||||
|
@ -375,6 +378,7 @@ module ibex_id_stage #(
|
|||
.ecall_insn_o ( ecall_insn_dec ),
|
||||
.wfi_insn_o ( wfi_insn_dec ),
|
||||
.jump_set_o ( jump_set ),
|
||||
.branch_taken_i ( branch_taken ),
|
||||
.icache_inval_o ( icache_inval_o ),
|
||||
|
||||
// from IF-ID pipeline register
|
||||
|
@ -582,19 +586,16 @@ module ibex_id_stage #(
|
|||
assign multdiv_operand_a_ex_o = rf_rdata_a_fwd;
|
||||
assign multdiv_operand_b_ex_o = rf_rdata_b_fwd;
|
||||
|
||||
typedef enum logic { FIRST_CYCLE, MULTI_CYCLE } id_fsm_e;
|
||||
id_fsm_e id_fsm_q, id_fsm_d;
|
||||
////////////////////////
|
||||
// Branch set control //
|
||||
////////////////////////
|
||||
|
||||
/////////////////////////////
|
||||
// ID-EX Pipeline Register //
|
||||
/////////////////////////////
|
||||
|
||||
if (BranchTargetALU) begin : g_branch_set_direct
|
||||
if (BranchTargetALU && !DataIndTiming) begin : g_branch_set_direct
|
||||
// Branch set fed straight to controller with branch target ALU
|
||||
// (condition pass/fail used same cycle as generated instruction request)
|
||||
assign branch_set = branch_set_d;
|
||||
end else begin : g_branch_set_flopped
|
||||
// Branch set flopped without branch target ALU
|
||||
end else begin : g_branch_set_flop
|
||||
// Branch set flopped without branch target ALU, or in fixed time execution mode
|
||||
// (condition pass/fail used next cycle where branch target is calculated)
|
||||
logic branch_set_q;
|
||||
|
||||
|
@ -606,7 +607,32 @@ module ibex_id_stage #(
|
|||
end
|
||||
end
|
||||
|
||||
assign branch_set = branch_set_q;
|
||||
// Branches always take two cycles in fixed time execution mode, with or without the branch
|
||||
// target ALU (to avoid a path from the branch decision into the branch target ALU operand
|
||||
// muxing).
|
||||
assign branch_set = (BranchTargetALU && !data_ind_timing_i) ? branch_set_d : branch_set_q;
|
||||
end
|
||||
|
||||
// Branch condition is calculated in the first cycle and flopped for use in the second cycle
|
||||
// (only used in fixed time execution mode to determine branch destination).
|
||||
if (DataIndTiming) begin : g_sec_branch_taken
|
||||
logic branch_taken_q;
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
branch_taken_q <= 1'b0;
|
||||
end else begin
|
||||
branch_taken_q <= branch_decision_i;
|
||||
end
|
||||
end
|
||||
|
||||
assign branch_taken = ~data_ind_timing_i | branch_taken_q;
|
||||
|
||||
end else begin : g_nosec_branch_taken
|
||||
|
||||
// Signal unused without fixed time execution mode - only taken branches will trigger branch_set
|
||||
assign branch_taken = 1'b1;
|
||||
|
||||
end
|
||||
|
||||
// Holding branch_set/jump_set high for more than one cycle may not cause a functional issue but
|
||||
|
@ -614,6 +640,13 @@ module ibex_id_stage #(
|
|||
// that this shouldn't ever happen.
|
||||
`ASSERT(NeverDoubleBranch, branch_set |=> ~branch_set)
|
||||
|
||||
///////////////
|
||||
// ID-EX FSM //
|
||||
///////////////
|
||||
|
||||
typedef enum logic { FIRST_CYCLE, MULTI_CYCLE } id_fsm_e;
|
||||
id_fsm_e id_fsm_q, id_fsm_d;
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin : id_pipeline_reg
|
||||
if (!rst_ni) begin
|
||||
id_fsm_q <= FIRST_CYCLE;
|
||||
|
@ -622,10 +655,6 @@ module ibex_id_stage #(
|
|||
end
|
||||
end
|
||||
|
||||
///////////////
|
||||
// ID-EX FSM //
|
||||
///////////////
|
||||
|
||||
// ID/EX stage can be in two states, FIRST_CYCLE and MULTI_CYCLE. An instruction enters
|
||||
// MULTI_CYCLE if it requires multiple cycles to complete regardless of stalls and other
|
||||
// considerations. An instruction may be held in FIRST_CYCLE if it's unable to begin executing
|
||||
|
@ -666,10 +695,13 @@ module ibex_id_stage #(
|
|||
end
|
||||
branch_in_dec: begin
|
||||
// cond branch operation
|
||||
id_fsm_d = (!BranchTargetALU && branch_decision_i) ? MULTI_CYCLE : FIRST_CYCLE;
|
||||
stall_branch = ~BranchTargetALU & branch_decision_i;
|
||||
branch_set_d = branch_decision_i;
|
||||
perf_branch_o = 1'b1;
|
||||
// All branches take two cycles in fixed time execution mode, regardless of branch
|
||||
// condition.
|
||||
id_fsm_d = (data_ind_timing_i || (!BranchTargetALU && branch_decision_i)) ?
|
||||
MULTI_CYCLE : FIRST_CYCLE;
|
||||
stall_branch = (~BranchTargetALU & branch_decision_i) | data_ind_timing_i;
|
||||
branch_set_d = branch_decision_i | data_ind_timing_i;
|
||||
perf_branch_o = 1'b1;
|
||||
end
|
||||
jump_in_dec: begin
|
||||
// uncond branch operation
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue