blob: 2bee56fca219f4b6143746d5c5526c98b95adfbd [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_reg_pkg::*;
#(
parameter int Depth = 512, // Needs to be a power of 2 if NumAddrScrRounds > 0.
parameter int Width = 32, // Needs to be Byte aligned for parity
parameter int CfgWidth = 8, // WTC, RTC, etc
// Scrambling parameters. Note that this needs to be low-latency, hence we have to keep the
// amount of cipher rounds low. PRINCE has 5 half rounds in its original form, which corresponds
// to 2*5 + 1 effective rounds. Setting this to 2 halves this to approximately 5 effective rounds.
parameter int NumPrinceRoundsHalf = 2, // Number of PRINCE half rounds, can be [1..5]
// Number of extra intra-Byte diffusion rounds. Setting this to 0 disables intra-Byte diffusion.
parameter int NumByteScrRounds = 2,
// Number of address scrambling rounds. Setting this to 0 disables address scrambling.
parameter int NumAddrScrRounds = 2,
// Derived parameters
localparam int AddrWidth = prim_util_pkg::vbits(Depth)
) (
// 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,
// Bus Interface (device) for SRAM
input tlul_pkg::tl_h2d_t sram_tl_i,
output tlul_pkg::tl_d2h_t sram_tl_o,
// Interrupt Requests
output logic sram_integ_error_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
);
// 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;
logic [otp_ctrl_pkg::SramKeyWidth-1:0] sram_scr_key;
logic [otp_ctrl_pkg::SramNonceWidth-1:0] sram_scr_nonce;
logic [CfgWidth-1:0] sram_scr_cfg;
assign sram_scr_key = key_q;
assign sram_scr_nonce = nonce_q;
assign sram_scr_cfg = reg2hw.sram_cfg.q;
// Status register outputs
logic [AddrWidth-1:0] sram_scr_raddr;
logic key_valid_d, key_valid_q;
logic key_seed_valid_d, key_seed_valid_q;
// Flag an error if any of the error bits is set.
assign hw2reg.status.error = |reg2hw.error_type;
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;
// Integrity errors
logic [1:0] sram_rerror;
assign hw2reg.error_type.correctable.d = sram_rerror[0];
assign hw2reg.error_type.correctable.de = sram_rerror[0];
assign hw2reg.error_type.uncorrectable.d = sram_rerror[1];
assign hw2reg.error_type.uncorrectable.de = sram_rerror[1];
assign hw2reg.error_address.d = 32'(sram_scr_raddr);
assign hw2reg.error_address.de = |sram_rerror;
////////////////
// Interrupts //
////////////////
prim_intr_hw #(
.Width(1)
) u_intr_sram_integ_error (
.clk_i,
.rst_ni,
.event_intr_i ( |sram_rerror ),
.reg2hw_intr_enable_q_i ( reg2hw.intr_enable.q ),
.reg2hw_intr_test_q_i ( reg2hw.intr_test.q ),
.reg2hw_intr_test_qe_i ( reg2hw.intr_test.qe ),
.reg2hw_intr_state_q_i ( reg2hw.intr_state.q ),
.hw2reg_intr_state_de_o ( hw2reg.intr_state.de ),
.hw2reg_intr_state_d_o ( hw2reg.intr_state.d ),
.intr_o ( sram_integ_error_o )
);
//////////////////////////////////////////
// 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;
assign key_valid_d = (key_req) ? 1'b0 :
(key_ack) ? 1'b1 : key_valid_q;
assign key_d = sram_otp_key_i.key;
assign nonce_d = sram_otp_key_i.nonce;
assign key_seed_valid_d = sram_otp_key_i.seed_valid;
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;
key_q <= '0;
nonce_q <= '0;
end else begin
key_req_pending_q <= key_req_pending_d;
key_valid_q <= key_valid_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.state != lc_ctrl_pkg::Off) begin
key_seed_valid_q <= '0;
key_q <= '0;
nonce_q <= '0;
end
end
end
prim_sync_reqack u_prim_sync_reqack (
.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 )
);
////////////////////////////////
// Scrambled Memory Primitive //
////////////////////////////////
logic tlul_req;
logic tlul_we;
logic [AddrWidth-1:0] tlul_addr;
logic [Width-1:0] tlul_wdata;
logic [Width-1:0] tlul_wmask;
logic [Width-1:0] tlul_rdata;
logic tlul_rvalid;
logic [1:0] tlul_rerror;
tlul_adapter_sram #(
.SramAw(AddrWidth),
.SramDw(Width)
) u_tlul_adapter_sram (
.clk_i,
.rst_ni,
.tl_i ( sram_tl_i ),
.tl_o ( sram_tl_o ),
.req_o ( tlul_req ),
.gnt_i ( 1'b1 ),
.we_o ( tlul_we ),
.addr_o ( tlul_addr ),
.wdata_o ( tlul_wdata ),
.wmask_o ( tlul_wmask ),
.rdata_i ( tlul_rdata ),
.rvalid_i ( tlul_rvalid ),
.rerror_i ( tlul_rerror )
);
logic sram_req;
logic sram_we;
logic [AddrWidth-1:0] sram_addr;
logic [Width-1:0] sram_wdata;
logic [Width-1:0] sram_wmask;
logic [Width-1:0] sram_rdata;
logic sram_rvalid;
prim_ram_1p_scr #(
.Depth ( Depth ),
.Width ( Width ),
.CfgWidth ( CfgWidth ),
.NumPrinceRoundsHalf ( NumPrinceRoundsHalf ),
.NumByteScrRounds ( NumByteScrRounds ),
.NumAddrScrRounds ( NumAddrScrRounds )
) i_prim_ram_1p_scr (
.clk_i,
.rst_ni,
.key_i ( sram_scr_key ),
.nonce_i ( sram_scr_nonce ),
.req_i ( sram_req ),
.write_i ( sram_we ),
.addr_i ( sram_addr ),
.wdata_i ( sram_wdata ),
.wmask_i ( sram_wmask ),
.rdata_o ( sram_rdata ),
.rvalid_o ( sram_rvalid ),
.rerror_o ( sram_rerror ),
.raddr_o ( sram_scr_raddr ),
.cfg_i ( sram_scr_cfg )
);
// Do not forward requests to SRAM while key request is pending.
// Instead, we return a TLUL error.
assign sram_req = ~key_req_pending_q & tlul_req;
assign sram_we = ~key_req_pending_q & tlul_we;
assign sram_addr = tlul_addr;
assign sram_wdata = tlul_wdata;
assign sram_wmask = tlul_wmask;
// Delay the key pending error by one cycle in order to match with the SRAM read latency.
logic key_pending_error_d, key_pending_error_q;
assign key_pending_error_d = key_req_pending_q & tlul_req;
assign tlul_rdata = (key_pending_error_q) ? '0 : sram_rdata;
assign tlul_rvalid = (key_pending_error_q) ? 1'b1 : sram_rvalid;
assign tlul_rerror = (key_pending_error_q) ? 2'b10 : sram_rerror; // signal as uncorrectable
always_ff @(posedge clk_i or negedge rst_ni) begin : p_error_reg
if (!rst_ni) begin
key_pending_error_q <= 1'b0;
end else begin
key_pending_error_q <= key_pending_error_d;
end
end
////////////////
// Assertions //
////////////////
`ASSERT_KNOWN(TlOutKnown_A, tl_o)
`ASSERT_KNOWN(TlSramOutKnown_A, sram_tl_o)
`ASSERT_KNOWN(IntrSramParityErrorKnown_A, sram_integ_error_o)
`ASSERT_KNOWN(SramOtpKeyKnown_A, sram_otp_key_o)
endmodule : sram_ctrl