[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