diff --git a/zynq7000-hal/src/eth/ll.rs b/zynq7000-hal/src/eth/ll.rs new file mode 100644 index 0000000..9d96fd2 --- /dev/null +++ b/zynq7000-hal/src/eth/ll.rs @@ -0,0 +1,197 @@ +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 { + 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 configure_mdc_clk_div() { + // 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 + } +} diff --git a/zynq7000-hal/src/eth/mdio.rs b/zynq7000-hal/src/eth/mdio.rs new file mode 100644 index 0000000..bcb1df6 --- /dev/null +++ b/zynq7000-hal/src/eth/mdio.rs @@ -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() {} + } +} diff --git a/zynq7000-hal/src/eth/mod.rs b/zynq7000-hal/src/eth/mod.rs index 4667112..1635fda 100644 --- a/zynq7000-hal/src/eth/mod.rs +++ b/zynq7000-hal/src/eth/mod.rs @@ -1,27 +1,24 @@ -use arbitrary_int::{Number, u2, u3, u6, u13, u30}; -use zynq7000::{ - eth::{ - GEM_0_BASE_ADDR, GEM_1_BASE_ADDR, InterruptControl, MmioEthernet, NetworkControl, RxStatus, - SpeedMode, TxStatus, - }, - slcr::reset::EthernetReset, -}; +use arbitrary_int::u3; +pub use zynq7000::eth::MdcClkDiv; +use zynq7000::eth::{GEM_0_BASE_ADDR, GEM_1_BASE_ADDR, MmioEthernet, SpeedMode}; + +pub use ll::{ClkConfig, EthernetLowLevel}; + +pub mod ll; +pub mod mdio; +pub mod rx_descr; +pub mod tx_descr; #[cfg(not(feature = "7z010-7z007s-clg225"))] use crate::gpio::mio::{ Mio16, Mio17, Mio18, Mio19, Mio20, Mio21, Mio22, Mio23, Mio24, Mio25, Mio26, Mio27, }; -use crate::{ - enable_amba_peripheral_clock, - gpio::{ - IoPeriphPin, - mio::{ - Mio28, Mio29, Mio30, Mio31, Mio32, Mio33, Mio34, Mio35, Mio36, Mio37, Mio38, Mio39, - Mio52, Mio53, MioPinMarker, MuxConf, Pin, - }, +use crate::gpio::{ + IoPeriphPin, + mio::{ + Mio28, Mio29, Mio30, Mio31, Mio32, Mio33, Mio34, Mio35, Mio36, Mio37, Mio38, Mio39, Mio52, + Mio53, MioPinMarker, MuxConf, Pin, }, - slcr::Slcr, - time::Hertz, }; pub const MUX_CONF_PHY: MuxConf = MuxConf::new_with_l0(); @@ -33,6 +30,22 @@ pub enum EthernetId { 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; @@ -185,196 +198,9 @@ impl RxData3 for Pin { const ETH_ID: EthernetId = EthernetId::Eth1; } -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 { - 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 configure_mdc_clk_div() { - // 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 - } -} - pub struct Ethernet { - ll: EthernetLowLevel, + ll: ll::EthernetLowLevel, + mdio: mdio::Mdio, } impl Ethernet { @@ -395,8 +221,9 @@ impl Ethernet { MdClkPin: MdClk, MdIoPin: MdIo, >( - mut ll: EthernetLowLevel, - clk_config: ClkConfig, + mut ll: ll::EthernetLowLevel, + clk_config: ll::ClkConfig, + mdc_clk_div: MdcClkDiv, tx_clk: TxClkPin, tx_ctrl: TxCtrlPin, tx_data: (TxData0Pin, TxData1Pin, TxData2Pin, TxData3Pin), @@ -522,13 +349,17 @@ impl Ethernet { }); } ll.configure_clock(clk_config); - Ethernet { ll } + let mut mdio = mdio::Mdio::new(&ll, true); + mdio.configure_clock_div(mdc_clk_div); + Ethernet { ll, mdio } } - pub fn new(mut ll: EthernetLowLevel, clk_config: ClkConfig) -> Self { + pub fn new(mut ll: EthernetLowLevel, clk_config: ClkConfig, mdc_clk_div: MdcClkDiv) -> Self { Self::common_init(&mut ll); ll.configure_clock(clk_config); - Ethernet { ll } + let mut mdio = mdio::Mdio::new(&ll, true); + mdio.configure_clock_div(mdc_clk_div); + Ethernet { ll, mdio } } fn common_init(ll: &mut EthernetLowLevel) { @@ -558,73 +389,10 @@ impl Ethernet { &mut self.ll.regs } } -/// 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. -pub struct RxBufDescr { - /// The first word of the descriptor. - pub word0: RxBufDescrWord0, - /// The second word of the descriptor. - pub word1: RxBufDescrWord1, -} #[bitbybit::bitenum(u1, exhaustive = true)] #[derive(Debug, PartialEq, Eq)] -pub enum Ownership { +pub(crate) enum Ownership { Hardware = 0, Software = 1, } - -#[bitbybit::bitfield(u32)] -#[derive(Debug, PartialEq, Eq)] -pub struct RxBufDescrWord0 { - /// The full reception address with the last two bits cleared. - #[bits(2..=31, rw)] - addr: u30, - #[bit(1, rw)] - wrap: bool, - #[bit(1, rw)] - ownership: Ownership, -} - -#[bitbybit::bitfield(u32)] -#[derive(Debug, PartialEq, Eq)] -pub struct RxBufDescrWord1 { - #[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, - #[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, -} diff --git a/zynq7000-hal/src/eth/rx_descr.rs b/zynq7000-hal/src/eth/rx_descr.rs new file mode 100644 index 0000000..793660e --- /dev/null +++ b/zynq7000-hal/src/eth/rx_descr.rs @@ -0,0 +1,85 @@ +//! RX buffer descriptor module. +pub use super::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, +} + +#[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, + #[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)); + } +} diff --git a/zynq7000-hal/src/eth/tx_descr.rs b/zynq7000-hal/src/eth/tx_descr.rs new file mode 100644 index 0000000..e02b312 --- /dev/null +++ b/zynq7000-hal/src/eth/tx_descr.rs @@ -0,0 +1,53 @@ +use arbitrary_int::u14; + +pub use super::Ownership; + +/// 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 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)] +#[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, +} diff --git a/zynq7000/src/eth.rs b/zynq7000/src/eth.rs index afe29cf..e6ce9a7 100644 --- a/zynq7000/src/eth.rs +++ b/zynq7000/src/eth.rs @@ -386,7 +386,7 @@ pub enum PhyOperation { Write = 0b01, } -#[bitbybit::bitfield(u32)] +#[bitbybit::bitfield(u32, default = 0x0)] #[derive(Debug)] pub struct PhyMaintenance { /// Must be 1 for Clause 22 operations. @@ -399,9 +399,9 @@ pub struct PhyMaintenance { #[bits(18..=22, rw)] reg_addr: u5, #[bits(16..=17, rw)] - must_be_10: u2, + must_be_0b10: u2, #[bits(0..=15, rw)] - data_mask: u16, + data: u16, } #[bitbybit::bitfield(u32)]