[fpga, sw] Enable SCA on ChipWhisperer CW310 FPGA board

This involves the following main changes:
- Two new outputs are defined on the CW310 chip-level to provide the
  target clock and capture trigger to the capture board.
- The capture trigger can now be selected at run time through software
  and GPIO. This allows for taking SCA measurements for different IP
  cores (KMAC, OTBN) without having to re-generate a different
  bitstream.
- The capture trigger is synchronized to the target clock (100 MHz).
  This is needed to prevent metastability issues in the scope depending
  on place and route on the target FPGA.
- UART1 is enabled and muxed to pins IOC9/IOC8 using the pinmux. This
  is needed for the simple serial communication between target and
  capture board, while UART0 remains available for debug prints and
  logging.
- The simple serial library is adjusted to use dif_uart_* only instead
  of a mix of dif_uart_* and base_printf() (this required the UART used
  for simple serial and stdout to be the same device previously).

Signed-off-by: Pirmin Vogel <vogelpi@lowrisc.org>
diff --git a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
index 6f8ec32..13b520b 100644
--- a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
+++ b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
@@ -11320,6 +11320,20 @@
             connection: manual
             desc: Manual USB UPHY signal for FPGA target
           }
+          {
+            name: IO_CLKOUT
+            type: BidirStd
+            bank: VCC
+            connection: manual
+            desc: Manual clock output for SCA setup
+          }
+          {
+            name: IO_TRIGGER
+            type: BidirStd
+            bank: VCC
+            connection: manual
+            desc: Manual trigger output for SCA setup
+          }
         ]
       }
       pinmux:
diff --git a/hw/top_earlgrey/data/clocks.xdc b/hw/top_earlgrey/data/clocks.xdc
index c82c5cf..e020776 100644
--- a/hw/top_earlgrey/data/clocks.xdc
+++ b/hw/top_earlgrey/data/clocks.xdc
@@ -27,5 +27,5 @@
 create_clock -add -name clk_spi_in  -period 100.00 -waveform {0 5} [get_pin top_*/u_spi_device/u_clk_spi_in_buf/gen_xilinx.u_impl_xilinx/bufg_i/O]
 create_clock -add -name clk_spi_out -period 100.00 -waveform {0 5} [get_pin top_*/u_spi_device/u_clk_spi_out_buf/gen_xilinx.u_impl_xilinx/bufg_i/O]
 
-set_clock_groups -group ${clks_10_unbuf} -group ${clks_48_unbuf} -group ${clks_aon_unbuf} -group clk_io_div2 -group clk_io_div4 -group lc_jtag_tck -group rv_jtag_tck -group clk_spi_in -group clk_spi_out -asynchronous
+set_clock_groups -group ${clks_10_unbuf} -group ${clks_48_unbuf} -group ${clks_aon_unbuf} -group clk_io_div2 -group clk_io_div4 -group lc_jtag_tck -group rv_jtag_tck -group clk_spi_in -group clk_spi_out -group sys_clk_pin -asynchronous
 
diff --git a/hw/top_earlgrey/data/pins_cw310.xdc b/hw/top_earlgrey/data/pins_cw310.xdc
index fabacc7..6a2f58a 100644
--- a/hw/top_earlgrey/data/pins_cw310.xdc
+++ b/hw/top_earlgrey/data/pins_cw310.xdc
@@ -60,18 +60,14 @@
 set_property -dict { PACKAGE_PIN E10 IOSTANDARD LVCMOS33 } [get_ports { IOC5 }]; #USERIOB-14
 set_property -dict { PACKAGE_PIN D8  IOSTANDARD LVCMOS33 } [get_ports { IOC6 }]; #USERIOB-16
 set_property -dict { PACKAGE_PIN D9  IOSTANDARD LVCMOS33 } [get_ports { IOC7 }]; #USERIOB-18
-set_property -dict { PACKAGE_PIN C9  IOSTANDARD LVCMOS33 } [get_ports { IOC8 }]; #USERIOB-24
-set_property -dict { PACKAGE_PIN D10 IOSTANDARD LVCMOS33 } [get_ports { IOC9 }]; #USERIOB-26
+#set_property -dict { PACKAGE_PIN C9  IOSTANDARD LVCMOS33 } [get_ports { IOC8 }]; #USERIOB-24
+#set_property -dict { PACKAGE_PIN D10 IOSTANDARD LVCMOS33 } [get_ports { IOC9 }]; #USERIOB-26
 
 ## ChipWhisperer 20-Pin Connector (J14)
-## TODO: This needs to be adapted to enable captures on the CW310. In particular,
-## - a precise capture trigger and the target clock need to be output, and
-## - a separate UART should be used for the simpleserial communication with the capture board.
-## See also chiplevel.sv.tpl
-#set_property -dict { PACKAGE_PIN AE25 IOSTANDARD LVCMOS33 } [get_ports { IOC11 }];      #J14 PIN 10 CWIO_IO1 (UART)
-#set_property -dict { PACKAGE_PIN AF25 IOSTANDARD LVCMOS33 } [get_ports { IOC10 }];      #J14 PIN 12 CWIO_IO2 (UART)
-#set_property -dict { PACKAGE_PIN AF24 IOSTANDARD LVCMOS33 } [get_ports { IOB6 }];       #J14 PIN 16 CWIO_IO4 (Trigger)
-#set_property -dict { PACKAGE_PIN AB21 IOSTANDARD LVCMOS33 } [get_ports { TIO_CLKOUT }]; #J14 PIN  4 CWIO_HS1 (Target clock)
+set_property -dict { PACKAGE_PIN AF25 IOSTANDARD LVCMOS33 } [get_ports { IOC9 }];       #J14 PIN 12 CWIO_IO2 - OpenTitan UART1 TX
+set_property -dict { PACKAGE_PIN AE25 IOSTANDARD LVCMOS33 } [get_ports { IOC8 }];       #J14 PIN 10 CWIO_IO1 - OpenTitan UART1 RX
+set_property -dict { PACKAGE_PIN AF24 IOSTANDARD LVCMOS33 } [get_ports { IO_TRIGGER }]; #J14 PIN 16 CWIO_IO4 - Capture Trigger
+set_property -dict { PACKAGE_PIN AB21 IOSTANDARD LVCMOS33 } [get_ports { IO_CLKOUT }];  #J14 PIN  4 CWIO_HS1 - Target clock
 
 ## TI TUSB1106 USB PHY usbdev testing
 set_property -dict { PACKAGE_PIN AF19  IOSTANDARD LVCMOS18 } [get_ports { IO_UPHY_DP_TX }]; #USRUSB_VPO
@@ -93,8 +89,8 @@
 set_property -dict { PACKAGE_PIN A13   IOSTANDARD LVCMOS33 } [get_ports { IO_USB_DPPULLUP0 }]; #USERIOB-27
 
 ## UART
-set_property -dict { PACKAGE_PIN AA22 IOSTANDARD LVCMOS33 } [get_ports { IOC11 }]; #UART1RXD
-set_property -dict { PACKAGE_PIN W24  IOSTANDARD LVCMOS33 } [get_ports { IOC10 }]; #UART1TXD
+set_property -dict { PACKAGE_PIN AA22 IOSTANDARD LVCMOS33 } [get_ports { IOC11 }]; #UART1RXD - OpenTitan UART0 TX
+set_property -dict { PACKAGE_PIN W24  IOSTANDARD LVCMOS33 } [get_ports { IOC10 }]; #UART1TXD - OpenTitan UART0 RX
 
 ## Configuration options, can be used for all designs
 set_property CONFIG_VOLTAGE 3.3 [current_design]
diff --git a/hw/top_earlgrey/data/top_earlgrey.hjson b/hw/top_earlgrey/data/top_earlgrey.hjson
index 6c9df7a..6df1b83 100644
--- a/hw/top_earlgrey/data/top_earlgrey.hjson
+++ b/hw/top_earlgrey/data/top_earlgrey.hjson
@@ -1396,6 +1396,9 @@
           { name: 'IO_UPHY_OE_N',     type: 'BidirStd', bank: 'VCC', connection: 'manual', desc: 'Manual USB UPHY signal for FPGA target'}
           { name: 'IO_UPHY_SENSE',    type: 'BidirStd', bank: 'VCC', connection: 'manual', desc: 'Manual USB UPHY signal for FPGA target'}
           { name: 'IO_UPHY_DPPULLUP', type: 'BidirStd', bank: 'VCC', connection: 'manual', desc: 'Manual USB UPHY signal for FPGA target'}
+          // ChipWhisperer IO
+          { name: 'IO_CLKOUT',        type: 'BidirStd', bank: 'VCC', connection: 'manual', desc: 'Manual clock output for SCA setup'}
+          { name: 'IO_TRIGGER',       type: 'BidirStd', bank: 'VCC', connection: 'manual', desc: 'Manual trigger output for SCA setup'}
         ],
       },
 
diff --git a/hw/top_earlgrey/rtl/autogen/chip_earlgrey_asic.sv b/hw/top_earlgrey/rtl/autogen/chip_earlgrey_asic.sv
index 0ff33a3..4fa0959 100644
--- a/hw/top_earlgrey/rtl/autogen/chip_earlgrey_asic.sv
+++ b/hw/top_earlgrey/rtl/autogen/chip_earlgrey_asic.sv
@@ -1159,5 +1159,4 @@
 
 
 
-
 endmodule : chip_earlgrey_asic
diff --git a/hw/top_earlgrey/rtl/autogen/chip_earlgrey_cw310.sv b/hw/top_earlgrey/rtl/autogen/chip_earlgrey_cw310.sv
index c29e049..4870180 100644
--- a/hw/top_earlgrey/rtl/autogen/chip_earlgrey_cw310.sv
+++ b/hw/top_earlgrey/rtl/autogen/chip_earlgrey_cw310.sv
@@ -38,6 +38,8 @@
   inout IO_UPHY_OE_N, // Manual Pad
   inout IO_UPHY_SENSE, // Manual Pad
   inout IO_UPHY_DPPULLUP, // Manual Pad
+  inout IO_CLKOUT, // Manual Pad
+  inout IO_TRIGGER, // Manual Pad
 
   // Muxed Pads
   inout IOA0, // MIO Pad 0
@@ -216,6 +218,8 @@
   logic manual_in_io_uphy_oe_n, manual_out_io_uphy_oe_n, manual_oe_io_uphy_oe_n;
   logic manual_in_io_uphy_sense, manual_out_io_uphy_sense, manual_oe_io_uphy_sense;
   logic manual_in_io_uphy_dppullup, manual_out_io_uphy_dppullup, manual_oe_io_uphy_dppullup;
+  logic manual_in_io_clkout, manual_out_io_clkout, manual_oe_io_clkout;
+  logic manual_in_io_trigger, manual_out_io_trigger, manual_oe_io_trigger;
 
   pad_attr_t manual_attr_por_n;
   pad_attr_t manual_attr_usb_p;
@@ -233,6 +237,8 @@
   pad_attr_t manual_attr_io_uphy_oe_n;
   pad_attr_t manual_attr_io_uphy_sense;
   pad_attr_t manual_attr_io_uphy_dppullup;
+  pad_attr_t manual_attr_io_clkout;
+  pad_attr_t manual_attr_io_trigger;
 
   /////////////////////////
   // Stubbed pad tie-off //
@@ -323,9 +329,11 @@
   padring #(
     // Padring specific counts may differ from pinmux config due
     // to custom, stubbed or added pads.
-    .NDioPads(20),
+    .NDioPads(22),
     .NMioPads(29),
     .DioPadType ({
+      BidirStd, // IO_TRIGGER
+      BidirStd, // IO_CLKOUT
       BidirStd, // IO_UPHY_DPPULLUP
       BidirStd, // IO_UPHY_SENSE
       BidirStd, // IO_UPHY_OE_N
@@ -385,6 +393,8 @@
     .dio_in_raw_o ( ),
     // Chip IOs
     .dio_pad_io ({
+      IO_TRIGGER,
+      IO_CLKOUT,
       IO_UPHY_DPPULLUP,
       IO_UPHY_SENSE,
       IO_UPHY_OE_N,
@@ -441,6 +451,8 @@
 
     // Core-facing
     .dio_in_o ({
+        manual_in_io_trigger,
+        manual_in_io_clkout,
         manual_in_io_uphy_dppullup,
         manual_in_io_uphy_sense,
         manual_in_io_uphy_oe_n,
@@ -463,6 +475,8 @@
         manual_in_por_n
       }),
     .dio_out_i ({
+        manual_out_io_trigger,
+        manual_out_io_clkout,
         manual_out_io_uphy_dppullup,
         manual_out_io_uphy_sense,
         manual_out_io_uphy_oe_n,
@@ -485,6 +499,8 @@
         manual_out_por_n
       }),
     .dio_oe_i ({
+        manual_oe_io_trigger,
+        manual_oe_io_clkout,
         manual_oe_io_uphy_dppullup,
         manual_oe_io_uphy_sense,
         manual_oe_io_uphy_oe_n,
@@ -507,6 +523,8 @@
         manual_oe_por_n
       }),
     .dio_attr_i ({
+        manual_attr_io_trigger,
+        manual_attr_io_clkout,
         manual_attr_io_uphy_dppullup,
         manual_attr_io_uphy_sense,
         manual_attr_io_uphy_oe_n,
@@ -724,7 +742,6 @@
   // for verilator purposes, make these two the same.
   lc_ctrl_pkg::lc_tx_t lc_clk_bypass;
 
-
 // TODO: align this with ASIC version to minimize the duplication.
 // Also need to add AST simulation and FPGA emulation models for things like entropy source -
 // otherwise Verilator / FPGA will hang.
@@ -803,5 +820,59 @@
   );
 
 
+  /////////////////////////////////////////////////////
+  // ChipWhisperer CW310/305 Capture Board Interface //
+  /////////////////////////////////////////////////////
+  // This is used to interface OpenTitan as a target with a capture board trough the ChipWhisperer
+  // 20-pin connector. This is used for SCA/FI experiments only.
+
+  logic unused_inputs;
+  assign unused_inputs = manual_in_io_clkout ^ manual_in_io_trigger;
+
+  // Synchronous clock output to capture board.
+  assign manual_out_io_clkout = manual_in_io_clk;
+  assign manual_oe_io_clkout = 1'b1;
+
+  // Capture trigger.
+  // We use the clkmgr_aon_idle signal of the IP of interest to form a precise capture trigger.
+  // GPIO[11:9] is used for selecting the IP of interest. The encoding is as follows (see
+  // hint_names_e enum in clkmgr_pkg.sv for details).
+  //
+  // IP              - GPIO[11:9] - Index for clkmgr_aon_idle
+  // ------------------------------------------------------------
+  //  AES            -   000      -  0
+  //  HMAC           -   001      -  1
+  //  KMAC           -   010      -  2 - not implemented on CW305
+  //  OTBN (IO_DIV4) -   011      -  3 - not implemented on CW305
+  //  OTBN           -   100      -  4 - not implemented on CW305
+  //
+  // In addition, GPIO8 is used for gating the capture trigger in software.
+  // Note that GPIO[11:8] are connected to LED[3:0] on the CW310.
+  // On the CW305, GPIO[9,8] are connected to LED[5,7].
+
+  clkmgr_pkg::hint_names_e trigger_sel;
+  always_comb begin : trigger_sel_mux
+    unique case ({mio_out[MioOutGpioGpio11], mio_out[MioOutGpioGpio10], mio_out[MioOutGpioGpio9]})
+      3'b000:  trigger_sel = clkmgr_pkg::HintMainAes;
+      3'b001:  trigger_sel = clkmgr_pkg::HintMainHmac;
+      3'b010:  trigger_sel = clkmgr_pkg::HintMainKmac;
+      3'b011:  trigger_sel = clkmgr_pkg::HintIoDiv4Otbn;
+      3'b100:  trigger_sel = clkmgr_pkg::HintMainOtbn;
+      default: trigger_sel = clkmgr_pkg::HintMainAes;
+    endcase;
+  end
+  logic trigger, trigger_oe;
+  assign trigger = mio_out[MioOutGpioGpio8] & ~top_earlgrey.clkmgr_aon_idle[trigger_sel];
+  assign trigger_oe = mio_oe[MioOutGpioGpio8];
+
+  // Synchronize trigger to manual_in_io_clk.
+  prim_flop_2sync #(
+    .Width ( 2 )
+  ) u_sync_trigger (
+    .clk_i  ( manual_in_io_clk                              ),
+    .rst_ni ( manual_in_por_n                               ),
+    .d_i    ( {trigger,               trigger_oe}           ),
+    .q_o    ( {manual_out_io_trigger, manual_oe_io_trigger} )
+  );
 
 endmodule : chip_earlgrey_cw310
diff --git a/hw/top_earlgrey/rtl/autogen/chip_earlgrey_nexysvideo.sv b/hw/top_earlgrey/rtl/autogen/chip_earlgrey_nexysvideo.sv
index 6090110..1950931 100644
--- a/hw/top_earlgrey/rtl/autogen/chip_earlgrey_nexysvideo.sv
+++ b/hw/top_earlgrey/rtl/autogen/chip_earlgrey_nexysvideo.sv
@@ -724,7 +724,6 @@
   // for verilator purposes, make these two the same.
   lc_ctrl_pkg::lc_tx_t lc_clk_bypass;
 
-
 // TODO: align this with ASIC version to minimize the duplication.
 // Also need to add AST simulation and FPGA emulation models for things like entropy source -
 // otherwise Verilator / FPGA will hang.
@@ -808,5 +807,4 @@
   );
 
 
-
 endmodule : chip_earlgrey_nexysvideo
diff --git a/hw/top_englishbreakfast/data/pins_cw305.xdc b/hw/top_englishbreakfast/data/pins_cw305.xdc
index ad5bcd2..1aec16d 100644
--- a/hw/top_englishbreakfast/data/pins_cw305.xdc
+++ b/hw/top_englishbreakfast/data/pins_cw305.xdc
@@ -40,6 +40,7 @@
 set_property -dict { PACKAGE_PIN F15 IOSTANDARD LVCMOS33 } [get_ports { IOB3 }]; #JP3.F15
 set_property -dict { PACKAGE_PIN E11 IOSTANDARD LVCMOS33 } [get_ports { IOB4 }]; #JP3.E11
 set_property -dict { PACKAGE_PIN F13 IOSTANDARD LVCMOS33 } [get_ports { IOB5 }]; #JP3.F13
+set_property -dict { PACKAGE_PIN A12 IOSTANDARD LVCMOS33 } [get_ports { IOB6 }]; #JP3.A12
 
 set_property -dict { PACKAGE_PIN C16 IOSTANDARD LVCMOS33 DRIVE 8 SLEW FAST } [get_ports { USB_P }]; #JP3.C16
 set_property -dict { PACKAGE_PIN D13 IOSTANDARD LVCMOS33 DRIVE 8 SLEW FAST } [get_ports { USB_N }]; #JP3.D13
@@ -49,14 +50,14 @@
 
 set_property -dict { PACKAGE_PIN G16 IOSTANDARD LVCMOS33 } [get_ports { IO_UTX_DEBUG }]; #JP3.G16 (UART) for debugging
 
-## Unused pins of JP3: A12, B12, F12
+## Unused pins of JP3: B12, F12
 
 ## 20-Pin Connector (JP1)
 
-set_property -dict { PACKAGE_PIN R16 IOSTANDARD LVCMOS33 } [get_ports { IOC11 }];     #JP1 PIN 10 (UART)
-set_property -dict { PACKAGE_PIN P16 IOSTANDARD LVCMOS33 } [get_ports { IOC10 }];     #JP1 PIN 12 (UART)
-set_property -dict { PACKAGE_PIN T14 IOSTANDARD LVCMOS33 } [get_ports { IOB6 }];    #JP1 PIN 16 TIO4 (Trigger)
-set_property -dict { PACKAGE_PIN M16 IOSTANDARD LVCMOS33 } [get_ports { TIO_CLKOUT }]; #JP1 PIN 4 TIO_HS1. Clock sync capture board.
+set_property -dict { PACKAGE_PIN R16 IOSTANDARD LVCMOS33 } [get_ports { IOC11 }];      #JP1 PIN 12 TIO2    - OpenTitan UART0 TX
+set_property -dict { PACKAGE_PIN P16 IOSTANDARD LVCMOS33 } [get_ports { IOC10 }];      #JP1 PIN 10 TIO1    - OpenTitan UART0 RX
+set_property -dict { PACKAGE_PIN T14 IOSTANDARD LVCMOS33 } [get_ports { IO_TRIGGER }]; #JP1 PIN 16 TIO4    - Capture Trigger
+set_property -dict { PACKAGE_PIN M16 IOSTANDARD LVCMOS33 } [get_ports { IO_CLKOUT }];  #JP1 PIN  4 TIO_HS1 - Target clock
 
 
 ## USB Connector
diff --git a/hw/top_englishbreakfast/data/top_englishbreakfast.hjson b/hw/top_englishbreakfast/data/top_englishbreakfast.hjson
index 5da4319..9c4c8d1 100644
--- a/hw/top_englishbreakfast/data/top_englishbreakfast.hjson
+++ b/hw/top_englishbreakfast/data/top_englishbreakfast.hjson
@@ -981,8 +981,9 @@
           { name: 'IO_USB_DNPULLUP0', type: 'BidirStd', bank: 'VCC', connection: 'manual', desc: 'Manual USB signal for FPGA target'}
           { name: 'IO_USB_DPPULLUP0', type: 'BidirStd', bank: 'VCC', connection: 'manual', desc: 'Manual USB signal for FPGA target'}
           // ChipWhisperer IO
-          { name: 'TIO_CLKOUT',       type: 'BidirStd', bank: 'VCC', connection: 'manual', desc: 'Manual clock output for SCA setup'}
-          { name: 'IO_UTX_DEBUG',     type: 'BidirStd', bank: 'VCC', connection: 'manual', desc: 'Manual UART TX debug output'      }
+          { name: 'IO_CLKOUT',        type: 'BidirStd', bank: 'VCC', connection: 'manual', desc: 'Manual clock output for SCA setup'}
+          { name: 'IO_TRIGGER',       type: 'BidirStd', bank: 'VCC', connection: 'manual', desc: 'Manual trigger output for SCA setup'}
+          { name: 'IO_UTX_DEBUG',     type: 'BidirStd', bank: 'VCC', connection: 'manual', desc: 'Manual UART TX debug output'}
         ],
       }
 
diff --git a/sw/device/lib/pinmux.c b/sw/device/lib/pinmux.c
index 476429a..8861f54 100644
--- a/sw/device/lib/pinmux.c
+++ b/sw/device/lib/pinmux.c
@@ -41,7 +41,7 @@
                  PINMUX_MIO_OUTSEL_0_OUT_0_MASK,
                  PINMUX_PERIPH_OUTSEL_IDX_OFFSET);
 
-  // Configure UART RX input to connect to MIO pad IOR2
+  // Configure UART0 RX input to connect to MIO pad IOC10
   mmio_region_t reg32 = mmio_region_from_addr(
       PINMUX0_BASE_ADDR + PINMUX_MIO_PERIPH_INSEL_0_REG_OFFSET);
   uint32_t reg_value = kTopEarlgreyPinmuxInselIoc10;
@@ -51,7 +51,7 @@
   uint32_t mask = PINMUX_MIO_PERIPH_INSEL_0_IN_0_MASK;
   mmio_region_write32(reg32, reg_offset, reg_value & mask);
 
-  // Configure UART TX output to connect to MIO pad IOR3
+  // Configure UART0 TX output to connect to MIO pad IOC11
   reg32 =
       mmio_region_from_addr(PINMUX0_BASE_ADDR + PINMUX_MIO_OUTSEL_0_REG_OFFSET);
   reg_value = kTopEarlgreyPinmuxOutselUart0Tx;
@@ -60,4 +60,24 @@
   reg_offset = kTopEarlgreyPinmuxMioOutIoc11 << 2;
   mask = PINMUX_MIO_OUTSEL_0_OUT_0_MASK;
   mmio_region_write32(reg32, reg_offset, reg_value & mask);
+
+  // Configure UART1 RX input to connect to MIO pad IOC8
+  reg32 = mmio_region_from_addr(PINMUX0_BASE_ADDR +
+                                PINMUX_MIO_PERIPH_INSEL_0_REG_OFFSET);
+  reg_value = kTopEarlgreyPinmuxInselIoc8;
+  // 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 = kTopEarlgreyPinmuxPeripheralInUart1Rx << 2;
+  mask = PINMUX_MIO_PERIPH_INSEL_0_IN_0_MASK;
+  mmio_region_write32(reg32, reg_offset, reg_value & mask);
+
+  // Configure UART1 TX output to connect to MIO pad IOC9
+  reg32 =
+      mmio_region_from_addr(PINMUX0_BASE_ADDR + PINMUX_MIO_OUTSEL_0_REG_OFFSET);
+  reg_value = kTopEarlgreyPinmuxOutselUart1Tx;
+  // 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 = kTopEarlgreyPinmuxMioOutIoc9 << 2;
+  mask = PINMUX_MIO_OUTSEL_0_OUT_0_MASK;
+  mmio_region_write32(reg32, reg_offset, reg_value & mask);
 }
diff --git a/sw/device/sca/aes_serial.c b/sw/device/sca/aes_serial.c
index 372421e..a6cd8c0 100644
--- a/sw/device/sca/aes_serial.c
+++ b/sw/device/sca/aes_serial.c
@@ -198,18 +198,23 @@
  * UART.
  */
 int main(void) {
-  const dif_uart_t *uart;
+  const dif_uart_t *uart1;
 
   sca_init();
-  sca_get_uart(&uart);
+  sca_get_uart(&uart1);
 
-  simple_serial_init(uart);
+  LOG_INFO("Running AES serial");
+
+  LOG_INFO("Initializing simple serial interface to capture board.");
+  simple_serial_init(uart1);
   simple_serial_register_handler('k', aes_serial_set_key);
   simple_serial_register_handler('p', aes_serial_single_encrypt);
   simple_serial_register_handler('b', aes_serial_batch_encrypt);
 
+  LOG_INFO("Initializing AES unit.");
   init_aes();
 
+  LOG_INFO("Starting simple serial packet handling.");
   while (true) {
     simple_serial_process_packet();
   }
diff --git a/sw/device/sca/lib/sca.c b/sw/device/sca/lib/sca.c
index 87c6147..758d330 100644
--- a/sw/device/sca/lib/sca.c
+++ b/sw/device/sca/lib/sca.c
@@ -3,6 +3,7 @@
 // SPDX-License-Identifier: Apache-2.0
 
 #include "sw/device/sca/lib/sca.h"
+
 #include "sw/device/lib/arch/device.h"
 #include "sw/device/lib/dif/dif_gpio.h"
 #include "sw/device/lib/dif/dif_rv_timer.h"
@@ -27,8 +28,15 @@
 enum {
   /**
    * GPIO capture trigger values.
+   *
+   * GPIO10[11:9]: Trigger select, 000 for AES, see chiplevel.sv.tpl for
+   *               details.
+   * GPIO8:        Trigger enable
    */
-  kGpioCaptureTriggerHigh = 0x08200,
+  kGpioCaptureTriggerSelMask = 0x00E00,
+  kGpioCaptureTriggerEnMask = 0x00100,
+  kGpioCaptureTriggerSel = 0x00000,
+  kGpioCaptureTriggerHigh = 0x00100,
   kGpioCaptureTriggerLow = 0x00000,
   /**
    * RV timer settings.
@@ -37,7 +45,8 @@
   kRvTimerHart = kTopEarlgreyPlicTargetIbex0,
 };
 
-static dif_uart_t uart;
+static dif_uart_t uart0;
+static dif_uart_t uart1;
 static dif_gpio_t gpio;
 static dif_rv_timer_t timer;
 
@@ -47,19 +56,27 @@
  * Initializes the UART peripheral.
  */
 static void sca_init_uart(void) {
+  const dif_uart_config_t uart_config = {
+      .baudrate = kUartBaudrate,
+      .clk_freq_hz = kClockFreqPeripheralHz,
+      .parity_enable = kDifUartToggleDisabled,
+      .parity = kDifUartParityEven,
+  };
+
   IGNORE_RESULT(dif_uart_init(
       (dif_uart_params_t){
           .base_addr = mmio_region_from_addr(TOP_EARLGREY_UART0_BASE_ADDR),
       },
-      &uart));
-  IGNORE_RESULT(
-      dif_uart_configure(&uart, (dif_uart_config_t){
-                                    .baudrate = kUartBaudrate,
-                                    .clk_freq_hz = kClockFreqPeripheralHz,
-                                    .parity_enable = kDifUartToggleDisabled,
-                                    .parity = kDifUartParityEven,
-                                }));
-  base_uart_stdout(&uart);
+      &uart0));
+  IGNORE_RESULT(dif_uart_configure(&uart0, uart_config));
+  base_uart_stdout(&uart0);
+
+  IGNORE_RESULT(dif_uart_init(
+      (dif_uart_params_t){
+          .base_addr = mmio_region_from_addr(TOP_EARLGREY_UART1_BASE_ADDR),
+      },
+      &uart1));
+  IGNORE_RESULT(dif_uart_configure(&uart1, uart_config));
 }
 
 /**
@@ -69,8 +86,10 @@
   dif_gpio_params_t gpio_params = {
       .base_addr = mmio_region_from_addr(TOP_EARLGREY_GPIO_BASE_ADDR)};
   IGNORE_RESULT(dif_gpio_init(gpio_params, &gpio));
-  IGNORE_RESULT(
-      dif_gpio_output_set_enabled_all(&gpio, kGpioCaptureTriggerHigh));
+  IGNORE_RESULT(dif_gpio_output_set_enabled_all(
+      &gpio, kGpioCaptureTriggerSelMask | kGpioCaptureTriggerEnMask));
+  IGNORE_RESULT(dif_gpio_write_masked(&gpio, kGpioCaptureTriggerSelMask,
+                                      kGpioCaptureTriggerSel));
 }
 
 /**
@@ -112,14 +131,16 @@
   sca_init_timer();
 }
 
-void sca_get_uart(const dif_uart_t **uart_out) { *uart_out = &uart; }
+void sca_get_uart(const dif_uart_t **uart_out) { *uart_out = &uart1; }
 
 void sca_set_trigger_high() {
-  IGNORE_RESULT(dif_gpio_write_all(&gpio, kGpioCaptureTriggerHigh));
+  IGNORE_RESULT(dif_gpio_write_masked(&gpio, kGpioCaptureTriggerEnMask,
+                                      kGpioCaptureTriggerHigh));
 }
 
 void sca_set_trigger_low() {
-  IGNORE_RESULT(dif_gpio_write_all(&gpio, kGpioCaptureTriggerLow));
+  IGNORE_RESULT(dif_gpio_write_masked(&gpio, kGpioCaptureTriggerEnMask,
+                                      kGpioCaptureTriggerLow));
 }
 
 void sca_call_and_sleep(sca_callee callee, uint32_t sleep_cycles) {
diff --git a/sw/device/sca/lib/simple_serial.c b/sw/device/sca/lib/simple_serial.c
index 2c4ebe5..c07f94f 100644
--- a/sw/device/sca/lib/simple_serial.c
+++ b/sw/device/sca/lib/simple_serial.c
@@ -3,6 +3,7 @@
 // SPDX-License-Identifier: Apache-2.0
 
 #include "sw/device/sca/lib/simple_serial.h"
+
 #include "sw/device/lib/arch/device.h"
 #include "sw/device/lib/base/memory.h"
 #include "sw/device/lib/dif/dif_uart.h"
@@ -205,9 +206,12 @@
 
 void simple_serial_send_packet(const uint8_t cmd, const uint8_t *data,
                                size_t data_len) {
-  base_printf("%c", cmd);
+  char buf;
+  base_snprintf(&buf, 1, "%c", cmd);
+  IGNORE_RESULT(dif_uart_byte_send_polled(uart, buf));
   simple_serial_print_hex(data, data_len);
-  base_printf("\n");
+  base_snprintf(&buf, 1, "\n");
+  IGNORE_RESULT(dif_uart_byte_send_polled(uart, buf));
 }
 
 void simple_serial_send_status(uint8_t res) {
@@ -215,7 +219,10 @@
 }
 
 void simple_serial_print_hex(const uint8_t *data, size_t data_len) {
+  char buf[2];
   for (size_t i = 0; i < data_len; ++i) {
-    base_printf("%2x", data[i]);
+    base_snprintf(&buf[0], 2, "%2x", data[i]);
+    IGNORE_RESULT(dif_uart_byte_send_polled(uart, buf[0]));
+    IGNORE_RESULT(dif_uart_byte_send_polled(uart, buf[1]));
   }
 }
diff --git a/util/topgen/templates/chiplevel.sv.tpl b/util/topgen/templates/chiplevel.sv.tpl
index 2bd7596..5426c00 100644
--- a/util/topgen/templates/chiplevel.sv.tpl
+++ b/util/topgen/templates/chiplevel.sv.tpl
@@ -1060,11 +1060,6 @@
   // for verilator purposes, make these two the same.
   lc_ctrl_pkg::lc_tx_t lc_clk_bypass;
 
-% if target["name"] == "cw305":
-  // This is used for outputting the capture trigger
-  logic [pinmux_reg_pkg::NMioPads-1:0] mio_out_pre;
-% endif
-
 // TODO: align this with ASIC version to minimize the duplication.
 // Also need to add AST simulation and FPGA emulation models for things like entropy source -
 // otherwise Verilator / FPGA will hang.
@@ -1141,11 +1136,7 @@
 
     // Multiplexed I/O
     .mio_in_i        ( mio_in   ),
-% if target["name"] == "cw305":
-    .mio_out_o       ( mio_out_pre  ),
-% else:
     .mio_out_o       ( mio_out  ),
-% endif
     .mio_oe_o        ( mio_oe   ),
 
     // Dedicated I/O
@@ -1170,45 +1161,74 @@
   );
 % endif
 
-
 ###################################################################
-## CW305 capture board interface                                 ##
+## CW310/305 capture board interface                             ##
 ###################################################################
-## TODO: This needs to be adapted to enable captures on the CW310. In particular,
-## - a precise capture trigger and the target clock need to be output, and
-## - a separate UART should be used for the simpleserial communication with the capture board.
-## See also pins_cw310.xdc
-% if target["name"] in ["cw305"]:
+% if target["name"] in ["cw310", "cw305"]:
 
-  //////////////////////////////////////
-  // Generate precise capture trigger //
-  //////////////////////////////////////
-
-  // TODO: make this a "manual" IO specific to the CW305 target
-  // such that we can decouple this from the MIO signals.
-  localparam int MioIdxTrigger = 15;
-
-  // To obtain a more precise capture trigger for side-channel analysis, we only forward the
-  // software-controlled capture trigger when the AES module is actually busy (performing
-  // either encryption/decryption or clearing internal registers).
-  // GPIO15 is used as capture trigger (mapped to IOB6 at the moment in pinmux.c).
-  always_comb begin : p_trigger
-    mio_out = mio_out_pre;
-    mio_out[MioIdxTrigger] = mio_out_pre[MioIdxTrigger] &
-                             ~top_${top["name"]}.clkmgr_aon_idle[clkmgr_pkg::HintMainAes];
-  end
-
-  //////////////////////
-  // ChipWhisperer IO //
-  //////////////////////
+  /////////////////////////////////////////////////////
+  // ChipWhisperer CW310/305 Capture Board Interface //
+  /////////////////////////////////////////////////////
+  // This is used to interface OpenTitan as a target with a capture board trough the ChipWhisperer
+  // 20-pin connector. This is used for SCA/FI experiments only.
 
   logic unused_inputs;
-  assign unused_inputs = manual_in_tio_clkout ^ manual_in_io_utx_debug;
+  % if target["name"] == "cw305":
+  assign unused_inputs = manual_in_io_clkout ^ manual_in_io_trigger ^ manual_in_io_utx_debug;
+  % else:
+  assign unused_inputs = manual_in_io_clkout ^ manual_in_io_trigger;
+  % endif
 
-  // Clock output to capture board.
-  assign manual_out_tio_clkout = manual_in_io_clk;
-  assign manual_oe_tio_clkout = 1'b1;
+  // Synchronous clock output to capture board.
+  assign manual_out_io_clkout = manual_in_io_clk;
+  assign manual_oe_io_clkout = 1'b1;
 
+  // Capture trigger.
+  // We use the clkmgr_aon_idle signal of the IP of interest to form a precise capture trigger.
+  // GPIO[11:9] is used for selecting the IP of interest. The encoding is as follows (see
+  // hint_names_e enum in clkmgr_pkg.sv for details).
+  //
+  // IP              - GPIO[11:9] - Index for clkmgr_aon_idle
+  // ------------------------------------------------------------
+  //  AES            -   000      -  0
+  //  HMAC           -   001      -  1
+  //  KMAC           -   010      -  2 - not implemented on CW305
+  //  OTBN (IO_DIV4) -   011      -  3 - not implemented on CW305
+  //  OTBN           -   100      -  4 - not implemented on CW305
+  //
+  // In addition, GPIO8 is used for gating the capture trigger in software.
+  // Note that GPIO[11:8] are connected to LED[3:0] on the CW310.
+  // On the CW305, GPIO[9,8] are connected to LED[5,7].
+
+  clkmgr_pkg::hint_names_e trigger_sel;
+  % if target["name"] == "cw305":
+  assign trigger_sel = mio_out[MioOutGpioGpio9] ? clkmgr_pkg::HintMainHmac :
+                                                  clkmgr_pkg::HintMainAes;
+  % else:
+  always_comb begin : trigger_sel_mux
+    unique case ({mio_out[MioOutGpioGpio11], mio_out[MioOutGpioGpio10], mio_out[MioOutGpioGpio9]})
+      3'b000:  trigger_sel = clkmgr_pkg::HintMainAes;
+      3'b001:  trigger_sel = clkmgr_pkg::HintMainHmac;
+      3'b010:  trigger_sel = clkmgr_pkg::HintMainKmac;
+      3'b011:  trigger_sel = clkmgr_pkg::HintIoDiv4Otbn;
+      3'b100:  trigger_sel = clkmgr_pkg::HintMainOtbn;
+      default: trigger_sel = clkmgr_pkg::HintMainAes;
+    endcase;
+  end
+  % endif
+  logic trigger, trigger_oe;
+  assign trigger = mio_out[MioOutGpioGpio8] & ~top_${top["name"]}.clkmgr_aon_idle[trigger_sel];
+  assign trigger_oe = mio_oe[MioOutGpioGpio8];
+
+  // Synchronize trigger to manual_in_io_clk.
+  prim_flop_2sync #(
+    .Width ( 2 )
+  ) u_sync_trigger (
+    .clk_i  ( manual_in_io_clk                              ),
+    .rst_ni ( manual_in_por_n                               ),
+    .d_i    ( {trigger,               trigger_oe}           ),
+    .q_o    ( {manual_out_io_trigger, manual_oe_io_trigger} )
+  );
 % endif
 ## This separate UART debugging output is needed for the CW305 only.
 % if target["name"] == "cw305":
@@ -1216,7 +1236,6 @@
   // UART Tx for debugging. The UART itself is connected to the capture board.
   assign manual_out_io_utx_debug = top_${top["name"]}.cio_uart0_tx_d2p;
   assign manual_oe_io_utx_debug = 1'b1;
-
 % endif
 
 endmodule : chip_${top["name"]}_${target["name"]}