Add tutorial 04_mailboxes

pull/4/head
Andre Richter 6 years ago
parent d2636fc9ba
commit 78ea9a9931

@ -1,43 +0,0 @@
#
# Copyright (C) 2018 bzt (bztsrc@github)
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use, copy,
# modify, merge, publish, distribute, sublicense, and/or sell copies
# of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
#
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
CFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles
all: clean kernel8.img
start.o: start.S
aarch64-elf-gcc $(CFLAGS) -c start.S -o start.o
%.o: %.c
aarch64-elf-gcc $(CFLAGS) -c $< -o $@
kernel8.img: start.o $(OBJS)
aarch64-elf-ld -nostdlib -nostartfiles start.o $(OBJS) -T link.ld -o kernel8.elf
aarch64-elf-objcopy -O binary kernel8.elf kernel8.img
clean:
rm kernel8.elf *.o >/dev/null 2>/dev/null || true

@ -1,41 +0,0 @@
Oktatóanyag 04 - Levelesládák
=============================
Mielőtt nekiugranánk az UART0-ának, szükségünk lesz a levelesládára. Ezért ebben az oktatóanyagban bemutatom a
mailbox interfészt. Arra használjuk, hogy lekérdezzük az alaplap egyedi sorszámát, majd kiírjuk azt.
FIGYELEM: qemu nem irányítja át alapból az UART1-et a terminálra, csak az UART0-át!
Uart.h, uart.c
--------------
`uart_hex(d)` kiír egy bináris értéket hexadecimális formátumban.
Mbox.h, mbox.c
--------------
A levelesláda interfésze. Először értékekkel feltöltjük az `mbox` tömböt, aztán meghívjuk a `mbox_call(ch)`-t,
hogy értesüljön róla a GPU, megadva közben a levelesláda csatornáját.
Ebben a példában a [property csatornát](https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface)
használtam, aminek az üzenete a következőképp néz ki:
```
0. üzenet teljes hossza bájtban, (x+1)*4
1. MBOX_REQUEST mágikus szám, kérés típusú üzenetet jelent
2-x. parancsok
x+1. MBOX_TAG_LAST mágikus szám, nincs további parancs jelölése
```
Ahol minden egyes parancs szerkezete a következő:
```
n+0. parancs azonosító
n+1. adatterület mérete bájtban
n+2. nulla
n+3. opcionális adatterület
```
Main
----
Lekérjük az alaplap egyedi szériaszámát, majd kiírjuk a soros konzolra.

@ -1,41 +0,0 @@
Tutorial 04 - Mailboxes
=======================
Before we could go on with UART0, we need mailboxes. So in this tutorial we introduce the mailbox interface.
We'll use it to query the board's serial number and print that out on UART1.
NOTE: qemu does not redirect UART1 to terminal by default, only UART0!
Uart.h, uart.c
--------------
`uart_hex(d)` prints out a binary value in hexadecimal format.
Mbox.h, mbox.c
--------------
The mailbox interface. First we fill up the message in `mbox` array, then we call
`mbox_call(ch)` to pass it to the GPU, specifying the mailbox channel.
In this example we have used the [property channel](https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface),
which requires the message to be formatted as:
```
0. size of the message in bytes, (x+1)*4
1. MBOX_REQUEST magic value, indicates request message
2-x. tags
x+1. MBOX_TAG_LAST magic value, indicates no more tags
```
Where each tag looks like:
```
n+0. tag identifier
n+1. value buffer size in bytes
n+2. must be zero
n+3. optional value buffer
```
Main
----
We query the board's serial number and then we display it on the serial console.

@ -1,45 +0,0 @@
/*
* Copyright (C) 2018 bzt (bztsrc@github)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
#define MMIO_BASE 0x3F000000
#define GPFSEL0 ((volatile unsigned int*)(MMIO_BASE+0x00200000))
#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004))
#define GPFSEL2 ((volatile unsigned int*)(MMIO_BASE+0x00200008))
#define GPFSEL3 ((volatile unsigned int*)(MMIO_BASE+0x0020000C))
#define GPFSEL4 ((volatile unsigned int*)(MMIO_BASE+0x00200010))
#define GPFSEL5 ((volatile unsigned int*)(MMIO_BASE+0x00200014))
#define GPSET0 ((volatile unsigned int*)(MMIO_BASE+0x0020001C))
#define GPSET1 ((volatile unsigned int*)(MMIO_BASE+0x00200020))
#define GPCLR0 ((volatile unsigned int*)(MMIO_BASE+0x00200028))
#define GPLEV0 ((volatile unsigned int*)(MMIO_BASE+0x00200034))
#define GPLEV1 ((volatile unsigned int*)(MMIO_BASE+0x00200038))
#define GPEDS0 ((volatile unsigned int*)(MMIO_BASE+0x00200040))
#define GPEDS1 ((volatile unsigned int*)(MMIO_BASE+0x00200044))
#define GPHEN0 ((volatile unsigned int*)(MMIO_BASE+0x00200064))
#define GPHEN1 ((volatile unsigned int*)(MMIO_BASE+0x00200068))
#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094))
#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098))
#define GPPUDCLK1 ((volatile unsigned int*)(MMIO_BASE+0x0020009C))

Binary file not shown.

@ -1,60 +0,0 @@
/*
* Copyright (C) 2018 bzt (bztsrc@github)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
#include "uart.h"
#include "mbox.h"
void main()
{
// set up serial console
uart_init();
// get the board's unique serial number with a mailbox call
mbox[0] = 8*4; // length of the message
mbox[1] = MBOX_REQUEST; // this is a request message
mbox[2] = MBOX_TAG_GETSERIAL; // get serial number command
mbox[3] = 8; // buffer size
mbox[4] = 8;
mbox[5] = 0; // clear output buffer
mbox[6] = 0;
mbox[7] = MBOX_TAG_LAST;
// send the message to the GPU and receive answer
if (mbox_call(MBOX_CH_PROP)) {
uart_puts("My serial number is: ");
uart_hex(mbox[6]);
uart_hex(mbox[5]);
uart_puts("\n");
} else {
uart_puts("Unable to query serial!\n");
}
// echo everything back
while(1) {
uart_send(uart_getc());
}
}

@ -1,63 +0,0 @@
/*
* Copyright (C) 2018 bzt (bztsrc@github)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
#include "gpio.h"
/* mailbox message buffer */
volatile unsigned int __attribute__((aligned(16))) mbox[36];
#define VIDEOCORE_MBOX (MMIO_BASE+0x0000B880)
#define MBOX_READ ((volatile unsigned int*)(VIDEOCORE_MBOX+0x0))
#define MBOX_POLL ((volatile unsigned int*)(VIDEOCORE_MBOX+0x10))
#define MBOX_SENDER ((volatile unsigned int*)(VIDEOCORE_MBOX+0x14))
#define MBOX_STATUS ((volatile unsigned int*)(VIDEOCORE_MBOX+0x18))
#define MBOX_CONFIG ((volatile unsigned int*)(VIDEOCORE_MBOX+0x1C))
#define MBOX_WRITE ((volatile unsigned int*)(VIDEOCORE_MBOX+0x20))
#define MBOX_RESPONSE 0x80000000
#define MBOX_FULL 0x80000000
#define MBOX_EMPTY 0x40000000
/**
* Make a mailbox call. Returns 0 on failure, non-zero on success
*/
int mbox_call(unsigned char ch)
{
unsigned int r;
/* wait until we can write to the mailbox */
do{asm volatile("nop");}while(*MBOX_STATUS & MBOX_FULL);
/* write the address of our message to the mailbox with channel identifier */
*MBOX_WRITE = (((unsigned int)((unsigned long)&mbox)&~0xF) | (ch&0xF));
/* now wait for the response */
while(1) {
/* is there a response? */
do{asm volatile("nop");}while(*MBOX_STATUS & MBOX_EMPTY);
r=*MBOX_READ;
/* is it a response to our message? */
if((unsigned char)(r&0xF)==ch && (r&~0xF)==(unsigned int)((unsigned long)&mbox))
/* is it a valid successful response? */
return mbox[1]==MBOX_RESPONSE;
}
return 0;
}

@ -1,46 +0,0 @@
/*
* Copyright (C) 2018 bzt (bztsrc@github)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
/* a properly aligned buffer */
extern volatile unsigned int mbox[36];
#define MBOX_REQUEST 0
/* channels */
#define MBOX_CH_POWER 0
#define MBOX_CH_FB 1
#define MBOX_CH_VUART 2
#define MBOX_CH_VCHIQ 3
#define MBOX_CH_LEDS 4
#define MBOX_CH_BTNS 5
#define MBOX_CH_TOUCH 6
#define MBOX_CH_COUNT 7
#define MBOX_CH_PROP 8
/* tags */
#define MBOX_TAG_GETSERIAL 0x10004
#define MBOX_TAG_LAST 0
int mbox_call(unsigned char ch);

@ -1,119 +0,0 @@
/*
* Copyright (C) 2018 bzt (bztsrc@github)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
#include "gpio.h"
/* Auxilary mini UART registers */
#define AUX_ENABLE ((volatile unsigned int*)(MMIO_BASE+0x00215004))
#define AUX_MU_IO ((volatile unsigned int*)(MMIO_BASE+0x00215040))
#define AUX_MU_IER ((volatile unsigned int*)(MMIO_BASE+0x00215044))
#define AUX_MU_IIR ((volatile unsigned int*)(MMIO_BASE+0x00215048))
#define AUX_MU_LCR ((volatile unsigned int*)(MMIO_BASE+0x0021504C))
#define AUX_MU_MCR ((volatile unsigned int*)(MMIO_BASE+0x00215050))
#define AUX_MU_LSR ((volatile unsigned int*)(MMIO_BASE+0x00215054))
#define AUX_MU_MSR ((volatile unsigned int*)(MMIO_BASE+0x00215058))
#define AUX_MU_SCRATCH ((volatile unsigned int*)(MMIO_BASE+0x0021505C))
#define AUX_MU_CNTL ((volatile unsigned int*)(MMIO_BASE+0x00215060))
#define AUX_MU_STAT ((volatile unsigned int*)(MMIO_BASE+0x00215064))
#define AUX_MU_BAUD ((volatile unsigned int*)(MMIO_BASE+0x00215068))
/**
* Set baud rate and characteristics (115200 8N1) and map to GPIO
*/
void uart_init()
{
register unsigned int r;
/* initialize UART */
*AUX_ENABLE |=1; // enable UART1, AUX mini uart
*AUX_MU_IER = 0;
*AUX_MU_CNTL = 0;
*AUX_MU_LCR = 3; // 8 bits
*AUX_MU_MCR = 0;
*AUX_MU_IER = 0;
*AUX_MU_IIR = 0xc6; // disable interrupts
*AUX_MU_BAUD = 270; // 115200 baud
/* map UART1 to GPIO pins */
r=*GPFSEL1;
r&=~((7<<12)|(7<<15)); // gpio14, gpio15
r|=(2<<12)|(2<<15); // alt5
*GPFSEL1 = r;
*GPPUD = 0; // enable pins 14 and 15
r=150; while(r--) { asm volatile("nop"); }
*GPPUDCLK0 = (1<<14)|(1<<15);
r=150; while(r--) { asm volatile("nop"); }
*GPPUDCLK0 = 0; // flush GPIO setup
*AUX_MU_CNTL = 3; // enable Tx, Rx
}
/**
* Send a character
*/
void uart_send(unsigned int c) {
/* wait until we can send */
do{asm volatile("nop");}while(!(*AUX_MU_LSR&0x20));
/* write the character to the buffer */
*AUX_MU_IO=c;
}
/**
* Receive a character
*/
char uart_getc() {
char r;
/* wait until something is in the buffer */
do{asm volatile("nop");}while(!(*AUX_MU_LSR&0x01));
/* read it and return */
r=(char)(*AUX_MU_IO);
/* convert carrige return to newline */
return r=='\r'?'\n':r;
}
/**
* Display a string
*/
void uart_puts(char *s) {
while(*s) {
/* convert newline to carrige return + newline */
if(*s=='\n')
uart_send('\r');
uart_send(*s++);
}
}
/**
* Display a binary value in hexadecimal
*/
void uart_hex(unsigned int d) {
unsigned int n;
int c;
for(c=28;c>=0;c-=4) {
// get highest tetrad
n=(d>>c)&0xF;
// 0-9 => '0'-'9', 10-15 => 'A'-'F'
n+=n>9?0x37:0x30;
uart_send(n);
}
}

@ -1,30 +0,0 @@
/*
* Copyright (C) 2018 bzt (bztsrc@github)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
void uart_init();
void uart_send(unsigned int c);
char uart_getc();
void uart_puts(char *s);
void uart_hex(unsigned int d);

@ -0,0 +1,35 @@
[[package]]
name = "kernel8"
version = "0.1.0"
dependencies = [
"raspi3_glue 0.1.0",
"rlibc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "raspi3_glue"
version = "0.1.0"
[[package]]
name = "rlibc"
version = "1.0.0"
source = "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"
[[package]]
name = "volatile-register"
version = "0.2.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 rlibc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe"
"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"

@ -0,0 +1,9 @@
[package]
name = "kernel8"
version = "0.1.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies]
raspi3_glue = { path = "raspi3_glue" }
volatile-register = "0.2.0"
rlibc = "1.0"

@ -0,0 +1,58 @@
#
# MIT License
#
# Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
TARGET = aarch64-raspi3-none-elf
CROSS_CONTAINER = ./dockcross-linux-aarch64
CROSS_CONTAINER_OBJCOPY = aarch64-linux-gnu-objcopy
QEMU_CONTAINER = andrerichter/raspi3-qemu
DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
all: clean kernel8.img
target/$(TARGET)/debug/kernel8: src/main.rs
RUST_TARGET_PATH=$(shell pwd) xargo build --target=$(TARGET)
cp $@ .
target/$(TARGET)/release/kernel8: src/main.rs
RUST_TARGET_PATH=$(shell pwd) xargo build --target=$(TARGET) --release
cp $@ .
ifeq ($(DEBUG),1)
kernel8: target/$(TARGET)/debug/kernel8
else
kernel8: target/$(TARGET)/release/kernel8
endif
kernel8.img: kernel8
$(CROSS_CONTAINER) $(CROSS_CONTAINER_OBJCOPY) -O binary -S $< kernel8.img
qemu:
$(DOCKER_CMD) $(QEMU_CONTAINER) $(QEMU_CMD) -serial stdio
clean:
cargo clean
rm -f kernel8

@ -0,0 +1,73 @@
# Tutorial 04 - Mailboxes
Before we could go on with UART0, we need mailboxes. So in this tutorial we
introduce the mailbox interface. We'll use it to query the board's serial
number and print that out on UART1.
NOTE: qemu does not redirect UART1 to terminal by default, only UART0!
## uart.rs
`MiniUart::hex(&self, d: u32)` prints out a binary value in hexadecimal format.
## mbox.rs
The mailbox interface. First we fill up the message in the `mbox.buffer` array,
then we call `Mbox::call(&mut self, channel: u32)` to pass it to the GPU,
specifying the mailbox channel. In this example we have used the [property
channel], which requires the message to be formatted as:
[property channel]: (https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface)
```
0. size of the message in bytes, (x+1)*4
1. mbox::REQUEST magic value, indicates request message
2-x. tags
x+1. mbox::tag::LAST magic value, indicates no more tags
```
Where each tag looks like:
```
n+0. tag identifier
n+1. value buffer size in bytes
n+2. must be zero
n+3. optional value buffer
```
### rlibc
The mailbox buffer is a fixed array that is zero-initialized. To achieve
zero-initialization, Rust utilizies and links to the `memset()` function, which
is normally provided by `libc`.
Since we are writing a `no_std` crate, we need to explicitly provide it. The
easiest way is pulling in [rlibc] by adding it as an `extern crate` to `main.rs`
and adding the dependency to `Cargo.toml`.
[rlibc]: https://github.com/alexcrichton/rlibc
### Synchronization
When signaling the GPU about a new mailbox message, we need to take care that
mailbox buffer setup has really finished. Both setting up mailbox contents and
signaling the GPU is done with store operations to memory (RAM and MMIO). There
is an unlikely chance that the compiler reorders instructions, resulting in
signaling the GPU _before_ all of the contents have been written to the mailbox
buffer. We prevent this by inserting a [compiler fence].
Please note that such reordering might also be done by CPUs that feature
[out-of-order execution]. Lucky us, although the Rasperry Pi 3 features
`ARMv8.0-A` CPU cores, the `Cortex-A53` variant is used, [which does not support
this feature]. Otherwise, a [fence] that additionally [emits corresponding CPU
instructions] to prevent this behavior would be needed.
[compiler fence]: https://doc.rust-lang.org/beta/core/sync/atomic/fn.compiler_fence.html
[out-of-order execution]: https://en.wikipedia.org/wiki/Out-of-order_execution
[which does not support this feature]: https://en.wikipedia.org/wiki/Comparison_of_ARMv8-A_cores
[fence]: https://doc.rust-lang.org/std/sync/atomic/fn.fence.html
[emits corresponding CPU instructions]: https://developer.arm.com/products/architecture/a-profile/docs/100941/latest/barriers
## Main
We query the board's serial number and then we display it on the serial console.

@ -0,0 +1,24 @@
{
"arch": "aarch64",
"data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
"executables": true,
"linker-flavor": "ld.lld",
"linker-is-gnu": true,
"pre-link-args": {
"ld.lld": [
"--script=link.ld"
]
},
"llvm-target": "aarch64-unknown-none",
"no-compiler-rt": true,
"features": "+a53,+strict-align",
"max-atomic-width": 128,
"os": "none",
"panic": "abort",
"panic-strategy": "abort",
"relocation-model": "pic",
"target-c-int-width": "32",
"target-endian": "little",
"target-pointer-width": "64",
"disable-redzone": true
}

@ -0,0 +1,246 @@
#!/bin/bash
DEFAULT_DOCKCROSS_IMAGE=dockcross/linux-arm64
#------------------------------------------------------------------------------
# Helpers
#
err() {
echo -e >&2 ERROR: $@\\n
}
die() {
err $@
exit 1
}
has() {
# eg. has command update
local kind=$1
local name=$2
type -t $kind:$name | grep -q function
}
#------------------------------------------------------------------------------
# Command handlers
#
command:update-image() {
docker pull $FINAL_IMAGE
}
help:update-image() {
echo Pull the latest $FINAL_IMAGE .
}
command:update-script() {
if cmp -s <( docker run --rm $FINAL_IMAGE ) $0; then
echo $0 is up to date
else
echo -n Updating $0 '... '
docker run --rm $FINAL_IMAGE > $0 && echo ok
fi
}
help:update-image() {
echo Update $0 from $FINAL_IMAGE .
}
command:update() {
command:update-image
command:update-script
}
help:update() {
echo Pull the latest $FINAL_IMAGE, and then update $0 from that.
}
command:help() {
if [[ $# != 0 ]]; then
if ! has command $1; then
err \"$1\" is not an dockcross command
command:help
elif ! has help $1; then
err No help found for \"$1\"
else
help:$1
fi
else
cat >&2 <<ENDHELP
Usage: dockcross [options] [--] command [args]
By default, run the given *command* in an dockcross Docker container.
The *options* can be one of:
--args|-a Extra args to the *docker run* command
--image|-i Docker cross-compiler image to use
--config|-c Bash script to source before running this script
Additionally, there are special update commands:
update-image
update-script
update
For update command help use: $0 help <command>
ENDHELP
exit 1
fi
}
#------------------------------------------------------------------------------
# Option processing
#
special_update_command=''
while [[ $# != 0 ]]; do
case $1 in
--)
shift
break
;;
--args|-a)
ARG_ARGS="$2"
shift 2
;;
--config|-c)
ARG_CONFIG="$2"
shift 2
;;
--image|-i)
ARG_IMAGE="$2"
shift 2
;;
update|update-image|update-script)
special_update_command=$1
break
;;
-*)
err Unknown option \"$1\"
command:help
exit
;;
*)
break
;;
esac
done
# The precedence for options is:
# 1. command-line arguments
# 2. environment variables
# 3. defaults
# Source the config file if it exists
DEFAULT_DOCKCROSS_CONFIG=~/.dockcross
FINAL_CONFIG=${ARG_CONFIG-${DOCKCROSS_CONFIG-$DEFAULT_DOCKCROSS_CONFIG}}
[[ -f "$FINAL_CONFIG" ]] && source "$FINAL_CONFIG"
# Set the docker image
FINAL_IMAGE=${ARG_IMAGE-${DOCKCROSS_IMAGE-$DEFAULT_DOCKCROSS_IMAGE}}
# Handle special update command
if [ "$special_update_command" != "" ]; then
case $special_update_command in
update)
command:update
exit $?
;;
update-image)
command:update-image
exit $?
;;
update-script)
command:update-script
exit $?
;;
esac
fi
# Set the docker run extra args (if any)
FINAL_ARGS=${ARG_ARGS-${DOCKCROSS_ARGS}}
# Bash on Ubuntu on Windows
UBUNTU_ON_WINDOWS=$([ -e /proc/version ] && grep -l Microsoft /proc/version || echo "")
# MSYS, Git Bash, etc.
MSYS=$([ -e /proc/version ] && grep -l MINGW /proc/version || echo "")
if [ -z "$UBUNTU_ON_WINDOWS" -a -z "$MSYS" ]; then
USER_IDS="-e BUILDER_UID=$( id -u ) -e BUILDER_GID=$( id -g ) -e BUILDER_USER=$( id -un ) -e BUILDER_GROUP=$( id -gn )"
fi
# Change the PWD when working in Docker on Windows
if [ -n "$UBUNTU_ON_WINDOWS" ]; then
HOST_PWD=$PWD
HOST_PWD=${HOST_PWD/\/mnt\//}
HOST_PWD=${HOST_PWD/\//:\/}
elif [ -n "$MSYS" ]; then
HOST_PWD=$PWD
HOST_PWD=${HOST_PWD/\//}
HOST_PWD=${HOST_PWD/\//:\/}
else
HOST_PWD=$PWD
fi
# Mount Additional Volumes
if [ -z "$SSH_DIR" ]; then
SSH_DIR="$HOME/.ssh"
fi
HOST_VOLUMES=
if [ -e "$SSH_DIR" ]; then
HOST_VOLUMES+="-v $SSH_DIR:/home/$(id -un)/.ssh"
fi
#------------------------------------------------------------------------------
# Now, finally, run the command in a container
#
tty -s && TTY_ARGS=-ti || TTY_ARGS=
CONTAINER_NAME=dockcross_$RANDOM
docker run $TTY_ARGS --name $CONTAINER_NAME \
-v "$HOST_PWD":/work \
$HOST_VOLUMES \
$USER_IDS \
$FINAL_ARGS \
$FINAL_IMAGE "$@"
run_exit_code=$?
# Attempt to delete container
rm_output=$(docker rm -f $CONTAINER_NAME 2>&1)
rm_exit_code=$?
if [[ $rm_exit_code != 0 ]]; then
if [[ "$CIRCLECI" == "true" ]] && [[ $rm_output == *"Driver btrfs failed to remove"* ]]; then
: # Ignore error because of https://circleci.com/docs/docker-btrfs-error/
else
echo "$rm_output"
exit $rm_exit_code
fi
fi
exit $run_exit_code
################################################################################
#
# This image is not intended to be run manually.
#
# To create a dockcross helper script for the
# dockcross/linux-armv7 image, run:
#
# docker run --rm dockcross/linux-armv7 > dockcross-linux-armv7
# chmod +x dockcross-linux-armv7
#
# You may then wish to move the dockcross script to your PATH.
#
################################################################################

Binary file not shown.

@ -0,0 +1,6 @@
[package]
name = "raspi3_glue"
version = "0.1.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies]

@ -1,5 +1,6 @@
/*
* Copyright (C) 2018 bzt (bztsrc@github)
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@ -25,9 +26,9 @@
.section ".text.boot"
.global _start
.global _boot_cores
_start:
_boot_cores:
// read cpu id, stop slave cores
mrs x1, mpidr_el1
and x1, x1, #3
@ -38,18 +39,10 @@ _start:
2: // cpu id == 0
// set stack before our code
ldr x1, =_start
ldr x1, =_boot_cores
mov sp, x1
// clear bss
ldr x1, =__bss_start
ldr w2, =__bss_size
3: cbz w2, 4f
str xzr, [x1], #8
sub w2, w2, #1
cbnz w2, 3b
// jump to C code, should not return
4: bl main
// jump to Rust code, should not return
bl reset
// for failsafe, halt this core too
b 1b

@ -0,0 +1,88 @@
/*
* MIT License
*
* Copyright (c) 2018 Jorge Aparicio
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#![feature(lang_items)]
#![no_std]
#![feature(global_asm)]
use core::ptr;
#[lang = "panic_fmt"]
unsafe extern "C" fn panic_fmt(
_args: core::fmt::Arguments,
_file: &'static str,
_line: u32,
_col: u32,
) -> ! {
loop {}
}
#[lang = "start"]
extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize
where
T: Termination,
{
user_main().report() as isize
}
#[lang = "termination"]
trait Termination {
fn report(self) -> i32;
}
impl Termination for () {
fn report(self) -> i32 {
0
}
}
#[no_mangle]
pub unsafe extern "C" fn reset() -> ! {
extern "C" {
fn main(argc: isize, argv: *const *const u8) -> isize;
static mut __bss_start: u32;
static mut __bss_end: u32;
}
zero_bss(&mut __bss_start, &mut __bss_end);
main(0, ptr::null());
loop {}
}
unsafe fn zero_bss(bss_start: *mut u32, bss_end: *mut u32) {
let mut bss = bss_start;
while bss < bss_end {
// NOTE(ptr::write*) to force aligned stores
// NOTE(volatile) to prevent the compiler from optimizing this into `memclr`
ptr::write_volatile(bss, 0);
bss = bss.offset(1);
}
}
// Disable all cores except core 0, and then jump to reset()
global_asm!(include_str!("boot_cores.S"));

@ -0,0 +1,30 @@
/*
* MIT License
*
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use volatile_register::RW;
use super::MMIO_BASE;
pub const GPFSEL1: *const RW<u32> = (MMIO_BASE + 0x00200004) as *const RW<u32>;
pub const GPPUD: *const RW<u32> = (MMIO_BASE + 0x00200094) as *const RW<u32>;
pub const GPPUDCLK0: *const RW<u32> = (MMIO_BASE + 0x00200098) as *const RW<u32>;

@ -0,0 +1,84 @@
/*
* MIT License
*
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#![no_std]
#![feature(asm)]
extern crate raspi3_glue;
extern crate rlibc; // for memset et al.b
extern crate volatile_register;
const MMIO_BASE: u32 = 0x3F000000;
mod mbox;
mod gpio;
mod uart;
use core::sync::atomic::{compiler_fence, Ordering};
fn main() {
let mut mbox = mbox::Mbox::new();
let uart = uart::MiniUart::new();
// set up serial console
uart.init();
// get the board's unique serial number with a mailbox call
mbox.buffer[0] = 8 * 4; // length of the message
mbox.buffer[1] = mbox::REQUEST; // this is a request message
mbox.buffer[2] = mbox::tag::GETSERIAL; // get serial number command
mbox.buffer[3] = 8; // buffer size
mbox.buffer[4] = 8;
mbox.buffer[5] = 0; // clear output buffer
mbox.buffer[6] = 0;
mbox.buffer[7] = mbox::tag::LAST;
// Insert a compiler fence that ensures that all stores to the
// mbox buffer are finished before the GPU is signaled (which is
// done by a store operation as well).
compiler_fence(Ordering::SeqCst);
// send the message to the GPU and receive answer
let serial_avail = match mbox.call(mbox::channel::PROP) {
Err(_) => false,
Ok(()) => true,
};
uart.getc(); // Press a key first before being greeted
uart.puts("Hello Rustacean!\n");
if serial_avail {
uart.puts("My serial number is: ");
uart.hex(mbox.buffer[6]);
uart.hex(mbox.buffer[5]);
uart.puts("\n");
} else {
uart.puts("Unable to query serial!\n");
}
// echo everything back
loop {
uart.send(uart.getc());
}
}

@ -0,0 +1,134 @@
/*
* MIT License
*
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use super::MMIO_BASE;
use volatile_register::{RO, WO};
pub const VIDEOCORE_MBOX: u32 = MMIO_BASE + 0xB880;
#[allow(non_snake_case)]
#[repr(C, packed)]
pub struct Registers {
READ: RO<u32>, // 0x00
reserved: [u8; 0xC], // 0x04
POLL: RO<u32>, // 0x10
SENDER: RO<u32>, // 0x14
STATUS: RO<u32>, // 0x18
CONFIG: RO<u32>, // 0x1C
WRITE: WO<u32>, // 0x20
}
// Custom errors
pub enum MboxError {
ResponseError,
UnknownError,
}
pub type Result<T> = ::core::result::Result<T, MboxError>;
// Channels
pub mod channel {
pub const PROP: u32 = 8;
}
// Tags
pub mod tag {
pub const GETSERIAL: u32 = 0x10004;
pub const LAST: u32 = 0;
}
// Responses
mod response {
pub const SUCCESS: u32 = 0x80000000;
pub const ERROR: u32 = 0x80000001; // error parsing request buffer (partial response)
}
pub const REQUEST: u32 = 0;
const FULL: u32 = 0x80000000;
const EMPTY: u32 = 0x40000000;
// Public interface to the mailbox
#[repr(C)]
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.
pub buffer: [u32; 36],
registers: *const Registers,
}
impl Mbox {
pub fn new() -> Mbox {
Mbox {
buffer: [0; 36],
registers: VIDEOCORE_MBOX as *const Registers,
}
}
/// Make a mailbox call. Returns Err(MboxError) on failure, Ok(()) success
pub fn call(&mut self, channel: u32) -> Result<()> {
// wait until we can write to the mailbox
loop {
unsafe {
if !(((*self.registers).STATUS.read() & FULL) == FULL) {
break;
}
asm!("nop" :::: "volatile");
}
}
// write the address of our message to the mailbox with channel identifier
unsafe {
(*self.registers)
.WRITE
.write(((self.buffer.as_mut_ptr() as u32) & !0xF) | (channel & 0xF));
}
// now wait for the response
loop {
// is there a response?
loop {
unsafe {
if !(((*self.registers).STATUS.read() & EMPTY) == EMPTY) {
break;
}
asm!("nop" :::: "volatile");
}
}
let resp: u32 = unsafe { (*self.registers).READ.read() };
// is it a response to our message?
if ((resp & 0xF) == channel) && ((resp & !0xF) == (self.buffer.as_mut_ptr() as u32)) {
// is it a valid successful response?
return match self.buffer[1] {
response::SUCCESS => Ok(()),
response::ERROR => Err(MboxError::ResponseError),
_ => Err(MboxError::UnknownError),
};
}
}
}
}

@ -0,0 +1,169 @@
/*
* MIT License
*
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use super::MMIO_BASE;
use volatile_register::*;
use gpio;
const MINI_UART_BASE: u32 = MMIO_BASE + 0x215000;
/// Auxilary mini UART registers
#[allow(non_snake_case)]
#[repr(C, packed)]
struct Registers {
_reserved0: u32, // 0x00
ENABLES: RW<u32>, // 0x04
_reserved1: [u8; 0x38], // 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
}
pub struct MiniUart {
registers: *const Registers,
}
impl MiniUart {
pub fn new() -> MiniUart {
MiniUart {
registers: MINI_UART_BASE as *const Registers,
}
}
///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self) {
// initialize UART
unsafe {
(*self.registers).ENABLES.modify(|x| x | 1); // enable UART1, AUX mini uart
(*self.registers).MU_IER.write(0);
(*self.registers).MU_CNTL.write(0);
(*self.registers).MU_LCR.write(3); // 8 bits
(*self.registers).MU_MCR.write(0);
(*self.registers).MU_IER.write(0);
(*self.registers).MU_IIR.write(0xC6); // disable interrupts
(*self.registers).MU_BAUD.write(270); // 115200 baud
// map UART1 to GPIO pins
(*gpio::GPFSEL1).modify(|x| {
// Modify with a closure
let mut ret = x;
ret &= !((7 << 12) | (7 << 15)); // gpio14, gpio15
ret |= (2 << 12) | (2 << 15); // alt5
ret
});
(*gpio::GPPUD).write(0); // enable pins 14 and 15
for _ in 0..150 {
asm!("nop" :::: "volatile");
}
(*gpio::GPPUDCLK0).write((1 << 14) | (1 << 15));
for _ in 0..150 {
asm!("nop" :::: "volatile");
}
(*gpio::GPPUDCLK0).write(0); // flush GPIO setup
(*self.registers).MU_CNTL.write(3); // enable Tx, Rx
}
}
/// Send a character
pub fn send(&self, c: char) {
unsafe {
// wait until we can send
loop {
if ((*self.registers).MU_LSR.read() & 0x20) == 0x20 {
break;
}
asm!("nop" :::: "volatile");
}
// write the character to the buffer
(*self.registers).MU_IO.write(c as u32);
}
}
/// Receive a character
pub fn getc(&self) -> char {
unsafe {
// wait until something is in the buffer
loop {
if ((*self.registers).MU_LSR.read() & 0x01) == 0x01 {
break;
}
asm!("nop" :::: "volatile");
}
}
// read it and return
let mut ret = unsafe { (*self.registers).MU_IO.read() as u8 as char };
// convert carrige return to newline
if ret == '\r' {
ret = '\n'
}
ret
}
/// Display a string
pub fn puts(&self, string: &str) {
for c in string.chars() {
// convert newline to carrige return + newline
if c == '\n' {
self.send('\r')
}
self.send(c);
}
}
/// Display a binary value in hexadecimal
pub fn hex(&self, d: u32) {
let mut n;
for i in 0..8 {
// get highest tetrad
n = d.wrapping_shr(28 - i * 4) & 0xF;
// 0-9 => '0'-'9', 10-15 => 'A'-'F'
// Add proper offset for ASCII table
if n > 9 {
n += 0x37;
} else {
n += 0x30;
}
self.send(n as u8 as char);
}
}
}
Loading…
Cancel
Save