blob: e8ba080285baf336a4d8dac6152ae1712853ed11 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// SRAM controller.
//
`include "prim_assert.sv"
module sram_ctrl
import sram_ctrl_pkg::*;
import sram_ctrl_reg_pkg::*;
#(
// Enable asynchronous transitions on alerts.
parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}},
parameter bit InstrExec = 1,
// Random netlist constants
parameter otp_ctrl_pkg::sram_key_t RndCnstSramKey = RndCnstSramKeyDefault,
parameter otp_ctrl_pkg::sram_nonce_t RndCnstSramNonce = RndCnstSramNonceDefault,
parameter lfsr_perm_t RndCnstSramLfsrPerm = RndCnstSramLfsrPermDefault
) (
// SRAM Clock
input clk_i,
input rst_ni,
// OTP Clock (for key interface)
input clk_otp_i,
input rst_otp_ni,
// Bus Interface (device) for CSRs
input tlul_pkg::tl_h2d_t tl_i,
output tlul_pkg::tl_d2h_t tl_o,
// Alert outputs.
input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i,
output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o,
// Life-cycle escalation input (scraps the scrambling keys)
input lc_ctrl_pkg::lc_tx_t lc_escalate_en_i,
input lc_ctrl_pkg::lc_tx_t lc_hw_debug_en_i,
// Otp configuration for sram execution
input otp_ctrl_part_pkg::otp_hw_cfg_t otp_hw_cfg_i,
// Key request to OTP (running on clk_fixed)
output otp_ctrl_pkg::sram_otp_key_req_t sram_otp_key_o,
input otp_ctrl_pkg::sram_otp_key_rsp_t sram_otp_key_i,
// Integrity error detection on corresponding sram
input intg_error_i,
// Interface with SRAM scrambling wrapper
output sram_scr_req_t sram_scr_o,
input sram_scr_rsp_t sram_scr_i,
// Interface with SRAM scrambling wrapper init
output sram_scr_init_req_t sram_scr_init_o,
input sram_scr_init_rsp_t sram_scr_init_i,
// Interface with corresponding tlul adapters
output tlul_pkg::tl_instr_en_e en_ifetch_o
);
// This peripheral only works up to a width of 64bits.
`ASSERT_INIT(WidthMustBeBelow64_A, Width <= 64)
`ASSERT_INIT(NonceWidthsLessThanSource_A, NonceWidth + RandInitSeed <=
otp_ctrl_pkg::SramNonceWidth)
//////////////////////////
// CSR Node and Mapping //
//////////////////////////
sram_ctrl_reg_pkg::sram_ctrl_reg2hw_t reg2hw;
sram_ctrl_reg_pkg::sram_ctrl_hw2reg_t hw2reg;
sram_ctrl_reg_top u_reg (
.clk_i,
.rst_ni,
.tl_i,
.tl_o,
.reg2hw,
.hw2reg,
.intg_err_o(),
.devmode_i (1'b1)
);
// Key and attribute outputs to scrambling device
logic [otp_ctrl_pkg::SramKeyWidth-1:0] key_d, key_q;
logic [otp_ctrl_pkg::SramNonceWidth-1:0] nonce_d, nonce_q;
assign sram_scr_o.key = key_q;
assign sram_scr_o.nonce = nonce_q[NonceWidth-1:0];
// tie-off unused nonce bits
if (otp_ctrl_pkg::SramNonceWidth > NonceWidth) begin : gen_nonce_tieoff
logic unused_nonce;
assign unused_nonce = ^nonce_q[otp_ctrl_pkg::SramNonceWidth-1:NonceWidth];
end
// Status register outputs
logic parity_error_d, parity_error_q;
logic escalated_q;
logic key_valid_d, key_valid_q;
logic key_seed_valid_d, key_seed_valid_q;
assign hw2reg.status.error = parity_error_q;
assign hw2reg.status.escalated.d = escalated_q;
assign hw2reg.status.scr_key_valid = key_valid_q;
assign hw2reg.status.scr_key_seed_valid = key_seed_valid_q;
// Control register
logic key_req;
assign key_req = reg2hw.ctrl.renew_scr_key.q & reg2hw.ctrl.renew_scr_key.qe;
// Parity error (the error is sticky and cannot be cleared).
assign hw2reg.error_address.d = sram_scr_i.raddr;
assign hw2reg.error_address.de = sram_scr_i.rerror[1];
assign parity_error_d = parity_error_q | sram_scr_i.rerror[1];
// Correctable RAM errors are not supported
logic unused_error;
assign unused_error = sram_scr_i.rerror[0];
// Parameter not used within module
// The memory is the user of the perm parameter. At the moment memories are not instantianted
// inside sram_ctrl but parallel to it.
lfsr_perm_t unused_perm_param;
assign unused_perm_param = RndCnstSramLfsrPerm;
//////////////////
// Alert Sender //
//////////////////
logic [NumAlerts-1:0] alert, alert_test;
assign alert = {
parity_error_q,
intg_error_i
};
assign alert_test = {
reg2hw.alert_test.fatal_parity_error.q &
reg2hw.alert_test.fatal_parity_error.qe,
reg2hw.alert_test.fatal_intg_error.q &
reg2hw.alert_test.fatal_intg_error.qe
};
for (genvar i=0; i < NumAlerts; i++) begin : gen_alerts
prim_alert_sender #(
.AsyncOn(AlertAsyncOn[i]),
.IsFatal(1)
) u_prim_alert_sender_parity (
.clk_i,
.rst_ni,
.alert_test_i ( alert_test[i] ),
.alert_req_i ( alert[i] ),
.alert_ack_o ( ),
.alert_state_o ( ),
.alert_rx_i ( alert_rx_i[i] ),
.alert_tx_o ( alert_tx_o[i] )
);
end
//////////////////////////////////////////
// Lifecycle Escalation Synchronization //
//////////////////////////////////////////
lc_ctrl_pkg::lc_tx_t escalate_en;
prim_lc_sync #(
.NumCopies (1)
) u_prim_lc_sync (
.clk_i,
.rst_ni,
.lc_en_i (lc_escalate_en_i),
.lc_en_o (escalate_en)
);
////////////////////////////
// Scrambling Key Request //
////////////////////////////
// The scrambling key and nonce have to be requested from the OTP controller via a req/ack
// protocol. Since the OTP controller works in a different clock domain, we have to synchronize
// the req/ack protocol as described in more details here:
// https://docs.opentitan.org/hw/ip/otp_ctrl/doc/index.html#interfaces-to-sram-and-otbn-scramblers
logic key_ack;
logic key_req_pending_d, key_req_pending_q;
assign key_req_pending_d = (key_req) ? 1'b1 :
(key_ack) ? 1'b0 : key_req_pending_q;
logic init_q;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
init_q <= '0;
end else if (init_q && sram_scr_init_i.ack) begin
init_q <= '0;
end else if (reg2hw.ctrl.init.q && reg2hw.ctrl.init.qe) begin
init_q <= 1'b1;
end
end
assign hw2reg.ctrl.renew_scr_key.d = '0;
assign hw2reg.ctrl.init.d = init_q;
// The SRAM scrambling wrapper will not accept any transactions while
// the key req is pending or if we have escalated.
assign sram_scr_o.valid = ~(key_req_pending_q | escalated_q);
assign sram_scr_init_o.req = init_q & key_valid_q;
assign sram_scr_init_o.seed = nonce_q[NonceWidth +: RandInitSeed];
assign key_valid_d = (key_req) ? 1'b0 :
(key_ack) ? 1'b1 : key_valid_q;
always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
if (!rst_ni) begin
key_req_pending_q <= 1'b0;
key_valid_q <= 1'b0;
key_seed_valid_q <= 1'b0;
parity_error_q <= 1'b0;
escalated_q <= 1'b0;
key_q <= RndCnstSramKey;
nonce_q <= RndCnstSramNonce;
end else begin
key_req_pending_q <= key_req_pending_d;
key_valid_q <= key_valid_d;
parity_error_q <= parity_error_d;
if (key_ack) begin
key_seed_valid_q <= key_seed_valid_d;
key_q <= key_d;
nonce_q <= nonce_d;
end
// This scraps the keys.
if (escalate_en != lc_ctrl_pkg::Off || escalated_q) begin
escalated_q <= 1'b1;
key_seed_valid_q <= 1'b0;
key_q <= RndCnstSramKey;
nonce_q <= RndCnstSramNonce;
end
end
end
prim_sync_reqack_data #(
.Width($bits(otp_ctrl_pkg::sram_otp_key_rsp_t)-1),
.DataSrc2Dst(1'b0)
) u_prim_sync_reqack_data (
.clk_src_i ( clk_i ),
.rst_src_ni ( rst_ni ),
.clk_dst_i ( clk_otp_i ),
.rst_dst_ni ( rst_otp_ni ),
.src_req_i ( key_req_pending_q ),
.src_ack_o ( key_ack ),
.dst_req_o ( sram_otp_key_o.req ),
.dst_ack_i ( sram_otp_key_i.ack ),
.data_i ( {sram_otp_key_i.key,
sram_otp_key_i.nonce,
sram_otp_key_i.seed_valid} ),
.data_o ( {key_d,
nonce_d,
key_seed_valid_d} )
);
////////////////////
// SRAM Execution //
////////////////////
if (InstrExec) begin : gen_instr_ctrl
tlul_pkg::tl_instr_en_e lc_ifetch_en;
tlul_pkg::tl_instr_en_e reg_ifetch_en;
assign lc_ifetch_en = (lc_hw_debug_en_i == lc_ctrl_pkg::On) ? tlul_pkg::InstrEn :
tlul_pkg::InstrDis;
assign reg_ifetch_en = tlul_pkg::tl_instr_en_e'(reg2hw.exec.q);
assign en_ifetch_o = (otp_hw_cfg_i.data.en_sram_ifetch == EnSramIfetch) ? reg_ifetch_en :
lc_ifetch_en;
end else begin : gen_tieoff
assign en_ifetch_o = tlul_pkg::InstrDis;
end
// tie off unused signal
if (!InstrExec) begin : gen_tieoff_unused
lc_ctrl_pkg::lc_tx_t unused_lc;
tl_instr_en_e unused_reg_en;
assign unused_lc = lc_hw_debug_en_i;
assign unused_reg_en = tl_instr_en_e'(reg2hw.exec.q);
end
logic unused_otp_bits;
assign unused_otp_bits = ^otp_hw_cfg_i;
////////////////
// Assertions //
////////////////
`ASSERT_KNOWN(TlOutKnown_A, tl_o)
`ASSERT_KNOWN(AlertOutKnown_A, alert_tx_o)
`ASSERT_KNOWN(SramOtpKeyKnown_A, sram_otp_key_o)
`ASSERT_KNOWN(SramScrOutKnown_A, sram_scr_o)
// Correctable RAM errors are not supported, so rerror[0] should never go high.
`ASSERT_NEVER(NoCorrectableErr_A, sram_scr_i.rerror[0])
endmodule : sram_ctrl