[dv/top] Test uart multiple instances

Pick a uart to test, backdoor modify memory to let SW test the
uart we pick.
Update pinmux to enable the other 3 uarts
Remove uart mon connection with scb, which is unused

Signed-off-by: Weicai Yang <weicai@google.com>
diff --git a/hw/top_earlgrey/data/chip_testplan.hjson b/hw/top_earlgrey/data/chip_testplan.hjson
index be2a800..a2ebdd1 100644
--- a/hw/top_earlgrey/data/chip_testplan.hjson
+++ b/hw/top_earlgrey/data/chip_testplan.hjson
@@ -45,7 +45,8 @@
             on all UART ports across the instances.
             '''
       milestone: V1
-      tests: ["chip_sw_uart_tx_rx"]
+      tests: ["chip_sw_uart_tx_rx", "chip_sw_uart_tx_rx_idx1", "chip_sw_uart_tx_rx_idx2",
+              "chip_sw_uart_tx_rx_idx3"]
     }
     {
       name: chip_uart_tx_rx_alt_clk_freq
diff --git a/hw/top_earlgrey/dv/chip_sim_cfg.hjson b/hw/top_earlgrey/dv/chip_sim_cfg.hjson
index 5ecdcc2..4b5e272 100644
--- a/hw/top_earlgrey/dv/chip_sim_cfg.hjson
+++ b/hw/top_earlgrey/dv/chip_sim_cfg.hjson
@@ -166,6 +166,31 @@
       uvm_test_seq: chip_sw_uart_tx_rx_vseq
       sw_images: ["sw/device/tests/uart_tx_rx_test:1"]
       en_run_modes: ["sw_test_mode"]
+      reseed: 10
+    }
+    {
+      name: chip_sw_uart_tx_rx_idx1
+      uvm_test_seq: chip_sw_uart_tx_rx_vseq
+      sw_images: ["sw/device/tests/uart_tx_rx_test:1"]
+      en_run_modes: ["sw_test_mode"]
+      run_opts: ["+uart_idx=1"]
+      reseed: 10
+    }
+    {
+      name: chip_sw_uart_tx_rx_idx2
+      uvm_test_seq: chip_sw_uart_tx_rx_vseq
+      sw_images: ["sw/device/tests/uart_tx_rx_test:1"]
+      en_run_modes: ["sw_test_mode"]
+      run_opts: ["+uart_idx=2"]
+      reseed: 10
+    }
+    {
+      name: chip_sw_uart_tx_rx_idx3
+      uvm_test_seq: chip_sw_uart_tx_rx_vseq
+      sw_images: ["sw/device/tests/uart_tx_rx_test:1"]
+      en_run_modes: ["sw_test_mode"]
+      run_opts: ["+uart_idx=3"]
+      reseed: 10
     }
     {
       name: chip_sw_uart_tx_rx_bootstrap
diff --git a/hw/top_earlgrey/dv/env/chip_env.sv b/hw/top_earlgrey/dv/env/chip_env.sv
index 33b01f4..d9f2043 100644
--- a/hw/top_earlgrey/dv/env/chip_env.sv
+++ b/hw/top_earlgrey/dv/env/chip_env.sv
@@ -10,7 +10,7 @@
   );
   `uvm_component_utils(chip_env)
 
-  uart_agent          m_uart_agent;
+  uart_agent          m_uart_agents[NUM_UARTS];
   jtag_riscv_agent    m_jtag_riscv_agent;
   spi_agent           m_spi_agent;
 
@@ -62,8 +62,11 @@
     end
 
     // create components
-    m_uart_agent = uart_agent::type_id::create("m_uart_agent", this);
-    uvm_config_db#(uart_agent_cfg)::set(this, "m_uart_agent*", "cfg", cfg.m_uart_agent_cfg);
+    foreach (m_uart_agents[i]) begin
+      m_uart_agents[i] = uart_agent::type_id::create($sformatf("m_uart_agent%0d", i), this);
+      uvm_config_db#(uart_agent_cfg)::set(this, $sformatf("m_uart_agent%0d*", i), "cfg",
+                                          cfg.m_uart_agent_cfgs[i]);
+    end
 
     m_jtag_riscv_agent = jtag_riscv_agent::type_id::create("m_jtag_riscv_agent", this);
     uvm_config_db#(jtag_riscv_agent_cfg)::set(this, "m_jtag_riscv_agent*", "cfg",
@@ -81,12 +84,12 @@
   function void connect_phase(uvm_phase phase);
     super.connect_phase(phase);
     if (cfg.en_scb) begin
-      m_uart_agent.monitor.tx_analysis_port.connect(scoreboard.uart_tx_fifo.analysis_export);
-      m_uart_agent.monitor.rx_analysis_port.connect(scoreboard.uart_rx_fifo.analysis_export);
       m_jtag_riscv_agent.monitor.analysis_port.connect(scoreboard.jtag_fifo.analysis_export);
     end
-    if (cfg.is_active && cfg.m_uart_agent_cfg.is_active) begin
-      virtual_sequencer.uart_sequencer_h = m_uart_agent.sequencer;
+    foreach (m_uart_agents[i]) begin
+      if (cfg.is_active && cfg.m_uart_agent_cfgs[i].is_active) begin
+        virtual_sequencer.uart_sequencer_hs[i] = m_uart_agents[i].sequencer;
+      end
     end
     if (cfg.is_active && cfg.m_jtag_riscv_agent_cfg.is_active) begin
       virtual_sequencer.jtag_sequencer_h = m_jtag_riscv_agent.sequencer;
@@ -96,7 +99,10 @@
     end
 
     // Connect the DUT's UART TX TLM port to the sequencer.
-    m_uart_agent.monitor.tx_analysis_port.connect(virtual_sequencer.uart_tx_fifo.analysis_export);
+    foreach (m_uart_agents[i]) begin
+      m_uart_agents[i].monitor.tx_analysis_port.connect(
+          virtual_sequencer.uart_tx_fifos[i].analysis_export);
+    end
   endfunction
 
   virtual function void end_of_elaboration_phase(uvm_phase phase);
diff --git a/hw/top_earlgrey/dv/env/chip_env_cfg.sv b/hw/top_earlgrey/dv/env/chip_env_cfg.sv
index 89c5e33..10ba957 100644
--- a/hw/top_earlgrey/dv/env/chip_env_cfg.sv
+++ b/hw/top_earlgrey/dv/env/chip_env_cfg.sv
@@ -58,13 +58,12 @@
   sw_test_status_vif  sw_test_status_vif;
 
   // ext component cfgs
-  rand uart_agent_cfg       m_uart_agent_cfg;
+  rand uart_agent_cfg       m_uart_agent_cfgs[NUM_UARTS];
   rand jtag_riscv_agent_cfg m_jtag_riscv_agent_cfg;
   rand spi_agent_cfg        m_spi_agent_cfg;
 
   `uvm_object_utils_begin(chip_env_cfg)
     `uvm_field_int   (stub_cpu,               UVM_DEFAULT)
-    `uvm_field_object(m_uart_agent_cfg,       UVM_DEFAULT)
     `uvm_field_object(m_jtag_riscv_agent_cfg, UVM_DEFAULT)
     `uvm_field_object(m_spi_agent_cfg,        UVM_DEFAULT)
   `uvm_object_utils_end
@@ -89,7 +88,9 @@
     m_tl_agent_cfg.valid_a_source_width = 6;
 
     // create uart agent config obj
-    m_uart_agent_cfg = uart_agent_cfg::type_id::create("m_uart_agent_cfg");
+    foreach (m_uart_agent_cfgs[i]) begin
+      m_uart_agent_cfgs[i] = uart_agent_cfg::type_id::create($sformatf("m_uart_agent_cfg%0d", i));
+    end
 
     // create jtag agent config obj
     m_jtag_riscv_agent_cfg = jtag_riscv_agent_cfg::type_id::create("m_jtag_riscv_agent_cfg");
diff --git a/hw/top_earlgrey/dv/env/chip_env_pkg.sv b/hw/top_earlgrey/dv/env/chip_env_pkg.sv
index 72b7c6b..427ed49 100644
--- a/hw/top_earlgrey/dv/env/chip_env_pkg.sv
+++ b/hw/top_earlgrey/dv/env/chip_env_pkg.sv
@@ -34,6 +34,7 @@
 
   // local parameters and types
   parameter uint NUM_GPIOS = 16;
+  parameter uint NUM_UARTS = 4;
   // Buffer is half of SPI_DEVICE Dual Port SRAM
   parameter uint SPI_FRAME_BYTE_SIZE = spi_device_reg_pkg::SPI_DEVICE_BUFFER_SIZE/2;
 
diff --git a/hw/top_earlgrey/dv/env/chip_scoreboard.sv b/hw/top_earlgrey/dv/env/chip_scoreboard.sv
index c3078ef..22a2df8 100644
--- a/hw/top_earlgrey/dv/env/chip_scoreboard.sv
+++ b/hw/top_earlgrey/dv/env/chip_scoreboard.sv
@@ -12,8 +12,6 @@
   // local variables
 
   // TLM agent fifos
-  uvm_tlm_analysis_fifo #(uart_item)       uart_tx_fifo;
-  uvm_tlm_analysis_fifo #(uart_item)       uart_rx_fifo;
   uvm_tlm_analysis_fifo #(jtag_riscv_item) jtag_fifo;
 
   // local queues to hold incoming packets pending comparison
@@ -25,8 +23,6 @@
 
   function void build_phase(uvm_phase phase);
     super.build_phase(phase);
-    uart_tx_fifo = new("uart_tx_fifo", this);
-    uart_rx_fifo = new("uart_rx_fifo", this);
     jtag_fifo = new("jtag_fifo", this);
   endfunction
 
diff --git a/hw/top_earlgrey/dv/env/chip_virtual_sequencer.sv b/hw/top_earlgrey/dv/env/chip_virtual_sequencer.sv
index cf5e17e..9c4bfad 100644
--- a/hw/top_earlgrey/dv/env/chip_virtual_sequencer.sv
+++ b/hw/top_earlgrey/dv/env/chip_virtual_sequencer.sv
@@ -8,18 +8,18 @@
   );
   `uvm_component_utils(chip_virtual_sequencer)
 
-  uart_sequencer       uart_sequencer_h;
+  uart_sequencer       uart_sequencer_hs[NUM_UARTS];
   jtag_riscv_sequencer jtag_sequencer_h;
   spi_sequencer        spi_sequencer_h;
 
   // Grab packets from UART TX port for in-sequence checking.
-  uvm_tlm_analysis_fifo #(uart_item) uart_tx_fifo;
+  uvm_tlm_analysis_fifo #(uart_item) uart_tx_fifos[NUM_UARTS];
 
   `uvm_component_new
 
   function void build_phase(uvm_phase phase);
     super.build_phase(phase);
-    uart_tx_fifo = new("uart_tx_fifo", this);
+    foreach (uart_tx_fifos[i]) uart_tx_fifos[i] = new($sformatf("uart_tx_fifo%0d", i), this);
   endfunction
 
 endclass
diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_base_vseq.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_base_vseq.sv
index 782b83c..765085c 100644
--- a/hw/top_earlgrey/dv/env/seq_lib/chip_base_vseq.sv
+++ b/hw/top_earlgrey/dv/env/seq_lib/chip_base_vseq.sv
@@ -80,10 +80,10 @@
   endtask
 
   // Grab packets sent by the DUT over the UART TX port.
-  virtual task get_uart_tx_items();
+  virtual task get_uart_tx_items(int uart_idx = 0);
     uart_item item;
     forever begin
-      p_sequencer.uart_tx_fifo.get(item);
+      p_sequencer.uart_tx_fifos[uart_idx].get(item);
       `uvm_info(`gfn, $sformatf("Received UART data over TX:\n%0h", item.data), UVM_HIGH)
       uart_tx_data_q.push_back(item.data);
     end
diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_stub_cpu_base_vseq.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_stub_cpu_base_vseq.sv
index 8945824..3997ced 100644
--- a/hw/top_earlgrey/dv/env/seq_lib/chip_stub_cpu_base_vseq.sv
+++ b/hw/top_earlgrey/dv/env/seq_lib/chip_stub_cpu_base_vseq.sv
@@ -19,9 +19,10 @@
     // In top-level uart RX pin may be selected in pinmux. CSR tests may randomly enable line
     // loopback, which will connect TX with RX. If RX isn't enabled in pinmux, it will be 0.
     // moniter will start to check the TX data when it changes from 1 to 0. But the length of 0 may
-    // be not right in CSR test.
-    // In block-level, we always ties RX to 1 (idle) in CSR test. No need to disable the monitor
-    cfg.m_uart_agent_cfg.en_tx_monitor = 0;
+    // be not right in CSR test, which causes a protocal error on TX
+    // In block-level, we always tie RX to 1 (idle) in CSR test so that we don't need to disable TX
+    // monitor in block-level
+    foreach (cfg.m_uart_agent_cfgs[i]) cfg.m_uart_agent_cfgs[i].en_tx_monitor = 0;
   endtask
 
   task post_start();
diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_sw_base_vseq.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_sw_base_vseq.sv
index 8707f9b..4206cb9 100644
--- a/hw/top_earlgrey/dv/env/seq_lib/chip_sw_base_vseq.sv
+++ b/hw/top_earlgrey/dv/env/seq_lib/chip_sw_base_vseq.sv
@@ -17,8 +17,10 @@
   // Backdoor load the sw test image, setup UART, logger and test status interfaces.
   virtual task cpu_init();
     // TODO: Fixing this for now - need to find a way to pass this on to the SW test.
-    cfg.m_uart_agent_cfg.set_parity(1'b0, 1'b0);
-    cfg.m_uart_agent_cfg.set_baud_rate(cfg.uart_baud_rate);
+    foreach (cfg.m_uart_agent_cfgs[i]) begin
+      cfg.m_uart_agent_cfgs[i].set_parity(1'b0, 1'b0);
+      cfg.m_uart_agent_cfgs[i].set_baud_rate(cfg.uart_baud_rate);
+    end
 
     // initialize the sw logger interface
     foreach (cfg.sw_images[i]) begin
diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_sw_uart_tx_rx_vseq.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_sw_uart_tx_rx_vseq.sv
index 188f826..dc4e08d 100644
--- a/hw/top_earlgrey/dv/env/seq_lib/chip_sw_uart_tx_rx_vseq.sv
+++ b/hw/top_earlgrey/dv/env/seq_lib/chip_sw_uart_tx_rx_vseq.sv
@@ -22,17 +22,29 @@
     uart_rx_data.size() == UART_DATASET_SIZE;
   }
 
+  byte uart_idx = 0;
+
+  task pre_start();
+    void'($value$plusargs("uart_idx=%0d", uart_idx));
+    `DV_CHECK_LT(uart_idx, NUM_UARTS)
+    super.pre_start();
+  endtask
+
   virtual task cpu_init();
+    // sw_symbol_backdoor_overwrite takes an array as the input
+    byte uart_idx_data[] = {uart_idx};
+
     super.cpu_init();
-    sw_symbol_backdoor_overwrite("uart_tx_data", exp_uart_tx_data);
-    sw_symbol_backdoor_overwrite("exp_uart_rx_data", uart_rx_data);
+    sw_symbol_backdoor_overwrite("kUartTxData", exp_uart_tx_data);
+    sw_symbol_backdoor_overwrite("kExpUartRxData", uart_rx_data);
+    sw_symbol_backdoor_overwrite("kUartIdx", uart_idx_data);
   endtask
 
   virtual task body();
     super.body();
 
     // Spawn off a thread to retrieve UART TX items.
-    fork get_uart_tx_items(); join_none
+    fork get_uart_tx_items(uart_idx); join_none
 
     // Wait until we receive at least 1 byte from the DUT (SW test).
     wait(uart_tx_data_q.size() > 0);
@@ -56,7 +68,7 @@
   // Send data over RX.
   virtual task send_uart_rx_data(int size = -1, bit random = 0);
     uart_default_seq send_rx_seq;
-    `uvm_create_on(send_rx_seq, p_sequencer.uart_sequencer_h);
+    `uvm_create_on(send_rx_seq, p_sequencer.uart_sequencer_hs[uart_idx]);
     if (size == -1) size = uart_rx_data.size();
     for (int i = 0; i < size; i++) begin
       byte rx_data = random ? $urandom : uart_rx_data[i];
diff --git a/hw/top_earlgrey/dv/tb/tb.sv b/hw/top_earlgrey/dv/tb/tb.sv
index ce96884..9e99edb 100644
--- a/hw/top_earlgrey/dv/tb/tb.sv
+++ b/hw/top_earlgrey/dv/tb/tb.sv
@@ -40,7 +40,7 @@
 
   wire usb_dp0, usb_dn0, usb_sense0, usb_dppullup0, usb_dnpullup0;
 
-  wire uart_rx, uart_tx;
+  wire uart_rx[NUM_UARTS], uart_tx[NUM_UARTS];
 
   bit stub_cpu;
   bit en_sim_sram = 1'b1;
@@ -62,7 +62,7 @@
   pins_if #(1) rst_n_mon_if(.pins(cpu_rst_n));
   spi_if spi_if(.rst_n);
   tl_if cpu_d_tl_if(.clk(cpu_clk), .rst_n(cpu_rst_n));
-  uart_if uart_if();
+  uart_if uart_if[NUM_UARTS-1:0]();
   jtag_if jtag_if();
 
   // TODO: Replace with correct interfaces once
@@ -129,8 +129,8 @@
     .IOC7(tie_off[7]),     // MIO 29
     .IOC8(tap_straps[0]),  // MIO 30
     .IOC9(tie_off[8]),     // MIO 31
-    .IOC10(uart_rx),       // MIO 32
-    .IOC11(uart_tx),       // MIO 33
+    .IOC10(uart_rx[0]),    // MIO 32
+    .IOC11(uart_tx[0]),    // MIO 33
     .IOC12(tie_off[9]),    // MIO 34
     // Bank R (VCC domain)
     .IOR0(jtag_tms),       // MIO 35
@@ -138,14 +138,14 @@
     .IOR2(jtag_tdi),       // MIO 37
     .IOR3(jtag_tck),       // MIO 38
     .IOR4(jtag_trst_n),    // MIO 39
-    .IOR5(tie_off[10]),    // MIO 40
-    .IOR6(tie_off[11]),    // MIO 41
-    .IOR7(tie_off[12]),    // MIO 42
-    .IOR8(tie_off[13]),    // MIO 43
-    .IOR9(tie_off[14]),    // MIO 44
-    .IOR10(tie_off[15]),   // MIO 45
-    .IOR11(tie_off[16]),   // MIO 46
-    .IOR12(tie_off[17]),   // MIO 47
+    .IOR5(uart_rx[1]),     // MIO 40
+    .IOR6(uart_tx[1]),     // MIO 41
+    .IOR7(uart_rx[2]),     // MIO 42
+    .IOR8(tie_off[13]),    // MIO 43, Dedicated sysrst_ctrl output (ec_rst_l)
+    .IOR9(tie_off[14]),    // MIO 44, Dedicated sysrst_ctrl output (pwrb_out)
+    .IOR10(uart_tx[2]),    // MIO 45
+    .IOR11(uart_rx[3]),    // MIO 46
+    .IOR12(uart_tx[3]),    // MIO 47
     .IOR13(tie_off[18]),   // MIO 48
     // DCD (VCC domain)
     .CC1(tie_off[19]),
@@ -180,13 +180,6 @@
   assign spi_device_sdi_i = spi_if.sio[0];
   assign spi_if.sio[1]    = spi_device_sdo_o;
 
-  // TODO: Replace this weak pull to a known value with initialization
-  // in the agent/interface.
-  assign (weak0, weak1) uart_rx = 1'b1;
-  assign (weak0, weak1) uart_tx = 1'b1;
-  assign uart_rx = uart_if.uart_rx;
-  assign uart_if.uart_tx = uart_tx;
-
   // TODO: USB-related signals, hookup an interface.
   assign usb_rst_n  = `USBDEV_HIER.rst_usb_48mhz_ni;
   assign usb_dp0    = 1'b1;
@@ -248,7 +241,6 @@
 
     // IO Interfaces
     uvm_config_db#(virtual pins_if #(NUM_GPIOS))::set(null, "*.env", "gpio_vif", gpio_if);
-    uvm_config_db#(virtual uart_if)::set(null, "*.env.m_uart_agent*", "vif", uart_if);
     uvm_config_db#(virtual jtag_if)::set(null, "*.env.m_jtag_riscv_agent*", "vif", jtag_if);
     uvm_config_db#(virtual spi_if)::set(null, "*.env.m_spi_agent*", "vif", spi_if);
     uvm_config_db#(virtual tl_if)::set(null, "*.env.m_tl_agent*", "vif", cpu_d_tl_if);
@@ -278,6 +270,19 @@
     run_test();
   end
 
+  for (genvar i = 0; i < NUM_UARTS; i++) begin : gen_uart_if
+    // TODO: Replace this weak pull to a known value with initialization
+    // in the agent/interface.
+    assign (weak0, weak1) uart_rx[i] = 1'b1;
+    assign (weak0, weak1) uart_tx[i] = 1'b1;
+    assign uart_rx[i] = uart_if[i].uart_rx;
+    assign uart_if[i].uart_tx = uart_tx[i];
+
+    initial begin
+      uvm_config_db#(virtual uart_if)::set(null, $sformatf("*.env.m_uart_agent%0d*", i),
+                                           "vif", uart_if[i]);
+    end
+  end
   `undef SIM_SRAM_IF
 
   // Instantitate the memory backdoor util instances.
diff --git a/hw/top_earlgrey/dv/tests/chip_base_test.sv b/hw/top_earlgrey/dv/tests/chip_base_test.sv
index 1da340b..e3aa51b 100644
--- a/hw/top_earlgrey/dv/tests/chip_base_test.sv
+++ b/hw/top_earlgrey/dv/tests/chip_base_test.sv
@@ -38,8 +38,8 @@
 
     // Knob to enable logging over UART (disabled by default).
     void'($value$plusargs("en_uart_logger=%0b", cfg.en_uart_logger));
-    cfg.m_uart_agent_cfg.en_logger = cfg.en_uart_logger;
-    cfg.m_uart_agent_cfg.write_logs_to_file = cfg.write_sw_logs_to_file;
+    cfg.m_uart_agent_cfgs[0].en_logger = cfg.en_uart_logger;
+    cfg.m_uart_agent_cfgs[0].write_logs_to_file = cfg.write_sw_logs_to_file;
 
     // Knob to set the sw_test_timeout_ns (set to 5ms by default).
     void'($value$plusargs("sw_test_timeout_ns=%0d", cfg.sw_test_timeout_ns));
diff --git a/sw/device/tests/sim_dv/meson.build b/sw/device/tests/sim_dv/meson.build
index 636b8e2..b45ed9c 100644
--- a/sw/device/tests/sim_dv/meson.build
+++ b/sw/device/tests/sim_dv/meson.build
@@ -5,7 +5,10 @@
 uart_tx_rx_test_lib = declare_dependency(
   link_with: static_library(
     'uart_tx_rx_test_lib',
-    sources: ['uart_tx_rx_test.c'],
+    sources: [
+      # TODO, remove it once pinout configuration is provided
+      hw_top_earlgrey_pinmux_reg_h,
+      'uart_tx_rx_test.c'],
     dependencies: [
       sw_lib_dif_uart,
       sw_lib_dif_plic,
diff --git a/sw/device/tests/sim_dv/uart_tx_rx_test.c b/sw/device/tests/sim_dv/uart_tx_rx_test.c
index f5b6b17..23168eb 100644
--- a/sw/device/tests/sim_dv/uart_tx_rx_test.c
+++ b/sw/device/tests/sim_dv/uart_tx_rx_test.c
@@ -16,6 +16,9 @@
 
 #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
 
+// TODO, remove it once pinout configuration is provided
+#include "pinmux_regs.h"
+
 #define UART_DATASET_SIZE 128
 
 static dif_uart_t uart;
@@ -48,10 +51,22 @@
   kUartReceive,
 } uart_direction_t;
 
-// TODO: Make these datasets random.
+/**
+ * Indicates the UART instance under test.
+ *
+ * From the software / compiler's perspective, this is a constant (hence the
+ * `const` qualifier). However, the external DV testbench finds this symbol's
+ * address and modifies it via backdoor, to test a different UART instance with
+ * the same test SW image. Hence, we add the `volatile` keyword to prevent the
+ * compiler from optimizing it out.
+ * The `const` is needed to put it in the .rodata section, otherwise it gets
+ * placed in .data section in the main SRAM. We cannot backdoor write anything
+ * in SRAM at the start of the test because the CRT init code wipes it to 0s.
+ */
+static volatile const uint8_t kUartIdx = 0x0;
 
 // A set of bytes to be send out of TX.
-static const uint8_t uart_tx_data[UART_DATASET_SIZE] = {
+static const uint8_t kUartTxData[UART_DATASET_SIZE] = {
     0xe8, 0x50, 0xc6, 0xb4, 0xbe, 0x16, 0xed, 0x55, 0x16, 0x1d, 0xe6, 0x1c,
     0xde, 0x9f, 0xfd, 0x24, 0x89, 0x81, 0x4d, 0x0d, 0x1a, 0x12, 0x4f, 0x57,
     0xea, 0xd6, 0x6f, 0xc0, 0x7d, 0x46, 0xe7, 0x37, 0x81, 0xd3, 0x8e, 0x16,
@@ -66,7 +81,7 @@
 };
 
 // The set of bytes expected to be received over RX.
-static const uint8_t exp_uart_rx_data[UART_DATASET_SIZE] = {
+static const uint8_t kExpUartRxData[UART_DATASET_SIZE] = {
     0x1b, 0x95, 0xc5, 0xb5, 0x8a, 0xa4, 0xa8, 0x9f, 0x6a, 0x7d, 0x6b, 0x0c,
     0xcd, 0xd5, 0xa6, 0x8f, 0x07, 0x3a, 0x9e, 0x82, 0xe6, 0xa2, 0x2b, 0xe0,
     0x0c, 0x30, 0xe8, 0x5a, 0x05, 0x14, 0x79, 0x8a, 0xFf, 0x88, 0x29, 0xda,
@@ -80,9 +95,25 @@
     0xc7, 0x33, 0xd1, 0x6a, 0xc7, 0xba, 0x78, 0x69,
 };
 
-// Set our expectation & event indications of the interrupts we intend to
-// exercise in this test. These are declared volatile since they are used by the
-// interrupt handler.
+// There are multiple uart instances in the chip. These variables will be
+// updated according to the uart we select.
+static volatile uint32_t uart_base_addr;
+static volatile uint32_t uart_peripheral_id;
+static volatile uint32_t uart_irq_tx_watermartk_id;
+static volatile uint32_t uart_irq_rx_watermartk_id;
+static volatile uint32_t uart_irq_tx_empty_id;
+static volatile uint32_t uart_irq_rx_overflow_id;
+static volatile uint32_t uart_irq_rx_frame_err_id;
+static volatile uint32_t uart_irq_rx_frame_err_id;
+static volatile uint32_t uart_irq_rx_break_err_id;
+static volatile uint32_t uart_irq_rx_timeout_id;
+static volatile uint32_t uart_irq_rx_parity_err_id;
+
+/**
+ * Set our expectation & event indications of the interrupts we intend to
+ * exercise in this test. These are declared volatile since they are used by the
+ * interrupt handler.
+ */
 static volatile bool exp_uart_irq_tx_watermark;
 static volatile bool uart_irq_tx_watermark_fired;
 static volatile bool exp_uart_irq_rx_watermark;
@@ -92,6 +123,95 @@
 static volatile bool exp_uart_irq_rx_overflow;
 static volatile bool uart_irq_rx_overflow_fired;
 
+// Configures the pinmux to connect the UART instance to chip IOs based on the
+// ChromeOS pinout configuration.
+//
+// The pinout configuration is documented here:
+// https://github.com/lowRISC/opentitan/blob/master/hw/top_earlgrey/data/top_earlgrey.hjson
+// TODO: Pinout configuration APIs based on customer usecases will be
+// auto-generated in future. This function is a stop-gap solution until that is
+// made available.
+static void pinmux_connect_uart_to_pads(uint32_t rx_pin_in_idx,
+                                        uint32_t rx_uart_idx,
+                                        uint32_t tx_pin_out_idx,
+                                        uint32_t tx_uart_idx) {
+  mmio_region_t reg32 = mmio_region_from_addr(
+      TOP_EARLGREY_PINMUX_AON_BASE_ADDR + PINMUX_MIO_PERIPH_INSEL_0_REG_OFFSET);
+  uint32_t reg_value = rx_pin_in_idx;
+  // We've got one insel configuration field per register. Hence, we have to
+  // convert the enumeration index into a byte address using << 2.
+  uint32_t reg_offset = rx_uart_idx << 2;
+  uint32_t mask = PINMUX_MIO_PERIPH_INSEL_0_IN_0_MASK;
+  mmio_region_write32(reg32, reg_offset, reg_value & mask);
+
+  reg32 = mmio_region_from_addr(TOP_EARLGREY_PINMUX_AON_BASE_ADDR +
+                                PINMUX_MIO_OUTSEL_0_REG_OFFSET);
+  reg_value = tx_uart_idx;
+  // We've got one insel configuration field per register. Hence, we have to
+  // convert the enumeration index into a byte address using << 2.
+  reg_offset = tx_pin_out_idx << 2;
+  mask = PINMUX_MIO_OUTSEL_0_OUT_0_MASK;
+  mmio_region_write32(reg32, reg_offset, reg_value & mask);
+}
+
+void update_uart_base_addr_and_irq_id(void) {
+  switch (kUartIdx) {
+    case 0:
+      uart_base_addr = TOP_EARLGREY_UART0_BASE_ADDR;
+      uart_peripheral_id = kTopEarlgreyPlicPeripheralUart0;
+      uart_irq_tx_watermartk_id = kTopEarlgreyPlicIrqIdUart0TxWatermark;
+      uart_irq_rx_watermartk_id = kTopEarlgreyPlicIrqIdUart0RxWatermark;
+      uart_irq_tx_empty_id = kTopEarlgreyPlicIrqIdUart0TxEmpty;
+      uart_irq_rx_overflow_id = kTopEarlgreyPlicIrqIdUart0RxOverflow;
+      uart_irq_rx_frame_err_id = kTopEarlgreyPlicIrqIdUart0RxFrameErr;
+      uart_irq_rx_frame_err_id = kTopEarlgreyPlicIrqIdUart0RxFrameErr;
+      uart_irq_rx_break_err_id = kTopEarlgreyPlicIrqIdUart0RxBreakErr;
+      uart_irq_rx_timeout_id = kTopEarlgreyPlicIrqIdUart0RxTimeout;
+      uart_irq_rx_parity_err_id = kTopEarlgreyPlicIrqIdUart0RxParityErr;
+      break;
+    case 1:
+      uart_base_addr = TOP_EARLGREY_UART1_BASE_ADDR;
+      uart_peripheral_id = kTopEarlgreyPlicPeripheralUart1;
+      uart_irq_tx_watermartk_id = kTopEarlgreyPlicIrqIdUart1TxWatermark;
+      uart_irq_rx_watermartk_id = kTopEarlgreyPlicIrqIdUart1RxWatermark;
+      uart_irq_tx_empty_id = kTopEarlgreyPlicIrqIdUart1TxEmpty;
+      uart_irq_rx_overflow_id = kTopEarlgreyPlicIrqIdUart1RxOverflow;
+      uart_irq_rx_frame_err_id = kTopEarlgreyPlicIrqIdUart1RxFrameErr;
+      uart_irq_rx_frame_err_id = kTopEarlgreyPlicIrqIdUart1RxFrameErr;
+      uart_irq_rx_break_err_id = kTopEarlgreyPlicIrqIdUart1RxBreakErr;
+      uart_irq_rx_timeout_id = kTopEarlgreyPlicIrqIdUart1RxTimeout;
+      uart_irq_rx_parity_err_id = kTopEarlgreyPlicIrqIdUart1RxParityErr;
+      break;
+    case 2:
+      uart_base_addr = TOP_EARLGREY_UART2_BASE_ADDR;
+      uart_peripheral_id = kTopEarlgreyPlicPeripheralUart2;
+      uart_irq_tx_watermartk_id = kTopEarlgreyPlicIrqIdUart2TxWatermark;
+      uart_irq_rx_watermartk_id = kTopEarlgreyPlicIrqIdUart2RxWatermark;
+      uart_irq_tx_empty_id = kTopEarlgreyPlicIrqIdUart2TxEmpty;
+      uart_irq_rx_overflow_id = kTopEarlgreyPlicIrqIdUart2RxOverflow;
+      uart_irq_rx_frame_err_id = kTopEarlgreyPlicIrqIdUart2RxFrameErr;
+      uart_irq_rx_frame_err_id = kTopEarlgreyPlicIrqIdUart2RxFrameErr;
+      uart_irq_rx_break_err_id = kTopEarlgreyPlicIrqIdUart2RxBreakErr;
+      uart_irq_rx_timeout_id = kTopEarlgreyPlicIrqIdUart2RxTimeout;
+      uart_irq_rx_parity_err_id = kTopEarlgreyPlicIrqIdUart2RxParityErr;
+      break;
+    case 3:
+      uart_base_addr = TOP_EARLGREY_UART3_BASE_ADDR;
+      uart_peripheral_id = kTopEarlgreyPlicPeripheralUart3;
+      uart_irq_tx_watermartk_id = kTopEarlgreyPlicIrqIdUart3TxWatermark;
+      uart_irq_rx_watermartk_id = kTopEarlgreyPlicIrqIdUart3RxWatermark;
+      uart_irq_tx_empty_id = kTopEarlgreyPlicIrqIdUart3TxEmpty;
+      uart_irq_rx_overflow_id = kTopEarlgreyPlicIrqIdUart3RxOverflow;
+      uart_irq_rx_frame_err_id = kTopEarlgreyPlicIrqIdUart3RxFrameErr;
+      uart_irq_rx_frame_err_id = kTopEarlgreyPlicIrqIdUart3RxFrameErr;
+      uart_irq_rx_break_err_id = kTopEarlgreyPlicIrqIdUart3RxBreakErr;
+      uart_irq_rx_timeout_id = kTopEarlgreyPlicIrqIdUart3RxTimeout;
+      uart_irq_rx_parity_err_id = kTopEarlgreyPlicIrqIdUart3RxParityErr;
+      break;
+    default:
+      LOG_FATAL("Unsupported uart ID %x", kUartIdx);
+  }
+}
 /**
  * Provides external irq handling for this test.
  *
@@ -108,39 +228,34 @@
   // Check if it is the right peripheral.
   top_earlgrey_plic_peripheral_t peripheral = (top_earlgrey_plic_peripheral_t)
       top_earlgrey_plic_interrupt_for_peripheral[plic_irq_id];
-  CHECK(peripheral == kTopEarlgreyPlicPeripheralUart0,
+  CHECK(peripheral == uart_peripheral_id,
         "Interurpt from unexpected peripheral: %d", peripheral);
 
   // Correlate the interrupt fired at PLIC with UART.
   dif_uart_irq_t uart_irq;
-  switch (plic_irq_id) {
-    case kTopEarlgreyPlicIrqIdUart0TxWatermark:
-      CHECK(exp_uart_irq_tx_watermark, "Unexpected TX watermark interrupt");
-      uart_irq_tx_watermark_fired = true;
-      uart_irq = kDifUartIrqTxWatermark;
-      break;
-    case kTopEarlgreyPlicIrqIdUart0RxWatermark:
-      CHECK(exp_uart_irq_rx_watermark, "Unexpected RX watermark interrupt");
-      uart_irq_rx_watermark_fired = true;
-      uart_irq = kDifUartIrqRxWatermark;
-      break;
-    case kTopEarlgreyPlicIrqIdUart0TxEmpty:
-      CHECK(exp_uart_irq_tx_empty, "Unexpected TX empty interrupt");
-      uart_irq_tx_empty_fired = true;
-      uart_irq = kDifUartIrqTxEmpty;
-      break;
-    case kTopEarlgreyPlicIrqIdUart0RxOverflow:
-      CHECK(exp_uart_irq_rx_overflow, "Unexpected RX overflow interrupt");
-      uart_irq_rx_overflow_fired = true;
-      uart_irq = kDifUartIrqRxOverflow;
-      break;
-    default:
-      LOG_ERROR("Unexpected interrupt (at PLIC): %d", plic_irq_id);
-      test_status_set(kTestStatusFailed);
-      // The `abort()` call below is redundant. It is added to prevent the
-      // compilation error due to not initializing the `uart_irq` enum variable
-      // above. See issue #2157 for moe details.
-      abort();
+  if (plic_irq_id == uart_irq_tx_watermartk_id) {
+    CHECK(exp_uart_irq_tx_watermark, "Unexpected TX watermark interrupt");
+    uart_irq_tx_watermark_fired = true;
+    uart_irq = kDifUartIrqTxWatermark;
+  } else if (plic_irq_id == uart_irq_rx_watermartk_id) {
+    CHECK(exp_uart_irq_rx_watermark, "Unexpected RX watermark interrupt");
+    uart_irq_rx_watermark_fired = true;
+    uart_irq = kDifUartIrqRxWatermark;
+  } else if (plic_irq_id == uart_irq_tx_empty_id) {
+    CHECK(exp_uart_irq_tx_empty, "Unexpected TX empty interrupt");
+    uart_irq_tx_empty_fired = true;
+    uart_irq = kDifUartIrqTxEmpty;
+  } else if (plic_irq_id == uart_irq_rx_overflow_id) {
+    CHECK(exp_uart_irq_rx_overflow, "Unexpected RX overflow interrupt");
+    uart_irq_rx_overflow_fired = true;
+    uart_irq = kDifUartIrqRxOverflow;
+  } else {
+    LOG_ERROR("Unexpected interrupt (at PLIC): %d", plic_irq_id);
+    test_status_set(kTestStatusFailed);
+    // The `abort()` call below is redundant. It is added to prevent the
+    // compilation error due to not initializing the `uart_irq` enum variable
+    // above. See issue #2157 for moe details.
+    abort();
   }
 
   // Check if the same interrupt fired at UART as well.
@@ -205,63 +320,63 @@
  * Initializes PLIC and enables the relevant UART interrupts.
  */
 static void plic_init_with_irqs(mmio_region_t base_addr, dif_plic_t *plic) {
-  LOG_INFO("Initializing the PLIC.");
+  LOG_INFO("Initializing the PLIC. %0x", uart_irq_tx_watermartk_id);
 
   CHECK(dif_plic_init((dif_plic_params_t){.base_addr = base_addr}, plic) ==
             kDifPlicOk,
         "dif_plic_init failed");
 
   // Enable UART interrupts at PLIC as edge triggered.
-  CHECK(dif_plic_irq_set_trigger(plic, kTopEarlgreyPlicIrqIdUart0TxWatermark,
+  CHECK(dif_plic_irq_set_trigger(plic, uart_irq_tx_watermartk_id,
                                  kDifPlicIrqTriggerEdge) == kDifPlicOk,
         "dif_plic_irq_set_trigger failed");
-  CHECK(dif_plic_irq_set_trigger(plic, kTopEarlgreyPlicIrqIdUart0RxWatermark,
+  CHECK(dif_plic_irq_set_trigger(plic, uart_irq_rx_watermartk_id,
                                  kDifPlicIrqTriggerEdge) == kDifPlicOk,
         "dif_plic_irq_set_trigger failed");
-  CHECK(dif_plic_irq_set_trigger(plic, kTopEarlgreyPlicIrqIdUart0TxEmpty,
+  CHECK(dif_plic_irq_set_trigger(plic, uart_irq_tx_empty_id,
                                  kDifPlicIrqTriggerEdge) == kDifPlicOk,
         "dif_plic_irq_set_trigger failed");
-  CHECK(dif_plic_irq_set_trigger(plic, kTopEarlgreyPlicIrqIdUart0RxOverflow,
+  CHECK(dif_plic_irq_set_trigger(plic, uart_irq_rx_overflow_id,
                                  kDifPlicIrqTriggerEdge) == kDifPlicOk,
         "dif_plic_irq_set_trigger failed");
-  CHECK(dif_plic_irq_set_trigger(plic, kTopEarlgreyPlicIrqIdUart0RxFrameErr,
+  CHECK(dif_plic_irq_set_trigger(plic, uart_irq_rx_frame_err_id,
                                  kDifPlicIrqTriggerEdge) == kDifPlicOk,
         "dif_plic_irq_set_trigger failed");
-  CHECK(dif_plic_irq_set_trigger(plic, kTopEarlgreyPlicIrqIdUart0RxBreakErr,
+  CHECK(dif_plic_irq_set_trigger(plic, uart_irq_rx_break_err_id,
                                  kDifPlicIrqTriggerEdge) == kDifPlicOk,
         "dif_plic_irq_set_trigger failed");
-  CHECK(dif_plic_irq_set_trigger(plic, kTopEarlgreyPlicIrqIdUart0RxTimeout,
+  CHECK(dif_plic_irq_set_trigger(plic, uart_irq_rx_timeout_id,
                                  kDifPlicIrqTriggerEdge) == kDifPlicOk,
         "dif_plic_irq_set_trigger failed");
-  CHECK(dif_plic_irq_set_trigger(plic, kTopEarlgreyPlicIrqIdUart0RxParityErr,
+  CHECK(dif_plic_irq_set_trigger(plic, uart_irq_rx_parity_err_id,
                                  kDifPlicIrqTriggerEdge) == kDifPlicOk,
         "dif_plic_irq_set_trigger failed");
 
   // Set the priority of UART interrupts at PLIC to be >=1 (so ensure the target
   // does get interrupted).
-  CHECK(dif_plic_irq_set_priority(plic, kTopEarlgreyPlicIrqIdUart0TxWatermark,
-                                  0x1) == kDifPlicOk,
+  CHECK(dif_plic_irq_set_priority(plic, uart_irq_tx_watermartk_id, 0x1) ==
+            kDifPlicOk,
         "dif_plic_irq_set_priority failed");
-  CHECK(dif_plic_irq_set_priority(plic, kTopEarlgreyPlicIrqIdUart0RxWatermark,
-                                  0x2) == kDifPlicOk,
+  CHECK(dif_plic_irq_set_priority(plic, uart_irq_rx_watermartk_id, 0x2) ==
+            kDifPlicOk,
         "dif_plic_irq_set_priority failed");
-  CHECK(dif_plic_irq_set_priority(plic, kTopEarlgreyPlicIrqIdUart0TxEmpty,
-                                  0x3) == kDifPlicOk,
-        , "dif_plic_irq_set_priority failed");
-  CHECK(dif_plic_irq_set_priority(plic, kTopEarlgreyPlicIrqIdUart0RxOverflow,
-                                  0x1) == kDifPlicOk,
+  CHECK(
+      dif_plic_irq_set_priority(plic, uart_irq_tx_empty_id, 0x3) == kDifPlicOk,
+      , "dif_plic_irq_set_priority failed");
+  CHECK(dif_plic_irq_set_priority(plic, uart_irq_rx_overflow_id, 0x1) ==
+            kDifPlicOk,
         "dif_plic_irq_set_priority failed");
-  CHECK(dif_plic_irq_set_priority(plic, kTopEarlgreyPlicIrqIdUart0RxFrameErr,
-                                  0x2) == kDifPlicOk,
+  CHECK(dif_plic_irq_set_priority(plic, uart_irq_rx_frame_err_id, 0x2) ==
+            kDifPlicOk,
         "dif_plic_irq_set_priority failed");
-  CHECK(dif_plic_irq_set_priority(plic, kTopEarlgreyPlicIrqIdUart0RxBreakErr,
-                                  0x3) == kDifPlicOk,
+  CHECK(dif_plic_irq_set_priority(plic, uart_irq_rx_break_err_id, 0x3) ==
+            kDifPlicOk,
         "dif_plic_irq_set_priority failed");
-  CHECK(dif_plic_irq_set_priority(plic, kTopEarlgreyPlicIrqIdUart0RxTimeout,
-                                  0x1) == kDifPlicOk,
+  CHECK(dif_plic_irq_set_priority(plic, uart_irq_rx_timeout_id, 0x1) ==
+            kDifPlicOk,
         "dif_plic_irq_set_priority failed");
-  CHECK(dif_plic_irq_set_priority(plic, kTopEarlgreyPlicIrqIdUart0RxParityErr,
-                                  0x2) == kDifPlicOk,
+  CHECK(dif_plic_irq_set_priority(plic, uart_irq_rx_parity_err_id, 0x2) ==
+            kDifPlicOk,
         "dif_plic_irq_set_priority failed");
 
   // Set the threshold for the Ibex to 0.
@@ -270,42 +385,42 @@
         "dif_plic_target_set_threshold failed");
 
   // Enable all UART interrupts at the PLIC.
-  CHECK(dif_plic_irq_set_enabled(plic, kTopEarlgreyPlicIrqIdUart0TxWatermark,
+  CHECK(dif_plic_irq_set_enabled(plic, uart_irq_tx_watermartk_id,
                                  kTopEarlgreyPlicTargetIbex0,
                                  kDifPlicToggleEnabled) == kDifPlicOk,
         "dif_plic_irq_set_enabled failed");
 
-  CHECK(dif_plic_irq_set_enabled(plic, kTopEarlgreyPlicIrqIdUart0RxWatermark,
+  CHECK(dif_plic_irq_set_enabled(plic, uart_irq_rx_watermartk_id,
                                  kTopEarlgreyPlicTargetIbex0,
                                  kDifPlicToggleEnabled) == kDifPlicOk,
         "dif_plic_irq_set_enabled failed");
 
-  CHECK(dif_plic_irq_set_enabled(plic, kTopEarlgreyPlicIrqIdUart0TxEmpty,
+  CHECK(dif_plic_irq_set_enabled(plic, uart_irq_tx_empty_id,
                                  kTopEarlgreyPlicTargetIbex0,
                                  kDifPlicToggleEnabled) == kDifPlicOk,
         "dif_plic_irq_set_enabled failed");
 
-  CHECK(dif_plic_irq_set_enabled(plic, kTopEarlgreyPlicIrqIdUart0RxOverflow,
+  CHECK(dif_plic_irq_set_enabled(plic, uart_irq_rx_overflow_id,
                                  kTopEarlgreyPlicTargetIbex0,
                                  kDifPlicToggleEnabled) == kDifPlicOk,
         "dif_plic_irq_set_enabled failed");
 
-  CHECK(dif_plic_irq_set_enabled(plic, kTopEarlgreyPlicIrqIdUart0RxFrameErr,
+  CHECK(dif_plic_irq_set_enabled(plic, uart_irq_rx_frame_err_id,
                                  kTopEarlgreyPlicTargetIbex0,
                                  kDifPlicToggleEnabled) == kDifPlicOk,
         "dif_plic_irq_set_enabled failed");
 
-  CHECK(dif_plic_irq_set_enabled(plic, kTopEarlgreyPlicIrqIdUart0RxBreakErr,
+  CHECK(dif_plic_irq_set_enabled(plic, uart_irq_rx_break_err_id,
                                  kTopEarlgreyPlicTargetIbex0,
                                  kDifPlicToggleEnabled) == kDifPlicOk,
         "dif_plic_irq_set_enabled failed");
 
-  CHECK(dif_plic_irq_set_enabled(plic, kTopEarlgreyPlicIrqIdUart0RxTimeout,
+  CHECK(dif_plic_irq_set_enabled(plic, uart_irq_rx_timeout_id,
                                  kTopEarlgreyPlicTargetIbex0,
                                  kDifPlicToggleEnabled) == kDifPlicOk,
         "dif_plic_irq_set_enabled failed");
 
-  CHECK(dif_plic_irq_set_enabled(plic, kTopEarlgreyPlicIrqIdUart0RxParityErr,
+  CHECK(dif_plic_irq_set_enabled(plic, uart_irq_rx_parity_err_id,
                                  kTopEarlgreyPlicTargetIbex0,
                                  kDifPlicToggleEnabled) == kDifPlicOk,
         "dif_plic_irq_set_enabled failed");
@@ -378,10 +493,10 @@
     if (!uart_tx_done && uart_irq_tx_watermark_fired) {
       uart_irq_tx_watermark_fired = false;
 
-      // Send the remaining uart_tx_data as and when the TX watermark fires.
-      CHECK(uart_transfer_ongoing_bytes(
-          uart, kUartSend, (uint8_t *)uart_tx_data, UART_DATASET_SIZE,
-          &uart_tx_bytes_written, &uart_tx_done));
+      // Send the remaining kUartTxData as and when the TX watermark fires.
+      CHECK(uart_transfer_ongoing_bytes(uart, kUartSend, (uint8_t *)kUartTxData,
+                                        UART_DATASET_SIZE,
+                                        &uart_tx_bytes_written, &uart_tx_done));
 
       if (uart_tx_done) {
         // At this point, we have sent the required number of bytes.
@@ -432,9 +547,9 @@
   // Check data consistency.
   LOG_INFO("Checking the received UART RX data for consistency.");
   for (int i = 0; i < UART_DATASET_SIZE; ++i) {
-    CHECK(uart_rx_data[i] == exp_uart_rx_data[i],
+    CHECK(uart_rx_data[i] == kExpUartRxData[i],
           "UART RX data[%0d] mismatched: {act: %x, exp: %x}", i,
-          uart_rx_data[i], exp_uart_rx_data[i]);
+          uart_rx_data[i], kExpUartRxData[i]);
   }
 
   // If we reached here, then the test passed.
@@ -444,12 +559,27 @@
 const test_config_t kTestConfig;
 
 bool test_main(void) {
-  LOG_INFO("UART TX RX test");
+  update_uart_base_addr_and_irq_id();
+
+  LOG_INFO("Test UART%d with base_addr: %8x", kUartIdx, uart_base_addr);
+
+  // TODO, remove thse once pinout configuration is provided
+  pinmux_connect_uart_to_pads(
+      kTopEarlgreyPinmuxInselIoc10, kTopEarlgreyPinmuxPeripheralInUart0Rx,
+      kTopEarlgreyPinmuxMioOutIoc11, kTopEarlgreyPinmuxOutselUart0Tx);
+  pinmux_connect_uart_to_pads(
+      kTopEarlgreyPinmuxInselIor5, kTopEarlgreyPinmuxPeripheralInUart1Rx,
+      kTopEarlgreyPinmuxMioOutIor6, kTopEarlgreyPinmuxOutselUart1Tx);
+  pinmux_connect_uart_to_pads(
+      kTopEarlgreyPinmuxInselIor7, kTopEarlgreyPinmuxPeripheralInUart2Rx,
+      kTopEarlgreyPinmuxMioOutIor10, kTopEarlgreyPinmuxOutselUart2Tx);
+  pinmux_connect_uart_to_pads(
+      kTopEarlgreyPinmuxInselIor11, kTopEarlgreyPinmuxPeripheralInUart3Rx,
+      kTopEarlgreyPinmuxMioOutIor12, kTopEarlgreyPinmuxOutselUart3Tx);
 
   // Initialize the UART.
-  mmio_region_t uart_base_addr =
-      mmio_region_from_addr(TOP_EARLGREY_UART0_BASE_ADDR);
-  uart_init_with_irqs(uart_base_addr, &uart);
+  mmio_region_t chosen_uart_region = mmio_region_from_addr(uart_base_addr);
+  uart_init_with_irqs(chosen_uart_region, &uart);
 
   // Initialize the PLIC.
   mmio_region_t plic_base_addr =