[sw/silicon_creator] Add flash_ctrl unit tests

Signed-off-by: Jon Flatley <jflat@google.com>
diff --git a/sw/device/silicon_creator/lib/drivers/flash_ctrl_unittest.cc b/sw/device/silicon_creator/lib/drivers/flash_ctrl_unittest.cc
new file mode 100644
index 0000000..42f14db
--- /dev/null
+++ b/sw/device/silicon_creator/lib/drivers/flash_ctrl_unittest.cc
@@ -0,0 +1,146 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "sw/device/silicon_creator/lib/drivers/flash_ctrl.h"
+
+#include "gtest/gtest.h"
+#include "sw/device/lib/base/testing/mock_mmio_test_utils.h"
+#include "sw/device/silicon_creator/lib/base/mock_abs_mmio.h"
+#include "sw/device/silicon_creator/lib/error.h"
+
+#include "flash_ctrl_regs.h"  // Generated.
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+
+namespace flash_ctrl_unittest {
+namespace {
+using ::testing::ElementsAre;
+
+class FlashCtrlTest : public mask_rom_test::MaskRomTest {
+ protected:
+  uint32_t base_ = TOP_EARLGREY_FLASH_CTRL_CORE_BASE_ADDR;
+  mask_rom_test::MockAbsMmio mmio_;
+};
+
+class InitTest : public FlashCtrlTest {};
+
+TEST_F(InitTest, Initialize) {
+  EXPECT_ABS_WRITE32(mmio_, base_ + FLASH_CTRL_INIT_REG_OFFSET,
+                     {{FLASH_CTRL_INIT_VAL_BIT, true}});
+
+  flash_ctrl_init();
+}
+
+class StatusCheckTest : public FlashCtrlTest {};
+
+TEST_F(StatusCheckTest, InvalidArgument) {
+  EXPECT_EQ(flash_ctrl_status_get(NULL), kErrorFlashCtrlInvalidArgument);
+}
+
+TEST_F(StatusCheckTest, DefaultStatus) {
+  EXPECT_ABS_READ32(mmio_, base_ + FLASH_CTRL_OP_STATUS_REG_OFFSET,
+                    {
+                        {FLASH_CTRL_OP_STATUS_DONE_BIT, false},
+                        {FLASH_CTRL_OP_STATUS_ERR_BIT, false},
+                    });
+  EXPECT_ABS_READ32(mmio_, base_ + FLASH_CTRL_STATUS_REG_OFFSET,
+                    {
+                        {FLASH_CTRL_STATUS_RD_FULL_BIT, false},
+                        {FLASH_CTRL_STATUS_RD_EMPTY_BIT, false},
+                        {FLASH_CTRL_STATUS_INIT_WIP_BIT, false},
+                    });
+
+  flash_ctrl_status_t status;
+  EXPECT_EQ(flash_ctrl_status_get(&status), kErrorOk);
+
+  EXPECT_EQ(status.done, false);
+  EXPECT_EQ(status.err, false);
+  EXPECT_EQ(status.init_wip, false);
+  EXPECT_EQ(status.rd_empty, false);
+  EXPECT_EQ(status.rd_full, false);
+}
+
+TEST_F(StatusCheckTest, AllSetStatus) {
+  EXPECT_ABS_READ32(mmio_, base_ + FLASH_CTRL_OP_STATUS_REG_OFFSET,
+                    {
+                        {FLASH_CTRL_OP_STATUS_DONE_BIT, true},
+                        {FLASH_CTRL_OP_STATUS_ERR_BIT, true},
+                    });
+  EXPECT_ABS_READ32(mmio_, base_ + FLASH_CTRL_STATUS_REG_OFFSET,
+                    {
+                        {FLASH_CTRL_STATUS_RD_FULL_BIT, true},
+                        {FLASH_CTRL_STATUS_RD_EMPTY_BIT, true},
+                        {FLASH_CTRL_STATUS_INIT_WIP_BIT, true},
+                    });
+
+  flash_ctrl_status_t status;
+  EXPECT_EQ(flash_ctrl_status_get(&status), kErrorOk);
+
+  EXPECT_EQ(status.done, true);
+  EXPECT_EQ(status.err, true);
+  EXPECT_EQ(status.init_wip, true);
+  EXPECT_EQ(status.rd_empty, true);
+  EXPECT_EQ(status.rd_full, true);
+}
+
+class ReadStartTest : public FlashCtrlTest {
+ protected:
+  void ExpectCheckBusy(bool busy) {
+    EXPECT_ABS_READ32(mmio_, base_ + FLASH_CTRL_CTRL_REGWEN_REG_OFFSET,
+                      {{FLASH_CTRL_CTRL_REGWEN_EN_BIT, !busy}});
+  }
+  void ExpectReadStart(uint8_t part_sel, uint8_t info_sel) {
+    EXPECT_ABS_WRITE32(mmio_, base_ + FLASH_CTRL_ADDR_REG_OFFSET, 0x01234567);
+    EXPECT_ABS_WRITE32(
+        mmio_, base_ + FLASH_CTRL_CONTROL_REG_OFFSET,
+        {
+            {FLASH_CTRL_CONTROL_OP_OFFSET, FLASH_CTRL_CONTROL_OP_VALUE_READ},
+            {FLASH_CTRL_CONTROL_PARTITION_SEL_BIT, part_sel},
+            {FLASH_CTRL_CONTROL_INFO_SEL_OFFSET, info_sel},
+            {FLASH_CTRL_CONTROL_NUM_OFFSET, 42},
+            {FLASH_CTRL_CONTROL_START_BIT, 1},
+        });
+  }
+};
+
+TEST_F(ReadStartTest, Busy) {
+  ExpectCheckBusy(true);
+  EXPECT_EQ(flash_ctrl_read_start(0, 0, kFlashCtrlRegionData),
+            kErrorFlashCtrlBusy);
+}
+
+TEST_F(ReadStartTest, ReadData) {
+  ExpectCheckBusy(false);
+  ExpectReadStart(0, 0);
+  EXPECT_EQ(flash_ctrl_read_start(0x01234567, 42, kFlashCtrlRegionData),
+            kErrorOk);
+}
+
+TEST_F(ReadStartTest, ReadInfo) {
+  ExpectCheckBusy(false);
+  ExpectReadStart(1, 2);
+  EXPECT_EQ(flash_ctrl_read_start(0x01234567, 42, kFlashCtrlRegionInfo2),
+            kErrorOk);
+}
+
+class ReadTest : public FlashCtrlTest {
+ protected:
+  const std::vector<uint32_t> words_ = {0x12345678, 0x90ABCDEF, 0x0F1E2D3C,
+                                        0x4B5A6978};
+  void ExpectReadData(const std::vector<uint32_t> &data) {
+    for (auto val : data) {
+      EXPECT_ABS_READ32(mmio_, base_ + FLASH_CTRL_RD_FIFO_REG_OFFSET, val);
+    }
+  }
+};
+
+TEST_F(ReadTest, ReadAll) {
+  ExpectReadData(words_);
+  std::vector<uint32_t> words_out(words_.size());
+  EXPECT_EQ(flash_ctrl_fifo_read(words_.size(), &words_out.front()),
+            words_.size());
+  EXPECT_EQ(words_out, words_);
+}
+
+}  // namespace
+}  // namespace flash_ctrl_unittest
diff --git a/sw/device/silicon_creator/lib/drivers/meson.build b/sw/device/silicon_creator/lib/drivers/meson.build
index c6c209a..7a05000 100644
--- a/sw/device/silicon_creator/lib/drivers/meson.build
+++ b/sw/device/silicon_creator/lib/drivers/meson.build
@@ -415,3 +415,21 @@
     ],
   ),
 )
+
+test('sw_silicon_creator_lib_driver_flash_ctrl_unittest', executable(
+    'sw_silicon_creator_lib_driver_flash_ctrl_unittest',
+    sources: [
+      'flash_ctrl_unittest.cc',
+      hw_ip_flash_ctrl_reg_h,
+      'flash_ctrl.c',
+    ],
+    dependencies: [
+      sw_vendor_gtest,
+      sw_silicon_creator_lib_base_mock_abs_mmio,
+    ],
+    native: true,
+    c_args: ['-DMOCK_ABS_MMIO'],
+    cpp_args: ['-DMOCK_ABS_MMIO'],
+  ),
+  suite: 'mask_rom',
+)