phy stuff
Some checks failed
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled

This commit is contained in:
2025-06-28 22:30:57 +02:00
parent f057b99ec5
commit 16e277aaa9
6 changed files with 236 additions and 20 deletions

View File

@@ -20,6 +20,7 @@ zynq7000-hal = { path = "../../zynq7000-hal" }
zynq7000-embassy = { path = "../../zynq7000-embassy" }
l3gd20 = { git = "https://github.com/us-irs/l3gd20.git", branch = "add-async-if" }
embedded-io = "0.6"
bitbybit = "1.3"
arbitrary-int = "1.3"
embedded-io-async = "0.6"
critical-section = "1"

View File

@@ -1,6 +1,8 @@
#![no_std]
#![no_main]
use arbitrary_int::{u2, u4, u5};
use bitbybit::bitfield;
use core::{mem::MaybeUninit, panic::PanicInfo};
use cortex_ar::asm::nop;
use embassy_executor::Spawner;
@@ -10,19 +12,21 @@ use embedded_io::Write;
use log::{error, info};
use zedboard::PS_CLOCK_FREQUENCY;
use zynq7000_hal::{
BootMode,
clocks::Clocks,
configure_level_shifter,
eth::{EthernetConfig, EthernetLowLevel},
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
gpio::{GpioPins, Output, PinState},
gtc::Gtc,
uart::{ClkConfigRaw, Uart, UartConfig},
BootMode,
};
use zynq7000::{PsPeripherals, slcr::LevelShifterConfig};
use zynq7000::{eth::MdcClkDiv, slcr::LevelShifterConfig, PsPeripherals};
use zynq7000_rt::{self as _, mmu::section_attrs::SHAREABLE_DEVICE, mmu_l1_table_mut};
const INIT_STRING: &str = "-- Zynq 7000 Zedboard Ethernet Example --\n\r";
const MAC_ADDRESS: [u8; 6] = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06];
#[unsafe(link_section = ".uncached")]
static RX_DESCRIPTORS: static_cell::ConstStaticCell<
@@ -59,11 +63,117 @@ pub extern "C" fn boot_core(cpu_id: u32) -> ! {
main();
}
#[derive(Clone, Debug)]
pub struct PhyIdentifier {
pub oui: u32,
pub model: u8,
pub rev: u8,
}
// Organizational Unique Identifier for Marvell 88E1518 PHY
const MARVELL_88E1518_OUI: u32 = 0x005043;
const MARVELL_88E1518_MODELL_NUMBER: u8 = 0b011101;
#[bitbybit::bitenum(u5, exhaustive = false)]
pub enum MarvellRegistersPage0 {
CopperControl = 0x00,
CopperStatus = 0x01,
IdReg1 = 0x02,
IdReg2 = 0x03,
}
pub struct Marvell88E1518Phy {
mdio: zynq7000_hal::eth::mdio::Mdio,
addr: u5,
}
impl<'mdio> Marvell88E1518Phy<'mdio> {
pub fn new_autoprobe_addr(
mdio: &zynq7000_hal::eth::mdio::Mdio,
) -> Option<(Self, u4)> {
for addr in 0..32 {
let phy_id_1 =
mdio.read_blocking(u5::new(addr), MarvellRegistersPage0::IdReg1.raw_value());
let phy_id_2 =
mdio.read_blocking(u5::new(addr), MarvellRegistersPage0::IdReg2.raw_value());
let oui = (((phy_id_2 as u32) >> 10) << 19) | ((phy_id_1 as u32) << 3);
let model_number = ((phy_id_2 >> 4) & 0b111111) as u8;
let revision_number = u4::new((phy_id_2 & 0b1111) as u8);
if oui == MARVELL_88E1518_OUI && model_number == MARVELL_88E1518_MODELL_NUMBER {
return Some((
Self {
mdio: unsafe { mdio.clone() },
addr: u5::new(addr),
},
revision_number,
));
}
}
None
}
pub fn new(mdio: zynq7000_hal::eth::mdio::Mdio, addr: u5) -> Self {
Self { mdio, addr }
}
}
#[bitbybit::bitfield(u16)]
pub struct CopperControlRegister {
#[bit(15, rw)]
copper_reset: bool,
#[bit(14, rw)]
loopback: bool,
#[bit(12, rw)]
auto_negotiation_enable: bool,
#[bit(11, rw)]
power_down: bool,
#[bit(10, rw)]
isolate: bool,
#[bit(9, rw)]
restart_auto_negotiation: bool,
/// 1: Full-duplex, 0: Half-duplex
#[bit(8, rw)]
copper_duplex_mode: bool,
#[bits([13, 6], rw)]
speed_selection: u2,
}
impl Marvell88E1518Phy<'_> {
pub fn reset(&mut self) {
let mut ctrl = CopperControlRegister::new_with_raw_value(
self.mdio
.read_blocking(self.addr, MarvellRegistersPage0::CopperControl.raw_value()),
);
ctrl.set_copper_reset(true);
self.mdio.write_blocking(
self.addr,
MarvellRegistersPage0::CopperControl.raw_value(),
ctrl.raw_value(),
);
}
pub fn restart_auto_negotiation(&mut self) {
let mut ctrl = CopperControlRegister::new_with_raw_value(
self.mdio
.read_blocking(self.addr, MarvellRegistersPage0::CopperControl.raw_value()),
);
ctrl.set_auto_negotiation_enable(true);
ctrl.set_restart_auto_negotiation(true);
self.mdio.write_blocking(
self.addr,
MarvellRegistersPage0::CopperControl.raw_value(),
ctrl.raw_value(),
);
}
}
#[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(_spawner: Spawner) -> ! {
// Configure the uncached memory region using the MMU.
mmu_l1_table_mut().update(UNCACHED_ADDR, SHAREABLE_DEVICE);
mmu_l1_table_mut()
.update(UNCACHED_ADDR, SHAREABLE_DEVICE)
.expect("configuring uncached memory section failed");
// Enable PS-PL level shifters.
configure_level_shifter(LevelShifterConfig::EnableAll);
@@ -121,6 +231,57 @@ async fn main(_spawner: Spawner) -> ! {
rx_descr_ref.set_rx_buf_address(index, rx_buf.0.as_ptr() as u32);
}
// Unwrap okay, this is a valid peripheral.
let eth_ll = EthernetLowLevel::new(dp.eth_0).unwrap();
let (clk_config, clk_error) = zynq7000_hal::eth::ClkConfig::calculate_for_rgmii(
clocks.io_clocks().ref_clk(),
zynq7000_hal::eth::Speed::Mbps1000,
);
info!(
"Calculated RGMII clock configuration: {:?}, error: {}",
clk_config, clk_error
);
// Unwrap okay, we use a standard clock config, and the clock config should never fail.
let eth_cfg = EthernetConfig::new(
clk_config,
zynq7000_hal::eth::calculate_mdc_clk_div(clocks.arm_clocks()).unwrap(),
MAC_ADDRESS,
);
// Configures all the physical pins for ethernet operation and sets up the
// ethernet peripheral.
let mut eth = zynq7000_hal::eth::Ethernet::new_with_mio(
eth_ll,
eth_cfg,
gpio_pins.mio.mio16,
gpio_pins.mio.mio21,
(
gpio_pins.mio.mio17,
gpio_pins.mio.mio18,
gpio_pins.mio.mio19,
gpio_pins.mio.mio20,
),
gpio_pins.mio.mio22,
gpio_pins.mio.mio27,
(
gpio_pins.mio.mio23,
gpio_pins.mio.mio24,
gpio_pins.mio.mio25,
gpio_pins.mio.mio26,
),
Some((gpio_pins.mio.mio52, gpio_pins.mio.mio53)),
);
eth.set_rx_buf_descriptor_base_address(rx_descr_ref.base_addr());
eth.set_tx_buf_descriptor_base_address(tx_descr_ref.base_addr());
let (mut phy, phy_rev) = Marvell88E1518Phy::new_autoprobe_addr(eth.mdio_mut()).unwrap();
info!("Detected Marvell 88E1518 PHY with revision number: {:?}", phy_rev);
phy.reset();
phy.restart_auto_negotiation();
// TODO:
// 1. PHY configuration.
// 2. Interrupt handler for ethernet RX and TX. Need to pass the buffers and descriptors
// to the interrupt handler.
//
info!("Boot mode: {:?}", boot_mode);
let mut ticker = Ticker::every(Duration::from_millis(200));

View File

@@ -15,12 +15,7 @@ critical-section = "1"
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
zynq7000-hal = { path = "../zynq7000-hal" }
<<<<<<< HEAD
embassy-time-driver = { git = "https://github.com/embassy-rs/embassy.git", branch = "main", version = "0.2" }
embassy-time-queue-utils = { git = "https://github.com/embassy-rs/embassy.git", branch = "main", version = "0.1" }
=======
# embassy-time-driver = { git = "https://github.com/us-irs/embassy", branch = "add-cortex-ar-support", version = "0.2" }
# embassy-time-queue-utils = { git = "https://github.com/us-irs/embassy", branch = "add-cortex-ar-support", version = "0.2" }
# embassy-time-driver = { git = "https://github.com/embassy-rs/embassy.git", branch = "main", version = "0.2" }
# embassy-time-queue-utils = { git = "https://github.com/embassy-rs/embassy.git", branch = "main", version = "0.1" }
embassy-time-driver = { path = "../../../Rust/embassy/embassy-time-driver", version = "0.2" }
embassy-time-queue-utils = { path = "../../../Rust/embassy/embassy-time-queue-utils", version = "0.1" }
>>>>>>> 807d9df (start adding ethernet support)

View File

@@ -26,6 +26,18 @@ impl Mdio {
}
}
/// Steals the MDIO handle from the given Ethernet low-level interface.
///
/// # Safety
///
/// Circumvents ownership and safety guarantees of the HAL.
pub unsafe fn clone(&self) -> Self {
Self {
regs: unsafe { self.regs.clone() },
clause22: self.clause22,
}
}
#[inline]
pub fn configure_clock_div(&mut self, clk_div: MdcClkDiv) {
self.regs.modify_net_cfg(|mut val| {

View File

@@ -1,11 +1,11 @@
use arbitrary_int::{u2, u3};
pub use zynq7000::eth::MdcClkDiv;
use zynq7000::eth::{
BurstLength, DmaRxBufSize, GEM_0_BASE_ADDR, GEM_1_BASE_ADDR, MmioEthernet, NetworkConfig,
SpeedMode,
BurstLength, DmaRxBufSize, MmioEthernet, NetworkConfig, SpeedMode, GEM_0_BASE_ADDR,
GEM_1_BASE_ADDR,
};
pub use ll::{ClkConfig, EthernetLowLevel};
pub use ll::{ClkConfig, EthernetLowLevel, Speed};
pub mod ll;
pub mod mdio;
@@ -13,17 +13,22 @@ pub mod rx_descr;
pub mod tx_descr;
pub const MTU: usize = 1536;
pub const MAX_MDC_SPEED: Hertz = Hertz::from_raw(2_500_000);
#[cfg(not(feature = "7z010-7z007s-clg225"))]
use crate::gpio::mio::{
Mio16, Mio17, Mio18, Mio19, Mio20, Mio21, Mio22, Mio23, Mio24, Mio25, Mio26, Mio27,
};
use crate::gpio::{
IoPeriphPin,
mio::{
Mio28, Mio29, Mio30, Mio31, Mio32, Mio33, Mio34, Mio35, Mio36, Mio37, Mio38, Mio39, Mio52,
Mio53, MioPinMarker, MuxConf, Pin,
use crate::{
clocks::ArmClocks,
gpio::{
mio::{
Mio28, Mio29, Mio30, Mio31, Mio32, Mio33, Mio34, Mio35, Mio36, Mio37, Mio38, Mio39,
Mio52, Mio53, MioPinMarker, MuxConf, Pin,
},
IoPeriphPin,
},
time::Hertz,
};
pub const MUX_CONF_PHY: MuxConf = MuxConf::new_with_l0();
@@ -203,6 +208,23 @@ impl RxData3 for Pin<Mio38> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
/// Calculate the CPU 1x clock divisor required to achieve a clock speed which is below
/// 2.5 MHz, as specified by the 802.3 standard.
pub fn calculate_mdc_clk_div(arm_clks: &ArmClocks) -> Option<MdcClkDiv> {
let div = arm_clks.cpu_1x_clk().raw().div_ceil(MAX_MDC_SPEED.raw());
match div {
0..8 => Some(MdcClkDiv::Div8),
8..16 => Some(MdcClkDiv::Div16),
16..32 => Some(MdcClkDiv::Div32),
32..64 => Some(MdcClkDiv::Div64),
64..128 => Some(MdcClkDiv::Div128),
128..224 => Some(MdcClkDiv::Div224),
// MDC clock divisor is too high for the maximum speed.
// This is not a valid configuration.
_ => None,
}
}
#[derive(Debug, Clone, Copy)]
pub struct EthernetConfig {
pub clk_config: ClkConfig,
@@ -372,7 +394,10 @@ impl Ethernet {
ll.configure_clock(config.clk_config);
let mut mdio = mdio::Mdio::new(&ll, true);
mdio.configure_clock_div(config.mdc_clk_div);
Ethernet { ll, mdio }
let eth = Ethernet { ll, mdio }
eth.set_rx_buf_descriptor_base_address(0);
eth.set_tx_buf_descriptor_base_address(0);
eth
}
pub fn new(mut ll: EthernetLowLevel, config: EthernetConfig) -> Self {
@@ -405,7 +430,6 @@ impl Ethernet {
// to disable the other addresses here.
ll.regs.write_addr1_low(macaddr_lsbs);
ll.regs.write_addr1_high(macaddr_msbs);
// TODO
ll.regs.modify_dma_cfg(|mut val| {
val.set_rx_packet_buf_size_sel(u2::new(0b11));
val.set_tx_packet_buf_size_sel(true);
@@ -428,10 +452,29 @@ impl Ethernet {
&self.ll.regs
}
#[inline]
pub fn mdio(&mut self) -> &mdio::Mdio {
&self.mdio
}
pub fn mdio_mut(&mut self) -> &mut mdio::Mdio {
&mut self.mdio
}
#[inline]
pub fn regs_mut(&mut self) -> &mut MmioEthernet<'static> {
&mut self.ll.regs
}
#[inline]
pub fn set_rx_buf_descriptor_base_address(&mut self, addr: u32) {
self.ll.set_rx_buf_descriptor_base_address(addr);
}
#[inline]
pub fn set_tx_buf_descriptor_base_address(&mut self, addr: u32) {
self.ll.set_rx_buf_descriptor_base_address(addr);
}
}
mod shared {

View File

@@ -48,6 +48,8 @@ pub struct PsPeripherals {
pub slcr: slcr::MmioSlcr<'static>,
pub ttc_0: ttc::MmioTtc<'static>,
pub ttc_1: ttc::MmioTtc<'static>,
pub eth_0: eth::MmioEthernet<'static>,
pub eth_1: eth::MmioEthernet<'static>
}
impl PsPeripherals {
@@ -81,6 +83,8 @@ impl PsPeripherals {
i2c_1: i2c::I2c::new_mmio_fixed_1(),
ttc_0: ttc::Ttc::new_mmio_fixed_0(),
ttc_1: ttc::Ttc::new_mmio_fixed_1(),
eth_0: eth::Ethernet::new_mmio_fixed_0(),
eth_1: eth::Ethernet::new_mmio_fixed_1(),
}
}
}