start adding smoltcp/ethernet support

This commit is contained in:
2025-05-27 12:02:57 +02:00
committed by Robin.Mueller
parent 61ffe06343
commit 05e23303c2
26 changed files with 5882 additions and 4217 deletions

View File

@ -13,7 +13,9 @@ categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
cortex-ar = { git = "https://github.com/rust-embedded/cortex-ar", branch = "main" }
zynq7000 = { path = "../zynq7000" }
zynq-mmu = { path = "../zynq-mmu", version = "0.1.0" }
bitbybit = "1.3"
arbitrary-int = "1.3"
thiserror = { version = "2", default-features = false }
num_enum = { version = "0.7", default-features = false }

212
zynq7000-hal/src/eth/ll.rs Normal file
View File

@ -0,0 +1,212 @@
use arbitrary_int::{Number, u6};
use zynq7000::{
eth::{InterruptControl, NetworkControl, RxStatus, TxStatus},
slcr::reset::EthernetReset,
};
use crate::{enable_amba_peripheral_clock, slcr::Slcr, time::Hertz};
use super::{EthernetId, PsEthernet as _};
pub struct EthernetLowLevel {
id: EthernetId,
pub regs: zynq7000::eth::MmioEthernet<'static>,
}
#[derive(Debug, Clone, Copy)]
pub struct ClkConfig {
pub src_sel: zynq7000::slcr::clocks::SrcSelIo,
pub use_emio_tx_clk: bool,
pub divisor_0: u6,
pub divisor_1: u6,
/// Enable the clock.
pub enable: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Speed {
Mbps10,
Mbps100,
Mbps1000,
}
impl Speed {
pub const fn rgmii_clk_rate(&self) -> Hertz {
match self {
Speed::Mbps10 => Hertz::from_raw(2_500_000),
Speed::Mbps100 => Hertz::from_raw(25_000_000),
Speed::Mbps1000 => Hertz::from_raw(125_000_000),
}
}
}
impl ClkConfig {
pub const fn new(divisor_0: u6, divisor_1: u6) -> Self {
Self {
src_sel: zynq7000::slcr::clocks::SrcSelIo::IoPll,
use_emio_tx_clk: false,
divisor_0,
divisor_1,
enable: true,
}
}
/// Calculate the best clock configuration (divisors) for the given reference clock
/// and desired target speed when using a RGMII interface.
///
/// Usually, the reference clock will be the IO clock.
///
/// Returns a tuple where the first entry is the calcualted clock configuration
/// and the second entry is the difference between calculated clock speed for the divisors
/// and the target speed. Ideally, this difference should be 0.
pub fn calculate_for_rgmii(ref_clk: Hertz, target_speed: Speed) -> (Self, u32) {
let mut smallest_diff = u32::MAX;
let target_speed = target_speed.rgmii_clk_rate();
let mut best_div_0 = u6::new(0);
let mut best_div_1 = u6::new(0);
for div_0 in 0..=u6::MAX.as_usize() {
for div_1 in 0..=u6::MAX.as_usize() {
let clk_rate = ref_clk.raw() / div_0 as u32 / div_1 as u32;
let diff = (target_speed.raw() as i64 - clk_rate as i64).unsigned_abs() as u32;
if diff < smallest_diff {
smallest_diff = diff;
best_div_0 = u6::new(div_0 as u8);
best_div_1 = u6::new(div_1 as u8);
}
// We found a perfect match. No need to continue.
if diff == 0 {
break;
}
}
}
(Self::new(best_div_0, best_div_1), smallest_diff)
}
}
/// Ethernet low-level interface.
impl EthernetLowLevel {
/// Creates a new instance of the Ethernet low-level interface.
#[inline]
pub fn new(regs: zynq7000::eth::MmioEthernet<'static>) -> Option<Self> {
regs.id()?;
Some(EthernetLowLevel {
id: regs.id().unwrap(),
regs,
})
}
/// Create a low-level instance for the given [EthernetId].
///
/// # Safety
///
/// Circumvents ownership and safety guarantees of the HAL.
#[inline]
pub const unsafe fn steal(id: EthernetId) -> Self {
Self {
id,
regs: unsafe {
match id {
EthernetId::Eth0 => zynq7000::eth::Ethernet::new_mmio_fixed_0(),
EthernetId::Eth1 => zynq7000::eth::Ethernet::new_mmio_fixed_1(),
}
},
}
}
pub fn reset(&mut self, cycles: usize) {
let assert_reset = match self.id {
EthernetId::Eth0 => EthernetReset::builder()
.with_gem1_ref_rst(false)
.with_gem0_ref_rst(true)
.with_gem1_rx_rst(false)
.with_gem0_rx_rst(true)
.with_gem1_cpu1x_rst(false)
.with_gem0_cpu1x_rst(true)
.build(),
EthernetId::Eth1 => EthernetReset::builder()
.with_gem1_ref_rst(true)
.with_gem0_ref_rst(false)
.with_gem1_rx_rst(true)
.with_gem0_rx_rst(false)
.with_gem1_cpu1x_rst(true)
.with_gem0_cpu1x_rst(false)
.build(),
};
unsafe {
Slcr::with(|regs| {
regs.reset_ctrl().write_eth(assert_reset);
for _ in 0..cycles {
cortex_ar::asm::nop();
}
regs.reset_ctrl().write_eth(EthernetReset::DEFAULT);
});
}
}
#[inline]
pub fn enable_peripheral_clock(&mut self) {
let periph_sel = match self.id {
EthernetId::Eth0 => crate::PeripheralSelect::Gem0,
EthernetId::Eth1 => crate::PeripheralSelect::Gem1,
};
enable_amba_peripheral_clock(periph_sel);
}
#[inline]
pub fn configure_clock(&mut self, cfg: ClkConfig) {
unsafe {
Slcr::with(|regs| {
regs.clk_ctrl().modify_gem_0_clk_ctrl(|mut val| {
val.set_srcsel(cfg.src_sel);
val.set_divisor_0(cfg.divisor_0);
val.set_divisor_1(cfg.divisor_1);
val.set_use_emio_tx_clk(cfg.use_emio_tx_clk);
val.set_clk_act(cfg.enable);
val
});
})
}
}
#[inline]
pub fn set_promiscous_mode(&mut self, enable: bool) {
self.regs.modify_net_cfg(|mut val| {
val.set_copy_all_frames(enable);
val
});
}
#[inline]
pub fn set_rx_buf_descriptor_base_address(&mut self, addr: u32) {
// self.regs.
// TODO
}
#[inline]
pub fn set_tx_buf_descriptor_base_address(&mut self, addr: u32) {
// self.regs.
// TODO
}
/// Performs initialization according to TRM p.541.
///
/// These steps do not include any resets or clock configuration.
pub fn initialize(&mut self) {
let mut ctrl_val = NetworkControl::new_with_raw_value(0);
self.regs.write_net_ctrl(ctrl_val);
// Now clear statistics.
ctrl_val.set_clear_statistics(true);
self.regs.write_net_ctrl(ctrl_val);
self.regs.write_tx_status(TxStatus::new_clear_all());
self.regs.write_rx_status(RxStatus::new_clear_all());
self.regs
.write_interrupt_disable(InterruptControl::new_clear_all());
self.regs.write_rx_buf_queue_base_addr(0);
self.regs.write_tx_buf_queue_base_addr(0);
}
#[inline]
pub const fn id(&self) -> EthernetId {
self.id
}
}

View File

@ -0,0 +1,65 @@
use arbitrary_int::{u2, u5};
use zynq7000::eth::{MdcClkDiv, PhyMaintenance};
use super::{EthernetId, ll::EthernetLowLevel};
pub struct Mdio {
regs: zynq7000::eth::MmioEthernet<'static>,
clause22: bool,
}
impl Mdio {
pub fn new(ll: &EthernetLowLevel, clause22: bool) -> Self {
Self {
regs: unsafe { ll.regs.clone() },
clause22,
}
}
/// # Safety
///
/// Circumvents ownership and safety guarantees of the HAL.
pub unsafe fn steal(eth_id: EthernetId, clause22: bool) -> Self {
Self {
regs: unsafe { eth_id.steal_regs() },
clause22,
}
}
#[inline]
pub fn configure_clock_div(&mut self, clk_div: MdcClkDiv) {
self.regs.modify_net_cfg(|mut val| {
val.set_mdc_clk_div(clk_div);
val
});
}
pub fn read_blocking(&mut self, phy_addr: u5, reg_addr: u5) -> u16 {
self.regs.write_phy_maintenance(
PhyMaintenance::builder()
.with_clause_22(self.clause22)
.with_op(zynq7000::eth::PhyOperation::Read)
.with_phy_addr(phy_addr)
.with_reg_addr(reg_addr)
.with_must_be_0b10(u2::new(0b10))
.with_data(0x0000)
.build(),
);
while !self.regs.read_net_status().phy_mgmt_idle() {}
self.regs.read_phy_maintenance().data()
}
pub fn write_blocking(&mut self, phy_addr: u5, reg_addr: u5, data: u16) {
self.regs.write_phy_maintenance(
PhyMaintenance::builder()
.with_clause_22(self.clause22)
.with_op(zynq7000::eth::PhyOperation::Write)
.with_phy_addr(phy_addr)
.with_reg_addr(reg_addr)
.with_must_be_0b10(u2::new(0b10))
.with_data(data)
.build(),
);
while !self.regs.read_net_status().phy_mgmt_idle() {}
}
}

444
zynq7000-hal/src/eth/mod.rs Normal file
View File

@ -0,0 +1,444 @@
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,
};
pub use ll::{ClkConfig, EthernetLowLevel};
pub mod ll;
pub mod mdio;
pub mod rx_descr;
pub mod tx_descr;
const MTU: usize = 1536;
#[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,
},
};
pub const MUX_CONF_PHY: MuxConf = MuxConf::new_with_l0();
pub const MUX_CONF_MDIO: MuxConf = MuxConf::new_with_l3(u3::new(0b100));
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EthernetId {
Eth0 = 0,
Eth1 = 1,
}
impl EthernetId {
/// Steal the ethernet register block for the given ethernet ID.
///
/// # Safety
///
/// Circumvents ownership and safety guarantees of the HAL.
pub const unsafe fn steal_regs(&self) -> zynq7000::eth::MmioEthernet<'static> {
unsafe {
match self {
EthernetId::Eth0 => zynq7000::eth::Ethernet::new_mmio_fixed_0(),
EthernetId::Eth1 => zynq7000::eth::Ethernet::new_mmio_fixed_1(),
}
}
}
}
pub trait PsEthernet {
fn reg_block(&self) -> MmioEthernet<'static>;
fn id(&self) -> Option<EthernetId>;
}
impl PsEthernet for MmioEthernet<'static> {
#[inline]
fn reg_block(&self) -> MmioEthernet<'static> {
unsafe { self.clone() }
}
#[inline]
fn id(&self) -> Option<EthernetId> {
let base_addr = unsafe { self.ptr() } as usize;
if base_addr == GEM_0_BASE_ADDR {
return Some(EthernetId::Eth0);
} else if base_addr == GEM_1_BASE_ADDR {
return Some(EthernetId::Eth1);
}
None
}
}
pub trait TxClk: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait TxCtrl: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait TxData0: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait TxData1: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait TxData2: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait TxData3: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait RxClk: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait RxCtrl: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait RxData0: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait RxData1: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait RxData2: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait RxData3: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait MdClk: MioPinMarker {}
pub trait MdIo: MioPinMarker {}
impl MdClk for Pin<Mio52> {}
impl MdIo for Pin<Mio53> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl TxClk for Pin<Mio16> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl TxCtrl for Pin<Mio21> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl TxData0 for Pin<Mio17> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl TxData1 for Pin<Mio18> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl TxData2 for Pin<Mio19> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl TxData3 for Pin<Mio20> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl RxClk for Pin<Mio22> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl RxCtrl for Pin<Mio27> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl RxData0 for Pin<Mio23> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl RxData1 for Pin<Mio24> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl RxData2 for Pin<Mio25> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl RxData3 for Pin<Mio26> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
impl TxClk for Pin<Mio28> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl TxCtrl for Pin<Mio33> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl TxData0 for Pin<Mio29> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl TxData1 for Pin<Mio30> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl TxData2 for Pin<Mio31> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl TxData3 for Pin<Mio32> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl RxClk for Pin<Mio34> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl RxCtrl for Pin<Mio39> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl RxData0 for Pin<Mio35> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl RxData1 for Pin<Mio36> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl RxData2 for Pin<Mio37> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl RxData3 for Pin<Mio38> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
#[derive(Debug, Clone, Copy)]
pub struct EthernetConfig {
pub clk_config: ClkConfig,
pub mdc_clk_div: MdcClkDiv,
pub mac_address: [u8; 6],
}
impl EthernetConfig {
pub fn new(clk_config: ClkConfig, mdc_clk_div: MdcClkDiv, mac_address: [u8; 6]) -> Self {
Self {
clk_config,
mdc_clk_div,
mac_address,
}
}
}
pub struct Ethernet {
ll: ll::EthernetLowLevel,
mdio: mdio::Mdio,
}
impl Ethernet {
#[allow(clippy::too_many_arguments)]
pub fn new_with_mio<
TxClkPin: TxClk,
TxCtrlPin: TxCtrl,
TxData0Pin: TxData0,
TxData1Pin: TxData1,
TxData2Pin: TxData2,
TxData3Pin: TxData3,
RxClkPin: RxClk,
RxCtrlPin: RxCtrl,
RxData0Pin: RxData0,
RxData1Pin: RxData1,
RxData2Pin: RxData2,
RxData3Pin: RxData3,
MdClkPin: MdClk,
MdIoPin: MdIo,
>(
mut ll: ll::EthernetLowLevel,
config: EthernetConfig,
tx_clk: TxClkPin,
tx_ctrl: TxCtrlPin,
tx_data: (TxData0Pin, TxData1Pin, TxData2Pin, TxData3Pin),
rx_clk: RxClkPin,
rx_ctrl: RxCtrlPin,
rx_data: (RxData0Pin, RxData1Pin, RxData2Pin, RxData3Pin),
md_pins: Option<(MdClkPin, MdIoPin)>,
) -> Self {
Self::common_init(&mut ll, config.mac_address);
let tx_mio_config = zynq7000::slcr::mio::Config::builder()
.with_disable_hstl_rcvr(true)
.with_pullup(true)
.with_io_type(zynq7000::slcr::mio::IoType::Hstl)
.with_speed(zynq7000::slcr::mio::Speed::FastCmosEdge)
.with_l3_sel(MUX_CONF_PHY.l3_sel())
.with_l2_sel(MUX_CONF_PHY.l2_sel())
.with_l1_sel(MUX_CONF_PHY.l1_sel())
.with_l0_sel(MUX_CONF_PHY.l0_sel())
.with_tri_enable(false)
.build();
let rx_mio_config = zynq7000::slcr::mio::Config::builder()
.with_disable_hstl_rcvr(false)
.with_pullup(true)
.with_io_type(zynq7000::slcr::mio::IoType::Hstl)
.with_speed(zynq7000::slcr::mio::Speed::FastCmosEdge)
.with_l3_sel(MUX_CONF_PHY.l3_sel())
.with_l2_sel(MUX_CONF_PHY.l2_sel())
.with_l1_sel(MUX_CONF_PHY.l1_sel())
.with_l0_sel(MUX_CONF_PHY.l0_sel())
// Disable output driver.
.with_tri_enable(true)
.build();
unsafe {
crate::slcr::Slcr::with(|slcr_mut| {
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
tx_clk,
slcr_mut,
tx_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
tx_ctrl,
slcr_mut,
tx_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
tx_data.0,
slcr_mut,
tx_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
tx_data.1,
slcr_mut,
tx_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
tx_data.2,
slcr_mut,
tx_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
tx_data.3,
slcr_mut,
tx_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
rx_clk,
slcr_mut,
rx_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
rx_ctrl,
slcr_mut,
rx_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
rx_data.0,
slcr_mut,
rx_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
rx_data.1,
slcr_mut,
rx_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
rx_data.2,
slcr_mut,
rx_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
rx_data.3,
slcr_mut,
rx_mio_config,
);
if let Some((md_clk, md_io)) = md_pins {
let md_mio_config = zynq7000::slcr::mio::Config::builder()
.with_disable_hstl_rcvr(false)
.with_pullup(true)
.with_io_type(zynq7000::slcr::mio::IoType::LvCmos18)
.with_speed(zynq7000::slcr::mio::Speed::SlowCmosEdge)
.with_l3_sel(MUX_CONF_MDIO.l3_sel())
.with_l2_sel(MUX_CONF_MDIO.l2_sel())
.with_l1_sel(MUX_CONF_MDIO.l1_sel())
.with_l0_sel(MUX_CONF_MDIO.l0_sel())
.with_tri_enable(false)
.build();
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
md_clk,
slcr_mut,
md_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
md_io,
slcr_mut,
md_mio_config,
);
}
// Enable VREF internal generator, which is required for HSTL pin mode.
slcr_mut.gpiob().modify_ctrl(|mut ctrl| {
ctrl.set_vref_en(true);
ctrl
});
});
}
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 }
}
pub fn new(mut ll: EthernetLowLevel, config: EthernetConfig) -> Self {
Self::common_init(&mut ll, config.mac_address);
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 }
}
fn common_init(ll: &mut EthernetLowLevel, mac_address: [u8; 6]) {
ll.enable_peripheral_clock();
ll.reset(3);
ll.initialize();
// By default, only modify critical network control bits to retain user configuration
// like the MDC clock divisor.
ll.regs.modify_net_cfg(|mut net_cfg| {
net_cfg.set_full_duplex(true);
net_cfg.set_gigabit_enable(true);
net_cfg.set_speed_mode(SpeedMode::High100Mbps);
net_cfg
});
let macaddr_msbs = (u32::from(mac_address[5]) << 8) | u32::from(mac_address[4]);
let macaddr_lsbs = (u32::from(mac_address[3]) << 24)
| (u32::from(mac_address[2]) << 16)
| (u32::from(mac_address[1]) << 8)
| u32::from(mac_address[0]);
// Writing to the lower address portion disables the address match, writing to the higher
// portion enables it again. Address matching is disabled on reset, so we do not need
// 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);
val.set_burst_length(BurstLength::Incr16.reg_value());
// Configure 1536 bytes receive buffer size. This is sufficient for regular Ethernet
// frames.
val.set_dma_rx_ahb_buf_size_sel(DmaRxBufSize::new((MTU >> 6) as u8).unwrap());
val.set_endian_swap_mgmt_descriptor(zynq7000::eth::AhbEndianess::Little);
val
});
}
#[inline]
pub fn ll(&mut self) -> &mut EthernetLowLevel {
&mut self.ll
}
#[inline]
pub fn regs(&mut self) -> &MmioEthernet<'static> {
&self.ll.regs
}
#[inline]
pub fn regs_mut(&mut self) -> &mut MmioEthernet<'static> {
&mut self.ll.regs
}
}
mod shared {
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum Ownership {
Hardware = 0,
Software = 1,
}
}

View File

@ -0,0 +1,122 @@
//! RX buffer descriptor module.
pub use super::shared::Ownership;
use arbitrary_int::{u2, u3, u13, u30};
/// RX buffer descriptor.
///
/// The user should declare an array of this structure inside uncached memory.
///
/// These descriptors are shared between software and hardware and contain information
/// related to frame reception.
#[repr(C)]
pub struct Descriptor {
/// The first word of the descriptor.
pub word0: Word0,
/// The second word of the descriptor.
pub word1: Word1,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug, PartialEq, Eq)]
pub struct Word0 {
/// The full reception address with the last two bits cleared.
#[bits(2..=31, rw)]
addr_upper_30_bits: u30,
#[bit(1, rw)]
wrap: bool,
#[bit(0, rw)]
ownership: Ownership,
}
/// This control word contains the status bits.
///
/// If the end of frame bit (15) is not set, the only valid status information is the start of
/// frame bit.
#[bitbybit::bitfield(u32)]
#[derive(Debug, PartialEq, Eq)]
pub struct Word1 {
#[bit(31, r)]
broadcast_detect: bool,
#[bit(30, r)]
multicast_hash: bool,
#[bit(29, r)]
unicast_hash: bool,
#[bit(27, r)]
specific_addr_match: bool,
/// Specifies which of the 4 specific address registers was matched.
#[bits(25..=26, r)]
specific_addr_match_info: u2,
#[bit(24, r)]
type_id_match_or_snap_info: bool,
#[bits(22..=23, r)]
type_id_match_info_or_chksum_status: u2,
#[bit(21, r)]
vlan_tag_detected: bool,
#[bit(20, r)]
priority_tag_detected: bool,
#[bits(17..=19, r)]
vlan_prio: u3,
#[bit(16, r)]
cfi_bit: bool,
/// If this bit is not set, the only other valid status bit is the start of frame bit.
#[bit(15, r)]
end_of_frame: bool,
#[bit(14, r)]
start_of_frame: bool,
/// Relevant when FCS errors are not ignored.
/// 0: Frame has good FCS, 1: Frame has bad FCS, but was copied to memory as the ignore FCS
/// functionality was enabled.
#[bit(13, r)]
fcs_status: bool,
#[bits(0..=12, r)]
rx_len: u13,
}
impl Descriptor {
#[inline]
pub fn set_ownership(&mut self, ownership: Ownership) {
self.word0.set_ownership(ownership);
}
#[inline]
pub fn set_wrap_bit(&mut self) {
self.word0.set_wrap(true);
}
#[inline]
pub fn write_rx_addr(&mut self, addr: u32) {
self.word0.set_addr_upper_30_bits(u30::new(addr >> 2));
}
}
pub struct DescriptorListRef<'a>(&'a [Descriptor]);
impl DescriptorListRef<'_> {
#[inline]
pub fn new(descriptor: &[Descriptor]) -> Self {
Self(descriptor)
}
#[inline]
pub fn base_ptr(&self) -> *const Descriptor {
self.0.as_ptr()
}
#[inline]
pub fn base_addr(&self) -> u32 {
self.base_ptr() as u32
}
pub fn init(&mut self) {
for desc in self.0.iter_mut() {
desc.set_ownership(Ownership::Hardware);
}
self.0.last().unwrap().set_wrap_bit();
}
pub fn set_rx_buf_addresses<const S: usize>(&mut self, buf: &[[u8; S]]) {
for (desc, buf) in self.0.iter_mut().zip(buf.iter()) {
desc.write_rx_addr(buf.as_ptr() as u32);
}
}
}

View File

@ -0,0 +1,103 @@
use arbitrary_int::u14;
pub use super::shared::Ownership;
/// TX buffer descriptor.
///
/// The user should declare an array of this structure inside uncached memory.
///
/// These descriptors are shared between software and hardware and contain information
/// related to frame reception.
#[repr(C)]
pub struct Descriptor {
/// The first word of the descriptor which is the byte address of the buffer.
pub word0: u32,
/// The second word of the descriptor.
pub word1: Word1,
}
#[bitbybit::bitenum(u3, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum TransmitChecksumGenerationStatus {
NoError = 0b000,
VlanError = 0b001,
SnapError = 0b010,
IpError = 0b011,
NotVlanOrSnapOrIp = 0b100,
NonSupportedPacketFragmentation = 0b101,
PacketNotTcpUdp = 0b110,
PrematureEndOfFrame = 0b111,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug, PartialEq, Eq)]
pub struct Word1 {
#[bit(31, rw)]
ownership: Ownership,
#[bit(30, rw)]
wrap: bool,
#[bit(29, rw)]
retry_limit_exceeded: bool,
#[bit(27, rw)]
transmit_frame_corruption_ahb_error: bool,
#[bit(26, rw)]
late_collision: bool,
#[bits(20..=22, rw)]
checksum_status: TransmitChecksumGenerationStatus,
#[bit(16, rw)]
no_crc_generation: bool,
#[bit(15, rw)]
last_buffer: bool,
#[bits(0..=13, rw)]
tx_len: u14,
}
impl Descriptor {
#[inline]
pub fn set_ownership(&mut self, ownership: Ownership) {
self.word1.set_ownership(ownership);
}
/// Set the wrap bit, which should be done for the last descriptor in the descriptor list.
#[inline]
pub fn set_wrap_bit(&mut self) {
self.word1.set_wrap(true);
}
/// Set the information for a transfer.
pub fn set_tx_transfer_info(
&mut self,
tx_len: u14,
last_buffer: bool,
no_crc_generation: bool,
) {
self.word1.set_tx_len(tx_len);
self.word1.set_last_buffer(last_buffer);
self.word1.set_no_crc_generation(no_crc_generation);
}
}
pub struct DescriptorListRef<'a>(&'a [Descriptor]);
impl DescriptorListRef {
pub fn new(descriptor: &[Descriptor]) -> Self {
Self(descriptor)
}
#[inline]
pub fn base_ptr(&self) -> *const Descriptor {
self.0.as_ptr()
}
#[inline]
pub fn base_addr(&self) -> u32 {
self.base_ptr() as u32
}
pub fn init(&mut self) {
for desc in self.0.iter_mut() {
desc.set_ownership(Ownership::Hardware);
}
self.0.last().unwrap().set_wrap_bit();
}
}

View File

@ -4,7 +4,7 @@ use zynq7000::gpio::{Gpio, MaskedOutput, MmioGpio};
use crate::slcr::Slcr;
use super::{mio::MuxConf, PinIsOutputOnly};
use super::{PinIsOutputOnly, mio::MuxConf};
#[derive(Debug, Clone, Copy)]
pub enum PinOffset {
@ -148,6 +148,23 @@ impl LowLevelGpio {
self.reconfigure_slcr_mio_cfg(false, pullup, Some(mux_conf));
}
pub fn set_mio_pin_config(&mut self, config: zynq7000::slcr::mio::Config) {
let raw_offset = self.offset.offset();
// Safety: We only modify the MIO config of the pin.
let mut slcr_wrapper = unsafe { Slcr::steal() };
slcr_wrapper.modify(|mut_slcr| mut_slcr.write_mio_pins(raw_offset, config).unwrap());
}
/// Set the MIO pin configuration with an unlocked SLCR.
pub fn set_mio_pin_config_with_unlocked_slcr(
&mut self,
slcr: &mut zynq7000::slcr::MmioSlcr<'static>,
config: zynq7000::slcr::mio::Config,
) {
let raw_offset = self.offset.offset();
slcr.write_mio_pins(raw_offset, config).unwrap();
}
#[inline]
pub fn is_low(&self) -> bool {
let (offset, in_reg) = self.get_data_in_reg_and_local_offset();

View File

@ -31,6 +31,10 @@ impl MuxConf {
Self { l3, l2, l1, l0 }
}
pub const fn new_with_l0() -> Self {
Self::new(true, false, u2::new(0b00), u3::new(0b000))
}
pub const fn new_with_l3(l3: u3) -> Self {
Self::new(false, false, u2::new(0b00), l3)
}

View File

@ -172,7 +172,8 @@ impl Flex {
pub fn configure_as_output_open_drain(&mut self, level: PinState, with_internal_pullup: bool) {
self.mode = PinMode::OutputOpenDrain;
self.ll.configure_as_output_open_drain(level, with_internal_pullup);
self.ll
.configure_as_output_open_drain(level, with_internal_pullup);
}
/// If the pin is configured as an input pin, this function does nothing.
@ -383,6 +384,8 @@ pub struct IoPeriphPin {
}
impl IoPeriphPin {
/// Constructor for IO peripheral pins where only the multiplexer and pullup configuration
/// need to be changed.
pub fn new(pin: impl MioPinMarker, mux_conf: MuxConf, pullup: Option<bool>) -> Self {
let mut low_level = LowLevelGpio::new(PinOffset::Mio(pin.offset()));
low_level.configure_as_io_periph_pin(mux_conf, pullup);
@ -391,6 +394,43 @@ impl IoPeriphPin {
mux_conf,
}
}
/// Constructor to fully configure an IO peripheral pin with a specific MIO pin configuration.
pub fn new_with_full_config(
pin: impl MioPinMarker,
config: zynq7000::slcr::mio::Config,
) -> Self {
let mut low_level = LowLevelGpio::new(PinOffset::Mio(pin.offset()));
low_level.set_mio_pin_config(config);
Self {
pin: low_level,
mux_conf: MuxConf::new(
config.l0_sel(),
config.l1_sel(),
config.l2_sel(),
config.l3_sel(),
),
}
}
/// Constructor to fully configure an IO peripheral pin with a specific MIO pin configuration.
pub fn new_with_full_config_and_unlocked_slcr(
pin: impl MioPinMarker,
slcr: &mut zynq7000::slcr::MmioSlcr<'static>,
config: zynq7000::slcr::mio::Config,
) -> Self {
let mut low_level = LowLevelGpio::new(PinOffset::Mio(pin.offset()));
low_level.set_mio_pin_config_with_unlocked_slcr(slcr, config);
Self {
pin: low_level,
mux_conf: MuxConf::new(
config.l0_sel(),
config.l1_sel(),
config.l2_sel(),
config.l3_sel(),
),
}
}
}
impl IoPinProvider for IoPeriphPin {

View File

@ -13,11 +13,13 @@ use slcr::Slcr;
use zynq7000::slcr::LevelShifterReg;
pub mod clocks;
pub mod eth;
pub mod gic;
pub mod gpio;
pub mod gtc;
pub mod i2c;
pub mod log;
pub mod mmu;
pub mod prelude;
pub mod slcr;
pub mod spi;

16
zynq7000-hal/src/mmu.rs Normal file
View File

@ -0,0 +1,16 @@
use zynq_mmu::L1Table;
pub struct Mmu(&'static mut L1Table);
impl Mmu {
#[inline]
pub const fn new(table: &'static mut L1Table) -> Self {
Mmu(table)
}
pub fn update_l1_table(&mut self, f: impl FnOnce(&mut L1Table)) {
// TODO: Disable MMU
f(self.0);
// DSB, ISB? enable MMU again.
}
}

View File

@ -14,7 +14,7 @@ impl Slcr {
/// This method unsafely steals the SLCR MMIO block and then calls a user provided function
/// with the [SLCR MMIO][MmioSlcr] block as an input argument. It is the user's responsibility
/// that the SLCR is not used concurrently in a way which leads to data races.
pub unsafe fn with<F: FnMut(&mut MmioSlcr)>(mut f: F) {
pub unsafe fn with<F: FnOnce(&mut MmioSlcr<'static>)>(f: F) {
let mut slcr = unsafe { zynq7000::slcr::Slcr::new_mmio_fixed() };
slcr.write_unlock(UNLOCK_KEY);
f(&mut slcr);