1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-05-17 16:12:39 +02:00

Extend bare metal afternoon exercise (#561)

* Add methods to set match register and check whether it matches.

* Add first extension to RTC exercise, using match register.

* No need for constants to be public.
This commit is contained in:
Andrew Walbran 2023-04-13 10:58:06 +01:00 committed by GitHub
parent 5b316b8b5b
commit d8e442b9cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 51 additions and 10 deletions

View File

@ -25,7 +25,7 @@ use log::error;
use psci::system_off;
/// Base address of the primary PL011 UART.
pub const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _;
const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _;
#[no_mangle]
extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) {

View File

@ -26,7 +26,7 @@ use log::{error, info, LevelFilter};
use psci::system_off;
/// Base address of the primary PL011 UART.
pub const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _;
const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _;
#[no_mangle]
extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) {

View File

@ -25,7 +25,7 @@ use log::error;
use psci::system_off;
/// Base address of the primary PL011 UART.
pub const PL011_BASE_ADDRESS: *mut u8 = 0x900_0000 as _;
const PL011_BASE_ADDRESS: *mut u8 = 0x900_0000 as _;
#[no_mangle]
extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) {

View File

@ -1,8 +1,12 @@
# RTC driver
The QEMU aarch64 virt machine has a [PL031][1] real-time clock at 0x9010000. For this exercise, you
should write a driver for it and use it to print the current time to the serial console. You can use
the [`chrono`][2] crate for date/time formatting.
should write a driver for it.
1. Use it to print the current time to the serial console. You can use the [`chrono`][2] crate for
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.)
Download the [exercise template](../../comprehensive-rust-exercises.zip) and look in the `rtc`
directory for the following files.
@ -20,6 +24,8 @@ directory for the following files.
// TODO: Initialise RTC and print value.
// TODO: Wait for 3 seconds.
{{#include rtc/src/main.rs:main_end}}
```
@ -115,3 +121,4 @@ Run the code in QEMU with `make qemu`.
[1]: https://developer.arm.com/documentation/ddi0224/c
[2]: https://crates.io/crates/chrono
[3]: https://doc.rust-lang.org/core/hint/fn.spin_loop.html

View File

@ -24,6 +24,7 @@ mod pl031;
use crate::pl031::Rtc;
use chrono::{TimeZone, Utc};
use core::hint::spin_loop;
// ANCHOR: imports
use crate::pl011::Uart;
use core::panic::PanicInfo;
@ -31,11 +32,11 @@ use log::{error, info, LevelFilter};
use psci::system_off;
/// Base address of the primary PL011 UART.
pub const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _;
const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _;
// ANCHOR_END: imports
/// Base address of the PL031 RTC.
pub const PL031_BASE_ADDRESS: *mut u32 = 0x901_0000 as _;
const PL031_BASE_ADDRESS: *mut u32 = 0x901_0000 as _;
// ANCHOR: main
#[no_mangle]
@ -50,10 +51,23 @@ extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) {
// Safe because `PL031_BASE_ADDRESS` is the base address of a PL031 device,
// and nothing else accesses that address range.
let rtc = unsafe { Rtc::new(PL031_BASE_ADDRESS) };
let time = Utc.timestamp_opt(rtc.read().into(), 0).unwrap();
let mut rtc = unsafe { Rtc::new(PL031_BASE_ADDRESS) };
let timestamp = rtc.read();
let time = Utc.timestamp_opt(timestamp.into(), 0).unwrap();
info!("RTC: {time}");
// Wait for 3 seconds, without interrupts.
let target = timestamp + 3;
rtc.set_match(target);
info!(
"Waiting for {}",
Utc.timestamp_opt(target.into(), 0).unwrap()
);
while !rtc.matched() {
spin_loop();
}
info!("Finished waiting");
// ANCHOR: main_end
system_off().unwrap();
}

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use core::ptr::addr_of;
use core::ptr::{addr_of, addr_of_mut};
#[repr(C, align(4))]
struct Registers {
@ -39,6 +39,7 @@ struct Registers {
_reserved4: [u8; 3],
}
/// Driver for a PL031 real-time clock.
#[derive(Debug)]
pub struct Rtc {
registers: *mut Registers,
@ -61,8 +62,27 @@ impl Rtc {
/// Reads the current RTC value.
pub fn read(&self) -> u32 {
// Safe because we know that self.registers points to the control
// registers of a PL031 device which is appropriately mapped.
unsafe { addr_of!((*self.registers).dr).read_volatile() }
}
/// Writes a match value. When the RTC value matches this then an interrupt
/// will be generated (if it is enabled).
pub fn set_match(&mut self, value: u32) {
// 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).mr).write_volatile(value) }
}
/// Returns whether the match register matches the RTC value, whether or not
/// the interrupt is enabled.
pub fn matched(&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).ris).read_volatile() };
(ris & 0x01) != 0
}
}
// Safe because it just contains a pointer to device memory, which can be