16 Commits

16 changed files with 278 additions and 134 deletions

2
Cargo.lock generated
View File

@@ -76,6 +76,7 @@ name = "libasync"
version = "0.0.0"
dependencies = [
"embedded-hal",
"libcortex_a9",
"nb 1.0.0",
"smoltcp",
]
@@ -103,6 +104,7 @@ dependencies = [
"core_io",
"fatfs",
"libboard_zynq",
"libcortex_a9",
"log",
]

58
flake.lock generated
View File

@@ -1,17 +1,40 @@
{
"nodes": {
"fenix": {
"inputs": {
"nixpkgs": [
"naersk",
"nixpkgs"
],
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1752475459,
"narHash": "sha256-z6QEu4ZFuHiqdOPbYss4/Q8B0BFhacR8ts6jO/F/aOU=",
"owner": "nix-community",
"repo": "fenix",
"rev": "bf0d6f70f4c9a9cf8845f992105652173f4b617f",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"type": "github"
}
},
"naersk": {
"inputs": {
"fenix": "fenix",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1745925850,
"narHash": "sha256-cyAAMal0aPrlb1NgzMxZqeN1mAJ2pJseDhm2m6Um8T0=",
"lastModified": 1768908532,
"narHash": "sha256-HIdLXEFaUVE8FiaCPJbCfBMsnF+mVtDub8Jwj2BD+mk=",
"owner": "nix-community",
"repo": "naersk",
"rev": "38bc60bbc157ae266d4a0c96671c6c742ee17a5f",
"rev": "8d97452673640eb7fabe428e8b6a425bc355008b",
"type": "github"
},
"original": {
@@ -22,11 +45,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1749794982,
"narHash": "sha256-Kh9K4taXbVuaLC0IL+9HcfvxsSUx8dPB5s5weJcc9pc=",
"lastModified": 1769018530,
"narHash": "sha256-MJ27Cy2NtBEV5tsK+YraYr2g851f3Fl1LpNHDzDX15c=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "ee930f9755f58096ac6e8ca94a1887e0534e2d81",
"rev": "88d3861acdd3d2f0e361767018218e51810df8a1",
"type": "github"
},
"original": {
@@ -43,6 +66,23 @@
"rust-overlay": "rust-overlay"
}
},
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1752428706,
"narHash": "sha256-EJcdxw3aXfP8Ex1Nm3s0awyH9egQvB2Gu+QEnJn2Sfg=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "591e3b7624be97e4443ea7b5542c191311aa141d",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": [
@@ -50,11 +90,11 @@
]
},
"locked": {
"lastModified": 1750041667,
"narHash": "sha256-/8F9L6T9w/Fx1D6L+BtWIXg5m9F6jwOFg6uhZpKnM/0=",
"lastModified": 1769136478,
"narHash": "sha256-8UNd5lmGf8phCr/aKxagJ4kNsF0pCHLish2G4ZKCFFY=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "d72bd8c9fda03c9834ea89d7a5a21c7880b79277",
"rev": "470ee44393bb19887056b557ea2c03fc5230bd5a",
"type": "github"
},
"original": {

210
flake.nix
View File

@@ -11,21 +11,30 @@
inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { self, nixpkgs, rust-overlay, naersk }:
let
pkgs = import nixpkgs { system = "x86_64-linux"; overlays = [ (import rust-overlay) crosspkgs-overlay ]; };
rust = pkgs.rust-bin.nightly."2025-03-28".default.override {
extensions = [ "rust-src" ];
targets = [ ];
};
naerskLib = pkgs.callPackage naersk {
rustc = rust;
cargo = rust;
};
outputs = {
self,
nixpkgs,
rust-overlay,
naersk,
}: let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [(import rust-overlay) crosspkgs-overlay];
};
crosspkgs-overlay = (self: super: {
pkgsCross = super.pkgsCross // {
rust = pkgs.rust-bin.nightly."2025-03-28".default.override {
extensions = ["rust-src"];
targets = [];
};
naerskLib = pkgs.callPackage naersk {
rustc = rust;
cargo = rust;
};
crosspkgs-overlay = self: super: {
pkgsCross =
super.pkgsCross
// {
zynq-baremetal = import super.path {
system = "x86_64-linux";
crossSystem = {
@@ -36,44 +45,47 @@
};
};
};
});
};
mkbootimage = pkgs.stdenv.mkDerivation {
pname = "mkbootimage";
version = "2.3dev";
mkbootimage = pkgs.stdenv.mkDerivation {
pname = "mkbootimage";
version = "2.3dev";
src = pkgs.fetchFromGitHub {
owner = "antmicro";
repo = "zynq-mkbootimage";
rev = "872363ce32c249f8278cf107bc6d3bdeb38d849f";
sha256 = "sha256-5FPyAhUWZDwHbqmp9J2ZXTmjaXPz+dzrJMolaNwADHs=";
};
propagatedBuildInputs = [ pkgs.libelf pkgs.pcre ];
patchPhase =
''
substituteInPlace Makefile --replace "git rev-parse --short HEAD" "echo nix"
'';
installPhase =
''
mkdir -p $out/bin
cp mkbootimage $out/bin
'';
hardeningDisable = [ "fortify" ];
src = pkgs.fetchFromGitHub {
owner = "antmicro";
repo = "zynq-mkbootimage";
rev = "872363ce32c249f8278cf107bc6d3bdeb38d849f";
sha256 = "sha256-5FPyAhUWZDwHbqmp9J2ZXTmjaXPz+dzrJMolaNwADHs=";
};
fsbl = { board ? "zc706" }: pkgs.stdenv.mkDerivation {
propagatedBuildInputs = [pkgs.libelf pkgs.pcre];
patchPhase = ''
substituteInPlace Makefile --replace "git rev-parse --short HEAD" "echo nix"
'';
installPhase = ''
mkdir -p $out/bin
cp mkbootimage $out/bin
'';
hardeningDisable = ["fortify"];
};
fsbl = {board ? "zc706"}:
pkgs.stdenv.mkDerivation {
name = "${board}-fsbl";
src = pkgs.fetchFromGitHub {
owner = "Xilinx";
repo = "embeddedsw";
rev = "xilinx_v2022.2";
sha256 = "sha256-UDz9KK/Hw3qM1BAeKif30rE8Bi6C2uvuZlvyvtJCMfw=";
rev = "xilinx_v2025.1_update1";
sha256 = "sha256-XAwhkox1PDyo/UmxP9kjgKsjuoeWgIVhg8X1qFk+Pdo=";
};
nativeBuildInputs = [
pkgs.pkgsCross.zynq-baremetal.buildPackages.binutils
pkgs.pkgsCross.zynq-baremetal.buildPackages.gcc
];
NIX_CFLAGS_COMPILE = "-DFSBL_DEBUG_INFO -g -no-pie";
NIX_LDFLAGS = "-no-pie";
patchPhase = ''
patchShebangs lib/sw_apps/zynq_fsbl/misc/copy_bsp.sh
@@ -84,7 +96,7 @@
'';
buildPhase = ''
cd lib/sw_apps/zynq_fsbl/src
make BOARD=${board} "CFLAGS=-DFSBL_DEBUG_INFO -g"
make BOARD=${board}
'';
installPhase = ''
mkdir $out
@@ -94,18 +106,21 @@
dontFixup = true;
};
build-crate = name: crate: features: naerskLib.buildPackage rec {
build-crate = name: crate: features:
naerskLib.buildPackage rec {
name = "${crate}";
src = ./.;
additionalCargoLock = "${rust}/lib/rustlib/src/rust/library/Cargo.lock";
nativeBuildInputs = [ pkgs.llvmPackages_20.clang-unwrapped ];
nativeBuildInputs = [pkgs.llvmPackages_20.clang-unwrapped];
singleStep = true;
release = true;
cargoBuildOptions = options: options ++ [
"-p ${crate}"
"--no-default-features"
"--features=${features}"
];
cargoBuildOptions = options:
options
++ [
"-p ${crate}"
"--no-default-features"
"--features=${features}"
];
overrideMain = _: {
installPhase = ''
mkdir -p $out $out/nix-support
@@ -116,59 +131,68 @@
};
};
targetCrates = target: {
"${target}-experiments" = build-crate "${target}-experiments" "experiments" "target_${target}";
"${target}-szl" = build-crate "${target}-szl" "szl" "target_${target}";
};
targets = ["zc706" "coraz7" "redpitaya" "kasli_soc" "ebaz4205"];
allTargetCrates = (builtins.foldl' (results: target:
results // targetCrates target
) {} targets);
szl = pkgs.runCommand "szl" {} (builtins.foldl' (commands: target:
let
targetCrates = target: {
"${target}-experiments" = build-crate "${target}-experiments" "experiments" "target_${target}";
"${target}-szl" = build-crate "${target}-szl" "szl" "target_${target}";
};
targets = ["zc706" "coraz7" "redpitaya" "kasli_soc" "ebaz4205"];
allTargetCrates =
builtins.foldl' (
results: target:
results // targetCrates target
) {}
targets;
szl = pkgs.runCommand "szl" {} (builtins.foldl' (
commands: target: let
szlResult = builtins.getAttr "${target}-szl" allTargetCrates;
in
commands + "ln -s ${szlResult}/szl.elf $out/szl-${target}.elf\n"
) "mkdir $out\n" targets);
) "mkdir $out\n"
targets);
fmt-check = pkgs.stdenvNoCC.mkDerivation {
name = "fmt-check";
fmt-check = pkgs.stdenvNoCC.mkDerivation {
name = "fmt-check";
src = ./.;
src = ./.;
nativeBuildInputs = [ rust ];
nativeBuildInputs = [rust];
phases = [ "unpackPhase" "buildPhase" ];
phases = ["unpackPhase" "buildPhase"];
buildPhase =
''
cargo fmt -- --check
touch $out
'';
};
in rec {
packages.x86_64-linux = {
inherit szl mkbootimage fmt-check;
zc706-fsbl = fsbl { board = "zc706"; };
} // allTargetCrates ;
hydraJobs = packages.x86_64-linux;
inherit rust naerskLib;
devShell.x86_64-linux = pkgs.mkShell {
name = "zynq-rs-dev-shell";
buildInputs = [
rust
pkgs.cargo-xbuild
mkbootimage
pkgs.openocd pkgs.gdb
pkgs.openssh pkgs.rsync
pkgs.llvmPackages_20.clang-unwrapped
(pkgs.python3.withPackages(ps: [ ps.pyftdi ]))
];
};
buildPhase = ''
cargo fmt -- --check
touch $out
'';
};
in rec {
packages.x86_64-linux =
{
inherit szl mkbootimage fmt-check;
zc706-fsbl = fsbl {board = "zc706";};
}
// allTargetCrates;
hydraJobs = packages.x86_64-linux;
inherit rust naerskLib;
formatter.x86_64-linux = pkgs.alejandra;
devShell.x86_64-linux = pkgs.mkShell {
name = "zynq-rs-dev-shell";
buildInputs = [
rust
pkgs.cargo-xbuild
mkbootimage
pkgs.openocd
pkgs.gdb
pkgs.openssh
pkgs.rsync
pkgs.llvmPackages_20.clang-unwrapped
(pkgs.python3.withPackages (ps: [ps.pyftdi]))
];
};
};
}

View File

@@ -8,6 +8,7 @@ edition = "2018"
[dependencies]
embedded-hal = "0.2"
nb = "1.0"
libcortex_a9 = { path = "../libcortex_a9" }
[dependencies.smoltcp]
version = "0.7"

View File

@@ -1,12 +1,13 @@
use alloc::vec::Vec;
use core::{cell::RefCell, task::Waker};
use libcortex_a9::once_lock::OnceLock;
use smoltcp::{iface::EthernetInterface, phy::Device, socket::SocketSet, time::Instant};
mod tcp_stream;
pub use tcp_stream::TcpStream;
static mut SOCKETS: Option<Sockets> = None;
static SOCKETS: OnceLock<Sockets> = OnceLock::new();
pub struct Sockets {
sockets: RefCell<SocketSet<'static>>,
@@ -24,14 +25,11 @@ impl Sockets {
let wakers = RefCell::new(Vec::new());
let instance = Sockets { sockets, wakers };
unsafe {
SOCKETS = Some(instance);
}
SOCKETS.set(instance).expect("SOCKETS can only be initialized once");
}
#[allow(static_mut_refs)]
pub fn instance() -> &'static Self {
unsafe { SOCKETS.as_ref().expect("Sockets") }
SOCKETS.get().expect("cannot get instance before it is initialized")
}
pub fn poll<'b, D: for<'d> Device<'d>>(&self, iface: &mut EthernetInterface<'b, D>, instant: Instant) {

View File

@@ -73,6 +73,7 @@ pub trait ClockSource {
.clone();
debug!("Set {} to {} Hz", Self::name(), target_freq);
log::logger().flush();
slcr::RegisterBlock::unlocked(|slcr| {
let (pll_ctrl, pll_cfg, pll_status) = Self::pll_regs(slcr);

View File

@@ -77,7 +77,5 @@ macro_rules! println {
let mut uart = $crate::stdio::get_uart();
let _ = write!(uart, $($arg)*);
let _ = write!(uart, "\n");
// flush after the newline
while !uart.tx_idle() {}
})
}

View File

@@ -7,6 +7,7 @@ edition = "2018"
[dependencies]
libboard_zynq = { path = "../libboard_zynq" }
log = "0.4"
libcortex_a9 = { path = "../libcortex_a9" }
[dependencies.core_io]
git = "https://git.m-labs.hk/M-Labs/rs-core_io.git"

View File

@@ -7,6 +7,7 @@ use core::fmt;
use core_io::{self as io, BufRead, BufReader, Read, Seek, SeekFrom, Write};
use libboard_zynq::sdio;
use libcortex_a9::once_lock::OnceLock;
pub mod bootgen;
pub mod net_settings;
@@ -66,12 +67,7 @@ fn parse_config(key: &str, buffer: &mut Vec<u8>, file: fatfs::File<sd_reader::Sd
}
type FileSystem = fatfs::FileSystem<sd_reader::SdReader>;
static mut FS: Option<FileSystem> = None;
#[allow(static_mut_refs)]
pub fn get_filesystem() -> &'static Option<FileSystem> {
unsafe { &FS }
}
pub static FS: OnceLock<FileSystem> = OnceLock::new();
const NEWLINE: &[u8] = b"\n";
@@ -85,18 +81,16 @@ pub fn init() -> Result<()> {
let fs = reader.mount_fatfs(sd_reader::PartitionEntry::Entry1)?;
unsafe {
FS = Some(fs);
}
from_fs(fs);
Ok(())
}
pub fn from_fs(fs: Option<FileSystem>) {
unsafe { FS = fs }
pub fn from_fs(fs: FileSystem) {
FS.set(fs).expect("filesystem can only be initialized once");
}
pub fn read(key: &str) -> Result<Vec<u8>> {
if let Some(fs) = get_filesystem() {
if let Some(fs) = FS.get() {
let root_dir = fs.root_dir();
let mut buffer: Vec<u8> = Vec::new();
match root_dir.open_file(&["/CONFIG/", key, ".BIN"].concat()) {
@@ -117,7 +111,7 @@ pub fn read_str(key: &str) -> Result<String> {
}
pub fn remove(key: &str) -> Result<()> {
if let Some(fs) = get_filesystem() {
if let Some(fs) = FS.get() {
let root_dir = fs.root_dir();
match root_dir.remove(&["/CONFIG/", key, ".BIN"].concat()) {
Ok(()) => Ok(()),
@@ -147,10 +141,10 @@ pub fn remove(key: &str) -> Result<()> {
}
pub fn write(key: &str, value: Vec<u8>) -> Result<()> {
if get_filesystem().is_none() {
if FS.get().is_none() {
return Err(Error::NoConfig);
}
let fs = get_filesystem().as_ref().unwrap();
let fs = FS.get().unwrap();
let root_dir = fs.root_dir();
let is_str = value.len() <= 100 && value.is_ascii() && !value.contains(&b'\n');
if key == "boot" {

View File

@@ -11,6 +11,14 @@ pub fn tlbiall() {
}
}
/// Invalidate TLB for given MVA
#[inline(always)]
pub fn tlbimva(mva: u32) {
unsafe {
asm!("mcr p15, 0, {}, c8, c7, 1", in(reg) mva);
}
}
/// Invalidate I-Cache
#[inline(always)]
pub fn iciallu() {

View File

@@ -9,6 +9,7 @@ mod fpu;
pub mod l2c;
pub mod mmu;
pub mod mutex;
pub mod once_lock;
pub mod regs;
pub mod semaphore;
pub mod sync_channel;

View File

@@ -401,6 +401,28 @@ impl L1Table {
self.table[index] = L1Entry::from_section(base, section);
}
pub fn remap_section(&mut self, virtual_addr: u32, new_physical_base: u32) {
assert!(virtual_addr & 0x000f_ffff == 0);
let index = (virtual_addr >> 20) as usize;
let entry = &mut self.table[index];
if entry.0 & !0x000f_ffff == new_physical_base {
return;
}
let section = entry.get_section();
*entry = L1Entry::from_section(new_physical_base, section);
// L1 I-cache cache is VIPT and 1MB page sizes guarantee different tags
// L1 D-cache and L2 cache is PIPT, unaffected by virtual memory changes
// thus flushing caches are unnecessary, we just
// invalidate the updated TLB entry and the branch predictor
tlbimva(virtual_addr);
bpiall();
dsb();
isb();
}
pub fn update<T, F, R>(&mut self, ptr: *const T, f: F) -> R
where F: FnOnce(&'_ mut L1Section) -> R {
let index = (ptr as usize) >> 20;
@@ -413,6 +435,8 @@ impl L1Table {
dcciall();
// // TODO: L2?
// Invalidate I-Cache
iciallu();
// Invalidate TLB
tlbiall();
// Invalidate all branch predictors
@@ -454,3 +478,7 @@ pub fn with_mmu<F: FnMut() -> !>(l1table: &L1Table, mut f: F) -> ! {
f();
}
pub fn remap_section(virtual_addr: u32, new_physical_base: u32) {
L1Table::get().remap_section(virtual_addr, new_physical_base);
}

View File

@@ -0,0 +1,36 @@
use core::{cell::UnsafeCell,
sync::atomic::{AtomicBool, Ordering}};
use crate::asm::{enter_critical, exit_critical};
pub struct OnceLock<T> {
once: AtomicBool,
data: UnsafeCell<Option<T>>,
}
impl<T> OnceLock<T> {
pub const fn new() -> Self {
Self {
once: AtomicBool::new(false),
data: UnsafeCell::new(None),
}
}
pub fn set(&self, data: T) -> Result<(), ()> {
let irq = unsafe { enter_critical() };
let res = self
.once
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
.map_err(|_| ())
.map(|_| unsafe { *self.data.get() = Some(data) });
unsafe { exit_critical(irq) };
res
}
pub fn get(&self) -> Option<&T> {
unsafe { &*self.data.get() }.as_ref()
}
}
unsafe impl<T> Sync for OnceLock<T> {}
unsafe impl<T> Send for OnceLock<T> {}

View File

@@ -107,9 +107,15 @@ register_bit!(mpidr,
pub struct DFAR;
def_reg_r!(DFAR, u32, "mrc p15, 0, {}, c6, c0, 0");
pub struct IFAR;
def_reg_r!(IFAR, u32, "mrc p15, 0, {}, c6, c0, 2");
pub struct DFSR;
def_reg_r!(DFSR, u32, "mrc p15, 0, {}, c5, c0, 0");
pub struct IFSR;
def_reg_r!(IFSR, u32, "mrc p15, 0, {}, c5, c0, 1");
pub struct SCTLR;
wrap_reg!(sctlr);
def_reg_r!(SCTLR, sctlr::Read, "mrc p15, 0, {}, c1, c0, 0");

View File

@@ -2,7 +2,7 @@ use core::arch::naked_asm;
use libboard_zynq::{println, stdio};
use libcortex_a9::{interrupt_handler,
regs::{DFSR, MPIDR, VBAR}};
regs::{DFAR, DFSR, IFAR, IFSR, MPIDR, VBAR}};
use libregister::{RegisterR, RegisterW};
pub fn set_vector_table(base_addr: u32) {
@@ -35,7 +35,9 @@ interrupt_handler!(
interrupt_handler!(PrefetchAbort, prefetch_abort, __irq_stack0_start, __irq_stack1_start, {
stdio::drop_uart();
println!("PrefetchAbort");
println!("PrefetchAbort on core {}", MPIDR.read().cpu_id());
println!("IFSR: {:03X}", IFSR.read());
println!("IFAR: {:08X}", IFAR.read());
loop {}
});
@@ -44,6 +46,7 @@ interrupt_handler!(DataAbort, data_abort, __irq_stack0_start, __irq_stack1_start
println!("DataAbort on core {}", MPIDR.read().cpu_id());
println!("DFSR: {:03X}", DFSR.read());
println!("DFAR: {:08X}", DFAR.read());
loop {}
});

View File

@@ -87,21 +87,24 @@ pub fn main_core0() {
ram::init_alloc_core0();
let sdio0 = sdio::Sdio::sdio0(true);
let fs = if sdio0.is_card_inserted() {
let mut bootgen_file = if sdio0.is_card_inserted() {
info!("Card inserted. Mounting file system.");
let sd = sdio::sd_card::SdCard::from_sdio(sdio0).unwrap();
let reader = sd_reader::SdReader::new(sd);
reader.mount_fatfs(sd_reader::PartitionEntry::Entry1).ok()
reader
.mount_fatfs(sd_reader::PartitionEntry::Entry1)
.and_then(|fs| {
libconfig::from_fs(fs);
let fs_ref = libconfig::FS.get().unwrap();
let root_dir = fs_ref.root_dir();
root_dir.open_file("/BOOT.BIN")
})
.ok()
} else {
info!("No SD card inserted.");
None
};
libconfig::from_fs(fs);
let fs_ref = libconfig::get_filesystem().as_ref();
let root_dir = fs_ref.map(|fs| fs.root_dir());
let mut bootgen_file = root_dir.and_then(|root_dir| root_dir.open_file("/BOOT.BIN").ok());
let max_len = (&raw const __runtime_end).addr() - (&raw const __runtime_start).addr();
match slcr::RegisterBlock::unlocked(|slcr| slcr.boot_mode.read().boot_mode_pins()) {
slcr::BootModePins::Jtag => netboot::netboot(&mut bootgen_file, (&raw mut __runtime_start).cast(), max_len),