Rewrite for register-rs.

We now have the same API for MMIO and CPU registers.
Makes the code more concise, inntuitive, and improves readability.

https://crates.io/crates/register
pull/4/head
Andre Richter 6 years ago
parent ea39d2ae1f
commit 747e902761
No known key found for this signature in database
GPG Key ID: 2116C1AB102F615E

@ -30,8 +30,6 @@
extern crate panic_abort; extern crate panic_abort;
extern crate r0; extern crate r0;
use core::ptr;
#[lang = "start"] #[lang = "start"]
extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize
where where
@ -53,6 +51,8 @@ impl Termination for () {
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn reset() -> ! { pub unsafe extern "C" fn reset() -> ! {
use core::ptr;
extern "C" { extern "C" {
fn main(argc: isize, argv: *const *const u8) -> isize; fn main(argc: isize, argv: *const *const u8) -> isize;

20
03_uart1/Cargo.lock generated

@ -3,7 +3,7 @@ name = "kernel8"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"raspi3_glue 0.1.0", "raspi3_glue 0.1.0",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -25,20 +25,20 @@ dependencies = [
] ]
[[package]] [[package]]
name = "vcell" name = "register"
version = "0.1.0" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "volatile-register" name = "tock-registers"
version = "0.2.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata] [metadata]
"checksum panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6bc796c620f27056d4ffe7c558533fd67ae5af0fd8e919fbe38de803368af73e" "checksum panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6bc796c620f27056d4ffe7c558533fd67ae5af0fd8e919fbe38de803368af73e"
"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" "checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
"checksum vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d" "checksum register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e985243ba7e1c336b62444ef2a10d7bd87cf41a222285ae3de605c859006479"
"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" "checksum tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2acc33f980e23cee18d234a32d0637fbc1ea55e13ab04012fa857b899fa1b7a9"

@ -5,4 +5,4 @@ authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies] [dependencies]
raspi3_glue = { path = "raspi3_glue" } raspi3_glue = { path = "raspi3_glue" }
volatile-register = "0.2.0" register = "0.1.1"

@ -7,15 +7,14 @@ NOTE: qemu does not redirect UART1 to terminal by default, only UART0!
## gpio.rs ## gpio.rs
We have a new header file. This defines the base MMIO address, and the GPIO We have a new file that defines the GPIO controller addresses. It is going to be
controller's addresses. This file going to be very popular, as many device needs very popular, as many device will need it in the future.
it.
We are using the [volatile_register] crate to modify MMIO addresses, because it We are using the [register][register] crate to modify MMIO addresses, because it
allows easy wrapping of addresses to volatile types. It will also be used for allows easy wrapping of addresses to volatile types. It will also be used for
UART registers. UART registers.
[volatile_register]: https://docs.rs/volatile-register/0.2.0/volatile_register/ [register]: https://crates.io/crates/register
## uart.rs ## uart.rs

Binary file not shown.

@ -30,8 +30,6 @@
extern crate panic_abort; extern crate panic_abort;
extern crate r0; extern crate r0;
use core::ptr;
#[lang = "start"] #[lang = "start"]
extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize
where where
@ -53,6 +51,8 @@ impl Termination for () {
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn reset() -> ! { pub unsafe extern "C" fn reset() -> ! {
use core::ptr;
extern "C" { extern "C" {
fn main(argc: isize, argv: *const *const u8) -> isize; fn main(argc: isize, argv: *const *const u8) -> isize;

@ -23,8 +23,52 @@
*/ */
use super::MMIO_BASE; use super::MMIO_BASE;
use volatile_register::RW; use register::mmio::ReadWrite;
pub const GPFSEL1: *const RW<u32> = (MMIO_BASE + 0x0020_0004) as *const RW<u32>; // Descriptions taken from
pub const GPPUD: *const RW<u32> = (MMIO_BASE + 0x0020_0094) as *const RW<u32>; // https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
pub const GPPUDCLK0: *const RW<u32> = (MMIO_BASE + 0x0020_0098) as *const RW<u32>; register_bitfields! {
u32,
/// GPIO Function Select 1
GPFSEL1 [
/// Pin 15
FSEL15 OFFSET(15) NUMBITS(3) [
Input = 0b000,
Output = 0b001,
RXD1 = 0b010 // Mini UART - Alternate function 5
],
/// Pin 14
FSEL14 OFFSET(12) NUMBITS(3) [
Input = 0b000,
Output = 0b001,
TXD1 = 0b010 // Mini UART - Alternate function 5
]
],
/// GPIO Pull-up/down Clock Register 0
GPPUDCLK0 [
/// Pin 15
PUDCLK15 OFFSET(15) NUMBITS(1) [
NoEffect = 0,
AssertClock = 1
],
/// Pin 14
PUDCLK14 OFFSET(14) NUMBITS(1) [
NoEffect = 0,
AssertClock = 1
]
]
}
pub const GPFSEL1: *const ReadWrite<u32, GPFSEL1::Register> =
(MMIO_BASE + 0x0020_0004) as *const ReadWrite<u32, GPFSEL1::Register>;
pub const GPPUD: *const ReadWrite<u32> = (MMIO_BASE + 0x0020_0094) as *const ReadWrite<u32>;
pub const GPPUDCLK0: *const ReadWrite<u32, GPPUDCLK0::Register> =
(MMIO_BASE + 0x0020_0098) as *const ReadWrite<u32, GPPUDCLK0::Register>;

@ -26,7 +26,9 @@
#![feature(asm)] #![feature(asm)]
extern crate raspi3_glue; extern crate raspi3_glue;
extern crate volatile_register;
#[macro_use]
extern crate register;
const MMIO_BASE: u32 = 0x3F00_0000; const MMIO_BASE: u32 = 0x3F00_0000;

@ -25,28 +25,98 @@
use super::MMIO_BASE; use super::MMIO_BASE;
use core::ops; use core::ops;
use gpio; use gpio;
use volatile_register::*; use register::mmio::*;
/// Auxilary mini UART registers
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
register_bitfields! {
u32,
/// Auxiliary enables
AUX_ENABLES [
/// If set the mini UART is enabled. The UART will immediately
/// start receiving data, especially if the UART1_RX line is
/// low.
/// If clear the mini UART is disabled. That also disables any
/// mini UART register access
MINI_UART_ENABLE OFFSET(0) NUMBITS(1) []
],
/// Mini Uart Interrupt Identify
AUX_MU_IIR [
/// Writing with bit 1 set will clear the receive FIFO
/// Writing with bit 2 set will clear the transmit FIFO
FIFO_CLEAR OFFSET(1) NUMBITS(2) [
Rx = 0b01,
Tx = 0b10,
All = 0b11
]
],
/// Mini Uart Line Control
AUX_MU_LCR [
/// Mode the UART works in
DATA_SIZE OFFSET(0) NUMBITS(2) [
SevenBit = 0b00,
EightBit = 0b11
]
],
/// Mini Uart Line Status
AUX_MU_LSR [
/// This bit is set if the transmit FIFO can accept at least
/// one byte.
TX_EMPTY OFFSET(5) NUMBITS(1) [],
/// This bit is set if the receive FIFO holds at least 1
/// symbol.
DATA_READY OFFSET(0) NUMBITS(1) []
],
/// Mini Uart Extra Control
AUX_MU_CNTL [
/// If this bit is set the mini UART transmitter is enabled.
/// If this bit is clear the mini UART transmitter is disabled.
TX_EN OFFSET(1) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// If this bit is set the mini UART receiver is enabled.
/// If this bit is clear the mini UART receiver is disabled.
RX_EN OFFSET(0) NUMBITS(1) [
Disabled = 0,
Enabled = 1
]
],
/// Mini Uart Baudrate
AUX_MU_BAUD [
/// Mini UART baudrate counter
RATE OFFSET(0) NUMBITS(16) []
]
}
const MINI_UART_BASE: u32 = MMIO_BASE + 0x21_5000; const MINI_UART_BASE: u32 = MMIO_BASE + 0x21_5000;
/// Auxilary mini UART registers
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[repr(C)] #[repr(C)]
pub struct RegisterBlock { pub struct RegisterBlock {
__reserved_0: u32, // 0x00 __reserved_0: u32, // 0x00
ENABLES: RW<u32>, // 0x04 AUX_ENABLES: ReadWrite<u32, AUX_ENABLES::Register>, // 0x04
__reserved_1: [u32; 14], // 0x08 __reserved_1: [u32; 14], // 0x08
MU_IO: RW<u32>, // 0x40 AUX_MU_IO: ReadWrite<u32>, // 0x40 - Mini Uart I/O Data
MU_IER: RW<u32>, // 0x44 AUX_MU_IER: WriteOnly<u32>, // 0x44 - Mini Uart Interrupt Enable
MU_IIR: RW<u32>, // 0x48 AUX_MU_IIR: WriteOnly<u32, AUX_MU_IIR::Register>, // 0x48
MU_LCR: RW<u32>, // 0x4C AUX_MU_LCR: WriteOnly<u32, AUX_MU_LCR::Register>, // 0x4C
MU_MCR: RW<u32>, // 0x50 AUX_MU_MCR: WriteOnly<u32>, // 0x50
MU_LSR: RW<u32>, // 0x54 AUX_MU_LSR: ReadOnly<u32, AUX_MU_LSR::Register>, // 0x54
MU_MSR: RW<u32>, // 0x58 __reserved_2: [u32; 2], // 0x58
MU_SCRATCH: RW<u32>, // 0x5C AUX_MU_CNTL: WriteOnly<u32, AUX_MU_CNTL::Register>, // 0x60
MU_CNTL: RW<u32>, // 0x60 __reserved_3: u32, // 0x64
MU_STAT: RW<u32>, // 0x64 AUX_MU_BAUD: WriteOnly<u32, AUX_MU_BAUD::Register>, // 0x68
MU_BAUD: RW<u32>, // 0x68
} }
pub struct MiniUart; pub struct MiniUart;
@ -82,45 +152,43 @@ impl MiniUart {
///Set baud rate and characteristics (115200 8N1) and map to GPIO ///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self) { pub fn init(&self) {
// initialize UART // initialize UART
self.AUX_ENABLES.modify(AUX_ENABLES::MINI_UART_ENABLE::SET);
self.AUX_MU_IER.set(0);
self.AUX_MU_CNTL.set(0);
self.AUX_MU_LCR.write(AUX_MU_LCR::DATA_SIZE::EightBit);
self.AUX_MU_MCR.set(0);
self.AUX_MU_IER.set(0);
self.AUX_MU_IIR.write(AUX_MU_IIR::FIFO_CLEAR::All);
self.AUX_MU_BAUD.write(AUX_MU_BAUD::RATE.val(270)); // 115200 baud
// map UART1 to GPIO pins
unsafe { unsafe {
self.ENABLES.modify(|x| x | 1); // enable UART1, AUX mini uart (*gpio::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD1 + gpio::GPFSEL1::FSEL15::RXD1);
self.MU_IER.write(0);
self.MU_CNTL.write(0); (*gpio::GPPUD).set(0); // enable pins 14 and 15
self.MU_LCR.write(3); // 8 bits
self.MU_MCR.write(0);
self.MU_IER.write(0);
self.MU_IIR.write(0xC6); // disable interrupts
self.MU_BAUD.write(270); // 115200 baud
// map UART1 to GPIO pins
(*gpio::GPFSEL1).modify(|x| {
// Modify with a closure
let mut ret = x;
ret &= !((7 << 12) | (7 << 15)); // gpio14, gpio15
ret |= (2 << 12) | (2 << 15); // alt5
ret
});
(*gpio::GPPUD).write(0); // enable pins 14 and 15
for _ in 0..150 { for _ in 0..150 {
asm!("nop" :::: "volatile"); asm!("nop" :::: "volatile");
} }
(*gpio::GPPUDCLK0).write((1 << 14) | (1 << 15)); (*gpio::GPPUDCLK0).write(
gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock,
);
for _ in 0..150 { for _ in 0..150 {
asm!("nop" :::: "volatile"); asm!("nop" :::: "volatile");
} }
(*gpio::GPPUDCLK0).write(0); // flush GPIO setup
self.MU_CNTL.write(3); // enable Tx, Rx (*gpio::GPPUDCLK0).set(0);
} }
self.AUX_MU_CNTL
.write(AUX_MU_CNTL::RX_EN::Enabled + AUX_MU_CNTL::TX_EN::Enabled);
} }
/// Send a character /// Send a character
pub fn send(&self, c: char) { pub fn send(&self, c: char) {
// wait until we can send // wait until we can send
loop { loop {
if (self.MU_LSR.read() & 0x20) == 0x20 { if self.AUX_MU_LSR.is_set(AUX_MU_LSR::TX_EMPTY) {
break; break;
} }
@ -128,14 +196,14 @@ impl MiniUart {
} }
// write the character to the buffer // write the character to the buffer
unsafe { self.MU_IO.write(c as u32) }; self.AUX_MU_IO.set(c as u32);
} }
/// Receive a character /// Receive a character
pub fn getc(&self) -> char { pub fn getc(&self) -> char {
// wait until something is in the buffer // wait until something is in the buffer
loop { loop {
if (self.MU_LSR.read() & 0x01) == 0x01 { if self.AUX_MU_LSR.is_set(AUX_MU_LSR::DATA_READY) {
break; break;
} }
@ -143,7 +211,7 @@ impl MiniUart {
} }
// read it and return // read it and return
let mut ret = self.MU_IO.read() as u8 as char; let mut ret = self.AUX_MU_IO.get() as u8 as char;
// convert carrige return to newline // convert carrige return to newline
if ret == '\r' { if ret == '\r' {

@ -3,7 +3,7 @@ name = "kernel8"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"raspi3_glue 0.1.0", "raspi3_glue 0.1.0",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -25,20 +25,20 @@ dependencies = [
] ]
[[package]] [[package]]
name = "vcell" name = "register"
version = "0.1.0" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "volatile-register" name = "tock-registers"
version = "0.2.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata] [metadata]
"checksum panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6bc796c620f27056d4ffe7c558533fd67ae5af0fd8e919fbe38de803368af73e" "checksum panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6bc796c620f27056d4ffe7c558533fd67ae5af0fd8e919fbe38de803368af73e"
"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" "checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
"checksum vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d" "checksum register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e985243ba7e1c336b62444ef2a10d7bd87cf41a222285ae3de605c859006479"
"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" "checksum tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2acc33f980e23cee18d234a32d0637fbc1ea55e13ab04012fa857b899fa1b7a9"

@ -5,4 +5,4 @@ authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies] [dependencies]
raspi3_glue = { path = "raspi3_glue" } raspi3_glue = { path = "raspi3_glue" }
volatile-register = "0.2.0" register = "0.1.1"

Binary file not shown.

@ -30,8 +30,6 @@
extern crate panic_abort; extern crate panic_abort;
extern crate r0; extern crate r0;
use core::ptr;
#[lang = "start"] #[lang = "start"]
extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize
where where
@ -53,6 +51,8 @@ impl Termination for () {
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn reset() -> ! { pub unsafe extern "C" fn reset() -> ! {
use core::ptr;
extern "C" { extern "C" {
fn main(argc: isize, argv: *const *const u8) -> isize; fn main(argc: isize, argv: *const *const u8) -> isize;

@ -23,8 +23,52 @@
*/ */
use super::MMIO_BASE; use super::MMIO_BASE;
use volatile_register::RW; use register::mmio::ReadWrite;
pub const GPFSEL1: *const RW<u32> = (MMIO_BASE + 0x0020_0004) as *const RW<u32>; // Descriptions taken from
pub const GPPUD: *const RW<u32> = (MMIO_BASE + 0x0020_0094) as *const RW<u32>; // https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
pub const GPPUDCLK0: *const RW<u32> = (MMIO_BASE + 0x0020_0098) as *const RW<u32>; register_bitfields! {
u32,
/// GPIO Function Select 1
GPFSEL1 [
/// Pin 15
FSEL15 OFFSET(15) NUMBITS(3) [
Input = 0b000,
Output = 0b001,
RXD1 = 0b010 // Mini UART - Alternate function 5
],
/// Pin 14
FSEL14 OFFSET(12) NUMBITS(3) [
Input = 0b000,
Output = 0b001,
TXD1 = 0b010 // Mini UART - Alternate function 5
]
],
/// GPIO Pull-up/down Clock Register 0
GPPUDCLK0 [
/// Pin 15
PUDCLK15 OFFSET(15) NUMBITS(1) [
NoEffect = 0,
AssertClock = 1
],
/// Pin 14
PUDCLK14 OFFSET(14) NUMBITS(1) [
NoEffect = 0,
AssertClock = 1
]
]
}
pub const GPFSEL1: *const ReadWrite<u32, GPFSEL1::Register> =
(MMIO_BASE + 0x0020_0004) as *const ReadWrite<u32, GPFSEL1::Register>;
pub const GPPUD: *const ReadWrite<u32> = (MMIO_BASE + 0x0020_0094) as *const ReadWrite<u32>;
pub const GPPUDCLK0: *const ReadWrite<u32, GPPUDCLK0::Register> =
(MMIO_BASE + 0x0020_0098) as *const ReadWrite<u32, GPPUDCLK0::Register>;

@ -26,7 +26,9 @@
#![feature(asm)] #![feature(asm)]
extern crate raspi3_glue; extern crate raspi3_glue;
extern crate volatile_register;
#[macro_use]
extern crate register;
const MMIO_BASE: u32 = 0x3F00_0000; const MMIO_BASE: u32 = 0x3F00_0000;

@ -24,20 +24,27 @@
use super::MMIO_BASE; use super::MMIO_BASE;
use core::ops; use core::ops;
use volatile_register::{RO, WO}; use register::mmio::{ReadOnly, WriteOnly};
register_bitfields! {
u32,
STATUS [
FULL OFFSET(31) NUMBITS(1) [],
EMPTY OFFSET(30) NUMBITS(1) []
]
}
const VIDEOCORE_MBOX: u32 = MMIO_BASE + 0xB880; const VIDEOCORE_MBOX: u32 = MMIO_BASE + 0xB880;
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[repr(C)] #[repr(C)]
pub struct RegisterBlock { pub struct RegisterBlock {
READ: RO<u32>, // 0x00 READ: ReadOnly<u32>, // 0x00
__reserved_0: [u32; 3], // 0x04 __reserved_0: [u32; 5], // 0x04
POLL: RO<u32>, // 0x10 STATUS: ReadOnly<u32, STATUS::Register>, // 0x18
SENDER: RO<u32>, // 0x14 __reserved_1: u32, // 0x1C
STATUS: RO<u32>, // 0x18 WRITE: WriteOnly<u32>, // 0x20
CONFIG: RO<u32>, // 0x1C
WRITE: WO<u32>, // 0x20
} }
// Custom errors // Custom errors
@ -65,17 +72,13 @@ mod response {
} }
pub const REQUEST: u32 = 0; pub const REQUEST: u32 = 0;
const FULL: u32 = 0x8000_0000;
const EMPTY: u32 = 0x4000_0000;
// Public interface to the mailbox // Public interface to the mailbox
#[repr(C)] #[repr(C)]
#[repr(align(16))]
pub struct Mbox { pub struct Mbox {
// The address for buffer needs to be 16-byte aligned so that the // The address for buffer needs to be 16-byte aligned so that the
// Videcore can handle it properly. We don't take precautions here // Videcore can handle it properly.
// to achieve that, but for now it just works. Since alignment of
// data structures in Rust is a bit of a hassle right now, we just
// close our eyes and roll with it.
pub buffer: [u32; 36], pub buffer: [u32; 36],
} }
@ -111,38 +114,33 @@ impl Mbox {
pub fn call(&self, channel: u32) -> Result<()> { pub fn call(&self, channel: u32) -> Result<()> {
// wait until we can write to the mailbox // wait until we can write to the mailbox
loop { loop {
if (self.STATUS.read() & FULL) != FULL { if !self.STATUS.is_set(STATUS::FULL) {
break; break;
} }
unsafe { unsafe { asm!("nop" :::: "volatile") };
asm!("nop" :::: "volatile");
}
} }
let buf_ptr = self.buffer.as_ptr() as u32;
// write the address of our message to the mailbox with channel identifier // write the address of our message to the mailbox with channel identifier
unsafe { self.WRITE.set((buf_ptr & !0xF) | (channel & 0xF));
self.WRITE
.write(((self.buffer.as_ptr() as u32) & !0xF) | (channel & 0xF));
}
// now wait for the response // now wait for the response
loop { loop {
// is there a response? // is there a response?
loop { loop {
if (self.STATUS.read() & EMPTY) != EMPTY { if !self.STATUS.is_set(STATUS::EMPTY) {
break; break;
} }
unsafe { unsafe { asm!("nop" :::: "volatile") };
asm!("nop" :::: "volatile");
}
} }
let resp: u32 = self.READ.read(); let resp: u32 = self.READ.get();
// is it a response to our message? // is it a response to our message?
if ((resp & 0xF) == channel) && ((resp & !0xF) == (self.buffer.as_ptr() as u32)) { if ((resp & 0xF) == channel) && ((resp & !0xF) == buf_ptr) {
// is it a valid successful response? // is it a valid successful response?
return match self.buffer[1] { return match self.buffer[1] {
response::SUCCESS => Ok(()), response::SUCCESS => Ok(()),

@ -25,32 +25,112 @@
use super::MMIO_BASE; use super::MMIO_BASE;
use core::ops; use core::ops;
use gpio; use gpio;
use volatile_register::*; use register::mmio::*;
/// Auxilary mini UART registers
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
register_bitfields! {
u32,
/// Auxiliary enables
AUX_ENABLES [
/// If set the mini UART is enabled. The UART will immediately
/// start receiving data, especially if the UART1_RX line is
/// low.
/// If clear the mini UART is disabled. That also disables any
/// mini UART register access
MINI_UART_ENABLE OFFSET(0) NUMBITS(1) []
],
/// Mini Uart Interrupt Identify
AUX_MU_IIR [
/// Writing with bit 1 set will clear the receive FIFO
/// Writing with bit 2 set will clear the transmit FIFO
FIFO_CLEAR OFFSET(1) NUMBITS(2) [
Rx = 0b01,
Tx = 0b10,
All = 0b11
]
],
/// Mini Uart Line Control
AUX_MU_LCR [
/// Mode the UART works in
DATA_SIZE OFFSET(0) NUMBITS(2) [
SevenBit = 0b00,
EightBit = 0b11
]
],
/// Mini Uart Line Status
AUX_MU_LSR [
/// This bit is set if the transmit FIFO can accept at least
/// one byte.
TX_EMPTY OFFSET(5) NUMBITS(1) [],
/// This bit is set if the receive FIFO holds at least 1
/// symbol.
DATA_READY OFFSET(0) NUMBITS(1) []
],
/// Mini Uart Extra Control
AUX_MU_CNTL [
/// If this bit is set the mini UART transmitter is enabled.
/// If this bit is clear the mini UART transmitter is disabled.
TX_EN OFFSET(1) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// If this bit is set the mini UART receiver is enabled.
/// If this bit is clear the mini UART receiver is disabled.
RX_EN OFFSET(0) NUMBITS(1) [
Disabled = 0,
Enabled = 1
]
],
/// Mini Uart Baudrate
AUX_MU_BAUD [
/// Mini UART baudrate counter
RATE OFFSET(0) NUMBITS(16) []
]
}
const MINI_UART_BASE: u32 = MMIO_BASE + 0x21_5000; const MINI_UART_BASE: u32 = MMIO_BASE + 0x21_5000;
/// Auxilary mini UART registers
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[repr(C)] #[repr(C)]
pub struct RegisterBlock { pub struct RegisterBlock {
__reserved_0: u32, // 0x00 __reserved_0: u32, // 0x00
ENABLES: RW<u32>, // 0x04 AUX_ENABLES: ReadWrite<u32, AUX_ENABLES::Register>, // 0x04
__reserved_1: [u32; 14], // 0x08 __reserved_1: [u32; 14], // 0x08
MU_IO: RW<u32>, // 0x40 AUX_MU_IO: ReadWrite<u32>, // 0x40 - Mini Uart I/O Data
MU_IER: RW<u32>, // 0x44 AUX_MU_IER: WriteOnly<u32>, // 0x44 - Mini Uart Interrupt Enable
MU_IIR: RW<u32>, // 0x48 AUX_MU_IIR: WriteOnly<u32, AUX_MU_IIR::Register>, // 0x48
MU_LCR: RW<u32>, // 0x4C AUX_MU_LCR: WriteOnly<u32, AUX_MU_LCR::Register>, // 0x4C
MU_MCR: RW<u32>, // 0x50 AUX_MU_MCR: WriteOnly<u32>, // 0x50
MU_LSR: RW<u32>, // 0x54 AUX_MU_LSR: ReadOnly<u32, AUX_MU_LSR::Register>, // 0x54
MU_MSR: RW<u32>, // 0x58 __reserved_2: [u32; 2], // 0x58
MU_SCRATCH: RW<u32>, // 0x5C AUX_MU_CNTL: WriteOnly<u32, AUX_MU_CNTL::Register>, // 0x60
MU_CNTL: RW<u32>, // 0x60 __reserved_3: u32, // 0x64
MU_STAT: RW<u32>, // 0x64 AUX_MU_BAUD: WriteOnly<u32, AUX_MU_BAUD::Register>, // 0x68
MU_BAUD: RW<u32>, // 0x68
} }
pub struct MiniUart; pub struct MiniUart;
/// Deref to RegisterBlock
///
/// Allows writing
/// ```
/// self.MU_IER.read()
/// ```
/// instead of something along the lines of
/// ```
/// unsafe { (*MiniUart::ptr()).MU_IER.read() }
/// ```
impl ops::Deref for MiniUart { impl ops::Deref for MiniUart {
type Target = RegisterBlock; type Target = RegisterBlock;
@ -72,45 +152,43 @@ impl MiniUart {
///Set baud rate and characteristics (115200 8N1) and map to GPIO ///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self) { pub fn init(&self) {
// initialize UART // initialize UART
self.AUX_ENABLES.modify(AUX_ENABLES::MINI_UART_ENABLE::SET);
self.AUX_MU_IER.set(0);
self.AUX_MU_CNTL.set(0);
self.AUX_MU_LCR.write(AUX_MU_LCR::DATA_SIZE::EightBit);
self.AUX_MU_MCR.set(0);
self.AUX_MU_IER.set(0);
self.AUX_MU_IIR.write(AUX_MU_IIR::FIFO_CLEAR::All);
self.AUX_MU_BAUD.write(AUX_MU_BAUD::RATE.val(270)); // 115200 baud
// map UART1 to GPIO pins
unsafe { unsafe {
self.ENABLES.modify(|x| x | 1); // enable UART1, AUX mini uart (*gpio::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD1 + gpio::GPFSEL1::FSEL15::RXD1);
self.MU_IER.write(0);
self.MU_CNTL.write(0); (*gpio::GPPUD).set(0); // enable pins 14 and 15
self.MU_LCR.write(3); // 8 bits
self.MU_MCR.write(0);
self.MU_IER.write(0);
self.MU_IIR.write(0xC6); // disable interrupts
self.MU_BAUD.write(270); // 115200 baud
// map UART1 to GPIO pins
(*gpio::GPFSEL1).modify(|x| {
// Modify with a closure
let mut ret = x;
ret &= !((7 << 12) | (7 << 15)); // gpio14, gpio15
ret |= (2 << 12) | (2 << 15); // alt5
ret
});
(*gpio::GPPUD).write(0); // enable pins 14 and 15
for _ in 0..150 { for _ in 0..150 {
asm!("nop" :::: "volatile"); asm!("nop" :::: "volatile");
} }
(*gpio::GPPUDCLK0).write((1 << 14) | (1 << 15)); (*gpio::GPPUDCLK0).write(
gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock,
);
for _ in 0..150 { for _ in 0..150 {
asm!("nop" :::: "volatile"); asm!("nop" :::: "volatile");
} }
(*gpio::GPPUDCLK0).write(0); // flush GPIO setup
self.MU_CNTL.write(3); // enable Tx, Rx (*gpio::GPPUDCLK0).set(0);
} }
self.AUX_MU_CNTL
.write(AUX_MU_CNTL::RX_EN::Enabled + AUX_MU_CNTL::TX_EN::Enabled);
} }
/// Send a character /// Send a character
pub fn send(&self, c: char) { pub fn send(&self, c: char) {
// wait until we can send // wait until we can send
loop { loop {
if (self.MU_LSR.read() & 0x20) == 0x20 { if self.AUX_MU_LSR.is_set(AUX_MU_LSR::TX_EMPTY) {
break; break;
} }
@ -118,14 +196,14 @@ impl MiniUart {
} }
// write the character to the buffer // write the character to the buffer
unsafe { self.MU_IO.write(c as u32) }; self.AUX_MU_IO.set(c as u32);
} }
/// Receive a character /// Receive a character
pub fn getc(&self) -> char { pub fn getc(&self) -> char {
// wait until something is in the buffer // wait until something is in the buffer
loop { loop {
if (self.MU_LSR.read() & 0x01) == 0x01 { if self.AUX_MU_LSR.is_set(AUX_MU_LSR::DATA_READY) {
break; break;
} }
@ -133,7 +211,7 @@ impl MiniUart {
} }
// read it and return // read it and return
let mut ret = self.MU_IO.read() as u8 as char; let mut ret = self.AUX_MU_IO.get() as u8 as char;
// convert carrige return to newline // convert carrige return to newline
if ret == '\r' { if ret == '\r' {

20
05_uart0/Cargo.lock generated

@ -3,7 +3,7 @@ name = "kernel8"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"raspi3_glue 0.1.0", "raspi3_glue 0.1.0",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -25,20 +25,20 @@ dependencies = [
] ]
[[package]] [[package]]
name = "vcell" name = "register"
version = "0.1.0" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "volatile-register" name = "tock-registers"
version = "0.2.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata] [metadata]
"checksum panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6bc796c620f27056d4ffe7c558533fd67ae5af0fd8e919fbe38de803368af73e" "checksum panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6bc796c620f27056d4ffe7c558533fd67ae5af0fd8e919fbe38de803368af73e"
"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" "checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
"checksum vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d" "checksum register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e985243ba7e1c336b62444ef2a10d7bd87cf41a222285ae3de605c859006479"
"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" "checksum tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2acc33f980e23cee18d234a32d0637fbc1ea55e13ab04012fa857b899fa1b7a9"

@ -5,4 +5,4 @@ authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies] [dependencies]
raspi3_glue = { path = "raspi3_glue" } raspi3_glue = { path = "raspi3_glue" }
volatile-register = "0.2.0" register = "0.1.1"

Binary file not shown.

@ -30,8 +30,6 @@
extern crate panic_abort; extern crate panic_abort;
extern crate r0; extern crate r0;
use core::ptr;
#[lang = "start"] #[lang = "start"]
extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize
where where
@ -53,6 +51,8 @@ impl Termination for () {
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn reset() -> ! { pub unsafe extern "C" fn reset() -> ! {
use core::ptr;
extern "C" { extern "C" {
fn main(argc: isize, argv: *const *const u8) -> isize; fn main(argc: isize, argv: *const *const u8) -> isize;

@ -23,8 +23,54 @@
*/ */
use super::MMIO_BASE; use super::MMIO_BASE;
use volatile_register::RW; use register::mmio::ReadWrite;
pub const GPFSEL1: *const RW<u32> = (MMIO_BASE + 0x0020_0004) as *const RW<u32>; // Descriptions taken from
pub const GPPUD: *const RW<u32> = (MMIO_BASE + 0x0020_0094) as *const RW<u32>; // https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
pub const GPPUDCLK0: *const RW<u32> = (MMIO_BASE + 0x0020_0098) as *const RW<u32>; register_bitfields! {
u32,
/// GPIO Function Select 1
GPFSEL1 [
/// Pin 15
FSEL15 OFFSET(15) NUMBITS(3) [
Input = 0b000,
Output = 0b001,
RXD0 = 0b100, // UART0 - Alternate function 0
RXD1 = 0b010 // Mini UART - Alternate function 5
],
/// Pin 14
FSEL14 OFFSET(12) NUMBITS(3) [
Input = 0b000,
Output = 0b001,
TXD0 = 0b100, // UART0 - Alternate function 0
TXD1 = 0b010 // Mini UART - Alternate function 5
]
],
/// GPIO Pull-up/down Clock Register 0
GPPUDCLK0 [
/// Pin 15
PUDCLK15 OFFSET(15) NUMBITS(1) [
NoEffect = 0,
AssertClock = 1
],
/// Pin 14
PUDCLK14 OFFSET(14) NUMBITS(1) [
NoEffect = 0,
AssertClock = 1
]
]
}
pub const GPFSEL1: *const ReadWrite<u32, GPFSEL1::Register> =
(MMIO_BASE + 0x0020_0004) as *const ReadWrite<u32, GPFSEL1::Register>;
pub const GPPUD: *const ReadWrite<u32> = (MMIO_BASE + 0x0020_0094) as *const ReadWrite<u32>;
pub const GPPUDCLK0: *const ReadWrite<u32, GPPUDCLK0::Register> =
(MMIO_BASE + 0x0020_0098) as *const ReadWrite<u32, GPPUDCLK0::Register>;

@ -26,7 +26,9 @@
#![feature(asm)] #![feature(asm)]
extern crate raspi3_glue; extern crate raspi3_glue;
extern crate volatile_register;
#[macro_use]
extern crate register;
const MMIO_BASE: u32 = 0x3F00_0000; const MMIO_BASE: u32 = 0x3F00_0000;

@ -24,20 +24,27 @@
use super::MMIO_BASE; use super::MMIO_BASE;
use core::ops; use core::ops;
use volatile_register::{RO, WO}; use register::mmio::{ReadOnly, WriteOnly};
register_bitfields! {
u32,
STATUS [
FULL OFFSET(31) NUMBITS(1) [],
EMPTY OFFSET(30) NUMBITS(1) []
]
}
const VIDEOCORE_MBOX: u32 = MMIO_BASE + 0xB880; const VIDEOCORE_MBOX: u32 = MMIO_BASE + 0xB880;
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[repr(C)] #[repr(C)]
pub struct RegisterBlock { pub struct RegisterBlock {
READ: RO<u32>, // 0x00 READ: ReadOnly<u32>, // 0x00
__reserved_0: [u32; 3], // 0x04 __reserved_0: [u32; 5], // 0x04
POLL: RO<u32>, // 0x10 STATUS: ReadOnly<u32, STATUS::Register>, // 0x18
SENDER: RO<u32>, // 0x14 __reserved_1: u32, // 0x1C
STATUS: RO<u32>, // 0x18 WRITE: WriteOnly<u32>, // 0x20
CONFIG: RO<u32>, // 0x1C
WRITE: WO<u32>, // 0x20
} }
// Custom errors // Custom errors
@ -71,17 +78,13 @@ mod response {
} }
pub const REQUEST: u32 = 0; pub const REQUEST: u32 = 0;
const FULL: u32 = 0x8000_0000;
const EMPTY: u32 = 0x4000_0000;
// Public interface to the mailbox // Public interface to the mailbox
#[repr(C)] #[repr(C)]
#[repr(align(16))]
pub struct Mbox { pub struct Mbox {
// The address for buffer needs to be 16-byte aligned so that the // The address for buffer needs to be 16-byte aligned so that the
// Videcore can handle it properly. We don't take precautions here // Videcore can handle it properly.
// to achieve that, but for now it just works. Since alignment of
// data structures in Rust is a bit of a hassle right now, we just
// close our eyes and roll with it.
pub buffer: [u32; 36], pub buffer: [u32; 36],
} }
@ -117,34 +120,33 @@ impl Mbox {
pub fn call(&self, channel: u32) -> Result<()> { pub fn call(&self, channel: u32) -> Result<()> {
// wait until we can write to the mailbox // wait until we can write to the mailbox
loop { loop {
if (self.STATUS.read() & FULL) != FULL { if !self.STATUS.is_set(STATUS::FULL) {
break; break;
} }
unsafe { asm!("nop" :::: "volatile") }; unsafe { asm!("nop" :::: "volatile") };
} }
let buf_ptr = self.buffer.as_ptr() as u32;
// write the address of our message to the mailbox with channel identifier // write the address of our message to the mailbox with channel identifier
unsafe { self.WRITE.set((buf_ptr & !0xF) | (channel & 0xF));
self.WRITE
.write(((self.buffer.as_ptr() as u32) & !0xF) | (channel & 0xF))
};
// now wait for the response // now wait for the response
loop { loop {
// is there a response? // is there a response?
loop { loop {
if (self.STATUS.read() & EMPTY) != EMPTY { if !self.STATUS.is_set(STATUS::EMPTY) {
break; break;
} }
unsafe { asm!("nop" :::: "volatile") }; unsafe { asm!("nop" :::: "volatile") };
} }
let resp: u32 = self.READ.read(); let resp: u32 = self.READ.get();
// is it a response to our message? // is it a response to our message?
if ((resp & 0xF) == channel) && ((resp & !0xF) == (self.buffer.as_ptr() as u32)) { if ((resp & 0xF) == channel) && ((resp & !0xF) == buf_ptr) {
// is it a valid successful response? // is it a valid successful response?
return match self.buffer[1] { return match self.buffer[1] {
response::SUCCESS => Ok(()), response::SUCCESS => Ok(()),

@ -23,28 +23,116 @@
*/ */
use super::MMIO_BASE; use super::MMIO_BASE;
use core::ops; use core::{
use core::sync::atomic::{compiler_fence, Ordering}; ops,
sync::atomic::{compiler_fence, Ordering},
};
use gpio; use gpio;
use mbox; use mbox;
use volatile_register::*; use register::mmio::*;
// PL011 UART registers.
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
register_bitfields! {
u32,
/// Flag Register
FR [
/// Transmit FIFO full. The meaning of this bit depends on the
/// state of the FEN bit in the UARTLCR_ LCRH Register. If the
/// FIFO is disabled, this bit is set when the transmit
/// holding register is full. If the FIFO is enabled, the TXFF
/// bit is set when the transmit FIFO is full.
TXFF OFFSET(5) NUMBITS(1) [],
/// Receive FIFO empty. The meaning of this bit depends on the
/// state of the FEN bit in the UARTLCR_H Register. If the
/// FIFO is disabled, this bit is set when the receive holding
/// register is empty. If the FIFO is enabled, the RXFE bit is
/// set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) []
],
/// Integer Baud rate divisor
IBRD [
/// Integer Baud rate divisor
IBRD OFFSET(0) NUMBITS(16) []
],
/// Fractional Baud rate divisor
FBRD [
/// Fractional Baud rate divisor
FBRD OFFSET(0) NUMBITS(6) []
],
/// Line Control register
LCRH [
/// Word length. These bits indicate the number of data bits
/// transmitted or received in a frame.
WLEN OFFSET(5) NUMBITS(2) [
FiveBit = 0b00,
SixBit = 0b01,
SevenBit = 0b10,
EightBit = 0b11
]
],
/// Control Register
CR [
/// Receive enable. If this bit is set to 1, the receive
/// section of the UART is enabled. Data reception occurs for
/// UART signals. When the UART is disabled in the middle of
/// reception, it completes the current character before
/// stopping.
RXE OFFSET(9) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Transmit enable. If this bit is set to 1, the transmit
/// section of the UART is enabled. Data transmission occurs
/// for UART signals. When the UART is disabled in the middle
/// of transmission, it completes the current character before
/// stopping.
TXE OFFSET(8) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// UART enable
UARTEN OFFSET(0) NUMBITS(1) [
/// If the UART is disabled in the middle of transmission
/// or reception, it completes the current character
/// before stopping.
Disabled = 0,
Enabled = 1
]
],
/// Interupt Clear Register
ICR [
/// Meta field for all pending interrupts
ALL OFFSET(0) NUMBITS(11) []
]
}
const UART_BASE: u32 = MMIO_BASE + 0x20_1000; const UART_BASE: u32 = MMIO_BASE + 0x20_1000;
// PL011 UART registers
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[repr(C)] #[repr(C)]
pub struct RegisterBlock { pub struct RegisterBlock {
DR: RW<u32>, // 0x00 DR: ReadWrite<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04 __reserved_0: [u32; 5], // 0x04
FR: RO<u32>, // 0x18 FR: ReadOnly<u32, FR::Register>, // 0x18
__reserved_1: [u32; 2], // 0x1c __reserved_1: [u32; 2], // 0x1c
IBRD: WO<u32>, // 0x24 IBRD: WriteOnly<u32, IBRD::Register>, // 0x24
FBRD: WO<u32>, // 0x28 FBRD: WriteOnly<u32, FBRD::Register>, // 0x28
LCRH: WO<u32>, // 0x2C LCRH: WriteOnly<u32, LCRH::Register>, // 0x2C
CR: WO<u32>, // 0x30 CR: WriteOnly<u32, CR::Register>, // 0x30
__reserved_2: [u32; 4], // 0x34 __reserved_2: [u32; 4], // 0x34
ICR: WO<u32>, // 0x44 ICR: WriteOnly<u32, ICR::Register>, // 0x44
} }
pub enum UartError { pub enum UartError {
@ -75,7 +163,7 @@ impl Uart {
///Set baud rate and characteristics (115200 8N1) and map to GPIO ///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self, mbox: &mut mbox::Mbox) -> Result<()> { pub fn init(&self, mbox: &mut mbox::Mbox) -> Result<()> {
// turn off UART0 // turn off UART0
unsafe { self.CR.write(0) }; self.CR.set(0);
// set up clock for consistent divisor values // set up clock for consistent divisor values
mbox.buffer[0] = 9 * 4; mbox.buffer[0] = 9 * 4;
@ -99,33 +187,30 @@ impl Uart {
// map UART0 to GPIO pins // map UART0 to GPIO pins
unsafe { unsafe {
(*gpio::GPFSEL1).modify(|x| { (*gpio::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0);
// Modify with a closure
let mut ret = x;
ret &= !((7 << 12) | (7 << 15)); // gpio14, gpio15
ret |= (4 << 12) | (4 << 15); // alt0
ret
});
(*gpio::GPPUD).write(0); // enable pins 14 and 15 (*gpio::GPPUD).set(0); // enable pins 14 and 15
for _ in 0..150 { for _ in 0..150 {
asm!("nop" :::: "volatile"); asm!("nop" :::: "volatile");
} }
(*gpio::GPPUDCLK0).write((1 << 14) | (1 << 15)); (*gpio::GPPUDCLK0).write(
gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock,
);
for _ in 0..150 { for _ in 0..150 {
asm!("nop" :::: "volatile"); asm!("nop" :::: "volatile");
} }
(*gpio::GPPUDCLK0).write(0);
self.ICR.write(0x7FF); // clear interrupts (*gpio::GPPUDCLK0).set(0);
self.IBRD.write(2); // 115200 baud
self.FBRD.write(0xB);
self.LCRH.write(0b11 << 5); // 8n1
self.CR.write(0x301); // enable Tx, Rx, FIFO
} }
self.ICR.write(ICR::ALL::CLEAR);
self.IBRD.write(IBRD::IBRD.val(2)); // Results in 115200 baud
self.FBRD.write(FBRD::FBRD.val(0xB));
self.LCRH.write(LCRH::WLEN::EightBit); // 8N1
self.CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
Ok(()) Ok(())
} }
@ -133,7 +218,7 @@ impl Uart {
pub fn send(&self, c: char) { pub fn send(&self, c: char) {
// wait until we can send // wait until we can send
loop { loop {
if (self.FR.read() & 0x20) != 0x20 { if !self.FR.is_set(FR::TXFF) {
break; break;
} }
@ -141,14 +226,14 @@ impl Uart {
} }
// write the character to the buffer // write the character to the buffer
unsafe { self.DR.write(c as u32) }; self.DR.set(c as u32);
} }
/// Receive a character /// Receive a character
pub fn getc(&self) -> char { pub fn getc(&self) -> char {
// wait until something is in the buffer // wait until something is in the buffer
loop { loop {
if (self.FR.read() & 0x10) != 0x10 { if !self.FR.is_set(FR::RXFE) {
break; break;
} }
@ -156,7 +241,7 @@ impl Uart {
} }
// read it and return // read it and return
let mut ret = self.DR.read() as u8 as char; let mut ret = self.DR.get() as u8 as char;
// convert carrige return to newline // convert carrige return to newline
if ret == '\r' { if ret == '\r' {

@ -3,7 +3,7 @@ name = "kernel8"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"raspi3_glue 0.1.0", "raspi3_glue 0.1.0",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -19,19 +19,19 @@ dependencies = [
] ]
[[package]] [[package]]
name = "vcell" name = "register"
version = "0.1.0" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "volatile-register" name = "tock-registers"
version = "0.2.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata] [metadata]
"checksum panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6bc796c620f27056d4ffe7c558533fd67ae5af0fd8e919fbe38de803368af73e" "checksum panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6bc796c620f27056d4ffe7c558533fd67ae5af0fd8e919fbe38de803368af73e"
"checksum vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d" "checksum register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e985243ba7e1c336b62444ef2a10d7bd87cf41a222285ae3de605c859006479"
"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" "checksum tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2acc33f980e23cee18d234a32d0637fbc1ea55e13ab04012fa857b899fa1b7a9"

@ -5,4 +5,4 @@ authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies] [dependencies]
raspi3_glue = { path = "raspi3_glue" } raspi3_glue = { path = "raspi3_glue" }
volatile-register = "0.2.0" register = "0.1.1"

Binary file not shown.

@ -29,8 +29,6 @@
extern crate panic_abort; extern crate panic_abort;
use core::ptr;
#[lang = "start"] #[lang = "start"]
extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize
where where
@ -52,6 +50,8 @@ impl Termination for () {
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn reset() -> ! { pub unsafe extern "C" fn reset() -> ! {
use core::ptr;
extern "C" { extern "C" {
fn main(argc: isize, argv: *const *const u8) -> isize; fn main(argc: isize, argv: *const *const u8) -> isize;
} }

@ -23,8 +23,54 @@
*/ */
use super::MMIO_BASE; use super::MMIO_BASE;
use volatile_register::RW; use register::mmio::ReadWrite;
pub const GPFSEL1: *const RW<u32> = (MMIO_BASE + 0x0020_0004) as *const RW<u32>; // Descriptions taken from
pub const GPPUD: *const RW<u32> = (MMIO_BASE + 0x0020_0094) as *const RW<u32>; // https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
pub const GPPUDCLK0: *const RW<u32> = (MMIO_BASE + 0x0020_0098) as *const RW<u32>; register_bitfields! {
u32,
/// GPIO Function Select 1
GPFSEL1 [
/// Pin 15
FSEL15 OFFSET(15) NUMBITS(3) [
Input = 0b000,
Output = 0b001,
RXD0 = 0b100, // UART0 - Alternate function 0
RXD1 = 0b010 // Mini UART - Alternate function 5
],
/// Pin 14
FSEL14 OFFSET(12) NUMBITS(3) [
Input = 0b000,
Output = 0b001,
TXD0 = 0b100, // UART0 - Alternate function 0
TXD1 = 0b010 // Mini UART - Alternate function 5
]
],
/// GPIO Pull-up/down Clock Register 0
GPPUDCLK0 [
/// Pin 15
PUDCLK15 OFFSET(15) NUMBITS(1) [
NoEffect = 0,
AssertClock = 1
],
/// Pin 14
PUDCLK14 OFFSET(14) NUMBITS(1) [
NoEffect = 0,
AssertClock = 1
]
]
}
pub const GPFSEL1: *const ReadWrite<u32, GPFSEL1::Register> =
(MMIO_BASE + 0x0020_0004) as *const ReadWrite<u32, GPFSEL1::Register>;
pub const GPPUD: *const ReadWrite<u32> = (MMIO_BASE + 0x0020_0094) as *const ReadWrite<u32>;
pub const GPPUDCLK0: *const ReadWrite<u32, GPPUDCLK0::Register> =
(MMIO_BASE + 0x0020_0098) as *const ReadWrite<u32, GPPUDCLK0::Register>;

@ -26,7 +26,9 @@
#![feature(asm)] #![feature(asm)]
extern crate raspi3_glue; extern crate raspi3_glue;
extern crate volatile_register;
#[macro_use]
extern crate register;
const MMIO_BASE: u32 = 0x3F00_0000; const MMIO_BASE: u32 = 0x3F00_0000;

@ -24,20 +24,27 @@
use super::MMIO_BASE; use super::MMIO_BASE;
use core::ops; use core::ops;
use volatile_register::{RO, WO}; use register::mmio::{ReadOnly, WriteOnly};
register_bitfields! {
u32,
STATUS [
FULL OFFSET(31) NUMBITS(1) [],
EMPTY OFFSET(30) NUMBITS(1) []
]
}
const VIDEOCORE_MBOX: u32 = MMIO_BASE + 0xB880; const VIDEOCORE_MBOX: u32 = MMIO_BASE + 0xB880;
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[repr(C)] #[repr(C)]
pub struct RegisterBlock { pub struct RegisterBlock {
READ: RO<u32>, // 0x00 READ: ReadOnly<u32>, // 0x00
__reserved_0: [u32; 3], // 0x04 __reserved_0: [u32; 5], // 0x04
POLL: RO<u32>, // 0x10 STATUS: ReadOnly<u32, STATUS::Register>, // 0x18
SENDER: RO<u32>, // 0x14 __reserved_1: u32, // 0x1C
STATUS: RO<u32>, // 0x18 WRITE: WriteOnly<u32>, // 0x20
CONFIG: RO<u32>, // 0x1C
WRITE: WO<u32>, // 0x20
} }
// Custom errors // Custom errors
@ -70,17 +77,13 @@ mod response {
} }
pub const REQUEST: u32 = 0; pub const REQUEST: u32 = 0;
const FULL: u32 = 0x8000_0000;
const EMPTY: u32 = 0x4000_0000;
// Public interface to the mailbox // Public interface to the mailbox
#[repr(C)] #[repr(C)]
#[repr(align(16))]
pub struct Mbox { pub struct Mbox {
// The address for buffer needs to be 16-byte aligned so that the // The address for buffer needs to be 16-byte aligned so that the
// Videcore can handle it properly. We don't take precautions here // Videcore can handle it properly.
// to achieve that, but for now it just works. Since alignment of
// data structures in Rust is a bit of a hassle right now, we just
// close our eyes and roll with it.
pub buffer: [u32; 36], pub buffer: [u32; 36],
} }
@ -116,34 +119,33 @@ impl Mbox {
pub fn call(&self, channel: u32) -> Result<()> { pub fn call(&self, channel: u32) -> Result<()> {
// wait until we can write to the mailbox // wait until we can write to the mailbox
loop { loop {
if (self.STATUS.read() & FULL) != FULL { if !self.STATUS.is_set(STATUS::FULL) {
break; break;
} }
unsafe { asm!("nop" :::: "volatile") }; unsafe { asm!("nop" :::: "volatile") };
} }
let buf_ptr = self.buffer.as_ptr() as u32;
// write the address of our message to the mailbox with channel identifier // write the address of our message to the mailbox with channel identifier
unsafe { self.WRITE.set((buf_ptr & !0xF) | (channel & 0xF));
self.WRITE
.write(((self.buffer.as_ptr() as u32) & !0xF) | (channel & 0xF))
};
// now wait for the response // now wait for the response
loop { loop {
// is there a response? // is there a response?
loop { loop {
if (self.STATUS.read() & EMPTY) != EMPTY { if !self.STATUS.is_set(STATUS::EMPTY) {
break; break;
} }
unsafe { asm!("nop" :::: "volatile") }; unsafe { asm!("nop" :::: "volatile") };
} }
let resp: u32 = self.READ.read(); let resp: u32 = self.READ.get();
// is it a response to our message? // is it a response to our message?
if ((resp & 0xF) == channel) && ((resp & !0xF) == (self.buffer.as_ptr() as u32)) { if ((resp & 0xF) == channel) && ((resp & !0xF) == buf_ptr) {
// is it a valid successful response? // is it a valid successful response?
return match self.buffer[1] { return match self.buffer[1] {
response::SUCCESS => Ok(()), response::SUCCESS => Ok(()),

@ -23,28 +23,116 @@
*/ */
use super::MMIO_BASE; use super::MMIO_BASE;
use core::ops; use core::{
use core::sync::atomic::{compiler_fence, Ordering}; ops,
sync::atomic::{compiler_fence, Ordering},
};
use gpio; use gpio;
use mbox; use mbox;
use volatile_register::*; use register::mmio::*;
// PL011 UART registers.
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
register_bitfields! {
u32,
/// Flag Register
FR [
/// Transmit FIFO full. The meaning of this bit depends on the
/// state of the FEN bit in the UARTLCR_ LCRH Register. If the
/// FIFO is disabled, this bit is set when the transmit
/// holding register is full. If the FIFO is enabled, the TXFF
/// bit is set when the transmit FIFO is full.
TXFF OFFSET(5) NUMBITS(1) [],
/// Receive FIFO empty. The meaning of this bit depends on the
/// state of the FEN bit in the UARTLCR_H Register. If the
/// FIFO is disabled, this bit is set when the receive holding
/// register is empty. If the FIFO is enabled, the RXFE bit is
/// set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) []
],
/// Integer Baud rate divisor
IBRD [
/// Integer Baud rate divisor
IBRD OFFSET(0) NUMBITS(16) []
],
/// Fractional Baud rate divisor
FBRD [
/// Fractional Baud rate divisor
FBRD OFFSET(0) NUMBITS(6) []
],
/// Line Control register
LCRH [
/// Word length. These bits indicate the number of data bits
/// transmitted or received in a frame.
WLEN OFFSET(5) NUMBITS(2) [
FiveBit = 0b00,
SixBit = 0b01,
SevenBit = 0b10,
EightBit = 0b11
]
],
/// Control Register
CR [
/// Receive enable. If this bit is set to 1, the receive
/// section of the UART is enabled. Data reception occurs for
/// UART signals. When the UART is disabled in the middle of
/// reception, it completes the current character before
/// stopping.
RXE OFFSET(9) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Transmit enable. If this bit is set to 1, the transmit
/// section of the UART is enabled. Data transmission occurs
/// for UART signals. When the UART is disabled in the middle
/// of transmission, it completes the current character before
/// stopping.
TXE OFFSET(8) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// UART enable
UARTEN OFFSET(0) NUMBITS(1) [
/// If the UART is disabled in the middle of transmission
/// or reception, it completes the current character
/// before stopping.
Disabled = 0,
Enabled = 1
]
],
/// Interupt Clear Register
ICR [
/// Meta field for all pending interrupts
ALL OFFSET(0) NUMBITS(11) []
]
}
const UART_BASE: u32 = MMIO_BASE + 0x20_1000; const UART_BASE: u32 = MMIO_BASE + 0x20_1000;
// PL011 UART registers
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[repr(C)] #[repr(C)]
pub struct RegisterBlock { pub struct RegisterBlock {
DR: RW<u32>, // 0x00 DR: ReadWrite<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04 __reserved_0: [u32; 5], // 0x04
FR: RO<u32>, // 0x18 FR: ReadOnly<u32, FR::Register>, // 0x18
__reserved_1: [u32; 2], // 0x1c __reserved_1: [u32; 2], // 0x1c
IBRD: WO<u32>, // 0x24 IBRD: WriteOnly<u32, IBRD::Register>, // 0x24
FBRD: WO<u32>, // 0x28 FBRD: WriteOnly<u32, FBRD::Register>, // 0x28
LCRH: WO<u32>, // 0x2C LCRH: WriteOnly<u32, LCRH::Register>, // 0x2C
CR: WO<u32>, // 0x30 CR: WriteOnly<u32, CR::Register>, // 0x30
__reserved_2: [u32; 4], // 0x34 __reserved_2: [u32; 4], // 0x34
ICR: WO<u32>, // 0x44 ICR: WriteOnly<u32, ICR::Register>, // 0x44
} }
pub enum UartError { pub enum UartError {
@ -75,7 +163,7 @@ impl Uart {
///Set baud rate and characteristics (115200 8N1) and map to GPIO ///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self, mbox: &mut mbox::Mbox) -> Result<()> { pub fn init(&self, mbox: &mut mbox::Mbox) -> Result<()> {
// turn off UART0 // turn off UART0
unsafe { self.CR.write(0) }; self.CR.set(0);
// set up clock for consistent divisor values // set up clock for consistent divisor values
mbox.buffer[0] = 9 * 4; mbox.buffer[0] = 9 * 4;
@ -99,33 +187,30 @@ impl Uart {
// map UART0 to GPIO pins // map UART0 to GPIO pins
unsafe { unsafe {
(*gpio::GPFSEL1).modify(|x| { (*gpio::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0);
// Modify with a closure
let mut ret = x;
ret &= !((7 << 12) | (7 << 15)); // gpio14, gpio15
ret |= (4 << 12) | (4 << 15); // alt0
ret
});
(*gpio::GPPUD).write(0); // enable pins 14 and 15 (*gpio::GPPUD).set(0); // enable pins 14 and 15
for _ in 0..150 { for _ in 0..150 {
asm!("nop" :::: "volatile"); asm!("nop" :::: "volatile");
} }
(*gpio::GPPUDCLK0).write((1 << 14) | (1 << 15)); (*gpio::GPPUDCLK0).modify(
gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock,
);
for _ in 0..150 { for _ in 0..150 {
asm!("nop" :::: "volatile"); asm!("nop" :::: "volatile");
} }
(*gpio::GPPUDCLK0).write(0);
self.ICR.write(0x7FF); // clear interrupts (*gpio::GPPUDCLK0).set(0);
self.IBRD.write(2); // 115200 baud
self.FBRD.write(0xB);
self.LCRH.write(0b11 << 5); // 8n1
self.CR.write(0x301); // enable Tx, Rx, FIFO
} }
self.ICR.write(ICR::ALL::CLEAR);
self.IBRD.write(IBRD::IBRD.val(2)); // Results in 115200 baud
self.FBRD.write(FBRD::FBRD.val(0xB));
self.LCRH.write(LCRH::WLEN::EightBit); // 8N1
self.CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
Ok(()) Ok(())
} }
@ -133,7 +218,7 @@ impl Uart {
pub fn send(&self, c: char) { pub fn send(&self, c: char) {
// wait until we can send // wait until we can send
loop { loop {
if (self.FR.read() & 0x20) != 0x20 { if !self.FR.is_set(FR::TXFF) {
break; break;
} }
@ -141,14 +226,14 @@ impl Uart {
} }
// write the character to the buffer // write the character to the buffer
unsafe { self.DR.write(c as u32) }; self.DR.set(c as u32);
} }
/// Receive a character /// Receive a character
pub fn getc(&self) -> u8 { pub fn getc(&self) -> u8 {
// wait until something is in the buffer // wait until something is in the buffer
loop { loop {
if (self.FR.read() & 0x10) != 0x10 { if !self.FR.is_set(FR::RXFE) {
break; break;
} }
@ -156,6 +241,6 @@ impl Uart {
} }
// read it and return // read it and return
self.DR.read() as u8 self.DR.get() as u8
} }
} }

@ -1,23 +1,18 @@
[[package]]
name = "bitflags"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "cortex-a" name = "cortex-a"
version = "0.1.3" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "kernel8" name = "kernel8"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"cortex-a 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-a 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"raspi3_glue 0.1.0", "raspi3_glue 0.1.0",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -34,28 +29,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "raspi3_glue" name = "raspi3_glue"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"cortex-a 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-a 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "vcell" name = "register"
version = "0.1.0" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "volatile-register" name = "tock-registers"
version = "0.2.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata] [metadata]
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" "checksum cortex-a 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24174fbfe16d46844856c0f5e16ee15f7adbacfd87cb0148d34463dd34b54eeb"
"checksum cortex-a 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a123fa5a346531ed0fc9fcb8f69ca34d9b8c55b15162731945d14c4d461c5bfe"
"checksum panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6bc796c620f27056d4ffe7c558533fd67ae5af0fd8e919fbe38de803368af73e" "checksum panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6bc796c620f27056d4ffe7c558533fd67ae5af0fd8e919fbe38de803368af73e"
"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" "checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
"checksum vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d" "checksum register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e985243ba7e1c336b62444ef2a10d7bd87cf41a222285ae3de605c859006479"
"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" "checksum tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2acc33f980e23cee18d234a32d0637fbc1ea55e13ab04012fa857b899fa1b7a9"

@ -5,5 +5,5 @@ authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies] [dependencies]
raspi3_glue = { path = "raspi3_glue" } raspi3_glue = { path = "raspi3_glue" }
cortex-a = "0.1.3" cortex-a = "1.0.0"
volatile-register = "0.2.0" register = "0.1.1"

@ -15,20 +15,30 @@ wrappers around assembly instructions.
For single assembler instructions, we now have the `cortex-a::asm` namespace, For single assembler instructions, we now have the `cortex-a::asm` namespace,
e.g. providing `asm::nop()`. e.g. providing `asm::nop()`.
For registers, there is `cortex-a::register`. For registers like the stack For registers, there is `cortex-a::regs`. The interface is the same as we have
pointer, which are generally read and written as a whole, there's simple it for MMIO accesses, aka provided by [register-rs][register-rs] and therefore
[read()][sp_read] and [write()][sp_write] functions which take and return based on [tock-regs][tock-regs]. For registers like the stack pointer, which are
primitive integer types. generally read and written as a whole, there's the common [get()][get] and
[set()][set] functions which take and return primitive integer types.
[register-rs]: https://github.com/rust-osdev/register-rs
[tock-regs]: https://github.com/tock/tock/tree/master/libraries/tock-register-interface
[get]: https://docs.rs/cortex-a/1.0.0/cortex_a/regs/sp/trait.RegisterReadWrite.html#tymethod.get
[set]: https://docs.rs/cortex-a/1.0.0/cortex_a/regs/sp/trait.RegisterReadWrite.html#tymethod.set
Registers that are divided into multiple fields, like `MPIDR_EL1` ([see the ARM
Reference Manual][el1]), on the other hand, are backed by a respective
[type][cntp_type] definition that allow for fine-grained reading and
modifications.
[sp_read]: https://docs.rs/cortex-a/0.1.2/cortex_a/register/sp/fn.read.html [el1]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0500g/BABHBJCI.html
[sp_write]: https://docs.rs/cortex-a/0.1.2/cortex_a/register/sp/fn.write.html [cntp_bitfields]: https://docs.rs/cortex-a/1.0.0/cortex_a/regs/cntp_ctl_el0/CNTP_CTL_EL0/index.html
Registers that are divided into multiple fields, e.g. `MPIDR_EL1` ([see the ARM The register API is based on the [tock project's][tock] register
Reference Manual][el1]), on the other hand, are abstracted into their [own interface. Please see [their homepage][tock_registers] for all the details.
types][mpidr_type] and offer getter and/or setter methods, respectively.
[el1]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0500g/BABHBJCI.html [tock]: https://github.com/tock/tock
[mpidr_type]:https://docs.rs/cortex-a/0.1.2/cortex_a/register/mpidr_el1/struct.MPIDR_EL1.html [tock_register]: https://github.com/tock/tock/tree/master/libraries/tock-register-interface
To some extent, this namespacing also makes our code more portable. For example, To some extent, this namespacing also makes our code more portable. For example,
if we want to reuse parts of it on another processor architecture, we could pull if we want to reuse parts of it on another processor architecture, we could pull
@ -56,11 +66,13 @@ replaced it with a Rust function. Why? Because we can, for the fun of it.
#[link_section = ".text.boot"] #[link_section = ".text.boot"]
#[no_mangle] #[no_mangle]
pub extern "C" fn _boot_cores() -> ! { pub extern "C" fn _boot_cores() -> ! {
match register::MPIDR_EL1::read_raw() & 0x3 { use cortex_a::{asm, regs::mpidr_el1::*, regs::sp::*};
0 => unsafe {
register::sp::write(0x80_000); match MPIDR_EL1.get() & 0x3 {
reset() 0 => {
}, SP.set(0x80_000);
unsafe { reset() }
}
_ => loop { _ => loop {
// if not core0, infinitely wait for events // if not core0, infinitely wait for events
asm::wfe(); asm::wfe();
@ -74,7 +86,7 @@ set up yet. Actually it is this function that will do it for the first
time. Therefore, it is important to check that code generated from this function time. Therefore, it is important to check that code generated from this function
does not call any subroutines that need a working stack themselves. does not call any subroutines that need a working stack themselves.
The `read_raw()` and `asm` wrappers that we use from the `cortex-a` crate are all The `get()` and `asm` wrappers that we use from the `cortex-a` crate are all
inlined, so we fulfill this requirement. The compilation result of this function inlined, so we fulfill this requirement. The compilation result of this function
should yield something like the following, where you can see that the stack should yield something like the following, where you can see that the stack
pointer is not used apart from ourselves setting it. pointer is not used apart from ourselves setting it.

Binary file not shown.

@ -4,6 +4,6 @@ version = "0.1.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"] authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies] [dependencies]
cortex-a = "0.1.3" cortex-a = "1.0.0"
panic-abort = "0.2.0" panic-abort = "0.2.0"
r0 = "0.2.2" r0 = "0.2.2"

@ -30,9 +30,6 @@ extern crate cortex_a;
extern crate panic_abort; extern crate panic_abort;
extern crate r0; extern crate r0;
use core::ptr;
use cortex_a::{asm, register};
#[lang = "start"] #[lang = "start"]
extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize
where where
@ -53,6 +50,8 @@ impl Termination for () {
} }
unsafe fn reset() -> ! { unsafe fn reset() -> ! {
use core::ptr;
extern "C" { extern "C" {
fn main(argc: isize, argv: *const *const u8) -> isize; fn main(argc: isize, argv: *const *const u8) -> isize;
@ -77,11 +76,13 @@ unsafe fn reset() -> ! {
#[link_section = ".text.boot"] #[link_section = ".text.boot"]
#[no_mangle] #[no_mangle]
pub extern "C" fn _boot_cores() -> ! { pub extern "C" fn _boot_cores() -> ! {
match register::MPIDR_EL1::read_raw() & 0x3 { use cortex_a::{asm, regs::mpidr_el1::*, regs::sp::*};
0 => unsafe {
register::SP::write_raw(0x80_000); match MPIDR_EL1.get() & 0x3 {
reset() 0 => {
}, SP.set(0x80_000);
unsafe { reset() }
}
_ => loop { _ => loop {
// if not core0, infinitely wait for events // if not core0, infinitely wait for events
asm::wfe(); asm::wfe();

@ -23,8 +23,54 @@
*/ */
use super::MMIO_BASE; use super::MMIO_BASE;
use volatile_register::RW; use register::mmio::ReadWrite;
pub const GPFSEL1: *const RW<u32> = (MMIO_BASE + 0x0020_0004) as *const RW<u32>; // Descriptions taken from
pub const GPPUD: *const RW<u32> = (MMIO_BASE + 0x0020_0094) as *const RW<u32>; // https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
pub const GPPUDCLK0: *const RW<u32> = (MMIO_BASE + 0x0020_0098) as *const RW<u32>; register_bitfields! {
u32,
/// GPIO Function Select 1
GPFSEL1 [
/// Pin 15
FSEL15 OFFSET(15) NUMBITS(3) [
Input = 0b000,
Output = 0b001,
RXD0 = 0b100, // UART0 - Alternate function 0
RXD1 = 0b010 // Mini UART - Alternate function 5
],
/// Pin 14
FSEL14 OFFSET(12) NUMBITS(3) [
Input = 0b000,
Output = 0b001,
TXD0 = 0b100, // UART0 - Alternate function 0
TXD1 = 0b010 // Mini UART - Alternate function 5
]
],
/// GPIO Pull-up/down Clock Register 0
GPPUDCLK0 [
/// Pin 15
PUDCLK15 OFFSET(15) NUMBITS(1) [
NoEffect = 0,
AssertClock = 1
],
/// Pin 14
PUDCLK14 OFFSET(14) NUMBITS(1) [
NoEffect = 0,
AssertClock = 1
]
]
}
pub const GPFSEL1: *const ReadWrite<u32, GPFSEL1::Register> =
(MMIO_BASE + 0x0020_0004) as *const ReadWrite<u32, GPFSEL1::Register>;
pub const GPPUD: *const ReadWrite<u32> = (MMIO_BASE + 0x0020_0094) as *const ReadWrite<u32>;
pub const GPPUDCLK0: *const ReadWrite<u32, GPPUDCLK0::Register> =
(MMIO_BASE + 0x0020_0098) as *const ReadWrite<u32, GPPUDCLK0::Register>;

@ -26,7 +26,9 @@
extern crate cortex_a; extern crate cortex_a;
extern crate raspi3_glue; extern crate raspi3_glue;
extern crate volatile_register;
#[macro_use]
extern crate register;
const MMIO_BASE: u32 = 0x3F00_0000; const MMIO_BASE: u32 = 0x3F00_0000;

@ -25,20 +25,27 @@
use super::MMIO_BASE; use super::MMIO_BASE;
use core::ops; use core::ops;
use cortex_a::asm; use cortex_a::asm;
use volatile_register::{RO, WO}; use register::mmio::{ReadOnly, WriteOnly};
register_bitfields! {
u32,
STATUS [
FULL OFFSET(31) NUMBITS(1) [],
EMPTY OFFSET(30) NUMBITS(1) []
]
}
const VIDEOCORE_MBOX: u32 = MMIO_BASE + 0xB880; const VIDEOCORE_MBOX: u32 = MMIO_BASE + 0xB880;
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[repr(C)] #[repr(C)]
pub struct RegisterBlock { pub struct RegisterBlock {
READ: RO<u32>, // 0x00 READ: ReadOnly<u32>, // 0x00
__reserved_0: [u32; 3], // 0x04 __reserved_0: [u32; 5], // 0x04
POLL: RO<u32>, // 0x10 STATUS: ReadOnly<u32, STATUS::Register>, // 0x18
SENDER: RO<u32>, // 0x14 __reserved_1: u32, // 0x1C
STATUS: RO<u32>, // 0x18 WRITE: WriteOnly<u32>, // 0x20
CONFIG: RO<u32>, // 0x1C
WRITE: WO<u32>, // 0x20
} }
// Custom errors // Custom errors
@ -72,17 +79,13 @@ mod response {
} }
pub const REQUEST: u32 = 0; pub const REQUEST: u32 = 0;
const FULL: u32 = 0x8000_0000;
const EMPTY: u32 = 0x4000_0000;
// Public interface to the mailbox // Public interface to the mailbox
#[repr(C)] #[repr(C)]
#[repr(align(16))]
pub struct Mbox { pub struct Mbox {
// The address for buffer needs to be 16-byte aligned so that the // The address for buffer needs to be 16-byte aligned so that the
// Videcore can handle it properly. We don't take precautions here // Videcore can handle it properly.
// to achieve that, but for now it just works. Since alignment of
// data structures in Rust is a bit of a hassle right now, we just
// close our eyes and roll with it.
pub buffer: [u32; 36], pub buffer: [u32; 36],
} }
@ -118,34 +121,33 @@ impl Mbox {
pub fn call(&self, channel: u32) -> Result<()> { pub fn call(&self, channel: u32) -> Result<()> {
// wait until we can write to the mailbox // wait until we can write to the mailbox
loop { loop {
if (self.STATUS.read() & FULL) != FULL { if !self.STATUS.is_set(STATUS::FULL) {
break; break;
} }
asm::nop(); asm::nop();
} }
let buf_ptr = self.buffer.as_ptr() as u32;
// write the address of our message to the mailbox with channel identifier // write the address of our message to the mailbox with channel identifier
unsafe { self.WRITE.set((buf_ptr & !0xF) | (channel & 0xF));
self.WRITE
.write(((self.buffer.as_ptr() as u32) & !0xF) | (channel & 0xF))
};
// now wait for the response // now wait for the response
loop { loop {
// is there a response? // is there a response?
loop { loop {
if (self.STATUS.read() & EMPTY) != EMPTY { if !self.STATUS.is_set(STATUS::EMPTY) {
break; break;
} }
asm::nop(); asm::nop();
} }
let resp: u32 = self.READ.read(); let resp: u32 = self.READ.get();
// is it a response to our message? // is it a response to our message?
if ((resp & 0xF) == channel) && ((resp & !0xF) == (self.buffer.as_ptr() as u32)) { if ((resp & 0xF) == channel) && ((resp & !0xF) == buf_ptr) {
// is it a valid successful response? // is it a valid successful response?
return match self.buffer[1] { return match self.buffer[1] {
response::SUCCESS => Ok(()), response::SUCCESS => Ok(()),

@ -23,29 +23,117 @@
*/ */
use super::MMIO_BASE; use super::MMIO_BASE;
use core::ops; use core::{
use core::sync::atomic::{compiler_fence, Ordering}; ops,
sync::atomic::{compiler_fence, Ordering},
};
use cortex_a::asm; use cortex_a::asm;
use gpio; use gpio;
use mbox; use mbox;
use volatile_register::*; use register::mmio::*;
// PL011 UART registers.
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
register_bitfields! {
u32,
/// Flag Register
FR [
/// Transmit FIFO full. The meaning of this bit depends on the
/// state of the FEN bit in the UARTLCR_ LCRH Register. If the
/// FIFO is disabled, this bit is set when the transmit
/// holding register is full. If the FIFO is enabled, the TXFF
/// bit is set when the transmit FIFO is full.
TXFF OFFSET(5) NUMBITS(1) [],
/// Receive FIFO empty. The meaning of this bit depends on the
/// state of the FEN bit in the UARTLCR_H Register. If the
/// FIFO is disabled, this bit is set when the receive holding
/// register is empty. If the FIFO is enabled, the RXFE bit is
/// set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) []
],
/// Integer Baud rate divisor
IBRD [
/// Integer Baud rate divisor
IBRD OFFSET(0) NUMBITS(16) []
],
/// Fractional Baud rate divisor
FBRD [
/// Fractional Baud rate divisor
FBRD OFFSET(0) NUMBITS(6) []
],
/// Line Control register
LCRH [
/// Word length. These bits indicate the number of data bits
/// transmitted or received in a frame.
WLEN OFFSET(5) NUMBITS(2) [
FiveBit = 0b00,
SixBit = 0b01,
SevenBit = 0b10,
EightBit = 0b11
]
],
/// Control Register
CR [
/// Receive enable. If this bit is set to 1, the receive
/// section of the UART is enabled. Data reception occurs for
/// UART signals. When the UART is disabled in the middle of
/// reception, it completes the current character before
/// stopping.
RXE OFFSET(9) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Transmit enable. If this bit is set to 1, the transmit
/// section of the UART is enabled. Data transmission occurs
/// for UART signals. When the UART is disabled in the middle
/// of transmission, it completes the current character before
/// stopping.
TXE OFFSET(8) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// UART enable
UARTEN OFFSET(0) NUMBITS(1) [
/// If the UART is disabled in the middle of transmission
/// or reception, it completes the current character
/// before stopping.
Disabled = 0,
Enabled = 1
]
],
/// Interupt Clear Register
ICR [
/// Meta field for all pending interrupts
ALL OFFSET(0) NUMBITS(11) []
]
}
const UART_BASE: u32 = MMIO_BASE + 0x20_1000; const UART_BASE: u32 = MMIO_BASE + 0x20_1000;
// PL011 UART registers
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[repr(C)] #[repr(C)]
pub struct RegisterBlock { pub struct RegisterBlock {
DR: RW<u32>, // 0x00 DR: ReadWrite<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04 __reserved_0: [u32; 5], // 0x04
FR: RO<u32>, // 0x18 FR: ReadOnly<u32, FR::Register>, // 0x18
__reserved_1: [u32; 2], // 0x1c __reserved_1: [u32; 2], // 0x1c
IBRD: WO<u32>, // 0x24 IBRD: WriteOnly<u32, IBRD::Register>, // 0x24
FBRD: WO<u32>, // 0x28 FBRD: WriteOnly<u32, FBRD::Register>, // 0x28
LCRH: WO<u32>, // 0x2C LCRH: WriteOnly<u32, LCRH::Register>, // 0x2C
CR: WO<u32>, // 0x30 CR: WriteOnly<u32, CR::Register>, // 0x30
__reserved_2: [u32; 4], // 0x34 __reserved_2: [u32; 4], // 0x34
ICR: WO<u32>, // 0x44 ICR: WriteOnly<u32, ICR::Register>, // 0x44
} }
pub enum UartError { pub enum UartError {
@ -76,7 +164,7 @@ impl Uart {
///Set baud rate and characteristics (115200 8N1) and map to GPIO ///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self, mbox: &mut mbox::Mbox) -> Result<()> { pub fn init(&self, mbox: &mut mbox::Mbox) -> Result<()> {
// turn off UART0 // turn off UART0
unsafe { self.CR.write(0) }; self.CR.set(0);
// set up clock for consistent divisor values // set up clock for consistent divisor values
mbox.buffer[0] = 9 * 4; mbox.buffer[0] = 9 * 4;
@ -100,33 +188,30 @@ impl Uart {
// map UART0 to GPIO pins // map UART0 to GPIO pins
unsafe { unsafe {
(*gpio::GPFSEL1).modify(|x| { (*gpio::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0);
// Modify with a closure
let mut ret = x;
ret &= !((7 << 12) | (7 << 15)); // gpio14, gpio15
ret |= (4 << 12) | (4 << 15); // alt0
ret
});
(*gpio::GPPUD).write(0); // enable pins 14 and 15 (*gpio::GPPUD).set(0); // enable pins 14 and 15
for _ in 0..150 { for _ in 0..150 {
asm::nop(); asm::nop();
} }
(*gpio::GPPUDCLK0).write((1 << 14) | (1 << 15)); (*gpio::GPPUDCLK0).modify(
gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock,
);
for _ in 0..150 { for _ in 0..150 {
asm::nop(); asm::nop();
} }
(*gpio::GPPUDCLK0).write(0);
self.ICR.write(0x7FF); // clear interrupts (*gpio::GPPUDCLK0).set(0);
self.IBRD.write(2); // 115200 baud
self.FBRD.write(0xB);
self.LCRH.write(0b11 << 5); // 8n1
self.CR.write(0x301); // enable Tx, Rx, FIFO
} }
self.ICR.write(ICR::ALL::CLEAR);
self.IBRD.write(IBRD::IBRD.val(2)); // Results in 115200 baud
self.FBRD.write(FBRD::FBRD.val(0xB));
self.LCRH.write(LCRH::WLEN::EightBit); // 8N1
self.CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
Ok(()) Ok(())
} }
@ -134,7 +219,7 @@ impl Uart {
pub fn send(&self, c: char) { pub fn send(&self, c: char) {
// wait until we can send // wait until we can send
loop { loop {
if (self.FR.read() & 0x20) != 0x20 { if !self.FR.is_set(FR::TXFF) {
break; break;
} }
@ -142,14 +227,14 @@ impl Uart {
} }
// write the character to the buffer // write the character to the buffer
unsafe { self.DR.write(c as u32) }; self.DR.set(c as u32);
} }
/// Receive a character /// Receive a character
pub fn getc(&self) -> char { pub fn getc(&self) -> char {
// wait until something is in the buffer // wait until something is in the buffer
loop { loop {
if (self.FR.read() & 0x10) != 0x10 { if !self.FR.is_set(FR::RXFE) {
break; break;
} }
@ -157,7 +242,7 @@ impl Uart {
} }
// read it and return // read it and return
let mut ret = self.DR.read() as u8 as char; let mut ret = self.DR.get() as u8 as char;
// convert carrige return to newline // convert carrige return to newline
if ret == '\r' { if ret == '\r' {

36
08_random/Cargo.lock generated

@ -1,23 +1,18 @@
[[package]]
name = "bitflags"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "cortex-a" name = "cortex-a"
version = "0.1.3" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "kernel8" name = "kernel8"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"cortex-a 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-a 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"raspi3_glue 0.1.0", "raspi3_glue 0.1.0",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -34,28 +29,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "raspi3_glue" name = "raspi3_glue"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"cortex-a 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-a 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "vcell" name = "register"
version = "0.1.0" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "volatile-register" name = "tock-registers"
version = "0.2.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata] [metadata]
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" "checksum cortex-a 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24174fbfe16d46844856c0f5e16ee15f7adbacfd87cb0148d34463dd34b54eeb"
"checksum cortex-a 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a123fa5a346531ed0fc9fcb8f69ca34d9b8c55b15162731945d14c4d461c5bfe"
"checksum panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6bc796c620f27056d4ffe7c558533fd67ae5af0fd8e919fbe38de803368af73e" "checksum panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6bc796c620f27056d4ffe7c558533fd67ae5af0fd8e919fbe38de803368af73e"
"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" "checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
"checksum vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d" "checksum register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e985243ba7e1c336b62444ef2a10d7bd87cf41a222285ae3de605c859006479"
"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" "checksum tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2acc33f980e23cee18d234a32d0637fbc1ea55e13ab04012fa857b899fa1b7a9"

@ -5,5 +5,5 @@ authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies] [dependencies]
raspi3_glue = { path = "raspi3_glue" } raspi3_glue = { path = "raspi3_glue" }
cortex-a = "0.1.3" cortex-a = "1.0.0"
volatile-register = "0.2.0" register = "0.1.1"

@ -2,11 +2,14 @@
This going to be an easy tutorial. We query a number from the (undocumented) This going to be an easy tutorial. We query a number from the (undocumented)
hardware random number generator. You can use this to implement a simple, but hardware random number generator. You can use this to implement a simple, but
accurate dice throw in any game. It is important as without hardware support accurate dice throw in any game. It is important as without hardware support you
you can only generate pseudo-random numbers. can only generate pseudo-random numbers.
## rand.s ## rand.s
Due to lack of documentation, we [mimic the respective Linux driver]
(https://github.com/torvalds/linux/blob/master/drivers/char/hw_random/bcm2835-rng.c).
`Rng::init(&self)` initializes the hardware. `Rng::init(&self)` initializes the hardware.
`Rng::rand(&self, min: u32, max: u32)` returns a random number between min and `Rng::rand(&self, min: u32, max: u32)` returns a random number between min and

Binary file not shown.

@ -4,6 +4,6 @@ version = "0.1.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"] authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies] [dependencies]
cortex-a = "0.1.3" cortex-a = "1.0.0"
panic-abort = "0.2.0" panic-abort = "0.2.0"
r0 = "0.2.2" r0 = "0.2.2"

@ -30,9 +30,6 @@ extern crate cortex_a;
extern crate panic_abort; extern crate panic_abort;
extern crate r0; extern crate r0;
use core::ptr;
use cortex_a::{asm, register};
#[lang = "start"] #[lang = "start"]
extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize
where where
@ -53,6 +50,8 @@ impl Termination for () {
} }
unsafe fn reset() -> ! { unsafe fn reset() -> ! {
use core::ptr;
extern "C" { extern "C" {
fn main(argc: isize, argv: *const *const u8) -> isize; fn main(argc: isize, argv: *const *const u8) -> isize;
@ -77,11 +76,13 @@ unsafe fn reset() -> ! {
#[link_section = ".text.boot"] #[link_section = ".text.boot"]
#[no_mangle] #[no_mangle]
pub extern "C" fn _boot_cores() -> ! { pub extern "C" fn _boot_cores() -> ! {
match register::MPIDR_EL1::read_raw() & 0x3 { use cortex_a::{asm, regs::mpidr_el1::*, regs::sp::*};
0 => unsafe {
register::SP::write_raw(0x80_000); match MPIDR_EL1.get() & 0x3 {
reset() 0 => {
}, SP.set(0x80_000);
unsafe { reset() }
}
_ => loop { _ => loop {
// if not core0, infinitely wait for events // if not core0, infinitely wait for events
asm::wfe(); asm::wfe();

@ -23,8 +23,54 @@
*/ */
use super::MMIO_BASE; use super::MMIO_BASE;
use volatile_register::RW; use register::mmio::ReadWrite;
pub const GPFSEL1: *const RW<u32> = (MMIO_BASE + 0x0020_0004) as *const RW<u32>; // Descriptions taken from
pub const GPPUD: *const RW<u32> = (MMIO_BASE + 0x0020_0094) as *const RW<u32>; // https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
pub const GPPUDCLK0: *const RW<u32> = (MMIO_BASE + 0x0020_0098) as *const RW<u32>; register_bitfields! {
u32,
/// GPIO Function Select 1
GPFSEL1 [
/// Pin 15
FSEL15 OFFSET(15) NUMBITS(3) [
Input = 0b000,
Output = 0b001,
RXD0 = 0b100, // UART0 - Alternate function 0
RXD1 = 0b010 // Mini UART - Alternate function 5
],
/// Pin 14
FSEL14 OFFSET(12) NUMBITS(3) [
Input = 0b000,
Output = 0b001,
TXD0 = 0b100, // UART0 - Alternate function 0
TXD1 = 0b010 // Mini UART - Alternate function 5
]
],
/// GPIO Pull-up/down Clock Register 0
GPPUDCLK0 [
/// Pin 15
PUDCLK15 OFFSET(15) NUMBITS(1) [
NoEffect = 0,
AssertClock = 1
],
/// Pin 14
PUDCLK14 OFFSET(14) NUMBITS(1) [
NoEffect = 0,
AssertClock = 1
]
]
}
pub const GPFSEL1: *const ReadWrite<u32, GPFSEL1::Register> =
(MMIO_BASE + 0x0020_0004) as *const ReadWrite<u32, GPFSEL1::Register>;
pub const GPPUD: *const ReadWrite<u32> = (MMIO_BASE + 0x0020_0094) as *const ReadWrite<u32>;
pub const GPPUDCLK0: *const ReadWrite<u32, GPPUDCLK0::Register> =
(MMIO_BASE + 0x0020_0098) as *const ReadWrite<u32, GPPUDCLK0::Register>;

@ -26,7 +26,9 @@
extern crate cortex_a; extern crate cortex_a;
extern crate raspi3_glue; extern crate raspi3_glue;
extern crate volatile_register;
#[macro_use]
extern crate register;
const MMIO_BASE: u32 = 0x3F00_0000; const MMIO_BASE: u32 = 0x3F00_0000;

@ -25,20 +25,27 @@
use super::MMIO_BASE; use super::MMIO_BASE;
use core::ops; use core::ops;
use cortex_a::asm; use cortex_a::asm;
use volatile_register::{RO, WO}; use register::mmio::{ReadOnly, WriteOnly};
register_bitfields! {
u32,
STATUS [
FULL OFFSET(31) NUMBITS(1) [],
EMPTY OFFSET(30) NUMBITS(1) []
]
}
const VIDEOCORE_MBOX: u32 = MMIO_BASE + 0xB880; const VIDEOCORE_MBOX: u32 = MMIO_BASE + 0xB880;
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[repr(C)] #[repr(C)]
pub struct RegisterBlock { pub struct RegisterBlock {
READ: RO<u32>, // 0x00 READ: ReadOnly<u32>, // 0x00
__reserved_0: [u32; 3], // 0x04 __reserved_0: [u32; 5], // 0x04
POLL: RO<u32>, // 0x10 STATUS: ReadOnly<u32, STATUS::Register>, // 0x18
SENDER: RO<u32>, // 0x14 __reserved_1: u32, // 0x1C
STATUS: RO<u32>, // 0x18 WRITE: WriteOnly<u32>, // 0x20
CONFIG: RO<u32>, // 0x1C
WRITE: WO<u32>, // 0x20
} }
// Custom errors // Custom errors
@ -71,17 +78,13 @@ mod response {
} }
pub const REQUEST: u32 = 0; pub const REQUEST: u32 = 0;
const FULL: u32 = 0x8000_0000;
const EMPTY: u32 = 0x4000_0000;
// Public interface to the mailbox // Public interface to the mailbox
#[repr(C)] #[repr(C)]
#[repr(align(16))]
pub struct Mbox { pub struct Mbox {
// The address for buffer needs to be 16-byte aligned so that the // The address for buffer needs to be 16-byte aligned so that the
// Videcore can handle it properly. We don't take precautions here // Videcore can handle it properly.
// to achieve that, but for now it just works. Since alignment of
// data structures in Rust is a bit of a hassle right now, we just
// close our eyes and roll with it.
pub buffer: [u32; 36], pub buffer: [u32; 36],
} }
@ -117,34 +120,33 @@ impl Mbox {
pub fn call(&self, channel: u32) -> Result<()> { pub fn call(&self, channel: u32) -> Result<()> {
// wait until we can write to the mailbox // wait until we can write to the mailbox
loop { loop {
if (self.STATUS.read() & FULL) != FULL { if !self.STATUS.is_set(STATUS::FULL) {
break; break;
} }
asm::nop(); asm::nop();
} }
let buf_ptr = self.buffer.as_ptr() as u32;
// write the address of our message to the mailbox with channel identifier // write the address of our message to the mailbox with channel identifier
unsafe { self.WRITE.set((buf_ptr & !0xF) | (channel & 0xF));
self.WRITE
.write(((self.buffer.as_ptr() as u32) & !0xF) | (channel & 0xF))
};
// now wait for the response // now wait for the response
loop { loop {
// is there a response? // is there a response?
loop { loop {
if (self.STATUS.read() & EMPTY) != EMPTY { if !self.STATUS.is_set(STATUS::EMPTY) {
break; break;
} }
asm::nop(); asm::nop();
} }
let resp: u32 = self.READ.read(); let resp: u32 = self.READ.get();
// is it a response to our message? // is it a response to our message?
if ((resp & 0xF) == channel) && ((resp & !0xF) == (self.buffer.as_ptr() as u32)) { if ((resp & 0xF) == channel) && ((resp & !0xF) == buf_ptr) {
// is it a valid successful response? // is it a valid successful response?
return match self.buffer[1] { return match self.buffer[1] {
response::SUCCESS => Ok(()), response::SUCCESS => Ok(()),

@ -25,18 +25,37 @@
use super::MMIO_BASE; use super::MMIO_BASE;
use core::ops; use core::ops;
use cortex_a::asm; use cortex_a::asm;
use volatile_register::*; use register::mmio::*;
register_bitfields! {
u32,
CTRL [
ENABLE OFFSET(0) NUMBITS(1) [
True = 1,
False = 0
]
],
INT_MASK [
INT_OFF OFFSET(0) NUMBITS(1) [
True = 1,
False = 0
]
]
}
const RNG_BASE: u32 = MMIO_BASE + 0x104_000; const RNG_BASE: u32 = MMIO_BASE + 0x104_000;
const RNG_WARMUP_COUNT: u32 = 0x40_000;
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[repr(C)] #[repr(C)]
pub struct RegisterBlock { pub struct RegisterBlock {
CTRL: RW<u32>, // 0x00 CTRL: ReadWrite<u32, CTRL::Register>, // 0x00
STATUS: RW<u32>, // 0x04 STATUS: ReadWrite<u32>, // 0x04
DATA: RO<u32>, // 0x08 DATA: ReadOnly<u32>, // 0x08
__reserved_0: u32, // 0x0c __reserved_0: u32, // 0x0c
INT_MASK: RW<u32>, // 0x10 INT_MASK: ReadWrite<u32, INT_MASK::Register>, // 0x10
} }
/// Public interface to the RNG /// Public interface to the RNG
@ -62,29 +81,26 @@ impl Rng {
/// Initialize the RNG /// Initialize the RNG
pub fn init(&self) { pub fn init(&self) {
unsafe { // Disable interrupts
self.STATUS.write(0x40_000); self.INT_MASK.modify(INT_MASK::INT_OFF::True);
// mask interrupt
self.INT_MASK.modify(|x| x | 1);
// enable // Set warm-up count and enable
self.CTRL.modify(|x| x | 1); self.STATUS.set(RNG_WARMUP_COUNT);
} self.CTRL.modify(CTRL::ENABLE::True);
}
/// Return a random number between [min..max]
pub fn rand(&self, min: u32, max: u32) -> u32 {
// wait for gaining some entropy // wait for gaining some entropy
loop { loop {
if (self.STATUS.read() >> 24) != 0 { if (self.STATUS.get() >> 24) != 0 {
break; break;
} }
asm::nop(); asm::nop();
} }
}
/// Return a random number between [min..max] let r = self.DATA.get();
pub fn rand(&self, min: u32, max: u32) -> u32 {
let r = self.DATA.read();
r % (max - min) + min r % (max - min) + min
} }

@ -23,29 +23,117 @@
*/ */
use super::MMIO_BASE; use super::MMIO_BASE;
use core::ops; use core::{
use core::sync::atomic::{compiler_fence, Ordering}; ops,
sync::atomic::{compiler_fence, Ordering},
};
use cortex_a::asm; use cortex_a::asm;
use gpio; use gpio;
use mbox; use mbox;
use volatile_register::*; use register::mmio::*;
// PL011 UART registers.
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
register_bitfields! {
u32,
/// Flag Register
FR [
/// Transmit FIFO full. The meaning of this bit depends on the
/// state of the FEN bit in the UARTLCR_ LCRH Register. If the
/// FIFO is disabled, this bit is set when the transmit
/// holding register is full. If the FIFO is enabled, the TXFF
/// bit is set when the transmit FIFO is full.
TXFF OFFSET(5) NUMBITS(1) [],
/// Receive FIFO empty. The meaning of this bit depends on the
/// state of the FEN bit in the UARTLCR_H Register. If the
/// FIFO is disabled, this bit is set when the receive holding
/// register is empty. If the FIFO is enabled, the RXFE bit is
/// set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) []
],
/// Integer Baud rate divisor
IBRD [
/// Integer Baud rate divisor
IBRD OFFSET(0) NUMBITS(16) []
],
/// Fractional Baud rate divisor
FBRD [
/// Fractional Baud rate divisor
FBRD OFFSET(0) NUMBITS(6) []
],
/// Line Control register
LCRH [
/// Word length. These bits indicate the number of data bits
/// transmitted or received in a frame.
WLEN OFFSET(5) NUMBITS(2) [
FiveBit = 0b00,
SixBit = 0b01,
SevenBit = 0b10,
EightBit = 0b11
]
],
/// Control Register
CR [
/// Receive enable. If this bit is set to 1, the receive
/// section of the UART is enabled. Data reception occurs for
/// UART signals. When the UART is disabled in the middle of
/// reception, it completes the current character before
/// stopping.
RXE OFFSET(9) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Transmit enable. If this bit is set to 1, the transmit
/// section of the UART is enabled. Data transmission occurs
/// for UART signals. When the UART is disabled in the middle
/// of transmission, it completes the current character before
/// stopping.
TXE OFFSET(8) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// UART enable
UARTEN OFFSET(0) NUMBITS(1) [
/// If the UART is disabled in the middle of transmission
/// or reception, it completes the current character
/// before stopping.
Disabled = 0,
Enabled = 1
]
],
/// Interupt Clear Register
ICR [
/// Meta field for all pending interrupts
ALL OFFSET(0) NUMBITS(11) []
]
}
const UART_BASE: u32 = MMIO_BASE + 0x20_1000; const UART_BASE: u32 = MMIO_BASE + 0x20_1000;
// PL011 UART registers
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[repr(C)] #[repr(C)]
pub struct RegisterBlock { pub struct RegisterBlock {
DR: RW<u32>, // 0x00 DR: ReadWrite<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04 __reserved_0: [u32; 5], // 0x04
FR: RO<u32>, // 0x18 FR: ReadOnly<u32, FR::Register>, // 0x18
__reserved_1: [u32; 2], // 0x1c __reserved_1: [u32; 2], // 0x1c
IBRD: WO<u32>, // 0x24 IBRD: WriteOnly<u32, IBRD::Register>, // 0x24
FBRD: WO<u32>, // 0x28 FBRD: WriteOnly<u32, FBRD::Register>, // 0x28
LCRH: WO<u32>, // 0x2C LCRH: WriteOnly<u32, LCRH::Register>, // 0x2C
CR: WO<u32>, // 0x30 CR: WriteOnly<u32, CR::Register>, // 0x30
__reserved_2: [u32; 4], // 0x34 __reserved_2: [u32; 4], // 0x34
ICR: WO<u32>, // 0x44 ICR: WriteOnly<u32, ICR::Register>, // 0x44
} }
pub enum UartError { pub enum UartError {
@ -76,7 +164,7 @@ impl Uart {
///Set baud rate and characteristics (115200 8N1) and map to GPIO ///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self, mbox: &mut mbox::Mbox) -> Result<()> { pub fn init(&self, mbox: &mut mbox::Mbox) -> Result<()> {
// turn off UART0 // turn off UART0
unsafe { self.CR.write(0) }; self.CR.set(0);
// set up clock for consistent divisor values // set up clock for consistent divisor values
mbox.buffer[0] = 9 * 4; mbox.buffer[0] = 9 * 4;
@ -100,33 +188,30 @@ impl Uart {
// map UART0 to GPIO pins // map UART0 to GPIO pins
unsafe { unsafe {
(*gpio::GPFSEL1).modify(|x| { (*gpio::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0);
// Modify with a closure
let mut ret = x;
ret &= !((7 << 12) | (7 << 15)); // gpio14, gpio15
ret |= (4 << 12) | (4 << 15); // alt0
ret
});
(*gpio::GPPUD).write(0); // enable pins 14 and 15 (*gpio::GPPUD).set(0); // enable pins 14 and 15
for _ in 0..150 { for _ in 0..150 {
asm::nop(); asm::nop();
} }
(*gpio::GPPUDCLK0).write((1 << 14) | (1 << 15)); (*gpio::GPPUDCLK0).modify(
gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock,
);
for _ in 0..150 { for _ in 0..150 {
asm::nop(); asm::nop();
} }
(*gpio::GPPUDCLK0).write(0);
self.ICR.write(0x7FF); // clear interrupts (*gpio::GPPUDCLK0).set(0);
self.IBRD.write(2); // 115200 baud
self.FBRD.write(0xB);
self.LCRH.write(0b11 << 5); // 8n1
self.CR.write(0x301); // enable Tx, Rx, FIFO
} }
self.ICR.write(ICR::ALL::CLEAR);
self.IBRD.write(IBRD::IBRD.val(2)); // Results in 115200 baud
self.FBRD.write(FBRD::FBRD.val(0xB));
self.LCRH.write(LCRH::WLEN::EightBit); // 8N1
self.CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
Ok(()) Ok(())
} }
@ -134,7 +219,7 @@ impl Uart {
pub fn send(&self, c: char) { pub fn send(&self, c: char) {
// wait until we can send // wait until we can send
loop { loop {
if (self.FR.read() & 0x20) != 0x20 { if !self.FR.is_set(FR::TXFF) {
break; break;
} }
@ -142,14 +227,14 @@ impl Uart {
} }
// write the character to the buffer // write the character to the buffer
unsafe { self.DR.write(c as u32) }; self.DR.set(c as u32);
} }
/// Receive a character /// Receive a character
pub fn getc(&self) -> char { pub fn getc(&self) -> char {
// wait until something is in the buffer // wait until something is in the buffer
loop { loop {
if (self.FR.read() & 0x10) != 0x10 { if !self.FR.is_set(FR::RXFE) {
break; break;
} }
@ -157,7 +242,7 @@ impl Uart {
} }
// read it and return // read it and return
let mut ret = self.DR.read() as u8 as char; let mut ret = self.DR.get() as u8 as char;
// convert carrige return to newline // convert carrige return to newline
if ret == '\r' { if ret == '\r' {

36
09_delays/Cargo.lock generated

@ -1,23 +1,18 @@
[[package]]
name = "bitflags"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "cortex-a" name = "cortex-a"
version = "0.1.3" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "kernel8" name = "kernel8"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"cortex-a 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-a 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"raspi3_glue 0.1.0", "raspi3_glue 0.1.0",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -34,28 +29,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "raspi3_glue" name = "raspi3_glue"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"cortex-a 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-a 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "vcell" name = "register"
version = "0.1.0" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "volatile-register" name = "tock-registers"
version = "0.2.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata] [metadata]
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" "checksum cortex-a 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24174fbfe16d46844856c0f5e16ee15f7adbacfd87cb0148d34463dd34b54eeb"
"checksum cortex-a 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a123fa5a346531ed0fc9fcb8f69ca34d9b8c55b15162731945d14c4d461c5bfe"
"checksum panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6bc796c620f27056d4ffe7c558533fd67ae5af0fd8e919fbe38de803368af73e" "checksum panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6bc796c620f27056d4ffe7c558533fd67ae5af0fd8e919fbe38de803368af73e"
"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" "checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
"checksum vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d" "checksum register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e985243ba7e1c336b62444ef2a10d7bd87cf41a222285ae3de605c859006479"
"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" "checksum tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2acc33f980e23cee18d234a32d0637fbc1ea55e13ab04012fa857b899fa1b7a9"

@ -5,5 +5,5 @@ authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies] [dependencies]
raspi3_glue = { path = "raspi3_glue" } raspi3_glue = { path = "raspi3_glue" }
cortex-a = "0.1.3" cortex-a = "1.0.0"
volatile-register = "0.2.0" register = "0.1.1"

Binary file not shown.

@ -4,6 +4,6 @@ version = "0.1.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"] authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies] [dependencies]
cortex-a = "0.1.3" cortex-a = "1.0.0"
panic-abort = "0.2.0" panic-abort = "0.2.0"
r0 = "0.2.2" r0 = "0.2.2"

@ -30,9 +30,6 @@ extern crate cortex_a;
extern crate panic_abort; extern crate panic_abort;
extern crate r0; extern crate r0;
use core::ptr;
use cortex_a::{asm, register};
#[lang = "start"] #[lang = "start"]
extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize
where where
@ -53,6 +50,8 @@ impl Termination for () {
} }
unsafe fn reset() -> ! { unsafe fn reset() -> ! {
use core::ptr;
extern "C" { extern "C" {
fn main(argc: isize, argv: *const *const u8) -> isize; fn main(argc: isize, argv: *const *const u8) -> isize;
@ -77,11 +76,13 @@ unsafe fn reset() -> ! {
#[link_section = ".text.boot"] #[link_section = ".text.boot"]
#[no_mangle] #[no_mangle]
pub extern "C" fn _boot_cores() -> ! { pub extern "C" fn _boot_cores() -> ! {
match register::MPIDR_EL1::read_raw() & 0x3 { use cortex_a::{asm, regs::mpidr_el1::*, regs::sp::*};
0 => unsafe {
register::SP::write_raw(0x80_000); match MPIDR_EL1.get() & 0x3 {
reset() 0 => {
}, SP.set(0x80_000);
unsafe { reset() }
}
_ => loop { _ => loop {
// if not core0, infinitely wait for events // if not core0, infinitely wait for events
asm::wfe(); asm::wfe();

@ -24,9 +24,11 @@
use super::MMIO_BASE; use super::MMIO_BASE;
use core::ops; use core::ops;
use cortex_a::{asm, use cortex_a::{
register::{CNTFRQ_EL0, CNTP_CTL_EL0, CNTP_TVAL_EL0}}; asm,
use volatile_register::*; regs::{cntfrq_el0::*, cntp_ctl_el0::*, cntp_tval_el0::*},
};
use register::mmio::*;
const DELAY_BASE: u32 = MMIO_BASE + 0x3004; const DELAY_BASE: u32 = MMIO_BASE + 0x3004;
@ -38,8 +40,8 @@ const DELAY_BASE: u32 = MMIO_BASE + 0x3004;
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[repr(C)] #[repr(C)]
pub struct RegisterBlock { pub struct RegisterBlock {
SYSTMR_LO: RO<u32>, // 0x00 SYSTMR_LO: ReadOnly<u32>, // 0x00
SYSTMR_HI: RO<u32>, // 0x04 SYSTMR_HI: ReadOnly<u32>, // 0x04
} }
/// Public interface to the BCM System Timer /// Public interface to the BCM System Timer
@ -66,16 +68,16 @@ impl SysTmr {
/// Get System Timer's counter /// Get System Timer's counter
pub fn get_system_timer(&self) -> u64 { pub fn get_system_timer(&self) -> u64 {
// Since it is MMIO, we must emit two separate 32 bit reads // Since it is MMIO, we must emit two separate 32 bit reads
let mut hi = self.SYSTMR_HI.read(); let mut hi = self.SYSTMR_HI.get();
// We have to repeat if high word changed during read. It // We have to repeat if high word changed during read. It
// looks a bit odd, but clippy insists that this is idiomatic // looks a bit odd, but clippy insists that this is idiomatic
// Rust! // Rust!
let lo = if hi != self.SYSTMR_HI.read() { let lo = if hi != self.SYSTMR_HI.get() {
hi = self.SYSTMR_HI.read(); hi = self.SYSTMR_HI.get();
self.SYSTMR_LO.read() self.SYSTMR_LO.get()
} else { } else {
self.SYSTMR_LO.read() self.SYSTMR_LO.get()
}; };
// Compose long int value // Compose long int value
@ -107,35 +109,26 @@ impl SysTmr {
/// Wait N microsec (ARM CPU only) /// Wait N microsec (ARM CPU only)
pub fn wait_msec(n: u32) { pub fn wait_msec(n: u32) {
// Get the counter frequency // Get the counter frequency
let frq = CNTFRQ_EL0::read_raw(); let frq = CNTFRQ_EL0.get();
// Calculate number of ticks // Calculate number of ticks
let tval = (frq as u32 / 1000) * n; let tval = (frq as u32 / 1000) * n;
unsafe { // Set the compare value register
// Set the compare value register CNTP_TVAL_EL0.set(tval);
CNTP_TVAL_EL0::write_raw(tval);
// Kick off the counting // Kick off the counting // Disable timer interrupt
CNTP_CTL_EL0::modify_flags(|r| { CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET);
r.set(CNTP_CTL_EL0::ENABLE, true);
r.set(CNTP_CTL_EL0::IMASK, true); // Disable timer interrupt
});
}
loop { loop {
// ISTATUS will be one when cval ticks have passed. Continuously check it. // ISTATUS will be one when cval ticks have passed. Continuously check it.
if CNTP_CTL_EL0::read_flags().contains(CNTP_CTL_EL0::ISTATUS) { if CNTP_CTL_EL0.is_set(CNTP_CTL_EL0::ISTATUS) {
break; break;
} }
} }
// Disable counting again // Disable counting again
unsafe { CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
CNTP_CTL_EL0::modify_flags(|r| {
r.set(CNTP_CTL_EL0::ENABLE, false);
});
}
} }
/* /*

@ -23,8 +23,54 @@
*/ */
use super::MMIO_BASE; use super::MMIO_BASE;
use volatile_register::RW; use register::mmio::ReadWrite;
pub const GPFSEL1: *const RW<u32> = (MMIO_BASE + 0x0020_0004) as *const RW<u32>; // Descriptions taken from
pub const GPPUD: *const RW<u32> = (MMIO_BASE + 0x0020_0094) as *const RW<u32>; // https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
pub const GPPUDCLK0: *const RW<u32> = (MMIO_BASE + 0x0020_0098) as *const RW<u32>; register_bitfields! {
u32,
/// GPIO Function Select 1
GPFSEL1 [
/// Pin 15
FSEL15 OFFSET(15) NUMBITS(3) [
Input = 0b000,
Output = 0b001,
RXD0 = 0b100, // UART0 - Alternate function 0
RXD1 = 0b010 // Mini UART - Alternate function 5
],
/// Pin 14
FSEL14 OFFSET(12) NUMBITS(3) [
Input = 0b000,
Output = 0b001,
TXD0 = 0b100, // UART0 - Alternate function 0
TXD1 = 0b010 // Mini UART - Alternate function 5
]
],
/// GPIO Pull-up/down Clock Register 0
GPPUDCLK0 [
/// Pin 15
PUDCLK15 OFFSET(15) NUMBITS(1) [
NoEffect = 0,
AssertClock = 1
],
/// Pin 14
PUDCLK14 OFFSET(14) NUMBITS(1) [
NoEffect = 0,
AssertClock = 1
]
]
}
pub const GPFSEL1: *const ReadWrite<u32, GPFSEL1::Register> =
(MMIO_BASE + 0x0020_0004) as *const ReadWrite<u32, GPFSEL1::Register>;
pub const GPPUD: *const ReadWrite<u32> = (MMIO_BASE + 0x0020_0094) as *const ReadWrite<u32>;
pub const GPPUDCLK0: *const ReadWrite<u32, GPPUDCLK0::Register> =
(MMIO_BASE + 0x0020_0098) as *const ReadWrite<u32, GPPUDCLK0::Register>;

@ -26,7 +26,9 @@
extern crate cortex_a; extern crate cortex_a;
extern crate raspi3_glue; extern crate raspi3_glue;
extern crate volatile_register;
#[macro_use]
extern crate register;
const MMIO_BASE: u32 = 0x3F00_0000; const MMIO_BASE: u32 = 0x3F00_0000;

@ -25,20 +25,27 @@
use super::MMIO_BASE; use super::MMIO_BASE;
use core::ops; use core::ops;
use cortex_a::asm; use cortex_a::asm;
use volatile_register::{RO, WO}; use register::mmio::{ReadOnly, WriteOnly};
register_bitfields! {
u32,
STATUS [
FULL OFFSET(31) NUMBITS(1) [],
EMPTY OFFSET(30) NUMBITS(1) []
]
}
const VIDEOCORE_MBOX: u32 = MMIO_BASE + 0xB880; const VIDEOCORE_MBOX: u32 = MMIO_BASE + 0xB880;
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[repr(C)] #[repr(C)]
pub struct RegisterBlock { pub struct RegisterBlock {
READ: RO<u32>, // 0x00 READ: ReadOnly<u32>, // 0x00
__reserved_0: [u32; 3], // 0x04 __reserved_0: [u32; 5], // 0x04
POLL: RO<u32>, // 0x10 STATUS: ReadOnly<u32, STATUS::Register>, // 0x18
SENDER: RO<u32>, // 0x14 __reserved_1: u32, // 0x1C
STATUS: RO<u32>, // 0x18 WRITE: WriteOnly<u32>, // 0x20
CONFIG: RO<u32>, // 0x1C
WRITE: WO<u32>, // 0x20
} }
// Custom errors // Custom errors
@ -71,17 +78,13 @@ mod response {
} }
pub const REQUEST: u32 = 0; pub const REQUEST: u32 = 0;
const FULL: u32 = 0x8000_0000;
const EMPTY: u32 = 0x4000_0000;
// Public interface to the mailbox // Public interface to the mailbox
#[repr(C)] #[repr(C)]
#[repr(align(16))]
pub struct Mbox { pub struct Mbox {
// The address for buffer needs to be 16-byte aligned so that the // The address for buffer needs to be 16-byte aligned so that the
// Videcore can handle it properly. We don't take precautions here // Videcore can handle it properly.
// to achieve that, but for now it just works. Since alignment of
// data structures in Rust is a bit of a hassle right now, we just
// close our eyes and roll with it.
pub buffer: [u32; 36], pub buffer: [u32; 36],
} }
@ -117,34 +120,33 @@ impl Mbox {
pub fn call(&self, channel: u32) -> Result<()> { pub fn call(&self, channel: u32) -> Result<()> {
// wait until we can write to the mailbox // wait until we can write to the mailbox
loop { loop {
if (self.STATUS.read() & FULL) != FULL { if !self.STATUS.is_set(STATUS::FULL) {
break; break;
} }
asm::nop(); asm::nop();
} }
let buf_ptr = self.buffer.as_ptr() as u32;
// write the address of our message to the mailbox with channel identifier // write the address of our message to the mailbox with channel identifier
unsafe { self.WRITE.set((buf_ptr & !0xF) | (channel & 0xF));
self.WRITE
.write(((self.buffer.as_ptr() as u32) & !0xF) | (channel & 0xF))
};
// now wait for the response // now wait for the response
loop { loop {
// is there a response? // is there a response?
loop { loop {
if (self.STATUS.read() & EMPTY) != EMPTY { if !self.STATUS.is_set(STATUS::EMPTY) {
break; break;
} }
asm::nop(); asm::nop();
} }
let resp: u32 = self.READ.read(); let resp: u32 = self.READ.get();
// is it a response to our message? // is it a response to our message?
if ((resp & 0xF) == channel) && ((resp & !0xF) == (self.buffer.as_ptr() as u32)) { if ((resp & 0xF) == channel) && ((resp & !0xF) == buf_ptr) {
// is it a valid successful response? // is it a valid successful response?
return match self.buffer[1] { return match self.buffer[1] {
response::SUCCESS => Ok(()), response::SUCCESS => Ok(()),

@ -23,30 +23,118 @@
*/ */
use super::MMIO_BASE; use super::MMIO_BASE;
use core::ops; use core::{
use core::sync::atomic::{compiler_fence, Ordering}; ops,
sync::atomic::{compiler_fence, Ordering},
};
use cortex_a::asm; use cortex_a::asm;
use delays; use delays;
use gpio; use gpio;
use mbox; use mbox;
use volatile_register::*; use register::mmio::*;
// PL011 UART registers.
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
register_bitfields! {
u32,
/// Flag Register
FR [
/// Transmit FIFO full. The meaning of this bit depends on the
/// state of the FEN bit in the UARTLCR_ LCRH Register. If the
/// FIFO is disabled, this bit is set when the transmit
/// holding register is full. If the FIFO is enabled, the TXFF
/// bit is set when the transmit FIFO is full.
TXFF OFFSET(5) NUMBITS(1) [],
/// Receive FIFO empty. The meaning of this bit depends on the
/// state of the FEN bit in the UARTLCR_H Register. If the
/// FIFO is disabled, this bit is set when the receive holding
/// register is empty. If the FIFO is enabled, the RXFE bit is
/// set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) []
],
/// Integer Baud rate divisor
IBRD [
/// Integer Baud rate divisor
IBRD OFFSET(0) NUMBITS(16) []
],
/// Fractional Baud rate divisor
FBRD [
/// Fractional Baud rate divisor
FBRD OFFSET(0) NUMBITS(6) []
],
/// Line Control register
LCRH [
/// Word length. These bits indicate the number of data bits
/// transmitted or received in a frame.
WLEN OFFSET(5) NUMBITS(2) [
FiveBit = 0b00,
SixBit = 0b01,
SevenBit = 0b10,
EightBit = 0b11
]
],
/// Control Register
CR [
/// Receive enable. If this bit is set to 1, the receive
/// section of the UART is enabled. Data reception occurs for
/// UART signals. When the UART is disabled in the middle of
/// reception, it completes the current character before
/// stopping.
RXE OFFSET(9) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Transmit enable. If this bit is set to 1, the transmit
/// section of the UART is enabled. Data transmission occurs
/// for UART signals. When the UART is disabled in the middle
/// of transmission, it completes the current character before
/// stopping.
TXE OFFSET(8) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// UART enable
UARTEN OFFSET(0) NUMBITS(1) [
/// If the UART is disabled in the middle of transmission
/// or reception, it completes the current character
/// before stopping.
Disabled = 0,
Enabled = 1
]
],
/// Interupt Clear Register
ICR [
/// Meta field for all pending interrupts
ALL OFFSET(0) NUMBITS(11) []
]
}
const UART_BASE: u32 = MMIO_BASE + 0x20_1000; const UART_BASE: u32 = MMIO_BASE + 0x20_1000;
// PL011 UART registers
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[repr(C)] #[repr(C)]
pub struct RegisterBlock { pub struct RegisterBlock {
DR: RW<u32>, // 0x00 DR: ReadWrite<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04 __reserved_0: [u32; 5], // 0x04
FR: RO<u32>, // 0x18 FR: ReadOnly<u32, FR::Register>, // 0x18
__reserved_1: [u32; 2], // 0x1c __reserved_1: [u32; 2], // 0x1c
IBRD: WO<u32>, // 0x24 IBRD: WriteOnly<u32, IBRD::Register>, // 0x24
FBRD: WO<u32>, // 0x28 FBRD: WriteOnly<u32, FBRD::Register>, // 0x28
LCRH: WO<u32>, // 0x2C LCRH: WriteOnly<u32, LCRH::Register>, // 0x2C
CR: WO<u32>, // 0x30 CR: WriteOnly<u32, CR::Register>, // 0x30
__reserved_2: [u32; 4], // 0x34 __reserved_2: [u32; 4], // 0x34
ICR: WO<u32>, // 0x44 ICR: WriteOnly<u32, ICR::Register>, // 0x44
} }
pub enum UartError { pub enum UartError {
@ -77,7 +165,7 @@ impl Uart {
///Set baud rate and characteristics (115200 8N1) and map to GPIO ///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self, mbox: &mut mbox::Mbox) -> Result<()> { pub fn init(&self, mbox: &mut mbox::Mbox) -> Result<()> {
// turn off UART0 // turn off UART0
unsafe { self.CR.write(0) }; self.CR.set(0);
// set up clock for consistent divisor values // set up clock for consistent divisor values
mbox.buffer[0] = 9 * 4; mbox.buffer[0] = 9 * 4;
@ -101,30 +189,26 @@ impl Uart {
// map UART0 to GPIO pins // map UART0 to GPIO pins
unsafe { unsafe {
(*gpio::GPFSEL1).modify(|x| { (*gpio::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0);
// Modify with a closure
let mut ret = x;
ret &= !((7 << 12) | (7 << 15)); // gpio14, gpio15
ret |= (4 << 12) | (4 << 15); // alt0
ret (*gpio::GPPUD).set(0); // enable pins 14 and 15
});
(*gpio::GPPUD).write(0); // enable pins 14 and 15
delays::wait_cycles(150); delays::wait_cycles(150);
(*gpio::GPPUDCLK0).write((1 << 14) | (1 << 15)); (*gpio::GPPUDCLK0).modify(
gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock,
);
delays::wait_cycles(150); delays::wait_cycles(150);
(*gpio::GPPUDCLK0).write(0); (*gpio::GPPUDCLK0).set(0);
self.ICR.write(0x7FF); // clear interrupts
self.IBRD.write(2); // 115200 baud
self.FBRD.write(0xB);
self.LCRH.write(0b11 << 5); // 8n1
self.CR.write(0x301); // enable Tx, Rx, FIFO
} }
self.ICR.write(ICR::ALL::CLEAR);
self.IBRD.write(IBRD::IBRD.val(2)); // Results in 115200 baud
self.FBRD.write(FBRD::FBRD.val(0xB));
self.LCRH.write(LCRH::WLEN::EightBit); // 8N1
self.CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
Ok(()) Ok(())
} }
@ -132,7 +216,7 @@ impl Uart {
pub fn send(&self, c: char) { pub fn send(&self, c: char) {
// wait until we can send // wait until we can send
loop { loop {
if (self.FR.read() & 0x20) != 0x20 { if !self.FR.is_set(FR::TXFF) {
break; break;
} }
@ -140,14 +224,14 @@ impl Uart {
} }
// write the character to the buffer // write the character to the buffer
unsafe { self.DR.write(c as u32) }; self.DR.set(c as u32);
} }
/// Receive a character /// Receive a character
pub fn getc(&self) -> char { pub fn getc(&self) -> char {
// wait until something is in the buffer // wait until something is in the buffer
loop { loop {
if (self.FR.read() & 0x10) != 0x10 { if !self.FR.is_set(FR::RXFE) {
break; break;
} }
@ -155,7 +239,7 @@ impl Uart {
} }
// read it and return // read it and return
let mut ret = self.DR.read() as u8 as char; let mut ret = self.DR.get() as u8 as char;
// convert carrige return to newline // convert carrige return to newline
if ret == '\r' { if ret == '\r' {

48
0A_power/Cargo.lock generated

@ -1,23 +1,26 @@
[[package]] [[package]]
name = "bitflags" name = "cortex-a"
version = "1.0.1" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/andre-richter/cortex-a.git?branch=registers#aae179e34af014725706bb1821e9e285674db97b"
dependencies = [
"register 0.1.1 (git+https://github.com/andre-richter/register-rs.git)",
]
[[package]] [[package]]
name = "cortex-a" name = "cortex-a"
version = "0.1.3" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "kernel8" name = "kernel8"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"cortex-a 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-a 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"raspi3_glue 0.1.0", "raspi3_glue 0.1.0",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -34,28 +37,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "raspi3_glue" name = "raspi3_glue"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"cortex-a 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-a 1.0.0 (git+https://github.com/andre-richter/cortex-a.git?branch=registers)",
"panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "vcell" name = "register"
version = "0.1.0" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/andre-richter/register-rs.git#e0fdae3f2dc1e9691bbe4d57519e746f0d9aada0"
dependencies = [
"tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "volatile-register" name = "register"
version = "0.2.0" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "tock-registers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata] [metadata]
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" "checksum cortex-a 1.0.0 (git+https://github.com/andre-richter/cortex-a.git?branch=registers)" = "<none>"
"checksum cortex-a 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a123fa5a346531ed0fc9fcb8f69ca34d9b8c55b15162731945d14c4d461c5bfe" "checksum cortex-a 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24174fbfe16d46844856c0f5e16ee15f7adbacfd87cb0148d34463dd34b54eeb"
"checksum panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6bc796c620f27056d4ffe7c558533fd67ae5af0fd8e919fbe38de803368af73e" "checksum panic-abort 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6bc796c620f27056d4ffe7c558533fd67ae5af0fd8e919fbe38de803368af73e"
"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" "checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
"checksum vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d" "checksum register 0.1.1 (git+https://github.com/andre-richter/register-rs.git)" = "<none>"
"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" "checksum register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e985243ba7e1c336b62444ef2a10d7bd87cf41a222285ae3de605c859006479"
"checksum tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2acc33f980e23cee18d234a32d0637fbc1ea55e13ab04012fa857b899fa1b7a9"

@ -5,5 +5,5 @@ authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies] [dependencies]
raspi3_glue = { path = "raspi3_glue" } raspi3_glue = { path = "raspi3_glue" }
cortex-a = "0.1.3" cortex-a = "1.0.0"
volatile-register = "0.2.0" register = "0.1.1"

@ -9,8 +9,14 @@ transistor connected to a data GPIO pin for example).
## power.rs ## power.rs
Unfortunately, the documentation about the PM interface is very very rare. We
will therefore more or less implement a carbon copy of respective functions of
Linux'
[bcm2835_wdt.c](https://github.com/torvalds/linux/blob/master/drivers/watchdog/bcm2835_wdt.c)
driver.
The power management controller is one of the peripherals that are not emulated The power management controller is one of the peripherals that are not emulated
properly by QEMU. Our implementation works on real hardware though. properly by QEMU. Our implementation therefore works on real hardware only.
`Power::off(&self, mbox: &mut mbox::Mbox, gpio: &gpio::GPIO)` shuts down the `Power::off(&self, mbox: &mut mbox::Mbox, gpio: &gpio::GPIO)` shuts down the
board to an almost zero power consumption state. board to an almost zero power consumption state.

Binary file not shown.

@ -4,6 +4,6 @@ version = "0.1.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"] authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies] [dependencies]
cortex-a = "0.1.3" cortex-a = { git = "https://github.com/andre-richter/cortex-a.git", branch = "registers" }
panic-abort = "0.2.0" panic-abort = "0.2.0"
r0 = "0.2.2" r0 = "0.2.2"

@ -30,9 +30,6 @@ extern crate cortex_a;
extern crate panic_abort; extern crate panic_abort;
extern crate r0; extern crate r0;
use core::ptr;
use cortex_a::{asm, register};
#[lang = "start"] #[lang = "start"]
extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize
where where
@ -53,6 +50,8 @@ impl Termination for () {
} }
unsafe fn reset() -> ! { unsafe fn reset() -> ! {
use core::ptr;
extern "C" { extern "C" {
fn main(argc: isize, argv: *const *const u8) -> isize; fn main(argc: isize, argv: *const *const u8) -> isize;
@ -77,11 +76,13 @@ unsafe fn reset() -> ! {
#[link_section = ".text.boot"] #[link_section = ".text.boot"]
#[no_mangle] #[no_mangle]
pub extern "C" fn _boot_cores() -> ! { pub extern "C" fn _boot_cores() -> ! {
match register::MPIDR_EL1::read_raw() & 0x3 { use cortex_a::{asm, regs::mpidr_el1::*, regs::sp::*};
0 => unsafe {
register::SP::write_raw(0x80_000); match MPIDR_EL1.get() & 0x3 {
reset() 0 => {
}, SP.set(0x80_000);
unsafe { reset() }
}
_ => loop { _ => loop {
// if not core0, infinitely wait for events // if not core0, infinitely wait for events
asm::wfe(); asm::wfe();

@ -24,9 +24,11 @@
use super::MMIO_BASE; use super::MMIO_BASE;
use core::ops; use core::ops;
use cortex_a::{asm, use cortex_a::{
register::{CNTFRQ_EL0, CNTP_CTL_EL0, CNTP_TVAL_EL0}}; asm,
use volatile_register::*; regs::{cntfrq_el0::*, cntp_ctl_el0::*, cntp_tval_el0::*},
};
use register::mmio::*;
const DELAY_BASE: u32 = MMIO_BASE + 0x3004; const DELAY_BASE: u32 = MMIO_BASE + 0x3004;
@ -38,8 +40,8 @@ const DELAY_BASE: u32 = MMIO_BASE + 0x3004;
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[repr(C)] #[repr(C)]
pub struct RegisterBlock { pub struct RegisterBlock {
SYSTMR_LO: RO<u32>, // 0x00 SYSTMR_LO: ReadOnly<u32>, // 0x00
SYSTMR_HI: RO<u32>, // 0x04 SYSTMR_HI: ReadOnly<u32>, // 0x04
} }
/// Public interface to the BCM System Timer /// Public interface to the BCM System Timer
@ -66,16 +68,16 @@ impl SysTmr {
/// Get System Timer's counter /// Get System Timer's counter
pub fn get_system_timer(&self) -> u64 { pub fn get_system_timer(&self) -> u64 {
// Since it is MMIO, we must emit two separate 32 bit reads // Since it is MMIO, we must emit two separate 32 bit reads
let mut hi = self.SYSTMR_HI.read(); let mut hi = self.SYSTMR_HI.get();
// We have to repeat if high word changed during read. It // We have to repeat if high word changed during read. It
// looks a bit odd, but clippy insists that this is idiomatic // looks a bit odd, but clippy insists that this is idiomatic
// Rust! // Rust!
let lo = if hi != self.SYSTMR_HI.read() { let lo = if hi != self.SYSTMR_HI.get() {
hi = self.SYSTMR_HI.read(); hi = self.SYSTMR_HI.get();
self.SYSTMR_LO.read() self.SYSTMR_LO.get()
} else { } else {
self.SYSTMR_LO.read() self.SYSTMR_LO.get()
}; };
// Compose long int value // Compose long int value
@ -107,35 +109,26 @@ impl SysTmr {
/// Wait N microsec (ARM CPU only) /// Wait N microsec (ARM CPU only)
pub fn wait_msec(n: u32) { pub fn wait_msec(n: u32) {
// Get the counter frequency // Get the counter frequency
let frq = CNTFRQ_EL0::read_raw(); let frq = CNTFRQ_EL0.get();
// Calculate number of ticks // Calculate number of ticks
let tval = (frq as u32 / 1000) * n; let tval = (frq as u32 / 1000) * n;
unsafe { // Set the compare value register
// Set the compare value register CNTP_TVAL_EL0.set(tval);
CNTP_TVAL_EL0::write_raw(tval);
// Kick off the counting // Kick off the counting // Disable timer interrupt
CNTP_CTL_EL0::modify_flags(|r| { CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET);
r.set(CNTP_CTL_EL0::ENABLE, true);
r.set(CNTP_CTL_EL0::IMASK, true); // Disable timer interrupt
});
}
loop { loop {
// ISTATUS will be one when cval ticks have passed. Continuously check it. // ISTATUS will be one when cval ticks have passed. Continuously check it.
if CNTP_CTL_EL0::read_flags().contains(CNTP_CTL_EL0::ISTATUS) { if CNTP_CTL_EL0.is_set(CNTP_CTL_EL0::ISTATUS) {
break; break;
} }
} }
// Disable counting again // Disable counting again
unsafe { CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
CNTP_CTL_EL0::modify_flags(|r| {
r.set(CNTP_CTL_EL0::ENABLE, false);
});
}
} }
/* /*

@ -24,37 +24,78 @@
use super::MMIO_BASE; use super::MMIO_BASE;
use core::ops; use core::ops;
use volatile_register::RW; use register::mmio::ReadWrite;
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
register_bitfields! {
u32,
/// GPIO Function Select 1
GPFSEL1 [
/// Pin 15
FSEL15 OFFSET(15) NUMBITS(3) [
Input = 0b000,
Output = 0b001,
RXD0 = 0b100, // UART0 - Alternate function 0
RXD1 = 0b010 // Mini UART - Alternate function 5
],
/// Pin 14
FSEL14 OFFSET(12) NUMBITS(3) [
Input = 0b000,
Output = 0b001,
TXD0 = 0b100, // UART0 - Alternate function 0
TXD1 = 0b010 // Mini UART - Alternate function 5
]
],
/// GPIO Pull-up/down Clock Register 0
GPPUDCLK0 [
/// Pin 15
PUDCLK15 OFFSET(15) NUMBITS(1) [
NoEffect = 0,
AssertClock = 1
],
/// Pin 14
PUDCLK14 OFFSET(14) NUMBITS(1) [
NoEffect = 0,
AssertClock = 1
]
]
}
const GPIO_BASE: u32 = MMIO_BASE + 0x200_000; const GPIO_BASE: u32 = MMIO_BASE + 0x200_000;
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[repr(C)] #[repr(C)]
pub struct RegisterBlock { pub struct RegisterBlock {
pub GPFSEL0: RW<u32>, // 0x00 pub GPFSEL0: ReadWrite<u32>, // 0x00
pub GPFSEL1: RW<u32>, // 0x04 pub GPFSEL1: ReadWrite<u32, GPFSEL1::Register>, // 0x04
pub GPFSEL2: RW<u32>, // 0x08 pub GPFSEL2: ReadWrite<u32>, // 0x08
pub GPFSEL3: RW<u32>, // 0x0C pub GPFSEL3: ReadWrite<u32>, // 0x0C
pub GPFSEL4: RW<u32>, // 0x10 pub GPFSEL4: ReadWrite<u32>, // 0x10
pub GPFSEL5: RW<u32>, // 0x14 pub GPFSEL5: ReadWrite<u32>, // 0x14
__reserved_0: u32, // 0x18 __reserved_0: u32, // 0x18
GPSET0: RW<u32>, // 0x1C GPSET0: ReadWrite<u32>, // 0x1C
GPSET1: RW<u32>, // 0x20 GPSET1: ReadWrite<u32>, // 0x20
__reserved_1: u32, // __reserved_1: u32, //
GPCLR0: RW<u32>, // 0x28 GPCLR0: ReadWrite<u32>, // 0x28
__reserved_2: [u32; 2], // __reserved_2: [u32; 2], //
GPLEV0: RW<u32>, // 0x34 GPLEV0: ReadWrite<u32>, // 0x34
GPLEV1: RW<u32>, // 0x38 GPLEV1: ReadWrite<u32>, // 0x38
__reserved_3: u32, // __reserved_3: u32, //
GPEDS0: RW<u32>, // 0x40 GPEDS0: ReadWrite<u32>, // 0x40
GPEDS1: RW<u32>, // 0x44 GPEDS1: ReadWrite<u32>, // 0x44
__reserved_4: [u32; 7], // __reserved_4: [u32; 7], //
GPHEN0: RW<u32>, // 0x64 GPHEN0: ReadWrite<u32>, // 0x64
GPHEN1: RW<u32>, // 0x68 GPHEN1: ReadWrite<u32>, // 0x68
__reserved_5: [u32; 10], // __reserved_5: [u32; 10], //
pub GPPUD: RW<u32>, // 0x94 pub GPPUD: ReadWrite<u32>, // 0x94
pub GPPUDCLK0: RW<u32>, // 0x98 pub GPPUDCLK0: ReadWrite<u32, GPPUDCLK0::Register>, // 0x98
pub GPPUDCLK1: RW<u32>, // 0x9C pub GPPUDCLK1: ReadWrite<u32>, // 0x9C
} }
/// Public interface to the GPIO MMIO area /// Public interface to the GPIO MMIO area

@ -26,7 +26,9 @@
extern crate cortex_a; extern crate cortex_a;
extern crate raspi3_glue; extern crate raspi3_glue;
extern crate volatile_register;
#[macro_use]
extern crate register;
const MMIO_BASE: u32 = 0x3F00_0000; const MMIO_BASE: u32 = 0x3F00_0000;
@ -58,7 +60,7 @@ fn main() {
match c { match c {
'1' => { '1' => {
if power.off(&mut mbox, &gpio).is_err() { if power.off(&mut mbox, &gpio).is_err() {
uart.puts("Error in Power::off()"); uart.puts("Mailbox error in Power::off()");
} }
} }
'2' => power.reset(), '2' => power.reset(),

@ -25,20 +25,27 @@
use super::MMIO_BASE; use super::MMIO_BASE;
use core::ops; use core::ops;
use cortex_a::asm; use cortex_a::asm;
use volatile_register::{RO, WO}; use register::mmio::{ReadOnly, WriteOnly};
register_bitfields! {
u32,
STATUS [
FULL OFFSET(31) NUMBITS(1) [],
EMPTY OFFSET(30) NUMBITS(1) []
]
}
const VIDEOCORE_MBOX: u32 = MMIO_BASE + 0xB880; const VIDEOCORE_MBOX: u32 = MMIO_BASE + 0xB880;
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[repr(C)] #[repr(C)]
pub struct RegisterBlock { pub struct RegisterBlock {
READ: RO<u32>, // 0x00 READ: ReadOnly<u32>, // 0x00
__reserved_0: [u32; 3], // 0x04 __reserved_0: [u32; 5], // 0x04
POLL: RO<u32>, // 0x10 STATUS: ReadOnly<u32, STATUS::Register>, // 0x18
SENDER: RO<u32>, // 0x14 __reserved_1: u32, // 0x1C
STATUS: RO<u32>, // 0x18 WRITE: WriteOnly<u32>, // 0x20
CONFIG: RO<u32>, // 0x1C
WRITE: WO<u32>, // 0x20
} }
// Custom errors // Custom errors
@ -72,17 +79,13 @@ mod response {
} }
pub const REQUEST: u32 = 0; pub const REQUEST: u32 = 0;
const FULL: u32 = 0x8000_0000;
const EMPTY: u32 = 0x4000_0000;
// Public interface to the mailbox // Public interface to the mailbox
#[repr(C)] #[repr(C)]
#[repr(align(16))]
pub struct Mbox { pub struct Mbox {
// The address for buffer needs to be 16-byte aligned so that the // The address for buffer needs to be 16-byte aligned so that the
// Videcore can handle it properly. We don't take precautions here // Videcore can handle it properly.
// to achieve that, but for now it just works. Since alignment of
// data structures in Rust is a bit of a hassle right now, we just
// close our eyes and roll with it.
pub buffer: [u32; 36], pub buffer: [u32; 36],
} }
@ -118,34 +121,33 @@ impl Mbox {
pub fn call(&self, channel: u32) -> Result<()> { pub fn call(&self, channel: u32) -> Result<()> {
// wait until we can write to the mailbox // wait until we can write to the mailbox
loop { loop {
if (self.STATUS.read() & FULL) != FULL { if !self.STATUS.is_set(STATUS::FULL) {
break; break;
} }
asm::nop(); asm::nop();
} }
let buf_ptr = self.buffer.as_ptr() as u32;
// write the address of our message to the mailbox with channel identifier // write the address of our message to the mailbox with channel identifier
unsafe { self.WRITE.set((buf_ptr & !0xF) | (channel & 0xF));
self.WRITE
.write(((self.buffer.as_ptr() as u32) & !0xF) | (channel & 0xF))
};
// now wait for the response // now wait for the response
loop { loop {
// is there a response? // is there a response?
loop { loop {
if (self.STATUS.read() & EMPTY) != EMPTY { if !self.STATUS.is_set(STATUS::EMPTY) {
break; break;
} }
asm::nop(); asm::nop();
} }
let resp: u32 = self.READ.read(); let resp: u32 = self.READ.get();
// is it a response to our message? // is it a response to our message?
if ((resp & 0xF) == channel) && ((resp & !0xF) == (self.buffer.as_ptr() as u32)) { if ((resp & 0xF) == channel) && ((resp & !0xF) == buf_ptr) {
// is it a valid successful response? // is it a valid successful response?
return match self.buffer[1] { return match self.buffer[1] {
response::SUCCESS => Ok(()), response::SUCCESS => Ok(()),

@ -28,20 +28,27 @@ use core::sync::atomic::{compiler_fence, Ordering};
use delays; use delays;
use gpio; use gpio;
use mbox; use mbox;
use volatile_register::*; use register::mmio::*;
const POWER_BASE: u32 = MMIO_BASE + 0x100_01C; const POWER_BASE: u32 = MMIO_BASE + 0x100_01C;
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[repr(C)] #[repr(C)]
pub struct RegisterBlock { pub struct RegisterBlock {
PM_RSTC: RW<u32>, // 0x1C PM_RSTC: ReadWrite<u32>, // 0x1C
PM_RSTS: RW<u32>, // 0x20 PM_RSTS: ReadWrite<u32>, // 0x20
PM_WDOG: RW<u32>, // 0x24 PM_WDOG: ReadWrite<u32>, // 0x24
} }
const PM_WDOG_MAGIC: u32 = 0x5a_000_000; const PM_PASSWORD: u32 = 0x5a_000_000;
const PM_RSTC_FULLRST: u32 = 0x20; const PM_RSTC_WRCFG_CLR: u32 = 0xffff_ffcf;
const PM_RSTC_WRCFG_FULL_RESET: u32 = 0x0000_0020;
// The Raspberry Pi firmware uses the RSTS register to know which
// partiton to boot from. The partiton value is spread into bits 0, 2,
// 4, 6, 8, 10. Partiton 63 is a special partition used by the
// firmware to indicate halt.
const PM_RSTS_RASPBERRYPI_HALT: u32 = 0x555;
pub enum PowerError { pub enum PowerError {
MailboxError, MailboxError,
@ -93,52 +100,44 @@ impl Power {
} }
// power off gpio pins (but not VCC pins) // power off gpio pins (but not VCC pins)
unsafe { gpio.GPFSEL0.set(0);
gpio.GPFSEL0.write(0); gpio.GPFSEL1.set(0);
gpio.GPFSEL1.write(0); gpio.GPFSEL2.set(0);
gpio.GPFSEL2.write(0); gpio.GPFSEL3.set(0);
gpio.GPFSEL3.write(0); gpio.GPFSEL4.set(0);
gpio.GPFSEL4.write(0); gpio.GPFSEL5.set(0);
gpio.GPFSEL5.write(0);
gpio.GPPUD.set(0);
gpio.GPPUD.write(0); delays::wait_cycles(150);
delays::wait_cycles(150);
gpio.GPPUDCLK0.set(0xffff_ffff);
gpio.GPPUDCLK0.write(0xffff_ffff); gpio.GPPUDCLK1.set(0xffff_ffff);
gpio.GPPUDCLK1.write(0xffff_ffff); delays::wait_cycles(150);
delays::wait_cycles(150);
// flush GPIO setup
// flush GPIO setup gpio.GPPUDCLK0.set(0);
gpio.GPPUDCLK0.write(0); gpio.GPPUDCLK1.set(0);
gpio.GPPUDCLK1.write(0);
// We set the watchdog hard reset bit here to distinguish this
// power off the SoC (GPU + CPU) // reset from the normal (full) reset. bootcode.bin will not
self.PM_RSTS.modify(|reg| { // reboot after a hard reset.
let mut r = reg; let mut val = self.PM_RSTS.get();
val |= PM_PASSWORD | PM_RSTS_RASPBERRYPI_HALT;
r &= !0xffff_faaa; self.PM_RSTS.set(val);
r |= 0x555; // partition 63 used to indicate halt
PM_WDOG_MAGIC | r // Continue with normal reset mechanism
}); self.reset();
self.PM_WDOG.write(PM_WDOG_MAGIC | 10);
self.PM_RSTC.write(PM_WDOG_MAGIC | PM_RSTC_FULLRST);
}
Ok(())
} }
/// Reboot /// Reboot
pub fn reset(&self) { pub fn reset(&self) -> ! {
// trigger a restart by instructing the GPU to boot from partition 0 // use a timeout of 10 ticks (~150us)
unsafe { self.PM_WDOG.set(PM_PASSWORD | 10);
self.PM_RSTS.modify(|reg| { let mut val = self.PM_RSTC.get();
let mut r = reg; val &= PM_RSTC_WRCFG_CLR;
val |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET;
r &= !0xffff_faaa; self.PM_RSTC.set(val);
PM_WDOG_MAGIC | r
}); loop {}
self.PM_WDOG.write(PM_WDOG_MAGIC | 10);
self.PM_RSTC.write(PM_WDOG_MAGIC | PM_RSTC_FULLRST);
}
} }
} }

@ -23,30 +23,118 @@
*/ */
use super::MMIO_BASE; use super::MMIO_BASE;
use core::ops; use core::{
use core::sync::atomic::{compiler_fence, Ordering}; ops,
sync::atomic::{compiler_fence, Ordering},
};
use cortex_a::asm; use cortex_a::asm;
use delays; use delays;
use gpio; use gpio;
use mbox; use mbox;
use volatile_register::*; use register::mmio::*;
// PL011 UART registers.
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
register_bitfields! {
u32,
/// Flag Register
FR [
/// Transmit FIFO full. The meaning of this bit depends on the
/// state of the FEN bit in the UARTLCR_ LCRH Register. If the
/// FIFO is disabled, this bit is set when the transmit
/// holding register is full. If the FIFO is enabled, the TXFF
/// bit is set when the transmit FIFO is full.
TXFF OFFSET(5) NUMBITS(1) [],
/// Receive FIFO empty. The meaning of this bit depends on the
/// state of the FEN bit in the UARTLCR_H Register. If the
/// FIFO is disabled, this bit is set when the receive holding
/// register is empty. If the FIFO is enabled, the RXFE bit is
/// set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) []
],
/// Integer Baud rate divisor
IBRD [
/// Integer Baud rate divisor
IBRD OFFSET(0) NUMBITS(16) []
],
/// Fractional Baud rate divisor
FBRD [
/// Fractional Baud rate divisor
FBRD OFFSET(0) NUMBITS(6) []
],
/// Line Control register
LCRH [
/// Word length. These bits indicate the number of data bits
/// transmitted or received in a frame.
WLEN OFFSET(5) NUMBITS(2) [
FiveBit = 0b00,
SixBit = 0b01,
SevenBit = 0b10,
EightBit = 0b11
]
],
/// Control Register
CR [
/// Receive enable. If this bit is set to 1, the receive
/// section of the UART is enabled. Data reception occurs for
/// UART signals. When the UART is disabled in the middle of
/// reception, it completes the current character before
/// stopping.
RXE OFFSET(9) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Transmit enable. If this bit is set to 1, the transmit
/// section of the UART is enabled. Data transmission occurs
/// for UART signals. When the UART is disabled in the middle
/// of transmission, it completes the current character before
/// stopping.
TXE OFFSET(8) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// UART enable
UARTEN OFFSET(0) NUMBITS(1) [
/// If the UART is disabled in the middle of transmission
/// or reception, it completes the current character
/// before stopping.
Disabled = 0,
Enabled = 1
]
],
/// Interupt Clear Register
ICR [
/// Meta field for all pending interrupts
ALL OFFSET(0) NUMBITS(11) []
]
}
const UART_BASE: u32 = MMIO_BASE + 0x20_1000; const UART_BASE: u32 = MMIO_BASE + 0x20_1000;
// PL011 UART registers
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[repr(C)] #[repr(C)]
pub struct RegisterBlock { pub struct RegisterBlock {
DR: RW<u32>, // 0x00 DR: ReadWrite<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04 __reserved_0: [u32; 5], // 0x04
FR: RO<u32>, // 0x18 FR: ReadOnly<u32, FR::Register>, // 0x18
__reserved_1: [u32; 2], // 0x1c __reserved_1: [u32; 2], // 0x1c
IBRD: WO<u32>, // 0x24 IBRD: WriteOnly<u32, IBRD::Register>, // 0x24
FBRD: WO<u32>, // 0x28 FBRD: WriteOnly<u32, FBRD::Register>, // 0x28
LCRH: WO<u32>, // 0x2C LCRH: WriteOnly<u32, LCRH::Register>, // 0x2C
CR: WO<u32>, // 0x30 CR: WriteOnly<u32, CR::Register>, // 0x30
__reserved_2: [u32; 4], // 0x34 __reserved_2: [u32; 4], // 0x34
ICR: WO<u32>, // 0x44 ICR: WriteOnly<u32, ICR::Register>, // 0x44
} }
pub enum UartError { pub enum UartError {
@ -77,7 +165,7 @@ impl Uart {
///Set baud rate and characteristics (115200 8N1) and map to GPIO ///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self, mbox: &mut mbox::Mbox, gpio: &gpio::GPIO) -> Result<()> { pub fn init(&self, mbox: &mut mbox::Mbox, gpio: &gpio::GPIO) -> Result<()> {
// turn off UART0 // turn off UART0
unsafe { self.CR.write(0) }; self.CR.set(0);
// set up clock for consistent divisor values // set up clock for consistent divisor values
mbox.buffer[0] = 9 * 4; mbox.buffer[0] = 9 * 4;
@ -100,30 +188,24 @@ impl Uart {
}; };
// map UART0 to GPIO pins // map UART0 to GPIO pins
unsafe { gpio.GPFSEL1.modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0);
gpio.GPFSEL1.modify(|x| {
// Modify with a closure
let mut ret = x;
ret &= !((7 << 12) | (7 << 15)); // gpio14, gpio15
ret |= (4 << 12) | (4 << 15); // alt0
ret
});
gpio.GPPUD.write(0); // enable pins 14 and 15 gpio.GPPUD.set(0); // enable pins 14 and 15
delays::wait_cycles(150); delays::wait_cycles(150);
gpio.GPPUDCLK0.write((1 << 14) | (1 << 15)); gpio.GPPUDCLK0.modify(
delays::wait_cycles(150); gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock,
);
delays::wait_cycles(150);
gpio.GPPUDCLK0.write(0); gpio.GPPUDCLK0.set(0);
self.ICR.write(0x7FF); // clear interrupts self.ICR.write(ICR::ALL::CLEAR);
self.IBRD.write(2); // 115200 baud self.IBRD.write(IBRD::IBRD.val(2)); // Results in 115200 baud
self.FBRD.write(0xB); self.FBRD.write(FBRD::FBRD.val(0xB));
self.LCRH.write(0b11 << 5); // 8n1 self.LCRH.write(LCRH::WLEN::EightBit); // 8N1
self.CR.write(0x301); // enable Tx, Rx, FIFO self.CR
} .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
Ok(()) Ok(())
} }
@ -132,7 +214,7 @@ impl Uart {
pub fn send(&self, c: char) { pub fn send(&self, c: char) {
// wait until we can send // wait until we can send
loop { loop {
if (self.FR.read() & 0x20) != 0x20 { if !self.FR.is_set(FR::TXFF) {
break; break;
} }
@ -140,14 +222,14 @@ impl Uart {
} }
// write the character to the buffer // write the character to the buffer
unsafe { self.DR.write(c as u32) }; self.DR.set(c as u32);
} }
/// Receive a character /// Receive a character
pub fn getc(&self) -> char { pub fn getc(&self) -> char {
// wait until something is in the buffer // wait until something is in the buffer
loop { loop {
if (self.FR.read() & 0x10) != 0x10 { if !self.FR.is_set(FR::RXFE) {
break; break;
} }
@ -155,7 +237,7 @@ impl Uart {
} }
// read it and return // read it and return
let mut ret = self.DR.read() as u8 as char; let mut ret = self.DR.get() as u8 as char;
// convert carrige return to newline // convert carrige return to newline
if ret == '\r' { if ret == '\r' {

Loading…
Cancel
Save