[dv/prim] add basic PRINCE testbench

Signed-off-by: Udi Jonnalagadda <udij@google.com>
diff --git a/hw/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince.c b/hw/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince.c
new file mode 100644
index 0000000..fba3a5b
--- /dev/null
+++ b/hw/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince.c
@@ -0,0 +1,33 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef _cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "prince_ref.h"
+#include "svdpi.h"
+
+extern uint64_t c_dpi_prince_encrypt(uint64_t plaintext, uint64_t key0,
+                                     uint64_t key1, int num_half_rounds,
+                                     int old_key_schedule) {
+  return prince_enc_dec_uint64(plaintext, key0, key1, 0, num_half_rounds,
+                               old_key_schedule);
+}
+
+extern uint64_t c_dpi_prince_decrypt(const uint64_t ciphertext,
+                                     const uint64_t key0, const uint64_t key1,
+                                     int num_half_rounds,
+                                     int old_key_schedule) {
+  return prince_enc_dec_uint64(ciphertext, key0, key1, 1, num_half_rounds,
+                               old_key_schedule);
+}
+
+#ifdef _cplusplus
+}
+#endif
diff --git a/hw/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince.core b/hw/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince.core
new file mode 100644
index 0000000..4d83d81
--- /dev/null
+++ b/hw/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince.core
@@ -0,0 +1,17 @@
+CAPI=2:
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+name: "lowrisc:dv:crypto_dpi_prince:0.1"
+description: "PRINCE block cipher reference C implementation from Sebastien Riou"
+filesets:
+  files_dv:
+    files:
+      - prince_ref.h: {file_type: cSource, is_include_file: true}
+      - crypto_dpi_prince.c: {file_type: cSource}
+      - crypto_dpi_prince_pkg.sv: {file_type: systemVerilogSource}
+
+targets:
+  default:
+    filesets:
+      - files_dv
diff --git a/hw/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince_pkg.sv b/hw/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince_pkg.sv
new file mode 100644
index 0000000..9dd7940
--- /dev/null
+++ b/hw/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince_pkg.sv
@@ -0,0 +1,64 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+package crypto_dpi_prince_pkg;
+
+  // dep packages
+
+  // parameters
+  // Max number of half-rounds.
+  parameter int unsigned NumRoundsHalf = 5;
+
+  // DPI-C imports
+  import "DPI-C" context function longint c_dpi_prince_encrypt(
+    input longint unsigned  data,
+    input longint unsigned  key0,
+    input longint unsigned  key1,
+    input int unsigned      num_half_rounds,
+    input int unsigned      new_key_schedule
+  );
+
+  import "DPI-C" context function longint c_dpi_prince_decrypt(
+    input longint unsigned  data,
+    input longint unsigned  key0,
+    input longint unsigned  key1,
+    input int unsigned      num_half_rounds,
+    input int unsigned      new_key_schedule
+  );
+
+  //////////////////////////////////////////////////////
+  // SV wrapper functions to be used by the testbench //
+  //////////////////////////////////////////////////////
+
+  function automatic void sv_dpi_prince_encrypt(
+    input bit [63:0]                      plaintext,
+    input bit [127:0]                     key,
+    input bit                             old_key_schedule,
+    output bit [NumRoundsHalf-1:0][63:0]  ciphertext
+  );
+    for (int i = 0; i < NumRoundsHalf; i++) begin
+      ciphertext[i] = c_dpi_prince_encrypt(plaintext,
+                                           key[63:0],
+                                           key[127:64],
+                                           i+1,
+                                           old_key_schedule);
+    end
+  endfunction
+
+  function automatic void sv_dpi_prince_decrypt(
+    input bit [NumRoundsHalf-1:0][63:0]   ciphertext,
+    input bit [127:0]                     key,
+    input bit                             old_key_schedule,
+    output bit [NumRoundsHalf-1:0][63:0]  plaintext
+  );
+    for (int i = 0; i < NumRoundsHalf; i++) begin
+      plaintext[i] = c_dpi_prince_decrypt(ciphertext[i],
+                                          key[63:0],
+                                          key[127:64],
+                                          i+1,
+                                          old_key_schedule);
+    end
+  endfunction
+
+endpackage
diff --git a/hw/ip/prim/dv/prim_prince/crypto_dpi_prince/prince_ref.h b/hw/ip/prim/dv/prim_prince/crypto_dpi_prince/prince_ref.h
new file mode 100644
index 0000000..76110c4
--- /dev/null
+++ b/hw/ip/prim/dv/prim_prince/crypto_dpi_prince/prince_ref.h
@@ -0,0 +1,344 @@
+// Copyright lowRISC contributors.
+// Copyright 2016 Sebastien Riou
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+/*! \file prince_ref.h
+    \brief Reference implementation of the Prince block cipher, complient to
+   C99. 'Reference' here means straightforward, unoptimized, and checked against
+   the few test vectors provided in the original paper
+   (http://eprint.iacr.org/2012/529.pdf). By defining the macro PRINCE_PRINT,
+   one can print out all successive internal states.
+*/
+
+/*
+ * This C-implementation of the PRINCE block cipher is taken from Sebastien
+ * Riou's open-sourced 'prince-c-ref' GitHub repository, found here:
+ * https://github.com/sebastien-riou/prince-c-ref/blob/master/include/prince_ref.h.
+ *
+ * Several modifications of varying severity have been made to this C model to
+ * enable extra external parameterizations for more thorough verification of the
+ * OpenTitan PRINCE hardware implementation.
+ * These modifications, in no particular order, are:
+ *
+ *    - Conversion of `prince_enc_dec_uint64_t(...)` to a non-static function to
+ *      allow usage by an external DPI-C model.
+ *    - Additional arguments added to `prince_enc_dec_uint64_t(...)`,
+ *      `prince_enc_dec(...)`, `prince_encrypt(...)` `prince_decrypt(...)`, and
+ *      `prince_core(...)` to allow an external DPI-C model to control the
+ *      number of cipher half-rounds and to toggle between the "legacy" key
+ *      schedule detailed in the original PRINCE paper and a newer key schedule.
+ *    - Modification of `prince_core(...)` to handle the new key schedule and
+ *      user-specified number of half-rounds.
+ */
+
+#ifndef OPENTITAN_HW_IP_PRIM_DV_PRIM_PRINCE_CRYPTO_DPI_PRINCE_PRINCE_REF_H_
+#define OPENTITAN_HW_IP_PRIM_DV_PRIM_PRINCE_CRYPTO_DPI_PRINCE_PRINCE_REF_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#ifndef PRINCE_PRINT
+#define PRINCE_PRINT(a) \
+  do {                  \
+  } while (0)
+#endif
+
+/**
+ * Converts a byte array into a 64-bit integer.
+ *
+ * The byte at index 0 is placed as the most significant byte.
+ */
+static uint64_t bytes_to_uint64(const uint8_t in[8]) {
+  uint64_t out = 0;
+  unsigned int i;
+  for (i = 0; i < 8; i++)
+    out = (out << 8) | in[i];
+  return out;
+}
+
+/**
+ * Converts a 64-bit integer into a byte array.
+ *
+ * The most significant byte is placed at index 0.
+ */
+static void uint64_to_bytes(const uint64_t in, uint8_t out[8]) {
+  unsigned int i;
+  for (i = 0; i < 8; i++)
+    out[i] = in >> ((7 - i) * 8);
+}
+
+/**
+ * Computes K0' from K0.
+ */
+static uint64_t prince_k0_to_k0_prime(const uint64_t k0) {
+  uint64_t k0_ror1 = (k0 >> 1) | (k0 << 63);
+  uint64_t k0_prime = k0_ror1 ^ (k0 >> 63);
+  return k0_prime;
+}
+
+static uint64_t prince_round_constant(const unsigned int round) {
+  uint64_t rc[] = {UINT64_C(0x0000000000000000), UINT64_C(0x13198a2e03707344),
+                   UINT64_C(0xa4093822299f31d0), UINT64_C(0x082efa98ec4e6c89),
+                   UINT64_C(0x452821e638d01377), UINT64_C(0xbe5466cf34e90c6c),
+                   UINT64_C(0x7ef84f78fd955cb1), UINT64_C(0x85840851f1ac43aa),
+                   UINT64_C(0xc882d32f25323c54), UINT64_C(0x64a51195e0e3610d),
+                   UINT64_C(0xd3b5a399ca0c2399), UINT64_C(0xc0ac29b7c97c50dd)};
+  return rc[round];
+}
+
+/**
+ * The 4 bit Prince sbox.
+ *
+ * Only the 4 lsb are taken into account to preserve nibble granularity.
+ */
+static unsigned int prince_sbox(unsigned int nibble) {
+  const unsigned int sbox[] = {0xb, 0xf, 0x3, 0x2, 0xa, 0xc, 0x9, 0x1,
+                               0x6, 0x7, 0x8, 0x0, 0xe, 0x5, 0xd, 0x4};
+  return sbox[nibble & 0xF];
+}
+
+/**
+ * The 4 bit Prince inverse sbox.
+ *
+ * Only the 4 lsb are taken into account to preserve nibble granularity.
+ */
+static unsigned int prince_sbox_inv(unsigned int nibble) {
+  const unsigned int sbox[] = {0xb, 0x7, 0x3, 0x2, 0xf, 0xd, 0x8, 0x9,
+                               0xa, 0x6, 0x4, 0x0, 0x5, 0xe, 0xc, 0x1};
+  return sbox[nibble & 0xF];
+}
+
+/**
+ * The S step of the Prince cipher.
+ */
+static uint64_t prince_s_layer(const uint64_t s_in) {
+  uint64_t s_out = 0;
+  for (unsigned int i = 0; i < 16; i++) {
+    const unsigned int shift = i * 4;
+    const unsigned int sbox_in = s_in >> shift;
+    const uint64_t sbox_out = prince_sbox(sbox_in);
+    s_out |= sbox_out << shift;
+  }
+  return s_out;
+}
+
+/**
+ * The S^-1 step of the Prince cipher.
+ */
+static uint64_t prince_s_inv_layer(const uint64_t s_inv_in) {
+  uint64_t s_inv_out = 0;
+  for (unsigned int i = 0; i < 16; i++) {
+    const unsigned int shift = i * 4;
+    const unsigned int sbox_in = s_inv_in >> shift;
+    const uint64_t sbox_out = prince_sbox_inv(sbox_in);
+    s_inv_out |= sbox_out << shift;
+  }
+  return s_inv_out;
+}
+
+static uint64_t gf2_mat_mult16_1(const uint64_t in, const uint64_t mat[16]) {
+  uint64_t out = 0;
+  for (unsigned int i = 0; i < 16; i++) {
+    if ((in >> i) & 1)
+      out ^= mat[i];
+  }
+  return out;
+}
+
+/**
+ * Build Prince's 16 bit matrices M0 and M1.
+ */
+static void prince_m16_matrices(uint64_t m16[2][16]) {
+  // 4 bits matrices m0 to m3 are stored in array m4
+  const uint64_t m4[4][4] = {// m0
+                             {0x0, 0x2, 0x4, 0x8},
+                             // m1
+                             {0x1, 0x0, 0x4, 0x8},
+                             // m2
+                             {0x1, 0x2, 0x0, 0x8},
+                             // m3
+                             {0x1, 0x2, 0x4, 0x0}};
+  // build 16 bits matrices
+  for (unsigned int i = 0; i < 16; i++) {
+    const unsigned int base = i / 4;
+    m16[0][i] =
+        (m4[(base + 3) % 4][i % 4] << 8) | (m4[(base + 2) % 4][i % 4] << 4) |
+        (m4[(base + 1) % 4][i % 4] << 0) | (m4[(base + 0) % 4][i % 4] << 12);
+    m16[1][i] =
+        (m16[0][i] >> 12) |
+        (0xFFFF & (m16[0][i] << 4));  // m1 is just a rotated version of m0
+  }
+}
+
+/**
+ * The M' step of the Prince cipher.
+ */
+static uint64_t prince_m_prime_layer(const uint64_t m_prime_in) {
+  // 16 bits matrices M0 and M1, generated using the code below
+  // uint64_t m16[2][16];prince_m16_matrices(m16);
+  // for(unsigned int i=0;i<16;i++) PRINCE_PRINT(m16[0][i]);
+  // for(unsigned int i=0;i<16;i++) PRINCE_PRINT(m16[1][i]);
+  static const uint64_t m16[2][16] = {
+      {0x0111, 0x2220, 0x4404, 0x8088, 0x1011, 0x0222, 0x4440, 0x8808, 0x1101,
+       0x2022, 0x0444, 0x8880, 0x1110, 0x2202, 0x4044, 0x0888},
+
+      {0x1110, 0x2202, 0x4044, 0x0888, 0x0111, 0x2220, 0x4404, 0x8088, 0x1011,
+       0x0222, 0x4440, 0x8808, 0x1101, 0x2022, 0x0444, 0x8880}};
+  const uint64_t chunk0 = gf2_mat_mult16_1(m_prime_in >> (0 * 16), m16[0]);
+  const uint64_t chunk1 = gf2_mat_mult16_1(m_prime_in >> (1 * 16), m16[1]);
+  const uint64_t chunk2 = gf2_mat_mult16_1(m_prime_in >> (2 * 16), m16[1]);
+  const uint64_t chunk3 = gf2_mat_mult16_1(m_prime_in >> (3 * 16), m16[0]);
+  const uint64_t m_prime_out = (chunk3 << (3 * 16)) | (chunk2 << (2 * 16)) |
+                               (chunk1 << (1 * 16)) | (chunk0 << (0 * 16));
+  return m_prime_out;
+}
+
+/**
+ * The shift row and inverse shift row of the Prince cipher.
+ */
+static uint64_t prince_shift_rows(const uint64_t in, int inverse) {
+  const uint64_t row_mask = UINT64_C(0xF000F000F000F000);
+  uint64_t shift_rows_out = 0;
+  for (unsigned int i = 0; i < 4; i++) {
+    const uint64_t row = in & (row_mask >> (4 * i));
+    const unsigned int shift = inverse ? i * 16 : 64 - i * 16;
+    shift_rows_out |= (row >> shift) | (row << (64 - shift));
+  }
+  return shift_rows_out;
+}
+
+/**
+ * The M step of the Prince cipher.
+ */
+static uint64_t prince_m_layer(const uint64_t m_in) {
+  const uint64_t m_prime_out = prince_m_prime_layer(m_in);
+  const uint64_t shift_rows_out = prince_shift_rows(m_prime_out, 0);
+  return shift_rows_out;
+}
+
+/**
+ * The M^-1 step of the Prince cipher.
+ */
+static uint64_t prince_m_inv_layer(const uint64_t m_inv_in) {
+  const uint64_t shift_rows_out = prince_shift_rows(m_inv_in, 1);
+  const uint64_t m_prime_out = prince_m_prime_layer(shift_rows_out);
+  return m_prime_out;
+}
+
+/**
+ * The core function of the Prince cipher.
+ */
+static uint64_t prince_core(const uint64_t core_input, const uint64_t k0,
+                            const uint64_t k1, int num_half_rounds,
+                            int old_key_schedule) {
+  PRINCE_PRINT(core_input);
+  PRINCE_PRINT(k1);
+  uint64_t k0_new = (old_key_schedule) ? k1 : k0;
+  uint64_t round_input = core_input ^ k1 ^ prince_round_constant(0);
+  for (unsigned int round = 1; round <= num_half_rounds; round++) {
+    PRINCE_PRINT(round_input);
+    const uint64_t s_out = prince_s_layer(round_input);
+    PRINCE_PRINT(s_out);
+    const uint64_t m_out = prince_m_layer(s_out);
+    PRINCE_PRINT(m_out);
+    round_input = (round % 2 == 1)
+                      ? m_out ^ k0_new ^ prince_round_constant(round)
+                      : m_out ^ k1 ^ prince_round_constant(round);
+  }
+  const uint64_t middle_round_s_out = prince_s_layer(round_input);
+  PRINCE_PRINT(middle_round_s_out);
+  const uint64_t m_prime_out = prince_m_prime_layer(middle_round_s_out);
+  PRINCE_PRINT(m_prime_out);
+  const uint64_t middle_round_s_inv_out = prince_s_inv_layer(m_prime_out);
+  round_input = middle_round_s_inv_out;
+  // for(unsigned int round = 6; round < num_half_rounds * 2 + 1; round ++){
+  for (unsigned int round = 1; round <= num_half_rounds; round++) {
+    PRINCE_PRINT(round_input);
+    const uint64_t constant_idx = 10 - num_half_rounds + round;
+    const uint64_t m_inv_in =
+        (round % 2 == 1)
+            ? round_input ^ k0_new ^ prince_round_constant(constant_idx)
+            : round_input ^ k1 ^ prince_round_constant(constant_idx);
+    PRINCE_PRINT(m_inv_in);
+    const uint64_t s_inv_in = prince_m_inv_layer(m_inv_in);
+    PRINCE_PRINT(s_inv_in);
+    const uint64_t s_inv_out = prince_s_inv_layer(s_inv_in);
+    round_input = s_inv_out;
+  }
+  const uint64_t core_output = round_input ^ k1 ^ prince_round_constant(11);
+  PRINCE_PRINT(core_output);
+  return core_output;
+}
+
+/**
+ * Top level function for Prince encryption/decryption.
+ *
+ * enc_k0 and enc_k1 must be the same for encryption and decryption.
+ * The handling of decryption is done internally.
+ */
+uint64_t prince_enc_dec_uint64(const uint64_t input, const uint64_t enc_k0,
+                               const uint64_t enc_k1, int decrypt,
+                               int num_half_rounds, int old_key_schedule) {
+  const uint64_t prince_alpha = UINT64_C(0xc0ac29b7c97c50dd);
+  const uint64_t k1 = enc_k1 ^ (decrypt ? prince_alpha : 0);
+  const uint64_t enc_k0_prime = prince_k0_to_k0_prime(enc_k0);
+  const uint64_t k0 = decrypt ? enc_k0_prime : enc_k0;
+  const uint64_t k0_prime = decrypt ? enc_k0 : enc_k0_prime;
+  PRINCE_PRINT(k0);
+  PRINCE_PRINT(input);
+  const uint64_t core_input = input ^ k0;
+  const uint64_t core_output =
+      prince_core(core_input, k0, k1, num_half_rounds, old_key_schedule);
+  const uint64_t output = core_output ^ k0_prime;
+  PRINCE_PRINT(k0_prime);
+  PRINCE_PRINT(output);
+  return output;
+}
+
+/**
+ * Byte oriented top level function for Prince encryption/decryption.
+ *
+ * key_bytes 0 to 7 must contain K0.
+ * key_bytes 8 to 15 must contain K1.
+ */
+static void prince_enc_dec(const uint8_t in_bytes[8],
+                           const uint8_t key_bytes[16], uint8_t out_bytes[8],
+                           int decrypt, int num_half_rounds,
+                           int old_key_schedule) {
+  const uint64_t input = bytes_to_uint64(in_bytes);
+  const uint64_t enc_k0 = bytes_to_uint64(key_bytes);
+  const uint64_t enc_k1 = bytes_to_uint64(key_bytes + 8);
+  const uint64_t output = prince_enc_dec_uint64(
+      input, enc_k0, enc_k1, decrypt, num_half_rounds, old_key_schedule);
+  uint64_to_bytes(output, out_bytes);
+}
+
+/**
+ * Byte oriented top level function for Prince encryption.
+ *
+ * key_bytes 0 to 7 must contain K0.
+ * key_bytes 8 to 15 must contain K1.
+ */
+static void prince_encrypt(const uint8_t in_bytes[8],
+                           const uint8_t key_bytes[16], uint8_t out_bytes[8],
+                           int num_half_rounds, int old_key_schedule) {
+  prince_enc_dec(in_bytes, key_bytes, out_bytes, 0, num_half_rounds,
+                 old_key_schedule);
+}
+
+/**
+ * Byte oriented top level function for Prince decryption.
+ *
+ * key_bytes 0 to 7 must contain K0.
+ * key_bytes 8 to 15 must contain K1.
+ */
+static void prince_decrypt(const uint8_t in_bytes[8],
+                           const uint8_t key_bytes[16], uint8_t out_bytes[8],
+                           int num_half_rounds, int old_key_schedule) {
+  prince_enc_dec(in_bytes, key_bytes, out_bytes, 1, num_half_rounds,
+                 old_key_schedule);
+  uint64_t m16[2][16];
+}
+
+#endif  // OPENTITAN_HW_IP_PRIM_DV_PRIM_PRINCE_CRYPTO_DPI_PRINCE_PRINCE_REF_H_
diff --git a/hw/ip/prim/dv/prim_prince/data/prim_prince_cover.cfg b/hw/ip/prim/dv/prim_prince/data/prim_prince_cover.cfg
new file mode 100644
index 0000000..574da3b
--- /dev/null
+++ b/hw/ip/prim/dv/prim_prince/data/prim_prince_cover.cfg
@@ -0,0 +1,11 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+// Enables coverage just only each of the prim_prince instances.
+// Limits toggle coverage to DUT IOs.
+
++module prim_prince
+begin tgl(portsonly)
+  +module prim_prince
+end
diff --git a/hw/ip/prim/dv/prim_prince/prim_prince_sim.core b/hw/ip/prim/dv/prim_prince/prim_prince_sim.core
new file mode 100644
index 0000000..120fee9
--- /dev/null
+++ b/hw/ip/prim/dv/prim_prince/prim_prince_sim.core
@@ -0,0 +1,28 @@
+CAPI=2:
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+name: "lowrisc:dv:prim_prince_sim:0.1"
+description: "PRINCE block cipher DV sim target"
+filesets:
+  files_rtl:
+    depend:
+      - lowrisc:prim:all
+    file_type: systemVerilogSource
+
+  files_dv:
+    depend:
+      - lowrisc:dv:crypto_dpi_prince:0.1
+      - lowrisc:dv:dv_utils
+      - lowrisc:dv:common_ifs
+    files:
+      - tb/prim_prince_tb.sv
+    file_type: systemVerilogSource
+
+targets:
+  sim:
+    toplevel: prim_prince_tb
+    filesets:
+      - files_rtl
+      - files_dv
+    default_tool: vcs
diff --git a/hw/ip/prim/dv/prim_prince/prim_prince_sim_cfg.hjson b/hw/ip/prim/dv/prim_prince/prim_prince_sim_cfg.hjson
new file mode 100644
index 0000000..47c5b00
--- /dev/null
+++ b/hw/ip/prim/dv/prim_prince/prim_prince_sim_cfg.hjson
@@ -0,0 +1,57 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+{
+  // Name of the sim cfg - typically same as the name of the DUT.
+  name: prim_prince
+
+  // Top level dut name (sv module).
+  dut: prim_prince
+
+  // Top level testbench name (sv module).
+  tb: prim_prince_tb
+
+  // Simulator used to sign off this block
+  tool: vcs
+
+  // Fusesoc core file used for building the file list.
+  fusesoc_core: lowrisc:dv:prim_prince_sim:0.1
+
+  // Import additional common sim cfg files.
+  import_cfgs: ["{proj_root}/hw/dv/data/common_sim_cfg.hjson"]
+
+  // Default iterations for all tests - each test entry can override this.
+  reseed: 50
+
+  tool_srcs: ["{proj_root}/hw/ip/prim/dv/prim_prince/data/prim_prince_cover.cfg"]
+
+  overrides: [
+    {
+      name: vcs_cov_cfg_file
+      value: "-cm_hier {tool_srcs_dir}/prim_prince_cover.cfg"
+    }
+  ]
+
+  // List of test specifications.
+  tests: [
+    {
+      name: prim_prince_test
+    }
+  ]
+
+  // List of regressions.
+  regressions: [
+    {
+      name: sanity
+      tests: ["prim_prince_test"]
+    }
+    {
+      name: nightly
+      // Run the same test as the "sanity" regression, just with a higher reseed value.
+      // This higher reseed value is due to the rather large state space created by
+      // the 128-bit key and 64-bit data values.
+      reseed: 500
+    }
+  ]
+}
+
diff --git a/hw/ip/prim/dv/prim_prince/tb/prim_prince_tb.sv b/hw/ip/prim/dv/prim_prince/tb/prim_prince_tb.sv
new file mode 100644
index 0000000..8945fd9
--- /dev/null
+++ b/hw/ip/prim/dv/prim_prince/tb/prim_prince_tb.sv
@@ -0,0 +1,294 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+//
+// Testbench module for prim_prince, drives various test vectors into all
+// implementations and compares intermediate and final output against
+// C-reference model, for both encryption and decryption.
+//
+// This testbench only tests the PRINCE block cipher in its 64-bit data
+// and 128-bit key configuration, other configurations with different sets of
+// widths remain untested.
+
+module prim_prince_tb;
+
+//////////////////////////////////////////////////////
+// config
+//////////////////////////////////////////////////////
+
+// Default to {data_width:64, key_width:128} configuration.
+// Data width and key width can be overriden from command-line if desired.
+
+`ifdef DATA_WIDTH
+  localparam int unsigned DataWidth = `DATA_WIDTH;
+`else
+  localparam int unsigned DataWidth = 64;
+`endif
+
+`ifdef KEY_WIDTH
+  localparam int unsigned KeyWidth = `KEY_WIDTH;
+`else
+  localparam int unsigned KeyWidth = 128;
+`endif
+
+  // Max number of half-rounds according to spec.
+  // Duplicate parameter definition here to avoid clutter due to long identifier.
+  localparam int unsigned NumRoundsHalf = crypto_dpi_prince_pkg::NumRoundsHalf;
+
+  // Use these to index the data/key arrays.
+  localparam bit Unregistered = 1'b0;
+  localparam bit Registered = 1'b1;
+
+  localparam bit NewKeySched = 1'b0;
+  localparam bit OldKeySched = 1'b1;
+
+  localparam time ClkPeriod = 10000;
+
+//////////////////////////////////////////////////////
+// Clock interface
+//////////////////////////////////////////////////////
+
+  wire clk, rst_n;
+
+  clk_rst_if clk_if (
+    .clk,
+    .rst_n
+  );
+
+//////////////////////////////////////////////////////
+// DUTs for both encryption and decryption
+//////////////////////////////////////////////////////
+
+  logic [1:0][1:0][NumRoundsHalf-1:0][DataWidth-1:0]  data_in;
+  logic [1:0][1:0][NumRoundsHalf-1:0][DataWidth-1:0]  data_out;
+  logic [1:0][1:0][NumRoundsHalf-1:0]                 valid_out;
+  logic                                               valid_in;
+  logic [KeyWidth-1:0]                                key_in;
+  logic                                               dec_in;
+
+  for (genvar i = 0; i < 2; i++) begin : gen_new_key_schedule
+    for (genvar j = 0; j < 2; j++) begin : gen_registered_variant
+      for (genvar k = 0; k < NumRoundsHalf; k++) begin : gen_duts
+        prim_prince #(
+          .DataWidth      ( DataWidth           ),
+          .KeyWidth       ( KeyWidth            ),
+          .NumRoundsHalf  ( k+1                 ),
+          .UseOldKeySched ( i                   ),
+          .HalfwayDataReg ( j                   ),
+          .HalfwayKeyReg  ( j                   )
+        ) dut (
+          .clk_i          ( clk                 ),
+          .rst_ni         ( rst_n               ),
+          .valid_i        ( valid_in            ),
+          .data_i         ( data_in[i][j][k]    ),
+          .key_i          ( key_in              ),
+          .dec_i          ( dec_in              ),
+          .valid_o        ( valid_out[i][j][k]  ),
+          .data_o         ( data_out[i][j][k]   )
+        );
+      end : gen_duts
+    end : gen_registered_variant
+  end : gen_new_key_schedule
+
+//////////////////////////////////////////////////////
+// API called by the testbench to drive/check stimulus
+//////////////////////////////////////////////////////
+
+  // Top level API task that should be called to run a full pass
+  // of encryption and decryption on some input data and key.
+  task test_prince(bit [DataWidth-1:0] plaintext,
+                   bit [KeyWidth-1:0]  key);
+
+    bit [1:0][1:0][NumRoundsHalf-1:0][DataWidth-1:0] encrypted_text;
+    $display("Starting encryption with data[0x%0x] and key[0x%0x]!", plaintext, key);
+    check_encryption(plaintext, key, encrypted_text);
+    $display("Starting decryption pass!");
+    check_decryption(encrypted_text, key);
+
+  endtask
+
+
+
+  // Helper task to drive plaintext and key into each encryption instance.
+  // Calls a subroutine to perform checks on the outputs (once they are available).
+  task check_encryption(input bit [DataWidth-1:0]                                 plaintext,
+                        input bit [KeyWidth-1:0]                                  key,
+                        output bit [1:0][1:0][NumRoundsHalf-1:0][DataWidth-1:0]   expected_cipher);
+
+    // Drive input into encryption instances.
+    key_in    = key;
+    dec_in    = 0;
+    valid_in  = 1;
+    for (int unsigned i = 0; i < 2; i++) begin
+      for (int unsigned j = 0; j < 2; j++) begin
+        for (int unsigned k = 0; k < NumRoundsHalf; k++) begin
+          data_in[i][j][k] = plaintext;
+        end
+      end
+    end
+    // Wait for the DUTs to finish calculations.
+    clk_if.wait_clks(2);
+    wait(&valid_out == 1);
+    valid_in = 0;
+    // query DPI model for expected encrypted output.
+    for (int i = 0; i < 2; i++) begin
+      for (int j = 0; j < 2; j++) begin
+        crypto_dpi_prince_pkg::sv_dpi_prince_encrypt(plaintext, key, i,
+                                                     expected_cipher[i][j]);
+      end
+    end
+    check_output(expected_cipher[OldKeySched],
+                 expected_cipher[NewKeySched],
+                 data_out[OldKeySched],
+                 data_out[NewKeySched],
+                 "Encryption");
+  endtask
+
+
+  // Helper task to drive ciphertext and key into each decryption instance.
+  // Calls a subroutine to perform checks on the outputs (once they are available).
+  task check_decryption(input bit [1:0][1:0][NumRoundsHalf-1:0][DataWidth-1:0]  ciphertext,
+                        input bit [KeyWidth-1:0]                                key);
+
+    // the expected plaintext after decryption will be provided by the C model.
+    bit [1:0][1:0][NumRoundsHalf-1:0][DataWidth-1:0] expected_plaintext;
+    // Drive input into decryption instances.
+    key_in = key;
+    dec_in = 1;
+    valid_in = 1;
+    for (int unsigned i = 0; i < 2; i++) begin
+      for (int unsigned j = 0; j < 2; j++) begin
+        for (int unsigned k = 0; k < NumRoundsHalf; k++) begin
+          data_in[i][j][k] = ciphertext[i][j][k];
+        end
+      end
+    end
+    // Wait for the DUTs to finish calculations.
+    clk_if.wait_clks(2);
+    wait(&valid_out == 1);
+    valid_in = 0;
+    // query DPI model for expected decrypted output.
+    for (int unsigned i = 0; i < 2; i++) begin
+      for (int unsigned j = 0; j < 2; j++) begin
+        crypto_dpi_prince_pkg::sv_dpi_prince_decrypt(ciphertext[i][j], key, i,
+                                                     expected_plaintext[i][j]);
+      end
+    end
+    check_output(expected_plaintext[OldKeySched],
+                 expected_plaintext[NewKeySched],
+                 data_out[OldKeySched],
+                 data_out[NewKeySched],
+                 "Decryption");
+  endtask
+
+
+  // Helper subroutine to compare key and data output values from
+  // the C-reference model and the DUTs.
+  //
+  // For each instance of PRINCE (whether using old or new key schedule),
+  // we need to check that the output data matches the output of the reference model.
+  //
+  // If any comparison error is seen, this task short-circuits immediately,
+  // printing out some debug information and the correct failure signature.
+  task check_output(input bit [1:0][NumRoundsHalf-1:0][DataWidth-1:0] expected_text_old_sched,
+                    input bit [1:0][NumRoundsHalf-1:0][DataWidth-1:0] expected_text_new_sched,
+                    input bit [1:0][NumRoundsHalf-1:0][DataWidth-1:0] actual_text_old_sched,
+                    input bit [1:0][NumRoundsHalf-1:0][DataWidth-1:0] actual_text_new_sched,
+                    input string                                      msg);
+
+    string reg_msg;
+    string err_msg;
+    for (int unsigned i = 0; i < 2; i++) begin
+      reg_msg = i ? "registered" : "unregistered";
+      for (int unsigned j = 0; j < NumRoundsHalf; j++) begin
+        // compare outputs generated using old key schedule.
+        if (expected_text_old_sched[i][j] != actual_text_old_sched[i][j]) begin
+          err_msg = {$sformatf("%s mismatch in %s design with %0d rounds and old key schedule!\n",
+                               msg, reg_msg, i+1),
+                     $sformatf("Expected[0x%0x] - Actual[0x%0x]\n", expected_text_old_sched[i][j],
+                               actual_text_old_sched[i][j]),
+                     "TEST FAILED CHECKS"};
+          $fatal(err_msg);
+        end
+        // compare outputs generated using new key schedule.
+        if (expected_text_new_sched[i][j] != actual_text_new_sched[i][j]) begin
+          err_msg = {$sformatf("%s mismatch in %s design with %0d rounds and old key schedule!\n",
+                               msg, reg_msg, i+1),
+                     $sformatf("Expected[0x%0x] - Actual[0x%0x]\n", expected_text_new_sched[i][j],
+                               actual_text_new_sched[i][j]),
+                     "TEST FAILED CHECKS"};
+          $fatal(err_msg);
+        end
+      end
+    end
+  endtask
+
+
+//////////////////////////////////////////////////////
+// main testbench body
+//////////////////////////////////////////////////////
+
+  initial begin : p_stimuli
+
+    // The key and plaintext/ciphertext to be fed into PRINCE instances.
+    bit [KeyWidth/2-1:0] k0, k1;
+    bit [DataWidth-1:0] plaintext;
+
+    clk_if.set_period_ns(ClkPeriod);
+    clk_if.set_active();
+    clk_if.apply_reset();
+    $timeformat(-12, 0, " ps", 12);
+    clk_if.wait_clks(10);
+
+    /////////////////////////////
+    // Test the 5 golden vectors.
+    /////////////////////////////
+
+    plaintext = '0;
+    k0 = '1;
+    k1 = '0;
+    test_prince(plaintext, {k1, k0});
+
+    plaintext = '0;
+    k0 = '0;
+    k1 = '0;
+    test_prince(plaintext, {k1, k0});
+
+    plaintext = '1;
+    k0 = '0;
+    k1 = '0;
+    test_prince(plaintext, {k1, k0});
+
+    plaintext = '0;
+    k0 = '0;
+    k1 = '1;
+    test_prince(plaintext, {k1, k0});
+
+    plaintext = 'h0123456789ABCDEF;
+    k0 = '0;
+    k1 = 'hFEDC_BA98_7654_3210;
+    test_prince(plaintext, {k1, k0});
+
+    // Test random vectors
+    for (int i = 0; i < 25000; i++) begin
+      if (!std::randomize(plaintext)) begin
+        $fatal("Randomization of plaintext failed!");
+      end
+      if (!std::randomize(k0)) begin
+        $fatal("Randomization of k0 failed!");
+      end
+      if (!std::randomize(k1)) begin
+        $fatal("Randomization of k1 failed!");
+      end
+      test_prince(plaintext, {k1, k0});
+    end
+
+
+    // Final error checking and print out the test signature (expected by simulation flow).
+    $display("All encryption and decryption passes were successful!");
+    $display("TEST PASSED CHECKS");
+    $finish();
+  end
+
+
+endmodule : prim_prince_tb
diff --git a/hw/top_earlgrey/dv/top_earlgrey_sim_cfgs.hjson b/hw/top_earlgrey/dv/top_earlgrey_sim_cfgs.hjson
index f06d175..aac538b 100644
--- a/hw/top_earlgrey/dv/top_earlgrey_sim_cfgs.hjson
+++ b/hw/top_earlgrey/dv/top_earlgrey_sim_cfgs.hjson
@@ -22,6 +22,7 @@
              "{proj_root}/hw/ip/usbdev/dv/usbdev_sim_cfg.hjson",
              "{proj_root}/hw/ip/prim/dv/prim_present/prim_present_sim_cfg.hjson",
              "{proj_root}/hw/ip/prim/dv/prim_lfsr/prim_lfsr_sim_cfg.hjson",
+             "{proj_root}/hw/ip/prim/dv/prim_prince/prim_prince_sim_cfg.hjson",
              "{proj_root}/hw/top_earlgrey/ip/alert_handler/dv/alert_handler_sim_cfg.hjson",
              "{proj_root}/hw/top_earlgrey/ip/xbar_main/dv/autogen/xbar_main_sim_cfg.hjson",
              "{proj_root}/hw/top_earlgrey/ip/xbar_peri/dv/autogen/xbar_peri_sim_cfg.hjson",
diff --git a/util/licence-checker.hjson b/util/licence-checker.hjson
index f800022..3ad1d9a 100644
--- a/util/licence-checker.hjson
+++ b/util/licence-checker.hjson
@@ -21,6 +21,8 @@
     'hw/dv/dpi/usbdpi/usb_crc.c',
     # Vendored HMAC model
     'hw/ip/hmac/dv/cryptoc_dpi/*',
+    # PRINCE C++ reference model from Sebastien Riou
+    'hw/ip/prim/dv/prim_prince/crypto_dpi_prince/prince_ref.h',
     # Vendored FPGA information
     'hw/top_earlgrey/data/*.xdc',
     'util/fpga/bram_load.mmi',