Skip to content

Add 10M MAC+PHY for the CH32V208 #89

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
12 changes: 5 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ license = "MIT OR Apache-2.0"
[dependencies]
ch32-metapac = { features = [
"rt",
], git = "https://github.com/ch32-rs/ch32-metapac", rev = "b1cbc7a98e43af3fd3170821654784e2c01cb26b" }
], git = "https://github.com/chmousset/rs-ch32-metapac", rev = "3c2fe80fb582c4a7feba3e8600234edfadc152e2" }

qingke = { version = "0.5.0", features = ["critical-section-impl"] }
qingke-rt = { version = "0.5.0", optional = true }
Expand Down Expand Up @@ -45,23 +45,21 @@ futures = { version = "0.3.31", default-features = false, features = [
rand_core = "0.6.3"
sdio-host = "0.5.0"
embassy-hal-internal = "0.2.0"
embassy-net-driver-channel = "0.3.0"
embassy-net-driver = "0.2.0"

[build-dependencies]
ch32-metapac = { features = [
"metadata",
], git = "https://github.com/ch32-rs/ch32-metapac", rev = "b1cbc7a98e43af3fd3170821654784e2c01cb26b" }

], git = "https://github.com/chmousset/rs-ch32-metapac", rev = "3c2fe80fb582c4a7feba3e8600234edfadc152e2" }
proc-macro2 = "1.0"
quote = "1.0"

[features]
default = ["embassy", "rt"]
rt = ["dep:qingke-rt"]
highcode = ["qingke-rt/highcode"]
embassy = [
"dep:embassy-time-driver",
"dep:embassy-time",
]
embassy = ["dep:embassy-time-driver", "dep:embassy-time"]
defmt = ["dep:defmt"]
memory-x = ["ch32-metapac/memory-x"]

Expand Down
25 changes: 23 additions & 2 deletions examples/ch32v208/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,41 @@ ch32-hal = { path = "../../", features = [
"memory-x",
"embassy",
"rt",
"time-driver-tim1",
], default-features = false }
embassy-executor = { version = "0.6.0", features = [
"integrated-timers",
"arch-riscv32",
"arch-spin",
"executor-thread",
] }
embassy-time = { version = "0.3.2" }

critical-section = "1.2.0"

# This is okay because we should automatically use whatever ch32-hal uses
qingke-rt = "*"
qingke = "*"

panic-halt = "1.0"

embassy-net = { version = "0.4.0", features = [
"proto-ipv4",
"udp",
"medium-ethernet",
"dhcpv4",
"log",
] }
static_cell = "2.1.0"
portable-atomic = { version = "*", features = ["critical-section"] }
embassy-futures = "0.1.1"
embassy-time = "0.3"
smoltcp = { version = "*", default-features = false, features = [
"socket-udp",
"proto-ipv4",
"medium-ethernet",
"log",
] }
defmt = "0.3.10"

[profile.release]
strip = false # symbols are not flashed to the microcontroller, so don't strip them.
lto = true
Expand Down
120 changes: 120 additions & 0 deletions examples/ch32v208/src/bin/eth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
#![feature(impl_trait_in_assoc_type)]

use embassy_executor::Spawner;
use embassy_futures::yield_now;
use embassy_net::udp::{PacketMetadata, UdpSocket};
use embassy_net::{self, Stack, StackResources};
use embassy_time::{Duration, Timer};
use hal::eth::generic_smi::GenericSMI;
use hal::eth::{Device, EthernetStationManagement, Runner, State};
use hal::rcc::*;
use hal::time::Hertz;
use hal::{eth, interrupt, println};
use static_cell::StaticCell;
use {ch32_hal as hal, panic_halt as _};

#[embassy_executor::task]
async fn ethernet_task(runner: Runner<'static, GenericSMI>) -> ! {
println!("ethernet_task()");
runner.run().await
}

#[embassy_executor::task]
async fn net_task(stack: &'static Stack<Device<'static>>) -> ! {
println!("net_task()");
stack.run().await
}

#[embassy_executor::main(entry = "ch32_hal::entry")]
async fn main(spawner: Spawner) -> ! {
// configure core speed at 60MHz
let _p = ch32_hal::init(ch32_hal::Config {
rcc: ch32_hal::rcc::Config {
hse: Some(Hse {
freq: Hertz(16_000_000),
mode: HseMode::Oscillator,
}),
sys: Sysclk::PLL,
pll_src: PllSource::HSE,
pll: Some(Pll {
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL15,
}),
pllx: None,
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
ls: LsConfig::default(),
hspll_src: HsPllSource::HSI,
hspll: None,
},
dma_interrupt_priority: interrupt::Priority::P0,
});
let core_freq: u32 = 60_000_000;

ch32_hal::debug::SDIPrint::enable();
Timer::after(Duration::from_millis(100)).await; // let some time to the debug interface to start
println!("Hello CH32!");

// Ethernet setup
let mac_addr = eth::get_mac();
println!("mac_addr: {mac_addr:?}");
static STATE: StaticCell<State<2, 2>> = StaticCell::new();
let state = STATE.init_with(|| State::<2, 2>::new());
let phy = GenericSMI::new(0);
let (device, runner) = hal::eth::new(mac_addr, state, core_freq, phy).await.unwrap();
let _station_management: EthernetStationManagement<ch32_hal::peripherals::ETH> = EthernetStationManagement::new();
spawner.spawn(ethernet_task(runner)).unwrap();

// Generate random seed
let seed = 0xdeadbeef_abadbabe;

// Init network stack
static STACK: StaticCell<Stack<Device<'static>>> = StaticCell::new();
static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new();
let stack = &*STACK.init(Stack::new(
device,
embassy_net::Config::dhcpv4(Default::default()),
RESOURCES.init(StackResources::<2>::new()),
seed,
));

// Launch network task
spawner.spawn(net_task(&stack)).unwrap();

println!("Waiting for DHCP...");
let cfg = wait_for_config(stack).await;
let local_addr = cfg.address.address();
println!("IP address: {:?}", local_addr);

// Then we can use it!
let mut rx_buffer = [0; 300];
let mut tx_buffer = [0; 300];
let mut rx_meta = [PacketMetadata::EMPTY; 16];
let mut tx_meta = [PacketMetadata::EMPTY; 16];
let mut buf = [0; 300];
loop {
let mut socket = UdpSocket::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer);
socket.bind(1234).unwrap();

loop {
let (n, ep) = socket.recv_from(&mut buf).await.unwrap();
if let Ok(s) = core::str::from_utf8(&buf[..n]) {
println!("rxd from {}: {}", ep, s);
}
socket.send_to(&buf[..n], ep).await.unwrap();
}
}
}

async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 {
loop {
if let Some(config) = stack.config_v4() {
return config.clone();
}
yield_now().await;
}
}
78 changes: 78 additions & 0 deletions src/eth/generic_smi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//! Generic SMI Ethernet PHY

use super::{StationManagement, PHY};

#[allow(dead_code)]
mod phy_consts {
pub const PHY_REG_BCR: u8 = 0x00;
pub const PHY_REG_BSR: u8 = 0x01;
pub const PHY_REG_ID1: u8 = 0x02;
pub const PHY_REG_ID2: u8 = 0x03;
pub const PHY_REG_ANTX: u8 = 0x04;
pub const PHY_REG_ANRX: u8 = 0x05;
pub const PHY_REG_ANEXP: u8 = 0x06;
pub const PHY_REG_ANNPTX: u8 = 0x07;
pub const PHY_REG_ANNPRX: u8 = 0x08;
pub const PHY_REG_CTL: u8 = 0x0D; // Ethernet PHY Register Control
pub const PHY_REG_ADDAR: u8 = 0x0E; // Ethernet PHY Address or Data

pub const PHY_REG_WUCSR: u16 = 0x8010;

pub const PHY_REG_BCR_COLTEST: u16 = 1 << 7;
pub const PHY_REG_BCR_FD: u16 = 1 << 8;
pub const PHY_REG_BCR_ANRST: u16 = 1 << 9;
pub const PHY_REG_BCR_ISOLATE: u16 = 1 << 10;
pub const PHY_REG_BCR_POWERDN: u16 = 1 << 11;
pub const PHY_REG_BCR_AN: u16 = 1 << 12;
pub const PHY_REG_BCR_100M: u16 = 1 << 13;
pub const PHY_REG_BCR_LOOPBACK: u16 = 1 << 14;
pub const PHY_REG_BCR_RESET: u16 = 1 << 15;

pub const PHY_REG_BSR_JABBER: u16 = 1 << 1;
pub const PHY_REG_BSR_UP: u16 = 1 << 2;
pub const PHY_REG_BSR_FAULT: u16 = 1 << 4;
pub const PHY_REG_BSR_ANDONE: u16 = 1 << 5;
}
use self::phy_consts::*;

/// Generic SMI Ethernet PHY implementation
pub struct GenericSMI {
phy_addr: u8,
}

impl GenericSMI {
/// Construct the PHY. It assumes the address `phy_addr` in the SMI communication
pub fn new(phy_addr: u8) -> Self {
Self { phy_addr }
}
}

unsafe impl PHY for GenericSMI {
fn phy_reset<S: StationManagement>(&mut self, sm: &mut S) {
sm.smi_write(self.phy_addr, PHY_REG_BCR, PHY_REG_BCR_RESET);
while sm.smi_read(self.phy_addr, PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {}
}

fn link_up<S: StationManagement>(&mut self, sm: &mut S) -> bool {
let bsr = sm.smi_read(self.phy_addr, PHY_REG_BSR);

bsr & PHY_REG_BSR_UP != 0
}
}

/// Public functions for the PHY
impl GenericSMI {
/// Set the SMI polling interval.
#[cfg(feature = "time")]
pub fn set_poll_interval(&mut self, poll_interval: Duration) {
self.poll_interval = poll_interval
}

// Writes a value to an extended PHY register in MMD address space
fn smi_write_ext<S: StationManagement>(&mut self, sm: &mut S, reg_addr: u16, reg_data: u16) {
sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x0003); // set address
sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_addr);
sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x4003); // set data
sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_data);
}
}
52 changes: 52 additions & 0 deletions src/eth/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//! Ethernet (ETH)
#![macro_use]

#[cfg_attr(eth_10m, path = "v10m/mod.rs")]
#[cfg_attr(eth_v1, path = "v1/mod.rs")]
mod _version;

pub mod generic_smi;

pub use self::_version::*;

#[allow(unused)]
const MTU: usize = 1514;

/// Station Management Interface (SMI) on an ethernet PHY
///
/// # Safety
///
/// The methods cannot move out of self
pub unsafe trait StationManagement {
/// Read a register over SMI.
fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16;
/// Write a register over SMI.
fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16);
}

/// Traits for an Ethernet PHY
///
/// # Safety
///
/// The methods cannot move S
pub unsafe trait PHY {
/// Reset PHY and wait for it to come out of reset.
fn phy_reset<S: StationManagement>(&mut self, sm: &mut S);
/// Read PHY registers to check if link is established
fn link_up<S: StationManagement>(&mut self, sm: &mut S) -> bool;
}

trait SealedInstance {
fn regs() -> crate::pac::eth::Eth;
}

/// Ethernet instance.
#[allow(private_bounds)]
pub trait Instance: SealedInstance + Send + 'static {}

impl SealedInstance for crate::peripherals::ETH {
fn regs() -> crate::pac::eth::Eth {
crate::pac::ETH
}
}
impl Instance for crate::peripherals::ETH {}
Empty file added src/eth/v1/mod.rs
Empty file.
Loading