[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',