[sw] Add sanity check for AES unit

Signed-off-by: Pirmin Vogel <vogelpi@lowrisc.org>
diff --git a/ci/run_sw_make.sh b/ci/run_sw_make.sh
index f1ec539..c1e2a92 100755
--- a/ci/run_sw_make.sh
+++ b/ci/run_sw_make.sh
@@ -31,6 +31,7 @@
 readonly BUILD_TARGETS=("boot_rom"
   "examples/hello_usbdev"
   "examples/hello_world"
+  "tests/aes"
   "tests/flash_ctrl"
   "tests/hmac"
   "tests/rv_timer"
diff --git a/ci/run_verilator_pytest.sh b/ci/run_verilator_pytest.sh
index 78228c7..e3492d0 100755
--- a/ci/run_verilator_pytest.sh
+++ b/ci/run_verilator_pytest.sh
@@ -15,6 +15,7 @@
 BOOT_ROM_TARGET="boot_rom/boot_rom.elf"
 
 TEST_TARGETS=(
+  "tests/aes/aes_test.elf"
   "tests/flash_ctrl/flash_test.elf"
   "tests/hmac/sha256_test.elf"
   "tests/rv_timer/rv_timer_test.elf"
@@ -23,6 +24,7 @@
 if [[ ! -z ${MAKE_BUILD+x} ]]; then
   BOOT_ROM_TARGET="sw/device/sim/boot_rom/rom.elf"
   TEST_TARGETS=(
+    "sw/device/sim/tests/aes/sw.elf"
     "sw/device/sim/tests/flash_ctrl/sw.elf"
     "sw/device/sim/tests/hmac/sw.elf"
     "sw/device/sim/tests/rv_timer/sw.elf"
diff --git a/meson.build b/meson.build
index c8dd7e1..1cf5112 100644
--- a/meson.build
+++ b/meson.build
@@ -69,6 +69,7 @@
 )
 
 # TODO: Considering moving these into hw/ip directories.
+hw_ip_aes_reg_h = gen_hw_hdr.process('hw/ip/aes/data/aes.hjson')
 hw_ip_flash_ctrl_regs_h = gen_hw_hdr.process('hw/ip/flash_ctrl/data/flash_ctrl.hjson')
 hw_ip_gpio_reg_h = gen_hw_hdr.process('hw/ip/gpio/data/gpio.hjson')
 hw_ip_hmac_reg_h = gen_hw_hdr.process('hw/ip/hmac/data/hmac.hjson')
diff --git a/sw/device/lib/aes.c b/sw/device/lib/aes.c
new file mode 100644
index 0000000..c1da3c3
--- /dev/null
+++ b/sw/device/lib/aes.c
@@ -0,0 +1,104 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "sw/device/lib/aes.h"
+
+#include "aes_regs.h"  // Generated.
+#include "sw/device/lib/common.h"
+
+#define AES0_BASE_ADDR 0x40110000
+#define AES_NUM_REGS_DATA 4
+#define AES_NUM_REGS_KEY 8
+
+void aes_init(aes_cfg_t aes_cfg) {
+  REG32(AES_CTRL(0)) =
+      (aes_cfg.mode << AES_CTRL_MODE) |
+      ((aes_cfg.key_len & AES_CTRL_KEY_LEN_MASK) << AES_CTRL_KEY_LEN_OFFSET) |
+      (aes_cfg.manual_start_trigger << AES_CTRL_MANUAL_START_TRIGGER) |
+      (aes_cfg.force_data_overwrite << AES_CTRL_FORCE_DATA_OVERWRITE);
+};
+
+void aes_key_put(const void *key, aes_key_len_t key_len) {
+  // Determine how many key registers to use.
+  size_t num_regs_key_used;
+  if (key_len == kAes256) {
+    num_regs_key_used = 8;
+  } else if (key_len == kAes192) {
+    num_regs_key_used = 6;
+  } else {
+    num_regs_key_used = 4;
+  }
+
+  // Write the used key registers.
+  for (int i = 0; i < num_regs_key_used; ++i) {
+    REG32(AES_KEY0(0) + i * sizeof(uint32_t)) = ((uint32_t *)key)[i];
+  }
+  // Write the unused key registers (the AES unit requires all key registers to
+  // be written).
+  for (int i = num_regs_key_used; i < AES_NUM_REGS_KEY; ++i) {
+    REG32(AES_KEY0(0) + i * sizeof(uint32_t)) = 0x0;
+  }
+}
+
+void aes_data_put_wait(const void *data) {
+  // Wait for AES unit to be ready for new input data.
+  while (!aes_data_ready()) {
+  }
+
+  // Provide the input data.
+  aes_data_put(data);
+}
+
+void aes_data_put(const void *data) {
+  // Write the four input data registers.
+  for (int i = 0; i < AES_NUM_REGS_DATA; ++i) {
+    REG32(AES_DATA_IN0(0) + i * sizeof(uint32_t)) = ((uint32_t *)data)[i];
+  }
+}
+
+void aes_data_get_wait(void *data) {
+  // Wait for AES unit to have valid output data.
+  while (!aes_data_valid()) {
+  }
+
+  // Get the data.
+  aes_data_get(data);
+}
+
+void aes_data_get(void *data) {
+  // Read the four output data registers.
+  for (int i = 0; i < AES_NUM_REGS_DATA; ++i) {
+    ((uint32_t *)data)[i] = REG32(AES_DATA_OUT0(0) + i * sizeof(uint32_t));
+  }
+}
+
+bool aes_data_ready(void) {
+  return (REG32(AES_STATUS(0)) & (0x1u << AES_STATUS_INPUT_READY));
+}
+
+bool aes_data_valid(void) {
+  return (REG32(AES_STATUS(0)) & (0x1u << AES_STATUS_OUTPUT_VALID));
+}
+
+bool aes_idle(void) {
+  return (REG32(AES_STATUS(0)) & (0x1u << AES_STATUS_IDLE));
+}
+
+void aes_clear(void) {
+  // Wait for AES unit to be idle.
+  while (!aes_idle()) {
+  }
+
+  // Disable autostart
+  REG32(AES_CTRL(0)) = 0x1u << AES_CTRL_MANUAL_START_TRIGGER;
+
+  // Clear internal key and output registers
+  REG32(AES_TRIGGER(0)) = (0x1u << AES_TRIGGER_KEY_CLEAR) |
+                          (0x1u << AES_TRIGGER_DATA_IN_CLEAR) |
+                          (0x1u << AES_TRIGGER_DATA_OUT_CLEAR);
+
+  // Wait for output not valid, and input ready
+  while (!(!aes_data_valid() && aes_data_ready())) {
+  }
+}
diff --git a/sw/device/lib/aes.h b/sw/device/lib/aes.h
new file mode 100644
index 0000000..99f655a
--- /dev/null
+++ b/sw/device/lib/aes.h
@@ -0,0 +1,112 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef _F_LIB_AES_H__
+#define _F_LIB_AES_H__
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/**
+ * Supported AES modes: encode or decode.
+ */
+typedef enum aes_mode { kAesEnc = 0, kAesDec = 1 } aes_mode_t;
+
+/**
+ * Supported AES key lengths: 128 bit, 192 bit or 256 bit. The hardware uses a
+ * one-hot encoding.
+ */
+typedef enum aes_key_len {
+  kAes128 = 0x1,
+  kAes192 = 0x2,
+  kAes256 = 0x4
+} aes_key_len_t;
+
+/**
+ * AES unit configuration options.
+ */
+typedef struct aes_cfg {
+  /** Operational mode @see aes_mode. */
+  aes_mode_t mode;
+  /** Key length @see aes_key_len. */
+  aes_key_len_t key_len;
+  /** Set to 1 to only start upon getting a trigger signal. */
+  bool manual_start_trigger;
+  /** Set to 1 to not stall when previous output data has not been read. */
+  bool force_data_overwrite;
+} aes_cfg_t;
+
+/**
+ * Intialize AES unit to desired mode.
+ *
+ * @param aes_cfg AES configuration settings.
+ */
+void aes_init(aes_cfg_t aes_cfg);
+
+/**
+ * Pass initial encryption key to AES unit.
+ *
+ * @param key              pointer to key.
+ * @param key_len_in_bytes key length in bytes (16, 24, 32)
+ */
+void aes_key_put(const void *key, size_t key_len_in_bytes);
+
+/**
+ * Wait for AES unit to be ready for new input data and then
+ * pass one 16B block of input data to AES unit.
+ *
+ * @param data pointer to input buffer.
+ */
+void aes_data_put_wait(const void *data);
+
+/**
+ * Pass one 16B block of input data to AES unit.
+ *
+ * @param data pointer to input buffer.
+ */
+void aes_data_put(const void *data);
+
+/**
+ * Wait for AES unit to have valid output data and then
+ * get one 16B block of output data from AES unit.
+ *
+ * @param data pointer to output buffer.
+ */
+void aes_data_get_wait(void *data);
+
+/**
+ * Get one 16B block of output data from AES unit.
+ *
+ * @param data pointer to output buffer.
+ */
+void aes_data_get(void *data);
+
+/**
+ * Check AES unit for being ready to accept new input data.
+ *
+ * @return true if ready for new input data, false otherwise.
+ */
+bool aes_data_ready(void);
+
+/**
+ * Check AES unit for having valid output data.
+ *
+ * @return true if valid output data available, false otherwise.
+ */
+bool aes_data_valid(void);
+
+/**
+ * Check AES unit for being idle.
+ *
+ * @return true if idle, false otherwise.
+ */
+bool aes_idle(void);
+
+/**
+ * Clear key, input and ouput registers of AES unit.
+ */
+void aes_clear(void);
+
+#endif  // _F_LIB_AES_H__
diff --git a/sw/device/lib/meson.build b/sw/device/lib/meson.build
index eb8982b..17c99ba 100644
--- a/sw/device/lib/meson.build
+++ b/sw/device/lib/meson.build
@@ -66,6 +66,18 @@
   )
 )
 
+# AES library (sw_lib_aes)
+sw_lib_aes = declare_dependency(
+  sources: [hw_ip_aes_reg_h],
+  link_with: static_library(
+    'aes_ot',
+    sources: [
+      hw_ip_aes_reg_h,
+      'aes.c',
+    ]
+  )
+)
+
 # HMAC library (sw_lib_hmac)
 sw_lib_hmac = declare_dependency(
   sources: [hw_ip_hmac_reg_h],
diff --git a/sw/device/lib/srcs.mk b/sw/device/lib/srcs.mk
index 5d46ffb..0d198c4 100644
--- a/sw/device/lib/srcs.mk
+++ b/sw/device/lib/srcs.mk
@@ -6,7 +6,7 @@
 INCS              += -I$(LIB_DIR)
 
 GEN_HEADERS       += $(LIB_LOC_DIF_SRCS:.c=_regs.h)
-LIB_LOC_DIF_SRCS  += uart.c gpio.c spi_device.c flash_ctrl.c hmac.c usbdev.c rv_timer.c pinmux.c
+LIB_LOC_DIF_SRCS  += uart.c gpio.c spi_device.c flash_ctrl.c aes.c hmac.c usbdev.c rv_timer.c pinmux.c
 LIB_LOC_EXT_SRCS  += usb_controlep.c usb_simpleserial.c irq.c handler.c print_log.c irq_vectors.S memcpy.c memset.c strlen.c abort.c
 
 LIB_SRCS          += $(addprefix $(LIB_DIR)/, $(LIB_LOC_DIF_SRCS) $(LIB_LOC_EXT_SRCS))
diff --git a/sw/device/tests/aes/aes_test.c b/sw/device/tests/aes/aes_test.c
new file mode 100644
index 0000000..68f529b
--- /dev/null
+++ b/sw/device/tests/aes/aes_test.c
@@ -0,0 +1,86 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "sw/device/lib/aes.h"
+#include "sw/device/lib/common.h"
+#include "sw/device/lib/uart.h"
+
+// The following plaintext, key and ciphertext are extracted from Appendix C of
+// the Advanced Encryption Standard (AES) FIPS Publication 197 available at
+// https://www.nist.gov/publications/advanced-encryption-standard-aes
+
+static const uint8_t plain_text_1[16] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
+                                         0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
+                                         0xcc, 0xdd, 0xee, 0xff};
+
+static const uint8_t key_32_1[32] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+    0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+    0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f};
+
+static const uint8_t cipher_text_gold_32_1[16] = {
+    0x8e, 0xa2, 0xb7, 0xca, 0x51, 0x67, 0x45, 0xbf,
+    0xea, 0xfc, 0x49, 0x90, 0x4b, 0x49, 0x60, 0x89};
+
+int main(int argc, char **argv) {
+  bool has_error = false;
+
+  // Wait for AES unit being idle
+  while (!aes_idle()) {
+  }
+
+  uint8_t buffer[16];
+
+  uart_init(UART_BAUD_RATE);
+  uart_send_str("Running AES test\r\n");
+
+  // Setup AES config
+  aes_cfg_t aes_cfg;
+  aes_cfg.mode = kAesEnc;
+  aes_cfg.key_len = kAes256;
+  aes_cfg.manual_start_trigger = false;
+  aes_cfg.force_data_overwrite = false;
+
+  aes_key_put((const void *)key_32_1, aes_cfg.key_len);
+
+  // Encode
+  aes_cfg.mode = kAesEnc;
+  aes_init(aes_cfg);
+  aes_data_put_wait((const void *)plain_text_1);
+  aes_data_get_wait((void *)buffer);
+
+  // Check against golden cipher text
+  for (int i = 0; i < 16; i++) {
+    if (cipher_text_gold_32_1[i] != buffer[i]) {
+      has_error = true;
+    }
+  }
+
+  // Decode
+  aes_cfg.mode = kAesDec;
+  aes_init(aes_cfg);
+  aes_data_put_wait((const void *)buffer);
+  aes_data_get_wait((void *)buffer);
+
+  // Check against input plain text
+  for (int i = 0; i < 16; i++) {
+    if (plain_text_1[i] != buffer[i]) {
+      has_error = true;
+    }
+  }
+
+  // Clear
+  aes_clear();
+
+  if (has_error) {
+    uart_send_str("FAIL!\r\n");
+    while (1) {
+    }
+  } else {
+    uart_send_str("PASS!\r\n");
+    __asm__ volatile("wfi;");
+  }
+
+  return 0;
+}
diff --git a/sw/device/tests/aes/meson.build b/sw/device/tests/aes/meson.build
new file mode 100644
index 0000000..17438f8
--- /dev/null
+++ b/sw/device/tests/aes/meson.build
@@ -0,0 +1,31 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+aes_test_elf = executable(
+  'aes_test',
+  sources: ['aes_test.c'],
+  name_suffix: 'elf',
+  dependencies: [
+    sw_lib_aes,
+    sw_lib_uart,
+    riscv_crt,
+  ],
+)
+
+aes_test_embedded = custom_target(
+  'aes_test',
+  command: make_embedded_target,
+  input: aes_test_elf,
+  output: make_embedded_target_outputs,
+  build_by_default: true,
+)
+
+custom_target(
+  'aes_test_export',
+  command: export_embedded_target,
+  input: [aes_test_elf, aes_test_embedded],
+  output: 'aes_test',
+  build_always_stale: true,
+  build_by_default: true,
+)
diff --git a/sw/device/tests/aes/srcs.mk b/sw/device/tests/aes/srcs.mk
new file mode 100644
index 0000000..0bca6c2
--- /dev/null
+++ b/sw/device/tests/aes/srcs.mk
@@ -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
+
+SW_NAME       ?= aes_test
+
+# list srcs for each test
+ifeq ($(SW_NAME), aes_test)
+  SW_SRCS     += $(SW_DIR)/aes_test.c
+  SW_SRCS     += $(SW_ROOT_DIR)/lib/aes.c
+endif
diff --git a/sw/device/tests/meson.build b/sw/device/tests/meson.build
index 298c2cf..a44c725 100644
--- a/sw/device/tests/meson.build
+++ b/sw/device/tests/meson.build
@@ -2,6 +2,7 @@
 # Licensed under the Apache License, Version 2.0, see LICENSE for details.
 # SPDX-License-Identifier: Apache-2.0
 
+subdir('aes')
 subdir('flash_ctrl')
 subdir('hmac')
 subdir('rv_timer')