Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

H723 ethernet #445

Merged
merged 8 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ required-features = ["rt", "stm32h747cm7", "ethernet"]
name = "ethernet-rtic-stm32h735g-dk"
required-features = ["rt", "stm32h735", "ethernet"]

[[example]]
name = "ethernet-rtic-nucleo-h723zg"
required-features = ["rt", "stm32h735", "ethernet"]

[[example]]
name = "ethernet-nucleo-h743zi2"
required-features = ["rt", "revision_v", "stm32h743v", "ethernet"]
Expand Down
235 changes: 235 additions & 0 deletions examples/ethernet-rtic-nucleo-h723zg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
//! Demo for Nucleo-H723ZG eval board using the Real Time for the Masses
//! (RTIC) framework.
//!
//! This demo responds to pings on 192.168.1.99 (IP address hardcoded below)
//!
//! We use the SysTick timer to create a 1ms timebase for use with smoltcp.
//!
//! The ethernet ring buffers are placed in AXI SRAM, where they can be
//! accessed by both the core and the Ethernet DMA.
//!
//! Run like
//!
//! `cargo flash --example ethernet-rtic-nucleo-h723zg --features=ethernet,stm32h735 --chip=STM32H723ZGTx`
#![deny(warnings)]
#![no_main]
#![no_std]

#[macro_use]
#[allow(unused)]
mod utilities;

use core::sync::atomic::AtomicU32;

use smoltcp::iface::{Config, Interface, SocketSet, SocketStorage};
use smoltcp::time::Instant;
use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr};

use stm32h7xx_hal::{ethernet, rcc::CoreClocks, stm32};

/// Configure SYSTICK for 1ms timebase
fn systick_init(mut syst: stm32::SYST, clocks: CoreClocks) {
let c_ck_mhz = clocks.c_ck().to_MHz();

let syst_calib = 0x3E8;

syst.set_clock_source(cortex_m::peripheral::syst::SystClkSource::Core);
syst.set_reload((syst_calib * c_ck_mhz) - 1);
syst.enable_interrupt();
syst.enable_counter();
}

/// TIME is an atomic u32 that counts milliseconds.
static TIME: AtomicU32 = AtomicU32::new(0);

/// Locally administered MAC address
const MAC_ADDRESS: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44];

/// Ethernet descriptor rings are a global singleton
#[link_section = ".axisram.eth"]
static mut DES_RING: ethernet::DesRing<4, 4> = ethernet::DesRing::new();

/// Net storage with static initialisation - another global singleton
pub struct NetStorageStatic<'a> {
socket_storage: [SocketStorage<'a>; 8],
}

static mut STORE: NetStorageStatic = NetStorageStatic {
socket_storage: [SocketStorage::EMPTY; 8],
richardeoin marked this conversation as resolved.
Show resolved Hide resolved
};

pub struct Net<'a> {
iface: Interface,
ethdev: ethernet::EthernetDMA<4, 4>,
sockets: SocketSet<'a>,
}
impl<'a> Net<'a> {
pub fn new(
store: &'a mut NetStorageStatic<'a>,
mut ethdev: ethernet::EthernetDMA<4, 4>,
ethernet_addr: HardwareAddress,
) -> Self {
let config = Config::new(ethernet_addr);

let mut iface = Interface::new(config, &mut ethdev, Instant::ZERO);
// Set IP address
iface.update_ip_addrs(|addrs| {
let _ = addrs.push(IpCidr::new(IpAddress::v4(192, 168, 1, 99), 0));
});

let sockets = SocketSet::new(&mut store.socket_storage[..]);

Net::<'a> {
iface,
ethdev,
sockets,
}
}

/// Polls on the ethernet interface. You should refer to the smoltcp
/// documentation for poll() to understand how to call poll efficiently
pub fn poll(&mut self, now: i64) {
let timestamp = Instant::from_millis(now);

self.iface
.poll(timestamp, &mut self.ethdev, &mut self.sockets);
}
}

#[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true)]
mod app {
use stm32h7xx_hal::{ethernet, ethernet::PHY, gpio, prelude::*};

use super::*;
use core::sync::atomic::Ordering;

#[shared]
struct SharedResources {}
#[local]
struct LocalResources {
net: Net<'static>,
lan8742a: ethernet::phy::LAN8742A<ethernet::EthernetMAC>,
link_led: gpio::gpioe::PE1<gpio::Output<gpio::PushPull>>,
}

#[init]
fn init(
mut ctx: init::Context,
) -> (SharedResources, LocalResources, init::Monotonics) {
utilities::logger::init();
// Initialise power...
let pwr = ctx.device.PWR.constrain();
let pwrcfg = pwr.ldo().freeze(); // nucleo-h723zg board doesn't have SMPS

// Initialise clocks...
let rcc = ctx.device.RCC.constrain();
let ccdr = rcc
.sys_ck(200.MHz())
.hclk(200.MHz())
.freeze(pwrcfg, &ctx.device.SYSCFG);

// Initialise system...
ctx.core.SCB.invalidate_icache();
ctx.core.SCB.enable_icache();
// TODO: ETH DMA coherence issues
// ctx.core.SCB.enable_dcache(&mut ctx.core.CPUID);
ctx.core.DWT.enable_cycle_counter();

// Initialise IO...
let gpioa = ctx.device.GPIOA.split(ccdr.peripheral.GPIOA);
let gpioc = ctx.device.GPIOC.split(ccdr.peripheral.GPIOC);
let gpioe = ctx.device.GPIOE.split(ccdr.peripheral.GPIOE);
let gpiob = ctx.device.GPIOB.split(ccdr.peripheral.GPIOB);
let mut link_led = gpioe.pe1.into_push_pull_output(); // USR LED1
link_led.set_high();

let rmii_ref_clk = gpioa.pa1.into_alternate();
let rmii_mdio = gpioa.pa2.into_alternate();
let rmii_mdc = gpioc.pc1.into_alternate();
let rmii_crs_dv = gpioa.pa7.into_alternate();
let rmii_rxd0 = gpioc.pc4.into_alternate();
let rmii_rxd1 = gpioc.pc5.into_alternate();
let rmii_tx_en = gpiob.pb11.into_alternate();
let rmii_txd0 = gpiob.pb12.into_alternate();
let rmii_txd1 = gpiob.pb13.into_alternate();

// Initialise ethernet...
assert_eq!(ccdr.clocks.hclk().raw(), 200_000_000); // HCLK 200MHz
assert_eq!(ccdr.clocks.pclk1().raw(), 100_000_000); // PCLK 100MHz
assert_eq!(ccdr.clocks.pclk2().raw(), 100_000_000); // PCLK 100MHz
assert_eq!(ccdr.clocks.pclk4().raw(), 100_000_000); // PCLK 100MHz

let mac_addr = smoltcp::wire::EthernetAddress::from_bytes(&MAC_ADDRESS);
let (eth_dma, eth_mac) = unsafe {
ethernet::new(
ctx.device.ETHERNET_MAC,
ctx.device.ETHERNET_MTL,
ctx.device.ETHERNET_DMA,
(
rmii_ref_clk,
rmii_mdio,
rmii_mdc,
rmii_crs_dv,
rmii_rxd0,
rmii_rxd1,
rmii_tx_en,
rmii_txd0,
rmii_txd1,
),
&mut DES_RING,
mac_addr,
ccdr.peripheral.ETH1MAC,
&ccdr.clocks,
)
};

// Initialise ethernet PHY...
let mut lan8742a = ethernet::phy::LAN8742A::new(eth_mac);
lan8742a.phy_reset();
lan8742a.phy_init();
// The eth_dma should not be used until the PHY reports the link is up

unsafe { ethernet::enable_interrupt() };

// unsafe: mutable reference to static storage, we only do this once
let store = unsafe { &mut STORE };
let net = Net::new(store, eth_dma, mac_addr.into());

// 1ms tick
systick_init(ctx.core.SYST, ccdr.clocks);

(
SharedResources {},
LocalResources {
net,
lan8742a,
link_led,
},
init::Monotonics(),
)
}

#[idle(local = [lan8742a, link_led])]
fn idle(ctx: idle::Context) -> ! {
loop {
// Ethernet
match ctx.local.lan8742a.poll_link() {
true => ctx.local.link_led.set_low(),
_ => ctx.local.link_led.set_high(),
}
}
}

#[task(binds = ETH, local = [net])]
fn ethernet_event(ctx: ethernet_event::Context) {
unsafe { ethernet::interrupt_handler() }

let time = TIME.load(Ordering::Relaxed);
ctx.local.net.poll(time as i64);
}

#[task(binds = SysTick, priority=15)]
fn systick_tick(_: systick_tick::Context) {
TIME.fetch_add(1, Ordering::Relaxed);
}
}
10 changes: 6 additions & 4 deletions src/ethernet/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,12 @@ pub unsafe fn new_unchecked<const TD: usize, const RD: usize>(
// Ensure syscfg is enabled (for PMCR)
rcc.apb4enr.modify(|_, w| w.syscfgen().set_bit());

// Reset ETH_DMA - write 1 and wait for 0.
// On the H723, we have to do this before prec.enable()
// or the DMA will never come out of reset
eth_dma.dmamr.modify(|_, w| w.swr().set_bit());
while eth_dma.dmamr.read().swr().bit_is_set() {}

// AHB1 ETH1MACEN
prec.enable();

Expand All @@ -472,10 +478,6 @@ pub unsafe fn new_unchecked<const TD: usize, const RD: usize>(
//rcc.ahb1rstr.modify(|_, w| w.eth1macrst().clear_bit());

cortex_m::interrupt::free(|_cs| {
// reset ETH_DMA - write 1 and wait for 0
eth_dma.dmamr.modify(|_, w| w.swr().set_bit());
while eth_dma.dmamr.read().swr().bit_is_set() {}

// 200 MHz
eth_mac
.mac1ustcr
Expand Down