Debugger to program boot ROM

Hi there,

I am just wondering if it is possible for the JTAG debugger to flash the program into the boot ROM, so we don’t need external SPI flash to do that? If so, what modifications do I need to make this happen?


My comments below refer to the FE310-G000 in the HiFive1, and for individual sale. If you make a different E300 SoC then you could do something different.

There is no flash memory on the FE310-G000 SoC. The boot ROM is just that: ROM.

The SoC has:

  • ROM. Programmed at manufacture, can’t be changed.

  • SRAM. Changable any time, contents lost if power is removed

  • OTP. One Time Programmable ROM. Programmed at manufacture to start the program from external flash.

So, no, you can’t do what you ask.

What you could do:

  1. put a program in the external flash that jumps immediately to a program in SRAM. You would then need to use JTAG to load the program every time the board was powered down. And you’d still need the external flash.

  2. convert the current “fence” NOP in the OTP to a branch somewhere else in the OTP and put new code in the OTP that jumps to SRAM. You would then need to use JTAG to load the program every time the board was powered down. But you would no longer need the external flash.

There is enough space in the OTP (8k) that you could modify the boot source back and forth quite a few times (hundreds, not thousands) before you killed the board.

  1. A small program could be stored entirely in the OTP. It has 8k. Twelve bytes are already used for the jump into SPI. Another twenty bytes are used for other purposes. So there are 8160 bytes free for your program. If your program used more than half the space then you could only load it one time into any given SoC.

Actually, I don’t know what the contents of the OTP are in the FE310-G000 chips that are being sold individually. I’m assuming it is the same as in the HiFive1, but maybe it’s completely blank, in which case you’ll need to use JTAG to put something there initially, but you could use all 8k as you wish.

Hi @brucehoult,

Thank you very much for your detailed reply. So the reason why I am asking is that now I am trying the freedom E300 on arty board. Whenever we want to upload an application to the board via debugger. The debugger always flash the program at 0x20004000 (or something like that) which is the flash memory address on the arty board.

Does the above apply where you could upload the program in the boot ROM (where the boot ROM in freedom E300 is just an assembly program that jumps to 0x20004000)? So whenever we uploaded the program, reset the core, and the processor boots and the application starts.



On the FPGA, yes it is possible to use different boot ROM contents, but not with the Debugger. You would have to modify the FPGA image itself in the repo. The .S file which is used is just this one:

If you use a different assembly file and then rebuild the FPGA MCS image, then that is what would be placed in your Boot ROM.

@brucehoult you are correct that the chips that we sell on CrowdSupply have the same OTP contents burned as those on the HiFive1. We also have a utility to burn different “jump to” addresses, available upon request.

Hi @mwachs5,

Yes, I’ve done this, I am thinking if it is possible to somehow flash the program (probably not to boot ROM) without using the internal/external flash memory chip in the Arty board.

What modification (both firmware and hardware) do I need to do? Thanks.



You should be able to program the scratchpad memory with the debugger and then jump to this out of reset. You would need to modify Boot ROM code to jump to the scratch pad (DTIM) memory (0x8000_0000) instead of to 0x2040_0000. The scratchpad memory contents are Xilinx Block RAMs, so they should persist across System Reset.

Then you will need to change your linker script to link to 0x8000_0000.

Note that this will only work across reset, not power-cycling the FPGA.


Thanks @mwachs5, this is for the boot ROM code only right? I assume we need to somehow modify the configuration in the freedom-e-sdk code because it is currently having the flash model where the external program (sample software provided for example) is loaded to flash?

Could you point me the things that needs to be modified to program it to DTIM memory (0x8000_0000) in the firwave side?


You are correct. You just need to change your linker script. For example, you can use the “” script:

Thanks @mwachs5, I’ve tried that out, but seems like the scratchpad is too small to fit a small program (just blinking the LEDs configured as GPIO)?

Later I changed the scratchpad memory from top level Chisel, and it fits, and I did some experiments on the max scratchpad memory you can configure, seems like it stuck at some numbers less than 100kB. Can I make it bigger than that?

Also, the current scheme for uploading a program to arty board must through n25q128 flash controller in the arty board, am I right? Is there any way to bypass this as other dev board doesn’t necessarily have this chip built in?

To just blink the LEDs configured as GPIO there should be plenty of room in the Scratchpad. But if you are including a printf or something, there are probably huge amounts of code linked in. Take a look at your ELF and see what all is included that you don’t really care about.

I’m not sure what you mean “can you make it bigger than that”. There is the FPGA size limit, is there another limit you were running into? What error did you get?

The Flash controller should be pretty generic, as it queries the device to ask what it is and then uses the appropriate read, erase, write commands. You can see the list of supported devices here:

And, to program the scratchpad the Flash controller is not used at all.

Hi @mwachs5,

Thanks for reminder, totally forgot the size limit of FPGA block RAM, Ouch… In fact, the small LED program doesn’t fit into the scratchpad, I had to re-compile the chisel the make the scratchpad memory larger (I made it to 64KB).

But now I am able to load the program in scratchpad without passing flash and controller, but I got this “error” in upload (although the program is uploaded successfully)

Info : JTAG tap: riscv.cpu tap/device found: 0x10e31913 (mfg: 0x489 (), part: 0x0e31, ver: 0x1)
JTAG tap: riscv.cpu tap/device found: 0x10e31913 (mfg: 0x489 (), part: 0x0e31, ver: 0x1)
riscv.cpu: target state: halted
halted at 0x0 due to debug interrupt
riscv.cpu: target state: halted
halted at 0x0 due to debug interrupt
invalid subcommand "protect 0 64 last off"
in procedure 'flash’
invalid subcommand "protect 0 64 last off"
in procedure 'flash’
Loading section .init, size 0x78 lma 0x80000000
Loading section .text, size 0xbc58 lma 0x80000078
Loading section .rodata, size 0xd3c lma 0x8000bcd0
Loading section .eh_frame, size 0x68 lma 0x8000ca0c
Loading section .data, size 0x9d0 lma 0x8000ca78
Start address 0x80000000, load size 54340
Transfer rate: 520 KB/sec, 6792 bytes/write.
halted at 0x80000004 due to step
halted at 0x80000004 due to step
shutdown command invoked
shutdown command invoked
A debugging session is active.

Do I need to modify openocd.cfg somehow to eliminate this error?


You can just remove that flash protect line, since I believe you are trying to work without programming the flash anyway?

Oh, right… That did the trick. Thank you so much @mwachs5!

I noticed that the Freedom repos has been changed significantly. After re-doing this for the latest version of Freedom, I am still able to upload a program to scratchpad via debugger, but extremely low speed (~4 KB/s) I also got this error (dtmcontrol_idle=5, dmi_busy_delay=4885, ac_busy_delay=0):

Open On-Chip Debugger 0.10.0-dev-g0d86772-dirty (2017-06-13-14:13)
Licensed under GNU GPL v2
For bug reports, read
adapter speed: 10000 kHz
Info : auto-selecting first available session transport “jtag”. To override use 'transport select '.
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: 0x20000913 (mfg: 0x489 (), part: 0x0000, ver: 0x2)
Info : dtmcontrol_idle=5, dmi_busy_delay=1, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=2, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=3, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=4, ac_busy_delay=0
Error: unable to execute program: (abstractcs=0x10000301)
Info : Examined RISC-V core
riscv.cpu: target state: halted
Info : accepting ‘gdb’ connection on tcp/3333
0x00000000 in ?? ()
Info : JTAG tap: riscv.cpu tap/device found: 0x20000913 (mfg: 0x489 (), part: 0x0000, ver: 0x2)
JTAG tap: riscv.cpu tap/device found: 0x20000913 (mfg: 0x489 (), part: 0x0000, ver: 0x2)
Info : dtmcontrol_idle=5, dmi_busy_delay=5, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=5, ac_busy_deInfo : dtmcontrol_idle=5, dmi_busy_delay=6, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=6, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=7, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=7, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=8, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=8, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=9, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=9, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=10, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=10, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=12, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=12, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=14, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=14, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=16, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=16, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=18, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=18, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=20, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=20, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=23, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=23, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=26, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=26, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=29, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=29, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=32, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=32, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=36, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=36, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=40, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=40, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=45, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=45, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=50, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=50, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=56, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=56, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=62, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=62, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=69, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=69, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=76, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=76, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=84, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=84, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=93, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=93, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=103, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=103, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=114, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=114, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=126, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=126, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=139, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=139, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=153, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=153, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=169, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=169, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=186, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=186, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=205, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=205, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=226, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=226, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=249, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=249, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=274, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=274, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=302, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=302, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=333, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=333, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=367, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=367, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=404, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=404, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=445, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=445, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=490, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=490, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=540, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=540, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=595, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=595, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=655, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=655, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=721, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=721, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=794, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=794, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=874, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=874, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=962, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=962, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=1059, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=1059, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=1165, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=1165, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=1282, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=1282, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=1411, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=1411, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=1553, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=1553, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=1709, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=1709, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=1880, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=1880, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=2069, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=2069, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=2276, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=2276, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=2504, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=2504, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=2755, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=2755, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=3031, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=3031, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=3335, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=3335, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=3669, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=3669, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=4036, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=4036, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=4440, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=4440, ac_busy_delay=0
Info : dtmcontrol_idle=5, dmi_busy_delay=4885, ac_busy_delay=0
dtmcontrol_idle=5, dmi_busy_delay=4885, ac_busy_delay=0
invalid subcommand "protect 0 64 last off"
in procedure 'flash’
invalid subcommand "protect 0 64 last off"
in procedure 'flash’
Loading section .init, size 0x78 lma 0x80000000
Loading section .text, size 0xbc38 lma 0x80000078
Loading section .rodata, size 0x1154 lma 0x8000bcb0
Loading section .eh_frame, size 0x68 lma 0x8000ce04
Loading section .data, size 0x9d0 lma 0x8000ce70
Start address 0x80000000, load size 55356
Transfer rate: 4 KB/sec, 6919 bytes/write.
shutdown command invoked
shutdown command invoked
A debugging session is active.

Inferior 1 [Remote target] will be detached.

Quit anyway? (y or n) [answered Y; input not from terminal]
Remote communication error. Target disconnected.: Connection reset by peer.
Successfully uploaded ‘led_flash’ to freedom-e300-arty.

Any bugs in openocd after update? Or any other suggestions? Thanks.