So, I decided to try getting my RISC-V GCC compiler working with my Kestrel-2DX again, and this time, for whatever reason, my mental model just "clicked" and things worked. It was an iterative process to get this far; however, I'll describe things in the rough order I've accomplished things.
Here's the C program I wanted to run:
/* Invert the contents of the video frame buffer on cold boot. */
unsigned long long *scrp, *endp;
void
_start(void) {
scrp = (unsigned long long *)0x10000;
endp = scrp + 2000;
while(scrp < endp) {
*scrp ^= 0xFFFFFFFFFFFFFFFF;
scrp++;
}
while(1) ;
}
I was able to compile this into statically-linked ELF binary file with the following command:
./riscv64-unknown-elf-gcc -O1 -c floop.c -o floop.o -march=RV64I ./riscv64-unknown-elf-ld floop.ld floop.o -o floop.exe -nostdlib
You'll notice that I have a custom loader script, which looks like this:
ENTRY(_start) MEMORY { ROM (rx) : ORIGIN = 0x00000, LENGTH = 0x8000 RAM (rwx) : ORIGIN = 0x14000, LENGTH = 0x8000 } SECTIONS { .text : { . = ALIGN(4); *(.text) *(.text*) . = ALIGN(4); } >ROM .rodata : { . = ALIGN(8); *(.rodata) *(.rodata*) . = ALIGN(8); } >ROM .data : { . = ALIGN(8); *(.data); *(.data*); . = ALIGN(8); } >RAM .bss : { . = ALIGN(8); *(.bss) *(.bss*) . = ALIGN(8); } >RAM }
RAM technically starts at address 0x10000; however, the MGIA fetches its video frame from that location, so we configure the linker to place global data variables 16KB away.
Then, to pull out just the code and constant data, I used the following:
./riscv64-unknown-elf-objcopy -j .text -j .rodata floop.exe -O binary floop.bin
At this point, I have a raw binary image. A problem remains, however. The constant data precedes the code; thus, I cannot just have the processor reset directly into _start.
$ xxd floop.bin # note data at offset $00, not $38 or something.
0000000: dec0 ad0b ea1d ad0b 1000 0000 0000 0000 ................
0000010: b707 0100 3747 0100 1307 07e8 83b6 0700 ....7G..........
0000020: 93c6 f6ff 23b0 d700 9387 8700 e398 e7fe ....#...........
0000030: b747 0100 9387 07e8 3707 0100 2330 f700 .G......7...#0..
0000040: 6f00 0000 o...
Thus, I need a bootstrap of some kind, and I need to place the C code somewhere away from address 0.
So, I write a simple bootstrap routine in raw assembly to scan ROM for a special "resident" structure (an idea I learned from coding directly on and for AmigaOS); nothing fancy, just something that would let me find the address of _start.
include "regs.i"
addi a0, x0, $200 ; Start at address 512
L0: auipc a1, 0
ld a1, chkdword-L0(a1)
lui a2, $8000
L1: ld a3, 0(a0) ; Did we find the checkword?
beq a3, a1, yup
addi a0, a0, 8
blt a0, a2, L1
addi a0, x0, -1 ; Deadlock with all LEDs lit if not found.
lui a1, $20000
sh a0, 2(a1)
jal x0, *
yup: ld a3, 8(a0) ; Get startup procedure's *offset*
add a3, a3, a0 ; Get startup procedure's *address*
lui sp, $14000 ; Set up C stack pointer.
jalr x0, 0(a3) ; Let there be C.
align 8
chkdword: dword $0BAD1DEA0BADC0DE
adv $8000, $CC
I then altered the C code to include the following at the very start:
struct Resident {
unsigned long long r_matchWord;
void (*r_fn)();
};
void _start(void);
const struct Resident R = { 0x0BAD1DEA0BADC0DE, &_start };
static unsigned long long *scrp, *endp;
// ...etc...
After recompiling as above, I now needed to embed the C code into the binary file my personal assembler produced.
dd if=floop.bin of=rom.bin bs=512 seek=1
Whoops, this has the effect of truncating the file; I have to re-pad it to 32KB before making the Verilog module with the ROM's contents.
dd if=/dev/zero of=rom.bin bs=1 count=1 seek=32767
There, I now have a completed 32KB image. I rebuild the Verilog ROM module:
make rtl/rom.v
edit the resulting Verilog file because Xilinx's flavor of Verilog is retarded and won't accept the sane syntax that Yosys, Icarus, *AND* Verilator accepts. One of these days, I'll fix my tooling to automate this.
Anyway, after that was done, I resynthesized the design, and lo and behold, I had a white video display! That means _start was discovered and dispatched to, and code generated by the C compiler was running! Woooo!!
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.
Woot! Glad it is coming along :)
Are you sure? yes | no
Me too. I'm tired of suffering defeat at the hands of fate. ;)
Are you sure? yes | no
Success! Congratulations.
Are you sure? yes | no
Thanks! I've since written text output code to print messages to the screen, and the rudiments of a cursor manipulation package as well. I think my next step is to integrate the KIA core and get a working PS/2 keyboard again.
Are you sure? yes | no