diff --git a/embassy/Cargo.toml b/embassy/Cargo.toml
index bbe657f40..611c35eed 100644
--- a/embassy/Cargo.toml
+++ b/embassy/Cargo.toml
@@ -29,7 +29,6 @@ executor-agnostic = []
 defmt = { version = "0.3", optional = true }
 log = { version = "0.4.14", optional = true }
 
-cortex-m = "0.7.3"
 futures     = { version = "0.3.17", default-features = false, features = [ "cfg-target-has-atomic", "unstable" ] }
 pin-project = { version = "1.0.8", default-features = false }
 embassy-macros  = { version = "0.1.0", path = "../embassy-macros"}
@@ -38,12 +37,28 @@ atomic-polyfill = "0.1.5"
 critical-section = "0.2.5"
 embedded-hal = "0.2.6"
 heapless = "0.7.5"
+cfg-if = "1.0.0"
 
 # WASM dependencies
 wasm-bindgen = { version = "0.2.76", features = ["nightly"], optional = true }
 js-sys = { version = "0.3", optional = true }
 wasm-timer = { version = "0.2.5", optional = true }
 
+[target."thumbv6m-none-eabi".dependencies]
+cortex-m = "0.7.3"
+[target."thumbv7m-none-eabi".dependencies]
+cortex-m = "0.7.3"
+[target."thumbv7em-none-eabi".dependencies]
+cortex-m = "0.7.3"
+[target."thumbv7em-none-eabihf".dependencies]
+cortex-m = "0.7.3"
+[target."thumbv8m.base-none-eabi".dependencies]
+cortex-m = "0.7.3"
+[target."thumbv8m.main-none-eabi".dependencies]
+cortex-m = "0.7.3"
+[target."thumbv8m.main-none-eabihf".dependencies]
+cortex-m = "0.7.3"
+
 [dev-dependencies]
 embassy = { path = ".", features = ["executor-agnostic"] }
 futures-executor = { version = "0.3.17", features = [ "thread-pool" ] }
diff --git a/embassy/src/blocking_mutex/kind.rs b/embassy/src/blocking_mutex/kind.rs
index 30fc90497..a4a45605f 100644
--- a/embassy/src/blocking_mutex/kind.rs
+++ b/embassy/src/blocking_mutex/kind.rs
@@ -1,19 +1,20 @@
-use super::{CriticalSectionMutex, Mutex, NoopMutex, ThreadModeMutex};
-
 pub trait MutexKind {
-    type Mutex<T>: Mutex<Data = T>;
+    type Mutex<T>: super::Mutex<Data = T>;
 }
 
 pub enum CriticalSection {}
 impl MutexKind for CriticalSection {
-    type Mutex<T> = CriticalSectionMutex<T>;
+    type Mutex<T> = super::CriticalSectionMutex<T>;
 }
 
+#[cfg(any(cortex_m, feature = "std"))]
 pub enum ThreadMode {}
+#[cfg(any(cortex_m, feature = "std"))]
 impl MutexKind for ThreadMode {
-    type Mutex<T> = ThreadModeMutex<T>;
+    type Mutex<T> = super::ThreadModeMutex<T>;
 }
+
 pub enum Noop {}
 impl MutexKind for Noop {
-    type Mutex<T> = NoopMutex<T>;
+    type Mutex<T> = super::NoopMutex<T>;
 }
diff --git a/embassy/src/blocking_mutex/mod.rs b/embassy/src/blocking_mutex/mod.rs
index 8ada27cb3..949531392 100644
--- a/embassy/src/blocking_mutex/mod.rs
+++ b/embassy/src/blocking_mutex/mod.rs
@@ -62,77 +62,84 @@ impl<T> Mutex for CriticalSectionMutex<T> {
     }
 }
 
-/// A "mutex" that only allows borrowing from thread mode.
-///
-/// # Safety
-///
-/// **This Mutex is only safe on single-core systems.**
-///
-/// On multi-core systems, a `ThreadModeMutex` **is not sufficient** to ensure exclusive access.
-pub struct ThreadModeMutex<T> {
-    inner: UnsafeCell<T>,
-}
+#[cfg(any(cortex_m, feature = "std"))]
+pub use thread_mode_mutex::*;
+#[cfg(any(cortex_m, feature = "std"))]
+mod thread_mode_mutex {
+    use super::*;
 
-// NOTE: ThreadModeMutex only allows borrowing from one execution context ever: thread mode.
-// Therefore it cannot be used to send non-sendable stuff between execution contexts, so it can
-// be Send+Sync even if T is not Send (unlike CriticalSectionMutex)
-unsafe impl<T> Sync for ThreadModeMutex<T> {}
-unsafe impl<T> Send for ThreadModeMutex<T> {}
+    /// A "mutex" that only allows borrowing from thread mode.
+    ///
+    /// # Safety
+    ///
+    /// **This Mutex is only safe on single-core systems.**
+    ///
+    /// On multi-core systems, a `ThreadModeMutex` **is not sufficient** to ensure exclusive access.
+    pub struct ThreadModeMutex<T> {
+        inner: UnsafeCell<T>,
+    }
 
-impl<T> ThreadModeMutex<T> {
-    /// Creates a new mutex
-    pub const fn new(value: T) -> Self {
-        ThreadModeMutex {
-            inner: UnsafeCell::new(value),
+    // NOTE: ThreadModeMutex only allows borrowing from one execution context ever: thread mode.
+    // Therefore it cannot be used to send non-sendable stuff between execution contexts, so it can
+    // be Send+Sync even if T is not Send (unlike CriticalSectionMutex)
+    unsafe impl<T> Sync for ThreadModeMutex<T> {}
+    unsafe impl<T> Send for ThreadModeMutex<T> {}
+
+    impl<T> ThreadModeMutex<T> {
+        /// Creates a new mutex
+        pub const fn new(value: T) -> Self {
+            ThreadModeMutex {
+                inner: UnsafeCell::new(value),
+            }
+        }
+
+        /// Borrows the data
+        pub fn borrow(&self) -> &T {
+            assert!(
+                in_thread_mode(),
+                "ThreadModeMutex can only be borrowed from thread mode."
+            );
+            unsafe { &*self.inner.get() }
         }
     }
 
-    /// Borrows the data
-    pub fn borrow(&self) -> &T {
-        assert!(
-            in_thread_mode(),
-            "ThreadModeMutex can only be borrowed from thread mode."
-        );
-        unsafe { &*self.inner.get() }
-    }
-}
+    impl<T> Mutex for ThreadModeMutex<T> {
+        type Data = T;
 
-impl<T> Mutex for ThreadModeMutex<T> {
-    type Data = T;
+        fn new(data: T) -> Self {
+            Self::new(data)
+        }
 
-    fn new(data: T) -> Self {
-        Self::new(data)
+        fn lock<R>(&self, f: impl FnOnce(&Self::Data) -> R) -> R {
+            f(self.borrow())
+        }
     }
 
-    fn lock<R>(&self, f: impl FnOnce(&Self::Data) -> R) -> R {
-        f(self.borrow())
+    impl<T> Drop for ThreadModeMutex<T> {
+        fn drop(&mut self) {
+            // Only allow dropping from thread mode. Dropping calls drop on the inner `T`, so
+            // `drop` needs the same guarantees as `lock`. `ThreadModeMutex<T>` is Send even if
+            // T isn't, so without this check a user could create a ThreadModeMutex in thread mode,
+            // send it to interrupt context and drop it there, which would "send" a T even if T is not Send.
+            assert!(
+                in_thread_mode(),
+                "ThreadModeMutex can only be dropped from thread mode."
+            );
+
+            // Drop of the inner `T` happens after this.
+        }
     }
-}
 
-impl<T> Drop for ThreadModeMutex<T> {
-    fn drop(&mut self) {
-        // Only allow dropping from thread mode. Dropping calls drop on the inner `T`, so
-        // `drop` needs the same guarantees as `lock`. `ThreadModeMutex<T>` is Send even if
-        // T isn't, so without this check a user could create a ThreadModeMutex in thread mode,
-        // send it to interrupt context and drop it there, which would "send" a T even if T is not Send.
-        assert!(
-            in_thread_mode(),
-            "ThreadModeMutex can only be dropped from thread mode."
-        );
+    pub fn in_thread_mode() -> bool {
+        #[cfg(feature = "std")]
+        return Some("main") == std::thread::current().name();
 
-        // Drop of the inner `T` happens after this.
+        #[cfg(not(feature = "std"))]
+        return cortex_m::peripheral::SCB::vect_active()
+            == cortex_m::peripheral::scb::VectActive::ThreadMode;
     }
 }
 
-pub fn in_thread_mode() -> bool {
-    #[cfg(feature = "std")]
-    return Some("main") == std::thread::current().name();
-
-    #[cfg(not(feature = "std"))]
-    return cortex_m::peripheral::SCB::vect_active()
-        == cortex_m::peripheral::scb::VectActive::ThreadMode;
-}
-
 /// A "mutex" that does nothing and cannot be shared between threads.
 pub struct NoopMutex<T> {
     inner: T,
diff --git a/embassy/src/executor/arch/arm.rs b/embassy/src/executor/arch/cortex_m.rs
similarity index 100%
rename from embassy/src/executor/arch/arm.rs
rename to embassy/src/executor/arch/cortex_m.rs
diff --git a/embassy/src/executor/mod.rs b/embassy/src/executor/mod.rs
index 3ec24c13c..5b53b8d0b 100644
--- a/embassy/src/executor/mod.rs
+++ b/embassy/src/executor/mod.rs
@@ -2,12 +2,25 @@
 
 #![deny(missing_docs)]
 
-#[cfg_attr(feature = "std", path = "arch/std.rs")]
-#[cfg_attr(feature = "wasm", path = "arch/wasm.rs")]
-#[cfg_attr(not(any(feature = "std", feature = "wasm")), path = "arch/arm.rs")]
-mod arch;
-pub mod raw;
-mod spawner;
+cfg_if::cfg_if! {
+    if #[cfg(cortex_m)] {
+        #[path="arch/cortex_m.rs"]
+        mod arch;
+        pub use arch::*;
+    }
+    else if #[cfg(feature="wasm")] {
+        #[path="arch/wasm.rs"]
+        mod arch;
+        pub use arch::*;
+    }
+    else if #[cfg(feature="std")] {
+        #[path="arch/std.rs"]
+        mod arch;
+        pub use arch::*;
+    }
+}
 
-pub use arch::*;
+pub mod raw;
+
+mod spawner;
 pub use spawner::*;
diff --git a/embassy/src/lib.rs b/embassy/src/lib.rs
index a74d1a514..9d8ef3888 100644
--- a/embassy/src/lib.rs
+++ b/embassy/src/lib.rs
@@ -13,6 +13,7 @@ pub mod channel;
 pub mod waitqueue;
 
 pub mod executor;
+#[cfg(cortex_m)]
 pub mod interrupt;
 pub mod io;
 #[cfg(feature = "time")]