Lab1: Mini UART & Simple Shell

MMIO

rpi3透過MMIO(memory mapped io)方式存取周邊裝置的暫存器
在ARM CPU和pheripheral bus間坐落VideoCore/ARM MMU,MMU將周邊裝置映射到0x3f000000-0x7e000000

GPIO

GPIO可設置為給LED或按鈕使用的input-output或UART、SPI使用的alternate functions兩種模式。使用alternate function時需額外設置pull up/down register來禁用GPIO pull up/down,透過設置GPPUDGPPUDCLKn
GPIO 14, 15腳位可用做於mini UART或PL011 UART。設置GPFSELnALT5來使用mini UART或ALT0來使用PL011 UART。

Setup

設置過程中多次參考BCM2835的文件,為什麼非BCM2837?

Unfortunately Broadcom (the manufacturer of the SoC chip) is legendary bad at documenting their products. The best we’ve got is the BCM2835 documentation, which is close enough. (REF: Bare Metal Programming on Raspberry Pi 3)

且BCM2837的peripherals映射地址以0x3F000000開頭而非BCM2835的0x7E000000

Similarily, all peripherals communicates in memory with the CPU. Each has it’s dedicated memory address starting from 0x3F000000

Shell

由於沒有glibc可用,必須要自行實作格式化輸出函數vsprintf ,用在命令的顯示輸出上。
要曉得變數的大小能使用aarch64-linux-gnu-gcc -c -Q -mcpu=cortex-a72 --help=target顯示data model。

vsprintf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
unsigned int vsprintf(char *dst, char *fmt, __builtin_va_list args) {

while (*fmt) {
// argument access
if (*fmt == '%') {
fmt++;
if (*fmt == '%') {
goto put;
}

if (*fmt == 'c') {
// character
arg = __builtin_va_arg(args, int);
*dst++ = (char)arg;
fmt++;
continue;
}

原理是逐字讀取,根據對應的字元從args讀取對應的參數:

1
2
3
4
// char
arg = __builtin_va_arg(args, int);
// float
arg = __builtin_va_arg(args, double);

值得注意的是charfloat並沒有使用原來的型態進行讀取,原因在於...對應的參數會被擴展為較大的型態,charshort擴展為intfloat擴展為double

6.5.2.2.7 The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments.

vsprintf目前只支援integercharfloat三種型態的格式化輸出,且float由兩個整數部份組成(整數18位+小數6位)無法自由調整。

1
2
char dst[20];
vsprintf(dst, "%d %f %c", 123, 1.23, 'a');

Reference