blob: c68b10c89eda6585c0491dbe08f2ad0f197f67b9 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
`include "prim_assert.sv"
//
// A scrambled ROM. This is scrambled with a fixed key, passed in as a parameter (this parameter
// will be a compile-time random constant).
//
// This code follows the structure of prim_ram_1p_scr.sv (although it's much simplified because the
// key is fixed and we don't support writes). For more information about what is going on, see that
// file. Using the parameter names in prim_ram_1p_scr, we have NumPrinceRoundsHalf = 2 (so
// approximately 5 effective rounds), NumDiffRounds = 2 and NumAddrScrRounds = 2 (enabling address
// scrambling with 2 rounds).
//
// There are two input address ports (rom_addr_i and prince_addr_i). These are expected to be
// connected to signals that are logically the same. The first is used as an input to the physical
// ROM index. The second is used when calculating the address-tweakable keystream. The trick is that
// you can mitigate fault-injection attacks that corrupt the address by splitting it somewhere
// "upstream". If a fault-injection only corrupts one of the two addresses, the result will be
// garbage.
module rom_ctrl_scrambled_rom
import prim_rom_pkg::rom_cfg_t;
#(
// The initial contents of the ROM. This is used for synthesis. For simulation, this is not used;
// instead, the simulator loads the contents of ROM over DPI.
//
// In either case, the input file should be scrambled. That is, it should contain the bits that
// will appear in the physical ROM.
parameter MemInitFile = "",
// The width of ROM words in bits
parameter int Width = 40,
// The number of words in the ROM
parameter int Depth = 16,
// The nonce for data and address scrambling
parameter bit [63:0] ScrNonce = '0,
// The (fixed) key for the PRINCE cipher
parameter bit [127:0] ScrKey = '0,
localparam int Aw = $clog2(Depth)
) (
input logic clk_i,
input logic rst_ni,
input logic req_i,
input logic [Aw-1:0] rom_addr_i,
input logic [Aw-1:0] prince_addr_i,
output logic rvalid_o,
output logic [Width-1:0] scr_rdata_o,
output logic [Width-1:0] clr_rdata_o,
input rom_cfg_t cfg_i
);
/////////////////////////////////////
// Anchor incoming seeds and constants
/////////////////////////////////////
localparam int TotalAnchorWidth = $bits(ScrNonce) +
$bits(ScrKey);
logic [63:0] scr_nonce;
logic [127:0] scr_key;
prim_sec_anchor_buf #(
.Width(TotalAnchorWidth)
) u_seed_anchor (
.in_i({ScrNonce,
ScrKey}),
.out_o({scr_nonce,
scr_key})
);
logic [63-Aw:0] data_scr_nonce;
logic [Aw-1:0] addr_scr_nonce;
assign data_scr_nonce = scr_nonce[63-Aw:0];
assign addr_scr_nonce = scr_nonce[63-:Aw];
// Parameter Checks ==========================================================
// The depth needs to be a power of 2 to use address scrambling
`ASSERT_INIT(DepthPow2Check_A, (Depth & (Depth - 1)) == 0)
// We only support a width up to 64
`ASSERT_INIT(MaxWidthCheck_A, Width <= 64)
// Address scrambling ========================================================
logic [Aw-1:0] addr_scr;
prim_subst_perm #(
.DataWidth (Aw),
.NumRounds (2),
.Decrypt (0)
) u_sp_addr (
.data_i (rom_addr_i),
.key_i (addr_scr_nonce),
.data_o (addr_scr)
);
// Keystream generation ======================================================
logic [63:0] keystream;
prim_prince #(
.DataWidth (64),
.KeyWidth (128),
.NumRoundsHalf (2),
.HalfwayDataReg (1'b1),
.HalfwayKeyReg (1'b1)
) u_prince (
.clk_i (clk_i),
.rst_ni (rst_ni),
.valid_i (req_i),
.data_i ({data_scr_nonce, prince_addr_i}),
.key_i (scr_key),
.dec_i (1'b0),
.data_o (keystream),
.valid_o ()
);
if (Width < 64) begin : gen_unread_keystream
// Ignore top bits of keystream: we just use the bottom Width bits.
logic unused_top_keystream;
assign unused_top_keystream = ^keystream[63:Width];
end
// The physical ROM ==========================================================
logic [Width-1:0] rdata_scr;
prim_rom_adv #(
.Width (Width),
.Depth (Depth),
.MemInitFile (MemInitFile)
) u_rom (
.clk_i (clk_i),
.rst_ni (rst_ni),
.req_i (req_i),
.addr_i (addr_scr),
.rvalid_o (rvalid_o),
.rdata_o (rdata_scr),
.cfg_i (cfg_i)
);
assign scr_rdata_o = rdata_scr;
// Data scrambling ===========================================================
logic [Width-1:0] rdata_xor;
prim_subst_perm #(
.DataWidth (Width),
.NumRounds (2),
.Decrypt (1)
) u_sp_data (
.data_i (rdata_scr),
.key_i ('0),
.data_o (rdata_xor)
);
// XOR rdata with keystream ==================================================
assign clr_rdata_o = rdata_xor ^ keystream[Width-1:0];
endmodule