Progress getting OpenOCD usable with RevB

I’ve been working on seeing if I can get OpenOCD to work with the Rev B (since OpenOCD should have J-link OB support since 0.10). I have made some progress, but have hit a roadblock that is stumping me.

First up, it seems OpenOCD can’t connect because the J-Link OB uses a new PID that libjaylink is not aware of–by patching libjaylink in openocd I can actually get the USB device to be opened

diff --git a/src/jtag/drivers/libjaylink/libjaylink/discovery_usb.c b/src/jtag/drivers/libjaylink/libjaylink/discovery_usb.c
index 48d532292..9d979013c 100644
--- a/src/jtag/drivers/libjaylink/libjaylink/discovery_usb.c
+++ b/src/jtag/drivers/libjaylink/libjaylink/discovery_usb.c
@@ -59,7 +59,8 @@ static const uint16_t pids[][2] = {
        {0x1015, 0},
        {0x1016, 0},
        {0x1017, 0},
-       {0x1018, 0}
+       {0x1018, 0},
+       {0x1051, 0},
 };

Then, from there I’ve been working on getting an openocd.cfg that can work with the RevB. So far, I have this:

interface jlink
transport select jtag

adapter_khz 4000

set _CHIPNAME riscv
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x20000913

set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME
$_TARGETNAME.0 configure -work-area-phys 0x80000000 -work-area-size 10000 -work-area-backup 1

flash bank spi0 fespi 0x20000000 0 0 0 $_TARGETNAME.0 0x10014000

And with this, I can start openocd and connect to it from riscv-gdb after loading an example elf and connect with target extended-remote localhost:3333 and gdb will connect and print the current execution

0x200152ae in __metal_driver_sifive_uart0_getc (uart=0x2, c=0x80000fe4)
    at freedom-e-sdk/freedom-metal/src/drivers/sifive_uart0.c:71
71	      *c = ch & 0x0ff;

Whoo! This was fantastic! But, this is where I’m stuck. If I try to run the rest of the commands to turn off flash protect, halt the processor, then load the elf, things…do not work. There’s lots of errors that occur (small snippet)

(gdb) monitor reset halt
JTAG tap: riscv.cpu tap/device found: 0x20000913 (mfg: 0x489 (SiFive Inc), part: 0x0000, ver: 0x2)
Failed to send data to device: LIBUSB_ERROR_TIMEOUT.
Failed to send data to device: LIBUSB_ERROR_TIMEOUT.
Sending data to device timed out.
transport_write() failed: timeout occurred.
jaylink_jtag_io() failed: timeout occurred.
dmi_scan failed jtag scan
Failed read (NOP) at 0x11; value=0x0, status=2
Last read operation left 68 bytes.
Failed to send data to device: LIBUSB_ERROR_TIMEOUT.
Failed to send data to device: LIBUSB_ERROR_TIMEOUT.
Sending data to device timed out.
transport_write() failed: timeout occurred.
jaylink_jtag_io() failed: timeout occurred.
dmi_scan failed jtag scan
failed read at 0x11, status=2
(gdb) monitor flash protect 0 64 last off
Last read operation left 281 bytes.
Failed to send data to device: LIBUSB_ERROR_TIMEOUT.
Failed to send data to device: LIBUSB_ERROR_TIMEOUT.
Sending data to device timed out.
transport_write() failed: timeout occurred.
jaylink_jtag_io() failed: timeout occurred.
dmi_scan failed jtag scan
failed read at 0x11, status=2
Last read operation left 281 bytes.
Failed to send data to device: LIBUSB_ERROR_TIMEOUT.
Failed to send data to device: LIBUSB_ERROR_TIMEOUT.
Sending data to device timed out.
transport_write() failed: timeout occurred.
jaylink_jtag_io() failed: timeout occurred.
dmi_scan failed jtag scan
Failed read (NOP) at 0x11; value=0x0, status=2
ERROR: first sector must be <= last

The last line seemed to be from flash protect, and if I add the following to my openocd.cfg

init
halt
flash protect 0 64 last off

Then I don’t see any timeouts, but I do see the flash error

Open On-Chip Debugger 0.10.0+dev-00841-g1449af5bd (2020-03-03-20:03)
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
Info : J-Link OB-K22-SiFive compiled Feb 28 2019 12:46:23
Info : Hardware version: 1.00
Info : VTarget = 3.300 V
Info : clock speed 4000 kHz
Info : JTAG tap: riscv.cpu tap/device found: 0x20000913 (mfg: 0x489 (SiFive Inc), part: 0x0000, ver: 0x2)
Info : datacount=1 progbufsize=16
Info : Disabling abstract command reads from CSRs.
Info : Examined RISC-V core; found 1 harts
Info :  hart 0: XLEN=32, misa=0x40101105
Info : Listening on port 3333 for gdb connections
Info : Found flash device 'issi is25lp032' (ID 0x0016609d)
ERROR: first sector must be <= last

…and this still happens even if I specify a size in the flash bank portion (and if I specify size for flash protect instead of last, then I see ERROR: last sector must be <= 63)

This all seems to me that the flash bank is not being properly setup. It does seem it is getting detected by openocd through jlink/jtag, but not sure what’s going wrong (also, flash banks isn’t printing on my build… but can debug that on my own at least).

I have been able to flash the board using USB mass storage, so I at least know the cable is good ;D

Any ideas?

2 Likes

Argh, still can’t figure out why when I build openocd it does not enable the command_print function. But, did make some progress regardless

I had thought the jlink driver autosets if it’s “v2” or “v3”, but apparently not, and it defaults to v2. If I update the config with jlink jtag 3 then it doesn’t timeout when halting the core. Whoo! …it still complains about flash protect, and further it seems to stall when loading the elf and hangs after a “warning” that it couldn’t allocate the work area

Warn : keep_alive() was not invoked in the 1000ms timelimit. GDB alive packet not sent! (1976). Workaround: increase "set remotetimeout" in GDB
keep_alive() was not invoked in the 1000ms timelimit. GDB alive packet not sent! (1976). Workaround: increase "set remotetimeout" in GDB
ERROR: first sector must be <= last
ERROR: first sector must be <= last
Info : JTAG tap: riscv.cpu tap/device found: 0x20000913 (mfg: 0x489 (SiFive Inc), part: 0x0000, ver: 0x2)
Warn : keep_alive() was not invoked in the 1000ms timelimit. GDB alive packet not sent! (2243). Workaround: increase "set remotetimeout" in GDB
Loading section .init, size 0x194 lma 0x20010000
Loading section .text, size 0x5144 lma 0x20010200
Loading section .rodata, size 0xb54 lma 0x20015344
Loading section .init_array, size 0xc lma 0x20015e98
Loading section .data, size 0xad0 lma 0x20015ea8
Info : Padding image section 0 at 0x20010194 with 108 bytes
Info : Padding image section 1 at 0x20015ea4 with 4 bytes
Warn : Couldn't allocate 796-byte working area.

…and it hangs there until I kill it. It must be doing something though as my board appears to hang itself on boot, and I need to reflash the hello example onto it via USB mass storage to get it to work again.

Fun find–it’s not that my command_prints don’t work, but they only get sent to the GDB connection, so I need to run those info commands via the GDB prompt :sweat_smile: This wasn’t the case when I didn’t build from source, so maybe something here has changed?

Executing flash info 0 showed that the flash bank already had protection turned off, hence that command not working.

Further, after parsing the debug logs I could see OpenOCD writing the program binary to flash, but it was doing it extremely slowly (around 24 kB over ~15 minutes). It was doing so apparently because I could start the flashing for different programs, and see that initial portion of data if I dump the flash spi via gdb and compared to the corresponding section in the elf.

…which made me suspect that it was the inability to allocate work area. I dug into this and found I had apparently cloned the riscv/riscv-openocd instead of sifive/riscv-openocd. The former has more changes and seems to be more active, but also now by default assumes the chip will be using virtual addressing and tries to detect the MMU (which fails here). By adding riscv set_enable_virt2phys off to my openocd.cfg, I can successfully connect via gdb and flash and debug my program!

Whoo!

…that being said I think I’ve broken something else along the way. If I had a serial terminal connected, I can see the results print from the different example programs after I load; monitor resume …but if I try to reset my board in any way (monitor reset, hitting reset button on board, or unplugging and replugging my board), then nothing happens, and if I connect gdb and bt, I see it’s stuck at #0 _enter () at /home/tincman/coding/freedom-e-sdk/freedom-metal/src/entry.S:24

…and not amount of monitor resume monitor halt; monitor step will change this.

Which, I must have broken something else because now I cannot even flash a hex file via USB mass storage (shows the same issue, and no FAIL.txt shows up in the usb drive either).

Is it possible I overwrote some bootcode? I thought that was in OTP and always jumped to 0x20000000?

…which maybe that is it? I see from the elf that nothing is written to 0x20000000?

(gdb) load
Loading section .init, size 0x194 lma 0x20010000
Loading section .text, size 0x5dc6 lma 0x20010200
Loading section .rodata, size 0x10d8 lma 0x20015fc8
Loading section .init_array, size 0xc lma 0x200170a0
Loading section .data, size 0xae8 lma 0x200170b0
Start address 0x20010000, load size 31526
Transfer rate: 5 KB/sec, 5254 bytes/write.

And I also just remembered the getting started guide mentioned on boot the board will turn off radios, and I remember previously seeing those AT commands print to serial.

…luckily I had a backup board I was able to pull those first 64kb off of and reflash it to the other board (thanks to OpenOCD working :D), and that seems to have fixed the problem! Now I can reset/reboot as I please.

I do wonder, is the source code for that boot procedure located anywhere? Mostly thinking of a recover procedure in case someone else ends up in a similar situation.

…just for fun as well as I am now able to restore the previous behavior. I also have a Sparkfun Red-V (like a revB, but has no ESP32) and I didn’t need the startup routine AFAIK, so I tried changing the linker scripts to use flash starting at 0x2000_0000 and that worked as well :smiley:

I’m fairly confident this is working now, but I’m going to give it some more testing before I get this cleaned up and see about getting it merged if there’s interest in it.

Very good insights here.
I also encountered similar behavior and was able to resolve it using riscv set_reset_timeout_sec 5, as well as doing sleep 5 after reset init
Seems that the FE310 takes a long time to come up out of reset.
Provided your code is small enough, do you notice any difference in performance if you load and run your code entirely from the ram at 0x80000000? Yes, the fespi loader is painfully slow, seems to be doing huge blocks of 64K at a time, and nothing smaller.

Ah ha, that makes sense: the original HiFive1 had a sleep routine after init, and that may explain why I can’t get openocd to connect for a period after powering on the board. I’ll be sure to add that in :]

Actually, making sure openocd can allocate and use a work area made a huge difference in speed. Apologies if that got lost in my posts.

From my impressions, the work area is a portion of ram openocd will use to store data and execute instructions to speed up operations. My guess is when the work area wasn’t enabled that the adapter was doing this a byte at a time over the jtag link vs the work area where it copies chunks and likely loops over it on the CPU itself.

The 64k makes sense as I think that’s the sector size flash info 0 reports.

Hmm, but execution from 0x80000000 could be interesting for other reasons. I’ll have to give that a try as well.

Note for others and future me (since I apparently forgot a detail and have been banging my head trying to reflash this image).

  1. flash protect 0 1 last off “works”–it looks like the numbers here are not some size, but sectors? (It doesn’t error, but also seems unnecessary since all my boards already have unprotected memory)
  2. In order to flash raw images, you must use flash write_image and you must use the erase flag: monitor flash write_image erase rom.bin 0x20000000 bin

Hi @tincman, granted this is an old conversation but I believe it is still a relevant topic. Just put together a bunch of overrides for the start-up process, proper delays, handling of failures during scan and examine, and made a couple suggestions for high level loading procedures for ram and rom destinations.

#------------- your_interface_file.cfg -------------------------
#
# INTERFACE
#

adapter driver ...

#-------------------------------------------

adapter speed 2000

gdb_port disabled  ;# 3333
tcl_port disabled  ;# 6666
telnet_port disabled  ;# 4444

#------------- fe310-g002.cfg -------------------------
#
# TARGET
#

transport select jtag

#
# SiFive FE310-G002 is "riscv" with cpuid "0x20000913"
#

jtag newtap riscv cpu -irlen 5 -expected-id 0x20000913

target create riscv.cpu.0 riscv -chain-position riscv.cpu

riscv.cpu.0 configure -event examine-start {
  echo "examine start"

  # prevent the dreaded 'Error: unable to halt hart 0' message
  if {[catch {riscv.cpu.0 arp_examine} err] != 0} {
    fe310_reset
    riscv.cpu.0 arp_examine
  }
}

proc fe310_reset { } {
  echo "proc fe310_reset"

  # pulse the reset line just in case h/w was locked
  #!!!CRITICAL -- to avoid dreaded 'Error: unable to halt hart 0' message during init ...

  echo "fe310_reset: pulsing reset line"
  #------------------------
  reset_config trst_only trst_open_drain separate
  jtag_ntrst_assert_width 0
  jtag_ntrst_delay 0
  adapter assert trst  ;#  ftdi_set_signal nTRST 0  ;# assert RST
  adapter deassert trst  ;#  ftdi_set_signal nTRST 1  ;# deassert RST
  reset_config none
  #------------------------

  jtag arp_init-reset  ;# this thing takes a long time to come back

  echo "fe310_reset: wait for target get into reset state (prevent impatient scan retries)"
  sleep 1500  ;# FE310 target AON reset block delay, default 2^8 cycles of core clock
}

proc jtag_init { } {  ;# overrides openocd/src/jtag/startup.tcl
  echo "proc jtag_init"
  if {[catch {jtag arp_init} err] != 0} {
    fe310_reset
  }
}

proc init_reset { mode } {  ;# overrides openocd/src/jtag/startup.tcl
  echo "proc init_reset"
  if {[catch {jtag arp_init} err] != 0} {
    fe310_reset
  }
}

riscv.cpu.0 configure -work-area-phys 0x80000000 -work-area-size 0x10000 -work-area-backup 0

riscv set_reset_timeout_sec 1

riscv set_enable_virt2phys off
riscv set_enable_virtual off

flash bank spi0 fespi 0x20000000 0 0 0 riscv.cpu.0 0x10014000


#--------------------------------------------------------------
#
# APPLICATION(S)
#

#
# fe310_ram_load <pgm-name-prefix> [<addr> [no_run]]
#
#   Preverifies the target memory to make sure writing is even necessary.
#   Allows for loading only when other than default address specified,
#   such as when preparing overlay code or non-executable data.
#
#   Typical usage:
#   openocd -f fe310-g002.cfg -c "adapter speed 2000" -c init -c "fe310_ram_load my_prog"\
#           -c shutdown -c exit
#   where my_prog-ram.bin has been linked for ram target addresses in the linker .lds script.

proc fe310_ram_load { program {addr "0x80000000"} {no_run ""} } {
  halt 100  ;# includes wait_halt 100

  # in case of verify_image pass, message returned is ''
  # in case of verify_image fail, message returned is 'contents differ', diff lines, abrupt exit
  #

  set rc [catch {verify_image ${program}-ram.bin ${addr} bin} err]  ;# trap exit on verify fail
  if {[expr $rc == 0]} {
    echo "ram already up to date - nothing to do"
  } else {
    echo [load_image ${program}-ram.bin ${addr} bin]
    echo [verify_image ${program}-ram.bin ${addr} bin]
  }

  if {[expr [string length $no_run] == 0]} {
    resume ${addr}
  }
}

#
# fe310_rom_load <pgm-name-prefix> [<addr> [no_run]]
#
#   Preverifies the target memory to make sure writing is even necessary.
#   When writing is necessary, prechecks the target memory to make sure
#   erasing is even necessary.
#   Determines smallest portion of target memory to work with, for
#   best programming speed and target memory endurance (lifetime).
#   Allows for loading only when other than default address specified,
#   such as when preparing overlay code or non-executable data.
#
#   assumes only one target, at position tap position 0, for now.
#
#   Typical usage:
#   openocd -f fe310-g002.cfg -c "adapter speed 2000" -c init -c "fe310_rom_load my_prog"\
#           -c shutdown -c exit
#   where my_prog-rom.bin has been linked for rom target addresses in the linker .lds script.
#
#   --------------------------------------------------------------------
#   Fixes the following bugs and issues:
#
#   [flash erase_check num]
#   Does not work, and takes a very long time, because of
#   message 'Running slow fallback erase check - add working memory'
#   All sectors are being tested, not just the ones which will be programmed.
#   See src/flash/nor/core.c:344 default_flash_mem_blank_check(),
#   src/flash/nor/core.c:391 default_flash_blank_check(),
#   src/flash/nor/fespi.c:1077 fespi_flash = { .erase_check=default_flash_blank_check; },
#   Suggest adding member 'int is_needed;' to struct flash_sector (nor/core.h:41)
#   to make default implementations of blank_check more intelligent.
#
#   [flash write_image erase unlock filename offset type]
#   Does not work, because of
#   messages 'Warn : Failed to write memory via program buffer.'
#   and 'Warn : Failed to write memory via abstract access.'
#   and also very long running time due to above problem when 'erase' specified.
#
#   [program filename preverify verify offset]
#   Does not work, because of
#   messages 'Warn : Failed to write memory via program buffer.'
#   and 'Warn : Failed to write memory via abstract access.'
#
#   [flash verify_image filename offset type]
#   seems to work okay.
#   --------------------------------------------------------------------
#

proc fe310_rom_load { program {addr "0x20000000"} {no_run ""} } {
  halt 100  ;# includes wait_halt 100

  # sector size also defined in ...src/flash/nor/core.h:45 (uint32_t) size
  set secsz [expr 0x1000]  ;# 4K sectors issi is25lp128d, typ. most NOR flash
  set len [file size ${program}-rom.bin]
  set endsec [expr (${len}/${secsz})+((${len}-(${len}/${secsz})*${secsz})>0)-1]

  #-----
  # equivalent implementation in /bin/bash script, for use in, e.g., mk files, is shown below:
  #$(eval SECSZ=$(shell echo "ibase=16; 1000" | bc))
  #$(eval LEN=$(shell <${PROGRAM}-rom.bin wc -c))
  #$(eval ENDSEC=$(shell echo "(${LEN}/${SECSZ})+((${LEN}-(${LEN}/${SECSZ})*${SECSZ})>0)-1" | bc))
  #-----

  set erased_value 255  ;# also defined in ...src/flash/nor/core.h:104 (uint8_t) erased_value
  set is_erased 1
  for {set i 0} { ($i <= $endsec) && $is_erased } {incr i} {
    riscv.cpu.0 mem2array buf 8 [expr ${addr} + ${secsz} * $i] ${secsz}
    foreach idx [array names buf] {
      if {$buf($idx) != ${erased_value}} {
        set is_erased 0
      }
    }
  }

  # in case of verify_bank pass, message returned is 'contents match'
  # in case of verify_bank fail, message returned is 'contents differ', diff lines, abrupt exit
  #

  set rc [catch {flash verify_bank 0 ${program}-rom.bin} err]  ;# trap exit on verify fail
  if {[expr $rc == 0]} {
    echo "flash device already up to date - nothing to do"
  } else {
    echo [flash protect 0 0 ${endsec} off]
    if {! $is_erased} {
      echo [flash erase_sector 0 0 ${endsec}]
    }
    echo [flash write_bank 0 ${program}-rom.bin]
    echo [flash verify_bank 0 ${program}-rom.bin]
    echo [flash protect 0 0 ${endsec} on]
  }

  if {[expr [string length $no_run] == 0]} {
    resume ${addr}
  }
}
1 Like

On the subject of an ftdi interface, such as the FT2232 inside the Olimex JTAG ARM-USB-TINY-H module, there is potential for a short glitch on the nTRST (and, perhaps, the nSRST) line depending how the layout is initialized and signal are defined for these lines.

For example, nTRST is the GPIOH0 signal (in MPSSE mode) and wired to bit 8, or ACBUS0 pin of the FT chip. This layout_init statement sets bit 8 as input, …
#ftdi_layout_init 0x0808 0x0a1b ;# BUG!!! tiny 10us glitch on nTRST at startup
… which momentarily later gets set to be an output with push-pull drive by this statement:
ftdi_layout_signal nTRST -data 0x0100 -oe 0x0100

The tiny glitch at startup can be eliminated by making bit 8 to be an output from the beginning:
ftdi_layout_init 0x0b08 0x0b1b ;# BUG FIX: rst lines (out) and (push-pull)

In general, for any hardware adapter, the following table might be helpful. Adjust the bits of the reset line(s) accordingly for which pins and how the adapter hardware is physically wired up.

ftdi_layout_init 0x0b08 0x0b0b  ;# rst lines (out) and (push-pull)
# Signal MPSSE   PIN                   Direction
# ------ ----    ---           ------  ---------
# TCK    ADBUS0  TCK/SK        0       1 (out)
# TDI    ADBUS1  TDI/DO        0       1 (out)
# TDO    ADBUS2  TDO/DI        0       0 (in)
# TMS    ADBUS3  TMS/CS        1 (ena) 1 (out)
# ???    GPIOL0  ADBUS4        0       1 (out)
#        GPIOL1  ADBUS5        0       0 (in)
#        GPIOL2  ADBUS6        0       0 (in)
#        GPIOL3  ADBUS7        0       0 (in)

# TRST   GPIOH0  ACBUS0        1 (ena) 1 (out)
# SRST   GPIOH1  ACBUS1        1 (ena) 1 (out)
#        GPIOH2  ACBUS2        0       0 (in)
# LED    GPIOH3  ACBUS3        1 (ena) 1 (out)
#        GPIOH4  ACBUS4        0       0 (in)
#        GPIOH5  ACBUS5        0       0 (in)
#        GPIOH6  ACBUS6        0       0 (in)
#        GPIOH7  ACBUS7        0       0 (in)

ftdi_layout_signal nSRST -data 0x0200 -oe 0x0200
ftdi_layout_signal nTRST -data 0x0100 -oe 0x0100

Lastly, the linker script can be revised for ram or rom targets in the .text and .rodata sections as follows; or, a separate script be made for each and selected in turn by different makefiles or makefile options.

OUTPUT_ARCH("riscv")

ENTRY( _start )

MEMORY
{
  rom          : ORIGIN = 0x20000000, LENGTH = 0x1000000     /* 16MB off-chip */
  ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 0x4000 /* 16KB on-chip */
}

SECTIONS
{
  .text :
  {
    PROVIDE(_text_start = .);
    *(.text*)
    PROVIDE(_text_end = .);
  }
  > ram  /* ram or rom */

  . = ALIGN(4);

  .rodata :
  {
    PROVIDE(_rodata_start = .);
    *(.rodata*)
    PROVIDE(_rodata_end = .);
  }
  > ram  /* ram or rom */

  . = ALIGN(4);

  .bss :
  {
    PROVIDE(_bss_start = .);
    *(.bss*)
    PROVIDE(_bss_end = .);
  }
  > ram
}

Then in the makefile fe310.mk you would have a pair of link instructions

ram : fe310-g002-ram.lds ...
	$(RISCVGNU)-ld ... -T fe310-g002-ram.lds -o $(PROGRAM)-ram.elf
	$(RISCVGNU)-objcopy $(PROGRAM)-ram.elf -O binary $(PROGRAM)-ram.bin
	@openocd -f fe310-g002.cfg -c "adapter speed 2000" -c init -c "fe310_ram_load ${PROGRAM}" -c shutdown -c exit

and

rom : fe310-g002-rom.lds ...
	$(RISCVGNU)-ld ... -T fe310-g002-rom.lds -o $(PROGRAM)-rom.elf
	$(RISCVGNU)-objcopy $(PROGRAM)-rom.elf -O binary $(PROGRAM)-rom.bin
	@openocd -f fe310-g002.cfg -c "adapter speed 2000" -c init -c "fe310_rom_load ${PROGRAM}" -c shutdown -c exit

So that at the command line, to do a full build (assemble, compile, link, load, and target device erase, program, verify) is easy:
make -f fe310.mk rom
or
make -f fe310.mk ram

2 Likes