[mask_rom] Refactor the UART to use the abs_mmio module

1. Refactor the uart driver to use the abs_mmio module.
2. Refactor the uart unittest to use the appropriate mock.
3. Pre-compute the desired UART NCO values in the device implementations
so we can avoid a uint64_t divide in the UART driver.

Signed-off-by: Chris Frantz <cfrantz@google.com>
diff --git a/sw/device/lib/arch/device.h b/sw/device/lib/arch/device.h
index ade03d8..7d70e5b 100644
--- a/sw/device/lib/arch/device.h
+++ b/sw/device/lib/arch/device.h
@@ -76,6 +76,20 @@
 extern const uint64_t kUartBaudrate;
 
 /**
+ * A helper macro to calculate NCO values.
+ * NOTE: the left shift value is dependent on the UART hardware.
+ * The NCO width is 16 bits and the NCO calculates a 16x oversampling clock.
+ */
+#define CALCULATE_UART_NCO(baudrate, peripheral_clock) \
+  (((baudrate) << (16 + 4)) / (peripheral_clock))
+
+/**
+ * The pre-calculated UART NCO value based on the Baudrate and Peripheral clock.
+ */
+extern const uint32_t kUartNCOValue;
+;
+
+/**
  * An address to write to to report test status.
  *
  * If this is zero, there is no address to write to to report test status.
diff --git a/sw/device/lib/arch/device_fpga_nexysvideo.c b/sw/device/lib/arch/device_fpga_nexysvideo.c
index 3f95219..aa3e3b6 100644
--- a/sw/device/lib/arch/device_fpga_nexysvideo.c
+++ b/sw/device/lib/arch/device_fpga_nexysvideo.c
@@ -19,6 +19,9 @@
 
 const uint64_t kUartBaudrate = 115200;
 
+const uint32_t kUartNCOValue =
+    CALCULATE_UART_NCO(kUartBaudrate, kClockFreqPeripheralHz);
+
 const uintptr_t kDeviceTestStatusAddress = 0;
 
 const uintptr_t kDeviceLogBypassUartAddress = 0;
diff --git a/sw/device/lib/arch/device_sim_dv.c b/sw/device/lib/arch/device_sim_dv.c
index 0c51286..c7d0c26 100644
--- a/sw/device/lib/arch/device_sim_dv.c
+++ b/sw/device/lib/arch/device_sim_dv.c
@@ -21,6 +21,9 @@
 
 const uint64_t kUartBaudrate = 1 * 1000 * 1000;  // 1Mbps
 
+const uint32_t kUartNCOValue =
+    CALCULATE_UART_NCO(kUartBaudrate, kClockFreqPeripheralHz);
+
 // Defined in `hw/top_earlgrey/dv/env/chip_env_pkg.sv`
 const uintptr_t kDeviceTestStatusAddress = 0x30000000;
 
diff --git a/sw/device/lib/arch/device_sim_verilator.c b/sw/device/lib/arch/device_sim_verilator.c
index 7cab9ce..53d1807 100644
--- a/sw/device/lib/arch/device_sim_verilator.c
+++ b/sw/device/lib/arch/device_sim_verilator.c
@@ -22,6 +22,9 @@
 
 const uint64_t kUartBaudrate = 7200;
 
+const uint32_t kUartNCOValue =
+    CALCULATE_UART_NCO(kUartBaudrate, kClockFreqPeripheralHz);
+
 // Defined in `hw/top_earlgrey/chip_earlgrey_verilator.core`
 const uintptr_t kDeviceTestStatusAddress = 0x30000000;
 
diff --git a/sw/device/silicon_creator/lib/drivers/meson.build b/sw/device/silicon_creator/lib/drivers/meson.build
index 1e0b4f5..7ea8aee 100644
--- a/sw/device/silicon_creator/lib/drivers/meson.build
+++ b/sw/device/silicon_creator/lib/drivers/meson.build
@@ -57,11 +57,11 @@
     ],
     dependencies: [
       sw_vendor_gtest,
-      sw_lib_base_testing_mock_mmio,
+      sw_silicon_creator_lib_base_mock_abs_mmio,
     ],
     native: true,
-    c_args: ['-DMOCK_MMIO'],
-    cpp_args: ['-DMOCK_MMIO'],
+    c_args: ['-DMOCK_ABS_MMIO'],
+    cpp_args: ['-DMOCK_ABS_MMIO'],
   ),
   suite: 'mask_rom',
 )
diff --git a/sw/device/silicon_creator/lib/drivers/uart.c b/sw/device/silicon_creator/lib/drivers/uart.c
index ab57c5f..4d591eb 100644
--- a/sw/device/silicon_creator/lib/drivers/uart.c
+++ b/sw/device/silicon_creator/lib/drivers/uart.c
@@ -9,95 +9,83 @@
 
 #include "sw/device/lib/arch/device.h"
 #include "sw/device/lib/base/bitfield.h"
-#include "sw/device/lib/base/mmio.h"
+#include "sw/device/silicon_creator/lib/base/abs_mmio.h"
 #include "sw/device/silicon_creator/lib/error.h"
 
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
 #include "uart_regs.h"  // Generated.
 
-#define NCO_WIDTH 16
-_Static_assert((1UL << NCO_WIDTH) - 1 == UART_CTRL_NCO_MASK,
-               "Bad value for NCO_WIDTH");
-
-static void uart_reset(const uart_t *uart) {
-  mmio_region_write32(uart->base_addr, UART_CTRL_REG_OFFSET, 0u);
+static void uart_reset(void) {
+  abs_mmio_write32(TOP_EARLGREY_UART0_BASE_ADDR + UART_CTRL_REG_OFFSET, 0u);
 
   // Write to the relevant bits clears the FIFOs.
   uint32_t reg = 0;
   reg = bitfield_bit32_write(reg, UART_FIFO_CTRL_RXRST_BIT, true);
   reg = bitfield_bit32_write(reg, UART_FIFO_CTRL_TXRST_BIT, true);
-  mmio_region_write32(uart->base_addr, UART_FIFO_CTRL_REG_OFFSET, reg);
+  abs_mmio_write32(TOP_EARLGREY_UART0_BASE_ADDR + UART_FIFO_CTRL_REG_OFFSET,
+                   reg);
 
-  mmio_region_write32(uart->base_addr, UART_OVRD_REG_OFFSET, 0u);
-  mmio_region_write32(uart->base_addr, UART_TIMEOUT_CTRL_REG_OFFSET, 0u);
-  mmio_region_write32(uart->base_addr, UART_INTR_ENABLE_REG_OFFSET, 0u);
-  mmio_region_write32(uart->base_addr, UART_INTR_STATE_REG_OFFSET, UINT32_MAX);
+  abs_mmio_write32(TOP_EARLGREY_UART0_BASE_ADDR + UART_OVRD_REG_OFFSET, 0u);
+  abs_mmio_write32(TOP_EARLGREY_UART0_BASE_ADDR + UART_TIMEOUT_CTRL_REG_OFFSET,
+                   0u);
+  abs_mmio_write32(TOP_EARLGREY_UART0_BASE_ADDR + UART_INTR_ENABLE_REG_OFFSET,
+                   0u);
+  abs_mmio_write32(TOP_EARLGREY_UART0_BASE_ADDR + UART_INTR_STATE_REG_OFFSET,
+                   UINT32_MAX);
 }
 
-rom_error_t uart_init(const uart_t *uart) {
-  if (uart == NULL) {
+rom_error_t uart_init(uint32_t precalculated_nco) {
+  if (precalculated_nco == 0 || precalculated_nco & ~UART_CTRL_NCO_MASK) {
     return kErrorUartInvalidArgument;
   }
 
-  if (uart->baudrate == 0 || uart->clk_freq_hz == 0) {
-    return kErrorUartInvalidArgument;
-  }
-
-  // Calculation formula: NCO = 16 * 2^nco_width * baud / fclk.
-  // NCO creates 16x of baudrate. So, in addition to the nco_width,
-  // 2^4 should be multiplied.
-  uint64_t nco =
-      ((uint64_t)uart->baudrate << (NCO_WIDTH + 4)) / uart->clk_freq_hz;
-  uint32_t nco_masked = nco & UART_CTRL_NCO_MASK;
-
-  // Requested baudrate is too high for the given clock frequency.
-  if (nco != nco_masked) {
-    return kErrorUartBadBaudRate;
-  }
-
   // Must be called before the first write to any of the UART registers.
-  uart_reset(uart);
+  uart_reset();
 
   // Set baudrate, TX, no parity bits.
   uint32_t reg = 0;
-  reg = bitfield_field32_write(reg, UART_CTRL_NCO_FIELD, nco_masked);
+  reg = bitfield_field32_write(reg, UART_CTRL_NCO_FIELD, precalculated_nco);
   reg = bitfield_bit32_write(reg, UART_CTRL_TX_BIT, true);
   reg = bitfield_bit32_write(reg, UART_CTRL_PARITY_EN_BIT, false);
-  mmio_region_write32(uart->base_addr, UART_CTRL_REG_OFFSET, reg);
+  abs_mmio_write32(TOP_EARLGREY_UART0_BASE_ADDR + UART_CTRL_REG_OFFSET, reg);
 
   // Disable interrupts.
-  mmio_region_write32(uart->base_addr, UART_INTR_ENABLE_REG_OFFSET, 0u);
+  abs_mmio_write32(TOP_EARLGREY_UART0_BASE_ADDR + UART_INTR_ENABLE_REG_OFFSET,
+                   0u);
   return kErrorOk;
 }
 
-static bool uart_tx_full(const uart_t *uart) {
-  uint32_t reg = mmio_region_read32(uart->base_addr, UART_STATUS_REG_OFFSET);
+static bool uart_tx_full(void) {
+  uint32_t reg =
+      abs_mmio_read32(TOP_EARLGREY_UART0_BASE_ADDR + UART_STATUS_REG_OFFSET);
   return bitfield_bit32_read(reg, UART_STATUS_TXFULL_BIT);
 }
 
-static bool uart_tx_idle(const uart_t *uart) {
-  uint32_t reg = mmio_region_read32(uart->base_addr, UART_STATUS_REG_OFFSET);
+static bool uart_tx_idle(void) {
+  uint32_t reg =
+      abs_mmio_read32(TOP_EARLGREY_UART0_BASE_ADDR + UART_STATUS_REG_OFFSET);
   return bitfield_bit32_read(reg, UART_STATUS_TXIDLE_BIT);
 }
 
-void uart_putchar(const uart_t *uart, uint8_t byte) {
+void uart_putchar(uint8_t byte) {
   // If the transmit FIFO is full, wait.
-  while (uart_tx_full(uart)) {
+  while (uart_tx_full()) {
   }
   uint32_t reg = bitfield_field32_write(0, UART_WDATA_WDATA_FIELD, byte);
-  mmio_region_write32(uart->base_addr, UART_WDATA_REG_OFFSET, reg);
+  abs_mmio_write32(TOP_EARLGREY_UART0_BASE_ADDR + UART_WDATA_REG_OFFSET, reg);
 
   // If the transmitter is active, wait.
-  while (!uart_tx_idle(uart)) {
+  while (!uart_tx_idle()) {
   }
 }
 
 /**
  * Write `len` bytes to the UART TX FIFO.
  */
-size_t uart_write(const uart_t *uart, const uint8_t *data, size_t len) {
+size_t uart_write(const uint8_t *data, size_t len) {
   size_t total = len;
   while (len) {
-    uart_putchar(uart, *data);
+    uart_putchar(*data);
     data++;
     len--;
   }
@@ -105,5 +93,6 @@
 }
 
 size_t uart_sink(void *uart, const char *data, size_t len) {
-  return uart_write((const uart_t *)uart, (const uint8_t *)data, len);
+  (void)uart;
+  return uart_write((const uint8_t *)data, len);
 }
diff --git a/sw/device/silicon_creator/lib/drivers/uart.h b/sw/device/silicon_creator/lib/drivers/uart.h
index 5160b02..a8d6b85 100644
--- a/sw/device/silicon_creator/lib/drivers/uart.h
+++ b/sw/device/silicon_creator/lib/drivers/uart.h
@@ -16,51 +16,30 @@
 #endif
 
 /**
- * Initialization parameters for UART.
- *
- */
-typedef struct uart {
-  /**
-   * The base address for the UART hardware registers.
-   */
-  mmio_region_t base_addr;
-  /**
-   * The desired baudrate of the UART.
-   */
-  uint32_t baudrate;
-  /**
-   * The peripheral clock frequency (used to compute the UART baudrate divisor).
-   */
-  uint32_t clk_freq_hz;
-} uart_t;
-
-/**
  * Initialize the UART with the request parameters.
  *
- * @param uart Pointer to uart_t with the requested parameters.
+ * @param precalculated_nco NCO value used to set the speed of the UART.
  * @return kErrorOk if successful, else an error code.
  */
-rom_error_t uart_init(const uart_t *uart);
+rom_error_t uart_init(uint32_t precalculated_nco);
 
 /**
  * Write a single byte to the UART.
  *
- * @param uart Pointer to uart_t represting the target UART.
  * @param byte Byte to send.
  */
-void uart_putchar(const uart_t *uart, uint8_t byte);
+void uart_putchar(uint8_t byte);
 
 /**
  * Write a buffer to the UART.
  *
  * Writes the complete buffer to the UART and wait for transmision to complete.
  *
- * @param uart Pointer to uart_t represting the target UART.
  * @param data Pointer to buffer to write.
  * @param len Length of the buffer to write.
  * @return Number of bytes written.
  */
-size_t uart_write(const uart_t *uart, const uint8_t *data, size_t len);
+size_t uart_write(const uint8_t *data, size_t len);
 
 /**
  * Sink a buffer to the UART.
@@ -68,7 +47,7 @@
  * This is a wrapper for uart_write which conforms to the type signature
  * required by the print library.
  *
- * @param uart Pointer to uart_t represting the target UART.
+ * @param uart Pointer a target so satisfy the shape of the sink API.
  * @param data Pointer to buffer to write.
  * @param len Length of the buffer to write.
  * @return Number of bytes written.
diff --git a/sw/device/silicon_creator/lib/drivers/uart_unittest.cc b/sw/device/silicon_creator/lib/drivers/uart_unittest.cc
index 3d35bb4..6e618e9 100644
--- a/sw/device/silicon_creator/lib/drivers/uart_unittest.cc
+++ b/sw/device/silicon_creator/lib/drivers/uart_unittest.cc
@@ -6,14 +6,13 @@
 
 #include "gtest/gtest.h"
 #include "sw/device/lib/base/mmio.h"
-#include "sw/device/lib/base/testing/mock_mmio.h"
+#include "sw/device/silicon_creator/lib/base/mock_abs_mmio.h"
 
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
 #include "uart_regs.h"  // Generated.
 
 namespace uart_unittest {
 namespace {
-using mock_mmio::MmioTest;
-using mock_mmio::MockDevice;
 using testing::Each;
 using testing::Eq;
 using testing::Test;
@@ -24,46 +23,44 @@
     'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
 };
 
-class UartTest : public Test, public MmioTest {
+class UartTest : public mask_rom_test::MaskRomTest {
  protected:
-  void ExpectDeviceReset() {
-    EXPECT_WRITE32(UART_CTRL_REG_OFFSET, 0);
-    EXPECT_WRITE32(UART_FIFO_CTRL_REG_OFFSET,
-                   {
-                       {UART_FIFO_CTRL_RXRST_BIT, true},
-                       {UART_FIFO_CTRL_TXRST_BIT, true},
-                   });
-    EXPECT_WRITE32(UART_OVRD_REG_OFFSET, 0);
-    EXPECT_WRITE32(UART_TIMEOUT_CTRL_REG_OFFSET, 0);
-    EXPECT_WRITE32(UART_INTR_ENABLE_REG_OFFSET, 0);
-    EXPECT_WRITE32(UART_INTR_STATE_REG_OFFSET,
-                   std::numeric_limits<uint32_t>::max());
-  }
+  uint32_t base_ = TOP_EARLGREY_UART0_BASE_ADDR;
+  mask_rom_test::MockAbsMmio mmio_;
 
-  uart_t uart_ = {
-      .base_addr = dev().region(),
-      .baudrate = 1,
-      .clk_freq_hz = 1048576,
-  };
+  void ExpectDeviceReset() {
+    EXPECT_ABS_WRITE32(mmio_, base_ + UART_CTRL_REG_OFFSET, 0);
+    EXPECT_ABS_WRITE32(mmio_, base_ + UART_FIFO_CTRL_REG_OFFSET,
+                       {
+                           {UART_FIFO_CTRL_RXRST_BIT, true},
+                           {UART_FIFO_CTRL_TXRST_BIT, true},
+                       });
+    EXPECT_ABS_WRITE32(mmio_, base_ + UART_OVRD_REG_OFFSET, 0);
+    EXPECT_ABS_WRITE32(mmio_, base_ + UART_TIMEOUT_CTRL_REG_OFFSET, 0);
+    EXPECT_ABS_WRITE32(mmio_, base_ + UART_INTR_ENABLE_REG_OFFSET, 0);
+    EXPECT_ABS_WRITE32(mmio_, base_ + UART_INTR_STATE_REG_OFFSET,
+                       std::numeric_limits<uint32_t>::max());
+  }
 };
 
 class InitTest : public UartTest {};
 
-TEST_F(InitTest, NullArgs) {
-  // FIXME: add tests with `uart_` misconfigured.
-  EXPECT_EQ(uart_init(nullptr), kErrorUartInvalidArgument);
-}
-
 TEST_F(InitTest, Initialize) {
   ExpectDeviceReset();
 
-  EXPECT_WRITE32(UART_CTRL_REG_OFFSET, {
-                                           {UART_CTRL_TX_BIT, true},
-                                           {UART_CTRL_NCO_OFFSET, 1},
-                                       });
-  EXPECT_WRITE32(UART_INTR_ENABLE_REG_OFFSET, 0);
+  EXPECT_ABS_WRITE32(mmio_, base_ + UART_CTRL_REG_OFFSET,
+                     {
+                         {UART_CTRL_TX_BIT, true},
+                         {UART_CTRL_NCO_OFFSET, 1},
+                     });
+  EXPECT_ABS_WRITE32(mmio_, base_ + UART_INTR_ENABLE_REG_OFFSET, 0);
 
-  EXPECT_EQ(uart_init(&uart_), kErrorOk);
+  EXPECT_EQ(uart_init(1), kErrorOk);
+}
+
+TEST_F(InitTest, InvalidInit) {
+  EXPECT_EQ(uart_init(0), kErrorUartInvalidArgument);
+  EXPECT_EQ(uart_init(65536), kErrorUartInvalidArgument);
 }
 
 class BytesSendTest : public UartTest {
@@ -78,9 +75,11 @@
     ASSERT_LE(num_elements, kBytesArray.size());
     for (int i = 0; i < num_elements; ++i) {
       uint32_t value = static_cast<uint32_t>(kBytesArray[i]);
-      EXPECT_READ32(UART_STATUS_REG_OFFSET, {{UART_STATUS_TXFULL_BIT, false}});
-      EXPECT_WRITE32(UART_WDATA_REG_OFFSET, value);
-      EXPECT_READ32(UART_STATUS_REG_OFFSET, {{UART_STATUS_TXIDLE_BIT, true}});
+      EXPECT_ABS_READ32(mmio_, base_ + UART_STATUS_REG_OFFSET,
+                        {{UART_STATUS_TXFULL_BIT, false}});
+      EXPECT_ABS_WRITE32(mmio_, base_ + UART_WDATA_REG_OFFSET, value);
+      EXPECT_ABS_READ32(mmio_, base_ + UART_STATUS_REG_OFFSET,
+                        {{UART_STATUS_TXIDLE_BIT, true}});
     }
   }
 };
@@ -88,23 +87,27 @@
 TEST_F(BytesSendTest, SendBuffer) {
   ExpectSendBytes();
   // Calling uart_write implicitly tests uart_putchar.
-  EXPECT_EQ(uart_write(&uart_, kBytesArray.data(), kBytesArray.size()),
+  EXPECT_EQ(uart_write(kBytesArray.data(), kBytesArray.size()),
             kBytesArray.size());
 }
 
 TEST_F(BytesSendTest, SendByteBusy) {
   // FIFO full for one cycle, then empty.
-  EXPECT_READ32(UART_STATUS_REG_OFFSET, {{UART_STATUS_TXFULL_BIT, true}});
-  EXPECT_READ32(UART_STATUS_REG_OFFSET, {{UART_STATUS_TXFULL_BIT, false}});
+  EXPECT_ABS_READ32(mmio_, base_ + UART_STATUS_REG_OFFSET,
+                    {{UART_STATUS_TXFULL_BIT, true}});
+  EXPECT_ABS_READ32(mmio_, base_ + UART_STATUS_REG_OFFSET,
+                    {{UART_STATUS_TXFULL_BIT, false}});
 
   // The value sent is 'X'
-  EXPECT_WRITE32(UART_WDATA_REG_OFFSET, 'X');
+  EXPECT_ABS_WRITE32(mmio_, base_ + UART_WDATA_REG_OFFSET, 'X');
 
   // Transmitter busy for one cycle, then idle.
-  EXPECT_READ32(UART_STATUS_REG_OFFSET, {{UART_STATUS_TXIDLE_BIT, false}});
-  EXPECT_READ32(UART_STATUS_REG_OFFSET, {{UART_STATUS_TXIDLE_BIT, true}});
+  EXPECT_ABS_READ32(mmio_, base_ + UART_STATUS_REG_OFFSET,
+                    {{UART_STATUS_TXIDLE_BIT, false}});
+  EXPECT_ABS_READ32(mmio_, base_ + UART_STATUS_REG_OFFSET,
+                    {{UART_STATUS_TXIDLE_BIT, true}});
 
-  uart_putchar(&uart_, 'X');
+  uart_putchar('X');
 }
 
 }  // namespace
diff --git a/sw/device/silicon_creator/mask_rom/mask_rom.c b/sw/device/silicon_creator/mask_rom/mask_rom.c
index 851c90c..0638359 100644
--- a/sw/device/silicon_creator/mask_rom/mask_rom.c
+++ b/sw/device/silicon_creator/mask_rom/mask_rom.c
@@ -35,8 +35,6 @@
 void mask_rom_exception_handler(void) { wait_for_interrupt(); }
 void mask_rom_nmi_handler(void) { wait_for_interrupt(); }
 
-uart_t uart;
-
 // FIXME: Temporary workaround to run functional test of SHA256.
 static int verify_rom_ext_identifier(rom_ext_manifest_t rom_ext) {
   uint32_t rom_ext_identifier = rom_ext_get_identifier(rom_ext);
@@ -62,13 +60,9 @@
   pinmux_init();
 
   // Configure UART0 as stdout.
-  // TODO(lowrisc/opentitan#6283): Move to constant driver handles.
-  uart.base_addr = mmio_region_from_addr(TOP_EARLGREY_UART0_BASE_ADDR);
-  uart.baudrate = kUartBaudrate;
-  uart.clk_freq_hz = kClockFreqPeripheralHz;
-  uart_init(&uart);
+  uart_init(kUartNCOValue);
   base_set_stdout((buffer_sink_t){
-      .data = &uart,
+      .data = NULL,
       .sink = uart_sink,
   });