4 Commits

Author SHA1 Message Date
e43cab0312 Merge pull request 'improve GIC modules' (#31) from improve-gic-modules into main
Some checks are pending
ci / Check build (push) Waiting to run
ci / Check formatting (push) Waiting to run
ci / Check Documentation Build (push) Waiting to run
ci / Clippy (push) Waiting to run
Reviewed-on: #31
2025-12-06 14:38:59 +01:00
b5f5ccb52c improve GIC modules
Some checks are pending
ci / Check build (push) Waiting to run
ci / Check formatting (push) Waiting to run
ci / Check Documentation Build (push) Waiting to run
ci / Clippy (push) Waiting to run
ci / Check build (pull_request) Waiting to run
ci / Check formatting (pull_request) Waiting to run
ci / Check Documentation Build (pull_request) Waiting to run
ci / Clippy (pull_request) Waiting to run
2025-12-06 14:37:35 +01:00
25c326c3f1 Merge pull request 'move to default aarch32-rt kmain method instead of boot_core' (#30) from zynq7000-rt-use-kmain into main
Some checks failed
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #30
2025-12-02 23:33:03 +01:00
116cb496d9 move to default aarch32-rt kmain method instead of boot_core
Some checks failed
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2025-12-02 23:30:17 +01:00
29 changed files with 296 additions and 215 deletions

2
tools/Cargo.lock generated
View File

@@ -725,7 +725,7 @@ dependencies = [
[[package]] [[package]]
name = "zynq7000-rt" name = "zynq7000-rt"
version = "0.1.2" version = "0.1.1"
dependencies = [ dependencies = [
"aarch32-cpu", "aarch32-cpu",
"arbitrary-int 2.0.0", "arbitrary-int 2.0.0",

View File

@@ -21,28 +21,23 @@ use zynq7000_hal::{
}; };
use zynq7000::Peripherals; use zynq7000::Peripherals;
use zynq7000_rt as _;
// Define the clock frequency as a constant // Define the clock frequency as a constant
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300); const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main();
}
/// Try to talk to a DHT22 sensor connected at MIO0. /// Try to talk to a DHT22 sensor connected at MIO0.
const DHT22_AT_MIO0: bool = true; const DHT22_AT_MIO0: bool = true;
/// Open drain pin testing. MIO9 needs to be tied to MIO14. /// Open drain pin testing. MIO9 needs to be tied to MIO14.
const OPEN_DRAIN_PINS_MIO9_TO_MIO14: bool = false; const OPEN_DRAIN_PINS_MIO9_TO_MIO14: bool = false;
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
fn entry_point() -> ! {
main();
}
#[embassy_executor::main] #[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(_spawner: Spawner) -> ! { async fn main(_spawner: Spawner) -> ! {
let mut dp = Peripherals::take().unwrap(); let mut dp = Peripherals::take().unwrap();
l2_cache::init_with_defaults(&mut dp.l2c); l2_cache::init_with_defaults(&mut dp.l2c);

View File

@@ -15,17 +15,13 @@ use zynq7000_rt as _;
// Define the clock frequency as a constant // Define the clock frequency as a constant
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300); const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
/// Entry point (not called like a normal main function) /// Entry point which calls the embassy main method.
#[unsafe(no_mangle)] #[zynq7000_rt::entry]
pub extern "C" fn boot_core(cpu_id: u32) -> ! { fn entry_point() -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main(); main();
} }
#[embassy_executor::main] #[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(_spawner: Spawner) -> ! { async fn main(_spawner: Spawner) -> ! {
let periphs = zynq7000_hal::init(zynq7000_hal::Config { let periphs = zynq7000_hal::init(zynq7000_hal::Config {
init_l2_cache: true, init_l2_cache: true,

View File

@@ -26,16 +26,12 @@ use zynq7000_rt as _;
// Define the clock frequency as a constant // Define the clock frequency as a constant
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300); const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
/// Entry point (not called like a normal main function) /// Entry point which calls the embassy main method.
#[unsafe(no_mangle)] #[zynq7000_rt::entry]
pub extern "C" fn boot_core(cpu_id: u32) -> ! { fn entry_point() -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main(); main();
} }
#[unsafe(export_name = "main")]
#[embassy_executor::main] #[embassy_executor::main]
async fn main(spawner: Spawner) -> ! { async fn main(spawner: Spawner) -> ! {
let mut dp = Peripherals::take().unwrap(); let mut dp = Peripherals::take().unwrap();

View File

@@ -32,17 +32,13 @@ use zynq7000_rt as _;
// Define the clock frequency as a constant // Define the clock frequency as a constant
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300); const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
/// Entry point (not called like a normal main function) /// Entry point which calls the embassy main method.
#[unsafe(no_mangle)] #[zynq7000_rt::entry]
pub extern "C" fn boot_core(cpu_id: u32) -> ! { fn entry_point() -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main(); main();
} }
#[embassy_executor::main] #[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(_spawner: Spawner) -> ! { async fn main(_spawner: Spawner) -> ! {
let mut dp = Peripherals::take().unwrap(); let mut dp = Peripherals::take().unwrap();
l2_cache::init_with_defaults(&mut dp.l2c); l2_cache::init_with_defaults(&mut dp.l2c);

View File

@@ -9,22 +9,16 @@ use embedded_hal::digital::StatefulOutputPin;
use log::error; use log::error;
use zynq7000_hal::{InteruptConfig, clocks, gic, gpio, gtc, time::Hertz}; use zynq7000_hal::{InteruptConfig, clocks, gic, gpio, gtc, time::Hertz};
use zynq7000_rt as _;
// Define the clock frequency as a constant // Define the clock frequency as a constant
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300); const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
/// Entry point (not called like a normal main function) /// Entry point which calls the embassy main method.
#[unsafe(no_mangle)] #[zynq7000_rt::entry]
pub extern "C" fn boot_core(cpu_id: u32) -> ! { fn entry_point() -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main(); main();
} }
#[embassy_executor::main] #[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(_spawner: Spawner) -> ! { async fn main(_spawner: Spawner) -> ! {
let periphs = zynq7000_hal::init(zynq7000_hal::Config { let periphs = zynq7000_hal::init(zynq7000_hal::Config {
init_l2_cache: true, init_l2_cache: true,

View File

@@ -13,7 +13,6 @@ use zynq7000_hal::{
priv_tim::CpuPrivateTimer, priv_tim::CpuPrivateTimer,
time::Hertz, time::Hertz,
}; };
use zynq7000_rt as _;
pub const LIB: Lib = Lib::Hal; pub const LIB: Lib = Lib::Hal;
@@ -31,17 +30,8 @@ pub enum Lib {
Hal, Hal,
} }
/// Entry point (not called like a normal main function) #[zynq7000_rt::entry]
#[unsafe(no_mangle)] fn main() -> ! {
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main();
}
#[unsafe(export_name = "main")]
pub fn main() -> ! {
l2_cache::init_with_defaults(&mut unsafe { zynq7000::l2_cache::Registers::new_mmio_fixed() }); l2_cache::init_with_defaults(&mut unsafe { zynq7000::l2_cache::Registers::new_mmio_fixed() });
match LIB { match LIB {
Lib::Pac => { Lib::Pac => {

View File

@@ -18,24 +18,13 @@ use zynq7000_hal::{
uart::{ClockConfig, Config, Uart}, uart::{ClockConfig, Config, Uart},
}; };
use zynq7000_rt as _;
// Define the clock frequency as a constant // Define the clock frequency as a constant
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_333); const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_333);
static MS_TICKS: AtomicU64 = AtomicU64::new(0); static MS_TICKS: AtomicU64 = AtomicU64::new(0);
/// Entry point (not called like a normal main function) #[zynq7000_rt::entry]
#[unsafe(no_mangle)] fn main() -> ! {
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main();
}
#[unsafe(export_name = "main")]
pub fn main() -> ! {
let mut dp = zynq7000::Peripherals::take().unwrap(); let mut dp = zynq7000::Peripherals::take().unwrap();
l2_cache::init_with_defaults(&mut dp.l2c); l2_cache::init_with_defaults(&mut dp.l2c);

View File

@@ -19,24 +19,13 @@ use zynq7000_hal::{
uart::{ClockConfig, Config, Uart}, uart::{ClockConfig, Config, Uart},
}; };
use zynq7000_rt as _;
// Define the clock frequency as a constant // Define the clock frequency as a constant
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300); const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
static MS_TICKS: AtomicU64 = AtomicU64::new(0); static MS_TICKS: AtomicU64 = AtomicU64::new(0);
/// Entry point (not called like a normal main function) #[zynq7000_rt::entry]
#[unsafe(no_mangle)] fn main() -> ! {
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main();
}
#[unsafe(export_name = "main")]
pub fn main() -> ! {
let mut dp = zynq7000::Peripherals::take().unwrap(); let mut dp = zynq7000::Peripherals::take().unwrap();
l2_cache::init_with_defaults(&mut dp.l2c); l2_cache::init_with_defaults(&mut dp.l2c);

View File

@@ -4,19 +4,9 @@
use aarch32_cpu::asm::nop; use aarch32_cpu::asm::nop;
use core::panic::PanicInfo; use core::panic::PanicInfo;
use zynq7000_rt as _;
/// Entry point (not called like a normal main function) #[zynq7000_rt::entry]
#[unsafe(no_mangle)] fn main() -> ! {
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main();
}
#[unsafe(export_name = "main")]
pub fn main() -> ! {
loop { loop {
nop(); nop();
} }

View File

@@ -104,12 +104,9 @@ pub enum IpMode {
StackReady, StackReady,
} }
/// Entry point (not called like a normal main function) /// Entry point which calls the embassy main method.
#[unsafe(no_mangle)] #[zynq7000_rt::entry]
pub extern "C" fn boot_core(cpu_id: u32) -> ! { fn entry_point() -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main(); main();
} }
@@ -204,7 +201,6 @@ async fn tcp_task(mut tcp: TcpSocket<'static>) -> ! {
} }
#[embassy_executor::main] #[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(spawner: Spawner) -> ! { async fn main(spawner: Spawner) -> ! {
let mut dp = Peripherals::take().unwrap(); let mut dp = Peripherals::take().unwrap();
l2_cache::init_with_defaults(&mut dp.l2c); l2_cache::init_with_defaults(&mut dp.l2c);

View File

@@ -36,17 +36,13 @@ use zynq7000_rt as _;
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300); const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
const I2C_ADDR_SEL: I2cAddr = I2cAddr::Sa0Low; const I2C_ADDR_SEL: I2cAddr = I2cAddr::Sa0Low;
/// Entry point (not called like a normal main function) /// Entry point which calls the embassy main method.
#[unsafe(no_mangle)] #[zynq7000_rt::entry]
pub extern "C" fn boot_core(cpu_id: u32) -> ! { fn entry_point() -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main(); main();
} }
#[embassy_executor::main] #[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(_spawner: Spawner) -> ! { async fn main(_spawner: Spawner) -> ! {
let mut dp = Peripherals::take().unwrap(); let mut dp = Peripherals::take().unwrap();
l2_cache::init_with_defaults(&mut dp.l2c); l2_cache::init_with_defaults(&mut dp.l2c);

View File

@@ -39,17 +39,13 @@ const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
const DEBUG_SPI_CLK_CONFIG: bool = false; const DEBUG_SPI_CLK_CONFIG: bool = false;
const BLOCKING: bool = false; const BLOCKING: bool = false;
/// Entry point (not called like a normal main function) /// Entry point which calls the embassy main method.
#[unsafe(no_mangle)] #[zynq7000_rt::entry]
pub extern "C" fn boot_core(cpu_id: u32) -> ! { fn entry_point() -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main(); main();
} }
#[embassy_executor::main] #[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(spawner: Spawner) -> ! { async fn main(spawner: Spawner) -> ! {
let mut dp = Peripherals::take().unwrap(); let mut dp = Peripherals::take().unwrap();
l2_cache::init_with_defaults(&mut dp.l2c); l2_cache::init_with_defaults(&mut dp.l2c);

View File

@@ -21,19 +21,15 @@ const QSPI_DEV_COMBINATION: qspi::QspiDeviceCombination = qspi::QspiDeviceCombin
two_devices: false, two_devices: false,
}; };
/// Entry point (not called like a normal main function) /// Entry point which calls the embassy main method.
#[unsafe(no_mangle)] #[zynq7000_rt::entry]
pub extern "C" fn boot_core(cpu_id: u32) -> ! { fn entry_point() -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main(); main();
} }
const ERASE_PROGRAM_READ_TEST: bool = false; const ERASE_PROGRAM_READ_TEST: bool = false;
#[embassy_executor::main] #[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(_spawner: Spawner) -> ! { async fn main(_spawner: Spawner) -> ! {
let periphs = zynq7000_hal::init(zynq7000_hal::Config { let periphs = zynq7000_hal::init(zynq7000_hal::Config {
init_l2_cache: true, init_l2_cache: true,

View File

@@ -31,12 +31,9 @@ const INIT_STRING: &str = "-- Zynq 7000 Zedboard blocking UART example --\n\r";
const AXI_UARTLITE_BASE_ADDR: u32 = 0x42C0_0000; const AXI_UARTLITE_BASE_ADDR: u32 = 0x42C0_0000;
const AXI_UAR16550_BASE_ADDR: u32 = 0x43C0_0000; const AXI_UAR16550_BASE_ADDR: u32 = 0x43C0_0000;
/// Entry point (not called like a normal main function) /// Entry point which calls the embassy main method.
#[unsafe(no_mangle)] #[zynq7000_rt::entry]
pub extern "C" fn boot_core(cpu_id: u32) -> ! { fn entry_point() -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main(); main();
} }
@@ -99,7 +96,6 @@ impl UartMultiplexer {
} }
} }
#[embassy_executor::main] #[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(_spawner: Spawner) -> ! { async fn main(_spawner: Spawner) -> ! {
let mut dp = Peripherals::take().unwrap(); let mut dp = Peripherals::take().unwrap();
l2_cache::init_with_defaults(&mut dp.l2c); l2_cache::init_with_defaults(&mut dp.l2c);

View File

@@ -95,12 +95,9 @@ static UARTLITE_PROD: Mutex<RefCell<Option<heapless::spsc::Producer<'static, u8,
static UART16550_PROD: Mutex<RefCell<Option<heapless::spsc::Producer<'static, u8, RB_SIZE>>>> = static UART16550_PROD: Mutex<RefCell<Option<heapless::spsc::Producer<'static, u8, RB_SIZE>>>> =
Mutex::new(RefCell::new(None)); Mutex::new(RefCell::new(None));
/// Entry point (not called like a normal main function) /// Entry point which calls the embassy main method.
#[unsafe(no_mangle)] #[zynq7000_rt::entry]
pub extern "C" fn boot_core(cpu_id: u32) -> ! { fn entry_point() -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main(); main();
} }
@@ -164,7 +161,6 @@ impl UartMultiplexer {
} }
#[embassy_executor::main] #[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(spawner: Spawner) -> ! { async fn main(spawner: Spawner) -> ! {
let mut dp = Peripherals::take().unwrap(); let mut dp = Peripherals::take().unwrap();
l2_cache::init_with_defaults(&mut dp.l2c); l2_cache::init_with_defaults(&mut dp.l2c);

View File

@@ -15,17 +15,13 @@ use zynq7000_rt as _;
const INIT_STRING: &str = "-- Zynq 7000 Zedboard GPIO blinky example --\n\r"; const INIT_STRING: &str = "-- Zynq 7000 Zedboard GPIO blinky example --\n\r";
/// Entry point (not called like a normal main function) /// Entry point which calls the embassy main method.
#[unsafe(no_mangle)] #[zynq7000_rt::entry]
pub extern "C" fn boot_core(cpu_id: u32) -> ! { fn entry_point() -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main(); main();
} }
#[embassy_executor::main] #[embassy_executor::main]
#[unsafe(export_name = "main")]
async fn main(_spawner: Spawner) -> ! { async fn main(_spawner: Spawner) -> ! {
let periphs = zynq7000_hal::init(zynq7000_hal::Config { let periphs = zynq7000_hal::init(zynq7000_hal::Config {
init_l2_cache: true, init_l2_cache: true,

View File

@@ -29,7 +29,6 @@ use zynq7000_hal::{
time::Hertz, time::Hertz,
uart::{ClockConfig, Config, Uart}, uart::{ClockConfig, Config, Uart},
}; };
use zynq7000_rt as _;
// PS clock input frequency. // PS clock input frequency.
const PS_CLK: Hertz = Hertz::from_raw(33_333_333); const PS_CLK: Hertz = Hertz::from_raw(33_333_333);
@@ -60,17 +59,8 @@ pub const ELF_BASE_ADDR: usize = 0x100000;
/// 8 MB reserved for application ELF. /// 8 MB reserved for application ELF.
pub const BOOT_BIN_STAGING_OFFSET: usize = 8 * 1024 * 1024; pub const BOOT_BIN_STAGING_OFFSET: usize = 8 * 1024 * 1024;
/// Entry point (not called like a normal main function) #[zynq7000_rt::entry]
#[unsafe(no_mangle)] fn main() -> ! {
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main();
}
#[unsafe(export_name = "main")]
pub fn main() -> ! {
let boot_mode = BootMode::new_from_regs(); let boot_mode = BootMode::new_from_regs();
// The unwraps are okay here, the provided clock frequencies are standard values also used // The unwraps are okay here, the provided clock frequencies are standard values also used
// by other Xilinx tools. // by other Xilinx tools.

View File

@@ -36,19 +36,10 @@ const QSPI_DEV_COMBINATION: qspi::QspiDeviceCombination = qspi::QspiDeviceCombin
two_devices: false, two_devices: false,
}; };
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main();
}
const INIT_STRING: &str = "-- Zynq 7000 Zedboard QSPI flasher --\n\r"; const INIT_STRING: &str = "-- Zynq 7000 Zedboard QSPI flasher --\n\r";
#[unsafe(export_name = "main")] #[zynq7000_rt::entry]
pub fn main() -> ! { fn main() -> ! {
let periphs = zynq7000_hal::init(zynq7000_hal::Config { let periphs = zynq7000_hal::init(zynq7000_hal::Config {
init_l2_cache: true, init_l2_cache: true,
level_shifter_config: Some(LevelShifterConfig::EnableAll), level_shifter_config: Some(LevelShifterConfig::EnableAll),

View File

@@ -12,6 +12,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Increased UART type safety by providing dedicated MIO constructors for UART 0 and UART 1 - Increased UART type safety by providing dedicated MIO constructors for UART 0 and UART 1
respectively. 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.
# [v0.1.1] 2025-10-10 # [v0.1.1] 2025-10-10

View File

@@ -20,6 +20,7 @@ bitbybit = "1.4"
arbitrary-int = "2" arbitrary-int = "2"
thiserror = { version = "2", default-features = false } thiserror = { version = "2", default-features = false }
num_enum = { version = "0.7", default-features = false } num_enum = { version = "0.7", default-features = false }
bitflags = "2"
ringbuf = { version = "0.4.8", default-features = false } ringbuf = { version = "0.4.8", default-features = false }
embedded-hal-nb = "1" embedded-hal-nb = "1"
embedded-io = "0.7" embedded-io = "0.7"
@@ -40,6 +41,7 @@ smoltcp = { version = "0.12", default-features = false, features = ["proto-ipv4"
vcell = "0.1" vcell = "0.1"
raw-slicee = "0.1" raw-slicee = "0.1"
embedded-io-async = "0.7" embedded-io-async = "0.7"
serde = { version = "1", optional = true, features = ["derive"] }
[features] [features]
std = ["thiserror/std", "alloc"] std = ["thiserror/std", "alloc"]

View File

@@ -6,17 +6,22 @@
//! # Examples //! # Examples
//! //!
//! - [GTC ticks](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/examples/simple/src/bin/gtc-ticks.rs) //! - [GTC ticks](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/examples/simple/src/bin/gtc-ticks.rs)
#![deny(missing_docs)]
use arbitrary_int::prelude::*; use arbitrary_int::prelude::*;
use aarch32_cpu::interrupt; use aarch32_cpu::interrupt;
use zynq7000::gic::{ use zynq7000::gic::{
CpuInterfaceRegisters, DistributorControlRegister, DistributorRegisters, InterfaceControl, CpuInterfaceRegisters, DistributorControlRegister, DistributorRegisters, InterfaceControl,
InterruptSignalRegister, MmioCpuInterfaceRegisters, MmioDistributorRegisters, PriorityRegister, InterruptProcessorTargetRegister, InterruptSignalRegister, MmioCpuInterfaceRegisters,
MmioDistributorRegisters, PriorityRegister,
}; };
const SPURIOUS_INTERRUPT_ID: u32 = 1023; /// Spurious interrupt ID.
pub const SPURIOUS_INTERRUPT_ID: u32 = 1023;
/// Highest interrupt priority (smallest number).
pub const HIGHEST_PRIORITY: u8 = 0; pub const HIGHEST_PRIORITY: u8 = 0;
/// Lowest interrupt priority (largest number).
pub const LOWEST_PRIORITY: u8 = 31; pub const LOWEST_PRIORITY: u8 = 31;
/// These fixed values must be programmed according to the Zynq7000 TRM p.236. /// These fixed values must be programmed according to the Zynq7000 TRM p.236.
@@ -36,119 +41,218 @@ pub const ICFR_4_FIXED_VALUE: u32 = 0b01110101010101010101010101010101;
pub const ICFR_5_FIXED_VALUE: u32 = 0b00000011010101010101010101010101; pub const ICFR_5_FIXED_VALUE: u32 = 0b00000011010101010101010101010101;
/// Helper value to target all interrupts which can be targetted to CPU 0 /// Helper value to target all interrupts which can be targetted to CPU 0
pub const TARGETS_ALL_CPU_0_IPTR_VAL: u32 = 0x01010101; pub const TARGETS_ALL_CPU_0_IPTR_VAL: InterruptProcessorTargetRegister =
InterruptProcessorTargetRegister::new_with_raw_value(0x01010101);
/// Helper value to target all interrupts which can be targetted to CPU 1 /// Helper value to target all interrupts which can be targetted to CPU 1
pub const TARGETS_ALL_CPU_1_IPTR_VAL: u32 = 0x02020202; pub const TARGETS_ALL_CPU_1_IPTR_VAL: InterruptProcessorTargetRegister =
InterruptProcessorTargetRegister::new_with_raw_value(0x02020202);
/// Mask for activating all softare generated interrupts.
pub const ACTIVATE_ALL_SGIS_MASK_ISER: u32 = 0x0000_FFFF; pub const ACTIVATE_ALL_SGIS_MASK_ISER: u32 = 0x0000_FFFF;
/// Mask for activating all private peripheral interrupts.
pub const ACTIVATE_ALL_PPIS_MASK_ISER: u32 = 0xF800_0000; pub const ACTIVATE_ALL_PPIS_MASK_ISER: u32 = 0xF800_0000;
/// Shared peripheral interrupt sensitivity.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SpiSensitivity { pub enum SpiSensitivity {
/// Level triggered interrupt.
Level = 0b01, Level = 0b01,
/// Edge triggered interrupt.
Edge = 0b11, Edge = 0b11,
} }
pub enum TargetCpu { /// CPU enumeration.
None = 0b00, #[derive(Debug, Clone, Copy, PartialEq, Eq)]
Cpu0 = 0b01, #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
Cpu1 = 0b10, pub enum Cpu {
Both = 0b11, /// CPU 0.
Cpu0,
/// CPU 1.
Cpu1,
}
bitflags::bitflags! {
/// Target CPU bitflags.
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TargetCpus: u8 {
/// No CPU.
const NONE = 0b00;
/// CPU 0.
const CPU_0 = 0b01;
/// CPU 1.
const CPU_1 = 0b10;
/// Both CPUs.
const BOTH_CPUS = 0b11;
}
} }
/// Private Peripheral Interrupt (PPI) which are private to the CPU. /// Private Peripheral Interrupt (PPI) which are private to the CPU.
#[derive(Debug, Eq, PartialEq, Clone, Copy, num_enum::TryFromPrimitive)] #[derive(Debug, Eq, PartialEq, Clone, Copy, num_enum::TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u8)] #[repr(u8)]
pub enum PpiInterrupt { pub enum PpiInterrupt {
/// Global timer.
GlobalTimer = 27, GlobalTimer = 27,
// Interrupt signal from the PL. CPU0: `IRQF2P[18]` and CPU1: `IRQF2P[19]` /// Interrupt signal from the PL. CPU0: `IRQF2P[18]` and CPU1: `IRQF2P[19]`
NFiq = 28, NFiq = 28,
/// CPU private timer.
CpuPrivateTimer = 29, CpuPrivateTimer = 29,
/// AWDT0 and AWDT1 for each CPU. /// AWDT0 and AWDT1 for each CPU.
Awdt = 30, Awdt = 30,
// Interrupt signal from the PL. CPU0: `IRQF2P[16]` and CPU1: `IRQF2P[17]` /// Interrupt signal from the PL. CPU0: `IRQF2P[16]` and CPU1: `IRQF2P[17]`
NIrq = 31, NIrq = 31,
} }
/// Shared Peripheral Interrupt IDs. /// Shared Peripheral Interrupt IDs.
#[derive(Debug, Eq, PartialEq, Clone, Copy, num_enum::TryFromPrimitive)] #[derive(Debug, Eq, PartialEq, Clone, Copy, num_enum::TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u8)] #[repr(u8)]
pub enum SpiInterrupt { pub enum SpiInterrupt {
/// CPU 0.
Cpu0 = 32, Cpu0 = 32,
/// CPU 1.
Cpu1 = 33, Cpu1 = 33,
/// L2 cache.
L2Cache = 34, L2Cache = 34,
/// On-chip memory.
Ocm = 35, Ocm = 35,
/// Reserved.
_Reserved0 = 36, _Reserved0 = 36,
/// Performance monitor unit 0.
Pmu0 = 37, Pmu0 = 37,
/// Performance monitor unit 1.
Pmu1 = 38, Pmu1 = 38,
/// XADC.
Xadc = 39, Xadc = 39,
/// Device configuration.
DevC = 40, DevC = 40,
/// System watchdog timer.
Swdt = 41, Swdt = 41,
/// Triple timer counter 00.
Ttc00 = 42, Ttc00 = 42,
/// Triple timer counter 01.
Ttc01 = 43, Ttc01 = 43,
/// Triple timer counter 02.
Ttc02 = 44, Ttc02 = 44,
/// DMAC abort.
DmacAbort = 45, DmacAbort = 45,
/// DMAC 0.
Dmac0 = 46, Dmac0 = 46,
/// DMAC 1.
Dmac1 = 47, Dmac1 = 47,
/// DMAC 2.
Dmac2 = 48, Dmac2 = 48,
/// DMAC 3.
Dmac3 = 49, Dmac3 = 49,
/// Shared memory controller.
Smc = 50, Smc = 50,
/// Quad SPI.
Qspi = 51, Qspi = 51,
/// GPIO.
Gpio = 52, Gpio = 52,
/// USB 0.
Usb0 = 53, Usb0 = 53,
/// Ethernet 0.
Eth0 = 54, Eth0 = 54,
/// Ethernet 0 wakeup.
Eth0Wakeup = 55, Eth0Wakeup = 55,
/// SDIO 0.
Sdio0 = 56, Sdio0 = 56,
/// I2C 0.
I2c0 = 57, I2c0 = 57,
/// SPI 0.
Spi0 = 58, Spi0 = 58,
/// UART 0.
Uart0 = 59, Uart0 = 59,
/// CAN 0.
Can0 = 60, Can0 = 60,
/// Programmable Logic 0.
Pl0 = 61, Pl0 = 61,
/// Programmable Logic 1.
Pl1 = 62, Pl1 = 62,
/// Programmable Logic 2.
Pl2 = 63, Pl2 = 63,
/// Programmable Logic 3.
Pl3 = 64, Pl3 = 64,
/// Programmable Logic 4.
Pl4 = 65, Pl4 = 65,
/// Programmable Logic 5.
Pl5 = 66, Pl5 = 66,
/// Programmable Logic 6.
Pl6 = 67, Pl6 = 67,
/// Programmable Logic 7.
Pl7 = 68, Pl7 = 68,
/// Triple timer counter 10.
Ttc10 = 69, Ttc10 = 69,
/// Triple timer counter 11.
Ttc11 = 70, Ttc11 = 70,
/// Triple timer counter 12.
Ttc12 = 71, Ttc12 = 71,
/// DMAC 4.
Dmac4 = 72, Dmac4 = 72,
/// DMAC 5.
Dmac5 = 73, Dmac5 = 73,
/// DMAC 6.
Dmac6 = 74, Dmac6 = 74,
/// DMAC 7.
Dmac7 = 75, Dmac7 = 75,
/// USB 1.
Usb1 = 76, Usb1 = 76,
/// Ethernet 1.
Eth1 = 77, Eth1 = 77,
/// Ethernet 1 wakeup.
Eth1Wakeup = 78, Eth1Wakeup = 78,
/// SDIO 1.
Sdio1 = 79, Sdio1 = 79,
/// I2C 1.
I2c1 = 80, I2c1 = 80,
/// SPI 1.
Spi1 = 81, Spi1 = 81,
/// UART 1.
Uart1 = 82, Uart1 = 82,
/// CAN 1.
Can1 = 83, Can1 = 83,
/// Programmable Logic 8.
Pl8 = 84, Pl8 = 84,
/// Programmable Logic 9.
Pl9 = 85, Pl9 = 85,
/// Programmable Logic 10.
Pl10 = 86, Pl10 = 86,
/// Programmable Logic 11.
Pl11 = 87, Pl11 = 87,
/// Programmable Logic 12.
Pl12 = 88, Pl12 = 88,
/// Programmable Logic 13.
Pl13 = 89, Pl13 = 89,
/// Programmable Logic 14.
Pl14 = 90, Pl14 = 90,
/// Programmable Logic 15.
Pl15 = 91, Pl15 = 91,
/// Snoop control unit parity.
ScuParity = 92, ScuParity = 92,
} }
/// Interrupt ID wrapper. /// Interrupt ID wrapper.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Interrupt { pub enum Interrupt {
/// Software-generated interrupt (SGI).
Sgi(usize), Sgi(usize),
/// Private peripheral interrupt (PPI).
Ppi(PpiInterrupt), Ppi(PpiInterrupt),
/// Shared peripheral interrupt (SPI).
Spi(SpiInterrupt), Spi(SpiInterrupt),
/// Detects an invalid interrupt ID. /// Detects an invalid interrupt ID.
Invalid(usize), Invalid(usize),
/// Spurious interrupt (ID# 1023). /// Spurious interrupt (ID# 1023, [SPURIOUS_INTERRUPT_ID]).
Spurious, Spurious,
} }
/// Interrupt information structure.
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct InterruptInfo { pub struct InterruptInfo {
raw_reg: InterruptSignalRegister, raw_reg: InterruptSignalRegister,
interrupt: Interrupt, interrupt: Interrupt,
@@ -156,34 +260,46 @@ pub struct InterruptInfo {
} }
impl InterruptInfo { impl InterruptInfo {
pub fn raw_reg(&self) -> InterruptSignalRegister { /// Raw interrupt signal register value.
#[inline]
pub const fn raw_reg(&self) -> InterruptSignalRegister {
self.raw_reg self.raw_reg
} }
pub fn cpu_id(&self) -> u8 { /// CPU ID.
#[inline]
pub const fn cpu_id(&self) -> u8 {
self.cpu_id self.cpu_id
} }
pub fn interrupt(&self) -> Interrupt { /// Interrupt ID.
#[inline]
pub const fn interrupt(&self) -> Interrupt {
self.interrupt self.interrupt
} }
} }
/// Invalid priority value error.
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[error("Invalid priority value {0}, range is [0, 31]")] #[error("Invalid priority value {0}, range is [0, 31]")]
pub struct InvalidPriorityValue(pub u8); pub struct InvalidPriorityValue(pub u8);
/// Invalid programmable logic interrupt ID.
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[error("Invalid PL interrupt ID {0}")] #[error("Invalid PL interrupt ID {0}")]
pub struct InvalidPlInterruptId(pub usize); pub struct InvalidPlInterruptId(pub usize);
/// Invalid Shared Peripheral Interrupt (SPI) ID. /// Invalid Shared Peripheral Interrupt (SPI) ID.
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[error("Invalid SPI interrupt ID {0}")] #[error("Invalid SPI interrupt ID {0}")]
pub struct InvalidSpiInterruptId(pub usize); pub struct InvalidSpiInterruptId(pub usize);
/// Invalid Software Generated Interrupt (SGI) ID. /// Invalid Software Generated Interrupt (SGI) ID.
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[error("Invalid SGI interrupt ID {0}")] #[error("Invalid SGI interrupt ID {0}")]
pub struct InvalidSgiInterruptId(pub usize); pub struct InvalidSgiInterruptId(pub usize);
@@ -202,7 +318,8 @@ pub struct InvalidSgiInterruptId(pub usize);
/// the [SpiSensitivity] enum. You can use the following (helper) API to configure the /// the [SpiSensitivity] enum. You can use the following (helper) API to configure the
/// interrupts: /// interrupts:
/// ///
/// - [Self::set_spi_interrupt_cpu_target] /// - [Self::set_spi_interrupt_target_for_cpu]
/// - [Self::set_spi_interrupt_cpu_target_flags]
/// - [Self::set_all_spi_interrupt_targets_cpu0] /// - [Self::set_all_spi_interrupt_targets_cpu0]
/// - [Self::set_pl_interrupt_sensitivity] /// - [Self::set_pl_interrupt_sensitivity]
/// ///
@@ -219,12 +336,14 @@ pub struct InvalidSgiInterruptId(pub usize);
/// You might also chose to enable these interrupts at run-time after the GIC was started. /// You might also chose to enable these interrupts at run-time after the GIC was started.
/// 4. Start the GIC by calling [Self::update_ctrl_regs] with the required settings or /// 4. Start the GIC by calling [Self::update_ctrl_regs] with the required settings or
/// with [Self::enable] which assumes a certain configuration. /// with [Self::enable] which assumes a certain configuration.
/// 5. Enable interrupts for the Cortex-AR core by calling [Self::enable_interrupts]. /// 5. Enable interrupts for the Cortex-A core by calling [Self::enable_interrupts].
/// ///
/// For the handling of the interrupts, you can use the [GicInterruptHelper] which assumes a /// For the handling of the interrupts, you can use the [GicInterruptHelper] which assumes a
/// properly configured GIC. /// properly configured GIC.
pub struct GicConfigurator { pub struct GicConfigurator {
/// GIC CPU interface registers.
pub gicc: MmioCpuInterfaceRegisters<'static>, pub gicc: MmioCpuInterfaceRegisters<'static>,
/// GIC Distributor interface registers.
pub gicd: MmioDistributorRegisters<'static>, pub gicd: MmioDistributorRegisters<'static>,
} }
@@ -325,45 +444,90 @@ impl GicConfigurator {
Ok(()) Ok(())
} }
/// Set the CPU target for a SPI interrupt. /// Set the CPU target(s) for a SPI interrupt.
/// ///
/// See [Self::set_all_spi_interrupt_targets_cpu0] for a utility method to handle all /// See [Self::set_all_spi_interrupt_targets_cpu0] for a utility method to handle all
/// interrupts with one core. /// interrupts with one core.
#[inline] #[inline]
pub fn set_spi_interrupt_cpu_target(&mut self, spi_int: SpiInterrupt, target: TargetCpu) { pub fn set_spi_interrupt_cpu_target_flags(
&mut self,
spi_int: SpiInterrupt,
target: TargetCpus,
) {
let spi_int_raw = spi_int as u32; let spi_int_raw = spi_int as u32;
let spi_offset_to_0 = spi_int_raw as usize - 32; let spi_offset_to_0 = spi_int_raw as usize - 32;
// Unwrap okay, calculated index is always valid. // Unwrap okay, calculated index is always valid.
self.gicd self.gicd
.write_iptr_spi( .modify_iptr_spi(spi_offset_to_0 / 4, |mut v| {
spi_offset_to_0 / 4, // Every register contains 4 target flags, the modulo extracts the index because
(target as u32) << ((spi_offset_to_0 % 4) * 8), // counting starts at 32 (32 -> index 0, 33 -> index 1 etc.).
) v.set_targets(spi_offset_to_0 % 4, u2::new(target.bits()));
v
})
.unwrap();
}
/// Enable SPI interrupt target for a specific CPU without clearing the other CPU bit if it is
/// set.
#[inline]
pub fn set_spi_interrupt_target_for_cpu(&mut self, spi_int: SpiInterrupt, cpu: Cpu) {
let bitflag = match cpu {
Cpu::Cpu0 => TargetCpus::CPU_0,
Cpu::Cpu1 => TargetCpus::CPU_1,
};
let spi_int_raw = spi_int as u32;
let spi_offset_to_0 = spi_int_raw as usize - 32;
// Unwrap okay, calculated index is always valid.
self.gicd
.modify_iptr_spi(spi_offset_to_0 / 4, |mut v| {
v.set_targets(
spi_offset_to_0 % 4,
// Extract the target bits, bitwise OR them with [TargetCpus::CPU_0], and set
// them back.
u2::new(
(TargetCpus::from_bits(v.targets(spi_offset_to_0 % 4).as_u8()).unwrap()
| bitflag)
.bits(),
),
);
v
})
.unwrap(); .unwrap();
} }
/// Utility function to set all SGI interrupt targets to CPU0. /// Utility function to set all SGI interrupt targets to CPU0.
/// ///
/// This does not clear interrupt target bits for CPU1, it only activates the interrupts for
/// CPU 0 as well.
/// This is useful if only CPU0 is active in a system, or if CPU0 handles most interrupts in /// This is useful if only CPU0 is active in a system, or if CPU0 handles most interrupts in
/// the system. /// the system.
#[inline] #[inline]
pub fn set_all_spi_interrupt_targets_cpu0(&mut self) { pub fn set_all_spi_interrupt_targets_cpu0(&mut self) {
for i in 0..0x10 { for i in 0..0x10 {
self.gicd self.gicd
.write_iptr_spi(i, TARGETS_ALL_CPU_0_IPTR_VAL) .modify_iptr_spi(i, |v| {
InterruptProcessorTargetRegister::new_with_raw_value(
v.raw_value() | TARGETS_ALL_CPU_0_IPTR_VAL.raw_value(),
)
})
.unwrap(); .unwrap();
} }
} }
/// Enable a specific SGI interrupt.
#[inline] #[inline]
pub fn enable_sgi_interrupt(&mut self, int_id: usize) -> Result<(), InvalidSpiInterruptId> { pub fn enable_sgi_interrupt(&mut self, int_id: usize) -> Result<(), InvalidSpiInterruptId> {
if int_id >= 16 { if int_id >= 16 {
return Err(InvalidSpiInterruptId(int_id)); return Err(InvalidSpiInterruptId(int_id));
} }
unsafe { self.gicd.write_iser_unchecked(0, 1 << int_id) }; unsafe {
self.gicd
.modify_iser_unchecked(0, |val| val | (1 << int_id))
};
Ok(()) Ok(())
} }
/// Enable all SGI interrupts.
#[inline] #[inline]
pub fn enable_all_sgi_interrupts(&mut self) { pub fn enable_all_sgi_interrupts(&mut self) {
// Unwrap okay, index is valid. // Unwrap okay, index is valid.
@@ -375,17 +539,16 @@ impl GicConfigurator {
.unwrap(); .unwrap();
} }
/// Enable specific PPI interrupt.
#[inline] #[inline]
pub fn enable_ppi_interrupt(&mut self, ppi_int: PpiInterrupt) { pub fn enable_ppi_interrupt(&mut self, ppi_int: PpiInterrupt) {
// Unwrap okay, index is valid. // Unwrap okay, index is valid.
self.gicd self.gicd
.modify_iser(0, |mut v| { .modify_iser(0, |v| v | 1 << (ppi_int as u32))
v |= 1 << (ppi_int as u32);
v
})
.unwrap(); .unwrap();
} }
/// Enable all PPI interrupts.
#[inline] #[inline]
pub fn enable_all_ppi_interrupts(&mut self) { pub fn enable_all_ppi_interrupts(&mut self) {
unsafe { unsafe {
@@ -396,6 +559,7 @@ impl GicConfigurator {
}; };
} }
/// Enable specific SPI interrupt.
#[inline] #[inline]
pub fn enable_spi_interrupt(&mut self, spi_int: SpiInterrupt) { pub fn enable_spi_interrupt(&mut self, spi_int: SpiInterrupt) {
let spi_int_raw = spi_int as u32; let spi_int_raw = spi_int as u32;
@@ -403,17 +567,18 @@ impl GicConfigurator {
32..=63 => { 32..=63 => {
let bit_pos = spi_int_raw - 32; let bit_pos = spi_int_raw - 32;
// Unwrap okay, valid index. // Unwrap okay, valid index.
self.gicd.write_iser(1, 1 << bit_pos).unwrap(); self.gicd.modify_iser(1, |v| v | (1 << bit_pos)).unwrap();
} }
64..=92 => { 64..=92 => {
let bit_pos = spi_int_raw - 64; let bit_pos = spi_int_raw - 64;
// Unwrap okay, valid index. // Unwrap okay, valid index.
self.gicd.write_iser(2, 1 << bit_pos).unwrap(); self.gicd.modify_iser(2, |v| v | (1 << bit_pos)).unwrap();
} }
_ => unreachable!(), _ => unreachable!(),
} }
} }
/// Enable all SPI interrupts.
#[inline] #[inline]
pub fn enable_all_spi_interrupts(&mut self) { pub fn enable_all_spi_interrupts(&mut self) {
self.gicd.write_iser(1, 0xFFFF_FFFF).unwrap(); self.gicd.write_iser(1, 0xFFFF_FFFF).unwrap();

View File

@@ -8,14 +8,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased] # [unreleased]
# [v0.1.2] 2025-11-28
Bugfixes in startup assembler code. Bugfixes in startup assembler code.
## Changed ## Changed
- `.data` initialization is skipped if it is already in place, which is usually the default - `.data` initialization is skipped if it is already in place, which is usually the default
case because it is flashed to RAM. case because it is flashed to RAM.
- 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.
## Fixed ## Fixed
@@ -31,7 +32,6 @@ Documentation fixes.
Initial release Initial release
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-rt-v0.1.2...HEAD [unreleased]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-rt-v0.1.0...HEAD
[v0.1.2]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-rt-v0.1.1...zynq7000-rt-v0.1.2
[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.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 [v0.1.0]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/tag/zynq7000-rt-v0.1.0

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "zynq7000-rt" name = "zynq7000-rt"
version = "0.1.2" version = "0.1.1"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"] authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2024" edition = "2024"
description = "Run-time support for the Zynq7000 family of SoCs for running bare-metal applications" description = "Run-time support for the Zynq7000 family of SoCs for running bare-metal applications"

View File

@@ -9,15 +9,18 @@ Startup code and minimal runtime for the AMD Zynq7000 SoC to write bare metal Ru
This run-time crate is strongly based on the This run-time crate is strongly based on the
[startup code provided by AMD](https://github.com/Xilinx/embeddedsw/blob/master/lib/bsp/standalone/src/arm/cortexa9/gcc/boot.S). [startup code provided by AMD](https://github.com/Xilinx/embeddedsw/blob/master/lib/bsp/standalone/src/arm/cortexa9/gcc/boot.S).
Some major differences: It mostly builds on [aarch32-rt](https://github.com/rust-embedded/aarch32/tree/main/aarch32-rt).
It activates the `fpu-d32` feature on that crate and overrides the `_default_start` method
to add necessary setup code for the Zynq7000. It re-exports the `aarch32-rt` crate, including
the attributes macros. The [documentation](https://docs.rs/aarch32-rt/latest/aarch32_rt/) specifies
these in detail.
Some major differences to the startup code provided by AMD:
- No L2 cache initialization is performed. - No L2 cache initialization is performed.
- MMU table is specified as Rust code. - MMU table is specified as Rust code.
- Modification to the stack setup code, because a different linker script is used. - Modification to the stack setup code, because a different linker script is used.
This crate pulls in the [aarch32-rt](https://github.com/rust-embedded/aarch32/tree/main/aarch32-rt)
crate to provide ARM vectors and the linker script.
## Features ## Features
- `rt` is a default feature which activates the run-time. - `rt` is a default feature which activates the run-time.

View File

@@ -1,8 +1,16 @@
//! # Rust bare metal run-time support for the AMD Zynq 7000 SoCs //! # Rust bare metal run-time support for the AMD Zynq 7000 SoCs
//! //!
//! This includes basic low-level startup code similar to the bare-metal boot routines //! Startup code and minimal runtime for the AMD Zynq7000 SoC to write bare metal Rust code.
//! [provided by Xilinx](https://github.com/Xilinx/embeddedsw/tree/master/lib/bsp/standalone/src/arm/cortexa9/gcc). //! This run-time crate is strongly based on the
//! Some major differences: //! [startup code provided by AMD](https://github.com/Xilinx/embeddedsw/blob/master/lib/bsp/standalone/src/arm/cortexa9/gcc/boot.S).
//!
//! It mostly builds on [aarch32-rt](https://github.com/rust-embedded/aarch32/tree/main/aarch32-rt).
//! It activates the `fpu-d32` feature on that crate and overrides the `_default_start` method
//! to add necessary setup code for the Zynq7000. It re-exports the `aarch32-rt` crate, including
//! the attributes macros. The [documentation](https://docs.rs/aarch32-rt/latest/aarch32_rt/) specifies
//! these in detail.
//!
//! Some major differences to the startup code provided by AMD:
//! //!
//! - No L2 cache initialization is performed. //! - No L2 cache initialization is performed.
//! - MMU table is specified as Rust code. //! - MMU table is specified as Rust code.

View File

@@ -213,7 +213,7 @@ data_init_done:
// Jump to application // Jump to application
// Load CPU ID 0, which will be used as a function argument to the boot_core function. // Load CPU ID 0, which will be used as a function argument to the boot_core function.
mov r0, #0x0 mov r0, #0x0
bl boot_core bl kmain
// In case the application returns, loop forever // In case the application returns, loop forever
b . b .
.size _start, . - _start .size _start, . - _start

View File

@@ -8,8 +8,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased] # [unreleased]
- Renamed all register blocks to `Registers` to subblocks to `<Subblock>Registers` - Renamed all register blocks to `Registers` to subblocks to `<Subblock>Registers`.
- Added SDIO registers - Updated IPTR registers in the GIC module to use a custom register type instead of a raw u32.
- Added SDIO registers.
# [v0.1.1] 2025-10-09 # [v0.1.1] 2025-10-09

View File

@@ -1,6 +1,6 @@
//! # GIC (Generic Interrupt Controller) register module. //! # GIC (Generic Interrupt Controller) register module.
pub use crate::mpcore::{GICC_BASE_ADDR, GICD_BASE_ADDR}; pub use crate::mpcore::{GICC_BASE_ADDR, GICD_BASE_ADDR};
use arbitrary_int::{u3, u5, u10}; use arbitrary_int::{u2, u3, u5, u10};
use static_assertions::const_assert_eq; use static_assertions::const_assert_eq;
/// Distributor Control Register /// Distributor Control Register
@@ -40,6 +40,16 @@ impl TypeRegister {
pub type Typer = TypeRegister; pub type Typer = TypeRegister;
// 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)]
targets: [u2; 4],
}
#[deprecated(note = "Use DistributorRegisters instead")] #[deprecated(note = "Use DistributorRegisters instead")]
pub type GicDistributorTyper = DistributorRegisters; pub type GicDistributorTyper = DistributorRegisters;
@@ -78,10 +88,11 @@ pub struct DistributorRegisters {
pub ipr: [u32; 0x18], pub ipr: [u32; 0x18],
_reserved_11: [u32; 0xE8], _reserved_11: [u32; 0xE8],
/// Interrupt Processor Targes Registers /// Interrupt Processor Targes Registers
pub iptr_sgi: [u32; 0x4], pub iptr_sgi: [InterruptProcessorTargetRegister; 0x4],
// TODO: Mark those read-only as soon as that works for arrays. /// These are read-only because they always target their private CPU.
pub iptr_ppi: [u32; 0x4], #[mmio(PureRead)]
pub iptr_spi: [u32; 0x10], pub iptr_ppi: [InterruptProcessorTargetRegister; 0x4],
pub iptr_spi: [InterruptProcessorTargetRegister; 0x10],
// Those are split in the ARM documentation for some reason.. // Those are split in the ARM documentation for some reason..
_reserved_12: [u32; 0xE8], _reserved_12: [u32; 0xE8],
/// Interrupt Configuration Registers /// Interrupt Configuration Registers