Used by devices that simulate a single PCIe function with a Type 0
header. Implements the pcie_device interface, defines a pcie_config bank
of type type_0_bank, and an upstream_target connect.
Also defines a phy port that handles transactions related to
the PCIe physical layer.
Inherits templates: pcie_phy, pcie_hreset, pcie_hot_reset, pcie_device.
Similar to pcie_endpoint but doesn’t define a pcie_config bank. Can be
used by devices that simulate multiple functions. A device using this
template must define at least one bank that is an instance of the
physical_config_bank template. Also defines a phy port that handles
transactions related to the PCIe physical layer.
Inherits templates: pcie_phy, pcie_hreset, pcie_hot_reset, pcie_device.
Implements the pcie_device interface, with default methods for connect and
disconnect that finds all instances of physical_config_bank in this device
and lets them map/demap themselves and their resources in the upstream
target.
Assumes that there is a connect upstream_target which it sets/clears
when the device is connected/disconnected.
Base template for register banks that implement a PCIe configuration
header. Defines the registers that are common to both Type 0 and Type 1
headers, as well as many utility methods. Typically not used
directly. Inherit one of the derived templates, e.g. type_0_bank or
type_1_bank instead.
Uses the following parameters
function: The PCIe function number that this bank will use, default 0is_physical: Whether this bank is a physical function, default truepcie_obj: The PCIe function object associated with this bank, default bank.obj;
used as function object for the pcie_map interface and as ATOM_initiator for transactions.update_function(uint16 device_id)Updates the mapping of this function’s configuration header in the upstream target, using the parameter device_id.
del_function()Deletes the function from the PCIe hierarchy. The configuration header is no longer accessible over PCIe wire. Additional resources like BARs are still mapped and must be unmapped separately by the user.
disable_function()Temporarily disables the function from the PCIe hierarchy. The configuration header, BAR resources, expansion ROM and downstream traffic are no longer accessible over PCIe wire.
enable_function()Enables the function into the PCIe hierarchy. The configuration header, BAR resources, expansion ROM and downstream traffic are now accessible over PCIe wire.
A function must be added to the PCIe hierarchy for the enablement to take effect.
get_device_id() -> (uint16)Returns the Device ID of this function, as known by the upstream target. Uses the parameter pcie_obj which defaults to bank.obj.
verify_pasid(pcie_pasid_info_t pasid) -> (bool)Verifies the function is correctly configured to support the contents of
the pasid argument. If the check fails it is considered a modelling error.
Returns true on success.
verify_pasid_with_at(pcie_pasid_info_t pasid, pcie_at_t at) -> (bool)Verifies the function is correctly configured to support the contents of the pasid
argument and the type of argument at.
If the check fails it is considered a modelling error.
Returns true on success.
del_mappings(pcie_type_t type)Deletes all BAR mappings of type type, that this function has added.
update_mappings(pcie_type_t type)Updates all BAR mappings of type type, that this function has added.
lock_hwinit_registers()Locks all registers that use the template hwinit
unlock_hwinit_registers()Unlocks all registers that use the template hwinit
raise_legacy_interrupt()Raises the PCIe Legacy Interrupt pin INTx, as configured in the
interrupt_pin register and if enabled in the status.ins register
field.
lower_legacy_interrupt()Lowers the PCIe Legacy Interrupt pin INTx, as configured in the
interrupt_pin register
issue_transaction(transaction_t *t, uint64 addr) -> (pcie_error_t)Low-level method for issuing a transaction into some PCIe address space.
Prepends required PCIe atoms to a transaction before issuing it to
upstream_target.
issue_transaction_target(transaction_t *t, uint64 addr) -> (pcie_error_t)Low-level method for issuing a transaction into some PCIe address space.
Prepends required PCIe atoms to a transaction before issuing it to
target.
Utility methods for reading and writing PCIe Memory data. Reads or
writes PCIe Memory data in upstream_target.map_target.
memory.read(uint64 addr, uint64 size) -> (pcie_error_t, uint64)memory.read_bytes(uint64 addr, buffer_t buf) -> (pcie_error_t)memory.read_custom(uint64 addr, buffer_t buf, atom_t *extra_atoms) -> (pcie_error_t)memory.write(uint64 addr, uint64 value, uint64 size) -> (pcie_error_t)memory.write_bytes(uint64 addr, bytes_t bytes) -> (pcie_error_t)memory.write_custom(uint64 addr, bytes_t bytes, atom_t *extra_atoms) -> (pcie_error_t)Messages sent downstream arrive through the message.operation method.
Broadcast messages and messages routed with terminate at receiver
are propagated to all functions within a multi-function device.
The following methods handle receiving messages. Their default
implementations are meant to be overridden by devices that wish to
handle the particular type of message. Unless otherwise noted, the
default implementation logs an unimpl message and returns
false, which indicates a ‘Completer Abort’.
message.operation(transaction_t *t, uint64 addr) -> (bool)Main method for receiving messages. Inspects the message type and redirects to the appropriate method for handling that specific message.
message.ats_invalidate(transaction_t *t, uint64 addr) -> (bool)message.prs_request(transaction_t *t, uint64 addr) -> (bool)message.prs_response(transaction_t *t, uint64 addr) -> (bool)These three methods redirect to their *_received counterparts in
the ats_capability group, if present.
message.vendor_defined_type_1(transaction_t *t, uint64 addr) -> (bool)PCIe demands that Vendor Defined Type 1 messages are silently
ignored, if not implemented. This method logs unimpl on level 2
and returns true, by default.
message.pm_active_state_nak(transaction_t *t, uint64 addr) -> (bool)message.pm_turn_off(transaction_t *t, uint64 addr) -> (bool)message.unlock(transaction_t *t, uint64 addr) -> (bool)message.set_slot_power_limit(transaction_t *t, uint64 addr) -> (bool)message.vendor_defined_type_0(transaction_t *t, uint64 addr) -> (bool)message.hot_plug(transaction_t *t, uint64 addr) -> (bool)Hot Plug messages are obsolete, according to PCIe, but must be silently ignored. This method logs unimpl on level 2 and returns 2, by default.
message.unknown(transaction_t *t, uint64 addr, pcie_message_type_t code) -> (bool)This method handles message types that are unknown to
message.operation, and gets the message type as a parameter.
The following methods can be used to send PCIe messages upstream. These messages may travel downstream, depending on message route, address and properties of the upstream target. The addr parameter contains the 8-byte address-field of the message. As in a real world TLP, the Device ID is the upper bits (63 through 48) of the address, while the lower bits may hold other message-specific information. The type and route parameters indicate the message type and the routing information.
For message.send_bytes the additional parameter bytes is used
for sending data in the message payload.
For message.send_custom the additional parameter extra_atoms is used
for devices to append additional required atoms that are not added automatically
by the library. These could be vendor specific atoms,
the destination segment atom (Sim_Atom_Id_pcie_destination_segment), etc.
For message.send_custom_target, the additional parameter target
is used to override the default target of issued PCIe messages
(upstream_target).
message.send(uint64 addr, pcie_message_type_t type, pcie_msg_route_t route) -> (pcie_error_t)message.send_bytes(uint64 addr, pcie_message_type_t type, pcie_msg_route_t route, bytes_t bytesmessage.send_custom(uint64 addr, pcie_message_type_t type, pcie_msg_route_t route, bytes_t bytes, atom_t extra_atoms[]message.send_custom_target(uint64 addr, pcie_message_type_t type, pcie_msg_route_t route, bytes_t bytes, atom_t extra_atoms[], map_target_t *targetmessage.trigger_pme() -> (pcie_error_t)Triggers a PME in a Type 0 device. A PME message is sent upstream if enabled in the PM capability.
Send an immediate PME_TO_Ack in response to a PME_Turn_Off. This implementation is suitable for Endpoints and Downstream Ports that have no connected downstream devices.
transaction_access(transaction_t *t, uint64 offset, void *aux) -> (exception_type_t)Entrypoint for access into the bank. Redirects to message or default
bank operation, depending on the type of the transaction. The pointer to
the transaction is passed as aux to the default operation, allowing
lower level methods to inspect and set atoms. Sets the pcie_error_ret
atom if present, and not set by lower level methods.
Base-template for physical functions, type 0 and 1. Inherits config_bank.
Inherits physical_config_bank and adds Type 0 specific registers.
Applied directly to registers and fields that are not explicitly sticky or
hwinit in the specification but shall still according to specification
not be affected by an FLR (Function Level Reset) of the PCIe function.
Inherits template pcie_function_level_reset and blocks default
pcie_function_level_reset functionality
and prevent propagation of pcie_function_level_reset to DML child
objects underneath the DML object.
Applied directly to registers and fields that are explicitly sticky
in the specification. The template causes FLR and Hot-Reset to have no
effect on these registers and fields.
Inherits templates soft_reset and pcie_no_function_level_reset.
It blocks default soft_reset functionality by overriding method soft_reset,
and prevents propagation of soft_reset to DML child objects underneath
the DML object. The PCIe library models Hot Reset with the soft_reset
templates defined in DML utilities.
Implements the HwInit access restrictions, as described in the PCIe
specification. Inherits templates init_val, write,
hard_reset, soft_reset and pcie_sticky.
Uses the following parameters:
writable_after_hreset: Is the register or field writable after hard reset, default falsewritable_once_only: Is the register or field writable once only, default trueInherits the desc template“
Inherits the write template and requires the register or field to
implement method pcie_write(uint64 val). The method is invoked upon a
write access.
Inherits the read template and requires the register or field to
implement method pcie_read() -> (uint64). The method is invoked upon a
read access.
Inherits the register template and declares method pcie_write_action(uint64 value)
which is invoked after the field write dispatching has taken place.
Inherits the register template and declares method pcie_read_action()
which is invoked after the field read dispatching has taken place.
Inherits the write template and clears the bits with 1´s written to.
Inherits the init_val template and requires the register or field to
implement method pcie_init(). The method is invoked upon object creation
and device reset.
Can be used to implement an object that is similar to the Base Address Registers in the PCIe configuration header.
The parameter type indicates in which upstream address space the resource
should be mapped. If the type is PCIE_Type_Memory or PCIE_Type_IO the
register will be called to update its resources when command.mem and
command.io, respectively, is written. If this is not desired, type can
be set to PCIE_Type_Other and the parameter map_type can be used to
indicate in which address space the resource should be mapped.
The parameter map_obj indicates which object will be mapped.
Users of this template must:
get_map_info() -> (map_info_t)enabled() -> (bool)Implements the common functionality for Memory and I/O Base Address Registers as specified in PCIe. It has a single unsized field address.
The parameter map_obj indicates which object will be mapped.
The parameter size_bits defaults to 12 and defines the number of address bits that this base address register uses. If a Resizable BAR capability is present and indicates that it controls the size of this BAR the size from the relevant RBAR Control register will be used instead.
Users of this template must:
enabled() -> (bool)Implements a Memory Base Address Register as specified in PCIe.
The parameter map_obj indicates which object will be mapped.
The parameter size_bits defaults to 12 and defines the number of address bits that this base address register uses. If a Resizable BAR capability is present and indicates that it controls the size of this BAR the size from the relevant RBAR Control register will be used instead.
64-bit BARs are by default prefetchable and 32-bit BARs are by
default not prefetchable.
Users of this template must:
Method is_pf_bar() return true if the BAR belongs to a physical function,
and false if it belongs to a virtual function.
Inherits the memory_base_address template. Used to model
32-bit BAR registers.
Inherits the memory_base_address template. Used to model
64-bit BAR registers.
Implements a 32-bit memory BAR but if memory_type indicates a 64-bit BAR,
it will fetch the upper 32-bits from the neighboring memory_base_address_split_high
at offset + 4. Can be used when the BAR is required to be split in two 32-bit
registers instead of a single 64-bit register.
Users of this template must:
Implements the upper 32-bits of memory BAR. Can be used when the BAR is required to be split in two 32-bit registers instead of a single 64-bit register.
Users of this template must:
Implements an I/O Base Address Register as specified in PCIe.
The parameter map_obj indicates which object will be mapped.
The parameter size_bits defaults to 12 and defines the number of address bits that this base address register uses. If a Resizable BAR capability is present and indicates that it controls the size of this BAR the size from the relevant RBAR Control register will be used instead.
Users of this template must:
Defines a register expansion_rom_base, as defined in PCIe. Additionally,
creates a group expansion which holds sub objects for a ROM and an image,
sized according to the parameter size_bits, which defaults to 11. The ROM
will be mapped in PCIe Memory Space, when enabled. Group expansion will inherit
the rom_subobj template. This template will set all necessary parameters for
group expansion.
Users of this template must:
Defines two subobjects in order to create a ROM:
romimageUsers of this template must:
size.Defines a pseudo-connect upstream_target, used by many other PCIe
templates.
Inherits the hreset template from DML utilities.
Invokes hard_reset() on the object instantiating this template.
For instance if the object instantiating this template is a DML subdevice
the soft_reset will be isolated to only affect the subdevice and not the entire
DML device.
Inherits template soft_reset and applies the soft_reset template
to all registers and fields below the DML object. The soft_reset
template is used to model a hot reset in PCIe.
PCIe Type 0 config banks will inherit this template. An is FLR triggered
by invoking method pcie_function_level_reset on the PCIe config bank
resetting the state of all relevant registers for FLR.
Adds a port phy that implements the transaction_interface_t. Only applicable
for PCIe endpoints and switch upstream ports.
Port phy is used for low level PCIe physical link exchanges. The library
implements link training through this port. A root port or a switch downstream port
initiates a link training transaction that ends up in this port.
The transaction_interface_t method issue(transaction_t *t, uint64 addr) -> (exception_type_t) in port phy can be overridden to extend the phy
with additional physical link exchanges. To add additional logic to the link
training that would for example handle custom atoms, override the
handle_link_custom() method. To modify existing logic, the individual link
training handler functions may be overridden.
handle_link_training(const transaction_t *t) -> (exception_type_t)Calls the individual link training handlers. The custom handler is called last. If any handler encounters an error fails, remaining handlers are skipped.
handle_device_readiness_status() -> (exception_type_t)Handles the Device Readiness Status (DRS) message. The DRS message is sent to the Upstream component if the Downstream component supports it. Read here for more details regarding what register fields are inspected and set in the default implementation of this method.
handle_l0p_support(const transaction_t *t) -> (exception_type_t)Handles the L1.0p support negotiation.
handle_link_flit_mode(const transaction_t *t) -> (exception_type_t)Handles the Flit Mode negotiation. The Flit Mode is negotiated between the two link partners. The Flit Mode is enabled if both link partners support it and the Downstream component does not disable it. Read here for more details regarding what register fields are inspected and set in the default implementation of this method.
handle_link_speed_width(const transaction_t *t) -> (exception_type_t)Handles the Link Speed and Link Width negotiation. The Link Speed and Link Width are negotiated between the two link partners. Read here for more details regarding what register fields are inspected and set in the default implementation of this method.
Adds a port upstream_ingress that implements the transaction_translator
interface. Port upstream_ingress receives all accesses before they
take any effect.
The transaction_translator_interface_t method translate
in port upstream_ingress can be overridden
to inspect/add/react to atoms in the transaction. When overridden, it is
recommended that the default implementation is called, such that
the transaction will flow onwards to the ultimate destination
(unless, of course, this is not desired).
ingress_blockers sits below the port upstream_ingressin the object hierarchy. It serves the purpose of conditionally block downstream transactions.
Its method is_blocked iterates over all blockers in the group and blocks the transaction
if any of them evaluate to true. The owner of a blocker is responsible for
calling SIM_translation_changed on object upstream_ingress.obj when
its blocking conditions change. This is to ensure that any cached translations
in the system are discarded.
Blockers in this group are added as DML subgroups and must inherit the upstream_ingress_blocker
template. See the upstream_ingress_blocker template for more details.
The upstream_ingress_blocker template is meant to be inherited
by DML subgroups of the ingress_blockers group
in the pcie_upstream_ingress_handling template.
It requires the object to define method block_transaction.
Method block_transaction will be checked for each downstream transaction
to determine if the transaction can be forwarded.
blocker_name is used to identify the blocker in the logs when a transaction is blocked.block_transaction(const transaction_t *t, uint64 addr, access_t access) -> (bool)This method is called for each downstream transaction. If it returns true, the transaction is blocked and will not be forwarded to the destination. If it returns false, the transaction will be allowed to proceed.
link_blockingTemplate for PCIe functions that participate in link blocking. Tracks
whether the local component is blocking the link (local_link_block)
and whether either side of the link is blocking it (global_link_block).
Blocking sources are modeled as pcie_link_blocker instances under the
link_blockers group (e.g. the Link Disable bit and the disable/enable
link API). update_link_blocking() re-evaluates the aggregate state and
propagates changes to the peer component across the link. The methods
disable_link() and re_enable_link() expose an API-level blocker, and
is_link_blocked() returns the current global state.
is_link_blocked()Returns the current global link blocking state.
update_link_blocking()Re-evaluates the link blocking state by querying all
pcie_link_blocker instances. If the state changed, notifies the
component on the other side of the link. Only acts on function 0.
pcie_disable_link()Disables the link by setting having the disable_link API PCIe link blocker indicate the link is disabled and updating the link state accordingly.
pcie_re_enable_link()Removes the disabled state from the API PCIe link blocker and updates the link state accordingly
pcie_link_blocker)The pcie_link_blocker template can be used to conditionally block the PCIe
link. Blocking the link means any link training attempt will fail and if a
successful link training has already occurred, the link will be disabled.
Custom blockers are added as groups instantiating the pcie_link_blocker
template inside the link_blockers group of exp_link. Each blocker must
implement the abstract is_blocking() -> (bool) method. If any blocker
returns true, the link is considered blocked. When a condition that is
returned by an implemented blocker changes, the
update_link_blocking() method MUST be called
to re-evaluate the link blocking status and disable the link if necessary.
The library provides one built-in blocker for Upstream Components on a link:
link_disable_bit_blocker, which blocks the link when the Link Disable bit
in the Link Control register is set. The library also provides one built-in
blocker for both Upstream and Downstream Components on a link:
link_disable_api, which blocks the link when the link_disabled variable
is set to true.
Example of adding custom link blockers:
group link_blockers {
group my_blocker is pcie_link_blocker {
method is_blocking() -> (bool) {
return some_condition;
}
}
}