FE310-G002 boot process entry point

The flash address range for E31 is 0x2000_0000 - 0x3FFF_FFFF, but the FE310-F002 manual mentions the boot process entry point into QSPI flash address 0x20010000.

If I’m right, the application entry (if its in flash) should be at 0 0x20010000 to boot after reset. is it right?

When I loaded my led-blink code with entry at 0x20010000 through gdb and run, led blinks, (I assume when I load through gdb into flash it stays even after reset).

But when I do a power-reset, the same led don’t blink. If boot reset jumps to 0x20010000, it should blink. What am I doing wrong?

Disassembly of section .text:

20010000 <_start_entry>:
20010000: 80000137 lui sp,0x80000

If you want your program to persist in the QSPI flash and boot from reset which normally starts program execution at 0x20010000, then you’ll need to program it into that flash. The docs explain how to do this using a simple drag and drop approach.

You can also program the flash using OpenOCD. I presume that the specific details of how to do this are documented somewhere.

A regular debug session will almost certainly not program the QSPI flash. But if the debugger is loading your program to 0x20010000 (QSPI flash) and is actually running ok then I’m not sure that’s going on unless the board’s flash somehow acts like volatile RAM when written directly instead of going through the relevant flash programming sequences.

@bsvtgc Admittedly, the boot process entry point is a little bit circuitous. Table 5 of Chapter 5 of the Manual shows how the boot vector address depends on the state of the MSEL pins (earlier Manual version v19p05, available elsewhere, has Table 7 called “Target of the reset vector” which explains the MSEL settings).

Unfortunately, the MSEL pins are not accessible, but are hard-wired (presumably floating up) to 11’b which is mentioned as the hardware default condition (and determined empirically).

This causes a jump to the Mask ROM at 0x1 0000.

Section 5.1.1 of the Manual says that the Mask ROM has instructions which further jump to OTP at 0x2 0000.

Now, the “Boot Code” section at the beginning of Chapter 5 of the Datasheet gives us the default OTP program which is a jump to SPI-Flash at 0x2000 0000. Quoting the Datasheet:

As shipped, the OTP memory at the boot location is preprogrammed to jump immediately to the end of the OTP memory, which contains the following code to jump to the beginning of the SPI-Flash at 0x2000 0000:

fence 0,0
li t0, 0x20000000
jr t0

fence 0,0 is encoded as 0x0000 000F, and the instruction may be modified by burning additional bits to transform it into a JAL instruction ( opcode 0x6F ) to execute arbitrary code rather than jumping directly to the beginning of the SPI-Flash.

Thus, at power-up, program execution will jump essentially jump to 0x2000_0000, not 0x2001000. Not sure from where you saw the latter.

You can either map/link and load your code to 0x8000 0000, for RAM operation; or to 0x2000 0000, for Flash/ROM operation, and all will be fine.

Erase the Flash/ROM in OpenOCD tcl with:

echo [flash protect 0 0 2 off]
echo [flash erase_sector 0 0 2]
echo [flash protect 0 0 2 on]

NOTE: BE CAREFUL when loading code into ROM! If there’s a bug, exception or otherwise tight infinite loop, reset & halt of the CPU might be difficult. See my earlier post for tips to ‘unbrick’ a system in this case.

@bsvtgc this earlier discussion details pretty much the same question you had. Basically, as I outlined above, the main SPI Flash/ROM entry point is 0x2000 0000. The “double-tap bootloader”, a specific program module for the HiFive1 board, resides at this ROM address and eventually goes to the address you question, 0x2001 0000. If you don’t have or don’t want to use the double-tap bootloader, just erase as much of the Flash as you need and link/load your code to 0x2000 0000.

@pds and @tgm

Thanks for your reply.

The confusion came up after referring to recent FE310-G002 manual chapter 5 boot process picture. Here the boot process jumps from OTP memory to 0x2001_0000. Whereas the FE310-G002 data sheet mentions the boot jump address is 0x2000_0000.

I wasn’t aware of the double-tap boot and it was booting well from 0x2001_000 until recently when I erased the first sector at 0x2000_0000 accidentally.

Now as you mentioned, I just erased the needed sectors and loaded my led blink program on FLASH starting from 0x2000_0000 address. It works fine.

Thanks,
Vincent.

Does the “double-tap” boot program @ 0x20000000 do anything else (e.g. any init/setup) other than trampolining to the user program at 0x20010000?

@tgm Not much more than blinking a few lights. This is the code for double_tap_dontboot.c and a related discussion (and binaries) are available here courtesy of @bruce and others.

Okay, now I see what code resides at 0x2000_0000 in the newly shipped hifive1-revb board. If needed, I can just compile and flash it back. In this regard, two questions:

#1. During boot process does the OCD (On chip debugger) allow keeping breakpoints at 0x2000_0000?
#2. How to emulate a hardware reset through code? (equivalent to RESET button press on board)

Vincent.

Thanks @pds - but it does mean that if a user skips using the “double tap” bootloader and their program does something to prevent the debugger connecting then they will have bricked their board?

#1. During boot process does the OCD (On chip debugger) allow keeping breakpoints at 0x2000_0000?

Not sure what you mean by “keep”.
But, at least in my experience (and unless something has changed in recent months with the RISC-V OpenOCD support), the debug connection doesn’t survive a reset and I suspect that hardware breakpoints set in a debug session (e.g. at 0x20000000) before reset don’t either. So if the user program at 0x20000000 happened to do something to brick the board then I don’t think that the debugger will save you. That seems to be the rationale for having the “double tap” bootloader in the first place - i.e. so that one can recover from a user program that might otherwise brick the board?

1 Like

You exactly got to what I am looking for.

What are the conditions to grab or establish a debug/gdb session with the core?
In other words, when a risc-v core would be ready to establish a debug session?

I am looking to modify the following entry code to be able to establish a debug session so that if application (app_entry) is screwed up, I should still be able to hijack between the reset power button press and app_entry.

Lalign4 _start_entry:

    /* set stack pointer */

    lui sp, %hi(linker_stack_start);
    addi sp, sp, %lo(linker_stack_start);

    /* set trap vector base with vector mode */

    lui t0, %hi(trap_base);
    addi t0, t0, %lo(trap_base);

.ifdef INTERRUPT_VECTOR_MODE
    ori t0, t0, 1;
.endif

    csrw mtvec, t0;

    call app_entry; /* This is the entry point for app */

j .

I’m fairly sure that if you want to ensure that you can always establish a debug connection to the target then your program in flash needs to have some option (most likely controlled by an external jumper, switch, pushbutton etc. input) that puts it in a busy loop or wfi etc. That way it stops before it can brick the device and a debug connection can be established.

Edit: that’s basically what the “double tap don’t boot” bootloader implements as option to intervene in the event that the user program at a higher flash address can cause a board “lockup”. If the issue is the loss of flash storage due to the bootloader then maybe the region for the user program could be shifted down while still retaining the bootloader? I don’t know if this is possible but it might be worth investigating?

#2. How to emulate a hardware reset through code? (equivalent to RESET button press on board)

You can use the watchdog timer with a purposely low timer value to force hardware reset.

No. Whether or not double_tap_dontboot is used, the board will never be bricked or made unavailable to debugger connection. It may be hard to start the board, such as in case of bad code turning off or incorrectly changing the system clock, but the board will never be bricked. See my other post Re: system reset and using a tweezer or pliers to discharge the long RC time constant of the 1V8 regulator enable pin.

The only way I know to truly “brick” a board is reprogram (by accident, hard to do) the OTP memory – code execution won’t go to 0x2000 0000 anymore, but somewhere else; or perhaps an infinite loop of exception jumps. You’d still be able to connect a debugger, change memory, and break out of a bad loop; but you’d never again be able to run the board without the debugger.

@pds what would happen if flash first sector 0x2000 0000 is erased (and has 0xffs) and has no code in written on it yet & reset-ed? would debugger still be able to connect?

@bstvgc Yes, absolutely. I work this way all the time: erased Flash/ROM; code linked and loaded to DTIM/RAM at 0x80000000. Sometimes it’s hard to attach OpenOCD, at least using the FT/JTAG (i.e., mpsse). You have to be persistent and patient with pressing the reset button (on HiFive and/or LoFive), and possibly even unplugging and re-plugging the USB connection (Windows’ dbus quirks).

However, definitely debugger will attach reliably with a completely blanked and erased Flash.