diff options
| author | Thales Fragoso <[email protected]> | 2021-06-07 02:30:38 -0300 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2021-06-16 16:48:35 +0200 |
| commit | 46e1bae9e36917c2e763082730d99df302c1c625 (patch) | |
| tree | 5e181740db8bdb8500808cc9b03889de8d3af05e | |
| parent | 6386c34079913732466046194f79a683a4aefce4 (diff) | |
eth-v2: Start Ethernet peripheral implementation
| -rw-r--r-- | embassy-net/src/lib.rs | 2 | ||||
| -rw-r--r-- | embassy-net/src/packet_pool.rs | 3 | ||||
| -rw-r--r-- | embassy-stm32/Cargo.toml | 3 | ||||
| -rw-r--r-- | embassy-stm32/src/eth/mod.rs | 7 | ||||
| -rw-r--r-- | embassy-stm32/src/eth/v1.rs | 1 | ||||
| -rw-r--r-- | embassy-stm32/src/eth/v2/descriptors.rs | 371 | ||||
| -rw-r--r-- | embassy-stm32/src/eth/v2/mod.rs | 350 | ||||
| -rw-r--r-- | embassy-stm32/src/lib.rs | 2 |
8 files changed, 737 insertions, 2 deletions
diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 88dcf0aa5..51eb97a2e 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs | |||
| @@ -13,7 +13,7 @@ pub use config::DhcpConfigurator; | |||
| 13 | pub use config::{Config, Configurator, Event as ConfigEvent, StaticConfigurator}; | 13 | pub use config::{Config, Configurator, Event as ConfigEvent, StaticConfigurator}; |
| 14 | 14 | ||
| 15 | pub use device::{Device, LinkState}; | 15 | pub use device::{Device, LinkState}; |
| 16 | pub use packet_pool::{Packet, PacketBox, PacketBoxExt, PacketBuf}; | 16 | pub use packet_pool::{Packet, PacketBox, PacketBoxExt, PacketBuf, MTU}; |
| 17 | pub use stack::{init, is_config_up, is_init, is_link_up, run}; | 17 | pub use stack::{init, is_config_up, is_init, is_link_up, run}; |
| 18 | 18 | ||
| 19 | #[cfg(feature = "tcp")] | 19 | #[cfg(feature = "tcp")] |
diff --git a/embassy-net/src/packet_pool.rs b/embassy-net/src/packet_pool.rs index 2c27d4013..0ec88e649 100644 --- a/embassy-net/src/packet_pool.rs +++ b/embassy-net/src/packet_pool.rs | |||
| @@ -3,12 +3,13 @@ use core::ops::{Deref, DerefMut, Range}; | |||
| 3 | 3 | ||
| 4 | use atomic_pool::{pool, Box}; | 4 | use atomic_pool::{pool, Box}; |
| 5 | 5 | ||
| 6 | pub const MTU: usize = 1514; | 6 | pub const MTU: usize = 1516; |
| 7 | pub const PACKET_POOL_SIZE: usize = 4; | 7 | pub const PACKET_POOL_SIZE: usize = 4; |
| 8 | 8 | ||
| 9 | pool!(pub PacketPool: [Packet; PACKET_POOL_SIZE]); | 9 | pool!(pub PacketPool: [Packet; PACKET_POOL_SIZE]); |
| 10 | pub type PacketBox = Box<PacketPool>; | 10 | pub type PacketBox = Box<PacketPool>; |
| 11 | 11 | ||
| 12 | #[repr(align(4))] | ||
| 12 | pub struct Packet(pub [u8; MTU]); | 13 | pub struct Packet(pub [u8; MTU]); |
| 13 | 14 | ||
| 14 | impl Packet { | 15 | impl Packet { |
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 682ebaefc..cf45fe21f 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -10,6 +10,7 @@ embassy = { version = "0.1.0", path = "../embassy" } | |||
| 10 | embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["stm32"] } | 10 | embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["stm32"] } |
| 11 | embassy-extras = {version = "0.1.0", path = "../embassy-extras" } | 11 | embassy-extras = {version = "0.1.0", path = "../embassy-extras" } |
| 12 | embassy-traits = {version = "0.1.0", path = "../embassy-traits" } | 12 | embassy-traits = {version = "0.1.0", path = "../embassy-traits" } |
| 13 | embassy-net = { version = "0.1.0", path = "../embassy-net", features = ["tcp", "medium-ip"] } | ||
| 13 | 14 | ||
| 14 | defmt = { version = "0.2.0", optional = true } | 15 | defmt = { version = "0.2.0", optional = true } |
| 15 | log = { version = "0.4.11", optional = true } | 16 | log = { version = "0.4.11", optional = true } |
| @@ -24,6 +25,8 @@ critical-section = "0.2.1" | |||
| 24 | bare-metal = "1.0.0" | 25 | bare-metal = "1.0.0" |
| 25 | atomic-polyfill = "0.1.2" | 26 | atomic-polyfill = "0.1.2" |
| 26 | stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", features = ["rt"] } | 27 | stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", features = ["rt"] } |
| 28 | vcell = "0.1.3" | ||
| 29 | |||
| 27 | cfg-if = "1.0.0" | 30 | cfg-if = "1.0.0" |
| 28 | 31 | ||
| 29 | [build-dependencies] | 32 | [build-dependencies] |
diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs new file mode 100644 index 000000000..8791e1552 --- /dev/null +++ b/embassy-stm32/src/eth/mod.rs | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | #![macro_use] | ||
| 2 | |||
| 3 | #[cfg_attr(eth_v1, path = "v1.rs")] | ||
| 4 | #[cfg_attr(eth_v2, path = "v2/mod.rs")] | ||
| 5 | mod _version; | ||
| 6 | |||
| 7 | pub use _version::*; | ||
diff --git a/embassy-stm32/src/eth/v1.rs b/embassy-stm32/src/eth/v1.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/embassy-stm32/src/eth/v1.rs | |||
| @@ -0,0 +1 @@ | |||
diff --git a/embassy-stm32/src/eth/v2/descriptors.rs b/embassy-stm32/src/eth/v2/descriptors.rs new file mode 100644 index 000000000..095eef20d --- /dev/null +++ b/embassy-stm32/src/eth/v2/descriptors.rs | |||
| @@ -0,0 +1,371 @@ | |||
| 1 | use core::sync::atomic::{fence, Ordering}; | ||
| 2 | |||
| 3 | use embassy_net::{Packet, PacketBox, PacketBoxExt, PacketBuf}; | ||
| 4 | use vcell::VolatileCell; | ||
| 5 | |||
| 6 | use crate::pac::ETH; | ||
| 7 | |||
| 8 | #[non_exhaustive] | ||
| 9 | #[derive(Debug, Copy, Clone)] | ||
| 10 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 11 | pub enum Error { | ||
| 12 | NoBufferAvailable, | ||
| 13 | // TODO: Break down this error into several others | ||
| 14 | TransmissionError, | ||
| 15 | } | ||
| 16 | |||
| 17 | /// Transmit and Receive Descriptor fields | ||
| 18 | #[allow(dead_code)] | ||
| 19 | mod emac_consts { | ||
| 20 | pub const EMAC_DES3_OWN: u32 = 0x8000_0000; | ||
| 21 | pub const EMAC_DES3_CTXT: u32 = 0x4000_0000; | ||
| 22 | pub const EMAC_DES3_FD: u32 = 0x2000_0000; | ||
| 23 | pub const EMAC_DES3_LD: u32 = 0x1000_0000; | ||
| 24 | pub const EMAC_DES3_ES: u32 = 0x0000_8000; | ||
| 25 | pub const EMAC_DES0_BUF1AP: u32 = 0xFFFF_FFFF; | ||
| 26 | |||
| 27 | pub const EMAC_TDES2_IOC: u32 = 0x8000_0000; | ||
| 28 | pub const EMAC_TDES2_B1L: u32 = 0x0000_3FFF; | ||
| 29 | |||
| 30 | pub const EMAC_RDES3_IOC: u32 = 0x4000_0000; | ||
| 31 | pub const EMAC_RDES3_PL: u32 = 0x0000_7FFF; | ||
| 32 | pub const EMAC_RDES3_BUF1V: u32 = 0x0100_0000; | ||
| 33 | pub const EMAC_RDES3_PKTLEN: u32 = 0x0000_7FFF; | ||
| 34 | } | ||
| 35 | use emac_consts::*; | ||
| 36 | |||
| 37 | /// Transmit Descriptor representation | ||
| 38 | /// | ||
| 39 | /// * tdes0: transmit buffer address | ||
| 40 | /// * tdes1: | ||
| 41 | /// * tdes2: buffer lengths | ||
| 42 | /// * tdes3: control and payload/frame length | ||
| 43 | #[repr(C)] | ||
| 44 | struct TDes { | ||
| 45 | tdes0: VolatileCell<u32>, | ||
| 46 | tdes1: VolatileCell<u32>, | ||
| 47 | tdes2: VolatileCell<u32>, | ||
| 48 | tdes3: VolatileCell<u32>, | ||
| 49 | } | ||
| 50 | |||
| 51 | impl TDes { | ||
| 52 | pub const fn new() -> Self { | ||
| 53 | Self { | ||
| 54 | tdes0: VolatileCell::new(0), | ||
| 55 | tdes1: VolatileCell::new(0), | ||
| 56 | tdes2: VolatileCell::new(0), | ||
| 57 | tdes3: VolatileCell::new(0), | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | /// Return true if this TDes is not currently owned by the DMA | ||
| 62 | pub fn available(&self) -> bool { | ||
| 63 | self.tdes3.get() & EMAC_DES3_OWN == 0 | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | pub(crate) struct TDesRing<const N: usize> { | ||
| 68 | td: [TDes; N], | ||
| 69 | buffers: [Option<PacketBuf>; N], | ||
| 70 | tdidx: usize, | ||
| 71 | } | ||
| 72 | |||
| 73 | impl<const N: usize> TDesRing<N> { | ||
| 74 | pub const fn new() -> Self { | ||
| 75 | const TDES: TDes = TDes::new(); | ||
| 76 | const BUFFERS: Option<PacketBuf> = None; | ||
| 77 | |||
| 78 | Self { | ||
| 79 | td: [TDES; N], | ||
| 80 | buffers: [BUFFERS; N], | ||
| 81 | tdidx: 0, | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | /// Initialise this TDesRing. Assume TDesRing is corrupt | ||
| 86 | /// | ||
| 87 | /// The current memory address of the buffers inside this TDesRing | ||
| 88 | /// will be stored in the descriptors, so ensure the TDesRing is | ||
| 89 | /// not moved after initialisation. | ||
| 90 | pub(crate) fn init(&mut self) { | ||
| 91 | assert!(N > 0); | ||
| 92 | |||
| 93 | for td in self.td.iter_mut() { | ||
| 94 | *td = TDes::new(); | ||
| 95 | } | ||
| 96 | self.tdidx = 0; | ||
| 97 | |||
| 98 | // Initialize the pointers in the DMA engine. (There will be a memory barrier later | ||
| 99 | // before the DMA engine is enabled.) | ||
| 100 | // NOTE (unsafe) Used for atomic writes | ||
| 101 | unsafe { | ||
| 102 | let dma = ETH.ethernet_dma(); | ||
| 103 | |||
| 104 | dma.dmactx_dlar() | ||
| 105 | .write(|w| w.set_tdesla(&self.td as *const _ as u32)); | ||
| 106 | dma.dmactx_rlr().write(|w| w.set_tdrl((N as u16) - 1)); | ||
| 107 | dma.dmactx_dtpr() | ||
| 108 | .write(|w| w.set_tdt(&self.td[0] as *const _ as u32)); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | /// Return true if a TDes is available for use | ||
| 113 | pub(crate) fn available(&self) -> bool { | ||
| 114 | self.td[self.tdidx].available() | ||
| 115 | } | ||
| 116 | |||
| 117 | pub(crate) fn transmit(&mut self, pkt: PacketBuf) -> Result<(), Error> { | ||
| 118 | if !self.available() { | ||
| 119 | return Err(Error::NoBufferAvailable); | ||
| 120 | } | ||
| 121 | let x = self.tdidx; | ||
| 122 | let td = &mut self.td[x]; | ||
| 123 | |||
| 124 | let pkt_len = pkt.len(); | ||
| 125 | assert!(pkt_len as u32 <= EMAC_TDES2_B1L); | ||
| 126 | let address = pkt.as_ptr() as u32; | ||
| 127 | |||
| 128 | // Read format | ||
| 129 | td.tdes0.set(address); | ||
| 130 | td.tdes2 | ||
| 131 | .set(pkt_len as u32 & EMAC_TDES2_B1L | EMAC_TDES2_IOC); | ||
| 132 | |||
| 133 | // FD: Contains first buffer of packet | ||
| 134 | // LD: Contains last buffer of packet | ||
| 135 | // Give the DMA engine ownership | ||
| 136 | td.tdes3.set(EMAC_DES3_FD | EMAC_DES3_LD | EMAC_DES3_OWN); | ||
| 137 | |||
| 138 | self.buffers[x].replace(pkt); | ||
| 139 | |||
| 140 | // Ensure changes to the descriptor are committed before DMA engine sees tail pointer store. | ||
| 141 | // This will generate an DMB instruction. | ||
| 142 | // "Preceding reads and writes cannot be moved past subsequent writes." | ||
| 143 | fence(Ordering::Release); | ||
| 144 | |||
| 145 | // Move the tail pointer (TPR) to the next descriptor | ||
| 146 | let x = (x + 1) % N; | ||
| 147 | // NOTE(unsafe) Atomic write | ||
| 148 | unsafe { | ||
| 149 | ETH.ethernet_dma() | ||
| 150 | .dmactx_dtpr() | ||
| 151 | .write(|w| w.set_tdt(&self.td[x] as *const _ as u32)); | ||
| 152 | } | ||
| 153 | self.tdidx = x; | ||
| 154 | Ok(()) | ||
| 155 | } | ||
| 156 | |||
| 157 | pub(crate) fn on_interrupt(&mut self) -> Result<(), Error> { | ||
| 158 | let previous = (self.tdidx + N - 1) % N; | ||
| 159 | let td = &self.td[previous]; | ||
| 160 | |||
| 161 | // DMB to ensure that we are reading an updated value, probably not needed at the hardware | ||
| 162 | // level, but this is also a hint to the compiler that we're syncing on the buffer. | ||
| 163 | fence(Ordering::SeqCst); | ||
| 164 | |||
| 165 | let tdes3 = td.tdes3.get(); | ||
| 166 | |||
| 167 | if tdes3 & EMAC_DES3_OWN != 0 { | ||
| 168 | // Transmission isn't done yet, probably a receive interrupt that fired this | ||
| 169 | return Ok(()); | ||
| 170 | } | ||
| 171 | assert!(tdes3 & EMAC_DES3_CTXT == 0); | ||
| 172 | |||
| 173 | // Release the buffer | ||
| 174 | self.buffers[previous].take(); | ||
| 175 | |||
| 176 | if tdes3 & EMAC_DES3_ES != 0 { | ||
| 177 | Err(Error::TransmissionError) | ||
| 178 | } else { | ||
| 179 | Ok(()) | ||
| 180 | } | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | /// Receive Descriptor representation | ||
| 185 | /// | ||
| 186 | /// * rdes0: recieve buffer address | ||
| 187 | /// * rdes1: | ||
| 188 | /// * rdes2: | ||
| 189 | /// * rdes3: OWN and Status | ||
| 190 | #[repr(C)] | ||
| 191 | struct RDes { | ||
| 192 | rdes0: VolatileCell<u32>, | ||
| 193 | rdes1: VolatileCell<u32>, | ||
| 194 | rdes2: VolatileCell<u32>, | ||
| 195 | rdes3: VolatileCell<u32>, | ||
| 196 | } | ||
| 197 | |||
| 198 | impl RDes { | ||
| 199 | pub const fn new() -> Self { | ||
| 200 | Self { | ||
| 201 | rdes0: VolatileCell::new(0), | ||
| 202 | rdes1: VolatileCell::new(0), | ||
| 203 | rdes2: VolatileCell::new(0), | ||
| 204 | rdes3: VolatileCell::new(0), | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | /// Return true if this RDes is acceptable to us | ||
| 209 | #[inline(always)] | ||
| 210 | pub fn valid(&self) -> bool { | ||
| 211 | // Write-back descriptor is valid if: | ||
| 212 | // | ||
| 213 | // Contains first buffer of packet AND contains last buf of | ||
| 214 | // packet AND no errors AND not a context descriptor | ||
| 215 | self.rdes3.get() & (EMAC_DES3_FD | EMAC_DES3_LD | EMAC_DES3_ES | EMAC_DES3_CTXT) | ||
| 216 | == (EMAC_DES3_FD | EMAC_DES3_LD) | ||
| 217 | } | ||
| 218 | |||
| 219 | /// Return true if this RDes is not currently owned by the DMA | ||
| 220 | #[inline(always)] | ||
| 221 | pub fn available(&self) -> bool { | ||
| 222 | self.rdes3.get() & EMAC_DES3_OWN == 0 // Owned by us | ||
| 223 | } | ||
| 224 | |||
| 225 | #[inline(always)] | ||
| 226 | pub fn set_ready(&mut self, buf_addr: u32) { | ||
| 227 | self.rdes0.set(buf_addr); | ||
| 228 | self.rdes3 | ||
| 229 | .set(EMAC_RDES3_BUF1V | EMAC_RDES3_IOC | EMAC_DES3_OWN); | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | pub(crate) struct RDesRing<const N: usize> { | ||
| 234 | rd: [RDes; N], | ||
| 235 | buffers: [Option<PacketBox>; N], | ||
| 236 | read_idx: usize, | ||
| 237 | tail_idx: usize, | ||
| 238 | } | ||
| 239 | |||
| 240 | impl<const N: usize> RDesRing<N> { | ||
| 241 | pub const fn new() -> Self { | ||
| 242 | const RDES: RDes = RDes::new(); | ||
| 243 | const BUFFERS: Option<PacketBox> = None; | ||
| 244 | |||
| 245 | Self { | ||
| 246 | rd: [RDES; N], | ||
| 247 | buffers: [BUFFERS; N], | ||
| 248 | read_idx: 0, | ||
| 249 | tail_idx: 0, | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | pub(crate) fn init(&mut self) { | ||
| 254 | assert!(N > 1); | ||
| 255 | |||
| 256 | for desc in self.rd.iter_mut() { | ||
| 257 | *desc = RDes::new(); | ||
| 258 | } | ||
| 259 | |||
| 260 | let mut last_index = 0; | ||
| 261 | for (index, buf) in self.buffers.iter_mut().enumerate() { | ||
| 262 | let pkt = match PacketBox::new(Packet::new()) { | ||
| 263 | Some(p) => p, | ||
| 264 | None => { | ||
| 265 | if index == 0 { | ||
| 266 | panic!("Could not allocate at least one buffer for Ethernet receiving"); | ||
| 267 | } else { | ||
| 268 | break; | ||
| 269 | } | ||
| 270 | } | ||
| 271 | }; | ||
| 272 | let addr = pkt.as_ptr() as u32; | ||
| 273 | *buf = Some(pkt); | ||
| 274 | self.rd[index].set_ready(addr); | ||
| 275 | last_index = index; | ||
| 276 | } | ||
| 277 | self.tail_idx = (last_index + 1) % N; | ||
| 278 | |||
| 279 | unsafe { | ||
| 280 | let dma = ETH.ethernet_dma(); | ||
| 281 | |||
| 282 | dma.dmacrx_dlar() | ||
| 283 | .write(|w| w.set_rdesla(self.rd.as_ptr() as u32)); | ||
| 284 | dma.dmacrx_rlr().write(|w| w.set_rdrl((N as u16) - 1)); | ||
| 285 | |||
| 286 | // We manage to allocate all buffers, set the index to the last one, that means | ||
| 287 | // that the DMA won't consider the last one as ready, because it (unfortunately) | ||
| 288 | // stops at the tail ptr and wraps at the end of the ring, which means that we | ||
| 289 | // can't tell it to stop after the last buffer. | ||
| 290 | let tail_ptr = &self.rd[last_index] as *const _ as u32; | ||
| 291 | fence(Ordering::Release); | ||
| 292 | |||
| 293 | dma.dmacrx_dtpr().write(|w| w.set_rdt(tail_ptr)); | ||
| 294 | } | ||
| 295 | } | ||
| 296 | |||
| 297 | pub(crate) fn on_interrupt(&mut self) { | ||
| 298 | // TODO! | ||
| 299 | } | ||
| 300 | |||
| 301 | pub(crate) fn pop_packet(&mut self) -> Option<PacketBuf> { | ||
| 302 | // Not sure if the contents of the write buffer on the M7 can affects reads, so we are using | ||
| 303 | // a DMB here just in case, it also serves as a hint to the compiler that we're syncing the | ||
| 304 | // buffer (I think .-.) | ||
| 305 | fence(Ordering::SeqCst); | ||
| 306 | |||
| 307 | let read_available = self.rd[self.read_idx].available(); | ||
| 308 | if !read_available && self.read_idx == self.tail_idx { | ||
| 309 | // Nothing to do | ||
| 310 | return None; | ||
| 311 | } | ||
| 312 | |||
| 313 | let pkt = if read_available { | ||
| 314 | let pkt = self.buffers[self.read_idx].take(); | ||
| 315 | let len = (self.rd[self.read_idx].rdes3.get() & EMAC_RDES3_PKTLEN) as usize; | ||
| 316 | |||
| 317 | assert!(pkt.is_some()); | ||
| 318 | let valid = self.rd[self.read_idx].valid(); | ||
| 319 | |||
| 320 | self.read_idx = (self.read_idx + 1) % N; | ||
| 321 | if valid { | ||
| 322 | pkt.map(|p| p.slice(0..len)) | ||
| 323 | } else { | ||
| 324 | None | ||
| 325 | } | ||
| 326 | } else { | ||
| 327 | None | ||
| 328 | }; | ||
| 329 | |||
| 330 | match PacketBox::new(Packet::new()) { | ||
| 331 | Some(b) => { | ||
| 332 | let addr = b.as_ptr() as u32; | ||
| 333 | self.buffers[self.tail_idx].replace(b); | ||
| 334 | self.rd[self.tail_idx].set_ready(addr); | ||
| 335 | |||
| 336 | // "Preceding reads and writes cannot be moved past subsequent writes." | ||
| 337 | fence(Ordering::Release); | ||
| 338 | |||
| 339 | // NOTE(unsafe) atomic write | ||
| 340 | unsafe { | ||
| 341 | ETH.ethernet_dma() | ||
| 342 | .dmacrx_dtpr() | ||
| 343 | .write(|w| w.set_rdt(&self.rd[self.read_idx] as *const _ as u32)); | ||
| 344 | } | ||
| 345 | |||
| 346 | self.tail_idx = (self.tail_idx + 1) % N; | ||
| 347 | } | ||
| 348 | None => {} | ||
| 349 | } | ||
| 350 | pkt | ||
| 351 | } | ||
| 352 | } | ||
| 353 | |||
| 354 | pub struct DescriptorRing<const T: usize, const R: usize> { | ||
| 355 | pub(crate) tx: TDesRing<T>, | ||
| 356 | pub(crate) rx: RDesRing<R>, | ||
| 357 | } | ||
| 358 | |||
| 359 | impl<const T: usize, const R: usize> DescriptorRing<T, R> { | ||
| 360 | pub const fn new() -> Self { | ||
| 361 | Self { | ||
| 362 | tx: TDesRing::new(), | ||
| 363 | rx: RDesRing::new(), | ||
| 364 | } | ||
| 365 | } | ||
| 366 | |||
| 367 | pub fn init(&mut self) { | ||
| 368 | self.tx.init(); | ||
| 369 | self.rx.init(); | ||
| 370 | } | ||
| 371 | } | ||
diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs new file mode 100644 index 000000000..d54c6e859 --- /dev/null +++ b/embassy-stm32/src/eth/v2/mod.rs | |||
| @@ -0,0 +1,350 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | use core::pin::Pin; | ||
| 3 | use core::sync::atomic::{fence, Ordering}; | ||
| 4 | |||
| 5 | use embassy::util::{AtomicWaker, Unborrow}; | ||
| 6 | use embassy_extras::peripheral::{PeripheralMutex, PeripheralState}; | ||
| 7 | use embassy_extras::unborrow; | ||
| 8 | use embassy_net::MTU; | ||
| 9 | |||
| 10 | use crate::gpio::sealed::Pin as __GpioPin; | ||
| 11 | use crate::gpio::AnyPin; | ||
| 12 | use crate::gpio::Pin as GpioPin; | ||
| 13 | use crate::interrupt::Interrupt; | ||
| 14 | use crate::pac::gpio::vals::Ospeedr; | ||
| 15 | use crate::pac::ETH; | ||
| 16 | use crate::peripherals; | ||
| 17 | |||
| 18 | mod descriptors; | ||
| 19 | use descriptors::DescriptorRing; | ||
| 20 | |||
| 21 | /// Station Management Interface (SMI) on an ethernet PHY | ||
| 22 | pub trait StationManagement { | ||
| 23 | /// Read a register over SMI. | ||
| 24 | fn smi_read(&mut self, reg: u8) -> u16; | ||
| 25 | /// Write a register over SMI. | ||
| 26 | fn smi_write(&mut self, reg: u8, val: u16); | ||
| 27 | } | ||
| 28 | |||
| 29 | /// Traits for an Ethernet PHY | ||
| 30 | pub trait PHY { | ||
| 31 | /// Reset PHY and wait for it to come out of reset. | ||
| 32 | fn phy_reset(&mut self); | ||
| 33 | /// PHY initialisation. | ||
| 34 | fn phy_init(&mut self); | ||
| 35 | } | ||
| 36 | |||
| 37 | pub struct Ethernet<'d, T: Instance, const TX: usize, const RX: usize> { | ||
| 38 | state: PeripheralMutex<Inner<'d, T, TX, RX>>, | ||
| 39 | pins: [AnyPin; 9], | ||
| 40 | } | ||
| 41 | |||
| 42 | impl<'d, T: Instance, const TX: usize, const RX: usize> Ethernet<'d, T, TX, RX> { | ||
| 43 | pub fn new( | ||
| 44 | peri: impl Unborrow<Target = T> + 'd, | ||
| 45 | interrupt: impl Unborrow<Target = T::Interrupt> + 'd, | ||
| 46 | ref_clk: impl Unborrow<Target = impl RefClkPin<T>> + 'd, | ||
| 47 | mdio: impl Unborrow<Target = impl MDIOPin<T>> + 'd, | ||
| 48 | mdc: impl Unborrow<Target = impl MDCPin<T>> + 'd, | ||
| 49 | crs: impl Unborrow<Target = impl CRSPin<T>> + 'd, | ||
| 50 | rx_d0: impl Unborrow<Target = impl RXD0Pin<T>> + 'd, | ||
| 51 | rx_d1: impl Unborrow<Target = impl RXD1Pin<T>> + 'd, | ||
| 52 | tx_d0: impl Unborrow<Target = impl TXD0Pin<T>> + 'd, | ||
| 53 | tx_d1: impl Unborrow<Target = impl TXD1Pin<T>> + 'd, | ||
| 54 | tx_en: impl Unborrow<Target = impl TXEnPin<T>> + 'd, | ||
| 55 | mac_addr: [u8; 6], | ||
| 56 | ) -> Self { | ||
| 57 | unborrow!(interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); | ||
| 58 | |||
| 59 | ref_clk.configure(); | ||
| 60 | mdio.configure(); | ||
| 61 | mdc.configure(); | ||
| 62 | crs.configure(); | ||
| 63 | rx_d0.configure(); | ||
| 64 | rx_d1.configure(); | ||
| 65 | tx_d0.configure(); | ||
| 66 | tx_d1.configure(); | ||
| 67 | tx_en.configure(); | ||
| 68 | |||
| 69 | let inner = Inner::new(peri); | ||
| 70 | let state = PeripheralMutex::new(inner, interrupt); | ||
| 71 | |||
| 72 | // NOTE(unsafe) We have exclusive access to the registers | ||
| 73 | unsafe { | ||
| 74 | let dma = ETH.ethernet_dma(); | ||
| 75 | let mac = ETH.ethernet_mac(); | ||
| 76 | let mtl = ETH.ethernet_mtl(); | ||
| 77 | |||
| 78 | // Reset and wait | ||
| 79 | dma.dmamr().modify(|w| w.set_swr(true)); | ||
| 80 | while dma.dmamr().read().swr() {} | ||
| 81 | |||
| 82 | // 200 MHz ? | ||
| 83 | mac.mac1ustcr().modify(|w| w.set_tic_1us_cntr(200 - 1)); | ||
| 84 | |||
| 85 | mac.maccr().modify(|w| { | ||
| 86 | w.set_ipg(0b000); // 96 bit times | ||
| 87 | w.set_acs(true); | ||
| 88 | w.set_fes(true); | ||
| 89 | w.set_dm(true); | ||
| 90 | // TODO: Carrier sense ? ECRSFD | ||
| 91 | }); | ||
| 92 | |||
| 93 | mac.maca0lr().write(|w| { | ||
| 94 | w.set_addrlo( | ||
| 95 | u32::from(mac_addr[0]) | ||
| 96 | | (u32::from(mac_addr[1]) << 8) | ||
| 97 | | (u32::from(mac_addr[2]) << 16) | ||
| 98 | | (u32::from(mac_addr[3]) << 24), | ||
| 99 | ) | ||
| 100 | }); | ||
| 101 | mac.maca0hr() | ||
| 102 | .modify(|w| w.set_addrhi(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8))); | ||
| 103 | |||
| 104 | // TODO: Enable filtering once we get the basics working | ||
| 105 | mac.macpfr().modify(|w| w.set_ra(true)); | ||
| 106 | mac.macqtx_fcr().modify(|w| w.set_pt(0x100)); | ||
| 107 | |||
| 108 | mtl.mtlrx_qomr().modify(|w| w.set_rsf(true)); | ||
| 109 | mtl.mtltx_qomr().modify(|w| w.set_tsf(true)); | ||
| 110 | |||
| 111 | // TODO: Address aligned beats plus fixed burst ? | ||
| 112 | dma.dmactx_cr().modify(|w| w.set_txpbl(1)); // 32 ? | ||
| 113 | dma.dmacrx_cr().modify(|w| { | ||
| 114 | w.set_rxpbl(1); // 32 ? | ||
| 115 | w.set_rbsz(MTU as u16); | ||
| 116 | }); | ||
| 117 | } | ||
| 118 | |||
| 119 | let pins = [ | ||
| 120 | ref_clk.degrade(), | ||
| 121 | mdio.degrade(), | ||
| 122 | mdc.degrade(), | ||
| 123 | crs.degrade(), | ||
| 124 | rx_d0.degrade(), | ||
| 125 | rx_d1.degrade(), | ||
| 126 | tx_d0.degrade(), | ||
| 127 | tx_d1.degrade(), | ||
| 128 | tx_en.degrade(), | ||
| 129 | ]; | ||
| 130 | |||
| 131 | Self { state, pins } | ||
| 132 | } | ||
| 133 | |||
| 134 | pub fn init(self: Pin<&mut Self>) { | ||
| 135 | // NOTE(unsafe) We won't move this | ||
| 136 | let this = unsafe { self.get_unchecked_mut() }; | ||
| 137 | let mutex = unsafe { Pin::new_unchecked(&mut this.state) }; | ||
| 138 | |||
| 139 | mutex.with(|s, _| { | ||
| 140 | s.desc_ring.init(); | ||
| 141 | |||
| 142 | fence(Ordering::SeqCst); | ||
| 143 | |||
| 144 | unsafe { | ||
| 145 | let mac = ETH.ethernet_mac(); | ||
| 146 | let mtl = ETH.ethernet_mtl(); | ||
| 147 | let dma = ETH.ethernet_dma(); | ||
| 148 | |||
| 149 | mac.maccr().modify(|w| { | ||
| 150 | w.set_re(true); | ||
| 151 | w.set_te(true); | ||
| 152 | }); | ||
| 153 | mtl.mtltx_qomr().modify(|w| w.set_ftq(true)); | ||
| 154 | |||
| 155 | dma.dmactx_cr().modify(|w| w.set_st(true)); | ||
| 156 | dma.dmacrx_cr().modify(|w| w.set_sr(true)); | ||
| 157 | |||
| 158 | // Enable interrupts | ||
| 159 | dma.dmacier().modify(|w| { | ||
| 160 | w.set_nie(true); | ||
| 161 | w.set_rie(true); | ||
| 162 | w.set_tie(true); | ||
| 163 | }); | ||
| 164 | } | ||
| 165 | }); | ||
| 166 | } | ||
| 167 | } | ||
| 168 | |||
| 169 | impl<'d, T: Instance, const TX: usize, const RX: usize> Drop for Ethernet<'d, T, TX, RX> { | ||
| 170 | fn drop(&mut self) { | ||
| 171 | for pin in self.pins.iter_mut() { | ||
| 172 | // NOTE(unsafe) Exclusive access to the regs | ||
| 173 | critical_section::with(|_| unsafe { | ||
| 174 | pin.set_as_analog(); | ||
| 175 | pin.block() | ||
| 176 | .ospeedr() | ||
| 177 | .modify(|w| w.set_ospeedr(pin.pin() as usize, Ospeedr::LOWSPEED)); | ||
| 178 | }) | ||
| 179 | } | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | //---------------------------------------------------------------------- | ||
| 184 | |||
| 185 | struct Inner<'d, T: Instance, const TX: usize, const RX: usize> { | ||
| 186 | _peri: PhantomData<&'d mut T>, | ||
| 187 | desc_ring: DescriptorRing<TX, RX>, | ||
| 188 | } | ||
| 189 | |||
| 190 | impl<'d, T: Instance, const TX: usize, const RX: usize> Inner<'d, T, TX, RX> { | ||
| 191 | pub fn new(_peri: impl Unborrow<Target = T> + 'd) -> Self { | ||
| 192 | Self { | ||
| 193 | _peri: PhantomData, | ||
| 194 | desc_ring: DescriptorRing::new(), | ||
| 195 | } | ||
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | impl<'d, T: Instance, const TX: usize, const RX: usize> PeripheralState for Inner<'d, T, TX, RX> { | ||
| 200 | type Interrupt = T::Interrupt; | ||
| 201 | |||
| 202 | fn on_interrupt(&mut self) { | ||
| 203 | unwrap!(self.desc_ring.tx.on_interrupt()); | ||
| 204 | self.desc_ring.rx.on_interrupt(); | ||
| 205 | |||
| 206 | T::state().wake(); | ||
| 207 | |||
| 208 | // TODO: Check and clear more flags | ||
| 209 | unsafe { | ||
| 210 | let dma = ETH.ethernet_dma(); | ||
| 211 | |||
| 212 | dma.dmacsr().modify(|w| { | ||
| 213 | w.set_ti(false); | ||
| 214 | w.set_ri(false); | ||
| 215 | }); | ||
| 216 | // Delay two peripheral's clock | ||
| 217 | dma.dmacsr().read(); | ||
| 218 | dma.dmacsr().read(); | ||
| 219 | } | ||
| 220 | } | ||
| 221 | } | ||
| 222 | |||
| 223 | mod sealed { | ||
| 224 | use super::*; | ||
| 225 | |||
| 226 | pub trait Instance { | ||
| 227 | type Interrupt: Interrupt; | ||
| 228 | |||
| 229 | fn state() -> &'static AtomicWaker; | ||
| 230 | } | ||
| 231 | |||
| 232 | pub trait RefClkPin<T: Instance>: GpioPin { | ||
| 233 | fn configure(&mut self); | ||
| 234 | } | ||
| 235 | |||
| 236 | pub trait MDIOPin<T: Instance>: GpioPin { | ||
| 237 | fn configure(&mut self); | ||
| 238 | } | ||
| 239 | |||
| 240 | pub trait MDCPin<T: Instance>: GpioPin { | ||
| 241 | fn configure(&mut self); | ||
| 242 | } | ||
| 243 | |||
| 244 | pub trait CRSPin<T: Instance>: GpioPin { | ||
| 245 | fn configure(&mut self); | ||
| 246 | } | ||
| 247 | |||
| 248 | pub trait RXD0Pin<T: Instance>: GpioPin { | ||
| 249 | fn configure(&mut self); | ||
| 250 | } | ||
| 251 | |||
| 252 | pub trait RXD1Pin<T: Instance>: GpioPin { | ||
| 253 | fn configure(&mut self); | ||
| 254 | } | ||
| 255 | |||
| 256 | pub trait TXD0Pin<T: Instance>: GpioPin { | ||
| 257 | fn configure(&mut self); | ||
| 258 | } | ||
| 259 | |||
| 260 | pub trait TXD1Pin<T: Instance>: GpioPin { | ||
| 261 | fn configure(&mut self); | ||
| 262 | } | ||
| 263 | |||
| 264 | pub trait TXEnPin<T: Instance>: GpioPin { | ||
| 265 | fn configure(&mut self); | ||
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 269 | pub trait Instance: sealed::Instance + 'static {} | ||
| 270 | |||
| 271 | pub trait RefClkPin<T: Instance>: sealed::RefClkPin<T> + 'static {} | ||
| 272 | |||
| 273 | pub trait MDIOPin<T: Instance>: sealed::MDIOPin<T> + 'static {} | ||
| 274 | |||
| 275 | pub trait MDCPin<T: Instance>: sealed::MDCPin<T> + 'static {} | ||
| 276 | |||
| 277 | pub trait CRSPin<T: Instance>: sealed::CRSPin<T> + 'static {} | ||
| 278 | |||
| 279 | pub trait RXD0Pin<T: Instance>: sealed::RXD0Pin<T> + 'static {} | ||
| 280 | |||
| 281 | pub trait RXD1Pin<T: Instance>: sealed::RXD1Pin<T> + 'static {} | ||
| 282 | |||
| 283 | pub trait TXD0Pin<T: Instance>: sealed::TXD0Pin<T> + 'static {} | ||
| 284 | |||
| 285 | pub trait TXD1Pin<T: Instance>: sealed::TXD1Pin<T> + 'static {} | ||
| 286 | |||
| 287 | pub trait TXEnPin<T: Instance>: sealed::TXEnPin<T> + 'static {} | ||
| 288 | |||
| 289 | crate::pac::peripherals!( | ||
| 290 | (eth, $inst:ident) => { | ||
| 291 | impl sealed::Instance for peripherals::$inst { | ||
| 292 | type Interrupt = crate::interrupt::$inst; | ||
| 293 | |||
| 294 | fn state() -> &'static AtomicWaker { | ||
| 295 | static WAKER: AtomicWaker = AtomicWaker::new(); | ||
| 296 | &WAKER | ||
| 297 | } | ||
| 298 | } | ||
| 299 | |||
| 300 | impl Instance for peripherals::$inst {} | ||
| 301 | }; | ||
| 302 | ); | ||
| 303 | |||
| 304 | macro_rules! impl_pin { | ||
| 305 | ($inst:ident, $pin:ident, $signal:ident, $af:expr) => { | ||
| 306 | impl sealed::$signal<peripherals::$inst> for peripherals::$pin { | ||
| 307 | fn configure(&mut self) { | ||
| 308 | // NOTE(unsafe) Exclusive access to the registers | ||
| 309 | critical_section::with(|_| unsafe { | ||
| 310 | self.set_as_af($af); | ||
| 311 | self.block() | ||
| 312 | .ospeedr() | ||
| 313 | .modify(|w| w.set_ospeedr(self.pin() as usize, Ospeedr::VERYHIGHSPEED)); | ||
| 314 | }) | ||
| 315 | } | ||
| 316 | } | ||
| 317 | |||
| 318 | impl $signal<peripherals::$inst> for peripherals::$pin {} | ||
| 319 | }; | ||
| 320 | } | ||
| 321 | |||
| 322 | crate::pac::peripheral_pins!( | ||
| 323 | ($inst:ident, eth, ETH, $pin:ident, REF_CLK, $af:expr) => { | ||
| 324 | impl_pin!($inst, $pin, RefClkPin, $af); | ||
| 325 | }; | ||
| 326 | ($inst:ident, eth, ETH, $pin:ident, MDIO, $af:expr) => { | ||
| 327 | impl_pin!($inst, $pin, MDIOPin, $af); | ||
| 328 | }; | ||
| 329 | ($inst:ident, eth, ETH, $pin:ident, MDC, $af:expr) => { | ||
| 330 | impl_pin!($inst, $pin, MDCPin, $af); | ||
| 331 | }; | ||
| 332 | ($inst:ident, eth, ETH, $pin:ident, CRS_DV, $af:expr) => { | ||
| 333 | impl_pin!($inst, $pin, CRSPin, $af); | ||
| 334 | }; | ||
| 335 | ($inst:ident, eth, ETH, $pin:ident, RXD0, $af:expr) => { | ||
| 336 | impl_pin!($inst, $pin, RXD0Pin, $af); | ||
| 337 | }; | ||
| 338 | ($inst:ident, eth, ETH, $pin:ident, RXD1, $af:expr) => { | ||
| 339 | impl_pin!($inst, $pin, RXD1Pin, $af); | ||
| 340 | }; | ||
| 341 | ($inst:ident, eth, ETH, $pin:ident, TXD0, $af:expr) => { | ||
| 342 | impl_pin!($inst, $pin, TXD0Pin, $af); | ||
| 343 | }; | ||
| 344 | ($inst:ident, eth, ETH, $pin:ident, TXD1, $af:expr) => { | ||
| 345 | impl_pin!($inst, $pin, TXD1Pin, $af); | ||
| 346 | }; | ||
| 347 | ($inst:ident, eth, ETH, $pin:ident, TX_EN, $af:expr) => { | ||
| 348 | impl_pin!($inst, $pin, TXEnPin, $af); | ||
| 349 | }; | ||
| 350 | ); | ||
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 6a08fc580..b99751e6d 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs | |||
| @@ -29,6 +29,8 @@ pub mod clock; | |||
| 29 | pub mod dac; | 29 | pub mod dac; |
| 30 | #[cfg(dma)] | 30 | #[cfg(dma)] |
| 31 | pub mod dma; | 31 | pub mod dma; |
| 32 | #[cfg(eth)] | ||
| 33 | pub mod eth; | ||
| 32 | #[cfg(i2c)] | 34 | #[cfg(i2c)] |
| 33 | pub mod i2c; | 35 | pub mod i2c; |
| 34 | #[cfg(pwr)] | 36 | #[cfg(pwr)] |
