[uart] Revise DIF to address NCO Width

As NCO formula depends on the NCO CSR width, DIF and the document are
revised.

Signed-off-by: Eunchan Kim <eunchan@opentitan.org>
diff --git a/hw/ip/uart/doc/_index.md b/hw/ip/uart/doc/_index.md
index c59a257..1da3d15 100644
--- a/hw/ip/uart/doc/_index.md
+++ b/hw/ip/uart/doc/_index.md
@@ -158,7 +158,13 @@
 set using the equation below, where `f_pclk` is the system clock frequency
 provided to the UART. and `f_baud` is the desired baud rate (in bits per second).
 
-$$ NCO = {{2^{20} * f\_{baud}} \over {f\_{pclk}}} $$
+$$ NCO = 16 \times {{2^{$bits(NCO)} \times f\_{baud}} \over {f\_{pclk}}} $$
+
+The formula above depends on the NCO CSR width.
+The logic creates a x16 tick when the NCO counter overflows.
+So, the computed baud rate from NCO value is below.
+
+$$ f\_{baud} = {{1 \over 16} \times {NCO \over {2^{$bits(NCO)}}} \times {f\_{pclk}}} $$
 
 Note that the NCO result from the above formula can be a fraction but
 the NCO register only accepts an integer value. This will create an
@@ -186,8 +192,8 @@
 can be supported, however if it is too far off an integer then the
 baud rate cannot be supported. This check is needed when
 
-$$ {{baud} < {{40 * f\_{pclk}} \over {2^{20}}}} \qquad OR \qquad
-{{f\_{pclk}} > {{{2^{20}} * {baud}} \over {40}}} $$
+$$ {{baud} < {{40 * f\_{pclk}} \over {2^{$bits(NCO)+4}}}} \qquad OR \qquad
+{{f\_{pclk}} > {{{2^{$bits(NCO)+4}} * {baud}} \over {40}}} $$
 
 Using rounded frequencies and common baud rates, this implies that
 care is needed for 9600 baud and below if the system clock is under
@@ -344,7 +350,7 @@
 #define CLK_FIXED_FREQ_HZ (50ULL * 1000 * 1000)
 
 void uart_init(unsigned int baud) {
-  // nco = 2^20 * baud / fclk
+  // nco = 2^20 * baud / fclk. Assume NCO width is 16bit.
   uint64_t uart_ctrl_nco = ((uint64_t)baud << 20) / CLK_FIXED_FREQ_HZ;
   REG32(UART_CTRL(0)) =
       ((uart_ctrl_nco & UART_CTRL_NCO_MASK) << UART_CTRL_NCO_OFFSET) |
diff --git a/sw/device/lib/dif/dif_uart.c b/sw/device/lib/dif/dif_uart.c
index ddbf49b..fef2bcf 100644
--- a/sw/device/lib/dif/dif_uart.c
+++ b/sw/device/lib/dif/dif_uart.c
@@ -8,6 +8,7 @@
 
 #include "sw/device/lib/base/bitfield.h"
 #include "sw/device/lib/base/mmio.h"
+
 #include "uart_regs.h"  // Generated.
 
 #define UART_INTR_STATE_MASK 0xffffffffu
@@ -105,8 +106,22 @@
     return kDifUartConfigBadConfig;
   }
 
-  // Calculation formula: NCO = 2^20 * baud / fclk.
-  uint64_t nco = ((uint64_t)config->baudrate << 20) / config->clk_freq_hz;
+  // Calculation formula: NCO = 16 * 2^nco_width * baud / fclk.
+
+  // Compute NCO register bit width
+  uint32_t nco_width = 0;
+
+  for (int i = 0; i < 32; i++) {
+    nco_width += (UART_CTRL_NCO_MASK >> i) & 1;
+  }
+
+  _Static_assert((UART_CTRL_NCO_MASK >> 28) == 0,
+                 "NCO bit width exceeds 28 bits.");
+
+  // NCO creates 16x of baudrate. So, in addition to the nco_width,
+  // 2^4 should be multiplied.
+  uint64_t nco =
+      ((uint64_t)config->baudrate << (nco_width + 4)) / config->clk_freq_hz;
   uint32_t nco_masked = nco & UART_CTRL_NCO_MASK;
 
   // Requested baudrate is too high for the given clock frequency.
@@ -513,8 +528,7 @@
   }
 
   bitfield_field32_t field = {
-      .mask = 0x1,
-      .index = loopback ? UART_CTRL_LLPBK : UART_CTRL_SLPBK,
+      .mask = 0x1, .index = loopback ? UART_CTRL_LLPBK : UART_CTRL_SLPBK,
   };
 
   uint32_t reg = mmio_region_read32(uart->base_addr, UART_CTRL_REG_OFFSET);