blob: 6b0c82fe3121093b359313fce40991d9195af95d [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Flash Phy Scramble Module
//
// This module implements the flash scramble / de-scramble operation
// This operation is actually XEX. However the components are broken
// in two and separately manipulated by the program and read pipelines.
//
module flash_phy_scramble import flash_phy_pkg::*; #(
parameter bit SecScrambleEn = 1'b1
) (
input clk_i,
input rst_ni,
input disable_i,
input calc_req_i, // calculate galois multiplier mask
input op_req_i, // request primitive operation
input cipher_ops_e op_type_i, // sramble or de-scramble
input [BankAddrW-1:0] addr_i,
input [DataWidth-1:0] plain_data_i,
input [DataWidth-1:0] scrambled_data_i,
input [KeySize-1:0] addr_key_i,
input [KeySize-1:0] data_key_i,
input [KeySize-1:0] rand_addr_key_i,
input [KeySize-1:0] rand_data_key_i,
output logic calc_ack_o,
output logic op_ack_o,
output logic [DataWidth-1:0] mask_o,
output logic [DataWidth-1:0] plain_data_o,
output logic [DataWidth-1:0] scrambled_data_o
);
localparam int AddrPadWidth = DataWidth - BankAddrW;
localparam int UnusedWidth = KeySize - AddrPadWidth;
// unused portion of addr_key
logic [KeySize-1:0] muxed_addr_key;
logic addr_key_sel;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
addr_key_sel <= '0;
end else if (!calc_req_i || calc_req_i && calc_ack_o) begin
addr_key_sel <= disable_i;
end
end
assign muxed_addr_key = addr_key_sel ? rand_addr_key_i : addr_key_i;
logic [UnusedWidth-1:0] unused_key;
assign unused_key = muxed_addr_key[KeySize-1 -: UnusedWidth];
// Galois Multiply portion
// Note: Degree of IPoly and width parameters must match (leading MSB of IPoly is dropped).
if (SecScrambleEn) begin : gen_gf_mult
prim_gf_mult # (
.Width(DataWidth),
.StagesPerCycle(DataWidth / GfMultCycles),
.IPoly(ScrambleIPoly)
) u_mult (
.clk_i,
.rst_ni,
.req_i(calc_req_i),
.operand_a_i({muxed_addr_key[DataWidth +: AddrPadWidth], addr_i}),
.operand_b_i(muxed_addr_key[DataWidth-1:0]),
.ack_o(calc_ack_o),
.prod_o(mask_o)
);
end else begin : gen_no_gf_mult
assign mask_o = '0;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
calc_ack_o <= '0;
end else if (calc_req_i && calc_ack_o) begin
calc_ack_o <= '0;
end else if (calc_req_i && !calc_ack_o) begin
calc_ack_o <= '1;
end
end
end
// Cipher portion
logic dec;
logic [DataWidth-1:0] data;
assign dec = op_type_i == DeScrambleOp;
// Do not allow the key to change during a transaction.
// While this may be desirable for security reasons, it creates
// timing issues for physical design
logic data_key_sel;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
data_key_sel <= '0;
end else if (!op_req_i || op_req_i && op_ack_o) begin
data_key_sel <= disable_i;
end
end
// the prim_prince valid_o is a flopped version of valid_i
// As a result, when op_req_i stays high due to multiple transactions
// in-flight, the receiving logic can misinterpret the 'ack' if we just
// tie it to valid_o.
// Add a little bit of shimming logic here to properly create the ack
logic cipher_valid_in_d, cipher_valid_in_q;
logic cipher_valid_out;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
cipher_valid_in_q <= '0;
end else begin
cipher_valid_in_q <= cipher_valid_in_d;
end
end
assign cipher_valid_in_d = op_ack_o ? '0 : op_req_i & !cipher_valid_out;
assign op_ack_o = cipher_valid_in_q & cipher_valid_out;
if (SecScrambleEn) begin : gen_prince
prim_prince # (
.DataWidth(DataWidth),
.KeyWidth(KeySize),
// Use improved key schedule proposed by https://eprint.iacr.org/2014/656.pdf (see appendix).
.UseOldKeySched(1'b0),
.HalfwayDataReg(1'b1),
// No key register is needed half way, since the data_key_i and operation op_type_i inputs
// remain constant until one data block has been processed.
.HalfwayKeyReg (1'b0)
) u_cipher (
.clk_i,
.rst_ni,
.valid_i(cipher_valid_in_d),
.data_i(dec ? scrambled_data_i : plain_data_i),
.key_i(data_key_sel ? rand_data_key_i : data_key_i),
.dec_i(dec),
.data_o(data),
.valid_o(cipher_valid_out)
);
end else begin : gen_no_prince
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
cipher_valid_out <= '0;
end else begin
cipher_valid_out <= cipher_valid_in_d;
end
end
assign data = dec ? scrambled_data_i : plain_data_i;
end
// if decrypt, output the unscrambled data, feed input through otherwise
assign plain_data_o = dec ? data : scrambled_data_i;
// if encrypt, output the scrambled data, feed input through otherwise
assign scrambled_data_o = dec ? plain_data_i : data;
endmodule // flash_phy_scramble