diff --git a/README.md b/README.md
index 1c9d6923147b8b64960a6f92dcc846c359d12af7..badbb05b9a1a22d857ae48fa6dd6190b375369a9 100644
--- a/README.md
+++ b/README.md
@@ -3,4 +3,5 @@
 - actually-realized data rates are often smaller in practice than according to underlying specs, 
   - see even i.e. transfer of some tens-of-megabytes onto an SD card (copying a gcode file) - we observe only ~ 20mb/sec of advertized 80mb/sec or so 
 - otherwise, you have notes on what-the-point is in your `scratch` 
-- and consider linking to the ring-test page 
\ No newline at end of file
+- and consider linking to the ring-test page 
+- and discuss that at the embedded limit, we often run into interrupt-request-handling-time troubles before anything else... 
\ No newline at end of file
diff --git a/rp2040_uart/2024-03_rp2040-uart.md b/rp2040_uart/2024-03_rp2040-uart.md
new file mode 100644
index 0000000000000000000000000000000000000000..613cf3ba9da1c8c9212bfdc898b70292e4384394
--- /dev/null
+++ b/rp2040_uart/2024-03_rp2040-uart.md
@@ -0,0 +1,15 @@
+## 2024 01 03
+
+Today I'd like to check out the RP2040's PIO via the UART example, and see how fast we can sendy UART frames between two devices, as a preliminary speed test. 
+
+So - we're wired up, we should hit the scope and then fire up some test code: display (?) uart PIO, blinky, etc ... 
+
+The first thing [I'm learning here](https://www.eevblog.com/forum/microcontrollers/8-uarts-using-asm_pio-pio-dma-micropython-on-the-rpi-pico/) and as shown in the [pio rx](https://github.com/raspberrypi/pico-examples/tree/master/pio/uart_rx) and [pio tx](https://github.com/raspberrypi/pico-examples/blob/master/pio/uart_tx/uart_tx.pio) examples is that each state machine can only do one half of a UART... TX or RX, not both as we are accustomed to with a UART peripheral. So, since we are interested in building our little router thing, we actually would only be able to max out at 6 total UARTS there: 2x the-og-peripheral, and 4x PIOs. 
+
+The second thing [I'm learning](https://www.instructables.com/Using-RP2040-PIO-in-Arduino-IDE-on-Windows/) is that working with PIO in C is not *that* simple; we write a PIO block `uart_tx.pio` and then use a pio-assembler to write `uart_tx.pio.h` that we can include in our sketch. There is an [online pioasm instance](https://wokwi.com/tools/pioasm). Doing this on windows is a little bit of a pain - and means that we will have two things to call before we can upload code, but not a major-major roadblock. 
+
+So, as for reasonable goals for today, I should basically just try to throw-and-catch a block, really simple-like, to test baseline perf. 
+
+Well actually, fk it, I will use the online pioasm for the time being...
+
+And we're up with a test, I will find the BAUD limit next, and check if that is affected by changes to f_cpu... 
\ No newline at end of file
diff --git a/rp2040_uart/code/uart_pio/screen.cpp b/rp2040_uart/code/uart_pio/screen.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..45f33a49e0baa49ed6a53d6334f04f65afcb17b1
--- /dev/null
+++ b/rp2040_uart/code/uart_pio/screen.cpp
@@ -0,0 +1,39 @@
+#include "screen.h"
+#include <Adafruit_GFX.h>
+#include <Adafruit_SSD1306.h>
+#include <Wire.h>
+
+// OLED 
+#define SCREEN_WIDTH 128 // OLED display width, in pixels
+#define SCREEN_HEIGHT 64 // OLED display height, in pixels
+
+#define X_POS 0
+#define Y_POS 0
+#define TXT_SIZE 1
+
+// even for displays with i.e. "0x78" printed on the back, 
+// the address that works is 0x3C, IDK 
+#define SCREEN_ADDRESS 0x3C
+
+Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT);
+
+// warning: is blocking, takes ~ 33ms ! 
+void displayPrint(String msg){
+  display.clearDisplay();
+  display.setCursor(X_POS, Y_POS);
+  display.print(msg);
+  display.display();
+}
+
+void displaySetup(void){
+  // initialize the screen,
+  // oddly, SWITCHCAPVCC is the option that works even though OLED is hooked to 5V 
+  display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
+  display.clearDisplay();
+  display.display();
+  display.setTextColor(SSD1306_WHITE);
+  display.setTextSize(TXT_SIZE);
+  display.setTextWrap(true);
+  display.dim(false);
+  displayPrint("bonjour...");
+}
\ No newline at end of file
diff --git a/rp2040_uart/code/uart_pio/screen.h b/rp2040_uart/code/uart_pio/screen.h
new file mode 100644
index 0000000000000000000000000000000000000000..91fb08f0bd738345349813ee51aec5739694a9f9
--- /dev/null
+++ b/rp2040_uart/code/uart_pio/screen.h
@@ -0,0 +1,9 @@
+#ifndef SCREEN_H_
+#define SCREEN_H_
+
+#include <Arduino.h>
+
+void displaySetup(void);
+void displayPrint(String msg);
+
+#endif 
\ No newline at end of file
diff --git a/rp2040_uart/code/uart_pio/uart_pio.ino b/rp2040_uart/code/uart_pio/uart_pio.ino
new file mode 100644
index 0000000000000000000000000000000000000000..5e83bc668639d6eef3b32f46d35c0658c0d81d97
--- /dev/null
+++ b/rp2040_uart/code/uart_pio/uart_pio.ino
@@ -0,0 +1,52 @@
+#include "screen.h"
+#include "uart_tx.pio.h"
+
+// using an RP2040 XIAO 
+// with earle philhower core 
+
+// "D10" - GPIO 3 
+#define PIN_DEBUG 3 
+
+// on XIAO "TX" - GPIO 0 
+#define PIN_TX 0
+#define PIO_BAUD 115200
+
+// the PIO, and statemachine ? 
+PIO pio = pio0;
+uint sm = 0;
+uint offset = 0;
+
+void setup(void){
+  pinMode(PIN_LED_B, OUTPUT);
+  digitalWrite(PIN_LED_B, LOW);
+
+  pinMode(PIN_DEBUG, OUTPUT);
+  digitalWrite(PIN_DEBUG, LOW);
+
+  // the display setup 
+  displaySetup();
+  displayPrint("bonjour...");
+
+  offset = pio_add_program(pio, &uart_tx_program);
+  uart_tx_program_init(pio, sm, offset, PIN_TX, PIO_BAUD);
+}
+
+uint32_t lastUpdate = 0;
+uint32_t updateInterval = 200;
+
+void loop(void){
+  digitalWrite(PIN_DEBUG, HIGH);
+  // blocking tx-put: 
+  uart_tx_program_putc(pio, sm, 85);
+  digitalWrite(PIN_DEBUG, LOW);
+  // ... 
+  if(lastUpdate + updateInterval < millis()){
+    lastUpdate = millis();
+    digitalWrite(PIN_LED_B, !digitalRead(PIN_LED_B));
+    // displayPrint(spipi_print());
+    // displayPrint(String(rxCount) + "\n" + 
+    //   String(rxSize) 
+    // );
+  }
+}
+
diff --git a/rp2040_uart/code/uart_pio/uart_tx.pio b/rp2040_uart/code/uart_pio/uart_tx.pio
new file mode 100644
index 0000000000000000000000000000000000000000..b6f91a89f02e181671ba5732830c9fd6f627383c
--- /dev/null
+++ b/rp2040_uart/code/uart_pio/uart_tx.pio
@@ -0,0 +1,61 @@
+;
+; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+;
+; SPDX-License-Identifier: BSD-3-Clause
+;
+
+.program uart_tx
+.side_set 1 opt
+
+; An 8n1 UART transmit program.
+; OUT pin 0 and side-set pin 0 are both mapped to UART TX pin.
+
+    pull       side 1 [7]  ; Assert stop bit, or stall with line in idle state
+    set x, 7   side 0 [7]  ; Preload bit counter, assert start bit for 8 clocks
+bitloop:                   ; This loop will run 8 times (8n1 UART)
+    out pins, 1            ; Shift 1 bit from OSR to the first OUT pin
+    jmp x-- bitloop   [6]  ; Each loop iteration is 8 cycles.
+
+
+% c-sdk {
+#include "hardware/clocks.h"
+
+static inline void uart_tx_program_init(PIO pio, uint sm, uint offset, uint pin_tx, uint baud) {
+    // Tell PIO to initially drive output-high on the selected pin, then map PIO
+    // onto that pin with the IO muxes.
+    pio_sm_set_pins_with_mask(pio, sm, 1u << pin_tx, 1u << pin_tx);
+    pio_sm_set_pindirs_with_mask(pio, sm, 1u << pin_tx, 1u << pin_tx);
+    pio_gpio_init(pio, pin_tx);
+
+    pio_sm_config c = uart_tx_program_get_default_config(offset);
+
+    // OUT shifts to right, no autopull
+    sm_config_set_out_shift(&c, true, false, 32);
+
+    // We are mapping both OUT and side-set to the same pin, because sometimes
+    // we need to assert user data onto the pin (with OUT) and sometimes
+    // assert constant values (start/stop bit)
+    sm_config_set_out_pins(&c, pin_tx, 1);
+    sm_config_set_sideset_pins(&c, pin_tx);
+
+    // We only need TX, so get an 8-deep FIFO!
+    sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
+
+    // SM transmits 1 bit per 8 execution cycles.
+    float div = (float)clock_get_hz(clk_sys) / (8 * baud);
+    sm_config_set_clkdiv(&c, div);
+
+    pio_sm_init(pio, sm, offset, &c);
+    pio_sm_set_enabled(pio, sm, true);
+}
+
+static inline void uart_tx_program_putc(PIO pio, uint sm, char c) {
+    pio_sm_put_blocking(pio, sm, (uint32_t)c);
+}
+
+static inline void uart_tx_program_puts(PIO pio, uint sm, const char *s) {
+    while (*s)
+        uart_tx_program_putc(pio, sm, *s++);
+}
+
+%}
\ No newline at end of file
diff --git a/rp2040_uart/code/uart_pio/uart_tx.pio.h b/rp2040_uart/code/uart_pio/uart_tx.pio.h
new file mode 100644
index 0000000000000000000000000000000000000000..30646d9d4d049df54e0763be4b20cb0c47cd463f
--- /dev/null
+++ b/rp2040_uart/code/uart_pio/uart_tx.pio.h
@@ -0,0 +1,72 @@
+// -------------------------------------------------- //
+// This file is autogenerated by pioasm; do not edit! //
+// -------------------------------------------------- //
+
+#pragma once
+
+#if !PICO_NO_HARDWARE
+#include "hardware/pio.h"
+#endif
+
+// ------- //
+// uart_tx //
+// ------- //
+
+#define uart_tx_wrap_target 0
+#define uart_tx_wrap 3
+
+static const uint16_t uart_tx_program_instructions[] = {
+            //     .wrap_target
+    0x9fa0, //  0: pull   block           side 1 [7] 
+    0xf727, //  1: set    x, 7            side 0 [7] 
+    0x6001, //  2: out    pins, 1                    
+    0x0642, //  3: jmp    x--, 2                 [6] 
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program uart_tx_program = {
+    .instructions = uart_tx_program_instructions,
+    .length = 4,
+    .origin = -1,
+};
+
+static inline pio_sm_config uart_tx_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + uart_tx_wrap_target, offset + uart_tx_wrap);
+    sm_config_set_sideset(&c, 2, true, false);
+    return c;
+}
+
+#include "hardware/clocks.h"
+static inline void uart_tx_program_init(PIO pio, uint sm, uint offset, uint pin_tx, uint baud) {
+    // Tell PIO to initially drive output-high on the selected pin, then map PIO
+    // onto that pin with the IO muxes.
+    pio_sm_set_pins_with_mask(pio, sm, 1u << pin_tx, 1u << pin_tx);
+    pio_sm_set_pindirs_with_mask(pio, sm, 1u << pin_tx, 1u << pin_tx);
+    pio_gpio_init(pio, pin_tx);
+    pio_sm_config c = uart_tx_program_get_default_config(offset);
+    // OUT shifts to right, no autopull
+    sm_config_set_out_shift(&c, true, false, 32);
+    // We are mapping both OUT and side-set to the same pin, because sometimes
+    // we need to assert user data onto the pin (with OUT) and sometimes
+    // assert constant values (start/stop bit)
+    sm_config_set_out_pins(&c, pin_tx, 1);
+    sm_config_set_sideset_pins(&c, pin_tx);
+    // We only need TX, so get an 8-deep FIFO!
+    sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
+    // SM transmits 1 bit per 8 execution cycles.
+    float div = (float)clock_get_hz(clk_sys) / (8 * baud);
+    sm_config_set_clkdiv(&c, div);
+    pio_sm_init(pio, sm, offset, &c);
+    pio_sm_set_enabled(pio, sm, true);
+}
+static inline void uart_tx_program_putc(PIO pio, uint sm, char c) {
+    pio_sm_put_blocking(pio, sm, (uint32_t)c);
+}
+static inline void uart_tx_program_puts(PIO pio, uint sm, const char *s) {
+    while (*s)
+        uart_tx_program_putc(pio, sm, *s++);
+}
+
+#endif