memory-25q

CLI reference

glasgow run memory-25q

Identify, read, erase, or program memories compatible with 25-series SPI/dual-SPI/quad-SPI NOR Flash memory, such as Microchip 25C320, Winbond W25Q32JV, Micron MT25QU256ABA, Macronix MX25L6445E, ISSI IS25LP128, or hundreds of other memories that typically have 25x (where “x” is a letter, usually C, F, or Q) in their part number. Note that 25N typically designates (Q)SPI NAND Flash memory, which is completely incompatible.

The pinout of a typical 25-series IC is as follows:

        16-pin                     8-pin
IO3/HOLD# @ * SCK               CS# @ * VCC
      VCC * * IO0/COPI     IO1/CIPO * * IO3/HOLD#
      N/C * * N/C           IO2/WP# * * SCK
      N/C * * N/C               GND * * IO0/COPI
      N/C * * N/C
      N/C * * N/C
      CS# * * GND
 IO1/CIPO * * IO2/WP#

This applet supports ordinary SPI mode, as well as dual-SPI and quad-SPI. When quad-SPI mode is not in use, the WP# and HOLD# pins are pulled high; these must not be left floating as the memories do not typically include internal pull-ups. These faster modes are not enabled by default because of the potential for bus contention and higher severity of crosstalk; use the options --dual-spi and --quad-spi to enable their use (when described in SFDP).

It is also possible to flash 25-series flash chips using the spi-flashrom applet, which requires a third-party tool flashrom. The advantage of using the flashrom applet is that flashrom offers compatibility with a wider variety of devices, some of which may not be supported by the memory-25q applet.

usage: glasgow run memory-25q [-h] [-V SPEC] [--cs PIN] [--sck PIN]
                              [--io PINS] [-f FREQ] [--dual-spi] [--quad-spi]
                              [-A WIDTH] [-P SIZE] [--opcode-erase-4k OPCODE]
                              [--opcode-erase-32k OPCODE]
                              [--opcode-erase-64k OPCODE]
                              OPERATION ...
-h, --help

show this help message and exit

-V <spec>, --voltage <spec>

configure I/O port voltage to SPEC (e.g.: ‘3.3’, ‘A=5.0,B=3.3’, ‘A=SA’)

--cs <pin>

bind the applet I/O line ‘cs’ to PIN (default: ‘A0’, required)

--sck <pin>

bind the applet I/O line ‘sck’ to PIN (default: ‘A1’, required)

--io <pins>

bind the applet I/O lines ‘copi’, ‘cipo’, ‘wp’, ‘hold’ to PINS (default: ‘A2,A3,A4,A5’, required)

-f <freq>, --frequency <freq>

set SCK frequency to FREQ kHz (default: 12000)

--dual-spi

use dual-SPI modes if present in SFDP

--quad-spi

use quad-SPI modes if present in SFDP

-A {3,4}, --address-bytes {3,4}

use WIDTH address bytes for data region (choices: 3, 4, default: 3)

-P <size>, --page-size <size>

do not cross multiple-of-SIZE boundaries when programming

--opcode-erase-4k <opcode>

opcode for erasing a 4 KiB region

--opcode-erase-32k <opcode>

opcode for erasing a 32 KiB region

--opcode-erase-64k <opcode>

opcode for erasing a 64 KiB region

glasgow run memory-25q erase

Erase memory range. Start and end of specified range must be aligned on erase size boundaries.

usage: glasgow run memory-25q erase [-h] ADDRESS LENGTH
address

start with byte at ADDRESS

length

continue for LENGTH bytes

-h, --help

show this help message and exit

glasgow run memory-25q erase-all

Erase memory range covering the entire chip.

usage: glasgow run memory-25q erase-all [-h]
-h, --help

show this help message and exit

glasgow run memory-25q identify

Identify memory by its JEDEC ID and (if available) Serial Flash Discoverable Parameters.

usage: glasgow run memory-25q identify [-h] [--annotate-raw-sfdp]
-h, --help

show this help message and exit

--annotate-raw-sfdp

annotate known raw SFDP DWORDs with fields

glasgow run memory-25q protect

Read or write Block Protection (BP) bits. The exact size and location of the protected area for a specific bit combination can only be determined from the device datasheet.

Setting all BP bits to zero may be necessary to execute the erase-all command or to resolve erase/program failures.

usage: glasgow run memory-25q protect [-h] [BITMASK]
bitmask

write BITMASK to BP bits (e.g. 0000 to allow all, 1111 to protect all)

-h, --help

show this help message and exit

glasgow run memory-25q read

Read memory range using the fastest available read command.

usage: glasgow run memory-25q read [-h] [-f FILENAME] ADDRESS LENGTH
address

start with byte at ADDRESS

length

continue for LENGTH bytes

-h, --help

show this help message and exit

-f <filename>, --file <filename>

write memory contents to FILENAME

glasgow run memory-25q verify

Read memory range using the fastest available read command, and compare with given data.

usage: glasgow run memory-25q verify [-h] (-d DATA | -f FILENAME) ADDRESS
address

start with byte at ADDRESS

-h, --help

show this help message and exit

-d <data>, --data <data>

use hex bytes DATA as memory contents

-f <filename>, --file <filename>

use data from FILENAME as memory contents

glasgow run memory-25q write

Write memory range using a read-modify-write sequence. Start and end of specified range do not need to be aligned on erase size boundaries.

usage: glasgow run memory-25q write [-h] (-d DATA | -f FILENAME) ADDRESS
address

start with byte at ADDRESS

-h, --help

show this help message and exit

-d <data>, --data <data>

use hex bytes DATA as memory contents

-f <filename>, --file <filename>

use data from FILENAME as memory contents

glasgow tool memory-25q

Dissect captured SPI/QSPI transactions and extract data into linear memory image files.

The expected capture file format is the same as ones used by spi-analyzer and qspi-analyzer applets. Specifically, one of the following Comma-Separated Value line formats is expected:

  • <COPI>,<CIPO>, where <COPI> and <CIPO> are hexadecimal byte sequences with each eight bits corresponding to samples of COPI and CIPO, respectively (from MSB to LSB).

  • <DATA>, where <DATA> is a hexadecimal nibble sequence with each four bits corresponding to samples of HOLD#, WP#, CIPO, COPI (from MSB to LSB).

The extracted data can be saved as pairs of data files and mask files, where the mask file contains a 1 bit for every bit in the data file that has been observed in a transaction, and a 0 bit otherwise.

The list below details every command that is recognized by this tool. If your capture includes commands not currently recognized, please open an issue with a capture file attached.

  • 03h (Read Data)

  • 04h (Write Disable)

  • 05h (Read Status Register)

  • 06h (Write Enable)

  • 0Bh (Fast Read)

  • 4Bh (Read Unique ID)

  • 5Ah (Read SFDP)

  • 9Fh (Read JEDEC ID)

  • B7h (Enter 4-Byte Address Mode)

  • E9h (Exit 4-Byte Address Mode)

  • 3Bh (Dual Output Fast Read)

  • 6Bh (Quad Output Fast Read)

  • BBh (Dual I/O Fast Read)

  • EBh (Quad I/O Fast Read)

usage: glasgow tool memory-25q [-h] [--data DATA-FILE] [--data-mask MASK-FILE]
                               [--sfdp DATA-FILE] [--sfdp-mask MASK-FILE]
                               [--uid DATA-FILE] [--uid-mask MASK-FILE]
                               CAPTURE-FILE
capture-file

read captured SPI transactions from CAPTURE-FILE

-h, --help

show this help message and exit

--data <data-file>

write extracted data to DATA-FILE

--data-mask <mask-file>

write data presence mask to MASK-FILE

--sfdp <data-file>

write extracted SFDP data to DATA-FILE

--sfdp-mask <mask-file>

write SFDP data presence mask to MASK-FILE

--uid <data-file>

write extracted UID data to DATA-FILE

--uid-mask <mask-file>

write UID data presence mask to MASK-FILE

API reference

exception glasgow.applet.memory._25q.Memory25QError
exception glasgow.applet.memory._25q.Memory25QVerifyError
class glasgow.applet.memory._25q.Memory25QInterface(logger: Logger, assembly, *, cs: GlasgowPin, sck: GlasgowPin, io: GlasgowPin)

Make sure to call initialize() first.

qspi: QSPIControllerInterface

Underlying QSPI interface.

cmds: CommandSet

Active NOR command set.

The command set may be modified or extended after the interface is created to better represent actual capabilities of the memory device.

sfdp: SFDPCollection | None

SFDP table collection.

Populated only if the tables were parsed successfully.

sfdp_used: bool

Whether SFDP tables are usable.

Will be True if SFDP information was used to successfully configure data transfer parameters, False otherwise.

property memory_size: int | None

Memory size.

Size of the data region in bytes, or None if SFDP data is not populated.

async power_up()

Power up the device.

Implemented using Opcode.ReleasePowerDown.

Important

A memory may not accept any commands besides this one if it is in a deep power-down state.

async power_down()

Power down the device.

Implemented using Opcode.PowerDown.

See power_up().

async jedec_id() tuple[int, int]

Read JEDEC IDs.

Implemented using Opcode.ReadJEDEC.

Returns (jedec_mfg_id, device_id).

async read_sfdp(address: int, length: int) memoryview

Read SFDP information.

Implemented using Opcode.ReadSFDP.

Returns length SFDP bytes starting at address, wrapping when reading past the end of the SFDP region.

async initialize(*, enable_dual: bool = False, enable_quad: bool = False)

Prepare device for data transfer.

Powers up the device and populates sfdp (if possible).

async read_data(address: int, length: int) bytes

Read data.

Implemented using the Command.ReadData, which is typically but not necessarily mapped to one of:

Returns length data bytes starting at address, wrapping when reading past the end of the data region.

Raises:

Memory25QError – If no read commands are configured.

async read_status_reg_1() StatusReg1

Read Status Register 1.

Implemented using Opcode.ReadStatusReg1.

async write_status_reg_1(value: StatusReg1)

Write Status Register 1.

Implemented using Opcode.WriteStatusReg1.

async write_enable()

Prefix for write operations.

All methods of this interface that perform write operations internally enable writes; only call this method explicitly when implementing new operations.

Raises:
async poll_busy()

Wait until operation is done.

Polls Status Register 1 until the BUSY bit is clear.

All methods of this interfaces that perform write operations internally wait for completion; only call this method explicitly when implementing new operations.

Raises:

Memory25QError – If the WREN bit in Status Register 1 is still set after the BUSY bit clears. This usually means that the preceding operation failed.

async set_quad_enabled(enabled: bool = True)

Enable or disable quad-SPI instructions.

Most devices power on and/or arrive from the factory configured with IO2/IO3 pins having the WP#/HOLD# function. Using quad-SPI instructions requires switching them to data I/O first. Since this can cause bus contention and disables hardware write protection (if any), this action must be taken explicitly.

Raises:
async erase_data_all()

Erase all data.

Implemented using Opcode.EraseChip.

Warning

This command can take a very long time (on the order of minutes) and does not read back data to verify success.

Tip

If the memory size is known (see memory_size), prefer using erase_data() with a range that covers the entire data region, which will report progress and verify that the data was successfully erased.

async erase_data(address: int, length: int)

Erase data and verify success.

Implemented using one of:

Erases memory range specified by address and length.

Raises:
  • ValueError – If either address or length is not divisible by the erase size of one of the available erase commands.

  • Memory25QError – If no erase commands are configured.

  • Memory25QVerifyError – If readback of erased data fails verification.

async program_data(address: int, data: bytes | bytearray | memoryview)

Program data and verify success.

Implemented using Command.ProgramData.

Programs data to memory range starting at address. The memory range must have been erased (using erase_data() or otherwise), otherwise the resulting contents will become a logical AND of existing data and written data, and will fail verification.

Raises:
async write_data(address: int, data: bytes | bytearray | memoryview)

Write data and verify success.

Implemented using erase_data() and program_data().

Writes data at address without any alignment constraints. If the written region is not already appropriately aligned, performs a read-modify-write cycle; in any case, data is first erased and then programmed.

Raises:
  • Memory25QError – If no erase or program commands are configured, or page size is not configured.

  • Memory25QVerifyError – If readback of erased or programmed data fails verification.

NOR command set reference

enum glasgow.arch.qspi.nor.Opcode(value)

Common (Q)SPI NOR Flash memory opcodes.

Member Type:

int

Valid values are as follows:

ResetDevice = Opcode.ResetDevice(FFh)
EnableReset = Opcode.EnableReset(66h)
PerformReset = Opcode.PerformReset(99h)
ReleasePowerDown = Opcode.ReleasePowerDown(ABh)
PowerDown = Opcode.PowerDown(B9h)
ReadSFDP = Opcode.ReadSFDP(5Ah)
ReadID = Opcode.ReadID(90h)
ReadJEDEC = Opcode.ReadJEDEC(9Fh)
Read = Opcode.Read(03h)
FastRead = Opcode.FastRead(0Bh)
FastReadDualOutput = Opcode.FastReadDualOutput(3Bh)
FastReadQuadOutput = Opcode.FastReadQuadOutput(6Bh)
FastReadDualInOut = Opcode.FastReadDualInOut(BBh)
FastReadQuadInOut = Opcode.FastReadQuadInOut(EBh)
PageProgram = Opcode.PageProgram(02h)
QuadInputPageProgram = Opcode.QuadInputPageProgram(32h)
EraseChip = Opcode.EraseChip(60h)
ReadStatusReg1 = Opcode.ReadStatusReg1(05h)
WriteStatusReg1 = Opcode.WriteStatusReg1(01h)
ReadStatusReg2 = Opcode.ReadStatusReg2(35h)
WriteStatusReg2 = Opcode.WriteStatusReg2(31h)
ReadStatusReg3 = Opcode.ReadStatusReg3(15h)
WriteStatusReg3 = Opcode.WriteStatusReg3(11h)
WriteDisable = Opcode.WriteDisable(04h)
WriteEnable = Opcode.WriteEnable(06h)
StatusRegWriteEnable = Opcode.StatusRegWriteEnable(50h)
Enter4ByteMode = Opcode.Enter4ByteMode(B7h)
Leave4ByteMode = Opcode.Leave4ByteMode(E9h)
enum glasgow.arch.qspi.nor.StatusReg1(value)

Status Register 1 flags.

Only includes six universally agreed upon LSBs. The two remaining MSBs have wildly varying function.

Member Type:

int

Valid values are as follows:

BUSY = StatusReg1.BUSY(00000001)

Operation in Progress bit

WREN = StatusReg1.WREN(00000010)

Write Enable Latch bit

BP0 = StatusReg1.BP0(00000100)

Block Protection bit 0

BP1 = StatusReg1.BP1(00001000)

Block Protection bit 1

BP2 = StatusReg1.BP2(00010000)

Block Protection bit 2

BP3 = StatusReg1.BP3(00100000)

Block Protection bit 3 or Top/Bottom Block Protection bit

enum glasgow.arch.qspi.nor.Command(value)

Abstract (Q)SPI NOR Flash memory commands.

While some commands directly correspond to a specific Opcode, other commands have multiple functionally equivalent opcodes with different throughput, or in some cases there is no globally uniform opcode assignment at all.

The mapping of Commands to Instructions (and therefore Opcodes) is maintained within a CommandSet.

Valid values are as follows:

PowerUp = Command.PowerUp
PowerDown = Command.PowerDown
ReadJEDEC = Command.ReadJEDEC
ReadSFDP = Command.ReadSFDP
ReadData = Command.ReadData
ProgramData = Command.ProgramData
EraseData4K = Command.EraseData4K
EraseData32K = Command.EraseData32K
EraseData64K = Command.EraseData64K
EraseDataAll = Command.EraseDataAll
WriteEnable = Command.WriteEnable
WriteDisable = Command.WriteDisable
ReadStatusReg1 = Command.ReadStatusReg1
ReadStatusReg2 = Command.ReadStatusReg2
ReadStatusReg3 = Command.ReadStatusReg3
WriteStatusRegs = Command.WriteStatusRegs
Enter4ByteMode = Command.Enter4ByteMode
Leave4ByteMode = Command.Leave4ByteMode

The Enum and its members also have the following methods:

classmethod all_erase_sizes() set[int]

Every known erase size.

classmethod erase_for_size(erase_size: int) Command

Select erase command for an erase_size-long region.

Raises:

ValueError – If erase_size is not in erase_erase_sizes().

class glasgow.arch.qspi.nor.CommandSet

Bases: BaseCommandSet[Command]

SPI NOR Flash command set.

When created, the command set only contains the opcodes that are implemented by every JESD216 compliant device. This makes identification and configuration possible, but data transfer cannot occur until the corresponding command set is configured via use_explicit() or use_jesd216().

Created with the following mappings:

use_explicit(*, address_bytes: int, page_size: int | None = None, opcode_erase_4k: Opcode | int | None = None, opcode_erase_32k: Opcode | int | None = None, opcode_erase_64k: Opcode | int | None = None, data_operation_prologue: Callable[[int], InstructionSequence] = lambda address: ...)

Use explicitly specified instructions.

Adds up to five mappings:

Configures the data_operation_prologue() method to execute data_operation_prologue.

This function is intended for “lowest common denominator” configuration in absence of SFDP data. If you need to configure a different or more complex command set, you should add the necessary mappings directly.

use_jesd216(sfdp: SFDPCollection, enable_dual: bool = False, enable_quad: bool = False)

Use instructions specified by sfdp.

To determine the highest throughput instruction, all the possible (1-x-y) modes are ordered first by y and then x.

Adds the following mappings:

Configures the data_operation_prologue() method to execute any appropriate mode switch or register access sequence for 4-byte addressing.

Raises:
  • ValueError – If JEDEC flash parameters table is not present.

  • ValueError – If JEDEC flash parameters table contains unsupported parameters for required features.

property min_erase_size: int | None

Smallest supported erase size.

data_operation_prologue(address: int) InstructionSequence

Compute prologue sequence for a data operation starting at address.

Returns an (address-specific) sequence of commands or write instructions that must be executed to prepare for any operation (reading, erasing, or programming) addressing the data region. A preceding call to use_explicit() or use_jesd216() may affect the specific sequence returned by this method.

Danger

If the steps above are not followed exactly, the outcome of any read, erase, or program operations may be completely unpredictable.