[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:
Tom Roberts 2020-04-01 09:25:43 +01:00 committed by Tom Roberts
parent 58c0e33334
commit 97a50d7f12
11 changed files with 137 additions and 41 deletions

View file

@ -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. |

View file

@ -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 |

View file

@ -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;

View file

@ -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:

View file

@ -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:

View file

@ -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

View file

@ -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 ),

View file

@ -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 (

View file

@ -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 //
////////////////

View file

@ -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

View file

@ -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