[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,
});