mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-03-30 01:16:17 +02:00
Add optional extension to RTC exercise (#562)
* Add methods to mask and clear interrupts. * Start on GICv3 driver. * Enable and use RTC interrupt. * WFI * Add newtype for interrupt ID. * Add extension to use interrupt. * Add method to send an SGI. * Silence warnings about unused methods in provided drivers. * Implement Debug manually for IntId. It's useful to say what kind of interrupt it is. * Acknowledge and log interrupt. We should end it as well, but doing so results in a loop unless we first clear the match. * cargo fmt with imports_granularity = "module" * Use arm-gic crate rather than including driver in the example.
This commit is contained in:
parent
d143528e07
commit
c5863f6642
@ -7,6 +7,8 @@ should write a driver for it.
|
||||
date/time formatting.
|
||||
2. Use the match register and raw interrupt status to busy-wait until a given time, e.g. 3 seconds
|
||||
in the future. (Call [`core::hint::spin_loop`][3] inside the loop.)
|
||||
3. _Extension if you have time:_ Enable and handle the interrupt generated by the RTC match. You can
|
||||
use the driver provided in the `arm-gic` crate to configure the Arm Generic Interrupt Controller.
|
||||
|
||||
Download the [exercise template](../../comprehensive-rust-exercises.zip) and look in the `rtc`
|
||||
directory for the following files.
|
||||
@ -29,7 +31,7 @@ directory for the following files.
|
||||
{{#include rtc/src/main.rs:main_end}}
|
||||
```
|
||||
|
||||
`src/exceptions.rs` (you shouldn't need to change this):
|
||||
`src/exceptions.rs` (you should only need to change this for the 3rd part of the exercise):
|
||||
|
||||
<!-- File src/exceptions.rs -->
|
||||
|
||||
|
14
src/exercises/bare-metal/rtc/Cargo.lock
generated
14
src/exercises/bare-metal/rtc/Cargo.lock
generated
@ -2,6 +2,15 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "arm-gic"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d16805d71f41d1714a51ee34f40633c42980a2afaf8ae237bcba09d6e15494ef"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
@ -10,9 +19,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.0.2"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1"
|
||||
checksum = "c70beb79cbb5ce9c4f8e20849978f34225931f665bb49efa6982875a4d5facb3"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
@ -84,6 +93,7 @@ checksum = "3374e3ae47f134467227a48be93b929e5d304efcd25ce5d176006403ca1d9bab"
|
||||
name = "rtc"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arm-gic",
|
||||
"bitflags",
|
||||
"cc",
|
||||
"chrono",
|
||||
|
@ -7,6 +7,7 @@ edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
arm-gic = "0.1.0"
|
||||
bitflags = "2.0.0"
|
||||
chrono = { version = "0.4.24", default-features = false }
|
||||
log = "0.4.17"
|
||||
|
@ -31,7 +31,7 @@ rtc.bin: build
|
||||
$(OBJCOPY) -O binary target/aarch64-unknown-none/debug/rtc $@
|
||||
|
||||
qemu: rtc.bin
|
||||
qemu-system-aarch64 -machine virt -cpu max -serial mon:stdio -display none -kernel $< -s
|
||||
qemu-system-aarch64 -machine virt,gic_version=3 -cpu max -serial mon:stdio -display none -kernel $< -s
|
||||
|
||||
clean:
|
||||
cargo clean
|
||||
|
@ -12,7 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use log::error;
|
||||
use arm_gic::gicv3::GicV3;
|
||||
use log::{error, info, trace};
|
||||
use psci::system_off;
|
||||
|
||||
#[no_mangle]
|
||||
@ -23,8 +24,9 @@ extern "C" fn sync_exception_current(_elr: u64, _spsr: u64) {
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn irq_current(_elr: u64, _spsr: u64) {
|
||||
error!("irq_current");
|
||||
system_off().unwrap();
|
||||
trace!("irq_current");
|
||||
let intid = GicV3::get_and_acknowledge_interrupt().expect("No pending interrupt");
|
||||
info!("IRQ {intid:?}");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -23,20 +23,29 @@ mod pl011;
|
||||
mod pl031;
|
||||
|
||||
use crate::pl031::Rtc;
|
||||
use arm_gic::gicv3::{IntId, Trigger};
|
||||
use arm_gic::{irq_enable, wfi};
|
||||
use chrono::{TimeZone, Utc};
|
||||
use core::hint::spin_loop;
|
||||
// ANCHOR: imports
|
||||
use crate::pl011::Uart;
|
||||
use arm_gic::gicv3::GicV3;
|
||||
use core::panic::PanicInfo;
|
||||
use log::{error, info, LevelFilter};
|
||||
use log::{error, info, trace, LevelFilter};
|
||||
use psci::system_off;
|
||||
|
||||
/// Base addresses of the GICv3.
|
||||
const GICD_BASE_ADDRESS: *mut u64 = 0x800_0000 as _;
|
||||
const GICR_BASE_ADDRESS: *mut u64 = 0x80A_0000 as _;
|
||||
|
||||
/// Base address of the primary PL011 UART.
|
||||
const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _;
|
||||
// ANCHOR_END: imports
|
||||
|
||||
/// Base address of the PL031 RTC.
|
||||
const PL031_BASE_ADDRESS: *mut u32 = 0x901_0000 as _;
|
||||
/// The IRQ used by the PL031 RTC.
|
||||
const PL031_IRQ: IntId = IntId::spi(2);
|
||||
|
||||
// ANCHOR: main
|
||||
#[no_mangle]
|
||||
@ -47,6 +56,12 @@ extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) {
|
||||
logger::init(uart, LevelFilter::Trace).unwrap();
|
||||
|
||||
info!("main({:#x}, {:#x}, {:#x}, {:#x})", x0, x1, x2, x3);
|
||||
|
||||
// Safe because `GICD_BASE_ADDRESS` and `GICR_BASE_ADDRESS` are the base
|
||||
// addresses of a GICv3 distributor and redistributor respectively, and
|
||||
// nothing else accesses those address ranges.
|
||||
let mut gic = unsafe { GicV3::new(GICD_BASE_ADDRESS, GICR_BASE_ADDRESS) };
|
||||
gic.setup();
|
||||
// ANCHOR_END: main
|
||||
|
||||
// Safe because `PL031_BASE_ADDRESS` is the base address of a PL031 device,
|
||||
@ -56,6 +71,12 @@ extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) {
|
||||
let time = Utc.timestamp_opt(timestamp.into(), 0).unwrap();
|
||||
info!("RTC: {time}");
|
||||
|
||||
GicV3::set_priority_mask(0xff);
|
||||
gic.set_interrupt_priority(PL031_IRQ, 0x80);
|
||||
gic.set_trigger(PL031_IRQ, Trigger::Level);
|
||||
irq_enable();
|
||||
gic.enable_interrupt(PL031_IRQ, true);
|
||||
|
||||
// Wait for 3 seconds, without interrupts.
|
||||
let target = timestamp + 3;
|
||||
rtc.set_match(target);
|
||||
@ -63,9 +84,43 @@ extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) {
|
||||
"Waiting for {}",
|
||||
Utc.timestamp_opt(target.into(), 0).unwrap()
|
||||
);
|
||||
trace!(
|
||||
"matched={}, interrupt_pending={}",
|
||||
rtc.matched(),
|
||||
rtc.interrupt_pending()
|
||||
);
|
||||
while !rtc.matched() {
|
||||
spin_loop();
|
||||
}
|
||||
trace!(
|
||||
"matched={}, interrupt_pending={}",
|
||||
rtc.matched(),
|
||||
rtc.interrupt_pending()
|
||||
);
|
||||
info!("Finished waiting");
|
||||
|
||||
// Wait another 3 seconds for an interrupt.
|
||||
let target = timestamp + 6;
|
||||
info!(
|
||||
"Waiting for {}",
|
||||
Utc.timestamp_opt(target.into(), 0).unwrap()
|
||||
);
|
||||
rtc.set_match(target);
|
||||
rtc.clear_interrupt();
|
||||
rtc.enable_interrupt(true);
|
||||
trace!(
|
||||
"matched={}, interrupt_pending={}",
|
||||
rtc.matched(),
|
||||
rtc.interrupt_pending()
|
||||
);
|
||||
while !rtc.interrupt_pending() {
|
||||
wfi();
|
||||
}
|
||||
trace!(
|
||||
"matched={}, interrupt_pending={}",
|
||||
rtc.matched(),
|
||||
rtc.interrupt_pending()
|
||||
);
|
||||
info!("Finished waiting");
|
||||
|
||||
// ANCHOR: main_end
|
||||
|
@ -12,6 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{self, Write};
|
||||
use core::ptr::{addr_of, addr_of_mut};
|
||||
|
||||
|
@ -83,6 +83,35 @@ impl Rtc {
|
||||
let ris = unsafe { addr_of!((*self.registers).ris).read_volatile() };
|
||||
(ris & 0x01) != 0
|
||||
}
|
||||
|
||||
/// Returns whether there is currently an interrupt pending.
|
||||
///
|
||||
/// This should be true iff `matched` returns true and the interrupt is
|
||||
/// masked.
|
||||
pub fn interrupt_pending(&self) -> bool {
|
||||
// Safe because we know that self.registers points to the control
|
||||
// registers of a PL031 device which is appropriately mapped.
|
||||
let ris = unsafe { addr_of!((*self.registers).mis).read_volatile() };
|
||||
(ris & 0x01) != 0
|
||||
}
|
||||
|
||||
/// Sets or clears the interrupt mask.
|
||||
///
|
||||
/// When the mask is true the interrupt is enabled; when it is false the
|
||||
/// interrupt is disabled.
|
||||
pub fn enable_interrupt(&mut self, mask: bool) {
|
||||
let imsc = if mask { 0x01 } else { 0x00 };
|
||||
// Safe because we know that self.registers points to the control
|
||||
// registers of a PL031 device which is appropriately mapped.
|
||||
unsafe { addr_of_mut!((*self.registers).imsc).write_volatile(imsc) }
|
||||
}
|
||||
|
||||
/// Clears a pending interrupt, if any.
|
||||
pub fn clear_interrupt(&mut self) {
|
||||
// Safe because we know that self.registers points to the control
|
||||
// registers of a PL031 device which is appropriately mapped.
|
||||
unsafe { addr_of_mut!((*self.registers).icr).write_volatile(0x01) }
|
||||
}
|
||||
}
|
||||
|
||||
// Safe because it just contains a pointer to device memory, which can be
|
||||
|
Loading…
x
Reference in New Issue
Block a user