blob: 5e98bd4eedb49ea4720ee767d756ab10a00afeed [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}},
// Random netlist constants
parameter otp_ctrl_pkg::sram_key_t RndCnstSramKey = RndCnstSramKeyDefault,
parameter otp_ctrl_pkg::sram_nonce_t RndCnstSramNonce = RndCnstSramNonceDefault
) (
// 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,
// 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,
// Interface with SRAM scrambling wrapper
output sram_scr_req_t sram_scr_o,
input sram_scr_rsp_t sram_scr_i
);
// This peripheral only works up to a width of 64bits.
`ASSERT_INIT(WidthMustBeBelow64_A, Width <= 64)
//////////////////////////
// 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,
.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;
// 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.q & reg2hw.ctrl.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];
//////////////////
// Alert Sender //
//////////////////
logic alert;
logic alert_test;
assign alert = parity_error_q;
assign alert_test = reg2hw.alert_test.q &
reg2hw.alert_test.qe;
prim_alert_sender #(
.AsyncOn(AlertAsyncOn[0]),
.IsFatal(1)
) u_prim_alert_sender (
.clk_i,
.rst_ni,
.alert_test_i ( alert_test ),
.alert_req_i ( alert ),
.alert_ack_o ( ),
.alert_state_o ( ),
.alert_rx_i ( alert_rx_i[0] ),
.alert_tx_o ( alert_tx_o[0] )
);
//////////////////////////////////////////
// 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;
// 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 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} )
);
////////////////
// 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