Reading S25FL256S OTP region via QSPI Indirect Transfer on Cyclone V HPS — data comes back incorrect
What I'm trying to do
I need to read OTP memory region of the S25FL256S flash using command 0x4B. The system normally runs in quad-SPI mode (0xEC, 4-byte address). Since the data I need is more than 8 bytes (STIG limit), I'm trying to use the indirect transfer path.
The 0x4B command requires:
- Single-SPI (1-wire address and data)
- 3-byte address
- 1 fixed dummy byte (8 clocks) after address
Setup
The QSPI controller is initialized at startup using the standard Altera HAL:
alt_qspi_init(); // detects flash JEDEC ID, configures timing, sets up qspi_config struct
alt_qspi_enable(); // enables controller, sets quad mode (0xEC, 4-byte addr, LC=10b)
After this, normal quad-SPI reads via alt_qspi_read() works correctly.
What I'm doing
Reconfigure controller for 0x4B (OTP Read)
ALT_QSPI_DEV_INST_CONFIG_t read_cfg = {
.op_code = 0x4B,
.inst_type = ALT_QSPI_MODE_SINGLE,
.addr_xfer_type = ALT_QSPI_MODE_SINGLE,
.data_xfer_type = ALT_QSPI_MODE_SINGLE,
.dummy_cycles = 8
};
alt_qspi_device_read_config_set(&read_cfg);
ALT_QSPI_DEV_SIZE_CONFIG_t size_cfg = {
...
.addr_size = 2, // N+1 encoding → 3 bytes on wire
.page_size = 256,
...
};
alt_qspi_device_size_config_set(&size_cfg);
Disable QUAD on the flash device itself via STIG
alt_qspi_read_register(0x35, ®s[1]); // read CR1
alt_qspi_read_register(0x05, ®s[0]); // read SR1
alt_qspi_device_wren();
regs[1] = (regs[1] & 0x3D) | 0x00 | 0x80; // QUAD=0, LC=10b
alt_qspi_stig_wr_cmd(0x01, 0, 2, (uint32_t*)regs, timeout);
alt_qspi_sr_wait_write(timeout);
Execute indirect read
// Internally: sets INDRDSTADDR, INDRDCNT, starts transfer,
// then CPU drains SRAM FIFO via ALT_QSPIDATA_ADDR
alt_qspi_read(dst, src, size);
Problem
The data returned by the indirect read is incorrect. A STIG-based read of the same region (using the same 0x4B command, 8 bytes at a time) returns the correct data. The indirect read returns wrong/shifted bytes. Hence do I need to configure anything else?
Result
correct value : 97C5995C5C1E9D5D7A00D4E6BD4ED53E
read value : FF97C5995C5C1E9D5D7A00D4E6BD4ED5
Hi Archer_Altera,
Thanks for the update, really appreciate the solution. Regarding the solutions
1) I tried to manually increase the dummy cycles, but that wouldn't help as the number of dummy cycles required for OTP read (0x4B) is 8 dummy cycles, so data would be shifted if made to 16.
2) The address width is 24-bit which is 3 bytes, which wouldn't work either as per the QSPI flash documentation for OTP Read. But as you specified running small STIG commands before executing indirect read does synchronize the controller to single SPI mode (but data still comes shifted by a single byte)
3) This is a good work-around, but the issue arises when the read happens from address 0x0. As the data is shifted right by a single byte, byte[0] cannot be retreived (lowest byte of the read data). Hence the exact byte at address 0x0 cannot be retreived using this workaround.
4) Byte alignment check is done in alt_qspi_read itself, so this check would be reduntant.I have figured out the issue, it is related to configuration in devrd register.
- The modebit is enabled by default on bootup for Quad read (0xEC) / write (0x34) configuration , therefore this adds extra cycles in read operation which shifts the data bits by 1 byte for Single mode commands like Fast read (0xB), OTP read (0x4B), etc.
- Here even though the modebit value is specified as '0' on reset, and although my ARM application does not set this bit anywhere, I think this bit is getting configured from the uboot itself. There I temporarily disable this bit for doing OTP read (0x4B) using alt_qspi_mode_bit_disable.
Hence the resulting code will look something like this :
... // Code specified previously for OTP Read configuration
alt_qspi_mode_bit_disable();
alt_qspi_read(dst, src, size);
...