Quick Notice

Before we start, I'd like to just say this is not intended to be a tutorial but rather my experience with writing RISC-V assembly on the Star64. If you'd like to you could try to follow and do it yourself. Now, with all of that aside here is what I tried.

Figuring out the UART

I decided to start with a Hello World program but first I installed the riscv-gnu-toolchain from the github repo here and installed the 64 bit version. After that was done, I was finally ready to start writing the assembly program. There was one problem though, I couldn't find much information about the UART by looking at the JH7110 data sheet but luckily I checked out some stuff on the rvspace forum in specific, this post. Somebody had been trying to do some bare metal programming on the VF2 that involved the UART and they had discovered that the UART on the JH7110 is a PC16550D so now I was able to know what registers to use for my assembly program. After that, I just went straight to the JH7110 datasheet and found my UART base address.

Writing the bare metal Assembly

After all of that, the final Assembly program I came up with is this:

.section .data

hello:
.asciz "Hello, World!\n"  # Null-terminated string with newline character

.equ UART_BASE, 0x10000000

.section .text
.globl _start

_start:
    # Set up UART
    la t0, UART_BASE         # Load address of UART base into t0

    # Send "Hello, World!" message
    la t1, hello             # Load address of the hello string into t1
    jal ra, print_loop     # Jump and link to print_loop subroutine

end_loop:
    j end_loop               # Infinite loop to prevent the program from terminating`

print_loop:
    lb a0, 0(t1)             # Load the next character from the string into a0
    beqz a0, print_done      # Check if we've reached the end of the string (null terminator)
    addi t1, t1, 1           # Move to the next character in the string

    # Otherwise, send the character to the UART
    sb a0, 0(t0)             # Store the character to the UART's Transmit Holding Register

    j print_loop             # Repeat for the next character in the string

print_done:
    ret

After that I wrote the linker file:

ENTRY(_start)   /* Entry point of the program */

MEMORY
{
    ram (rwx) : ORIGIN = 0x80000000, LENGTH = 64K  /* Define a 64KB region starting at 0x80000000 for RAM */
}

SECTIONS
{
    .text :
    {
        _start = .;     /* Set the symbol _start to the current address */
        *(.text)        /* .text section (code) */
    } > ram

    .data :
    {
        *(.data)        /* .data section (initialized data) */
    } > ram

    .bss :
    {
        *(.bss)         /* .bss section (uninitialized data) */
    } > ram


    /DISCARD/ :
    {
        *(*)            /* Discard all other sections */
    }
}

It's important that the origin is set to something that's not already occupied in memory. When I was done writing that I compiled it. I used the gnu riscv64 bit toolchain as mentioned earlier. riscv64-unknown-elf-as -o hello.o main.s I linked it with riscv64-unknown-elf-ld -T linker.ld -o hello.elf hello.o This is all great, but the one thing is for bare metal it needs to be a .bin file so I used objcopy riscv64-unknown-elf-objcopy -O binary hello.elf hello.bin

Running it with U-boot

Now I could finally run it so I copied the binary onto a microSD set the dip switches to 0 (Flash SPI) so I could run my binary with U-boot commands. My microSD is using FAT as the filesystem. I turned the board on and used tio with baud rate set to 115200 bps, then ran these U-boot commands in order:

  1. fatload mmc 1 0x80000000 hello.bin
  2. go 0x80000000

It's important to put these commands in order, because fatload loads hello.bin at 0x80000000 and then the go command executes hello.bin (which is at 0x80000000)

Hello, World!

After all of that work, I've finally got the output of Hello, World! on bare metal with no errors!

Bare metal RISC-V with Rust

After I ran some assembly programs and got C programs to work with bare-metal RISC-V on the Star64, I started experimenting with using the Rust programming language for bare metal RISC-V. So far, I've ran an infinite loop but have had no luck with getting a Hello World program to run with the riscv64gc-unknown-none-elf target. I've been using objcopy to turn it into a .bin but when I try to run it on the Star64 I get unknown instruction errors. I'm still experimenting so I'll make a new blog post if I figure it out, or if any progress is made.

Contact

If anyone has gotten bare metal Rust on RISC-V to work with the Star64 or VF2 then please contact me and let me know how you did it or any possibility's for what I'm doing wrong. Also, if you notice anything wrong please contact me about the issue with my blog. If you're interested in messing with the Star64 I highly recommend checking out the Star64 matrix.

Thank you for reading!