Ethernet and smoltcp/embassy-net support
Some checks failed
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled

This commit is contained in:
2025-05-27 12:02:57 +02:00
committed by Robin Mueller
parent 037668e5a9
commit c5a734210a
38 changed files with 8970 additions and 4231 deletions

View File

@@ -8,5 +8,6 @@ members = [
"examples/simple",
"examples/embassy",
"examples/zedboard",
"zynq-mmu",
]
exclude = ["experiments"]

View File

@@ -11,7 +11,9 @@ keywords = ["no-std", "arm", "cortex-a", "amd", "zynq7000"]
categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
cortex-ar = "0.2"
# cortex-ar = "0.2"
# cortex-ar = { git = "https://github.com/rust-embedded/cortex-ar", branch = "main", features = ["critical-section-single-core"] }
cortex-ar = { version = "0.2", path = "../../../../Rust/cortex-ar/cortex-ar", features = ["critical-section-single-core"] }
zynq7000-rt = { path = "../../zynq7000-rt" }
zynq7000 = { path = "../../zynq7000" }
zynq7000-hal = { path = "../../zynq7000-hal" }
@@ -25,11 +27,17 @@ embedded-hal = "1"
fugit = "0.3"
log = "0.4"
embassy-executor = { git = "https://github.com/embassy-rs/embassy.git", branch = "main", features = [
# embassy-executor = { git = "https://github.com/us-irs/embassy", branch = "add-cortex-ar-support", features = [
# "arch-cortex-ar",
# "executor-thread",
# "task-arena-size-65536"
# ]}
embassy-executor = { path = "../../../../Rust/embassy/embassy-executor", features = [
"arch-cortex-ar",
"executor-thread",
]}
embassy-time = { git = "https://github.com/embassy-rs/embassy.git", branch = "main", version = "0.4", features = ["tick-hz-1_000_000"] }
# embassy-time = { git = "https://github.com/us-irs/embassy", branch = "add-cortex-ar-support", version = "0.4", features = ["tick-hz-1_000_000"] }
embassy-time = { path = "../../../../Rust/embassy/embassy-time", version = "0.4", features = ["tick-hz-1_000_000"] }
[profile.release]
codegen-units = 1

View File

@@ -9,7 +9,9 @@ repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
license = "MIT OR Apache-2.0"
[dependencies]
cortex-ar = "0.2"
# cortex-ar = "0.2"
# cortex-ar = { git = "https://github.com/rust-embedded/cortex-ar", branch = "main" }
cortex-ar = { version = "0.2", path = "../../../../Rust/cortex-ar/cortex-ar", features = ["critical-section-single-core"] }
zynq7000-rt = { path = "../../zynq7000-rt" }
zynq7000 = { path = "../../zynq7000" }
zynq7000-hal = { path = "../../zynq7000-hal" }

View File

@@ -11,13 +11,16 @@ keywords = ["no-std", "arm", "cortex-a", "amd", "zynq7000"]
categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
cortex-ar = "0.2"
# cortex-ar = "0.2"
# cortex-ar = { git = "https://github.com/rust-embedded/cortex-ar", branch = "main", features = ["critical-section-single-core"] }
cortex-ar = { version = "0.2", path = "../../../../Rust/cortex-ar/cortex-ar", features = ["critical-section-single-core"] }
zynq7000-rt = { path = "../../zynq7000-rt" }
zynq7000 = { path = "../../zynq7000" }
zynq7000-hal = { path = "../../zynq7000-hal" }
zynq7000-embassy = { path = "../../zynq7000-embassy" }
l3gd20 = { git = "https://github.com/us-irs/l3gd20.git", branch = "add-async-if" }
embedded-io = "0.6"
bitbybit = "1.3"
arbitrary-int = "1.3"
embedded-io-async = "0.6"
critical-section = "1"
@@ -27,12 +30,19 @@ embedded-hal = "1"
embedded-hal-async = "1"
fugit = "0.3"
log = "0.4"
rand = { version = "0.9", default-features = false, features = ["small_rng"] }
embassy-executor = { git = "https://github.com/embassy-rs/embassy.git", branch = "main", features = [
# embassy-executor = { git = "https://github.com/us-irs/embassy", path = "../embassy/embassy-executor", branch = "local-cortex-ar", features = [
# "arch-cortex-ar",
# "executor-thread",
# ]}
embassy-executor = { path = "../../../../Rust/embassy/embassy-executor", features = [
"arch-cortex-ar",
"executor-thread",
]}
embassy-time = { git = "https://github.com/embassy-rs/embassy.git", branch = "main", version = "0.4", features = ["tick-hz-1_000_000"] }
# embassy-time = { git = "https://github.com/us-irs/embassy", branch = "local-cortex-ar", version = "0.4", features = ["tick-hz-1_000_000"] }
embassy-time = { path = "../../../../Rust/embassy/embassy-time", version = "0.4", features = ["tick-hz-1_000_000"] }
embassy-net = { path = "../../../../Rust/embassy/embassy-net", version = "0.7", features = ["dhcpv4"] }
heapless = "0.8"
axi-uartlite = { git = "https://egit.irs.uni-stuttgart.de/rust/axi-uartlite.git" }
axi-uart16550 = { git = "https://egit.irs.uni-stuttgart.de/rust/axi-uart16550.git" }

View File

@@ -0,0 +1,299 @@
#![no_std]
#![no_main]
use core::{mem::MaybeUninit, panic::PanicInfo};
use cortex_ar::asm::nop;
use embassy_executor::Spawner;
use embassy_time::{Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write;
use log::{error, info};
use rand::{RngCore, SeedableRng};
use zedboard::PS_CLOCK_FREQUENCY;
use zynq7000_hal::{
BootMode,
clocks::Clocks,
configure_level_shifter,
eth::{AlignedBuffer, EthernetConfig, EthernetLowLevel},
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
gpio::{GpioPins, Output, PinState},
gtc::Gtc,
uart::{ClkConfigRaw, Uart, UartConfig},
};
use zynq7000::{PsPeripherals, slcr::LevelShifterConfig};
use zynq7000_rt::{self as _, mmu::section_attrs::SHAREABLE_DEVICE, mmu_l1_table_mut};
const INIT_STRING: &str = "-- Zynq 7000 Zedboard Ethernet Example --\n\r";
const MAC_ADDRESS: [u8; 6] = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06];
#[unsafe(link_section = ".uncached")]
static RX_DESCRIPTORS: static_cell::ConstStaticCell<
MaybeUninit<[zynq7000_hal::eth::rx_descr::Descriptor; 32]>,
> = static_cell::ConstStaticCell::new(MaybeUninit::uninit());
#[unsafe(link_section = ".uncached")]
static TX_DESCRIPTORS: static_cell::ConstStaticCell<
MaybeUninit<[zynq7000_hal::eth::tx_descr::Descriptor; 32]>,
> = static_cell::ConstStaticCell::new(MaybeUninit::uninit());
const NUM_RX_BUFS: usize = 32;
const NUM_TX_BUFS: usize = 32;
static ETH_RX_BUFS: static_cell::ConstStaticCell<[AlignedBuffer; NUM_RX_BUFS]> =
static_cell::ConstStaticCell::new([AlignedBuffer([0; zynq7000_hal::eth::MTU]); NUM_RX_BUFS]);
static ETH_TX_BUFS: static_cell::ConstStaticCell<[AlignedBuffer; NUM_TX_BUFS]> =
static_cell::ConstStaticCell::new([AlignedBuffer([0; zynq7000_hal::eth::MTU]); NUM_TX_BUFS]);
/// See memory.x file. 1 MB starting at this address will be configured as uncached memory using the
/// MMU.
const UNCACHED_ADDR: u32 = 0x4000000;
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main();
}
#[embassy_executor::task]
async fn net_task(
mut runner: embassy_net::Runner<'static, zynq7000_hal::eth::embassy_net::Driver>,
) -> ! {
runner.run().await
}
#[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(spawner: Spawner) -> ! {
// Configure the uncached memory region using the MMU.
mmu_l1_table_mut()
.update(UNCACHED_ADDR, SHAREABLE_DEVICE)
.expect("configuring uncached memory section failed");
// Enable PS-PL level shifters.
configure_level_shifter(LevelShifterConfig::EnableAll);
let dp = PsPeripherals::take().unwrap();
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// Set up the global interrupt controller.
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.enable();
unsafe {
gic.enable_interrupts();
}
let mut gpio_pins = GpioPins::new(dp.gpio);
// Set up global timer counter and embassy time driver.
let gtc = Gtc::new(dp.gtc, clocks.arm_clocks());
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
// Set up the UART, we are logging with it.
let uart_clk_config = ClkConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut uart = Uart::new_with_mio(
dp.uart_1,
UartConfig::new_with_clk_config(uart_clk_config),
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
)
.unwrap();
uart.write_all(INIT_STRING.as_bytes()).unwrap();
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Trace,
false,
)
};
let boot_mode = BootMode::new();
info!("Boot mode: {:?}", boot_mode);
let rx_bufs = ETH_RX_BUFS.take();
let tx_bufs = ETH_TX_BUFS.take();
let rx_descr = RX_DESCRIPTORS.take();
let tx_descr = TX_DESCRIPTORS.take();
rx_descr.write([const { zynq7000_hal::eth::rx_descr::Descriptor::new() }; 32]);
tx_descr.write([const { zynq7000_hal::eth::tx_descr::Descriptor::new() }; 32]);
let rx_descr_init = unsafe { rx_descr.assume_init_mut() };
let tx_descr_init = unsafe { tx_descr.assume_init_mut() };
// Unwraps okay, list length is not 0
let mut rx_descr_ref =
zynq7000_hal::eth::rx_descr::DescriptorList::new(rx_descr_init.as_mut_slice()).unwrap();
// Create an unsafe copy for error handling.
// let rx_descr_copy_for_error_handling = unsafe { rx_descr_ref.clone_unchecked() };
let mut tx_descr_ref =
zynq7000_hal::eth::tx_descr::DescriptorList::new(tx_descr_init.as_mut_slice());
rx_descr_ref.init_with_aligned_bufs(rx_bufs.as_slice());
tx_descr_ref.init_or_reset();
// Unwrap okay, this is a valid peripheral.
let eth_ll = EthernetLowLevel::new(dp.eth_0).unwrap();
let (clk_config, clk_error) = zynq7000_hal::eth::ClkConfig::calculate_for_rgmii(
clocks.io_clocks().ref_clk(),
zynq7000_hal::eth::Speed::Mbps1000,
);
info!(
"Calculated RGMII clock configuration: {:?}, error: {}",
clk_config, clk_error
);
// Unwrap okay, we use a standard clock config, and the clock config should never fail.
let eth_cfg = EthernetConfig::new(
clk_config,
zynq7000_hal::eth::calculate_mdc_clk_div(clocks.arm_clocks()).unwrap(),
MAC_ADDRESS,
);
// Configures all the physical pins for ethernet operation and sets up the
// ethernet peripheral.
let mut eth = zynq7000_hal::eth::Ethernet::new_with_mio(
eth_ll,
eth_cfg,
gpio_pins.mio.mio16,
gpio_pins.mio.mio21,
(
gpio_pins.mio.mio17,
gpio_pins.mio.mio18,
gpio_pins.mio.mio19,
gpio_pins.mio.mio20,
),
gpio_pins.mio.mio22,
gpio_pins.mio.mio27,
(
gpio_pins.mio.mio23,
gpio_pins.mio.mio24,
gpio_pins.mio.mio25,
gpio_pins.mio.mio26,
),
Some((gpio_pins.mio.mio52, gpio_pins.mio.mio53)),
);
eth.set_rx_buf_descriptor_base_address(rx_descr_ref.base_addr());
eth.set_tx_buf_descriptor_base_address(tx_descr_ref.base_addr());
let (mut phy, phy_rev) =
zedboard::phy_marvell::Marvell88E1518Phy::new_autoprobe_addr(eth.mdio_mut()).unwrap();
info!(
"Detected Marvell 88E1518 PHY with revision number: {:?}",
phy_rev
);
phy.reset();
phy.restart_auto_negotiation();
// TODO:
// 1. PHY configuration.
// 2. Interrupt handler for ethernet RX and TX. Need to pass the buffers and descriptors
// to the interrupt handler.
// 3. Create embassy-net driver and schedule it.
//
let bufs = zynq7000_hal::eth::embassy_net::DescriptorsAndBuffers::new(
rx_descr_ref,
rx_bufs,
tx_descr_ref,
tx_bufs,
)
.unwrap();
let driver = zynq7000_hal::eth::embassy_net::Driver::new(&eth, MAC_ADDRESS, bufs);
let config = embassy_net::Config::dhcpv4(Default::default());
static RESOURCES: static_cell::StaticCell<embassy_net::StackResources<3>> =
static_cell::StaticCell::new();
let mut rng = rand::rngs::SmallRng::seed_from_u64(1);
let (stack, runner) = embassy_net::new(
driver,
config,
RESOURCES.init(embassy_net::StackResources::new()),
rng.next_u64(),
);
spawner.spawn(net_task(runner)).unwrap();
stack.wait_config_up().await;
let network_config = stack.config_v4();
info!(
"Network configuration is up: DHCP config: {:?}!",
network_config
);
let mut ticker = Ticker::every(Duration::from_millis(200));
let mut mio_led = Output::new_for_mio(gpio_pins.mio.mio7, PinState::Low);
let mut emio_leds: [Output; 8] = [
Output::new_for_emio(gpio_pins.emio.take(0).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(1).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(2).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(3).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(4).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(5).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(6).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(7).unwrap(), PinState::Low),
];
loop {
mio_led.toggle().unwrap();
// Create a wave pattern for emio_leds
for led in emio_leds.iter_mut() {
led.toggle().unwrap();
ticker.next().await; // Wait for the next ticker for each toggle
}
ticker.next().await; // Wait for the next cycle of the ticker
}
}
#[unsafe(no_mangle)]
pub extern "C" fn _irq_handler() {
let mut gic_helper = GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
Interrupt::Sgi(_) => (),
Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
Interrupt::Spi(spi_interrupt) => {
if spi_interrupt == zynq7000_hal::gic::SpiInterrupt::Eth0 {
let result = zynq7000_hal::eth::embassy_net::on_interrupt(
zynq7000_hal::eth::EthernetId::Eth0,
);
// TODO: Send the result structure back to the main thread.
}
}
Interrupt::Invalid(_) => (),
Interrupt::Spurious => (),
}
gic_helper.end_of_interrupt(irq_info);
}
#[unsafe(no_mangle)]
pub extern "C" fn _abort_handler() {
loop {
nop();
}
}
#[unsafe(no_mangle)]
pub extern "C" fn _undefined_handler() {
loop {
nop();
}
}
#[unsafe(no_mangle)]
pub extern "C" fn _prefetch_handler() {
loop {
nop();
}
}
/// Panic handler
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
error!("Panic: {info:?}");
loop {}
}

View File

@@ -1,5 +1,6 @@
#![no_std]
use zynq7000_hal::time::Hertz;
pub mod phy_marvell;
// Define the clock frequency as a constant
pub const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);

View File

@@ -0,0 +1,217 @@
use arbitrary_int::{u2, u4, u5};
#[derive(Clone, Debug)]
pub struct PhyIdentifier {
pub oui: u32,
pub model: u8,
pub rev: u8,
}
// Organizational Unique Identifier for Marvell 88E1518 PHY
const MARVELL_88E1518_OUI: u32 = 0x005043;
const MARVELL_88E1518_MODELL_NUMBER: u8 = 0b011101;
#[bitbybit::bitenum(u5, exhaustive = false)]
pub enum MarvellRegistersPage0 {
CopperControl = 0,
CopperStatus = 1,
IdReg1 = 2,
IdReg2 = 3,
CopperSpecificStatus = 17,
}
#[bitbybit::bitfield(u16)]
pub struct CopperControlRegister {
#[bit(15, rw)]
copper_reset: bool,
#[bit(14, rw)]
loopback: bool,
#[bit(12, rw)]
auto_negotiation_enable: bool,
#[bit(11, rw)]
power_down: bool,
#[bit(10, rw)]
isolate: bool,
#[bit(9, rw)]
restart_auto_negotiation: bool,
/// 1: Full-duplex, 0: Half-duplex
#[bit(8, rw)]
copper_duplex_mode: bool,
#[bits([13, 6], rw)]
speed_selection: u2,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
pub enum LatchingLinkStatus {
Up = 1,
DownSinceLastRead = 0,
}
#[bitbybit::bitfield(u16)]
pub struct CopperStatusRegister {
/// Always 0, the 100BASE-T4 protocol is not available on Marvell 88E15XX.
#[bit(15, r)]
p_100_base_t4: bool,
/// Always 1 for Marvell 88E15XX
#[bit(14, r)]
p_100_base_x_full_duplex: bool,
/// Always 1 for Marvell 88E15XX
#[bit(13, r)]
p_100_base_x_half_duplex: bool,
/// Always 1 for Marvell 88E15XX
#[bit(12, r)]
p_10_base_t_full_duplex: bool,
/// Always 1 for Marvell 88E15XX
#[bit(11, r)]
p_10_base_t_half_duplex: bool,
/// Always 0 for Marvell 88E15XX
#[bit(10, r)]
p_100_base_t2_full_duplex: bool,
/// Always 0 for Marvell 88E15XX
#[bit(9, r)]
p_100_base_t2_half_duplex: bool,
/// Always 1 for Marvell 88E15XX
#[bit(8, r)]
extended_status: bool,
/// Always 1 for Marvell 88E15XX
#[bit(6, r)]
mf_preamble_suppression: bool,
#[bit(5, r)]
auto_negotiation_complete: bool,
// Latching high register bit.
#[bit(4, r)]
copper_remote_fault: bool,
/// Always 1 for Marvell 88E15XX
#[bit(3, r)]
auto_negotation_ability: bool,
// Latching low register bit. For the current link status, this register should be read back
// to back, or the link real time register (17_0.10) should be read
#[bit(2, r)]
copper_link_status: LatchingLinkStatus,
// Latching high register bit.
#[bit(1, r)]
jabber_detect: bool,
/// Always 1 for Marvell 88E15XX
#[bit(0, r)]
extended_capabilities: bool,
}
#[bitbybit::bitenum(u2, exhaustive = true)]
pub enum PhySpeedBits {
Reserved = 0b11,
Mbps1000 = 0b10,
Mbps100 = 0b01,
Mbps10 = 0b00,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
pub enum PhyDuplexBit {
Full = 1,
Half = 0,
}
#[bitbybit::bitfield(u16)]
pub struct CopperSpecificStatusRegister {
#[bits(14..=15, r)]
speed: PhySpeedBits,
#[bit(13, r)]
duplex: PhyDuplexBit,
/// Latching high register bit.
#[bit(12, r)]
page_received: bool,
/// This is 1 when auto-negotiation is not enabled.
#[bit(11, r)]
speed_and_duplex_resolved: bool,
/// This is the real-time link status.
#[bit(10, r)]
copper_link: bool,
#[bit(9, r)]
transmit_pause_enabled: bool,
#[bit(8, r)]
received_pause_enabled: bool,
#[bit(6, r)]
mdi_crossover_status: bool,
#[bit(4, r)]
copper_energy_detect_status: bool,
#[bit(3, r)]
global_link_status: bool,
#[bit(1, r)]
polarity: bool,
#[bit(0, r)]
jabber: bool,
}
pub struct Marvell88E1518Phy {
mdio: zynq7000_hal::eth::mdio::Mdio,
addr: u5,
}
impl Marvell88E1518Phy {
pub fn new_autoprobe_addr(mdio: &mut zynq7000_hal::eth::mdio::Mdio) -> Option<(Self, u4)> {
for addr in 0..32 {
let phy_id_1 =
mdio.read_blocking(u5::new(addr), MarvellRegistersPage0::IdReg1.raw_value());
let phy_id_2 =
mdio.read_blocking(u5::new(addr), MarvellRegistersPage0::IdReg2.raw_value());
let oui = (((phy_id_2 as u32) >> 10) << 19) | ((phy_id_1 as u32) << 3);
let model_number = ((phy_id_2 >> 4) & 0b111111) as u8;
let revision_number = u4::new((phy_id_2 & 0b1111) as u8);
if oui == MARVELL_88E1518_OUI && model_number == MARVELL_88E1518_MODELL_NUMBER {
return Some((
Self {
mdio: unsafe { mdio.clone() },
addr: u5::new(addr),
},
revision_number,
));
}
}
None
}
pub fn new(mdio: zynq7000_hal::eth::mdio::Mdio, addr: u5) -> Self {
Self { mdio, addr }
}
pub fn reset(&mut self) {
let mut ctrl = CopperControlRegister::new_with_raw_value(
self.mdio
.read_blocking(self.addr, MarvellRegistersPage0::CopperControl.raw_value()),
);
ctrl.set_copper_reset(true);
self.mdio.write_blocking(
self.addr,
MarvellRegistersPage0::CopperControl.raw_value(),
ctrl.raw_value(),
);
}
pub fn restart_auto_negotiation(&mut self) {
let mut ctrl = CopperControlRegister::new_with_raw_value(
self.mdio
.read_blocking(self.addr, MarvellRegistersPage0::CopperControl.raw_value()),
);
ctrl.set_auto_negotiation_enable(true);
ctrl.set_restart_auto_negotiation(true);
self.mdio.write_blocking(
self.addr,
MarvellRegistersPage0::CopperControl.raw_value(),
ctrl.raw_value(),
);
}
pub fn read_copper_status(&mut self) -> CopperStatusRegister {
let raw_value = self
.mdio
.read_blocking(self.addr, MarvellRegistersPage0::CopperStatus.raw_value());
CopperStatusRegister::new_with_raw_value(raw_value)
}
pub fn read_copper_specific_status_register_1(&mut self) -> CopperSpecificStatusRegister {
let raw_value = self.mdio.read_blocking(
self.addr,
MarvellRegistersPage0::CopperSpecificStatus.raw_value(),
);
CopperSpecificStatusRegister::new_with_raw_value(raw_value)
}
}

View File

@@ -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 = .;
*(.uncached .uncached.*);
. = ALIGN(4);
_ebss_uncached = .;
} > UNCACHED
}

9
zynq-mmu/Cargo.toml Normal file
View 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 }

73
zynq-mmu/src/lib.rs Normal file
View File

@@ -0,0 +1,73 @@
//! 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},
cache::clean_and_invalidate_l1_data_cache,
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(
&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.raw();
// The Zynq 7000 has a 32 kB 4-way associative cache with a line length of 32 bytes.
// 4-way associative cache: A == 2
// 32 bytes line length: N == 5
// 256 (32kB / (32 * 4)) sets: S == 8
clean_and_invalidate_l1_data_cache::<2, 5, 8>();
TlbIAll::write();
BpIAll::write();
dsb();
isb();
Ok(())
}
}
pub struct L1TableRef<'a>(pub &'a mut L1Table);
impl<'a> L1TableRef<'a> {
pub fn new(l1_table: &'a mut L1Table) -> L1TableRef<'a> {
L1TableRef(l1_table)
}
}
impl L1TableRef<'_> {
pub fn update(
&mut self,
addr: u32,
section_attrs: SectionAttributes,
) -> Result<(), AddrNotAlignedToOneMb> {
self.0.update(addr, section_attrs)
}
}

View File

@@ -0,0 +1,485 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "addr2line"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9acbfca36652500c911ddb767ed433e3ed99b032b5d935be73c6923662db1d43"
dependencies = [
"gimli",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
dependencies = [
"rustc-std-workspace-core",
]
[[package]]
name = "alloc"
version = "0.0.0"
dependencies = [
"compiler_builtins",
"core",
]
[[package]]
name = "alloctests"
version = "0.0.0"
dependencies = [
"rand",
"rand_xorshift",
]
[[package]]
name = "cc"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8"
dependencies = [
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
dependencies = [
"rustc-std-workspace-core",
]
[[package]]
name = "compiler_builtins"
version = "0.1.160"
dependencies = [
"cc",
"core",
]
[[package]]
name = "core"
version = "0.0.0"
[[package]]
name = "coretests"
version = "0.0.0"
dependencies = [
"rand",
"rand_xorshift",
]
[[package]]
name = "dlmalloc"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d01597dde41c0b9da50d5f8c219023d63d8f27f39a27095070fd191fddc83891"
dependencies = [
"cfg-if",
"libc",
"rustc-std-workspace-core",
"windows-sys",
]
[[package]]
name = "fortanix-sgx-abi"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57cafc2274c10fab234f176b25903ce17e690fca7597090d50880e047a0389c5"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-core",
]
[[package]]
name = "getopts"
version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1"
dependencies = [
"rustc-std-workspace-core",
"rustc-std-workspace-std",
"unicode-width",
]
[[package]]
name = "gimli"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93563d740bc9ef04104f9ed6f86f1e3275c2cdafb95664e26584b9ca807a8ffe"
dependencies = [
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
[[package]]
name = "hashbrown"
version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
dependencies = [
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
[[package]]
name = "hermit-abi"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
dependencies = [
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
[[package]]
name = "libc"
version = "0.2.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
dependencies = [
"rustc-std-workspace-core",
]
[[package]]
name = "memchr"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
dependencies = [
"rustc-std-workspace-core",
]
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
[[package]]
name = "object"
version = "0.37.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03fd943161069e1768b4b3d050890ba48730e590f57e56d4aa04e7e090e61b4a"
dependencies = [
"memchr",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
[[package]]
name = "panic_abort"
version = "0.0.0"
dependencies = [
"alloc",
"compiler_builtins",
"core",
"libc",
]
[[package]]
name = "panic_unwind"
version = "0.0.0"
dependencies = [
"alloc",
"cfg-if",
"compiler_builtins",
"core",
"libc",
"unwind",
]
[[package]]
name = "proc_macro"
version = "0.0.0"
dependencies = [
"core",
"rustc-literal-escaper",
"std",
]
[[package]]
name = "profiler_builtins"
version = "0.0.0"
dependencies = [
"cc",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
dependencies = [
"rustc-std-workspace-core",
]
[[package]]
name = "r-efi-alloc"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc2f58ef3ca9bb0f9c44d9aa8537601bcd3df94cc9314a40178cadf7d4466354"
dependencies = [
"r-efi",
"rustc-std-workspace-core",
]
[[package]]
name = "rand"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
dependencies = [
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
[[package]]
name = "rand_xorshift"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
dependencies = [
"rand_core",
]
[[package]]
name = "rustc-demangle"
version = "0.1.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
dependencies = [
"rustc-std-workspace-core",
]
[[package]]
name = "rustc-literal-escaper"
version = "0.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0041b6238913c41fe704213a4a9329e2f685a156d1781998128b4149c230ad04"
dependencies = [
"rustc-std-workspace-std",
]
[[package]]
name = "rustc-std-workspace-alloc"
version = "1.99.0"
dependencies = [
"alloc",
]
[[package]]
name = "rustc-std-workspace-core"
version = "1.99.0"
dependencies = [
"compiler_builtins",
"core",
]
[[package]]
name = "rustc-std-workspace-std"
version = "1.99.0"
dependencies = [
"std",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "std"
version = "0.0.0"
dependencies = [
"addr2line",
"alloc",
"cfg-if",
"compiler_builtins",
"core",
"dlmalloc",
"fortanix-sgx-abi",
"hashbrown",
"hermit-abi",
"libc",
"miniz_oxide",
"object",
"panic_abort",
"panic_unwind",
"r-efi",
"r-efi-alloc",
"rand",
"rand_xorshift",
"rustc-demangle",
"std_detect",
"unwind",
"wasi",
"windows-targets 0.0.0",
]
[[package]]
name = "std_detect"
version = "0.1.5"
dependencies = [
"cfg-if",
"libc",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
[[package]]
name = "sysroot"
version = "0.0.0"
dependencies = [
"proc_macro",
"profiler_builtins",
"std",
"test",
]
[[package]]
name = "test"
version = "0.0.0"
dependencies = [
"core",
"getopts",
"libc",
"std",
]
[[package]]
name = "unicode-width"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
dependencies = [
"rustc-std-workspace-core",
"rustc-std-workspace-std",
]
[[package]]
name = "unwind"
version = "0.0.0"
dependencies = [
"cfg-if",
"compiler_builtins",
"core",
"libc",
"unwinding",
]
[[package]]
name = "unwinding"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d80f6c2bfede213d9a90b4a14f3eb99b84e33c52df6c1a15de0a100f5a88751"
dependencies = [
"compiler_builtins",
"gimli",
"rustc-std-workspace-core",
]
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
dependencies = [
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.0.0"
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

View File

@@ -0,0 +1,964 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "approx"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
dependencies = [
"num-traits",
]
[[package]]
name = "arbitrary-int"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "825297538d77367557b912770ca3083f778a196054b3ee63b22673c4a3cae0a5"
[[package]]
name = "arm-targets"
version = "0.2.0"
[[package]]
name = "arm-targets"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49743b294a925497a72abb5d7f3b0c9a1b390a65a92d72e38e915df767438664"
[[package]]
name = "arm-targets"
version = "0.2.0"
source = "git+https://github.com/rust-embedded/cortex-ar?branch=main#cdf224f23ce30f37897e94a9a34e3cc3696d1225"
[[package]]
name = "arm-targets"
version = "0.2.0"
source = "git+https://github.com/rust-embedded/cortex-ar.git#cdf224f23ce30f37897e94a9a34e3cc3696d1225"
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "axi-uart16550"
version = "0.1.0"
source = "git+https://egit.irs.uni-stuttgart.de/rust/axi-uart16550.git#59ca4f1003e5f7178952fc7674b824e059e579fd"
dependencies = [
"arbitrary-int",
"bitbybit",
"critical-section",
"derive-mmio 0.4.0 (git+https://github.com/us-irs/derive-mmio.git?branch=inner-mmio-by-shared-ref)",
"embassy-sync",
"embedded-hal-async",
"embedded-hal-nb",
"embedded-io",
"embedded-io-async",
"fugit",
"libm",
"nb 1.1.0",
"raw-slicee",
"thiserror",
]
[[package]]
name = "axi-uartlite"
version = "0.1.0"
source = "git+https://egit.irs.uni-stuttgart.de/rust/axi-uartlite.git#6618b4342f12518f9369450a7aa5f333886d48a0"
dependencies = [
"arbitrary-int",
"bitbybit",
"critical-section",
"derive-mmio 0.4.0 (git+https://github.com/us-irs/derive-mmio.git?branch=inner-mmio-by-shared-ref)",
"embassy-sync",
"embedded-hal-nb",
"embedded-io",
"embedded-io-async",
"nb 1.1.0",
"raw-slicee",
"thiserror",
]
[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "bisync"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5020822f6d6f23196ccaf55e228db36f9de1cf788052b37992e17cbc96ec41a7"
dependencies = [
"bisync_macros",
]
[[package]]
name = "bisync_macros"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d21f40d350a700f6aa107e45fb26448cf489d34794b2ba4522181dc9f1173af6"
[[package]]
name = "bitbybit"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d317eeca82e7d88d606419a430590d83552bdceb899cb29904f63d694344b7fc"
dependencies = [
"arbitrary-int",
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "blinky"
version = "0.1.0"
dependencies = [
"cortex-ar 0.2.0 (git+https://github.com/rust-embedded/cortex-ar?branch=main)",
"embedded-hal 1.0.0",
"embedded-io",
"fugit",
"log",
"zynq7000",
"zynq7000-hal",
"zynq7000-rt",
]
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cfg-if"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]]
name = "const-default"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa"
[[package]]
name = "cortex-a-rt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e12f7bc4041b2d606b5d6d029dd1f206429ec933e80efb750f6cc383fe5a53"
dependencies = [
"arm-targets 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cortex-ar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cortex-ar-rt-macros",
]
[[package]]
name = "cortex-ar"
version = "0.2.0"
dependencies = [
"arbitrary-int",
"arm-targets 0.2.0",
"bitbybit",
"critical-section",
"num_enum",
"thiserror",
]
[[package]]
name = "cortex-ar"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13f5d76ad942231363ff04266e7ce867db0450f520f30579fe3007b639902367"
dependencies = [
"arbitrary-int",
"arm-targets 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitbybit",
"num_enum",
]
[[package]]
name = "cortex-ar"
version = "0.2.0"
source = "git+https://github.com/rust-embedded/cortex-ar?branch=main#cdf224f23ce30f37897e94a9a34e3cc3696d1225"
dependencies = [
"arbitrary-int",
"arm-targets 0.2.0 (git+https://github.com/rust-embedded/cortex-ar?branch=main)",
"bitbybit",
"critical-section",
"num_enum",
]
[[package]]
name = "cortex-ar"
version = "0.2.0"
source = "git+https://github.com/rust-embedded/cortex-ar.git#cdf224f23ce30f37897e94a9a34e3cc3696d1225"
dependencies = [
"arbitrary-int",
"arm-targets 0.2.0 (git+https://github.com/rust-embedded/cortex-ar.git)",
"bitbybit",
"num_enum",
]
[[package]]
name = "cortex-ar-rt-macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8f9e342ee3ad1cfa79ab8746e26e0173241ef10d992f6c1d915d93052690c27"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "critical-section"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "darling"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 2.0.104",
]
[[package]]
name = "darling_macro"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core",
"quote",
"syn 2.0.104",
]
[[package]]
name = "delegate"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9b6483c2bbed26f97861cf57651d4f2b731964a28cd2257f934a4b452480d21"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "derive-mmio"
version = "0.4.0"
source = "git+https://github.com/us-irs/derive-mmio.git?branch=inner-mmio-by-shared-ref#73272cb5e79aa5fdf1c70229243ba2ba6f3acaeb"
dependencies = [
"derive-mmio-macro 0.4.0 (git+https://github.com/us-irs/derive-mmio.git?branch=inner-mmio-by-shared-ref)",
"rustversion",
]
[[package]]
name = "derive-mmio"
version = "0.4.0"
source = "git+https://github.com/us-irs/derive-mmio?branch=main#85712c66c29a2cdffb8221664edd027f7793071d"
dependencies = [
"derive-mmio-macro 0.4.0 (git+https://github.com/us-irs/derive-mmio?branch=main)",
"rustversion",
]
[[package]]
name = "derive-mmio-macro"
version = "0.4.0"
source = "git+https://github.com/us-irs/derive-mmio.git?branch=inner-mmio-by-shared-ref#73272cb5e79aa5fdf1c70229243ba2ba6f3acaeb"
dependencies = [
"proc-macro-error2",
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "derive-mmio-macro"
version = "0.4.0"
source = "git+https://github.com/us-irs/derive-mmio?branch=main#85712c66c29a2cdffb8221664edd027f7793071d"
dependencies = [
"proc-macro-error2",
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "dht-sensor"
version = "0.2.1"
source = "git+https://github.com/robamu/dht-sensor.git?branch=bump-embedded-hal-deps-update-async#b1f2ab2def2319a48790b10e36a1c0a6e0a8207b"
dependencies = [
"embedded-hal 1.0.0",
"embedded-hal-async",
]
[[package]]
name = "document-features"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d"
dependencies = [
"litrs",
]
[[package]]
name = "embassy-examples"
version = "0.1.0"
dependencies = [
"cortex-ar 0.2.0 (git+https://github.com/rust-embedded/cortex-ar?branch=main)",
"critical-section",
"dht-sensor",
"embassy-executor",
"embassy-time",
"embedded-hal 1.0.0",
"embedded-io",
"fugit",
"heapless",
"log",
"static_cell",
"zynq7000",
"zynq7000-embassy",
"zynq7000-hal",
"zynq7000-rt",
]
[[package]]
name = "embassy-executor"
version = "0.7.0"
source = "git+https://github.com/us-irs/embassy?branch=add-cortex-ar-support#2eb45161f7ec52eb0429d908027d655414e6deb3"
dependencies = [
"cortex-ar 0.2.0 (git+https://github.com/rust-embedded/cortex-ar.git)",
"critical-section",
"document-features",
"embassy-executor-macros",
]
[[package]]
name = "embassy-executor-macros"
version = "0.6.2"
source = "git+https://github.com/us-irs/embassy?branch=add-cortex-ar-support#2eb45161f7ec52eb0429d908027d655414e6deb3"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "embassy-sync"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d2c8cdff05a7a51ba0087489ea44b0b1d97a296ca6b1d6d1a33ea7423d34049"
dependencies = [
"cfg-if",
"critical-section",
"embedded-io-async",
"futures-sink",
"futures-util",
"heapless",
]
[[package]]
name = "embassy-time"
version = "0.4.0"
source = "git+https://github.com/us-irs/embassy?branch=add-cortex-ar-support#2eb45161f7ec52eb0429d908027d655414e6deb3"
dependencies = [
"cfg-if",
"critical-section",
"document-features",
"embassy-time-driver",
"embedded-hal 0.2.7",
"embedded-hal 1.0.0",
"embedded-hal-async",
"futures-util",
]
[[package]]
name = "embassy-time-driver"
version = "0.2.0"
source = "git+https://github.com/us-irs/embassy?branch=add-cortex-ar-support#2eb45161f7ec52eb0429d908027d655414e6deb3"
dependencies = [
"document-features",
]
[[package]]
name = "embassy-time-queue-utils"
version = "0.1.0"
source = "git+https://github.com/us-irs/embassy?branch=add-cortex-ar-support#2eb45161f7ec52eb0429d908027d655414e6deb3"
dependencies = [
"embassy-executor",
"heapless",
]
[[package]]
name = "embedded-alloc"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f2de9133f68db0d4627ad69db767726c99ff8585272716708227008d3f1bddd"
dependencies = [
"const-default",
"critical-section",
"linked_list_allocator",
"rlsf",
]
[[package]]
name = "embedded-dma"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446"
dependencies = [
"stable_deref_trait",
]
[[package]]
name = "embedded-hal"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
dependencies = [
"nb 0.1.3",
"void",
]
[[package]]
name = "embedded-hal"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
[[package]]
name = "embedded-hal-async"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884"
dependencies = [
"embedded-hal 1.0.0",
]
[[package]]
name = "embedded-hal-nb"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605"
dependencies = [
"embedded-hal 1.0.0",
"nb 1.1.0",
]
[[package]]
name = "embedded-io"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
[[package]]
name = "embedded-io-async"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f"
dependencies = [
"embedded-io",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "fugit"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7"
dependencies = [
"gcd",
]
[[package]]
name = "futures-core"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-sink"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
[[package]]
name = "futures-task"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-util"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"pin-utils",
]
[[package]]
name = "gcd"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
[[package]]
name = "hash32"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
dependencies = [
"byteorder",
]
[[package]]
name = "heapless"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
dependencies = [
"hash32",
"stable_deref_trait",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "l3gd20"
version = "0.4.0"
source = "git+https://github.com/us-irs/l3gd20.git?branch=add-async-if#93598f25819686997b18ce04d6677369567f87c4"
dependencies = [
"bisync",
"embedded-hal 1.0.0",
"embedded-hal-async",
]
[[package]]
name = "libc"
version = "0.2.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
[[package]]
name = "libm"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
[[package]]
name = "linked_list_allocator"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286"
[[package]]
name = "litrs"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
[[package]]
name = "log"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "nb"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
dependencies = [
"nb 1.1.0",
]
[[package]]
name = "nb"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "num_enum"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a"
dependencies = [
"num_enum_derive",
"rustversion",
]
[[package]]
name = "num_enum_derive"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
dependencies = [
"critical-section",
"portable-atomic",
]
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "portable-atomic"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
[[package]]
name = "proc-macro-error-attr2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "proc-macro-error2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
dependencies = [
"proc-macro-error-attr2",
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "raw-slicee"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d8fb5aaf355f81e39a4834840f68e809ee98c83f556dcfb8c16fd7004fe4dc"
dependencies = [
"embedded-dma",
]
[[package]]
name = "ringbuf"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe47b720588c8702e34b5979cb3271a8b1842c7cb6f57408efa70c779363488c"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "rlsf"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "222fb240c3286247ecdee6fa5341e7cdad0ffdf8e7e401d9937f2d58482a20bf"
dependencies = [
"cfg-if",
"const-default",
"libc",
"svgbobdoc",
]
[[package]]
name = "rustversion"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "static_cell"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0530892bb4fa575ee0da4b86f86c667132a94b74bb72160f58ee5a4afec74c23"
dependencies = [
"portable-atomic",
]
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "svgbobdoc"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2c04b93fc15d79b39c63218f15e3fdffaa4c227830686e3b7c5f41244eb3e50"
dependencies = [
"base64",
"proc-macro2",
"quote",
"syn 1.0.109",
"unicode-width",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "unicode-width"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "zedboard"
version = "0.1.0"
dependencies = [
"arbitrary-int",
"axi-uart16550",
"axi-uartlite",
"cortex-ar 0.2.0",
"critical-section",
"embassy-executor",
"embassy-time",
"embedded-alloc",
"embedded-hal 1.0.0",
"embedded-hal-async",
"embedded-io",
"embedded-io-async",
"fugit",
"heapless",
"l3gd20",
"log",
"static_cell",
"zynq7000",
"zynq7000-embassy",
"zynq7000-hal",
"zynq7000-rt",
]
[[package]]
name = "zynq-mmu"
version = "0.1.0"
dependencies = [
"cortex-ar 0.2.0",
"thiserror",
]
[[package]]
name = "zynq7000"
version = "0.1.0"
dependencies = [
"approx",
"arbitrary-int",
"bitbybit",
"derive-mmio 0.4.0 (git+https://github.com/us-irs/derive-mmio?branch=main)",
"once_cell",
"rustversion",
"static_assertions",
"thiserror",
]
[[package]]
name = "zynq7000-embassy"
version = "0.1.0"
dependencies = [
"critical-section",
"embassy-time-driver",
"embassy-time-queue-utils",
"once_cell",
"zynq7000-hal",
]
[[package]]
name = "zynq7000-hal"
version = "0.1.0"
dependencies = [
"approx",
"arbitrary-int",
"bitbybit",
"cortex-ar 0.2.0",
"critical-section",
"delegate",
"embassy-sync",
"embedded-hal 1.0.0",
"embedded-hal-async",
"embedded-hal-nb",
"embedded-io",
"embedded-io-async",
"fugit",
"heapless",
"libm",
"log",
"nb 1.1.0",
"num_enum",
"paste",
"raw-slicee",
"ringbuf",
"static_cell",
"thiserror",
"zynq-mmu",
"zynq7000",
]
[[package]]
name = "zynq7000-rt"
version = "0.1.0"
dependencies = [
"cortex-a-rt",
"cortex-ar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"zynq-mmu",
]

View File

@@ -15,5 +15,7 @@ critical-section = "1"
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
zynq7000-hal = { path = "../zynq7000-hal" }
embassy-time-driver = { git = "https://github.com/embassy-rs/embassy.git", branch = "main", version = "0.2" }
embassy-time-queue-utils = { git = "https://github.com/embassy-rs/embassy.git", branch = "main", version = "0.1" }
# embassy-time-driver = { git = "https://github.com/embassy-rs/embassy.git", branch = "main", version = "0.2" }
# embassy-time-queue-utils = { git = "https://github.com/embassy-rs/embassy.git", branch = "main", version = "0.1" }
embassy-time-driver = { path = "../../../Rust/embassy/embassy-time-driver", version = "0.2" }
embassy-time-queue-utils = { path = "../../../Rust/embassy/embassy-time-queue-utils", version = "0.1" }

View File

@@ -11,9 +11,12 @@ keywords = ["no-std", "hal", "amd", "zynq7000", "xilinx", "bare-metal"]
categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
cortex-ar = "0.2"
# cortex-ar = "0.2"
cortex-ar = { version = "0.2", path = "../../../Rust/cortex-ar/cortex-ar" }
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 }
@@ -32,6 +35,8 @@ critical-section = "1"
libm = "0.2"
log = "0.4"
embassy-sync = "0.6"
embassy-net-driver = { path = "../../../Rust/embassy/embassy-net-driver", version = "0.2" }
vcell = "0.1"
raw-slicee = "0.1"
embedded-io-async = "0.6"

95
zynq7000-hal/src/cache.rs Normal file
View File

@@ -0,0 +1,95 @@
use cortex_ar::{
asm::dsb,
cache::{
clean_and_invalidate_data_cache_line_to_poc, clean_data_cache_line_to_poc,
invalidate_data_cache_line_to_poc,
},
};
use zynq7000::l2_cache::{L2Cache, MmioL2Cache};
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[error("alignment error, addresses and lengths must be aligned to 32 byte cache line length")]
pub struct AlignmentError;
pub fn clean_and_invalidate_l2c_line(l2c: &mut MmioL2Cache<'static>, addr: u32) {
l2c.write_clean_by_pa(addr);
l2c.write_invalidate_by_pa(addr);
}
/// Invalidate an address range.
///
/// This function invalidates both the L1 and L2 cache. The L2C must be enabled and set up
/// correctly for this function to work correctly.
///
/// The provided address and the range to invalidate must both be aligned to the 32 byte cache line
/// length.
pub fn invalidate_data_cache_range(addr: u32, len: usize) -> Result<(), AlignmentError> {
if addr % 32 != 0 || len % 32 != 0 {
return Err(AlignmentError);
}
let mut l2c = unsafe { L2Cache::new_mmio_fixed() };
let mut current_addr = addr;
// Invalidate outer caches lines first, see chapter 3.3.10 of the L2C technical reference manual.
while current_addr < addr + len as u32 {
l2c.write_clean_by_pa(current_addr);
current_addr += 32;
}
while l2c.read_cache_sync().busy() {}
// Invalidate inner cache lines.
current_addr = addr;
while current_addr < addr + len as u32 {
invalidate_data_cache_line_to_poc(addr);
current_addr += 32;
}
// Synchronize the cache maintenance.
dsb();
Ok(())
}
/// Clean and then invalidate an address range.
///
/// This is commonly also called cache flushing. This function cleans and invalidates both L1
/// and L2 cache. The L2C must be enabled and set up correctly for this function to work correctly.
///
/// Both the address and length to clean and invalidate must be a multiple of the 32 byte cache
/// line.
pub fn clean_and_invalidate_data_cache_range(addr: u32, len: usize) -> Result<(), AlignmentError> {
if addr % 32 != 0 || len % 32 != 0 {
return Err(AlignmentError);
}
// For details on the following section, see chapter 3.3.10 of the L2C technical reference
// manual.
// Clean inner cache lines first.
let mut current_addr = addr;
while current_addr < addr + len as u32 {
clean_data_cache_line_to_poc(current_addr);
current_addr += 32;
}
dsb();
// Clean and invalidates outer cache.
let mut l2c = unsafe { L2Cache::new_mmio_fixed() };
current_addr = addr;
while current_addr < addr + len as u32 {
// ARM errate 588369 specifies that clean and invalidate need to be separate, but the
// current revision of the L2C on the Zynq7000 seems to be revision 8 (r3p2), and the
// errata was fixed in r2p0. Both Xilinx and zynq-rs use the clean and invalidate operation,
// so it should be fine. Considering the debug control in Xilinx code which disable
// linefills and write-backs, zynq-rs does not appear to do that and it should not be
// necessary.. I think this was related to the errata.
l2c.write_clean_invalidate_by_pa(current_addr);
current_addr += 32;
}
while l2c.read_cache_sync().busy() {}
// Now clean and invalidate inner cache.
current_addr = addr;
while current_addr < addr + len as u32 {
clean_and_invalidate_data_cache_line_to_poc(current_addr);
current_addr += 32;
}
dsb();
Ok(())
}

View File

@@ -0,0 +1,336 @@
use core::sync::atomic::AtomicBool;
use crate::cache::{clean_and_invalidate_data_cache_range, invalidate_data_cache_range};
use crate::eth::AlignedBuffer;
use arbitrary_int::u14;
use embassy_sync::waitqueue::AtomicWaker;
use zynq7000::eth::{InterruptStatus, RxStatus, TxStatus};
pub use super::rx_descr;
pub use super::tx_descr;
static TX_WAKER: AtomicWaker = AtomicWaker::new();
static RX_WAKER: AtomicWaker = AtomicWaker::new();
static LINK_WAKER: AtomicWaker = AtomicWaker::new();
static LINK_STATE: AtomicBool = AtomicBool::new(false);
#[inline]
pub fn link_state() -> embassy_net_driver::LinkState {
match LINK_STATE.load(core::sync::atomic::Ordering::Relaxed) {
true => embassy_net_driver::LinkState::Up,
false => embassy_net_driver::LinkState::Down,
}
}
pub fn update_link_state(new_state: embassy_net_driver::LinkState) {
let new_value = match new_state {
embassy_net_driver::LinkState::Up => true,
embassy_net_driver::LinkState::Down => false,
};
if LINK_STATE.swap(new_value, core::sync::atomic::Ordering::Relaxed) != new_value {
LINK_WAKER.wake();
}
}
/// Anomalies which are not necesarily errors, but might indicate an issue.
#[derive(Debug, Clone, Copy, Default)]
pub struct EthAnomalies {
/// According to the TMR, p.551, this condition implies a packet is dropped because the
/// packet buffer is full. It occurs occasionally when the controller is unable to process
/// the packets if they arrive very fast. No special action for error recovery needs to
/// be taken, but we still report it.
rx_overrun: bool,
/// Possibly indicator for high traffic, not enough descriptors available or not handled
/// quick enough.
rx_descr_read_when_used: bool,
/// This should really never happen because the driver will check whether there is actually
/// space available.
tx_descr_read_when_used: bool,
}
/// Possibly critical errors.
#[derive(Debug, Clone, Copy, Default)]
pub struct EthErrors {
/// The TMR recommends re-initializing the controller and the buffer descriptors for
/// receive and transmit paths.
hresp_error: bool,
/// The TMR recommends disabling the ethernet transmitter, re-initializing the buffer
/// descriptors on the transmit side and re-enabling the transmitter.
tx_frame_corruption_ahb_error: bool,
/// Only set in Gigabit mode, for 10/100 mode, late collision and collisions are treated
/// the same.
tx_late_collision: bool,
/// In 10/100 mode, this is set when the retry limit was reached.
///
/// According to the TMR, p.551, this implies there are a series of collisions for which
/// an Ethernet frame could not be sent out even with a number of retries in half-duplex mode.
/// No drastic measures need to be taken, but this could also be an indicator for a duplex
/// missmatch.
tx_retry_limit_reached: bool,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct InterruptResult {
frame_received: bool,
frame_sent: bool,
/// These are anomalies.
anomalies: EthAnomalies,
/// These are full errors.
errors: EthErrors,
}
impl InterruptResult {
#[inline]
pub fn has_errors(&self) -> bool {
self.errors.hresp_error
|| self.errors.tx_frame_corruption_ahb_error
|| self.errors.tx_late_collision
|| self.errors.tx_retry_limit_reached
}
#[inline]
pub fn has_anomalies(&self) -> bool {
self.anomalies.rx_overrun
|| self.anomalies.rx_descr_read_when_used
|| self.anomalies.tx_descr_read_when_used
}
}
pub fn on_interrupt(eth_id: super::EthernetId) -> InterruptResult {
let mut eth_regs = unsafe { eth_id.steal_regs() };
let status = eth_regs.read_interrupt_status();
let mut clear = InterruptStatus::new_with_raw_value(0);
let mut tx_status_clear = TxStatus::new_with_raw_value(0);
let mut rx_status_clear = RxStatus::new_with_raw_value(0);
let mut result = InterruptResult::default();
if status.frame_received() {
RX_WAKER.wake();
clear.set_frame_received(true);
}
if status.frame_sent() {
TX_WAKER.wake();
tx_status_clear.set_complete(true);
clear.set_frame_sent(true);
}
if status.hresp_not_ok() {
result.errors.hresp_error = true;
clear.set_hresp_not_ok(true);
tx_status_clear.set_hresp_not_ok(true);
rx_status_clear.set_hresp_not_ok(true);
}
if status.tx_retry_limit_reached_or_late_collision() {
let tx_status = eth_regs.read_tx_status();
if tx_status.late_collision() {
result.errors.tx_late_collision = true;
tx_status_clear.set_late_collision(true);
} else {
result.errors.tx_retry_limit_reached = true;
tx_status_clear.set_retry_limit_reached(true);
}
// Clear this in any case.
tx_status_clear.set_collision(true);
clear.set_tx_retry_limit_reached_or_late_collision(true);
}
if status.tx_frame_corruption_ahb_error() {
result.errors.tx_frame_corruption_ahb_error = true;
// The interrupt status bit is cleared on a read.
tx_status_clear.set_frame_corruption_ahb_error(true);
}
if status.rx_descr_read_when_used() {
result.anomalies.rx_descr_read_when_used = true;
// I am guessing that those are related.
rx_status_clear.set_buf_not_available(true);
clear.set_rx_descr_read_when_used(true);
}
if status.rx_overrun() {
result.anomalies.rx_overrun = true;
rx_status_clear.set_overrun(true);
clear.set_rx_overrun(true);
}
eth_regs.write_interrupt_status(clear);
eth_regs.write_tx_status(tx_status_clear);
eth_regs.write_rx_status(rx_status_clear);
result
}
pub struct DescriptorsAndBuffers {
pub rx_descr: rx_descr::DescriptorList<'static>,
pub rx_bufs: &'static mut [super::AlignedBuffer],
pub tx_descr: tx_descr::DescriptorList<'static>,
pub tx_bufs: &'static mut [super::AlignedBuffer],
}
impl DescriptorsAndBuffers {
pub fn new(
rx_descr: rx_descr::DescriptorList<'static>,
rx_bufs: &'static mut [super::AlignedBuffer],
tx_descr: tx_descr::DescriptorList<'static>,
tx_bufs: &'static mut [super::AlignedBuffer],
) -> Option<Self> {
if rx_descr.len() != rx_bufs.len() || tx_descr.len() != tx_bufs.len() {
return None;
}
Some(Self {
rx_descr,
rx_bufs,
tx_descr,
tx_bufs,
})
}
#[inline]
pub fn tx_burst_len(&self) -> usize {
self.tx_descr.len()
}
}
pub struct Driver {
pub burst_size: usize,
mac_addr: [u8; 6],
bufs: DescriptorsAndBuffers,
}
impl Driver {
pub fn new(_eth: &super::Ethernet, mac_addr: [u8; 6], bufs: DescriptorsAndBuffers) -> Self {
Self {
burst_size: bufs.tx_burst_len(),
mac_addr,
bufs,
}
}
}
pub struct EmbassyNetRxToken<'a> {
descr_list: &'a mut rx_descr::DescriptorList<'static>,
slot_index: usize,
rx_buf: &'a mut super::AlignedBuffer,
rx_size: usize,
}
impl embassy_net_driver::RxToken for EmbassyNetRxToken<'_> {
fn consume<R, F>(self, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
// The DMA will write the received frame into DDR. The L1 and L2 cache lines for the
// particular reception address need to be invalidated, to avoid fetching stale data from
// the cache instead of the DDR.
invalidate_data_cache_range(
self.rx_buf.0.as_ptr() as u32,
core::mem::size_of::<AlignedBuffer>(),
)
.expect("RX buffer or buffer size not aligned to cache line size");
let result = f(&mut self.rx_buf.0[0..self.rx_size]);
self.descr_list.clear_slot(self.slot_index);
result
}
}
pub struct EmbassyNetTxToken<'a> {
descr_list: &'a mut tx_descr::DescriptorList<'static>,
tx_bufs: &'a mut [super::AlignedBuffer],
}
impl embassy_net_driver::TxToken for EmbassyNetTxToken<'_> {
fn consume<R, F>(self, len: usize, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
assert!(len <= super::MTU, "packet length exceeds MTU");
// In the transmit call, it was checked that the buffer queue actually is not full.
let tx_idx = self.descr_list.current_tx_idx();
let buffer = self.tx_bufs.get_mut(tx_idx).unwrap();
let result = f(&mut buffer.0);
let addr = buffer.0.as_ptr() as u32;
// DMA accesses the DDR memory directly, so we need to flush everything that might
// still be in the L1 or L2 cache to the DDR.
clean_and_invalidate_data_cache_range(addr, core::mem::size_of::<AlignedBuffer>())
.expect("TX buffer or buffer size not aligned to cache line size");
self.descr_list
.prepare_transfer_unchecked(addr, u14::new(len as u16), true, false);
result
}
}
impl embassy_net_driver::Driver for Driver {
type RxToken<'a>
= EmbassyNetRxToken<'a>
where
Self: 'a;
type TxToken<'a>
= EmbassyNetTxToken<'a>
where
Self: 'a;
fn receive(
&mut self,
cx: &mut core::task::Context,
) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
RX_WAKER.register(cx.waker());
if self.bufs.tx_descr.full() {
TX_WAKER.register(cx.waker());
return None;
}
match self.bufs.rx_descr.scan_and_handle_next_received_frame() {
// Nothing to do.
rx_descr::FrameScanResult::NoFrames => None,
rx_descr::FrameScanResult::SingleFrame { index, size } => Some((
EmbassyNetRxToken {
descr_list: &mut self.bufs.rx_descr,
slot_index: index,
rx_buf: self.bufs.rx_bufs.get_mut(index).unwrap(),
rx_size: size,
},
EmbassyNetTxToken {
descr_list: &mut self.bufs.tx_descr,
tx_bufs: self.bufs.tx_bufs,
},
)),
rx_descr::FrameScanResult::MultiSlotFrame {
first_slot_index: _,
last_slot_index: _,
} => {
// We can not really handle multi-slot frames.. this should never happen.
None
}
rx_descr::FrameScanResult::BrokenFragments {
first_slot_index: _,
last_slot_index: _,
} => None,
}
}
fn transmit(&mut self, cx: &mut core::task::Context) -> Option<Self::TxToken<'_>> {
TX_WAKER.register(cx.waker());
if self.bufs.tx_descr.full() {
return None;
}
Some(EmbassyNetTxToken {
descr_list: &mut self.bufs.tx_descr,
tx_bufs: self.bufs.tx_bufs,
})
}
fn link_state(&mut self, cx: &mut core::task::Context) -> embassy_net_driver::LinkState {
LINK_WAKER.register(cx.waker());
link_state()
}
fn capabilities(&self) -> embassy_net_driver::Capabilities {
let mut capabilities = embassy_net_driver::Capabilities::default();
capabilities.max_transmission_unit = super::MTU;
capabilities.max_burst_size = Some(self.burst_size);
capabilities.checksum.ipv4 = embassy_net_driver::Checksum::Both;
capabilities.checksum.udp = embassy_net_driver::Checksum::Both;
capabilities.checksum.tcp = embassy_net_driver::Checksum::Both;
capabilities.checksum.icmpv4 = embassy_net_driver::Checksum::None;
capabilities.checksum.icmpv6 = embassy_net_driver::Checksum::None;
capabilities
}
fn hardware_address(&self) -> embassy_net_driver::HardwareAddress {
embassy_net_driver::HardwareAddress::Ethernet(self.mac_addr)
}
}

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

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

View File

@@ -0,0 +1,77 @@
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,
}
}
/// Steals the MDIO handle from the given Ethernet low-level interface.
///
/// # Safety
///
/// Circumvents ownership and safety guarantees of the HAL.
pub unsafe fn clone(&self) -> Self {
Self {
regs: unsafe { self.regs.clone() },
clause22: self.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() {}
}
}

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

@@ -0,0 +1,522 @@
use arbitrary_int::{u2, u3};
pub use zynq7000::eth::MdcClkDiv;
use zynq7000::eth::{
BurstLength, DmaRxBufSize, GEM_0_BASE_ADDR, GEM_1_BASE_ADDR, InterruptControl, MmioEthernet,
SpeedMode,
};
pub use ll::{ClkConfig, EthernetLowLevel, Speed};
pub mod embassy_net;
pub mod ll;
pub mod mdio;
pub mod rx_descr;
pub mod tx_descr;
pub const MTU: usize = 1536;
pub const MAX_MDC_SPEED: Hertz = Hertz::from_raw(2_500_000);
#[repr(C, align(32))]
#[derive(Debug, Clone, Copy)]
pub struct AlignedBuffer(pub [u8; MTU]);
#[cfg(not(feature = "7z010-7z007s-clg225"))]
use crate::gpio::mio::{
Mio16, Mio17, Mio18, Mio19, Mio20, Mio21, Mio22, Mio23, Mio24, Mio25, Mio26, Mio27,
};
use crate::{
clocks::ArmClocks,
gpio::{
IoPeriphPin,
mio::{
Mio28, Mio29, Mio30, Mio31, Mio32, Mio33, Mio34, Mio35, Mio36, Mio37, Mio38, Mio39,
Mio52, Mio53, MioPinMarker, MuxConf, Pin,
},
},
time::Hertz,
};
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;
}
/// Calculate the CPU 1x clock divisor required to achieve a clock speed which is below
/// 2.5 MHz, as specified by the 802.3 standard.
pub fn calculate_mdc_clk_div(arm_clks: &ArmClocks) -> Option<MdcClkDiv> {
let div = arm_clks.cpu_1x_clk().raw().div_ceil(MAX_MDC_SPEED.raw());
match div {
0..8 => Some(MdcClkDiv::Div8),
8..16 => Some(MdcClkDiv::Div16),
16..32 => Some(MdcClkDiv::Div32),
32..64 => Some(MdcClkDiv::Div64),
64..128 => Some(MdcClkDiv::Div128),
128..224 => Some(MdcClkDiv::Div224),
// MDC clock divisor is too high for the maximum speed.
// This is not a valid configuration.
_ => None,
}
}
#[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,
}
const IRQ_CONTROL: InterruptControl = InterruptControl::builder()
.with_tsu_sec_incr(false)
.with_partner_pg_rx(false)
.with_auto_negotiation_complete(false)
.with_external_interrupt(false)
.with_pause_transmitted(false)
.with_pause_time_zero(false)
.with_pause_with_non_zero_quantum(false)
.with_hresp_not_ok(true)
.with_rx_overrun(true)
.with_link_changed(false)
.with_frame_sent(true)
.with_tx_frame_corruption_ahb_error(true)
.with_tx_retry_limit_reached_or_late_collision(true)
.with_tx_descr_read_when_used(true)
.with_rx_descr_read_when_used(true)
.with_frame_received(true)
.with_mgmt_frame_sent(false)
.build();
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);
let mut eth = Ethernet { ll, mdio };
eth.set_rx_buf_descriptor_base_address(0);
eth.set_tx_buf_descriptor_base_address(0);
eth
}
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);
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 enable_interrupts(&mut self) {
self.ll.regs.write_interrupt_enable(IRQ_CONTROL);
}
#[inline]
pub fn disable_interrupts(&mut self) {
self.ll.regs.write_interrupt_disable(IRQ_CONTROL);
}
#[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 mdio(&mut self) -> &mdio::Mdio {
&self.mdio
}
pub fn mdio_mut(&mut self) -> &mut mdio::Mdio {
&mut self.mdio
}
#[inline]
pub fn regs_mut(&mut self) -> &mut MmioEthernet<'static> {
&mut self.ll.regs
}
#[inline]
pub fn set_rx_buf_descriptor_base_address(&mut self, addr: u32) {
self.ll.set_rx_buf_descriptor_base_address(addr);
}
#[inline]
pub fn set_tx_buf_descriptor_base_address(&mut self, addr: u32) {
self.ll.set_rx_buf_descriptor_base_address(addr);
}
}
mod shared {
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum Ownership {
Hardware = 0,
Software = 1,
}
}

View File

@@ -0,0 +1,307 @@
//! RX buffer descriptor module.
use crate::eth::AlignedBuffer;
pub use super::shared::Ownership;
use arbitrary_int::{Number, u2, u3, u13, u30};
use vcell::VolatileCell;
/// 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, align(8))]
pub struct Descriptor {
/// The first word of the descriptor.
pub word_0: VolatileCell<Word0>,
/// The second word of the descriptor.
pub status: VolatileCell<StatusWord>,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug, PartialEq, Eq)]
pub struct Word0 {
/// The full reception address with the last two bits cleared.
#[bits(2..=31, rw)]
addr_upper_30_bits: u30,
#[bit(1, rw)]
wrap: bool,
#[bit(0, rw)]
ownership: Ownership,
}
/// This control word contains the status bits.
///
/// If the end of frame bit (15) is not set, the only valid status information is the start of
/// frame bit.
#[bitbybit::bitfield(u32)]
#[derive(Debug, PartialEq, Eq)]
pub struct StatusWord {
#[bit(31, r)]
broadcast_detect: bool,
#[bit(30, r)]
multicast_hash: bool,
#[bit(29, r)]
unicast_hash: bool,
#[bit(27, r)]
specific_addr_match: bool,
/// Specifies which of the 4 specific address registers was matched.
#[bits(25..=26, r)]
specific_addr_match_info: u2,
#[bit(24, r)]
type_id_match_or_snap_info: bool,
#[bits(22..=23, r)]
type_id_match_info_or_chksum_status: u2,
#[bit(21, r)]
vlan_tag_detected: bool,
#[bit(20, r)]
priority_tag_detected: bool,
#[bits(17..=19, r)]
vlan_prio: u3,
#[bit(16, r)]
cfi_bit: bool,
/// If this bit is not set, the only other valid status bit is the start of frame bit.
#[bit(15, r)]
end_of_frame: bool,
#[bit(14, r)]
start_of_frame: bool,
/// Relevant when FCS errors are not ignored.
/// 0: Frame has good FCS, 1: Frame has bad FCS, but was copied to memory as the ignore FCS
/// functionality was enabled.
#[bit(13, r)]
fcs_status: bool,
#[bits(0..=12, r)]
rx_len: u13,
}
impl Descriptor {
#[inline]
pub const fn new() -> Self {
Self {
word_0: VolatileCell::new(Word0::new_with_raw_value(0)),
status: VolatileCell::new(StatusWord::new_with_raw_value(0)),
}
}
#[inline]
pub fn ownership(&self) -> Ownership {
self.word_0.get().ownership()
}
#[inline]
pub fn set_word_0(&mut self, word: Word0) {
self.word_0.set(word);
}
#[inline]
pub fn set_word_1(&mut self, word: StatusWord) {
self.status.set(word);
}
#[inline]
pub fn word_0(&mut self) -> Word0 {
self.word_0.get()
}
#[inline]
pub fn status_word(&mut self) -> StatusWord {
self.status.get()
}
}
impl Default for Descriptor {
#[inline]
fn default() -> Self {
Self::new()
}
}
pub enum FrameScanResult {
NoFrames,
SingleFrame {
index: usize,
size: usize,
},
MultiSlotFrame {
first_slot_index: usize,
last_slot_index: usize,
},
BrokenFragments {
first_slot_index: usize,
last_slot_index: usize,
},
}
pub struct DescriptorList<'a> {
list: &'a mut [Descriptor],
current_idx: usize,
}
impl<'a> DescriptorList<'a> {
#[inline]
pub fn new(descr_list: &'a mut [Descriptor]) -> Option<Self> {
if descr_list.is_empty() {
return None;
}
Some(Self {
list: descr_list,
current_idx: 0,
})
}
/// Unsafely clone this descriptor list. See safety notes.
//
/// # Safety
//
/// You must not use both the original and the clone at the same time.
pub unsafe fn clone_unchecked(&mut self) -> Self {
Self {
list: unsafe {
core::slice::from_raw_parts_mut(self.list.as_mut().as_mut_ptr(), self.list.len())
},
current_idx: self.current_idx,
}
}
}
impl DescriptorList<'_> {
#[allow(clippy::len_without_is_empty)]
#[inline]
pub fn len(&self) -> usize {
self.list.len()
}
#[inline]
pub fn base_ptr(&self) -> *const Descriptor {
self.list.as_ptr()
}
#[inline]
pub fn base_addr(&self) -> u32 {
self.base_ptr() as u32
}
/// Resets the descriptor list. This retains the previous configured reception
/// addresses, but sets the ownership for all descriptors to the hardware again.
pub fn reset(&mut self) {
self.current_idx = 0;
let list_len = self.list.len();
let mut word_0;
for desc in self.list.iter_mut().take(list_len - 1) {
word_0 = desc.word_0();
word_0.set_ownership(Ownership::Hardware);
word_0.set_wrap(false);
desc.set_word_0(word_0);
}
let last = self.list.last_mut().unwrap();
word_0 = last.word_0();
word_0.set_ownership(Ownership::Hardware);
word_0.set_wrap(true);
last.set_word_0(word_0);
}
pub fn init_with_aligned_bufs(&mut self, aligned_bufs: &[AlignedBuffer]) {
self.current_idx = 0;
let list_len = self.list.len();
for (desc, buf) in self.list.iter_mut().take(list_len - 1).zip(aligned_bufs) {
desc.set_word_0(
Word0::builder()
.with_addr_upper_30_bits(u30::new(buf.0.as_ptr() as u32 >> 2))
.with_wrap(false)
.with_ownership(Ownership::Hardware)
.build(),
);
}
self.list.last_mut().unwrap().set_word_0(
Word0::builder()
.with_addr_upper_30_bits(u30::new(
aligned_bufs.last().unwrap().0.as_ptr() as u32 >> 2,
))
.with_wrap(true)
.with_ownership(Ownership::Hardware)
.build(),
);
}
pub fn scan_and_handle_next_received_frame(&mut self) -> FrameScanResult {
let mut handled_slots = 0;
let mut current_idx = self.current_idx;
let mut start_found = false;
let mut start_idx = 0;
while handled_slots < self.list.len() {
let word_0 = self.list[current_idx].word_0.get();
if word_0.ownership() == Ownership::Hardware {
// The descriptor is not owned by the hardware, so it is not ready for processing.
current_idx = (current_idx + 1) % self.list.len();
handled_slots += 1;
continue;
}
let status = self.list[current_idx].status_word();
match (status.start_of_frame(), status.end_of_frame()) {
(true, true) => {
self.current_idx = (current_idx + 1) % self.list.len();
return FrameScanResult::SingleFrame {
index: current_idx,
size: status.rx_len().as_usize(),
};
}
(true, false) => {
// Consecutive start frame.. Which means something went wrong, and we need
// to discard all the slots until the second start frame slot.
if start_found {
self.clear_slots(start_idx, current_idx);
self.current_idx = (current_idx + 1) % self.list.len();
return FrameScanResult::BrokenFragments {
first_slot_index: start_idx,
last_slot_index: current_idx,
};
} else {
start_found = true;
start_idx = current_idx;
}
}
(false, true) => {
// Detected frame spanning multiple buffers.. which should really not happen
// if we only use frames with a certain MTU, but we will handle it.
if start_found {
self.current_idx = (current_idx + 1) % self.list.len();
return FrameScanResult::MultiSlotFrame {
first_slot_index: start_idx,
last_slot_index: current_idx,
};
}
}
(false, false) => {
// If a slot is neither the start nor the end of frame.
}
}
current_idx = (current_idx + 1) % self.list.len();
handled_slots += 1;
}
FrameScanResult::NoFrames
}
/// Clear a slot range by setting the ownership bit back to [Ownership::Hardware].
pub fn clear_slots(&mut self, start: usize, end: usize) {
if start >= self.list.len() || end >= self.list.len() {
return;
}
let mut current_idx = start;
while current_idx != end {
let mut word_0 = self.list[current_idx].word_0.get();
word_0.set_ownership(Ownership::Hardware);
self.list[current_idx].set_word_0(word_0);
current_idx = (current_idx + 1) % self.list.len();
}
}
pub fn clear_slot(&mut self, index: usize) {
if index >= self.list.len() {
return;
}
let mut word_0 = self.list[index].word_0.get();
word_0.set_ownership(Ownership::Hardware);
self.list[index].set_word_0(word_0);
}
}

View File

@@ -0,0 +1,304 @@
use arbitrary_int::u14;
pub use super::shared::Ownership;
use vcell::VolatileCell;
/// 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, align(8))]
pub struct Descriptor {
/// The first word of the descriptor which is the byte address of the buffer.
pub word0: VolatileCell<u32>,
/// The second word of the descriptor.
pub word1: VolatileCell<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 {
/// The ownership bit must be set to [Ownership::Hardware] if a frame should be transmitted.
///
/// The controller will set this to [Ownership::Software] once the frame has been transmitted.
#[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 const fn new() -> Self {
Self {
word0: VolatileCell::new(0),
word1: VolatileCell::new(Word1::new_with_raw_value(0)),
}
}
/*
#[inline]
pub fn set_ownership(&mut self, ownership: Ownership) {
self.word1.set_ownership(ownership);
}
*/
#[inline]
pub fn ownership(&self) -> Ownership {
self.word1.get().ownership()
}
#[inline]
pub fn set_addr(&self, addr: u32) {
self.word0.set(addr)
}
#[inline]
pub fn read_word_1(&self) -> Word1 {
self.word1.get()
}
#[inline]
pub fn set_word_1(&self, word1: Word1) {
self.word1.set(word1);
}
/// Set the information for a transfer.
pub fn setup_tx_transfer_unchecked(
&mut self,
addr: u32,
tx_len: u14,
last_buffer: bool,
no_crc_generation: bool,
) {
self.set_addr(addr);
// Perform the read-modify-write sequence manually to ensure a minimum of reads/writes
// for the uncached memory.
let mut word1 = self.word1.get();
word1.set_tx_len(tx_len);
word1.set_last_buffer(last_buffer);
word1.set_no_crc_generation(no_crc_generation);
word1.set_ownership(Ownership::Hardware);
self.word1.set(word1);
}
}
impl Default for Descriptor {
#[inline]
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[error("tx error: {self:?}")]
pub struct TxError {
retry_limit_exceeded: bool,
late_collisions: bool,
ahb_error: bool,
checksum_generation: Option<TransmitChecksumGenerationStatus>,
}
impl TxError {
pub fn from_word1(word1: &Word1) -> Self {
Self {
retry_limit_exceeded: word1.retry_limit_exceeded(),
late_collisions: word1.late_collision(),
ahb_error: word1.transmit_frame_corruption_ahb_error(),
checksum_generation: if word1.checksum_status()
== TransmitChecksumGenerationStatus::NoError
{
None
} else {
Some(word1.checksum_status())
},
}
}
}
pub enum IncrementResult {
Busy,
Ok,
}
pub enum BusyHandlingResult {
/// Handled a descriptor slot where a TX error has occured.
TxError(TxError),
/// Handled one busy descriptor slot. More calls to this function are required to handle
/// all busy slots.
SlotHandled,
/// The busy index has caught up to the transmission index (all frames have been sent), or
/// the busy index is at an active transmission index.
Complete,
}
pub struct DescriptorList<'a> {
list: &'a mut [Descriptor],
/// The head index is used to handle the transmission of new frames.
tx_idx: usize,
/// The tail index is used to track the progress of active transmissions.
busy_idx: usize,
}
impl core::fmt::Debug for DescriptorList<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("DescriptorList")
.field("tx_idx", &self.tx_idx)
.field("busy_idx", &self.busy_idx)
.field("list_len", &self.list.len())
.finish()
}
}
impl<'a> DescriptorList<'a> {
#[inline]
pub fn new(descr_list: &'a mut [Descriptor]) -> Self {
Self {
list: descr_list,
tx_idx: 0,
busy_idx: 0,
}
}
}
impl DescriptorList<'_> {
#[allow(clippy::len_without_is_empty)]
#[inline]
pub fn len(&self) -> usize {
self.list.len()
}
#[inline]
pub fn base_ptr(&self) -> *const Descriptor {
self.list.as_ptr()
}
#[inline]
pub fn base_addr(&self) -> u32 {
self.base_ptr() as u32
}
pub fn init_or_reset(&mut self) {
self.tx_idx = 0;
self.busy_idx = 0;
let mut reset_val = Word1::builder()
.with_ownership(Ownership::Software)
.with_wrap(false)
.with_retry_limit_exceeded(false)
.with_transmit_frame_corruption_ahb_error(false)
.with_late_collision(false)
.with_checksum_status(TransmitChecksumGenerationStatus::NoError)
.with_no_crc_generation(false)
.with_last_buffer(false)
.with_tx_len(u14::new(0))
.build();
let list_len = self.list.len();
for desc in self.list.iter_mut().take(list_len - 1) {
desc.set_word_1(reset_val);
}
reset_val.set_wrap(true);
self.list.last_mut().unwrap().set_word_1(reset_val);
}
#[inline]
pub fn increment_tx_idx(&mut self) {
self.tx_idx = (self.tx_idx + 1) % self.list.len();
}
/// Increment the busy index without checking whether it has become
/// larger than the transmission index.
#[inline]
pub fn increment_busy_idx_unchecked(&mut self) {
self.busy_idx = (self.busy_idx + 1) % self.list.len();
}
#[inline]
pub fn full(&self) -> bool {
self.list[self.tx_idx].ownership() == Ownership::Hardware
}
/// Check whether a tranfer has completed and handles the descriptor accordingly.
///
/// This should be called continuosly when a TX error or a TX completion interrupt has
/// occured until it returns [BusyHandlingResult::Complete].
pub fn check_and_handle_completed_transfer(&mut self) -> BusyHandlingResult {
let word1 = self.list[self.busy_idx].word1.get();
if word1.ownership() == Ownership::Hardware || self.busy_idx == self.tx_idx {
return BusyHandlingResult::Complete;
}
if word1.transmit_frame_corruption_ahb_error()
|| word1.retry_limit_exceeded()
|| word1.checksum_status() != TransmitChecksumGenerationStatus::NoError
|| word1.late_collision()
{
return BusyHandlingResult::TxError(TxError::from_word1(&word1));
}
self.list[self.busy_idx].word1.set(
Word1::builder()
.with_ownership(Ownership::Software)
.with_wrap(word1.wrap())
.with_retry_limit_exceeded(false)
.with_transmit_frame_corruption_ahb_error(false)
.with_late_collision(false)
.with_checksum_status(TransmitChecksumGenerationStatus::NoError)
.with_no_crc_generation(false)
.with_last_buffer(false)
.with_tx_len(u14::new(0))
.build(),
);
self.increment_busy_idx_unchecked();
BusyHandlingResult::SlotHandled
}
#[inline]
pub fn current_tx_idx(&self) -> usize {
self.tx_idx
}
/// Prepare a transfer without checking whether that particular descriptor slot is used by
/// the hardware.
pub fn prepare_transfer_unchecked(
&mut self,
addr: u32,
tx_len: u14,
last_buffer: bool,
no_crc_generation: bool,
) {
self.list[self.tx_idx].setup_tx_transfer_unchecked(
addr,
tx_len,
last_buffer,
no_crc_generation,
);
self.increment_tx_idx();
}
}

View File

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

View File

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

View File

@@ -384,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);
@@ -392,6 +394,43 @@ impl IoPeriphPin {
mux_conf,
}
}
/// Constructor to fully configure an IO peripheral pin with a specific MIO pin configuration.
pub fn new_with_full_config(
pin: impl MioPinMarker,
config: zynq7000::slcr::mio::Config,
) -> Self {
let mut low_level = LowLevelGpio::new(PinOffset::Mio(pin.offset()));
low_level.set_mio_pin_config(config);
Self {
pin: low_level,
mux_conf: MuxConf::new(
config.l0_sel(),
config.l1_sel(),
config.l2_sel(),
config.l3_sel(),
),
}
}
/// Constructor to fully configure an IO peripheral pin with a specific MIO pin configuration.
pub fn new_with_full_config_and_unlocked_slcr(
pin: impl MioPinMarker,
slcr: &mut zynq7000::slcr::MmioSlcr<'static>,
config: zynq7000::slcr::mio::Config,
) -> Self {
let mut low_level = LowLevelGpio::new(PinOffset::Mio(pin.offset()));
low_level.set_mio_pin_config_with_unlocked_slcr(slcr, config);
Self {
pin: low_level,
mux_conf: MuxConf::new(
config.l0_sel(),
config.l1_sel(),
config.l2_sel(),
config.l3_sel(),
),
}
}
}
impl IoPinProvider for IoPeriphPin {

View File

@@ -12,7 +12,9 @@
use slcr::Slcr;
use zynq7000::slcr::LevelShifterReg;
pub mod cache;
pub mod clocks;
pub mod eth;
pub mod gic;
pub mod gpio;
pub mod gtc;

View File

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

View File

@@ -12,7 +12,10 @@ categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
cortex-a-rt = { version = "0.1", optional = true, features = ["vfp-dp"] }
cortex-ar = "0.2"
# cortex-ar = "0.2"
cortex-ar = { version = "0.2", path = "../../../Rust/cortex-ar/cortex-ar" }
arbitrary-int = "1.3"
zynq-mmu = { path = "../zynq-mmu", version = "0.1.0" }
[features]
default = ["rt"]

View File

@@ -4,6 +4,17 @@ use std::process::Command;
use zynq7000_rt::mmu::ONE_MB;
pub use zynq7000_rt::mmu::segments::*;
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";
let file = File::create(file_path).expect("Failed to create file");
@@ -35,6 +46,7 @@ fn main() {
+ OCM_MAPPED_HIGH,
4096
);
let mut buf_writer = std::io::BufWriter::new(file);
writeln!(
buf_writer,
@@ -63,44 +75,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 +102,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 +112,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 +122,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 +150,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 +160,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 +170,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 +186,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;
}

View File

@@ -4,8 +4,16 @@
//! [provided by Xilinx](https://github.com/Xilinx/embeddedsw/tree/master/lib/bsp/standalone/src/arm/cortexa9/gcc).
#![no_std]
use zynq_mmu::L1TableRef;
pub mod mmu;
#[cfg(feature = "rt")]
mod mmu_table;
#[cfg(feature = "rt")]
pub mod rt;
/// Retrieves a mutable reference to the MMU L1 page table.
pub fn mmu_l1_table_mut() -> L1TableRef<'static> {
// Safety: We retrieve a reference to the MMU page table singleton.
L1TableRef::new(unsafe { &mut *mmu_table::MMU_L1_PAGE_TABLE.get() })
}

View File

@@ -84,17 +84,23 @@ pub mod segments {
}
pub mod section_attrs {
use arbitrary_int::u4;
use cortex_ar::mmu::{
AccessPermissions, CacheableMemoryAttribute, MemoryRegionAttributes, SectionAttributes,
};
pub const DEFAULT_DOMAIN: u4 = u4::new(0b0000);
// DDR is in different domain, but all domains are set as manager domains during run-time
// initialization.
pub const DDR_DOMAIN: u4 = u4::new(0b1111);
pub const DDR: SectionAttributes = SectionAttributes {
non_global: false,
p_bit: false,
shareable: true,
access: AccessPermissions::FullAccess,
// Manager domain
domain: 0b1111,
domain: DDR_DOMAIN,
execute_never: false,
memory_attrs: MemoryRegionAttributes::CacheableMemory {
inner: CacheableMemoryAttribute::WriteBackWriteAlloc,
@@ -107,7 +113,7 @@ pub mod section_attrs {
p_bit: false,
shareable: false,
access: AccessPermissions::FullAccess,
domain: 0b0000,
domain: DEFAULT_DOMAIN,
execute_never: false,
memory_attrs: MemoryRegionAttributes::StronglyOrdered.as_raw(),
};
@@ -116,7 +122,7 @@ pub mod section_attrs {
p_bit: false,
shareable: false,
access: AccessPermissions::FullAccess,
domain: 0b0000,
domain: DEFAULT_DOMAIN,
execute_never: false,
memory_attrs: MemoryRegionAttributes::ShareableDevice.as_raw(),
};
@@ -125,7 +131,7 @@ pub mod section_attrs {
p_bit: false,
shareable: false,
access: AccessPermissions::FullAccess,
domain: 0b0000,
domain: DEFAULT_DOMAIN,
execute_never: false,
memory_attrs: MemoryRegionAttributes::OuterAndInnerWriteBackNoWriteAlloc.as_raw(),
};
@@ -134,7 +140,7 @@ pub mod section_attrs {
p_bit: false,
shareable: false,
access: AccessPermissions::FullAccess,
domain: 0b0000,
domain: DEFAULT_DOMAIN,
execute_never: false,
memory_attrs: MemoryRegionAttributes::OuterAndInnerWriteThroughNoWriteAlloc.as_raw(),
};
@@ -143,7 +149,7 @@ pub mod section_attrs {
p_bit: false,
shareable: false,
access: AccessPermissions::FullAccess,
domain: 0b0000,
domain: DEFAULT_DOMAIN,
execute_never: false,
memory_attrs: MemoryRegionAttributes::CacheableMemory {
inner: CacheableMemoryAttribute::WriteThroughNoWriteAlloc,
@@ -156,18 +162,12 @@ pub mod section_attrs {
p_bit: false,
shareable: false,
access: AccessPermissions::PermissionFault,
domain: 0b0000,
domain: DEFAULT_DOMAIN,
execute_never: false,
memory_attrs: MemoryRegionAttributes::StronglyOrdered.as_raw(),
};
}
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
@@ -178,7 +178,7 @@ pub struct L1Table(pub(crate) [u32; NUM_L1_PAGE_TABLE_ENTRIES]);
#[unsafe(no_mangle)]
#[cfg(feature = "rt")]
unsafe extern "C" fn load_mmu_table() {
let table_base = &crate::mmu_table::MMU_L1_PAGE_TABLE.0 as *const _ as u32;
let table_base = crate::mmu_table::MMU_L1_PAGE_TABLE.get() as u32;
unsafe {
core::arch::asm!(

File diff suppressed because it is too large Load Diff

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

@@ -0,0 +1,601 @@
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,
/// This bit should never be se because the DMA is configured for packet buffer mode.
#[bit(6, rw)]
underrun: bool,
#[bit(5, rw)]
complete: bool,
#[bit(4, rw)]
frame_corruption_ahb_error: bool,
/// Its called "tx_go" inside the Zynq 7000 documentation, but I think this is just a
/// TX active bit.
#[bit(3, r)]
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)]
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, rw)]
frame_sent: bool,
/// Cleared on read.
#[bit(6, r)]
tx_frame_corruption_ahb_error: bool,
#[bit(5, rw)]
tx_retry_limit_reached_or_late_collision: 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, default = 0x00)]
#[derive(Debug)]
pub struct InterruptControl {
#[bit(26, w)]
tsu_sec_incr: bool,
/// Marked N/A in datasheet. Probably because external PHYs are used.
#[bit(17, w)]
partner_pg_rx: bool,
/// Marked N/A in datasheet. Probably because external PHYs are used.
#[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. Probably because external PHYs are used.
#[bit(9, w)]
link_changed: bool,
#[bit(7, w)]
frame_sent: bool,
#[bit(6, w)]
tx_frame_corruption_ahb_error: bool,
#[bit(5, w)]
tx_retry_limit_reached_or_late_collision: 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) }
}
}

View File

@@ -178,6 +178,8 @@ pub struct I2c {
idr: InterruptControl,
}
static_assertions::const_assert_eq!(core::mem::size_of::<I2c>(), 0x2C);
impl I2c {
/// Create a new I2C MMIO instance for I2C0 at address [I2C_0_BASE_ADDR].
///

144
zynq7000/src/l2_cache.rs Normal file
View File

@@ -0,0 +1,144 @@
use arbitrary_int::{u4, u6};
pub const L2C_BASE_ADDR: usize = 0xF8F0_2000;
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct LockdownRegisters {
data: u32,
instruction: u32,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct CacheSync {
#[bit(0, r)]
busy: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct DebugControl {
#[bit(2, rw)]
spniden: bool,
#[bit(1, rw)]
disable_write_back: bool,
#[bit(0, rw)]
disable_cache_linefill: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct CacheId {
#[bits(24..=31, r)]
implementer: u8,
#[bits(10..=15, r)]
cache_id: u6,
#[bits(6..=9, r)]
part_number: u4,
#[bits(0..=5, r)]
rtl_release: u6,
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct L2Cache {
#[mmio(PureRead)]
cache_id: CacheId,
#[mmio(PureRead)]
cache_type: u32,
_reserved: [u32; 0x3E],
control: u32,
aux_control: u32,
tag_ram_control: u32,
data_ram_control: u32,
_reserved2: [u32; 0x3C],
event_counter_control: u32,
event_counter_1_config: u32,
event_counter_0_config: u32,
event_counter_1: u32,
event_counter_0: u32,
interrupt_mask: u32,
#[mmio(PureRead)]
interrupt_mask_status: u32,
#[mmio(PureRead)]
interrupt_raw_status: u32,
#[mmio(Write)]
interrupt_clear: u32,
_reserved3: [u32; 0x143],
cache_sync: CacheSync,
_reserved4: [u32; 0xF],
invalidate_by_pa: u32,
_reserved5: [u32; 0x2],
invalidate_by_way: u32,
_reserved6: [u32; 0xC],
clean_by_pa: u32,
_reserved7: u32,
clean_by_index: u32,
clean_by_way: u32,
_reserved8: [u32; 0xC],
clean_invalidate_by_pa: u32,
_reserved9: u32,
clean_invalidate_by_index: u32,
clean_invalidate_by_way: u32,
_reserved10: [u32; 0x40],
#[mmio(inner)]
lockdown_regs: [LockdownRegisters; 8],
_reserved11: [u32; 0x4],
lockdown_by_line_enable: u32,
unlock_way: u32,
_reserved12: [u32; 0xAA],
addr_filtering_start: u32,
addr_filtering_end: u32,
_reserved13: [u32; 0xCE],
debug_control: DebugControl,
_reserved14: [u32; 0x7],
prefetch_control: u32,
_reserved15: [u32; 0x7],
power_control: u32,
}
static_assertions::const_assert_eq!(core::mem::size_of::<L2Cache>(), 0xF84);
impl L2Cache {
/// Create a new L2C MMIO instance for for L2 Cache at address [I2C_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() -> MmioL2Cache<'static> {
unsafe { Self::new_mmio_at(L2C_BASE_ADDR) }
}
}

View File

@@ -17,10 +17,12 @@ extern crate std;
pub const MPCORE_BASE_ADDR: usize = 0xF8F0_0000;
pub mod eth;
pub mod gic;
pub mod gpio;
pub mod gtc;
pub mod i2c;
pub mod l2_cache;
pub mod mpcore;
pub mod slcr;
pub mod spi;
@@ -48,6 +50,8 @@ pub struct PsPeripherals {
pub slcr: slcr::MmioSlcr<'static>,
pub ttc_0: ttc::MmioTtc<'static>,
pub ttc_1: ttc::MmioTtc<'static>,
pub eth_0: eth::MmioEthernet<'static>,
pub eth_1: eth::MmioEthernet<'static>,
}
impl PsPeripherals {
@@ -81,6 +85,8 @@ impl PsPeripherals {
i2c_1: i2c::I2c::new_mmio_fixed_1(),
ttc_0: ttc::Ttc::new_mmio_fixed_0(),
ttc_1: ttc::Ttc::new_mmio_fixed_1(),
eth_0: eth::Ethernet::new_mmio_fixed_0(),
eth_1: eth::Ethernet::new_mmio_fixed_1(),
}
}
}

View File

@@ -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)]

View File

@@ -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,
@@ -176,14 +194,15 @@ pub struct Slcr {
_gap15: [u32; 0x42],
reserved: u32,
/// Xilinx marks this as reserved but writes to it in their low-level L2 cache configuration.
magic_l2c_register: u32,
_gap16: [u32; 0x38],
_gap18: [u32; 0x09],
#[mmio(inner)]
gpiob: GpiobCtrl,
gpiob: GpiobRegisters,
#[mmio(inner)]
ddriob: DdrIoB,

View File

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