Writing to the External QSPI Flash Memory

Hello everyone,

In the memory map of the board HiFive1 Rev B, I have seen that QSPI Flash region is write-protected, only read and execute are allowed. I could read lines of the QSPI flash by simply accessing with pointers, but could not write it.

Is there a way to write QSPI Flash? Or is there a way to disable this write-protection? I want to update the lines of QSPI flash at run-time.

Thank you in advance.

1 Like

Easy @Utkk it’s pretty simple with a single CLI command. See file riscv.mk shown in Demonstrating AMO

 make -f riscv.mk [romload | romrun | romdebug]

Discussion in more detail is at Demystifying OpenOCD

Basically, you define a bank item for each Flash device and/or memory range. You can use any name you wish, spi0 is a good choice. You can have as many banks as you want.

The SPI peripheral base address which is wired to the Flash memory is 0x10014000, and the Flash memory device is mapped at address 0x20000000, both are constants specified in the Data Sheet and User Manual for the FE310 SoC of the HiFive 1 Rev B board.

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

It’s a five-step procedure to write the Flash memory: First, turn off the write protection for the sector(s) you will write; then erase the desired sector(s); followed by writing and verifying those sectors; lastly and optionally turn on the write protection.

flash protect       0   0 4 off
flash erase_sector  0   0 4
flash write_bank    0 filename
flash verify_bank   0 filename
flash protect       0   0 4 on

The two numbers {0, 4} above are the beginning and ending sector numbers of the Flash memory. Computing them for desired length of filename is a little tricky, to do a proper integer-based calculation. The number 4 the example steps is ENDSEC below; the number 0 in the example is trivial, starting from beginning of Flash memory.

In tcl script, for use in openocd files:

set secsz [expr 0x1000]  ;# 4K sectors issi is25lp128d, typ. most NOR flash
set len [file size filename]
set endsec [expr (${len}/${secsz})+((${len}-(${len}/${secsz})*${secsz})>0)-1]

In /bin/bash script, for use in, e.g., make files:

$(eval SECSZ=$(shell echo "ibase=16; 1000" | bc))
$(eval LEN=$(shell <filename wc -c))
$(eval ENDSEC=$(shell echo "(${LEN}/${SECSZ})+((${LEN}-(${LEN}/${SECSZ})*${SECSZ})>0)-1" | bc))

Where SECSZ for the issi is25lp128d Flash memory chip is 4K, or 0x1000 bytes. It is also defined in …src/flash/nor/core.h:45 of the openocd library.

@pds Thank you for your response. I have tried what you said and this is the output of flash info 0 command:

    #  0: 0x00000000 (0x10000 64kB) not protected
#  1: 0x00010000 (0x10000 64kB) not protected
#  2: 0x00020000 (0x10000 64kB) not protected
#  3: 0x00030000 (0x10000 64kB) not protected
#  4: 0x00040000 (0x10000 64kB) not protected
#  5: 0x00050000 (0x10000 64kB) not protected
#  6: 0x00060000 (0x10000 64kB) not protected
#  7: 0x00070000 (0x10000 64kB) not protected
#  8: 0x00080000 (0x10000 64kB) not protected
#  9: 0x00090000 (0x10000 64kB) not protected
# 10: 0x000a0000 (0x10000 64kB) not protected
# 11: 0x000b0000 (0x10000 64kB) not protected
# 12: 0x000c0000 (0x10000 64kB) not protected
# 13: 0x000d0000 (0x10000 64kB) not protected
# 14: 0x000e0000 (0x10000 64kB) not protected
# 15: 0x000f0000 (0x10000 64kB) not protected
# 16: 0x00100000 (0x10000 64kB) not protected
# 17: 0x00110000 (0x10000 64kB) not protected
# 18: 0x00120000 (0x10000 64kB) not protected
# 19: 0x00130000 (0x10000 64kB) not protected
# 20: 0x00140000 (0x10000 64kB) not protected
# 21: 0x00150000 (0x10000 64kB) not protected
# 22: 0x00160000 (0x10000 64kB) not protected
# 23: 0x00170000 (0x10000 64kB) not protected
# 24: 0x00180000 (0x10000 64kB) not protected
# 25: 0x00190000 (0x10000 64kB) not protected
# 26: 0x001a0000 (0x10000 64kB) not protected
# 27: 0x001b0000 (0x10000 64kB) not protected
# 28: 0x001c0000 (0x10000 64kB) not protected
# 29: 0x001d0000 (0x10000 64kB) not protected
# 30: 0x001e0000 (0x10000 64kB) not protected
# 31: 0x001f0000 (0x10000 64kB) not protected
# 32: 0x00200000 (0x10000 64kB) not protected
# 33: 0x00210000 (0x10000 64kB) not protected
# 34: 0x00220000 (0x10000 64kB) not protected
# 35: 0x00230000 (0x10000 64kB) not protected
# 36: 0x00240000 (0x10000 64kB) not protected
# 37: 0x00250000 (0x10000 64kB) not protected
# 38: 0x00260000 (0x10000 64kB) not protected
# 39: 0x00270000 (0x10000 64kB) not protected
# 40: 0x00280000 (0x10000 64kB) not protected
# 41: 0x00290000 (0x10000 64kB) not protected
# 42: 0x002a0000 (0x10000 64kB) not protected
# 43: 0x002b0000 (0x10000 64kB) not protected
# 44: 0x002c0000 (0x10000 64kB) not protected
# 45: 0x002d0000 (0x10000 64kB) not protected
# 46: 0x002e0000 (0x10000 64kB) not protected
# 47: 0x002f0000 (0x10000 64kB) not protected
# 48: 0x00300000 (0x10000 64kB) not protected
# 49: 0x00310000 (0x10000 64kB) not protected
# 50: 0x00320000 (0x10000 64kB) not protected
# 51: 0x00330000 (0x10000 64kB) not protected
# 52: 0x00340000 (0x10000 64kB) not protected
# 53: 0x00350000 (0x10000 64kB) not protected
# 54: 0x00360000 (0x10000 64kB) not protected
# 55: 0x00370000 (0x10000 64kB) not protected
# 56: 0x00380000 (0x10000 64kB) not protected
# 57: 0x00390000 (0x10000 64kB) not protected
# 58: 0x003a0000 (0x10000 64kB) not protected
# 59: 0x003b0000 (0x10000 64kB) not protected
# 60: 0x003c0000 (0x10000 64kB) not protected
# 61: 0x003d0000 (0x10000 64kB) not protected
# 62: 0x003e0000 (0x10000 64kB) not protected
# 63: 0x003f0000 (0x10000 64kB) not protected

So all regions look like unprotected. But my problem is more related to the run-time. So, I want to write a flash region using pointers in my c code. I can access and read the single flash lines but cannot write on them. According to the datasheet of the document, the SPI flash has only read and execute rights, as depicted in Figure below:

image

Probably, that is why, I cannot write it. Is there a way to disable this protection and write in flash at run-time?

@Utkk You are right. The FE310-G002 Manual describes in Section 19 (SPI peripheral) that the built-in Flash read sequencer of QSPI0 “… exposes the external SPI flash contents as a read/execute-only memory-mapped device.” It does this with the fctrl and ffmt registers. See Sections 19.16 and 19.17.

The only hope to do writing flash at run-time is not use the Flash read sequencer (i.e., set fctrl.en = 0), instead using txdata and rxdata registers as is customary with any other SPI device. You would then be able to implement the erase, write, and any other Flash command you like, besides the standard Winbond/Numonyx serial read (0x03) command. Unfortunately, write and other commands vary from one flash chip to another. See the issi IS25LP128D datasheet, or whichever device is on the HiFive 1 Rev B board, for details.

That’s my guess. It’s probably how OpenOCD implements the flash command, you could look in the repo for clues.

1 Like

I tried to write the data for QSPI flash memory but I can’t able to write and read the QSPI flash memory … memory mapped QSPI external* flash? in STM 32 MCUs … Quad Serial Peripheral Interface QSPI is a serial communication interface. QSPI has been specifically designed for sign language alphabet talking to flash chips. Many QSPI Memory devices support both 3-Byte addressing for memory size 128Mb and 4-Byte addressing (for memory size 128Mb . Often the device powers up in the 3-Byte mode and must be sent a special command to enable 4-byte addressing.

I’m afraid it may not be possible to write and read at the same time without additional hacks. In order to issue write commands one needs to disable memory mapping and also exit any non-default modes of operation used by it (like XIP), if any. One can execute code from RAM while QSPI mapping is disabled.