blob: e97c7d50ad58830c3c2b503d1c452491a83a260d [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Simple parameterizable gate generator. Used to fill up the netlist
// with gates that cannot be optimized away.
//
// The module leverages 4bit SBoxes from the PRINCE cipher, and interleaves
// them with registers, resulting in a split of around 50/50 between logic and
// sequential cells.
//
// This generator has been tested with 32bit wide data, and produces
// the following results:
//
// if valid_i constantly set to 1'b1:
// -------------+-----------+----------
// requested GE | actual GE | GE error
// -------------+-----------+----------
// 500 | 679 | 179
// 1000 | 1018 | 18
// 1500 | 1696 | 196
// 2500 | 2714 | 214
// 5000 | 5210 | 210
// 7500 | 7456 | -44
// 10000 | 10015 | 15
// 15000 | 15191 | 191
// 25000 | 25228 | 228
// 50000 | 50485 | 485
//
// otherwise, with clock gating enabled:
// -------------+-----------+----------
// requested GE | actual GE | GE error
// -------------+-----------+----------
// 500 | 696 | 196
// 1000 | 1043 | 43
// 1500 | 1737 | 237
// 2500 | 2779 | 279
// 5000 | 5340 | 340
// 7500 | 7634 | 134
// 10000 | 10284 | 284
// 15000 | 15585 | 585
// 25000 | 25855 | 855
// 50000 | 51732 | 1732
//
// Note that the generator is not very accurate for smaller gate counts due
// to the generate loop granularity. Hence, do not use for fever than 500 GE.
module prim_gate_gen #(
parameter int DataWidth = 32,
parameter int NumGates = 1000
) (
input clk_i,
input rst_ni,
input valid_i,
input [DataWidth-1:0] data_i,
output logic [DataWidth-1:0] data_o,
output valid_o
);
/////////////////////////////////////
// Local parameters and assertions //
/////////////////////////////////////
// technology specific tuning, do not modify.
// an inner round is comprised of a 2bit rotation, followed by a 4bit SBox Layer.
localparam int NumInnerRounds = 2;
localparam int GatesPerRound = DataWidth * 10;
// an outer round consists of NumInnerRounds, followed by a register.
localparam int NumOuterRounds = (NumGates + GatesPerRound / 2) / GatesPerRound;
// do not use for fewer than 500 GE
`ASSERT(MinimumNumGates_A, NumGates >= 500)
`ASSERT(DataMustBeMultipleOfFour_A, DataWidth % 4 == 0)
/////////////////////
// Generator Loops //
/////////////////////
logic [NumOuterRounds-1:0][DataWidth-1:0] regs_d, regs_q;
logic [NumOuterRounds-1:0] valid_d, valid_q;
for (genvar k = 0; k < NumOuterRounds; k++) begin : gen_outer_round
logic [NumInnerRounds:0][DataWidth-1:0] inner_data;
if (k==0) begin : gen_first
assign inner_data[0] = data_i;
assign valid_d[0] = valid_i;
end else begin : gen_others
assign inner_data[0] = regs_q[k-1];
assign valid_d[k] = valid_q[k-1];
end
for (genvar l = 0; l < NumInnerRounds; l++) begin : gen_inner
// 2bit rotation + sbox layer
assign inner_data[l+1] = prim_cipher_pkg::sbox4_32bit({inner_data[l][1:0],
inner_data[l][DataWidth-1:2]},
prim_cipher_pkg::PRINCE_SBOX4);
end
assign regs_d[k] = inner_data[NumInnerRounds];
end
always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
if (!rst_ni) begin
regs_q <= '0;
valid_q <= '0;
end else begin
valid_q <= valid_d;
for (int k = 0; k < NumOuterRounds; k++) begin
if (valid_d[k]) begin
regs_q[k] <= regs_d[k];
end
end
end
end
assign data_o = regs_q[NumOuterRounds-1];
assign valid_o = valid_q[NumOuterRounds-1];
endmodule : prim_gate_gen