[prim] Add non-linear out option to prim_lfsr

This follows AES's example and just uses the prince sbox on the output
of the LFSR.

Signed-off-by: Timothy Chen <timothytim@google.com>
diff --git a/hw/ip/prim/prim_lfsr.core b/hw/ip/prim/prim_lfsr.core
index 8d1c453..70aec74 100644
--- a/hw/ip/prim/prim_lfsr.core
+++ b/hw/ip/prim/prim_lfsr.core
@@ -9,6 +9,7 @@
   files_rtl:
     depend:
       - lowrisc:prim:assert
+      - lowrisc:prim:cipher_pkg
     files:
       - rtl/prim_lfsr.sv
     file_type: systemVerilogSource
diff --git a/hw/ip/prim/rtl/prim_lfsr.sv b/hw/ip/prim/rtl/prim_lfsr.sv
index 81c810b..dff9e8a 100644
--- a/hw/ip/prim/rtl/prim_lfsr.sv
+++ b/hw/ip/prim/rtl/prim_lfsr.sv
@@ -49,7 +49,11 @@
   // inputs are unused in order to not distort coverage
   // (the SVA will be unreachable in such cases)
   parameter bit                LockupSVA    = 1'b1,
-  parameter bit                ExtSeedSVA   = 1'b1
+  parameter bit                ExtSeedSVA   = 1'b1,
+  // Introduce non-linearity to lfsr output
+  // Note, unlike StatePermEn, this feature is not "for free".
+  // Please double check that this feature is indeed required.
+  parameter bit                NonLinearOut = 1'b0
 ) (
   input                         clk_i,
   input                         rst_ni,
@@ -370,14 +374,29 @@
                   (lfsr_en_i)           ? next_lfsr_state :
                                           lfsr_q;
 
+  logic [StateOutDw-1:0] state;
   if (StatePermEn) begin : gen_state_perm
     for (genvar k = 0; k < StateOutDw; k++) begin : gen_perm_loop
-      assign state_o[k] = lfsr_q[StatePerm[k]];
+      assign state[k] = lfsr_q[StatePerm[k]];
     end
   end else begin : gen_no_state_perm
-    assign state_o  = lfsr_q[StateOutDw-1:0];
+    assign state  = lfsr_q[StateOutDw-1:0];
   end
 
+  if (NonLinearOut) begin : gen_out_non_linear
+    localparam int NumBytes = StateOutDw / 8;
+    logic [NumBytes-1:0][7:0] sbox_in, sbox_out;
+    assign sbox_in = state;
+    assign state_o = sbox_out;
+    for (genvar b = 0; b < NumBytes; b++) begin : gen_sub
+      assign sbox_out[b] =
+        prim_cipher_pkg::sbox4_8bit(sbox_in[b], prim_cipher_pkg::PRINCE_SBOX4);
+    end
+  end else begin : gen_out_passthru
+    assign state_o = state;
+  end
+
+
   always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg
     if (!rst_ni) begin
       lfsr_q <= DefaultSeed;
@@ -463,7 +482,7 @@
   // output check
   `ASSERT_KNOWN(OutputKnown_A, state_o)
   if (!StatePermEn) begin : gen_output_sva
-    `ASSERT(OutputCheck_A, state_o == StateOutDw'(lfsr_q))
+    `ASSERT(OutputCheck_A, state == StateOutDw'(lfsr_q))
   end
   // if no external input changes the lfsr state, a lockup must not occur (by design)
   //`ASSERT(NoLockups_A, (!entropy_i) && (!seed_en_i) |=> !lockup, clk_i, !rst_ni)
@@ -485,6 +504,18 @@
     `ASSERT(LfsrLockupCheck_A, lfsr_en_i && lockup && !seed_en_i |=> !lockup)
   end
 
+  // If non-linear output requested, the output must be multiples of 8-bits
+  if(NonLinearOut) begin : gen_byte_check_sva
+    `ASSERT_INIT(SboxByteAlign_A, StateOutDw % 8 == 0)
+  end
+
+  // It does not make sense to enable non-linear output but not permutation.
+  // Permutation is basically for free, so if NonLinear is enabled so should
+  // permutation.  If non-linear is enabled but permutation is not, it is
+  // perhaps an error where the user intended to enable permutation only.
+  `ASSERT_INIT(SboxOutParamCheck_A, ~(~StatePermEn & NonLinearOut))
+
+
   if (MaxLenSVA) begin : gen_max_len_sva
 
 `ifndef SYNTHESIS