diff --git a/ci.sh b/ci.sh
index 36a6dea30..04877dcd3 100755
--- a/ci.sh
+++ b/ci.sh
@@ -258,8 +258,12 @@ cargo batch \
     --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h503rb --out-dir out/tests/stm32h503rb \
     --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u083rc --out-dir out/tests/stm32u083rc \
     --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \
-    --- build --release --manifest-path tests/nrf52840/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \
-    --- build --release --manifest-path tests/nrf51422/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/nrf51-dk \
+    --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51422 --out-dir out/tests/nrf51422-dk \
+    --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52832 --out-dir out/tests/nrf52832-dk \
+    --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52833 --out-dir out/tests/nrf52833-dk \
+    --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840 --out-dir out/tests/nrf52840-dk \
+    --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340 --out-dir out/tests/nrf5340-dk \
+    --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160 --out-dir out/tests/nrf9160-dk \
     --- build --release --manifest-path tests/riscv32/Cargo.toml --target riscv32imac-unknown-none-elf \
     $BUILD_EXTRA
 
diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs
index d7f075722..a74b3157b 100644
--- a/embassy-nrf/src/gpiote.rs
+++ b/embassy-nrf/src/gpiote.rs
@@ -19,9 +19,9 @@ const CHANNEL_COUNT: usize = 4;
 /// Amount of GPIOTE channels in the chip.
 const CHANNEL_COUNT: usize = 8;
 
-#[cfg(any(feature = "nrf52833", feature = "nrf52840"))]
+#[cfg(any(feature = "nrf52833", feature = "nrf52840", feature = "_nrf5340"))]
 const PIN_COUNT: usize = 48;
-#[cfg(not(any(feature = "nrf52833", feature = "nrf52840")))]
+#[cfg(not(any(feature = "nrf52833", feature = "nrf52840", feature = "_nrf5340")))]
 const PIN_COUNT: usize = 32;
 
 #[allow(clippy::declare_interior_mutable_const)]
@@ -67,9 +67,9 @@ pub(crate) fn init(irq_prio: crate::interrupt::Priority) {
     // no latched GPIO detect in nrf51.
     #[cfg(not(feature = "_nrf51"))]
     {
-        #[cfg(any(feature = "nrf52833", feature = "nrf52840"))]
+        #[cfg(any(feature = "nrf52833", feature = "nrf52840", feature = "_nrf5340"))]
         let ports = unsafe { &[&*pac::P0::ptr(), &*pac::P1::ptr()] };
-        #[cfg(not(any(feature = "_nrf51", feature = "nrf52833", feature = "nrf52840")))]
+        #[cfg(not(any(feature = "_nrf51", feature = "nrf52833", feature = "nrf52840", feature = "_nrf5340")))]
         let ports = unsafe { &[&*pac::P0::ptr()] };
 
         for &p in ports {
@@ -130,9 +130,9 @@ unsafe fn handle_gpiote_interrupt() {
     if g.events_port.read().bits() != 0 {
         g.events_port.write(|w| w);
 
-        #[cfg(any(feature = "nrf52833", feature = "nrf52840"))]
+        #[cfg(any(feature = "nrf52833", feature = "nrf52840", feature = "_nrf5340"))]
         let ports = &[&*pac::P0::ptr(), &*pac::P1::ptr()];
-        #[cfg(not(any(feature = "_nrf51", feature = "nrf52833", feature = "nrf52840")))]
+        #[cfg(not(any(feature = "_nrf51", feature = "nrf52833", feature = "nrf52840", feature = "_nrf5340")))]
         let ports = &[&*pac::P0::ptr()];
         #[cfg(feature = "_nrf51")]
         let ports = unsafe { &[&*pac::GPIO::ptr()] };
@@ -214,7 +214,7 @@ impl<'d> InputChannel<'d> {
                 InputChannelPolarity::None => w.mode().event().polarity().none(),
                 InputChannelPolarity::Toggle => w.mode().event().polarity().toggle(),
             };
-            #[cfg(any(feature = "nrf52833", feature = "nrf52840"))]
+            #[cfg(any(feature = "nrf52833", feature = "nrf52840", feature = "_nrf5340"))]
             w.port().bit(match pin.pin.pin.port() {
                 crate::gpio::Port::Port0 => false,
                 crate::gpio::Port::Port1 => true,
@@ -288,7 +288,7 @@ impl<'d> OutputChannel<'d> {
                 OutputChannelPolarity::Clear => w.polarity().hi_to_lo(),
                 OutputChannelPolarity::Toggle => w.polarity().toggle(),
             };
-            #[cfg(any(feature = "nrf52833", feature = "nrf52840"))]
+            #[cfg(any(feature = "nrf52833", feature = "nrf52840", feature = "_nrf5340"))]
             w.port().bit(match pin.pin.pin.port() {
                 crate::gpio::Port::Port0 => false,
                 crate::gpio::Port::Port1 => true,
diff --git a/tests/nrf52840/.cargo/config.toml b/tests/nrf/.cargo/config.toml
similarity index 80%
rename from tests/nrf52840/.cargo/config.toml
rename to tests/nrf/.cargo/config.toml
index 9d6b0313a..8f9bccbc0 100644
--- a/tests/nrf52840/.cargo/config.toml
+++ b/tests/nrf/.cargo/config.toml
@@ -3,7 +3,9 @@
 runner = "teleprobe client run"
 
 [build]
+#target = "thumbv6m-none-eabi"
 target = "thumbv7em-none-eabi"
+#target = "thumbv8m.main-none-eabihf"
 
 [env]
 DEFMT_LOG = "trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,smoltcp=info"
diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml
new file mode 100644
index 000000000..2faee70e3
--- /dev/null
+++ b/tests/nrf/Cargo.toml
@@ -0,0 +1,98 @@
+[package]
+edition = "2021"
+name = "embassy-nrf-examples"
+version = "0.1.0"
+license = "MIT OR Apache-2.0"
+
+[dependencies]
+teleprobe-meta = "1"
+
+embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
+embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt", ] }
+embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "task-arena-size-16384", "integrated-timers"] }
+embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt",  "defmt-timestamp-uptime"] }
+embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt",  "time-driver-rtc1", "gpiote", "unstable-pac"] }
+embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
+embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] }
+embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] }
+embassy-net-enc28j60 = { version = "0.1.0", path = "../../embassy-net-enc28j60", features = ["defmt"] }
+embedded-hal-async = { version = "1.0" }
+embedded-hal-bus = { version = "0.1", features = ["async"] }
+static_cell = "2"
+perf-client = { path = "../perf-client" }
+
+defmt = "0.3"
+defmt-rtt = "0.4"
+
+cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
+cortex-m-rt = "0.7.0"
+panic-probe = { version = "0.3", features = ["print-defmt"] }
+portable-atomic = { version = "1.6.0" }
+
+[features]
+nrf51422 = ["embassy-nrf/nrf51", "portable-atomic/unsafe-assume-single-core"]
+nrf52832 = ["embassy-nrf/nrf52832",      "easydma"]
+nrf52833 = ["embassy-nrf/nrf52833",      "easydma"]
+nrf52840 = ["embassy-nrf/nrf52840",      "easydma"]
+nrf5340  = ["embassy-nrf/nrf5340-app-s", "easydma"]
+nrf9160  = ["embassy-nrf/nrf9160-s",     "easydma"]
+
+easydma = []
+
+[profile.release]
+codegen-units = 1
+debug = 2
+debug-assertions = false
+incremental = false
+lto = "fat"
+opt-level = 's'
+overflow-checks = false
+
+# BEGIN TESTS
+# Generated by gen_test.py. DO NOT EDIT.
+[[bin]]
+name = "buffered_uart"
+path = "src/bin/buffered_uart.rs"
+required-features = [ "nrf52840",]
+
+[[bin]]
+name = "buffered_uart_full"
+path = "src/bin/buffered_uart_full.rs"
+required-features = [ "nrf52840",]
+
+[[bin]]
+name = "buffered_uart_halves"
+path = "src/bin/buffered_uart_halves.rs"
+required-features = [ "nrf52840",]
+
+[[bin]]
+name = "buffered_uart_spam"
+path = "src/bin/buffered_uart_spam.rs"
+required-features = [ "nrf52840",]
+
+[[bin]]
+name = "ethernet_enc28j60_perf"
+path = "src/bin/ethernet_enc28j60_perf.rs"
+required-features = [ "nrf52840",]
+
+[[bin]]
+name = "gpio"
+path = "src/bin/gpio.rs"
+required-features = []
+
+[[bin]]
+name = "gpiote"
+path = "src/bin/gpiote.rs"
+required-features = []
+
+[[bin]]
+name = "timer"
+path = "src/bin/timer.rs"
+required-features = []
+
+[[bin]]
+name = "wifi_esp_hosted_perf"
+path = "src/bin/wifi_esp_hosted_perf.rs"
+required-features = [ "nrf52840",]
+
+# END TESTS
diff --git a/tests/nrf/build.rs b/tests/nrf/build.rs
new file mode 100644
index 000000000..3c15cf10f
--- /dev/null
+++ b/tests/nrf/build.rs
@@ -0,0 +1,37 @@
+use std::error::Error;
+use std::path::PathBuf;
+use std::{env, fs};
+
+fn main() -> Result<(), Box<dyn Error>> {
+    let out = PathBuf::from(env::var("OUT_DIR").unwrap());
+
+    // copy the right memory.x
+    #[cfg(feature = "nrf51422")]
+    let memory_x = include_bytes!("memory-nrf51422.x");
+    #[cfg(feature = "nrf52832")]
+    let memory_x = include_bytes!("memory-nrf52832.x");
+    #[cfg(feature = "nrf52833")]
+    let memory_x = include_bytes!("memory-nrf52833.x");
+    #[cfg(feature = "nrf52840")]
+    let memory_x = include_bytes!("memory-nrf52840.x");
+    #[cfg(feature = "nrf5340")]
+    let memory_x = include_bytes!("memory-nrf5340.x");
+    #[cfg(feature = "nrf9160")]
+    let memory_x = include_bytes!("memory-nrf9160.x");
+    fs::write(out.join("memory.x"), memory_x).unwrap();
+
+    // copy main linker script.
+    fs::write(out.join("link_ram.x"), include_bytes!("../link_ram_cortex_m.x")).unwrap();
+    println!("cargo:rustc-link-search={}", out.display());
+    println!("cargo:rerun-if-changed=link_ram.x");
+
+    println!("cargo:rustc-link-arg-bins=--nmagic");
+    #[cfg(feature = "nrf51422")]
+    println!("cargo:rustc-link-arg-bins=-Tlink.x");
+    #[cfg(not(feature = "nrf51422"))]
+    println!("cargo:rustc-link-arg-bins=-Tlink_ram.x");
+    println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
+    println!("cargo:rustc-link-arg-bins=-Tteleprobe.x");
+
+    Ok(())
+}
diff --git a/tests/nrf/gen_test.py b/tests/nrf/gen_test.py
new file mode 100644
index 000000000..daf714376
--- /dev/null
+++ b/tests/nrf/gen_test.py
@@ -0,0 +1,44 @@
+import os
+import toml
+from glob import glob
+
+abspath = os.path.abspath(__file__)
+dname = os.path.dirname(abspath)
+os.chdir(dname)
+
+# ======= load test list
+tests = {}
+for f in sorted(glob('./src/bin/*.rs')):
+    name = os.path.splitext(os.path.basename(f))[0]
+    features = []
+    with open(f, 'r') as f:
+        for line in f:
+            if line.startswith('// required-features:'):
+                features = [feature.strip() for feature in line.split(':', 2)[1].strip().split(',')]
+
+    tests[name] = features
+
+# ========= Update Cargo.toml
+
+things = {
+    'bin': [
+        {
+            'name': f'{name}',
+            'path': f'src/bin/{name}.rs',
+            'required-features': features,
+        }
+        for name, features in tests.items()
+    ]
+}
+
+SEPARATOR_START = '# BEGIN TESTS\n'
+SEPARATOR_END = '# END TESTS\n'
+HELP = '# Generated by gen_test.py. DO NOT EDIT.\n'
+with open('Cargo.toml', 'r') as f:
+    data = f.read()
+before, data = data.split(SEPARATOR_START, maxsplit=1)
+_, after = data.split(SEPARATOR_END, maxsplit=1)
+data = before + SEPARATOR_START + HELP + \
+    toml.dumps(things) + SEPARATOR_END + after
+with open('Cargo.toml', 'w') as f:
+    f.write(data)
diff --git a/tests/nrf/memory-nrf51422.x b/tests/nrf/memory-nrf51422.x
new file mode 100644
index 000000000..c140005ce
--- /dev/null
+++ b/tests/nrf/memory-nrf51422.x
@@ -0,0 +1,5 @@
+MEMORY
+{
+  FLASH : ORIGIN = 0x00000000, LENGTH = 256K
+  RAM : ORIGIN = 0x20000000, LENGTH = 32K
+}
diff --git a/tests/nrf/memory-nrf52832.x b/tests/nrf/memory-nrf52832.x
new file mode 100644
index 000000000..c140005ce
--- /dev/null
+++ b/tests/nrf/memory-nrf52832.x
@@ -0,0 +1,5 @@
+MEMORY
+{
+  FLASH : ORIGIN = 0x00000000, LENGTH = 256K
+  RAM : ORIGIN = 0x20000000, LENGTH = 32K
+}
diff --git a/tests/nrf/memory-nrf52833.x b/tests/nrf/memory-nrf52833.x
new file mode 100644
index 000000000..a4baa2dc4
--- /dev/null
+++ b/tests/nrf/memory-nrf52833.x
@@ -0,0 +1,5 @@
+MEMORY
+{
+  FLASH : ORIGIN = 0x00000000, LENGTH = 512K
+  RAM : ORIGIN = 0x20000000, LENGTH = 64K
+}
diff --git a/tests/nrf52840/memory.x b/tests/nrf/memory-nrf52840.x
similarity index 100%
rename from tests/nrf52840/memory.x
rename to tests/nrf/memory-nrf52840.x
diff --git a/tests/nrf/memory-nrf5340.x b/tests/nrf/memory-nrf5340.x
new file mode 100644
index 000000000..58900a7bd
--- /dev/null
+++ b/tests/nrf/memory-nrf5340.x
@@ -0,0 +1,5 @@
+MEMORY
+{
+  FLASH : ORIGIN = 0x00000000, LENGTH = 1024K
+  RAM : ORIGIN = 0x20000000, LENGTH = 256K
+}
diff --git a/tests/nrf/memory-nrf9160.x b/tests/nrf/memory-nrf9160.x
new file mode 100644
index 000000000..58900a7bd
--- /dev/null
+++ b/tests/nrf/memory-nrf9160.x
@@ -0,0 +1,5 @@
+MEMORY
+{
+  FLASH : ORIGIN = 0x00000000, LENGTH = 1024K
+  RAM : ORIGIN = 0x20000000, LENGTH = 256K
+}
diff --git a/tests/nrf52840/src/bin/buffered_uart.rs b/tests/nrf/src/bin/buffered_uart.rs
similarity index 98%
rename from tests/nrf52840/src/bin/buffered_uart.rs
rename to tests/nrf/src/bin/buffered_uart.rs
index a01d66d85..89314bfc9 100644
--- a/tests/nrf52840/src/bin/buffered_uart.rs
+++ b/tests/nrf/src/bin/buffered_uart.rs
@@ -1,3 +1,4 @@
+// required-features: nrf52840
 #![no_std]
 #![no_main]
 teleprobe_meta::target!(b"nrf52840-dk");
diff --git a/tests/nrf52840/src/bin/buffered_uart_full.rs b/tests/nrf/src/bin/buffered_uart_full.rs
similarity index 98%
rename from tests/nrf52840/src/bin/buffered_uart_full.rs
rename to tests/nrf/src/bin/buffered_uart_full.rs
index 62edaed25..a7d9a3717 100644
--- a/tests/nrf52840/src/bin/buffered_uart_full.rs
+++ b/tests/nrf/src/bin/buffered_uart_full.rs
@@ -1,3 +1,4 @@
+// required-features: nrf52840
 #![no_std]
 #![no_main]
 teleprobe_meta::target!(b"nrf52840-dk");
diff --git a/tests/nrf52840/src/bin/buffered_uart_halves.rs b/tests/nrf/src/bin/buffered_uart_halves.rs
similarity index 98%
rename from tests/nrf52840/src/bin/buffered_uart_halves.rs
rename to tests/nrf/src/bin/buffered_uart_halves.rs
index 54a9fef5b..ae1021f04 100644
--- a/tests/nrf52840/src/bin/buffered_uart_halves.rs
+++ b/tests/nrf/src/bin/buffered_uart_halves.rs
@@ -1,3 +1,4 @@
+// required-features: nrf52840
 #![no_std]
 #![no_main]
 teleprobe_meta::target!(b"nrf52840-dk");
diff --git a/tests/nrf52840/src/bin/buffered_uart_spam.rs b/tests/nrf/src/bin/buffered_uart_spam.rs
similarity index 98%
rename from tests/nrf52840/src/bin/buffered_uart_spam.rs
rename to tests/nrf/src/bin/buffered_uart_spam.rs
index 400c0df99..906723229 100644
--- a/tests/nrf52840/src/bin/buffered_uart_spam.rs
+++ b/tests/nrf/src/bin/buffered_uart_spam.rs
@@ -1,3 +1,4 @@
+// required-features: nrf52840
 #![no_std]
 #![no_main]
 teleprobe_meta::target!(b"nrf52840-dk");
diff --git a/tests/nrf52840/src/bin/ethernet_enc28j60_perf.rs b/tests/nrf/src/bin/ethernet_enc28j60_perf.rs
similarity index 98%
rename from tests/nrf52840/src/bin/ethernet_enc28j60_perf.rs
rename to tests/nrf/src/bin/ethernet_enc28j60_perf.rs
index 33c2f4235..5f4220b1e 100644
--- a/tests/nrf52840/src/bin/ethernet_enc28j60_perf.rs
+++ b/tests/nrf/src/bin/ethernet_enc28j60_perf.rs
@@ -1,3 +1,4 @@
+// required-features: nrf52840
 #![no_std]
 #![no_main]
 teleprobe_meta::target!(b"ak-gwe-r7");
diff --git a/tests/nrf51422/src/bin/gpio.rs b/tests/nrf/src/bin/gpio.rs
similarity index 71%
rename from tests/nrf51422/src/bin/gpio.rs
rename to tests/nrf/src/bin/gpio.rs
index 6d5a87d0a..9e809a694 100644
--- a/tests/nrf51422/src/bin/gpio.rs
+++ b/tests/nrf/src/bin/gpio.rs
@@ -1,19 +1,20 @@
 #![no_std]
 #![no_main]
-teleprobe_meta::target!(b"nrf51-dk");
+
+#[path = "../common.rs"]
+mod common;
 
 use defmt::{assert, info};
 use embassy_executor::Spawner;
 use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull};
 use embassy_time::Timer;
-use {defmt_rtt as _, panic_probe as _};
 
 #[embassy_executor::main]
 async fn main(_spawner: Spawner) {
     let p = embassy_nrf::init(Default::default());
 
-    let input = Input::new(p.P0_13, Pull::Up);
-    let mut output = Output::new(p.P0_14, Level::Low, OutputDrive::Standard);
+    let input = Input::new(peri!(p, PIN_A), Pull::Up);
+    let mut output = Output::new(peri!(p, PIN_B), Level::Low, OutputDrive::Standard);
 
     output.set_low();
     Timer::after_millis(10).await;
diff --git a/tests/nrf51422/src/bin/gpiote.rs b/tests/nrf/src/bin/gpiote.rs
similarity index 80%
rename from tests/nrf51422/src/bin/gpiote.rs
rename to tests/nrf/src/bin/gpiote.rs
index 330fe993e..0700016d1 100644
--- a/tests/nrf51422/src/bin/gpiote.rs
+++ b/tests/nrf/src/bin/gpiote.rs
@@ -1,20 +1,21 @@
 #![no_std]
 #![no_main]
-teleprobe_meta::target!(b"nrf51-dk");
+
+#[path = "../common.rs"]
+mod common;
 
 use defmt::{assert, info};
 use embassy_executor::Spawner;
 use embassy_futures::join::join;
 use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull};
 use embassy_time::{Duration, Instant, Timer};
-use {defmt_rtt as _, panic_probe as _};
 
 #[embassy_executor::main]
 async fn main(_spawner: Spawner) {
     let p = embassy_nrf::init(Default::default());
 
-    let mut input = Input::new(p.P0_13, Pull::Up);
-    let mut output = Output::new(p.P0_14, Level::Low, OutputDrive::Standard);
+    let mut input = Input::new(peri!(p, PIN_A), Pull::Up);
+    let mut output = Output::new(peri!(p, PIN_B), Level::Low, OutputDrive::Standard);
 
     let fut1 = async {
         Timer::after_millis(100).await;
@@ -24,6 +25,7 @@ async fn main(_spawner: Spawner) {
         let start = Instant::now();
         input.wait_for_high().await;
         let dur = Instant::now() - start;
+        info!("took {} ms", dur.as_millis());
         assert!((Duration::from_millis(90)..Duration::from_millis(110)).contains(&dur));
     };
 
@@ -37,6 +39,7 @@ async fn main(_spawner: Spawner) {
         let start = Instant::now();
         input.wait_for_low().await;
         let dur = Instant::now() - start;
+        info!("took {} ms", dur.as_millis());
         assert!((Duration::from_millis(90)..Duration::from_millis(110)).contains(&dur));
     };
 
diff --git a/tests/nrf51422/src/bin/timer.rs b/tests/nrf/src/bin/timer.rs
similarity index 93%
rename from tests/nrf51422/src/bin/timer.rs
rename to tests/nrf/src/bin/timer.rs
index cf9ea41a8..1ae9dd647 100644
--- a/tests/nrf51422/src/bin/timer.rs
+++ b/tests/nrf/src/bin/timer.rs
@@ -1,6 +1,8 @@
 #![no_std]
 #![no_main]
-teleprobe_meta::target!(b"nrf51-dk");
+
+#[path = "../common.rs"]
+mod common;
 
 use defmt::{assert, info};
 use embassy_executor::Spawner;
diff --git a/tests/nrf52840/src/bin/wifi_esp_hosted_perf.rs b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs
similarity index 99%
rename from tests/nrf52840/src/bin/wifi_esp_hosted_perf.rs
rename to tests/nrf/src/bin/wifi_esp_hosted_perf.rs
index b83edddc4..a6c93c8a6 100644
--- a/tests/nrf52840/src/bin/wifi_esp_hosted_perf.rs
+++ b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs
@@ -1,3 +1,4 @@
+// required-features: nrf52840
 #![no_std]
 #![no_main]
 teleprobe_meta::target!(b"nrf52840-dk");
diff --git a/tests/nrf/src/common.rs b/tests/nrf/src/common.rs
new file mode 100644
index 000000000..79336c5de
--- /dev/null
+++ b/tests/nrf/src/common.rs
@@ -0,0 +1,65 @@
+#![macro_use]
+
+use {defmt_rtt as _, panic_probe as _};
+
+#[cfg(feature = "nrf52832")]
+teleprobe_meta::target!(b"nrf52832-dk");
+#[cfg(feature = "nrf52840")]
+teleprobe_meta::target!(b"nrf52840-dk");
+#[cfg(feature = "nrf52833")]
+teleprobe_meta::target!(b"nrf52833-dk");
+#[cfg(feature = "nrf5340")]
+teleprobe_meta::target!(b"nrf5340-dk");
+#[cfg(feature = "nrf9160")]
+teleprobe_meta::target!(b"nrf9160-dk");
+#[cfg(feature = "nrf51422")]
+teleprobe_meta::target!(b"nrf51-dk");
+
+macro_rules! define_peris {
+    ($($name:ident = $peri:ident,)* $(@irq $irq_name:ident = $irq_code:tt,)*) => {
+        #[allow(unused_macros)]
+        macro_rules! peri {
+            $(
+                ($p:expr, $name) => {
+                    $p.$peri
+                };
+            )*
+        }
+        #[allow(unused_macros)]
+        macro_rules! irqs {
+            $(
+                ($irq_name) => {{
+                    embassy_nrf::bind_interrupts!(struct Irqs $irq_code);
+                    Irqs
+                }};
+            )*
+            ( @ dummy ) => {};
+        }
+
+        #[allow(unused)]
+        #[allow(non_camel_case_types)]
+        pub mod peris {
+            $(
+                pub type $name = embassy_nrf::peripherals::$peri;
+            )*
+        }
+    };
+}
+
+#[cfg(feature = "nrf51422")]
+define_peris!(PIN_A = P0_13, PIN_B = P0_14,);
+
+#[cfg(feature = "nrf52832")]
+define_peris!(PIN_A = P0_11, PIN_B = P0_12,);
+
+#[cfg(feature = "nrf52833")]
+define_peris!(PIN_A = P1_01, PIN_B = P1_02,);
+
+#[cfg(feature = "nrf52840")]
+define_peris!(PIN_A = P1_02, PIN_B = P1_03,);
+
+#[cfg(feature = "nrf5340")]
+define_peris!(PIN_A = P1_00, PIN_B = P1_01,);
+
+#[cfg(feature = "nrf9160")]
+define_peris!(PIN_A = P0_00, PIN_B = P0_01,);
diff --git a/tests/nrf51422/.cargo/config.toml b/tests/nrf51422/.cargo/config.toml
deleted file mode 100644
index 634805633..000000000
--- a/tests/nrf51422/.cargo/config.toml
+++ /dev/null
@@ -1,9 +0,0 @@
-[target.'cfg(all(target_arch = "arm", target_os = "none"))']
-#runner = "teleprobe local run --chip nRF51422_xxAA --elf"
-runner = "teleprobe client run"
-
-[build]
-target = "thumbv6m-none-eabi"
-
-[env]
-DEFMT_LOG = "trace,embassy_hal_internal=debug"
diff --git a/tests/nrf51422/Cargo.toml b/tests/nrf51422/Cargo.toml
deleted file mode 100644
index 8bfcc2922..000000000
--- a/tests/nrf51422/Cargo.toml
+++ /dev/null
@@ -1,23 +0,0 @@
-[package]
-edition = "2021"
-name = "embassy-nrf51-tests"
-version = "0.1.0"
-license = "MIT OR Apache-2.0"
-
-[dependencies]
-teleprobe-meta = "1"
-
-embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
-embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt", ] }
-embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "task-arena-size-128", "integrated-timers"] }
-embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt",  "defmt-timestamp-uptime"] }
-embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt",  "nrf51", "time-driver-rtc1", "unstable-pac", "time", "gpiote"] }
-embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
-embedded-hal-async = { version = "1.0" }
-
-defmt = "0.3"
-defmt-rtt = "0.4"
-
-cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
-cortex-m-rt = "0.7.0"
-panic-probe = { version = "0.3", features = ["print-defmt"] }
diff --git a/tests/nrf51422/build.rs b/tests/nrf51422/build.rs
deleted file mode 100644
index 13ebbe4ee..000000000
--- a/tests/nrf51422/build.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-use std::error::Error;
-use std::path::PathBuf;
-use std::{env, fs};
-
-fn main() -> Result<(), Box<dyn Error>> {
-    let out = PathBuf::from(env::var("OUT_DIR").unwrap());
-    fs::write(out.join("memory.x"), include_bytes!("memory.x")).unwrap();
-    println!("cargo:rustc-link-search={}", out.display());
-    println!("cargo:rerun-if-changed=memory.x");
-
-    println!("cargo:rustc-link-arg-bins=--nmagic");
-    println!("cargo:rustc-link-arg-bins=-Tlink.x");
-    println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
-    println!("cargo:rustc-link-arg-bins=-Tteleprobe.x");
-
-    Ok(())
-}
diff --git a/tests/nrf51422/memory.x b/tests/nrf51422/memory.x
deleted file mode 100644
index a5881e66f..000000000
--- a/tests/nrf51422/memory.x
+++ /dev/null
@@ -1,5 +0,0 @@
-MEMORY
-{
-  FLASH : ORIGIN = 0x00000000, LENGTH = 128K
-  RAM : ORIGIN = 0x20000000, LENGTH = 16K
-}
diff --git a/tests/nrf52840/Cargo.toml b/tests/nrf52840/Cargo.toml
deleted file mode 100644
index 1670b92ad..000000000
--- a/tests/nrf52840/Cargo.toml
+++ /dev/null
@@ -1,29 +0,0 @@
-[package]
-edition = "2021"
-name = "embassy-nrf-examples"
-version = "0.1.0"
-license = "MIT OR Apache-2.0"
-
-[dependencies]
-teleprobe-meta = "1"
-
-embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
-embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt", ] }
-embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "task-arena-size-16384", "integrated-timers"] }
-embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt",  "defmt-timestamp-uptime"] }
-embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt",  "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
-embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
-embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] }
-embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] }
-embassy-net-enc28j60 = { version = "0.1.0", path = "../../embassy-net-enc28j60", features = ["defmt"] }
-embedded-hal-async = { version = "1.0" }
-embedded-hal-bus = { version = "0.1", features = ["async"] }
-static_cell = "2"
-perf-client = { path = "../perf-client" }
-
-defmt = "0.3"
-defmt-rtt = "0.4"
-
-cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
-cortex-m-rt = "0.7.0"
-panic-probe = { version = "0.3", features = ["print-defmt"] }
diff --git a/tests/nrf52840/build.rs b/tests/nrf52840/build.rs
deleted file mode 100644
index 71c82a70f..000000000
--- a/tests/nrf52840/build.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-use std::error::Error;
-use std::path::PathBuf;
-use std::{env, fs};
-
-fn main() -> Result<(), Box<dyn Error>> {
-    let out = PathBuf::from(env::var("OUT_DIR").unwrap());
-    fs::write(out.join("link_ram.x"), include_bytes!("../link_ram_cortex_m.x")).unwrap();
-    println!("cargo:rustc-link-search={}", out.display());
-    println!("cargo:rerun-if-changed=link_ram.x");
-
-    println!("cargo:rustc-link-arg-bins=--nmagic");
-    println!("cargo:rustc-link-arg-bins=-Tlink_ram.x");
-    println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
-    println!("cargo:rustc-link-arg-bins=-Tteleprobe.x");
-
-    Ok(())
-}
diff --git a/tests/nrf52840/src/bin/timer.rs b/tests/nrf52840/src/bin/timer.rs
deleted file mode 100644
index 117947a94..000000000
--- a/tests/nrf52840/src/bin/timer.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-#![no_std]
-#![no_main]
-teleprobe_meta::target!(b"nrf52840-dk");
-
-use defmt::{assert, info};
-use embassy_executor::Spawner;
-use embassy_time::{Instant, Timer};
-use {defmt_rtt as _, panic_probe as _};
-
-#[embassy_executor::main]
-async fn main(_spawner: Spawner) {
-    let _p = embassy_nrf::init(Default::default());
-    info!("Hello World!");
-
-    let start = Instant::now();
-    Timer::after_millis(100).await;
-    let end = Instant::now();
-    let ms = (end - start).as_millis();
-    info!("slept for {} ms", ms);
-    assert!(ms >= 99);
-
-    info!("Test OK");
-    cortex_m::asm::bkpt();
-}