qspi-controller

CLI reference

glasgow run qspi-controller

Initiate transactions on the extended variant of the SPI bus with four I/O channels.

This applet can control a wide range of devices, primarily memories, that use multi-bit variants of the SPI bus. Electrically, they are all compatible, with the names indicating differences in protocol logic:

  • “SPI” uses COPI/CIPO for both commands and data;

  • “dual-SPI” uses COPI/CIPO for commands and IO0/IO1 for data;

  • “quad-SPI” uses COPI/CIPO for commands and IO0/IO1/IO2/IO3 for data;

  • “QPI” uses IO0/IO1/IO2/IO3 for both commands and data.

In this list, COPI and CIPO refer to IO0 and IO1 respectively used as fixed direction I/O. Note that vendors often make further distinction between modes, e.g. between “dual output SPI” and “dual I/O SPI”; refer to the vendor documentation for details.

The command line interface only initiates SPI mode transfers. Use the REPL for other modes.

usage: glasgow run qspi-controller [-h] [-V SPEC] [--sck PIN] [--io PINS]
                                   [--cs PIN] [-f FREQ]
                                   DATA [DATA ...]
data

hex bytes to exchange with the device in SPI mode

-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’)

--sck <pin>

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

--io <pins>

bind the applet I/O lines ‘copi’, ‘cipo’, ‘io2’, ‘io3’ to PINS (default: ‘A1,A2,A3,A4’, required)

--cs <pin>

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

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

set SCK frequency to FREQ kHz (default: 1000)

API reference

exception glasgow.applet.interface.qspi_controller.QSPIControllerError
class glasgow.applet.interface.qspi_controller.QSPIControllerInterface(logger: Logger, assembly: AbstractAssembly, *, cs: GlasgowPin, sck: GlasgowPin, io: GlasgowPin)

Pull-ups are enabled on all io pins.

property clock: ClockDivisor

SCK clock divisor.

select(index=0)

Perform a transaction.

Starting a transaction configures the first transfer within the transaction to assert the index-th chip select signal; ending a transaction deasserts the chip select signal. Methods write(), read(), exchange(), and dummy() may be called only while a transaction is active to transfer data on the bus.

For example, to read 16 bytes from an SPI NOR flash at address 0x001234 using the Fast Read (0Bh) command, use the following code:

async with iface.select():
    await iface.write([0x0B])
    await iface.write((0x001234).to_bytes(3, byteorder="big"))
    await iface.dummy(8)
    data = await iface.read(16)

An empty transaction (where the body does not call write(), read(), exchange(), or dummy()) is allowed and does not cause any chip select activity.

Raises:
async exchange(octets: bytes | bytearray | memoryview) memoryview

Exchange bytes.

Clock octets out via the IO0 (COPI) pin while clocking return value in via the IO1 (CIPO) pin. Unlike write() and read(), this method always uses x1 transfer rate.

Raises:

QSPIControllerError – If called outside of a transaction.

async write(octets: bytes | bytearray | memoryview, *, x: Literal[1, 2, 4] = 1)

Write bytes.

Clock octets out via x IOn pins, corresponding to single, dual, or quad SPI.

Raises:

QSPIControllerError – If called outside of a transaction.

async read(count: int, *, x: Literal[1, 2, 4] = 1) memoryview

Read bytes.

Clock count octets in via x IOn pins, corresponding to single, dual, or quad SPI.

Raises:

QSPIControllerError – If called outside of a transaction.

async dummy(count: int)

Clock dummy cycles.

Clock count dummy cycles. One octet corresponds to 8 dummy cycles in single SPI mode, 4 dummy cycles in dual SPI mode, and 2 dummy cycles in quad SPI mode. Some devices may require a non-multiple

Warning

The state of IOn pins is undefined during this operation.

Raises:

QSPIControllerError – If called outside of a transaction.

async delay_us(duration: int)

Delay operations.

Delays the following QSPI bus operations by duration microseconds.

async delay_ms(duration: int)

Delay operations.

Delays the following QSPI bus operations by duration milliseconds. Equivalent to delay_us(duration * 1000).

async synchronize()

Synchronization barrier.

Ensures that once this method returns, all previously submitted operations have completed.

async execute_cmd(instr: Instruction[int], *, address: int | None = None)

Execute a memory technology instruction without data transfer.

This is a low-level operation; in most cases, a memory technology specific interface should be used instead.

async execute_read(instr: Instruction, *, address: int | None = None, length: int | None = None) memoryview

Execute a memory technology instruction that reads data.

This is a low-level operation; in most cases, a memory technology specific interface should be used instead.

async execute_write(instr: Instruction, *, address: int | None = None, data: bytes | bytearray | memoryview)

Execute a memory technology instruction that writes data.

This is a low-level operation; in most cases, a memory technology specific interface should be used instead.

Memory command set reference

The building blocks below are used for implementing memory technology interfaces, such as memory-25q. They are typically not used on their own.

enum glasgow.arch.qspi.Direction(value)

Transfer direction.

Specified relative to the controller.

Valid values are as follows:

Read = Direction.Read
Write = Direction.Write
class glasgow.arch.qspi.CommandMode(opcode: Literal[0, 1, 2, 4], address: Literal[0, 1, 2, 4], data: Literal[0, 1, 2, 4])

Command mode.

Specifies gearing for the opcode, address, and data phase.

opcode: Literal[0, 1, 2, 4]

Gearing of the opcode phase.

address: Literal[0, 1, 2, 4]

Gearing of the address phase.

If 0, there is no address phase.

data: Literal[0, 1, 2, 4]

Gearing of the data phase.

If 0, there is no data phase.

property uses_dual: bool

Whether any phase uses dual SPI.

property uses_quad: bool

Whether any phase uses quad SPI.

class glasgow.arch.qspi.Instruction(*, opcode: OpcodeT, x_opcode: Literal[0, 1, 2, 4] = 1, x_address: Literal[0, 1, 2, 4] = 1, x_data: Literal[0, 1, 2, 4] = 1, address_octets: int = 0, mode_cycles: int = 0, dummy_cycles: int = 0, direction: Direction | None = None, data_octets: int | None = 0, data_repeats: bool = False)

Instruction format.

The Instruction describes the framing of each part (opcode, address, dummy, and data) of an abstract (Q)SPI instruction. The (x_opcode, x_address, x_data) tuple corresponds to the SFDP x-y-z terminology (e.g. 1-1-4 for quad output has x_opcode, x_address, x_data = 1, 1, 4). These “gearing” parameters indicate how many bits are transmitted per cycle, e.g. it takes 8 // x_opcode cycles to transmit the instruction opcode.

opcode: OpcodeT

Instruction opcode (one octet).

x_opcode: Literal[0, 1, 2, 4] = 1

Gearing of the opcode phase.

x_address: Literal[0, 1, 2, 4] = 1

Gearing of the address phase.

If 0, there is no address phase, and address_octets must also be 0.

x_data: Literal[0, 1, 2, 4] = 1

Gearing of the data phase.

If 0, there is no data phase, and data_octets must also be 0.

address_octets: int = 0

Length of the address phase, in octets.

Typically, between 0 and 4 inclusive, but this is not a hard requirement.

mode_cycles: int = 0

Length of the mode phase, in cycles.

Typically, between 0 and 4 inclusive, and an integer multiple of the number of cycles needed to exchange a byte in the address phase, but this is not a hard requirement.

dummy_cycles: int = 0

Length of the dummy phase, in cycles.

Typically, either 0 or 8 // x_address, but this is not guaranteed: certain devices require dummy phase of a length that is not an integer multiple of the number of cycles needed to exchange a byte in the address or data phase.

direction: Direction | None = None

Direction of the data phase.

Must be None if and only if x_data == 0.

data_octets: int | None = 0

Length of the data phase, in octets.

Typically, variable (indicated as None), but certain commands return a fixed amount of data. See also data_repeats.

data_repeats: bool = False

Whether the data output repeats.

If true, then the output repeats after transferring more than data_octets. For status registers, each repeat indicates up-to-date state at the moment of the transfer.

If false, data output might return all-zeroes, all-ones, or be undriven after transferring data_octets, and this condition should be avoided.

classmethod spi_1_0_0(opcode: OpcodeT)

SPI instruction with 1-0-0 gearing.

classmethod spi_1_1_0(opcode: OpcodeT, *, address_octets: int = 0)

SPI instruction with 1-1-0 gearing.

classmethod spi_1_0_1(opcode: OpcodeT, *, dummy_cycles: int = 0, direction: Direction | Literal['read', 'write'], data_octets: int | None = None, data_repeats: bool = False)

SPI instruction with 1-0-1 gearing.

classmethod spi_1_1_1(opcode: OpcodeT, *, address_octets: int = 0, dummy_cycles: int = 0, direction: Direction | Literal['read', 'write'], data_octets: int | None = None, data_repeats: bool = False)

SPI instruction with 1-1-1 gearing.

classmethod dspi_1_1_2(opcode: OpcodeT, *, address_octets: int = 0, mode_cycles: int = 0, dummy_cycles: int = 0, direction: Direction | Literal['read', 'write'], data_octets: int | None = None, data_repeats: bool = False)

Dual SPI instruction with 1-1-2 gearing.

classmethod dspi_1_2_2(opcode: OpcodeT, *, address_octets: int = 0, mode_cycles: int = 0, dummy_cycles: int = 0, direction: Direction | Literal['read', 'write'], data_octets: int | None = None, data_repeats: bool = False)

Dual SPI instruction with 1-2-2 gearing.

classmethod qspi_1_1_4(opcode: OpcodeT, *, address_octets: int = 0, mode_cycles: int = 0, dummy_cycles: int = 0, direction: Direction | Literal['read', 'write'], data_octets: int | None = None, data_repeats: bool = False)

Quad SPI instruction with 1-1-4 gearing.

classmethod qspi_1_4_4(opcode: OpcodeT, *, address_octets: int = 0, mode_cycles: int = 0, dummy_cycles: int = 0, direction: Direction | Literal['read', 'write'], data_octets: int | None = None, data_repeats: bool = False)

Quad SPI instruction with 1-4-4 gearing.

property mode: CommandMode

Command mode of this instruction.

class glasgow.arch.qspi.BaseCommandSet

Base class for command sets.

A command set is a mapping from an abstract command (e.g. “Erase 4K Sector”) to a specific concrete instruction (e.g. “20h, 1-0-0, 3 address bytes”). Maintaining this mapping is necessary because similar devices from different vendors, different variants of the same device from the same vendor, or the exact same device with different volatile configuration, may have the same abstract commands represented by different concrete opcodes.

Technology-specific command sets should inherit from this class and include methods that parse memory architecture self-description data (if available) or assist in manually configuring the instruction set; for example, see glasgow.arch.qspi.nor.CommandSet.

update(updates: dict[CommandT, Instruction])

Add multiple CommandT-Instruction mappings.

Overwrites mappings that already exist.

__setitem__(command: CommandT, instruction: Instruction)

Add a single CommandT-Instruction mapping.

Overwrites mappings that already exist.

__getitem__(command: CommandT) Instruction

Map a CommandT to a specific Instruction.

Raises:

KeyError – If the mapping does not exist.

__contains__(command: CommandT) bool

Check whether command is mapped.

__iter__() Iterator

Iterate through every mapped CommandT.