Skip to content

Commit e3db76b

Browse files
committed
rtl: update regfile implementations to use common security module
1 parent 3bbd28c commit e3db76b

File tree

6 files changed

+114
-311
lines changed

6 files changed

+114
-311
lines changed

dv/uvm/core_ibex/ibex_dv.f

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
${PRJ_DIR}/rtl/ibex_multdiv_fast.sv
9898
${PRJ_DIR}/rtl/ibex_prefetch_buffer.sv
9999
${PRJ_DIR}/rtl/ibex_fetch_fifo.sv
100+
${PRJ_DIR}/rtl/ibex_register_file_common.sv
100101
${PRJ_DIR}/rtl/ibex_register_file_ff.sv
101102
${PRJ_DIR}/rtl/ibex_register_file_fpga.sv
102103
${PRJ_DIR}/rtl/ibex_register_file_latch.sv

ibex_top.core

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ filesets:
1919
- lowrisc:prim:onehot_check
2020
- lowrisc:prim:onehot
2121
files:
22+
- rtl/ibex_register_file_common.sv
2223
- rtl/ibex_register_file_ff.sv # generic FF-based
2324
- rtl/ibex_register_file_fpga.sv # FPGA
2425
- rtl/ibex_register_file_latch.sv # ASIC

rtl/ibex_core.f

+1
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@
1717
ibex_multdiv_fast.sv
1818
ibex_prefetch_buffer.sv
1919
ibex_fetch_fifo.sv
20+
ibex_register_file_common.sv
2021
ibex_register_file_ff.sv
2122
ibex_core.sv

rtl/ibex_register_file_ff.sv

+37-111
Original file line numberDiff line numberDiff line change
@@ -48,46 +48,34 @@ module ibex_register_file_ff #(
4848
localparam int unsigned NUM_WORDS = 2**ADDR_WIDTH;
4949

5050
logic [DataWidth-1:0] rf_reg [NUM_WORDS];
51-
logic [NUM_WORDS-1:0] we_a_dec;
5251

52+
// Encode we_a/raddr_a/raddr_b into one-hot encoded signals
53+
logic [NUM_WORDS-1:0] raddr_onehot_a, raddr_onehot_b, we_onehot_a;
54+
55+
// One-hot encoding error signals
5356
logic oh_raddr_a_err, oh_raddr_b_err, oh_we_err;
5457

55-
always_comb begin : we_a_decoder
56-
for (int unsigned i = 0; i < NUM_WORDS; i++) begin
57-
we_a_dec[i] = (waddr_a_i == 5'(i)) ? we_a_i : 1'b0;
58-
end
59-
end
60-
61-
// SEC_CM: DATA_REG_SW.GLITCH_DETECT
62-
// This checks for spurious WE strobes on the regfile.
63-
if (WrenCheck) begin : gen_wren_check
64-
// Buffer the decoded write enable bits so that the checker
65-
// is not optimized into the address decoding logic.
66-
logic [NUM_WORDS-1:0] we_a_dec_buf;
67-
prim_buf #(
68-
.Width(NUM_WORDS)
69-
) u_prim_buf (
70-
.in_i(we_a_dec),
71-
.out_o(we_a_dec_buf)
72-
);
73-
74-
prim_onehot_check #(
75-
.AddrWidth(ADDR_WIDTH),
76-
.AddrCheck(1),
77-
.EnableCheck(1)
78-
) u_prim_onehot_check (
79-
.clk_i,
80-
.rst_ni,
81-
.oh_i(we_a_dec_buf),
82-
.addr_i(waddr_a_i),
83-
.en_i(we_a_i),
84-
.err_o(oh_we_err)
85-
);
86-
end else begin : gen_no_wren_check
87-
logic unused_strobe;
88-
assign unused_strobe = we_a_dec[0]; // this is never read from in this case
89-
assign oh_we_err = 1'b0;
90-
end
58+
// Common security functionality
59+
ibex_register_file_common #(
60+
.AddrWidth(ADDR_WIDTH),
61+
.NumWords(NUM_WORDS),
62+
.WrenCheck(WrenCheck),
63+
.RdataMuxCheck(RdataMuxCheck)
64+
) security_module (
65+
.clk_i,
66+
.rst_ni,
67+
.raddr_a_i,
68+
.raddr_onehot_a,
69+
.oh_raddr_a_err,
70+
.raddr_b_i,
71+
.raddr_onehot_b,
72+
.oh_raddr_b_err,
73+
.waddr_a_i,
74+
.we_a_i,
75+
.we_onehot_a,
76+
.oh_we_err,
77+
.err_o
78+
);
9179

9280
// No flops for R0 as it's hard-wired to 0
9381
for (genvar i = 1; i < NUM_WORDS; i++) begin : g_rf_flops
@@ -96,7 +84,7 @@ module ibex_register_file_ff #(
9684
always_ff @(posedge clk_i or negedge rst_ni) begin
9785
if (!rst_ni) begin
9886
rf_reg_q <= WordZeroVal;
99-
end else if (we_a_dec[i]) begin
87+
end else if (we_onehot_a[i]) begin
10088
rf_reg_q <= wdata_a_i;
10189
end
10290
end
@@ -134,75 +122,6 @@ module ibex_register_file_ff #(
134122
end
135123

136124
if (RdataMuxCheck) begin : gen_rdata_mux_check
137-
// Encode raddr_a/b into one-hot encoded signals.
138-
logic [NUM_WORDS-1:0] raddr_onehot_a, raddr_onehot_b;
139-
logic [NUM_WORDS-1:0] raddr_onehot_a_buf, raddr_onehot_b_buf;
140-
prim_onehot_enc #(
141-
.OneHotWidth(NUM_WORDS)
142-
) u_prim_onehot_enc_raddr_a (
143-
.in_i (raddr_a_i),
144-
.en_i (1'b1),
145-
.out_o (raddr_onehot_a)
146-
);
147-
148-
prim_onehot_enc #(
149-
.OneHotWidth(NUM_WORDS)
150-
) u_prim_onehot_enc_raddr_b (
151-
.in_i (raddr_b_i),
152-
.en_i (1'b1),
153-
.out_o (raddr_onehot_b)
154-
);
155-
156-
// Buffer the one-hot encoded signals so that the checkers
157-
// are not optimized.
158-
prim_buf #(
159-
.Width(NUM_WORDS)
160-
) u_prim_buf_raddr_a (
161-
.in_i (raddr_onehot_a),
162-
.out_o(raddr_onehot_a_buf)
163-
);
164-
165-
prim_buf #(
166-
.Width(NUM_WORDS)
167-
) u_prim_buf_raddr_b (
168-
.in_i (raddr_onehot_b),
169-
.out_o(raddr_onehot_b_buf)
170-
);
171-
172-
// SEC_CM: DATA_REG_SW.GLITCH_DETECT
173-
// Check the one-hot encoded signals for glitches.
174-
prim_onehot_check #(
175-
.AddrWidth(ADDR_WIDTH),
176-
.OneHotWidth(NUM_WORDS),
177-
.AddrCheck(1),
178-
// When AddrCheck=1 also EnableCheck needs to be 1.
179-
.EnableCheck(1)
180-
) u_prim_onehot_check_raddr_a (
181-
.clk_i,
182-
.rst_ni,
183-
.oh_i (raddr_onehot_a_buf),
184-
.addr_i (raddr_a_i),
185-
// Set enable=1 as address is always valid.
186-
.en_i (1'b1),
187-
.err_o (oh_raddr_a_err)
188-
);
189-
190-
prim_onehot_check #(
191-
.AddrWidth(ADDR_WIDTH),
192-
.OneHotWidth(NUM_WORDS),
193-
.AddrCheck(1),
194-
// When AddrCheck=1 also EnableCheck needs to be 1.
195-
.EnableCheck(1)
196-
) u_prim_onehot_check_raddr_b (
197-
.clk_i,
198-
.rst_ni,
199-
.oh_i (raddr_onehot_b_buf),
200-
.addr_i (raddr_b_i),
201-
// Set enable=1 as address is always valid.
202-
.en_i (1'b1),
203-
.err_o (oh_raddr_b_err)
204-
);
205-
206125
// MUX register to rdata_a/b_o according to raddr_a/b_onehot.
207126
prim_onehot_mux #(
208127
.Width(DataWidth),
@@ -228,14 +147,21 @@ module ibex_register_file_ff #(
228147
end else begin : gen_no_rdata_mux_check
229148
assign rdata_a_o = rf_reg[raddr_a_i];
230149
assign rdata_b_o = rf_reg[raddr_b_i];
231-
assign oh_raddr_a_err = 1'b0;
232-
assign oh_raddr_b_err = 1'b0;
233-
end
234150

235-
assign err_o = oh_raddr_a_err || oh_raddr_b_err || oh_we_err;
151+
logic unused_raddr_onehot, unused_oh_raddr_err;
152+
assign unused_raddr_onehot = ^{raddr_onehot_a, raddr_onehot_b};
153+
assign unused_oh_raddr_err = ^{oh_raddr_a_err, oh_raddr_b_err};
154+
end
236155

237156
// Signal not used in FF register file
238157
logic unused_test_en;
239158
assign unused_test_en = test_en_i;
240159

160+
if (WrenCheck) begin : gen_wren_check
161+
end else begin : gen_no_wren_check
162+
logic unused_strobe, unused_oh_we_err;
163+
assign unused_strobe = we_onehot_a[0]; // this is never read from in this case
164+
assign unused_oh_we_err = oh_we_err; // this is never read from in this case
165+
end
166+
241167
endmodule

rtl/ibex_register_file_fpga.sv

+36-82
Original file line numberDiff line numberDiff line change
@@ -48,82 +48,38 @@ module ibex_register_file_fpga #(
4848
logic [DataWidth-1:0] mem[NUM_WORDS];
4949
logic we; // write enable if writing to any register other than R0
5050

51-
logic [DataWidth-1:0] mem_o_a, mem_o_b;
51+
// Encode we_a/raddr_a/raddr_b into one-hot encoded signals
52+
logic [NUM_WORDS-1:0] raddr_onehot_a, raddr_onehot_b, we_onehot_a;
5253

5354
// WE strobe and one-hot encoded raddr alert.
5455
logic oh_raddr_a_err, oh_raddr_b_err, oh_we_err;
55-
assign err_o = oh_raddr_a_err || oh_raddr_b_err || oh_we_err;
56-
57-
if (RdataMuxCheck) begin : gen_rdata_mux_check
58-
// Encode raddr_a/b into one-hot encoded signals.
59-
logic [NUM_WORDS-1:0] raddr_onehot_a, raddr_onehot_b;
60-
logic [NUM_WORDS-1:0] raddr_onehot_a_buf, raddr_onehot_b_buf;
61-
prim_onehot_enc #(
62-
.OneHotWidth(NUM_WORDS)
63-
) u_prim_onehot_enc_raddr_a (
64-
.in_i (raddr_a_i),
65-
.en_i (1'b1),
66-
.out_o (raddr_onehot_a)
67-
);
6856

69-
prim_onehot_enc #(
70-
.OneHotWidth(NUM_WORDS)
71-
) u_prim_onehot_enc_raddr_b (
72-
.in_i (raddr_b_i),
73-
.en_i (1'b1),
74-
.out_o (raddr_onehot_b)
75-
);
76-
77-
// Buffer the one-hot encoded signals so that the checkers
78-
// are not optimized.
79-
prim_buf #(
80-
.Width(NUM_WORDS)
81-
) u_prim_buf_raddr_a (
82-
.in_i (raddr_onehot_a),
83-
.out_o(raddr_onehot_a_buf)
84-
);
85-
86-
prim_buf #(
87-
.Width(NUM_WORDS)
88-
) u_prim_buf_raddr_b (
89-
.in_i (raddr_onehot_b),
90-
.out_o(raddr_onehot_b_buf)
91-
);
92-
93-
// SEC_CM: DATA_REG_SW.GLITCH_DETECT
94-
// Check the one-hot encoded signals for glitches.
95-
prim_onehot_check #(
96-
.AddrWidth(ADDR_WIDTH),
97-
.OneHotWidth(NUM_WORDS),
98-
.AddrCheck(1),
99-
// When AddrCheck=1 also EnableCheck needs to be 1.
100-
.EnableCheck(1)
101-
) u_prim_onehot_check_raddr_a (
102-
.clk_i,
103-
.rst_ni,
104-
.oh_i (raddr_onehot_a_buf),
105-
.addr_i (raddr_a_i),
106-
// Set enable=1 as address is always valid.
107-
.en_i (1'b1),
108-
.err_o (oh_raddr_a_err)
109-
);
57+
logic [DataWidth-1:0] mem_o_a, mem_o_b;
11058

111-
prim_onehot_check #(
112-
.AddrWidth(ADDR_WIDTH),
113-
.OneHotWidth(NUM_WORDS),
114-
.AddrCheck(1),
115-
// When AddrCheck=1 also EnableCheck needs to be 1.
116-
.EnableCheck(1)
117-
) u_prim_onehot_check_raddr_b (
118-
.clk_i,
119-
.rst_ni,
120-
.oh_i (raddr_onehot_b_buf),
121-
.addr_i (raddr_b_i),
122-
// Set enable=1 as address is always valid.
123-
.en_i (1'b1),
124-
.err_o (oh_raddr_b_err)
125-
);
59+
// Common security functionality
60+
ibex_register_file_common #(
61+
.FPGA(1),
62+
.AddrWidth(ADDR_WIDTH),
63+
.NumWords(NUM_WORDS),
64+
.WrenCheck(WrenCheck),
65+
.RdataMuxCheck(RdataMuxCheck)
66+
) security_module (
67+
.clk_i,
68+
.rst_ni,
69+
.raddr_a_i,
70+
.raddr_onehot_a,
71+
.oh_raddr_a_err,
72+
.raddr_b_i,
73+
.raddr_onehot_b,
74+
.oh_raddr_b_err,
75+
.waddr_a_i,
76+
.we_a_i,
77+
.we_onehot_a,
78+
.oh_we_err,
79+
.err_o
80+
);
12681

82+
if (RdataMuxCheck) begin : gen_rdata_mux_check
12783
// MUX register to rdata_a/b_o according to raddr_a/b_onehot.
12884
prim_onehot_mux #(
12985
.Width(DataWidth),
@@ -153,21 +109,13 @@ module ibex_register_file_fpga #(
153109
assign rdata_a_o = (raddr_a_i == '0) ? WordZeroVal : mem[raddr_a_i];
154110
assign rdata_b_o = (raddr_b_i == '0) ? WordZeroVal : mem[raddr_b_i];
155111

156-
assign oh_raddr_a_err = 1'b0;
157-
assign oh_raddr_b_err = 1'b0;
112+
logic unused_raddr_onehot, unused_oh_raddr_err;
113+
assign unused_raddr_onehot = ^{raddr_onehot_a, raddr_onehot_b};
114+
assign unused_oh_raddr_err = ^{oh_raddr_a_err, oh_raddr_b_err};
158115
end
159116

160117
// we select
161-
assign we = (waddr_a_i == '0) ? 1'b0 : we_a_i;
162-
163-
// SEC_CM: DATA_REG_SW.GLITCH_DETECT
164-
// This checks for spurious WE strobes on the regfile.
165-
if (WrenCheck) begin : gen_wren_check
166-
// Since the FPGA uses a memory macro, there is only one write-enable strobe to check.
167-
assign oh_we_err = we && !we_a_i;
168-
end else begin : gen_no_wren_check
169-
assign oh_we_err = 1'b0;
170-
end
118+
assign we = (waddr_a_i == '0) ? 1'b0 : we_onehot_a[0];
171119

172120
// Note that the SystemVerilog LRM requires variables on the LHS of assignments within
173121
// "always_ff" to not be written to by any other process. However, to enable the initialization
@@ -198,4 +146,10 @@ module ibex_register_file_fpga #(
198146
logic unused_test_en;
199147
assign unused_test_en = test_en_i;
200148

149+
if (WrenCheck) begin : gen_wren_check
150+
end else begin : gen_no_wren_check
151+
logic unused_oh_we_err;
152+
assign unused_oh_we_err = oh_we_err; // this is never read from in this case
153+
end
154+
201155
endmodule

0 commit comments

Comments
 (0)