[aes/dv/aes_model_dpi] Add DPI infrastructure
This commit adds DPI infrastructure to call the reference C model or
OpenSSL/BoringSSL from within the RTL design simulated e.g. in
Verilator.
diff --git a/hw/ip/aes/dv/aes_model_dpi/aes_model_dpi.c b/hw/ip/aes/dv/aes_model_dpi/aes_model_dpi.c
new file mode 100644
index 0000000..a5796b2
--- /dev/null
+++ b/hw/ip/aes/dv/aes_model_dpi/aes_model_dpi.c
@@ -0,0 +1,271 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cstring>
+
+#include "aes.h"
+#include "crypto.h"
+#include "svdpi.h"
+
+#include "aes_model_dpi.h"
+
+void aes_crypt_dpi(const unsigned char impl_i, const unsigned char mode_i,
+ const svBitVecVal *key_len_i, const svOpenArrayHandle key_i,
+ const svOpenArrayHandle data_i,
+ const svOpenArrayHandle data_o) {
+ // get input data from simulator
+ unsigned char *key = aes_key_get(key_i);
+ unsigned char *ref_in = aes_data_get(data_i);
+
+ // key_len_i is one-hot encoded
+ int key_len;
+ if (*key_len_i == 0x1) {
+ key_len = 16;
+ } else if (*key_len_i == 0x2) {
+ key_len = 24;
+ } else { // 0x4
+ key_len = 32;
+ }
+
+ // We encrypt/decrypt one 16B block of data
+ int num_elem_ref_out = 16;
+ if (impl_i) {
+ // OpenSSL/BoringSSL require space for one spare block
+ num_elem_ref_out += 16;
+ }
+
+ // allocate memory
+ unsigned char *ref_out =
+ (unsigned char *)malloc(num_elem_ref_out * sizeof(unsigned char));
+ if (!ref_out) {
+ printf("ERROR: malloc() for aes_crypt_dpi failed");
+ return;
+ }
+
+ if (impl_i == 0) {
+ if (!mode_i) {
+ aes_encrypt_block(ref_in, key, key_len, ref_out);
+ } else {
+ aes_decrypt_block(ref_in, key, key_len, ref_out);
+ }
+ } else { // OpenSSL/BoringSSL
+ unsigned char iv[16];
+ memset(iv, 0, 16);
+
+ // always do an encrypt first as crypto_decrypt() requires
+ // the final spare block produced by crypto_encrypt()
+ int len = crypto_encrypt(ref_out, iv, ref_in, 16, key, key_len);
+
+ if (mode_i) {
+ // prepare
+ unsigned char *ref_in_crypto =
+ (unsigned char *)malloc(num_elem_ref_out * sizeof(unsigned char));
+ if (!ref_in_crypto) {
+ printf("ERROR: malloc() for aes_crypt_dpi failed");
+ return;
+ }
+ memcpy((void *)&ref_in_crypto[0], (const void *)&ref_in[0],
+ (size_t)(len - 16));
+ memcpy((void *)&ref_in_crypto[len - 16], (const void *)&ref_out[len - 16],
+ (size_t)16);
+
+ // do the decrypt
+ crypto_decrypt(ref_out, iv, ref_in_crypto, len, key, key_len);
+
+ // cleanup
+ free(ref_in_crypto);
+ }
+ }
+
+ // write output data back to simulator
+ aes_data_put(data_o, ref_out);
+
+ // free memory
+ free(key);
+ free(ref_in);
+
+ return;
+}
+
+void aes_sub_bytes_dpi(const unsigned char mode_i,
+ const svOpenArrayHandle data_i,
+ const svOpenArrayHandle data_o) {
+ // get input data from simulator
+ unsigned char *data = aes_data_get(data_i);
+
+ // perform shift rows
+ if (!mode_i) {
+ aes_sub_bytes(data);
+ } else {
+ aes_inv_sub_bytes(data);
+ }
+
+ // write output data back to simulator
+ aes_data_put(data_o, data);
+
+ return;
+}
+
+void aes_shift_rows_dpi(const unsigned char mode_i,
+ const svOpenArrayHandle data_i,
+ const svOpenArrayHandle data_o) {
+ // get input data from simulator
+ unsigned char *data = aes_data_get(data_i);
+
+ // perform shift rows
+ if (!mode_i) {
+ aes_shift_rows(data);
+ } else {
+ aes_inv_shift_rows(data);
+ }
+
+ // write output data back to simulator
+ aes_data_put(data_o, data);
+
+ return;
+}
+
+void aes_mix_columns_dpi(const unsigned char mode_i,
+ const svOpenArrayHandle data_i,
+ const svOpenArrayHandle data_o) {
+ // get input data from simulator
+ unsigned char *data = aes_data_get(data_i);
+
+ // perform shift rows
+ if (!mode_i) {
+ aes_mix_columns(data);
+ } else {
+ aes_inv_mix_columns(data);
+ }
+
+ // write output data back to simulator
+ aes_data_put(data_o, data);
+
+ return;
+}
+
+void aes_key_expand_dpi(const unsigned char mode_i, const svBitVecVal *rcon_old,
+ const svBitVecVal *round_i,
+ const svBitVecVal *key_len_i,
+ const svOpenArrayHandle key_i,
+ const svOpenArrayHandle key_o) {
+ unsigned char round_key[16]; // just used by model
+
+ // get input data
+ unsigned char *key = aes_key_get(key_i);
+ unsigned char rcon = (unsigned char)*rcon_old;
+ int rnd = (int)*round_i;
+
+ // key_len_i is one-hot encoded
+ int key_len;
+ if (*key_len_i == 0x1) {
+ key_len = 16;
+ } else if (*key_len_i == 0x2) {
+ key_len = 24;
+ } else { // 0x4
+ key_len = 32;
+ }
+
+ // perform shift rows
+ if (!mode_i) {
+ aes_key_expand(round_key, key, key_len, &rcon, rnd);
+ } else {
+ aes_inv_key_expand(round_key, key, key_len, &rcon, rnd);
+ }
+
+ // write output key back to simulator
+ aes_key_put(key_o, key);
+
+ return;
+}
+
+unsigned char *aes_data_get(const svOpenArrayHandle data_i) {
+ unsigned char *data;
+ int len;
+ svBitVecVal value;
+
+ // alloc data buffer
+ len = svSize(data_i, 1);
+ data = (unsigned char *)malloc(len * sizeof(unsigned char));
+ if (!data) {
+ printf("ERROR: malloc() for aes_data_get failed");
+ return 0;
+ }
+
+ // get data from simulator
+ for (int i = 0; i < len; i++) {
+ svGetBitArrElem1VecVal(&value, data_i, i);
+ data[i] = (unsigned char)value;
+ }
+
+ return data;
+}
+
+void aes_data_put(const svOpenArrayHandle data_o, unsigned char *data) {
+ int len;
+ svBitVecVal value;
+
+ // get size of data buffer
+ len = svSize(data_o, 1);
+
+ // write output data to simulation
+ for (int i = 0; i < len; i++) {
+ value = (svBitVecVal)data[i];
+ svPutBitArrElem1VecVal(data_o, &value, i);
+ }
+
+ // free data
+ free(data);
+
+ return;
+}
+
+unsigned char *aes_key_get(const svOpenArrayHandle key_i) {
+ unsigned char *key;
+ int len;
+ svBitVecVal value;
+
+ // alloc data buffer
+ len = svSize(key_i, 1);
+ key = (unsigned char *)malloc(len * 4 * sizeof(unsigned char));
+ if (!key) {
+ printf("ERROR: malloc() for aes_key_get failed");
+ return 0;
+ }
+
+ // get data from simulator
+ for (int i = 0; i < len; i++) {
+ svGetBitArrElem1VecVal(&value, key_i, i);
+ key[4 * i + 0] = (unsigned char)(value >> 0);
+ key[4 * i + 1] = (unsigned char)(value >> 8);
+ key[4 * i + 2] = (unsigned char)(value >> 16);
+ key[4 * i + 3] = (unsigned char)(value >> 24);
+ }
+
+ return key;
+}
+
+void aes_key_put(const svOpenArrayHandle key_o, unsigned char *key) {
+ int len;
+ svBitVecVal value;
+
+ // get size of data buffer
+ len = svSize(key_o, 1);
+
+ // write output data to simulation
+ for (int i = 0; i < len; i++) {
+ value = (svBitVecVal)(((key[4 * i + 0] << 0) & 0xFF) +
+ ((key[4 * i + 1] << 8) & 0xFF00) +
+ ((key[4 * i + 2] << 16) & 0xFF0000) +
+ ((key[4 * i + 3] << 24) & 0xFF000000));
+ svPutBitArrElem1VecVal(key_o, &value, i);
+ }
+
+ // free data
+ free(key);
+
+ return;
+}
diff --git a/hw/ip/aes/dv/aes_model_dpi/aes_model_dpi.core b/hw/ip/aes/dv/aes_model_dpi/aes_model_dpi.core
new file mode 100644
index 0000000..22176c7
--- /dev/null
+++ b/hw/ip/aes/dv/aes_model_dpi/aes_model_dpi.core
@@ -0,0 +1,18 @@
+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:aes_model_dpi:0.5"
+description: "AES model DPI"
+filesets:
+ files_dv:
+ files:
+ # Note: VCS needs the c/h-files to be treated as systemVerilogSource
+ - aes_model_dpi.c: { file_type: systemVerilogSource}
+ - aes_model_dpi.h: { file_type: systemVerilogSource, is_include_file: true}
+ - aes_model_dpi_pkg.sv: { file_type: systemVerilogSource }
+
+targets:
+ default:
+ filesets:
+ - files_dv
diff --git a/hw/ip/aes/dv/aes_model_dpi/aes_model_dpi.h b/hw/ip/aes/dv/aes_model_dpi/aes_model_dpi.h
new file mode 100644
index 0000000..64c8d45
--- /dev/null
+++ b/hw/ip/aes/dv/aes_model_dpi/aes_model_dpi.h
@@ -0,0 +1,113 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef _AES_MODEL_DPI_H_
+#define _AES_MODEL_DPI_H_
+
+#include "svdpi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Perform encryption/decryption of one block.
+ *
+ * @param impl_i Select reference impl.: 0 = C model, 1 = OpenSSL/BoringSSL
+ * @param mode_i Operation mode: 0 = encryption, 1 = decryption
+ * @param key_len_i Key length: 3'b001 = 128b, 3'b010 = 192b, 3'b100 = 256b
+ * @param key_i Full input key
+ * @param data_i Input data
+ * @param data_o Output data
+ */
+void aes_crypt_dpi(const unsigned char impl_i, const unsigned char mode_i,
+ const svBitVecVal *key_len_i, const svOpenArrayHandle key_i,
+ const svOpenArrayHandle data_i,
+ const svOpenArrayHandle data_o);
+
+/**
+ * Perform sub bytes operation during encryption/decryption.
+ *
+ * @param mode_i Operation mode: 0 = encryption, 1 = decryption
+ * @param data_i Input data
+ * @param data_o Output data
+ */
+void aes_sub_bytes_dpi(const unsigned char mode_i,
+ const svOpenArrayHandle data_i,
+ const svOpenArrayHandle data_o);
+
+/**
+ * Perform shift rows operation during encryption/decryption.
+ *
+ * @param mode_i Operation mode: 0 = encryption, 1 = decryption
+ * @param data_i Input data
+ * @param data_o Output data
+ */
+void aes_shift_rows_dpi(const unsigned char mode_i,
+ const svOpenArrayHandle data_i,
+ const svOpenArrayHandle data_o);
+
+/**
+ * Perform mix columns operation during encryption/decryption.
+ *
+ * @param mode_i Operation mode: 0 = encryption, 1 = decryption
+ * @param data_i Input data
+ * @param data_o Output data
+ */
+void aes_mix_columns_dpi(const unsigned char mode_i,
+ const svOpenArrayHandle data_i,
+ const svOpenArrayHandle data_o);
+
+/**
+ * Generate full key for next round during encryption/decryption.
+ *
+ * @param mode_i Operation mode: 0 = encryption, 1 = decryption
+ * @param rcon_old Previous rcon (updates intenally before being used)
+ * @param round_i Round index
+ * @param key_len_i Key length: 3'b001 = 128b, 3'b010 = 192b, 3'b100 = 256b
+ * @param key_i Full input key
+ * @param key_o Full output key
+ */
+void aes_key_expand_dpi(const unsigned char mode_i, const svBitVecVal *rcon_old,
+ const svBitVecVal *round_i,
+ const svBitVecVal *key_len_i,
+ const svOpenArrayHandle key_i,
+ const svOpenArrayHandle key_o);
+
+/**
+ * Get data block from simulation.
+ *
+ * @param data_i Input data from simulation
+ * @return Pointer to data copied to memory, 0 in case of an error
+ */
+unsigned char *aes_data_get(const svOpenArrayHandle data_i);
+
+/**
+ * Write data block to simulation and free the source buffer afterwards.
+ *
+ * @param data_o Output data for simulation
+ * @param data Data to be copied to simulation, freed after the operation
+ */
+void aes_data_put(const svOpenArrayHandle data_o, unsigned char *data);
+
+/**
+ * Get key block from simulation.
+ *
+ * @param key_i Input key from simulation
+ * @return Pointer to key copied to memory, 0 in case of an error
+ */
+unsigned char *aes_key_get(const svOpenArrayHandle key_i);
+
+/**
+ * Write key block to simulation and free the source buffer afterwards.
+ *
+ * @param key_o Output key for simulation
+ * @param key Key to be copied to simulation, freed after the operation
+ */
+void aes_key_put(const svOpenArrayHandle key_o, unsigned char *key);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+#endif // _AES_MODEL_DPI_H_
diff --git a/hw/ip/aes/dv/aes_model_dpi/aes_model_dpi_pkg.sv b/hw/ip/aes/dv/aes_model_dpi/aes_model_dpi_pkg.sv
new file mode 100644
index 0000000..7a7569b
--- /dev/null
+++ b/hw/ip/aes/dv/aes_model_dpi/aes_model_dpi_pkg.sv
@@ -0,0 +1,44 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+package aes_model_dpi_pkg;
+
+ // DPI-C imports
+ import "DPI-C" context function void aes_crypt_dpi(
+ input bit impl_i,
+ input bit mode_i,
+ input bit[2:0] key_len_i,
+ input bit[31:0] key_i[],
+ input bit[7:0] data_i[],
+ output bit[7:0] data_o[]
+ );
+
+ import "DPI-C" context function void aes_sub_bytes_dpi(
+ input bit mode_i,
+ input bit[7:0] data_i[],
+ output bit[7:0] data_o[]
+ );
+
+ import "DPI-C" context function void aes_shift_rows_dpi(
+ input bit mode_i,
+ input bit[7:0] data_i[],
+ output bit[7:0] data_o[]
+ );
+
+ import "DPI-C" context function void aes_mix_columns_dpi(
+ input bit mode_i,
+ input bit[7:0] data_i[],
+ output bit[7:0] data_o[]
+ );
+
+ import "DPI-C" context function void aes_key_expand_dpi(
+ input bit mode_i,
+ input bit[7:0] rcon_q,
+ input bit[3:0] round_i,
+ input bit[2:0] key_len_i,
+ input bit[31:0] key_i[],
+ output bit[31:0] key_o[]
+ );
+
+endpackage