Writing a simple assembly program for HiFive1

I just got my HiFive1 yesterday and so far everything work OK. I was able to compile some sample programs under software/ like hello and it works fine even with run_gdb (the documentation is a bit outdated as it indicate run_debug).

But I then had to spent a couple of hours to try to find and compile a simple assembly program. After some search, I found some sample program, so I used this one here:

Next problem was that there is no facility to compile and build assembly files, so I had to create a hello_assembly dir un software, and wrote this Makefile:

TARGET = hello_assembly
FILES = hello_assembly.s
#CFLAGS = -O2 -nostdlib -nostartfiles -fno-builtin-printf

builddir := ../../work/build
# Pointers to various important tools in the toolchain.
toolchain_builddir := $(builddir)/riscv-gnu-toolchain/riscv64-unknown-elf
toolchain_prefix := $(toolchain_builddir)/prefix

RISCV_PATH ?= $(toolchain_prefix)

RISCV_AS      := $(abspath $(RISCV_PATH)/bin/riscv64-unknown-elf-as)
RISCV_LD      := $(abspath $(RISCV_PATH)/bin/riscv64-unknown-elf-ld)

#ASM_FLAGS     = -f

OBJS = $(FILES:.s=.o)

all : $(TARGET)

hello_assembly : $(OBJS)
	$(RISCV_LD) -o $(TARGET) $(OBJS)

%.o : %.s	
	$(RISCV_AS) $(ASM_FLAGS) -o $@ $<

.PHONY : clean
clean:
	rm -f *.o *.a

#BSP_BASE = ../../bsp
#include $(BSP_BASE)/env/common.mk

Now if I run make software PROGRAM=hello_assembly it builds fine and I can upload it. However, I cannot use run_dbg, this is what happens:

<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from software/hello_assembly/hello_assembly...(no debugging symbols found)...done.
Remote debugging using localhost:3333
0x00007fff20400370 in ?? ()
(gdb) bt
#0  0x00007fff20400370 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) info files
Symbols from "/home/jasem/Projects/development/hardware/freedom-e-sdk/software/hello_assembly/hello_assembly".
Extended remote serial target in gdb-specific protocol:
Debugging a target over a serial line.
        While running this, GDB does not access memory from...
Local exec file:
        `/home/jasem/Projects/development/hardware/freedom-e-sdk/software/hello_assembly/hello_assembly', file type elf64-littleriscv.
        Entry point: 0x10078
        0x0000000000010078 - 0x00000000000100b0 is .text
        0x00000000000100b0 - 0x00000000000100bd is .rodata
(gdb) j 0x10078
Function "0x10078" not defined.
(gdb) j 10078
No symbol table is loaded.  Use the "file" command.
(gdb) j _start
Continuing at 0x10078.
Remote connection closed

In the terminal where I have openocd running, I see this:

Open On-Chip Debugger 0.10.0+dev (2017-10-22-00:41)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
adapter speed: 10000 kHz
Info : auto-selecting first available session transport "jtag". To override use 'transport select <transport>'.
Info : ftdi: if you experience problems at higher adapter clocks, try the command "ftdi_tdo_sample_edge falling"
Info : clock speed 10000 kHz
Info : JTAG tap: riscv.cpu tap/device found: 0x10e31913 (mfg: 0x489 (SiFive, Inc.), part: 0x0e31, ver: 0x1)
Info : Examined RISCV core; XLEN=32, misa=0x40001105
Info : Listening on port 3333 for gdb connections
Info : [0] Found 2 triggers
halted at 0x20400370 due to debug interrupt
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : accepting 'gdb' connection on tcp/3333
Info : Found flash device 'issi is25lp128' (ID 0x0018609d)
Core got an exception (0xffffffff) while reading from 0x10078
Core got an exception (0xffffffff) while reading from 0x1007c
Core got an exception (0xffffffff) while reading from 0x10080
Core got an exception (0xffffffff) while reading from 0x10084
Core got an exception (0xffffffff) while reading from 0x10088
Core got an exception (0xffffffff) while reading from 0x1008c
Core got an exception (0xffffffff) while reading from 0x10090
Core got an exception (0xffffffff) while reading from 0x10094
Core got an exception (0xffffffff) while reading from 0x10098
Core got an exception (0xffffffff) while reading from 0x1009c
Core got an exception (0xffffffff) while reading from 0x100a0
Core got an exception (0xffffffff) while reading from 0x100a4
Core got an exception (0xffffffff) while reading from 0x100a8
Core got an exception (0xffffffff) while reading from 0x100ac
Core got an exception (0xffffffff) while reading from 0x100b0
Core got an exception (0xffffffff) while reading from 0x100b4
Core got an exception (0xffffffff) while reading from 0x100b8
Core got an exception (0xffffffff) while reading from 0x100bc
Core got an exception (0xffffffff) while reading from 0x100c0
Core got an exception (0xffffffff) while reading from 0x100c4
Core got an exception (0xffffffff) while reading from 0x100c8
Core got an exception (0xffffffff) while reading from 0x100cc
Core got an exception (0xffffffff) while reading from 0x100d0
Core got an exception (0xffffffff) while reading from 0x100d4
Core got an exception (0xffffffff) while reading from 0x100d8
Error: gdb sent a packet with wrong register size
Info : dropped 'gdb' connection

So I’m stuck now. If I try to add more debugging via --gstabs+ but then the assembler fails with internal error. Any hints on what to do?

1 Like

Have you looked at the disassembled output of your resulting ELF file? You can do this with the make dasm target. Take a look at where the program is actually getting linked to. It seems like it is linked to 0x10000, which isn’t a memory location to which you can actually load a program on the HiFive1 (usually we link to 0x20400000 for SPI Flash, or 0x80000000 for Scratchpad). If that is the case, you will need to adjust your linker script, take a look at the flash.lds and scratchpad.lds linker scripts included for the boards in the Freedom E SDK. This is part of what is handled by the last few makefile lines which you commented out for your target.

1 Like

Also, the simple example assembly program is not going to work, as it is assuming that you’ve set up an ecall handler to do the write. There is no such ecall handler defined (this is bare-metal assembly which you are writing, so you’ll need to make the ecall handler yourself to print to the UART).

1 Like

Thank you. Yeah, this makes sense. I’ll try to see if I can use the SPI Flash address 0x20400000 to load the assembly program. If I have something akin to “hello” but written in bare assembly, I’ll try to submit it for inclusion later. It would be a nice tool to learn the internals of how this platform work.

Ok so I spent pretty much the whole day checking how “hello” works. I’m probably going to write a detailed blog about this so that others might find it useful, but overall:

  1. RISV-V GNU tools compiled to produce code for the RISC-V architecture (gcc, ld, libc,…et al).
  2. libwrap implements minimal memory handling (malloc/free) and syscalls required by many C functions (write,fstat,read…etc) that interface with file descriptors or underlying kernel (and there is none) so most of these syscalls are not implemented since there is no OS, but it helps build simple C programs.
  3. libwarp.mk Makefile uses --wrap for all the syscalls so that they get called instead of any others that might be defined in libc
  4. flash.lds maps the binary elf image sections to fit within the expected SPI EPROM memory map accepted by the FE310-G000 processor in the HiFive1 board. This sets all sections including stack, heap boundaries, rodata…etc
  5. start.S is where the execution begins. Here .data is loaded into loaded from 0x80000000 virtual addresses to physical addresses and .bss is cleared. Other boilerplate operations are executed as well before pushing the stack and calling main()

There is of course a lot more detail. At any rate, one thing I couldn’t fathom is the location of .rodata

From dasm output, the location is:

2040be20 <blanks.4376-0x1bc>:
2040be20:	6568                	flw	fa0,76(a0)
2040be22:	6c6c                	flw	fa1,92(s0)
2040be24:	6f77206f          	j	2047ed1a <__fini_array_end+0x72166>
2040be28:	6c72                	flw	fs8,28(sp)
2040be2a:	0a64                	addi	s1,sp,284

The above is “hello world\n”. Also, if you checkout main dasm, you see the following:

2040006c <main>:
2040006c:	1141                	addi	sp,sp,-16
2040006e:	0000c517          	auipc	a0,0xc
20400072:	db250513          	addi	a0,a0,-590 # 2040be20 <__clzsi2+0x44>
20400076:	c606                	sw	ra,12(sp)
20400078:	00000097          	auipc	ra,0x0
2040007c:	54c080e7          	jalr	1356(ra) # 204005c4 <printf>
20400080:	40b2                	lw	ra,12(sp)
20400082:	4501                	li	a0,0
20400084:	0141                	addi	sp,sp,16
20400086:	8082                	ret
20400088:	0000                	unimp

So it is loading a0 with 0x2040be20 which matches .rodata address above. However, when running the program on the board, I get different result:

          main:
2040006c:   addi    sp,sp,-16
5           printf("hello world\n");
2040006e:   auipc   a0,0xc
20400072:   addi    a0,a0,1150 # 0x2040c4ec
4         {
20400076:   sw      ra,12(sp)
5           printf("hello world\n");
20400078:   auipc   ra,0x0
2040007c:   jalr    2046(ra) # 0x20400876 <printf>
7         }
20400080:   lw      ra,12(sp)
20400082:   li      a0,0
20400084:   addi    sp,sp,16
20400086:   ret

Where does 0x2040c4ec come from? Looks like the .ro data was shifted down a bit, but why?

1 Like

Nice work and writeup!

“When running the program on the board”… not sure exactly what generated the second section of output. Are you sure that you actually loaded the (new) program and reset the board?

1 Like

I used the FreedomStudio IDE to debug the program so the second section of the output was from the IDE. The weird bit is that when I use make run_debug PROGRAM=hello, I get the expected results, but the IDE always shows a different address, which happens to contain the correct data!

In the IDE, I pointed it to use the compiled freedome-e-sdk and not the built-in one it ships with, so this shouldn’t be an issue of two different SDKs generating different results. Of course, I uploaded and reset the boards several times, but all ending to the same result.

EDIT: Doh, I figured out why. I was using the “hello” example from the FreedomStudio when I was running the IDE, and not the Freedom-e-SDK “hello”. I hope no one else falls in this stupid mistake!

1 Like