Compare commits
9 Commits
main
...
add-smoltc
Author | SHA1 | Date | |
---|---|---|---|
adf5f853c0 | |||
6697b24f91 | |||
7229ff4150 | |||
b8bb7e23c7 | |||
d0d0d48780 | |||
42335ab8c1 | |||
47b150369d | |||
8c9e34f96a | |||
b8fdf1008b |
@ -7,6 +7,6 @@ members = [
|
||||
"zynq7000-embassy",
|
||||
"examples/simple",
|
||||
"examples/embassy",
|
||||
"examples/zedboard",
|
||||
"examples/zedboard", "zynq-mmu",
|
||||
]
|
||||
exclude = ["experiments"]
|
||||
|
@ -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;
|
||||
|
19
memory.x
19
memory.x
@ -1,8 +1,23 @@
|
||||
|
||||
MEMORY
|
||||
{
|
||||
/* Zedboard: 512 MB DDR3. Only use 256 MB for now, should be plenty for a bare-metal app. */
|
||||
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 256M
|
||||
/* Zedboard: 512 MB DDR3. Only use 63 MB for now, should be plenty for a bare-metal app.
|
||||
Leave 1 MB of memory which will be configured as uncached device memory by the MPU. This is
|
||||
recommended for something like DMA descriptors. */
|
||||
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 63M
|
||||
UNCACHED(rx): ORIGIN = 0x4000000, LENGTH = 1M
|
||||
}
|
||||
|
||||
REGION_ALIAS("DATA", CODE);
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* Uncached memory */
|
||||
uncached (NOLOAD) : ALIGN(4) {
|
||||
. = ALIGN(4);
|
||||
_sbss_uncached = .;
|
||||
*(.bss.uncached .bss.uncached.*);
|
||||
. = ALIGN(4);
|
||||
_ebss_uncached = .;
|
||||
} > UNCACHED
|
||||
}
|
||||
|
9
zynq-mmu/Cargo.toml
Normal file
9
zynq-mmu/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "zynq-mmu"
|
||||
description = "Zynq MMU structures"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
cortex-ar = { version = "0.2", path = "../../../Rust/cortex-ar/cortex-ar" }
|
||||
thiserror = { version = "2", default-features = false }
|
48
zynq-mmu/src/lib.rs
Normal file
48
zynq-mmu/src/lib.rs
Normal file
@ -0,0 +1,48 @@
|
||||
//! The MMU structures live inside a dedicated shared crate so it can be used by both the Zynq
|
||||
//! runtime crate and teh HAL crate.
|
||||
#![no_std]
|
||||
|
||||
use cortex_ar::{
|
||||
asm::{dsb, isb},
|
||||
mmu::SectionAttributes,
|
||||
register::{BpIAll, TlbIAll},
|
||||
};
|
||||
|
||||
pub const NUM_L1_PAGE_TABLE_ENTRIES: usize = 4096;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||
#[error("address is not aligned to 1MB boundary")]
|
||||
pub struct AddrNotAlignedToOneMb;
|
||||
|
||||
#[repr(C, align(16384))]
|
||||
pub struct L1Table(pub [u32; NUM_L1_PAGE_TABLE_ENTRIES]);
|
||||
|
||||
impl L1Table {
|
||||
#[inline(always)]
|
||||
pub const fn as_ptr(&self) -> *const u32 {
|
||||
self.0.as_ptr()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn as_mut_ptr(&mut self) -> *mut u32 {
|
||||
self.0.as_mut_ptr()
|
||||
}
|
||||
|
||||
pub fn update_page_attr(
|
||||
&mut self,
|
||||
addr: u32,
|
||||
section_attrs: SectionAttributes,
|
||||
) -> Result<(), AddrNotAlignedToOneMb> {
|
||||
if addr & 0x000F_FFFF != 0 {
|
||||
return Err(AddrNotAlignedToOneMb);
|
||||
}
|
||||
let index = addr as usize / 0x10_0000;
|
||||
self.0[index] = (self.0[index] & 0xFFF0_0000) | section_attrs.as_raw_bits();
|
||||
// TODO: DCache flush.
|
||||
TlbIAll::write();
|
||||
BpIAll::write();
|
||||
dsb();
|
||||
isb();
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -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 }
|
||||
|
200
zynq7000-hal/src/eth/ll.rs
Normal file
200
zynq7000-hal/src/eth/ll.rs
Normal file
@ -0,0 +1,200 @@
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
}
|
65
zynq7000-hal/src/eth/mdio.rs
Normal file
65
zynq7000-hal/src/eth/mdio.rs
Normal 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
444
zynq7000-hal/src/eth/mod.rs
Normal file
@ -0,0 +1,444 @@
|
||||
use arbitrary_int::{u2, u3};
|
||||
pub use zynq7000::eth::MdcClkDiv;
|
||||
use zynq7000::eth::{
|
||||
BurstLength, DmaRxBufSize, MmioEthernet, NetworkConfig, SpeedMode, GEM_0_BASE_ADDR,
|
||||
GEM_1_BASE_ADDR,
|
||||
};
|
||||
|
||||
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::{
|
||||
mio::{
|
||||
Mio28, Mio29, Mio30, Mio31, Mio32, Mio33, Mio34, Mio35, Mio36, Mio37, Mio38, Mio39, Mio52,
|
||||
Mio53, MioPinMarker, MuxConf, Pin,
|
||||
},
|
||||
IoPeriphPin,
|
||||
};
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
85
zynq7000-hal/src/eth/rx_descr.rs
Normal file
85
zynq7000-hal/src/eth/rx_descr.rs
Normal file
@ -0,0 +1,85 @@
|
||||
//! 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,
|
||||
}
|
||||
|
||||
#[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));
|
||||
}
|
||||
}
|
78
zynq7000-hal/src/eth/tx_descr.rs
Normal file
78
zynq7000-hal/src/eth/tx_descr.rs
Normal file
@ -0,0 +1,78 @@
|
||||
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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -13,6 +13,8 @@ use slcr::Slcr;
|
||||
use zynq7000::slcr::LevelShifterReg;
|
||||
|
||||
pub mod clocks;
|
||||
pub mod mmu;
|
||||
pub mod eth;
|
||||
pub mod gic;
|
||||
pub mod gpio;
|
||||
pub mod gtc;
|
||||
|
16
zynq7000-hal/src/mmu.rs
Normal file
16
zynq7000-hal/src/mmu.rs
Normal 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.
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -11,8 +11,9 @@ keywords = ["no-std", "arm", "cortex-a", "amd", "zynq7000"]
|
||||
categories = ["embedded", "no-std", "hardware-support"]
|
||||
|
||||
[dependencies]
|
||||
cortex-a-rt = { git = "https://github.com/rust-embedded/cortex-ar", branch = "main", optional = true, features = ["vfp-dp"] }
|
||||
cortex-ar = { git = "https://github.com/rust-embedded/cortex-ar", branch = "main" }
|
||||
cortex-a-rt = { version = "0.1", optional = true, features = ["vfp-dp"] }
|
||||
cortex-ar = "0.2"
|
||||
zynq-mmu = { path = "../zynq-mmu", version = "0.1.0" }
|
||||
|
||||
[features]
|
||||
default = ["rt"]
|
||||
|
@ -1,8 +1,14 @@
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::process::Command;
|
||||
use zynq7000_rt::mmu::ONE_MB;
|
||||
pub use zynq7000_rt::mmu::segments::*;
|
||||
use zynq7000_rt::mmu::ONE_MB;
|
||||
|
||||
macro_rules! write_l1_section {
|
||||
($writer:expr, $offset:expr, $attr:expr) => {
|
||||
writeln!($writer, "L1Section::new({:#010x}, {}).raw_value(),", $offset, $attr).unwrap();
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let file_path = "src/mmu_table.rs";
|
||||
@ -35,6 +41,7 @@ fn main() {
|
||||
+ OCM_MAPPED_HIGH,
|
||||
4096
|
||||
);
|
||||
|
||||
let mut buf_writer = std::io::BufWriter::new(file);
|
||||
writeln!(
|
||||
buf_writer,
|
||||
@ -63,44 +70,24 @@ fn main() {
|
||||
"// First DDR segment, OCM memory (0x0000_0000 - 0x0010_0000)"
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(
|
||||
buf_writer,
|
||||
"L1Section::new({}, {}).raw_value(),",
|
||||
offset, attr_ddr
|
||||
)
|
||||
.unwrap();
|
||||
write_l1_section!(buf_writer, offset, attr_ddr);
|
||||
|
||||
offset += ONE_MB;
|
||||
writeln!(buf_writer, "// DDR memory (0x00100000 - 0x4000_0000)").unwrap();
|
||||
for _ in 0..DDR_FULL_ACCESSIBLE {
|
||||
writeln!(
|
||||
buf_writer,
|
||||
"L1Section::new({}, {}).raw_value(),",
|
||||
offset, attr_ddr
|
||||
)
|
||||
.unwrap();
|
||||
write_l1_section!(buf_writer, offset, attr_ddr);
|
||||
offset += ONE_MB;
|
||||
}
|
||||
|
||||
writeln!(buf_writer, "// FPGA slave 0 (0x4000_0000 - 0x8000_0000)").unwrap();
|
||||
for _ in 0..FPGA_SLAVE {
|
||||
writeln!(
|
||||
buf_writer,
|
||||
"L1Section::new({}, {}).raw_value(),",
|
||||
offset, attr_fpga_slaves
|
||||
)
|
||||
.unwrap();
|
||||
write_l1_section!(buf_writer, offset, attr_fpga_slaves);
|
||||
offset += ONE_MB;
|
||||
}
|
||||
|
||||
writeln!(buf_writer, "// FPGA slave 1 (0x8000_0000 - 0xC000_0000)").unwrap();
|
||||
for _ in 0..FPGA_SLAVE {
|
||||
writeln!(
|
||||
buf_writer,
|
||||
"L1Section::new({}, {}).raw_value(),",
|
||||
offset, attr_fpga_slaves
|
||||
)
|
||||
.unwrap();
|
||||
write_l1_section!(buf_writer, offset, attr_fpga_slaves);
|
||||
offset += ONE_MB;
|
||||
}
|
||||
|
||||
@ -110,12 +97,7 @@ fn main() {
|
||||
)
|
||||
.unwrap();
|
||||
for _ in 0..UNASSIGNED_0 {
|
||||
writeln!(
|
||||
buf_writer,
|
||||
"L1Section::new({}, {}).raw_value(),",
|
||||
offset, attr_unassigned
|
||||
)
|
||||
.unwrap();
|
||||
write_l1_section!(buf_writer, offset, attr_unassigned);
|
||||
offset += ONE_MB;
|
||||
}
|
||||
|
||||
@ -125,12 +107,7 @@ fn main() {
|
||||
)
|
||||
.unwrap();
|
||||
for _ in 0..IO_PERIPHS {
|
||||
writeln!(
|
||||
buf_writer,
|
||||
"L1Section::new({}, {}).raw_value(),",
|
||||
offset, attr_shared_dev
|
||||
)
|
||||
.unwrap();
|
||||
write_l1_section!(buf_writer, offset, attr_shared_dev);
|
||||
offset += ONE_MB;
|
||||
}
|
||||
|
||||
@ -140,45 +117,25 @@ fn main() {
|
||||
)
|
||||
.unwrap();
|
||||
for _ in 0..UNASSIGNED_1 {
|
||||
writeln!(
|
||||
buf_writer,
|
||||
"L1Section::new({}, {}).raw_value(),",
|
||||
offset, attr_unassigned
|
||||
)
|
||||
.unwrap();
|
||||
write_l1_section!(buf_writer, offset, attr_unassigned);
|
||||
offset += ONE_MB;
|
||||
}
|
||||
|
||||
writeln!(buf_writer, "// NAND (0xE100_0000 - 0xE200_0000)").unwrap();
|
||||
for _ in 0..NAND {
|
||||
writeln!(
|
||||
buf_writer,
|
||||
"L1Section::new({}, {}).raw_value(),",
|
||||
offset, attr_shared_dev
|
||||
)
|
||||
.unwrap();
|
||||
write_l1_section!(buf_writer, offset, attr_shared_dev);
|
||||
offset += ONE_MB;
|
||||
}
|
||||
|
||||
writeln!(buf_writer, "// NOR (0xE200_0000 - 0xE400_0000)").unwrap();
|
||||
for _ in 0..NOR {
|
||||
writeln!(
|
||||
buf_writer,
|
||||
"L1Section::new({}, {}).raw_value(),",
|
||||
offset, attr_shared_dev
|
||||
)
|
||||
.unwrap();
|
||||
write_l1_section!(buf_writer, offset, attr_shared_dev);
|
||||
offset += ONE_MB;
|
||||
}
|
||||
|
||||
writeln!(buf_writer, "// SRAM (0xE400_0000 - 0xE600_0000)").unwrap();
|
||||
for _ in 0..SRAM {
|
||||
writeln!(
|
||||
buf_writer,
|
||||
"L1Section::new({}, {}).raw_value(),",
|
||||
offset, attr_sram
|
||||
)
|
||||
.unwrap();
|
||||
write_l1_section!(buf_writer, offset, attr_sram);
|
||||
offset += ONE_MB;
|
||||
}
|
||||
|
||||
@ -188,12 +145,7 @@ fn main() {
|
||||
)
|
||||
.unwrap();
|
||||
for _ in 0..SEGMENTS_UNASSIGNED_2 {
|
||||
writeln!(
|
||||
buf_writer,
|
||||
"L1Section::new({}, {}).raw_value(),",
|
||||
offset, attr_unassigned
|
||||
)
|
||||
.unwrap();
|
||||
write_l1_section!(buf_writer, offset, attr_unassigned);
|
||||
offset += ONE_MB;
|
||||
}
|
||||
|
||||
@ -203,12 +155,7 @@ fn main() {
|
||||
)
|
||||
.unwrap();
|
||||
for _ in 0..AMBA_APB {
|
||||
writeln!(
|
||||
buf_writer,
|
||||
"L1Section::new({}, {}).raw_value(),",
|
||||
offset, attr_shared_dev
|
||||
)
|
||||
.unwrap();
|
||||
write_l1_section!(buf_writer, offset, attr_shared_dev);
|
||||
offset += ONE_MB;
|
||||
}
|
||||
|
||||
@ -218,23 +165,13 @@ fn main() {
|
||||
)
|
||||
.unwrap();
|
||||
for _ in 0..UNASSIGNED_3 {
|
||||
writeln!(
|
||||
buf_writer,
|
||||
"L1Section::new({}, {}).raw_value(),",
|
||||
offset, attr_unassigned
|
||||
)
|
||||
.unwrap();
|
||||
write_l1_section!(buf_writer, offset, attr_unassigned);
|
||||
offset += ONE_MB;
|
||||
}
|
||||
|
||||
writeln!(buf_writer, "// QSPI XIP (0xFC00_0000 - 0xFE00_0000)").unwrap();
|
||||
for _ in 0..QSPI_XIP {
|
||||
writeln!(
|
||||
buf_writer,
|
||||
"L1Section::new({}, {}).raw_value(),",
|
||||
offset, attr_qspi
|
||||
)
|
||||
.unwrap();
|
||||
write_l1_section!(buf_writer, offset, attr_qspi);
|
||||
offset += ONE_MB;
|
||||
}
|
||||
|
||||
@ -244,24 +181,14 @@ fn main() {
|
||||
)
|
||||
.unwrap();
|
||||
for _ in 0..UNASSIGNED_4 {
|
||||
writeln!(
|
||||
buf_writer,
|
||||
"L1Section::new({}, {}).raw_value(),",
|
||||
offset, attr_unassigned
|
||||
)
|
||||
.unwrap();
|
||||
write_l1_section!(buf_writer, offset, attr_unassigned);
|
||||
offset += ONE_MB;
|
||||
}
|
||||
|
||||
writeln!(buf_writer, "// OCM High (0xFFF0_0000 - 0xFFFF_FFFF)").unwrap();
|
||||
let mut offset_u64 = offset as u64;
|
||||
for _ in 0..OCM_MAPPED_HIGH {
|
||||
writeln!(
|
||||
buf_writer,
|
||||
"L1Section::new({}, {}).raw_value(),",
|
||||
offset, attr_ocm_high
|
||||
)
|
||||
.unwrap();
|
||||
write_l1_section!(buf_writer, offset, attr_ocm_high);
|
||||
|
||||
offset_u64 += ONE_MB as u64;
|
||||
}
|
||||
|
@ -162,12 +162,6 @@ pub mod section_attrs {
|
||||
};
|
||||
}
|
||||
|
||||
pub const NUM_L1_PAGE_TABLE_ENTRIES: usize = 4096;
|
||||
|
||||
#[repr(C, align(16384))]
|
||||
#[cfg(feature = "rt")]
|
||||
pub struct L1Table(pub(crate) [u32; NUM_L1_PAGE_TABLE_ENTRIES]);
|
||||
|
||||
/// Load the MMU translation table base address into the MMU.
|
||||
///
|
||||
/// # Safety
|
||||
|
File diff suppressed because it is too large
Load Diff
599
zynq7000/src/eth.rs
Normal file
599
zynq7000/src/eth.rs
Normal file
@ -0,0 +1,599 @@
|
||||
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, default = 0x0)]
|
||||
#[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,
|
||||
// Default value is 0x0 (little endian)
|
||||
#[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,
|
||||
}
|
||||
|
||||
impl TxStatus {
|
||||
pub fn new_clear_all() -> Self {
|
||||
Self::new_with_raw_value(0xFF)
|
||||
}
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
|
||||
impl RxStatus {
|
||||
pub fn new_clear_all() -> Self {
|
||||
Self::new_with_raw_value(0xF)
|
||||
}
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
|
||||
impl InterruptControl {
|
||||
pub fn new_clear_all() -> Self {
|
||||
Self::new_with_raw_value(0xFFFF_FFFF)
|
||||
}
|
||||
}
|
||||
|
||||
#[bitbybit::bitenum(u2, exhaustive = false)]
|
||||
pub enum PhyOperation {
|
||||
Read = 0b10,
|
||||
Write = 0b01,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||
#[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_0b10: u2,
|
||||
#[bits(0..=15, rw)]
|
||||
data: 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) }
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -17,7 +17,7 @@ pub enum IoType {
|
||||
Hstl = 0b100,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||
#[derive(Debug)]
|
||||
pub struct Config {
|
||||
#[bit(13, rw)]
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! System Level Control Registers (slcr)
|
||||
//!
|
||||
//! Writing any of these registers required unlocking the SLCR first.
|
||||
use arbitrary_int::u4;
|
||||
use arbitrary_int::{u3, u4};
|
||||
pub use clocks::{ClockControl, MmioClockControl};
|
||||
pub use reset::{MmioResetControl, ResetControl};
|
||||
|
||||
@ -50,10 +50,27 @@ impl DdrIoB {
|
||||
|
||||
static_assertions::const_assert_eq!(core::mem::size_of::<DdrIoB>(), 0x38);
|
||||
|
||||
#[bitbybit::bitenum(u3, exhaustive = false)]
|
||||
pub enum VrefSel {
|
||||
Disabled = 0b000,
|
||||
Vref0_9V = 0b001,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct GpiobControl {
|
||||
#[bit(11, rw)]
|
||||
vref_sw_en: bool,
|
||||
#[bits(4..=6, rw)]
|
||||
vref_sel: Option<VrefSel>,
|
||||
#[bit(0, rw)]
|
||||
vref_en: bool,
|
||||
}
|
||||
|
||||
#[derive(derive_mmio::Mmio)]
|
||||
#[repr(C)]
|
||||
pub struct GpiobCtrl {
|
||||
ctrl: u32,
|
||||
pub struct GpiobRegisters {
|
||||
ctrl: GpiobControl,
|
||||
cfg_cmos18: u32,
|
||||
cfg_cmos25: u32,
|
||||
cfg_cmos33: u32,
|
||||
@ -62,7 +79,7 @@ pub struct GpiobCtrl {
|
||||
drvr_bias_ctrl: u32,
|
||||
}
|
||||
|
||||
impl GpiobCtrl {
|
||||
impl GpiobRegisters {
|
||||
/// Create a new handle to this peripheral.
|
||||
///
|
||||
/// Writing to this register requires unlocking the SLCR registers first.
|
||||
@ -71,12 +88,13 @@ impl GpiobCtrl {
|
||||
///
|
||||
/// If you create multiple instances of this handle at the same time, you are responsible for
|
||||
/// ensuring that there are no read-modify-write races on any of the registers.
|
||||
pub unsafe fn new_mmio_fixed() -> MmioGpiobCtrl<'static> {
|
||||
pub unsafe fn new_mmio_fixed() -> MmioGpiobRegisters<'static> {
|
||||
unsafe { Self::new_mmio_at(SLCR_BASE_ADDR + GPIOB_OFFSET) }
|
||||
}
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct BootModeRegister {
|
||||
#[bit(4, r)]
|
||||
pll_bypass: bool,
|
||||
@ -183,7 +201,7 @@ pub struct Slcr {
|
||||
_gap18: [u32; 0x09],
|
||||
|
||||
#[mmio(inner)]
|
||||
gpiob: GpiobCtrl,
|
||||
gpiob: GpiobRegisters,
|
||||
|
||||
#[mmio(inner)]
|
||||
ddriob: DdrIoB,
|
||||
|
@ -35,6 +35,23 @@ pub struct GpioClockReset {
|
||||
gpio_cpu1x_rst: bool,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||
#[derive(Debug)]
|
||||
pub struct EthernetReset {
|
||||
#[bit(5, rw)]
|
||||
gem1_ref_rst: bool,
|
||||
#[bit(4, rw)]
|
||||
gem0_ref_rst: bool,
|
||||
#[bit(3, rw)]
|
||||
gem1_rx_rst: bool,
|
||||
#[bit(2, rw)]
|
||||
gem0_rx_rst: bool,
|
||||
#[bit(1, rw)]
|
||||
gem1_cpu1x_rst: bool,
|
||||
#[bit(0, rw)]
|
||||
gem0_cpu1x_rst: bool,
|
||||
}
|
||||
|
||||
#[derive(derive_mmio::Mmio)]
|
||||
#[repr(C)]
|
||||
pub struct ResetControl {
|
||||
@ -45,7 +62,7 @@ pub struct ResetControl {
|
||||
topsw: u32,
|
||||
dmac: u32,
|
||||
usb: u32,
|
||||
gem: u32,
|
||||
eth: EthernetReset,
|
||||
sdio: DualRefAndClockReset,
|
||||
spi: DualRefAndClockReset,
|
||||
can: DualClockReset,
|
||||
|
Loading…
x
Reference in New Issue
Block a user