Compare commits

..

5 Commits

Author SHA1 Message Date
muellerr 2267dd0bab it works!
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
2026-01-28 18:26:17 +01:00
muellerr fbf741e589 more experiments
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
2026-01-26 12:00:21 +01:00
muellerr ee41d245c8 issues with RTT
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
2026-01-23 14:42:43 +01:00
muellerr d32e99f6a5 weird stuff happening
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
2026-01-21 10:51:55 +01:00
muellerr 55c815fa5e probe-rs experiments
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
2026-01-21 10:29:41 +01:00
96 changed files with 3173 additions and 4612 deletions
-1
View File
@@ -10,4 +10,3 @@
# running the application. You only need to do this once for unchanged bitstream as long as you
# do not reset the whole board.
# ZYNQ_BITSTREAM = "/home/$user/$project/$sdt_dir/bitstream.bit"
# HW_SERVER_IP = "localhost"
+8 -8
View File
@@ -11,8 +11,8 @@ jobs:
- uses: dtolnay/rust-toolchain@stable
with:
targets: "armv7a-none-eabihf"
- run: just check-dir firmware
- run: just check-dir host
- run: just check firmware
- run: just check host
build:
name: Check build
@@ -24,8 +24,8 @@ jobs:
- uses: dtolnay/rust-toolchain@stable
with:
targets: "armv7a-none-eabihf"
- run: just build-zynq
- run: just build-dir host
- run: just check firmware
- run: just check host
fmt:
name: Check formatting
@@ -37,8 +37,8 @@ jobs:
with:
components: rustfmt
targets: "armv7a-none-eabihf"
- run: just check-fmt-dir firmware
- run: just check-fmt-dir host
- run: just check-fmt firmware
- run: just check-fmt host
docs:
name: Check Documentation Build
@@ -62,9 +62,9 @@ jobs:
with:
components: clippy, rust-src
targets: "armv7a-none-eabihf"
- run: just clippy-dir firmware
- run: just clippy firmware
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- run: just clippy-dir host
- run: just clippy host
+17 -16
View File
@@ -8,47 +8,48 @@ family of SoCs.
This project contains the following crates:
## [Firmware Workspace](./firmware)
## [Firmware Workspace](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/firmware)
This workspace contains libraries and application which can only be run on the target system.
- The [`zynq7000-rt`](./firmware/zynq7000-rt)
- The [`zynq7000-rt`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/firmware/zynq7000-rt)
run-time crate containing basic low-level startup code necessary to boot a Rust app on the
Zynq7000.
- The [`zynq7000`](./firmware/zynq7000) PAC crate containing basic low-level register access API.
- The [`zynq7000-mmu`](./firmware/zynq7000-mmu)
- The [`zynq7000`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/firmware/zynq7000) PAC
crate containing basic low-level register definitions.
- The [`zynq7000-mmu`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/firmware/zynq7000-hal)
crate containing common MMU abstractions used by both the HAL and the run-time crate.
- The [`zynq7000-hal`](./firmware/zynq7000-hal) HAL crate containing higher-level abstractions on
top of the PAC register crate.
- The [`zynq7000-embassy`](./firmware/zynq7000-embassy) crate containing an embassy-rs time driver
using the global timer counter peripheral.
- The [`zynq7000-hal`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/firmware/zynq7000-hal)
HAL crate containing higher-level abstractions on top of the PAC register crate.
- The [`zynq7000-embassy`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/firmware/zynq7000-embassy)
crate containing support for running the embassy-rs asynchronous run-time.
This project was developed using a Zedboard, so there are several crates available targeted towards
this board:
- The [`zedboard-bsp`](./firmware/zedboard-bsp)
- The [`zedboard-bsp`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/firmware/zedboard-bsp)
crate containing board specific components for the Zedboard.
- The [`zedboard-fsbl`](./firmware/zedboard-fsbl)
- The [`zedboard-fsbl`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/firmware/zedboard-fsbl)
contains a simple first-stage bootloader application for the Zedboard.
- The [`zedboard-qspi-flasher`](./firmware/zedboard-qspi-flasher)
- The [`zedboard-qspi-flasher`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/firmware/zedboard-qspi-flasher)
contains an application which is able to flash a boot binary from DDR to the QSPI.
It also contains the following helper crates:
- The [`examples`](./firmware/examples)
- The [`examples`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/firmware/examples)
folder contains various example applications crates using the HAL and the PAC.
This folder also contains dedicated example applications using the
[`embassy`](https://github.com/embassy-rs/embassy) native Rust RTOS.
## Other libraries and tools
- The [`zedboard-fpga-design`](./zedboard-fpga-design)
- The [`zedboard-fpga-design`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zedboard-fpga-design)
folder contains a sample FPGA design and block design which was used in some of the provided software examples. The project was created with Vivado version 2024.1.
The folder contains a README with all the steps required to load this project from a TCL script.
- The [`zynq7000-boot-image`](./host/zynq7000-boot-image)
- The [`zynq7000-boot-image`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/host/zynq7000-boot-image)
library contains generic helpers to interface with the AMD
[boot binary](https://docs.amd.com/r/en-US/ug1283-bootgen-user-guide).
- The [`zynq7000-ps7init-extract`](./host/zynq7000-ps7init-extract)
- The [`tools/zynq7000-ps7init-extract`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/host/zynq7000-ps7init-extract)
tool allows extracting configuration from the AMD generated `ps7init.tcl` file which contains
static configuration parameters for DDR initialization.
@@ -156,7 +157,7 @@ Zedboard are configured for JTAG boot.
You can use the `-tui` argument to also have a terminal UI.
This repository provides a `scripts/runner.sh` which performs all the steps specified above.
The `.cargo/config.toml.template` script contains the runner and some template environmental
The `.cargo/def-config.toml` script contains the runner and some template environmental
variables that need to be set for this to work. The command above also loaded the app, but
this task can be performed by the `zynq7000-init.py` wrapper as well.
+1 -1
View File
@@ -11,7 +11,7 @@ rustflags = [
# If this is not enabled, debugging / stepping can become problematic.
"-Cforce-frame-pointers=yes",
# Can be useful for debugging.
# "-Clink-args=-Map=app.map"
"-Clink-args=-Map=app.map"
]
[build]
+1 -1
View File
@@ -9,7 +9,7 @@ repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
license = "MIT OR Apache-2.0"
[dependencies]
aarch32-cpu = { version = "0.2" }
aarch32-cpu = { version = "0.1", features = ["critical-section-single-core"] }
zynq7000-rt = { path = "../../zynq7000-rt" }
zynq7000 = { path = "../../zynq7000" }
zynq7000-hal = { path = "../../zynq7000-hal", features = ["defmt"] }
+2 -7
View File
@@ -3,18 +3,13 @@ MEMORY
/* 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 MMU. This is
recommended for something like DMA descriptors. */
/*CODE(rx) : ORIGIN = 0x00100000, LENGTH = 63M*/
/* CODE(rx) : ORIGIN = 0x00100000, LENGTH = 62M */
CODE(rx) : ORIGIN = 0x00000000, LENGTH = 192K
/* STACK: ORIGIN = 0x3F00000, LENGTH = 1M */
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 63M
/*CODE(rx) : ORIGIN = 0x00000000, LENGTH = 192K*/
UNCACHED(rx): ORIGIN = 0x4000000, LENGTH = 1M
OCM_UPPER(rx): ORIGIN = 0xFFFF0000, LENGTH = 64K
}
REGION_ALIAS("VECTORS", CODE);
REGION_ALIAS("DATA", CODE);
/* Use the upper OCM as the stack */
REGION_ALIAS("STACKS", OCM_UPPER);
SECTIONS
{
+1
View File
@@ -45,6 +45,7 @@ fn main() -> ! {
let mut led = Output::new_for_mio(mio_pins.mio7, PinState::High);
loop {
defmt::info!("toggling LED!");
zynq7000_hal::cache::clean_and_invalidate_data_cache();
led.toggle().unwrap();
cpu_tim.delay_ms(1000);
}
+5 -4
View File
@@ -11,7 +11,7 @@ keywords = ["no-std", "arm", "cortex-a", "amd", "zynq7000"]
categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
aarch32-cpu = { version = "0.2", features = ["critical-section-single-core"] }
aarch32-cpu = { version = "0.1", features = ["critical-section-single-core"] }
zynq7000-rt = { path = "../../zynq7000-rt" }
zynq7000 = { path = "../../zynq7000" }
zynq7000-hal = { path = "../../zynq7000-hal" }
@@ -25,8 +25,9 @@ embedded-hal = "1"
fugit = "0.3"
log = "0.4"
embassy-executor = { version = "0.10", features = [
"platform-cortex-ar",
embassy-executor = { version = "0.9", features = [
"arch-cortex-ar",
"executor-thread",
]}
embassy-time = { version = "0.5", features = ["tick-hz-1_000_000"] }
# TODO: Remove generic-queue-16 feature as soon as upstream executor is used again.
embassy-time = { version = "0.5", features = ["tick-hz-1_000_000", "generic-queue-16"] }
+4 -6
View File
@@ -1,12 +1,10 @@
MEMORY
{
/* Zedboard: 512 MB DDR3. Only use 62 MB for now, should be plenty for a bare-metal app.
1 MB stack memory and 1 MB of memory which will be configured as uncached device memory by the
MMU. This is recommended for something like DMA descriptors. */
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 62M
STACKS : ORIGIN = 0x3F00000, LENGTH = 1M
/* 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 MMU. This is
recommended for something like DMA descriptors. */
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 63M
UNCACHED(rx): ORIGIN = 0x4000000, LENGTH = 1M
OCM_UPPER(rx): ORIGIN = 0xFFFF0000, LENGTH = 64K
}
REGION_ALIAS("VECTORS", CODE);
@@ -76,7 +76,7 @@ async fn main(spawner: Spawner) -> ! {
info!("Boot mode: {:?}", boot_mode);
let led = Output::new_for_mio(mio_pins.mio7, PinState::Low);
spawner.spawn(led_task(led).unwrap());
spawner.spawn(led_task(led)).unwrap();
let mut log_buf: [u8; 2048] = [0; 2048];
let frame_queue = zynq7000_hal::log::rb::get_frame_queue();
loop {
+1 -1
View File
@@ -9,7 +9,7 @@ repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
license = "MIT OR Apache-2.0"
[dependencies]
aarch32-cpu = { version = "0.2" }
aarch32-cpu = { version = "0.1" }
zynq7000-rt = { path = "../../zynq7000-rt" }
zynq7000 = { path = "../../zynq7000" }
zynq7000-hal = { path = "../../zynq7000-hal" }
+4 -6
View File
@@ -1,12 +1,10 @@
MEMORY
{
/* Zedboard: 512 MB DDR3. Only use 62 MB for now, should be plenty for a bare-metal app.
1 MB stack memory and 1 MB of memory which will be configured as uncached device memory by the
MMU. This is recommended for something like DMA descriptors. */
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 62M
STACKS : ORIGIN = 0x3F00000, LENGTH = 1M
/* 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 MMU. This is
recommended for something like DMA descriptors. */
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 63M
UNCACHED(rx): ORIGIN = 0x4000000, LENGTH = 1M
OCM_UPPER(rx): ORIGIN = 0xFFFF0000, LENGTH = 64K
}
REGION_ALIAS("VECTORS", CODE);
+15 -11
View File
@@ -11,7 +11,7 @@ keywords = ["no-std", "arm", "cortex-a", "amd", "zynq7000"]
categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
aarch32-cpu = { version = "0.2", features = ["critical-section-single-core"] }
aarch32-cpu = { version = "0.1" }
zynq7000-rt = { path = "../../zynq7000-rt" }
zynq7000 = { path = "../../zynq7000" }
zynq7000-hal = { path = "../../zynq7000-hal" }
@@ -20,23 +20,27 @@ zedboard-bsp = { path = "../../zedboard-bsp" }
num_enum = { version = "0.7", default-features = false }
l3gd20 = { git = "https://github.com/us-irs/l3gd20.git", branch = "add-async-if" }
embedded-io = "0.7"
bitbybit = "2"
bitbybit = "1.4"
arbitrary-int = "2"
embedded-io-async = "0.7"
embedded-io-async = "0.6"
critical-section = "1"
static_cell = "2"
embedded-alloc = "0.7"
embedded-alloc = "0.6"
embedded-hal = "1"
embedded-hal-async = "1"
fugit = "0.3"
log = "0.4"
rand = { version = "0.10", default-features = false }
rand = { version = "0.9", default-features = false, features = ["small_rng"] }
embassy-executor = { version = "0.10", features = ["platform-cortex-ar", "executor-thread"] }
embassy-time = { version = "0.5", features = ["tick-hz-1_000_000"] }
embassy-net = { version = "0.9", features = ["dhcpv4", "packet-trace", "medium-ethernet", "icmp", "tcp", "udp"] }
embedded-sdmmc = { git = "https://github.com/robamu/embedded-sdmmc-rs.git", branch = "all-features" }
embassy-sync = { version = "0.8" }
heapless = "0.9"
embassy-executor = { git = "https://github.com/us-irs/embassy.git", branch = "cortex-ar-update", features = [
"arch-cortex-ar",
"executor-thread",
]}
# TODO: Remove generic-queue-16 feature as soon as upstream executor is used again.
embassy-time = { version = "0.5", features = ["tick-hz-1_000_000", "generic-queue-16"] }
embassy-net = { version = "0.7", features = ["dhcpv4", "packet-trace", "medium-ethernet", "icmp", "tcp", "udp"] }
embassy-sync = { version = "0.7" }
# TODO: Bump as soon as new compatible smoltcp/embassy-net version is released.
heapless = "0.8"
axi-uartlite = { version = "0.1" }
axi-uart16550 = { version = "0.1" }
+4 -6
View File
@@ -1,12 +1,10 @@
MEMORY
{
/* Zedboard: 512 MB DDR3. Only use 62 MB for now, should be plenty for a bare-metal app.
1 MB stack memory and 1 MB of memory which will be configured as uncached device memory by the
MMU. This is recommended for something like DMA descriptors. */
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 62M
STACKS : ORIGIN = 0x3F00000, LENGTH = 1M
/* 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 MMU. This is
recommended for something like DMA descriptors. */
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 63M
UNCACHED(rx): ORIGIN = 0x4000000, LENGTH = 1M
OCM_UPPER(rx): ORIGIN = 0xFFFF0000, LENGTH = 64K
}
REGION_ALIAS("VECTORS", CODE);
@@ -32,7 +32,7 @@ use embassy_time::{Duration, Timer};
use embedded_io::Write;
use embedded_io_async::Write as _;
use log::{LevelFilter, debug, error, info, warn};
use rand::{Rng, SeedableRng};
use rand::{RngCore, SeedableRng};
use zedboard::PS_CLOCK_FREQUENCY;
use zedboard_bsp::phy_marvell;
use zynq7000_hal::{
@@ -373,9 +373,9 @@ async fn main(spawner: Spawner) -> ! {
let tcp_socket = TcpSocket::new(stack, RX_TCP_BUFS.take(), TX_TCP_BUFS.take());
// Spawn all embassy tasks.
spawner.spawn(embassy_net_task(runner).unwrap());
spawner.spawn(udp_task(udp_socket).unwrap());
spawner.spawn(tcp_task(tcp_socket).unwrap());
spawner.spawn(embassy_net_task(runner)).unwrap();
spawner.spawn(udp_task(udp_socket)).unwrap();
spawner.spawn(tcp_task(tcp_socket)).unwrap();
let mut mio_led = Output::new_for_mio(gpio_pins.mio.mio7, PinState::Low);
@@ -155,7 +155,7 @@ async fn main(spawner: Spawner) -> ! {
}
}
spawner.spawn(logger_task(uart).unwrap());
spawner.spawn(logger_task(uart)).unwrap();
if BLOCKING {
blocking_application(mio_led, emio_leds, spi).await;
} else {
+11 -45
View File
@@ -2,7 +2,6 @@
#![no_main]
use aarch32_cpu::asm::nop;
use arbitrary_int::{traits::Integer as _, u2};
use core::panic::PanicInfo;
use embassy_executor::Spawner;
use embassy_time::{Duration, Ticker};
@@ -15,7 +14,6 @@ use zynq7000_hal::{BootMode, clocks, gic, gpio, gtc, prelude::*, qspi, uart};
use zynq7000_rt as _;
const DISPLAY_CLOCK_CONFIG: bool = false;
const INIT_STRING: &str = "-- Zynq 7000 Zedboard QSPI example --\n\r";
const QSPI_DEV_COMBINATION: qspi::QspiDeviceCombination = qspi::QspiDeviceCombination {
vendor: qspi::QspiVendor::WinbondAndSpansion,
@@ -30,7 +28,6 @@ fn entry_point() -> ! {
}
const ERASE_PROGRAM_READ_TEST: bool = false;
const TEST_QSPI_BASE: u32 = 0x20000;
#[embassy_executor::main]
async fn main(_spawner: Spawner) -> ! {
@@ -71,9 +68,6 @@ async fn main(_spawner: Spawner) -> ! {
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
if DISPLAY_CLOCK_CONFIG {
log::debug!("clock config: {:?}", clocks);
}
let qspi_clock_config =
qspi::ClockConfig::calculate_with_loopback(qspi::SrcSelIo::IoPll, &clocks, 100.MHz())
@@ -96,14 +90,7 @@ async fn main(_spawner: Spawner) -> ! {
let qspi_io_mode = qspi.into_io_mode(false);
let mut spansion_qspi = qspi_spansion::QspiSpansionS25Fl256SIoMode::new(
qspi_io_mode,
qspi_spansion::Config {
set_quad_bit_if_necessary: true,
latency_config: Some(u2::ZERO),
clear_write_protection: true,
},
);
let mut spansion_qspi = qspi_spansion::QspiSpansionS25Fl256SIoMode::new(qspi_io_mode, true);
let rdid = spansion_qspi.read_rdid_extended();
info!(
@@ -116,48 +103,27 @@ async fn main(_spawner: Spawner) -> ! {
);
let cr1 = spansion_qspi.read_configuration_register();
info!("QSPI Configuration Register 1: {:?}", cr1);
let sr = spansion_qspi.read_status_register_1();
info!("QSPI Status Register: {:?}", sr);
let mut write_buf: [u8; 100 * qspi_spansion::PAGE_SIZE] = [0x0; 100 * qspi_spansion::PAGE_SIZE];
let mut write_buf: [u8; u8::MAX as usize + 1] = [0x0; u8::MAX as usize + 1];
for (idx, byte) in write_buf.iter_mut().enumerate() {
*byte = (idx % u8::MAX as usize) as u8;
*byte = idx as u8;
}
let mut read_buf = [0u8; 100 * qspi_spansion::PAGE_SIZE];
let mut read_buf = [0u8; 256];
if ERASE_PROGRAM_READ_TEST {
info!("performing erase, program, read test");
spansion_qspi
.erase_sector(TEST_QSPI_BASE)
.erase_sector(0x10000)
.expect("erasing sector failed");
spansion_qspi.read_fast_read(TEST_QSPI_BASE, &mut read_buf, true);
spansion_qspi.read_page_fast_read(0x10000, &mut read_buf, true);
for read in read_buf.iter() {
assert_eq!(*read, 0xFF);
}
read_buf.fill(0);
let mut current_offset = 0_usize;
let mut current_qspi_offset = TEST_QSPI_BASE;
for (idx, chunk) in write_buf
.chunks(qspi_spansion::RECOMMENDED_PROGRAM_PAGE_SIZE)
.enumerate()
{
spansion_qspi
.program(current_qspi_offset, chunk)
.expect("programming failed");
spansion_qspi.read_fast_read(
current_qspi_offset,
&mut read_buf[current_offset..current_offset + chunk.len()],
true,
);
assert_eq!(
chunk,
&read_buf[current_offset..current_offset + chunk.len()],
"read and write missmatch at chunk index {}, data offset {}",
idx,
current_offset
);
current_offset += chunk.len();
current_qspi_offset += chunk.len() as u32;
spansion_qspi.program_page(0x10000, &write_buf).unwrap();
spansion_qspi.read_page_fast_read(0x10000, &mut read_buf, true);
for (read, written) in read_buf.iter().zip(write_buf.iter()) {
assert_eq!(read, written);
}
info!("test successful");
}
@@ -167,7 +133,7 @@ async fn main(_spawner: Spawner) -> ! {
let guard = spansion_lqspi.read_guard();
unsafe {
core::ptr::copy_nonoverlapping(
(qspi::QSPI_START_ADDRESS + TEST_QSPI_BASE as usize) as *const u8,
(qspi::QSPI_START_ADDRESS + 0x10000) as *const u8,
read_buf.as_mut_ptr(),
256,
);
-275
View File
@@ -1,275 +0,0 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use embassy_executor::Spawner;
use embassy_time::{Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write;
use log::error;
use zedboard::PS_CLOCK_FREQUENCY;
use zynq7000_hal::gpio::Input;
use zynq7000_hal::prelude::*;
use zynq7000_hal::sd::SdClockConfig;
use zynq7000_hal::{BootMode, clocks, gic, gpio, gtc, sd::SdCardUninit, uart};
use zynq7000_rt as _;
const INIT_STRING: &str = "-- Zynq 7000 Zedboard SDIO example --\n\r";
// These are off by default because they write to the SD card as well.
const LOW_LEVEL_TESTS: bool = false;
const SDMMC_RS_TESTS: bool = false;
#[derive(Debug, Clone, Copy)]
pub struct DummyTimeSource;
impl embedded_sdmmc::TimeSource for DummyTimeSource {
fn get_timestamp(&self) -> embedded_sdmmc::Timestamp {
embedded_sdmmc::Timestamp::from_calendar(1970, 1, 1, 0, 0, 0).unwrap()
}
}
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
fn entry_point() -> ! {
main();
}
#[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(_spawner: Spawner) -> ! {
let periphs = zynq7000_hal::init(zynq7000_hal::Config {
init_l2_cache: true,
level_shifter_config: Some(zynq7000_hal::LevelShifterConfig::EnableAll),
interrupt_config: Some(zynq7000_hal::InteruptConfig::AllInterruptsToCpu0),
})
.unwrap();
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = clocks::Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
let gpio_pins = gpio::GpioPins::new(periphs.gpio);
// Set up global timer counter and embassy time driver.
let gtc = gtc::GlobalTimerCounter::new(periphs.gtc, clocks.arm_clocks());
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
// Set up the UART, we are logging with it.
let uart_clk_config = uart::ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut uart = uart::Uart::new_with_mio_for_uart_1(
periphs.uart_1,
uart::Config::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 sdio_clock_config =
SdClockConfig::calculate_for_io_clock(clocks.io_clocks(), 100.MHz(), 10.MHz()).unwrap();
log::info!("SDIO clock config: {:?}", sdio_clock_config);
let sd_card_uninit = SdCardUninit::new_for_sdio_0(
periphs.sdio_0,
sdio_clock_config,
// On the zedboard, the bank has a 1.8 V voltage which is shifted up to 3.3 V by a
// level shifter.
zynq7000_hal::sd::IoType::LvCmos18,
gpio_pins.mio.mio40,
gpio_pins.mio.mio41,
(
gpio_pins.mio.mio42,
gpio_pins.mio.mio43,
gpio_pins.mio.mio44,
gpio_pins.mio.mio45,
),
)
.unwrap();
let card_detect = Input::new_for_mio(gpio_pins.mio.mio47).unwrap();
let write_protect = Input::new_for_mio(gpio_pins.mio.mio46).unwrap();
// The card detect being active low makes sense according to the Zedboard docs. Not sure
// about write-protect though.. It seems that write protect on means that the
// the pin is pulled high.
log::info!("Card detect state: {:?}", card_detect.is_low());
log::info!("Write protect state: {:?}", write_protect.is_high());
let capabilities = sd_card_uninit.ll().capabilities();
log::debug!("SDIO Capabilities: {:?}", capabilities);
let present_state = sd_card_uninit.ll().read_present_state();
log::debug!("SD present state: {:?}", present_state);
let boot_mode = BootMode::new_from_regs();
log::info!("Boot mode: {:?}", boot_mode);
let mut ticker = Ticker::every(Duration::from_millis(200));
let mut mio_led = gpio::Output::new_for_mio(gpio_pins.mio.mio7, gpio::PinState::Low);
let sd_result = sd_card_uninit.initialize();
let sd_card = match sd_result {
Ok(card) => {
log::info!("SD card info: {:?}", card.card_info());
card
}
Err(e) => {
panic!("SDIO init error: {e:?}");
}
};
let mut buf: [u8; 4096] = [0; 4096];
if LOW_LEVEL_TESTS {
log::info!("doing SD card low-level tests");
let mut cache_buf: [u8; 4096] = [0; 4096];
// cache the data, will be written back later..
sd_card
.read_multiple_blocks(&mut cache_buf, 0x1000)
.unwrap();
let mut write_data: [u8; 4096] = [0; 4096];
for chunk in write_data.chunks_mut(u8::MAX as usize) {
for (idx, byte) in chunk.iter_mut().enumerate() {
*byte = idx as u8;
}
}
sd_card.write_multiple_blocks(&write_data, 0x1000).unwrap();
sd_card.read_multiple_blocks(&mut buf, 0x1000).unwrap();
for chunk in buf.chunks(u8::MAX as usize) {
for (idx, byte) in chunk.iter().enumerate() {
assert_eq!(idx as u8, *byte);
}
}
sd_card.write_multiple_blocks(&cache_buf, 0x1000).unwrap();
log::info!("SD card low-level tests success");
}
buf.fill(0);
if SDMMC_RS_TESTS {
log::info!("doing SD card embedded-sdmmc-rs tests");
// Now let's look for volumes (also known as partitions) on our block device.
// To do this we need a Volume Manager. It will take ownership of the block device.
let volume_mgr = embedded_sdmmc::VolumeManager::new(sd_card, DummyTimeSource);
// Try and access Volume 0 (i.e. the first partition).
// The volume object holds information about the filesystem on that volume.
let volume0 = volume_mgr
.open_volume(embedded_sdmmc::VolumeIdx(0))
.unwrap();
// Open the root directory (mutably borrows from the volume).
let mut current_dir = volume0.open_root_dir().unwrap();
log::info!("iterating root directory");
current_dir
.iterate_dir(|entry| {
log::info!("{:?}", entry);
core::ops::ControlFlow::Continue(())
})
.unwrap();
let new_file = current_dir
.open_file_in_dir("__T.TXT", embedded_sdmmc::Mode::ReadWriteCreateOrTruncate)
.unwrap();
let string = "test string\n";
new_file.write(string.as_bytes()).unwrap();
new_file.close().unwrap();
let read_new = current_dir
.open_file_in_dir("__T.TXT", embedded_sdmmc::Mode::ReadOnly)
.unwrap();
assert_eq!(read_new.length(), string.len() as u32);
read_new.read(&mut buf).unwrap();
let buf_as_str = core::str::from_utf8(&buf[0..string.len()]).unwrap();
assert_eq!(buf_as_str, string);
read_new.close().unwrap();
current_dir.delete_entry_in_dir("__T.TXT").unwrap();
assert_eq!(
current_dir.find_directory_entry("__T.TXT").unwrap_err(),
embedded_sdmmc::Error::NotFound
);
if current_dir.find_directory_entry("_TDIR").is_ok() {
current_dir.delete_entry_in_dir("_TDIR").unwrap();
}
current_dir.make_dir_in_dir("_TDIR").unwrap();
current_dir.change_dir("_TDIR").unwrap();
current_dir.change_dir("..").unwrap();
current_dir.delete_entry_in_dir("_TDIR").unwrap();
current_dir.close().unwrap();
log::info!("SD card embedded-sdmmc-rs success");
}
loop {
mio_led.toggle().unwrap();
ticker.next().await; // Wait for the next cycle of the ticker
}
}
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = gic::GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
gic::Interrupt::Sgi(_) => (),
gic::Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
gic::Interrupt::Spi(_spi_interrupt) => (),
gic::Interrupt::Invalid(_) => (),
gic::Interrupt::Spurious => (),
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
fn data_abort_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(Undefined)]
fn undefined_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(PrefetchAbort)]
fn prefetch_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
/// Panic handler
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
error!("Panic: {info:?}");
loop {}
}
@@ -88,11 +88,11 @@ static QUEUE_UART16550: static_cell::ConstStaticCell<heapless::spsc::Queue<u8, R
// Those are all used by the interrupt handler, so we have to do the Mutex dance.
static RX_UART_0: Mutex<RefCell<Option<zynq7000_hal::uart::Rx>>> = Mutex::new(RefCell::new(None));
static UART_0_PROD: Mutex<RefCell<Option<heapless::spsc::Producer<'static, u8>>>> =
static UART_0_PROD: Mutex<RefCell<Option<heapless::spsc::Producer<'static, u8, RB_SIZE>>>> =
Mutex::new(RefCell::new(None));
static UARTLITE_PROD: Mutex<RefCell<Option<heapless::spsc::Producer<'static, u8>>>> =
static UARTLITE_PROD: Mutex<RefCell<Option<heapless::spsc::Producer<'static, u8, RB_SIZE>>>> =
Mutex::new(RefCell::new(None));
static UART16550_PROD: Mutex<RefCell<Option<heapless::spsc::Producer<'static, u8>>>> =
static UART16550_PROD: Mutex<RefCell<Option<heapless::spsc::Producer<'static, u8, RB_SIZE>>>> =
Mutex::new(RefCell::new(None));
/// Entry point which calls the embassy main method.
@@ -284,20 +284,20 @@ async fn main(spawner: Spawner) -> ! {
.replace(uart16550_prod);
RX_UART_0.borrow(cs).borrow_mut().replace(uart_0_rx);
});
spawner.spawn(led_task(mio_led, emio_leds).unwrap());
spawner.spawn(led_task(mio_led, emio_leds)).unwrap();
match UART_MODE {
UartMode::Uart0ToUartlite => {
spawner.spawn(uartlite_task(uartlite_tx).unwrap());
spawner.spawn(uart_0_task(uart_0_tx).unwrap());
spawner.spawn(uartlite_task(uartlite_tx)).unwrap();
spawner.spawn(uart_0_task(uart_0_tx)).unwrap();
}
UartMode::Uart0ToUart16550 => {
spawner.spawn(uart_0_task(uart_0_tx).unwrap());
spawner.spawn(uart_16550_task(uart_16550_tx).unwrap());
spawner.spawn(uart_0_task(uart_0_tx)).unwrap();
spawner.spawn(uart_16550_task(uart_16550_tx)).unwrap();
}
UartMode::UartliteToUart16550 => {
spawner.spawn(uartlite_task(uartlite_tx).unwrap());
spawner.spawn(uart_16550_task(uart_16550_tx).unwrap());
spawner.spawn(uartlite_task(uartlite_tx)).unwrap();
spawner.spawn(uart_16550_task(uart_16550_tx)).unwrap();
}
}
let mut read_buf: [u8; RB_SIZE] = [0; RB_SIZE];
-13
View File
@@ -8,19 +8,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased]
## Fixed
- QSPI robustness fixes. Read, fast-read and write operations are now chunked according to the 252
byte limit specified in the TRM.
## Added
- QSPI constructor can now optionally clear block protection and set latency configuration.
## Changed
- Alignment rules of Spansion QSPI page program now only require 4 byte aligned size.
# [v0.1.0]
Initial release
+2 -7
View File
@@ -10,14 +10,9 @@ keywords = ["no-std", "zedboard", "bare-metal", "amd", "zynq7000"]
categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
zynq7000 = { path = "../zynq7000", version = "0.2" }
zynq7000 = { path = "../zynq7000", version = "0.1" }
zynq7000-hal = { path = "../zynq7000-hal", version = "0.1" }
bitbybit = "2"
log = "0.4"
bitbybit = "1.4"
arbitrary-int = "2"
num_enum = { version = "0.7", default-features = false }
thiserror = { version = "2", default-features = false }
[package.metadata.docs.rs]
targets = ["armv7a-none-eabihf"]
rustdoc-args = ["--generate-link-to-definition"]
@@ -1,4 +1,4 @@
#![doc = r"This file was auto-generated by the [zynq7000-ps7init-extract](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/host/zynq7000-ps7init-extract) program."]
#![doc = r"This file was auto-generated by the [zynq7000-ps7init-extract](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/tools/zynq7000-ps7init-extract) program."]
#![doc = r""]
#![doc = r"This configuration file contains static DDR configuration parameters extracted from the"]
#![doc = r"AMD ps7init.tcl file. It was generated for the MT41K128M16JT-125 DDR chip."]
@@ -34,7 +34,7 @@ pub const DDRC_CONFIG_ZEDBOARD: DdrcConfigSet = DdrcConfigSet {
ctrl_reg5: regs::CtrlReg5::new_with_raw_value(0x00466111),
ctrl_reg6: regs::CtrlReg6::new_with_raw_value(0x00032222),
che_t_zq: regs::CheTZq::new_with_raw_value(0x10200802),
che_t_zq_short_interval_reg: regs::CheTZqShortInterval::new_with_raw_value(0x0690cb73),
che_t_zq_short_interval_reg: regs::CheTZqShortInterval::new_with_raw_value(0x10200802),
deep_powerdown: regs::DeepPowerdown::new_with_raw_value(0x000001fe),
reg_2c: regs::Reg2c::new_with_raw_value(0x1cffffff),
reg_2d: regs::Reg2d::new_with_raw_value(0x00000200),
@@ -1,11 +1,10 @@
#![doc = r"This file was auto-generated by the [zynq7000-ps7init-extract](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/host/zynq7000-ps7init-extract) program."]
#![doc = r"This file was auto-generated by the [zynq7000-ps7init-extract](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/tools/zynq7000-ps7init-extract) program."]
#![doc = r""]
#![doc = r"This configuration file contains static DDRIOB configuration parameters extracted from the"]
#![doc = r"AMD ps7init.tcl file. It was generated for the MT41K128M16JT-125 DDR chip."]
use zynq7000::ddrc::regs;
use zynq7000_hal::ddr::DdriobConfigSet;
pub const DDRIOB_CONFIG_SET_ZEDBOARD: DdriobConfigSet = DdriobConfigSet {
ddr_control: zynq7000::slcr::ddriob::DdrControl::new_with_raw_value(0x00000260),
addr0: regs::DdriobConfig::new_with_raw_value(0x00000600),
addr1: regs::DdriobConfig::new_with_raw_value(0x00000600),
data0: regs::DdriobConfig::new_with_raw_value(0x00000672),
+150 -256
View File
@@ -2,17 +2,10 @@ use core::cell::RefCell;
use arbitrary_int::{prelude::*, u24};
use zynq7000_hal::qspi::{
FIFO_DEPTH, LinearQspiConfig, MAX_BYTES_PER_TRANSFER_IO_MODE, QspiIoMode, QspiLinearAddressing,
FIFO_DEPTH, LinearQspiConfig, QspiIoMode, QspiIoTransferGuard, QspiLinearAddressing,
QspiLinearReadGuard,
};
/// 4 bytes are reserved for command byte and address. Rounded down at a 16 byte boundary,
/// recommended by flash memory datasheet.
pub const MAX_DATA_BYTES_PER_WRITE: usize = 240;
/// Probably the most performant chunk/program size to program the chip without crossing page
/// boundaries without exceeding the FIFO size.
pub const RECOMMENDED_PROGRAM_PAGE_SIZE: usize = 128;
pub const QSPI_DEV_COMBINATION_REV_F: zynq7000_hal::qspi::QspiDeviceCombination =
zynq7000_hal::qspi::QspiDeviceCombination {
vendor: zynq7000_hal::qspi::QspiVendor::WinbondAndSpansion,
@@ -20,8 +13,7 @@ pub const QSPI_DEV_COMBINATION_REV_F: zynq7000_hal::qspi::QspiDeviceCombination
two_devices: false,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
#[derive(Debug, Clone, Copy)]
pub enum RegisterId {
/// WRR
WriteRegisters = 0x01,
@@ -72,8 +64,7 @@ pub enum SectorArchictecture {
Hybrid = 0x01,
}
pub const PAGE_SIZE: usize = 0x100;
pub const SECTOR_SIZE: usize = 0x10000;
pub const PAGE_SIZE: usize = 256;
#[derive(Debug, Clone, Copy)]
pub struct BaseDeviceId {
@@ -168,7 +159,8 @@ impl ExtendedDeviceId {
}
}
#[bitbybit::bitfield(u8, debug, forbid_overlaps)]
#[bitbybit::bitfield(u8)]
#[derive(Debug)]
pub struct StatusRegister1 {
#[bit(7, rw)]
status_register_write_disable: bool,
@@ -176,18 +168,25 @@ pub struct StatusRegister1 {
programming_error: bool,
#[bit(5, r)]
erase_error: bool,
#[bits(2..=4, rw)]
block_protection: u3,
#[bit(4, r)]
bp_2: bool,
#[bit(3, r)]
bp_1: bool,
#[bit(2, r)]
bp_0: bool,
#[bit(1, r)]
write_enable_latch: bool,
#[bit(0, r)]
write_in_progress: bool,
}
#[bitbybit::bitfield(u8, debug, forbid_overlaps)]
#[bitbybit::bitfield(u8)]
#[derive(Debug)]
pub struct ConfigRegister1 {
#[bits(6..=7, rw)]
latency_code: u2,
#[bit(7, rw)]
latency_code_1: bool,
#[bit(6, rw)]
latency_code_0: bool,
/// This is an OTP bit. It can not be set back to 0 once it has been set to 1!
#[bit(5, rw)]
tbprot: bool,
@@ -224,60 +223,27 @@ pub enum ProgramPageError {
ProgrammingErrorBitSet,
#[error("address error: {0}")]
Addr(#[from] AddrError),
#[error("program data is larger than page size {PAGE_SIZE}")]
DataTooLarge,
#[error("program data is not aligned to 4 bytes")]
NotAligned,
#[error("program data crosses page boundary")]
CrossesPageBoundary,
}
#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
pub struct Config {
pub set_quad_bit_if_necessary: bool,
pub latency_config: Option<u2>,
pub clear_write_protection: bool,
}
impl Config {
pub fn sr_or_cr_update_possibly_required(&self) -> bool {
self.set_quad_bit_if_necessary
|| self.latency_config.is_some()
|| self.clear_write_protection
}
#[error("data is larger than page size {PAGE_SIZE}")]
DataLargerThanPage,
}
pub struct QspiSpansionS25Fl256SIoMode(RefCell<QspiIoMode>);
impl QspiSpansionS25Fl256SIoMode {
pub fn new(qspi: QspiIoMode, config: Config) -> Self {
pub fn new(qspi: QspiIoMode, set_quad_bit_if_necessary: bool) -> Self {
let mut spansion_qspi = QspiSpansionS25Fl256SIoMode(RefCell::new(qspi));
spansion_qspi.clear_status();
let mut write_required = false;
if config.sr_or_cr_update_possibly_required() {
if set_quad_bit_if_necessary {
let mut cr1 = spansion_qspi.read_configuration_register();
if config.set_quad_bit_if_necessary && !cr1.quad() {
cr1.set_quad(true);
write_required = true;
}
if let Some(latency_config) = config.latency_config
&& cr1.latency_code() != latency_config
{
cr1.set_latency_code(latency_config);
write_required = true;
if cr1.quad() {
// Quad bit is already set.
return spansion_qspi;
}
cr1.set_quad(true);
// Preserve the status register by reading it first.
let mut sr1 = spansion_qspi.read_status_register_1();
if config.clear_write_protection && sr1.block_protection() != u3::ZERO {
sr1.set_status_register_write_disable(false);
sr1.set_block_protection(u3::ZERO);
write_required = true;
}
if write_required {
// Safety: Only the QUAD bit was set while all other bits are preserved.
unsafe {
spansion_qspi.write_status_and_config_register(sr1, cr1);
}
let sr1 = spansion_qspi.read_status_register_1();
// Safety: Only the QUAD bit was set while all other bits are preserved.
unsafe {
spansion_qspi.write_status_and_config_register(sr1, cr1);
}
}
spansion_qspi
@@ -291,16 +257,6 @@ impl QspiSpansionS25Fl256SIoMode {
QspiSpansionS25Fl256SLinearMode(qspi)
}
pub fn set_write_protection(&mut self, write_protection: u3) {
unsafe {
self.modify_status_and_config_register(|mut sr, cr| {
sr.set_status_register_write_disable(false);
sr.set_block_protection(write_protection);
(sr, cr)
});
}
}
pub fn write_enable(&mut self) {
let qspi = self.0.get_mut();
let mut transfer = qspi.transfer_guard();
@@ -345,38 +301,7 @@ impl QspiSpansionS25Fl256SIoMode {
/// # Safety
///
/// Misuse of this API does not lead to undefined behavior. However, it writes the
/// configuration register, which is OTP bits. Changing these bits from 0 to 1 is an
/// irreversible operation.
pub unsafe fn modify_status_and_config_register(
&mut self,
f: impl FnOnce(StatusRegister1, ConfigRegister1) -> (StatusRegister1, ConfigRegister1),
) {
self.write_enable();
let mut qspi = self.0.borrow_mut();
let mut transfer = qspi.transfer_guard();
let sr1 = self.read_status_register_1();
let cr1 = self.read_configuration_register();
let (sr1, cr1) = f(sr1, cr1);
transfer.write_word_txd_11(u32::from_ne_bytes([
RegisterId::WriteRegisters as u8,
sr1.raw_value(),
cr1.raw_value(),
0x00,
]));
transfer.start();
while !transfer.read_status().rx_above_threshold() {}
transfer.read_rx_data();
}
/// Write a new value for the status register. It is strongly recommended to read both
/// the status and config register first and preserve all unchanged bits.
///
/// This API must be used if the QUAD bit (CR1\[1\]) is set.
///
/// # Safety
///
/// Misuse of this API does not lead to undefined behavior. However, it writes the
/// configuration register, which is OTP bits. Changing these bits from 0 to 1 is an
/// configuration register, which as OTP bits. Changing these bits from 0 to 1 is an
/// irreversible operation.
pub unsafe fn write_status_and_config_register(
&mut self,
@@ -463,10 +388,10 @@ impl QspiSpansionS25Fl256SIoMode {
/// This function will block until the operation has completed.
pub fn erase_sector(&mut self, addr: u32) -> Result<(), EraseError> {
if addr + SECTOR_SIZE as u32 > u24::MAX.as_u32() {
if addr + 0x10000 > u24::MAX.as_u32() {
return Err(AddrError::OutOfRange.into());
}
if !addr.is_multiple_of(SECTOR_SIZE as u32) {
if !addr.is_multiple_of(0x10000) {
return Err(AddrError::Alignment.into());
}
self.write_enable();
@@ -504,35 +429,19 @@ impl QspiSpansionS25Fl256SIoMode {
}
}
pub fn write_pages(&mut self, mut addr: u32, data: &[u8]) -> Result<(), ProgramPageError> {
if addr + data.len() as u32 > u24::MAX.as_u32() {
return Err(AddrError::OutOfRange.into());
}
for chunk in data.chunks(RECOMMENDED_PROGRAM_PAGE_SIZE) {
self.program(addr, chunk)?;
addr += chunk.len() as u32;
}
Ok(())
}
/// This function also takes care of enabling writes before programming the page.
/// This function will block until the operation has completed.
///
/// The data length may not exceed [MAX_DATA_BYTES_PER_WRITE]. Furthermore, the data needs
/// to be aligned to 4 bytes and the programming operation is not allowed to cross a page.
/// boundary. It is recommended to program in 128 byte chunks.
pub fn program(&mut self, addr: u32, data: &[u8]) -> Result<(), ProgramPageError> {
/// The data length max not exceed the page size [PAGE_SIZE].
pub fn program_page(&mut self, addr: u32, data: &[u8]) -> Result<(), ProgramPageError> {
if addr + data.len() as u32 > u24::MAX.as_u32() {
return Err(AddrError::OutOfRange.into());
}
if data.len() > MAX_DATA_BYTES_PER_WRITE {
return Err(ProgramPageError::DataTooLarge);
if !addr.is_multiple_of(0x100) {
return Err(AddrError::Alignment.into());
}
if !data.len().is_multiple_of(4) {
return Err(ProgramPageError::NotAligned);
}
if (addr as usize % PAGE_SIZE) + data.len() > PAGE_SIZE {
return Err(ProgramPageError::CrossesPageBoundary);
if data.len() > PAGE_SIZE {
return Err(ProgramPageError::DataLargerThanPage);
}
self.write_enable();
let qspi = self.0.get_mut();
@@ -546,8 +455,7 @@ impl QspiSpansionS25Fl256SIoMode {
transfer.write_word_txd_00(u32::from_ne_bytes(raw_word));
let mut read_index: u32 = 0;
let mut current_byte_index = 0;
// Full four byte writes.
let fifo_writes = data.len() / 4;
let fifo_writes = data.len().div_ceil(4);
// Fill the FIFO until it is full.
for _ in 0..core::cmp::min(fifo_writes, FIFO_DEPTH - 1) {
transfer.write_word_txd_00(u32::from_ne_bytes(
@@ -557,14 +465,52 @@ impl QspiSpansionS25Fl256SIoMode {
));
current_byte_index += 4;
}
transfer.start();
// Wait until the transfer is done by waiting until all RX bytes have been received.
let mut wait_for_tx_slot = |transfer: &mut QspiIoTransferGuard| loop {
let status = transfer.read_status();
if status.rx_above_threshold() {
transfer.read_rx_data();
read_index = read_index.wrapping_add(4);
}
if !status.tx_full() {
break;
}
};
while current_byte_index < data.len() {
// Immediately fill the FIFO again with the remaining 8 bytes.
wait_for_tx_slot(&mut transfer);
let word = match core::cmp::min(4, data.len() - current_byte_index) {
1 => {
let mut bytes = [0; 4];
bytes[0] = data[current_byte_index];
u32::from_ne_bytes(bytes)
}
2 => {
let mut bytes = [0; 4];
bytes[0..2].copy_from_slice(&data[current_byte_index..current_byte_index + 2]);
u32::from_ne_bytes(bytes)
}
3 => {
let mut bytes = [0; 4];
bytes[0..3].copy_from_slice(&data[current_byte_index..current_byte_index + 3]);
u32::from_ne_bytes(bytes)
}
4 => u32::from_ne_bytes(
data[current_byte_index..current_byte_index + 4]
.try_into()
.unwrap(),
),
_ => unreachable!(),
};
transfer.write_word_txd_00(word);
current_byte_index += 4;
}
while read_index < data.len() as u32 {
// Double read to avoid RX underflows as specified in TRM.
let status_read = transfer.read_status();
if status_read.rx_above_threshold() && transfer.read_status().rx_above_threshold() {
if transfer.read_status().rx_above_threshold() {
transfer.read_rx_data();
read_index = read_index.wrapping_add(4);
}
@@ -587,134 +533,84 @@ impl QspiSpansionS25Fl256SIoMode {
}
}
fn generic_read(&self, addr: u32, buf: &mut [u8], dummy_byte: bool, fast_read: bool) {
let mut offset = 0;
let reg_id = if fast_read {
RegisterId::FastRead
} else {
RegisterId::Read
};
pub fn read_page_fast_read(&self, addr: u32, buf: &mut [u8], dummy_byte: bool) {
let mut qspi = self.0.borrow_mut();
let mut max_chunk_size = MAX_BYTES_PER_TRANSFER_IO_MODE - 4;
let mut transfer = qspi.transfer_guard();
let raw_word: [u8; 4] = [
RegisterId::FastRead as u8,
((addr >> 16) & 0xff) as u8,
((addr >> 8) & 0xff) as u8,
(addr & 0xff) as u8,
];
transfer.write_word_txd_00(u32::from_ne_bytes(raw_word));
let mut read_index = 0;
let mut written_words = 0;
let mut bytes_to_write = buf.len();
if dummy_byte {
max_chunk_size -= 1;
bytes_to_write += 1;
}
let fifo_writes = bytes_to_write.div_ceil(4);
// Fill the FIFO until it is full or all 0 bytes have been written.
for _ in 0..core::cmp::min(fifo_writes, FIFO_DEPTH - 1) {
transfer.write_word_txd_00(0);
written_words += 1;
}
while offset < buf.len() {
// Calculate the size of the current chunk (max 248 bytes)
let chunk_size = core::cmp::min(max_chunk_size, buf.len() - offset);
let current_addr = addr + offset as u32;
transfer.start();
let mut reply_word_index = 0;
// Create a mutable slice for the current chunk
let chunk_slice = &mut buf[offset..offset + chunk_size];
// This ensures the hardware transaction (Chip Select, etc.) restarts for each chunk.
{
let mut transfer = qspi.transfer_guard();
let raw_word: [u8; 4] = [
reg_id as u8,
((current_addr >> 16) & 0xff) as u8,
((current_addr >> 8) & 0xff) as u8,
(current_addr & 0xff) as u8,
];
transfer.write_word_txd_00(u32::from_ne_bytes(raw_word));
let mut read_index = 0;
let mut written_words = 0;
// Use chunk_size instead of the full buffer length
let mut bytes_to_write = chunk_size;
if dummy_byte {
bytes_to_write += 1;
while read_index < buf.len() {
if transfer.read_status().rx_above_threshold() {
let reply = transfer.read_rx_data();
if reply_word_index == 0 {
reply_word_index += 1;
continue;
}
let fifo_writes = bytes_to_write.div_ceil(4);
// Fill the FIFO until it is full or all 0 bytes have been written.
for _ in 0..core::cmp::min(fifo_writes, FIFO_DEPTH - 1) {
transfer.write_word_txd_00(0);
written_words += 1;
}
transfer.start();
let mut reply_word_index = 0;
// Loop based on the current chunk's size
while read_index < chunk_size {
let rx_is_above_threshold = transfer.read_status().rx_above_threshold();
// See p.374 of the TRM: Do a double read to ensure this is correct information.
if rx_is_above_threshold && transfer.read_status().rx_above_threshold() {
let reply = transfer.read_rx_data();
if reply_word_index == 0 {
reply_word_index += 1;
continue;
}
let reply_as_bytes = reply.to_ne_bytes();
// Calculate remaining bytes in this specific chunk
let reply_size = core::cmp::min(chunk_size - read_index, 4);
read_index += match (reply_size, reply_word_index == 1 && dummy_byte) {
(1, false) => {
chunk_slice[read_index] = reply_as_bytes[0];
1
}
(1, true) => {
chunk_slice[read_index] = reply_as_bytes[1];
1
}
(2, false) => {
chunk_slice[read_index..read_index + 2]
.copy_from_slice(&reply_as_bytes[0..2]);
2
}
(2, true) => {
chunk_slice[read_index..read_index + 2]
.copy_from_slice(&reply_as_bytes[1..3]);
2
}
(3, false) => {
chunk_slice[read_index..read_index + 3]
.copy_from_slice(&reply_as_bytes[0..3]);
3
}
(3, true) => {
chunk_slice[read_index..read_index + 3]
.copy_from_slice(&reply_as_bytes[1..4]);
3
}
(4, false) => {
chunk_slice[read_index..read_index + 4]
.copy_from_slice(&reply_as_bytes[0..4]);
4
}
(4, true) => {
chunk_slice[read_index..read_index + 3]
.copy_from_slice(&reply_as_bytes[1..4]);
3
}
_ => unreachable!(),
};
reply_word_index += 1;
let reply_as_bytes = reply.to_ne_bytes();
let reply_size = core::cmp::min(buf.len() - read_index, 4);
read_index += match (reply_size, reply_word_index == 1 && dummy_byte) {
(1, false) => {
buf[read_index] = reply_as_bytes[0];
1
}
if written_words < fifo_writes && !transfer.read_status().tx_full() {
transfer.write_word_txd_00(0);
written_words += 1;
(1, true) => {
buf[read_index] = reply_as_bytes[1];
1
}
}
(2, false) => {
buf[read_index..read_index + 2].copy_from_slice(&reply_as_bytes[0..2]);
2
}
(2, true) => {
buf[read_index..read_index + 2].copy_from_slice(&reply_as_bytes[1..3]);
2
}
(3, false) => {
buf[read_index..read_index + 3].copy_from_slice(&reply_as_bytes[0..3]);
3
}
(3, true) => {
buf[read_index..read_index + 3].copy_from_slice(&reply_as_bytes[1..4]);
3
}
(4, false) => {
buf[read_index..read_index + 4].copy_from_slice(&reply_as_bytes[0..4]);
4
}
(4, true) => {
buf[read_index..read_index + 3].copy_from_slice(&reply_as_bytes[1..4]);
3
}
_ => unreachable!(),
};
reply_word_index += 1;
}
if written_words < fifo_writes && !transfer.read_status().tx_full() {
transfer.write_word_txd_00(0);
written_words += 1;
}
offset += chunk_size;
}
}
pub fn read_fast_read(&self, addr: u32, buf: &mut [u8], dummy_byte: bool) {
self.generic_read(addr, buf, dummy_byte, true)
}
/// Only works if the clock speed is slower than 50 MHz according to datasheet.
pub fn read_page_read(&self, addr: u32, buf: &mut [u8]) {
self.generic_read(addr, buf, false, false)
}
}
/// If the Spansion QSPI is used in linear addressed mode, no IO operations are allowed.
@@ -722,12 +618,10 @@ pub struct QspiSpansionS25Fl256SLinearMode(QspiLinearAddressing);
impl QspiSpansionS25Fl256SLinearMode {
pub const BASE_ADDR: usize = QspiLinearAddressing::BASE_ADDRESS;
pub const PAGE_SIZE: usize = PAGE_SIZE;
pub const SECTOR_SIZE: usize = SECTOR_SIZE;
pub fn into_io_mode(self, dual_flash: bool) -> QspiSpansionS25Fl256SIoMode {
let qspi = self.0.into_io_mode(dual_flash);
QspiSpansionS25Fl256SIoMode::new(qspi, Config::default())
QspiSpansionS25Fl256SIoMode::new(qspi, false)
}
pub fn read_guard(&mut self) -> QspiLinearReadGuard<'_> {
+1 -1
View File
@@ -9,7 +9,7 @@ repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
license = "MIT OR Apache-2.0"
[dependencies]
aarch32-cpu = { version = "0.2", features = ["critical-section-single-core"] }
aarch32-cpu = { version = "0.1", features = ["critical-section-single-core"] }
zynq7000-rt = { path = "../zynq7000-rt" }
zynq7000 = { path = "../zynq7000" }
zynq7000-hal = { path = "../zynq7000-hal" }
+2 -4
View File
@@ -1,7 +1,7 @@
MEMORY
{
/* The Zynq7000 has 256 kB of OCM memory of which 196 kB can be used for the FSBL */
CODE(rx) : ORIGIN = 0x00000000, LENGTH = 196K
/* The Zynq7000 has 192 kB of OCM memory which can be used for the FSBL */
CODE(rx) : ORIGIN = 0x00000000, LENGTH = 192K
OCM_UPPER(rx): ORIGIN = 0xFFFF0000, LENGTH = 64K
/* Leave 1 MB of memory which will be configured as uncached device memory by the MMU. This can
be used for something like DMA descriptors, but the DDR needs to be set up first in addition
@@ -11,8 +11,6 @@ MEMORY
REGION_ALIAS("VECTORS", CODE);
REGION_ALIAS("DATA", CODE);
/* Use the upper OCM as the stack */
REGION_ALIAS("STACKS", OCM_UPPER);
SECTIONS
{
+5 -54
View File
@@ -8,15 +8,13 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use arbitrary_int::traits::Integer as _;
use arbitrary_int::{u2, u6};
use arbitrary_int::u6;
use core::panic::PanicInfo;
use aarch32_cpu::asm::nop;
use embedded_io::Write as _;
use log::{error, info};
use zedboard_bsp::qspi_spansion::{self, QspiSpansionS25Fl256SLinearMode};
use zynq7000_boot_image::DestinationDevice;
use zynq7000_hal::clocks::ArmClocks;
use zynq7000_hal::priv_tim;
use zynq7000_hal::{
BootMode,
@@ -25,7 +23,7 @@ use zynq7000_hal::{
pll::{PllConfig, configure_arm_pll, configure_io_pll},
},
ddr::{DdrClockSetupConfig, configure_ddr_for_ddr3, memtest},
gic, gpio, l2_cache,
devcfg, gic, gpio, l2_cache,
prelude::*,
qspi::{self, QSPI_START_ADDRESS},
time::Hertz,
@@ -76,20 +74,6 @@ fn main() -> ! {
);
let mut periphs = zynq7000::Peripherals::take().unwrap();
l2_cache::disable();
// Initialize the ARM clock. Safety: We only run this once.
unsafe {
ArmClocks::new_with_cpu_clock_init(
ARM_CLK,
zynq7000_hal::clocks::CpuClockRatio::SixToTwoToOne,
u6::new(2),
);
// This is done by the AMD FSBL.
zynq7000_hal::Slcr::with(|val| {
val.gpiob().modify_ctrl(|val| val.with_vref_en(true));
});
}
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = Clocks::new_from_regs(PS_CLK).unwrap();
@@ -178,19 +162,11 @@ fn main() -> ! {
);
let qspi_io_mode = qspi.into_io_mode(false);
let spansion_qspi = qspi_spansion::QspiSpansionS25Fl256SIoMode::new(
qspi_io_mode,
qspi_spansion::Config {
set_quad_bit_if_necessary: true,
latency_config: Some(u2::ZERO),
clear_write_protection: true,
},
);
let spansion_qspi = qspi_spansion::QspiSpansionS25Fl256SIoMode::new(qspi_io_mode, true);
let spansion_lqspi =
spansion_qspi.into_linear_addressed(qspi_spansion::QSPI_DEV_COMBINATION_REV_F.into());
qspi_boot(spansion_lqspi, priv_tim);
}
loop {
aarch32_cpu::asm::nop();
}
@@ -280,7 +256,7 @@ fn qspi_boot(mut qspi: QspiSpansionS25Fl256SLinearMode, _priv_tim: priv_tim::Cpu
};
// The DMA will read from the linear mapped QSPI directly, so it
// has to be configured for reads using the guard!
zynq7000_hal::pl::configure_bitstream_non_secure(true, boot_bin_slice)
devcfg::configure_bitstream_non_secure(true, boot_bin_slice)
.expect("unexpected unaligned address");
log::info!("loaded bitstream successfully");
}
@@ -328,10 +304,6 @@ fn qspi_boot(mut qspi: QspiSpansionS25Fl256SLinearMode, _priv_tim: priv_tim::Cpu
}
}
// The PL is in reset state after power-up. This method needs to be called in the first-stage
// bootloader to put it out of reset.
zynq7000_hal::pl::deassert_reset();
match opt_jump_addr {
Some(jump_addr) => {
log::info!("jumping to address {}", jump_addr);
@@ -341,7 +313,6 @@ fn qspi_boot(mut qspi: QspiSpansionS25Fl256SLinearMode, _priv_tim: priv_tim::Cpu
zynq7000_hal::cache::clean_and_invalidate_data_cache();
aarch32_cpu::register::TlbIAll::write();
aarch32_cpu::register::BpIAll::write();
l2_cache::disable();
aarch32_cpu::asm::dsb();
aarch32_cpu::asm::isb();
@@ -352,26 +323,6 @@ fn qspi_boot(mut qspi: QspiSpansionS25Fl256SLinearMode, _priv_tim: priv_tim::Cpu
}
}
#[zynq7000_rt::irq]
fn interrupt_handler() {
let mut gic_helper = gic::GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
gic::Interrupt::Sgi(_) => (),
gic::Interrupt::Ppi(ppi_interrupt) => {
log::warn!("unexpected PPI interrupt: {:?}", ppi_interrupt);
}
gic::Interrupt::Spi(spi_interrupt) => {
log::warn!("unexpected SPI interrupt: {:?}", spi_interrupt);
}
gic::Interrupt::Invalid(_) => (),
gic::Interrupt::Spurious => {
log::warn!("spurious interrupt");
}
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
fn data_abort_handler(_faulting_addr: usize) -> ! {
loop {
@@ -1 +0,0 @@
/boot.bin
+1 -2
View File
@@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2024"
[dependencies]
aarch32-cpu = { version = "0.2", features = ["critical-section-single-core"] }
aarch32-cpu = { version = "0.1", features = ["critical-section-single-core"] }
zynq7000-rt = { path = "../zynq7000-rt" }
zynq7000 = { path = "../zynq7000" }
zynq7000-hal = { path = "../zynq7000-hal" }
@@ -12,6 +12,5 @@ zynq7000-boot-image = { path = "../../host/zynq7000-boot-image" }
zedboard-bsp = { path = "../zedboard-bsp" }
embedded-io = "0.7"
embedded-hal = "1"
arbitrary-int = "2"
log = "0.4"
libm = "0.2"
+1 -14
View File
@@ -3,17 +3,4 @@ Zedboard QSPI flasher
This application flashes a boot binary generated by the AMD `bootgen` utility from DDR
to the Zedboard QSPI. This project contains a `qspi-flasher.tcl` script which can be invoked
with `xsct` to flash a `boot.bin` and the QSPI flasher to DDR and then run the application.
The main `justfile` provides a convenience runner:
```sh
just flash-nor-zedboard <path to my boot.bin>
```
Please note that `xsct` must be callable for this to be usable which is part of a Xilinx Vitis installation.
If the hardware server is running on a remote target the IP address can be specified by setting the environment variable ip_address_hw_server.
````sh
$ export ip_address_hw_server=<ip-address>
````
with `xsct` to flash a `boot.bin` and the QSPI flasher to DDR adn then run the application.
+4 -6
View File
@@ -1,11 +1,9 @@
MEMORY
{
/* Zedboard: 512 MB DDR3. Only use 62 MB for now, should be plenty for a bare-metal app.
1 MB stack memory and 1 MB of memory which will be configured as uncached device memory by the
MMU. This is recommended for something like DMA descriptors. */
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 62M
OCM_UPPER(rx): ORIGIN = 0xFFFF0000, LENGTH = 64K
STACKS : ORIGIN = 0x3F00000, LENGTH = 1M
/* 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 MMU. This is
recommended for something like DMA descriptors. */
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 63M
UNCACHED(rx): ORIGIN = 0x4000000, LENGTH = 1M
}
+9 -16
View File
@@ -4,7 +4,6 @@
#![no_main]
use aarch32_cpu::asm::nop;
use arbitrary_int::{traits::Integer as _, u2};
use core::panic::PanicInfo;
use embedded_hal::{delay::DelayNs as _, digital::StatefulOutputPin as _};
use embedded_io::Write as _;
@@ -98,14 +97,7 @@ fn main() -> ! {
let qspi_io_mode = qspi.into_io_mode(false);
let mut spansion_qspi = qspi_spansion::QspiSpansionS25Fl256SIoMode::new(
qspi_io_mode,
qspi_spansion::Config {
set_quad_bit_if_necessary: true,
latency_config: Some(u2::ZERO),
clear_write_protection: true,
},
);
let mut spansion_qspi = qspi_spansion::QspiSpansionS25Fl256SIoMode::new(qspi_io_mode, true);
let mut boot_bin_slice = unsafe {
core::slice::from_raw_parts(BOOT_BIN_BASE_ADDR as *const _, BootHeader::FIXED_SIZED_PART)
@@ -129,7 +121,7 @@ fn main() -> ! {
);
let mut current_addr = 0;
let mut read_buf = [0u8; qspi_spansion::PAGE_SIZE];
let mut read_buf = [0u8; 256];
let mut next_checkpoint = 0.05;
while current_addr < boot_bin_size {
if current_addr % 0x10000 == 0 {
@@ -145,13 +137,10 @@ fn main() -> ! {
}
}
}
let write_size = core::cmp::min(
qspi_spansion::RECOMMENDED_PROGRAM_PAGE_SIZE,
boot_bin_size - current_addr,
);
let write_size = core::cmp::min(256, boot_bin_size - current_addr);
let write_slice = &boot_bin_slice[current_addr..current_addr + write_size];
log::debug!("Programming address {:#x}", current_addr);
match spansion_qspi.program(current_addr as u32, write_slice) {
match spansion_qspi.program_page(current_addr as u32, write_slice) {
Ok(()) => {}
Err(e) => {
log::error!(
@@ -163,7 +152,11 @@ fn main() -> ! {
}
}
if VERIFY_PROGRAMMING {
spansion_qspi.read_fast_read(current_addr as u32, &mut read_buf[0..write_size], true);
spansion_qspi.read_page_fast_read(
current_addr as u32,
&mut read_buf[0..write_size],
true,
);
if &read_buf[0..write_size] != write_slice {
error!(
"data verification failed at address {:#x}: wrote {:x?}, read {:x?}",
+2 -7
View File
@@ -8,14 +8,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased]
# [v0.1.1] 2026-03-13
- Try to fix docs build for docs.rs
# [v0.1.0] 2026-02-14
# [v0.1.0] 2025-10-09
Initial release
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-embassy-v0.1.0...HEAD
[v0.1.1]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-embassy-v0.1.0...zynq7000-embassy-v0.1.1
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/v0.1.0...HEAD
[v0.1.0]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/tag/zynq7000-embassy-v0.1.0
+2 -6
View File
@@ -1,9 +1,9 @@
[package]
name = "zynq7000-embassy"
version = "0.1.1"
version = "0.1.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2024"
description = "Embassy time support for the Zynq7000 family of SoCs"
description = "Embassy-rs support for the Zynq7000 family of SoCs"
homepage = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
license = "MIT OR Apache-2.0"
@@ -17,7 +17,3 @@ zynq7000-hal = { path = "../zynq7000-hal", version = "0.1" }
embassy-time-driver = "0.2"
embassy-time-queue-utils = "0.3"
[package.metadata.docs.rs]
targets = ["armv7a-none-eabihf"]
rustdoc-args = ["--generate-link-to-definition"]
+5 -4
View File
@@ -2,10 +2,11 @@
[![docs.rs](https://img.shields.io/docsrs/zynq7000-embassy)](https://docs.rs/zynq7000-embassy)
[![ci](https://github.com/us-irs/zynq7000-rs/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/us-irs/zynq7000-rs/actions/workflows/ci.yml)
# Embassy time support for the AMD Zynq7000 SoC family
# Embassy-rs support for the AMD Zynq7000 SoC family
This repository contains the [embassy-rs](https://github.com/embassy-rs/embassy) time support for
the AMD Zynq7000 SoC family. It currently provides one driver using the global timer peripheral
provided by the Zynq7000 PS for this purpose.
This repository contains the [embassy-rs](https://github.com/embassy-rs/embassy) support for the
AMD Zynq7000 SoC family. Currently, it contains the time driver to allow using embassy-rs. It
currently provides one driver using the global timer peripheral provided by the Zynq7000 PS for
this purpose.
The documentation contains more information on how to use this crate.
-7
View File
@@ -1,10 +1,3 @@
//! # Embassy time support for the AMD Zynq7000 SoC family
//!
//! This project contains the [embassy-rs](https://github.com/embassy-rs/embassy) time support for
//! the AMD Zynq7000 SoC family. It currently provides one driver using the global timer peripheral
//! provided by the Zynq7000 PS for this purpose.
//!
//! The [crate::init] method must be called once for the time driver to work properly.
#![no_std]
use core::cell::{Cell, RefCell};
+3 -17
View File
@@ -8,28 +8,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased]
## Fixed
## Changed
- Bugfix for DDR initialization: `calibrate_iob_impedance_for_ddr3` and `calibrate_iob_impedance`
now expect a `zynq7000::slcr::ddriob::DdrControl` input argument. This register write was
missing
- Increased UART type safety by providing dedicated MIO constructors for UART 0 and UART 1
respectively.
- Several bugfixes and improvements for GIC module. Some of the registers previously were
completely overwritten instead of only modifying their own bit portions. Also allow targeting
interrupts without clearing other CPU target.
## Changed
- `devcfg` moved to `pl` module
- Added division by zero check in gtc frequency_to_ticks to avoid runtime panic
- Increased UART type safety by providing dedicated MIO constructors for UART 0 and UART 1
respectively.
## Added
- Method to de-assert PL reset.
- ARM clock initialization for the `ArmClocks` structure
- The `ArmClocks` structure now caches the CPU clock ratio
# [v0.1.1] 2025-10-10
Documentation fixes.
+9 -9
View File
@@ -11,11 +11,12 @@ keywords = ["no-std", "hal", "amd", "zynq7000", "bare-metal"]
categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
aarch32-cpu = { version = "0.2" }
zynq7000 = { path = "../zynq7000", version = "0.2" }
aarch32-cpu = { version = "0.1" }
zynq7000 = { path = "../zynq7000", version = "0.1" }
zynq7000-mmu = { path = "../zynq7000-mmu", version = "0.1" }
static_assertions = "1.1"
bitbybit = "2"
bitbybit = "1.4"
arbitrary-int = "2"
thiserror = { version = "2", default-features = false }
num_enum = { version = "0.7", default-features = false }
@@ -28,27 +29,25 @@ embedded-hal-async = "1"
heapless = "0.9"
static_cell = "2"
delegate = "0.13"
pastey = "0.2.1"
paste = "1"
nb = "1"
fugit = "0.3"
critical-section = "1"
libm = "0.2"
log = "0.4"
embassy-sync = "0.8"
embassy-sync = "0.7"
embassy-net-driver = "0.2"
smoltcp = { version = "0.13", default-features = false, features = ["proto-ipv4", "medium-ethernet", "socket-raw"] }
smoltcp = { version = "0.12", default-features = false, features = ["proto-ipv4", "medium-ethernet", "socket-raw"] }
vcell = "0.1"
raw-slicee = "0.1"
embedded-io-async = "0.7"
serde = { version = "1", optional = true, features = ["derive"] }
defmt = { version = "1", optional = true }
embedded-sdmmc = { git = "https://github.com/robamu/embedded-sdmmc-rs.git", branch = "all-features" }
bytemuck = "1.25"
[features]
std = ["thiserror/std", "alloc"]
alloc = []
defmt = ["dep:defmt", "fugit/defmt", "zynq7000/defmt"]
defmt = ["dep:defmt", "fugit/defmt"]
# These devices have a lower pin count.
7z010-7z007s-clg225 = []
@@ -58,4 +57,5 @@ approx = "0.5"
[package.metadata.docs.rs]
features = ["alloc"]
targets = ["armv7a-none-eabihf"]
cargo-args = ["-Z", "build-std=core,alloc"]
rustdoc-args = ["--generate-link-to-definition"]
+2 -2
View File
@@ -5,12 +5,12 @@
# HAL for the AMD Zynq 7000 SoC family
This repository contains the **H**ardware **A**bstraction **L**ayer (HAL), which is an additional
hardware abstraction on top of the [peripheral access API](../zynq7000).
hardware abstraction on top of the [peripheral access API](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/zynq7000).
It is the result of reading the datasheet for the device and encoding a type-safe layer over the
raw PAC. This crate also implements traits specified by the
[embedded-hal](https://github.com/rust-embedded/embedded-hal) project, making it compatible with
various drivers in the embedded rust ecosystem.
The [top-level README](../../README.md) and the documentation
The [top-level README](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs) and the documentation
contain more information on how to use this crate.
+5 -69
View File
@@ -3,12 +3,11 @@ use arbitrary_int::{prelude::*, u6};
pub mod pll;
pub use zynq7000::slcr::clocks::CpuClockRatio;
use zynq7000::slcr::{
ClockControlRegisters,
clocks::{
ArmClockControl, ClockRatioSelectReg, DualCommonPeriphIoClockControl, FpgaClockControl,
GigEthClockControl, SingleCommonPeriphIoClockControl,
ClockkRatioSelect, DualCommonPeriphIoClockControl, FpgaClockControl, GigEthClockControl,
SingleCommonPeriphIoClockControl,
},
};
@@ -18,7 +17,6 @@ use super::time::Hertz;
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ArmClocks {
ref_clk: Hertz,
ratio: CpuClockRatio,
cpu_1x_clk: Hertz,
cpu_2x_clk: Hertz,
cpu_3x2x_clk: Hertz,
@@ -26,82 +24,23 @@ pub struct ArmClocks {
}
impl ArmClocks {
/// Configure the ARM clocks based on the ARM PLL input clock.
///
/// # Safety
///
/// This changes the CPU clock frequency. You must pass the ARM PLL clock frequency and
/// you must ensure that this is only run once during system initialization, for example
/// in the first-stage bootloader.
pub unsafe fn new_with_cpu_clock_init(
arm_pll_clk: Hertz,
clock_ratio: CpuClockRatio,
divisor: u6,
) -> Self {
unsafe {
crate::slcr::Slcr::with(|slcr| {
slcr.clk_ctrl().write_clk_ratio_select(
ClockRatioSelectReg::builder().with_sel(clock_ratio).build(),
);
slcr.clk_ctrl().write_arm_clk_ctrl(
ArmClockControl::builder()
.with_cpu_peri_clk_act(true)
.with_cpu_1x_clk_act(true)
.with_cpu_2x_clk_act(true)
.with_cpu_3or2x_clk_act(true)
.with_cpu_6or4x_clk_act(true)
.with_divisor(divisor)
.with_srcsel(zynq7000::slcr::clocks::SrcSelArm::ArmPll)
.build(),
);
});
}
let cpu_6x4x_clk = arm_pll_clk / divisor.as_u32();
let cpu_1x_clk = match clock_ratio {
CpuClockRatio::FourToTwoToOne => cpu_6x4x_clk / 4,
CpuClockRatio::SixToTwoToOne => cpu_6x4x_clk / 6,
};
Self {
ref_clk: arm_pll_clk,
ratio: clock_ratio,
cpu_1x_clk,
cpu_2x_clk: cpu_1x_clk * 2,
cpu_3x2x_clk: match clock_ratio {
CpuClockRatio::SixToTwoToOne => cpu_1x_clk * 3,
CpuClockRatio::FourToTwoToOne => cpu_1x_clk * 2,
},
cpu_6x4x_clk,
}
}
#[inline]
pub const fn ratio(&self) -> CpuClockRatio {
self.ratio
}
/// Reference clock provided by ARM PLL which is used to calculate all other clock frequencies.
#[inline]
pub const fn ref_clk(&self) -> Hertz {
self.ref_clk
}
#[inline]
pub const fn cpu_1x_clk(&self) -> Hertz {
self.cpu_1x_clk
}
#[inline]
pub const fn cpu_2x_clk(&self) -> Hertz {
self.cpu_2x_clk
}
#[inline]
pub const fn cpu_3x2x_clk(&self) -> Hertz {
self.cpu_3x2x_clk
}
#[inline]
pub const fn cpu_6x4x_clk(&self) -> Hertz {
self.cpu_6x4x_clk
}
@@ -258,7 +197,6 @@ impl IoClocks {
}
}
// TODO: Display impl for clock config.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Clocks {
@@ -339,27 +277,25 @@ impl Clocks {
zynq7000::slcr::clocks::SrcSelArm::DdrPll => ddr_pll_out,
zynq7000::slcr::clocks::SrcSelArm::IoPll => io_pll_out,
};
let clk_sel = clk_regs.read_clk_ratio_select();
let clk_sel = clk_regs.read_clk_621_true();
if arm_clk_ctrl.divisor().as_u32() == 0 {
return Err(ClockReadError::DivisorZero(DivisorZero(ClockModuleId::Arm)));
}
let arm_clk_divided = arm_base_clk / arm_clk_ctrl.divisor().as_u32();
let arm_clks = match clk_sel.sel() {
CpuClockRatio::FourToTwoToOne => ArmClocks {
ClockkRatioSelect::FourToTwoToOne => ArmClocks {
ref_clk: arm_pll_out,
cpu_1x_clk: arm_clk_divided / 4,
cpu_2x_clk: arm_clk_divided / 2,
cpu_3x2x_clk: arm_clk_divided / 2,
cpu_6x4x_clk: arm_clk_divided,
ratio: clk_sel.sel(),
},
CpuClockRatio::SixToTwoToOne => ArmClocks {
ClockkRatioSelect::SixToTwoToOne => ArmClocks {
ref_clk: arm_pll_out,
cpu_1x_clk: arm_clk_divided / 6,
cpu_2x_clk: arm_clk_divided / 3,
cpu_3x2x_clk: arm_clk_divided / 2,
cpu_6x4x_clk: arm_clk_divided,
ratio: clk_sel.sel(),
},
};
+5 -5
View File
@@ -196,7 +196,7 @@ impl PllConfig {
/// This function configures the ARM PLL based on the provided [PllConfig].
pub fn configure_arm_pll(boot_mode: BootMode, pll_config: PllConfig) {
if ARM_PLL_INIT.swap(true, core::sync::atomic::Ordering::Relaxed) {
if ARM_PLL_INIT.swap(true, core::sync::atomic::Ordering::SeqCst) {
return;
}
// Safety: This will only run at most once because of the atomic boolean check.
@@ -205,20 +205,20 @@ pub fn configure_arm_pll(boot_mode: BootMode, pll_config: PllConfig) {
/// This function configures the IO PLL based on the provided [PllConfig].
pub fn configure_io_pll(boot_mode: BootMode, pll_config: PllConfig) {
if IO_PLL_INIT.swap(true, core::sync::atomic::Ordering::Relaxed) {
if IO_PLL_INIT.swap(true, core::sync::atomic::Ordering::SeqCst) {
return;
}
// Safety: This will only run at most once because of the atomic boolean check.
unsafe { configure_io_pll_unchecked(boot_mode, pll_config) };
unsafe { configure_arm_pll_unchecked(boot_mode, pll_config) };
}
/// This function configures the DDR PLL based on the provided [PllConfig].
pub fn configure_ddr_pll(boot_mode: BootMode, pll_config: PllConfig) {
if DDR_PLL_INIT.swap(true, core::sync::atomic::Ordering::Relaxed) {
if DDR_PLL_INIT.swap(true, core::sync::atomic::Ordering::SeqCst) {
return;
}
// Safety: This will only run at most once because of the atomic boolean check.
unsafe { configure_ddr_pll_unchecked(boot_mode, pll_config) };
unsafe { configure_arm_pll_unchecked(boot_mode, pll_config) };
}
/// This function configures the ARM PLL based on the provided [PllConfig].
+31 -43
View File
@@ -86,42 +86,20 @@ pub unsafe fn configure_dci(ddr_clk: &DdrClocks) {
///
/// This function writes to the DDR IOB related registers. It should only be called once during
/// DDR initialization.
pub unsafe fn calibrate_iob_impedance_for_ddr3(
ddr_control: zynq7000::slcr::ddriob::DdrControl,
dci_clk_cfg: DciClkConfig,
poll_for_done: bool,
) {
pub unsafe fn calibrate_iob_impedance_for_ddr3(dci_clk_cfg: DciClkConfig, poll_for_done: bool) {
unsafe {
calibrate_iob_impedance(
ddr_control,
dci_clk_cfg,
CalibrationParams::new_ddr3(),
u3::new(0),
u2::new(0),
u3::new(0b001),
u3::new(0),
u2::new(0),
poll_for_done,
);
}
}
/// DDR IOB impedance calibration parameters.
pub struct CalibrationParams {
pub pref_opt2: u3,
pub pref_opt1: u2,
pub nref_opt4: u3,
pub nref_opt2: u3,
pub nref_opt1: u2,
}
impl CalibrationParams {
pub const fn new_ddr3() -> Self {
Self {
pref_opt2: u3::new(0),
pref_opt1: u2::new(0),
nref_opt4: u3::new(0b001),
nref_opt2: u3::new(0),
nref_opt1: u2::new(0),
}
}
}
/// Calibrates the IOB impedance according to to TRM p.325, DDR IOB Impedance calibration.
///
/// This function will also enable the DCI clock with the provided clock configuration.
@@ -137,9 +115,12 @@ impl CalibrationParams {
/// This function writes to the DDR IOB related registers. It should only be called once during
/// DDR initialization.
pub unsafe fn calibrate_iob_impedance(
ddr_control: zynq7000::slcr::ddriob::DdrControl,
dci_clk_cfg: DciClkConfig,
calibration_params: CalibrationParams,
pref_opt2: u3,
pref_opt1: u2,
nref_opt4: u3,
nref_opt2: u3,
nref_opt1: u2,
poll_for_done: bool,
) {
// Safety: Only writes to DDR IOB related registers.
@@ -153,23 +134,31 @@ pub unsafe fn calibrate_iob_impedance(
.build(),
);
let mut ddriob = slcr.ddriob();
ddriob.write_ddr_control(ddr_control);
ddriob.modify_dci_control(|val| val.with_reset(true));
ddriob.modify_dci_control(|val| val.with_reset(false));
ddriob.modify_dci_control(|val| val.with_reset(true));
ddriob.modify_dci_control(|mut val| {
val.set_pref_opt2(calibration_params.pref_opt2);
val.set_pref_opt1(calibration_params.pref_opt1);
val.set_nref_opt4(calibration_params.nref_opt4);
val.set_nref_opt2(calibration_params.nref_opt2);
val.set_nref_opt1(calibration_params.nref_opt1);
ddriob.modify_dci_ctrl(|mut val| {
val.set_reset(true);
val
});
ddriob.modify_dci_control(|mut val| {
ddriob.modify_dci_ctrl(|mut val| {
val.set_reset(false);
val
});
ddriob.modify_dci_ctrl(|mut val| {
val.set_reset(true);
val
});
ddriob.modify_dci_ctrl(|mut val| {
val.set_pref_opt2(pref_opt2);
val.set_pref_opt1(pref_opt1);
val.set_nref_opt4(nref_opt4);
val.set_nref_opt2(nref_opt2);
val.set_nref_opt1(nref_opt1);
val
});
ddriob.modify_dci_ctrl(|mut val| {
val.set_update_control(false);
val
});
ddriob.modify_dci_control(|mut val| {
ddriob.modify_dci_ctrl(|mut val| {
val.set_enable(true);
val
});
@@ -181,7 +170,6 @@ pub unsafe fn calibrate_iob_impedance(
/// Static configuration for DDR IOBs.
pub struct DdriobConfigSet {
pub ddr_control: zynq7000::slcr::ddriob::DdrControl,
pub addr0: DdriobConfig,
pub addr1: DdriobConfig,
pub data0: DdriobConfig,
+2 -2
View File
@@ -76,7 +76,7 @@ pub fn configure_ddr_for_ddr3(
ll::configure_iob(ddriob_cfg);
// Do not wait for completion, it takes a bit of time. We can set all the DDR config registers
// before polling for completion.
ll::calibrate_iob_impedance_for_ddr3(ddriob_cfg.ddr_control, dci_clk_cfg, false);
ll::calibrate_iob_impedance_for_ddr3(dci_clk_cfg, false);
}
ll::configure_ddr_config(&mut ddrc_regs, ddr_cfg);
// Safety: This is only called once during DDR initialization, and we only modify DDR related
@@ -129,7 +129,7 @@ pub mod memtest {
/// This tests writes and reads on a memory block starting at the base address
/// with the size `words` times 4.
pub unsafe fn walking_one_test(base_addr: usize, words: usize) -> Result<(), MemTestError> {
unsafe { walking_value_test(false, base_addr, words) }
unsafe { walking_value_test(true, base_addr, words) }
}
/// # Safety
@@ -1,33 +1,4 @@
//! # Programmable Logic (PL) support module.
//!
//! Provides the [configure_bitstream_non_secure] method to program the PL using the device
//! configuration (`devcfg`) peripheral.
use arbitrary_int::{traits::Integer as _, u17};
use zynq7000::slcr::reset::FpgaResetControl;
use crate::slcr::Slcr;
/// Put the PL out of reset.
///
/// The PL is in reset state after power-up. This method should be called in the first-stage
/// bootloader to put it out of reset.
pub fn deassert_reset() {
// Safety: We only touch the PL reset register here.
unsafe {
Slcr::with(|slcr| {
slcr.reset_ctrl().write_fpga(
FpgaResetControl::builder()
.with_zero_block_0(u17::ZERO)
.with_fpga_3(false)
.with_fpga_2(false)
.with_fpga_1(false)
.with_fpga_0(false)
.build(),
);
})
};
}
//! # Device Configuration Module
#[derive(Debug, thiserror::Error)]
#[error("unaligned address: {0}")]
pub struct UnalignedAddrError(usize);
@@ -82,14 +53,10 @@ pub fn configure_bitstream_non_secure(
val.set_pcap_rate_enable(false);
val
});
// As specified in the TMR,
// Setting the two LSBs of the source and destination address to 2'b01 indicates to the DevC
// DMA module the last DMA command of an overall transfer
devcfg.write_dma_source_addr(bitstream.as_ptr() as u32 | 0b01);
devcfg.write_dma_source_addr(bitstream.as_ptr() as u32);
devcfg.write_dma_dest_addr(0xFFFF_FFFF);
devcfg.write_dma_source_len(bitstream.len() as u32 / 4);
devcfg.write_dma_dest_len(bitstream.len() as u32 / 4);
devcfg.write_dma_source_len(bitstream.len() as u32);
devcfg.write_dma_dest_len(bitstream.len() as u32);
while !devcfg.read_interrupt_status().dma_done() {}
// TODO: Check for errors.
+33 -45
View File
@@ -8,6 +8,11 @@ use crate::{clocks::IoClocks, enable_amba_peripheral_clock, slcr::Slcr, time::He
use super::{EthernetId, PsEthernet as _};
pub struct EthernetLowLevel {
id: EthernetId,
pub regs: zynq7000::eth::MmioRegisters<'static>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Speed {
Mbps10,
@@ -47,10 +52,7 @@ impl ClockDivisors {
/// Calls [Self::calculate_for_rgmii], assuming that the IO clock is the reference clock,
/// which is the default clock for the Ethernet module.
pub fn calculate_for_rgmii_and_io_clock(
io_clks: &IoClocks,
target_speed: Speed,
) -> (Self, u32) {
pub fn calculate_for_rgmii_and_io_clock(io_clks: IoClocks, target_speed: Speed) -> (Self, u32) {
Self::calculate_for_rgmii(io_clks.ref_clk(), target_speed)
}
@@ -172,17 +174,8 @@ impl ClockDivSet {
/// Ethernet low-level interface.
///
/// Basic building block for higher-level abstraction.
pub struct EthernetLowLevel {
id: EthernetId,
/// Register block. Direct public access is allowed to allow low-level operations.
pub regs: zynq7000::eth::MmioRegisters<'static>,
}
impl EthernetLowLevel {
/// Creates a new instance of the Ethernet low-level interface.
///
/// Returns [None] if the given registers block base address does not correspond to a valid
/// Ethernet peripheral.
#[inline]
pub fn new(regs: zynq7000::eth::MmioRegisters<'static>) -> Option<Self> {
regs.id()?;
@@ -211,7 +204,33 @@ impl EthernetLowLevel {
}
pub fn reset(&mut self, cycles: usize) {
reset(self.id, cycles);
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 {
aarch32_cpu::asm::nop();
}
regs.reset_ctrl().write_eth(EthernetReset::DEFAULT);
});
}
}
#[inline]
@@ -364,34 +383,3 @@ impl EthernetLowLevel {
self.id
}
}
/// Resets the Ethernet peripheral with the given ID.
pub fn reset(id: EthernetId, cycles: usize) {
let assert_reset = match 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 {
aarch32_cpu::asm::nop();
}
regs.reset_ctrl().write_eth(EthernetReset::DEFAULT);
});
}
}
+1 -1
View File
@@ -84,7 +84,7 @@ pub trait PinId {
macro_rules! pin_id {
($Id:ident, $num:literal) => {
// Need paste macro to use ident in doc attribute
pastey::paste! {
paste::paste! {
#[doc = "Pin ID representing pin " $Id]
#[derive(Debug)]
pub enum $Id {}
+1 -5
View File
@@ -18,11 +18,7 @@ unsafe impl Send for GlobalTimerCounter {}
/// Convert a frequency to GTC ticks given a clock frequency.
pub const fn frequency_to_ticks(clock: Hertz, frequency: Hertz) -> u32 {
if frequency.raw() != 0 {
clock.raw().div_ceil(frequency.raw())
} else {
0
}
clock.raw().div_ceil(frequency.raw())
}
impl GlobalTimerCounter {
-7
View File
@@ -81,10 +81,3 @@ pub fn init(
}
l2c_mmio.write_control(Control::new_enabled());
}
/// Disable the L2 cache.
#[inline]
pub fn disable() {
let mut l2c_mmio = unsafe { zynq7000::l2_cache::Registers::new_mmio_fixed() };
l2c_mmio.write_control(Control::new_disabled());
}
+2 -3
View File
@@ -18,7 +18,7 @@
#[cfg(feature = "alloc")]
extern crate alloc;
pub use slcr::Slcr;
use slcr::Slcr;
use zynq7000::{
SpiClockPhase, SpiClockPolarity,
slcr::{BootModeRegister, BootPllConfig, LevelShifterRegister},
@@ -27,6 +27,7 @@ use zynq7000::{
pub mod cache;
pub mod clocks;
pub mod ddr;
pub mod devcfg;
pub mod eth;
pub mod gic;
pub mod gpio;
@@ -34,11 +35,9 @@ pub mod gtc;
pub mod i2c;
pub mod l2_cache;
pub mod log;
pub mod pl;
pub mod prelude;
pub mod priv_tim;
pub mod qspi;
pub mod sd;
pub mod slcr;
pub mod spi;
pub mod time;
+9 -17
View File
@@ -8,7 +8,7 @@ use zynq7000::{
BaudRateDivisor, Config, InstructionCode, InterruptStatus, LoopbackMasterClockDelay,
SpiEnable,
},
slcr::{clocks::SingleCommonPeriphIoClockControl, mio::Speed, reset::ResetControlQspiSmc},
slcr::{clocks::SingleCommonPeriphIoClockControl, mio::Speed, reset::QspiResetControl},
};
pub use embedded_hal::spi::{MODE_0, MODE_1, MODE_2, MODE_3, Mode};
@@ -35,12 +35,6 @@ pub(crate) mod lqspi_configs;
pub const QSPI_MUX_CONFIG: MuxConfig = MuxConfig::new_with_l0();
pub const FIFO_DEPTH: usize = 63;
/// From the TRM:
///
/// > The maximum number of bytes per command sequence in this mode is limited by the depth of the
/// > TxFIFO of 252 bytes.
pub const MAX_BYTES_PER_TRANSFER_IO_MODE: usize = FIFO_DEPTH * 4;
/// In linear-addressed mode, the QSPI is memory-mapped, with the address starting here.
pub const QSPI_START_ADDRESS: usize = 0xFC00_0000;
@@ -267,9 +261,7 @@ impl ClockConfig {
if qspi_ref_clk < clocks.arm_clocks().cpu_1x_clk() {
return Err(ClockCalculationError::RefClockSmallerThanCpu1xClock);
}
let qspi_baud_rate_div = qspi_ref_clk
.raw()
.div_ceil(target_qspi_interface_clock.raw());
let qspi_baud_rate_div = qspi_ref_clk / target_qspi_interface_clock;
let baud_rate_div = match qspi_baud_rate_div {
0..=2 => BaudRateDivisor::_2,
3..=4 => BaudRateDivisor::_4,
@@ -417,7 +409,7 @@ impl Qspi {
.with_disable_hstl_rcvr(false)
.with_pullup(true)
.with_io_type(voltage)
.with_speed(Speed::FastCmosEdge)
.with_speed(Speed::SlowCmosEdge)
.with_l3_sel(QSPI_MUX_CONFIG.l3_sel())
.with_l2_sel(QSPI_MUX_CONFIG.l2_sel())
.with_l1_sel(QSPI_MUX_CONFIG.l1_sel())
@@ -429,7 +421,7 @@ impl Qspi {
.with_disable_hstl_rcvr(false)
.with_pullup(false)
.with_io_type(voltage)
.with_speed(Speed::FastCmosEdge)
.with_speed(Speed::SlowCmosEdge)
.with_l3_sel(QSPI_MUX_CONFIG.l3_sel())
.with_l2_sel(QSPI_MUX_CONFIG.l2_sel())
.with_l1_sel(QSPI_MUX_CONFIG.l1_sel())
@@ -471,7 +463,7 @@ impl Qspi {
.with_disable_hstl_rcvr(false)
.with_pullup(false)
.with_io_type(voltage)
.with_speed(Speed::FastCmosEdge)
.with_speed(Speed::SlowCmosEdge)
.with_l3_sel(QSPI_MUX_CONFIG.l3_sel())
.with_l2_sel(QSPI_MUX_CONFIG.l2_sel())
.with_l1_sel(QSPI_MUX_CONFIG.l1_sel())
@@ -675,16 +667,16 @@ pub fn reset() {
unsafe {
Slcr::with(|regs| {
regs.reset_ctrl().write_lqspi(
ResetControlQspiSmc::builder()
.with_ref_reset(true)
QspiResetControl::builder()
.with_qspi_ref_reset(true)
.with_cpu_1x_reset(true)
.build(),
);
// Keep it in reset for some cycles.
for _ in 0..10 {
for _ in 0..3 {
aarch32_cpu::asm::nop();
}
regs.reset_ctrl().write_lqspi(ResetControlQspiSmc::DEFAULT);
regs.reset_ctrl().write_lqspi(QspiResetControl::DEFAULT);
});
}
}
-132
View File
@@ -1,132 +0,0 @@
use arbitrary_int::u6;
use zynq7000::sdio::{BlockSelect, CommandRegister, ResponseType};
use embedded_sdmmc::sdcard::{AcmdId, CmdId};
pub struct CommandConfig {
pub id: u6,
pub response_type: ResponseType,
pub index_check: bool,
pub crc_check: bool,
}
impl CommandConfig {
pub const fn new_no_response(id: u6) -> Self {
Self {
id,
response_type: ResponseType::None,
index_check: false,
crc_check: false,
}
}
pub const fn new_with_r1_response(id: u6) -> Self {
Self {
id,
response_type: ResponseType::_48bits,
index_check: true,
crc_check: true,
}
}
pub const fn new_with_r2_response(id: u6) -> Self {
Self {
id,
response_type: ResponseType::_136bits,
index_check: false,
crc_check: true,
}
}
pub const fn new_with_r3_response(id: u6) -> Self {
Self {
id,
response_type: ResponseType::_48bits,
index_check: false,
crc_check: false,
}
}
pub const fn new_with_r6_response(id: u6) -> Self {
Self {
id,
response_type: ResponseType::_48bitsWithCheck,
index_check: false,
crc_check: false,
}
}
}
pub const fn build_command_without_data(config: CommandConfig) -> CommandRegister {
CommandRegister::builder()
.with_command_index(config.id)
.with_command_type(zynq7000::sdio::CommandType::Normal)
.with_data_is_present(false)
.with_command_index_check_enable(config.index_check)
.with_command_crc_check_enable(config.crc_check)
.with_response_type_select(config.response_type)
.with_block_select(zynq7000::sdio::BlockSelect::SingleBlock)
.with_data_transfer_direction(zynq7000::sdio::TransferDirection::Write)
.with_auto_cmd12_enable(false)
.with_block_count_enable(false)
.with_dma_enable(false)
.build()
}
pub const CMD0_GO_IDLE_MODE: CommandRegister = build_command_without_data(
CommandConfig::new_no_response(CmdId::CMD0_GoIdleState.raw_value()),
);
pub const CMD2_ALL_SEND_CID: CommandRegister = build_command_without_data(
CommandConfig::new_with_r2_response(CmdId::CMD2_AllSendCid.raw_value()),
);
pub const CMD3_SEND_RELATIVE_ADDR: CommandRegister = build_command_without_data(
CommandConfig::new_with_r6_response(CmdId::CMD3_SendRelativeAddr.raw_value()),
);
pub const CMD7_SELECT_SD_CARD: CommandRegister = build_command_without_data(
CommandConfig::new_with_r1_response(CmdId::CMD7_SelectCard.raw_value()),
);
pub const CMD8_SEND_IF_COND: CommandRegister = build_command_without_data(
CommandConfig::new_with_r1_response(CmdId::CMD8_SendIfCond.raw_value()),
);
pub const CMD9_SEND_CSD: CommandRegister = build_command_without_data(
CommandConfig::new_with_r2_response(CmdId::CMD9_SendCsd.raw_value()),
);
pub const CMD13_SEND_STATUS: CommandRegister = build_command_without_data(
CommandConfig::new_with_r1_response(CmdId::CMD13_SendStatus.raw_value()),
);
pub const CMD17_READ_SINGLE_BLOCK: CommandRegister = CommandRegister::builder()
.with_command_index(CmdId::CMD17_ReadSingleBlock.raw_value())
.with_command_type(zynq7000::sdio::CommandType::Normal)
.with_data_is_present(true)
.with_command_index_check_enable(true)
.with_command_crc_check_enable(true)
.with_response_type_select(ResponseType::_48bits)
.with_block_select(BlockSelect::SingleBlock)
.with_data_transfer_direction(zynq7000::sdio::TransferDirection::Read)
.with_auto_cmd12_enable(false)
.with_block_count_enable(false)
.with_dma_enable(false)
.build();
pub const CMD24_WRITE_BLOCK: CommandRegister = CommandRegister::builder()
.with_command_index(CmdId::CMD24_WriteBlock.raw_value())
.with_command_type(zynq7000::sdio::CommandType::Normal)
.with_data_is_present(true)
.with_command_index_check_enable(true)
.with_command_crc_check_enable(true)
.with_response_type_select(ResponseType::_48bits)
.with_block_select(BlockSelect::SingleBlock)
.with_data_transfer_direction(zynq7000::sdio::TransferDirection::Write)
.with_auto_cmd12_enable(false)
.with_block_count_enable(false)
.with_dma_enable(false)
.build();
pub const CMD55_APP_CMD: CommandRegister = build_command_without_data(
CommandConfig::new_with_r1_response(CmdId::CMD55_AppCmd.raw_value()),
);
pub const ACMD6_SET_BUS_WIDTH: CommandRegister = build_command_without_data(
CommandConfig::new_with_r1_response(AcmdId::ACMD6_SetBusWidth.raw_value()),
);
pub const ACMD41_SEND_IF_COND: CommandRegister = build_command_without_data(
CommandConfig::new_with_r3_response(AcmdId::ACMD41_SdSendOpCond.raw_value()),
);
File diff suppressed because it is too large Load Diff
-99
View File
@@ -1,99 +0,0 @@
use crate::gpio::mio::{
Mio10, Mio11, Mio12, Mio13, Mio14, Mio15, Mio28, Mio29, Mio30, Mio31, Mio32, Mio33, Mio34,
Mio35, Mio36, Mio37, Mio38, Mio39, Mio48, Mio49, MioPin, Pin,
};
#[cfg(not(feature = "7z010-7z007s-clg225"))]
use crate::gpio::mio::{
Mio16, Mio17, Mio18, Mio19, Mio20, Mio21, Mio22, Mio23, Mio24, Mio25, Mio26, Mio27, Mio40,
Mio41, Mio42, Mio43, Mio44, Mio45, Mio46, Mio47, Mio50, Mio51,
};
pub trait Sdio0ClockPin: MioPin {}
pub trait Sdio0CommandPin: MioPin {}
pub trait Sdio0Data0Pin: MioPin {}
pub trait Sdio0Data1Pin: MioPin {}
pub trait Sdio0Data2Pin: MioPin {}
pub trait Sdio0Data3Pin: MioPin {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio0ClockPin for Pin<Mio16> {}
impl Sdio0ClockPin for Pin<Mio28> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio0ClockPin for Pin<Mio40> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio0CommandPin for Pin<Mio17> {}
impl Sdio0CommandPin for Pin<Mio29> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio0CommandPin for Pin<Mio41> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio0Data0Pin for Pin<Mio18> {}
impl Sdio0Data0Pin for Pin<Mio30> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio0Data0Pin for Pin<Mio42> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio0Data1Pin for Pin<Mio19> {}
impl Sdio0Data1Pin for Pin<Mio31> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio0Data1Pin for Pin<Mio43> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio0Data2Pin for Pin<Mio20> {}
impl Sdio0Data2Pin for Pin<Mio32> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio0Data2Pin for Pin<Mio44> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio0Data3Pin for Pin<Mio21> {}
impl Sdio0Data3Pin for Pin<Mio33> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio0Data3Pin for Pin<Mio45> {}
pub trait Sdio1ClockPin: MioPin {}
pub trait Sdio1CommandPin: MioPin {}
pub trait Sdio1Data0Pin: MioPin {}
pub trait Sdio1Data1Pin: MioPin {}
pub trait Sdio1Data2Pin: MioPin {}
pub trait Sdio1Data3Pin: MioPin {}
impl Sdio1ClockPin for Pin<Mio12> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio1ClockPin for Pin<Mio24> {}
impl Sdio1ClockPin for Pin<Mio36> {}
impl Sdio1ClockPin for Pin<Mio48> {}
impl Sdio1CommandPin for Pin<Mio11> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio1CommandPin for Pin<Mio23> {}
impl Sdio1CommandPin for Pin<Mio35> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio1CommandPin for Pin<Mio47> {}
impl Sdio1Data0Pin for Pin<Mio10> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio1Data0Pin for Pin<Mio22> {}
impl Sdio1Data0Pin for Pin<Mio34> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio1Data0Pin for Pin<Mio46> {}
impl Sdio1Data1Pin for Pin<Mio13> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio1Data1Pin for Pin<Mio25> {}
impl Sdio1Data1Pin for Pin<Mio37> {}
impl Sdio1Data1Pin for Pin<Mio49> {}
impl Sdio1Data2Pin for Pin<Mio14> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio1Data2Pin for Pin<Mio26> {}
impl Sdio1Data2Pin for Pin<Mio38> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio1Data2Pin for Pin<Mio50> {}
impl Sdio1Data2Pin for Pin<Mio15> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio1Data3Pin for Pin<Mio27> {}
impl Sdio1Data3Pin for Pin<Mio39> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio1Data3Pin for Pin<Mio51> {}
+36 -54
View File
@@ -18,7 +18,8 @@ use crate::{clocks::IoClocks, slcr::Slcr, time::Hertz};
use arbitrary_int::{prelude::*, u3, u4, u6};
use embedded_hal::delay::DelayNs;
pub use embedded_hal::spi::Mode;
use zynq7000::slcr::reset::DualRefAndClockResetSpiUart;
use embedded_hal::spi::SpiBus as _;
use zynq7000::slcr::reset::DualRefAndClockReset;
use zynq7000::spi::{
BaudDivSel, DelayControl, FifoWrite, InterruptControl, InterruptMask, InterruptStatus,
MmioRegisters, SPI_0_BASE_ADDR, SPI_1_BASE_ADDR,
@@ -873,7 +874,7 @@ impl Spi {
fn prepare_generic_blocking_transfer(&mut self, words: &[u8]) -> usize {
// We want to ensure the FIFO is empty for a new transfer. This is the simpler
// implementation for now.
self.flush();
self.flush().unwrap();
// Write this to 1 in any case to allow polling, defensive programming.
self.inner.regs.write_rx_trig(1);
@@ -885,14 +886,20 @@ impl Spi {
self.issue_manual_start_for_manual_cfg();
written
}
}
fn read(&mut self, words: &mut [u8]) {
impl embedded_hal::spi::ErrorType for Spi {
type Error = Infallible;
}
impl embedded_hal::spi::SpiBus for Spi {
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
if words.is_empty() {
return;
return Ok(());
}
// We want to ensure the FIFO is empty for a new transfer. This is the simpler
// implementation for now.
self.flush();
self.flush()?;
// Write this to 1 in any case to allow polling, defensive programming.
self.regs().write_rx_trig(1);
@@ -919,11 +926,13 @@ impl Spi {
write_idx += 1;
}
}
Ok(())
}
fn write(&mut self, words: &[u8]) {
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
if words.is_empty() {
return;
return Ok(());
}
let mut written = self.prepare_generic_blocking_transfer(words);
let mut read_idx = 0;
@@ -945,11 +954,12 @@ impl Spi {
// We use the FIFO trigger mechanism to determine when we can read all the remaining bytes.
self.regs().write_rx_trig((words.len() - read_idx) as u32);
self.outstanding_rx = true;
Ok(())
}
fn transfer(&mut self, read: &mut [u8], write: &[u8]) {
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
if read.is_empty() {
return;
return Ok(());
}
let mut write_idx = self.prepare_generic_blocking_transfer(write);
let mut read_idx = 0;
@@ -981,11 +991,13 @@ impl Spi {
writes_finished = write_idx == max_idx;
reads_finished = read_idx == max_idx;
}
Ok(())
}
fn transfer_in_place(&mut self, words: &mut [u8]) {
fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
if words.is_empty() {
return;
return Ok(());
}
let mut write_idx = self.prepare_generic_blocking_transfer(words);
let mut read_idx = 0;
@@ -1006,12 +1018,14 @@ impl Spi {
writes_finished = write_idx == words.len();
reads_finished = read_idx == words.len();
}
Ok(())
}
/// Blocking flush implementation.
fn flush(&mut self) {
fn flush(&mut self) -> Result<(), Self::Error> {
if !self.outstanding_rx {
return;
return Ok(());
}
let rx_trig = self.inner.read_rx_not_empty_threshold();
while !self.inner.read_isr().rx_not_empty() {}
@@ -1020,37 +1034,6 @@ impl Spi {
});
self.inner.set_rx_fifo_trigger(1).unwrap();
self.outstanding_rx = false;
}
}
impl embedded_hal::spi::ErrorType for Spi {
type Error = Infallible;
}
impl embedded_hal::spi::SpiBus for Spi {
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
Self::read(self, words);
Ok(())
}
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
Self::write(self, words);
Ok(())
}
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
Self::transfer(self, read, write);
Ok(())
}
fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
Self::transfer_in_place(self, words);
Ok(())
}
/// Blocking flush implementation.
fn flush(&mut self) -> Result<(), Self::Error> {
Self::flush(self);
Ok(())
}
}
@@ -1084,23 +1067,23 @@ impl<Delay: DelayNs> embedded_hal::spi::SpiDevice for SpiWithHwCs<Delay> {
for op in operations {
match op {
embedded_hal::spi::Operation::Read(items) => {
self.spi.read(items);
self.spi.read(items)?;
}
embedded_hal::spi::Operation::Write(items) => {
self.spi.write(items);
self.spi.write(items)?;
}
embedded_hal::spi::Operation::Transfer(read, write) => {
self.spi.transfer(read, write);
self.spi.transfer(read, write)?;
}
embedded_hal::spi::Operation::TransferInPlace(items) => {
self.spi.transfer_in_place(items);
self.spi.transfer_in_place(items)?;
}
embedded_hal::spi::Operation::DelayNs(delay) => {
self.delay.delay_ns(*delay);
}
}
}
self.spi.flush();
self.spi.flush()?;
self.spi.inner.no_hw_cs();
Ok(())
}
@@ -1113,13 +1096,13 @@ impl<Delay: DelayNs> embedded_hal::spi::SpiDevice for SpiWithHwCs<Delay> {
#[inline]
pub fn reset(id: SpiId) {
let assert_reset = match id {
SpiId::Spi0 => DualRefAndClockResetSpiUart::builder()
SpiId::Spi0 => DualRefAndClockReset::builder()
.with_periph1_ref_rst(false)
.with_periph0_ref_rst(true)
.with_periph1_cpu1x_rst(false)
.with_periph0_cpu1x_rst(true)
.build(),
SpiId::Spi1 => DualRefAndClockResetSpiUart::builder()
SpiId::Spi1 => DualRefAndClockReset::builder()
.with_periph1_ref_rst(true)
.with_periph0_ref_rst(false)
.with_periph1_cpu1x_rst(true)
@@ -1131,11 +1114,10 @@ pub fn reset(id: SpiId) {
regs.reset_ctrl().write_spi(assert_reset);
// Keep it in reset for some cycles.. The TMR just mentions some small delay,
// no idea what is meant with that.
for _ in 0..5 {
for _ in 0..3 {
aarch32_cpu::asm::nop();
}
regs.reset_ctrl()
.write_spi(DualRefAndClockResetSpiUart::ZERO);
regs.reset_ctrl().write_spi(DualRefAndClockReset::DEFAULT);
});
}
}
+7 -10
View File
@@ -13,7 +13,7 @@ use core::convert::Infallible;
use arbitrary_int::u3;
use libm::round;
use zynq7000::{
slcr::reset::DualRefAndClockResetSpiUart,
slcr::reset::DualRefAndClockReset,
uart::{
BaudRateDivisor, Baudgen, ChMode, ClockSelect, FifoTrigger, InterruptControl,
MmioRegisters, Mode, UART_0_BASE, UART_1_BASE,
@@ -141,7 +141,7 @@ pub struct DivisorZero;
macro_rules! pin_pairs {
($index:literal, $UartPeriph:path, ($( [$(#[$meta:meta], )? $TxMio:ident, $RxMio:ident] ),+ $(,)? )) => {
$(
pastey::paste! {
paste::paste! {
$( #[$meta] )?
impl [<TxPin $index>] for Pin<$TxMio> {}
@@ -720,13 +720,13 @@ impl embedded_io::Read for Uart {
#[inline]
pub fn reset(id: UartId) {
let assert_reset = match id {
UartId::Uart0 => DualRefAndClockResetSpiUart::builder()
UartId::Uart0 => DualRefAndClockReset::builder()
.with_periph1_ref_rst(false)
.with_periph0_ref_rst(true)
.with_periph1_cpu1x_rst(false)
.with_periph0_cpu1x_rst(true)
.build(),
UartId::Uart1 => DualRefAndClockResetSpiUart::builder()
UartId::Uart1 => DualRefAndClockReset::builder()
.with_periph1_ref_rst(true)
.with_periph0_ref_rst(false)
.with_periph1_cpu1x_rst(true)
@@ -736,12 +736,9 @@ pub fn reset(id: UartId) {
unsafe {
Slcr::with(|regs| {
regs.reset_ctrl().write_uart(assert_reset);
// Keep it in reset for a few cycles.. not sure if this is necessary.
for _ in 0..5 {
aarch32_cpu::asm::nop();
}
regs.reset_ctrl()
.write_uart(DualRefAndClockResetSpiUart::ZERO);
// Keep it in reset for one cycle.. not sure if this is necessary.
aarch32_cpu::asm::nop();
regs.reset_ctrl().write_uart(DualRefAndClockReset::DEFAULT);
});
}
}
+1 -6
View File
@@ -8,10 +8,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased]
# [v0.1.2] 2026-02-14
Bumped `aarch32-cpu` to v0.2
# [v0.1.1] 2025-10-10
Documentation fixes.
@@ -20,7 +16,6 @@ Documentation fixes.
Initial release
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-mmu-v0.1.2...HEAD
[v0.1.2]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-mmu-v0.1.1...zynq7000-mmu-v0.1.2
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-mmu-v0.1.0...HEAD
[v0.1.1]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-mmu-v0.1.0...zynq7000-mmu-v0.1.1
[v0.1.0]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/tag/zynq7000-mmu-v0.1.0
+3 -2
View File
@@ -1,7 +1,7 @@
[package]
name = "zynq7000-mmu"
description = "Zynq7000 MMU structures"
version = "0.1.2"
version = "0.1.1"
edition = "2024"
license = "MIT OR Apache-2.0"
homepage = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
@@ -11,7 +11,7 @@ categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
thiserror = { version = "2", default-features = false }
aarch32-cpu = { version = "0.2" }
aarch32-cpu = { version = "0.1" }
[build-dependencies]
arm-targets = { version = "0.4" }
@@ -21,4 +21,5 @@ tools = []
[package.metadata.docs.rs]
targets = ["armv7a-none-eabihf"]
cargo-args = ["-Z", "build-std=core"]
rustdoc-args = ["--generate-link-to-definition"]
+1 -5
View File
@@ -8,8 +8,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased]
# [v0.2.0] 2026-02-14
Bugfixes in startup assembler code.
## Changed
@@ -19,7 +17,6 @@ Bugfixes in startup assembler code.
- Runtime now calls a `kmain` method similar to the re-export `aarch32-rt` crate.
Former `boot_core` method must be renamed to `kmain`, but it is recommended to use
the `zynq7000-rt::entry` proc macro to annotate the main method.
- Bumped `aarch32-rt` to v0.2 which now requires the `memory.x` file to place the `STACKS` segment
## Fixed
@@ -35,7 +32,6 @@ Documentation fixes.
Initial release
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-rt-v0.2.0...HEAD
[v0.2.0]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-rt-v0.1.1...zynq7000-rt-v0.2.0
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-rt-v0.1.0...HEAD
[v0.1.1]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-rt-v0.1.0...zynq7000-rt-v0.1.1
[v0.1.0]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/tag/zynq7000-rt-v0.1.0
+4 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "zynq7000-rt"
version = "0.2.0"
version = "0.1.1"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2024"
description = "Run-time support for the Zynq7000 family of SoCs for running bare-metal applications"
@@ -11,8 +11,8 @@ keywords = ["no-std", "rt", "cortex-a", "amd", "zynq7000"]
categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
aarch32-rt = { version = "0.2", optional = true, features = ["fpu-d32"] }
aarch32-cpu = { version = "0.2" }
aarch32-rt = { version = "0.1", optional = true, features = ["fpu-d32"] }
aarch32-cpu = { version = "0.1" }
arbitrary-int = "2"
zynq7000-mmu = { path = "../zynq7000-mmu", version = "0.1" }
@@ -25,4 +25,5 @@ rt = ["dep:aarch32-rt"]
[package.metadata.docs.rs]
targets = ["armv7a-none-eabihf"]
cargo-args = ["-Z", "build-std=core"]
rustdoc-args = ["--generate-link-to-definition"]
+194 -112
View File
@@ -5,6 +5,7 @@
//! but does NOT provide the L2 cache initialization.
//!
//! The boot routine includes stack, MMU and .bss/.data section initialization.
use aarch32_cpu::register::{Cpsr, cpsr::ProcessorMode};
use aarch32_rt as _;
// Start-up code for Armv7-A
@@ -12,23 +13,23 @@ use aarch32_rt as _;
// We set up our stacks and `kmain` in system mode.
core::arch::global_asm!(
r#"
.set PSS_L2CC_BASE_ADDR, 0xF8F02000
.set PSS_SLCR_BASE_ADDR, 0xF8000000
.set PSS_L2CC_BASE_ADDR, 0xF8F02000
.set PSS_SLCR_BASE_ADDR, 0xF8000000
.set SLCRlockReg, (PSS_SLCR_BASE_ADDR + 0x04) /*(PSS_SLCR_BASE_ADDR + XPSS_SLCR_LOCK_OFFSET)*/
.set SLCRUnlockReg, (PSS_SLCR_BASE_ADDR + 0x08) /*(PSS_SLCR_BASE_ADDR + XPSS_SLCR_UNLOCK_OFFSET)*/
.set SLCRL2cRamReg, (PSS_SLCR_BASE_ADDR + 0xA1C) /*(PSS_SLCR_BASE_ADDR + XPSS_SLCR_L2C_RAM_OFFSET)*/
.set SLCRCPURSTReg, (0xF8000000 + 0x244) /*(XPS_SYS_CTRL_BASEADDR + A9_CPU_RST_CTRL_OFFSET)*/
.set EFUSEStatus, (0xF800D000 + 0x10) /*(XPS_EFUSE_BASEADDR + EFUSE_STATUS_OFFSET)*/
.set SLCRlockReg, (PSS_SLCR_BASE_ADDR + 0x04) /*(PSS_SLCR_BASE_ADDR + XPSS_SLCR_LOCK_OFFSET)*/
.set SLCRUnlockReg, (PSS_SLCR_BASE_ADDR + 0x08) /*(PSS_SLCR_BASE_ADDR + XPSS_SLCR_UNLOCK_OFFSET)*/
.set SLCRL2cRamReg, (PSS_SLCR_BASE_ADDR + 0xA1C) /*(PSS_SLCR_BASE_ADDR + XPSS_SLCR_L2C_RAM_OFFSET)*/
.set SLCRCPURSTReg, (0xF8000000 + 0x244) /*(XPS_SYS_CTRL_BASEADDR + A9_CPU_RST_CTRL_OFFSET)*/
.set EFUSEStatus, (0xF800D000 + 0x10) /*(XPS_EFUSE_BASEADDR + EFUSE_STATUS_OFFSET)*/
.set CRValMmuCac, 0b01000000000101 /* Enable IDC, and MMU */
.set CRValHiVectorAddr, 0b10000000000000 /* Set the Vector address to high, 0xFFFF0000 */
.set CRValMmuCac, 0b01000000000101 /* Enable IDC, and MMU */
.set CRValHiVectorAddr, 0b10000000000000 /* Set the Vector address to high, 0xFFFF0000 */
.set SLCRlockKey, 0x767B /* SLCR lock key */
.set SLCRUnlockKey, 0xDF0D /* SLCR unlock key */
.set SLCRlockKey, 0x767B /* SLCR lock key */
.set SLCRUnlockKey, 0xDF0D /* SLCR unlock key */
.set SLCRL2cRamConfig, 0x00020202 /* SLCR L2C ram configuration */
.set FPEXC_EN, 0x40000000 /* FPU enable bit, (1 << 30) */
.set FPEXC_EN, 0x40000000 /* FPU enable bit, (1 << 30) */
.section .text.startup
.align 0
@@ -38,98 +39,137 @@ core::arch::global_asm!(
_start:
// only allow cpu0 through
// Read MPIDR
mrc p15,0,r1,c0,c0,5
mrc p15,0,r1,c0,c0,5
// Extract CPU ID bits. For single-core systems, this should always be 0
and r1, r1, #0x3
cmp r1, #0
beq check_efuse
b initialize
and r1, r1, #0x3
cmp r1, #0
beq check_efuse
b initialize
// Zynq specific code. It is recommended to reset CPU1 according to page 160 of the datasheet
check_efuse:
ldr r0, =EFUSEStatus
// Read eFuse setting
ldr r1, [r0]
// Check whether device is having single core
ands r1,r1,#0x80
beq initialize
ldr r0,=EFUSEStatus
ldr r1,[r0] /* Read eFuse setting */
ands r1,r1,#0x80 /* Check whether device is having single core */
beq initialize
/* single core device, reset cpu1 */
ldr r0,=SLCRUnlockReg /* Load SLCR base address base + unlock register */
ldr r1,=SLCRUnlockKey /* set unlock key */
str r1, [r0] /* Unlock SLCR */
ldr r0,=SLCRCPURSTReg
ldr r1,[r0] /* Read CPU Software Reset Control register */
orr r1,r1,#0x22
str r1,[r0] /* Reset CPU1 */
ldr r0,=SLCRCPURSTReg
ldr r1,[r0] /* Read CPU Software Reset Control register */
orr r1,r1,#0x22
str r1,[r0] /* Reset CPU1 */
ldr r0,=SLCRlockReg /* Load SLCR base address base + lock register */
ldr r1,=SLCRlockKey /* set lock key */
str r1, [r0] /* lock SLCR */
ldr r0,=SLCRlockReg /* Load SLCR base address base + lock register */
ldr r1,=SLCRlockKey /* set lock key */
str r1, [r0] /* lock SLCR */
initialize:
mrc p15, 0, r0, c0, c0, 0 /* Get the revision */
mrc p15, 0, r0, c0, c0, 0 /* Get the revision */
and r5, r0, #0x00f00000
and r6, r0, #0x0000000f
orr r6, r6, r5, lsr #20-4
/* set VBAR to the _vector_table address in linker script */
ldr r0, =_vector_table
mcr p15, 0, r0, c12, c0, 0
ldr r0, =_vector_table
mcr p15, 0, r0, c12, c0, 0
/* Invalidate scu */
ldr r7, =0xf8f0000c
ldr r6, =0xffff
str r6, [r7]
ldr r7, =0xf8f0000c
ldr r6, =0xffff
str r6, [r7]
/* Invalidate caches and TLBs */
mov r0,#0 /* r0 = 0 */
mcr p15, 0, r0, c8, c7, 0 /* invalidate TLBs */
mcr p15, 0, r0, c7, c5, 0 /* invalidate icache */
mcr p15, 0, r0, c7, c5, 6 /* Invalidate branch predictor array */
bl invalidate_dcache /* invalidate dcache */
mov r0,#0 /* r0 = 0 */
mcr p15, 0, r0, c8, c7, 0 /* invalidate TLBs */
mcr p15, 0, r0, c7, c5, 0 /* invalidate icache */
mcr p15, 0, r0, c7, c5, 6 /* Invalidate branch predictor array */
bl invalidate_dcache /* invalidate dcache */
/* Disable MMU, if enabled */
mrc p15, 0, r0, c1, c0, 0 /* read CP15 register 1 */
bic r0, r0, #0x1 /* clear bit 0 */
mcr p15, 0, r0, c1, c0, 0 /* write value back */
mrc p15, 0, r0, c1, c0, 0 /* read CP15 register 1 */
bic r0, r0, #0x1 /* clear bit 0 */
mcr p15, 0, r0, c1, c0, 0 /* write value back */
bl _stack_setup_preallocated
// Set up stacks first.
ldr r3, =_stack_top
// Set stack pointer (right after) and mask interrupts for IRQ mode (Mode 0x12)
msr cpsr_c, {irq_mode}
// IRQ stack pointer
mov sp, r3
ldr r1, =_irq_stack_size
sub r3, r3, r1
// Set stack pointer (right after) and mask interrupts for Supervisor/SVC mode (Mode 0x13)
msr cpsr_c, {svc_mode}
// Supervisor stack pointer
mov sp, r3
ldr r1, =_svc_stack_size
sub r3, r3, r1
// Set stack pointer (right after) and mask interrupts for Abort/ABT mode (Mode 0x17)
msr cpsr_c, {abt_mode}
// Abort stack pointer
mov sp, r3
ldr r1, =_abt_stack_size
sub r3, r3, r1
// Set stack pointer (right after) and mask interrupts for FIQ mode (Mode 0x11)
msr cpsr_c, {fiq_mode}
// FIQ stack pointer
mov sp, r3
ldr r1, =_fiq_stack_size
sub r3, r3, r1
// Set stack pointer (right after) and mask interrupts for Undefined/UND mode (Mode 0x1B)
msr cpsr_c, {und_mode}
// Undefined stack pointer
mov sp, r3
ldr r1, =_und_stack_size
sub r3, r3, r1
// Set stack pointer (right after) and mask interrupts for System/SYS mode (Mode 0x1F)
msr cpsr_c, {sys_mode}
// System stack pointer (main stack)
mov sp, r3
// set scu enable bit in scu
ldr r7, =0xf8f00000
ldr r0, [r7]
orr r0, r0, #0x1
str r0, [r7]
ldr r7, =0xf8f00000
ldr r0, [r7]
orr r0, r0, #0x1
str r0, [r7]
/* Write to ACTLR */
mrc p15, 0, r0, c1, c0, 1 /* Read ACTLR*/
orr r0, r0, #(0x01 << 6) /* set SMP bit */
orr r0, r0, #(0x01 ) /* Cache/TLB maintenance broadcast */
mcr p15, 0, r0, c1, c0, 1 /* Write ACTLR*/
mrc p15, 0, r0, c1, c0, 1 /* Read ACTLR*/
orr r0, r0, #(0x01 << 6) /* set SMP bit */
orr r0, r0, #(0x01 ) /* Cache/TLB maintenance broadcast */
mcr p15, 0, r0, c1, c0, 1 /* Write ACTLR*/
mov r0, r0
mrc p15, 0, r1, c1, c0, 2 /* read cp access control register (CACR) into r1 */
orr r1, r1, #(0xf << 20) /* enable full access for p10 & p11 */
mcr p15, 0, r1, c1, c0, 2 /* write back into CACR */
mov r0, r0
mrc p15, 0, r1, c1, c0, 2 /* read cp access control register (CACR) into r1 */
orr r1, r1, #(0xf << 20) /* enable full access for p10 & p11 */
mcr p15, 0, r1, c1, c0, 2 /* write back into CACR */
/* enable vfp */
fmrx r1, FPEXC /* read the exception register */
orr r1,r1, #FPEXC_EN /* set VFP enable bit, leave the others in orig state */
fmxr FPEXC, r1 /* write back the exception register */
fmrx r1, FPEXC /* read the exception register */
orr r1,r1, #FPEXC_EN /* set VFP enable bit, leave the others in orig state */
fmxr FPEXC, r1 /* write back the exception register */
mrc p15,0,r0,c1,c0,0 /* flow prediction enable */
orr r0, r0, #(0x01 << 11) /* #0x8000 */
mcr p15,0,r0,c1,c0,0
mrc p15,0,r0,c1,c0,0 /* flow prediction enable */
orr r0, r0, #(0x01 << 11) /* #0x8000 */
mcr p15,0,r0,c1,c0,0
mrc p15,0,r0,c1,c0,1 /* read Auxiliary Control Register */
orr r0, r0, #(0x1 << 2) /* enable Dside prefetch */
orr r0, r0, #(0x1 << 1) /* enable L2 Prefetch hint */
mcr p15,0,r0,c1,c0,1 /* write Auxiliary Control Register */
mrc p15,0,r0,c1,c0,1 /* read Auxiliary Control Register */
orr r0, r0, #(0x1 << 2) /* enable Dside prefetch */
orr r0, r0, #(0x1 << 1) /* enable L2 Prefetch hint */
mcr p15,0,r0,c1,c0,1 /* write Auxiliary Control Register */
mrs r0, cpsr /* get the current PSR */
bic r0, r0, #0x100 /* enable asynchronous abort exception */
msr cpsr_xsf, r0
mrs r0, cpsr /* get the current PSR */
bic r0, r0, #0x100 /* enable asynchronous abort exception */
msr cpsr_xsf, r0
/* Zero BSS and initialize data before calling any function which might require them. */
@@ -159,20 +199,20 @@ data_init_done:
/* enable MMU and cache */
/* MMU Table is in .data, so this needs to be performed after .data is relocated */
/* (Even if in most cases, .data is already in RAM and relocation is a no-op) */
bl load_mmu_table
bl load_mmu_table
mvn r0,#0 /* Load MMU domains -- all ones=manager */
mcr p15,0,r0,c3,c0,0
mvn r0,#0 /* Load MMU domains -- all ones=manager */
mcr p15,0,r0,c3,c0,0
/* Enable mmu, icache and dcache */
ldr r0,=CRValMmuCac
mcr p15,0,r0,c1,c0,0 /* Enable cache and MMU */
dsb /* dsb allow the MMU to start up */
isb /* isb flush prefetch buffer */
ldr r0,=CRValMmuCac
mcr p15,0,r0,c1,c0,0 /* Enable cache and MMU */
dsb /* dsb allow the MMU to start up */
isb /* isb flush prefetch buffer */
// Jump to application
// Load CPU ID 0, which will be used as a function argument to the boot_core function.
mov r0, #0x0
mov r0, #0x0
bl kmain
// In case the application returns, loop forever
b .
@@ -180,48 +220,90 @@ data_init_done:
.type _invalidate_dcache, %function
invalidate_dcache:
mrc p15, 1, r0, c0, c0, 1 /* read CLIDR */
ands r3, r0, #0x7000000
mov r3, r3, lsr #23 /* cache level value (naturally aligned) */
beq finished
mov r10, #0 /* start with level 0 */
mrc p15, 1, r0, c0, c0, 1 /* read CLIDR */
ands r3, r0, #0x7000000
mov r3, r3, lsr #23 /* cache level value (naturally aligned) */
beq finished
mov r10, #0 /* start with level 0 */
loop1:
add r2, r10, r10, lsr #1 /* work out 3xcachelevel */
mov r1, r0, lsr r2 /* bottom 3 bits are the Cache type for this level */
and r1, r1, #7 /* get those 3 bits alone */
cmp r1, #2
blt skip /* no cache or only instruction cache at this level */
mcr p15, 2, r10, c0, c0, 0 /* write the Cache Size selection register */
isb /* isb to sync the change to the CacheSizeID reg */
mrc p15, 1, r1, c0, c0, 0 /* reads current Cache Size ID register */
and r2, r1, #7 /* extract the line length field */
add r2, r2, #4 /* add 4 for the line length offset (log2 16 bytes) */
ldr r4, =0x3ff
ands r4, r4, r1, lsr #3 /* r4 is the max number on the way size (right aligned) */
clz r5, r4 /* r5 is the bit position of the way size increment */
ldr r7, =0x7fff
ands r7, r7, r1, lsr #13 /* r7 is the max number of the index size (right aligned) */
add r2, r10, r10, lsr #1 /* work out 3xcachelevel */
mov r1, r0, lsr r2 /* bottom 3 bits are the Cache type for this level */
and r1, r1, #7 /* get those 3 bits alone */
cmp r1, #2
blt skip /* no cache or only instruction cache at this level */
mcr p15, 2, r10, c0, c0, 0 /* write the Cache Size selection register */
isb /* isb to sync the change to the CacheSizeID reg */
mrc p15, 1, r1, c0, c0, 0 /* reads current Cache Size ID register */
and r2, r1, #7 /* extract the line length field */
add r2, r2, #4 /* add 4 for the line length offset (log2 16 bytes) */
ldr r4, =0x3ff
ands r4, r4, r1, lsr #3 /* r4 is the max number on the way size (right aligned) */
clz r5, r4 /* r5 is the bit position of the way size increment */
ldr r7, =0x7fff
ands r7, r7, r1, lsr #13 /* r7 is the max number of the index size (right aligned) */
loop2:
mov r9, r4 /* r9 working copy of the max way size (right aligned) */
mov r9, r4 /* r9 working copy of the max way size (right aligned) */
loop3:
orr r11, r10, r9, lsl r5 /* factor in the way number and cache number into r11 */
orr r11, r11, r7, lsl r2 /* factor in the index number */
mcr p15, 0, r11, c7, c6, 2 /* invalidate by set/way */
subs r9, r9, #1 /* decrement the way number */
bge loop3
subs r7, r7, #1 /* decrement the index */
bge loop2
orr r11, r10, r9, lsl r5 /* factor in the way number and cache number into r11 */
orr r11, r11, r7, lsl r2 /* factor in the index number */
mcr p15, 0, r11, c7, c6, 2 /* invalidate by set/way */
subs r9, r9, #1 /* decrement the way number */
bge loop3
subs r7, r7, #1 /* decrement the index */
bge loop2
skip:
add r10, r10, #2 /* increment the cache number */
cmp r3, r10
bgt loop1
add r10, r10, #2 /* increment the cache number */
cmp r3, r10
bgt loop1
finished:
mov r10, #0 /* switch back to cache level 0 */
mcr p15, 2, r10, c0, c0, 0 /* select current cache level in cssr */
mov r10, #0 /* switch back to cache level 0 */
mcr p15, 2, r10, c0, c0, 0 /* select current cache level in cssr */
dsb
isb
bx lr
bx lr
.size invalidate_dcache, . - invalidate_dcache
"#,
fiq_mode = const {
Cpsr::new_with_raw_value(0)
.with_mode(ProcessorMode::Fiq)
.with_i(true)
.with_f(true)
.raw_value()
},
irq_mode = const {
Cpsr::new_with_raw_value(0)
.with_mode(ProcessorMode::Irq)
.with_i(true)
.with_f(true)
.raw_value()
},
svc_mode = const {
Cpsr::new_with_raw_value(0)
.with_mode(ProcessorMode::Svc)
.with_i(true)
.with_f(true)
.raw_value()
},
und_mode = const {
Cpsr::new_with_raw_value(0)
.with_mode(ProcessorMode::Und)
.with_i(true)
.with_f(true)
.raw_value()
},
abt_mode = const {
Cpsr::new_with_raw_value(0)
.with_mode(ProcessorMode::Abt)
.with_i(true)
.with_f(true)
.raw_value()
},
sys_mode = const {
Cpsr::new_with_raw_value(0)
.with_mode(ProcessorMode::Sys)
.with_i(true)
.with_f(true)
.raw_value()
},
);
+1 -8
View File
@@ -8,15 +8,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased]
# [v0.2.0] 2026-04-01
- Renamed all register blocks to `Registers` to subblocks to `<Subblock>Registers`.
- Updated IPTR registers in the GIC module to use a custom register type instead of a raw u32.
- Added SDIO registers.
- Fixed wrong position in QSPI reset register in SLCR Module
- Added some missing reset register definitions.
- Added `defmt` support
- Some other minor renaming of registers (e.g. `ctrl` replaced by `control`)
# [v0.1.1] 2025-10-09
@@ -26,7 +20,6 @@ Documentation fix
Initial release
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-v0.2.0...HEAD
[v0.2.0]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-v0.1.0...zynq7000-v0.2.0
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-v0.1.1...HEAD
[v0.1.1]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-v0.1.0...zynq7000-v0.1.1
[v0.1.0]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/tag/zynq7000-v0.1.0
+3 -6
View File
@@ -1,6 +1,6 @@
[package]
name = "zynq7000"
version = "0.2.0"
version = "0.1.1"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2024"
description = "Peripheral Access Crate (PAC) for the Zynq7000 family of SoCs"
@@ -13,19 +13,16 @@ categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
static_assertions = "1.1"
derive-mmio = { version = "0.6", default-features = false }
bitbybit = "2"
bitbybit = "1.4"
arbitrary-int = "2"
rustversion = "1"
thiserror = { version = "2", default-features = false }
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
defmt = { version = "1", optional = true }
[features]
defmt = ["dep:defmt", "arbitrary-int/defmt"]
[dev-dependencies]
approx = "0.5"
[package.metadata.docs.rs]
targets = ["armv7a-none-eabihf"]
cargo-args = ["-Z", "build-std=core"]
rustdoc-args = ["--generate-link-to-definition"]
+3 -2
View File
@@ -6,7 +6,8 @@
This repository contains the Peripheral Access Crate (PAC) for the AMD Zynq7000 SoC family.
If you are interested in higher-level abstractions, it is recommended you visit the
[`zynq7000-hal`](../zynq7000-hal) HAL crate which build on top of this PAC.
If you are interested in higher-level abstractions, it is recommended you visit
the [`zynq7000-hal`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/zynq7000-hal)
HAL crate which build on top of this PAC.
Check out the documentation for more details.
+55 -371
View File
@@ -6,7 +6,6 @@ pub mod regs {
#[bitbybit::bitenum(u2)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DataBusWidth {
_32Bit = 0b00,
_16Bit = 0b01,
@@ -14,18 +13,11 @@ pub mod regs {
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SoftReset {
Reset = 0,
Active = 1,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DdrcControl {
#[bit(16, rw)]
disable_auto_refresh: bool,
@@ -45,14 +37,7 @@ pub mod regs {
soft_reset: SoftReset,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct TwoRankConfig {
#[bits(14..=18, rw)]
addrmap_cs_bit0: u5,
@@ -65,13 +50,7 @@ pub mod regs {
}
/// Queue control for the low priority and high priority read queues.
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct LprHprQueueControl {
#[bits(22..=25, rw)]
xact_run_length: u4,
@@ -81,13 +60,7 @@ pub mod regs {
min_non_critical_x32: u11,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct WriteQueueControl {
#[bits(15..=25, rw)]
max_starve_x32: u11,
@@ -97,13 +70,7 @@ pub mod regs {
min_non_critical_x32: u11,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramParamReg0 {
/// Minimum time to wait after coming out of self refresh before doing anything. This must be
/// bigger than all the constraints that exist.
@@ -118,13 +85,7 @@ pub mod regs {
t_rc: u6,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramParamReg1 {
#[bits(28..=31, rw)]
t_cke: u4,
@@ -140,13 +101,7 @@ pub mod regs {
wr2pre: u5,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramParamReg2 {
#[bits(28..=31, rw)]
t_rcd: u4,
@@ -166,19 +121,12 @@ pub mod regs {
/// Weird naming.
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum MobileSetting {
Ddr2Ddr3 = 0,
Lpddr2 = 1,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramParamReg3 {
#[bit(30, rw)]
disable_pad_pd_feature: bool,
@@ -206,19 +154,12 @@ pub mod regs {
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ModeRegisterType {
Write = 0,
Read = 1,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramParamReg4 {
#[bit(27, rw)]
mr_rdata_valid: bool,
@@ -238,13 +179,7 @@ pub mod regs {
enable_2t_timing_mode: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramInitParam {
#[bits(11..=13, rw)]
t_mrd: u3,
@@ -254,13 +189,7 @@ pub mod regs {
final_wait_x32: u7,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramEmr {
#[bits(16..=31, rw)]
emr3: u16,
@@ -268,13 +197,7 @@ pub mod regs {
emr2: u16,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramEmrMr {
#[bits(16..=31, rw)]
emr: u16,
@@ -282,13 +205,7 @@ pub mod regs {
mr: u16,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramBurst8ReadWrite {
#[bits(0..=3, rw)]
burst_rdwr: u4,
@@ -300,13 +217,7 @@ pub mod regs {
burstchop: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DisableDq {
#[bit(1, rw)]
dis_dq: bool,
@@ -314,13 +225,7 @@ pub mod regs {
force_low_pri_n: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramAddrMapBank {
#[bits(16..=19, rw)]
addrmap_bank_b6: u4,
@@ -334,13 +239,7 @@ pub mod regs {
addrmap_bank_b0: u4,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramAddrMapColumn {
#[bits(28..=31, rw)]
addrmap_col_b11: u4,
@@ -360,13 +259,7 @@ pub mod regs {
addrmap_col_b2: u4,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramAddrMapRow {
#[bits(24..=27, rw)]
addrmap_row_b15: u4,
@@ -384,13 +277,7 @@ pub mod regs {
addrmap_row_b0: u4,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DramOdt {
#[bits(16..=17, rw)]
phy_idle_local_odt: u2,
@@ -404,13 +291,7 @@ pub mod regs {
rank0_rd_odt: u3,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct PhyCmdTimeoutRdDataCpt {
#[bits(28..=31, rw)]
wrlvl_num_of_dq0: u4,
@@ -433,32 +314,19 @@ pub mod regs {
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum DllCalibSel {
Periodic = 0,
Manual = 1,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DllCalib {
#[bit(16, rw)]
sel: DllCalibSel,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct OdtDelayHold {
#[bits(12..=15, rw)]
wr_odt_hold: u4,
@@ -470,13 +338,7 @@ pub mod regs {
rd_odt_delay: u4,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct CtrlReg1 {
#[bit(12, rw)]
selfref_enable: bool,
@@ -494,13 +356,7 @@ pub mod regs {
pageclose: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct CtrlReg2 {
#[bit(17, rw)]
go_2_critcal_enable: bool,
@@ -508,13 +364,7 @@ pub mod regs {
go_2_critical_hysteresis: u8,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct CtrlReg3 {
#[bits(16..=25, rw)]
dfi_t_wlmrd: u10,
@@ -524,13 +374,7 @@ pub mod regs {
wrlvl_ww: u8,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct CtrlReg4 {
#[bits(8..=15, rw)]
dfi_t_ctrlupd_interval_max_x1024: u8,
@@ -538,13 +382,7 @@ pub mod regs {
dfi_t_ctrlupd_interval_min_x1024: u8,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct CtrlReg5 {
#[bits(20..=25, rw)]
t_ckesr: u6,
@@ -560,13 +398,7 @@ pub mod regs {
dfi_t_ctrl_delay: u4,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct CtrlReg6 {
#[bits(16..=19, rw)]
t_cksx: u4,
@@ -580,13 +412,7 @@ pub mod regs {
t_ckpde: u4,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct CheTZq {
#[bits(22..=31, rw)]
t_zq_short_nop: u10,
@@ -600,13 +426,7 @@ pub mod regs {
dis_auto_zq: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct CheTZqShortInterval {
#[bits(20..=27, rw)]
dram_rstn_x1024: u8,
@@ -614,13 +434,7 @@ pub mod regs {
t_zq_short_interval: u20,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DeepPowerdown {
#[bits(1..=8, rw)]
deep_powerdown_to_x1024: u8,
@@ -628,13 +442,7 @@ pub mod regs {
enable: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct Reg2c {
#[bit(28, rw)]
dfi_rd_data_eye_train: bool,
@@ -652,25 +460,13 @@ pub mod regs {
dfi_wrlvl_max_x1024: u12,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct Reg2d {
#[bit(9, rw)]
skip_ocd: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DfiTiming {
#[bits(15..=24, rw)]
dfi_t_ctrlup_max: u10,
@@ -680,13 +476,7 @@ pub mod regs {
dfi_t_rddata_enable: u5,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct CheEccControl {
#[bit(1, rw)]
clear_correctable_errors: bool,
@@ -695,20 +485,13 @@ pub mod regs {
}
#[bitbybit::bitenum(u3, exhaustive = false)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum EccMode {
NoEcc = 0b000,
SecDecOverOneBeat = 0b100,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct EccScrub {
#[bit(3, rw)]
disable_scrub: bool,
@@ -716,13 +499,7 @@ pub mod regs {
ecc_mode: Option<EccMode>,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct PhyReceiverEnable {
#[bits(4..=7, rw)]
phy_dif_off: u4,
@@ -730,13 +507,7 @@ pub mod regs {
phy_dif_on: u4,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct PhyConfig {
#[bits(24..=30, rw)]
dq_offset: u7,
@@ -750,13 +521,7 @@ pub mod regs {
data_slice_in_use: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct PhyInitRatio {
#[bits(10..=19, rw)]
gatelvl_init_ratio: u10,
@@ -764,13 +529,7 @@ pub mod regs {
wrlvl_init_ratio: u10,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct PhyDqsConfig {
#[bits(11..=19, rw)]
dqs_slave_delay: u9,
@@ -780,13 +539,7 @@ pub mod regs {
dqs_slave_ratio: u10,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct PhyWriteEnableConfig {
#[bits(12..=20, rw)]
fifo_we_in_delay: u9,
@@ -796,13 +549,7 @@ pub mod regs {
fifo_we_slave_ratio: u11,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct PhyWriteDataSlaveConfig {
#[bits(11..=19, rw)]
wr_data_slave_delay: u9,
@@ -812,13 +559,7 @@ pub mod regs {
wr_data_slave_ratio: u10,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct Reg64 {
#[bit(30, rw)]
cmd_latency: bool,
@@ -838,13 +579,7 @@ pub mod regs {
bl2: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct Reg65 {
#[bits(18..=19, rw)]
ctrl_slave_delay: u2,
@@ -864,13 +599,7 @@ pub mod regs {
wr_rl_delay: u5,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct AxiPriorityWritePort {
#[bit(18, rw)]
disable_page_match: bool,
@@ -882,13 +611,7 @@ pub mod regs {
pri_wr_port: u10,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct AxiPriorityReadPort {
#[bit(19, rw)]
enable_hpr: bool,
@@ -902,13 +625,7 @@ pub mod regs {
pri_rd_port_n: u10,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct ExclusiveAccessConfig {
#[bits(9..=17, rw)]
access_id1_port: u9,
@@ -917,20 +634,13 @@ pub mod regs {
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum LpddrBit {
Ddr2Ddr3 = 0,
Lpddr2 = 1,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct LpddrControl0 {
#[bits(4..=11, rw)]
mr4_margin: u8,
@@ -942,25 +652,13 @@ pub mod regs {
lpddr2: LpddrBit,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct LpddrControl1 {
#[bits(0..=31, rw)]
mr4_read_interval: u32,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct LpddrControl2 {
#[bits(12..=21, rw)]
t_mrw: u10,
@@ -970,13 +668,7 @@ pub mod regs {
min_stable_clock_x1: u4,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct LpddrControl3 {
#[bits(8..=17, rw)]
dev_zqinit_x32: u10,
@@ -986,7 +678,6 @@ pub mod regs {
#[bitbybit::bitenum(u3, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum OperatingMode {
DdrcInit = 0,
NormalOperation = 1,
@@ -1012,19 +703,12 @@ pub mod regs {
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DebugStallBit {
CommandsAccepted = 0,
CommandsNotAccepted = 1,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct ModeStatus {
#[bits(16..=20, r)]
dbg_hpr_queue_depth: u5,
+6 -21
View File
@@ -4,7 +4,6 @@ pub const DEVCFG_BASE_ADDR: usize = 0xF8007000;
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PlConfigAccess {
/// Used for JTAG access
TapController = 0,
@@ -14,7 +13,6 @@ pub enum PlConfigAccess {
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ConfigAccessPortSelect {
/// Internal Configuration Access Port (ICAP), using PL or PS-based software.
Icap = 0,
@@ -24,7 +22,6 @@ pub enum ConfigAccessPortSelect {
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TimerSelect {
_64kTimer = 0,
_4kTimer = 1,
@@ -32,7 +29,6 @@ pub enum TimerSelect {
#[bitbybit::bitenum(u3, exhaustive = false)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AesEnable {
Disable = 0b000,
Enable = 0b111,
@@ -40,7 +36,6 @@ pub enum AesEnable {
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PsBootMode {
NonSecure = 0,
Secure = 1,
@@ -48,18 +43,11 @@ pub enum PsBootMode {
#[bitbybit::bitenum(u3, exhaustive = false)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ArmDapEnable {
Enabled = 0b111,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, debug)]
pub struct Control {
#[bit(31, rw)]
force_reset: bool,
@@ -107,7 +95,7 @@ pub struct Control {
/// The bits in this register and read/write, set-only, which means that only a PS_POR_B reset
/// can clear the bits.
#[bitbybit::bitfield(u32, debug, debug, defmt_fields(feature = "defmt"), forbid_overlaps)]
#[bitbybit::bitfield(u32, debug)]
pub struct Lock {
#[bit(4, rw)]
aes_fuse: bool,
@@ -125,7 +113,6 @@ pub struct Lock {
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum EdgeConfig {
Falling = 0,
Rising = 1,
@@ -134,7 +121,6 @@ pub enum EdgeConfig {
/// Related to the full level for reads, and the empty level for writes.
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FifoThresholdConfig {
OneFourth = 0b00,
HalfEmpty = 0b01,
@@ -142,7 +128,7 @@ pub enum FifoThresholdConfig {
EmptyOrFull = 0b11,
}
#[bitbybit::bitfield(u32, debug, defmt_fields(feature = "defmt"), forbid_overlaps)]
#[bitbybit::bitfield(u32, debug)]
pub struct Config {
#[bits(10..=11, rw)]
read_fifo_threshhold: FifoThresholdConfig,
@@ -158,7 +144,7 @@ pub struct Config {
disable_dst_incremenet: bool,
}
#[bitbybit::bitfield(u32, debug, defmt_fields(feature = "defmt"), forbid_overlaps)]
#[bitbybit::bitfield(u32, debug)]
pub struct Interrupt {
/// Tri-state PL IO during HIZ.
#[bit(31, rw)]
@@ -213,7 +199,7 @@ pub struct Interrupt {
negative_edge_pl_init: bool,
}
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"), forbid_overlaps)]
#[bitbybit::bitfield(u32, debug)]
pub struct MiscControl {
#[bits(28..=31, r)]
ps_version: u4,
@@ -227,7 +213,6 @@ pub struct MiscControl {
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum UnacknowledgedDmaTransfers {
None = 0b00,
One = 0b01,
@@ -235,7 +220,7 @@ pub enum UnacknowledgedDmaTransfers {
ThreeOrMore = 0b11,
}
#[bitbybit::bitfield(u32, debug, defmt_fields(feature = "defmt"), forbid_overlaps)]
#[bitbybit::bitfield(u32, debug)]
pub struct Status {
#[bit(31, rw)]
dma_command_queue_full: bool,
+12 -54
View File
@@ -4,7 +4,8 @@ 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, debug, defmt_bitfields(feature = "defmt"), forbid_overlaps)]
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct NetworkControl {
#[bit(18, w)]
flush_next_rx_dpram_pkt: bool,
@@ -40,7 +41,6 @@ pub struct NetworkControl {
/// The speed mode selects between 10 Mbps and 100 Mbps if the Gigabit enable bit is cleared.
#[bitbybit::bitenum(u1, exhaustive = true)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug, PartialEq, Eq)]
pub enum SpeedMode {
Low10Mbps = 0,
@@ -49,7 +49,6 @@ pub enum SpeedMode {
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PcsSelect {
GmiiMii = 0,
Tbi = 1,
@@ -57,7 +56,6 @@ pub enum PcsSelect {
#[bitbybit::bitenum(u3, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum MdcClockDivisor {
Div8 = 0,
Div16 = 1,
@@ -84,13 +82,7 @@ impl MdcClockDivisor {
}
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
forbid_overlaps,
defmt_fields(feature = "defmt")
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct NetworkConfig {
#[bit(30, rw)]
ignore_ipg_rx_error: bool,
@@ -148,7 +140,7 @@ pub struct NetworkConfig {
}
/// PHY management status information.
#[bitbybit::bitfield(u32, debug, forbid_overlaps, defmt_fields(feature = "defmt"))]
#[bitbybit::bitfield(u32, debug)]
pub struct NetworkStatus {
#[bit(6, r)]
pfc_pri_pause_neg: bool,
@@ -167,7 +159,6 @@ pub struct NetworkStatus {
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum BurstLength {
Single,
#[default]
@@ -189,14 +180,12 @@ impl BurstLength {
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AhbEndianess {
Little = 0,
Big = 1,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DmaRxBufSize(u8);
impl DmaRxBufSize {
@@ -224,13 +213,7 @@ impl DmaRxBufSize {
}
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
forbid_overlaps,
defmt_fields(feature = "defmt")
)]
#[bitbybit::bitfield(u32, debug)]
pub struct DmaConfig {
#[bit(24, rw)]
discard_when_ahb_full: bool,
@@ -258,7 +241,7 @@ pub struct DmaConfig {
burst_length: u5,
}
#[bitbybit::bitfield(u32, debug, forbid_overlaps, defmt_bitfields(feature = "defmt"))]
#[bitbybit::bitfield(u32, debug)]
pub struct TxStatus {
#[bit(8, rw)]
hresp_not_ok: bool,
@@ -289,7 +272,7 @@ impl TxStatus {
}
}
#[bitbybit::bitfield(u32, debug, forbid_overlaps, defmt_bitfields(feature = "defmt"))]
#[bitbybit::bitfield(u32, debug)]
pub struct RxStatus {
#[bit(3, rw)]
hresp_not_ok: bool,
@@ -307,13 +290,7 @@ impl RxStatus {
}
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
forbid_overlaps,
defmt_bitfields(feature = "defmt")
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct InterruptStatus {
#[bit(26, rw)]
tsu_sec_incr: bool,
@@ -404,20 +381,13 @@ impl InterruptControl {
}
#[bitbybit::bitenum(u2, exhaustive = false)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum PhyOperation {
Read = 0b10,
Write = 0b01,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
forbid_overlaps,
defmt_fields(feature = "defmt")
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct PhyMaintenance {
/// Must be 1 for Clause 22 operations.
#[bit(30, rw)]
@@ -434,25 +404,13 @@ pub struct PhyMaintenance {
data: u16,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
forbid_overlaps,
defmt_bitfields(feature = "defmt")
)]
#[bitbybit::bitfield(u32, debug)]
pub struct PauseQuantum {
#[bits(0..=15, rw)]
value: u16,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
forbid_overlaps,
defmt_bitfields(feature = "defmt")
)]
#[bitbybit::bitfield(u32, debug)]
pub struct MatchRegister {
#[bit(31, rw)]
copy_enable: bool,
+9 -20
View File
@@ -4,13 +4,7 @@ use arbitrary_int::{u2, u3, u5, u10};
use static_assertions::const_assert_eq;
/// Distributor Control Register
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DistributorControlRegister {
#[bit(1, rw)]
enable_non_secure: bool,
@@ -19,7 +13,7 @@ pub struct DistributorControlRegister {
}
/// Read only bit. This register only returns fixed constants.
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"), forbid_overlaps)]
#[bitbybit::bitfield(u32, debug)]
pub struct TypeRegister {
#[bits(11..=15, r)]
lspi: u5,
@@ -46,9 +40,10 @@ impl TypeRegister {
pub type Typer = TypeRegister;
#[bitbybit::bitfield(u32, debug)]
#[derive(PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
// TODO: Use bitbybit debug derive if new release was released.
/// Interrupt processor target register (IPTR).
#[bitbybit::bitfield(u32)]
#[derive(Debug, PartialEq, Eq)]
pub struct InterruptProcessorTargetRegister {
/// Target array. Every register holds the information for 4 interrupts.
#[bits(0..=1, rw, stride = 8)]
@@ -146,13 +141,7 @@ impl DistributorRegisters {
}
/// CPU interface control register.
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct InterfaceControl {
#[bit(4, rw)]
sbpr: bool,
@@ -167,14 +156,14 @@ pub struct InterfaceControl {
}
/// Priority Mask Register
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"), forbid_overlaps)]
#[bitbybit::bitfield(u32, debug)]
pub struct PriorityRegister {
#[bits(0..=7, rw)]
priority: u8,
}
/// Interrupt acknowledge register.
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"), forbid_overlaps)]
#[bitbybit::bitfield(u32, debug)]
pub struct InterruptSignalRegister {
#[bits(10..=12, rw)]
cpu_id: u3,
+1 -6
View File
@@ -1,10 +1,5 @@
//! # GPIO register module.
#[bitbybit::bitfield(
u32,
default = 0x0,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct MaskedOutput {
#[bits(16..=31, w)]
+2 -2
View File
@@ -2,7 +2,7 @@
pub const GTC_BASE_ADDR: usize = super::mpcore::MPCORE_BASE_ADDR + 0x0000_0200;
#[bitbybit::bitfield(u32, debug, forbid_overlaps, defmt_bitfields(feature = "defmt"))]
#[bitbybit::bitfield(u32, debug)]
pub struct GtcControl {
#[bits(8..=15, rw)]
prescaler: u8,
@@ -16,7 +16,7 @@ pub struct GtcControl {
enable: bool,
}
#[bitbybit::bitfield(u32, debug, forbid_overlaps, defmt_bitfields(feature = "defmt"))]
#[bitbybit::bitfield(u32, debug)]
pub struct InterruptStatus {
#[bit(0, rw)]
event_flag: bool,
+11 -13
View File
@@ -5,22 +5,20 @@ pub const I2C_0_BASE_ADDR: usize = 0xE000_4000;
pub const I2C_1_BASE_ADDR: usize = 0xE000_5000;
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum Direction {
Receiver = 0b1,
Transmitter = 0b0,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum Mode {
Slave = 0b0,
Master = 0b1,
}
#[bitbybit::bitfield(u32, default = 0x0, debug, defmt_fields(feature = "defmt"))]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct Control {
/// Divides the input PCLK frequency by this value + 1
#[bits(14..=15, rw)]
@@ -49,7 +47,7 @@ pub struct Control {
dir: Direction,
}
#[bitbybit::bitfield(u32, default = 0x0, debug, defmt_bitfields(feature = "defmt"))]
#[bitbybit::bitfield(u32, debug)]
pub struct Status {
#[bit(8, r)]
bus_active: bool,
@@ -67,19 +65,19 @@ pub struct Status {
rx_rw: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug, defmt_bitfields(feature = "defmt"))]
#[bitbybit::bitfield(u32, debug)]
pub struct Address {
#[bits(0..=9, rw)]
addr: u10,
}
#[bitbybit::bitfield(u32, default = 0x0, debug, defmt_bitfields(feature = "defmt"))]
#[bitbybit::bitfield(u32, debug)]
pub struct Fifo {
#[bits(0..=7, rw)]
data: u8,
}
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"))]
#[bitbybit::bitfield(u32, debug)]
pub struct InterruptStatus {
#[bit(9, rw)]
arbitration_lost: bool,
@@ -101,7 +99,7 @@ pub struct InterruptStatus {
complete: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug, defmt_bitfields(feature = "defmt"))]
#[bitbybit::bitfield(u32, debug)]
pub struct InterruptMask {
#[bit(9, r)]
arbitration_lost: bool,
@@ -123,7 +121,7 @@ pub struct InterruptMask {
complete: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug, defmt_bitfields(feature = "defmt"))]
#[bitbybit::bitfield(u32)]
pub struct InterruptControl {
#[bit(9, w)]
arbitration_lost: bool,
@@ -145,14 +143,14 @@ pub struct InterruptControl {
complete: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug, defmt_bitfields(feature = "defmt"))]
#[bitbybit::bitfield(u32, debug)]
pub struct Timeout {
/// Reset value: 0x1F.
#[bits(0..=7, rw)]
timeout: u8,
}
#[bitbybit::bitfield(u32, default = 0x0, debug, defmt_bitfields(feature = "defmt"))]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct TransferSize {
#[bits(0..=7, rw)]
size: u8,
+10 -30
View File
@@ -9,13 +9,13 @@ pub struct LockdownRegisters {
instruction: u32,
}
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"))]
#[bitbybit::bitfield(u32, debug)]
pub struct CacheSync {
#[bit(0, r)]
busy: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug, defmt_bitfields(feature = "defmt"))]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DebugControl {
#[bit(2, rw)]
spniden: bool,
@@ -25,7 +25,7 @@ pub struct DebugControl {
disable_cache_linefill: bool,
}
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"))]
#[bitbybit::bitfield(u32, debug)]
pub struct CacheId {
#[bits(24..=31, r)]
implementer: u8,
@@ -56,16 +56,14 @@ impl Control {
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum ReplacementPolicy {
PseudoRandomWithLfsr = 0,
RoundRobin = 1,
}
#[bitbybit::bitenum(u3, exhaustive = true)]
#[derive(Default, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Default, Debug)]
pub enum WaySize {
__Reserved0 = 0b000,
_16kB = 0b001,
@@ -79,21 +77,14 @@ pub enum WaySize {
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Default, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Default, Debug)]
pub enum Associativity {
#[default]
_8Way = 0,
_16Way = 1,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct AuxControl {
#[bit(30, rw)]
early_bresp_enable: bool,
@@ -132,13 +123,7 @@ pub struct AuxControl {
full_line_zero_enable: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
#[derive(PartialEq, Eq)]
pub struct LatencyConfig {
/// Latency is the numerical value + 1 cycles.
@@ -152,7 +137,7 @@ pub struct LatencyConfig {
setup_latency: u3,
}
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"), forbid_overlaps)]
#[bitbybit::bitfield(u32, debug)]
pub struct InterruptStatus {
#[bit(8, r)]
dec_error_l3: bool,
@@ -175,12 +160,7 @@ pub struct InterruptStatus {
event_counter_overflow_increment: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct InterruptControl {
#[bit(8, w)]
-11
View File
@@ -8,10 +8,6 @@
//! on top of it.
//! [The Zynq7000 HAL library](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/zynq7000-hal)
//! contains such a HAL which builds on this PAC.
//!
//! ## Features
//!
//! * `defmt` - Add support for the [`defmt`](https://github.com/knurling-rs/defmt) logging library.
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
@@ -33,7 +29,6 @@ pub mod l2_cache;
pub mod mpcore;
pub mod priv_tim;
pub mod qspi;
pub mod sdio;
pub mod slcr;
pub mod spi;
pub mod ttc;
@@ -68,8 +63,6 @@ pub struct Peripherals {
pub qspi: qspi::MmioRegisters<'static>,
pub devcfg: devcfg::MmioRegisters<'static>,
pub xadc: xadc::MmioRegisters<'static>,
pub sdio_0: sdio::MmioRegisters<'static>,
pub sdio_1: sdio::MmioRegisters<'static>,
}
impl Peripherals {
@@ -110,8 +103,6 @@ impl Peripherals {
qspi: qspi::Registers::new_mmio_fixed(),
devcfg: devcfg::Registers::new_mmio_fixed(),
xadc: xadc::Registers::new_mmio_fixed(),
sdio_0: sdio::Registers::new_mmio_fixed_0(),
sdio_1: sdio::Registers::new_mmio_fixed_1(),
}
}
}
@@ -119,7 +110,6 @@ impl Peripherals {
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SpiClockPhase {
ActiveOutsideOfWord = 0,
InactiveOutsideOfWord = 1,
@@ -127,7 +117,6 @@ pub enum SpiClockPhase {
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SpiClockPolarity {
QuiescentLow = 0,
QuiescentHigh = 1,
+1 -1
View File
@@ -19,7 +19,7 @@ pub const GICD_BASE_ADDR: usize = MPCORE_BASE_ADDR + 0x1000;
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct SnoopControlUnit {
control: u32,
ctrl: u32,
config: u32,
cpu_power_status: u32,
invalidate_all_regs_in_secure_state: u32,
+2 -14
View File
@@ -2,13 +2,7 @@
pub const CPU_PRIV_TIM_BASE_ADDR: usize = super::mpcore::MPCORE_BASE_ADDR + 0x0000_0600;
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct Control {
#[bits(8..=15, rw)]
prescaler: u8,
@@ -20,13 +14,7 @@ pub struct Control {
enable: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct InterruptStatus {
/// Cleared by writing a one.
#[bit(0, rw)]
+4 -11
View File
@@ -6,7 +6,6 @@ pub const QSPI_BASE_ADDR: usize = 0xE000D000;
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InterfaceMode {
LegacySpi = 0,
FlashMemoryInterface = 1,
@@ -14,7 +13,6 @@ pub enum InterfaceMode {
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Endianness {
Little = 0,
Big = 1,
@@ -23,7 +21,6 @@ pub enum Endianness {
/// Baud rate divisor register values.
#[bitbybit::bitenum(u3, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum BaudRateDivisor {
_2 = 0b000,
_4 = 0b001,
@@ -51,13 +48,9 @@ impl BaudRateDivisor {
}
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
// TODO: Use bitbybit debug support as soon as support for write fields has been implemented.
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct Config {
#[bit(31, rw)]
interface_mode: InterfaceMode,
@@ -244,7 +237,7 @@ pub struct Registers {
/// Transmits 1-byte command and 3-byte data OR 4-byte data.
#[mmio(Write)]
tx_data_00: u32,
#[mmio(Read)]
#[mmio(PureRead)]
rx_data: u32,
slave_idle_count: u32,
/// Defines the level at which the TX FIFO not full interrupt is generated.
-542
View File
@@ -1,542 +0,0 @@
use arbitrary_int::{u2, u3, u4, u6, u12};
pub const SDIO_BASE_ADDR_0: usize = 0xE010_0000;
pub const SDIO_BASE_ADDR_1: usize = 0xE010_1000;
#[bitbybit::bitenum(u3, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum BufferSize {
_4kB = 0b000,
_8kB = 0b001,
_16kB = 0b010,
_32kB = 0b011,
_64kB = 0b100,
_128kB = 0b101,
_256kB = 0b110,
_512kB = 0b111,
}
#[bitbybit::bitfield(
u32,
debug,
default = 0x0,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
pub struct BlockParams {
#[bits(16..=31, rw)]
blocks_count: u16,
#[bits(12..=14, rw)]
buffer_size: BufferSize,
#[bits(0..=11, rw)]
block_size: u12,
}
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Default, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CommandType {
#[default]
Normal = 0b00,
Suspend = 0b01,
Resume = 0b10,
Abort = 0b11,
}
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ResponseType {
// No response.
None = 0b00,
_136bits = 0b01,
_48bits = 0b10,
_48bitsWithCheck = 0b11,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum BlockSelect {
SingleBlock = 0,
MultiBlock = 1,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TransferDirection {
/// Host to card.
Write = 0,
/// Card to host.
Read = 1,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
pub struct CommandRegister {
/// Set to command number (CMD0-63, ACMD0-63)
#[bits(24..=29, rw)]
command_index: u6,
#[bits(22..=23, rw)]
command_type: CommandType,
/// Set to [false] for the following:
///
/// 1. Commands using only CMD line (ex. CMD52).
/// 2. Commands with no data transfer but using busy signal on DAT\[0\].
/// 3. Resume Command.
#[bit(21, rw)]
data_is_present: bool,
/// When 1, the host controller checks the index field in the response to see if it has the
/// same value as the command index.
#[bit(20, rw)]
command_index_check_enable: bool,
/// When 1, the host controller checks the CRC field in the response.
#[bit(18, rw)]
command_crc_check_enable: bool,
#[bits(16..=17, rw)]
response_type_select: ResponseType,
#[bit(5, rw)]
block_select: BlockSelect,
#[bit(4, rw)]
data_transfer_direction: TransferDirection,
/// Multiple block transfers for memory require CMD12 to stop the transaction. When this bit is
/// 1, the host controller issues CMD12 automatically when completing the last block tranfer.
#[bit(2, rw)]
auto_cmd12_enable: bool,
/// Enable block count register, which is only relevant for multiple block transfers.
#[bit(1, rw)]
block_count_enable: bool,
#[bit(0, rw)]
dma_enable: bool,
}
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"), forbid_overlaps)]
pub struct PresentState {
#[bit(24, r)]
cmd_line_signal_level: bool,
#[bits(20..=23, r)]
data_line_signal_level: u4,
/// The Write Protect Switch is supported for memory and combo cards. This bit reflects the
/// inversion of the SDx_WP pin.
#[bit(19, r)]
write_protect_switch_level: bool,
/// This bit reflects the inverse value of the SDx_CDn pin.
#[bit(18, r)]
card_detect_pin_level: bool,
/// This bit is used for testing. If it is 0, the Card Detect Pin Level is not stable. If this
/// bit is set to 1, it means the Card Detect Pin Level is stable. The Software Reset For All
/// in the Software Reset Register shall not affect this bit.
#[bit(17, r)]
card_state_stable: bool,
/// This bit indicates whether a card has been inserted. Changing from 0 to 1 generates a Card
/// Insertion interrupt in the Normal Interrupt Status register and changing from 1 to 0
/// generates a Card Removal Interrupt in the Normal Interrupt Status register. The Software
/// Reset For All in the Software Reset register shall not affect this bit. If a Card is
/// removed while its power is on and its clock is oscillating, the HC shall clear SD Bus Power
/// in the Power Control register and SD Clock Enable in the Clock control register. In
/// addition the HD should clear the HC by the Software Reset For All in Software register. The
/// card detect is active regardless of the SD Bus Power.
#[bit(16, r)]
card_inserted: bool,
/// This status is used for non-DMA read transfers. This read only flag indicates that valid
/// data exists in the host side buffer status. If this bit is 1, readable data exists in the
/// buffer. A change of this bit from 1 to 0 occurs when all the block data is read from the
/// buffer. A change of this bit from 0 to 1 occurs when all the block data is ready in the
/// buffer and generates the Buffer Read Ready Interrupt.
#[bit(11, r)]
buffer_readable: bool,
/// This status is used for non-DMA write transfers. This read only flag indicates if space is
/// available for write data. If this bit is 1, data can be written to the buffer. A change of
/// this bit from 1 to 0 occurs when all the block data is written to the buffer. A change of
/// this bit from 0 to 1 occurs when top of block data can be written to the buffer and
/// generates the Buffer Write Ready Interrupt.
#[bit(10, r)]
buffer_writable: bool,
/// This status is used for detecting completion of a read transfer. This bit is set to 1 for
/// either of the following conditions:
///
/// 1. After the end bit of the read command
/// 2. When writing a 1 to continue Request in the Block Gap Control register to restart a read
/// transfer.
///
/// This bit is cleared to 0 for either of the following conditions:
///
/// 1. When the last data block as specified by block length is transferred to the system.
/// 2. When all valid data blocks have been transferred to the system and no current block
/// transfers are being sent as a result of the Stop At Block Gap Request set to 1. A transfer
/// complete interrupt is generated when this bit changes to 0.
#[bit(9, r)]
read_transfer_active: bool,
/// This status indicates a write transfer is active. If this bit is 0, it means no valid write
/// data exists in the HC. This bit is set in either of the following cases: 1. After the end
/// bit of the write command. 2. When writing a 1 to Continue Request in the Block Gap Control
/// register to restart a write transfer.
///
/// This bit is cleared in either of the following cases:
///
/// 1. After getting the CRC status of the last data block as specified by the transfer count
/// (Single or Multiple)
/// 2. After getting a CRC status of any block where data transmission is about to be stopped
/// by a Stop At Block Gap Request.
///
/// During a write transaction, a Block Gap Event interrupt is generated when this bit is
/// changed to 0, as a result of the Stop At Block Gap Request being set. This status is useful
/// for the HD in determining when to issue commands during write busy.
#[bit(8, r)]
write_transfer_active: bool,
#[bit(2, r)]
dat_line_active: bool,
/// This status bit is generated if either the DAT Line Active or the Read transfer Active is
/// set to 1. If this bit is 0, it indicates the HC can issue the next SD command. Commands
/// with busy signal belong to Command Inhibit (DAT) (ex. R1b, R5b type). Changing from 1 to 0
/// generates a Transfer Complete interrupt in the Normal interrupt status register.
#[bit(1, r)]
command_inhibit_dat: bool,
/// 0 indicates the CMD line is not in use and the host controller can issue a SD command
/// using the CMD line. This bit is set immediately after the Command register (00Fh) is
/// written. This bit is cleared when the command response is received. Even if the Command
/// Inhibit (DAT) is set to 1, Commands using only the CMD line can be issued if this bit is 0.
/// Changing from 1 to 0 generates a Command complete interrupt in the Normal Interrupt Status
/// register. If the HC cannot issue the command because of a command conflict error or because
/// of Command Not Issued By Auto CMD12 Error, this bit shall remain 1 and the Command Complete
/// is not set. Status issuing Auto CMD12 is not read from this bit.
#[bit(0, r)]
command_inhibit_cmd: bool,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum BusWidth {
_1bit = 0,
_4bits = 1,
}
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DmaSelect {
Sdma = 0b00,
Adma1_32bits = 0b01,
Adma2_32bits = 0b10,
Adma2_64bits = 0b11,
}
#[bitbybit::bitenum(u3, exhaustive = false)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SdBusVoltageSelect {
Off = 0b000,
_1_8V = 0b101,
_3_0V = 0b110,
_3_3V = 0b111,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
pub struct HostPowerBlockgapWakeupControl {
#[bit(26, rw)]
wakeup_event_enable_on_sd_card_removal: bool,
#[bit(25, rw)]
wakeup_event_enable_on_sd_card_insertion: bool,
#[bit(24, rw)]
wakeup_event_enable_on_card_interrupt: bool,
#[bit(19, rw)]
interrupt_at_block_gap: bool,
#[bit(18, rw)]
read_wait_control: bool,
#[bit(17, rw)]
continue_request: bool,
#[bit(16, rw)]
stop_as_block_gap_request: bool,
#[bits(9..=11, rw)]
sd_bus_voltage_select: Option<SdBusVoltageSelect>,
#[bit(8, rw)]
sd_bus_power: bool,
#[bit(7, rw)]
card_detect_signal_detection: bool,
#[bit(6, rw)]
card_detetect_test_level: bool,
#[bits(3..=4, rw)]
dma_select: DmaSelect,
#[bit(2, rw)]
high_speed_enable: bool,
#[bit(1, rw)]
bus_width: BusWidth,
#[bit(0, rw)]
led_control: bool,
}
#[bitbybit::bitenum(u8, exhaustive = false)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SdClockDivisor {
Div256 = 0x80,
Div128 = 0x40,
Div64 = 0x20,
Div32 = 0x10,
Div16 = 0x08,
Div8 = 0x04,
Div4 = 0x02,
Div2 = 0x01,
Div1 = 0x00,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
pub struct ClockAndTimeoutAndSwResetControl {
#[bit(26, rw)]
software_reset_for_dat_line: bool,
#[bit(25, rw)]
software_reset_for_cmd_line: bool,
#[bit(24, rw)]
software_reset_for_all: bool,
/// Interval: TMCLK * 2^(13 + register value)
///
/// 0b1111 is reserved.
#[bits(16..=19, rw)]
data_timeout_counter_value: u4,
#[bits(8..=15, rw)]
sdclk_frequency_select: Option<SdClockDivisor>,
#[bit(2, rw)]
sd_clock_enable: bool,
#[bit(1, r)]
internal_clock_stable: bool,
#[bit(0, rw)]
internal_clock_enable: bool,
}
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"), forbid_overlaps)]
pub struct InterruptStatus {
#[bit(29, rw)]
ceata_error_status: bool,
#[bit(28, rw)]
target_response_error: bool,
#[bit(25, rw)]
adma_error: bool,
#[bit(24, rw)]
auto_cmd12_error: bool,
#[bit(23, rw)]
current_limit_error: bool,
#[bit(22, rw)]
data_end_bit_error: bool,
#[bit(21, rw)]
data_crc_error: bool,
#[bit(20, rw)]
data_timeout_error: bool,
#[bit(19, rw)]
command_index_error: bool,
#[bit(18, rw)]
command_end_bit_error: bool,
#[bit(17, rw)]
command_crc_error: bool,
#[bit(16, rw)]
command_timeout_error: bool,
#[bit(15, r)]
error_interrupt: bool,
#[bit(10, rw)]
boot_terminate: bool,
#[bit(9, rw)]
boot_ack_recv: bool,
#[bit(8, r)]
card_interrupt: bool,
#[bit(7, rw)]
card_removal: bool,
#[bit(6, rw)]
card_insertion: bool,
#[bit(5, rw)]
buffer_read_ready: bool,
#[bit(4, rw)]
buffer_write_ready: bool,
#[bit(3, rw)]
dma_interrupt: bool,
#[bit(2, rw)]
blockgap_event: bool,
#[bit(1, rw)]
transfer_complete: bool,
#[bit(0, rw)]
command_complete: bool,
}
impl InterruptStatus {
pub const ALL_BITS: u32 = 0x33FF06FF;
}
#[bitbybit::bitfield(u32, default = 0x0, debug, defmt_bitfields(feature = "defmt"))]
pub struct InterruptMask {
#[bit(29, rw)]
ceata_error_status: bool,
#[bit(28, rw)]
target_response_error: bool,
#[bit(25, rw)]
adma_error: bool,
#[bit(24, rw)]
auto_cmd12_error: bool,
#[bit(23, rw)]
current_limit_error: bool,
#[bit(22, rw)]
data_end_bit_error: bool,
#[bit(21, rw)]
data_crc_error: bool,
#[bit(20, rw)]
data_timeout_error: bool,
#[bit(19, rw)]
command_index_error: bool,
#[bit(18, rw)]
command_end_bit_error: bool,
#[bit(17, rw)]
command_crc_error: bool,
#[bit(16, rw)]
command_timeout_error: bool,
#[bit(15, rw)]
error_interrupt: bool,
#[bit(10, rw)]
boot_terminate: bool,
#[bit(9, rw)]
boot_ack_recv: bool,
#[bit(8, rw)]
card_interrupt: bool,
#[bit(7, rw)]
card_removal: bool,
#[bit(6, rw)]
card_insertion: bool,
#[bit(5, rw)]
buffer_read_ready: bool,
#[bit(4, rw)]
buffer_write_ready: bool,
#[bit(3, rw)]
dma_interrupt: bool,
#[bit(2, rw)]
blockgap_event: bool,
#[bit(1, rw)]
transfer_complete: bool,
#[bit(0, rw)]
command_complete: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug, defmt_bitfields(feature = "defmt"))]
pub struct Capabilities {
#[bit(30, rw)]
spi_block_mode: bool,
#[bit(29, rw)]
spi_mode: bool,
#[bit(28, rw)]
_64_bit_system_bus_support: bool,
#[bit(27, rw)]
interrupt_mode: bool,
#[bit(26, rw)]
voltage_support_1_8v: bool,
#[bit(25, rw)]
voltage_support_3_0v: bool,
#[bit(24, rw)]
voltage_support_3_3v: bool,
#[bit(23, rw)]
suspend_resume_support: bool,
#[bit(22, rw)]
sdma_support: bool,
#[bit(21, rw)]
high_speed_support: bool,
#[bit(19, rw)]
adma2_support: bool,
#[bit(18, rw)]
extended_media_bus_support: bool,
#[bits(16..=17, rw)]
max_block_length: u2,
#[bit(7, rw)]
timeout_clock_unit: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug, defmt_bitfields(feature = "defmt"))]
pub struct BlockSizeRegister {
/// Enabled when block count enable in the transfer mode register is set to 1 and only
/// valid for multiple block transfers.
#[bits(16..=31, rw)]
block_counts_for_current_transfer: u16,
#[bits(12..=14, rw)]
host_sdma_buffer_size: u3,
#[bits(0..=11, rw)]
transfer_block_size: u12,
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct Registers {
sdma_system_addr: u32,
block_params: BlockSizeRegister,
/// Bit 39-8 of Command-Format.
argument: u32,
command: CommandRegister,
#[mmio(PureRead)]
responses: [u32; 4],
buffer_data_port: u32,
#[mmio(PureRead)]
present_state: PresentState,
host_power_blockgap_wakeup_control: HostPowerBlockgapWakeupControl,
clock_timeout_sw_reset_control: ClockAndTimeoutAndSwResetControl,
interrupt_status: InterruptStatus,
interrupt_status_enable: InterruptMask,
interrupt_signal_enable: InterruptMask,
#[mmio(PureRead)]
auto_cmd12_error_status: u32,
#[mmio(PureRead)]
capabilities: Capabilities,
_gap_0: u32,
#[mmio(PureRead)]
maximum_current_capabilities: u32,
_gap_1: u32,
force_event_register: u32,
adma_error_status: u32,
adma_system_address: u32,
_gap_2: u32,
boot_timeout_control: u32,
debug_selection: u32,
_gap_3: [u32; 0x22],
spi_interrupt_support: u32,
_gap_4: [u32; 0x2],
slot_interrupt_status_host_controll_version: u32,
}
static_assertions::const_assert_eq!(core::mem::size_of::<Registers>(), 0x100);
impl Registers {
/// Create a new SDIO MMIO instance for SDIO 0 at address [SDIO_BASE_ADDR_0].
///
/// # 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.
#[inline]
pub const unsafe fn new_mmio_fixed_0() -> MmioRegisters<'static> {
unsafe { Self::new_mmio_at(SDIO_BASE_ADDR_0) }
}
/// Create a new SDIO MMIO instance for SDIO 1 at address [SDIO_BASE_ADDR_1].
///
/// # 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.
#[inline]
pub const unsafe fn new_mmio_fixed_1() -> MmioRegisters<'static> {
unsafe { Self::new_mmio_at(SDIO_BASE_ADDR_1) }
}
}
+19 -77
View File
@@ -4,7 +4,6 @@
use super::{CLOCK_CONTROL_OFFSET, SLCR_BASE_ADDR};
use arbitrary_int::{u4, u6, u7, u10};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Bypass {
NotBypassed = 0b00,
/// This is the default reset value.
@@ -13,13 +12,7 @@ pub enum Bypass {
BypassedRegardlessOfPinStrapping = 0b11,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, debug)]
pub struct PllControl {
/// Feedback divisor for the PLL.
///
@@ -75,7 +68,7 @@ impl PllControl {
}
}
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"), forbid_overlaps)]
#[bitbybit::bitfield(u32, debug)]
pub struct PllConfig {
#[bits(12..=21, rw)]
lock_count: u10,
@@ -87,7 +80,7 @@ pub struct PllConfig {
loop_resistor: u4,
}
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"), forbid_overlaps)]
#[bitbybit::bitfield(u32, debug)]
pub struct PllStatus {
#[bit(5, r)]
io_pll_stable: bool,
@@ -103,7 +96,7 @@ pub struct PllStatus {
arm_pll_lock: bool,
}
#[bitbybit::bitfield(u32, debug, defmt_fields(feature = "defmt"), forbid_overlaps)]
#[bitbybit::bitfield(u32, debug)]
pub struct FpgaClockControl {
// Reset value 0x1
#[bits(20..=25, rw)]
@@ -135,7 +128,7 @@ pub enum SrcSelArm {
IoPll = 0b11,
}
#[bitbybit::bitfield(u32, debug, default = 0x0)]
#[bitbybit::bitfield(u32, debug)]
pub struct ArmClockControl {
#[bit(28, rw)]
cpu_peri_clk_act: bool,
@@ -185,32 +178,24 @@ pub struct DciClockControl {
clk_act: bool,
}
#[bitbybit::bitfield(u32, debug, default = 0x0)]
#[bitbybit::bitfield(u32, debug)]
pub struct ClockRatioSelectReg {
/// Reset value: 0x1 (6:2:1 clock)
#[bit(0, rw)]
sel: CpuClockRatio,
sel: ClockkRatioSelect,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CpuClockRatio {
pub enum ClockkRatioSelect {
/// 4:2:1 clock ratio, which is an abbreviation for 4:2:2:1.
///
/// The 4x clock is calculated by dividing the reference clock
/// by the divisor, the rest by dividing by 2, 2 and 4 respectively.
FourToTwoToOne = 0b0,
/// 6:2:1 clock ratio, which is an abbreviation for 6:3:2:1.
///
/// The 6x clock is calculated by dividing the reference clock
/// by the divisor, the rest by dividing by 2, 3 and 6 respectively.
SixToTwoToOne = 0b1,
}
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Debug, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SrcSelIo {
IoPll = 0b00,
IoPllAlt = 0b01,
@@ -237,13 +222,7 @@ impl PartialEq for SrcSelIo {
}
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, debug)]
pub struct GigEthClockControl {
#[bits(20..=25, rw)]
divisor_1: u6,
@@ -258,20 +237,13 @@ pub struct GigEthClockControl {
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum SrcSelGigEthRclk {
Mio = 0,
Emio = 1,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, debug)]
pub struct GigEthRclkControl {
#[bit(4, rw)]
srcsel: SrcSelGigEthRclk,
@@ -280,13 +252,8 @@ pub struct GigEthRclkControl {
clk_enable: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct CanClockControl {
#[bits(20..=25, rw)]
divisor_1: u6,
@@ -300,13 +267,7 @@ pub struct CanClockControl {
clk_0_act: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0)]
pub struct SingleCommonPeriphIoClockControl {
#[bits(8..=13, rw)]
divisor: u6,
@@ -316,13 +277,7 @@ pub struct SingleCommonPeriphIoClockControl {
clk_act: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, debug)]
pub struct DualCommonPeriphIoClockControl {
#[bits(8..=13, rw)]
divisor: u6,
@@ -335,8 +290,7 @@ pub struct DualCommonPeriphIoClockControl {
}
#[bitbybit::bitenum(u3, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum SrcSelTpiu {
IoPll = 0b000,
IoPllAlt = 0b001,
@@ -348,13 +302,7 @@ pub enum SrcSelTpiu {
EmioTraceClkAlt2 = 0b111,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, debug)]
pub struct TracePortClockControl {
#[bits(8..=13, rw)]
divisor: u6,
@@ -369,13 +317,7 @@ pub struct TracePortClockControl {
/// AMBA peripheral clock control.
///
/// These clocks must be enabled if you want to read from the peripheral register space.
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, debug)]
pub struct AperClockControl {
#[bit(24, rw)]
smc_1x_clk_act: bool,
@@ -457,7 +399,7 @@ pub struct ClockControlRegisters {
#[mmio(Inner)]
fpga_3_clk_ctrl: FpgaClockControlRegisters,
_gap1: [u32; 5],
clk_ratio_select: ClockRatioSelectReg,
clk_621_true: ClockRatioSelectReg,
}
impl ClockControlRegisters {
+10 -38
View File
@@ -1,8 +1,7 @@
use arbitrary_int::{u2, u3};
#[bitbybit::bitenum(u4, exhaustive = false)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum VRefSel {
/// VREF = 0.6 V
Lpddr2 = 0b0001,
@@ -14,13 +13,7 @@ pub enum VRefSel {
Ddr2 = 0b1000,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, debug)]
pub struct DdrControl {
/// Enables VRP/VRN.
#[bit(9, rw)]
@@ -35,13 +28,7 @@ pub struct DdrControl {
vref_int_en: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x00,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x00, debug)]
pub struct DciControl {
#[bit(20, rw)]
update_control: bool,
@@ -62,13 +49,7 @@ pub struct DciControl {
reset: bool,
}
#[bitbybit::bitfield(
u32,
default = 0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, debug)]
pub struct DciStatus {
#[bit(13, rw)]
done: bool,
@@ -77,8 +58,7 @@ pub struct DciStatus {
}
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum OutputEnable {
IBuf = 0b00,
__Reserved0 = 0b01,
@@ -87,8 +67,7 @@ pub enum OutputEnable {
}
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum InputType {
Off = 0b00,
VRefBasedDifferentialReceiverForSstlHstl = 0b01,
@@ -97,8 +76,7 @@ pub enum InputType {
}
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum DciType {
Disabled = 0b00,
DciDrive = 0b01,
@@ -106,13 +84,7 @@ pub enum DciType {
DciTermination = 0b11,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DdriobConfig {
#[bit(11, rw)]
pullup_enable: bool,
@@ -146,8 +118,8 @@ pub struct DdrIobRegisters {
ddriob_drive_slew_data: u32,
ddriob_drive_slew_diff: u32,
ddriob_drive_slew_clock: u32,
ddr_control: DdrControl,
dci_control: DciControl,
ddr_ctrl: DdrControl,
dci_ctrl: DciControl,
dci_status: DciStatus,
}
+3 -5
View File
@@ -4,16 +4,14 @@
use arbitrary_int::{u2, u3};
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum Speed {
SlowCmosEdge = 0b0,
FastCmosEdge = 0b1,
}
#[bitbybit::bitenum(u3)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum IoType {
LvCmos18 = 0b001,
LvCmos25 = 0b010,
@@ -21,7 +19,7 @@ pub enum IoType {
Hstl = 0b100,
}
#[bitbybit::bitfield(u32, default = 0x0, debug, defmt_fields(feature = "defmt"))]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
#[derive(PartialEq, Eq)]
pub struct Config {
#[bit(13, rw)]
+6 -13
View File
@@ -17,14 +17,14 @@ pub mod mio;
pub mod reset;
#[bitbybit::bitenum(u3, exhaustive = false)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum VrefSel {
Disabled = 0b000,
Vref0_9V = 0b001,
}
#[bitbybit::bitfield(u32, default = 0, debug, defmt_fields(feature = "defmt"))]
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct GpiobControl {
#[bit(11, rw)]
vref_sw_en: bool,
@@ -62,14 +62,14 @@ impl GpiobRegisters {
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum BootPllConfig {
Enabled = 0,
/// Disabled and bypassed.
Bypassed = 1,
}
#[bitbybit::bitfield(u32, default = 0, debug, defmt_fields(feature = "defmt"))]
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct BootModeRegister {
#[bit(4, r)]
pll_config: BootPllConfig,
@@ -79,20 +79,13 @@ pub struct BootModeRegister {
#[bitbybit::bitenum(u4)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum LevelShifterConfig {
DisableAll = 0x00,
EnablePsToPl = 0xA,
EnableAll = 0xF,
}
#[bitbybit::bitfield(
u32,
default = 0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, debug)]
pub struct LevelShifterRegister {
#[bits(0..=3, rw)]
user_lvl_shftr_en: Option<LevelShifterConfig>,
+24 -185
View File
@@ -1,14 +1,6 @@
use arbitrary_int::u17;
use super::{RESET_BLOCK_OFFSET, SLCR_BASE_ADDR};
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DualClockReset {
/// Peripheral 1 AMBA software reset.
#[bit(1, rw)]
@@ -18,14 +10,8 @@ pub struct DualClockReset {
periph0_cpu1x_rst: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
pub struct DualRefAndClockResetSpiUart {
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DualRefAndClockReset {
/// Periperal 1 Reference software reset.
#[bit(3, rw)]
periph1_ref_rst: bool,
@@ -40,47 +26,13 @@ pub struct DualRefAndClockResetSpiUart {
periph0_cpu1x_rst: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
pub struct DualRefAndClockResetSdio {
/// Periperal 1 Reference software reset.
#[bit(5, rw)]
periph1_ref_rst: bool,
/// Peripheral 0 Reference software reset.
#[bit(4, rw)]
periph0_ref_rst: bool,
/// Peripheral 1 AMBA software reset.
#[bit(1, rw)]
periph1_cpu1x_rst: bool,
/// Peripheral 0 AMBA software reset.
#[bit(0, rw)]
periph0_cpu1x_rst: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct GpioClockReset {
#[bit(0, rw)]
gpio_cpu1x_rst: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct EthernetReset {
#[bit(5, rw)]
gem1_ref_rst: bool,
@@ -96,152 +48,39 @@ pub struct EthernetReset {
gem0_cpu1x_rst: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
pub struct ResetControlQspiSmc {
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct QspiResetControl {
#[bit(2, rw)]
qspi_ref_reset: bool,
#[bit(1, rw)]
ref_reset: bool,
#[bit(0, rw)]
cpu_1x_reset: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
pub struct FpgaResetControl {
/// This block always needs to be written with 0. I think it contains some other hidden
/// reset lines. This field makes this explicit.
#[bits(8..=24, rw)]
zero_block_0: u17,
#[bit(3, rw)]
fpga_3: bool,
#[bit(2, rw)]
fpga_2: bool,
#[bit(1, rw)]
fpga_1: bool,
#[bit(0, rw)]
fpga_0: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
pub struct CpuResetControl {
#[bit(8, rw)]
peripheral_reset: bool,
#[bit(5, rw)]
cpu1_clockstop: bool,
#[bit(4, rw)]
cpu0_clockstop: bool,
#[bit(1, rw)]
cpu1_reset: bool,
#[bit(0, rw)]
cpu0_reset: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
pub struct PsResetControl {
#[bit(0, rw)]
soft_reset: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
pub struct ResetControlSingleBit {
#[bit(0, rw)]
reset: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
pub struct ResetControlInterconnect {
/// Care must be taken to ensure that the AXI
/// interconnect does not have outstanding
/// transactions and the bus is idle.
#[bit(0, rw)]
reset: bool,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ApuWatchdogTarget {
/// Same system level as PS_SRST_B.
PsSrstB = 1,
CpuAssociatedWithWdt = 0,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
pub struct WatchTimerResetControl {
#[bit(1, rw)]
apu_wdt_1_reset_target: ApuWatchdogTarget,
#[bit(0, rw)]
apu_wdt_0_reset_target: ApuWatchdogTarget,
}
/// Reset control block.
///
/// All reset signal bits are active high, writing a 1 asserts the reset.
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct ResetControl {
/// Processing System Software reset control
pss: PsResetControl,
ddr: ResetControlSingleBit,
/// PS Software reset control
pss: u32,
ddr: u32,
/// Central interconnect reset control
topsw: ResetControlInterconnect,
dmac: ResetControlSingleBit,
usb: DualClockReset,
topsw: u32,
dmac: u32,
usb: u32,
eth: EthernetReset,
sdio: DualRefAndClockResetSdio,
spi: DualRefAndClockResetSpiUart,
sdio: DualRefAndClockReset,
spi: DualRefAndClockReset,
can: DualClockReset,
i2c: DualClockReset,
uart: DualRefAndClockResetSpiUart,
uart: DualRefAndClockReset,
gpio: GpioClockReset,
lqspi: ResetControlQspiSmc,
smc: ResetControlQspiSmc,
ocm: ResetControlSingleBit,
lqspi: QspiResetControl,
smc: u32,
ocm: u32,
_gap0: u32,
fpga: FpgaResetControl,
a9_cpu: CpuResetControl,
fpga: u32,
a9_cpu: u32,
_gap1: u32,
rs_awdt: WatchTimerResetControl,
rs_awdt: u32,
}
impl ResetControl {
+6 -31
View File
@@ -9,7 +9,6 @@ pub const SPI_1_BASE_ADDR: usize = 0xE000_7000;
/// The SPI reference block will be divided by a divisor value.
#[bitbybit::bitenum(u3)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum BaudDivSel {
By4 = 0b001,
By8 = 0b010,
@@ -34,13 +33,8 @@ impl BaudDivSel {
}
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
// TODO: Use bitbybit debug support as soon as it was added.
#[bitbybit::bitfield(u32, default = 0x0)]
pub struct Config {
#[bit(17, rw)]
modefail_gen_en: bool,
@@ -73,13 +67,7 @@ pub struct Config {
master_ern: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct InterruptStatus {
#[bit(6, rw)]
tx_underflow: bool,
@@ -98,12 +86,7 @@ pub struct InterruptStatus {
rx_ovr: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct InterruptControl {
#[bit(6, w)]
@@ -123,7 +106,7 @@ pub struct InterruptControl {
rx_ovr: bool,
}
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"), forbid_overlaps)]
#[bitbybit::bitfield(u32, debug)]
pub struct InterruptMask {
#[bit(6, r)]
tx_underflow: bool,
@@ -143,7 +126,6 @@ pub struct InterruptMask {
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FifoWrite(arbitrary_int::UInt<u32, 8>);
impl FifoWrite {
@@ -164,7 +146,6 @@ impl FifoWrite {
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FifoRead(arbitrary_int::UInt<u32, 8>);
impl FifoRead {
@@ -180,13 +161,7 @@ impl FifoRead {
}
/// The numbers specified in the register fields are always specified in number of
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
forbid_overlaps,
defmt_bitfields(feature = "defmt")
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct DelayControl {
/// Number of cycles the chip select is de-asserted between words when CPHA = 0
#[bits(24..=31, rw)]
+12 -46
View File
@@ -4,9 +4,8 @@ use arbitrary_int::u4;
pub const TTC_0_BASE_ADDR: usize = 0xF800_1000;
pub const TTC_1_BASE_ADDR: usize = 0xF800_2000;
#[derive(Debug, Default)]
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ClockSource {
/// PS internal bus clock.
#[default]
@@ -14,13 +13,7 @@ pub enum ClockSource {
External = 0b1,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct ClockControl {
/// When this bit is set and the external clock is selected, the counter clocks on the
/// negative edge of the external clock input.
@@ -35,16 +28,14 @@ pub struct ClockControl {
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum Mode {
Overflow = 0b0,
Interval = 0b1,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug, Default)]
pub enum WavePolarity {
/// The waveform output goes from high to low on a match 0 interrupt and returns high on
/// overflow or interval interrupt.
@@ -56,20 +47,13 @@ pub enum WavePolarity {
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum WaveEnable {
Enable = 0b0,
Disable = 0b1,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct CounterControl {
#[bit(6, rw)]
wave_polarity: WavePolarity,
@@ -92,25 +76,19 @@ pub struct CounterControl {
disable: bool,
}
#[bitbybit::bitfield(u32, debug, defmt_fields(feature = "defmt"), forbid_overlaps)]
#[bitbybit::bitfield(u32, debug)]
pub struct Counter {
#[bits(0..=15, r)]
count: u16,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_fields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, debug)]
pub struct RwValue {
#[bits(0..=15, rw)]
value: u16,
}
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"), forbid_overlaps)]
#[bitbybit::bitfield(u32, debug)]
pub struct InterruptStatus {
/// Even timer overflow interrupt.
#[bit(5, r)]
@@ -127,13 +105,7 @@ pub struct InterruptStatus {
interval: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, debug)]
pub struct InterruptControl {
/// Even timer overflow interrupt.
#[bit(5, rw)]
@@ -150,13 +122,7 @@ pub struct InterruptControl {
interval: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, debug)]
pub struct EventControl {
/// E_Ov bit. When set to 0, the event timer is disabled and set to 0 when an event timer
/// register overflow occurs. Otherwise, continue counting on overflow.
@@ -170,7 +136,7 @@ pub struct EventControl {
enable: bool,
}
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"), forbid_overlaps)]
#[bitbybit::bitfield(u32, debug)]
pub struct EventCount {
#[bits(0..=15, r)]
count: u16,
+13 -55
View File
@@ -5,8 +5,7 @@ pub const UART_0_BASE: usize = 0xE000_0000;
pub const UART_1_BASE: usize = 0xE000_1000;
#[bitbybit::bitenum(u3, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum Parity {
Even = 0b000,
Odd = 0b001,
@@ -22,7 +21,6 @@ pub enum Parity {
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Default, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CharLen {
SixBits = 0b11,
SevenBits = 0b10,
@@ -33,7 +31,6 @@ pub enum CharLen {
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Default, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ClockSelect {
#[default]
UartRefClk = 0b0,
@@ -42,7 +39,6 @@ pub enum ClockSelect {
#[bitbybit::bitenum(u2)]
#[derive(Default, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Stopbits {
#[default]
One = 0b00,
@@ -52,7 +48,6 @@ pub enum Stopbits {
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Debug, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ChMode {
#[default]
Normal = 0b00,
@@ -61,13 +56,7 @@ pub enum ChMode {
RemoteLoopback = 0b11,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
forbid_overlaps,
defmt_bitfields(feature = "defmt")
)]
#[bitbybit::bitfield(u32, debug)]
pub struct Control {
/// Stop transmitter break.
#[bit(8, rw)]
@@ -98,13 +87,7 @@ pub struct Control {
rx_rst: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
forbid_overlaps,
defmt_fields(feature = "defmt")
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct Mode {
#[bits(8..=9, rw)]
chmode: ChMode,
@@ -119,45 +102,32 @@ pub struct Mode {
clksel: ClockSelect,
}
#[bitbybit::bitfield(
u32,
default = 0,
debug,
forbid_overlaps,
defmt_fields(feature = "defmt")
)]
#[bitbybit::bitfield(u32, default = 0, debug)]
pub struct Baudgen {
#[bits(0..=15, rw)]
cd: u16,
}
#[bitbybit::bitfield(
u32,
default = 0,
debug,
forbid_overlaps,
defmt_fields(feature = "defmt")
)]
#[bitbybit::bitfield(u32, default = 0, debug)]
pub struct BaudRateDivisor {
#[bits(0..=7, rw)]
bdiv: u8,
}
#[bitbybit::bitfield(u32, debug, forbid_overlaps, defmt_fields(feature = "defmt"))]
#[bitbybit::bitfield(u32, debug)]
pub struct Fifo {
#[bits(0..=7, rw)]
fifo: u8,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum Ttrig {
LessThanTTrig = 0b0,
GreaterEqualTTrig = 0b1,
}
#[bitbybit::bitfield(u32, debug, forbid_overlaps, defmt_bitfields(feature = "defmt"))]
#[bitbybit::bitfield(u32, debug)]
pub struct Status {
#[bit(14, r)]
tx_near_full: bool,
@@ -184,13 +154,8 @@ pub struct Status {
rx_trg: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
forbid_overlaps,
defmt_bitfields(feature = "defmt")
)]
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct InterruptControl {
#[bit(12, w)]
tx_over: bool,
@@ -223,14 +188,13 @@ pub struct InterruptControl {
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FifoTrigger {
#[bits(0..=5, rw)]
trig: u6,
}
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"))]
#[derive(PartialEq, Eq)]
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct InterruptMask {
#[bit(12, r)]
tx_over: bool,
@@ -262,13 +226,7 @@ pub struct InterruptMask {
rx_trg: bool,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct InterruptStatus {
#[bit(12, rw)]
tx_over: bool,
+8 -19
View File
@@ -4,13 +4,13 @@ version = 4
[[package]]
name = "aarch32-cpu"
version = "0.2.0"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1417bbf608824a44cb2fa2ad74b5ec28c0ae4c83df62a4bd2b532bf04c241ade"
checksum = "5db6700cf01549520abec199376115e1ceb6fde1d1de30064f0f230be8a0c305"
dependencies = [
"arbitrary-int 2.0.0",
"arm-targets",
"bitbybit 1.4.0",
"bitbybit",
"num_enum",
"thiserror",
]
@@ -104,17 +104,6 @@ dependencies = [
"syn",
]
[[package]]
name = "bitbybit"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d2a3353d70ac1091a33cbf31fc7e77b19091538a7e306e3740712af19807ca"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "boot-image-test"
version = "0.1.0"
@@ -691,10 +680,10 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
name = "zynq7000"
version = "0.2.0"
version = "0.1.1"
dependencies = [
"arbitrary-int 2.0.0",
"bitbybit 2.0.0",
"bitbybit",
"derive-mmio",
"once_cell",
"rustversion",
@@ -707,13 +696,13 @@ name = "zynq7000-boot-image"
version = "0.1.0"
dependencies = [
"arbitrary-int 2.0.0",
"bitbybit 1.4.0",
"bitbybit",
"thiserror",
]
[[package]]
name = "zynq7000-mmu"
version = "0.1.2"
version = "0.1.1"
dependencies = [
"aarch32-cpu",
"arm-targets",
@@ -736,7 +725,7 @@ dependencies = [
[[package]]
name = "zynq7000-rt"
version = "0.2.0"
version = "0.1.1"
dependencies = [
"aarch32-cpu",
"arbitrary-int 2.0.0",
+3 -5
View File
@@ -4,7 +4,7 @@ use clap::Parser as _;
use simple_logger::SimpleLogger;
const DDRC_ADDR_RANGE: RangeInclusive<u32> = 0xf800_6000..=0xf800_62b4;
const DDRIOB_ADDR_RANGE: RangeInclusive<u32> = 0xf800_0b40..=0xf800_0b6c;
const DDRIOB_ADDR_RANGE: RangeInclusive<u32> = 0xf800_0b40..=0xf800_0b68;
const DDRC_FILE_NAME: &str = "ddrc_config_autogen.rs";
const DDRIOB_FILE_NAME: &str = "ddriob_config_autogen.rs";
@@ -198,7 +198,7 @@ fn generate_ddrc_config(
let lpddr_ctrl_3 = reg_to_values.val_as_token("LPDDR CTRL 3", 0xF800_62B4);
let generated = quote::quote! {
//!This file was auto-generated by the [zynq7000-ps7init-extract](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/host/zynq7000-ps7init-extract) program.
//!This file was auto-generated by the [zynq7000-ps7init-extract](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/tools/zynq7000-ps7init-extract) program.
//!
//!This configuration file contains static DDR configuration parameters extracted from the
//!AMD ps7init.tcl file
@@ -310,7 +310,6 @@ fn generate_ddriob_config(
file_name: &str,
) -> std::io::Result<()> {
// Format as hex strings
let ddr_control = reg_to_values.val_as_token("DDRIOB DDR Control", 0xF800_0B6C);
let addr0 = reg_to_values.val_as_token("DDRIOB Addr 0", 0xF800_0B40);
let addr1 = reg_to_values.val_as_token("DDRIOB Addr 1", 0xF800_0B44);
let data0 = reg_to_values.val_as_token("DDRIOB Data 0", 0xF800_0B48);
@@ -319,7 +318,7 @@ fn generate_ddriob_config(
let diff1 = reg_to_values.val_as_token("DDRIOB Diff 1", 0xF800_0B54);
let clock = reg_to_values.val_as_token("DDRIOB Clock", 0xF800_0B58);
let generated = quote::quote! {
//!This file was auto-generated by the [zynq7000-ps7init-extract](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/host/zynq7000-ps7init-extract) program.
//!This file was auto-generated by the [zynq7000-ps7init-extract](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/tools/zynq7000-ps7init-extract) program.
//!
//!This configuration file contains static DDRIOB configuration parameters extracted from the
//!AMD ps7init.tcl file
@@ -327,7 +326,6 @@ fn generate_ddriob_config(
use zynq7000_hal::ddr::DdriobConfigSet;
pub const DDRIOB_CONFIG_SET_ZEDBOARD: DdriobConfigSet = DdriobConfigSet {
ddr_control: zynq7000::slcr::ddriob::DdrControl::new_with_raw_value(#ddr_control),
addr0: regs::DdriobConfig::new_with_raw_value(#addr0),
addr1: regs::DdriobConfig::new_with_raw_value(#addr1),
data0: regs::DdriobConfig::new_with_raw_value(#data0),
+17 -22
View File
@@ -1,33 +1,31 @@
all: check build check-fmt clippy docs-zynq
all: check-all build-all clean-all fmt-all clippy-all docs-zynq
check: (check-dir "firmware") (check-dir "host")
clean: (clean-dir "firmware") (clean-dir "host")
build: build-zynq (build-dir "host")
fmt: (fmt-dir "firmware") (fmt-dir "host")
check-fmt: (check-fmt-dir "firmware") (check-fmt-dir "host")
clippy: (clippy-dir "firmware") (clippy-dir "host")
check-all: (check "firmware") (check "host")
clean-all: (clean "firmware") (clean "host")
build-all: build-zynq (build "host")
fmt-all: (fmt "firmware") (fmt "host")
check-fmt-all: (check-fmt "firmware") (check-fmt "host")
clippy-all: (clippy "firmware") (clippy "host")
check-dir target:
check target:
cd {{target}} && cargo check
build-dir target:
build target:
cd {{target}} && cargo build
[working-directory: 'firmware']
build-zynq: (build-dir "firmware")
cd "zynq7000" && cargo build --all-features
cd "zedboard-fsbl" && cargo build --release
build-zynq: (build "firmware")
cd "firmware/zedboard-fsbl" && cargo build --release
clean-dir target:
clean target:
cd {{target}} && cargo clean
check-fmt-dir target:
check-fmt target:
cd {{target}} && cargo +stable fmt --all -- --check
fmt-dir target:
fmt target:
cd {{target}} && cargo +stable fmt
clippy-dir target:
clippy target:
cd {{target}} && cargo clippy -- -D warnings
[working-directory: 'firmware']
@@ -58,8 +56,5 @@ run binary:
python3 {{justfile_directory()}}/scripts/zynq7000-init.py
# Run the GDB debugger in GUI mode.
gdb-multiarch -q -x {{justfile_directory()}}/firmware/gdb.gdb {{binary}} -tui
flash-nor-zedboard boot_binary:
cd {{justfile_directory()}}/firmware/zedboard-qspi-flasher && cargo build --release
xsct firmware/zedboard-qspi-flasher/qspi-flasher.tcl scripts/ps7_init.tcl -b {{invocation_directory()}}/{{boot_binary}}
# gdb-multiarch -q -x {{justfile_directory()}}/zynq/gdb.gdb {{binary}} -tui
probe-rs run --chip X7Z --protocol jtag --verify --skip-reset {{binary}}
+2121
View File
File diff suppressed because it is too large Load Diff
+11
View File
@@ -0,0 +1,11 @@
[package]
name = "probe-rs-test"
version = "0.1.0"
edition = "2024"
[dependencies]
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
probe-rs = { path = "../../probe-rs/probe-rs" }
defmt-decoder = { version = "1" }
anyhow = { version = "1" }
+20
View File
@@ -0,0 +1,20 @@
source [find interface/ftdi/digilent_jtag_smt2.cfg]
source [find target/zynq_7000.cfg]
init
# Halt the target
halt
# Load the ELF file
load_image ../firmware/target/armv7a-none-eabihf/release/defmt
verify_image ../firmware/target/armv7a-none-eabihf/release/defmt
# Set PC to entrypoint
reg pc 0x100000
# Resume
resume
# Optional: Exit OpenOCD if running in batch mode
shutdown
+88
View File
@@ -0,0 +1,88 @@
use std::{path::Path, time::Duration};
use probe_rs::{
Permissions,
architecture::arm::dp::DpAddress,
flashing::{self, DownloadOptions, ElfOptions, Format},
probe::{WireProtocol, list::Lister},
rtt,
};
use tracing_subscriber::EnvFilter;
fn main() -> anyhow::Result<()> {
// Initialize the subscriber with an environment filter.
// This allows you to control log levels using the `RUST_LOG` environment variable.
tracing_subscriber::fmt()
.with_env_filter(
// Reads the RUST_LOG environment variable.
// Defaults to "info" if not set.
EnvFilter::from_default_env(),
)
.init();
let lister = Lister::new();
let probes = lister.list_all();
let mut probe = probes[0].open()?;
probe.select_protocol(WireProtocol::Jtag).unwrap();
let mut session = probe.attach("X7Z", Permissions::default()).unwrap();
println!("target: {:?}", session.target().memory_ports);
let arm_if = session.get_arm_interface()?;
arm_if.select_debug_port(DpAddress::Default)?;
let elf_path = Path::new("../firmware/target/armv7a-none-eabihf/release/defmt");
let format = Format::Elf(ElfOptions::default());
let mut download_opts = DownloadOptions::default();
download_opts.verify = true;
download_opts.skip_reset = true;
println!("flashing ELF file");
let loader = flashing::build_loader(&mut session, elf_path, format.clone(), None)?;
let elf_file = std::fs::read(elf_path).unwrap();
let rtt_addr = rtt::find_rtt_control_block_in_raw_file(&elf_file)?.unwrap();
let vector_table_addr = loader.vector_table_addr().unwrap();
loader.commit(&mut session, download_opts)?;
println!("attaching RTT");
let mut memory = session.memory_access_port(0)?;
let mut rtt = rtt::Rtt::attach_at(&mut memory, rtt_addr)?;
drop(memory);
session.prepare_running_on_ram(vector_table_addr, 0)?;
let mut core = session.core(0)?;
println!("running core");
core.run()?;
let defmt_table = defmt_decoder::Table::parse(&elf_file)?.unwrap();
let mut stream_decoder = defmt_table.new_stream_decoder();
drop(core);
// Read from a channel
loop {
let mut memory = session.memory_access_port(0)?;
for channel in rtt.up_channels() {
let mut buf = [0u8; 1024];
let count = channel.read(&mut memory, &mut buf[..])?;
if count > 0 {
stream_decoder.received(&buf[..count]);
// decode the received data
match stream_decoder.decode() {
Ok(frame) => {
println!("defmt frame: {}", frame.display_message())
}
Err(defmt_decoder::DecodeError::UnexpectedEof) => {
println!("unexpected EOF");
}
Err(defmt_decoder::DecodeError::Malformed) => {
println!("malformed defmt frame");
}
}
}
}
std::thread::sleep(Duration::from_millis(100));
}
}
+4 -6
View File
@@ -1,12 +1,10 @@
MEMORY
{
/* Zedboard: 512 MB DDR3. Only use 62 MB for now, should be plenty for a bare-metal app.
1 MB stack memory and 1 MB of memory which will be configured as uncached device memory by the
MMU. This is recommended for something like DMA descriptors. */
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 62M
STACKS : ORIGIN = 0x3F00000, LENGTH = 1M
/* 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 MMU. This is
recommended for something like DMA descriptors. */
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 63M
UNCACHED(rx): ORIGIN = 0x4000000, LENGTH = 1M
OCM_UPPER(rx): ORIGIN = 0xFFFF0000, LENGTH = 64K
}
REGION_ALIAS("DATA", CODE);
+2 -5
View File
@@ -1,7 +1,7 @@
MEMORY
{
/* The Zynq7000 has 256 kB of OCM memory of which 196 kB can be used for the FSBL */
CODE(rx) : ORIGIN = 0x00000000, LENGTH = 196K
/* The Zynq7000 has 192 kB of OCM memory which can be used for the FSBL */
CODE(rx) : ORIGIN = 0x00000000, LENGTH = 192K
OCM_UPPER(rx): ORIGIN = 0xFFFF0000, LENGTH = 64K
/* Leave 1 MB of memory which will be configured as uncached device memory by the MMU. This can
be used for something like DMA descriptors, but the DDR needs to be set up first in addition
@@ -9,10 +9,7 @@ MEMORY
UNCACHED(rx): ORIGIN = 0x4000000, LENGTH = 1M
}
REGION_ALIAS("VECTORS", CODE);
REGION_ALIAS("DATA", CODE);
/* Use the upper OCM as the stack */
REGION_ALIAS("STACKS", OCM_UPPER);
SECTIONS
{
+48 -48
View File
@@ -84,26 +84,26 @@ proc ps7_ddr_init_data_3_0 {} {
mask_write 0XF800611C 0x7FFFFFCF 0x40000001
mask_write 0XF8006120 0x7FFFFFCF 0x40000001
mask_write 0XF8006124 0x7FFFFFCF 0x40000001
mask_write 0XF800612C 0x000FFFFF 0x00033C03
mask_write 0XF8006130 0x000FFFFF 0x00034003
mask_write 0XF8006134 0x000FFFFF 0x0002F400
mask_write 0XF8006138 0x000FFFFF 0x00030400
mask_write 0XF800612C 0x000FFFFF 0x00024000
mask_write 0XF8006130 0x000FFFFF 0x00022C00
mask_write 0XF8006134 0x000FFFFF 0x00023000
mask_write 0XF8006138 0x000FFFFF 0x00024C00
mask_write 0XF8006140 0x000FFFFF 0x00000035
mask_write 0XF8006144 0x000FFFFF 0x00000035
mask_write 0XF8006148 0x000FFFFF 0x00000035
mask_write 0XF800614C 0x000FFFFF 0x00000035
mask_write 0XF8006154 0x000FFFFF 0x00000083
mask_write 0XF8006158 0x000FFFFF 0x00000083
mask_write 0XF800615C 0x000FFFFF 0x0000007F
mask_write 0XF8006160 0x000FFFFF 0x00000078
mask_write 0XF8006168 0x001FFFFF 0x00000124
mask_write 0XF800616C 0x001FFFFF 0x00000125
mask_write 0XF8006170 0x001FFFFF 0x00000112
mask_write 0XF8006174 0x001FFFFF 0x00000116
mask_write 0XF800617C 0x000FFFFF 0x000000C3
mask_write 0XF8006180 0x000FFFFF 0x000000C3
mask_write 0XF8006184 0x000FFFFF 0x000000BF
mask_write 0XF8006188 0x000FFFFF 0x000000B8
mask_write 0XF8006154 0x000FFFFF 0x00000077
mask_write 0XF8006158 0x000FFFFF 0x0000007C
mask_write 0XF800615C 0x000FFFFF 0x0000007C
mask_write 0XF8006160 0x000FFFFF 0x00000075
mask_write 0XF8006168 0x001FFFFF 0x000000E5
mask_write 0XF800616C 0x001FFFFF 0x000000E0
mask_write 0XF8006170 0x001FFFFF 0x000000E1
mask_write 0XF8006174 0x001FFFFF 0x000000E8
mask_write 0XF800617C 0x000FFFFF 0x000000B7
mask_write 0XF8006180 0x000FFFFF 0x000000BC
mask_write 0XF8006184 0x000FFFFF 0x000000BC
mask_write 0XF8006188 0x000FFFFF 0x000000B5
mask_write 0XF8006190 0x6FFFFEFE 0x00040080
mask_write 0XF8006194 0x000FFFFF 0x0001FC82
mask_write 0XF8006204 0xFFFFFFFF 0x00000000
@@ -320,26 +320,26 @@ proc ps7_ddr_init_data_2_0 {} {
mask_write 0XF800611C 0x7FFFFFFF 0x40000001
mask_write 0XF8006120 0x7FFFFFFF 0x40000001
mask_write 0XF8006124 0x7FFFFFFF 0x40000001
mask_write 0XF800612C 0x000FFFFF 0x00033C03
mask_write 0XF8006130 0x000FFFFF 0x00034003
mask_write 0XF8006134 0x000FFFFF 0x0002F400
mask_write 0XF8006138 0x000FFFFF 0x00030400
mask_write 0XF800612C 0x000FFFFF 0x00024000
mask_write 0XF8006130 0x000FFFFF 0x00022C00
mask_write 0XF8006134 0x000FFFFF 0x00023000
mask_write 0XF8006138 0x000FFFFF 0x00024C00
mask_write 0XF8006140 0x000FFFFF 0x00000035
mask_write 0XF8006144 0x000FFFFF 0x00000035
mask_write 0XF8006148 0x000FFFFF 0x00000035
mask_write 0XF800614C 0x000FFFFF 0x00000035
mask_write 0XF8006154 0x000FFFFF 0x00000083
mask_write 0XF8006158 0x000FFFFF 0x00000083
mask_write 0XF800615C 0x000FFFFF 0x0000007F
mask_write 0XF8006160 0x000FFFFF 0x00000078
mask_write 0XF8006168 0x001FFFFF 0x00000124
mask_write 0XF800616C 0x001FFFFF 0x00000125
mask_write 0XF8006170 0x001FFFFF 0x00000112
mask_write 0XF8006174 0x001FFFFF 0x00000116
mask_write 0XF800617C 0x000FFFFF 0x000000C3
mask_write 0XF8006180 0x000FFFFF 0x000000C3
mask_write 0XF8006184 0x000FFFFF 0x000000BF
mask_write 0XF8006188 0x000FFFFF 0x000000B8
mask_write 0XF8006154 0x000FFFFF 0x00000077
mask_write 0XF8006158 0x000FFFFF 0x0000007C
mask_write 0XF800615C 0x000FFFFF 0x0000007C
mask_write 0XF8006160 0x000FFFFF 0x00000075
mask_write 0XF8006168 0x001FFFFF 0x000000E5
mask_write 0XF800616C 0x001FFFFF 0x000000E0
mask_write 0XF8006170 0x001FFFFF 0x000000E1
mask_write 0XF8006174 0x001FFFFF 0x000000E8
mask_write 0XF800617C 0x000FFFFF 0x000000B7
mask_write 0XF8006180 0x000FFFFF 0x000000BC
mask_write 0XF8006184 0x000FFFFF 0x000000BC
mask_write 0XF8006188 0x000FFFFF 0x000000B5
mask_write 0XF8006190 0xFFFFFFFF 0x10040080
mask_write 0XF8006194 0x000FFFFF 0x0001FC82
mask_write 0XF8006204 0xFFFFFFFF 0x00000000
@@ -554,26 +554,26 @@ proc ps7_ddr_init_data_1_0 {} {
mask_write 0XF800611C 0x7FFFFFFF 0x40000001
mask_write 0XF8006120 0x7FFFFFFF 0x40000001
mask_write 0XF8006124 0x7FFFFFFF 0x40000001
mask_write 0XF800612C 0x000FFFFF 0x00033C03
mask_write 0XF8006130 0x000FFFFF 0x00034003
mask_write 0XF8006134 0x000FFFFF 0x0002F400
mask_write 0XF8006138 0x000FFFFF 0x00030400
mask_write 0XF800612C 0x000FFFFF 0x00024000
mask_write 0XF8006130 0x000FFFFF 0x00022C00
mask_write 0XF8006134 0x000FFFFF 0x00023000
mask_write 0XF8006138 0x000FFFFF 0x00024C00
mask_write 0XF8006140 0x000FFFFF 0x00000035
mask_write 0XF8006144 0x000FFFFF 0x00000035
mask_write 0XF8006148 0x000FFFFF 0x00000035
mask_write 0XF800614C 0x000FFFFF 0x00000035
mask_write 0XF8006154 0x000FFFFF 0x00000083
mask_write 0XF8006158 0x000FFFFF 0x00000083
mask_write 0XF800615C 0x000FFFFF 0x0000007F
mask_write 0XF8006160 0x000FFFFF 0x00000078
mask_write 0XF8006168 0x001FFFFF 0x00000124
mask_write 0XF800616C 0x001FFFFF 0x00000125
mask_write 0XF8006170 0x001FFFFF 0x00000112
mask_write 0XF8006174 0x001FFFFF 0x00000116
mask_write 0XF800617C 0x000FFFFF 0x000000C3
mask_write 0XF8006180 0x000FFFFF 0x000000C3
mask_write 0XF8006184 0x000FFFFF 0x000000BF
mask_write 0XF8006188 0x000FFFFF 0x000000B8
mask_write 0XF8006154 0x000FFFFF 0x00000077
mask_write 0XF8006158 0x000FFFFF 0x0000007C
mask_write 0XF800615C 0x000FFFFF 0x0000007C
mask_write 0XF8006160 0x000FFFFF 0x00000075
mask_write 0XF8006168 0x001FFFFF 0x000000E5
mask_write 0XF800616C 0x001FFFFF 0x000000E0
mask_write 0XF8006170 0x001FFFFF 0x000000E1
mask_write 0XF8006174 0x001FFFFF 0x000000E8
mask_write 0XF800617C 0x000FFFFF 0x000000B7
mask_write 0XF8006180 0x000FFFFF 0x000000BC
mask_write 0XF8006184 0x000FFFFF 0x000000BC
mask_write 0XF8006188 0x000FFFFF 0x000000B5
mask_write 0XF8006190 0xFFFFFFFF 0x10040080
mask_write 0XF8006194 0x000FFFFF 0x0001FC82
mask_write 0XF8006204 0xFFFFFFFF 0x00000000
+2 -2
View File
@@ -1,5 +1,5 @@
if {[info exists env(XSCT_HW_SERVER_IP)]} {
set ip $env(XSCT_HW_SERVER_IP)
if {[info exists env(ip_address_hw_server)]} {
set ip $env(ip_address_hw_server)
} else {
set ip "localhost"
}
+6 -7
View File
@@ -22,6 +22,8 @@ def main():
parser.add_argument(
"-t",
"--tools",
# Required only if env var is not set
required=not bool(os.getenv("AMD_TOOLS")),
# Use env var if set
default=os.getenv("AMD_TOOLS"),
help="The path to the tool to use. Must point to a valid Vivado tools installation which"
@@ -41,13 +43,10 @@ def main():
help="No bitstream flashing for initialization with SDT.",
)
parser.add_argument("-a", "--app", dest="app", help="Path to the app to program")
default_ip = os.getenv("HW_SERVER_IP")
if not default_ip:
default_ip = DEFAULT_IP_ADDRESS_HW_SERVER
parser.add_argument(
"-i",
"--ip",
default=default_ip,
default=DEFAULT_IP_ADDRESS_HW_SERVER,
help="The IP address of the hardware server (default: localhost)",
)
parser.add_argument(
@@ -92,7 +91,7 @@ def main():
print(f"The app '{args.app}' does not exist")
sys.exit(1)
os.environ["XSCT_HW_SERVER_IP"] = args.ip
os.environ["IP_ADDRESS_HW_SERVER"] = args.ip
init_tcl = None
bitstream = None
if args.bit:
@@ -112,8 +111,8 @@ def main():
init_script = sdt_path / "ps7_init.tcl"
if not init_script.exists():
sys.exit("Error: ps7_init.tcl file not found in the SDT folder.")
if not args.init_tcl:
init_tcl = str(init_script)
init_tcl = str(init_script)
else:
if not args.init_tcl:
print("Error: No ps7_init.tcl file specified.")