2021年1月23日 星期六

Raspberry Pi Zero W Project Part 1 - bare metal printf implementation

Step 0 - update config.txt and overlay

config.txt:

enable_uart=1
dtoverlay=miniuart-bt # or disable-bt as you want
kernel=bare_metal.bin

Please remember to download miniuart-bt.dtbo or disable-bt.dtbo and place the files to a folder named "overlays" in your SD card. 

Now, Let's start the lab.

The first step to control a CPU is to dump any message you want!

$ git clone https://github.com/champyen/rpiz_bare_metal.git

$ git checkout 4e769069

There are 4 major files in the example:

  • bare_metal.c - the main example flow
  • head.S - the glue code for entering the flow
  • bare_metal.c - the main example flow
  • printf.c - printf for RPi Zero's PL011 uart
  • bare_metal.ld - linker script of the example

From the "Boot options in config.txt" of Raspberry Pi Document, we know that the default start address is 0x8000.

In bare_metal.ld, you can see the linker script:

OUTPUT_ARCH(arm)
SECTIONS {
    . = 0x8000;
    .text . : {
        *(.text)
    }
    . = ALIGN(4);
    .data . : {
        *(.data)
    }
    . = ALIGN(4);
    .bss . : {
        *(.bss)
        *(COMMON)
    }
    . = ALIGN(4);
    .rodata . : {
        *(.rodata)
        *(.rodata.*)
    }
}
In Makefile, you can see the linking order is - head.o bare_metal.o printf.o.


Therefore, after bootcode.bin, it will jump to the first function in head.S.

In head.S:

.text
_start:
    ldr    sp, =stack_top
    bl    bare_metal_start

stack_top:      .word   0x100000
Before calling the demo function - bare_metal_start, head.S does only one thing - setup the address of 0x100000 as stack pointer.

In bare_metal.c:

void printf(const char *fmt, ...);
void bare_metal_start(void)
{
    printf("\n\n%s: Hello World! %s %s %d\n\n", __func__, __DATE__, __TIME__, __LINE__);
    printf("enter busy loop\n");
    while(1);
}

the bare_metal_start function calls the 'printf' implemented in printf.c to dump two messages.

In printf, the fundamental function is _putc.

#define PL011_BASE  0x20201000
#define PL011_DR    (PL011_BASE + 0x00)
#define PL011_FR    (PL011_BASE + 0x18)
#define _REG(x)        *((unsigned int t *)(x))

void _putc(unsigned char c)
{
    while( (_REG(PL011_FR) & 0x80) == 0);
    _REG(PL011_DR) = c;
}
Please refer to Chapter 13 of the "Peripheral Specificaion" of BCM2835, the _putc just check the Transmit buffer status, and send out a char when Transmit buffer/register is empty.





沒有留言:

Chisel 學習筆記 - Scala 與 Chisel 基礎語法

標題為筆記, 但這篇比較屬於心得 延續 上一篇 的環境建立, 這次計劃藉由 Jserv 最新的 課程安排 來學習 Chisel, 當然個人目標是能夠按照 Jserv 的課程規劃在 期限之內 完成 Lab 3, 由於個人並非 digital designer (現在這年紀也算老貓學...