diff --git a/examples/embassy/src/bin/dht22-open-drain-pins.rs b/examples/embassy/src/bin/dht22-open-drain-pins.rs index e81c99c..a366783 100644 --- a/examples/embassy/src/bin/dht22-open-drain-pins.rs +++ b/examples/embassy/src/bin/dht22-open-drain-pins.rs @@ -10,13 +10,13 @@ use embedded_hal::{delay::DelayNs, digital::StatefulOutputPin}; use embedded_io::Write; use log::{error, info, warn}; use zynq7000_hal::{ + BootMode, clocks::Clocks, gic::{GicConfigurator, GicInterruptHelper, Interrupt}, - gpio::{mio, Flex, Output, PinState}, + gpio::{Flex, Output, PinState, mio}, gtc::Gtc, time::Hertz, uart::{ClkConfigRaw, Uart, UartConfig}, - BootMode, }; use zynq7000::PsPeripherals; diff --git a/zynq7000-hal/Cargo.toml b/zynq7000-hal/Cargo.toml index fda74ba..61f3017 100644 --- a/zynq7000-hal/Cargo.toml +++ b/zynq7000-hal/Cargo.toml @@ -14,6 +14,7 @@ categories = ["embedded", "no-std", "hardware-support"] cortex-ar = { git = "https://github.com/rust-embedded/cortex-ar", branch = "main" } zynq7000 = { path = "../zynq7000" } +bitbybit = "1.3" arbitrary-int = "1.3" thiserror = { version = "2", default-features = false } num_enum = { version = "0.7", default-features = false } diff --git a/zynq7000-hal/src/eth/mod.rs b/zynq7000-hal/src/eth/mod.rs new file mode 100644 index 0000000..2be43a6 --- /dev/null +++ b/zynq7000-hal/src/eth/mod.rs @@ -0,0 +1,72 @@ +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. +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 { + 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/gpio/ll.rs b/zynq7000-hal/src/gpio/ll.rs index a05681d..5a85275 100644 --- a/zynq7000-hal/src/gpio/ll.rs +++ b/zynq7000-hal/src/gpio/ll.rs @@ -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 { diff --git a/zynq7000-hal/src/gpio/mod.rs b/zynq7000-hal/src/gpio/mod.rs index 60cc5d1..fca11cc 100644 --- a/zynq7000-hal/src/gpio/mod.rs +++ b/zynq7000-hal/src/gpio/mod.rs @@ -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. diff --git a/zynq7000-hal/src/lib.rs b/zynq7000-hal/src/lib.rs index 9ef1931..a2ebcc9 100644 --- a/zynq7000-hal/src/lib.rs +++ b/zynq7000-hal/src/lib.rs @@ -13,6 +13,7 @@ use slcr::Slcr; use zynq7000::slcr::LevelShifterReg; pub mod clocks; +pub mod eth; pub mod gic; pub mod gpio; pub mod gtc; diff --git a/zynq7000/src/eth.rs b/zynq7000/src/eth.rs new file mode 100644 index 0000000..1d4c3f1 --- /dev/null +++ b/zynq7000/src/eth.rs @@ -0,0 +1,580 @@ +use arbitrary_int::{u2, u5}; + +pub const GEM_0_BASE_ADDR: usize = 0xE000_B000; +pub const GEM_1_BASE_ADDR: usize = 0xE000_C000; + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct NetworkControl { + #[bit(18, w)] + flush_next_rx_dpram_pkt: bool, + #[bit(17, w)] + tx_pfc_pri_pause_frame: bool, + #[bit(16, w)] + enable_pfc_pri_pause_rx: bool, + #[bit(12, w)] + zero_pause_tx: bool, + #[bit(11, w)] + pause_tx: bool, + #[bit(10, w)] + stop_tx: bool, + #[bit(9, w)] + start_tx: bool, + #[bit(8, rw)] + back_pressure: bool, + #[bit(7, rw)] + statistics_write_enable: bool, + #[bit(6, w)] + increment_statistics: bool, + #[bit(5, w)] + clear_statistics: bool, + #[bit(4, rw)] + management_port_enable: bool, + #[bit(3, rw)] + tx_enable: bool, + #[bit(2, rw)] + rx_enable: bool, + #[bit(1, rw)] + loopback_local: bool, +} + +#[bitbybit::bitenum(u1, exhaustive = true)] +#[derive(Debug, PartialEq, Eq)] +pub enum SpeedMode { + Low10Mbps = 0, + High100Mbps = 1, +} + +#[bitbybit::bitenum(u1, exhaustive = true)] +#[derive(Debug, PartialEq, Eq)] +pub enum PcsSelect { + GmiiMii = 0, + Tbi = 1, +} + +#[bitbybit::bitenum(u3, exhaustive = true)] +#[derive(Debug, PartialEq, Eq)] +pub enum MdcClkDiv { + Div8 = 0, + Div16 = 1, + Div32 = 2, + Div48 = 3, + Div64 = 4, + Div96 = 5, + Div128 = 6, + Div224 = 7, +} + +impl MdcClkDiv { + pub fn divisor(&self) -> usize { + match self { + MdcClkDiv::Div8 => 8, + MdcClkDiv::Div16 => 16, + MdcClkDiv::Div32 => 32, + MdcClkDiv::Div48 => 48, + MdcClkDiv::Div64 => 64, + MdcClkDiv::Div96 => 96, + MdcClkDiv::Div128 => 128, + MdcClkDiv::Div224 => 224, + } + } +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct NetworkConfig { + #[bit(30, rw)] + ignore_ipg_rx_error: bool, + #[bit(29, rw)] + allow_bad_preamble: bool, + #[bit(28, rw)] + ipg_stretch_enable: bool, + #[bit(27, rw)] + sgmii_enable: bool, + #[bit(26, rw)] + ignore_rx_fcs: bool, + #[bit(25, rw)] + half_duplex_rx_enable: bool, + #[bit(24, rw)] + rx_checksum_enable: bool, + #[bit(23, rw)] + disable_copy_pause_frames: bool, + /// Zynq defines this as 0b00 for 32-bit AMBA AHB data bus width. + #[bits(21..=22, r)] + dbus_width: u2, + #[bits(18..=20, rw)] + mdc_clk_div: MdcClkDiv, + #[bit(17, rw)] + fcs_removal: bool, + #[bit(16, rw)] + length_field_error_discard: bool, + #[bits(14..=15, rw)] + rx_buf_offset: u2, + #[bit(13, rw)] + pause_enable: bool, + #[bit(12, rw)] + retry_test_enable: bool, + #[bit(11, rw)] + pcs_select: PcsSelect, + #[bit(10, rw)] + gigabit_enable: bool, + #[bit(9, rw)] + ext_addr_match_enable: bool, + #[bit(8, rw)] + rx_enable_1536: bool, + #[bit(7, rw)] + unicast_hash_enable: bool, + #[bit(6, rw)] + multicast_hash_enable: bool, + #[bit(5, rw)] + no_broadcast: bool, + #[bit(4, rw)] + copy_all_frames: bool, + #[bit(2, rw)] + discard_non_vlan: bool, + #[bit(1, rw)] + full_duplex: bool, + #[bit(0, rw)] + speed_mode: SpeedMode, +} + +/// PHY management status information. +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct NetworkStatus { + #[bit(6, r)] + pfc_pri_pause_neg: bool, + #[bit(5, r)] + pcs_autoneg_pause_tx_res: bool, + #[bit(4, r)] + pcs_autoneg_pause_rx_res: bool, + #[bit(3, r)] + pcs_autoneg_dup_res: bool, + #[bit(2, r)] + phy_mgmt_idle: bool, + #[bit(1, r)] + mdio_in: bool, + #[bit(0, r)] + pcs_link_state: bool, +} + +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] +pub enum BurstLength { + Single, + #[default] + Incr4, + Incr8, + Incr16, +} + +impl BurstLength { + pub const fn reg_value(&self) -> u5 { + u5::new(match self { + BurstLength::Single => 0b1, + BurstLength::Incr4 => 0b100, + BurstLength::Incr8 => 0b1000, + BurstLength::Incr16 => 0b10000, + }) + } +} + +#[bitbybit::bitenum(u1, exhaustive = true)] +#[derive(Debug, PartialEq, Eq)] +pub enum AhbEndianess { + Little = 0, + Big = 1, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct DmaRxBufSize(u8); + +impl DmaRxBufSize { + pub const fn new_with_raw_value(size: u8) -> Self { + Self(size) + } + + pub const fn new(size: u8) -> Option { + if size == 0 { + return None; + } + Some(Self(size)) + } + + pub const fn raw_value(&self) -> u8 { + self.0 + } + + pub const fn size_in_bytes(&self) -> usize { + self.0 as usize * 64 + } + + pub const fn reg_value(&self) -> u8 { + self.0 + } +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct DmaConfig { + #[bit(24, rw)] + discard_when_ahb_full: bool, + /// DMA receive buffer size in AHB system memory. + #[bits(16..=23, rw)] + dma_rx_ahb_buf_size_sel: DmaRxBufSize, + #[bit(11, rw)] + chksum_offload_enable: bool, + /// Select size for packet buffer SRAM. Should be set to 1 to use the full configurable address + /// space of 4 kB for the packet buffer. + #[bit(10, rw)] + tx_packet_buf_size_sel: bool, + /// Select size for packet buffer SRAM. Should be set to 0b11 to use the full configurable + /// address space of 8 kB for the packet buffer. + #[bits(8..=9, rw)] + rx_packet_buf_size_sel: u2, + /// Default value is 0x1 (big endian) + #[bit(7, rw)] + endian_swap_packet_data: AhbEndianess, + #[bit(6, rw)] + endian_swap_mgmt_descriptor: AhbEndianess, + #[bits(0..=4, rw)] + burst_length: u5, +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct TxStatus { + #[bit(8, rw)] + hresp_not_ok: bool, + #[bit(7, rw)] + late_collision: bool, + #[bit(6, rw)] + tx_underrun: bool, + #[bit(5, rw)] + tx_complete: bool, + #[bit(4, rw)] + tx_frame_corruption_ahb_error: bool, + #[bit(3, r)] + tx_go: bool, + #[bit(2, rw)] + retry_limit_reached: bool, + #[bit(1, rw)] + collision: bool, + #[bit(0, rw)] + read_when_used: bool, +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct RxStatus { + #[bit(3, rw)] + hresp_not_ok: bool, + #[bit(2, rw)] + rx_overrun: bool, + #[bit(1, rw)] + frame_received: bool, + #[bit(0, rw)] + buf_not_available: bool, +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct InterruptStatus { + #[bit(26, rw)] + tsu_sec_incr: bool, + /// Marked N/A in datasheet. + #[bit(17, rw)] + partner_pg_rx: bool, + /// Marked N/A in datasheet. + #[bit(16, rw)] + auto_negotiation_complete: bool, + #[bit(15, rw)] + external_interrupt: bool, + #[bit(14, rw)] + pause_transmitted: bool, + #[bit(13, rw)] + pause_time_zero: bool, + #[bit(12, rw)] + pause_with_non_zero_quantum: bool, + #[bit(11, rw)] + hresp_not_ok: bool, + #[bit(10, rw)] + rx_overrun: bool, + /// Marked N/A in datasheet. + #[bit(9, rw)] + link_changed: bool, + #[bit(7, r)] + tx_complete: bool, + /// Cleared on read. + #[bit(6, r)] + tx_frame_corruption_ahb_error: bool, + #[bit(5, rw)] + retry_limit_reached: bool, + #[bit(3, rw)] + tx_descr_read_when_used: bool, + #[bit(2, rw)] + rx_descr_read_when_used: bool, + #[bit(1, rw)] + frame_received: bool, + #[bit(0, rw)] + mgmt_frame_sent: bool, +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct InterruptControl { + #[bit(26, w)] + tsu_sec_incr: bool, + /// Marked N/A in datasheet. + #[bit(17, w)] + partner_pg_rx: bool, + /// Marked N/A in datasheet. + #[bit(16, w)] + auto_negotiation_complete: bool, + #[bit(15, w)] + external_interrupt: bool, + #[bit(14, w)] + pause_transmitted: bool, + #[bit(13, w)] + pause_time_zero: bool, + #[bit(12, w)] + pause_with_non_zero_quantum: bool, + #[bit(11, w)] + hresp_not_ok: bool, + #[bit(10, w)] + rx_overrun: bool, + /// Marked N/A in datasheet. + #[bit(9, w)] + link_changed: bool, + #[bit(7, w)] + tx_complete: bool, + /// Cleared on read. + #[bit(6, w)] + tx_frame_corruption_ahb_error: bool, + #[bit(5, w)] + retry_limit_reached: bool, + #[bit(3, w)] + tx_descr_read_when_used: bool, + #[bit(2, w)] + rx_descr_read_when_used: bool, + #[bit(1, w)] + frame_received: bool, + #[bit(0, w)] + mgmt_frame_sent: bool, +} + +#[bitbybit::bitenum(u2, exhaustive = false)] +pub enum PhyOperation { + Read = 0b10, + Write = 0b01, +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct PhyMaintenance { + /// Must be 1 for Clause 22 operations. + #[bit(30, rw)] + clause_22: bool, + #[bits(28..=29, rw)] + op: Option, + #[bits(23..=27, rw)] + phy_addr: u5, + #[bits(18..=22, rw)] + reg_addr: u5, + #[bits(16..=17, rw)] + must_be_10: u2, + #[bits(0..=15, rw)] + data_mask: u16, +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct PauseQuantum { + #[bits(0..=15, rw)] + value: u16, +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct MatchRegister { + #[bit(31, rw)] + copy_enable: bool, + #[bits(0..=15, rw)] + type_id: u16, +} + +/// Gigabit Ethernet Controller (GEM) registers for Zynq-7000 +#[derive(derive_mmio::Mmio)] +#[repr(C)] +pub struct Ethernet { + net_ctrl: NetworkControl, + net_cfg: NetworkConfig, + #[mmio(PureRead)] + net_status: NetworkStatus, + _reserved0: u32, + dma_cfg: DmaConfig, + tx_status: TxStatus, + rx_buf_queue_base_addr: u32, + tx_buf_queue_base_addr: u32, + rx_status: RxStatus, + interrupt_status: InterruptStatus, + interrupt_enable: InterruptControl, + interrupt_disable: InterruptControl, + interrupt_mask: InterruptStatus, + phy_maintenance: PhyMaintenance, + #[mmio(PureRead)] + rx_pause_quantum: PauseQuantum, + tx_pause_quantum: PauseQuantum, + _reserved1: [u32; 0x10], + hash_low: u32, + hash_high: u32, + addr1_low: u32, + addr1_high: u32, + addr2_low: u32, + addr2_high: u32, + addr3_low: u32, + addr3_high: u32, + addr4_low: u32, + addr4_high: u32, + match_reg: [MatchRegister; 4], + wake_on_lan: u32, + ipg_stretch: u32, + stacked_vlan: u32, + tx_pfc: u32, + addr1_mask_low: u32, + addr1_mask_high: u32, + _reserved2: [u32; 0x0B], + /// Should be 0x20118. + #[mmio(PureRead)] + module_id: u32, + #[mmio(inner)] + statistics: Statistics, + _reserved3: [u32; 0x34], + #[mmio(PureRead)] + design_cfg_2: u32, + #[mmio(PureRead)] + design_cfg_3: u32, + #[mmio(PureRead)] + design_cfg_4: u32, + #[mmio(PureRead)] + design_cfg_5: u32, +} + +static_assertions::const_assert_eq!(core::mem::size_of::(), 0x294); + +/// GEM statistics registers +#[derive(derive_mmio::Mmio)] +#[repr(C)] +pub struct Statistics { + #[mmio(PureRead)] + tx_octets_low: u32, + #[mmio(PureRead)] + tx_octets_high: u32, + #[mmio(PureRead)] + tx_count: u32, + #[mmio(PureRead)] + tx_broadcast: u32, + #[mmio(PureRead)] + tx_multicast: u32, + #[mmio(PureRead)] + tx_pause: u32, + #[mmio(PureRead)] + tx_64_bits: u32, + #[mmio(PureRead)] + tx_65_to_127_bits: u32, + #[mmio(PureRead)] + tx_128_to_255_bits: u32, + #[mmio(PureRead)] + tx_256_to_511_bits: u32, + #[mmio(PureRead)] + tx_512_to_1023_bits: u32, + #[mmio(PureRead)] + tx_1024_to_1518_bits: u32, + _reserved0: u32, + #[mmio(PureRead)] + tx_underruns: u32, + #[mmio(PureRead)] + single_collision_frames: u32, + #[mmio(PureRead)] + multi_collision_frames: u32, + #[mmio(PureRead)] + excessive_collisions: u32, + #[mmio(PureRead)] + late_collisions: u32, + #[mmio(PureRead)] + deferred_tx: u32, + #[mmio(PureRead)] + carrier_sense_errors: u32, + #[mmio(PureRead)] + rx_octets_low: u32, + #[mmio(PureRead)] + rx_octets_high: u32, + #[mmio(PureRead)] + rx_count: u32, + #[mmio(PureRead)] + rx_broadcast: u32, + #[mmio(PureRead)] + rx_multicast: u32, + #[mmio(PureRead)] + rx_pause: u32, + #[mmio(PureRead)] + rx_64_bits: u32, + #[mmio(PureRead)] + rx_65_to_127_bits: u32, + #[mmio(PureRead)] + rx_128_to_255_bits: u32, + #[mmio(PureRead)] + rx_256_to_511_bits: u32, + #[mmio(PureRead)] + rx_512_to_1023_bits: u32, + #[mmio(PureRead)] + rx_1024_to_1518_bits: u32, + _reserved1: u32, + #[mmio(PureRead)] + rx_undersize: u32, + #[mmio(PureRead)] + rx_oversize: u32, + #[mmio(PureRead)] + rx_jabber: u32, + #[mmio(PureRead)] + rx_frame_check_sequence_errors: u32, + #[mmio(PureRead)] + rx_length_field_errors: u32, + #[mmio(PureRead)] + rx_symbol_errors: u32, + #[mmio(PureRead)] + rx_alignment_errors: u32, + #[mmio(PureRead)] + rx_resource_errors: u32, + #[mmio(PureRead)] + rx_overrun_errors: u32, + #[mmio(PureRead)] + rx_ip_header_checksum_errors: u32, + #[mmio(PureRead)] + rx_tcp_checksum_errors: u32, + #[mmio(PureRead)] + rx_udp_checksum_errors: u32, +} + +impl Ethernet { + /// Create a new Gigabit Ethernet MMIO instance for GEM 0 at address [GEM_0_BASE_ADDR]. + /// + /// # Safety + /// + /// This API can be used to potentially create a driver to the same peripheral structure + /// from multiple threads. The user must ensure that concurrent accesses are safe and do not + /// interfere with each other. + pub const unsafe fn new_mmio_fixed_0() -> MmioEthernet<'static> { + unsafe { Self::new_mmio_at(GEM_0_BASE_ADDR) } + } + + /// Create a new Gigabit Ethernet MMIO instance for GEM 1 at address [GEM_1_BASE_ADDR]. + /// + /// # Safety + /// + /// This API can be used to potentially create a driver to the same peripheral structure + /// from multiple threads. The user must ensure that concurrent accesses are safe and do not + /// interfere with each other. + pub const unsafe fn new_mmio_fixed_1() -> MmioEthernet<'static> { + unsafe { Self::new_mmio_at(GEM_1_BASE_ADDR) } + } +} diff --git a/zynq7000/src/lib.rs b/zynq7000/src/lib.rs index 5ad2a15..d9a2538 100644 --- a/zynq7000/src/lib.rs +++ b/zynq7000/src/lib.rs @@ -15,6 +15,7 @@ extern crate std; pub const MPCORE_BASE_ADDR: usize = 0xF8F0_0000; +pub mod eth; pub mod gic; pub mod gpio; pub mod gtc;