-
Notifications
You must be signed in to change notification settings - Fork 193
Move QEMU executables to a non-default member test-qemu, and add multi-hart tests
#382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1b6b400
9d45dbd
209053f
b8aee79
cc5a1a4
149c39c
73f1a1c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| Hart 0: Initializing | ||
| Hart 1: Running | ||
| Hart 0: Both harts done |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| [package] | ||
| name = "tests-qemu" | ||
| version = "0.1.0" | ||
| edition = "2021" | ||
|
|
||
| [build-dependencies] | ||
|
|
||
| [dependencies] | ||
| panic-halt = "1.0" | ||
| riscv = { path = "../riscv" } | ||
| riscv-rt = { path = "../riscv-rt" } | ||
| riscv-semihosting = { path = "../riscv-semihosting" } | ||
|
|
||
| [features] | ||
| default = ["single-hart"] | ||
| s-mode = ["riscv-rt/s-mode", "single-hart"] | ||
| single-hart = ["riscv-rt/single-hart", "riscv/critical-section-single-hart"] | ||
| multi-hart = [] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| use std::{env, fs::File, io::Write, path::PathBuf}; | ||
|
|
||
| fn main() { | ||
| let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); | ||
|
|
||
| let s_mode = env::var_os("CARGO_FEATURE_S_MODE").is_some(); | ||
| let multi_hart = env::var_os("CARGO_FEATURE_MULTI_HART").is_some(); | ||
|
|
||
| // Multi-hart is only supported in M-mode; s-mode takes priority if both are enabled | ||
| let memory_x = match (s_mode, multi_hart) { | ||
| (true, _) => include_bytes!("memory-s-mode.x").as_slice(), | ||
| (_, true) => include_bytes!("memory-multihart.x").as_slice(), | ||
| _ => include_bytes!("memory.x").as_slice(), | ||
| }; | ||
|
|
||
| File::create(out.join("memory.x")) | ||
| .unwrap() | ||
| .write_all(memory_x) | ||
| .unwrap(); | ||
| println!("cargo:rustc-link-search={}", out.display()); | ||
|
|
||
| println!("cargo:rustc-link-arg=-Tmemory.x"); | ||
|
|
||
| println!("cargo:rerun-if-changed=memory.x"); | ||
| println!("cargo:rerun-if-changed=memory-multihart.x"); | ||
| println!("cargo:rerun-if-changed=memory-s-mode.x"); | ||
| println!("cargo:rerun-if-changed=build.rs"); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| //! Multi-hart example demonstrating IPI-based hart synchronization. | ||
| //! | ||
| //! Hart 0 initializes UART and wakes Hart 1 via software interrupt (CLINT). | ||
| //! Both harts print messages and synchronize before exit. | ||
|
|
||
| #![no_std] | ||
| #![no_main] | ||
|
|
||
| extern crate panic_halt; | ||
|
|
||
| use core::arch::global_asm; | ||
| use core::sync::atomic::{AtomicBool, Ordering}; | ||
| use riscv_rt::entry; | ||
| use riscv_semihosting::debug::{self, EXIT_SUCCESS}; | ||
|
|
||
| const UART_BASE: usize = 0x1000_0000; | ||
| const UART_THR: usize = UART_BASE; | ||
| const UART_LCR: usize = UART_BASE + 3; | ||
| const UART_LSR: usize = UART_BASE + 5; | ||
| const LCR_DLAB: u8 = 1 << 7; | ||
| const LCR_8N1: u8 = 0x03; | ||
| const LSR_THRE: u8 = 1 << 5; | ||
|
|
||
| static UART_LOCK: AtomicBool = AtomicBool::new(false); | ||
| static HART1_DONE: AtomicBool = AtomicBool::new(false); | ||
|
|
||
| fn uart_lock() { | ||
| while UART_LOCK | ||
| .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) | ||
| .is_err() | ||
| { | ||
| core::hint::spin_loop(); | ||
| } | ||
| } | ||
|
|
||
| fn uart_unlock() { | ||
| UART_LOCK.store(false, Ordering::Release); | ||
| } | ||
|
|
||
| unsafe fn uart_write_reg(off: usize, v: u8) { | ||
| (off as *mut u8).write_volatile(v); | ||
| } | ||
|
|
||
| unsafe fn uart_read_reg(off: usize) -> u8 { | ||
| (off as *const u8).read_volatile() | ||
| } | ||
|
|
||
| fn uart_init() { | ||
| unsafe { | ||
| uart_write_reg(UART_LCR, LCR_DLAB); | ||
| uart_write_reg(UART_THR, 0x01); | ||
| uart_write_reg(UART_BASE + 1, 0x00); | ||
| uart_write_reg(UART_LCR, LCR_8N1); | ||
| uart_write_reg(UART_BASE + 2, 0x07); | ||
| } | ||
| } | ||
|
|
||
| fn uart_write_byte(b: u8) { | ||
| unsafe { | ||
| while (uart_read_reg(UART_LSR) & LSR_THRE) == 0 {} | ||
| uart_write_reg(UART_THR, b); | ||
| } | ||
| } | ||
|
|
||
| fn uart_print(s: &str) { | ||
| uart_lock(); | ||
| for &b in s.as_bytes() { | ||
| uart_write_byte(b); | ||
| } | ||
| uart_unlock(); | ||
| } | ||
|
|
||
| // Custom _mp_hook implementation in assembly | ||
| // Hart 0 returns 1 (true) to initialize RAM | ||
| // Hart 1 polls for IPI via CLINT, then returns 0 (false) to skip RAM init | ||
| global_asm!( | ||
| r#" | ||
| .section .init.mp_hook, "ax" | ||
| .global _mp_hook | ||
| _mp_hook: | ||
| beqz a0, 2f // if hart 0, return true | ||
|
|
||
| // Hart 1: Poll for IPI (no interrupts, just polling) | ||
| // Clear any pending software interrupt first | ||
| li t0, 0x02000004 // CLINT msip address for hart 1 | ||
| sw zero, 0(t0) | ||
|
|
||
| 1: // Poll mip register for software interrupt pending | ||
| csrr t0, mip | ||
| andi t0, t0, 8 // Check MSIP bit | ||
| beqz t0, 1b // If not set, keep polling | ||
|
|
||
| // Clear the software interrupt | ||
| li t0, 0x02000004 | ||
| sw zero, 0(t0) | ||
|
|
||
| // Return false (0) - don't initialize RAM again | ||
| li a0, 0 | ||
| ret | ||
|
|
||
| 2: // Hart 0: return true to initialize RAM | ||
| li a0, 1 | ||
| ret | ||
| "# | ||
| ); | ||
|
|
||
| #[entry] | ||
| fn main(hartid: usize) -> ! { | ||
| if hartid == 0 { | ||
| uart_init(); | ||
| uart_print("Hart 0: Initializing\n"); | ||
|
|
||
| // Send IPI to Hart 1 (write to CLINT msip register for hart 1) | ||
| unsafe { | ||
| (0x02000004usize as *mut u32).write_volatile(1); | ||
| } | ||
|
|
||
| while !HART1_DONE.load(Ordering::Acquire) { | ||
| core::hint::spin_loop(); | ||
| } | ||
|
|
||
| uart_print("Hart 0: Both harts done\n"); | ||
| debug::exit(EXIT_SUCCESS); | ||
| } else { | ||
| // Hart 1 reaches here after _mp_hook detects IPI | ||
| uart_print("Hart 1: Running\n"); | ||
| HART1_DONE.store(true, Ordering::Release); | ||
| } | ||
|
|
||
| loop { | ||
| core::hint::spin_loop(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| MEMORY | ||
| { | ||
| RAM : ORIGIN = 0x80000000, LENGTH = 16M | ||
| } | ||
| REGION_ALIAS("REGION_TEXT", RAM); | ||
| REGION_ALIAS("REGION_RODATA", RAM); | ||
| REGION_ALIAS("REGION_DATA", RAM); | ||
| REGION_ALIAS("REGION_BSS", RAM); | ||
| REGION_ALIAS("REGION_HEAP", RAM); | ||
| REGION_ALIAS("REGION_STACK", RAM); | ||
| _max_hart_id = 1; | ||
| INCLUDE link.x |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| MEMORY | ||
| { | ||
| RAM : ORIGIN = 0x80200000, LENGTH = 16M | ||
| } | ||
| REGION_ALIAS("REGION_TEXT", RAM); | ||
| REGION_ALIAS("REGION_RODATA", RAM); | ||
| REGION_ALIAS("REGION_DATA", RAM); | ||
| REGION_ALIAS("REGION_BSS", RAM); | ||
| REGION_ALIAS("REGION_HEAP", RAM); | ||
| REGION_ALIAS("REGION_STACK", RAM); | ||
| INCLUDE link.x | ||
|
Comment on lines
+1
to
+11
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this file is not required, use
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| #![no_std] |
Uh oh!
There was an error while loading. Please reload this page.