Skip to content

Commit 9227208

Browse files
mitchmindtreesystec-ms
authored andcommitted
Remove SMI and PHY management assumptions, narrow scope to Ethernet
*@thalesfragoso feel free to take or leave this! This direction for `stm32-eth` has been motivated by my use of the crate over the past few months for this installation. We currently use a branch that combines a variation on these changes along with the `stm32f1` branch (#23).* This PR introduces a couple of significant breaking changes, primarily: 1. **Remove the assumption that the MCU must be associated with a single PHY.** MCUs are capable of Ethernet communication without requiring knowledge of the PHYs involved at all. In cases where a MCU is connected directly to a switch IC via MII or RMII (e.g. using something like the KSZ8863), association with a single PHY does not make meaningful sense, and an API that expects one can be a little frustrating to work around. 2. **Refactor the `smi` module and expose the `Smi` type.** The idea behind this change is to narrow the scope of `stm32-eth` to the fundamentals of enabling Ethernet communication, while exposing the necessary components to allow PHY management to be handled externally (e.g. via [`mdio`](https://crates.io/crates/mdio) or device-specific crate like [`ksz8863`](https://crates.io/crates/ksz8863)). Further, by not requiring that the `Eth` constructor take ownership over the `MDIO` and `MDC` pins, we allow the user to use them for custom MDIO-based protocols. E.g. the KSZ8863 requires setting the R/W OP code bits to `0b00`, however this is not possible to configure using the STM32 MAC's limited SMI interface (`MACMIIAR`), so a bit-banged implementation of MDIO is required. Without these changes, it is not possible to use `stm32-eth` alongside a custom bit-banged implementation on the MDIO and MDC pins. Here's roughly how usage of the new SMI API should look: ```rust // Borrows MACMIIAR and MACMIIDR from `Eth` along with the MDIO and MDC pins. let mut smi = eth.smi(&mut mdc, &mut mdio); // Do stuff with `smi` in scope. let data = smi.read(phy_addr, reg_addr); smi.write(phy_addr, reg_addr, data); // `Smi` instance is dropped at end of scope and borrows are released. ``` Some uses might be as simple as: ```rust let data = eth.smi(&mut mdc, &mut mdio).read(phy_addr, reg_addr); ``` The `pktgen.rs` example has been updated to demonstrate. **Follow-up** - Update `stm32f1` PR for removal of MDIO and MDC pin ownership.
1 parent 48754fa commit 9227208

File tree

8 files changed

+110
-260
lines changed

8 files changed

+110
-260
lines changed

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ travis-ci = { repository = "astro/stm32-eth", branch = "master" }
1515
maintenance = { status = "experimental" }
1616

1717
[package.metadata.docs.rs]
18-
features = [ "smoltcp-phy", "stm32f429" ]
18+
features = [ "smi", "smoltcp-phy", "stm32f429" ]
1919

2020
[dependencies]
2121
volatile-register = "0.2"
@@ -34,6 +34,7 @@ optional = true
3434
[features]
3535
device-selected = []
3636
fence = []
37+
smi = []
3738

3839
stm32f407 = ["stm32f4xx-hal/stm32f407", "device-selected"]
3940
stm32f417 = ["stm32f4xx-hal/stm32f417", "device-selected"]
@@ -65,7 +66,7 @@ stm32f4xx-hal = {version = "0.8.3", features = ["rt"] }
6566

6667
[[example]]
6768
name = "pktgen"
68-
required-features = ["stm32f429"]
69+
required-features = ["smi", "stm32f429"]
6970

7071
[[example]]
7172
name = "ip"

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ fn main() {
7070
p.ETHERNET_DMA,
7171
&mut rx_ring[..],
7272
&mut tx_ring[..],
73-
PhyAddress::_0,
7473
clocks,
7574
eth_pins,
7675
)

examples/ip.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
2424
use smoltcp::time::Instant;
2525
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address};
2626

27-
use stm32_eth::{Eth, EthPins, PhyAddress, RingEntry};
27+
use stm32_eth::{Eth, EthPins, RingEntry};
2828

2929
static mut LOGGER: HioLogger = HioLogger {};
3030

@@ -75,8 +75,6 @@ fn main() -> ! {
7575

7676
let eth_pins = EthPins {
7777
ref_clk: gpioa.pa1,
78-
md_io: gpioa.pa2,
79-
md_clk: gpioc.pc1,
8078
crs: gpioa.pa7,
8179
tx_en: gpiog.pg11,
8280
tx_d0: gpiog.pg13,
@@ -92,7 +90,6 @@ fn main() -> ! {
9290
p.ETHERNET_DMA,
9391
&mut rx_ring[..],
9492
&mut tx_ring[..],
95-
PhyAddress::_0,
9693
clocks,
9794
eth_pins,
9895
)

examples/pktgen.rs

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
//! Example assumes use of an STM32F429 connected to a network via a single PHY whose address is
2+
//! `0`. The PHY is expected to be accessible via SMI with an implementation of the standard basic
3+
//! status register as described in the IEEE 802.3 Ethernet standard.
4+
15
#![no_std]
26
#![no_main]
37

@@ -10,20 +14,22 @@ use cortex_m_rt::{entry, exception};
1014
use cortex_m::asm;
1115
use cortex_m::interrupt::Mutex;
1216
use stm32_eth::{
13-
hal::gpio::GpioExt,
17+
hal::gpio::{GpioExt, Speed},
1418
hal::rcc::RccExt,
1519
hal::time::U32Ext,
20+
smi,
1621
stm32::{interrupt, CorePeripherals, Peripherals, SYST},
1722
};
1823

1924
use core::fmt::Write;
2025
use cortex_m_semihosting::hio;
2126

22-
use stm32_eth::{Eth, EthPins, PhyAddress, RingEntry, TxError};
27+
use stm32_eth::{Eth, EthPins, RingEntry, TxError};
2328

2429
const SRC_MAC: [u8; 6] = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];
2530
const DST_MAC: [u8; 6] = [0x00, 0x00, 0xBE, 0xEF, 0xDE, 0xAD];
2631
const ETH_TYPE: [u8; 2] = [0x80, 0x00];
32+
const PHY_ADDR: u8 = 0;
2733

2834
static TIME: Mutex<RefCell<usize>> = Mutex::new(RefCell::new(0));
2935
static ETH_PENDING: Mutex<RefCell<bool>> = Mutex::new(RefCell::new(false));
@@ -49,8 +55,6 @@ fn main() -> ! {
4955

5056
let eth_pins = EthPins {
5157
ref_clk: gpioa.pa1,
52-
md_io: gpioa.pa2,
53-
md_clk: gpioc.pc1,
5458
crs: gpioa.pa7,
5559
tx_en: gpiog.pg11,
5660
tx_d0: gpiog.pg13,
@@ -59,14 +63,16 @@ fn main() -> ! {
5963
rx_d1: gpioc.pc5,
6064
};
6165

66+
let mut mdio = gpioa.pa2.into_alternate_af11().set_speed(Speed::VeryHigh);
67+
let mut mdc = gpioc.pc1.into_alternate_af11().set_speed(Speed::VeryHigh);
68+
6269
let mut rx_ring: [RingEntry<_>; 16] = Default::default();
6370
let mut tx_ring: [RingEntry<_>; 8] = Default::default();
6471
let mut eth = Eth::new(
6572
p.ETHERNET_MAC,
6673
p.ETHERNET_DMA,
6774
&mut rx_ring[..],
6875
&mut tx_ring[..],
69-
PhyAddress::_0,
7076
clocks,
7177
eth_pins,
7278
)
@@ -79,7 +85,7 @@ fn main() -> ! {
7985
let mut rx_pkts = 0usize;
8086
let mut tx_bytes = 0usize;
8187
let mut tx_pkts = 0usize;
82-
let mut last_status = None;
88+
let mut last_link_up = false;
8389

8490
loop {
8591
let time: usize = cortex_m::interrupt::free(|cs| *TIME.borrow(cs).borrow());
@@ -106,28 +112,14 @@ fn main() -> ! {
106112
}
107113

108114
// Link change detection
109-
let status = eth.status();
110-
if last_status
111-
.map(|last_status| last_status != status)
112-
.unwrap_or(true)
113-
{
114-
if !status.link_detected() {
115+
let link_up = link_detected(eth.smi(&mut mdio, &mut mdc));
116+
if link_up != last_link_up {
117+
if link_up {
115118
writeln!(stdout, "Ethernet: no link detected").unwrap();
116119
} else {
117-
writeln!(
118-
stdout,
119-
"Ethernet: link detected with {} Mbps/{}",
120-
status.speed(),
121-
match status.is_full_duplex() {
122-
Some(true) => "FD",
123-
Some(false) => "HD",
124-
None => "?",
125-
}
126-
)
127-
.unwrap();
120+
writeln!(stdout, "Ethernet: link detected!").unwrap();
128121
}
129-
130-
last_status = Some(status);
122+
last_link_up = link_up;
131123
}
132124

133125
cortex_m::interrupt::free(|cs| {
@@ -156,7 +148,7 @@ fn main() -> ! {
156148

157149
// fill tx queue
158150
const SIZE: usize = 1500;
159-
if status.link_detected() {
151+
if link_detected(eth.smi(&mut mdio, &mut mdc)) {
160152
'egress: loop {
161153
let r = eth.send(SIZE, |buf| {
162154
buf[0..6].copy_from_slice(&DST_MAC);
@@ -218,3 +210,14 @@ fn ETH() {
218210
let p = unsafe { Peripherals::steal() };
219211
stm32_eth::eth_interrupt_handler(&p.ETHERNET_DMA);
220212
}
213+
214+
fn link_detected<Mdio, Mdc>(smi: smi::Smi<Mdio, Mdc>) -> bool
215+
where
216+
Mdio: smi::MdioPin,
217+
Mdc: smi::MdcPin,
218+
{
219+
const STATUS_REG_ADDR: u8 = 0x3;
220+
const STATUS_REG_UP_MASK: u16 = 1 << 2;
221+
let status = smi.read(PHY_ADDR, STATUS_REG_ADDR);
222+
(status & STATUS_REG_UP_MASK) == STATUS_REG_UP_MASK
223+
}

src/lib.rs

Lines changed: 26 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@ pub use stm32f4xx_hal::stm32;
1717
use hal::rcc::Clocks;
1818
use stm32::{Interrupt, ETHERNET_DMA, ETHERNET_MAC, NVIC};
1919

20-
pub mod phy;
21-
use phy::{Phy, PhyStatus};
2220
mod ring;
23-
mod smi;
21+
#[cfg(feature = "smi")]
22+
pub mod smi;
2423
pub use ring::RingEntry;
2524
mod desc;
2625
mod rx;
@@ -33,7 +32,7 @@ pub mod setup;
3332
pub use setup::EthPins;
3433
use setup::{
3534
AlternateVeryHighSpeed, RmiiCrsDv, RmiiRefClk, RmiiRxD0, RmiiRxD1, RmiiTxD0, RmiiTxD1,
36-
RmiiTxEN, MDC, MDIO,
35+
RmiiTxEN,
3736
};
3837

3938
#[cfg(feature = "smoltcp-phy")]
@@ -64,21 +63,12 @@ use self::consts::*;
6463
#[derive(Debug)]
6564
pub struct WrongClock;
6665

67-
/// Initial PHY address, must be zero or one.
68-
#[derive(Copy, Clone, Debug)]
69-
#[repr(u8)]
70-
pub enum PhyAddress {
71-
_0 = 0,
72-
_1 = 1,
73-
}
74-
75-
/// Ethernet driver for *STM32* chips with a RMII [`Phy`](phy/struct.Phy.html).
66+
/// Ethernet driver for *STM32* chips.
7667
pub struct Eth<'rx, 'tx> {
7768
eth_mac: ETHERNET_MAC,
7869
eth_dma: ETHERNET_DMA,
7970
rx_ring: RxRing<'rx>,
8071
tx_ring: TxRing<'tx>,
81-
phy_address: PhyAddress,
8272
}
8373

8474
impl<'rx, 'tx> Eth<'rx, 'tx> {
@@ -94,19 +84,16 @@ impl<'rx, 'tx> Eth<'rx, 'tx> {
9484
/// Other than that, initializes and starts the Ethernet hardware
9585
/// so that you can [`send()`](#method.send) and
9686
/// [`recv_next()`](#method.recv_next).
97-
pub fn new<REFCLK, IO, CLK, CRS, TXEN, TXD0, TXD1, RXD0, RXD1>(
87+
pub fn new<REFCLK, CRS, TXEN, TXD0, TXD1, RXD0, RXD1>(
9888
eth_mac: ETHERNET_MAC,
9989
eth_dma: ETHERNET_DMA,
10090
rx_buffer: &'rx mut [RxRingEntry],
10191
tx_buffer: &'tx mut [TxRingEntry],
102-
phy_address: PhyAddress,
10392
clocks: Clocks,
104-
pins: EthPins<REFCLK, IO, CLK, CRS, TXEN, TXD0, TXD1, RXD0, RXD1>,
93+
pins: EthPins<REFCLK, CRS, TXEN, TXD0, TXD1, RXD0, RXD1>,
10594
) -> Result<Self, WrongClock>
10695
where
10796
REFCLK: RmiiRefClk + AlternateVeryHighSpeed,
108-
IO: MDIO + AlternateVeryHighSpeed,
109-
CLK: MDC + AlternateVeryHighSpeed,
11097
CRS: RmiiCrsDv + AlternateVeryHighSpeed,
11198
TXEN: RmiiTxEN + AlternateVeryHighSpeed,
11299
TXD0: RmiiTxD0 + AlternateVeryHighSpeed,
@@ -121,7 +108,6 @@ impl<'rx, 'tx> Eth<'rx, 'tx> {
121108
eth_dma,
122109
rx_ring: RxRing::new(rx_buffer),
123110
tx_ring: TxRing::new(tx_buffer),
124-
phy_address,
125111
};
126112
eth.init(clocks)?;
127113
eth.rx_ring.start(&eth.eth_dma);
@@ -145,8 +131,6 @@ impl<'rx, 'tx> Eth<'rx, 'tx> {
145131
.macmiiar
146132
.modify(|_, w| unsafe { w.cr().bits(clock_range) });
147133

148-
self.get_phy().reset().set_autoneg();
149-
150134
// Configuration Register
151135
self.eth_mac.maccr.modify(|_, w| {
152136
// CRC stripping for Type frames
@@ -269,20 +253,6 @@ impl<'rx, 'tx> Eth<'rx, 'tx> {
269253
eth_interrupt_handler(&self.eth_dma);
270254
}
271255

272-
/// Construct a PHY driver
273-
pub fn get_phy(&self) -> Phy {
274-
Phy::new(
275-
&self.eth_mac.macmiiar,
276-
&self.eth_mac.macmiidr,
277-
self.phy_address as u8,
278-
)
279-
}
280-
281-
/// Obtain PHY status
282-
pub fn status(&self) -> PhyStatus {
283-
self.get_phy().status()
284-
}
285-
286256
/// Is Rx DMA currently running?
287257
///
288258
/// It stops if the ring is full. Call `recv_next()` to free an
@@ -312,6 +282,26 @@ impl<'rx, 'tx> Eth<'rx, 'tx> {
312282
self.tx_ring.demand_poll(&self.eth_dma);
313283
result
314284
}
285+
286+
#[cfg(feature = "smi")]
287+
/// Borrow access to the MAC's SMI.
288+
///
289+
/// Allows for controlling and monitoring any PHYs that may be accessible via the MDIO/MDC
290+
/// pins.
291+
///
292+
/// Exclusive access to the `MDIO` and `MDC` is required to ensure that are not used elsewhere
293+
/// for the duration of SMI communication.
294+
pub fn smi<'eth, 'pins, Mdio, Mdc>(
295+
&'eth mut self,
296+
mdio: &'pins mut Mdio,
297+
mdc: &'pins mut Mdc,
298+
) -> smi::Smi<'eth, 'pins, Mdio, Mdc>
299+
where
300+
Mdio: smi::MdioPin,
301+
Mdc: smi::MdcPin,
302+
{
303+
smi::Smi::new(&self.eth_mac.macmiiar, &self.eth_mac.macmiidr, mdio, mdc)
304+
}
315305
}
316306

317307
/// Call in interrupt handler to clear interrupt reason, when

0 commit comments

Comments
 (0)