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 r0;
use core::ptr;
#[lang = "start"]
extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize
where
@ -53,6 +51,8 @@ impl Termination for () {
#[no_mangle]
pub unsafe extern "C" fn reset() -> ! {
use core::ptr;
extern "C" {
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"
dependencies = [
"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]]
@ -25,20 +25,20 @@ dependencies = [
]
[[package]]
name = "vcell"
version = "0.1.0"
name = "register"
version = "0.1.1"
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]]
name = "volatile-register"
version = "0.2.0"
name = "tock-registers"
version = "0.1.0"
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]
"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 vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d"
"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,4 +5,4 @@ authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies]
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
We have a new header file. This defines the base MMIO address, and the GPIO
controller's addresses. This file going to be very popular, as many device needs
it.
We have a new file that defines the GPIO controller addresses. It is going to be
very popular, as many device will need it in the future.
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
UART registers.
[volatile_register]: https://docs.rs/volatile-register/0.2.0/volatile_register/
[register]: https://crates.io/crates/register
## uart.rs

Binary file not shown.

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

@ -23,8 +23,52 @@
*/
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>;
pub const GPPUD: *const RW<u32> = (MMIO_BASE + 0x0020_0094) as *const RW<u32>;
pub const GPPUDCLK0: *const RW<u32> = (MMIO_BASE + 0x0020_0098) as *const RW<u32>;
// 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,
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)]
extern crate raspi3_glue;
extern crate volatile_register;
#[macro_use]
extern crate register;
const MMIO_BASE: u32 = 0x3F00_0000;

@ -25,28 +25,98 @@
use super::MMIO_BASE;
use core::ops;
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;
/// Auxilary mini UART registers
#[allow(non_snake_case)]
#[repr(C)]
pub struct RegisterBlock {
__reserved_0: u32, // 0x00
ENABLES: RW<u32>, // 0x04
__reserved_1: [u32; 14], // 0x08
MU_IO: RW<u32>, // 0x40
MU_IER: RW<u32>, // 0x44
MU_IIR: RW<u32>, // 0x48
MU_LCR: RW<u32>, // 0x4C
MU_MCR: RW<u32>, // 0x50
MU_LSR: RW<u32>, // 0x54
MU_MSR: RW<u32>, // 0x58
MU_SCRATCH: RW<u32>, // 0x5C
MU_CNTL: RW<u32>, // 0x60
MU_STAT: RW<u32>, // 0x64
MU_BAUD: RW<u32>, // 0x68
__reserved_0: u32, // 0x00
AUX_ENABLES: ReadWrite<u32, AUX_ENABLES::Register>, // 0x04
__reserved_1: [u32; 14], // 0x08
AUX_MU_IO: ReadWrite<u32>, // 0x40 - Mini Uart I/O Data
AUX_MU_IER: WriteOnly<u32>, // 0x44 - Mini Uart Interrupt Enable
AUX_MU_IIR: WriteOnly<u32, AUX_MU_IIR::Register>, // 0x48
AUX_MU_LCR: WriteOnly<u32, AUX_MU_LCR::Register>, // 0x4C
AUX_MU_MCR: WriteOnly<u32>, // 0x50
AUX_MU_LSR: ReadOnly<u32, AUX_MU_LSR::Register>, // 0x54
__reserved_2: [u32; 2], // 0x58
AUX_MU_CNTL: WriteOnly<u32, AUX_MU_CNTL::Register>, // 0x60
__reserved_3: u32, // 0x64
AUX_MU_BAUD: WriteOnly<u32, AUX_MU_BAUD::Register>, // 0x68
}
pub struct MiniUart;
@ -82,45 +152,43 @@ impl MiniUart {
///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self) {
// 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 {
self.ENABLES.modify(|x| x | 1); // enable UART1, AUX mini uart
self.MU_IER.write(0);
self.MU_CNTL.write(0);
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
(*gpio::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD1 + gpio::GPFSEL1::FSEL15::RXD1);
(*gpio::GPPUD).set(0); // enable pins 14 and 15
for _ in 0..150 {
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 {
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
pub fn send(&self, c: char) {
// wait until we can send
loop {
if (self.MU_LSR.read() & 0x20) == 0x20 {
if self.AUX_MU_LSR.is_set(AUX_MU_LSR::TX_EMPTY) {
break;
}
@ -128,14 +196,14 @@ impl MiniUart {
}
// 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
pub fn getc(&self) -> char {
// wait until something is in the buffer
loop {
if (self.MU_LSR.read() & 0x01) == 0x01 {
if self.AUX_MU_LSR.is_set(AUX_MU_LSR::DATA_READY) {
break;
}
@ -143,7 +211,7 @@ impl MiniUart {
}
// 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
if ret == '\r' {

@ -3,7 +3,7 @@ name = "kernel8"
version = "0.1.0"
dependencies = [
"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]]
@ -25,20 +25,20 @@ dependencies = [
]
[[package]]
name = "vcell"
version = "0.1.0"
name = "register"
version = "0.1.1"
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]]
name = "volatile-register"
version = "0.2.0"
name = "tock-registers"
version = "0.1.0"
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]
"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 vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d"
"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,4 +5,4 @@ authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies]
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 r0;
use core::ptr;
#[lang = "start"]
extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize
where
@ -53,6 +51,8 @@ impl Termination for () {
#[no_mangle]
pub unsafe extern "C" fn reset() -> ! {
use core::ptr;
extern "C" {
fn main(argc: isize, argv: *const *const u8) -> isize;

@ -23,8 +23,52 @@
*/
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>;
pub const GPPUD: *const RW<u32> = (MMIO_BASE + 0x0020_0094) as *const RW<u32>;
pub const GPPUDCLK0: *const RW<u32> = (MMIO_BASE + 0x0020_0098) as *const RW<u32>;
// 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,
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)]
extern crate raspi3_glue;
extern crate volatile_register;
#[macro_use]
extern crate register;
const MMIO_BASE: u32 = 0x3F00_0000;

@ -24,20 +24,27 @@
use super::MMIO_BASE;
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;
#[allow(non_snake_case)]
#[repr(C)]
pub struct RegisterBlock {
READ: RO<u32>, // 0x00
__reserved_0: [u32; 3], // 0x04
POLL: RO<u32>, // 0x10
SENDER: RO<u32>, // 0x14
STATUS: RO<u32>, // 0x18
CONFIG: RO<u32>, // 0x1C
WRITE: WO<u32>, // 0x20
READ: ReadOnly<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04
STATUS: ReadOnly<u32, STATUS::Register>, // 0x18
__reserved_1: u32, // 0x1C
WRITE: WriteOnly<u32>, // 0x20
}
// Custom errors
@ -65,17 +72,13 @@ mod response {
}
pub const REQUEST: u32 = 0;
const FULL: u32 = 0x8000_0000;
const EMPTY: u32 = 0x4000_0000;
// Public interface to the mailbox
#[repr(C)]
#[repr(align(16))]
pub struct Mbox {
// The address for buffer needs to be 16-byte aligned so that the
// Videcore can handle it properly. We don't take precautions here
// 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.
// Videcore can handle it properly.
pub buffer: [u32; 36],
}
@ -111,38 +114,33 @@ impl Mbox {
pub fn call(&self, channel: u32) -> Result<()> {
// wait until we can write to the mailbox
loop {
if (self.STATUS.read() & FULL) != FULL {
if !self.STATUS.is_set(STATUS::FULL) {
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
unsafe {
self.WRITE
.write(((self.buffer.as_ptr() as u32) & !0xF) | (channel & 0xF));
}
self.WRITE.set((buf_ptr & !0xF) | (channel & 0xF));
// now wait for the response
loop {
// is there a response?
loop {
if (self.STATUS.read() & EMPTY) != EMPTY {
if !self.STATUS.is_set(STATUS::EMPTY) {
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?
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?
return match self.buffer[1] {
response::SUCCESS => Ok(()),

@ -25,32 +25,112 @@
use super::MMIO_BASE;
use core::ops;
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;
/// Auxilary mini UART registers
#[allow(non_snake_case)]
#[repr(C)]
pub struct RegisterBlock {
__reserved_0: u32, // 0x00
ENABLES: RW<u32>, // 0x04
__reserved_1: [u32; 14], // 0x08
MU_IO: RW<u32>, // 0x40
MU_IER: RW<u32>, // 0x44
MU_IIR: RW<u32>, // 0x48
MU_LCR: RW<u32>, // 0x4C
MU_MCR: RW<u32>, // 0x50
MU_LSR: RW<u32>, // 0x54
MU_MSR: RW<u32>, // 0x58
MU_SCRATCH: RW<u32>, // 0x5C
MU_CNTL: RW<u32>, // 0x60
MU_STAT: RW<u32>, // 0x64
MU_BAUD: RW<u32>, // 0x68
__reserved_0: u32, // 0x00
AUX_ENABLES: ReadWrite<u32, AUX_ENABLES::Register>, // 0x04
__reserved_1: [u32; 14], // 0x08
AUX_MU_IO: ReadWrite<u32>, // 0x40 - Mini Uart I/O Data
AUX_MU_IER: WriteOnly<u32>, // 0x44 - Mini Uart Interrupt Enable
AUX_MU_IIR: WriteOnly<u32, AUX_MU_IIR::Register>, // 0x48
AUX_MU_LCR: WriteOnly<u32, AUX_MU_LCR::Register>, // 0x4C
AUX_MU_MCR: WriteOnly<u32>, // 0x50
AUX_MU_LSR: ReadOnly<u32, AUX_MU_LSR::Register>, // 0x54
__reserved_2: [u32; 2], // 0x58
AUX_MU_CNTL: WriteOnly<u32, AUX_MU_CNTL::Register>, // 0x60
__reserved_3: u32, // 0x64
AUX_MU_BAUD: WriteOnly<u32, AUX_MU_BAUD::Register>, // 0x68
}
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 {
type Target = RegisterBlock;
@ -72,45 +152,43 @@ impl MiniUart {
///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self) {
// 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 {
self.ENABLES.modify(|x| x | 1); // enable UART1, AUX mini uart
self.MU_IER.write(0);
self.MU_CNTL.write(0);
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
(*gpio::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD1 + gpio::GPFSEL1::FSEL15::RXD1);
(*gpio::GPPUD).set(0); // enable pins 14 and 15
for _ in 0..150 {
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 {
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
pub fn send(&self, c: char) {
// wait until we can send
loop {
if (self.MU_LSR.read() & 0x20) == 0x20 {
if self.AUX_MU_LSR.is_set(AUX_MU_LSR::TX_EMPTY) {
break;
}
@ -118,14 +196,14 @@ impl MiniUart {
}
// 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
pub fn getc(&self) -> char {
// wait until something is in the buffer
loop {
if (self.MU_LSR.read() & 0x01) == 0x01 {
if self.AUX_MU_LSR.is_set(AUX_MU_LSR::DATA_READY) {
break;
}
@ -133,7 +211,7 @@ impl MiniUart {
}
// 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
if ret == '\r' {

20
05_uart0/Cargo.lock generated

@ -3,7 +3,7 @@ name = "kernel8"
version = "0.1.0"
dependencies = [
"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]]
@ -25,20 +25,20 @@ dependencies = [
]
[[package]]
name = "vcell"
version = "0.1.0"
name = "register"
version = "0.1.1"
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]]
name = "volatile-register"
version = "0.2.0"
name = "tock-registers"
version = "0.1.0"
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]
"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 vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d"
"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,4 +5,4 @@ authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies]
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 r0;
use core::ptr;
#[lang = "start"]
extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize
where
@ -53,6 +51,8 @@ impl Termination for () {
#[no_mangle]
pub unsafe extern "C" fn reset() -> ! {
use core::ptr;
extern "C" {
fn main(argc: isize, argv: *const *const u8) -> isize;

@ -23,8 +23,54 @@
*/
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>;
pub const GPPUD: *const RW<u32> = (MMIO_BASE + 0x0020_0094) as *const RW<u32>;
pub const GPPUDCLK0: *const RW<u32> = (MMIO_BASE + 0x0020_0098) as *const RW<u32>;
// 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
]
]
}
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)]
extern crate raspi3_glue;
extern crate volatile_register;
#[macro_use]
extern crate register;
const MMIO_BASE: u32 = 0x3F00_0000;

@ -24,20 +24,27 @@
use super::MMIO_BASE;
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;
#[allow(non_snake_case)]
#[repr(C)]
pub struct RegisterBlock {
READ: RO<u32>, // 0x00
__reserved_0: [u32; 3], // 0x04
POLL: RO<u32>, // 0x10
SENDER: RO<u32>, // 0x14
STATUS: RO<u32>, // 0x18
CONFIG: RO<u32>, // 0x1C
WRITE: WO<u32>, // 0x20
READ: ReadOnly<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04
STATUS: ReadOnly<u32, STATUS::Register>, // 0x18
__reserved_1: u32, // 0x1C
WRITE: WriteOnly<u32>, // 0x20
}
// Custom errors
@ -71,17 +78,13 @@ mod response {
}
pub const REQUEST: u32 = 0;
const FULL: u32 = 0x8000_0000;
const EMPTY: u32 = 0x4000_0000;
// Public interface to the mailbox
#[repr(C)]
#[repr(align(16))]
pub struct Mbox {
// The address for buffer needs to be 16-byte aligned so that the
// Videcore can handle it properly. We don't take precautions here
// 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.
// Videcore can handle it properly.
pub buffer: [u32; 36],
}
@ -117,34 +120,33 @@ impl Mbox {
pub fn call(&self, channel: u32) -> Result<()> {
// wait until we can write to the mailbox
loop {
if (self.STATUS.read() & FULL) != FULL {
if !self.STATUS.is_set(STATUS::FULL) {
break;
}
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
unsafe {
self.WRITE
.write(((self.buffer.as_ptr() as u32) & !0xF) | (channel & 0xF))
};
self.WRITE.set((buf_ptr & !0xF) | (channel & 0xF));
// now wait for the response
loop {
// is there a response?
loop {
if (self.STATUS.read() & EMPTY) != EMPTY {
if !self.STATUS.is_set(STATUS::EMPTY) {
break;
}
unsafe { asm!("nop" :::: "volatile") };
}
let resp: u32 = self.READ.read();
let resp: u32 = self.READ.get();
// 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?
return match self.buffer[1] {
response::SUCCESS => Ok(()),

@ -23,28 +23,116 @@
*/
use super::MMIO_BASE;
use core::ops;
use core::sync::atomic::{compiler_fence, Ordering};
use core::{
ops,
sync::atomic::{compiler_fence, Ordering},
};
use gpio;
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;
// PL011 UART registers
#[allow(non_snake_case)]
#[repr(C)]
pub struct RegisterBlock {
DR: RW<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04
FR: RO<u32>, // 0x18
__reserved_1: [u32; 2], // 0x1c
IBRD: WO<u32>, // 0x24
FBRD: WO<u32>, // 0x28
LCRH: WO<u32>, // 0x2C
CR: WO<u32>, // 0x30
__reserved_2: [u32; 4], // 0x34
ICR: WO<u32>, // 0x44
DR: ReadWrite<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04
FR: ReadOnly<u32, FR::Register>, // 0x18
__reserved_1: [u32; 2], // 0x1c
IBRD: WriteOnly<u32, IBRD::Register>, // 0x24
FBRD: WriteOnly<u32, FBRD::Register>, // 0x28
LCRH: WriteOnly<u32, LCRH::Register>, // 0x2C
CR: WriteOnly<u32, CR::Register>, // 0x30
__reserved_2: [u32; 4], // 0x34
ICR: WriteOnly<u32, ICR::Register>, // 0x44
}
pub enum UartError {
@ -75,7 +163,7 @@ impl Uart {
///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self, mbox: &mut mbox::Mbox) -> Result<()> {
// turn off UART0
unsafe { self.CR.write(0) };
self.CR.set(0);
// set up clock for consistent divisor values
mbox.buffer[0] = 9 * 4;
@ -99,33 +187,30 @@ impl Uart {
// map UART0 to GPIO pins
unsafe {
(*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::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0);
(*gpio::GPPUD).write(0); // enable pins 14 and 15
(*gpio::GPPUD).set(0); // enable pins 14 and 15
for _ in 0..150 {
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 {
asm!("nop" :::: "volatile");
}
(*gpio::GPPUDCLK0).write(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
(*gpio::GPPUDCLK0).set(0);
}
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(())
}
@ -133,7 +218,7 @@ impl Uart {
pub fn send(&self, c: char) {
// wait until we can send
loop {
if (self.FR.read() & 0x20) != 0x20 {
if !self.FR.is_set(FR::TXFF) {
break;
}
@ -141,14 +226,14 @@ impl Uart {
}
// write the character to the buffer
unsafe { self.DR.write(c as u32) };
self.DR.set(c as u32);
}
/// Receive a character
pub fn getc(&self) -> char {
// wait until something is in the buffer
loop {
if (self.FR.read() & 0x10) != 0x10 {
if !self.FR.is_set(FR::RXFE) {
break;
}
@ -156,7 +241,7 @@ impl Uart {
}
// 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
if ret == '\r' {

@ -3,7 +3,7 @@ name = "kernel8"
version = "0.1.0"
dependencies = [
"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]]
@ -19,19 +19,19 @@ dependencies = [
]
[[package]]
name = "vcell"
version = "0.1.0"
name = "register"
version = "0.1.1"
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]]
name = "volatile-register"
version = "0.2.0"
name = "tock-registers"
version = "0.1.0"
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]
"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 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,4 +5,4 @@ authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies]
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;
use core::ptr;
#[lang = "start"]
extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize
where
@ -52,6 +50,8 @@ impl Termination for () {
#[no_mangle]
pub unsafe extern "C" fn reset() -> ! {
use core::ptr;
extern "C" {
fn main(argc: isize, argv: *const *const u8) -> isize;
}

@ -23,8 +23,54 @@
*/
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>;
pub const GPPUD: *const RW<u32> = (MMIO_BASE + 0x0020_0094) as *const RW<u32>;
pub const GPPUDCLK0: *const RW<u32> = (MMIO_BASE + 0x0020_0098) as *const RW<u32>;
// 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
]
]
}
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)]
extern crate raspi3_glue;
extern crate volatile_register;
#[macro_use]
extern crate register;
const MMIO_BASE: u32 = 0x3F00_0000;

@ -24,20 +24,27 @@
use super::MMIO_BASE;
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;
#[allow(non_snake_case)]
#[repr(C)]
pub struct RegisterBlock {
READ: RO<u32>, // 0x00
__reserved_0: [u32; 3], // 0x04
POLL: RO<u32>, // 0x10
SENDER: RO<u32>, // 0x14
STATUS: RO<u32>, // 0x18
CONFIG: RO<u32>, // 0x1C
WRITE: WO<u32>, // 0x20
READ: ReadOnly<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04
STATUS: ReadOnly<u32, STATUS::Register>, // 0x18
__reserved_1: u32, // 0x1C
WRITE: WriteOnly<u32>, // 0x20
}
// Custom errors
@ -70,17 +77,13 @@ mod response {
}
pub const REQUEST: u32 = 0;
const FULL: u32 = 0x8000_0000;
const EMPTY: u32 = 0x4000_0000;
// Public interface to the mailbox
#[repr(C)]
#[repr(align(16))]
pub struct Mbox {
// The address for buffer needs to be 16-byte aligned so that the
// Videcore can handle it properly. We don't take precautions here
// 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.
// Videcore can handle it properly.
pub buffer: [u32; 36],
}
@ -116,34 +119,33 @@ impl Mbox {
pub fn call(&self, channel: u32) -> Result<()> {
// wait until we can write to the mailbox
loop {
if (self.STATUS.read() & FULL) != FULL {
if !self.STATUS.is_set(STATUS::FULL) {
break;
}
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
unsafe {
self.WRITE
.write(((self.buffer.as_ptr() as u32) & !0xF) | (channel & 0xF))
};
self.WRITE.set((buf_ptr & !0xF) | (channel & 0xF));
// now wait for the response
loop {
// is there a response?
loop {
if (self.STATUS.read() & EMPTY) != EMPTY {
if !self.STATUS.is_set(STATUS::EMPTY) {
break;
}
unsafe { asm!("nop" :::: "volatile") };
}
let resp: u32 = self.READ.read();
let resp: u32 = self.READ.get();
// 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?
return match self.buffer[1] {
response::SUCCESS => Ok(()),

@ -23,28 +23,116 @@
*/
use super::MMIO_BASE;
use core::ops;
use core::sync::atomic::{compiler_fence, Ordering};
use core::{
ops,
sync::atomic::{compiler_fence, Ordering},
};
use gpio;
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;
// PL011 UART registers
#[allow(non_snake_case)]
#[repr(C)]
pub struct RegisterBlock {
DR: RW<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04
FR: RO<u32>, // 0x18
__reserved_1: [u32; 2], // 0x1c
IBRD: WO<u32>, // 0x24
FBRD: WO<u32>, // 0x28
LCRH: WO<u32>, // 0x2C
CR: WO<u32>, // 0x30
__reserved_2: [u32; 4], // 0x34
ICR: WO<u32>, // 0x44
DR: ReadWrite<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04
FR: ReadOnly<u32, FR::Register>, // 0x18
__reserved_1: [u32; 2], // 0x1c
IBRD: WriteOnly<u32, IBRD::Register>, // 0x24
FBRD: WriteOnly<u32, FBRD::Register>, // 0x28
LCRH: WriteOnly<u32, LCRH::Register>, // 0x2C
CR: WriteOnly<u32, CR::Register>, // 0x30
__reserved_2: [u32; 4], // 0x34
ICR: WriteOnly<u32, ICR::Register>, // 0x44
}
pub enum UartError {
@ -75,7 +163,7 @@ impl Uart {
///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self, mbox: &mut mbox::Mbox) -> Result<()> {
// turn off UART0
unsafe { self.CR.write(0) };
self.CR.set(0);
// set up clock for consistent divisor values
mbox.buffer[0] = 9 * 4;
@ -99,33 +187,30 @@ impl Uart {
// map UART0 to GPIO pins
unsafe {
(*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::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0);
(*gpio::GPPUD).write(0); // enable pins 14 and 15
(*gpio::GPPUD).set(0); // enable pins 14 and 15
for _ in 0..150 {
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 {
asm!("nop" :::: "volatile");
}
(*gpio::GPPUDCLK0).write(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
(*gpio::GPPUDCLK0).set(0);
}
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(())
}
@ -133,7 +218,7 @@ impl Uart {
pub fn send(&self, c: char) {
// wait until we can send
loop {
if (self.FR.read() & 0x20) != 0x20 {
if !self.FR.is_set(FR::TXFF) {
break;
}
@ -141,14 +226,14 @@ impl Uart {
}
// write the character to the buffer
unsafe { self.DR.write(c as u32) };
self.DR.set(c as u32);
}
/// Receive a character
pub fn getc(&self) -> u8 {
// wait until something is in the buffer
loop {
if (self.FR.read() & 0x10) != 0x10 {
if !self.FR.is_set(FR::RXFE) {
break;
}
@ -156,6 +241,6 @@ impl Uart {
}
// 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]]
name = "cortex-a"
version = "0.1.3"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "kernel8"
version = "0.1.0"
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",
"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]]
@ -34,28 +29,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "raspi3_glue"
version = "0.1.0"
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)",
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "vcell"
version = "0.1.0"
name = "register"
version = "0.1.1"
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]]
name = "volatile-register"
version = "0.2.0"
name = "tock-registers"
version = "0.1.0"
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]
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
"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 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 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]
raspi3_glue = { path = "raspi3_glue" }
cortex-a = "0.1.3"
volatile-register = "0.2.0"
cortex-a = "1.0.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,
e.g. providing `asm::nop()`.
For registers, there is `cortex-a::register`. For registers like the stack
pointer, which are generally read and written as a whole, there's simple
[read()][sp_read] and [write()][sp_write] functions which take and return
primitive integer types.
For registers, there is `cortex-a::regs`. The interface is the same as we have
it for MMIO accesses, aka provided by [register-rs][register-rs] and therefore
based on [tock-regs][tock-regs]. For registers like the stack pointer, which are
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
[sp_write]: https://docs.rs/cortex-a/0.1.2/cortex_a/register/sp/fn.write.html
[el1]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0500g/BABHBJCI.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
Reference Manual][el1]), on the other hand, are abstracted into their [own
types][mpidr_type] and offer getter and/or setter methods, respectively.
The register API is based on the [tock project's][tock] register
interface. Please see [their homepage][tock_registers] for all the details.
[el1]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0500g/BABHBJCI.html
[mpidr_type]:https://docs.rs/cortex-a/0.1.2/cortex_a/register/mpidr_el1/struct.MPIDR_EL1.html
[tock]: https://github.com/tock/tock
[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,
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"]
#[no_mangle]
pub extern "C" fn _boot_cores() -> ! {
match register::MPIDR_EL1::read_raw() & 0x3 {
0 => unsafe {
register::sp::write(0x80_000);
reset()
},
use cortex_a::{asm, regs::mpidr_el1::*, regs::sp::*};
match MPIDR_EL1.get() & 0x3 {
0 => {
SP.set(0x80_000);
unsafe { reset() }
}
_ => loop {
// if not core0, infinitely wait for events
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
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
should yield something like the following, where you can see that the stack
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>"]
[dependencies]
cortex-a = "0.1.3"
cortex-a = "1.0.0"
panic-abort = "0.2.0"
r0 = "0.2.2"

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

@ -23,8 +23,54 @@
*/
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>;
pub const GPPUD: *const RW<u32> = (MMIO_BASE + 0x0020_0094) as *const RW<u32>;
pub const GPPUDCLK0: *const RW<u32> = (MMIO_BASE + 0x0020_0098) as *const RW<u32>;
// 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
]
]
}
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 raspi3_glue;
extern crate volatile_register;
#[macro_use]
extern crate register;
const MMIO_BASE: u32 = 0x3F00_0000;

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

@ -23,29 +23,117 @@
*/
use super::MMIO_BASE;
use core::ops;
use core::sync::atomic::{compiler_fence, Ordering};
use core::{
ops,
sync::atomic::{compiler_fence, Ordering},
};
use cortex_a::asm;
use gpio;
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;
// PL011 UART registers
#[allow(non_snake_case)]
#[repr(C)]
pub struct RegisterBlock {
DR: RW<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04
FR: RO<u32>, // 0x18
__reserved_1: [u32; 2], // 0x1c
IBRD: WO<u32>, // 0x24
FBRD: WO<u32>, // 0x28
LCRH: WO<u32>, // 0x2C
CR: WO<u32>, // 0x30
__reserved_2: [u32; 4], // 0x34
ICR: WO<u32>, // 0x44
DR: ReadWrite<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04
FR: ReadOnly<u32, FR::Register>, // 0x18
__reserved_1: [u32; 2], // 0x1c
IBRD: WriteOnly<u32, IBRD::Register>, // 0x24
FBRD: WriteOnly<u32, FBRD::Register>, // 0x28
LCRH: WriteOnly<u32, LCRH::Register>, // 0x2C
CR: WriteOnly<u32, CR::Register>, // 0x30
__reserved_2: [u32; 4], // 0x34
ICR: WriteOnly<u32, ICR::Register>, // 0x44
}
pub enum UartError {
@ -76,7 +164,7 @@ impl Uart {
///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self, mbox: &mut mbox::Mbox) -> Result<()> {
// turn off UART0
unsafe { self.CR.write(0) };
self.CR.set(0);
// set up clock for consistent divisor values
mbox.buffer[0] = 9 * 4;
@ -100,33 +188,30 @@ impl Uart {
// map UART0 to GPIO pins
unsafe {
(*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::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0);
(*gpio::GPPUD).write(0); // enable pins 14 and 15
(*gpio::GPPUD).set(0); // enable pins 14 and 15
for _ in 0..150 {
asm::nop();
}
(*gpio::GPPUDCLK0).write((1 << 14) | (1 << 15));
(*gpio::GPPUDCLK0).modify(
gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock,
);
for _ in 0..150 {
asm::nop();
}
(*gpio::GPPUDCLK0).write(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
(*gpio::GPPUDCLK0).set(0);
}
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(())
}
@ -134,7 +219,7 @@ impl Uart {
pub fn send(&self, c: char) {
// wait until we can send
loop {
if (self.FR.read() & 0x20) != 0x20 {
if !self.FR.is_set(FR::TXFF) {
break;
}
@ -142,14 +227,14 @@ impl Uart {
}
// write the character to the buffer
unsafe { self.DR.write(c as u32) };
self.DR.set(c as u32);
}
/// Receive a character
pub fn getc(&self) -> char {
// wait until something is in the buffer
loop {
if (self.FR.read() & 0x10) != 0x10 {
if !self.FR.is_set(FR::RXFE) {
break;
}
@ -157,7 +242,7 @@ impl Uart {
}
// 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
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]]
name = "cortex-a"
version = "0.1.3"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "kernel8"
version = "0.1.0"
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",
"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]]
@ -34,28 +29,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "raspi3_glue"
version = "0.1.0"
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)",
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "vcell"
version = "0.1.0"
name = "register"
version = "0.1.1"
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]]
name = "volatile-register"
version = "0.2.0"
name = "tock-registers"
version = "0.1.0"
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]
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
"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 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 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]
raspi3_glue = { path = "raspi3_glue" }
cortex-a = "0.1.3"
volatile-register = "0.2.0"
cortex-a = "1.0.0"
register = "0.1.1"

@ -2,11 +2,14 @@
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
accurate dice throw in any game. It is important as without hardware support
you can only generate pseudo-random numbers.
accurate dice throw in any game. It is important as without hardware support you
can only generate pseudo-random numbers.
## 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::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>"]
[dependencies]
cortex-a = "0.1.3"
cortex-a = "1.0.0"
panic-abort = "0.2.0"
r0 = "0.2.2"

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

@ -23,8 +23,54 @@
*/
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>;
pub const GPPUD: *const RW<u32> = (MMIO_BASE + 0x0020_0094) as *const RW<u32>;
pub const GPPUDCLK0: *const RW<u32> = (MMIO_BASE + 0x0020_0098) as *const RW<u32>;
// 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
]
]
}
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 raspi3_glue;
extern crate volatile_register;
#[macro_use]
extern crate register;
const MMIO_BASE: u32 = 0x3F00_0000;

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

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

@ -23,29 +23,117 @@
*/
use super::MMIO_BASE;
use core::ops;
use core::sync::atomic::{compiler_fence, Ordering};
use core::{
ops,
sync::atomic::{compiler_fence, Ordering},
};
use cortex_a::asm;
use gpio;
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;
// PL011 UART registers
#[allow(non_snake_case)]
#[repr(C)]
pub struct RegisterBlock {
DR: RW<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04
FR: RO<u32>, // 0x18
__reserved_1: [u32; 2], // 0x1c
IBRD: WO<u32>, // 0x24
FBRD: WO<u32>, // 0x28
LCRH: WO<u32>, // 0x2C
CR: WO<u32>, // 0x30
__reserved_2: [u32; 4], // 0x34
ICR: WO<u32>, // 0x44
DR: ReadWrite<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04
FR: ReadOnly<u32, FR::Register>, // 0x18
__reserved_1: [u32; 2], // 0x1c
IBRD: WriteOnly<u32, IBRD::Register>, // 0x24
FBRD: WriteOnly<u32, FBRD::Register>, // 0x28
LCRH: WriteOnly<u32, LCRH::Register>, // 0x2C
CR: WriteOnly<u32, CR::Register>, // 0x30
__reserved_2: [u32; 4], // 0x34
ICR: WriteOnly<u32, ICR::Register>, // 0x44
}
pub enum UartError {
@ -76,7 +164,7 @@ impl Uart {
///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self, mbox: &mut mbox::Mbox) -> Result<()> {
// turn off UART0
unsafe { self.CR.write(0) };
self.CR.set(0);
// set up clock for consistent divisor values
mbox.buffer[0] = 9 * 4;
@ -100,33 +188,30 @@ impl Uart {
// map UART0 to GPIO pins
unsafe {
(*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::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0);
(*gpio::GPPUD).write(0); // enable pins 14 and 15
(*gpio::GPPUD).set(0); // enable pins 14 and 15
for _ in 0..150 {
asm::nop();
}
(*gpio::GPPUDCLK0).write((1 << 14) | (1 << 15));
(*gpio::GPPUDCLK0).modify(
gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock,
);
for _ in 0..150 {
asm::nop();
}
(*gpio::GPPUDCLK0).write(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
(*gpio::GPPUDCLK0).set(0);
}
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(())
}
@ -134,7 +219,7 @@ impl Uart {
pub fn send(&self, c: char) {
// wait until we can send
loop {
if (self.FR.read() & 0x20) != 0x20 {
if !self.FR.is_set(FR::TXFF) {
break;
}
@ -142,14 +227,14 @@ impl Uart {
}
// write the character to the buffer
unsafe { self.DR.write(c as u32) };
self.DR.set(c as u32);
}
/// Receive a character
pub fn getc(&self) -> char {
// wait until something is in the buffer
loop {
if (self.FR.read() & 0x10) != 0x10 {
if !self.FR.is_set(FR::RXFE) {
break;
}
@ -157,7 +242,7 @@ impl Uart {
}
// 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
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]]
name = "cortex-a"
version = "0.1.3"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "kernel8"
version = "0.1.0"
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",
"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]]
@ -34,28 +29,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "raspi3_glue"
version = "0.1.0"
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)",
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "vcell"
version = "0.1.0"
name = "register"
version = "0.1.1"
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]]
name = "volatile-register"
version = "0.2.0"
name = "tock-registers"
version = "0.1.0"
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]
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
"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 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 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]
raspi3_glue = { path = "raspi3_glue" }
cortex-a = "0.1.3"
volatile-register = "0.2.0"
cortex-a = "1.0.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>"]
[dependencies]
cortex-a = "0.1.3"
cortex-a = "1.0.0"
panic-abort = "0.2.0"
r0 = "0.2.2"

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

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

@ -23,8 +23,54 @@
*/
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>;
pub const GPPUD: *const RW<u32> = (MMIO_BASE + 0x0020_0094) as *const RW<u32>;
pub const GPPUDCLK0: *const RW<u32> = (MMIO_BASE + 0x0020_0098) as *const RW<u32>;
// 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
]
]
}
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 raspi3_glue;
extern crate volatile_register;
#[macro_use]
extern crate register;
const MMIO_BASE: u32 = 0x3F00_0000;

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

@ -23,30 +23,118 @@
*/
use super::MMIO_BASE;
use core::ops;
use core::sync::atomic::{compiler_fence, Ordering};
use core::{
ops,
sync::atomic::{compiler_fence, Ordering},
};
use cortex_a::asm;
use delays;
use gpio;
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;
// PL011 UART registers
#[allow(non_snake_case)]
#[repr(C)]
pub struct RegisterBlock {
DR: RW<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04
FR: RO<u32>, // 0x18
__reserved_1: [u32; 2], // 0x1c
IBRD: WO<u32>, // 0x24
FBRD: WO<u32>, // 0x28
LCRH: WO<u32>, // 0x2C
CR: WO<u32>, // 0x30
__reserved_2: [u32; 4], // 0x34
ICR: WO<u32>, // 0x44
DR: ReadWrite<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04
FR: ReadOnly<u32, FR::Register>, // 0x18
__reserved_1: [u32; 2], // 0x1c
IBRD: WriteOnly<u32, IBRD::Register>, // 0x24
FBRD: WriteOnly<u32, FBRD::Register>, // 0x28
LCRH: WriteOnly<u32, LCRH::Register>, // 0x2C
CR: WriteOnly<u32, CR::Register>, // 0x30
__reserved_2: [u32; 4], // 0x34
ICR: WriteOnly<u32, ICR::Register>, // 0x44
}
pub enum UartError {
@ -77,7 +165,7 @@ impl Uart {
///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self, mbox: &mut mbox::Mbox) -> Result<()> {
// turn off UART0
unsafe { self.CR.write(0) };
self.CR.set(0);
// set up clock for consistent divisor values
mbox.buffer[0] = 9 * 4;
@ -101,30 +189,26 @@ impl Uart {
// map UART0 to GPIO pins
unsafe {
(*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
(*gpio::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0);
ret
});
(*gpio::GPPUD).write(0); // enable pins 14 and 15
(*gpio::GPPUD).set(0); // enable pins 14 and 15
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);
(*gpio::GPPUDCLK0).write(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
(*gpio::GPPUDCLK0).set(0);
}
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(())
}
@ -132,7 +216,7 @@ impl Uart {
pub fn send(&self, c: char) {
// wait until we can send
loop {
if (self.FR.read() & 0x20) != 0x20 {
if !self.FR.is_set(FR::TXFF) {
break;
}
@ -140,14 +224,14 @@ impl Uart {
}
// write the character to the buffer
unsafe { self.DR.write(c as u32) };
self.DR.set(c as u32);
}
/// Receive a character
pub fn getc(&self) -> char {
// wait until something is in the buffer
loop {
if (self.FR.read() & 0x10) != 0x10 {
if !self.FR.is_set(FR::RXFE) {
break;
}
@ -155,7 +239,7 @@ impl Uart {
}
// 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
if ret == '\r' {

48
0A_power/Cargo.lock generated

@ -1,23 +1,26 @@
[[package]]
name = "bitflags"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
name = "cortex-a"
version = "1.0.0"
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]]
name = "cortex-a"
version = "0.1.3"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "kernel8"
version = "0.1.0"
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",
"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]]
@ -34,28 +37,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "raspi3_glue"
version = "0.1.0"
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)",
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "vcell"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
name = "register"
version = "0.1.1"
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]]
name = "volatile-register"
version = "0.2.0"
name = "register"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
"checksum cortex-a 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a123fa5a346531ed0fc9fcb8f69ca34d9b8c55b15162731945d14c4d461c5bfe"
"checksum cortex-a 1.0.0 (git+https://github.com/andre-richter/cortex-a.git?branch=registers)" = "<none>"
"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 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 volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286"
"checksum register 0.1.1 (git+https://github.com/andre-richter/register-rs.git)" = "<none>"
"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]
raspi3_glue = { path = "raspi3_glue" }
cortex-a = "0.1.3"
volatile-register = "0.2.0"
cortex-a = "1.0.0"
register = "0.1.1"

@ -9,8 +9,14 @@ transistor connected to a data GPIO pin for example).
## 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
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
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>"]
[dependencies]
cortex-a = "0.1.3"
cortex-a = { git = "https://github.com/andre-richter/cortex-a.git", branch = "registers" }
panic-abort = "0.2.0"
r0 = "0.2.2"

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

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

@ -24,37 +24,78 @@
use super::MMIO_BASE;
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;
#[allow(non_snake_case)]
#[repr(C)]
pub struct RegisterBlock {
pub GPFSEL0: RW<u32>, // 0x00
pub GPFSEL1: RW<u32>, // 0x04
pub GPFSEL2: RW<u32>, // 0x08
pub GPFSEL3: RW<u32>, // 0x0C
pub GPFSEL4: RW<u32>, // 0x10
pub GPFSEL5: RW<u32>, // 0x14
__reserved_0: u32, // 0x18
GPSET0: RW<u32>, // 0x1C
GPSET1: RW<u32>, // 0x20
__reserved_1: u32, //
GPCLR0: RW<u32>, // 0x28
__reserved_2: [u32; 2], //
GPLEV0: RW<u32>, // 0x34
GPLEV1: RW<u32>, // 0x38
__reserved_3: u32, //
GPEDS0: RW<u32>, // 0x40
GPEDS1: RW<u32>, // 0x44
__reserved_4: [u32; 7], //
GPHEN0: RW<u32>, // 0x64
GPHEN1: RW<u32>, // 0x68
__reserved_5: [u32; 10], //
pub GPPUD: RW<u32>, // 0x94
pub GPPUDCLK0: RW<u32>, // 0x98
pub GPPUDCLK1: RW<u32>, // 0x9C
pub GPFSEL0: ReadWrite<u32>, // 0x00
pub GPFSEL1: ReadWrite<u32, GPFSEL1::Register>, // 0x04
pub GPFSEL2: ReadWrite<u32>, // 0x08
pub GPFSEL3: ReadWrite<u32>, // 0x0C
pub GPFSEL4: ReadWrite<u32>, // 0x10
pub GPFSEL5: ReadWrite<u32>, // 0x14
__reserved_0: u32, // 0x18
GPSET0: ReadWrite<u32>, // 0x1C
GPSET1: ReadWrite<u32>, // 0x20
__reserved_1: u32, //
GPCLR0: ReadWrite<u32>, // 0x28
__reserved_2: [u32; 2], //
GPLEV0: ReadWrite<u32>, // 0x34
GPLEV1: ReadWrite<u32>, // 0x38
__reserved_3: u32, //
GPEDS0: ReadWrite<u32>, // 0x40
GPEDS1: ReadWrite<u32>, // 0x44
__reserved_4: [u32; 7], //
GPHEN0: ReadWrite<u32>, // 0x64
GPHEN1: ReadWrite<u32>, // 0x68
__reserved_5: [u32; 10], //
pub GPPUD: ReadWrite<u32>, // 0x94
pub GPPUDCLK0: ReadWrite<u32, GPPUDCLK0::Register>, // 0x98
pub GPPUDCLK1: ReadWrite<u32>, // 0x9C
}
/// Public interface to the GPIO MMIO area

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

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

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

@ -23,30 +23,118 @@
*/
use super::MMIO_BASE;
use core::ops;
use core::sync::atomic::{compiler_fence, Ordering};
use core::{
ops,
sync::atomic::{compiler_fence, Ordering},
};
use cortex_a::asm;
use delays;
use gpio;
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;
// PL011 UART registers
#[allow(non_snake_case)]
#[repr(C)]
pub struct RegisterBlock {
DR: RW<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04
FR: RO<u32>, // 0x18
__reserved_1: [u32; 2], // 0x1c
IBRD: WO<u32>, // 0x24
FBRD: WO<u32>, // 0x28
LCRH: WO<u32>, // 0x2C
CR: WO<u32>, // 0x30
__reserved_2: [u32; 4], // 0x34
ICR: WO<u32>, // 0x44
DR: ReadWrite<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04
FR: ReadOnly<u32, FR::Register>, // 0x18
__reserved_1: [u32; 2], // 0x1c
IBRD: WriteOnly<u32, IBRD::Register>, // 0x24
FBRD: WriteOnly<u32, FBRD::Register>, // 0x28
LCRH: WriteOnly<u32, LCRH::Register>, // 0x2C
CR: WriteOnly<u32, CR::Register>, // 0x30
__reserved_2: [u32; 4], // 0x34
ICR: WriteOnly<u32, ICR::Register>, // 0x44
}
pub enum UartError {
@ -77,7 +165,7 @@ impl Uart {
///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self, mbox: &mut mbox::Mbox, gpio: &gpio::GPIO) -> Result<()> {
// turn off UART0
unsafe { self.CR.write(0) };
self.CR.set(0);
// set up clock for consistent divisor values
mbox.buffer[0] = 9 * 4;
@ -100,30 +188,24 @@ impl Uart {
};
// map UART0 to GPIO pins
unsafe {
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.GPFSEL1.modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0);
gpio.GPPUD.write(0); // enable pins 14 and 15
delays::wait_cycles(150);
gpio.GPPUD.set(0); // enable pins 14 and 15
delays::wait_cycles(150);
gpio.GPPUDCLK0.write((1 << 14) | (1 << 15));
delays::wait_cycles(150);
gpio.GPPUDCLK0.modify(
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.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(())
}
@ -132,7 +214,7 @@ impl Uart {
pub fn send(&self, c: char) {
// wait until we can send
loop {
if (self.FR.read() & 0x20) != 0x20 {
if !self.FR.is_set(FR::TXFF) {
break;
}
@ -140,14 +222,14 @@ impl Uart {
}
// write the character to the buffer
unsafe { self.DR.write(c as u32) };
self.DR.set(c as u32);
}
/// Receive a character
pub fn getc(&self) -> char {
// wait until something is in the buffer
loop {
if (self.FR.read() & 0x10) != 0x10 {
if !self.FR.is_set(FR::RXFE) {
break;
}
@ -155,7 +237,7 @@ impl Uart {
}
// 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
if ret == '\r' {

Loading…
Cancel
Save