mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-05-17 16:12:39 +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.
|
date/time formatting.
|
||||||
2. Use the match register and raw interrupt status to busy-wait until a given time, e.g. 3 seconds
|
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.)
|
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`
|
Download the [exercise template](../../comprehensive-rust-exercises.zip) and look in the `rtc`
|
||||||
directory for the following files.
|
directory for the following files.
|
||||||
@ -29,7 +31,7 @@ directory for the following files.
|
|||||||
{{#include rtc/src/main.rs:main_end}}
|
{{#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 -->
|
<!-- 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.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
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]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@ -10,9 +19,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.0.2"
|
version = "2.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1"
|
checksum = "c70beb79cbb5ce9c4f8e20849978f34225931f665bb49efa6982875a4d5facb3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
@ -84,6 +93,7 @@ checksum = "3374e3ae47f134467227a48be93b929e5d304efcd25ce5d176006403ca1d9bab"
|
|||||||
name = "rtc"
|
name = "rtc"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"arm-gic",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cc",
|
"cc",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
@ -7,6 +7,7 @@ edition = "2021"
|
|||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
arm-gic = "0.1.0"
|
||||||
bitflags = "2.0.0"
|
bitflags = "2.0.0"
|
||||||
chrono = { version = "0.4.24", default-features = false }
|
chrono = { version = "0.4.24", default-features = false }
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
|
@ -31,7 +31,7 @@ rtc.bin: build
|
|||||||
$(OBJCOPY) -O binary target/aarch64-unknown-none/debug/rtc $@
|
$(OBJCOPY) -O binary target/aarch64-unknown-none/debug/rtc $@
|
||||||
|
|
||||||
qemu: rtc.bin
|
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:
|
clean:
|
||||||
cargo clean
|
cargo clean
|
||||||
|
@ -12,7 +12,8 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use log::error;
|
use arm_gic::gicv3::GicV3;
|
||||||
|
use log::{error, info, trace};
|
||||||
use psci::system_off;
|
use psci::system_off;
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@ -23,8 +24,9 @@ extern "C" fn sync_exception_current(_elr: u64, _spsr: u64) {
|
|||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" fn irq_current(_elr: u64, _spsr: u64) {
|
extern "C" fn irq_current(_elr: u64, _spsr: u64) {
|
||||||
error!("irq_current");
|
trace!("irq_current");
|
||||||
system_off().unwrap();
|
let intid = GicV3::get_and_acknowledge_interrupt().expect("No pending interrupt");
|
||||||
|
info!("IRQ {intid:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -23,20 +23,29 @@ mod pl011;
|
|||||||
mod pl031;
|
mod pl031;
|
||||||
|
|
||||||
use crate::pl031::Rtc;
|
use crate::pl031::Rtc;
|
||||||
|
use arm_gic::gicv3::{IntId, Trigger};
|
||||||
|
use arm_gic::{irq_enable, wfi};
|
||||||
use chrono::{TimeZone, Utc};
|
use chrono::{TimeZone, Utc};
|
||||||
use core::hint::spin_loop;
|
use core::hint::spin_loop;
|
||||||
// ANCHOR: imports
|
// ANCHOR: imports
|
||||||
use crate::pl011::Uart;
|
use crate::pl011::Uart;
|
||||||
|
use arm_gic::gicv3::GicV3;
|
||||||
use core::panic::PanicInfo;
|
use core::panic::PanicInfo;
|
||||||
use log::{error, info, LevelFilter};
|
use log::{error, info, trace, LevelFilter};
|
||||||
use psci::system_off;
|
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.
|
/// Base address of the primary PL011 UART.
|
||||||
const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _;
|
const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _;
|
||||||
// ANCHOR_END: imports
|
// ANCHOR_END: imports
|
||||||
|
|
||||||
/// Base address of the PL031 RTC.
|
/// Base address of the PL031 RTC.
|
||||||
const PL031_BASE_ADDRESS: *mut u32 = 0x901_0000 as _;
|
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
|
// ANCHOR: main
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@ -47,6 +56,12 @@ extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) {
|
|||||||
logger::init(uart, LevelFilter::Trace).unwrap();
|
logger::init(uart, LevelFilter::Trace).unwrap();
|
||||||
|
|
||||||
info!("main({:#x}, {:#x}, {:#x}, {:#x})", x0, x1, x2, x3);
|
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
|
// ANCHOR_END: main
|
||||||
|
|
||||||
// Safe because `PL031_BASE_ADDRESS` is the base address of a PL031 device,
|
// 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();
|
let time = Utc.timestamp_opt(timestamp.into(), 0).unwrap();
|
||||||
info!("RTC: {time}");
|
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.
|
// Wait for 3 seconds, without interrupts.
|
||||||
let target = timestamp + 3;
|
let target = timestamp + 3;
|
||||||
rtc.set_match(target);
|
rtc.set_match(target);
|
||||||
@ -63,9 +84,43 @@ extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) {
|
|||||||
"Waiting for {}",
|
"Waiting for {}",
|
||||||
Utc.timestamp_opt(target.into(), 0).unwrap()
|
Utc.timestamp_opt(target.into(), 0).unwrap()
|
||||||
);
|
);
|
||||||
|
trace!(
|
||||||
|
"matched={}, interrupt_pending={}",
|
||||||
|
rtc.matched(),
|
||||||
|
rtc.interrupt_pending()
|
||||||
|
);
|
||||||
while !rtc.matched() {
|
while !rtc.matched() {
|
||||||
spin_loop();
|
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");
|
info!("Finished waiting");
|
||||||
|
|
||||||
// ANCHOR: main_end
|
// ANCHOR: main_end
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
use core::fmt::{self, Write};
|
use core::fmt::{self, Write};
|
||||||
use core::ptr::{addr_of, addr_of_mut};
|
use core::ptr::{addr_of, addr_of_mut};
|
||||||
|
|
||||||
|
@ -83,6 +83,35 @@ impl Rtc {
|
|||||||
let ris = unsafe { addr_of!((*self.registers).ris).read_volatile() };
|
let ris = unsafe { addr_of!((*self.registers).ris).read_volatile() };
|
||||||
(ris & 0x01) != 0
|
(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
|
// Safe because it just contains a pointer to device memory, which can be
|
||||||
|
Loading…
x
Reference in New Issue
Block a user