diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml
index bfe5bc823..2b01def5c 100644
--- a/tests/stm32/Cargo.toml
+++ b/tests/stm32/Cargo.toml
@@ -7,13 +7,13 @@ autobins = false
 
 [features]
 stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"]     # Blue Pill
-stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "stop", "can", "not-gpdma", "dac-adc-pin"]     # Nucleo "sdmmc"
+stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "eth", "stop", "can", "not-gpdma", "dac-adc-pin"]     # Nucleo "sdmmc"
 stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma", "dac-adc-pin"]     # Nucleo
 stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"]     # Nucleo
 stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"]     # Nucleo
-stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "not-gpdma", "dac-adc-pin"] # Nucleo
+stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "not-gpdma", "eth", "dac-adc-pin"] # Nucleo
 stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma", "ble", "mac" ]     # Nucleo
-stm32h563zi = ["embassy-stm32/stm32h563zi"]     # Nucleo
+stm32h563zi = ["embassy-stm32/stm32h563zi", "eth"]     # Nucleo
 stm32u585ai = ["embassy-stm32/stm32u585ai"]     # IoT board
 stm32l073rz = ["embassy-stm32/stm32l073rz", "not-gpdma"]     # Nucleo
 stm32l152re = ["embassy-stm32/stm32l152re", "not-gpdma"]     # Nucleo
@@ -21,6 +21,7 @@ stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "not-gpdma"]     # Nucleo
 stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "not-gpdma"]     # Nucleo
 stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma"]     # Nucleo
 
+eth = []
 sdmmc = []
 stop = ["embassy-stm32/low-power"]
 chrono = ["embassy-stm32/chrono", "dep:chrono"]
@@ -40,6 +41,8 @@ embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["de
 embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-any"]  }
 embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
 embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", optional = true, features = ["defmt", "stm32wb55rg", "ble"] }
+embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] }
+perf-client = { path = "../perf-client" }
 
 defmt = "0.3.0"
 defmt-rtt = "0.4"
@@ -69,6 +72,11 @@ name = "dac"
 path = "src/bin/dac.rs"
 required-features = [ "dac-adc-pin",]
 
+[[bin]]
+name = "eth"
+path = "src/bin/eth.rs"
+required-features = [ "eth",]
+
 [[bin]]
 name = "gpio"
 path = "src/bin/gpio.rs"
diff --git a/tests/stm32/src/bin/eth.rs b/tests/stm32/src/bin/eth.rs
new file mode 100644
index 000000000..0b32b60b3
--- /dev/null
+++ b/tests/stm32/src/bin/eth.rs
@@ -0,0 +1,113 @@
+// required-features: eth
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+#[path = "../common.rs"]
+mod common;
+use common::*;
+use embassy_executor::Spawner;
+use embassy_net::{Stack, StackResources};
+use embassy_stm32::eth::generic_smi::GenericSMI;
+use embassy_stm32::eth::{Ethernet, PacketQueue};
+use embassy_stm32::peripherals::ETH;
+use embassy_stm32::rng::Rng;
+use embassy_stm32::{bind_interrupts, eth, peripherals, rng};
+use rand_core::RngCore;
+use static_cell::make_static;
+use {defmt_rtt as _, panic_probe as _};
+
+teleprobe_meta::timeout!(120);
+
+#[cfg(not(feature = "stm32h563zi"))]
+bind_interrupts!(struct Irqs {
+    ETH => eth::InterruptHandler;
+    HASH_RNG => rng::InterruptHandler<peripherals::RNG>;
+});
+#[cfg(feature = "stm32h563zi")]
+bind_interrupts!(struct Irqs {
+    ETH => eth::InterruptHandler;
+    RNG => rng::InterruptHandler<peripherals::RNG>;
+});
+
+type Device = Ethernet<'static, ETH, GenericSMI>;
+
+#[embassy_executor::task]
+async fn net_task(stack: &'static Stack<Device>) -> ! {
+    stack.run().await
+}
+
+#[embassy_executor::main]
+async fn main(spawner: Spawner) {
+    let p = embassy_stm32::init(config());
+    info!("Hello World!");
+
+    // Generate random seed.
+    let mut rng = Rng::new(p.RNG, Irqs);
+    let mut seed = [0; 8];
+    rng.fill_bytes(&mut seed);
+    let seed = u64::from_le_bytes(seed);
+
+    // Ensure different boards get different MAC
+    // so running tests concurrently doesn't break (they're all in the same LAN)
+    #[cfg(feature = "stm32f429zi")]
+    let n = 1;
+    #[cfg(feature = "stm32h755zi")]
+    let n = 2;
+    #[cfg(feature = "stm32h563zi")]
+    let n = 3;
+
+    let mac_addr = [0x00, n, 0xDE, 0xAD, 0xBE, 0xEF];
+
+    let device = Ethernet::new(
+        make_static!(PacketQueue::<4, 4>::new()),
+        p.ETH,
+        Irqs,
+        p.PA1,
+        p.PA2,
+        p.PC1,
+        p.PA7,
+        p.PC4,
+        p.PC5,
+        p.PG13,
+        #[cfg(not(feature = "stm32h563zi"))]
+        p.PB13,
+        #[cfg(feature = "stm32h563zi")]
+        p.PB15,
+        p.PG11,
+        GenericSMI::new(),
+        mac_addr,
+        0,
+    );
+
+    let config = embassy_net::Config::dhcpv4(Default::default());
+    //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {
+    //    address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),
+    //    dns_servers: Vec::new(),
+    //    gateway: Some(Ipv4Address::new(10, 42, 0, 1)),
+    //});
+
+    // Init network stack
+    let stack = &*make_static!(Stack::new(
+        device,
+        config,
+        make_static!(StackResources::<2>::new()),
+        seed
+    ));
+
+    // Launch network task
+    unwrap!(spawner.spawn(net_task(&stack)));
+
+    perf_client::run(
+        stack,
+        perf_client::Expected {
+            down_kbps: 1000,
+            up_kbps: 1000,
+            updown_kbps: 1000,
+        },
+    )
+    .await;
+
+    info!("Test OK");
+    cortex_m::asm::bkpt();
+}
diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs
index 9c0b8c39e..6bf5c36ef 100644
--- a/tests/stm32/src/common.rs
+++ b/tests/stm32/src/common.rs
@@ -154,11 +154,46 @@ pub fn config() -> Config {
     #[allow(unused_mut)]
     let mut config = Config::default();
 
+    #[cfg(feature = "stm32f429zi")]
+    {
+        // TODO: stm32f429zi can do up to 180mhz, but that makes tests fail.
+        // perhaps we have some bug w.r.t overdrive.
+        config.rcc.sys_ck = Some(Hertz(168_000_000));
+        config.rcc.pclk1 = Some(Hertz(42_000_000));
+        config.rcc.pclk2 = Some(Hertz(84_000_000));
+    }
+
+    #[cfg(feature = "stm32h563zi")]
+    {
+        use embassy_stm32::rcc::*;
+        config.rcc.hsi = None;
+        config.rcc.hsi48 = true; // needed for rng
+        config.rcc.hse = Some(Hse {
+            freq: Hertz(8_000_000),
+            mode: HseMode::BypassDigital,
+        });
+        config.rcc.pll1 = Some(Pll {
+            source: PllSource::Hse,
+            prediv: 2,
+            mul: 125,
+            divp: Some(2),
+            divq: Some(2),
+            divr: None,
+        });
+        config.rcc.ahb_pre = AHBPrescaler::DIV1;
+        config.rcc.apb1_pre = APBPrescaler::DIV1;
+        config.rcc.apb2_pre = APBPrescaler::DIV1;
+        config.rcc.apb3_pre = APBPrescaler::DIV1;
+        config.rcc.sys = Sysclk::Pll1P;
+        config.rcc.voltage_scale = VoltageScale::Scale0;
+    }
+
     #[cfg(feature = "stm32h755zi")]
     {
         use embassy_stm32::rcc::*;
         config.rcc.hsi = Some(Hsi::Mhz64);
         config.rcc.csi = true;
+        config.rcc.hsi48 = true; // needed for RNG
         config.rcc.pll_src = PllSource::Hsi;
         config.rcc.pll1 = Some(Pll {
             prediv: 4,