arm: working bootloader, example app

'make' will produce two outputs:
- bootloader.elf to be loaded into the bitstream
- app.bin to be loaded through the programmer

Loading app.bin is as simple as:
- reset the board
- `python3 prog.py app.bin`
This commit is contained in:
Paul Mathieu 2022-05-10 11:20:02 -07:00
parent 43d245bae2
commit b8b0d94065
10 changed files with 231 additions and 46 deletions

34
arm/app.ld Normal file
View File

@ -0,0 +1,34 @@
MEMORY
{
ICTM (rwx) : ORIGIN = 0x00000800, LENGTH = 12228
}
SECTIONS
{
.text :
{
KEEP(*(.app_init))
*(.text*)
*(.rodata*)
} > ICTM
.bss (NOLOAD) :
{
_bss_begin = .;
*(.bss*)
*(COMMON)
_bss_end = .;
} > ICTM
.data :
{
*(.data*)
__exidx_start = .;
*(.exidx*)
__exidx_end = .;
} > ICTM
_initial_stack_pointer = 16384;
}

23
arm/app_init.cc Normal file
View File

@ -0,0 +1,23 @@
#include <cstdint>
extern "C" int main();
extern uint32_t _bss_begin, _bss_end, _initial_stack_pointer;
__attribute__((section(".app_init")))
void AppInit() {
*(uint32_t*)(0x40000000) = 0;
asm ("mov sp, %0"
:
: "r" (&_initial_stack_pointer)
:
);
// clear .bss
for (uint32_t* ptr = &_bss_begin; ptr < &_bss_end; ptr++) {
*ptr = 0;
}
main();
while(true) {}
}

63
arm/bootloader.cc Normal file
View File

@ -0,0 +1,63 @@
#include <cstdint>
#include "gpio.h"
#include "uart.h"
namespace {
uint8_t UartRead() {
uint8_t c;
while (XUartLite_Recv(uart0, &c, 1) < 1) {}
return c;
}
void UartWrite(uint8_t c) {
XUartLite_Send(uart0, &c, 1);
}
uint32_t UartRead32() {
uint32_t val = 0;
// little endian
val |= (UartRead() << 0);
val |= (UartRead() << 8);
val |= (UartRead() << 16);
val |= (UartRead() << 24);
return val;
}
} // namespace
int main() {
gpio0->data = 1;
InitUarts();
while(1) {
uint8_t c = UartRead();
if (c == 'c') {
uint32_t addr = UartRead32();
uint32_t bytes = UartRead32();
uint8_t* start = reinterpret_cast<uint8_t*>(addr);
uint8_t* end = reinterpret_cast<uint8_t*>(addr + bytes);
for (uint8_t* ptr = start; ptr < end; ptr++) {
*ptr = UartRead();
}
UartWrite('a');
UartWrite(bytes);
gpio0->data = 0xf0;
} else if (c == 'j') {
uint32_t addr = UartRead32();
gpio0->data = 0x55;
addr |= 0x0001;
auto jump = reinterpret_cast<void(*)()>(addr);
jump();
}
}
}

View File

@ -1,6 +1,6 @@
MEMORY
{
ICTM (rwx) : ORIGIN = 0x00000000, LENGTH = 16384
ICTM (rwx) : ORIGIN = 0x00000000, LENGTH = 2048
}
SECTIONS
@ -30,5 +30,5 @@ SECTIONS
__exidx_end = .;
} > ICTM
_initial_stack_pointer = 16384;
_initial_stack_pointer = 2048;
}

9
arm/gpio.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include <cstdint>
struct Gpio {
volatile uint32_t data;
};
#define gpio0 ((Gpio*) 0x40000000)

View File

@ -1,30 +1,12 @@
#include <cstdint>
#include "xuartlite.h"
struct Gpio {
volatile uint32_t data;
};
#define gpio0 ((Gpio*) 0x40000000)
#include "gpio.h"
namespace {
constexpr uintptr_t kUart0BaseAddress = 0x40001000;
XUartLite uart0;
XUartLite_Config uart0_config = {
.DeviceId = 0,
.RegBaseAddr = kUart0BaseAddress,
.BaudRate = 115200,
.UseParity = false,
.DataBits = 8,
};
[[maybe_unused]]
void sleep(int ms) {
for (int i = 0; i < ms; i++) {
// sleep for 1 ms
for (int j = 0; j < 50000; j++) {
for (int j = 0; j < 22000; j++) {
asm("");
}
}
@ -32,24 +14,19 @@ void sleep(int ms) {
} // namespace
int main() {
int recv = 0;
gpio0->data = 37;
extern "C" int main() {
uint8_t cnt = 0;
XUartLite_CfgInitialize(&uart0, &uart0_config, uart0_config.RegBaseAddr);
for (int i = 0; i < 256; i++) {
gpio0->data = cnt;
cnt++;
while (true) {
uint8_t c;
int received_bytes = XUartLite_Recv(&uart0, &c, 1);
if (received_bytes < 1) {
continue;
}
recv++;
gpio0->data = recv & 0xff;
XUartLite_Send(&uart0, &c, 1);
if (c == '\r') {
c = '\n';
XUartLite_Send(&uart0, &c, 1);
}
sleep(125);
}
auto* airc = reinterpret_cast<volatile uint32_t*>(0xe000ed0c);
uint32_t a = *airc;
*airc = a | 0x04;
while (1) {}
}

View File

@ -11,7 +11,7 @@ CFLAGS = -march=armv6-m -g -ffunction-sections -fdata-sections -O2 -Werror -Wall
CXXFLAGS = $(CFLAGS) -std=c++20 -fno-exceptions
LDFLAGS = -march=armv6-m \
-g --specs=nano.specs --specs=nosys.specs \
-Wl,--gc-sections -Wl,-T$(linker_script) -O2 \
-Wl,--gc-sections -O2 \
-Wl,--print-memory-usage
sources += hal/lib/common/xil_assert.c
@ -20,21 +20,25 @@ includes += -Ihal/lib/common
sources += hal/uart/xuartlite.c hal/uart/xuartlite_stats.c
includes += -Ihal/uart
objects = main.o vector_table.o $(sources:.c=.o)
bootloader_objects = uart.o bootloader.o vector_table.o $(sources:.c=.o)
app_objects = app_init.o main.o
CFLAGS += $(includes)
all: bootloader.elf
all: bootloader.elf app.bin
%.bin: %.elf
$(OBJCOPY) -O binary $< $@
bootloader.elf: $(objects)
bootloader.elf: $(bootloader_objects)
$(LD) -Wl,-Tbootloader.ld $(LDFLAGS) -o $@ $^
app.elf: $(app_objects)
%.elf:
$(LD) $(LDFLAGS) -o $@ $^
$(LD) -Wl,-Tapp.ld $(LDFLAGS) -o $@ $^
.PHONY: clean
clean:
rm -rf *.elf *.bin $(objects)
rm -rf *.elf *.bin $(app_objects) $(bootloader_objects)

47
arm/prog.py Normal file
View File

@ -0,0 +1,47 @@
import serial
import struct
import sys
offset = 0x800
tty = 'tty'
baud = 115200
chunksize = 128
def write(s, offset, dat):
for i in range(0, len(dat), chunksize):
chunk = dat[i: i + chunksize]
cmd = struct.pack('<cII', b'c', offset + i, len(chunk))
print(f'Sending {len(chunk)} bytes @0x{offset + i:04x}')
s.write(cmd)
s.write(chunk)
ack = s.read(2)
if len(ack) < 2:
raise RuntimeError(f'timeout waiting for full ack. got {ack}')
if ack[0] != b'a'[0]:
raise RuntimeError(f'expected ack, got this instead: {ack}')
print(f'Ack! len={ack[1]}')
def jump(s, offset):
addr = struct.pack('<I', offset)
s.write(b'j')
s.write(addr)
def main():
binfile = sys.argv[1]
with open(binfile, 'rb') as f:
dat = f.read()
s = serial.Serial(tty, baud, timeout=1)
write(s, offset, dat)
jump(s, offset)
if __name__ == "__main__":
main()

21
arm/uart.cc Normal file
View File

@ -0,0 +1,21 @@
#include "uart.h"
namespace {
constexpr uintptr_t kUart0BaseAddress = 0x40001000;
XUartLite uart0_inst;
XUartLite_Config uart0_config = {
.DeviceId = 0,
.RegBaseAddr = kUart0BaseAddress,
.BaudRate = 115200,
.UseParity = false,
.DataBits = 8,
};
} // namespace
XUartLite* uart0 = &uart0_inst;
void InitUarts() {
XUartLite_CfgInitialize(uart0, &uart0_config, uart0_config.RegBaseAddr);
}

7
arm/uart.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include "xuartlite.h"
extern XUartLite* uart0;
void InitUarts();