mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-05-16 23:55:42 +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:
parent
5b316b8b5b
commit
d8e442b9cb
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user