|
|
Programmed I/O (PIO)
4.1 Overview
Programmed I/O (PIO) refers to data transfers initiated by a CPU under driver software control to access registers or memory on a device. This is contrasted with Direct Memory Access (DMA), which involves transfers initiated by a device to access system memory. On some hardware platforms PIO is handled via normal memory loads and stores ("memory-mapped I/O"); on others it requires special I/O instructions. UDI hides this difference from drivers.
In UDI, Programmed I/O (PIO) operations are performed through environment service calls coded as function calls rather than by direct memory references or I/O instructions in the drivers. This abstraction is necessary for driver portability across different machine architectures for (at least) the following reasons:
- Different machines support different endian architectures and some provide hardware-assisted endianness swapping.
- Some machine architectures may restrict direct access.
- Some bus bridges are non-transparent and require software intervention (via a bus bridge driver) for each PIO access.
To reduce the overhead associated with using function calls, UDI allows multiple PIO transactions to be performed with a single function call.
PIO access properties are encapsulated with a PIO handle (udi_pio_handle_t). A PIO handle is an opaque data type containing the addressing, data translation and access constraint information required to access a device or memory address in a particular address space. For example, for PCI devices the address space could be memory space, I/O space or configuration space. Information specifying endianness, required atomicity, and data ordering constraints is also contained in the PIO handle.
Also associated with each PIO handle is a transaction list, that specifies the PIO operations to be invoked when that handle is passed to udi_pio_trans.
For each transaction that accesses the device, a PIO offset is specified, which indicates the offset into the space referenced by a PIO handle at which an I/O operation is to occur. The space covered by a particular PIO handle is contiguous with respect to the addresses seen by the device.
Synchronization among different PIO transaction lists is defined by the serialization_domain argument to the PIO mapping call. The execution of a PIO transaction list is serialized with respect to the execution of all other PIO transaction lists mapped to the same device instance and serialization domain; i.e., for a given device and serialization domain, at most one thread of execution will be active executing a corresponding transaction list and each such transaction list will execute to completion before another transaction list for this serialization domain begins execution.
Note, however, that PIO trans lists do not have any serialization guarantees with respect to region execution. A sequence of PIO transactions, encapsulated in a PIO transaction list, is passed to the environment via udi_pio_trans and may be performed outside the context of the caller's region. As a result, the PIO sequence may be executed in parallel to code executing in the region.
There are no ordering guarantees with respect to the processing of transactions lists for separate udi_pio_trans calls except that calls made from the same region for the same serialization domain will be processed in FIFO order. Additionally, the callbacks for these udi_pio_trans calls are also called in FIFO order for that serialization domain, although the callback processing is not necessarily consecutive with the processing of the transaction list.
Synchronization and ordering requirements for PIO operations, with respect to system operations such as cache or I/O buffer flushes, are device and driver specific. Drivers control when such synchronization and ordering operations are performed through the use of UDI_PIO_SYNC and UDI_PIO_BARRIER transactions. Additional data ordering and synchronization requirements may be associated with a PIO handle via the pio_attributes parameter to udi_pio_map.
4.2 PIO Handle Allocation and Initialization
The following functions are used to allocate and initialize handles to PIO-accessible memory. The udi_pio_map function allocates a handle for a range of device memory/registers. The udi_pio_unmap function deallocates the PIO handle and any associated resources when the driver no longer needs them. The udi_pio_atomic_sizes function returns a bitmask of transaction sizes that can be handled atomically.
Additionally, the driver must register a special PIO handle with the environment via the udi_pio_abort_sequence function, which the environment can use when "killing" a faulting region to stop the corresponding device from initiating further actions.
NAME udi_pio_handle_t
PIO handle type
#include <udi.h>typedef <HANDLE> udi_pio_handle_t; /* Null handle value for udi_pio_handle_t */#define UDI_NULL_PIO_HANDLE <NULL_HANDLE>description The PIO handle type, udi_pio_handle_t, holds an opaque handle that refers to an environment object which contains addressing, data translation and access information, as well as a PIO transaction list.
PIO handles are transferable between regions.
Drivers will often have multiple PIO handles for different address spaces on the same device (e.g. one handle for configuration space, another for I/O registers, etc.). Drivers might also have multiple PIO handles for the same address space if different translation requirements or constraints exist within that address space, or if different types of operations are to be used at different times.
NAME udi_pio_map
Map device memory/registers for access
#include <udi.h>void udi_pio_map ( udi_pio_map_call_t *callback, udi_cb_t *gcb, udi_ubit32_t regset_idx, udi_ubit32_t base_offset, udi_ubit32_t length, udi_pio_trans_t *trans_list, udi_ubit16_t list_length, udi_ubit16_t pio_attributes, udi_ubit32_t pace, udi_index_t serialization_domain ); typedef void udi_pio_map_call_t ( udi_cb_t *gcb, udi_pio_handle_t new_pio_handle ); /* Values for pio_attributes */#define UDI_PIO_STRICTORDER (1U<<0) #define UDI_PIO_UNORDERED_OK (1U<<1) #define UDI_PIO_MERGING_OK (1U<<2) #define UDI_PIO_LOADCACHING_OK (1U<<3) #define UDI_PIO_STORECACHING_OK (1U<<4) #define UDI_PIO_BIG_ENDIAN (1U<<5) #define UDI_PIO_LITTLE_ENDIAN (1U<<6) #define UDI_PIO_NEVERSWAP (1U<<7) #define UDI_PIO_UNALIGNED (1U<<8)ARGUMENTS callback, gcb are standard arguments described in the "Asynchronous Service Calls" section of "Standard Calling Sequences" in the UDI Core Specification.
regset_idx is the index to the specified register set within a given address space. The definition of regset_idx is bus and device dependent, and must be determined by the driver via the bus_type instance attribute (see Chapter 15, "Instance Attribute Management" in the UDI Core Specification) in conjunction with the corresponding UDI bus bindings (see "Section 3: Bus Bindings"). For example, if bus_type is "pci" then the settings would be based on the definitions in the UDI PCI Bus Binding Specification.
base_offset is the offset into the selected register address space at which this mapping is to start. This offset must be a multiple of all transaction sizes used to access device addresses through this handle, unless UDI_PIO_UNALIGNED is set in pio_attributes.
length is the length to be mapped in bytes.
trans_list is a list of one or more PIO transactions to apply to this device when using udi_pio_trans with this PIO handle. The memory pointed to by trans_list must be in a module-global (and thus read-only) variable.
list_length is the number of elements in the trans_list list.
pio_attributes are attribute flags that specify the data translation and ordering requirements of accesses to the device memory.
Attribute flags specifying the data translation requirements of the device are mutually exclusive. At most one must be used in a given handle allocation. The default is UDI_PIO_NEVERSWAP if no data translation flags are specified.
The following data translation flags are defined:
UDI_PIO_BIG_ENDIAN - access data in big endian format.
UDI_PIO_LITTLE_ENDIAN - access data in little endian format.
UDI_PIO_NEVERSWAP - access data with no byte swapping (appropriate for character data). Device transactions greater than one byte in size are illegal if this flag is set.
Attribute flags specifying the data ordering requirements for the device may be used in combination. With the exception of UDI_PIO_STRICTORDER these values are advisory, not mandatory. For example, data can be ordered without being merged or cached, even though a driver permits unordered, merged and cached operation. Strict ordering may be required for certain I/O operations but can be very costly on high performance computers. For unordered execution, merging or caching, the UDI_PIO_SYNC operation (see udi_pio_trans) can provide more specific and efficient synchronization.
The default is UDI_PIO_STRICTORDER if no ordering flags are specified. If pace is non-zero, pio_attributes must not include any data ordering flags other than UDI_PIO_STRICTORDER.
The following data ordering flags are defined:
UDI_PIO_STRICTORDER - the CPU must issue the references in order, as the programmer specified. Strict ordering is the default if no other data ordering flags are set. UDI_PIO_STRICTORDER must not be combined with any other data ordering flags.
UDI_PIO_UNORDERED_OK - the CPU may reorder both load and store references to the mapped area.
UDI_PIO_MERGING_OK - merging and batching: the CPU may merge individual stores to consecutive locations (for example, turn two consecutive byte stores into one halfword store), and it may batch individual loads (for example, turn two consecutive byte loads into one halfword load). If UDI_PIO_MERGING_OK is set, UDI_PIO_UNORDERED_OK is treated as if it were set, even if it isn't actually set.
UDI_PIO_LOADCACHING_OK - load caching: the CPU may cache the data it fetches and reuse it until another store occurs. The default is to fetch new data on every load. If UDI_PIO_LOADCACHING_OK is set, UDI_PIO_MERGING_OK is treated as if it were set, even if it isn't actually set.
UDI_PIO_STORECACHING_OK - store caching: the CPU may keep the data in the cache and push it to the device (perhaps with other data) at a later time. The default is to push data immediately to the device. If UDI_PIO_STORECACHING_OK is set, UDI_PIO_LOADCACHING_OK is treated as if it were set, even if it isn't actually set.
UDI_PIO_UNALIGNED - unaligned accesses allowed: if this flag is set, there are no restrictions on base_offset and on PIO offsets for individual transactions; otherwise, both of these must be multiples of all transaction sizes used in trans_list. If this flag is set, none of the transactions using this handle are guaranteed to be atomic.
pace is the PIO pacing time, in microseconds, for this handle. The environment will guarantee that any device access that occurs using this PIO handle will be followed by at least pace microseconds before another access occurs to the same device register set (via any handle). If non-zero, the UDI_PIO_STRICTORDER attribute must be specified.
PIO transactions may be posted/cached for future completion, so the apparent execution time of PIO operation calls may vary from the driver's perspective, but there will be at least pace microseconds between them from the hardware's perspective1.
serialization_domain is a numeric value indicating the serialization domain for this PIO handle. Transaction lists for a given device with the same serialization domain are serialized with respect to each other, and will further be executed in FIFO order with respect to udi_pio_trans calls from a given region. See the ordering definitions on page 4-1 for additional details.
new_pio_handle is an opaque data type containing the addressing, endian translation, and access constraint information required to access the associated device memory.
DESCRIPTION udi_pio_map initializes a handle through which a driver instance may access its device using PIO access routines. The regset_idx, base_offset, and length arguments select the range of device memory to be made accessible; the pio_attributes argument specifies the ways in which it may be accessed.
Note that the driver is allowed to map the same piece of device memory multiple times for different access requirements. For example, if the driver wants to do both strongly-ordered and weakly ordered accesses to a given register set it can map the register set once with UDI_PIO_STRICTORDER (strongly ordered accesses) and once with UDI_PIO_UNORDERED_OK (weakly ordered accesses). The driver would then use the "strong" PIO handle when it wants to do a strongly ordered access, and the "weak" handle otherwise. Similarly, multiple handles to a given register set could be used for zero pacing vs. various nonzero pacing values, and for other variations in attributes, offset, length, or trans list.
REFERENCES udi_pio_handle_t, udi_pio_unmap, udi_pio_trans
NAME udi_pio_unmap
Unmap a PIO handle and free associated resources
#include <udi.h>void udi_pio_unmap ( udi_pio_handle_t pio_handle );ARGUMENTS pio_handle is the PIO handle to be freed.
DESCRIPTION This service call unmaps a PIO access handle, freeing any associated resources. Upon return pio_handle is no longer valid.
If pio_handle is equal to UDI_NULL_PIO_HANDLE, either explicitly or implicitly (zeroed by initial value or by using udi_memset), this function acts as a no-op. Otherwise, pio_handle must have been allocated by udi_pio_map.
REFERENCES udi_pio_handle_t, udi_pio_map
NAME udi_pio_atomic_sizes
Retrieve supported PIO operation atomicity
#include <udi.h>udi_ubit32_t udi_pio_atomic_sizes ( udi_pio_handle_t pio_handle );ARGUMENTS pio_handle is a PIO handle previously acquired via udi_pio_map.
DESCRIPTION This routine retrieves an encoding of the PIO atomic transaction sizes (see atomic transaction in the Glossary) for which atomicity is supported when doing PIO accesses through pio_handle. The set of supported atomic sizes is platform and bus specific, and may also be dependent on device address space, and register set.
The driver will typically use this routine to verify up front that the atomic sizes it requires are supported, and then fail its initialization if they're not supported. If such a check is not done and the platform does not support the device's atomicity requirements then the results of corresponding PIO accesses will be indeterminate.
Atomicity is not guaranteed, regardless of transaction size, for any handle mapped with the UDI_PIO_UNALIGNED flag, or when using udi_pio_probe.
RETURN VALUES A bit encoding of the PIO operation sizes for which atomicity is supported is returned. Each bit position corresponds to a power of two byte PIO operation size. If a bit is set PIO operations of the corresponding size are supported atomically through the bus hierarchy to the device. For example, a configuration supporting atomic PIO operations of 1, 2, and 4 bytes would return a binary 0111.
NAME udi_pio_abort_sequence
Register a PIO abort sequence
#include <udi.h>void udi_pio_abort_sequence ( udi_pio_handle_t pio_handle, udi_size_t scratch_requirement );ARGUMENTS pio_handle is a special PIO handle, previously acquired from udi_pio_map, whose associated trans list is designed to stop the associated device from initiating (mastering) transactions on its bus in case the driver fails.
scratch_requirement is the number of bytes of scratch space needed during processing of the associated trans list.
DESCRIPTION When a driver does something illegal, causes a system fault, or otherwise misbehaves, the UDI environment can "kill" the corresponding driver region. However, since the faulting driver region can no longer be trusted to execute correctly, the environment needs some way to stop the corresponding device from proceeding without having to re-execute the faulting region. udi_pio_abort_sequence provides the environment a PIO handle (with an associated trans_list) which it can use for this purpose.
The driver calls udi_pio_abort_sequence to register pio_handle with the environment. pio_handle must be mapped with a trans list that can be used to stop the corresponding device from initiating (mastering) transactions on the bus, including the generation of DMA transactions and interrupts. The PIO sequence specified by the associated trans list does not need to flush data or preserve device state, and should do the simplest sequence possible to stop the device, such as resetting it or otherwise stopping its ability to do bus mastering.
If needed, the registered trans list will be executed as if by udi_pio_trans. The UDI_PIO_SCRATCH addressing mode may be used to access up to scratch_requirement bytes of scratch space. UDI_PIO_BUF and UDI_PIO_MEM must not be used.
The abort trans list will be executed immediately in its own serialization domain without regard to the state of other PIO operations in other serialization domains; the abort operations will preempt any PIO trans lists currently executing or scheduled for executing and those PIO trans lists will be deallocated by the environment rather than being continued or executed. Additionally, no regions of this driver instance will be entered after initiation of the abort trans list and all channels to parents of that driver instance will be closed to release associated resources.
To facilitate handling faults in as wide a portion of the driver as possible, the driver should call udi_pio_abort_sequence as early as possible in its per-instance initialization sequence. If the device changes state in such a way that a different procedure is needed to shut it down, the driver may call udi_pio_abort_sequence again to replace the previously-registered sequence, but this should only be done if absolutely necessary.
The PIO handle passed to this service call is "given away" (as if with udi_pio_unmap). The driver must no longer access this handle.
A driver is not required to register a PIO abort sequence with this call if the operational characteristics of the device are such that it will not generate any activity (DMA, interrupts, etc) even if the driver is abruptly removed.
REFERENCES udi_pio_map, udi_pio_unmap, udi_pio_trans_t
4.3 PIO Access Service Calls and Structures
PIO devices are accessed by passing a list of PIO transactions to udi_pio_trans. Each of the transactions are processed in turn. When all the transactions in the list are completed, the driver's callback routine is called.
There are three types of PIO transactions that affect PIO devices: input, output, and synchronization. Input transactions read data from a device; output transactions send data to a device; synchronization transactions are used to control ordering of input/output transactions on weakly-ordered architectures.
The following rules and restrictions apply to all PIO transactions:
- The tran_size parameter of each transaction descriptor must be from 0 to 5 (which may be represented as UDI_PIO_1BYTE, UDI_PIO_2BYTE, ...). Since it encodes size as a power of two, this represents transaction sizes from 1 to 32 bytes (256 bits). For transaction descriptors to which transaction size does not apply (e.g. UDI_PIO_BRANCH), tran_size must be zero.
- Endian translation is performed, when needed, on each 2^tran_size byte quantity. To transfer a character or byte array to or from device memory the driver must use a tran_size of UDI_PIO_1BYTE with an appropriate repeat count rather than using UDI_PIO_NEVERSWAP with a single basic transaction.
- If the pio_attributes parameter to udi_pio_map did not include UDI_PIO_UNALIGNED, then the base_offset in udi_pio_map, the base address alignment of the register set itself, and any PIO offsets accessed by PIO transactions must all be transaction-size aligned (i.e. multiples of 2^tran_size).
- If copied to driver memory, the datum must be transaction-size aligned relative to a udi_mem_alloc'd piece of memory, a control block's scratch area, or a variable whose data type is 2^tran_size bytes in size, even if UDI_PIO_UNALIGNED was set in pio_attributes.
- PIO pacing, when indicated, occurs between each 2^tran_size byte transfer that accesses the PIO device (once per repetition). The only PIO transaction types that access the PIO device are UDI_PIO_IN, UDI_PIO_OUT, UDI_PIO_IN_IND, UDI_PIO_OUT_IND, UDI_PIO_REP_IN_IND, and UDI_PIO_REP_OUT_IND.
- If a PIO device access uses a transaction size that is one of the atomic transaction sizes indicated by udi_pio_atomic_sizes, all 2^tran_size bytes are guaranteed to be transferred together "atomically" (as defined in "atomic transaction" in the Glossary), unless UDI_PIO_UNALIGNED was set in pio_attributes.
Since odd sizes (eg. 3 bytes) are not allowed, access to, for example, a 24-bit register must either be encapsulated within a larger access and the unused bits appropriately tossed (as described below), or it must be split up into smaller accesses (eg. 1- and 2-byte accesses) as appropriate for the IO card or shared control structure.
For example, a 24-bit register at PIO offsets 1 through 3 in little endian device memory can be read in an endian-neutral manner by doing a 32-bit read at PIO offset 0 and shifting the result right by 8 bits.
A 24-bit register at offsets 0 through 2 could be read by doing the 32-bit read and then extracting the low-order 3 bytes using a mask of 0xFFFFFF.
A 24-bit register at offsets 2 through 4 could be read by doing a 16-bit read at PIO offset 2, and adding this to the result of an 8-bit read at offset 4 shifted left by 16 bits.
NAME udi_pio_trans_t
PIO transaction descriptor
#include <udi.h>typedef const struct { udi_ubit8_t pio_op; udi_ubit8_t tran_size; udi_ubit16_t operand; } udi_pio_trans_t; /* Values for tran_size */#define UDI_PIO_1BYTE 0 #define UDI_PIO_2BYTE 1 #define UDI_PIO_4BYTE 2 #define UDI_PIO_8BYTE 3 #define UDI_PIO_16BYTE 4 #define UDI_PIO_32BYTE 5/* Values for register numbers in pio_op */#define UDI_PIO_R0 0 #define UDI_PIO_R1 1 #define UDI_PIO_R2 2 #define UDI_PIO_R3 3 #define UDI_PIO_R4 4 #define UDI_PIO_R5 5 #define UDI_PIO_R6 6 #define UDI_PIO_R7 7/* Values for addressing modes in pio_op */#define UDI_PIO_DIRECT 0x00 #define UDI_PIO_SCRATCH 0x08 #define UDI_PIO_BUF 0x10 #define UDI_PIO_MEM 0x18/* Values for Class A opcodes in pio_op */#define UDI_PIO_IN 0x00 #define UDI_PIO_OUT 0x20 #define UDI_PIO_LOAD 0x40 #define UDI_PIO_STORE 0x60/* Values for Class B opcodes in pio_op */#define UDI_PIO_LOAD_IMM 0x80 #define UDI_PIO_CSKIP 0x88 #define UDI_PIO_IN_IND 0x90 #define UDI_PIO_OUT_IND 0x98 #define UDI_PIO_SHIFT_LEFT 0xA0 #define UDI_PIO_SHIFT_RIGHT 0xA8 #define UDI_PIO_AND 0xB0 #define UDI_PIO_AND_IMM 0xB8 #define UDI_PIO_OR 0xC0 #define UDI_PIO_OR_IMM 0xC8 #define UDI_PIO_XOR 0xD0 #define UDI_PIO_ADD 0xD8 #define UDI_PIO_ADD_IMM 0xE0 #define UDI_PIO_SUB 0xE8/* Values for Class C opcodes in pio_op */#define UDI_PIO_BRANCH 0xF0 #define UDI_PIO_LABEL 0xF1 #define UDI_PIO_REP_IN_IND 0xF2 #define UDI_PIO_REP_OUT_IND 0xF3 #define UDI_PIO_DELAY 0xF4 #define UDI_PIO_BARRIER 0xF5 #define UDI_PIO_SYNC 0xF6 #define UDI_PIO_SYNC_OUT 0xF7 #define UDI_PIO_DEBUG 0xF8 #define UDI_PIO_END 0xFE #define UDI_PIO_END_IMM 0xFF/* Values for UDI_PIO_DEBUG operand */#define UDI_PIO_TRACE_OPS_NONE 0 #define UDI_PIO_TRACE_OPS1 1 #define UDI_PIO_TRACE_OPS2 2 #define UDI_PIO_TRACE_OPS3 3 #define UDI_PIO_TRACE_REGS_NONE (0U<<2) #define UDI_PIO_TRACE_REGS1 (1U<<2) #define UDI_PIO_TRACE_REGS2 (2U<<2) #define UDI_PIO_TRACE_REGS3 (3U<<2) #define UDI_PIO_TRACE_DEV_NONE (0U<<4) #define UDI_PIO_TRACE_DEV1 (1U<<4) #define UDI_PIO_TRACE_DEV2 (2U<<4) #define UDI_PIO_TRACE_DEV3 (3U<<4)MEMBERS pio_op is the type of operation to use for this transaction. pio_op encodes the type of transaction and the selection of source and destination operands.
tran_size is the power-of-2 size of a basic PIO transaction. The actual size is 2tran_size bytes. The tran_size value must be from 0 to 5, corresponding to a transaction size of 1 to 32 bytes (256 bits). The mnemonic constants UDI_PIO_1BYTE through UDI_PIO_32BYTE are available for use in setting tran_size.
operand is an operand value or address for the transaction. Its interpretation depends on the type of operation given by pio_op.
DESCRIPTION A PIO transaction descriptor (udi_pio_trans_t) describes in most cases a single PIO register/memory access transaction or an operation using a set of temporary registers. Multiple such transaction descriptors may be combined into a single list, called a trans list, to perform more complex sequences with a single call to udi_pio_trans. Advanced operations in the trans list allow for bit manipulations, repeat counts, serialization, and conditional branching. All PIO transactions are subject to the rules and procedural information listed on page 4-13.
Each element in the trans list array represents a basic transaction, and operates on a single transaction-sized piece of data. When transferring data to or from a device (for example, with UDI_PIO_IN or UDI_PIO_OUT), the entire transaction-size bytes will be transferred as a single atomic bus transaction if possible. See udi_pio_atomic_sizes for a description of how to determine the atomic sizes supported on a particular platform.
While simple transactions can be handled with simple operations, UDI supports a rich set of PIO operation types, so that even fairly sophisticated manipulation of device data can be performed with a single call to udi_pio_trans. To allow these operations to be efficiently expressed, udi_pio_trans uses an abstract register load/store model.
During execution of udi_pio_trans, the UDI environment maintains a set of 8 temporary "registers", numbered 0 through 7, each of which can hold one data item up to 32 bytes (256 bits) in size. Most PIO operations use one or more of these registers. If the size of a value loaded into a register (determined by tran_size) is less than 32 bytes, the "upper" (most-significant) bytes are treated as zero if the register value is subsequently used with a larger transaction size. If a register value is used in an operation with a smaller transaction size than when the register was last loaded, the upper bytes will be ignored. Arithmetic operations do not generate underflows or overflows, they simply wrap around; in other words, all arithmetic is modulo 2^((2^tran_size)*8).
In addition to using the temporary registers, some PIO operations allow access to permanent memory associated with one of the arguments to udi_pio_trans. These operations can access control block scratch space, udi_buf_t buffer contents, or driver memory. Values in permanent memory are stored in the driver's endianness, even for transaction sizes larger than native word sizes. These values will be either purely little endian or purely big endian. (See the definitions of big endian and little endian in Section 3.2.2, "Common Terms," on page 3-2 of the UDI Core Specification.)
PIO operations are divided into 3 classes, based on the type of operands used: Class A, Class B, and Class C operations.
Class A Operations: Class A operations can use register values or permanent memory values. The pio_op value for a class A operation selects a register number (0-7) as well as an addressing mode from the following table.
To set pio_op for a Class A operation, the register number and exactly one of the addressing mode mnemonics must be added to the opcode mnemonic:
pio_op = operation_code + addressing_mode + register
The mnemonic constants UDI_PIO_R0 through UDI_PIO_R7 are available for use in selecting registers for all opcodes and operands that use register numbers.
Class A pio_op values are encoded with the following bit patterns: 000aarrr - 011aarrr, where aa selects the addressing mode according to Table 4-1 and rrr selects a register number.
Class B Operations: Class B operations can use register values but not permanent memory values. The pio_op value for a class B operation selects a register number (0-7), which is used as if the UDI_PIO_DIRECT addressing mode were specified.
To set pio_op for a Class B operation, the register number must be added to the opcode mnemonic:
pio_op = operation_code + register
Class B pio_op values are encoded with the following bit patterns: 10000rrr - 11101rrr, where rrr selects a register number to be used with that addressing mode.
Class C Operations: Class C operations use neither register values nor permanent memory values. The pio_op value for a class C operation consists only of the opcode:
Class C pio_op values are encoded with the following bit patterns: 11110000 - 11111111.
The following tables list the opcodes for each class and their encoded values, along with the corresponding operation definitions, including an indication of how the operand value is used in each case. In these tables, addr means the contents of the location referred to by the selected addressing mode and register, reg(operand) means a direct register value where the low-order 3 bits of operand are used to select the register number (unless otherwise specified, the remaining bits must be zero), PIO(operand) means a location on the PIO device where operand indicates the desired PIO offset, and reg means the selected register contents.
Table 4-3 Class B PIO Operation Codes Operation Code Value Operation UDI_PIO_LOAD_IMM 0x80 reg <- operand (multi-part) UDI_PIO_CSKIP 0x88 Conditional skip. The value in reg is compared with zero. The operand value selects a condiction code from Table 4-5 to determine the type of comparison. If the condition is TRUE, the following trans list element will be skipped. UDI_PIO_IN_IND 0x90 reg <- PIO(reg(operand)) UDI_PIO_OUT_IND 0x98 PIO(reg(operand)) <- reg UDI_PIO_SHIFT_LEFT 0xA0 reg <- reg << operand UDI_PIO_SHIFT_RIGHT 0xA8 reg <- reg >> operand UDI_PIO_AND 0xB0 reg <- reg & reg(operand) UDI_PIO_AND_IMM 0xB8 reg <- reg & operand (zero-extended) UDI_PIO_OR 0xC0 reg <- reg | reg(operand) UDI_PIO_OR_IMM 0xC8 reg <- reg | operand (zero-extended) UDI_PIO_XOR 0xD0 reg <- reg ^ reg(operand) UDI_PIO_ADD 0xD8 reg <- reg + reg(operand) UDI_PIO_ADD_IMM 0xE0 reg <- reg + operand (sign-extended) UDI_PIO_SUB 0xE8 reg <- reg - reg(operand) Detailed description of Addressing Modes:
UDI_PIO_DIRECT - In this mode, the contents of the selected register, reg, are used for the addr value itself. In this case, addr is unaffected by any stride values from UDI_PIO_REP_IN_IND or UDI_PIO_REP_OUT_IND operations.
UDI_PIO_SCRATCH - In this mode, the contents of the selected register, reg, are used as an offset into the scratch space of the control block passed to udi_pio_trans. The scratch space data bytes starting at this offset and extending for the selected transaction size are used as the addr value. Only the low-order 32 bits of the register value are used; any higher order bits are ignored; smaller values previously loaded into the register are zero-extended to 32 bits. The offset must be a multiple of the transaction size.
UDI_PIO_BUF - In this mode, the contents of the selected register, reg, are used as an offset into the valid data area of the udi_buf_t buffer passed to udi_pio_trans. The buffer data bytes starting at this offset and extending for the selected transaction size are used as the addr value. Only the low-order 32 bits of the register value are used; any higher order bits are ignored; smaller values previously loaded into the register are zero-extended to 32 bits. The offset must be a multiple of the transaction size. All offsets accessed with UDI_PIO_BUF must be less than the buffer's buf_size value at the time udi_pio_trans was called.
UDI_PIO_MEM - In this mode, the contents of the selected register, reg, are used as an offset into the auxiliary memory block pointed to by the mem_ptr argument passed to udi_pio_trans. The memory block data bytes starting at this offset and extending for the selected transaction size are used as the addr value. Only the low-order 32 bits of the register value are used; any higher order bits are ignored; smaller values previously loaded into the register are zero-extended to 32 bits. The offset must be a multiple of the transaction size.
When PIO device registers/memory are used as the source (UDI_PIO_IN, UDI_PIO_IN_IND, or UDI_PIO_REP_IN_IND) or destination (UDI_PIO_OUT, UDI_PIO_OUT_IND, or UDI_PIO_REP_OUT_IND) of an operation, the specified PIO offset is interpreted relative to the register set and base offset specified through arguments to udi_pio_map and passed to udi_pio_trans via the pio_handle argument. UDI_PIO_IN and UDI_PIO_OUT only support PIO offsets up to 65535. UDI_PIO_IN_IND, UDI_PIO_OUT_IND, UDI_PIO_REP_IN_IND and UDI_PIO_REP_OUT_IND use a register value to provide the PIO offset, allowing for indirections and larger offsets; the 32 low-order bits of the register value are used for the PIO offset; any higher order bits are ignored; smaller values previously loaded into the register are zero extended to 32 bits.
The immediate load operation takes a value directly from the operand portion of a transaction descriptor and loads it into the selected temporary register. The transaction size for this operation must be at least 2 bytes.
If UDI_PIO_LOAD_IMM is used with a transaction size greater than 2 bytes, multiple trans list elements are used to hold all the bytes of the immediate value. Starting from the least significant 2 bytes, pairs of bytes are loaded from each successive operand value starting from the UDI_PIO_LOAD_IMM trans list entry, where each pair is listed as most-significant-byte followed by least-significant-byte. The subsequent trans list elements which contain the additional byte values must repeat the pio_op and tran_size values from the initial UDI_PIO_LOAD_IMM operation. Normal trans list execution resumes after the last such element.
UDI_PIO_SHIFT_LEFT and UDI_PIO_SHIFT_RIGHT use the operand value as a shift count for shifting the entire transaction-size byte register value. The shift count must be from 1 to 32. Any bits shifted out of the low order transaction-size bytes will be discarded. All bits shifted in to the low order transaction-size bytes will be zero. Any previous more significant bytes shall effectively be zeroed.
A UDI_PIO_REP_IN_IND or UDI_PIO_REP_OUT_IND operation can be used to repeat a basic PIO transaction up to 232-1 times. The basic PIO transaction is like a UDI_PIO_IN_IND or UDI_PIO_OUT_IND, in that it performs a PIO transaction between a PIO offset indicated in one register and a permanent memory location addressed via another register. A third register holds the repeat count, which must be from zero to 232-1.
The operand value for a repeat operation holds a number of parameters, intialized via the UDI_PIO_REP_ARGS macro. See UDI_PIO_REP_ARGS for more details on repeat operations.
UDI_PIO_BRANCH is an unconditional branch operation. The operand value is used to find the next trans list element to execute instead of continuing in order. The operand value is matched against UDI_PIO_LABEL trans list elements; execution continues after the UDI_PIO_LABEL with the same operand value as the UDI_PIO_BRANCH. It is illegal to have two UDI_PIO_LABEL elements with the same operand value in one trans list. It is illegal to have a UDI_PIO_BRANCH that does not have a corresponding UDI_PIO_LABEL.
It is illegal to specify a UDI_PIO_LABEL or UDI_PIO_BRANCH transaction with an operand value of zero.
If a UDI_PIO_LABEL operation is executed sequentially, it acts as a no-op.
UDI_PIO_CSKIP is a conditional skip operation. It compares a register value with zero and skips the following operation if the comparison meets the required conditions. The operand specifies the condition code, according to the following table:
Table 4-5 PIO Condition Codes Mnemonic Value Condition Description UDI_PIO_Z reg == 0 UDI_PIO_NZ reg != 0 UDI_PIO_NEG reg < 0 [signed] UDI_PIO_NNEG reg >= 0 [signed] A UDI_PIO_DELAY pauses execution of the trans list for a specified time. Transaction lists that loop waiting for device events (such as ready bits being set in status registers) must include a UDI_PIO_DELAY in the loop to avoid consuming excessive CPU resources. The operand value provides the desired delay time in microseconds. This is a hint to the environment, and must not be used to perform precise timings. The environment will pause the execution of the PIO trans list for at least the requested amount of time.
The synchronization operations (UDI_PIO_BARRIER, UDI_PIO_SYNC and UDI_PIO_SYNC_OUT) do not actually transfer any new data. Instead, they make sure that any PIO transactions that have already been generated are made visible to the device before any subsequent PIO transactions to/from the same device register set become visible (thus acting as a "barrier"). These operations only affect accesses to the device or memory referenced by the PIO handle, not buffer or driver memory.
The operand value for UDI_PIO_BARRIER must be either 0 or UDI_PIO_OUT. If UDI_PIO_OUT, the barrier is only guaranteed to act with respect to device output transactions; this may be faster than a full barrier.
The UDI_PIO_SYNC and UDI_PIO_SYNC_OUT operations provide even stronger synchronization: they also wait for the affected transactions to reach the device. UDI_PIO_SYNC affects all PIO transactions; UDI_PIO_SYNC_OUT is only required to affect output transactions. operand and tran_size must be set to a PIO offset range of the device that causes no side effects if input from. On some platforms, PIO transactions can only be synchronized by performing an additional PIO input transaction. In such cases, the environment will read 2tran_size bytes from offset operand of the device (and discard the result).
Synchronization operations are not needed if UDI_PIO_STRICTORDER was specified in the PIO attributes for the PIO handle. With UDI_PIO_STRICTORDER, each basic transaction that accesses the PIO device is followed by an implicit UDI_PIO_SYNC.
Upon completion of a udi_pio_trans sequence, via execution of a UDI_PIO_END operation, an implicit UDI_PIO_BARRIER with operand of zero is performed.
Environment implementations may choose to impose stronger ordering and synchronization than required by the synchronization operations used, up to and including strict ordering.
The UDI_PIO_DEBUG operation controls PIO debug tracing. This opcode will be seldom, if ever, used in production drivers and is to be used mostly while a driver is under development. As with the other debugging facilities in UDI like udi_debug_printf and udi_debug_break, an environment may choose to completely or partially ignore this opcode. Each UDI environment must document how the debugging output is made available to the developer.
Upon entry to a trans list, all debugging is disabled. UDI_PIO_DEBUG opcodes are interpreted like all other opcodes; they are synchronous with the execution of the trans list. Each UDI_PIO_DEBUG opcode will reset the debug level regardless of the current debug level; they are not cumulative.
The trans_size must be zero since no PIO-visible data is transferred by this opcode. The operand provides a bitmask to control the level of debugging. If no flags are specified, debugging is disabled.
For each field, tracing information is generated only if the current debug level in the trans list is greater than or equal to the debugging level of the environment. The output at various levels is explictly specified; it's an agreement between the trans list author and the user of the environment controlling the trace level.
Upon reaching a UDI_PIO_END_IMM, processing of the trans list is terminated and the result code (an arbitrary 16-bit code that is defined by the driver) in the udi_pio_trans_call_t callback is set to the low byte of the operand value.
Upon reaching a UDI_PIO_END, processing of the trans list terminates as in the UDI_PIO_END_IMM case, except that the operand specifies the register number whose contents are to be returned as the result code.
The last element of a PIO trans list must be either a UDI_PIO_END operation, a UDI_PIO_END_IMM operation, or a UDI_PIO_BRANCH.
REFERENCES udi_pio_map, udi_pio_trans, UDI_PIO_REP_ARGS, udi_buf_t, udi_mem_alloc
NAME UDI_PIO_REP_ARGS
Parameters for repeated PIO transactions
#include <udi.h>#define \ UDI_PIO_REP_ARGS ( \ mode, mem_reg, mem_stride, \ pio_reg, pio_stride, cnt_reg ) \ ((mode)|(mem_reg)|((mem_stride)<<5)| \ ((pio_reg)<<7)|((pio_stride)<<10)| \ ((cnt_reg)<<13))ARGUMENTS mode is the addressing mode used to access the memory operand, according to Table 4-1, "PIO Addressing Modes".
mem_reg is the number of the register used to access the memory operand, according to Table 4-1.
mem_stride is the stride value used to increment the memory offset between repeats.
pio_reg is the number of the register used to hold the initial PIO offset. The 32 low-order bits of the register value are used for the PIO offset; any higher order bits are ignored; smaller values previously loaded into the register are zero extended to 32 bits.
pio_stride is the stride value used to increment the PIO offset between repeats.
cnt_reg is the number of the register used to hold the repeat count. The 32 low-order bits of the register value are used for the count; any higher order bits are ignored; smaller values previously loaded into the register are zero extended to 32 bits.
description The UDI_PIO_REP_ARGS macro is used to construct the operand value for UDI_PIO_REP_IN_IND and UDI_PIO_REP_OUT_IND repeating PIO operations (see udi_pio_trans_t).
A repeat operation repeats a basic PIO transaction the number of times indicated by the repeat count from the cnt_reg register. The memory location and PIO offset for the first repetition are determined by mode, mem_reg, and pio_reg. For subsequent repetitions, the memory offset (if mode is not UDI_PIO_DIRECT) and PIO offset are incremented according to the corresponding stride values. The values in the original registers (mem_reg, pio_reg, and cnt_reg) are not affected by stride increments or by repeat count decrements.
The stride values mem_stride and pio_stride indicate a (possibly zero) multiple of the target operation's transaction size, according to the following table.
Table 4-6 Stride Values for PIO Repeat Operations Stride Code Stride Size in Bytes Multiple of Transaction Size 0 2^tran_size 2^(tran_size+1) 2^(tran_size+2) Some examples of how to use stride values are shown in the following table.
NAME udi_pio_trans
Generate PIO transactions
#include <udi.h>void udi_pio_trans ( udi_pio_trans_call_t *callback, udi_cb_t *gcb, udi_pio_handle_t pio_handle, udi_index_t start_label, udi_buf_t *buf, void *mem_ptr ); typedef void udi_pio_trans_call_t ( udi_cb_t *gcb, udi_buf_t *new_buf, udi_status_t status, udi_ubit16_t result );ARGUMENTS callback, gcb are standard arguments described in the "Asynchronous Service Calls" section of "Standard Calling Sequences" in the UDI Core Specification.
pio_handle is the PIO handle associated with the desired device register set and transaction list, previously acquired from udi_pio_map.
start_label specifies which UDI_PIO_LABEL value the trans list execution should begin at. A start_label value of zero indicates that the trans list execution should start from the beginning, otherwise values from 1-7 indicate a request to start the trans list at the corresponding UDI_PIO_LABEL position. Any other values for this argument are illegal.
buf is a pointer to a data buffer that can be accessed by the PIO transactions using UDI_PIO_BUF. If buf is set to NULL, UDI_PIO_BUF transactions are illegal for this call.
mem_ptr is a pointer to an auxiliary memory block that can be accessed by PIO transactions that specify UDI_PIO_MEM. If mem_ptr is set to NULL, such transactions are illegal for this call.
new_buf is a possibly new UDI buffer that the driver must use in place of the original buf after the callback is called, since the environment may need to re-allocate the buffer to accomodate new data added to the buffer as a result of this service call.
status is a UDI status code. If the PIO transactions all completed without error, it will be set to UDI_OK; otherwise it will be set to UDI_STAT_HW_PROBLEM. If not UDI_OK, the status code may already contain a correlate value.
result is a result code set by a UDI_PIO_END or a UDI_PIO_END_IMM transaction.
DESCRIPTION udi_pio_trans performs a series of one or more PIO transactions to the area of a device indicated by the PIO handle, pio_handle. The list of (possibly repeated) transactions to perform is given by the trans_list which was associated with the PIO handle at the time of udi_pio_map. See udi_pio_trans_t for a detailed description of PIO transactions.
Synchronization of udi_pio_trans calls and the execution of the corresponding transaction lists is provided by the environment independently from the region synchronization. For proper driver design refer to the PIO synchronization and ordering definitions on page 4-1.
If the PIO attributes associated with pio_handle indicate that the endianness of the device's data structures are different from the driver's endianness, udi_pio_trans will automatically perform necessary byte-swapping. The driver must not do the byte-swapping itself, as udi_pio_trans may be able to take advantage of special hardware support for byte-swapped PIO transactions.
Pacing delays and other attributes encoded in pio_handle, such as ordering and atomicity requirements, will be applied to each individual I/O operation.
Once the last transaction has been executed, including any pacing delay, the callback routine will be called. An implicit UDI_PIO_BARRIER will be executed at the end of the transaction list, but if UDI_PIO_SYNC or UDI_PIO_SYNC_OUT are required they must be explicitly included.
warnings Use of the mem_ptr parameter must conform to the rules described in "Using Memory Pointers with Asynchronous Service Calls" on page 5-2 of the UDI Core Specification.
STATUS VALUES A UDI status code indicating the success or failure of the PIO transactions. If a hardware error, such as a bus timeout or parity error, is detected in conjunction with a PIO transaction, execution of the PIO transaction list is terminated and status is set to UDI_STAT_HW_PROBLEM. Transactions following the one that caused the error may or may not be executed. Environments are not required to detect errors during udi_pio_trans; in such environments, the effect of a PIO hardware error is indeterminate and may include driver or system termination. If a driver expects that the PIO might reasonably fail (e.g. because the device is not present) it must use udi_pio_probe instead of udi_pio_trans.
REFERENCES udi_pio_map, udi_pio_trans_t, udi_pio_probe
NAME udi_pio_probe
Probe a PIO device that might not be present
#include <udi.h>void udi_pio_probe ( udi_pio_probe_call_t *callback, udi_cb_t *gcb, udi_pio_handle_t pio_handle, void *mem_ptr, udi_ubit32_t pio_offset, udi_ubit8_t tran_size, udi_ubit8_t direction ); typedef void udi_pio_probe_call_t ( udi_cb_t *gcb, udi_status_t status ); /* Values for direction */#define UDI_PIO_IN 0x00 #define UDI_PIO_OUT 0x20ARGUMENTS callback, gcb are standard arguments described in the "Asynchronous Service Calls" section of "Standard Calling Sequences" in the UDI Core Specification.
pio_handle is a PIO handle previously allocated via udi_pio_map.
mem_ptr is a pointer to a memory location which is the source or destination of the PIO transaction.
pio_offset is the offset into the mapped device at which to perform the transaction. There are no alignment requirements on pio_offset.
tran_size is the power-of-2 size of the PIO transaction. The actual size is 2tran_size bytes. The tran_size value must be from 0 to 5, corresponding to a transaction size of 1 to 32 bytes (256 bits). The mnemonic constants UDI_PIO_1BYTE through UDI_PIO_32BYTE are available for use in setting tran_size.
direction is exactly one of UDI_PIO_IN or UDI_PIO_OUT, to indicate the direction of transfer.
status indicates the success or failure of the PIO probe attempt.
DESCRIPTION This service call attempts to perform a PIO access to a device that may or may not be present. It will do this in such a way that there will be no adverse side effects (such as driver or system aborts) if there is in fact no device present. If a different device is present at the same or overlapping location then it might be accessed instead of the intended device, causing an indeterminate effect on the state of that device; environments are not required to prevent this from happening.
The trans list associated with pio_handle is ignored; instead, a single basic PIO transaction of 2tran_size bytes is performed, in the direction indicated by direction.
If a driver's device might not have been reliably enumerated and might not be actually present, udi_pio_probe must be used to determine if the device is present before using it for normal operation. Otherwise, udi_pio_probe should not be used. It is likely to be slower than udi_pio_trans, possibly affecting overall system performance as well. In some environments, use of udi_pio_probe may force the driver to be run only during certain stages of system initialization; such an environment may require system re-initialization in order to use a driver that uses udi_pio_probe.
In order for a driver to use udi_pio_probe, its region's "pio_probe" region attribute must be set to "yes". Some environments may refuse to use drivers with "pio_probe" set to "yes". See Section 1.4, "Extensions to Static Driver Properties" for details.
Transactions using udi_pio_probe are not guaranteed to be atomic.
If a driver determines that its device is not present during binding, it must log an error using udi_log_write and respond to the Management Agent with a status of UDI_STAT_NOT_RESPONDING via udi_channel_event_complete.
warnings Use of the mem_ptr parameter must conform to the rules described in "Using Memory Pointers with Asynchronous Service Calls" on page 5-2 of the UDI Core Specification.
UDI_STAT_HW_PROBLEM - the device failed to respond to the PIO access. Some environments may return "garbage" data in such cases, rather than indicating an error with UDI_STAT_HW_PROBLEM.
4.4 PIO Handle Transferability
Since PIO handles are transferable between regions, they need to be represented in layout specifiers for use by metalanguages and by drivers that specify inline layouts. This section defines an extension to the data layout specifier defined in the UDI Core Specification.
NAME udi_layout_t (PIO)
Data layout specifier for PIO
#include <udi.h>typedef udi_ubit8_t udi_layout_t; /* PIO Handle Layout Element Type Code */#define UDI_DL_PIO_HANDLE_T 200DESCRIPTION This page lists additional layout specifier codes that can be used with the udi_layout_t type, defined in the UDI Core Specification, to specify PIO-related data layouts.
A data layout specifier consists of an array of one or more udi_layout_t layout elements. Each element contains a type code indicating one of the UDI data types that can be passed into a channel operation, either as a field in the control block or as an additional parameter. Each successive element of the array represents successive offsets within the described structure, with padding automatically inserted for alignment purposes as if the specified data types had appeared in a C struct declaration.
A UDI_DL_PIO_HANDLE_T layout element represents a PIO handle, of type udi_pio_handle_t, which may be UDI_NULL_PIO_HANDLE.
1The PIO pacing delay may be implemented in hardware or software by the environment, but represents a potential inline delay in region execution. The driver writer is strongly advised to use only the delays required for their device, as excessive use can have adverse effects on the driver and the rest of the system.