From 298bec39c806d25381eb11b21f2cf5c7b3de79f0 Mon Sep 17 00:00:00 2001 From: Andre Richter Date: Sun, 1 Apr 2018 16:41:31 +0200 Subject: [PATCH] Add tutorial 03_uart1 --- .03_uart1/Makefile | 43 --- .03_uart1/OLVASSEL.md | 35 --- .03_uart1/README.md | 34 --- .03_uart1/gpio.h | 45 ---- .03_uart1/kernel8.img | Bin 560 -> 0 bytes .03_uart1/main.c | 40 --- .03_uart1/uart.c | 104 -------- .03_uart1/uart.h | 29 --- 03_uart1/Cargo.lock | 28 ++ 03_uart1/Cargo.toml | 8 + 03_uart1/Makefile | 58 +++++ 03_uart1/README.md | 44 ++++ 03_uart1/aarch64-raspi3-none-elf.json | 24 ++ 03_uart1/dockcross-linux-aarch64 | 246 ++++++++++++++++++ 03_uart1/kernel8.img | Bin 0 -> 832 bytes {.03_uart1 => 03_uart1}/link.ld | 0 03_uart1/raspi3_glue/Cargo.toml | 6 + .../raspi3_glue/src/boot_cores.S | 19 +- 03_uart1/raspi3_glue/src/lib.rs | 71 +++++ 03_uart1/src/gpio.rs | 30 +++ 03_uart1/src/main.rs | 48 ++++ 03_uart1/src/uart.rs | 149 +++++++++++ README.md | 4 +- 23 files changed, 720 insertions(+), 345 deletions(-) delete mode 100644 .03_uart1/Makefile delete mode 100644 .03_uart1/OLVASSEL.md delete mode 100644 .03_uart1/README.md delete mode 100644 .03_uart1/gpio.h delete mode 100755 .03_uart1/kernel8.img delete mode 100644 .03_uart1/main.c delete mode 100644 .03_uart1/uart.c delete mode 100644 .03_uart1/uart.h create mode 100644 03_uart1/Cargo.lock create mode 100644 03_uart1/Cargo.toml create mode 100644 03_uart1/Makefile create mode 100644 03_uart1/README.md create mode 100644 03_uart1/aarch64-raspi3-none-elf.json create mode 100755 03_uart1/dockcross-linux-aarch64 create mode 100755 03_uart1/kernel8.img rename {.03_uart1 => 03_uart1}/link.ld (100%) create mode 100644 03_uart1/raspi3_glue/Cargo.toml rename .03_uart1/start.S => 03_uart1/raspi3_glue/src/boot_cores.S (84%) create mode 100644 03_uart1/raspi3_glue/src/lib.rs create mode 100644 03_uart1/src/gpio.rs create mode 100644 03_uart1/src/main.rs create mode 100644 03_uart1/src/uart.rs diff --git a/.03_uart1/Makefile b/.03_uart1/Makefile deleted file mode 100644 index f6ba7559..00000000 --- a/.03_uart1/Makefile +++ /dev/null @@ -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 diff --git a/.03_uart1/OLVASSEL.md b/.03_uart1/OLVASSEL.md deleted file mode 100644 index 99c77f23..00000000 --- a/.03_uart1/OLVASSEL.md +++ /dev/null @@ -1,35 +0,0 @@ -Oktatóanyag 03 - UART1, Auxilary mini UART -========================================== - -Ezúttal a hírhedt Helló Világ példát vesszük elő. Előbb az UART1-re írjuk meg, mivel azt egyszerűbb programozni, -mivel fix órafrekvenciája van. - -FIGYELEM: qemu nem irányítja át alapból az UART1-et a terminálra, csak az UART0-át! - -Gpio.h ------- - -Van egy új fejléc fájlunk. Ebben definiáljuk az MMIO címét, és a GPIO vezérlő szavainak címeit. Ez egy nagyon -népszerű fejléc lesz, majd minden eszközhöz kelleni fog. - -Uart.h, uart.c --------------- - -Egy nagyon minimális változat. - -`uart_init()` inicializálja az UART csipet, és soros vonalat leképezi a GPIO lábakra. - -`uart_send(c)` kiküld egy karatert a soros vonalra. - -`uart_getc()` fogad egy karatert. A kocsivissza karakter (13) automatikusan újsor karakterré (10) konvertálódik. - -`uart_puts(s)` kiír egy szöveget. Újsor karakternél kiküld egy kocsivissza karatert is (13 + 10). - -Main ----- - -Először is meg kell hívni az uart inicializáló kódját. Aztán kiküldjük, "Helló Világ!". Ha beszereztél USB -soros kábelt, akkor ennek meg kell jelennie a minicom ablakában. Ezután minden, minicom-ban leütött karaktert -visszaküld és kiír. Ha nem kapcsoltad ki a helyi visszhangot (local echo), akkor ez azt jelenti, hogy minden -leütött karaktert duplán fog kiírni a minicom. - diff --git a/.03_uart1/README.md b/.03_uart1/README.md deleted file mode 100644 index e176b9e2..00000000 --- a/.03_uart1/README.md +++ /dev/null @@ -1,34 +0,0 @@ -Tutorial 03 - UART1, Auxilary mini UART -======================================= - -It is time for the famous Hello World example. We're going to write on the UART1 first, as it's easier to program -as it has a fixed clocked frequency. - -NOTE: qemu does not redirect UART1 to terminal by default, only UART0! - -Gpio.h ------- - -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. - -Uart.h, uart.c --------------- - -A very minimal implementation. - -`uart_init()` initializes the device and maps it to the GPIO ports. - -`uart_send(c)` sends a character over the serial line. - -`uart_getc()` receives a character. The carrige return character (13) will be converted into a newline character (10). - -`uart_puts(s)` prints out a string. On newline, a carrige return character will also be sent (13 + 10). - -Main ----- - -First we have to call the uart initialization code. Then we say "Hello World!". If you've purchased an USB -serial cable, you should see it on minicom's screen. After that every character typed in minicom will be -echoed back. If you haven't turned off local echo, that means you'll see every pressed key twice. - diff --git a/.03_uart1/gpio.h b/.03_uart1/gpio.h deleted file mode 100644 index 52fa671d..00000000 --- a/.03_uart1/gpio.h +++ /dev/null @@ -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)) diff --git a/.03_uart1/kernel8.img b/.03_uart1/kernel8.img deleted file mode 100755 index 1a126bb4f64fd86372666758d6435cc0a39c9c22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 560 zcmZ8eze^)g5dPloo<>oTc%X>ju0M{$MvxbGNdiNJ7f=zgS6LTQ9Fgw4#nQ!LJ4`gowFFY&(r$yhrvDS{36xQ=V zL-ai`Rs}No8lIOueEwzOmu0V|Ro8*4*5T9GSsz$VN@GVlT6YSJ~d53v$cHt53 z5@#zCqbM!6cw6NTvZM4A&T~7zq`0^2>~}Ges}LtKv3=rNws#4{6_k#oFBroF(vum) z7_N*y8K7Pp~9;BKFq%5-|Ng5<`i%Aug}|jo%zS^-P*fn pCv6vvBWu6%76;I&#vj_(;&YxW>Nc?}o8|JhzqY+o-U#{$d;#dW(Z>J) diff --git a/.03_uart1/main.c b/.03_uart1/main.c deleted file mode 100644 index d2f6850a..00000000 --- a/.03_uart1/main.c +++ /dev/null @@ -1,40 +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" - -void main() -{ - // set up serial console - uart_init(); - - // say hello - uart_puts("Hello World!\n"); - - // echo everything back - while(1) { - uart_send(uart_getc()); - } -} diff --git a/.03_uart1/uart.c b/.03_uart1/uart.c deleted file mode 100644 index a76ef93d..00000000 --- a/.03_uart1/uart.c +++ /dev/null @@ -1,104 +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++); - } -} diff --git a/.03_uart1/uart.h b/.03_uart1/uart.h deleted file mode 100644 index 72f8e315..00000000 --- a/.03_uart1/uart.h +++ /dev/null @@ -1,29 +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); diff --git a/03_uart1/Cargo.lock b/03_uart1/Cargo.lock new file mode 100644 index 00000000..3352bcb2 --- /dev/null +++ b/03_uart1/Cargo.lock @@ -0,0 +1,28 @@ +[[package]] +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)", +] + +[[package]] +name = "raspi3_glue" +version = "0.1.0" + +[[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 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" diff --git a/03_uart1/Cargo.toml b/03_uart1/Cargo.toml new file mode 100644 index 00000000..df9e43a9 --- /dev/null +++ b/03_uart1/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "kernel8" +version = "0.1.0" +authors = ["Andre Richter "] + +[dependencies] +raspi3_glue = { path = "raspi3_glue" } +volatile-register = "0.2.0" diff --git a/03_uart1/Makefile b/03_uart1/Makefile new file mode 100644 index 00000000..ea74c4da --- /dev/null +++ b/03_uart1/Makefile @@ -0,0 +1,58 @@ +# +# MIT License +# +# Copyright (c) 2018 Andre Richter +# +# 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) -d in_asm + +clean: + cargo clean + rm -f kernel8 diff --git a/03_uart1/README.md b/03_uart1/README.md new file mode 100644 index 00000000..d541800d --- /dev/null +++ b/03_uart1/README.md @@ -0,0 +1,44 @@ +Tutorial 03 - UART1, Auxilary mini UART +======================================= + +It is time for the famous Hello World example. We're going to write on the UART1 +first, as it's easier to program as it has a fixed clocked frequency. + +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 are using the [volatile_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/ + +uart.rs +-------------- + +A very minimal implementation. + +`MiniUart::init(&self)` initializes the device and maps it to the GPIO ports. + +`MiniUart::send(&self, c: char)` sends a character over the serial line. + +`MiniUart::getc(&self)` receives a character. The carrige return character (13) +will be converted into a newline character (10). + +`MiniUart::puts(&self, string: &str)` prints out a string. On newline, a carrige +return character will also be sent (13 + 10). + +Main +---- + +First we have to call the uart initialization code. Then we wait for the first +keypress from the user before we say "Hello Rustacean!". If you've purchased an +USB serial cable, you should see it on `screen`'s screen. After that, every +character typed in `screen` will be echoed back. If you haven't turned off local +echo, that means you'll see every pressed key twice. diff --git a/03_uart1/aarch64-raspi3-none-elf.json b/03_uart1/aarch64-raspi3-none-elf.json new file mode 100644 index 00000000..407f21b8 --- /dev/null +++ b/03_uart1/aarch64-raspi3-none-elf.json @@ -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 +} diff --git a/03_uart1/dockcross-linux-aarch64 b/03_uart1/dockcross-linux-aarch64 new file mode 100755 index 00000000..3c7481d2 --- /dev/null +++ b/03_uart1/dockcross-linux-aarch64 @@ -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 + 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. +# +################################################################################ diff --git a/03_uart1/kernel8.img b/03_uart1/kernel8.img new file mode 100755 index 0000000000000000000000000000000000000000..e5229c8ed74a02aa376e737671062876aefaf736 GIT binary patch literal 832 zcmZuwO-K}B7=C7FWp{Sh!fmAtV%&nzQIj@OJH*abf^B$knxIR_5+2m0+OqSmb;&<) zqlkh?A&3;|;=#GCdMYdEV8?JF0-fquZlYh$w;EV9@G;-}yx;r0&phuplW1Fxdd>SO zV79wfT^2&@p9C()f&44r!3JZUIN*nT&IB=A2hT{O7x|Y;&PsAMp>S&Y5W-EK_12;q zn#$HAGf@FMn>d##Xs|;tOJkQzXAE_%l1uL2iRPA(1e`ehZlibbNpVxyXMxBw#qUmd zV2@RsZ8OyfOTKg!!r4JJGs7NtnUggIFYIFxv+X_4yeFQWS2IgBmBu1*nFsqfF)ule zlJ}dc8%5Rs7uN!NPS=DCsdw_1u!Gc@rnb=?opS${I(w<}e|X<+{Xd1hkA1%H^x3WH z&W5o2k8k%$@!S`BD)o$NdoIdv8mS2y{Yqt7N9u>LKQUiWyFW_5xAyAm_b_bLeSUFM ztHOs>A4&nmbX*@|L&#Yg@uY%SK!Nk-K+e*sNxuRocplDDlyN6xVu5yIJ#Y-xUhw3s zzUs&cL)L_IRy+?US%Hk4&AbrynlBtP9&YD$<8fl_AJfRMb(zi(P@bpUzZSk0{j}*Q zzqf0`)8d9M}#TVmz7 z9di;}(&_89*6mvZsjEGyYtaBfQ{Da#{=9z!H(eE| literal 0 HcmV?d00001 diff --git a/.03_uart1/link.ld b/03_uart1/link.ld similarity index 100% rename from .03_uart1/link.ld rename to 03_uart1/link.ld diff --git a/03_uart1/raspi3_glue/Cargo.toml b/03_uart1/raspi3_glue/Cargo.toml new file mode 100644 index 00000000..99c8e683 --- /dev/null +++ b/03_uart1/raspi3_glue/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "raspi3_glue" +version = "0.1.0" +authors = ["Andre Richter "] + +[dependencies] diff --git a/.03_uart1/start.S b/03_uart1/raspi3_glue/src/boot_cores.S similarity index 84% rename from .03_uart1/start.S rename to 03_uart1/raspi3_glue/src/boot_cores.S index 27d0c503..f6f28af6 100644 --- a/.03_uart1/start.S +++ b/03_uart1/raspi3_glue/src/boot_cores.S @@ -1,5 +1,6 @@ /* * Copyright (C) 2018 bzt (bztsrc@github) + * Copyright (c) 2018 Andre Richter * * 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 diff --git a/03_uart1/raspi3_glue/src/lib.rs b/03_uart1/raspi3_glue/src/lib.rs new file mode 100644 index 00000000..c9866e3a --- /dev/null +++ b/03_uart1/raspi3_glue/src/lib.rs @@ -0,0 +1,71 @@ +// "Embedded glue code from "The Embedonomicon" by Jorge Aparicio, +// used under CC BY 4.0 +// +// Minor changes and additions were made. +// +// Original Author: https://github.com/japaric +// License: https://creativecommons.org/licenses/by/4.0/ + +#![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(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")); diff --git a/03_uart1/src/gpio.rs b/03_uart1/src/gpio.rs new file mode 100644 index 00000000..03914da2 --- /dev/null +++ b/03_uart1/src/gpio.rs @@ -0,0 +1,30 @@ +/* + * MIT License + * + * Copyright (c) 2018 Andre Richter + * + * 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 = (MMIO_BASE + 0x00200004) as *const RW; +pub const GPPUD: *const RW = (MMIO_BASE + 0x00200094) as *const RW; +pub const GPPUDCLK0: *const RW = (MMIO_BASE + 0x00200098) as *const RW; diff --git a/03_uart1/src/main.rs b/03_uart1/src/main.rs new file mode 100644 index 00000000..660261e8 --- /dev/null +++ b/03_uart1/src/main.rs @@ -0,0 +1,48 @@ +/* + * MIT License + * + * Copyright (c) 2018 Andre Richter + * + * 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 volatile_register; + +const MMIO_BASE: u32 = 0x3F000000; + +mod gpio; +mod uart; + +fn main() { + let uart = uart::MiniUart::new(); + + // set up serial console + uart.init(); + + uart.getc(); // Press a key first before being greeted + uart.puts("Hello Rustacean!\n"); + + loop { + uart.send(uart.getc()); + } +} diff --git a/03_uart1/src/uart.rs b/03_uart1/src/uart.rs new file mode 100644 index 00000000..09fbe060 --- /dev/null +++ b/03_uart1/src/uart.rs @@ -0,0 +1,149 @@ +/* + * MIT License + * + * Copyright (c) 2018 Andre Richter + * + * 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, // 0x04 + _reserved1: [u8; 0x38], // 0x08 + MU_IO: RW, // 0x40 + MU_IER: RW, // 0x44 + MU_IIR: RW, // 0x48 + MU_LCR: RW, // 0x4C + MU_MCR: RW, // 0x50 + MU_LSR: RW, // 0x54 + MU_MSR: RW, // 0x58 + MU_SCRATCH: RW, // 0x5C + MU_CNTL: RW, // 0x60 + MU_STAT: RW, // 0x64 + MU_BAUD: RW, // 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); + } + } +} diff --git a/README.md b/README.md index 7fb23d3d..a40bdfa1 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ This repo follows two main principles: 1. Most importantly: No toolchain hassles. Users eager to try the code should not be bothered with complicated toolchain installation/compilation steps. This is achieved by trying to use the standard Rust toolchain as much as possible, and where not possible, using Docker containers. Please [install Docker for your distro]. 1. Compiler and linker can be used from Rust nightly. 2. QEMU will be used for emulation, but RPi3 support in QEMU is very fresh and has not landed in most of the pre-packaged versions of popular distributions. [This] container will provide it ready to go. - 3. aarch64 toolchain binaries that are not provided Rust, like `objcopy`, will be provided with a container from the [dockcross] project, which does an awesome job of curating various toolchains in containers. + 3. aarch64 toolchain binaries that are not provided by Rust, like `objcopy`, will be provided with a container from the [dockcross] project, which does an awesome job of curating various toolchains in containers. 2. Use as little assembler as possible. Do as much as possible in Rust. Please notice that you won't need to download or prepare the containers upfront. As long as you have docker installed, they will be pulled automatically the first time the Makefile needs them. @@ -28,7 +28,7 @@ Please notice that you won't need to download or prepare the containers upfront. [This]: https://github.com/andre-richter/docker-raspi3-qemu [dockcross]: https://github.com/dockcross/dockcross -For now, only tutorial `05_uart0` is ready. It has been done first in order to provide a classic physical `Hello World` over a real UART. It is planned to add more examples later. Contributions welcome! +For now, only a few basic tutorials are ready, but more will be ported over time. ## Introduction