start adding smoltcp/ethernet support

This commit is contained in:
Robin Müller 2025-05-27 12:02:57 +02:00
parent 61ffe06343
commit b8fdf1008b
Signed by: muellerr
GPG Key ID: A649FB78196E3849
8 changed files with 660 additions and 4 deletions

View File

@ -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;

View File

@ -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 }

View File

@ -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,
}

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 {

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.

View File

@ -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;

580
zynq7000/src/eth.rs Normal file
View File

@ -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<Self> {
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<PhyOperation>,
#[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::<Ethernet>(), 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) }
}
}

View File

@ -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;