[aes] Use sparse encodings for FSMs, trigger alert for invalid states
This commit changes all FSMs inside AES to use sparse state encodings with
a minimum Hamming distance of 3 between states. If any FSM enters an
invalid state, the AES unit locks and an alert is triggered. The cipher
core is prevented from finishing the current operation or starting a new
operation. Writes to the control register are ignored. The AES unit needs
to be reset.
This commit also renames the alert signals. Update errors in the shadow
control register are collected in `recoverable`. More severe errors such as
storage errors in the shadow control register, entering invalid FSM
states trigger a `fatal` alert.
Signed-off-by: Pirmin Vogel <vogelpi@lowrisc.org>
diff --git a/hw/ip/aes/data/aes.hjson b/hw/ip/aes/data/aes.hjson
index 608d8ba..5cf6156 100644
--- a/hw/ip/aes/data/aes.hjson
+++ b/hw/ip/aes/data/aes.hjson
@@ -133,17 +133,24 @@
}
],
alert_list: [
- { name: "ctrl_err_update",
+ //{ name: "informative",
+ // desc: '''
+ // The informative alert can currently not be triggered.
+ // The AES unit recovers from such a condition automatically.
+ // No further action needs to be taken but this should be monitored by the system.
+ // '''
+ //}
+ { name: "recoverable",
desc: '''
- This minor alert is triggered upon detecting an update error in the Control Register.
- The AES unit recovers from such a condition automatically.
- No further action needs to be taken but this should be monitored by the system.
+ The recoverable alert is triggered upon detecting an update error in the Control Register.
+ The content of the Control Register is not modified (See Control Register).
+ The AES unit can be recovered from such a condition by restarting the AES operation, i.e., by successfully updating the Control Register.
+ This should be monitored by the system.
'''
}
- { name: "ctrl_err_storage",
+ { name: "fatal",
desc: '''
- This major alert is triggered upon detecting a storage error in the Control Register.
- It is fatal.
+ The fatal alert is triggered i) upon detecting a storage error in the Control Register, or ii) if any internal FSM enters an invalid state.
The AES unit cannot recover from such an error and needs to be reset.
'''
}
@@ -278,7 +285,8 @@
Control Register. Can only be updated when the AES unit is idle. If the
AES unit is non-idle, writes to this register are ignored.
This register is shadowed, meaning two subsequent write operations are required to change its content.
- If the two write operations try to set a different value, a ctrl_err alert is triggered.
+ If the two write operations try to set a different value, a recoverable alert is triggered (See Status Register).
+ A read operation clears the internal phase tracking: The next write operation is always considered a first write operation of an update sequence.
Any write operation to this register will clear the status tracking required for automatic mode (See MANUAL_OPERATION field).
A write to the Control Register is considered the start of a new message.
Hence, software needs to provide new key, IV and input data afterwards.
@@ -519,11 +527,21 @@
tags: ["excl:CsrNonInitTests:CsrExclCheck"]
}
{ bits: "4",
- name: "CTRL_ERR_STORAGE",
+ name: "ALERT_RECOVERABLE",
resval: "0",
desc: '''
- No storage error detected in the Control Register (0).
- A storage error has been detected in the Control Register and the AES unit needs to be reset (1).
+ No recoverable alert condition has occurred (0).
+ A recoverable alert condition has occurred and AES operation needs to be restarted by successfully updating the Control Register (1).
+ Examples for recoverable alert conditions include update errors in the Control Register.
+ '''
+ }
+ { bits: "5",
+ name: "ALERT_FATAL",
+ resval: "0",
+ desc: '''
+ No fatal alert condition has occurred (0).
+ A fatal alert condition has occurred and the AES unit needs to be reset (1).
+ Examples for fatal alert conditions include i) storage errors in the Control Register, and ii) if any internal FSM enters an invalid state.
'''
}
]
diff --git a/hw/ip/aes/doc/_index.md b/hw/ip/aes/doc/_index.md
index 05575bf..aaa4348 100644
--- a/hw/ip/aes/doc/_index.md
+++ b/hw/ip/aes/doc/_index.md
@@ -323,7 +323,7 @@
If the AES unit is not idle, write operations to {{< regref "CTRL" >}}, the Initial Key registers {{< regref "KEY_SHARE0_0" >}} - {{< regref "KEY_SHARE1_7" >}} and initialization vector (IV) registers {{< regref "IV_0" >}} - {{< regref "IV_3" >}} are ignored.
To initialize the AES unit, software must first provide the configuration to the {{< regref "CTRL_SHADOWED" >}} register.
-Then software must write the initial key to the Initial Key registers {< regref "KEY_SHARE0_0" >}} - {{< regref "KEY_SHARE1_7" >}}.
+Then software must write the initial key to the Initial Key registers {{< regref "KEY_SHARE0_0" >}} - {{< regref "KEY_SHARE1_7" >}}.
The key is provided in two shares:
The first share is written to {{< regref "KEY_SHARE0_0" >}} - {{< regref "KEY_SHARE0_7" >}} and the second share is written to {{< regref "KEY_SHARE1_0" >}} - {{< regref "KEY_SHARE1_7" >}}.
The actual initial key used for encryption corresponds to the value obtained by XORing {{< regref "KEY_SHARE0_0" >}} - {{< regref "KEY_SHARE0_7" >}} with {{< regref "KEY_SHARE1_0" >}} - {{< regref "KEY_SHARE1_7" >}}.
diff --git a/hw/ip/aes/dv/env/aes_env_pkg.sv b/hw/ip/aes/dv/env/aes_env_pkg.sv
index af11bc8..04894e0 100644
--- a/hw/ip/aes/dv/env/aes_env_pkg.sv
+++ b/hw/ip/aes/dv/env/aes_env_pkg.sv
@@ -22,7 +22,7 @@
`include "dv_macros.svh"
// parameters
- parameter string LIST_OF_ALERTS[] = {"ctrl_err_update", "ctrl_err_storage"};
+ parameter string LIST_OF_ALERTS[] = {"recoverable", "fatal"};
parameter uint NUM_ALERTS = 2;
typedef enum int { AES_CFG=0, AES_DATA=1, AES_ERR_INJ=2 } aes_item_type_e;
diff --git a/hw/ip/aes/dv/env/seq_lib/aes_common_vseq.sv b/hw/ip/aes/dv/env/seq_lib/aes_common_vseq.sv
index 449bf09..766717f 100644
--- a/hw/ip/aes/dv/env/seq_lib/aes_common_vseq.sv
+++ b/hw/ip/aes/dv/env/seq_lib/aes_common_vseq.sv
@@ -18,7 +18,7 @@
endtask
virtual function void shadow_reg_storage_err_post_write();
- void'(ral.status.ctrl_err_storage.predict(1));
+ void'(ral.status.alert_fatal.predict(1));
endfunction
// for AES ctrl_shadowed register, the write transaction is valid only if the status is Idle
diff --git a/hw/ip/aes/rtl/aes.sv b/hw/ip/aes/rtl/aes.sv
index b1d6d85..ca5d808 100644
--- a/hw/ip/aes/rtl/aes.sv
+++ b/hw/ip/aes/rtl/aes.sv
@@ -94,8 +94,8 @@
.entropy_masking_ack_i ( 1'b1 ),
.entropy_masking_i ( RndCnstMaskingLfsrSeedDefault ),
- .ctrl_err_update_o ( alert[0] ),
- .ctrl_err_storage_o ( alert[1] ),
+ .alert_recoverable_o ( alert[0] ),
+ .alert_fatal_o ( alert[1] ),
.reg2hw ( reg2hw ),
.hw2reg ( hw2reg )
@@ -105,10 +105,10 @@
logic [NumAlerts-1:0] alert_test;
assign alert_test = {
- reg2hw.alert_test.ctrl_err_storage.q &
- reg2hw.alert_test.ctrl_err_storage.qe,
- reg2hw.alert_test.ctrl_err_update.q &
- reg2hw.alert_test.ctrl_err_update.qe
+ reg2hw.alert_test.fatal.q &
+ reg2hw.alert_test.fatal.qe,
+ reg2hw.alert_test.recoverable.q &
+ reg2hw.alert_test.recoverable.qe
};
for (genvar i = 0; i < NumAlerts; i++) begin : gen_alert_tx
diff --git a/hw/ip/aes/rtl/aes_cipher_control.sv b/hw/ip/aes/rtl/aes_cipher_control.sv
index 90b0c75..bc37150 100644
--- a/hw/ip/aes/rtl/aes_cipher_control.sv
+++ b/hw/ip/aes/rtl/aes_cipher_control.sv
@@ -35,6 +35,7 @@
output logic key_clear_o,
input logic data_out_clear_i,
output logic data_out_clear_o,
+ output logic alert_o,
// Control signals for masking PRNG
output logic prng_update_o,
@@ -67,8 +68,31 @@
import aes_pkg::*;
// Types
- typedef enum logic [2:0] {
- IDLE, INIT, ROUND, FINISH, CLEAR_S, CLEAR_KD
+ // $ ./sparse-fsm-encode.py -d 3 -m 7 -n 6 \
+ // -s 31468618 --language=sv
+ //
+ // Hamming distance histogram:
+ //
+ // 0: --
+ // 1: --
+ // 2: --
+ // 3: |||||||||||||||||||| (57.14%)
+ // 4: ||||||||||||||| (42.86%)
+ // 5: --
+ // 6: --
+ //
+ // Minimum Hamming distance: 3
+ // Maximum Hamming distance: 4
+ //
+ localparam int StateWidth = 6;
+ typedef enum logic [StateWidth-1:0] {
+ IDLE = 6'b111100,
+ INIT = 6'b101001,
+ ROUND = 6'b010000,
+ FINISH = 6'b100010,
+ CLEAR_S = 6'b011011,
+ CLEAR_KD = 6'b110111,
+ ERROR = 6'b001110
} aes_cipher_ctrl_e;
aes_cipher_ctrl_e aes_cipher_ctrl_ns, aes_cipher_ctrl_cs;
@@ -126,6 +150,9 @@
data_out_clear_d = data_out_clear_q;
prng_reseed_done_d = prng_reseed_done_q | prng_reseed_ack_i;
+ // Alert
+ alert_o = 1'b0;
+
unique case (aes_cipher_ctrl_cs)
IDLE: begin
@@ -337,13 +364,35 @@
end
end
- default: aes_cipher_ctrl_ns = IDLE;
+ ERROR: begin
+ // Terminal error state
+ alert_o = 1'b1;
+ end
+
+ // We should never get here. If we do (e.g. via a malicious glitch), error out immediately.
+ default: begin
+ alert_o = 1'b1;
+ aes_cipher_ctrl_ns = ERROR;
+ end
endcase
end
+ // This primitive is used to place a size-only constraint on the
+ // flops in order to prevent FSM state encoding optimizations.
+ logic [StateWidth-1:0] aes_cipher_ctrl_cs_raw;
+ assign aes_cipher_ctrl_cs = aes_cipher_ctrl_e'(aes_cipher_ctrl_cs_raw);
+ prim_flop #(
+ .Width(StateWidth),
+ .ResetValue(StateWidth'(IDLE))
+ ) u_state_regs (
+ .clk_i,
+ .rst_ni,
+ .d_i ( aes_cipher_ctrl_ns ),
+ .q_o ( aes_cipher_ctrl_cs_raw )
+ );
+
always_ff @(posedge clk_i or negedge rst_ni) begin : reg_fsm
if (!rst_ni) begin
- aes_cipher_ctrl_cs <= IDLE;
round_q <= '0;
num_rounds_q <= '0;
crypt_q <= 1'b0;
@@ -352,7 +401,6 @@
data_out_clear_q <= 1'b0;
prng_reseed_done_q <= 1'b0;
end else begin
- aes_cipher_ctrl_cs <= aes_cipher_ctrl_ns;
round_q <= round_d;
num_rounds_q <= num_rounds_d;
crypt_q <= crypt_d;
@@ -387,7 +435,7 @@
AES_192,
AES_256
})
- `ASSERT(AesControlStateValid, aes_cipher_ctrl_cs inside {
+ `ASSERT(AesControlStateValid, !alert_o |-> aes_cipher_ctrl_cs inside {
IDLE,
INIT,
ROUND,
diff --git a/hw/ip/aes/rtl/aes_cipher_core.sv b/hw/ip/aes/rtl/aes_cipher_core.sv
index b36d549..c252614 100644
--- a/hw/ip/aes/rtl/aes_cipher_core.sv
+++ b/hw/ip/aes/rtl/aes_cipher_core.sv
@@ -127,6 +127,7 @@
output logic key_clear_o,
input logic data_out_clear_i, // Re-use the cipher core muxes.
output logic data_out_clear_o,
+ output logic alert_o,
// Pseudo-random data for register clearing
input logic [WidthPRDClearing-1:0] prd_clearing_i,
@@ -456,6 +457,7 @@
.key_clear_o ( key_clear_o ),
.data_out_clear_i ( data_out_clear_i ),
.data_out_clear_o ( data_out_clear_o ),
+ .alert_o ( alert_o ),
.prng_update_o ( prd_masking_upd ),
.prng_reseed_req_o ( prd_masking_rsd_req ),
diff --git a/hw/ip/aes/rtl/aes_control.sv b/hw/ip/aes/rtl/aes_control.sv
index a8d256f..799f617 100644
--- a/hw/ip/aes/rtl/aes_control.sv
+++ b/hw/ip/aes/rtl/aes_control.sv
@@ -29,6 +29,8 @@
input logic data_in_clear_i,
input logic data_out_clear_i,
input logic prng_reseed_i,
+ input logic alert_fatal_i,
+ output logic alert_o,
// I/O register read/write enables
input logic [7:0] key_init_qe_i [2],
@@ -108,8 +110,30 @@
import aes_pkg::*;
// Types
- typedef enum logic [2:0] {
- IDLE, LOAD, UPDATE_PRNG, FINISH, CLEAR
+ // $ ./sparse-fsm-encode.py -d 3 -m 6 -n 6 \
+ // -s 31468618 --language=sv
+ //
+ // Hamming distance histogram:
+ //
+ // 0: --
+ // 1: --
+ // 2: --
+ // 3: |||||||||||||||||||| (53.33%)
+ // 4: ||||||||||||||| (40.00%)
+ // 5: || (6.67%)
+ // 6: --
+ //
+ // Minimum Hamming distance: 3
+ // Maximum Hamming distance: 5
+ //
+ localparam int StateWidth = 6;
+ typedef enum logic [StateWidth-1:0] {
+ IDLE = 6'b011101,
+ LOAD = 6'b110000,
+ UPDATE_PRNG = 6'b001000,
+ FINISH = 6'b000011,
+ CLEAR = 6'b111110,
+ ERROR = 6'b100101
} aes_ctrl_e;
aes_ctrl_e aes_ctrl_ns, aes_ctrl_cs;
@@ -137,6 +161,7 @@
logic start_trigger;
logic cfg_valid;
+ logic no_alert;
logic start, finish;
logic cipher_crypt;
logic cipher_out_done;
@@ -175,13 +200,14 @@
iv_qe_i[1], iv_qe_i[1], iv_qe_i[0], iv_qe_i[0]};
// The cipher core is only ever allowed to start or finish if the control register holds a valid
- // configuration.
+ // configuration and if no fatal alert condition occured.
assign cfg_valid = ~((mode_i == AES_NONE) | ctrl_err_storage_i);
+ assign no_alert = ~alert_fatal_i;
// If set to start manually, we just wait for the trigger. Otherwise, we start once we have valid
// data available. If the IV (and counter) is needed, we only start if also the IV (and counter)
// is ready.
- assign start = cfg_valid &
+ assign start = cfg_valid & no_alert &
( manual_operation_i ? start_trigger :
(mode_i == AES_ECB) ? (key_init_ready & data_in_new) :
(mode_i == AES_CBC) ? (key_init_ready & data_in_new & iv_ready) :
@@ -192,7 +218,8 @@
// If not set to overwrite data, we wait for any previous output data to be read. data_out_read
// synchronously clears output_valid_q, unless new output data is written in the exact same
// clock cycle.
- assign finish = cfg_valid & (manual_operation_i ? 1'b1 : (~output_valid_q | data_out_read));
+ assign finish = cfg_valid & no_alert &
+ (manual_operation_i ? 1'b1 : (~output_valid_q | data_out_read));
// Helper signals for FSM
assign cipher_crypt = cipher_crypt_o | cipher_crypt_i;
@@ -238,6 +265,9 @@
// Control register
ctrl_we_o = 1'b0;
+ // Alert
+ alert_o = 1'b0;
+
// Pseudo-random number generator control
prng_data_req_o = 1'b0;
prng_reseed_req_o = 1'b0;
@@ -504,17 +534,32 @@
end
end
- default: aes_ctrl_ns = IDLE;
+ ERROR: begin
+ // Terminal error state
+ alert_o = 1'b1;
+ end
+
+ // We should never get here. If we do (e.g. via a malicious glitch), error out immediately.
+ default: begin
+ alert_o = 1'b1;
+ aes_ctrl_ns = ERROR;
+ end
endcase
end
- always_ff @(posedge clk_i or negedge rst_ni) begin : reg_fsm
- if (!rst_ni) begin
- aes_ctrl_cs <= IDLE;
- end else begin
- aes_ctrl_cs <= aes_ctrl_ns;
- end
- end
+ // This primitive is used to place a size-only constraint on the
+ // flops in order to prevent FSM state encoding optimizations.
+ logic [StateWidth-1:0] aes_ctrl_cs_raw;
+ assign aes_ctrl_cs = aes_ctrl_e'(aes_ctrl_cs_raw);
+ prim_flop #(
+ .Width(StateWidth),
+ .ResetValue(StateWidth'(IDLE))
+ ) u_state_regs (
+ .clk_i,
+ .rst_ni,
+ .d_i ( aes_ctrl_ns ),
+ .q_o ( aes_ctrl_cs_raw )
+ );
// We only use clean initial keys. Either software/counter has updated
// - all initial key registers, or
@@ -634,6 +679,12 @@
})
`ASSERT_KNOWN(AesOpKnown, op_i)
`ASSERT_KNOWN(AesCiphOpKnown, cipher_op_i)
- `ASSERT_KNOWN(AesControlStateValid, aes_ctrl_cs)
+ `ASSERT(AesControlStateValid, !alert_o |-> aes_ctrl_cs inside {
+ IDLE,
+ LOAD,
+ UPDATE_PRNG,
+ FINISH,
+ CLEAR
+ })
endmodule
diff --git a/hw/ip/aes/rtl/aes_core.sv b/hw/ip/aes/rtl/aes_core.sv
index 1ad52f8..6c6928b 100644
--- a/hw/ip/aes/rtl/aes_core.sv
+++ b/hw/ip/aes/rtl/aes_core.sv
@@ -35,8 +35,8 @@
input logic [WidthPRDMasking-1:0] entropy_masking_i,
// Alerts
- output logic ctrl_err_update_o,
- output logic ctrl_err_storage_o,
+ output logic alert_recoverable_o,
+ output logic alert_fatal_o,
// Bus Interface
input aes_reg2hw_t reg2hw,
@@ -56,6 +56,16 @@
logic manual_operation_q;
logic force_zero_masks_q;
ctrl_reg_t ctrl_d, ctrl_q;
+ logic ctrl_err_update_we;
+ logic ctrl_err_update;
+ logic ctrl_err_update_d;
+ logic ctrl_err_update_q;
+ logic ctrl_err_storage_we;
+ logic ctrl_err_storage;
+ logic ctrl_err_storage_d;
+ logic ctrl_err_storage_q;
+ logic ctrl_alert;
+
logic [3:0][3:0][7:0] state_in;
si_sel_e state_in_sel;
@@ -86,6 +96,7 @@
logic [7:0] ctr_we;
logic ctr_incr;
logic ctr_ready;
+ logic ctr_alert;
logic [3:0][31:0] data_in_prev_d;
logic [3:0][31:0] data_in_prev_q;
@@ -116,6 +127,7 @@
logic cipher_key_clear_busy;
logic cipher_data_out_clear;
logic cipher_data_out_clear_busy;
+ logic cipher_alert;
// Pseudo-random data for clearing purposes
logic [WidthPRDClearing-1:0] prd_clearing;
@@ -268,6 +280,7 @@
.incr_i ( ctr_incr ),
.ready_o ( ctr_ready ),
+ .alert_o ( ctr_alert ),
.ctr_i ( iv_q ),
.ctr_o ( ctr ),
@@ -345,7 +358,7 @@
.out_valid_o ( cipher_out_valid ),
.out_ready_i ( cipher_out_ready ),
- .cfg_valid_i ( ~ctrl_err_storage_o ),
+ .cfg_valid_i ( ~ctrl_err_storage ), // Used for gating assertions only.
.op_i ( cipher_op ),
.key_len_i ( key_len_q ),
.crypt_i ( cipher_crypt ),
@@ -356,6 +369,7 @@
.key_clear_o ( cipher_key_clear_busy ),
.data_out_clear_i ( cipher_data_out_clear ),
.data_out_clear_o ( cipher_data_out_clear_busy ),
+ .alert_o ( cipher_alert ),
.prd_clearing_i ( prd_clearing ),
@@ -453,14 +467,10 @@
.qe ( ),
.q ( ctrl_q ),
.qs ( ),
- .err_update ( ctrl_err_update_o ),
- .err_storage ( ctrl_err_storage_o )
+ .err_update ( ctrl_err_update_d ),
+ .err_storage ( ctrl_err_storage_d )
);
- // Make sure the storage error is observable via status register.
- assign hw2reg.status.ctrl_err_storage.d = ctrl_err_storage_o;
- assign hw2reg.status.ctrl_err_storage.de = ctrl_err_storage_o;
-
// Get shorter references.
assign aes_op_q = ctrl_q.operation;
assign aes_mode_q = ctrl_q.mode;
@@ -468,10 +478,6 @@
assign manual_operation_q = ctrl_q.manual_operation;
assign force_zero_masks_q = ctrl_q.force_zero_masks;
- // Unused alert signals
- logic unused_alert_signals;
- assign unused_alert_signals = ^reg2hw.alert_test;
-
/////////////
// Control //
/////////////
@@ -485,7 +491,7 @@
.ctrl_qe_i ( ctrl_qe ),
.ctrl_we_o ( ctrl_we ),
- .ctrl_err_storage_i ( ctrl_err_storage_o ),
+ .ctrl_err_storage_i ( ctrl_err_storage ),
.op_i ( aes_op_q ),
.mode_i ( aes_mode_q ),
.cipher_op_i ( cipher_op ),
@@ -496,6 +502,8 @@
.data_in_clear_i ( reg2hw.trigger.data_in_clear.q ),
.data_out_clear_i ( reg2hw.trigger.data_out_clear.q ),
.prng_reseed_i ( reg2hw.trigger.prng_reseed.q ),
+ .alert_fatal_i ( alert_fatal_o ),
+ .alert_o ( ctrl_alert ),
.key_init_qe_i ( key_init_qe ),
.iv_qe_i ( iv_qe ),
@@ -608,6 +616,48 @@
assign hw2reg.ctrl_shadowed.manual_operation.d = manual_operation_q;
assign hw2reg.ctrl_shadowed.force_zero_masks.d = force_zero_masks_q;
+ ////////////
+ // Alerts //
+ ////////////
+
+ // Recoverable alert conditions remain asserted until AES operation is restarted by rewriting the
+ // Control Register.
+ assign ctrl_err_update_we = ctrl_err_update_d | ctrl_we;
+ always_ff @(posedge clk_i or negedge rst_ni) begin : ctrl_err_update_reg
+ if (!rst_ni) begin
+ ctrl_err_update_q <= 1'b0;
+ end else if (ctrl_err_update_we) begin
+ ctrl_err_update_q <= ctrl_err_update_d;
+ end
+ end
+ assign ctrl_err_update = ctrl_err_update_d | ctrl_err_update_q;
+
+ // Fatal alert conditions need to remain asserted until reset.
+ assign ctrl_err_storage_we = ctrl_err_storage_d;
+ always_ff @(posedge clk_i or negedge rst_ni) begin : ctrl_err_storage_reg
+ if (!rst_ni) begin
+ ctrl_err_storage_q <= 1'b0;
+ end else if (ctrl_err_storage_we) begin
+ ctrl_err_storage_q <= 1'b1;
+ end
+ end
+ assign ctrl_err_storage = ctrl_err_storage_d | ctrl_err_storage_q;
+
+ // Collect alert signals.
+ assign alert_recoverable_o = ctrl_err_update;
+ assign alert_fatal_o = ctrl_err_storage | ctr_alert | cipher_alert | ctrl_alert;
+
+ // Make alerts observable via status register.
+ assign hw2reg.status.alert_recoverable.d = alert_recoverable_o;
+ assign hw2reg.status.alert_recoverable.de = ctrl_err_update_we;
+
+ assign hw2reg.status.alert_fatal.d = alert_fatal_o;
+ assign hw2reg.status.alert_fatal.de = alert_fatal_o;
+
+ // Unused alert signals
+ logic unused_alert_signals;
+ assign unused_alert_signals = ^reg2hw.alert_test;
+
////////////////
// Assertions //
////////////////
@@ -623,7 +673,7 @@
IV_CLEAR
})
`ASSERT_KNOWN(AesDataInPrevSelKnown, data_in_prev_sel)
- `ASSERT(AesModeValid, !ctrl_err_storage_o |-> aes_mode_q inside {
+ `ASSERT(AesModeValid, !ctrl_err_storage |-> aes_mode_q inside {
AES_ECB,
AES_CBC,
AES_CFB,
diff --git a/hw/ip/aes/rtl/aes_ctr.sv b/hw/ip/aes/rtl/aes_ctr.sv
index 3554001..ee7e839 100644
--- a/hw/ip/aes/rtl/aes_ctr.sv
+++ b/hw/ip/aes/rtl/aes_ctr.sv
@@ -14,6 +14,7 @@
input logic incr_i,
output logic ready_o,
+ output logic alert_o,
input logic [7:0][15:0] ctr_i, // 8 times 2 bytes
output logic [7:0][15:0] ctr_o, // 8 times 2 bytes
@@ -39,8 +40,26 @@
endfunction
// Types
- typedef enum logic {
- IDLE, INCR
+ // $ ./sparse-fsm-encode.py -d 3 -m 3 -n 5 \
+ // -s 31468618 --language=sv
+ //
+ // Hamming distance histogram:
+ //
+ // 0: --
+ // 1: --
+ // 2: --
+ // 3: |||||||||||||||||||| (66.67%)
+ // 4: |||||||||| (33.33%)
+ // 5: --
+ //
+ // Minimum Hamming distance: 3
+ // Maximum Hamming distance: 4
+ //
+ localparam int StateWidth = 5;
+ typedef enum logic [StateWidth-1:0] {
+ IDLE = 5'b01110,
+ INCR = 5'b11000,
+ ERROR = 5'b00001
} aes_ctr_e;
// Signals
@@ -83,6 +102,7 @@
// Outputs
ready_o = 1'b0;
ctr_we = 1'b0;
+ alert_o = 1'b0;
// FSM
aes_ctr_ns = aes_ctr_cs;
@@ -111,23 +131,45 @@
end
end
- default: aes_ctr_ns = IDLE;
+ ERROR: begin
+ // Terminal error state
+ alert_o = 1'b1;
+ end
+
+ // We should never get here. If we do (e.g. via a malicious
+ // glitch), error out immediately.
+ default: begin
+ alert_o = 1'b1;
+ aes_ctr_ns = ERROR;
+ end
endcase
end
// Registers
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
- aes_ctr_cs <= IDLE;
ctr_slice_idx_q <= '0;
ctr_carry_q <= '0;
end else begin
- aes_ctr_cs <= aes_ctr_ns;
ctr_slice_idx_q <= ctr_slice_idx_d;
ctr_carry_q <= ctr_carry_d;
end
end
+ // This primitive is used to place a size-only constraint on the
+ // flops in order to prevent FSM state encoding optimizations.
+ logic [StateWidth-1:0] aes_ctr_cs_raw;
+ assign aes_ctr_cs = aes_ctr_e'(aes_ctr_cs_raw);
+ prim_flop #(
+ .Width(StateWidth),
+ .ResetValue(StateWidth'(IDLE))
+ ) u_state_regs (
+ .clk_i,
+ .rst_ni,
+ .d_i ( aes_ctr_ns ),
+ .q_o ( aes_ctr_cs_raw )
+ );
+
/////////////
// Outputs //
/////////////
@@ -151,6 +193,9 @@
////////////////
// Assertions //
////////////////
- `ASSERT_KNOWN(AesCtrStateKnown, aes_ctr_cs)
+ `ASSERT(AesCtrStateValid, !alert_o |-> aes_ctr_cs inside {
+ IDLE,
+ INCR
+ })
endmodule
diff --git a/hw/ip/aes/rtl/aes_reg_pkg.sv b/hw/ip/aes/rtl/aes_reg_pkg.sv
index d4ef540..35944e1 100644
--- a/hw/ip/aes/rtl/aes_reg_pkg.sv
+++ b/hw/ip/aes/rtl/aes_reg_pkg.sv
@@ -19,11 +19,11 @@
struct packed {
logic q;
logic qe;
- } ctrl_err_update;
+ } recoverable;
struct packed {
logic q;
logic qe;
- } ctrl_err_storage;
+ } fatal;
} aes_reg2hw_alert_test_reg_t;
typedef struct packed {
@@ -187,7 +187,11 @@
struct packed {
logic d;
logic de;
- } ctrl_err_storage;
+ } alert_recoverable;
+ struct packed {
+ logic d;
+ logic de;
+ } alert_fatal;
} aes_hw2reg_status_reg_t;
@@ -209,14 +213,14 @@
// Internal design logic to register //
///////////////////////////////////////
typedef struct packed {
- aes_hw2reg_key_share0_mreg_t [7:0] key_share0; // [933:678]
- aes_hw2reg_key_share1_mreg_t [7:0] key_share1; // [677:422]
- aes_hw2reg_iv_mreg_t [3:0] iv; // [421:294]
- aes_hw2reg_data_in_mreg_t [3:0] data_in; // [293:162]
- aes_hw2reg_data_out_mreg_t [3:0] data_out; // [161:34]
- aes_hw2reg_ctrl_shadowed_reg_t ctrl_shadowed; // [33:22]
- aes_hw2reg_trigger_reg_t trigger; // [21:10]
- aes_hw2reg_status_reg_t status; // [9:0]
+ aes_hw2reg_key_share0_mreg_t [7:0] key_share0; // [935:680]
+ aes_hw2reg_key_share1_mreg_t [7:0] key_share1; // [679:424]
+ aes_hw2reg_iv_mreg_t [3:0] iv; // [423:296]
+ aes_hw2reg_data_in_mreg_t [3:0] data_in; // [295:164]
+ aes_hw2reg_data_out_mreg_t [3:0] data_out; // [163:36]
+ aes_hw2reg_ctrl_shadowed_reg_t ctrl_shadowed; // [35:24]
+ aes_hw2reg_trigger_reg_t trigger; // [23:12]
+ aes_hw2reg_status_reg_t status; // [11:0]
} aes_hw2reg_t;
// Register Address
diff --git a/hw/ip/aes/rtl/aes_reg_top.sv b/hw/ip/aes/rtl/aes_reg_top.sv
index eb41b73..7b7d6e5 100644
--- a/hw/ip/aes/rtl/aes_reg_top.sv
+++ b/hw/ip/aes/rtl/aes_reg_top.sv
@@ -71,10 +71,10 @@
// Define SW related signals
// Format: <reg>_<field>_{wd|we|qs}
// or <reg>_{wd|we|qs} if field == 1 or 0
- logic alert_test_ctrl_err_update_wd;
- logic alert_test_ctrl_err_update_we;
- logic alert_test_ctrl_err_storage_wd;
- logic alert_test_ctrl_err_storage_we;
+ logic alert_test_recoverable_wd;
+ logic alert_test_recoverable_we;
+ logic alert_test_fatal_wd;
+ logic alert_test_fatal_we;
logic [31:0] key_share0_0_wd;
logic key_share0_0_we;
logic [31:0] key_share0_1_wd;
@@ -167,37 +167,38 @@
logic status_stall_qs;
logic status_output_valid_qs;
logic status_input_ready_qs;
- logic status_ctrl_err_storage_qs;
+ logic status_alert_recoverable_qs;
+ logic status_alert_fatal_qs;
// Register instances
// R[alert_test]: V(True)
- // F[ctrl_err_update]: 0:0
+ // F[recoverable]: 0:0
prim_subreg_ext #(
.DW (1)
- ) u_alert_test_ctrl_err_update (
+ ) u_alert_test_recoverable (
.re (1'b0),
- .we (alert_test_ctrl_err_update_we),
- .wd (alert_test_ctrl_err_update_wd),
+ .we (alert_test_recoverable_we),
+ .wd (alert_test_recoverable_wd),
.d ('0),
.qre (),
- .qe (reg2hw.alert_test.ctrl_err_update.qe),
- .q (reg2hw.alert_test.ctrl_err_update.q ),
+ .qe (reg2hw.alert_test.recoverable.qe),
+ .q (reg2hw.alert_test.recoverable.q ),
.qs ()
);
- // F[ctrl_err_storage]: 1:1
+ // F[fatal]: 1:1
prim_subreg_ext #(
.DW (1)
- ) u_alert_test_ctrl_err_storage (
+ ) u_alert_test_fatal (
.re (1'b0),
- .we (alert_test_ctrl_err_storage_we),
- .wd (alert_test_ctrl_err_storage_wd),
+ .we (alert_test_fatal_we),
+ .wd (alert_test_fatal_wd),
.d ('0),
.qre (),
- .qe (reg2hw.alert_test.ctrl_err_storage.qe),
- .q (reg2hw.alert_test.ctrl_err_storage.q ),
+ .qe (reg2hw.alert_test.fatal.qe),
+ .q (reg2hw.alert_test.fatal.q ),
.qs ()
);
@@ -1031,12 +1032,12 @@
);
- // F[ctrl_err_storage]: 4:4
+ // F[alert_recoverable]: 4:4
prim_subreg #(
.DW (1),
.SWACCESS("RO"),
.RESVAL (1'h0)
- ) u_status_ctrl_err_storage (
+ ) u_status_alert_recoverable (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
@@ -1044,15 +1045,40 @@
.wd ('0 ),
// from internal hardware
- .de (hw2reg.status.ctrl_err_storage.de),
- .d (hw2reg.status.ctrl_err_storage.d ),
+ .de (hw2reg.status.alert_recoverable.de),
+ .d (hw2reg.status.alert_recoverable.d ),
// to internal hardware
.qe (),
.q (),
// to register interface (read)
- .qs (status_ctrl_err_storage_qs)
+ .qs (status_alert_recoverable_qs)
+ );
+
+
+ // F[alert_fatal]: 5:5
+ prim_subreg #(
+ .DW (1),
+ .SWACCESS("RO"),
+ .RESVAL (1'h0)
+ ) u_status_alert_fatal (
+ .clk_i (clk_i ),
+ .rst_ni (rst_ni ),
+
+ .we (1'b0),
+ .wd ('0 ),
+
+ // from internal hardware
+ .de (hw2reg.status.alert_fatal.de),
+ .d (hw2reg.status.alert_fatal.d ),
+
+ // to internal hardware
+ .qe (),
+ .q (),
+
+ // to register interface (read)
+ .qs (status_alert_fatal_qs)
);
@@ -1134,11 +1160,11 @@
if (addr_hit[31] && reg_we && (AES_PERMIT[31] != (AES_PERMIT[31] & reg_be))) wr_err = 1'b1 ;
end
- assign alert_test_ctrl_err_update_we = addr_hit[0] & reg_we & ~wr_err;
- assign alert_test_ctrl_err_update_wd = reg_wdata[0];
+ assign alert_test_recoverable_we = addr_hit[0] & reg_we & ~wr_err;
+ assign alert_test_recoverable_wd = reg_wdata[0];
- assign alert_test_ctrl_err_storage_we = addr_hit[0] & reg_we & ~wr_err;
- assign alert_test_ctrl_err_storage_wd = reg_wdata[1];
+ assign alert_test_fatal_we = addr_hit[0] & reg_we & ~wr_err;
+ assign alert_test_fatal_wd = reg_wdata[1];
assign key_share0_0_we = addr_hit[1] & reg_we & ~wr_err;
assign key_share0_0_wd = reg_wdata[31:0];
@@ -1263,6 +1289,7 @@
+
// Read data return
always_comb begin
reg_rdata_next = '0;
@@ -1406,7 +1433,8 @@
reg_rdata_next[1] = status_stall_qs;
reg_rdata_next[2] = status_output_valid_qs;
reg_rdata_next[3] = status_input_ready_qs;
- reg_rdata_next[4] = status_ctrl_err_storage_qs;
+ reg_rdata_next[4] = status_alert_recoverable_qs;
+ reg_rdata_next[5] = status_alert_fatal_qs;
end
default: begin
diff --git a/hw/ip/csrng/rtl/csrng_block_encrypt.sv b/hw/ip/csrng/rtl/csrng_block_encrypt.sv
index 54e6185..72c2b3f 100644
--- a/hw/ip/csrng/rtl/csrng_block_encrypt.sv
+++ b/hw/ip/csrng/rtl/csrng_block_encrypt.sv
@@ -105,6 +105,7 @@
.key_clear_o ( ),
.data_out_clear_i ( 1'b0 ), // Disable
.data_out_clear_o ( ),
+ .alert_o ( ), // Currently unused.
.prd_clearing_i ( '0 ),
.force_zero_masks_i ( 1'b0 ),
.data_in_mask_o ( ),
diff --git a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
index 8379f5f..d08c7e8 100644
--- a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
+++ b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
@@ -3822,7 +3822,7 @@
alert_list:
[
{
- name: ctrl_err_update
+ name: recoverable
width: 1
bits: "0"
bitinfo:
@@ -3835,7 +3835,7 @@
async: 1
}
{
- name: ctrl_err_storage
+ name: fatal
width: 1
bits: "1"
bitinfo:
@@ -8086,7 +8086,7 @@
alert:
[
{
- name: aes_ctrl_err_update
+ name: aes_recoverable
width: 1
bits: "0"
bitinfo:
@@ -8100,7 +8100,7 @@
module_name: aes
}
{
- name: aes_ctrl_err_storage
+ name: aes_fatal
width: 1
bits: "1"
bitinfo:
diff --git a/hw/top_earlgrey/dv/env/autogen/alert_handler_env_pkg__params.sv b/hw/top_earlgrey/dv/env/autogen/alert_handler_env_pkg__params.sv
index 878af25..96dae1c 100644
--- a/hw/top_earlgrey/dv/env/autogen/alert_handler_env_pkg__params.sv
+++ b/hw/top_earlgrey/dv/env/autogen/alert_handler_env_pkg__params.sv
@@ -5,8 +5,8 @@
// alert_handler_env_pkg__params.sv is auto-generated by `topgen.py` tool
parameter string LIST_OF_ALERTS[] = {
- "aes_ctrl_err_update",
- "aes_ctrl_err_storage",
+ "aes_recoverable",
+ "aes_fatal",
"otbn_fatal",
"otbn_recoverable",
"sensor_ctrl_as",
diff --git a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
index 595e5e7..d06ce2d 100644
--- a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
+++ b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
@@ -1260,8 +1260,8 @@
.RndCnstMskgChunkLfsrPerm(aes_pkg::RndCnstMskgChunkLfsrPermDefault)
) u_aes (
- // [12]: ctrl_err_update
- // [13]: ctrl_err_storage
+ // [12]: recoverable
+ // [13]: fatal
.alert_tx_o ( alert_tx[13:12] ),
.alert_rx_i ( alert_rx[13:12] ),
diff --git a/hw/top_earlgrey/sw/autogen/top_earlgrey.c b/hw/top_earlgrey/sw/autogen/top_earlgrey.c
index 25930e9..bc7a248 100644
--- a/hw/top_earlgrey/sw/autogen/top_earlgrey.c
+++ b/hw/top_earlgrey/sw/autogen/top_earlgrey.c
@@ -122,8 +122,8 @@
*/
const top_earlgrey_alert_peripheral_t
top_earlgrey_alert_for_peripheral[20] = {
- [kTopEarlgreyAlertIdAesCtrlErrUpdate] = kTopEarlgreyAlertPeripheralAes,
- [kTopEarlgreyAlertIdAesCtrlErrStorage] = kTopEarlgreyAlertPeripheralAes,
+ [kTopEarlgreyAlertIdAesRecoverable] = kTopEarlgreyAlertPeripheralAes,
+ [kTopEarlgreyAlertIdAesFatal] = kTopEarlgreyAlertPeripheralAes,
[kTopEarlgreyAlertIdOtbnFatal] = kTopEarlgreyAlertPeripheralOtbn,
[kTopEarlgreyAlertIdOtbnRecoverable] = kTopEarlgreyAlertPeripheralOtbn,
[kTopEarlgreyAlertIdSensorCtrlAs] = kTopEarlgreyAlertPeripheralSensorCtrl,
diff --git a/hw/top_earlgrey/sw/autogen/top_earlgrey.h b/hw/top_earlgrey/sw/autogen/top_earlgrey.h
index b013810..52d004e 100644
--- a/hw/top_earlgrey/sw/autogen/top_earlgrey.h
+++ b/hw/top_earlgrey/sw/autogen/top_earlgrey.h
@@ -754,8 +754,8 @@
* the same peripheral are guaranteed to be consecutive.
*/
typedef enum top_earlgrey_alert_id {
- kTopEarlgreyAlertIdAesCtrlErrUpdate = 0, /**< aes_ctrl_err_update */
- kTopEarlgreyAlertIdAesCtrlErrStorage = 1, /**< aes_ctrl_err_storage */
+ kTopEarlgreyAlertIdAesRecoverable = 0, /**< aes_recoverable */
+ kTopEarlgreyAlertIdAesFatal = 1, /**< aes_fatal */
kTopEarlgreyAlertIdOtbnFatal = 2, /**< otbn_fatal */
kTopEarlgreyAlertIdOtbnRecoverable = 3, /**< otbn_recoverable */
kTopEarlgreyAlertIdSensorCtrlAs = 4, /**< sensor_ctrl_as */