Name
Common SPI Memory Device Hardware Driver — Interface to a hardware device driver
Description
Note | |
---|---|
Application developers should not normally need to concern themselves with the internal API between this common layer and the H/W specific device drivers. The following information is primarily for H/W device driver developers. |
In most cases the platform (PLF) declares the individual flash
driver instances. The top-level descriptor as used with the
flash API (CYGPKG_IO_FLASH
) should
reference the flash API functions provided by this package
(cyg_devs_flash_common_funs
) as well as
provide a per-instance
cyg_flash_csm_context_t
structure
initialised with a reference to the instance-specific hardware
driver descriptor in the p_hwdriver
field.
The driver instance specific
cyg_spi_common_hwdriver_t
descriptor is
used to describe the hardware driver specific features to this
common layer.
Flash API Common H/W Driver (struct cyg_flash_dev).priv -> (cyg_flash_csm_context_t).p_hwdriver -> (cyg_spi_common_hwdriver_t)
All architecture/platform/HAL eCos
x
SPI device drivers using the
CYGPKG_DEVS_FLASH_SPI_COMMON
package must
implement a standard interface defined by the header
<cyg/io/flash_csm_dev.h>
.
The interface descriptor structure includes a private pointer
for the H/W driver context, a “features” set and a set of
function pointers for various operations: initialization,
memory operation, memory-mapped access and general
configuration.
struct cyg_spi_common_hwdriver { // H/W driver private (opaque) context: const void *p_io; // H/W driver specific I/O information // H/W driver feature set descriptor: const cyg_flash_csm_features_t * const p_features; // Common H/W driver API cyg_spi_common_hwdriver_init *init; // initialisation function cyg_spi_common_hwdriver_op *op; // single command operation function cyg_spi_common_hwdriver_mm_start *mm_start; // memory-mapped start/enable cyg_spi_common_hwdriver_mm_stop *mm_stop; // memory-mapped stop/disable cyg_spi_common_hwdriver_config *config; // get/set config+control };
Hardware Driver Features
The p_features
structure provides fixed
information used to describe to this common layer the features
and settings of the H/W driver instance:
typedef struct cyg_flash_csm_features { cyg_uint32 avail; // bitmask of H/W driver available features cyg_uint32 mmaddr; // if MM capable, base address for MM region // Since early JESD216 standards do not provide a mechanism for the device // to report its maximum frequency we allow the platform/variant HAL to be // configured with maximum rates. cyg_uint32 max_sdr; // if non-zero platform/variant HAL provided maximum SDR baudrate cyg_uint32 max_ddr; // if non-zero platform/variant HAL provided maximum DDR baudrate cyg_uint32 nmodes; // number of modes present in modes vector const cyg_uint32 * const p_modes; // pointer to vector of FLASH_CSM_OP_MODE_MASK // covered bitmasks for available modes } cyg_flash_csm_features_t;
The p_features
structure allows the H/W
driver to report the SPI modes capable by the device
driver. This can, for example, be used in conjunction with
information gathered from the device using SFDP to select the
common subset of supported access methods: e.g. Quad (QSPI) vs
Octal (OSPI).
The current set of available feature flags indicating H/W driver support is:
Flag | Description |
---|---|
FLASH_CSM_FEATURE_ADDR3 | 3-byte addressing supported |
FLASH_CSM_FEATURE_ADDR4 | 4-byte addressing supported |
FLASH_CSM_FEATURE_ADDR5 | 5-byte addressing supported |
FLASH_CSM_FEATURE_MODEBITS | Driver supports writing mode bits (sometimes referred to as OPT or Alternate) bits |
FLASH_CSM_FEATURE_CMD8 | 8-bit commands supported |
FLASH_CSM_FEATURE_CMD16 | 16-bit commands supported |
FLASH_CSM_FEATURE_MM | Memory mapped access supported |
FLASH_CSM_FEATURE_MM_XIP | eXecute-In-Place supported |
FLASH_CSM_FEATURE_MM_SM | Driver can memory-map serial-memory as well as data-memory |
FLASH_CSM_FEATURE_MM_SM_RA | Random Access supported for memory mapped serial-memory |
FLASH_CSM_FEATURE_CR | Continuous Read supported |
FLASH_CSM_FEATURE_DS | Data Strobe signalling available |
The p_modes
pointer references the
nmodes
deep vector of access modes
supported, encoded using the same OP bitmask encoding as used
for the individual memory operations. For example an Octal
(OSPI) capable driver might define:
// List of possible modes for this driver: static const cyg_uint32 cyg_hwdriver_modes[] = { // 8-line Octal (OSPI) FLASH_CSM_OP_MODE_1S1S8S, // 0 FLASH_CSM_OP_MODE_1S8S8S, // 1 FLASH_CSM_OP_MODE_8S8S8S, // 2 FLASH_CSM_OP_MODE_8D8D8D, // 3 // 4-line QSPI FLASH_CSM_OP_MODE_1S1S4S, // 4 FLASH_CSM_OP_MODE_1S4S4S, // 5 FLASH_CSM_OP_MODE_4S4S4S, // 6 FLASH_CSM_OP_MODE_4S4D4D, // 7 // 2-line FLASH_CSM_OP_MODE_1S1S2S, // 8 FLASH_CSM_OP_MODE_1S2S2S, // 9 FLASH_CSM_OP_MODE_2S2S2S, // 10 // 1-line FLASH_CSM_OP_MODE_1S1S1S, // 11 };
Referencing the vector in its feature descriptor:
static const cyg_flash_csm_features_t hwdriver1_features = { ..elided.. .nmodes = NUMOF_(cyg_hwdriver_modes), .p_modes = &cyg_hwdriver_modes[0] };
Hardware Driver-Specific Structure
The p_io
pointer allows the H/W device
driver to hold per-instance private data as needed for the
operation of the driver.
Normally the driver context would be split into read-only, constant, data that could be held in the code with only the truly dynamic context occupying RAM space. See the Hardware Example below for an outline.
Functions
The H/W driver provides its common driver API via the
cyg_spi_common_hwdriver_t
descriptor. For
the function pointers the NULL
value can
be used to indicate that the relevant support is not
required. Only the op
function
must be provided, though it
is unlikely that the driver would not require a
init
function to be called at startup.
Initialization
typedef int cyg_spi_common_hwdriver_init(const void *p_info, cyg_bool do_reset, cyg_uint32 baudrate);
This function allows the H/W driver to complete the run-time initialisation of any dynamic context needed, along with setting up the controller in preperation for the first operation.
This can consist of attaching any ISR/DSR or DMA handlers needed, setting up I/O pin configurations (if the platform/architecture uses pin multiplexing), etc.
The reset
parameter indicates whether the
upper layer requires the hardware to be “reset”
back to a known state.
The baudrate
is the clock frequency that
will be used for the initial operations. Normally (for SFDP
devices) this will be 50MHz.
The function call should return standard flash API status
code. e.g. CYG_FLASH_ERR_OK
to indicate
success.
Memory Operation
typedef cyg_bool cyg_spi_common_hwdriver_op(const void *p_info, const cyg_flash_csm_op_t *p_op);
This is the core operation of the H/W driver interface. The
referenced cyg_flash_csm_op_t
pointer
p_op
describes the basic operation to be
performed on the serial memory device.
The p_info
pointer is a reference to the
H/W driver specific context supplied when declaring the
flash descriptor.
The function should return a simple boolean
true
success indication, or
false
if an error occurred.
The <cyg/io/flash_csm_dev.h>
header defines the referenced p_op
structure:
typedef struct cyg_flash_csm_op { cyg_uint32 mode; // Encoded bus information and control cyg_uint32 cmdflags; // Instruction CMD and extra control flags cyg_uint32 address; // Device relative address cyg_uint32 opt; // Upto 32-bits of OPT (mode-bits; alternate) data cyg_uint32 timeout; // Millisecond timeout for operation cyg_uint32 nbytes; // Non-zero is number of valid bytes from p_buff cyg_uint8 *p_buff; // Pointer to data buffer for transfer } cyg_flash_csm_op_t;
The mode
and cmdflags
fields define whether the other fields are
used/required. For example, a simple device operation to
enable the write-latch will not normally require any data to
be written (or read) and so the nbytes
and p_buff
fields would not be referenced
for that operation.
The <cyg/io/flash_csm_dev.h>
header is the definitive source for the bitmask use for the
mode
and cmdflags
fields and should be examined by the developer writing a H/W
driver. The header contains helper manifests and macros to
aid the decoding of the fields.
mode
The mode
bitmask encodes the
operation. It contains some single-bit boolean flags as
well as some multi-bit values with specific encodings.
The value 0x00000000
(CSM_OP_INVALID
) is never a valid
descriptor since we should always have at least one of the
instruction, address, opt or data phases defined.
An operation compromises one or more phases in the order:
Instruction, Address, Mode and Data. The
mode
bitmask encodes which phases are
enabled, and hence their associated bitmask flags and
values are valid, as well as some general operation
control flags.
Instruction phase
If an instruction phase is required then the
FLASH_CSM_OP_IP
mask will have the valueFLASH_CSM_OP_IP_ACTIVE
, otherwise the flag will have the valueFLASH_CSM_OP_IP_NONE
.If
FLASH_CSM_OP_IP_ACTIVE
then theFLASH_CSM_OP_IW_MASK
bits encode the number of lines to be used for the instruction phase:Value Description FLASH_CSM_OP_IW_LINE1
1-line (SPI) FLASH_CSM_OP_IW_LINE2
2-lines (Dual) FLASH_CSM_OP_IW_LINE4
4-lines (Quad) FLASH_CSM_OP_IW_LINE8
8-lines (Octal) If
FLASH_CSM_OP_IP_ACTIVE
then thep_op
fieldcmdflags
holds the command instruction.The command length is encoded by the
FLASH_CSM_OP_CL
mask. The value should beFLASH_CSM_OP_CL_8BIT
for 8-bit commands, andFLASH_CSM_OP_CL_16BIT
for 16-bit commands.If
FLASH_CSM_OP_IP_ACTIVE
then theFLASH_CSM_OP_IP_IR
mask encodes whether the instruction phase is Single-Data-Rate (FLASH_CSM_OP_IR_SDR
) or Dual-Data-Rate (FLASH_CSM_OP_IR_DDR
).Address phase
If an address phase is required then the
FLASH_CSM_OP_AP
mask will have the valueFLASH_CSM_OP_AP_ACTIVE
, otherwise the flag will have the valueFLASH_CSM_OP_AP_NONE
.If
FLASH_CSM_OP_AP_ACTIVE
then theFLASH_CSM_OP_AB_MASK
bits encode the number of bytes used for an address:Value Description FLASH_CSM_OP_AB_3BYTE
3-byte (24-bit) address FLASH_CSM_OP_AB_4BYTE
4-byte (32-bit) address FLASH_CSM_OP_AB_5BYTE
5-byte (40-bit) address (not currently supported) If
FLASH_CSM_OP_AP_ACTIVE
then theFLASH_CSM_OP_AW_MASK
bits encode the number of lines to be used for the address phase:Value Description FLASH_CSM_OP_AW_LINE1
1-line (SPI) FLASH_CSM_OP_AW_LINE2
2-lines (Dual) FLASH_CSM_OP_AW_LINE4
4-lines (Quad) FLASH_CSM_OP_AW_LINE8
8-lines (Octal) If
FLASH_CSM_OP_AP_ACTIVE
then thep_op
fieldaddress
should define the device relative address for the operation.If
FLASH_CSM_OP_AP_ACTIVE
then theFLASH_CSM_OP_AR
mask encodes whether the address phase is Single-Data-Rate (FLASH_CSM_OP_AR_SDR
) or Dual-Data-Rate (FLASH_CSM_OP_AR_DDR
).Mode phase
Different hardware implementations support OPT/Alternate bytes of differing sizes and limitations. The JESD216D.01 standard describes these as “mode bits” and are sent after the address phase.
If a mode phase is required then the
FLASH_CSM_OP_MP
mask will have the valueFLASH_CSM_OP_MP_ACTIVE
, otherwise the flag will have the valueFLASH_CSM_OP_MP_NONE
.If
FLASH_CSM_OP_MP_ACTIVE
then theFLASH_CSM_OP_MB_MASK
bits encode the number of bits (range 1..32). The mode bits are signalled on the same number of SPI lines as the address phase.If
FLASH_CSM_OP_MP_ACTIVE
then thep_op
fieldopt
holds the mode bits value.If
FLASH_CSM_OP_MP_ACTIVE
then theFLASH_CSM_OP_MR
mask encodes whether the mode bits phase is Single-Data-Rate (FLASH_CSM_OP_MR_SDR
) or Dual-Data-Rate (FLASH_CSM_OP_MR_DDR
).Data phase
If an data phase is required then the
FLASH_CSM_OP_DP
mask will have the valueFLASH_CSM_OP_DP_ACTIVE
, otherwise the flag will have the valueFLASH_CSM_OP_DP_NONE
.If
FLASH_CSM_OP_DP_ACTIVE
then theFLASH_CSM_OP_DW_MASK
bits encode the number of lines to be used for the data phase:Value Description FLASH_CSM_OP_DW_LINE1
1-line (SPI) FLASH_CSM_OP_DW_LINE2
2-lines (Dual) FLASH_CSM_OP_DW_LINE4
4-lines (Quad) FLASH_CSM_OP_DW_LINE8
8-lines (Octal) If
FLASH_CSM_OP_DP_ACTIVE
then thep_op
fieldnbytes
should define the number of valid memory bytes referenced by thep_buff
pointer.If
FLASH_CSM_OP_DP_ACTIVE
then theFLASH_CSM_OP_DR
mask encodes whether the address phase is Single-Data-Rate (FLASH_CSM_OP_DR_SDR
) or Dual-Data-Rate (FLASH_CSM_OP_DR_DDR
).
The mode
field also encodes other
information that may be required by the H/W driver.
The FLASH_CSM_OP_SD
mask has the value
FLASH_CSM_OP_SD_DATA
when the operation
is accessing the data-memory of the flash device, and the
value FLASH_CSM_OP_SD_DEVICE
if
accessing device-internal “memory” (e.g. SFDP
tables, unique IDs, etc.).
The FLASH_CSM_OP_TD
mask encodes the
Transfer Direction, whether the operation is a read
(FLASH_CSM_OP_TD_READ
) or a write
(FLASH_CSM_OP_TD_WRITE
).
The FLASH_CSM_OP_DA
indicates whether
the transfer should be undertaken by the operation call
directly (FLASH_CSM_OP_DA_XFER
) or
should be deferred for subsequent memory-mapped access
(FLASH_CSM_OP_DA_DEFER
).
When reading the FLASH_CSM_OP_CR
mask
indicates that the device is configured for Continuous
Read. If FLASH_CSM_OP_CR_NONE
then
continuous read is not configured. If
FLASH_CSM_OP_CR_ACTIVE
then it
indicates that the controller can setup access for
continuous read mode.
The FLASH_CSM_OP_DS
encodes whether the
operation requires the Data Strobe signal
(FLASH_CSM_OP_DS_ACTIVE
) or the signal
is not required (FLASH_CSM_OP_DS_NONE
).
cmdflags
When required by the mode
bitmask the
cmdflags
field encodes the command code
(8- or 16-bit), the number of Delay Cycles and whether the
timeout
is valid.
timeout
The timeout
value is only valid if the
cmdflag
flag
FLASH_CSM_CMD_TO_VALID
is set,
otherwise the field is ignored.
The timeout
field is a millisecond
operation timeout, or one of the special values:
CYG_FLASH_CSM_TO_NOWAIT
or
CYG_FLASH_CSM_TO_INFINITY
. The
...NOWAIT
value is for an immediate,
polled, return without waiting, operation. The
...INFINITY
value is for when the
operation should block indefinately until completion
(success or error indicated).
Memory Mapped
typedef cyg_bool cyg_spi_common_hwdriver_mm_start(const void *p_info);
typedef cyg_bool cyg_spi_common_hwdriver_mm_stop(const void *p_info);
The optional mm_start
and
mm_stop
functions are used to notify the
H/W driver when memory-mapped state is being changed.
Since most devices cannot continue to provide memory-mapped access whilst being erased or programmed the common driver layer allows the H/W driver to perform any controller operations needed to ensure the hardware is in the correct mode. For example, this may include changing the cached/uncached state for the memory covered by flash device, or require specific controller operations to abort any active memory-mapped pre-fetching that may be occurring.
Configuration
typedef cyg_bool cyg_spi_common_hwdriver_config(const void *p_info, cyg_uint32 key, void *p_buff, cyg_uint32 *p_len);
The optional H/W driver supplied config
function is used with specific configuration key values to
interact with the H/W driver:
-
CYG_CSM_CFG_SET_BAUDRATE
,CYG_CSM_CFG_GET_BAUDRATE
Used by the common layer to control the clock frequency (normally named
SCK
) of the H/W driver instance. Themax_sdr
andmax_ddr
fields of the H/W driver supplied(cyg_flash_csm_features_t)
descriptor allow the H/W driver to limit the upper frequency to that supported by the specific controller, with the common layer device support being used for the actual flash memory device maximum rates possible.The frequency setting is a simple 32-bit unsigned integer (e.g.
cyg_uint32
).-
CYG_CSM_CFG_SET_MEMTYPE
,CYG_CSM_CFG_GET_MEMTYPE
If required, for OCTOSPI devices, these options provide common layer control of the memory type as used by the H/W driver.
The memory type setting is currently a simple 32-bit unsigned integer (e.g.
cyg_uint32
):-
CYG_CSM_MEMTYPE_DTR_D0D1
- This option indicates Micron style byte-ordering.
-
CYG_CSM_MEMTYPE_DTR_D1D0
- This option indicates Macronix style byte-ordering.
-
CYG_CSM_MEMTYPE_STANDARD
- Indicates normal SPI access.
-
CYG_CSM_MEMTYPE_UNDEFINED
- This value indicates that no specific memory type has been set.
-
-
CYG_CSM_CFG_SET_DATASTROBE
,CYG_CSM_CFG_GET_DATASTROBE
For configurarions that use a data strobe signal (
DQS
) these config options provide the mechanism for informing the H/W driver of the device data strobe timing.The data strobe settings are a simple 32-bit unsigned integer (e.g.
cyg_uint32
):-
CYG_CSM_DATASTROBE_START
- Start of first data bit aligned with the first rising edge of DQS.
-
CYG_CSM_DATASTROBE_MIDDLE
- First rising edge of DQS in the middle of the first data bit.
-
CYG_CSM_DATASTROBE_HALF
- First rising edge of DQS is half a clock cycle before the start of the first data bit.
-
CYG_CSM_DATASTROBE_UNDEFINED
- This setting is used to indicate that the data strobe timing is not defined or unknown.
-
Example
The following section provides an example skeleton of how a H/W driver instance can be declared.
Since device drivers normally have a requirement for some
fixed (constant) information describing the hardware
configuration as well as possibly some dynamic state to
hold run-time information (e.g. ISR or DSR state) the example
below shows a simple framework. The example
hw_driver_ctx_t
structure used for the
dynamic context and the hw_driver_io_t
structure holding the constant/fixed information are specific
to the H/W driver implementation and the underlying H/W
controller requirements.
For our example instance in the H/W driver source we can provide a RAM based private context for the dynamic state:
static hw_driver_ctx_t hw_dynamic1 = {};
This can then be referenced from a constant (normally placed in read-only memory by the linker) structure with the fixed information for the driver along with a pointer to the RAM based dynamic run-time context structure:
static const hw_driver_io_t hw_context1 { .p_ctx = &hw_driver_dynamic1, // dynamic H/W driver state // The fixed information needed by the H/W driver .intr_vec = <HAL_INTERRUPT_NUMBER> .pin_sclk = <HAL/PLF_SCLK_PIN_DESCRIPTOR> ..elided.. };
The driver can then provide a per-instance common H/W driver API structure referencing the H/W driver context and the features and functions provided by the driver:
const cyg_spi_common_hwdriver_t cyg_dev_flash_csm_example1= { .p_io = &hw_context1, .p_features = &hw_features, .init = hw_init, .op = hw_op, .mm_start = NULL, // op interface sufficient for this driver .mm_stop = NULL, // op interface sufficient for this driver .config = hw_config };
With the H/W driver providing the
cyg_spi_common_hwdriver_t
structure the
platform specific sources would then reference the H/W driver
instance when declaring the flash object in the platform/HAL
specific source file:
static struct cyg_flash_block_info cyg_flash_common_block_info; static cyg_flash_csm_context_t cyg_flash_common_ctx = { .p_hwdriver = &cyg_dev_flash_csm_example1, // reference H/W instance }; CYG_FLASH_DRIVER(cyg_common_device, &cyg_devs_flash_common_funs, 0, <BASE_ADDR>, // start (normally same as p_features.mmaddr field <BASE_ADDR>, // end (depends on detected device, so filled in at run-time) 1, // number of flash block info structures &cyg_flash_common_block_info, &cyg_flash_common_ctx);
After successful flash device initialisation the
struct cyg_flash_dev
field
end
will hold the end address for the flash
area. The start and end addresses are used by the flash API to
select the relevant device descriptor when accessing a flashg
area.
When called, the common layer code can use the
p_hwdriver
field to access the specific H/W
instance used to access the actual flash device for the area,
with the H/W driver subsequently de-referencing its own
structures to access the fixed and dynamic portions of its
context.
2024-03-18 | Open Publication License |