diff --git a/Cargo.lock b/Cargo.lock index 9e0e15a..523e54b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1290,10 +1290,11 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -2505,9 +2506,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c" dependencies = [ "cfg-if", "once_cell", @@ -2516,9 +2517,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd" dependencies = [ "bumpalo", "log", @@ -2531,9 +2532,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2541,9 +2542,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d" dependencies = [ "proc-macro2", "quote", @@ -2554,9 +2555,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" [[package]] name = "winapi" @@ -2695,6 +2696,7 @@ version = "0.1.0" dependencies = [ "clap 4.5.21", "crc32fast", + "elf", "proc-macro2", "quote", "registers-generator", diff --git a/emulator/cpu/src/internal_timers.rs b/emulator/cpu/src/internal_timers.rs index 03bae0c..956699d 100644 --- a/emulator/cpu/src/internal_timers.rs +++ b/emulator/cpu/src/internal_timers.rs @@ -96,8 +96,8 @@ impl InternalTimer { self.read_time(now) >= self.bound } - fn ticks_to_interrupt(&self, now: u64) -> u32 { - self.bound.wrapping_sub(self.read_time(now)) + pub(crate) fn ticks_to_interrupt(&self, now: u64) -> u32 { + self.bound.saturating_sub(self.read_time(now)).max(1) } fn arm(&mut self, timer: &Timer, now: u64) { @@ -195,3 +195,29 @@ impl InternalTimers { ) } } + +#[cfg(test)] +mod test { + use std::rc::Rc; + + use super::InternalTimer; + use emulator_bus::Clock; + + #[test] + fn test_ticks_to_interrupt() { + let clock = Rc::new(Clock::new()); + let t = clock.timer(); + let mut itimer = InternalTimer::new(0); + itimer.write_bound(300, &t, 100); + assert_eq!(200, itimer.ticks_to_interrupt(100)); + } + + #[test] + fn test_ticks_to_interrupt_underflow() { + let clock = Rc::new(Clock::new()); + let t = clock.timer(); + let mut itimer = InternalTimer::new(0); + itimer.write_bound(300, &t, 100); + assert_eq!(1, itimer.ticks_to_interrupt(400)); + } +} diff --git a/runtime/apps/pldm/src/riscv.rs b/runtime/apps/pldm/src/riscv.rs index 40f91b4..8060d00 100644 --- a/runtime/apps/pldm/src/riscv.rs +++ b/runtime/apps/pldm/src/riscv.rs @@ -71,6 +71,12 @@ fn init(spawner: Spawner) { async fn async_main() { let mut console_writer = Console::writer(); writeln!(console_writer, "Hello async world!").unwrap(); + writeln!( + console_writer, + "Timer frequency: {}", + AsyncAlarm::::get_frequency().unwrap().0 + ) + .unwrap(); match AsyncAlarm::::exists() { Ok(()) => {} @@ -86,8 +92,8 @@ async fn async_main() { }; for _ in 0..5 { - writeln!(console_writer, "Sleeping for 10 millisecond").unwrap(); - sleep(Milliseconds(10)).await; + writeln!(console_writer, "Sleeping for 1 millisecond").unwrap(); + sleep(Milliseconds(1)).await; writeln!(console_writer, "async sleeper woke").unwrap(); } writeln!(console_writer, "app finished").unwrap(); @@ -150,12 +156,12 @@ impl AsyncAlarm { Ok(ticks.saturating_div(freq / 1000)) } - pub async fn sleep_for(_time: T) -> Result<(), ErrorCode> { - // TODO: this seems to never return, so we just sleep for 1 tick - // let freq = Self::get_frequency()?; - // let ticks = time.to_ticks(freq); + pub async fn sleep_for(time: T) -> Result<(), ErrorCode> { + let freq = Self::get_frequency()?; + let ticks = time.to_ticks(freq).0; + writeln!(Console::writer(), "Sleeping for {} ticks", ticks).unwrap(); let sub = TockSubscribe::subscribe::(DRIVER_NUM, 0); - S::command(DRIVER_NUM, command::SET_RELATIVE, 1, 0) + S::command(DRIVER_NUM, command::SET_RELATIVE, ticks, 0) .to_result() .map(|_when: u32| ())?; sub.await.map(|_| ()) diff --git a/runtime/kernel_layout.ld b/runtime/kernel_layout.ld index 42cc545..bd09702 100644 --- a/runtime/kernel_layout.ld +++ b/runtime/kernel_layout.ld @@ -235,18 +235,6 @@ SECTIONS . = ALIGN(4); _sapps = .; - /* Include placeholder bytes in this section so that the linker - * includes a segment for it. Otherwise the section will be empty and - * the linker will ignore it when defining the segments. - * If less then 4 bytes, some linkers set this section to size 0 - * and openocd fails to write it. - * - * An issue has been submitted https://github.com/raspberrypi/openocd/issues/25 - */ - BYTE(0xFF) - BYTE(0xFF) - BYTE(0xFF) - BYTE(0xFF) } > prog /* _eapps symbol used by tock to calculate the length of app flash */ _eapps = _sapps + LENGTH(prog); @@ -332,6 +320,7 @@ SECTIONS * future enhancement may allow the kernel to parcel this memory space * dynamically, requiring changes to this section. */ + . = ALIGN(4096); /* align to page boundary due to lld limitation */ _sappmem = .; *(.app_memory) } > ram @@ -423,5 +412,5 @@ _erom = ORIGIN(rom) + LENGTH(rom); /* This assert works out because even though some of the relative positions are * off, the sizes are sane in each pass. */ -ASSERT((_etext - _stext) + (_erelocate - _srelocate) + (_eattributes - _sattributes) < LENGTH(rom), -"Text plus relocations plus attributes exceeds the available ROM space."); +/*ASSERT((_etext - _stext) + (_erelocate - _srelocate) + (_eattributes - _sattributes) < LENGTH(rom), +"Text plus relocations plus attributes exceeds the available ROM space.");*/ diff --git a/runtime/src/board.rs b/runtime/src/board.rs index 6b92090..d57171c 100644 --- a/runtime/src/board.rs +++ b/runtime/src/board.rs @@ -48,6 +48,10 @@ struct VeeR { VirtualMuxAlarm<'static, InternalTimers<'static>>, >, console: &'static capsules_core::console::Console<'static>, + lldb: &'static capsules_core::low_level_debug::LowLevelDebug< + 'static, + capsules_core::virtualizers::virtual_uart::UartDevice<'static>, + >, scheduler: &'static CooperativeSched<'static>, scheduler_timer: &'static VirtualSchedulerTimer>>, @@ -62,6 +66,7 @@ impl SyscallDriverLookup for VeeR { match driver_num { capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)), capsules_core::console::DRIVER_NUM => f(Some(self.console)), + capsules_core::low_level_debug::DRIVER_NUM => f(Some(self.lldb)), _ => f(None), } } @@ -162,6 +167,13 @@ pub unsafe fn main() { components::debug_writer::DebugWriterComponent::new(uart_mux) .finalize(components::debug_writer_component_static!()); + let lldb = components::lldb::LowLevelDebugComponent::new( + board_kernel, + capsules_core::low_level_debug::DRIVER_NUM, + uart_mux, + ) + .finalize(components::low_level_debug_component_static!()); + // Setup the console. let console = components::console::ConsoleComponent::new( board_kernel, @@ -226,6 +238,7 @@ pub unsafe fn main() { let veer = VeeR { alarm, console, + lldb, scheduler, scheduler_timer, }; diff --git a/runtime/src/timers.rs b/runtime/src/timers.rs index 6b6aad4..ae26bd6 100644 --- a/runtime/src/timers.rs +++ b/runtime/src/timers.rs @@ -6,7 +6,7 @@ //! Create a timer using the VeeR EL2 Internal Timer registers. use core::cell::Cell; -use kernel::hil::time::{self, Alarm, ConvertTicks, Freq32KHz, Frequency, Ticks, Ticks64, Time}; +use kernel::hil::time::{self, Alarm, ConvertTicks, Freq1MHz, Frequency, Ticks, Ticks64, Time}; use kernel::utilities::cells::OptionalCell; use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable}; use kernel::utilities::registers::register_bitfields; @@ -134,7 +134,8 @@ impl<'a> InternalTimers<'a> { impl Time for InternalTimers<'_> { // TODO: replace with real VeeR frequency - type Frequency = Freq32KHz; + // This is roughly okay for the emulator though. + type Frequency = Freq1MHz; type Ticks = Ticks64; fn now(&self) -> Ticks64 { diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 02a768e..4ef2aff 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -11,6 +11,7 @@ tempfile = "3.14.0" walkdir = "2.5.0" proc-macro2.workspace = true clap.workspace = true +elf.workspace = true registers-generator.workspace = true registers-systemrdl.workspace = true quote.workspace = true diff --git a/xtask/src/apps_build.rs b/xtask/src/apps_build.rs index 05fa441..4b6654f 100644 --- a/xtask/src/apps_build.rs +++ b/xtask/src/apps_build.rs @@ -29,22 +29,29 @@ pub const BASE_PERMISSIONS: &[(u32, u32)] = &[ (1, 1), (1, 2), (1, 3), + (8, 0), // Low-level debug + (8, 1), // Low-level debug + (8, 2), // Low-level debug + (8, 3), // Low-level debug ]; // creates a single flat binary with all the apps built with TBF headers -pub fn apps_build_flat_tbf(start: usize) -> Result, DynError> { +pub fn apps_build_flat_tbf(start: usize, ram_start: usize) -> Result, DynError> { let mut bin = vec![]; let mut offset = start; + let mut ram_start = ram_start; for app in APPS.iter() { - let app_bin = app_build_tbf(app, offset)?; + let app_bin = app_build_tbf(app, offset, ram_start)?; bin.extend_from_slice(&app_bin); offset += app_bin.len(); + // TODO: support different amount of RAM per app + ram_start += 0x4000; } Ok(bin) } // creates a flat binary of the app with the TBF header -fn app_build_tbf(app: &App, start: usize) -> Result, DynError> { +fn app_build_tbf(app: &App, start: usize, ram_start: usize) -> Result, DynError> { // start the TBF header let mut tbf = TbfHeader::new(); let mut permissions = BASE_PERMISSIONS.to_vec(); @@ -63,7 +70,7 @@ fn app_build_tbf(app: &App, start: usize) -> Result, DynError> { tbf.set_binary_end_offset(0); // temporary just to get the size of the header let tbf_header_size = tbf.generate()?.get_ref().len(); - app_build(app.name, start, tbf_header_size)?; + app_build(app.name, start, ram_start, tbf_header_size)?; let objcopy = objcopy()?; let app_bin = target_binary(&format!("{}.bin", app.name)); @@ -95,7 +102,12 @@ fn app_build_tbf(app: &App, start: usize) -> Result, DynError> { } // creates an ELF of the app -fn app_build(app_name: &str, offset: usize, tbf_header_size: usize) -> Result<(), DynError> { +fn app_build( + app_name: &str, + offset: usize, + ram_start: usize, + tbf_header_size: usize, +) -> Result<(), DynError> { let layout_ld = &PROJECT_ROOT.join("runtime").join("apps").join("layout.ld"); // TODO: do we need to fix the RAM start and length? @@ -107,10 +119,10 @@ fn app_build(app_name: &str, offset: usize, tbf_header_size: usize) -> Result<() TBF_HEADER_SIZE = 0x{:x}; FLASH_START = 0x{:x}; FLASH_LENGTH = 0x10000; -RAM_START = 0x50000000; -RAM_LENGTH = 0x10000; +RAM_START = 0x{:x}; +RAM_LENGTH = 0x4000; INCLUDE runtime/apps/app_layout.ld", - tbf_header_size, offset, + tbf_header_size, offset, ram_start ), )?; @@ -120,7 +132,7 @@ INCLUDE runtime/apps/app_layout.ld", .current_dir(&*PROJECT_ROOT) .env("LIBTOCK_LINKER_FLASH", format!("0x{:x}", offset)) .env("LIBTOCK_LINKER_FLASH_LENGTH", "128K") - .env("LIBTOCK_LINKER_RAM", "0x50000000") + .env("LIBTOCK_LINKER_RAM", format!("0x{:x}", ram_start)) .env("LIBTOCK_LINKER_RAM_LENGTH", "128K") .args([ "rustc", diff --git a/xtask/src/runtime_build.rs b/xtask/src/runtime_build.rs index 66e2a18..9672405 100644 --- a/xtask/src/runtime_build.rs +++ b/xtask/src/runtime_build.rs @@ -9,6 +9,8 @@ use crate::apps_build::apps_build_flat_tbf; use crate::{DynError, PROJECT_ROOT, TARGET}; +use elf::endian::AnyEndian; +use elf::ElfBytes; use std::path::PathBuf; use std::process::Command; use std::sync::LazyLock; @@ -19,7 +21,6 @@ const INTERRUPT_TABLE_SIZE: usize = 128; const ICCM_SIZE: usize = 256 * 1024; const RAM_START: usize = 0x5000_0000; const RAM_SIZE: usize = 128 * 1024; -const BSS_SIZE: usize = 5000; // this is approximate. Increase it if there are "sram" errors when linking static SYSROOT: LazyLock = LazyLock::new(|| { // cache this in the target directory as it seems to be very slow to call rustc @@ -91,11 +92,27 @@ pub fn objcopy() -> Result { }) } +fn get_apps_memory_offset(elf_file: PathBuf) -> Result { + let elf_bytes = std::fs::read(&elf_file)?; + let elf_file = ElfBytes::::minimal_parse(&elf_bytes)?; + let x = elf_file + .symbol_table() + .unwrap() + .iter() + .find_map(|(parse_table, string_table)| { + parse_table + .iter() + .find(|p| string_table.get(p.st_name as usize).unwrap_or_default() == "_sappmem") + .map(|symbol| symbol.st_value as usize) + }); + x.ok_or("error finding _sappmem symbol".into()) +} + fn runtime_build_no_apps( apps_offset: usize, features: &[&str], output_name: &str, -) -> Result<(), DynError> { +) -> Result { let tock_dir = &PROJECT_ROOT.join("runtime"); let sysr = SYSROOT.clone(); let ld_file_path = tock_dir.join("layout.ld"); @@ -249,7 +266,7 @@ INCLUDE runtime/kernel_layout.ld Err("objcopy failed to build runtime")?; } - Ok(()) + get_apps_memory_offset(target_binary("runtime")) } pub fn runtime_build_with_apps( @@ -261,13 +278,15 @@ pub fn runtime_build_with_apps( let runtime_bin = target_binary(output_name); // build once to get the size of the runtime binary without apps - runtime_build_no_apps(RUNTIME_START + 0x2_0000, features, output_name)?; + let apps_memory_offset = + runtime_build_no_apps(RUNTIME_START + 0x2_0000, features, output_name)?; let runtime_bin_size = std::fs::metadata(&runtime_bin)?.len() as usize; app_offset += runtime_bin_size; let runtime_end_offset = app_offset; - app_offset += BSS_SIZE; // it's not clear why this is necessary as the BSS should be part of .sram, but the linker fails without this - app_offset = app_offset.next_multiple_of(4096); // align to 4096 bytes. Needed for rust-lld + // ensure that we leave space for the interrupt table + // and align to 4096 bytes (needed for rust-lld) + let app_offset = (runtime_end_offset + INTERRUPT_TABLE_SIZE).next_multiple_of(4096); let padding = app_offset - runtime_end_offset - INTERRUPT_TABLE_SIZE; // re-link and place the apps after the runtime binary @@ -278,7 +297,7 @@ pub fn runtime_build_with_apps( println!("Kernel binary built: {} bytes", kernel_size); // build the apps starting at the correct offset - let apps_bin = apps_build_flat_tbf(app_offset)?; + let apps_bin = apps_build_flat_tbf(app_offset, apps_memory_offset)?; println!("Apps built: {} bytes", apps_bin.len()); bin.extend_from_slice(vec![0; padding].as_slice()); bin.extend_from_slice(&apps_bin);