[prim] Add simple gate generator primitive

This primitive leverages the 4bit SBOX from the PRINCE block cipher
to generate a requested amount of gates. The split between registers and
logic is about 50/50.

Signed-off-by: Michael Schaffner <msf@opentitan.org>
diff --git a/hw/ip/prim/rtl/prim_gate_gen.sv b/hw/ip/prim/rtl/prim_gate_gen.sv
new file mode 100644
index 0000000..0e369d9
--- /dev/null
+++ b/hw/ip/prim/rtl/prim_gate_gen.sv
@@ -0,0 +1,109 @@
+// 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 it is accurate to
+// within around 250 GE. Do not use for fever than 500 GE.
+//
+// -------------+-----------+----------
+// 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
+//
+
+module prim_gate_gen #(
+  parameter int DataWidth = 32,
+  parameter int NumGates = 1000
+) (
+  input                        clk_i,
+  input                        rst_ni,
+
+  input        [DataWidth-1:0] data_i,
+  output logic [DataWidth-1:0] data_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)
+
+  /////////////////////
+  // Helper Function //
+  /////////////////////
+
+  // this is the sbox from the prince cipher
+  localparam logic[15:0][3:0] SBox4 = {4'h4, 4'hD, 4'h5, 4'hE,
+                                       4'h0, 4'h8, 4'h7, 4'h6,
+                                       4'h1, 4'h9, 4'hC, 4'hA,
+                                       4'h2, 4'h3, 4'hF, 4'hB};
+
+  function automatic logic [DataWidth-1:0] sbox4_layer(logic [DataWidth-1:0] state_in);
+    logic [DataWidth-1:0] state_out;
+    // note that if simulation performance becomes an issue, this loop can be unrolled
+    for (int k = 0; k < DataWidth/4; k++) begin
+      state_out[k*4  +: 4] = SBox4[state_in[k*4  +: 4]];
+    end
+    return state_out;
+  endfunction : sbox4_layer
+
+  /////////////////////
+  // Generator Loops //
+  /////////////////////
+
+  (* preserve *) logic [NumOuterRounds-1:0][DataWidth-1:0] regs_d, regs_q;
+
+  for (genvar k = 0; k < NumOuterRounds; k++) begin : gen_outer_round
+
+    (* preserve *) logic [NumInnerRounds:0][DataWidth-1:0] inner_data;
+
+    if (k==0) begin : gen_first
+      assign inner_data[0] = data_i;
+    end else begin : gen_others
+      assign inner_data[0] = regs_q[k-1];
+    end
+
+    for (genvar l = 0; l < NumInnerRounds; l++) begin : gen_inner
+      // 2bit rotation + sbox layer
+      assign inner_data[l+1] = sbox4_layer({inner_data[l][1:0], inner_data[l][DataWidth-1:2]});
+    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;
+    end else begin
+      regs_q <= regs_d;
+    end
+  end
+
+  assign data_o = regs_q[NumOuterRounds-1];
+
+endmodule : prim_gate_gen