diff --git a/hw/Makefile b/hw/Makefile index c5a87f9316a23..238e348a7a005 100644 --- a/hw/Makefile +++ b/hw/Makefile @@ -28,6 +28,7 @@ IPS ?= aes \ keymgr_dpe \ kmac \ lc_ctrl \ + mbx \ otbn \ otp_ctrl \ pattgen \ diff --git a/hw/ip/README.md b/hw/ip/README.md index c0d3400b15822..9cc0e2cd20abb 100644 --- a/hw/ip/README.md +++ b/hw/ip/README.md @@ -19,6 +19,7 @@ | [`keymgr_dpe`] | Manage multiple DICE sessions in a DPE-compatible way | | [`kmac`] | Accelerator for Keccak-based keyed hash message authentication code and SHA-3 hash functions; with SCA and FI countermeasures | | [`lc_ctrl`] | Manages device life cycle states and transitions, and controls key manager, flash, OTP, and debug access | +| [`mbx`] | DOE mailbox for use as an integrated OpenTitan communication channel. | | [`otbn`] | Programmable coprocessor for asymmetric cryptography with SCA and FI countermeasures | | [`otp_ctrl`] | Interfaces integrated one-time programmable memory, supports scrambling, integrity and secure wipe | | [`pattgen`] | Transmission of short time-dependent data patterns on two clock-parallel output channels | @@ -53,6 +54,7 @@ [`keymgr_dpe`]: ./keymgr_dpe/README.md [`kmac`]: ./kmac/README.md [`lc_ctrl`]: ./lc_ctrl/README.md +[`mbx`]: ./mbx/README.md [`otbn`]: ./otbn/README.md [`otp_ctrl`]: ./otp_ctrl/README.md [`pattgen`]: ./pattgen/README.md diff --git a/hw/ip/mbx/README.md b/hw/ip/mbx/README.md new file mode 100644 index 0000000000000..f80f81fea59b8 --- /dev/null +++ b/hw/ip/mbx/README.md @@ -0,0 +1,562 @@ +# OpenTitan Secure Mailbox Interface (secure_mailbox_spec) + +## Scope + +Document a proposal for the secure mailbox communication channel for the Integrated OpenTitan project. + +## Glossary + +| Term | Description | +|-------|---------------------------------------------------------| +| PCIe | PCI Express | +| DOE | Data Object Exchange | +| CXL | Compute Express Link | +| MCTP | Management Component Transport Protocol | +| SPDM | Security Protocol and Data Model | +| CMA | Component Measurement and Attestation | +| DMTF | formerly known as the Distributed Management Task Force | +| OT | OpenTitan | +| RoT | Root Of Trust | +| CTN | ConTrol Network | +| SoC | System On Chip | +| SCS | System Control Subsystem | +| IOMMU | Input Output Memory Management Unit | +| ECR | Engineering Change Request | +| ECN | Engineering Change Notification | + +## Overview + +The integrated version of OpenTitan Root-Of-Trust may provide security services to the SoC such as: + +- Encryption or decryption of data blobs. +- Cryptographic hashing of data blobs. +- Key derivation. +- Random seed generation service. +- Public Key ( PK ) signing. +- PK verification. +- Root of Trust for Measurement, Reporting and Storage. +- Secure Firmware update. +- Access to secure storage. +- Mutual authentication / attestation services. +- SoC security monitoring / book-keeping services. +- Debug authentication / unlock service. + +The SoC Host (or other SoC Execution Engines) may request these services from OpenTitan. +This means that pre-defined control information must be passed to OpenTitan from the security service requester. + +A secure inbound/outbound mailbox is defined to enable the exchange of such control information between the root-of-trust and the security service requester. +This proposal adopts the [PCIe specification defined Data-Object-Exchange](https://members.pcisig.com/wg/PCI-SIG/document/18363) mailbox protocol as the OpenTitan Integrated mailbox communication channel. + +As an example, a DMA controller may be used in conjunction with the newly defined OpenTitan mailbox interface. +The mailbox interface is used to pass pointers to data blobs external to the OT RoT and request operations via pre-defined command objects. + +## Secure Mailbox Interface + +![](doc/mbx_interface.svg) + +### Mailbox Terminology + +- **Requester**\ +*Typically System Host or an SoC firmware agent that would request a security service via predefined DOE objects.* + +- **Responder**\ +*Entity that processes the DOE request object and generates a DOE response in case one is expected for the original request.*\ +OpenTitan would generally have the responder role; however there may be use cases where OpenTitan is a DOE requester. + +- **OT Mailbox registers (Inbox/Outbox regs)**\ +*PCIe DOE specification defined registers used for reading and writing to the mailbox.*\ +These registers may be mapped into the PCIe Config space for the System Host to access the mailbox via the PCIe defined mechanism. +Accesses from other SoC firmware based agents may not be mapped into the config address space. +The access mechanism and relevant address space for such agents is defined by the SoC integrator. +*(See the PCI Express Base Specification 6.0 section 7.9.24 for further details.)* + +- **OT Inbox Memory**\ +*Memory within the OpenTitan RoT secure perimeter that is allocated to the mailbox mechanism to store data objects passed from System/SoC mailbox writer.*\ +System/SoC mailbox writer can write this memory via mailbox interface registers only. +OpenTitan Ibex core may have direct read/write access to this memory. +Please refer to the section below for inbox memory implementation options. + +- **OT Outbox Memory**\ +*Memory within the OpenTitan RoT secure perimeter that is allocated to the mailbox mechanism to store data objects passed from OT to System/SoC mailbox reader.*\ +System/SoC mailbox reader can read this memory via mailbox interface registers only. +OpenTitan Ibex core may have direct read/write access to this memory. +Please refer to the section below for outbox memory implementation options. + +- **DOE mailbox instance**\ +*A collection of the mailbox registers, inbound mailbox memory and outbound mailbox memory that can be used to exchange objects between OT and an SoC agent.*\ +A separate mailbox instance is required for each uncoordinated SoC agent that communicates with Integrated OpenTitan via the mailbox mechanism. +OpenTitan may arbitrate between (read or write) objects from each DOE mailbox instance in a simple round robin fashion. +Note that this is firmware controlled based on pending mailbox interrupts. + +- **Inbox / Outbox Handler**\ +*Hardware widget to move mailbox data back and forth between mailbox registers and corresponding memories.* + +### Mailbox Memory Topology Options + +The following options for memory topologies should be considered for a mailbox implementation: + +#### **Dedicated Memory within mailbox instance** + +![](doc/dedicated_memory.svg) + +✅ Pros + +- Each mailbox instance has access to its own inbox memory space. +- No memory arbitration required when mailbox is written. +- Easy to prevent IBEX instruction fetch port from accessing mailbox memory (prevent address decode) + +❌ Cons + +- Difficult to implement memory protection mechanisms such as scrambling. + +#### **Shared Mailbox Memory within mailbox wrapper** + +![](doc/local_shared_memory.svg) + +- local write port, read access by memory +- (separate instance than RoT private memory) + +✅ Pros + +- Easier to implement memory protection schemes such as scrambling, if desired. +- Mailboxes not an initiator on TL-UL fabric; additional access control not required. +- Easy to prevent IBEX instruction fetch port from accessing mailbox memory (prevent address decode). + +❌ Cons + +- Memory controller may need dedicated arbitration logic. + +#### **Shared Mailbox Memory On RoT Fabric** + +![](doc/separate_shared_memory.svg) + +- (separate instance than RoT private memory) + +✅ Pros + +- Memory access arbitration handled by fabric. +- Easier to implement memory protection schemes such as scrambling, if desired. +- Easy to prevent IBEX instruction fetch port from accessing mailbox memory (prevent address decode). + +❌ Cons + +- Mailbox port is an initiator port on the fabric; requires additional security access control mechanisms such as IOPMP (access range protection registers etc). + +#### **Shared Mailbox Memory (carved out of existing RoT memory)** + +![](doc/carved_shared_memory.svg) + +- *Note that this option is not preferred* + +✅ Pros + +- Easier to manage memory size requirements (flexible range setup). + +❌ Cons + +- Hard address decode based prevention of IBEX instruction fetch from this memory not possible (mixed with IBEX code memory) + +### Mailbox Basics + +- PCIe specification section 6.30 defines the (optional) Data Object Exchange mechanism.\ +It is accessed using a PCIe extended capability defined in section 7.9.24 that provides a Mailbox for data exchange. +- All inbound and outbound mailbox communication takes place via a set of mailbox registers + - [DOE Control Register](#doe-control-register) + - [DOE Status Register](#doe-status-register) + - [DOE Write Data Mailbox Register](#doe-write-data-mailbox-register) + - [DOE Read Data Mailbox Register](#doe-read-data-mailbox-register) +- [DOE Control Register](#doe-control-register) and [DOE Status Register](#doe-status-register) are used by the requester and the responder to perform a valid handshake protocol while transferring objects back and forth. +- An entity that supports the DOE mechanism is permitted to specify its own vendor defined data objects. +- DOE Inbox is used by a ‘requester’ to query the supported data object formats + - A mailbox instance is not required to support all DOE object formats. + - PCIe Vendor ID and an object format index number together uniquely identify the supported object format. + - Mailbox requester FW/SW knows how to interpret the data object format(s) of interest. + - DOE specification defined query mechanism enables Mailbox requester to check if the DOE instance supports the data object format of interest. +- DOE Inbox is used by a ‘requester’ to write supported data objects into the DOE mailbox. + - DOE mailbox drops the object in case it is an unsupported format or does not match the expected format. + - Each mailbox write is a non-posted transaction and expects a response back. + - [DOE Write Data Mailbox Register](#doe-write-data-mailbox-register) is used to write the inbox, one DWORD (32 bit) at a time. +- DOE Outbox is used by the responder to populate a response, in case one is expected +- DOE instances have the ability to send an interrupt to notify that an object response is ready. Note that a PCIe compatible DOE mailbox instance shall have the ability to send an MSI interrupt. +- Various error handling scenarios are described later in this document. + +Following is a basic mailbox read / write sequence: + +1. System host consults the DOE Busy bit in the [DOE Status Register](#doe-status-register) to check if the DOE instance is free..\ +Busy being Clear indicates that the mailbox instance is not being actively used and is ready to accept new requests.\ +2. System host writes the entire data object one DWORD at a time via the [DOE Write Data Mailbox Register](#doe-write-data-mailbox-register). + - The inbox handler places each DWORD into the appropriate location of the OT inbox memory to assemble the object being transferred. +3. System host sets the DOE Go bit in [DOE Control Register](#doe-control-register) + - The inbox handler generates an interrupt to notify the responder (e.g. OpenTitan Ibex core) to start parsing the transferred object. + - The parser consumes the DOE request from the DOE mailbox. +4. After successful processing, the responder (OT Ibex core) generates a response, in case one is expected. + - OT host places the response in the outbox memory. + - OT host sets the Data Object Ready bit. + - DOE outbox handler generates a notification interrupt, if supported and enabled. +5. System host waits for an interrupt if applicable.\ +Upon receiving an interrupt, it checks the [DOE Status Register](#doe-status-register).*ready* bit to see if the object is ready.\ +Alternatively, if an interrupt is not supported, it polls the [DOE Status Register](#doe-status-register).*ready* bit.\ +Note that SoC level power management schemes and related interrupt delivery mechanism are outside the scope of this document.\ +Any logic required to support SoC wake from deeper power management states for interrupt delivery shall be implemented at the SoC level at the time of integration. +6. If the ready bit is Set: + - System host reads data from the [DOE Read Data Mailbox Register](#doe-read-data-mailbox-register) one DWORD at a time. + - For each DWORD, it writes the [DOE Read Data Mailbox Register](#doe-read-data-mailbox-register) to indicate a successful read. + - The process is repeated until the entire object is read. + +**Note**: Sequence above assumes System Host as the requester of the mailbox object write. +In this case a PCIe compatible DOE mailbox instance is implemented. +Similar mechanism and sequence would apply for communication with other SoC firmware controllers. +For such a DOE mailbox instance, a fully PCIe compatible implementation is not required. +See [below](#system-level-use-cases) for more details. + +**Note**: Please refer to the [PCIe Specification](https://members.pcisig.com/wg/PCI-SIG/document/18363) for more detailed and up to date information on the PCIe compatible DOE Mailbox operation basics. + +### Integrated OpenTitan Usage Of DOE Mailbox Mechanism + +Integrated OpenTitan shall: + +- Adopt the basic mechanisms as defined in the PCIe specification. +- Support one or more DOE mailbox instances. +- Support design options to allow configuration of number of mailbox instances depending upon the needs of the integrating SoC. +- Support the following Interrupt mechanisms: + - A firmware based mechanism to generate an interrupt.\ +Such a mechanism may require a method to ‘write’ to a predefined address in the appropriate address space (System, CTN or other) via the corresponding port of a DMA controller.\ +Example: Write to a location within system address space via the [SYS port of the Integrated OT DMA controller](../dma/README.md) to generate an MSI-interrupt for PCIe compatible DOE instance(s). + - Wired interrupt output(s) for each DOE instance.\ +Such a mechanism may be applied for DOE instances assigned to agents that support wired interrupt mechanism. + - Depending upon the application & PCIe compatibility requirement for the instance, SoC may decide to use the wired interrupt or firmware based MSI interrupt mechanism at the time of SoC integration.\ +Alternatively, for PCIe compatible DOE mailbox instance, an SoC may also decide to convert the wired interrupt into an MSI via dedicated hardware support at the SoC level. +- Not required to support a dedicated mutex mechanism for more than one agent to access the same DOE instance. +- Object Definition; Vendor ID + +The integrating SoC shall: + +- For the DOE instance dedicated to System Host: + - Provide the plumbing to map into PCIe Config address space. + - Provide the plumbing necessary to route the MSI interrupt to the appropriate location in the SoC’s system address space. +- For mailboxes dedicated to other SoC FW agents: + - Provide an access controlled path for the SoC FW Agent to access the DOE registers. + - Provide the plumbing necessary to route the MSI interrupt to the appropriate location in the SoC’s relevant address space (System, CTN or otherwise). +- For Access Control: + - Provide proper protection to make sure that no requester other than the one to which the mailbox is assigned, is able to access the mailbox registers. + - May be static or dynamic based on SoC level access mechanisms. +- Regarding assignment of more than one coordinated requesters / agents to the same DOE instance + - If the SoC chooses to do so, it will be the responsibility of the cooperating agents to get ownership of the DOE mailbox in a SoC defined synchronization mechanism.\ +For example, consider the case where there is more than one physical instance of SoC firmware based controllers implementing a common function like power management.\ +In such a case, the two power management controllers may acquire a software mutex or a hardware mutex, as defined and implemented by SoC. + +### Mailbox Use Cases + +#### Interprocessor Communication + +A basic usage of the DOE mailbox mechanism is to achieve Interprocessor communication (IPC) between the OpenTitan based SoC root of trust and other SoC firmware based controllers. +An example scenario is as follows: + +Such an IPC mechanism may be deployed during a system secure boot up operation. +Here OT RoT has the responsibility to fetch firmware images from external flash, verify / measure & them and place in appropriate memory locations on the SoC that are protected via access control mechanisms to prevent further modification of firmware images. +Other firmware based controllers may be configured to boot from such a memory location. +Proper reset sequencing, voltage, clock and other configuration of the SoC region / subsystem under consideration may be needed prior to this operation. +An SoC power management controller (PMC) may be responsible for these activities. +OT RoT and the PMC may need to work together to complete the operation to bootstrap the firmware based controller of a subsystem under consideration. +OT RoT and the PMC may exchange messages via DOE based mailbox mechanism through predefined message data objects to achieve this coordination. +More coordination may be required during runtime as well and may continue to use the mailboxes for further communication beyond boot time operation. + +In another example, more than one OT RoT instance may exist within an SoC - for example an RoT on each chiplet in an SoC compiled of multiple chiplets. +Such RoTs may have a primary - secondary relationship, and may require communication to perform different operations. +A dedicated DOE mailbox channel and predefined DOE objects may be assigned for such communication. + +#### System level use cases + +DOE mailbox is used to perform various security protocols like SPDM based component measurement and authentication. + +#### PCIe component measurement + +A PCIe device, either discrete or integrated within a system, may need to establish a security trust level with the appropriate software running on the System Host for various security based applications like: + +- Allowing a datacenter operator to query the state of the platform via remote mechanisms before deploying any workloads to the platform.\ +Such mechanisms may involve cryptographic measurements of the hardware components including any unique device bindings and firmware running on that platform, and attestation to a local (e.g sideband management controller) or a remote (e.g. datacenter operator) agent.\ +An OpenTitan-based root of trust integrated within a PCIe device may participate in such an authentication protocol, with the responsibility to create such measurements and present them to the system software upon request via a standard PCIe based DOE mailbox communication channel. +- Similar authentication scheme may be used for PCIe device hot plug support (adding new cards during system runtime) where the system software may decide to include the hot plugged device within its operation only upon successfully attesting to a local agent or a remote agent. + +#### Setup of Trusted Execution Environments + +An SoC may participate in providing Trusted Execution environments. +An integrated root of trust would have the responsibility to bring up, perform secure boot, measure, establish the proper chain of trust in order to bootstrap the trusted execution environment. +Trusted Software would use the DOE mailbox communication channel to establish secure protocols with the additional components (e.g. a new PCIe virtual function) before allowing them into the trusted execution boundary. + +#### Generic security services + +An integrated OpenTitan RoT may provide generic security services to the SoC or system as a whole, such as secure storage, secure firmware update, PK crypto based certificate signing, encryption/decryption. +Such operations require exchange of information (commands as well as data) back and forth between the requesters of these security services and OT. +DOE mailbox mechanism with properly defined DOE objects supporting such information exchange is envisioned to be the method to expose these security services to other components. + +## Data Object Definition + +### PCIe defined + +Following are the PCIe specification defined object types that shall be supported by the OT DOE implementation: +Vendor ID 0x0001, Data Object Types 0x00, 0x01, 0x02, 0x03, 0x04 and 0x05. + +### OT defined + +An example DOE mapping for integrated OpenTitan can be found [here](./doc/DOE.md) + +## External DOE Registers + +Note: The DOE mailbox is accessible in two different forms: + +1. PCIe compatible DOE mailbox - exposed as part of the PCIe configuration space.\ +The [DOE Extended Capability Header](#doe-extended-capability-header) and the [DOE Capability Header](#doe-capability-header) registers are supported for such a DOE mailbox instance only. +2. Firmware to Firmware communication mailboxes - uses the same underlying mechanism to transferring data objects between a firmware requester and a firmware responder.\ +However such mailboxes do not require the DOE capability registers.\ +For such mailbox instances, these address offsets are utilized to specify / configure the address of the register where a doorbell interrupt shall be sent as well as the corresponding data to be sent. + +### DOE Extended Capability Header + +| | | +|----------|--------------------------------| +| Register | DOE Extended Capability Header | +| Width | 32 bits | +| Offset | 0x00 | +| Access | RO | + +| Bit Pos | Bit Definition | Notes | +|---------|------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 15:0 | Capability ID | PCIe defined Capability ID. Should read value of 0x002E for DOE capability | +| 19:16 | Capability Version | Second version; Value of 2 | +| 31:20 | Next Capability Offset | Offset to the next capability structure. Equal to zero if no other items exist. Can be tool generated to link to other extended capabilities that may be supported. | + + +### DOE Capability Header + +| | | +|----------|-----------------------| +| Register | DOE Capability Header | +| Width | 32 bits | +| Offset | 0x04 | +| Access | RO | + +| Bit Pos | Bit Definition | Notes | +|---------|------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 0 | DOE Interrupt Support | One when interrupts are supported. In the PCIe world, this means MSI/MSI-x support. | +| 11:1 | DOE Interrupt Message number | When value is 0: Responder only support MSI (not MSI-X); and same message data for all interrupts including the DOE interrupt. MSI address/data pairs are configured in the MSI or MSI-X capability of the PCIe function hosting the mailbox. When MSI-X is implemented, this message number indexes into the table of address/data pairs to determine the one to use. **Note:** Non-PCIe DOE mailboxes (e.g. Firmware - Firmware communication mailbox) may use the wired interrupt capability. [Please see above for interrupt support.](#integrated-opentitan-usage-of-doe-mailbox-mechanism) | +| 31:12 | Reserved | | + +### DOE Interrupt Message Address Register + +| | | +|----------|----------------------------------------| +| Register | DOE Interrupt Message Address Register | +| Width | 32 bits | +| Offset | 0xFIXME | +| Access | RW | + +| Bit Pos | Bit Definition | Notes | +|---------|----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 31:0 | Interrupt Register Address | Defined only in case of a firmware to firmware mailbox communication. Utilized by the mailbox responder to send an interrupt message to the requester via a write to the configured address. Note that such FW 2 FW mailboxes may primarily be accessible in the CTN address space. As such the configured address is part of the SoC CTN address space. | + +### DOE Interrupt Message Data Register + +| | | +|----------|----------------------------------------| +| Register | DOE Interrupt Message Address Register | +| Width | 32 bits | +| Offset | 0xFIXME | +| Access | RW | + +| Bit Pos | Bit Definition | Notes | +|---------|-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 31:0 | Interrupt Register Data | Interrupt message data to be sent to the address configured in the [DOE Interrupt Message Address Register](#DOE-Interrupt-Message-Address-Register) | + +### DOE Control Register + +| | | +|----------|----------------------| +| Register | DOE Control Register | +| Width | 32 bits | +| Offset | 0xFIXME | +| Access | RW | + +Please refer to Table 7-315 of the PCIe specification for detailed information on this register. + +| Bit Pos | Bit Definition | Notes | +|---------|--------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 0 | DOE Abort | Abort all data object transfer operations once a value of 1’b1 is written to this field. Reads for this field always return 0. | +| 1 | DOE Interrupt Enable | When set, DOE instance is enabled to send an interrupt. Also see the [interrupt support mechanisms here](#Integrated-OpenTitan-Usage-Of-DOE-Mailbox-Mechanism). | +| 2 | | TBD | +| 3 | DOE Async Message Enable | Adds capability for the responder to send asynchronous messages to the requester. (N.B. This capability is part of a PCIe ECR that is not in the released specification yet) | +| 4:30 | Reserved | | +| 31 | DOE Go | Indicates that the DOE object transferred via the [DOE Write Data Mailbox Register](#DOE-Write-Data-Mailbox-Register) is ready for consumption. Behavior is undefined if Go bit is set prior to transferring the entire object. Behavior is undefined if Go bit is set while DOE busy bit is asserted. Read of this bit always returns a zero. | + +### DOE Status Register + +| | | +|----------|---------------------| +| Register | DOE Status Register | +| Width | 32 bits | +| Offset | 0xFIXME | +| Access | RW | + +Please refer to Table 7-316 from the PCIe specification for details on this register. + +| Bit Pos | Bit Definition | Notes | +|---------|--------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 0 | DOE Busy | This bit is set by the DOE instance when it is busy processing a received data object. When set, indicates that the DOE Instance is busy and cannot accept a new data object via the [DOE Write Data Mailbox Register](#DOE-Write-Data-Mailbox-Register). This bit must be set by the DOE instance while processing an abort command. Cleared when abort handling is complete. | +| 1 | DOE Interrupt Status | An interrupt, if enabled, is generated to indicate that a data object (response) is ready for the requester to be consumed or DOE error is set, or DOE busy bit is cleared (i.e. ready to accept new objects). This bit is set when such an interrupt is asserted. Bit is a write-1-to-clear bit i.e. writing a value of 1 to this bit clears the status bit. | +| 2 | DOE Error | Set by the DOE instance if an internal error occurs with processing of a received data object or an unsupported data object. Bit is cleared by writing a 1’b1 to the DOE abort bit in the [DOE Control Register](#doe-control-register). DOE Abort is the only mechanism to clear this status bit. | +| 3 | DOE Async Message Status | Set by the responder when there are one or more asynchronous messages ready to be transferred to the requester. Note that this capability is part of a PCIe ECR that is not in the released specification yet. | +| 4 | | TBD | +| 5:30 | Reserved | | +| 31 | DOE Object Ready | Indicates that the DOE object (response) is ready to be ready by the System Host via the [DOE Read Data Mailbox Register](#DOE-Read-Data-Mailbox-Register). DOE instance shall clear this bit once the entire data object is ready by system software and no further objects are ready for transfer. DOE instance shall clear this bit in response to a DOE Abort handling, if not already clear. Read of this bit always returns a zero. Default value is 1’b0. | + +### DOE Write Data Mailbox Register + +| | | +|----------|---------------------| +| Register | DOE Status Register | +| Width | 32 bits | +| Offset | 0xFIXME | +| Access | RW | + +Please refer to Table 7-317 in the PCIe specification for details on this register. + +| Bit Pos | Bit Definition | Notes | +|---------|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 0:31 | DOE Write Data DWORD | A DOE object is transferred to a DOE instance by writing to this register 1 DWORD at a time. A successful write adds one DWORD to the data object being assembled in the DOE instance. A write of 1’b1 to the DOE Go bit in the DOE Control Register marks the completion of the data transfer i.e final DWORD for the object has been written. | + +### DOE Read Data Mailbox Register + +| | | +|----------|---------------------| +| Register | DOE Status Register | +| Width | 32 bits | +| Offset | 0xFIXME | +| Access | RW | + +Please refer to Table 7-317 in the PCIe specification for details on this register. + +| Bit Pos | Bit Definition | Notes | +|---------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 0:31 | DOE Read Data DWORD | A DOE object is read by system software from a DOE instance by reading this register 1 DWORD at a time, once the DOE Object Ready bit is set by the DOE instance in the DOE status register. A write of any value to this register indicates a successful read of the current DWORD. The following read from this register shall return to the next DWORD from the data object being read. Back to back data objects can be transferred while the DOE Object ready bit is set. If Data Object Ready bit is clear, writes of any value to this register must have no effect and a read from this register must return zeros. | + +## OpenTitan Internal DOE Registers + +The following registers are visible in the OT internal private address +space only. As such, these registers are accessible to the firmware +running on IBEX core only + +### Inbox Write Pointer Register + +| | | +|----------|------------------------------| +| Register | Inbox Write Pointer Register | +| Width | 32 bits | +| Offset | 0xFIXME | +| Access | RW | + +| Bit Pos | Bit Definition | Notes | +|---------|----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 0:31 | Wr Pointer | Pointer to a location within the Inbox memory where the next DWORD will be written. Pointer is initialized to the Inbox memory base address before the start of a transfer. Inbox handler maintains the updated pointer as data DWORDS are received by the DOE inbox. | + +### Outbox Read Pointer Register + +| | | +|----------|------------------------------| +| Register | Outbox Read Pointer Register | +| Width | 32 bits | +| Offset | 0xFIXME | +| Access | RW | + +| Bit Pos | Bit Definition | Notes | +|---------|----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 0:31 | Rd Pointer | Pointer to a location within the Outbox memory from where the next DWORD will be read. Pointer is initialized to the Outbox memory base address before the start of a outgoing object transfer. Outbox handler maintains the updated pointer as data DWORDS are read from the DOE instance by the requester. | + +### DOE Inbox Memory Range Base Register + +| | | +|----------|--------------------------------------| +| Register | DOE Inbox Memory Range Base Register | +| Width | 32 bits | +| Offset | 0xFIXME | +| Access | RW | + +| Bit Pos | Bit Definition | Notes | +|---------|------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------| +| 0:31 | Memory Range Base Addr | Base Address to mark the start of the enabled memory range within the OT internal memory space. Note that this address must be DWORD aligned. | + +### DOE Inbox Memory Range Limit Register + +| | | +|----------|---------------------------------------| +| Register | DOE Inbox Memory Range Limit Register | +| Width | 32 bits | +| Offset | 0xFIXME | +| Access | RW | + +| Bit Pos | Bit Definition | Notes | +|---------|-------------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| 0:31 | Memory Range Limit Addr | Limit to mark the end of the DOE Inbox memory range within the OT internal memory space. Note that this address must be DWORD aligned. | + +### DOE Outbox Memory Range Base Register + +| | | +|----------|---------------------------------------| +| Register | DOE Outbox Memory Range Base Register | +| Width | 32 bits | +| Offset | 0xFIXME | +| Access | RW | + +| Bit Pos | Bit Definition | Notes | +|---------|------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------| +| 0:31 | Memory Range Base Addr | Base Address to mark the start of the DOE outbox memory range within the OT internal memory space. Note that this address must be DWORD aligned. | + +### DOE Outbox Memory Range Limit Register + +| | | +|----------|----------------------------------------| +| Register | DOE Outbox Memory Range Limit Register | +| Width | 32 bits | +| Offset | 0xFIXME | +| Access | RW | + +| Bit Pos | Bit Definition | Notes | +|---------|-------------------------|-----------------------------------------------------------------------------------------------------------------------------------------| +| 0:31 | Memory Range Limit Addr | Limit to mark the end of the DOE Outbox memory range within the OT internal memory space. Note that this address must be DWORD aligned. | + +### DOE Memory Range Register LockEnable + +| | | +|----------|--------------------------------------| +| Register | DOE Memory Range Register LockEnable | +| Width | 32 bits | +| Offset | 0xFIXME | +| Access | RW | + +| Bit Pos | Bit Definition | Notes | +|---------|----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 0 | Lock | Global Lock bit. Once Set, the inbox and outbox memory range registers shall not be further modifiable. The lock bit, once set, can be cleared only upon next reset assertion | +| 1 | Enable | Global Enable bit. Once Set, the inbox and outbox memory range registers (base and limit) shall be active. | +| 2:31 | Reserved | | + +### DOE Outbox Object Size Register + +| | | +|----------|---------------------------------| +| Register | DOE Outbox Object Size Register | +| Width | 32 bits | +| Offset | 0xFIXME | +| Access | RW | + +| Bit Pos | Bit Definition | Notes | +|---------|----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 0:9 | Size | Indicates the size of the data object to be transferred out. Written by the Responder firmware. Used by Outbox handler to track the size of the message to be read. Note that this size specifies the number of DWORDs to be read. Maximum size supported by any OT DOE instance is 1K DWORDS. (Note that maximum permitted size is 256K DWORDS) | +| 10:31 | Reserved | | + +## Appendix + +A few important constraints from the PCIe specification for correct mailbox operation and error handling. +Please refer to the PCIe specification section 6.30 for detailed information, specifically the sections titled: + +- Response time +- Abort function +- Error handling diff --git a/hw/ip/mbx/data/BUILD b/hw/ip/mbx/data/BUILD new file mode 100644 index 0000000000000..d252a50b4bc27 --- /dev/null +++ b/hw/ip/mbx/data/BUILD @@ -0,0 +1,30 @@ +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +package(default_visibility = ["//visibility:public"]) + +load( + "//rules:autogen.bzl", + "autogen_hjson_c_header", + "autogen_hjson_rust_header", +) + +autogen_hjson_c_header( + name = "mbx_c_regs", + srcs = [ + "mbx.hjson", + ], +) + +autogen_hjson_rust_header( + name = "mbx_rust_regs", + srcs = [ + "mbx.hjson", + ], +) + +filegroup( + name = "all_files", + srcs = glob(["**"]), +) diff --git a/hw/ip/mbx/data/mbx.hjson b/hw/ip/mbx/data/mbx.hjson new file mode 100644 index 0000000000000..ecad9597e9a23 --- /dev/null +++ b/hw/ip/mbx/data/mbx.hjson @@ -0,0 +1,488 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +# DOE register template +{ + name: "mbx" + human_name: "DOE Mailbox" + one_line_desc: "DOE mailbox for use as an integrated OpenTitan communication channel." + one_paragraph_desc: ''' + The SoC (i.e., the SoC Host CPU or other SoC Execution Engines) may request security services from OpenTitan. + This means that pre-defined control information must be passed to OpenTitan from the security service requester. + A secure inbound/outbound mailbox can be used to implement a secure channel for these request/response transactions between the SoC and OpenTitan. + ''' + cip_id: "37" + design_spec: "../doc" + dv_doc: "../doc/dv" + version: "0.1.0" + + clocking: [{clock: "clk_i", reset: "rst_ni", primary: true}] + bus_interfaces: [ + { protocol: "tlul", direction: "device", name: "core" } + { protocol: "tlul", direction: "host", name: "sram" } + { protocol: "tlul", direction: "device", name: "soc" } + ] + inter_signal_list: [ + { struct: "logic", + type: "uni", + name: "doe_intr_support", + act: "req", + package: "", + default: "1'b0" + } + { struct: "logic", + type: "uni", + name: "doe_intr_en", + act: "req", + package: "", + default: "1'b0" + } + { struct: "logic", + type: "uni", + name: "doe_intr", + act: "req", + package: "", + default: "1'b0" + } + { struct: "logic", + type: "uni", + name: "doe_async_msg_support", + act: "req", + package: "", + default: "1'b0" + } + ] + interrupt_list: [ + { name: "mbx_ready" + desc: "A new object was received in the inbound mailbox." + } + { name: "mbx_abort" + desc: "An abort request was received from the requester." + } + { name: "mbx_error" + desc: "The mailbox instance generated an error." + } + ] + alert_list: [ + { name: "fatal_fault" + desc: "This fatal alert is triggered when a fatal TL-UL bus integrity fault is detected." + } + { name: "recov_fault", + desc: "This recoverable alert is triggered when memory with invalid ECC (e.g., uninitialized memory) or at an invalid address is accessed." + } + ] + countermeasures: [ + { name: "BUS.INTEGRITY" + desc: "End-to-end bus integrity scheme." + } + { name: "ADDRESS_RANGE.CONFIG.REGWEN_MUBI", + desc: "Private SRAM memory range is software multibit lockable." + } + ] + regwidth: "32" + registers: { + core: [ + { name: "CONTROL" + desc: "DOE mailbox control register visible to OpenTitan" + hwext: "true" + fields: [ + { name: "abort" + desc: "Alias of the DoE mailbox abort bit" + bits: "0" + resval: "0x0" + swaccess: "rw" + hwaccess: "hrw" + hwqe: "true" + } + { name: "error" + desc: ''' + Set by firmware to signal an error, e.g. unable to provide a response to request. + Set by hardware, on SYS.WDATA or SYS.RDATA performing an invalid access. + Cleared by the hardware when SYS sets CONTROL.ABORT. + ''' + bits: "1" + resval: "0x0" + swaccess: "rw" + hwaccess: "hrw" + hwqe: "true" + } + { name: "sys_async_msg" + desc: "Indicates an async message request" + bits: "3" + resval: "0x0" + swaccess: "wo" + hwaccess: "hro" + hwqe: "true" + } + ] + tags: ["excl:CsrAllTests:CsrExclAll"] // alias of another register; no automated CSR tests applicable + } + { name: "STATUS" + desc: "DOE mailbox status register visible to OpenTitan" + hwext: "true" + fields: [ + { name: "busy" + desc: "Alias of the DoE mailbox busy bit" + bits: "0" + resval: "0x1" + swaccess: "ro" + hwaccess: "hwo" + } + { name: "sys_intr_state" + desc: "Alias of the DoE mailbox interrupt status bit" + bits: "1" + resval: "0x0" + swaccess: "ro" + hwaccess: "hwo" + } + { name: "sys_intr_enable" + desc: "Alias of the DoE mailbox interrupt enable bit" + bits: "2" + resval: "0x0" + swaccess: "ro" + hwaccess: "hwo" + } + { name: "sys_async_enable" + desc: "Alias of the DoE mailbox async message support enable bit" + bits: "3" + resval: "0x0" + swaccess: "ro" + hwaccess: "hwo" + } + ] + tags: ["excl:CsrAllTests:CsrExclAll"] // alias of another register; no automated CSR tests applicable + } + { name: "ADDRESS_RANGE_REGWEN" + desc: "Used to lock the inbound/outbound base/limit configuration registers." + swaccess: "rw0c" + hwaccess: "none" + fields: [ + { name: "regwen" + desc: ''' + Once cleared the mailbox inbound/outbound base/limit registers will be locked until the next reset. + + Default Value = kMultiBitBool4True -> Unlocked at reset. + ''' + bits: "3:0" + mubi: "true" + resval: true + } + ] + tags: ["excl:CsrNonInitTests:CsrExclAll"] // only check initial value because writing will have side effects + } + { name: "ADDRESS_RANGE_VALID" + desc: "Used to mark the inbound/outbound base/limit configuration registers to have a valid configuration." + swaccess: "rw" + hwaccess: "hro" + hwqe: "true" + fields: [ + { name: "range_valid" + desc: "Once set the mailbox inbound/outbound base/limit registers are valid." + bits: "0" + resval: "0" + } + ] + tags: ["excl:CsrAllTests:CsrExclAll"] // TODO(#477) + } + { name: "INBOUND_BASE_ADDRESS" + desc: '''Base address of SRAM region, which is used to back up the inbound mailbox data. + This address is 4-byte aligned, the lower 2 bits are ignored. + ''' + regwen: "ADDRESS_RANGE_REGWEN" + swaccess: "rw" + hwaccess: "hro" + fields: [ + { name: "base_address" + desc: "Base address of SRAM region, which is used to back up the inbound mailbox data." + bits: "31:2" + resval: "0x0" + } + ] + tags: ["excl:CsrAllTests:CsrExclAll"] // TODO(#477) + } + { name: "INBOUND_LIMIT_ADDRESS" + desc: '''Inclusive end address of the inbound mailbox memory range in the private SRAM. + This address is 4-byte aligned and it specifies the start address of the final valid + DWORD location. The lower 2 bits are ignored. + ''' + regwen: "ADDRESS_RANGE_REGWEN" + swaccess: "rw" + hwaccess: "hro" + fields: [ + { name: "limit" + desc: "Limit Address to mark the end of the inbound mailbox memory range in the private SRAM." + bits: "31:2" + resval: "0x0" + } + ] + tags: ["excl:CsrAllTests:CsrExclAll"] // TODO(#477) + } + { name: "INBOUND_WRITE_PTR" + desc: '''Write pointer for the next inbound data write. + This pointer is 4-byte aligned, the lower 2 bits are always zero. + ''' + hwext: "true" + fields: [ + { name: "inbound_read_ptr" + desc: "Write pointer for the next inbound data write." + bits: "31:2" + resval: "0x0" + swaccess: "ro" + hwaccess: "hwo" + } + ] + tags: ["excl:CsrAllTests:CsrExclAll"] // TODO(#477) + } + { name: "OUTBOUND_BASE_ADDRESS" + desc: '''Base address of SRAM region, which is used to buffer the outbound mailbox data. + This address is 4-byte aligned, the lower 2 bits are ignored. + ''' + regwen: "ADDRESS_RANGE_REGWEN" + swaccess: "rw" + hwaccess: "hro" + fields: [ + { name: "base_address" + desc: "Base address of SRAM region, which is used to buffer the outbound mailbox data." + bits: "31:2" + resval: "0x0" + } + ] + tags: ["excl:CsrAllTests:CsrExclAll"] // TODO(#477) + } + { name: "OUTBOUND_LIMIT_ADDRESS" + desc: '''Inclusive end address of the outbound mailbox memory range in the private SRAM. + This address is 4-byte aligned and it specifies the start address of the final valid + DWORD location. The lower 2 bits are ignored. + ''' + regwen: "ADDRESS_RANGE_REGWEN" + swaccess: "rw" + hwaccess: "hro" + fields: [ + { name: "limit" + desc: "Limit Address to mark the end of the outbound mailbox memory range in the private SRAM." + bits: "31:2" + resval: "0x0" + } + ] + tags: ["excl:CsrAllTests:CsrExclAll"] // TODO(#477) + } + { name: "OUTBOUND_READ_PTR" + desc: '''Read pointer for the next outbound data read. + This pointer is 4-byte aligned, the lower 2 bits are always zero. + ''' + hwext: "true" + fields: [ + { name: "outbound_write_ptr" + desc: "Read pointer for the next outbound data read." + bits: "31:2" + resval: "0x0" + swaccess: "ro" + hwaccess: "hwo" + } + ] + tags: ["excl:CsrAllTests:CsrExclAll"] // TODO(#477) + } + { name: "OUTBOUND_OBJECT_SIZE" + desc: '''Indicates the size of the data object to be transferred out. + Note that this size specifies the number of 4-byte words (DWORD). + Maximum size supported by any OT DOE instance is 1K DWORDS. + ''' + fields: [ + { name: "cnt" + desc: "Indicates the size of the data object to be transferred out in 4-byte words." + bits: "10:0" + resval: "0x0" + swaccess: "rw" + hwaccess: "hrw" + hwqe: "true" + } + ] + tags: ["excl:CsrAllTests:CsrExclAll"] // TODO(#477) + } + { name: "DOE_INTR_MSG_ADDR" + desc: ''' + Software read-only alias of the DOE_INTR_MSG_ADDR register of the SoC interface for convenient access of the OT firmware. + Defined only for FW-to-FW mailboxes. + ''' + hwext: "true" + hwaccess: "hwo" + swaccess: "ro" + fields: [ + { name: "doe_intr_msg_addr" + desc: "Utilized by the mailbox responder to send an interrupt message to the requester via a write to the configured address." + bits: "31:0" + resval: "0x0" + } + ] + tags: ["excl:CsrAllTests:CsrExclAll"] // TODO(#477) + } + { name: "DOE_INTR_MSG_DATA" + desc: ''' + Software read-only alias of the DOE_INTR_MSG_DATA register of the SoC interface for convenient access of the OT firmware. + Defined only for FW-to-FW mailboxes. + ''' + hwext: "true" + hwaccess: "hwo" + swaccess: "ro" + fields: [ + { name: "doe_intr_msg_data" + desc: "Interrupt message data to be sent to the address configured in the DOE_INTR_MSG_ADDR register." + bits: "31:0" + resval: "0x0" + } + ] + tags: ["excl:CsrAllTests:CsrExclAll"] // TODO(#477) + } + ] + soc: [ + { skipto: "0x8" } + { name: "SOC_CONTROL" + desc: "DOE mailbox control register." + hwaccess: "hrw" + hwqe: "true" + hwext: "true" + fields: [ + { name: "abort" + desc: ''' + A write of 1 to this bit causes all data object transfer operations associated with this DOE instance to be aborted. + ''' + bits: "0" + resval: "0x0" + swaccess: "wo" + } + { name: "doe_intr_en" + desc: ''' + If DOE interrupt support is set, when this bit is set and MSI/MSI-X is enabled in MSI capability registers, the DOE instance must issue an MSI/MSI-X interrupt. + ''' + bits: "1" + resval: "0x0" + swaccess: "rw" + } + { name: "doe_async_msg_en" + desc: ''' + If DOE Async Message Support is Set, this bit, when Set, enables the use of the DOE Async Message mechanism. + When this bit is set, it allows the DOE instance to raise the SOC_STATUS.doe_async_msg_status, indicating an asnchronous message request. + ''' + bits: "3" + resval: "0x0" + swaccess: "rw" + } + { name: "go" + desc: ''' + A write of 1 to this bit indicates to the DOE instance that it can start consuming the data object transferred through the DOE Write Data Mailbox register. + ''' + bits: "31" + resval: "0x0" + swaccess: "wo" + } + ] + tags: ["excl:CsrNonInitTests:CsrExclAll"] // only check initial value because writing will have side effects + } + { name: "SOC_STATUS" + desc: "DOE mailbox status register" + fields: [ + { name: "busy" + desc: ''' + When Set, this bit indicates the DOE instance is temporarily unable to receive a new data object through the DOE Write Data Mailbox register. + ''' + bits: "0" + resval: "0x1" + swaccess: "ro" + hwaccess: "hrw" + } + { name: "doe_intr_status" + desc: "This bit is set when an interrupt event occurs." + bits: "1" + resval: "0x0" + swaccess: "rw1c" + hwaccess: "hrw" + } + { name: "error" + desc: ''' + When Set, this bit indicates that there has been an internal error associated with data object received, or that a data object has been received for which the DOE instance is unable to provide a response. + The transition of this bit from Clear to Set is an interrupt triggering event. + ''' + bits: "2" + resval: "0x0" + swaccess: "ro" + hwaccess: "hrw" + } + { name: "doe_async_msg_status" + desc: ''' + This bit, when Set, indicates the DOE instance has one or more asynchronous messages to transfer. + The transition of this bit from Clear to Set is an interrupt triggering event.This bit is set when an interrupt event occurs. + ''' + bits: "3" + resval: "0x0" + swaccess: "ro" + hwaccess: "hwo" + } + { name: "ready" + desc: ''' + When Set, this bit indicates the DOE instance has a data object available to be read by SoC firmware/software. + The transition of this bit from Clear to Set is an interrupt triggering event. + ''' + bits: "31" + resval: "0x0" + swaccess: "ro" + hwaccess: "hrw" + } + ] + tags: ["excl:CsrAllTests:CsrExclAll"] // TODO(#477) + } + { window: { + name: "WDATA" + items: "1" + swaccess: "wo" + desc: '''DOE mailbox write data register. + The DOE instance receives data objects via writes to this register. + A successful write adds one DWORD to the data object being assembled in the DOE instance. + A write of 1 to the DOE Go bit in the DOE Control Register marks the completion of the data transfer, i.e, final DWORD for the object has been written. + ''' + } + } + { window: { + name: "RDATA" + desc: '''DOE mailbox read data register + If the Data Object Ready bit is Set, a read of this register returns the current DW of the data object. + A write of any value to this register indicates a successful read of the current DWORD. + The next read to this register shall return to the next DW from the data object being read + ''' + items: "1" + swaccess: "rw" + } + } + { name: "SOC_DOE_INTR_MSG_ADDR" + desc: ''' + Utilized by the mailbox responder to send an interrupt message to the requester via a write to the configured address. + Defined only for FW-to-FW mailboxes. + ''' + swaccess: "rw" + hwaccess: "hro" + fields: [ + { name: "doe_intr_msg_addr" + desc: "Utilized by the mailbox responder to send an interrupt message to the requester via a write to the configured address." + bits: "31:0" + resval: "0x0" + } + ] + } + { name: "SOC_DOE_INTR_MSG_DATA" + desc: ''' + Interrupt message data to be sent to the address configured in the DOE_INTR_MSG_ADDR register. + Defined only for FW-to-FW mailboxes. + ''' + swaccess: "rw" + hwaccess: "hro" + fields: [ + { name: "doe_intr_msg_data" + desc: "Interrupt message data to be sent to the address configured in the DOE_INTR_MSG_ADDR register." + bits: "31:0" + resval: "0x0" + } + ] + } + ] + } +} diff --git a/hw/ip/mbx/data/mbx_sec_cm_testplan.hjson b/hw/ip/mbx/data/mbx_sec_cm_testplan.hjson new file mode 100644 index 0000000000000..cab79175ca590 --- /dev/null +++ b/hw/ip/mbx/data/mbx_sec_cm_testplan.hjson @@ -0,0 +1,39 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// Security countermeasures testplan extracted from the IP Hjson using reggen. +// +// This testplan is auto-generated only the first time it is created. This is +// because this testplan needs to be hand-editable. It is possible that these +// testpoints can go out of date if the spec is updated with new +// countermeasures. When `reggen` is invoked when this testplan already exists, +// It checks if the list of testpoints is up-to-date and enforces the user to +// make further manual updates. +// +// These countermeasures and their descriptions can be found here: +// .../mbx/data/mbx.hjson +// +// It is possible that the testing of some of these countermeasures may already +// be covered as a testpoint in a different testplan. This duplication is ok - +// the test would have likely already been developed. We simply map those tests +// to the testpoints below using the `tests` key. +// +// Please ensure that this testplan is imported in: +// .../mbx/data/mbx_testplan.hjson +{ + testpoints: [ + { + name: sec_cm_bus_integrity + desc: "Verify the countermeasure(s) BUS.INTEGRITY." + stage: V2S + tests: [] + } + { + name: sec_cm_address_range_config_regwen_mubi + desc: "Verify the countermeasure(s) ADDRESS_RANGE.CONFIG.REGWEN_MUBI." + stage: V2S + tests: [] + } + ] +} diff --git a/hw/ip/mbx/data/mbx_testplan.hjson b/hw/ip/mbx/data/mbx_testplan.hjson new file mode 100644 index 0000000000000..21f0d82dbfb01 --- /dev/null +++ b/hw/ip/mbx/data/mbx_testplan.hjson @@ -0,0 +1,44 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + name: "mbx" + import_testplans: [ + hw/dv/tools/dvsim/testplans/alert_test_testplan.hjson + hw/dv/tools/dvsim/testplans/csr_testplan.hjson + hw/dv/tools/dvsim/testplans/intr_test_testplan.hjson + hw/dv/tools/dvsim/testplans/stress_all_with_reset_testplan.hjson + hw/dv/tools/dvsim/testplans/tl_device_access_types_testplan.hjson + mbx_sec_cm_testplan.hjson + ] + testpoints: [ + { name: mbx_smoke + desc: ''' + Smoke test + ''' + stage: V1 + tests: ["mbx_smoke"] + } + { name: mbx_stress + desc: ''' + Stress test + ''' + stage: V2 + tests: ["mbx_stress", "mbx_stress_zero_delays"] + } + { name: mbx_stress_all + desc: ''' + Run the other tests in random order while injecting TL errors and running automated CSR tests in parallel. + ''' + stage: V2 + tests: ["mbx_stress_all"] + } + { name: mbx_imbx_oob + desc: ''' + Test accesses that are out of bounds for the inbound mailbox. + ''' + stage: V2 + tests: ["mbx_imbx_oob"] + } + ] +} diff --git a/hw/ip/mbx/doc/DOE.md b/hw/ip/mbx/doc/DOE.md new file mode 100644 index 0000000000000..fbefc785f7a2b --- /dev/null +++ b/hw/ip/mbx/doc/DOE.md @@ -0,0 +1,1090 @@ +# OpenTitan-defined DOE objects + +Please refer to the following pages for additional details on the DMA controller. +- [OpenTitan DMA Controller specification](../../dma/README.md) + +## Simple DMA transfer request + +Requester specifies Source address, Destination address, source space ID and Destination space ID as required by the DMA transfer operation. +Integrated OpenTitan Host firmware parses the object, checks the request and configures the OT DMA controller if it deems the transfer to be OK. + +Response Object: [Response DOE object expected](#simple-dma-completion-response-object) + +Interrupt: Can be configured to generate interrupt to Requester, if enabled, once DMA transfer complete. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
3130292827262524232221201918171615141312111009080706050403020100Byte Offset
ReservedObject Data Type=0x0Vendor ID=<OT TBD>0x0
ReservedLength = 0x8 DWORDS0x4
ReservedOpcode = <SimpleDMA> + +Reserved\[15:10\], + +Dest Addr Space ID\[9:8\] + + + +Reserved\[7:2\], + +Source Addr Space ID\[1:0\] + +0x8
Total Data size  (in bytes)0xC
Source Address Low0x10
+ +Source Address High (Used only if targeting 64 bit +address space; else all zero) + +0x14
Destination Address Low0x18
+ +Destination Address High (Used only if targeting 64 +bit address space; else all zero) + +0x1C
+ +## Simple DMA completion response object + +DMA transfer completion response to the requester. +Response object conveys the status of the operation. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
3130292827262524232221201918171615141312111009080706050403020100Byte Offset
ReservedObject Data Type= 0x0Vendor ID=<OT TBD>0x0
ReservedLength = 0x3 DWORDS0x4
+ +DMA Completion Status {Error Code, Error/Success, +Done}; \[exact format TBD\] + +0x8
+ +## Firmware Update request + +Used by System Host software to initiate a firmware update request. +System software places the firmware update blob in the system memory. +Requester specifies Source address pointer to memory. The source address space ID is fixed toSystem). +Destination address shall be ignored by the responder. +Destination address space ID is fixed to 0x03 (FLASH). +Note that the assumption here is that OT RoT firmware is in control and aware of the Flash map. + +Integrated OpenTitan Host firmware parses the object, checks the request and configures the OT DMA controller if it deems the transfer to be OK to write the update binary into an appropriate location of the OT flash, cryptographically verifies the update candidate blob , then initiates and completes the final flash update operation upon successful verification. + +Response Object: [Response DOE object expected](#simple-dma-completion-response-object) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
3130292827262524232221201918171615141312111009080706050403020100Byte Offset
ReservedObject Data Type= 0x1Vendor ID=<OT TBD>0x0
ReservedLength = 0x8 DWORDS0x4
+ +Interrupt to Requester  = <Yes/No> + + + +Opcode = <FirmwareUpdate> + +Reserved\[15:08\],Reserved\[7:0\],0x8
Total Data size (in bytes)0xC
Source Address Low0x10
+ +Source Address High (Used only if targeting 64 bit +address space; else all zero) + +0x14
Reserved0x18
Reserved0x1C
+ +## Firmware Update Ready for Reset Notification + +Notify Requester, to system HOST that it is ready for system reset to complete the update operation. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
3130292827262524232221201918171615141312111009080706050403020100Byte Offset
ReservedObject Data Type=0x1Vendor ID=<OT TBD>0x0
ReservedLength = 0x3 DWORDS0x4
+ +Ready for Reset / Error \[exact format TBD\] + +0x8
+ +## Secure Storage Read Proxy + +Used by Requester (System Host or SoC firmware elements) to request the OT RoT to provide proxy access to the flash storage owned by the OT RoT. +Requester initiates a read request via the DOE object. +Responder performs the corresponding flash operation and sends the contents of the flash back to the requester via a DMA operation. + +Requester sets the following parameters in the request object: + +Source address: Pointer to the flash storage address from where data is read +Source address ID: = 0x3 (FLASH) +Destination address: memory location where NV storage read data shall be placed +Destination address ID: System memory or CTN memory, based on requester requirements +Total Size: size of the data block (in bytes) requested + +OT shall initiate the read access if it’s security policy allows the section of flash to be read by the requester + +Response Object: [Response DOE object expected](#secure-storage-read-completion-response) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
3130292827262524232221201918171615141312111009080706050403020100Byte Offset
ReservedObject Data Type=0x2Vendor ID=<OT TBD>0x0
ReservedLength = 0x8 DWORDS0x4
+ +Interrupt Requester  = <Yes/No> + + + +Opcode = <NVStorageRead> + + + +Reserved\[15:08\], + +Dest Addr Space ID\[1:0\] + +Reserved\[7:0\],0x8
Total Data size (in bytes)0xC
Source Address Low0x10
+ +Source Address High (Used only if targeting 64 bit +address space; else all zero) + +0x14
Destination Address Low0x18
+ +Destination Address High (Used only if targeting 64 +bit address space; else all zero) + +0x1C
+ +## Secure Storage Read Completion response + +Proxy Read completion response to the requester. +Response object conveys the status of the operation. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
3130292827262524232221201918171615141312111009080706050403020100Byte Offset
ReservedObject Data Type=0x2Vendor ID=<OT TBD>0x0
ReservedLength = 0x3 DWORDS0x4
+ +Secure Storage Read Completion Status {Error Code, +Error/Success, Done}; \[exact format TBD\] + +0x8
+ +## Secure Storage Write Proxy + +Used by Requester (System Host or SoC firmware elements) to request the OT RoT to provide proxy access to the flash storage owned by the OT RoT. +The contents to be written to the flash are placed by the requester in a SoC memory location & passes the write request along with memory pointers via the DOE object. +Responder performs the security checks, initiates a DMA transfer of the contents to be written to flash and completes the flash write operation. + +Requester sets the following parameters in the request object: + +Destination address: Pointer to the flash storage address from where data is read +Destination address ID: = 0x3 (FLASH) +Source address: memory location where NV storage read data shall be placed +Source address ID: System memory or CTN memory, based on requester requirements +Total Size: size of the data block to be written + +OT shall initiate the write access if it’s policy allows the section of flash to be written + +Response Object: [Response DOE object expected](#secure-storage-write-completion-response) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
3130292827262524232221201918171615141312111009080706050403020100Byte Offset
ReservedObject Data Type=0x3Vendor ID=<OT TBD>0x0
ReservedLength = 0x8 DWORDS0x4
+ +Interrupt Requester  = <Yes/No> + + + +Opcode = <NVStorageWrite> + + + +Reserved\[15:08\], + +Dest Addr Space ID\[1:0\] + +Reserved\[7:0\],0x8
Total Data size0xC
Source Address Low0x10
+ +Source Address High (Used only if targeting 64 bit +address space; else all zero) + +0x14
Destination Address Low0x18
+ +Destination Address High (Used only if targeting 64 +bit address space; else all zero) + +0x1C
+ +## Secure Storage Write Completion response + +Proxy Read completion response to the requester. +Response object conveys the status of the operation. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
3130292827262524232221201918171615141312111009080706050403020100Byte Offset
ReservedObject Data Type=0x3Vendor ID=<OT TBD>0x0
ReservedLength = 0x3 DWORDS0x4
+ +Secure Storage Write Completion Status {Error Code, +Error/Success, Done}; \[exact format TBD\] + +0x8
+ +## Secure Authenticated Debug Unlock Request + +Used by Requester (System Host, SoC firmware agents or JTAG proxy) to initiate debug authentication & unlock request. +OT shall initiate the crypto challenge-response protocol with the authorizing agent; System Host software or an alternate agent (implementation specific) may provide network access to the remote authorizing agent. +OT shall send a signed challenge token to the remote agent. +The remote agent shall send a signed response. +OT verifies the response & sets the corresponding feature unlock command to other SoC components using the [OT debug unlock bus]() + +Requester sets the following parameters in the request object: + +Destination Address: Pointer to the memory location where signed challenge is placed in response to this Req +Unlock Category: +- Requester sets the desired unlock category +- Provides up to 16 different debug categories; Each category may define its own Debug feature unlock privileges +- Unlocking / enabling specific debug / DFT features in a category is SoC defined + +Response Object: +- Signed Challenge Token; DOE response is generated only if DOE Rsp is set to YES +- Debug authentication via JTAG proxy channel may use the DOE response object scheme. +- System Host based authentication may benefit from DMA transfer to system memory. +- If DOE response is set to NO, then signed challenge is placed in the destination point using the OT DMA operation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
3130292827262524232221201918171615141312111009080706050403020100Byte Offset
ReservedObject Data Type=0x4Vendor ID=<OT TBD>0x0
ReservedLength = 0x8 DWORDS0x4
+ +DOE Rsp = <Yes/No> + + + +Send INT = <Yes/No> + + + +Opcode = <AuthDebugUnlockReq> + + + +Reserved\[15:08\], + +Dest Addr Space ID\[1:0\] + +ReservedUnlock Category0x8
Destination Buffer Size0xC
Reserved0x10
Reserved0x14
Destination Address Low0x18
+ +Destination Address High (Used only if targeting 64 +bit address space; else all zero) + +0x1C
+ +## DOE Response - Debug Unlock Challenge + +OT sends this DOE response message in response to the [Secure Authenticated Debug Unlock Request](#secure-authenticated-debug-unlock-request) (if DOE Rsp is set to YES) to initiate the crypto challenge-response protocol with the authorizing agent. +Requester (JTAG proxy or system software) shall read the token via DOE outbox and relay to the remote agent. +The remote agent shall send a signed unlock token in response. +OT verifies the response & sets the corresponding feature unlock command to other SoC components using the [OT debug unlock bus](). + +Responder (OT RoT) sets the following parameters in the request object: + +Nonce: OT generates a NONCE; Size TBD; Embeds within the object. +Unlock Category: +- Sets the desired unlock category based on the original request. +- Provides up to 16 different debug categories; Each category may define its own Debug feature unlock privileges +- Unlocking / enabling specific debug / DFT features in a category is SoC defined +Signature: Size TBD; Algorithm TBD. + +OT generates a digital signature using a private key over the header, NONCE & the Requested unlock category. +Embeds the digital signature within the Response object. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
3130292827262524232221201918171615141312111009080706050403020100Byte Offset
ReservedObject Data Type=0x5Vendor ID=<OT TBD>0x0
Reserved + +Length = <TBD>  DWORDS + +0x4
+ +<TBD> <Signed Debug Unlock Challenge Token +format> + +0x8
0xC
0x10
0x14
0x18
0x1C
+ +## DOE Request Object - Debug Unlock Response + +System Software receives a response to the Debug Unlock Challenge from the remote server. +System software sends this Unlock response to the OT RoT through this “DOE Request Object - Debug Unlock Response”. +The signed response contains the Unlock Category that the RoT is allowed to set (same as the original request of a lower category, as determined by the remote authentication agent). +Upon successful verification of the response token, the RoT sets the corresponding unlock category via the [OT debug unlock bus](). + +The Object Contains the following format: + +- DOE Header (2 DWORDS) +- Debug Unlock Response Token: Size and Format TBD. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
3130292827262524232221201918171615141312111009080706050403020100Byte Offset
ReservedObject Data Type=0x5Vendor ID=<OT TBD>0x0
Reserved + +Length = <TBD>  DWORDS + +0x4
+ +<TBD> <Signed Debug Unlock Response Token +format> + +0x8
0xC
0x10
0x14
0x18
0x1C
+ +## DOE TPM-like Service objects + +Placeholder for defining DOE objects to provide TPM like services. + +One or more just object definitions may be needed for TPM interface. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
3130292827262524232221201918171615141312111009080706050403020100Byte Offset
ReservedObject Data Type=0x5Vendor ID=<OT TBD>0x0
Reserved + +Length = <TBD>  DWORDS + +0x4
+ +<TBD> <Signed Debug Unlock Challenge Token +format> + +0x8
0xC
0x10
0x14
0x18
0x1C
diff --git a/hw/ip/mbx/doc/carved_shared_memory.svg b/hw/ip/mbx/doc/carved_shared_memory.svg new file mode 100644 index 0000000000000..a85c3dba7e386 --- /dev/null +++ b/hw/ip/mbx/doc/carved_shared_memory.svg @@ -0,0 +1,509 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw/ip/mbx/doc/dedicated_memory.svg b/hw/ip/mbx/doc/dedicated_memory.svg new file mode 100644 index 0000000000000..ae74d08478c52 --- /dev/null +++ b/hw/ip/mbx/doc/dedicated_memory.svg @@ -0,0 +1,367 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw/ip/mbx/doc/local_shared_memory.svg b/hw/ip/mbx/doc/local_shared_memory.svg new file mode 100644 index 0000000000000..9a432b1f07ea3 --- /dev/null +++ b/hw/ip/mbx/doc/local_shared_memory.svg @@ -0,0 +1,406 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw/ip/mbx/doc/mbx_interface.svg b/hw/ip/mbx/doc/mbx_interface.svg new file mode 100644 index 0000000000000..049a020b85e7d --- /dev/null +++ b/hw/ip/mbx/doc/mbx_interface.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/hw/ip/mbx/doc/separate_shared_memory.svg b/hw/ip/mbx/doc/separate_shared_memory.svg new file mode 100644 index 0000000000000..d8b530e2af877 --- /dev/null +++ b/hw/ip/mbx/doc/separate_shared_memory.svg @@ -0,0 +1,539 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw/ip/mbx/dv/env/mbx_env.core b/hw/ip/mbx/dv/env/mbx_env.core new file mode 100644 index 0000000000000..f83c6e66102d8 --- /dev/null +++ b/hw/ip/mbx/dv/env/mbx_env.core @@ -0,0 +1,44 @@ +CAPI=2: +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:dv:mbx_env:0.1" +description: "MBX DV UVM environment" +filesets: + files_dv: + depend: + - lowrisc:ip:mbx + - lowrisc:dv:ralgen + - lowrisc:dv:cip_lib + files: + - mbx_mem_ral_pkg.sv + - mbx_env_pkg.sv + - mbx_env_cfg.sv: {is_include_file: true} + - mbx_env_cov.sv: {is_include_file: true} + - mbx_virtual_sequencer.sv: {is_include_file: true} + - mbx_seq_item.sv: {is_include_file: true} + - mbx_scoreboard.sv: {is_include_file: true} + - mbx_env.sv: {is_include_file: true} + - seq_lib/mbx_tl_reg_seq.sv: {is_include_file: true} + - seq_lib/mbx_tl_device_seq.sv: {is_include_file: true} + - seq_lib/mbx_base_vseq.sv: {is_include_file: true} + - seq_lib/mbx_common_vseq.sv: {is_include_file: true} + - seq_lib/mbx_smoke_vseq.sv: {is_include_file: true} + - seq_lib/mbx_vseq_list.sv: {is_include_file: true} + - seq_lib/mbx_stress_vseq.sv: {is_include_file: true} + - seq_lib/mbx_imbx_oob_vseq.sv: {is_include_file: true} + file_type: systemVerilogSource + +generate: + ral: + generator: ralgen + parameters: + name: mbx + ip_hjson: ../../data/mbx.hjson + +targets: + default: + filesets: + - files_dv + generate: + - ral diff --git a/hw/ip/mbx/dv/env/mbx_env.sv b/hw/ip/mbx/dv/env/mbx_env.sv new file mode 100644 index 0000000000000..fe727489d1dec --- /dev/null +++ b/hw/ip/mbx/dv/env/mbx_env.sv @@ -0,0 +1,29 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class mbx_env extends cip_base_env #( + .CFG_T (mbx_env_cfg), + .COV_T (mbx_env_cov), + .VIRTUAL_SEQUENCER_T(mbx_virtual_sequencer), + .SCOREBOARD_T (mbx_scoreboard) + ); + + `uvm_component_utils(mbx_env) + + `uvm_component_new + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + + if (!uvm_config_db#(intr_vif)::get(this, "", "intr_soc_vif", cfg.intr_soc_vif) && + cfg.num_interrupts > 0) begin + `uvm_fatal(get_full_name(), "failed to get intr_soc_vif from uvm_config_db") + end + endfunction: build_phase + + function void connect_phase(uvm_phase phase); + super.connect_phase(phase); + endfunction: connect_phase + +endclass: mbx_env diff --git a/hw/ip/mbx/dv/env/mbx_env_cfg.sv b/hw/ip/mbx/dv/env/mbx_env_cfg.sv new file mode 100644 index 0000000000000..eaa9d456b30ce --- /dev/null +++ b/hw/ip/mbx/dv/env/mbx_env_cfg.sv @@ -0,0 +1,55 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class mbx_env_cfg extends cip_base_env_cfg #( + .RAL_T(mbx_core_reg_block) +); + string mbx_mem_ral_name = "mbx_mem_reg_block"; + string mbx_soc_ral_name = "mbx_soc_reg_block"; + + virtual pins_if #(NUM_MAX_INTERRUPTS) intr_soc_vif; + + `uvm_object_utils(mbx_env_cfg) + + function new(string name = ""); + super.new(name); + has_devmode = 0; + endfunction: new + + virtual function void initialize(bit [31:0] csr_base_addr = '1); + list_of_alerts = mbx_env_pkg::LIST_OF_ALERTS; + // RAL model for the memory TL interface + ral_model_names.push_back(mbx_mem_ral_name); + ral_model_names.push_back(mbx_soc_ral_name); + + super.initialize(csr_base_addr); + + // TODO: Revisit the configuration parameters for tl_agent_cfg + // scxb_mbx_core_cfg + m_tl_agent_cfgs[mbx_soc_ral_name].max_outstanding_req = 16; + m_tl_agent_cfgs[mbx_soc_ral_name].if_mode = dv_utils_pkg::Host; + + // agxb_mbx_core_cfg + m_tl_agent_cfgs[RAL_T::type_name].max_outstanding_req = 16; + m_tl_agent_cfgs[RAL_T::type_name].if_mode = dv_utils_pkg::Host; + + // mbx_agxb TL I/F + m_tl_agent_cfgs[mbx_mem_ral_name].max_outstanding_req = 16; + m_tl_agent_cfgs[mbx_mem_ral_name].if_mode = dv_utils_pkg::Device; + + endfunction: initialize + + virtual function dv_base_reg_block create_ral_by_name(string name); + if (name == RAL_T::type_name) begin + return super.create_ral_by_name(name); + end else if (name == mbx_mem_ral_name) begin + return mbx_mem_reg_block::type_id::create(mbx_mem_ral_name); + end else if (name == mbx_soc_ral_name) begin + return mbx_soc_reg_block::type_id::create(mbx_soc_ral_name); + end else begin + `uvm_error(`gfn, $sformatf("%0s is an illegal RAL model name", name)) + end + endfunction: create_ral_by_name + +endclass: mbx_env_cfg diff --git a/hw/ip/mbx/dv/env/mbx_env_cov.sv b/hw/ip/mbx/dv/env/mbx_env_cov.sv new file mode 100644 index 0000000000000..0cfff71c21f01 --- /dev/null +++ b/hw/ip/mbx/dv/env/mbx_env_cov.sv @@ -0,0 +1,32 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +/** + * Covergoups that are dependent on run-time parameters that may be available + * only in build_phase can be defined here + * Covergroups may also be wrapped inside helper classes if needed. + */ + +class mbx_env_cov extends cip_base_env_cov #(.CFG_T(mbx_env_cfg)); + `uvm_component_utils(mbx_env_cov) + + // the base class provides the following handles for use: + // mbx_env_cfg: cfg + + // covergroups + // [add covergroups here] + + function new(string name, uvm_component parent); + super.new(name, parent); + // [instantiate covergroups here] + endfunction : new + + virtual function void build_phase(uvm_phase phase); + super.build_phase(phase); + // [or instantiate covergroups here] + // Please instantiate sticky_intr_cov array of objects for all interrupts that are sticky + // See cip_base_env_cov for details + endfunction + +endclass diff --git a/hw/ip/mbx/dv/env/mbx_env_pkg.sv b/hw/ip/mbx/dv/env/mbx_env_pkg.sv new file mode 100644 index 0000000000000..21eb925bde3e6 --- /dev/null +++ b/hw/ip/mbx/dv/env/mbx_env_pkg.sv @@ -0,0 +1,65 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +package mbx_env_pkg; + // dep packages + import uvm_pkg::*; + import top_pkg::*; + import dv_utils_pkg::*; + import dv_lib_pkg::*; + import dv_base_reg_pkg::*; + import prim_mubi_pkg::*; + import mbx_core_ral_pkg::*; + import mbx_soc_ral_pkg::*; + import mbx_mem_ral_pkg::*; + import tl_agent_pkg::*; + import cip_base_pkg::*; + import dv_base_reg_pkg::*; + import csr_utils_pkg::*; + import mem_model_pkg::*; + + // macro includes + `include "uvm_macros.svh" + `include "dv_macros.svh" + + // parameters + parameter int unsigned NUM_ALERTS = 2; + parameter string LIST_OF_ALERTS[] = {"fatal_fault", "recov_fault"}; + + parameter int unsigned MBX_DV_DW_SIZE_BYTES = 4; + parameter int unsigned MBX_DV_MAX_DW = 1024; + + // Addresses used by the mailbox DUT. + typedef bit [top_pkg::TL_AW-1:0] mbx_addr_t; + // Mailbox specification is in terms of 32-bit DWORDs. + typedef bit [31:0] mbx_dword_t; + + `include "mbx_seq_item.sv" + + + typedef int unsigned uint_t; + typedef enum bit { + READ = 0, + WRITE = 1 + } reg_op_e; + typedef enum { + MbxCoreReady, + MbxCoreAbort, + MbxCoreError + } mbx_core_intr_e; + typedef enum { + MbxSocDOE + } mbx_soc_intr_e; + + // functions + + // package sources + `include "mbx_env_cfg.sv" + `include "mbx_env_cov.sv" + `include "mbx_virtual_sequencer.sv" + `include "mbx_scoreboard.sv" + `include "mbx_env.sv" + `include "mbx_vseq_list.sv" + +endpackage: mbx_env_pkg diff --git a/hw/ip/mbx/dv/env/mbx_mem_ral_pkg.sv b/hw/ip/mbx/dv/env/mbx_mem_ral_pkg.sv new file mode 100644 index 0000000000000..6b5258ba8bd9d --- /dev/null +++ b/hw/ip/mbx/dv/env/mbx_mem_ral_pkg.sv @@ -0,0 +1,66 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +package mbx_mem_ral_pkg; + + import uvm_pkg::*; + import dv_base_reg_pkg::*; + + `include "uvm_macros.svh" + + typedef int unsigned uint; + typedef class mbx_mem; + typedef class mbx_mem_reg_block; + + class mbx_mem #(parameter int unsigned MemDepth = 32) extends dv_base_mem; + + `uvm_object_param_utils(mbx_mem#(MemDepth)) + + function new(string name = "mbx_mem", + longint unsigned size = MemDepth, + int unsigned n_bits = 32, + string access = "RW", + int has_coverage = UVM_NO_COVERAGE); + super.new(name, size, n_bits, access, has_coverage); + endfunction : new + + endclass : mbx_mem + + + class mbx_mem_reg_block extends dv_base_reg_block; + // memories + // TODO: The dv_reg_base_block uses int to calculate max_offset in max2 function. + // To map the complete range, 30 bits are required but int type supports at max only 31 bits. + rand mbx_mem #(uint'(2 ** 29)) m_mem; + + `uvm_object_param_utils(mbx_mem_reg_block) + + function new(string name = "mbx_mem_reg_block", + int has_coverage = UVM_NO_COVERAGE); + super.new(name, has_coverage); + endfunction : new + + virtual function void build(uvm_reg_addr_t base_addr, + csr_excl_item csr_excl = null); + // create default map + this.default_map = create_map(.name("default_map"), + .base_addr(base_addr), + .n_bytes(4), + .endian(UVM_LITTLE_ENDIAN)); + if (csr_excl == null) begin + csr_excl = csr_excl_item::type_id::create("csr_excl"); + this.csr_excl = csr_excl; + end + + // create memories + m_mem = mbx_mem#(uint'(2 ** 29))::type_id::create("m_mem"); + m_mem.configure(.parent(this)); + default_map.add_mem(.mem(m_mem), + .offset(32'h0), + .rights("RW")); + + endfunction : build + endclass : mbx_mem_reg_block + +endpackage diff --git a/hw/ip/mbx/dv/env/mbx_scoreboard.sv b/hw/ip/mbx/dv/env/mbx_scoreboard.sv new file mode 100644 index 0000000000000..afbc5e17fdfdc --- /dev/null +++ b/hw/ip/mbx/dv/env/mbx_scoreboard.sv @@ -0,0 +1,537 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class mbx_scoreboard extends cip_base_scoreboard #( + .CFG_T(mbx_env_cfg), + .RAL_T(mbx_core_reg_block), + .COV_T(mbx_env_cov) + ); + `uvm_component_utils(mbx_scoreboard) + + // local variables + bit [top_pkg::TL_AW-1:0] m_ibmbx_ptr; + bit [top_pkg::TL_AW-1:0] m_obmbx_ptr_q[$]; + bit [top_pkg::TL_AW-1:0] m_obmbx_ptr; + bit [9:0] m_obdwcnt; + bit mbxsts_core_wr_in_progress; + bit mbxsts_system_rd_in_progress; + bit mbxsts_core_rd_in_progress; + bit mbxsts_system_wr_in_progress; + + bit skip_read_check; + bit exp_mbx_core_irq; + bit exp_mbx_core_irq_q[$]; + + // TLM agent fifos + + // local queues to hold incoming packets pending comparison + bit [top_pkg::TL_DW-1:0] m_ib_q[$]; + bit [top_pkg::TL_DW-1:0] m_ob_q[$]; + + // System RAL model + mbx_soc_reg_block m_mbx_soc_ral; + + `uvm_component_new + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + endfunction + + function void connect_phase(uvm_phase phase); + super.connect_phase(phase); + endfunction + + virtual task monitor_core_interrupt(); + `uvm_info(`gfn, "monitor_core_interrupt -- Start", UVM_DEBUG) + // `DV_CHECK_CASE_EQ(exp_mbx_core_irq, cfg.intr_vif.pins, "Default state of interrupt pin is 0") + forever begin + @(cfg.intr_vif.pins); + `uvm_info(`gfn, $sformatf("Change in interrupt pin('b%b)", cfg.intr_vif.pins), UVM_LOW) + `DV_CHECK_CASE_EQ(exp_mbx_core_irq, cfg.intr_vif.pins[MbxCoreReady], + "Exp. interrupt doesn't match actual") + end + `uvm_info(`gfn, "monitor_core_interrupt -- End", UVM_DEBUG) + endtask: monitor_core_interrupt + + virtual task monitor_exp_core_interrupts(); + `uvm_info(`gfn, "monitor_exp_core_interrupts -- Start", UVM_DEBUG) + fork + forever begin + bit exp_irq; + + wait(exp_mbx_core_irq_q.size() != 0); + exp_irq = exp_mbx_core_irq_q.pop_front(); + if(exp_irq == 1) begin + cfg.clk_rst_vif.wait_n_clks(2); + `DV_CHECK_EQ(exp_irq, cfg.intr_vif.pins[MbxCoreReady], + "Expecting interrupt pin to go high") + end + if(exp_irq == 0) begin + // TODO: Earlier it was set to '1', updating it to larger value for the RTL change + // to go, reduce it once the RTL is fixed. + cfg.clk_rst_vif.wait_n_clks(5); + `DV_CHECK_EQ(exp_irq, cfg.intr_vif.pins[MbxCoreReady], "Expecting interrupt pin to go low") + end + end + join_none + wait fork; + `uvm_info(`gfn, "monitor_exp_core_interrupts -- End", UVM_DEBUG) + endtask: monitor_exp_core_interrupts + + task run_phase(uvm_phase phase); + super.run_phase(phase); + `downcast(m_mbx_soc_ral, cfg.ral_models[cfg.mbx_soc_ral_name]) + // TODO: Re-enable interrupt checking once scoreboard is fully functional + //fork + // monitor_core_interrupt(); + // monitor_exp_core_interrupts(); + //join_none + endtask + + virtual task process_tl_access(tl_seq_item item, tl_channels_e channel, string ral_name); + uvm_reg csr; + bit do_read_check = 1'b1; + bit write = item.is_write(); + uvm_reg_addr_t csr_addr = cfg.ral_models[ral_name].get_word_aligned_addr(item.a_addr); + bit [31:0] mask = 'hffff_ffff; + + bit addr_phase_read = (!write && channel == AddrChannel); + bit addr_phase_write = (write && channel == AddrChannel); + bit data_phase_read = (!write && channel == DataChannel); + bit data_phase_write = (write && channel == DataChannel); + + // if access was to a valid csr, get the csr handle + if (ral_name != cfg.mbx_mem_ral_name) begin + if (csr_addr inside {cfg.ral_models[ral_name].csr_addrs}) begin + csr = cfg.ral_models[ral_name].default_map.get_reg_by_offset(csr_addr); + `DV_CHECK_NE_FATAL(csr, null) + end + else begin + // TODO: this does not yet cope with the fact that WDATA and RDATA accesses do not produce + // a hit in the above test, since they are not CSRs + // `uvm_fatal(`gfn, $sformatf("Access unexpected addr 0x%0h", csr_addr)) + return; + end + + // if incoming access is a write to a valid csr, then make updates right away + if(addr_phase_write) begin + void'(csr.predict(.value(item.a_data), .kind(UVM_PREDICT_WRITE), .be(item.a_mask))); + end + end + + if((ral_name == RAL_T::type_name) + && (csr.get_name() == "status") + && addr_phase_write) begin + mbxsts_core_wr_in_progress = 1'b1; + end + if((ral_name == RAL_T::type_name) + && (csr.get_name() == "status") + && data_phase_write) begin + mbxsts_core_wr_in_progress = 1'b0; + end + + if((ral_name == RAL_T::type_name) + && (csr.get_name() == "status") + && addr_phase_read) begin + mbxsts_core_rd_in_progress = 1'b1; + end + if((ral_name == RAL_T::type_name) + && (csr.get_name() == "status") + && data_phase_read) begin + mbxsts_core_rd_in_progress = 1'b0; + end + + if((ral_name == cfg.mbx_soc_ral_name) + && (csr.get_name() == "soc_status") + && addr_phase_read) begin + mbxsts_system_rd_in_progress = 1'b1; + end + if((ral_name == cfg.mbx_soc_ral_name) + && (csr.get_name() == "soc_status") + && data_phase_read) begin + mbxsts_system_rd_in_progress = 1'b0; + end + + if((ral_name == cfg.mbx_soc_ral_name) + && (csr.get_name() == "control") + && addr_phase_write) begin + mbxsts_system_wr_in_progress = 1'b1; + end + if((ral_name == cfg.mbx_soc_ral_name) + && (csr.get_name() == "control") + && data_phase_write) begin + mbxsts_system_wr_in_progress = 1'b0; + end + + + if((mbxsts_core_wr_in_progress + && ((ral_name == cfg.mbx_soc_ral_name) + && (csr.get_name() == "soc_status") + && data_phase_read)) + || ((ral_name == RAL_T::type_name) + && (csr.get_name() == "status") + && data_phase_read) + && (mbxsts_system_wr_in_progress)) begin + skip_read_check = 1'b1; + end + + if(((ral_name == cfg.mbx_soc_ral_name) + ||(ral_name == RAL_T::type_name)) + && ((csr.get_name() == "soc_status") + || (csr.get_name() == "status"))) begin + do_read_check = ~skip_read_check; + if(do_read_check == 1'b0) begin + `uvm_info(`gfn, + "Skipping read for status from system/core as core/system is updating it", + UVM_LOW) + end + end + +// TODO: The scoreboard needs updating and completing. +return; + + // process the csr req + // for write, update local variable and fifo at address phase + // for read, update predication at address phase and compare at data phase + case (ral_name) + RAL_T::type_name : begin + process_tl_mbx_core_access(item, channel); + end + cfg.mbx_soc_ral_name : begin + process_tl_mbx_soc_access(item, channel); + end + cfg.mbx_mem_ral_name : begin + process_tl_mbx_mem_access(item, channel); + return; + end + default: begin + `uvm_fatal(`gfn, $sformatf("invalid ral: %0s", ral_name)) + end + endcase + + // On reads, if do_read_check, is set, then check mirrored_value against item.d_data + if (data_phase_read) begin + if(csr.get_name() == "status") begin + // TODO: Remove this, once mbx doe ready bit issue is resolved + mask = 'h7fff_ffff; + end + if (do_read_check) begin + void'(csr.predict(.value(item.d_data), .kind(UVM_PREDICT_READ))); + end + + end + if(((ral_name == cfg.mbx_soc_ral_name) + || (ral_name == RAL_T::type_name)) + && (csr.get_name() == "status") + && data_phase_read) begin + skip_read_check = 1'b0; + end + endtask + + virtual function void process_tl_mbx_core_access(tl_seq_item item, tl_channels_e channel); + uvm_reg csr; + bit do_read_check = 1'b1; + bit write = item.is_write(); + uvm_reg_addr_t csr_addr = cfg.ral_models[RAL_T::type_name].get_word_aligned_addr( + item.a_addr); + + bit addr_phase_read = (!write && channel == AddrChannel); + bit addr_phase_write = (write && channel == AddrChannel); + bit data_phase_read = (!write && channel == DataChannel); + bit data_phase_write = (write && channel == DataChannel); + + `uvm_info(`gfn, "process_tl_mbx_core_access -- Start", UVM_DEBUG) + csr = cfg.ral_models[RAL_T::type_name].default_map.get_reg_by_offset(csr_addr); + + case(csr.get_name()) + default:; // Do nothing + "inbound_write_ptr" : begin + if(addr_phase_write) begin + m_ibmbx_ptr = item.a_data; + end + if(addr_phase_read) begin + void'(ral.inbound_write_ptr.predict( + .value(ral.inbound_base_address.get() + m_ibmbx_ptr), + .kind(UVM_PREDICT_READ))); + end + end + "outbound_read_ptr" : begin + if(addr_phase_write) begin + m_obmbx_ptr_q[0] = item.a_data; + m_obmbx_ptr = item.a_data; + end + if(addr_phase_read) begin + void'(ral.outbound_read_ptr.predict( + .value(ral.outbound_base_address.get() + (m_obmbx_ptr_q[0])), + .kind(UVM_PREDICT_READ))); + end + end + "inbound_base_address" : begin + // Do nothing + end + "inbound_limit_address" : begin + // Do nothing + end + "outbound_base_address" : begin + // Do nothing + end + "outbound_limit_address" : begin + // Do nothing + end + "outbound_object_size" : begin + if(addr_phase_write) begin + m_obdwcnt = item.a_data; + `uvm_info(`gfn, $sformatf("Updating m_obdwcnt to %0d", m_obdwcnt), UVM_LOW) + end + if(addr_phase_read) begin + void'(ral.outbound_object_size.predict(.value(m_obdwcnt), .kind(UVM_PREDICT_READ))); + end + end + "control" : begin + if(addr_phase_write) begin + if(ral.control.abort.get_mirrored_value() == 0) begin + exp_mbx_core_irq = 0; + exp_mbx_core_irq_q.push_back(0); + end + if(ral.control.error.get_mirrored_value() == 1) begin + // TODO: Check if busy bit is expected to be cleared - RVSCS-491 + void'(ral.status.busy.predict(.value(0), .kind(UVM_PREDICT_READ))); + end + end + end + "status" : begin + if(addr_phase_read) begin + //TODO: Review ready field + //void'(ral.status.ready.predict(.value(m_obdwcnt != 0), .kind(UVM_PREDICT_READ))); + end + // TODO: Add the support for async message sts + if(addr_phase_write) begin + if(ral.status.busy.get_mirrored_value() == 0) begin + exp_mbx_core_irq = 0; + exp_mbx_core_irq_q.push_back(0); + end + end + end + // TODO: Add interrupt registers + endcase + `uvm_info(`gfn, "process_tl_mbx_core_access -- End", UVM_DEBUG) + endfunction: process_tl_mbx_core_access + + virtual function void process_tl_mbx_soc_access(tl_seq_item item, tl_channels_e channel); + uvm_reg csr; + bit do_read_check = 1'b1; + bit write = item.is_write(); + uvm_reg_addr_t csr_addr = + cfg.ral_models[cfg.mbx_soc_ral_name].get_word_aligned_addr(item.a_addr); + + bit addr_phase_read = (!write && channel == AddrChannel); + bit addr_phase_write = (write && channel == AddrChannel); + bit data_phase_read = (!write && channel == DataChannel); + bit data_phase_write = (write && channel == DataChannel); + + `uvm_info(`gfn, "process_tl_mbx_soc_access -- Start", UVM_DEBUG) + csr = cfg.ral_models[cfg.mbx_soc_ral_name].default_map.get_reg_by_offset(csr_addr); + case(csr.get_name()) + default:; // Do nothing + "soc_control" : begin + mbx_soc_reg_soc_control ctl_reg_h; + + `DV_CHECK_FATAL($cast(ctl_reg_h, csr), "Unable to cast csr handle to MBX control type") + if(data_phase_write) begin + if(ctl_reg_h.abort.get() == 1) begin + exp_mbx_core_irq = 1; + exp_mbx_core_irq_q.push_back(1); + // TODO: Check if busy bit also needs to be set + void'(ral.control.abort.predict(.value(1), .kind(UVM_PREDICT_WRITE))); + void'(ral.status.busy.predict(.value(1), .kind(UVM_PREDICT_WRITE))); + if((ral.status.busy.get() == 1) || (ral.control.error.get() == 1) || + (m_obdwcnt != 0)) begin + void'(ral.status.busy.predict(.value(1), .kind(UVM_PREDICT_WRITE))); + end + end else if((item.a_data[31] & item.a_mask[3]) == 1) begin + // if (ctl_reg_h.abort.get() == 1) + if((ral.status.busy.get() == 0) && (ral.control.error.get() == 0) && + (ral.control.abort.get() == 0)) begin + void'(ral.status.busy.predict(.value(1), .kind(UVM_PREDICT_WRITE))); + void'(m_mbx_soc_ral.soc_status.busy.predict(.value(1), .kind(UVM_PREDICT_READ))); + + exp_mbx_core_irq = 1; + exp_mbx_core_irq_q.push_back(1); + end + end + void'(ral.status.sys_intr_enable.predict( + .value(ctl_reg_h.doe_intr_en.get()), + .kind(UVM_PREDICT_WRITE))); + // TODO: Add async logic + end + if(addr_phase_read) begin + void'(ctl_reg_h.abort.predict( + .value(0), + .kind(UVM_PREDICT_READ))); + void'(ctl_reg_h.doe_intr_en.predict( + .value(ral.status.sys_intr_enable.get()), + .kind(UVM_PREDICT_READ))); + void'(ctl_reg_h.go.predict( + .value(0), + .kind(UVM_PREDICT_READ))); + // TODO Add async logic + end + end + "soc_status" : begin + mbx_soc_reg_soc_status soc_sts_reg_h; + mbx_core_reg_status hst_sts_reg_h; + mbx_core_reg_control hst_ctl_reg_h; + + `DV_CHECK_FATAL($cast(soc_sts_reg_h, csr), + "Unable to cast csr handle to soc_status type") + hst_sts_reg_h = ral.status; + hst_ctl_reg_h = ral.control; + if(addr_phase_read) begin + void'(soc_sts_reg_h.busy.predict( + .value(hst_sts_reg_h.busy.get()), + .kind(UVM_PREDICT_READ))); + void'(soc_sts_reg_h.doe_intr_status.predict( + .value(hst_sts_reg_h.sys_intr_state.get()), + .kind(UVM_PREDICT_READ))); + void'(soc_sts_reg_h.error.predict( + .value(hst_ctl_reg_h.error.get()), + .kind(UVM_PREDICT_READ))); + //TODO: Review new ready field + void'(soc_sts_reg_h.ready.predict( + .value(m_obdwcnt != 0), + .kind(UVM_PREDICT_READ))); + end + if(addr_phase_write) begin + if((item.a_data[1] & item.a_mask[0]) == 1) begin + void'(hst_sts_reg_h.sys_intr_state.predict(.value(0))); + end + end + end + "wdata" : begin + if(addr_phase_write) begin + m_ib_q.push_back(item.a_data); + end + if(addr_phase_read) begin + void'(csr.predict(.value(0), .kind(UVM_PREDICT_READ))); + end + end + "rdata" : begin + if(addr_phase_read) begin + if(m_obdwcnt == 0) begin + void'(csr.predict(.value(0), .kind(UVM_PREDICT_READ))); + end else begin + void'(csr.predict(.value(m_ob_q[0]), .kind(UVM_PREDICT_READ))); + end + end + if(addr_phase_write) begin + int tmp_ptr=0; + if(m_obmbx_ptr_q.size() == 0) + tmp_ptr = m_obmbx_ptr+4; + else + tmp_ptr = m_obmbx_ptr_q[$]+4; + m_obmbx_ptr_q.push_back(tmp_ptr); + m_obmbx_ptr = tmp_ptr; + m_obdwcnt--; + void'(m_ob_q.pop_front()); + end + end + endcase + `uvm_info(`gfn, "process_tl_mbx_soc_access -- End", UVM_DEBUG) + endfunction: process_tl_mbx_soc_access + + virtual function void process_tl_mbx_mem_access(tl_seq_item item, tl_channels_e channel); + bit write = item.is_write(); + bit addr_phase_read = (!write && channel == AddrChannel); + bit addr_phase_write = (write && channel == AddrChannel); + bit data_phase_read = (!write && channel == DataChannel); + bit data_phase_write = (write && channel == DataChannel); + + `uvm_info(`gfn, "process_tl_mbx_mem_access -- Start", UVM_DEBUG) + if(addr_phase_read || addr_phase_write) begin + // Check for integrity error on Address + void'(item.is_a_chan_intg_ok(.throw_error(1))); + + // Check the transaction is full 4B access + `DV_CHECK_EQ(((item.a_size == 2) && (item.a_mask == '1)), 1, + $sformatf("Incorrect a_size(%0d) or a_mask('b%0b)", item.a_size, item.a_mask)) + + // Check the addresses are generated between the configured limits only + if(addr_phase_write) begin + bit is_addr_match; + + // Check if address is within IB Mailbox SRAM range + if((item.a_addr >= ral.inbound_base_address.get()) && + (item.a_addr < ral.inbound_limit_address.get())) begin + is_addr_match = 1'b1; + end + `DV_CHECK_EQ(is_addr_match, 1, + $sformatf("Address('h%0h) doesn't match any of inbound mailbox address ranges", + item.a_addr)) + + // Check if the SRAM write is expected or not + `DV_CHECK_NE(m_ib_q.size(), 0, "No write data in mbxwrdat register") + + // Check if the SRAM write address is correct. + `DV_CHECK_EQ(item.a_addr, (ral.inbound_base_address.get() + m_ibmbx_ptr), + "Incorrect address seen on the write to SRAM") + m_ibmbx_ptr += 4; + + // Check if the data written matches with the data written to wrdat register + `DV_CHECK_EQ(item.a_data, m_ib_q[0], + "Bus data doesn't match with data written to wdat") + void'(m_ib_q.pop_front()); + end + end + if(data_phase_read) begin + bit is_addr_match; + + // Check if address is within OB Mailbox SRAM range + if((item.a_addr >= ral.outbound_base_address.get()) && + (item.a_addr < ral.outbound_limit_address.get())) begin + is_addr_match = 1'b1; + end + `DV_CHECK_EQ(is_addr_match, 1, + $sformatf("Address('h%0h) out of outbound mailbox address ranges", item.a_addr)) + + // No read should occur when obdwcnt is '0' + `DV_CHECK_NE(m_obdwcnt, 0, "Illegal read from memory when obdwcnt is 0") + + m_obmbx_ptr = m_obmbx_ptr_q[0]; + // Check if the SRAM read address is correct. + `DV_CHECK_EQ(item.a_addr, (ral.outbound_base_address.get() + m_obmbx_ptr_q[0]), + "Incorrect address seen on the read to SRAM") + `uvm_info(`gfn, $sformatf("Added data 'h%0h to m_ob_q", item.d_data), UVM_LOW) + m_ob_q.push_back(item.d_data); + void'(m_obmbx_ptr_q.pop_front()); + + end + `uvm_info(`gfn, "process_tl_mbx_mem_access -- End", UVM_DEBUG) + endfunction: process_tl_mbx_mem_access + + virtual function void reset(string kind = "HARD"); + super.reset(kind); + // reset local fifos queues and variables + m_ib_q.delete(); + m_ob_q.delete(); + m_ibmbx_ptr = 0; + m_obmbx_ptr = 0; + m_obdwcnt = 0; + exp_mbx_core_irq = 0; + endfunction + + function void check_phase(uvm_phase phase); + super.check_phase(phase); + // post test checks - ensure that all local fifos and queues are empty + if(m_ib_q.size() > 0) begin + `uvm_error(`gfn, $sformatf("m_ib_q is not empty(%0d)", m_ib_q.size())) + end + if(m_ob_q.size() > 0) begin + `uvm_error(`gfn, $sformatf("m_ob_q is not empty(%0d)", m_ob_q.size())) + end + if(exp_mbx_core_irq_q.size() > 0) begin + `uvm_error(`gfn, $sformatf("exp_mbx_core_irq_q is not empty(%0d)", exp_mbx_core_irq_q.size())) + end + endfunction + +endclass diff --git a/hw/ip/mbx/dv/env/mbx_seq_item.sv b/hw/ip/mbx/dv/env/mbx_seq_item.sv new file mode 100644 index 0000000000000..25a40ee1cb765 --- /dev/null +++ b/hw/ip/mbx/dv/env/mbx_seq_item.sv @@ -0,0 +1,89 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class mbx_seq_item extends uvm_sequence_item; + + rand bit [top_pkg::TL_AW-1:0] ibmbx_base_addr; + rand bit [top_pkg::TL_AW-1:0] ibmbx_limit_addr; + rand bit [top_pkg::TL_AW-1:0] obmbx_base_addr; + rand bit [top_pkg::TL_AW-1:0] obmbx_limit_addr; + + rand bit address_range_valid; + rand mubi4_t address_range_regwen; + + // Sizes of Request and Response message in DWORDs + rand bit [10:0] request_dwords; + rand bit [10:0] response_dwords; + + `uvm_object_utils_begin(mbx_seq_item) + `uvm_field_int(ibmbx_base_addr, UVM_DEFAULT) + `uvm_field_int(ibmbx_limit_addr, UVM_DEFAULT) + `uvm_field_int(obmbx_base_addr, UVM_DEFAULT) + `uvm_field_int(obmbx_limit_addr, UVM_DEFAULT) + `uvm_field_int(address_range_valid, UVM_DEFAULT) + `uvm_field_enum(mubi4_t, address_range_regwen, UVM_DEFAULT) + `uvm_field_int(request_dwords, UVM_DEFAULT) + `uvm_field_int(response_dwords, UVM_DEFAULT) + `uvm_object_utils_end + + // Constructor: new + function new(string name = ""); + super.new(name); + endfunction : new + + function void set_address_range_randomization(bit enabled); + ibmbx_base_addr.rand_mode(enabled); + ibmbx_limit_addr.rand_mode(enabled); + obmbx_base_addr.rand_mode(enabled); + obmbx_limit_addr.rand_mode(enabled); + endfunction + + constraint legal_ibmbx_addr_range_c { + // TODO: Should have a full 32-bit address space! + (ibmbx_limit_addr < 32'h4000_0000); + + (ibmbx_limit_addr >= ibmbx_base_addr); + } + + constraint imbx_addr_range_lock_limit_c { + // Ensure that the allocated address range is large enough for all valid messages because + // otherwise we run the risk of making all subsequent response messages artificially small + // because the address range has been locked. + ((ibmbx_limit_addr - ibmbx_base_addr) / 4 >= MBX_DV_MAX_DW); + } + + constraint legal_obmbx_addr_range_c { + // TODO: Should have a full 32-bit address space! + (obmbx_limit_addr < 32'h4000_0000); + + (obmbx_limit_addr >= obmbx_base_addr); + } + + constraint legal_address_range_valid_c { + // TODO: presently we are concerned only with generating valid hardware configurations. + address_range_valid == 1'b1; + } + + constraint legal_request_dwords_c { + request_dwords > 0 && request_dwords < 'h400; + } + + constraint legal_response_dwords_c { + // There is an additioanl constraint upon the length of the Response because the register + // OUTBOUND_OBJECT_SIZE is limited. + response_dwords > 0 && response_dwords <= MBX_DV_MAX_DW; + } + + constraint legal_non_overlapping_region_c { + ibmbx_limit_addr < obmbx_base_addr || ibmbx_base_addr > obmbx_limit_addr; + } + + constraint legal_addr_alignment_c { + (ibmbx_base_addr[1:0] == 0); + (ibmbx_limit_addr[1:0] == 0); + (obmbx_base_addr[1:0] == 0); + (obmbx_limit_addr[1:0] == 0); + } + +endclass : mbx_seq_item diff --git a/hw/ip/mbx/dv/env/mbx_virtual_sequencer.sv b/hw/ip/mbx/dv/env/mbx_virtual_sequencer.sv new file mode 100644 index 0000000000000..d29bfca9bcad9 --- /dev/null +++ b/hw/ip/mbx/dv/env/mbx_virtual_sequencer.sv @@ -0,0 +1,13 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +class mbx_virtual_sequencer extends cip_base_virtual_sequencer #( + .CFG_T(mbx_env_cfg), + .COV_T(mbx_env_cov) + ); + + `uvm_component_utils(mbx_virtual_sequencer) + + `uvm_component_new + +endclass: mbx_virtual_sequencer diff --git a/hw/ip/mbx/dv/env/seq_lib/mbx_async_msg_vseq.sv b/hw/ip/mbx/dv/env/seq_lib/mbx_async_msg_vseq.sv new file mode 100644 index 0000000000000..28f95a3ca8aea --- /dev/null +++ b/hw/ip/mbx/dv/env/seq_lib/mbx_async_msg_vseq.sv @@ -0,0 +1,143 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class mbx_async_msg_vseq extends mbx_base_vseq; + + rand uint_t m_req_dw_sz; + rand uint_t m_rsp_dw_sz; + rand uint_t m_num_msg; + rand uint_t m_num_of_iterations; + rand uint_t m_mbx_num; + + `uvm_object_utils(mbx_async_msg_vseq) + + constraint mbx_num_c { m_mbx_num == 0; } + + constraint req_dw_sz_c { m_req_dw_sz inside {[1 : 5]}; } + + constraint rsp_dw_sz_c { m_rsp_dw_sz inside {[1 : 5]}; } + + constraint num_msg_c { m_num_msg inside {[1 : 2]}; } + + constraint num_of_iterations_c { m_num_of_iterations inside {[1 : 3]}; } + + `uvm_object_new + + virtual task system_thread(uint_t num_msg); + uint_t msg_rcvd; + + `uvm_info(`gfn, + $sformatf("system_thread(num_msg=%0d) -- Start", num_msg), + UVM_DEBUG) + // Wait for the async msg sts to be set + csr_spinwait( + .ptr(m_mbx_soc_ral.m_mbxsoc_reg_block.mbxsts.asyncmsgsts), + .exp_data(1) + ); + forever begin + bit [31:0] rd_data; + + msg_rcvd++; + + // Wait for busy bit to be '0' + csr_spinwait( + .ptr(m_mbx_soc_ral.m_mbxsoc_reg_block.mbxsts.mbxbusy), + .exp_data(0) + ); + + // Generate request + for(uint_t dw = 0; dw < m_req_dw_sz; dw++) begin + csr_wr(m_mbx_soc_ral.m_mbxsoc_reg_block.mbxwrdat, $urandom); + end + csr_wr(m_mbx_soc_ral.m_mbxsoc_reg_block.mbxctl.mbxgo, 1); + + // Wait for response + for(uint_t dw = 0; dw < m_rsp_dw_sz; dw++) begin + csr_spinwait( + .ptr(m_mbx_soc_ral.m_mbxsoc_reg_block.mbxsts.mbxready), + .exp_data(1) + ); + + csr_rd(m_mbx_soc_ral.m_mbxsoc_reg_block.mbxrddat, rd_data); + csr_wr(m_mbx_soc_ral.m_mbxsoc_reg_block.mbxrddat, $urandom); + end + // MBXREADY should be '0', once all the data has been read + csr_rd_check(.ptr(m_mbx_soc_ral.m_mbxsoc_reg_block.mbxsts.mbxready), .compare_value(0)); + + // Check if asyncmsgsts is still set + csr_rd(m_mbx_soc_ral.m_mbxsoc_reg_block.mbxsts.asyncmsgsts, rd_data); + + if(rd_data == 0) begin + `uvm_info(`gfn, "No more async messages to be sent by core", UVM_LOW) + break; + end + `DV_CHECK_RANDOMIZE_FATAL(this) + end + `DV_CHECK_EQ(num_msg, msg_rcvd, "Expected async messages doesn't receive actual") + `uvm_info(`gfn, + $sformatf("system_thread(num_msg=%0d) -- End", num_msg), + UVM_DEBUG) + endtask: system_thread + + virtual task core_thread(uint_t num_msg); + `uvm_info(`gfn, + $sformatf("core_thread(num_msg=%0d) -- Start", num_msg), + UVM_DEBUG) + // Set the asyncmsgsts bit + csr_wr(ral.m_mbxhst_reg_block.mbxsts.asyncmsgsts, 1); + + for(uint_t msg = 0; msg < num_msg; msg++) begin + byte q[$]; + + // Wait for request packet to be written + wait_for_core_interrupt(); + + // Check num of DW sent is same as received + csr_rd_check(.ptr(ral.m_mbxhst_reg_block.mbxibwrptr), + .compare_value(m_req_dw_sz * 4 + ral.m_mbxhst_reg_block.mbxibbase.get())); + + // Set MBXIBWRPTR back to MBXIBBASE + csr_wr(ral.m_mbxhst_reg_block.mbxibwrptr, 0); + // Clear busy + csr_wr(ral.m_mbxhst_reg_block.mbxsts.mbxbusy, 0); + + // Create response + for(uint_t dw = 0; dw < m_rsp_dw_sz; dw++) begin + for(uint_t byte_cnt = 0; byte_cnt < MBX_DV_DW_SIZE_BYTES; byte_cnt++) begin + q.push_back($urandom); + end + end + write_mem(obmbx_base_addr, q); + csr_wr(ral.m_mbxhst_reg_block.mbxobrdptr, 0); + csr_wr(ral.m_mbxhst_reg_block.mbxobdwcnt, m_rsp_dw_sz); + end + + // Set asyncmsgsts bit to '0' + csr_wr(ral.m_mbxhst_reg_block.mbxsts.asyncmsgsts, 0); + `uvm_info(`gfn, + $sformatf("core_thread(num_msg=%0d) -- End", num_msg), + UVM_DEBUG) + endtask: core_thread + + virtual task body(); + `uvm_info(`gfn, "body -- Start", UVM_DEBUG) + super.body(); + ibmbx_base_addr.rand_mode(0); + ibmbx_limit_addr.rand_mode(0); + obmbx_base_addr.rand_mode(0); + obmbx_limit_addr.rand_mode(0); + m_num_of_iterations.rand_mode(0); + for(uint_t iter = 0; iter < m_num_of_iterations; iter++) begin + m_num_msg.rand_mode(0); + fork + system_thread(m_num_msg); + core_thread(m_num_msg); + join + m_num_msg.rand_mode(1); + `DV_CHECK_RANDOMIZE_FATAL(this) + end + `uvm_info(`gfn, "body -- End", UVM_DEBUG) + endtask: body + +endclass: mbx_async_msg_vseq diff --git a/hw/ip/mbx/dv/env/seq_lib/mbx_base_vseq.sv b/hw/ip/mbx/dv/env/seq_lib/mbx_base_vseq.sv new file mode 100644 index 0000000000000..de3dee2421ef9 --- /dev/null +++ b/hw/ip/mbx/dv/env/seq_lib/mbx_base_vseq.sv @@ -0,0 +1,690 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +/* + The body of this base_vseq exercises the DUT by creating a number of 'transactions', which + nominally involve the requester posting a message, the responder receiving this message then + replying with its own message in return. Each transaction is a sequential series of operations, + with hooks which optionally may insert 'stressors' (SOC_ABORT, ERROR, ROT_ABORT) which causes + the nominal control flow to short-circuit to the end of the txn, while confirming we see the + effect of these stressors. + Each 'iteration' (num_iters) consists of a number of 'transactions' (num_txns), with a DUT reset + seperating each iteration. This allows the regwen features to be meaningfully exercised. + This is a quick, minimal effort extension of the existing 'smoke' test into something that more + thoroughly exercises the mailbox IP at block level, including exceptional traffic/conditions. + The linear-nature of stimulus->checking means it is not the basis of a proper DV stress test. +*/ + +class mbx_base_vseq extends cip_base_vseq #( + .RAL_T (mbx_core_reg_block), + .CFG_T (mbx_env_cfg), + .COV_T (mbx_env_cov), + .VIRTUAL_SEQUENCER_T (mbx_virtual_sequencer) + ); + + `uvm_object_utils(mbx_base_vseq) + + string mbx_mem_ral_name = "mbx_mem_reg_block"; + string mbx_soc_ral_name = "mbx_soc_reg_block"; + + // Number of iterations + rand int unsigned num_iters; + // Number of transactions (per iteration) + // (sequential message transfers without an intervening DUT reset) + rand int unsigned num_txns; + + mem_model seq_mem_model; + + mbx_tl_device_seq seq_h; + + mbx_mem_reg_block m_mbx_mem_ral; + mbx_soc_reg_block m_mbx_soc_ral; + + mbx_seq_item mbx_config; + bit p_expect_error = 1'b0; // Predictor variable + + // Number of words of memory available to the mailbox(es) + int unsigned mbx_mem_words = 'h400; + + // Raise an Abort request from the SoC side? + // Note: `aborted` shall already be clear, and be set here only when a new stimulus is applied. + virtual task do_abort(ref bit aborted); + endtask + // Raise an Error from the Core side? + // Note: `errored` shall already be clear, and be set here only when a new stimulus is applied. + virtual task do_error(ref bit errored); + endtask + // Raise a FW-initiated Reset from the Core side? + // Note: `panicked` shall already be clear, and be set here only when a new stimulus is applied. + virtual task do_panic(ref bit panicked); + endtask + + // Apply stressors to DUT? + // Note: each of `aborted`, `errored` and `panicked` shall already be clear, and be set here only + // when a new stimulus is applied. + virtual task stressors(ref bit aborted, ref bit errored, ref bit panicked); + do_abort(aborted); + do_error(errored); + do_panic(panicked); + endtask + + // Decide upon the access delays for this transaction. + virtual function bit choose_access_delays(output int min_acc_delay, output int max_acc_delay); + min_acc_delay = 0; + max_acc_delay = 0; + // Do not modify them by default. + return 1'b0; + endfunction + + virtual function bit choose_response_delays(output int min_rsp_delay, output int max_rsp_delay); + min_rsp_delay = 0; + max_rsp_delay = 0; + // Do not modify them by default. + return 1'b0; + endfunction + + function new(string name = ""); + super.new(); + mbx_config = mbx_seq_item::type_id::create("mbx_config"); + seq_mem_model = mem_model#()::type_id::create("seq_mem_model"); + endfunction: new + + function void pre_randomize(); + super.pre_randomize(); + `downcast(m_mbx_soc_ral, cfg.ral_models[cfg.mbx_soc_ral_name]) + `downcast(m_mbx_mem_ral, cfg.ral_models[cfg.mbx_mem_ral_name]) + endfunction: pre_randomize + + // Task: Simulate a clock delay + virtual task delay(int num = 1); + cfg.clk_rst_vif.wait_clks(num); + endtask : delay + + // Set the minimum and maximum grant delays of the mailbox SRAM + // TODO: this function was introduced to guarantee the desired values during bring up; + // it may no longer be required. + function void set_access_delays(int min, int max); + cfg.m_tl_agent_cfgs[mbx_mem_ral_name].a_ready_delay_min = min; + cfg.m_tl_agent_cfgs[mbx_mem_ral_name].a_ready_delay_max = max; + + cfg.m_tl_agent_cfgs[RAL_T::type_name].a_valid_delay_min = min; + cfg.m_tl_agent_cfgs[RAL_T::type_name].a_valid_delay_max = max; + + cfg.m_tl_agent_cfgs[mbx_soc_ral_name].a_valid_delay_min = min; + cfg.m_tl_agent_cfgs[mbx_soc_ral_name].a_valid_delay_max = max; + endfunction + + // Set the minimum and maximum response delays of the mailbox SRAM + // TODO: this function was introduced to guarantee the desired values during bring up; + // it may no longer be required. + function void set_response_delays(int min, int max); + cfg.m_tl_agent_cfgs[mbx_mem_ral_name].use_seq_item_d_valid_delay = 1'b0; + cfg.m_tl_agent_cfgs[mbx_mem_ral_name].d_valid_delay_min = min; + cfg.m_tl_agent_cfgs[mbx_mem_ral_name].d_valid_delay_max = max; + + cfg.m_tl_agent_cfgs[RAL_T::type_name].d_ready_delay_min = min; + cfg.m_tl_agent_cfgs[RAL_T::type_name].d_ready_delay_max = max; + + cfg.m_tl_agent_cfgs[mbx_soc_ral_name].d_ready_delay_min = min; + cfg.m_tl_agent_cfgs[mbx_soc_ral_name].d_ready_delay_max = max; + + seq_h.min_rsp_delay = min; + seq_h.max_rsp_delay = max; + endfunction + + // Enable/disable errors on TL-UL buses with the given percentage probability/word + function void enable_bus_errors(int pct); + seq_h.d_error_pct = pct; + endfunction + + virtual function void randomize_mbx_config(); + `DV_CHECK_RANDOMIZE_FATAL(mbx_config) + `uvm_info(`gfn, $sformatf("MBX: Randomized a new transaction:%s", + mbx_config.convert2string()), UVM_HIGH) + endfunction + + virtual task dut_init(string reset_kind = "HARD"); + super.dut_init(); + endtask: dut_init + + // Start a sequence that creates a memory_model on the 'mem' interface. + virtual task start_mem_seq(); + seq_h = mbx_tl_device_seq::type_id::create("seq_h"); + seq_h.mem = seq_mem_model; + fork + seq_h.start(p_sequencer.tl_sequencer_hs[cfg.mbx_mem_ral_name]); + join_none + endtask: start_mem_seq + + virtual task set_mem_range_regwen(mubi4_t regwen); + `uvm_info(`gfn, $sformatf("Setting memory range regwen to 0x%x", regwen), UVM_MEDIUM) + + ral.address_range_regwen.regwen.set(int'(regwen)); + csr_update(ral.address_range_regwen); + endtask + + virtual task set_address_range(mbx_addr_t ibmbx_base_addr, mbx_addr_t ibmbx_limit_addr, + mbx_addr_t obmbx_base_addr, mbx_addr_t obmbx_limit_addr, + bit range_valid, mubi4_t regwen); + // Program the memory address ranges into the configuration registers + csr_wr(ral.inbound_base_address, ibmbx_base_addr); + csr_wr(ral.inbound_limit_address, ibmbx_limit_addr); + csr_wr(ral.outbound_base_address, obmbx_base_addr); + csr_wr(ral.outbound_limit_address, obmbx_limit_addr); + // Specify whether a valid range has been supplied + csr_wr(ral.address_range_valid, range_valid); + // Set the lock as requested + set_mem_range_regwen(regwen); + endtask + + virtual task clear_mem(); + `uvm_info(`gfn, "Clearing memory", UVM_DEBUG) + seq_mem_model.init(); + endtask + + virtual task write_mem(int start_addr, byte q[$]); + `uvm_info(get_full_name(), + $sformatf("write_mem(start_addr='h%0h, q=%p) -- Start", start_addr, q), + UVM_DEBUG); + foreach(q[ii]) begin + seq_mem_model.write_byte((start_addr + ii), q[ii]); + end + `uvm_info(get_full_name(), + $sformatf("write_mem(start_addr='h%0h, q=%p) -- End", start_addr, q), + UVM_DEBUG); + endtask: write_mem + + virtual task read_mem(int start_addr, int sz, ref byte q[$]); + `uvm_info(get_full_name(), + $sformatf("read_mem(start_addr='h%0h, sz=%0d) -- Start", start_addr, sz), + UVM_DEBUG) + q = {}; + for(int ii = 0; ii < sz; ii++) begin + q[ii] = seq_mem_model.read_byte(start_addr + ii); + end + `uvm_info(get_full_name(), + $sformatf("read_mem(start_addr='h%0h', sz=%0d, q=%p) -- Start", start_addr, sz, q), + UVM_DEBUG) + endtask: read_mem + + virtual task mbx_init(); + uvm_status_e status; + + `uvm_info(get_full_name(), + $sformatf("mbx_init -- Start"), + UVM_DEBUG) + + randomize_mbx_config(); + + `uvm_info(get_full_name(), + $sformatf("mbx_init -- End"), + UVM_DEBUG) + endtask: mbx_init + + virtual task mbx_abort(); + `uvm_info(`gfn, "ABORTing operation", UVM_HIGH) + m_mbx_soc_ral.soc_control.go.set(1'b0); + m_mbx_soc_ral.soc_control.abort.set(1'b1); + csr_update(m_mbx_soc_ral.soc_control); + endtask + + virtual task wait_for_core_interrupt(ref bit intr_ready, ref bit intr_abort, + input int clks_timeout = 'h10000); + bit aborted = 1'b0; + `uvm_info(`gfn, $sformatf("wait_for_core_interrupt -- Start"), UVM_DEBUG) + fork begin : iso_fork + fork + begin + `DV_WAIT(cfg.intr_vif.pins[MbxCoreReady] == 1'b1, "core ready interrupt wait timeout") + end + begin + `DV_WAIT(cfg.intr_vif.pins[MbxCoreAbort] == 1'b1, "core abort interrupt wait timeout") + aborted = 1'b1; + end + begin + cfg.clk_rst_vif.wait_clks(clks_timeout); + `dv_fatal($sformatf("Timed out after %d clocks waiting for a core interrupt", + clks_timeout), `gfn) + end + join_any; + disable fork; + end: iso_fork join + if (aborted) begin + intr_abort = 1'b1; + end else begin + intr_ready = 1'b1; + end + `uvm_info(`gfn, $sformatf("wait_for_core_interrupt -- End"), UVM_DEBUG) + endtask : wait_for_core_interrupt + + virtual task wait_for_soc_interrupt(int clks_timeout = 'h10000); + `uvm_info(`gfn, $sformatf("wait_for_soc_interrupt -- Start"), UVM_DEBUG) + fork begin : iso_fork + fork + begin + `DV_WAIT(cfg.intr_soc_vif.pins[0] == 1'b1, "soc interrupt wait timeout") + end + begin + cfg.clk_rst_vif.wait_clks(clks_timeout); + `dv_fatal($sformatf("Timed out after %d clocks waiting for a soc interrupt", + clks_timeout), `gfn) + end + join_any; + disable fork; + end: iso_fork join + `uvm_info(`gfn, $sformatf("wait_for_soc_interrupt -- End"), UVM_DEBUG) + endtask : wait_for_soc_interrupt + + // Wait until the Ready/Abort interrupt is received on the Core side, or alternatively rely + // upon the INTR_STATE register and polling. + // If we decide to perform a FW-initiated reset ('panic') then neither will occur. + task automatic wait_for_core_signal(output bit intr_ready, + output bit intr_abort, + output bit aborted, + output bit errored, + output bit panicked, + input bit intr_driven); + // Detected interrupt/status bits on Core side + bit got_ready = 1'b0; + bit got_abort = 1'b0; + // Generated stimuli + bit gen_abort = 1'b0; + bit gen_error = 1'b0; + bit gen_panic = 1'b0; + event e_stop; + fork begin: iso_fork + // With two processes we monitor CSR activity and interrupt signals concurrently. + fork + // CSR-driving thread generates errors and aborts as well as optionally polling the + // INTR_STATE register. + begin + do begin + // Ensure simulation time advances, even if this process has nothing to do! + delay(1); + stressors(gen_abort, gen_error, gen_panic); + if (!intr_driven) begin + bit [top_pkg::TL_DW-1:0] rd_data; + csr_rd(ral.intr_state, rd_data); + got_ready |= get_field_val(ral.intr_state.mbx_ready, rd_data); + got_abort |= get_field_val(ral.intr_state.mbx_abort, rd_data); + end + end while (!(got_ready | got_abort | gen_error | gen_panic)); + // Signal to the parallel thread that the CSR registers are no longer being accessed. + ->e_stop; + end + begin + if (intr_driven) wait_for_core_interrupt(got_ready, got_abort); + // Note: we must never kill the CSR-polling process because it may leave the RAL + // locked, so this thread must only terminate with permission of the CSR process. + wait (e_stop.triggered); + end + join_any + // Ensure that the interrupt-monitoring process terminates. + disable fork; + end: iso_fork join + // Signals from DUT + intr_ready = got_ready; + intr_abort = got_abort; + // Generated stimuli + aborted = gen_abort; + errored = gen_error; + panicked = gen_panic; + endtask + + virtual task body(); + `uvm_info(get_full_name(), "body -- Start", UVM_DEBUG) + + // Start a sequence that creates a memory_model on the 'mem' interface. + start_mem_seq(); + + // TODO: gross change to prevent explosions on accessing RDATA, since it does not behave like + // a regular memory + cfg.en_scb_mem_chk = 1'b0; + + for (int unsigned iter = 0; iter < num_iters; iter++) begin: b_num_iters + `uvm_info(`gfn, $sformatf("Starting iteration %0d of %0d", iter + 1, num_iters), UVM_LOW) + + // Since the DUT has just been reset, we should take the opportunity to choose new memory + // addresses. + mbx_config.set_address_range_randomization(1'b1); + + // Initialize mailbox with randomized memory configuration. + mbx_init(); + + for (int unsigned txn = 0; txn < num_txns; txn++) begin + bit [top_pkg::TL_DW-1:0] rd_data; + bit [top_pkg::TL_DW-1:0] wr_data; + int error_pct = $urandom_range(0, 200); + bit intr_driven = $urandom & 1; + bit check_request = 1'b0; + bit send_response = 1'b0; + int unsigned req_size_limit; + int unsigned rsp_size_limit; + int unsigned req_size; + int unsigned rsp_size; + bit panicked = 1'b0; + bit errored = 1'b0; + bit aborted = 1'b0; + mbx_dword_t req[$]; + mbx_dword_t rsp[$]; + mbx_dword_t qd; + bit obs_ready; + bit obs_error; + bit obs_busy; + int min_rsp_delay; + int max_rsp_delay; + int min_acc_delay; + int max_acc_delay; + // TODO: whether to perform RDATA write accesses as blocking operations. + bit rdata_wr_blocking = 1'b0; + + // TODO: perhaps we should change read_mem/write_mem to avoid issues. + // The mailbox operates only on DWORD quantities. + // mbx_dword_t q[$]; + byte q[$]; + + // Empty the mailbox memory model of any previous contents. + clear_mem(); + + `uvm_info(`gfn, $sformatf("Starting transaction %0d of %0d", txn + 1, num_txns), UVM_LOW) + + // Generate a new configuration for the transaction. + `DV_CHECK_RANDOMIZE_FATAL(mbx_config); + + // Generate a new configuration for the transaction. + randomize_mbx_config(); + + set_address_range(mbx_config.ibmbx_base_addr, mbx_config.ibmbx_limit_addr, + mbx_config.obmbx_base_addr, mbx_config.obmbx_limit_addr, + mbx_config.address_range_valid, mbx_config.address_range_regwen); + + // Enable/Disable Core interrupts. + cfg_interrupts((1 << MbxCoreError) | + (1 << MbxCoreReady) | + (1 << MbxCoreAbort), intr_driven); + `uvm_info(`gfn, $sformatf("Using interrupts? %s", intr_driven ? "Y" : "N"), UVM_MEDIUM) + + // Generate TL-UL bus errors during the operation. + // TODO: this trips up the base scoreboard at present + // enable_bus_errors((error_pct >= 100) ? 0 : error_pct); + + // Let the derived sequence vary the timing as appropriate; because of the back-pressuring + // signals in DUT, there's a sequence that specifically sets 'zero_delays' to exercise + // low latency bus responses. + if (!cfg.zero_delays && choose_access_delays(min_acc_delay, max_acc_delay)) begin + set_access_delays(min_acc_delay, max_acc_delay); + `uvm_info(`gfn, $sformatf("Setting access delays [%d,%d]", + min_acc_delay, max_acc_delay), UVM_MEDIUM) + end + if (!cfg.zero_delays && choose_response_delays(min_rsp_delay, max_rsp_delay)) begin + set_response_delays(min_rsp_delay, max_rsp_delay); + `uvm_info(`gfn, $sformatf("Setting response delays [%d,%d]", + min_rsp_delay, max_rsp_delay), UVM_MEDIUM) + end + + // ---------------------------------------------------------------------------------------- + // Request from SoC to RoT + // ---------------------------------------------------------------------------------------- + + // Data from R-code to ROT + req_size = mbx_config.request_dwords; + rsp_size = mbx_config.response_dwords; + + `uvm_info(`gfn, $sformatf("Request size 0x%x DWORD(s), Response size 0x%x DWORD(s)", + req_size, rsp_size), UVM_MEDIUM) + `uvm_info(`gfn, + $sformatf("Inbox should use address range [0x%x,0x%x]", + mbx_config.ibmbx_base_addr, mbx_config.ibmbx_base_addr + req_size * 4), + UVM_MEDIUM) + `uvm_info(`gfn, + $sformatf("Outbox should use address range [0x%x,0x%x]", + mbx_config.obmbx_base_addr, mbx_config.obmbx_base_addr + rsp_size * 4), + UVM_MEDIUM) + + `uvm_info(`gfn, $sformatf("Constructing Request of 0x%0x DWORDs", req_size), UVM_MEDIUM) + for(int unsigned ii = 0; ii < req_size; ii++) begin + stressors(aborted, errored, panicked); + + if (aborted | panicked) begin + if (!panicked) begin + // We know that we're not permitted to write to WDATA whilst the mailbox is BUSY, + // which should be expected if we've ABORTed the operation. + uvm_reg_data_t data; + csr_rd(m_mbx_soc_ral.soc_status, data); + `DV_CHECK_EQ(get_field_val(m_mbx_soc_ral.soc_status.busy, data), 1'b1, + "SOC_STATUS.busy not set when expected in response to Abort request") + end + // Do NOT send the remainder of the Request. + break; + end else begin + wr_data = $urandom(); + `uvm_info(`gfn, $sformatf(" - Offset 0x%0x : 0x%0x", ii, wr_data), UVM_HIGH) + req.push_back(wr_data); + tl_access(.addr(m_mbx_soc_ral.get_addr_from_offset(mbx_reg_pkg::MBX_WDATA_OFFSET)), + .write(1'b1), .data(wr_data), .mask(4'hF), .blocking(1'b1), + .tl_sequencer_h(p_sequencer.tl_sequencer_hs[cfg.mbx_soc_ral_name])); + end + end + + csr_rd(m_mbx_soc_ral.soc_status, rd_data); + obs_error = get_field_val(m_mbx_soc_ral.soc_status.error, rd_data); + + if (!panicked & !aborted & !obs_error) begin + // No disruption in the supply of the Request, activate the RoT + bit intr_abort; + bit intr_ready; + + // As we supplied all of the data, it should all make it into the mailbox SRAM. + check_request = 1'b1; + + // Set the GO bit to mark complete transmission of the Request to the OT FW. + m_mbx_soc_ral.soc_control.abort.set(1'b0); + m_mbx_soc_ral.soc_control.go.set(1'b1); + csr_update(m_mbx_soc_ral.soc_control); + + // Wait until the Ready interrupt is received on the Core side, or alternatively rely + // upon the INTR_STATE register and polling. + // If we decide to perform a FW-initiated reset ('panic') then neither will occur. + wait_for_core_signal(intr_ready, intr_abort, // Core side signals from DUT + aborted, errored, panicked, // Stimuli generated by DV + intr_driven); // Interrupt-driven operation? + + // Are we expecting the mailbox to be operational still? + `uvm_info(`gfn, $sformatf("intr_ready %d abort %d errored %d panicked %d", + intr_ready, intr_abort, errored, panicked), UVM_HIGH); + send_response = intr_ready & !intr_abort & !aborted & !errored & !panicked; + end + + if (check_request) begin + // Collect the request message from the OT mailbox memory + read_mem(mbx_config.ibmbx_base_addr, req_size << 2, q); + + for(int unsigned ii = 0; ii < req_size; ii++) begin + qd = {q[ii*4+3],q[ii*4+2],q[ii*4+1],q[ii*4]}; + `uvm_info(`gfn, $sformatf("Expected Request DWORD %0h got %0h", req[ii], qd), UVM_HIGH) + if (qd !== req[ii]) begin + `uvm_error(`gfn, + $sformatf("Request DWORD mismatches q[%0d]('h%0h) != req[%0d]('h%0h)", + ii, qd, ii, req[ii])) + end + end + `uvm_info(`gfn, "Request data matched expectations", UVM_MEDIUM) + end + + if (send_response) begin: b_send_response + // Data from ROT to R-code + q.delete(); + `uvm_info(`gfn, $sformatf("Constructing Response of 0x%0x DWORDs", rsp_size), UVM_MEDIUM) + for(int unsigned ii = 0 ; ii < rsp_size; ii++) begin + mbx_dword_t data = $urandom; + `uvm_info(`gfn, $sformatf(" - Offset 0x%0x : 0x%0x", ii, data), UVM_HIGH) + // TODO: replace this byte queue with DWORDs + q.push_back(data[7:0]); + q.push_back(data[15:8]); + q.push_back(data[23:16]); + q.push_back(data[31:24]); + end + + // -------------------------------------------------------------------------------------- + // Response from RoT to SoC + // -------------------------------------------------------------------------------------- + + write_mem(mbx_config.obmbx_base_addr, q); + // Writing to 'outbound_object_size' triggers the mbx to make the response available. + csr_wr(ral.outbound_object_size, rsp_size); + + // Await assertion of READY bit or interrupt indicating that there's a Response available. + do begin + stressors(aborted, errored, panicked); + + csr_rd(m_mbx_soc_ral.soc_status, rd_data); + `uvm_info(`gfn, $sformatf("rd_data for soc_status is :'h%0h", rd_data), UVM_DEBUG) + + // TODO: wait_soc_interrupt here + // if intr_driven ... + + // We're waiting to see the READY bit, but we may also see BUSY in response to + // an ABORT or ERROR if we raised an error. + obs_error = get_field_val(m_mbx_soc_ral.soc_status.error, rd_data); + obs_ready = get_field_val(m_mbx_soc_ral.soc_status.ready, rd_data); + end while (!(aborted | panicked | obs_error | obs_ready)); + + if (obs_ready & !aborted & !errored & !panicked) begin + bit check_response = 1'b1; + + // Use an explicit termination signal for the other parallel thread, to avoid killing + // it during a CSR access since doing so could leave the RAL locked. + bit done = 1'b0; + fork + begin + // Collect the entire message before checking it. + // Note: this may not be the best approach unless we can time out in the event of a + // lock up in the provision of new RDATA values. + for(int unsigned ii = 0; ii < rsp_size && !done; ii++) begin + // Read from RDATA to collect the next message word + tl_access(.addr(cfg.ral.get_addr_from_offset(mbx_reg_pkg::MBX_RDATA_OFFSET)), + .write(1'b0), .data(rd_data), .mask(4'hF), .compare_mask(0), + .blocking(1'b1), + .tl_sequencer_h(p_sequencer.tl_sequencer_hs[cfg.mbx_soc_ral_name])); + + `uvm_info(`gfn, $sformatf("Mailbox read data is : 'h%0h", rd_data), UVM_HIGH) + + rsp.push_back(rd_data); + + // Write anything to RDATA to advance to the next word. + wr_data = $urandom; + tl_access(.addr(cfg.ral.get_addr_from_offset(mbx_reg_pkg::MBX_RDATA_OFFSET)), + .write(1'b1), .data(wr_data), .mask(4'hF), .blocking(rdata_wr_blocking), + .tl_sequencer_h(p_sequencer.tl_sequencer_hs[cfg.mbx_soc_ral_name])); + end + `uvm_info(`gfn, "READ all DATA from SoC side", UVM_HIGH); + done = 1'b1; + end + begin + while (!done) begin + // Ensure simulation time advances, even if this process has nothing to do! + delay(1); + stressors(aborted, errored, panicked); + + if (aborted | errored | panicked) begin + check_response = 1'b0; + done = 1'b1; + end + end + `uvm_info(`gfn, "Abort/Error process stopping", UVM_HIGH); + end + join + + if (check_response) begin + for(int unsigned ii = 0; ii < rsp_size; ii++) begin + qd = {q[ii*4+3],q[ii*4+2],q[ii*4+1],q[ii*4]}; + `uvm_info(`gfn, + $sformatf("Expected Response DWORD %0h got %0h", qd, rsp[ii]), UVM_HIGH) + if (qd !== rsp[ii]) begin + `uvm_error(`gfn, + $sformatf("Response DWORD mismatches q[%0d]('h%0h) != rsp[%0d]('h%0h)", + ii, qd, ii, rsp[ii])) + end + end + end + end + end: b_send_response + + csr_rd(m_mbx_soc_ral.soc_status, rd_data); + `uvm_info(`gfn, $sformatf("Transaction complete; SOC_STATUS 0x%0x", rd_data), UVM_MEDIUM) + + // Collect SoC.STATUS bits + obs_busy = get_field_val(m_mbx_soc_ral.soc_status.busy, rd_data); + obs_error = get_field_val(m_mbx_soc_ral.soc_status.error, rd_data); + + if (!p_expect_error) begin + if (obs_error) `DV_CHECK_EQ(errored, 1'b1, "ERROR occurred, but not due to stressors!") + end else if (p_expect_error && !obs_error && !(aborted || panicked)) begin + // If we aborted/panicked, then even if the stimulus should have generated an error, we + // won't see it. + `uvm_error(`gfn, "Didn't observe an error when stimulus expected to generate one!") + end + p_expect_error = 1'b0; + + // Cleanup after error + if (obs_error) begin + `uvm_info(`gfn, "Clearing ERROR condition from SoC side using ABORT mechanism", UVM_HIGH) + mbx_abort(); + aborted = 1'b1; + + // Check that the BUSY bit becomes set + csr_rd(m_mbx_soc_ral.soc_status, rd_data); + obs_busy = get_field_val(m_mbx_soc_ral.soc_status.busy, rd_data); + obs_error = get_field_val(m_mbx_soc_ral.soc_status.error, rd_data); + `DV_CHECK_EQ(obs_busy, 1'b1, "BUSY bit has not become set when ABORTing") + end + + if (obs_busy) begin + `DV_CHECK_EQ(aborted, 1'b1, "BUSY asserted but not ABORTed") + + // Abort occurred, clear it from the OT FW side + `uvm_info(`gfn, "Clearing ABORT condition from OT FW side", UVM_HIGH) + ral.control.abort.set(1'b1); + ral.control.error.set(1'b0); // Don't raise another ERROR! + csr_update(ral.control); + + // Check that the BUSY bit resets + csr_rd(m_mbx_soc_ral.soc_status, rd_data); + obs_busy = get_field_val(m_mbx_soc_ral.soc_status.busy, rd_data); + obs_error = get_field_val(m_mbx_soc_ral.soc_status.error, rd_data); + `DV_CHECK_EQ(obs_busy, 1'b0, "BUSY bit cannot be cleared") + `DV_CHECK_EQ(obs_error, 1'b0, "ERROR bit cannot be cleared") + end + + `DV_CHECK_EQ(rd_data[31], 1'b0, "Ready bit still set") + + `uvm_info(`gfn, $sformatf("Completed transaction %0d of %0d", txn + 1, num_txns), UVM_LOW) + + // Ensure that we clear any asserted interrupts because otherwise they could interfere + // with subsequent CSR-driven tests, in particular. + csr_wr(ral.intr_state, (1 << MbxCoreError) | + (1 << MbxCoreReady) | + (1 << MbxCoreAbort)); + // Similarly control bits + csr_wr(ral.control, 0); + // SoC side interrupt + m_mbx_soc_ral.soc_status.doe_intr_status.set(1'b1); + csr_update(m_mbx_soc_ral.soc_status); + + // Can the memory range still be changed on subsequent transactions? + if (mbx_config.address_range_regwen != MuBi4True) begin + // Further changes are not expected to work until an IP reset, and the DV values must + // remain in step with those used by the DUT. + mbx_config.set_address_range_randomization(1'b0); + end + end: b_num_txns + + apply_resets_concurrently(); + delay(10); + end: b_num_iters + + `uvm_info(get_full_name(), "body -- End", UVM_DEBUG) + endtask : body + +endclass : mbx_base_vseq diff --git a/hw/ip/mbx/dv/env/seq_lib/mbx_common_vseq.sv b/hw/ip/mbx/dv/env/seq_lib/mbx_common_vseq.sv new file mode 100644 index 0000000000000..91690ed990e11 --- /dev/null +++ b/hw/ip/mbx/dv/env/seq_lib/mbx_common_vseq.sv @@ -0,0 +1,17 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class mbx_common_vseq extends mbx_base_vseq; + `uvm_object_utils(mbx_common_vseq) + + constraint num_trans_c { + num_trans inside {[1:2]}; + } + `uvm_object_new + + virtual task body(); + run_common_vseq_wrapper(num_trans); + endtask : body + +endclass diff --git a/hw/ip/mbx/dv/env/seq_lib/mbx_imbx_oob_vseq.sv b/hw/ip/mbx/dv/env/seq_lib/mbx_imbx_oob_vseq.sv new file mode 100644 index 0000000000000..f51f3546f7fbd --- /dev/null +++ b/hw/ip/mbx/dv/env/seq_lib/mbx_imbx_oob_vseq.sv @@ -0,0 +1,31 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// Add extra constraints to the mbx_config to ensure we generate messages +// which exceed the size of the imbx. +// This should trigger a HW-based check when the imbx attempts to write +// outside the configured address range, raising the 'Error' condition. + +class mbx_imbx_oob_vseq extends mbx_stress_vseq; + + `uvm_object_utils(mbx_imbx_oob_vseq) + `uvm_object_new + + function void randomize_mbx_config(); + // Disabling this constraint allows the solver to randomize to an otherwise-invalid + // config of imbx size + response_dwords that elicits the out-of-bounds error. + mbx_config.imbx_addr_range_lock_limit_c.constraint_mode(0); + `DV_CHECK_RANDOMIZE_WITH_FATAL(mbx_config, + // imbx_size is inclusive of limit address, hence +1 + ((ibmbx_limit_addr - ibmbx_base_addr) / 4) + 1 < request_dwords; + ) + `uvm_info(`gfn, + $sformatf("imbx_size_dwords = %0d, request_dwords = %0d", + ((mbx_config.ibmbx_limit_addr - mbx_config.ibmbx_base_addr) / 4) + 1, + mbx_config.request_dwords), + UVM_MEDIUM) + p_expect_error = 1'b1; + endfunction + +endclass : mbx_imbx_oob_vseq diff --git a/hw/ip/mbx/dv/env/seq_lib/mbx_reg_reset_val_vseq.sv b/hw/ip/mbx/dv/env/seq_lib/mbx_reg_reset_val_vseq.sv new file mode 100644 index 0000000000000..2ae00081d71ee --- /dev/null +++ b/hw/ip/mbx/dv/env/seq_lib/mbx_reg_reset_val_vseq.sv @@ -0,0 +1,90 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class mbx_reg_reset_val_vseq extends mbx_base_vseq; + + rand int unsigned m_mbx_num; + + `uvm_object_utils(mbx_reg_reset_val_vseq) + + constraint mbx_num_c { m_mbx_num == 0; } + + function new(string name = ""); + super.new(); + do_mbx_init = 1'b0; + endfunction: new + + task soc_reg_reset_val_check(); + bit [31:0] rd_data; + + `uvm_info(get_full_name(), "soc_reg_reset_val_check -- Start", UVM_DEBUG) + + // DOE Control Register + csr_rd(m_mbx_soc_ral.m_mbxsoc_reg_block[m_mbx_num].mbxctl, rd_data); + `DV_CHECK_EQ(rd_data, 0, "Reset value mismatch for mbxctl") + + // DOE Status Register + csr_rd(m_mbx_soc_ral.m_mbxsoc_reg_block[m_mbx_num].mbxsts, rd_data); + `DV_CHECK_EQ(rd_data, 1, "Reset value mismatch for mbxsts") + + // DOE Write Data Register + csr_rd(m_mbx_soc_ral.m_mbxsoc_reg_block[m_mbx_num].mbxwrdat, rd_data); + `DV_CHECK_EQ(rd_data, 0, "Reset value mismatch for mbxwrdat") + + // DOE Read Data Register + csr_rd(m_mbx_soc_ral.m_mbxsoc_reg_block[m_mbx_num].mbxrddat, rd_data); + `DV_CHECK_EQ(rd_data, 0, "Reset value mismatch for mbxrddat") + + `uvm_info(get_full_name(), "soc_reg_reset_val_check -- End", UVM_DEBUG) + endtask: soc_reg_reset_val_check + + task core_reg_reset_val_check(); + bit [31:0] rd_data; + + `uvm_info(get_full_name(), "core_reg_reset_val_check -- Start", UVM_DEBUG) + // DOE Control Register + csr_rd(ral.m_mbxhst_reg_block[m_mbx_num].mbxctl, rd_data); + `DV_CHECK_EQ(rd_data, 0, "Reset value mismatch for uarch mbxctl") + + // DOE Status Register + csr_rd(ral.m_mbxhst_reg_block[m_mbx_num].mbxsts, rd_data); + `DV_CHECK_EQ(rd_data, 1, "Reset value mismatch for uarch mbxsts") + + // DOE IBBASE Register + csr_rd(ral.m_mbxhst_reg_block[m_mbx_num].mbxibbase, rd_data); + `DV_CHECK_EQ(rd_data, 0, "Reset value mismatch for uarch mbxibbase") + + // DOE IBLIMIT Register + csr_rd(ral.m_mbxhst_reg_block[m_mbx_num].mbxiblmit, rd_data); + `DV_CHECK_EQ(rd_data, 0, "Reset value mismatch for uarch mbxiblmit") + + // DOE OBBASE Register + csr_rd(ral.m_mbxhst_reg_block[m_mbx_num].mbxobbase, rd_data); + `DV_CHECK_EQ(rd_data, 0, "Reset value mismatch for uarch mbxobbase") + + // DOE OBLIMIT Register + csr_rd(ral.m_mbxhst_reg_block[m_mbx_num].mbxoblmit, rd_data); + `DV_CHECK_EQ(rd_data, 0, "Reset value mismatch for uarch mbxoblmit") + + // DOE OBDWCNT Register + csr_rd(ral.m_mbxhst_reg_block[m_mbx_num].mbxobdwcnt, rd_data); + `DV_CHECK_EQ(rd_data, 0, "Reset value mismatch for uarch mbxobdwcnt") + + `uvm_info(get_full_name(), "core_reg_reset_val_check -- End", UVM_DEBUG) + endtask: core_reg_reset_val_check + + task body(); + `uvm_info(get_full_name(), "body -- Start", UVM_DEBUG) + super.body(); + + // System Side + soc_reg_reset_val_check(); + + // Host Side + core_reg_reset_val_check(); + + `uvm_info(get_full_name(), "body -- End", UVM_DEBUG) + endtask: body + +endclass: mbx_reg_reset_val_vseq diff --git a/hw/ip/mbx/dv/env/seq_lib/mbx_smoke_vseq.sv b/hw/ip/mbx/dv/env/seq_lib/mbx_smoke_vseq.sv new file mode 100644 index 0000000000000..14521b42f2f28 --- /dev/null +++ b/hw/ip/mbx/dv/env/seq_lib/mbx_smoke_vseq.sv @@ -0,0 +1,24 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class mbx_smoke_vseq extends mbx_base_vseq; + + `uvm_object_utils(mbx_smoke_vseq) + + // Constrain the iteration and transaction counts to produce a longer stress test and, + // importantly, perform multiple request and responses without an intervening block reset. + constraint num_iters_c { num_iters inside {[2:5]}; } + constraint num_txns_c { num_txns inside {[5:10]}; } + + function new(string name = "mbx_smoke_vseq"); + super.new(name); + endfunction : new + + virtual task body(); + `uvm_info(get_full_name(), "body -- smoke test -- Start", UVM_DEBUG) + super.body(); + `uvm_info(get_full_name(), "body -- smoke test -- End", UVM_DEBUG) + endtask : body + +endclass : mbx_smoke_vseq diff --git a/hw/ip/mbx/dv/env/seq_lib/mbx_stress_vseq.sv b/hw/ip/mbx/dv/env/seq_lib/mbx_stress_vseq.sv new file mode 100644 index 0000000000000..81a9127b43e23 --- /dev/null +++ b/hw/ip/mbx/dv/env/seq_lib/mbx_stress_vseq.sv @@ -0,0 +1,87 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class mbx_stress_vseq extends mbx_base_vseq; + + `uvm_object_utils(mbx_stress_vseq) + + // Constrain the iteration and transaction counts to produce a longer stress test and, + // importantly, perform multiple request and responses without an intervening block reset. + constraint num_iters_c { num_iters inside {[5:8]}; } + constraint num_txns_c { num_txns inside {[2:12]}; } + + // Whether to produce these stimuli to stress the DUT. + rand bit aborts_en; // Aborts from the SoC side. + rand bit errors_en; // Errors from the Core side. + rand bit panics_en; // FW-initiated reset/Abort clear from the Core side. + + constraint stressors_en_c { + aborts_en dist {0:/75, 1:/25}; + errors_en dist {0:/75, 1:/25}; + panics_en dist {0:/75, 1:/25}; + } + + // TODO: decide how often errors and aborts should be generated; sequences shall probably want + // to override the behavior, but we shall also want some kind of sensible default. Perhaps + // randomize the number of clock cycles until we raise an abort, rather than treating each clock + // cycle as an independent event? + + // Raise an Abort request from the SoC side? + virtual task do_abort(ref bit aborted); + if (aborts_en && !($urandom() % 1024)) begin + `uvm_info(`gfn, "Setting ABORT condition", UVM_LOW) + mbx_abort(); + aborted = 1'b1; + end + endtask + + // Raise an Error from the Core side? + virtual task do_error(ref bit errored); + if (errors_en && !($urandom() % 1024)) begin + `uvm_info(`gfn, "Setting ERROR condition", UVM_LOW) + ral.control.error.set(1'b1); + csr_update(ral.control); + errored = 1'b1; + end + endtask + + // Raise a FW-initiated Reset from the Core side? + virtual task do_panic(ref bit panicked); + if (panics_en && !($urandom() % 1024)) begin + `uvm_info(`gfn, "Setting FW RESET/ABORT ACK condition", UVM_LOW) + ral.control.abort.set(1'b1); + csr_update(ral.control); + panicked = 1'b1; + end + endtask + + // Decide upon the access delays for this transaction. + virtual function bit choose_access_delays(output int min_acc_delay, output int max_acc_delay); + min_acc_delay = $urandom_range(0, 5); + max_acc_delay = $urandom_range(min_acc_delay, 20); + return 1'b1; + endfunction + + virtual function bit choose_response_delays(output int min_rsp_delay, output int max_rsp_delay); + min_rsp_delay = $urandom_range(0, 5); + max_rsp_delay = $urandom_range(min_rsp_delay, 20); + return 1'b1; + endfunction + + function new(string name = "mbx_stress_vseq"); + super.new(name); + endfunction : new + + virtual task body(); + `uvm_info(get_full_name(), "body -- stress test -- Start", UVM_DEBUG) + + `uvm_info(`gfn, $sformatf("aborts_en = %0b", aborts_en), UVM_LOW) + `uvm_info(`gfn, $sformatf("errors_en = %0b", errors_en), UVM_LOW) + `uvm_info(`gfn, $sformatf("panics_en = %0b", panics_en), UVM_LOW) + + super.body(); + `uvm_info(get_full_name(), "body -- stress test -- End", UVM_DEBUG) + endtask : body + +endclass : mbx_stress_vseq diff --git a/hw/ip/mbx/dv/env/seq_lib/mbx_tl_device_seq.sv b/hw/ip/mbx/dv/env/seq_lib/mbx_tl_device_seq.sv new file mode 100644 index 0000000000000..88faae49889dc --- /dev/null +++ b/hw/ip/mbx/dv/env/seq_lib/mbx_tl_device_seq.sv @@ -0,0 +1,20 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class mbx_tl_device_seq extends cip_tl_device_seq; + + `uvm_object_utils(mbx_tl_device_seq) + + `uvm_object_new + + virtual function void update_mem(REQ rsp); + super.update_mem(rsp); + rsp.d_user = rsp.compute_d_user(); + if (inject_d_chan_intg_err) begin + rsp.tl_intg_err_type = tl_intg_err_type; + rsp.inject_d_chan_intg_err(); + end + endfunction: update_mem + +endclass: mbx_tl_device_seq diff --git a/hw/ip/mbx/dv/env/seq_lib/mbx_tl_reg_seq.sv b/hw/ip/mbx/dv/env/seq_lib/mbx_tl_reg_seq.sv new file mode 100644 index 0000000000000..a385a4694b27a --- /dev/null +++ b/hw/ip/mbx/dv/env/seq_lib/mbx_tl_reg_seq.sv @@ -0,0 +1,26 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class mbx_tl_reg_seq extends cip_tl_host_single_seq; + + rand tlul_pkg::tl_a_user_t auser; + + `uvm_object_utils(mbx_tl_reg_seq) + + constraint size_c { size == 2; } + constraint mask_c { mask == 'hf; } + constraint auser_c { auser.instr_type == prim_mubi_pkg::MuBi4False; } + + function new (string name = ""); + super.new(name); + endfunction: new + + virtual function void randomize_req(REQ req, int idx); + control_rand_size = 1; + control_rand_opcode = 1; + super.randomize_req(req, idx); + req.a_user = auser; + endfunction + +endclass: mbx_tl_reg_seq diff --git a/hw/ip/mbx/dv/env/seq_lib/mbx_vseq_list.sv b/hw/ip/mbx/dv/env/seq_lib/mbx_vseq_list.sv new file mode 100644 index 0000000000000..594ac63e41d09 --- /dev/null +++ b/hw/ip/mbx/dv/env/seq_lib/mbx_vseq_list.sv @@ -0,0 +1,11 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`include "mbx_tl_reg_seq.sv" +`include "mbx_tl_device_seq.sv" +`include "mbx_base_vseq.sv" +`include "mbx_common_vseq.sv" +`include "mbx_smoke_vseq.sv" +`include "mbx_stress_vseq.sv" +`include "mbx_imbx_oob_vseq.sv" diff --git a/hw/ip/mbx/dv/mbx_sim.core b/hw/ip/mbx/dv/mbx_sim.core new file mode 100644 index 0000000000000..7a6db0723c85f --- /dev/null +++ b/hw/ip/mbx/dv/mbx_sim.core @@ -0,0 +1,29 @@ +CAPI=2: +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:dv:mbx_sim:0.1" +description: "MBX DV sim target" +filesets: + files_rtl: + depend: + - lowrisc:ip:mbx + + files_dv: + depend: + - lowrisc:dv:mbx_test + - lowrisc:dv:mbx_sva + files: + - tb.sv + file_type: systemVerilogSource + +targets: + sim: &sim_target + toplevel: tb + filesets: + - files_rtl + - files_dv + default_tool: vcs + + lint: + <<: *sim_target diff --git a/hw/ip/mbx/dv/mbx_sim_cfg.hjson b/hw/ip/mbx/dv/mbx_sim_cfg.hjson new file mode 100644 index 0000000000000..803039c078fc2 --- /dev/null +++ b/hw/ip/mbx/dv/mbx_sim_cfg.hjson @@ -0,0 +1,110 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + // Name of the sim cfg - typically same as the name of the DUT. + name: mbx + + // Top level dut name (sv module). + dut: mbx + + // Top level testbench name (sv module). + tb: tb + + // Simulator used to sign off this block + tool: xcelium + + // Fusesoc core file used for building the file list. + fusesoc_core: lowrisc:dv:mbx_sim:0.1 + + // Testplan hjson file. + testplan: "{proj_root}/hw/ip/mbx/data/mbx_testplan.hjson" + + // RAL spec - used to generate the RAL model. + ral_spec: "{proj_root}/hw/ip/mbx/data/mbx.hjson" + + // Import additional common sim cfg files. + import_cfgs: [// Project wide common sim cfg file + "{proj_root}/hw/dv/tools/dvsim/common_sim_cfg.hjson", + "{proj_root}/hw/dv/tools/dvsim/tests/csr_tests.hjson", + "{proj_root}/hw/dv/tools/dvsim/tests/alert_test.hjson", + "{proj_root}/hw/dv/tools/dvsim/tests/tl_access_tests.hjson", + "{proj_root}/hw/dv/tools/dvsim/tests/sec_cm_tests.hjson", + // Disable stress_all and stress_all_with_rand_reset tests as they are not + // implemented yet #21298 + // "{proj_root}/hw/dv/tools/dvsim/tests/stress_tests.hjson" + ] + + // Add additional tops for simulation. +// sim_tops: ["mbx_bind", "mbx_cov_bind"] + + // Default iterations for all tests - each test entry can override this. + reseed: 2 + +// overrides: [ +// // +// // Override coverage config files to add our elaboration time coverage exclusions etc. +// // +// { +// name: default_vcs_cov_cfg_file +// value: "-cm_hier {dv_root}/tools/vcs/cover.cfg+{dv_root}/tools/vcs/common_cov_excl.cfg+{proj_root}/hw/ip/mbx/dv/cov/mbx_cover.cfg" +// } +// // The jtag agent requires the data and bytenable widths to be increased. +// { +// name: tl_dw +// value: 64 +// } +// { +// name: tl_dbw +// value: 8 +// } +// ] + +// // Add MBX specific exclusion files. +// vcs_cov_excl_files: ["{proj_root}/hw/ip/mbx/dv/cov/mbx_cov_unr_excl.el", +// "{proj_root}/hw/ip/mbx/dv/cov/mbx_terminal_st_excl.el"] + + // Default UVM test and seq class name. + uvm_test: mbx_base_test + uvm_test_seq: mbx_base_vseq + + run_opts: ["+cdc_instrumentation_enabled=1"] + + // List of test specifications. + tests: [ + { + name: mbx_smoke + uvm_test_seq: mbx_smoke_vseq + } + { + name: mbx_stress + uvm_test_seq: mbx_stress_vseq + } + { + name: mbx_stress_zero_delays + uvm_test_seq: mbx_stress_vseq + run_opts: ["+zero_delays=1"] + } + { + name: mbx_imbx_oob + uvm_test_seq: mbx_imbx_oob_vseq + } + ] + + // List of regressions. + regressions: [ + { + name: smoke + tests: ["mbx_smoke"] + } + { + name: stress + tests : ["mbx_stress", + "mbx_stress_zero_delays" +// "mbx_sw_access", +// "mbx_sw_smoke", +// "mbx_sw_alert_test" + ] + } + ] +} diff --git a/hw/ip/mbx/dv/sva/mbx_bind.sv b/hw/ip/mbx/dv/sva/mbx_bind.sv new file mode 100644 index 0000000000000..36cfdcdf0dd60 --- /dev/null +++ b/hw/ip/mbx/dv/sva/mbx_bind.sv @@ -0,0 +1,42 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +module mbx_bind; + + bind mbx tlul_assert #( + .EndpointType("Host") + ) mbx_agx_tlul_assert_core ( + .clk_i(hstRegAccL3), + .rst_ni(hstRegReset), + .h2d (mbx_agx_tlReQH2d), + .d2h (agx_mbx_tlRsPD2h) + ); + + bind mbx tlul_assert #( + .EndpointType("Device") + ) agx_mbx_tlul_assert_device ( + .clk_i(hstRegAccL3), + .rst_ni(hstRegReset), + .h2d (agx_mbx_tlReQH2d), + .d2h (mbx_agx_tlRsPD2h) + ); + + bind mbx tlul_assert #( + .EndpointType("Device") + ) scx_mbx_tlul_assert_device( + .clk_i(sysRegAccL3), + .rst_ni(sysRegReset), + .h2d (scx_mbx_tlReQH2d), + .d2h (mbx_scx_tlRsPD2h) + ); + + // TODO: Revisit this + // bind mbx mbx_regs_csr_assert_fpv mbx_regs_csr_assert ( + // .clk_i, + // .rst_ni, + // .h2d (regs_tl_i), + // .d2h (regs_tl_o) + // ); + +endmodule diff --git a/hw/ip/mbx/dv/sva/mbx_sva.core b/hw/ip/mbx/dv/sva/mbx_sva.core new file mode 100644 index 0000000000000..aab799b77d541 --- /dev/null +++ b/hw/ip/mbx/dv/sva/mbx_sva.core @@ -0,0 +1,39 @@ +CAPI=2: +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:dv:mbx_sva:0.1" +description: "MBX assertion modules and bind file." +filesets: + files_dv: + depend: + - lowrisc:tlul:headers + # TODO: Revisit this + # - lowrisc:fpv:csr_assert_gen + files: + - mbx_bind.sv + file_type: systemVerilogSource + + files_formal: + depend: + - lowrisc:ip:mbx + +generate: + csr_assert_gen: + generator: csr_assert_gen + parameters: + spec: ../../data/mbx.hjson + +targets: + default: &default_target + filesets: + - files_dv + # TODO: Revisit this + # generate: + # - csr_assert_gen + formal: + <<: *default_target + filesets: + - files_formal + - files_dv + toplevel: mbx diff --git a/hw/ip/mbx/dv/tb.sv b/hw/ip/mbx/dv/tb.sv new file mode 100644 index 0000000000000..1b82cda51c387 --- /dev/null +++ b/hw/ip/mbx/dv/tb.sv @@ -0,0 +1,109 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +module tb; + // dep packages + import uvm_pkg::*; + import dv_utils_pkg::*; + import mbx_env_pkg::*; + import mbx_test_pkg::*; + + // macro includes + `include "uvm_macros.svh" + `include "dv_macros.svh" + `include "cip_macros.svh" + + wire clk, rst_n; + wire [NUM_MAX_INTERRUPTS-1:0] interrupts; + wire [NUM_MAX_INTERRUPTS-1:0] interrupts_soc; + + clk_rst_if i_clk_rst_if(.clk(clk), .rst_n(rst_n)); + tl_if i_tl_scxb_mbx_core_if(.clk(clk), .rst_n(rst_n)); + tl_if i_tl_agxb_mbx_core_if(.clk(clk), .rst_n(rst_n)); + tl_if i_tl_mbx_agxb_device_if(.clk(clk), .rst_n(rst_n)); + pins_if #(NUM_MAX_INTERRUPTS) i_intr_core_if(interrupts); + pins_if #(NUM_MAX_INTERRUPTS) i_intr_soc_if(interrupts_soc); + + `DV_ALERT_IF_CONNECT() + + // dut + mbx #() dut + ( + .clk_i(clk), + .rst_ni(rst_n), + .doe_intr_support_o(), + .doe_intr_en_o(), + .doe_intr_o(interrupts_soc[MbxSocDOE]), + .doe_async_msg_support_o(), + // various tlul interfaces + .core_tl_d_o(i_tl_agxb_mbx_core_if.d2h), + .core_tl_d_i(i_tl_agxb_mbx_core_if.h2d), + .soc_tl_d_o(i_tl_scxb_mbx_core_if.d2h), + .soc_tl_d_i(i_tl_scxb_mbx_core_if.h2d), + .sram_tl_h_o(i_tl_mbx_agxb_device_if.h2d), + .sram_tl_h_i(i_tl_mbx_agxb_device_if.d2h), + // alerts and interrupts + .intr_mbx_ready_o(interrupts[MbxCoreReady]), + .intr_mbx_abort_o(interrupts[MbxCoreAbort]), + .intr_mbx_error_o(interrupts[MbxCoreError]), + .alert_rx_i(alert_rx), + .alert_tx_o(alert_tx) + ); + + initial begin + // drive clk and rst_n from clk_if + i_clk_rst_if.set_active(); + uvm_config_db#(virtual clk_rst_if)::set( + null, "*.env", "clk_rst_vif", i_clk_rst_if); + uvm_config_db#(virtual clk_rst_if)::set( + null, "*.env", "clk_rst_vif_mbx_mem_reg_block", i_clk_rst_if); + uvm_config_db#(virtual clk_rst_if)::set( + null, "*.env", "clk_rst_vif_mbx_soc_reg_block", i_clk_rst_if); + uvm_config_db#(virtual clk_rst_if)::set( + null, "*.env", "clk_rst_vif_mbx_reg_block", i_clk_rst_if); + uvm_config_db#(virtual clk_rst_if)::set( + null, "*.env", "clk_rst_vif_mbx_core_reg_block", i_clk_rst_if); + uvm_config_db#(virtual clk_rst_if)::set( + null, "*.env", "clk_rst_vif_mbx_soc_reg_block", i_clk_rst_if); + uvm_config_db#(virtual tl_if)::set( + null, "*.env.m_tl_agent_mbx_core_reg_block*", "vif", + i_tl_agxb_mbx_core_if); + uvm_config_db#(virtual tl_if)::set( + null, "*.env.m_tl_agent_mbx_soc_reg_block*", "vif", + i_tl_scxb_mbx_core_if); + +// uvm_config_db#(virtual tl_if)::set( +// null, "*.env.m_tl_agent_mbxuarch_reg_block*", "vif", +// i_tl_agxb_mbx_core_if); +// uvm_config_db#(virtual tl_if)::set( +// null, "*.env.m_tl_agent_mbx_reg_block*", "vif", +// i_tl_scxb_mbx_core_if); + + uvm_config_db#(virtual tl_if)::set( + null, "*.env.m_tl_agent_mbx_mem_reg_block*", "vif", + i_tl_mbx_agxb_device_if); + uvm_config_db#(intr_vif)::set( + null, "*.env", "intr_vif", i_intr_core_if); + uvm_config_db#(intr_vif)::set( + null, "*.env", "intr_soc_vif", i_intr_soc_if); + $timeformat(-12, 0, " ps", 12); + run_test(); + end + + //FIXME: Previous reg_intf + //mbx_reg_intf i_mbx_reg_intf( + // .clk(clk), + // .rst_n(rst_n), + // .req(i_tl_scxb_mbx_core_if.h2d), + // .rsp(i_tl_scxb_mbx_core_if.d2h) + //); + + //mbxuarch_reg_intf i_mbxuarch_reg_intf( + // .clk(clk), + // .rst_n(rst_n), + // .req(i_tl_agxb_mbx_core_if.h2d), + // .rsp(i_tl_agxb_mbx_core_if.d2h) + //); + +endmodule diff --git a/hw/ip/mbx/dv/tests/mbx_base_test.sv b/hw/ip/mbx/dv/tests/mbx_base_test.sv new file mode 100644 index 0000000000000..08de1b16e48f7 --- /dev/null +++ b/hw/ip/mbx/dv/tests/mbx_base_test.sv @@ -0,0 +1,20 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class mbx_base_test extends cip_base_test #( + .CFG_T(mbx_env_cfg), + .ENV_T(mbx_env) + ); + + `uvm_component_utils(mbx_base_test) + `uvm_component_new + + // the base class dv_base_test creates the following instances: + // mbx_env_cfg: cfg + // mbx_env: env + + // the base class also looks up UVM_TEST_SEQ plusarg to create and run that seq in + // the run_phase; as such, nothing more needs to be done + +endclass : mbx_base_test diff --git a/hw/ip/mbx/dv/tests/mbx_test.core b/hw/ip/mbx/dv/tests/mbx_test.core new file mode 100644 index 0000000000000..d0f625e6f5385 --- /dev/null +++ b/hw/ip/mbx/dv/tests/mbx_test.core @@ -0,0 +1,19 @@ +CAPI=2: +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:dv:mbx_test:0.1" +description: "MBX DV UVM test" +filesets: + files_dv: + depend: + - lowrisc:dv:mbx_env + files: + - mbx_test_pkg.sv + - mbx_base_test.sv: {is_include_file: true} + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_dv diff --git a/hw/ip/mbx/dv/tests/mbx_test_pkg.sv b/hw/ip/mbx/dv/tests/mbx_test_pkg.sv new file mode 100644 index 0000000000000..1c18848f3b221 --- /dev/null +++ b/hw/ip/mbx/dv/tests/mbx_test_pkg.sv @@ -0,0 +1,22 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +package mbx_test_pkg; + // dep packages + import uvm_pkg::*; + import cip_base_pkg::*; + import mbx_env_pkg::*; + + // macro includes + `include "uvm_macros.svh" + `include "dv_macros.svh" + + // local types + + // functions + + // package sources + `include "mbx_base_test.sv" + +endpackage: mbx_test_pkg diff --git a/hw/ip/mbx/lint/mbx.vlt b/hw/ip/mbx/lint/mbx.vlt new file mode 100644 index 0000000000000..137d9512c2b70 --- /dev/null +++ b/hw/ip/mbx/lint/mbx.vlt @@ -0,0 +1,7 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// waiver file for mbx + +`verilator_config diff --git a/hw/ip/mbx/lint/mbx.waiver b/hw/ip/mbx/lint/mbx.waiver new file mode 100644 index 0000000000000..fe94f4a9fa725 --- /dev/null +++ b/hw/ip/mbx/lint/mbx.waiver @@ -0,0 +1,40 @@ +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# waiver file for mbx + +waive -rules SAME_NAME_TYPE -location {mbx.sv} -regexp {'mbx' is used as a module here, and as a} \ + -comment "The module has a very short name and it is hence possible that other scopes use the same name for a signal." + +waive -rules INPUT_NOT_READ -location {mbx_imbx.sv} -regexp {Input port 'sysif_status_busy_i' is not read from} \ + -comment "Signal only used for assertions" +waive -rules HIER_BRANCH_NOT_READ: -location {mbx_imbx.sv} -regexp {Net 'sysif_status_busy_i' is not read from} \ + -comment "Signal only used for assertions" + +waive -rules INPUT_NOT_READ -location {mbx_hostif.sv} -regexp {Input port 'hostif_imbx_write_ptr_i\[1:0\]' is not read from} \ + -comment "Write pointer is always 4B aligned" +waive -rules HIER_BRANCH_NOT_READ -location {mbx_hostif.sv} -regexp {Net 'hostif_imbx_write_ptr_i\[1:0\]' is not read from} \ + -comment "Write pointer is always 4B aligned" +waive -rules INPUT_NOT_READ -location {mbx_hostif.sv} -regexp {Input port 'hostif_ombx_read_ptr_i\[1:0\]' is not read from} \ + -comment "Read pointer is always 4B aligned" +waive -rules HIER_BRANCH_NOT_READ -location {mbx_hostif.sv} -regexp {Net 'hostif_ombx_read_ptr_i\[1:0\]' is not read from} \ + -comment "Read pointer is always 4B aligned" + +waive -rules INPUT_NOT_READ -location {mbx_ombx.sv} -regexp {Input port 'sysif_status_ready_i' is not read from} \ + -comment "Signal only used for assertions" +waive -rules HIER_BRANCH_NOT_READ -location {mbx_ombx.sv} -regexp {Net 'sysif_status_ready_i' is not read from} \ + -comment "Signal only used for assertions" + +# sysif_read_data_read_valid_i and all connected signals are only used for assertions +waive -rules INPUT_NOT_READ -location {mbx_ombx.sv} -regexp {Input port 'sysif_read_data_read_valid_i' is not read from} \ + -comment "Signal only used for assertions" +waive -rules HIER_BRANCH_NOT_READ -location {mbx_ombx.sv} -regexp {Net 'sysif_read_data_read_valid_i' is not read from} \ + -comment "Signal only used for assertions" +waive -rules HIER_NET_NOT_READ: -location {mbx.sv} -regexp {Net 'sysif_read_data_read_valid' is not read from} \ + -comment "Signal only used for assertions" +waive -rules HIER_NET_NOT_READ: -location {mbx.sv} -regexp {Connected net 'sysif_read_data_read_valid_i' at} \ + -comment "Signal only used for assertions" + +waive -rules NOT_READ -location {mbx_ombx.sv} -regexp {Signal 'ombx_is_empty' is not read from} \ + -comment "Signal only used for assertions" diff --git a/hw/ip/mbx/mbx.core b/hw/ip/mbx/mbx.core new file mode 100644 index 0000000000000..fa24ff79d1627 --- /dev/null +++ b/hw/ip/mbx/mbx.core @@ -0,0 +1,82 @@ +CAPI=2: +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:ip:mbx:0.1" +description: "DOE Mailbox" +filesets: + files_rtl: + depend: + - lowrisc:prim:all + - lowrisc:tlul:headers + - lowrisc:ip:tlul + files: + - rtl/mbx_reg_pkg.sv + - rtl/mbx.sv + - rtl/mbx_core_reg_top.sv + - rtl/mbx_soc_reg_top.sv + - rtl/mbx_hostif.sv + - rtl/mbx_sysif.sv + - rtl/mbx_sramrwarb.sv + - rtl/mbx_fsm.sv + - rtl/mbx_imbx.sv + - rtl/mbx_ombx.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + files: + - lint/mbx.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + files: + - lint/mbx.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + +parameters: + SYNTHESIS: + datatype: bool + paramtype: vlogdefine + +targets: + default: &default_target + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl + toplevel: mbx + + lint: + <<: *default_target + default_tool: verilator + parameters: + - SYNTHESIS=true + tools: + verilator: + mode: lint-only + verilator_options: + - "-Wall" + + syn: + <<: *default_target + # TODO: set default to DC once + # this option is available + # olofk/edalize#89 + default_tool: icarus + parameters: + - SYNTHESIS=true diff --git a/hw/ip/mbx/rtl/mbx.sv b/hw/ip/mbx/rtl/mbx.sv new file mode 100644 index 0000000000000..9cb4dd46aa677 --- /dev/null +++ b/hw/ip/mbx/rtl/mbx.sv @@ -0,0 +1,345 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`include "prim_assert.sv" + +module mbx + import tlul_pkg::*; + import mbx_reg_pkg::*; +#( + parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}}, + parameter int unsigned CfgSramAddrWidth = 32, + parameter int unsigned CfgSramDataWidth = 32, + parameter int unsigned CfgObjectSizeWidth = 11, + parameter bit DoeIrqSupport = 1'b1, + parameter bit DoeAsyncMsgSupport = 1'b1 +) ( + input logic clk_i, + input logic rst_ni, + // Comportable interrupt to the OT + output logic intr_mbx_ready_o, + output logic intr_mbx_abort_o, + output logic intr_mbx_error_o, + // Custom straps for capability register implementation + output logic doe_intr_support_o, + output logic doe_intr_en_o, + output logic doe_intr_o, + output logic doe_async_msg_support_o, + // Alerts + input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i, + output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o, + // Device port facing OpenTitan + input tlul_pkg::tl_h2d_t core_tl_d_i, + output tlul_pkg::tl_d2h_t core_tl_d_o, + // Device port facing SoC + input tlul_pkg::tl_h2d_t soc_tl_d_i, + output tlul_pkg::tl_d2h_t soc_tl_d_o, + // Host port to access private SRAM + input tlul_pkg::tl_d2h_t sram_tl_h_i, + output tlul_pkg::tl_h2d_t sram_tl_h_o +); + ////////////////////////////////////////////////////////////////////////////// + // General signals for the mailbox + ////////////////////////////////////////////////////////////////////////////// + + // Collect all error sources + logic sysif_intg_err, tl_sram_intg_err, imbx_state_error, ombx_state_error; + logic alert_signal, sram_err; + + assign alert_signal = sysif_intg_err | + tl_sram_intg_err | + ombx_state_error | + imbx_state_error; + + ////////////////////////////////////////////////////////////////////////////// + // Control and Status signals of the host interface + ////////////////////////////////////////////////////////////////////////////// + + logic hostif_event_intr_ready, hostif_event_intr_abort; + logic hostif_address_range_valid, hostif_address_range_valid_write; + logic sysif_control_abort_set; + logic doe_async_msg_en; + logic imbx_overflow_error_set; + + // Status signal inputs from the sysif to the hostif + logic sysif_status_busy, sysif_status_error; + logic doe_async_msg_set, doe_async_msg_clear; + + // Setter signals from the hostif to the sysif + logic hostif_control_abort_clear, hostif_control_error_set; + + // Alias signals from the sys interface + logic [CfgSramAddrWidth-1:0] sysif_intr_msg_addr; + logic [CfgSramDataWidth-1:0] sysif_intr_msg_data; + + ////////////////////////////////////////////////////////////////////////////// + // Signals for the Inbox + ////////////////////////////////////////////////////////////////////////////// + + logic [CfgSramAddrWidth-1:0] hostif_imbx_base, hostif_imbx_limit; + logic [CfgSramAddrWidth-1:0] imbx_sram_write_ptr; + + ////////////////////////////////////////////////////////////////////////////// + // Signals for the Outbox + ////////////////////////////////////////////////////////////////////////////// + + logic [CfgSramAddrWidth-1:0] hostif_ombx_base, hostif_ombx_limit; + logic [CfgSramAddrWidth-1:0] ombx_sram_read_ptr; + + logic hostif_ombx_object_size_write, hostif_ombx_object_size_update; + logic [CfgObjectSizeWidth-1:0] hostif_ombx_object_size_wdata, hostif_ombx_object_size_rdata; + + mbx_hostif #( + .AlertAsyncOn ( AlertAsyncOn ), + .CfgSramAddrWidth ( CfgSramAddrWidth ), + .CfgSramDataWidth ( CfgSramDataWidth ), + .CfgObjectSizeWidth( CfgObjectSizeWidth ) + ) u_hostif ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + // Device port to the host side + .tl_host_i ( core_tl_d_i ), + .tl_host_o ( core_tl_d_o ), + .event_intr_ready_i ( hostif_event_intr_ready ), + .event_intr_abort_i ( hostif_event_intr_abort ), + .event_intr_error_i ( imbx_overflow_error_set ), + .intr_ready_o ( intr_mbx_ready_o ), + .intr_abort_o ( intr_mbx_abort_o ), + .intr_error_o ( intr_mbx_error_o ), + .intg_err_i ( alert_signal ), + .sram_err_i ( sram_err ), + .alert_rx_i ( alert_rx_i ), + .alert_tx_o ( alert_tx_o ), + // Access to the control register + .hostif_control_abort_clear_o ( hostif_control_abort_clear ), + .hostif_control_error_set_o ( hostif_control_error_set ), + .hostif_control_error_i ( sysif_status_error ), + .hostif_control_async_msg_set_o ( doe_async_msg_set ), + .hostif_control_async_msg_clear_o ( doe_async_msg_clear ), + // Access to the status register + .hostif_status_busy_i ( sysif_status_busy ), + .hostif_status_sys_intr_en_i ( doe_intr_en_o ), + .hostif_status_sys_async_en_i ( doe_async_msg_en ), + .hostif_status_sys_intr_state_i ( doe_intr_o ), + // Access to the IB/OB RD/WR Pointers + .hostif_imbx_write_ptr_i ( imbx_sram_write_ptr ), + .hostif_ombx_read_ptr_i ( ombx_sram_read_ptr ), + // Access to the memory region registers + .hostif_address_range_valid_write_o ( hostif_address_range_valid_write ), + .hostif_address_range_valid_o ( hostif_address_range_valid ), + .hostif_imbx_base_o ( hostif_imbx_base ), + .hostif_imbx_limit_o ( hostif_imbx_limit ), + .hostif_ombx_base_o ( hostif_ombx_base ), + .hostif_ombx_limit_o ( hostif_ombx_limit ), + // Read/Write access for the OB DW Count register + .hostif_ombx_object_size_write_o ( hostif_ombx_object_size_write ), + .hostif_ombx_object_size_o ( hostif_ombx_object_size_wdata ), + .hostif_ombx_object_size_update_i ( hostif_ombx_object_size_update ), + .hostif_ombx_object_size_i ( hostif_ombx_object_size_rdata ), + // Alias of the interrupt address and data registers from the SYS interface + .sysif_intr_msg_addr_i ( sysif_intr_msg_addr ), + .sysif_intr_msg_data_i ( sysif_intr_msg_data ), + // Control and status inputs coming from the system registers interface + .sysif_control_abort_set_i ( sysif_control_abort_set ) + ); + + ////////////////////////////////////////////////////////////////////////////// + // Control and Status signals of the system interface + ////////////////////////////////////////////////////////////////////////////// + logic sysif_control_go_set; + + ////////////////////////////////////////////////////////////////////////////// + // Signals for the Inbox + ////////////////////////////////////////////////////////////////////////////// + logic imbx_pending; + logic imbx_status_busy_valid, imbx_status_busy; + + // Communication from the outbox to the inbox that all data has been read + logic sys_read_all; + + // Interface signals for SRAM host access to write the incoming data to memory + logic imbx_sram_write_req, imbx_sram_write_gnt; + logic imbx_sram_all_vld_rcvd; + logic [CfgSramDataWidth-1:0] sysif_write_data; + logic sysif_write_data_write_valid; + + ////////////////////////////////////////////////////////////////////////////// + // Signals for the Outbox + ////////////////////////////////////////////////////////////////////////////// + logic ombx_pending; + logic ombx_status_ready_valid, ombx_status_ready; + logic sysif_status_ready; + logic ombx_doe_intr_ready_set; + + // Interface signals for SRAM host access to read the memory and serve it to the outbox + logic ombx_sram_read_req, ombx_sram_read_gnt; + logic ombx_sram_read_resp_vld; + logic [CfgSramDataWidth-1:0] ombx_sram_read_data, sysif_read_data; + logic sysif_read_data_read_valid, sysif_read_data_write_valid; + + // Combine error outputs of all modules and distribute back error to them to bring all + // modules to the error state if needed + logic mbx_error_set; + assign mbx_error_set = hostif_control_error_set | imbx_overflow_error_set; + + mbx_sysif #( + .CfgSramAddrWidth ( CfgSramAddrWidth ), + .CfgSramDataWidth ( CfgSramDataWidth ), + .DoeIrqSupport ( DoeIrqSupport ), + .DoeAsyncMsgSupport ( DoeAsyncMsgSupport ) + ) u_sysif ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .tl_sys_i ( soc_tl_d_i ), + .tl_sys_o ( soc_tl_d_o ), + .intg_err_o ( sysif_intg_err ), + // Custom interrupt to the system requester + .doe_intr_support_o ( doe_intr_support_o ), + .doe_intr_en_o ( doe_intr_en_o ), + .doe_intr_o ( doe_intr_o ), + // Asynchronous message to the requester + .doe_async_msg_support_o ( doe_async_msg_support_o ), + .doe_async_msg_en_o ( doe_async_msg_en ), + .doe_async_msg_set_i ( doe_async_msg_set ), + .doe_async_msg_clear_i ( doe_async_msg_clear ), + // Abort clearing from the host + .sysif_abort_ack_i ( hostif_control_abort_clear ), + // Access to the control register + .sysif_control_abort_set_o ( sysif_control_abort_set ), + .sysif_control_go_set_o ( sysif_control_go_set ), + // Access to the status register + .sysif_status_busy_valid_i ( imbx_status_busy_valid ), + .sysif_status_busy_i ( imbx_status_busy ), + .sysif_status_busy_o ( sysif_status_busy ), + .sysif_status_doe_intr_ready_set_i ( ombx_doe_intr_ready_set ), + .sysif_status_error_set_i ( mbx_error_set ), + .sysif_status_error_o ( sysif_status_error ), + .sysif_status_ready_valid_i ( ombx_status_ready_valid ), + .sysif_status_ready_i ( ombx_status_ready ), + .sysif_status_ready_o ( sysif_status_ready ), + // Alias of the interrupt address and data registers to the host interface + .sysif_intr_msg_addr_o ( sysif_intr_msg_addr ), + .sysif_intr_msg_data_o ( sysif_intr_msg_data ), + // Control lines for backpressuring the bus + .imbx_pending_i ( imbx_pending ), + .ombx_pending_i ( ombx_pending ), + // Data interface for inbound and outbound mailbox + .write_data_write_valid_o ( sysif_write_data_write_valid ), + .write_data_o ( sysif_write_data ), + .read_data_read_valid_o ( sysif_read_data_read_valid ), + .read_data_write_valid_o ( sysif_read_data_write_valid ), + .read_data_i ( sysif_read_data ) + ); + + + mbx_imbx #( + .CfgSramAddrWidth( CfgSramAddrWidth ), + .CfgSramDataWidth( CfgSramDataWidth ) + ) u_imbx ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + // Interface to the host port + .imbx_state_error_o ( imbx_state_error ), + .imbx_pending_o ( imbx_pending ), + .imbx_irq_ready_o ( hostif_event_intr_ready ), + .imbx_irq_abort_o ( hostif_event_intr_abort ), + .imbx_status_busy_update_o ( imbx_status_busy_valid ), + .imbx_overflow_error_set_o ( imbx_overflow_error_set ), + .imbx_status_busy_o ( imbx_status_busy ), + .hostif_control_abort_clear_i( hostif_control_abort_clear ), + .mbx_error_set_i ( mbx_error_set ), + .sys_read_all_i ( sys_read_all ), + // SRAM range configuration + .hostif_range_valid_write_i ( hostif_address_range_valid_write ), + .hostif_range_valid_i ( hostif_address_range_valid ), + .hostif_base_i ( hostif_imbx_base ), + .hostif_limit_i ( hostif_imbx_limit ), + // Interface to the system port + .sysif_status_busy_i ( sysif_status_busy ), + .sysif_control_go_set_i ( sysif_control_go_set ), + .sysif_control_abort_set_i ( sysif_control_abort_set ), + .sysif_data_write_valid_i ( sysif_write_data_write_valid ), + // Host interface to access private SRAM + .hostif_sram_write_req_o ( imbx_sram_write_req ), + .hostif_sram_write_gnt_i ( imbx_sram_write_gnt ), + .hostif_sram_all_vld_rcvd_i ( imbx_sram_all_vld_rcvd ), + .hostif_sram_write_ptr_o ( imbx_sram_write_ptr ) + ); + + + mbx_ombx #( + .CfgSramAddrWidth ( CfgSramAddrWidth ), + .CfgSramDataWidth ( CfgSramDataWidth ), + .CfgObjectSizeWidth ( CfgObjectSizeWidth ) + ) u_ombx ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .ombx_doe_intr_ready_set_o ( ombx_doe_intr_ready_set ), + // Interface to the host port + .ombx_state_error_o ( ombx_state_error ), + .ombx_pending_o ( ombx_pending ), + .ombx_status_ready_update_o ( ombx_status_ready_valid ), + .ombx_status_ready_o ( ombx_status_ready ), + // SRAM range configuration + .hostif_range_valid_write_i ( hostif_address_range_valid_write ), + .hostif_range_valid_i ( hostif_address_range_valid ), + .hostif_base_i ( hostif_ombx_base ), + .hostif_limit_i ( hostif_ombx_limit ), + .sys_read_all_o ( sys_read_all ), + // Control and signals from the host and system interface + .sysif_status_ready_i ( sysif_status_ready ), + // Writing a 1 to control.abort register clears the abort condition + .hostif_control_abort_clear_i ( hostif_control_abort_clear ), + .mbx_error_set_i ( mbx_error_set ), + .sysif_control_abort_set_i ( sysif_control_abort_set ), + .sysif_read_data_read_valid_i ( sysif_read_data_read_valid ), + .sysif_read_data_write_valid_i ( sysif_read_data_write_valid ), + // Interface for the object size register + .hostif_ombx_object_size_write_i ( hostif_ombx_object_size_write ), + .hostif_ombx_object_size_i ( hostif_ombx_object_size_wdata ), + .hostif_ombx_object_size_update_o( hostif_ombx_object_size_update ), + .hostif_ombx_object_size_o ( hostif_ombx_object_size_rdata ), + // DOE data coming from the SRAM + .ombx_read_data_o ( sysif_read_data ), + // Host interface to access private SRAM + .ombx_sram_read_req_o ( ombx_sram_read_req ), + .ombx_sram_read_gnt_i ( ombx_sram_read_gnt ), + .ombx_sram_read_ptr_o ( ombx_sram_read_ptr ), + .ombx_sram_read_resp_valid_i ( ombx_sram_read_resp_vld ), + .ombx_sram_read_resp_i ( ombx_sram_read_data ) + ); + + // Host port connection to access the private SRAM. + // Arbitrates between inbound and outbound mailbox + mbx_sramrwarb #( + .CfgSramAddrWidth( CfgSramAddrWidth ), + .CfgSramDataWidth( CfgSramDataWidth ) + ) u_sramrwarb ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .tl_host_o ( sram_tl_h_o ), + .tl_host_i ( sram_tl_h_i ), + .intg_err_o ( tl_sram_intg_err ), + .sram_err_o ( sram_err ), + // Host-side acknowledgement of an Abort operation + .hostif_control_abort_clear_i ( hostif_control_abort_clear ), + // Interface to the inbound mailbox + .imbx_sram_write_req_i ( imbx_sram_write_req ), + .imbx_sram_write_gnt_o ( imbx_sram_write_gnt ), + .imbx_sram_write_ptr_i ( imbx_sram_write_ptr ), + .imbx_sram_all_vld_rcvd_o ( imbx_sram_all_vld_rcvd ), + .imbx_write_data_i ( sysif_write_data ), + // Interface to the outbound mailbox + .ombx_sram_read_req_i ( ombx_sram_read_req ), + .ombx_sram_read_gnt_o ( ombx_sram_read_gnt ), + .ombx_sram_read_ptr_i ( ombx_sram_read_ptr ), + .ombx_sram_read_resp_vld_o ( ombx_sram_read_resp_vld ), + .ombx_sram_read_resp_o ( ombx_sram_read_data ) + ); + + // Assertions + `ASSERT_PRIM_REG_WE_ONEHOT_ERROR_TRIGGER_ALERT(RegWeOnehotCheck_A, + u_sysif.u_soc_regs, + alert_tx_o[0]) +endmodule diff --git a/hw/ip/mbx/rtl/mbx_core_reg_top.sv b/hw/ip/mbx/rtl/mbx_core_reg_top.sv new file mode 100644 index 0000000000000..f3ac101447a2c --- /dev/null +++ b/hw/ip/mbx/rtl/mbx_core_reg_top.sv @@ -0,0 +1,1112 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Top module auto-generated by `reggen` + +`include "prim_assert.sv" + +module mbx_core_reg_top ( + input clk_i, + input rst_ni, + input tlul_pkg::tl_h2d_t tl_i, + output tlul_pkg::tl_d2h_t tl_o, + // To HW + output mbx_reg_pkg::mbx_core_reg2hw_t reg2hw, // Write + input mbx_reg_pkg::mbx_core_hw2reg_t hw2reg, // Read + + // Integrity check errors + output logic intg_err_o +); + + import mbx_reg_pkg::* ; + + localparam int AW = 7; + localparam int DW = 32; + localparam int DBW = DW/8; // Byte Width + + // register signals + logic reg_we; + logic reg_re; + logic [AW-1:0] reg_addr; + logic [DW-1:0] reg_wdata; + logic [DBW-1:0] reg_be; + logic [DW-1:0] reg_rdata; + logic reg_error; + + logic addrmiss, wr_err; + + logic [DW-1:0] reg_rdata_next; + logic reg_busy; + + tlul_pkg::tl_h2d_t tl_reg_h2d; + tlul_pkg::tl_d2h_t tl_reg_d2h; + + + // incoming payload check + logic intg_err; + tlul_cmd_intg_chk u_chk ( + .tl_i(tl_i), + .err_o(intg_err) + ); + + // also check for spurious write enables + logic reg_we_err; + logic [16:0] reg_we_check; + prim_reg_we_check #( + .OneHotWidth(17) + ) u_prim_reg_we_check ( + .clk_i(clk_i), + .rst_ni(rst_ni), + .oh_i (reg_we_check), + .en_i (reg_we && !addrmiss), + .err_o (reg_we_err) + ); + + logic err_q; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + err_q <= '0; + end else if (intg_err || reg_we_err) begin + err_q <= 1'b1; + end + end + + // integrity error output is permanent and should be used for alert generation + // register errors are transactional + assign intg_err_o = err_q | intg_err | reg_we_err; + + // outgoing integrity generation + tlul_pkg::tl_d2h_t tl_o_pre; + tlul_rsp_intg_gen #( + .EnableRspIntgGen(1), + .EnableDataIntgGen(1) + ) u_rsp_intg_gen ( + .tl_i(tl_o_pre), + .tl_o(tl_o) + ); + + assign tl_reg_h2d = tl_i; + assign tl_o_pre = tl_reg_d2h; + + tlul_adapter_reg #( + .RegAw(AW), + .RegDw(DW), + .EnableDataIntgGen(0) + ) u_reg_if ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + .tl_i (tl_reg_h2d), + .tl_o (tl_reg_d2h), + + .en_ifetch_i(prim_mubi_pkg::MuBi4False), + .intg_error_o(), + + .we_o (reg_we), + .re_o (reg_re), + .addr_o (reg_addr), + .wdata_o (reg_wdata), + .be_o (reg_be), + .busy_i (reg_busy), + .rdata_i (reg_rdata), + .error_i (reg_error) + ); + + // cdc oversampling signals + + assign reg_rdata = reg_rdata_next ; + assign reg_error = addrmiss | wr_err | intg_err; + + // Define SW related signals + // Format: __{wd|we|qs} + // or _{wd|we|qs} if field == 1 or 0 + logic intr_state_we; + logic intr_state_mbx_ready_qs; + logic intr_state_mbx_ready_wd; + logic intr_state_mbx_abort_qs; + logic intr_state_mbx_abort_wd; + logic intr_state_mbx_error_qs; + logic intr_state_mbx_error_wd; + logic intr_enable_we; + logic intr_enable_mbx_ready_qs; + logic intr_enable_mbx_ready_wd; + logic intr_enable_mbx_abort_qs; + logic intr_enable_mbx_abort_wd; + logic intr_enable_mbx_error_qs; + logic intr_enable_mbx_error_wd; + logic intr_test_we; + logic intr_test_mbx_ready_wd; + logic intr_test_mbx_abort_wd; + logic intr_test_mbx_error_wd; + logic alert_test_we; + logic alert_test_fatal_fault_wd; + logic alert_test_recov_fault_wd; + logic control_re; + logic control_we; + logic control_abort_qs; + logic control_abort_wd; + logic control_error_qs; + logic control_error_wd; + logic control_sys_async_msg_wd; + logic status_re; + logic status_busy_qs; + logic status_sys_intr_state_qs; + logic status_sys_intr_enable_qs; + logic status_sys_async_enable_qs; + logic address_range_regwen_we; + logic [3:0] address_range_regwen_qs; + logic [3:0] address_range_regwen_wd; + logic address_range_valid_we; + logic address_range_valid_qs; + logic address_range_valid_wd; + logic inbound_base_address_we; + logic [29:0] inbound_base_address_qs; + logic [29:0] inbound_base_address_wd; + logic inbound_limit_address_we; + logic [29:0] inbound_limit_address_qs; + logic [29:0] inbound_limit_address_wd; + logic inbound_write_ptr_re; + logic [29:0] inbound_write_ptr_qs; + logic outbound_base_address_we; + logic [29:0] outbound_base_address_qs; + logic [29:0] outbound_base_address_wd; + logic outbound_limit_address_we; + logic [29:0] outbound_limit_address_qs; + logic [29:0] outbound_limit_address_wd; + logic outbound_read_ptr_re; + logic [29:0] outbound_read_ptr_qs; + logic outbound_object_size_we; + logic [10:0] outbound_object_size_qs; + logic [10:0] outbound_object_size_wd; + logic doe_intr_msg_addr_re; + logic [31:0] doe_intr_msg_addr_qs; + logic doe_intr_msg_data_re; + logic [31:0] doe_intr_msg_data_qs; + + // Register instances + // R[intr_state]: V(False) + // F[mbx_ready]: 0:0 + prim_subreg #( + .DW (1), + .SwAccess(prim_subreg_pkg::SwAccessW1C), + .RESVAL (1'h0), + .Mubi (1'b0) + ) u_intr_state_mbx_ready ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (intr_state_we), + .wd (intr_state_mbx_ready_wd), + + // from internal hardware + .de (hw2reg.intr_state.mbx_ready.de), + .d (hw2reg.intr_state.mbx_ready.d), + + // to internal hardware + .qe (), + .q (reg2hw.intr_state.mbx_ready.q), + .ds (), + + // to register interface (read) + .qs (intr_state_mbx_ready_qs) + ); + + // F[mbx_abort]: 1:1 + prim_subreg #( + .DW (1), + .SwAccess(prim_subreg_pkg::SwAccessW1C), + .RESVAL (1'h0), + .Mubi (1'b0) + ) u_intr_state_mbx_abort ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (intr_state_we), + .wd (intr_state_mbx_abort_wd), + + // from internal hardware + .de (hw2reg.intr_state.mbx_abort.de), + .d (hw2reg.intr_state.mbx_abort.d), + + // to internal hardware + .qe (), + .q (reg2hw.intr_state.mbx_abort.q), + .ds (), + + // to register interface (read) + .qs (intr_state_mbx_abort_qs) + ); + + // F[mbx_error]: 2:2 + prim_subreg #( + .DW (1), + .SwAccess(prim_subreg_pkg::SwAccessW1C), + .RESVAL (1'h0), + .Mubi (1'b0) + ) u_intr_state_mbx_error ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (intr_state_we), + .wd (intr_state_mbx_error_wd), + + // from internal hardware + .de (hw2reg.intr_state.mbx_error.de), + .d (hw2reg.intr_state.mbx_error.d), + + // to internal hardware + .qe (), + .q (reg2hw.intr_state.mbx_error.q), + .ds (), + + // to register interface (read) + .qs (intr_state_mbx_error_qs) + ); + + + // R[intr_enable]: V(False) + // F[mbx_ready]: 0:0 + prim_subreg #( + .DW (1), + .SwAccess(prim_subreg_pkg::SwAccessRW), + .RESVAL (1'h0), + .Mubi (1'b0) + ) u_intr_enable_mbx_ready ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (intr_enable_we), + .wd (intr_enable_mbx_ready_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (), + .q (reg2hw.intr_enable.mbx_ready.q), + .ds (), + + // to register interface (read) + .qs (intr_enable_mbx_ready_qs) + ); + + // F[mbx_abort]: 1:1 + prim_subreg #( + .DW (1), + .SwAccess(prim_subreg_pkg::SwAccessRW), + .RESVAL (1'h0), + .Mubi (1'b0) + ) u_intr_enable_mbx_abort ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (intr_enable_we), + .wd (intr_enable_mbx_abort_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (), + .q (reg2hw.intr_enable.mbx_abort.q), + .ds (), + + // to register interface (read) + .qs (intr_enable_mbx_abort_qs) + ); + + // F[mbx_error]: 2:2 + prim_subreg #( + .DW (1), + .SwAccess(prim_subreg_pkg::SwAccessRW), + .RESVAL (1'h0), + .Mubi (1'b0) + ) u_intr_enable_mbx_error ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (intr_enable_we), + .wd (intr_enable_mbx_error_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (), + .q (reg2hw.intr_enable.mbx_error.q), + .ds (), + + // to register interface (read) + .qs (intr_enable_mbx_error_qs) + ); + + + // R[intr_test]: V(True) + logic intr_test_qe; + logic [2:0] intr_test_flds_we; + assign intr_test_qe = &intr_test_flds_we; + // F[mbx_ready]: 0:0 + prim_subreg_ext #( + .DW (1) + ) u_intr_test_mbx_ready ( + .re (1'b0), + .we (intr_test_we), + .wd (intr_test_mbx_ready_wd), + .d ('0), + .qre (), + .qe (intr_test_flds_we[0]), + .q (reg2hw.intr_test.mbx_ready.q), + .ds (), + .qs () + ); + assign reg2hw.intr_test.mbx_ready.qe = intr_test_qe; + + // F[mbx_abort]: 1:1 + prim_subreg_ext #( + .DW (1) + ) u_intr_test_mbx_abort ( + .re (1'b0), + .we (intr_test_we), + .wd (intr_test_mbx_abort_wd), + .d ('0), + .qre (), + .qe (intr_test_flds_we[1]), + .q (reg2hw.intr_test.mbx_abort.q), + .ds (), + .qs () + ); + assign reg2hw.intr_test.mbx_abort.qe = intr_test_qe; + + // F[mbx_error]: 2:2 + prim_subreg_ext #( + .DW (1) + ) u_intr_test_mbx_error ( + .re (1'b0), + .we (intr_test_we), + .wd (intr_test_mbx_error_wd), + .d ('0), + .qre (), + .qe (intr_test_flds_we[2]), + .q (reg2hw.intr_test.mbx_error.q), + .ds (), + .qs () + ); + assign reg2hw.intr_test.mbx_error.qe = intr_test_qe; + + + // R[alert_test]: V(True) + logic alert_test_qe; + logic [1:0] alert_test_flds_we; + assign alert_test_qe = &alert_test_flds_we; + // F[fatal_fault]: 0:0 + prim_subreg_ext #( + .DW (1) + ) u_alert_test_fatal_fault ( + .re (1'b0), + .we (alert_test_we), + .wd (alert_test_fatal_fault_wd), + .d ('0), + .qre (), + .qe (alert_test_flds_we[0]), + .q (reg2hw.alert_test.fatal_fault.q), + .ds (), + .qs () + ); + assign reg2hw.alert_test.fatal_fault.qe = alert_test_qe; + + // F[recov_fault]: 1:1 + prim_subreg_ext #( + .DW (1) + ) u_alert_test_recov_fault ( + .re (1'b0), + .we (alert_test_we), + .wd (alert_test_recov_fault_wd), + .d ('0), + .qre (), + .qe (alert_test_flds_we[1]), + .q (reg2hw.alert_test.recov_fault.q), + .ds (), + .qs () + ); + assign reg2hw.alert_test.recov_fault.qe = alert_test_qe; + + + // R[control]: V(True) + logic control_qe; + logic [2:0] control_flds_we; + assign control_qe = &control_flds_we; + // F[abort]: 0:0 + prim_subreg_ext #( + .DW (1) + ) u_control_abort ( + .re (control_re), + .we (control_we), + .wd (control_abort_wd), + .d (hw2reg.control.abort.d), + .qre (), + .qe (control_flds_we[0]), + .q (reg2hw.control.abort.q), + .ds (), + .qs (control_abort_qs) + ); + assign reg2hw.control.abort.qe = control_qe; + + // F[error]: 1:1 + prim_subreg_ext #( + .DW (1) + ) u_control_error ( + .re (control_re), + .we (control_we), + .wd (control_error_wd), + .d (hw2reg.control.error.d), + .qre (), + .qe (control_flds_we[1]), + .q (reg2hw.control.error.q), + .ds (), + .qs (control_error_qs) + ); + assign reg2hw.control.error.qe = control_qe; + + // F[sys_async_msg]: 3:3 + prim_subreg_ext #( + .DW (1) + ) u_control_sys_async_msg ( + .re (1'b0), + .we (control_we), + .wd (control_sys_async_msg_wd), + .d ('0), + .qre (), + .qe (control_flds_we[2]), + .q (reg2hw.control.sys_async_msg.q), + .ds (), + .qs () + ); + assign reg2hw.control.sys_async_msg.qe = control_qe; + + + // R[status]: V(True) + // F[busy]: 0:0 + prim_subreg_ext #( + .DW (1) + ) u_status_busy ( + .re (status_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.status.busy.d), + .qre (), + .qe (), + .q (), + .ds (), + .qs (status_busy_qs) + ); + + // F[sys_intr_state]: 1:1 + prim_subreg_ext #( + .DW (1) + ) u_status_sys_intr_state ( + .re (status_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.status.sys_intr_state.d), + .qre (), + .qe (), + .q (), + .ds (), + .qs (status_sys_intr_state_qs) + ); + + // F[sys_intr_enable]: 2:2 + prim_subreg_ext #( + .DW (1) + ) u_status_sys_intr_enable ( + .re (status_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.status.sys_intr_enable.d), + .qre (), + .qe (), + .q (), + .ds (), + .qs (status_sys_intr_enable_qs) + ); + + // F[sys_async_enable]: 3:3 + prim_subreg_ext #( + .DW (1) + ) u_status_sys_async_enable ( + .re (status_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.status.sys_async_enable.d), + .qre (), + .qe (), + .q (), + .ds (), + .qs (status_sys_async_enable_qs) + ); + + + // R[address_range_regwen]: V(False) + prim_subreg #( + .DW (4), + .SwAccess(prim_subreg_pkg::SwAccessW0C), + .RESVAL (4'h6), + .Mubi (1'b1) + ) u_address_range_regwen ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (address_range_regwen_we), + .wd (address_range_regwen_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (), + .q (), + .ds (), + + // to register interface (read) + .qs (address_range_regwen_qs) + ); + + + // R[address_range_valid]: V(False) + logic address_range_valid_qe; + logic [0:0] address_range_valid_flds_we; + prim_flop #( + .Width(1), + .ResetValue(0) + ) u_address_range_valid0_qe ( + .clk_i(clk_i), + .rst_ni(rst_ni), + .d_i(&address_range_valid_flds_we), + .q_o(address_range_valid_qe) + ); + prim_subreg #( + .DW (1), + .SwAccess(prim_subreg_pkg::SwAccessRW), + .RESVAL (1'h0), + .Mubi (1'b0) + ) u_address_range_valid ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (address_range_valid_we), + .wd (address_range_valid_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (address_range_valid_flds_we[0]), + .q (reg2hw.address_range_valid.q), + .ds (), + + // to register interface (read) + .qs (address_range_valid_qs) + ); + assign reg2hw.address_range_valid.qe = address_range_valid_qe; + + + // R[inbound_base_address]: V(False) + // Create REGWEN-gated WE signal + logic inbound_base_address_gated_we; + assign inbound_base_address_gated_we = + inbound_base_address_we & + prim_mubi_pkg::mubi4_test_true_strict(prim_mubi_pkg::mubi4_t'(address_range_regwen_qs)); + prim_subreg #( + .DW (30), + .SwAccess(prim_subreg_pkg::SwAccessRW), + .RESVAL (30'h0), + .Mubi (1'b0) + ) u_inbound_base_address ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (inbound_base_address_gated_we), + .wd (inbound_base_address_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (), + .q (reg2hw.inbound_base_address.q), + .ds (), + + // to register interface (read) + .qs (inbound_base_address_qs) + ); + + + // R[inbound_limit_address]: V(False) + // Create REGWEN-gated WE signal + logic inbound_limit_address_gated_we; + assign inbound_limit_address_gated_we = + inbound_limit_address_we & + prim_mubi_pkg::mubi4_test_true_strict(prim_mubi_pkg::mubi4_t'(address_range_regwen_qs)); + prim_subreg #( + .DW (30), + .SwAccess(prim_subreg_pkg::SwAccessRW), + .RESVAL (30'h0), + .Mubi (1'b0) + ) u_inbound_limit_address ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (inbound_limit_address_gated_we), + .wd (inbound_limit_address_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (), + .q (reg2hw.inbound_limit_address.q), + .ds (), + + // to register interface (read) + .qs (inbound_limit_address_qs) + ); + + + // R[inbound_write_ptr]: V(True) + prim_subreg_ext #( + .DW (30) + ) u_inbound_write_ptr ( + .re (inbound_write_ptr_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.inbound_write_ptr.d), + .qre (), + .qe (), + .q (), + .ds (), + .qs (inbound_write_ptr_qs) + ); + + + // R[outbound_base_address]: V(False) + // Create REGWEN-gated WE signal + logic outbound_base_address_gated_we; + assign outbound_base_address_gated_we = + outbound_base_address_we & + prim_mubi_pkg::mubi4_test_true_strict(prim_mubi_pkg::mubi4_t'(address_range_regwen_qs)); + prim_subreg #( + .DW (30), + .SwAccess(prim_subreg_pkg::SwAccessRW), + .RESVAL (30'h0), + .Mubi (1'b0) + ) u_outbound_base_address ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (outbound_base_address_gated_we), + .wd (outbound_base_address_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (), + .q (reg2hw.outbound_base_address.q), + .ds (), + + // to register interface (read) + .qs (outbound_base_address_qs) + ); + + + // R[outbound_limit_address]: V(False) + // Create REGWEN-gated WE signal + logic outbound_limit_address_gated_we; + assign outbound_limit_address_gated_we = + outbound_limit_address_we & + prim_mubi_pkg::mubi4_test_true_strict(prim_mubi_pkg::mubi4_t'(address_range_regwen_qs)); + prim_subreg #( + .DW (30), + .SwAccess(prim_subreg_pkg::SwAccessRW), + .RESVAL (30'h0), + .Mubi (1'b0) + ) u_outbound_limit_address ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (outbound_limit_address_gated_we), + .wd (outbound_limit_address_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (), + .q (reg2hw.outbound_limit_address.q), + .ds (), + + // to register interface (read) + .qs (outbound_limit_address_qs) + ); + + + // R[outbound_read_ptr]: V(True) + prim_subreg_ext #( + .DW (30) + ) u_outbound_read_ptr ( + .re (outbound_read_ptr_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.outbound_read_ptr.d), + .qre (), + .qe (), + .q (), + .ds (), + .qs (outbound_read_ptr_qs) + ); + + + // R[outbound_object_size]: V(False) + logic outbound_object_size_qe; + logic [0:0] outbound_object_size_flds_we; + prim_flop #( + .Width(1), + .ResetValue(0) + ) u_outbound_object_size0_qe ( + .clk_i(clk_i), + .rst_ni(rst_ni), + .d_i(&outbound_object_size_flds_we), + .q_o(outbound_object_size_qe) + ); + prim_subreg #( + .DW (11), + .SwAccess(prim_subreg_pkg::SwAccessRW), + .RESVAL (11'h0), + .Mubi (1'b0) + ) u_outbound_object_size ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (outbound_object_size_we), + .wd (outbound_object_size_wd), + + // from internal hardware + .de (hw2reg.outbound_object_size.de), + .d (hw2reg.outbound_object_size.d), + + // to internal hardware + .qe (outbound_object_size_flds_we[0]), + .q (reg2hw.outbound_object_size.q), + .ds (), + + // to register interface (read) + .qs (outbound_object_size_qs) + ); + assign reg2hw.outbound_object_size.qe = outbound_object_size_qe; + + + // R[doe_intr_msg_addr]: V(True) + prim_subreg_ext #( + .DW (32) + ) u_doe_intr_msg_addr ( + .re (doe_intr_msg_addr_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.doe_intr_msg_addr.d), + .qre (), + .qe (), + .q (), + .ds (), + .qs (doe_intr_msg_addr_qs) + ); + + + // R[doe_intr_msg_data]: V(True) + prim_subreg_ext #( + .DW (32) + ) u_doe_intr_msg_data ( + .re (doe_intr_msg_data_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.doe_intr_msg_data.d), + .qre (), + .qe (), + .q (), + .ds (), + .qs (doe_intr_msg_data_qs) + ); + + + + logic [16:0] addr_hit; + always_comb begin + addr_hit = '0; + addr_hit[ 0] = (reg_addr == MBX_INTR_STATE_OFFSET); + addr_hit[ 1] = (reg_addr == MBX_INTR_ENABLE_OFFSET); + addr_hit[ 2] = (reg_addr == MBX_INTR_TEST_OFFSET); + addr_hit[ 3] = (reg_addr == MBX_ALERT_TEST_OFFSET); + addr_hit[ 4] = (reg_addr == MBX_CONTROL_OFFSET); + addr_hit[ 5] = (reg_addr == MBX_STATUS_OFFSET); + addr_hit[ 6] = (reg_addr == MBX_ADDRESS_RANGE_REGWEN_OFFSET); + addr_hit[ 7] = (reg_addr == MBX_ADDRESS_RANGE_VALID_OFFSET); + addr_hit[ 8] = (reg_addr == MBX_INBOUND_BASE_ADDRESS_OFFSET); + addr_hit[ 9] = (reg_addr == MBX_INBOUND_LIMIT_ADDRESS_OFFSET); + addr_hit[10] = (reg_addr == MBX_INBOUND_WRITE_PTR_OFFSET); + addr_hit[11] = (reg_addr == MBX_OUTBOUND_BASE_ADDRESS_OFFSET); + addr_hit[12] = (reg_addr == MBX_OUTBOUND_LIMIT_ADDRESS_OFFSET); + addr_hit[13] = (reg_addr == MBX_OUTBOUND_READ_PTR_OFFSET); + addr_hit[14] = (reg_addr == MBX_OUTBOUND_OBJECT_SIZE_OFFSET); + addr_hit[15] = (reg_addr == MBX_DOE_INTR_MSG_ADDR_OFFSET); + addr_hit[16] = (reg_addr == MBX_DOE_INTR_MSG_DATA_OFFSET); + end + + assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; + + // Check sub-word write is permitted + always_comb begin + wr_err = (reg_we & + ((addr_hit[ 0] & (|(MBX_CORE_PERMIT[ 0] & ~reg_be))) | + (addr_hit[ 1] & (|(MBX_CORE_PERMIT[ 1] & ~reg_be))) | + (addr_hit[ 2] & (|(MBX_CORE_PERMIT[ 2] & ~reg_be))) | + (addr_hit[ 3] & (|(MBX_CORE_PERMIT[ 3] & ~reg_be))) | + (addr_hit[ 4] & (|(MBX_CORE_PERMIT[ 4] & ~reg_be))) | + (addr_hit[ 5] & (|(MBX_CORE_PERMIT[ 5] & ~reg_be))) | + (addr_hit[ 6] & (|(MBX_CORE_PERMIT[ 6] & ~reg_be))) | + (addr_hit[ 7] & (|(MBX_CORE_PERMIT[ 7] & ~reg_be))) | + (addr_hit[ 8] & (|(MBX_CORE_PERMIT[ 8] & ~reg_be))) | + (addr_hit[ 9] & (|(MBX_CORE_PERMIT[ 9] & ~reg_be))) | + (addr_hit[10] & (|(MBX_CORE_PERMIT[10] & ~reg_be))) | + (addr_hit[11] & (|(MBX_CORE_PERMIT[11] & ~reg_be))) | + (addr_hit[12] & (|(MBX_CORE_PERMIT[12] & ~reg_be))) | + (addr_hit[13] & (|(MBX_CORE_PERMIT[13] & ~reg_be))) | + (addr_hit[14] & (|(MBX_CORE_PERMIT[14] & ~reg_be))) | + (addr_hit[15] & (|(MBX_CORE_PERMIT[15] & ~reg_be))) | + (addr_hit[16] & (|(MBX_CORE_PERMIT[16] & ~reg_be))))); + end + + // Generate write-enables + assign intr_state_we = addr_hit[0] & reg_we & !reg_error; + + assign intr_state_mbx_ready_wd = reg_wdata[0]; + + assign intr_state_mbx_abort_wd = reg_wdata[1]; + + assign intr_state_mbx_error_wd = reg_wdata[2]; + assign intr_enable_we = addr_hit[1] & reg_we & !reg_error; + + assign intr_enable_mbx_ready_wd = reg_wdata[0]; + + assign intr_enable_mbx_abort_wd = reg_wdata[1]; + + assign intr_enable_mbx_error_wd = reg_wdata[2]; + assign intr_test_we = addr_hit[2] & reg_we & !reg_error; + + assign intr_test_mbx_ready_wd = reg_wdata[0]; + + assign intr_test_mbx_abort_wd = reg_wdata[1]; + + assign intr_test_mbx_error_wd = reg_wdata[2]; + assign alert_test_we = addr_hit[3] & reg_we & !reg_error; + + assign alert_test_fatal_fault_wd = reg_wdata[0]; + + assign alert_test_recov_fault_wd = reg_wdata[1]; + assign control_re = addr_hit[4] & reg_re & !reg_error; + assign control_we = addr_hit[4] & reg_we & !reg_error; + + assign control_abort_wd = reg_wdata[0]; + + assign control_error_wd = reg_wdata[1]; + + assign control_sys_async_msg_wd = reg_wdata[3]; + assign status_re = addr_hit[5] & reg_re & !reg_error; + assign address_range_regwen_we = addr_hit[6] & reg_we & !reg_error; + + assign address_range_regwen_wd = reg_wdata[3:0]; + assign address_range_valid_we = addr_hit[7] & reg_we & !reg_error; + + assign address_range_valid_wd = reg_wdata[0]; + assign inbound_base_address_we = addr_hit[8] & reg_we & !reg_error; + + assign inbound_base_address_wd = reg_wdata[31:2]; + assign inbound_limit_address_we = addr_hit[9] & reg_we & !reg_error; + + assign inbound_limit_address_wd = reg_wdata[31:2]; + assign inbound_write_ptr_re = addr_hit[10] & reg_re & !reg_error; + assign outbound_base_address_we = addr_hit[11] & reg_we & !reg_error; + + assign outbound_base_address_wd = reg_wdata[31:2]; + assign outbound_limit_address_we = addr_hit[12] & reg_we & !reg_error; + + assign outbound_limit_address_wd = reg_wdata[31:2]; + assign outbound_read_ptr_re = addr_hit[13] & reg_re & !reg_error; + assign outbound_object_size_we = addr_hit[14] & reg_we & !reg_error; + + assign outbound_object_size_wd = reg_wdata[10:0]; + assign doe_intr_msg_addr_re = addr_hit[15] & reg_re & !reg_error; + assign doe_intr_msg_data_re = addr_hit[16] & reg_re & !reg_error; + + // Assign write-enables to checker logic vector. + always_comb begin + reg_we_check = '0; + reg_we_check[0] = intr_state_we; + reg_we_check[1] = intr_enable_we; + reg_we_check[2] = intr_test_we; + reg_we_check[3] = alert_test_we; + reg_we_check[4] = control_we; + reg_we_check[5] = 1'b0; + reg_we_check[6] = address_range_regwen_we; + reg_we_check[7] = address_range_valid_we; + reg_we_check[8] = inbound_base_address_gated_we; + reg_we_check[9] = inbound_limit_address_gated_we; + reg_we_check[10] = 1'b0; + reg_we_check[11] = outbound_base_address_gated_we; + reg_we_check[12] = outbound_limit_address_gated_we; + reg_we_check[13] = 1'b0; + reg_we_check[14] = outbound_object_size_we; + reg_we_check[15] = 1'b0; + reg_we_check[16] = 1'b0; + end + + // Read data return + always_comb begin + reg_rdata_next = '0; + unique case (1'b1) + addr_hit[0]: begin + reg_rdata_next[0] = intr_state_mbx_ready_qs; + reg_rdata_next[1] = intr_state_mbx_abort_qs; + reg_rdata_next[2] = intr_state_mbx_error_qs; + end + + addr_hit[1]: begin + reg_rdata_next[0] = intr_enable_mbx_ready_qs; + reg_rdata_next[1] = intr_enable_mbx_abort_qs; + reg_rdata_next[2] = intr_enable_mbx_error_qs; + end + + addr_hit[2]: begin + reg_rdata_next[0] = '0; + reg_rdata_next[1] = '0; + reg_rdata_next[2] = '0; + end + + addr_hit[3]: begin + reg_rdata_next[0] = '0; + reg_rdata_next[1] = '0; + end + + addr_hit[4]: begin + reg_rdata_next[0] = control_abort_qs; + reg_rdata_next[1] = control_error_qs; + reg_rdata_next[3] = '0; + end + + addr_hit[5]: begin + reg_rdata_next[0] = status_busy_qs; + reg_rdata_next[1] = status_sys_intr_state_qs; + reg_rdata_next[2] = status_sys_intr_enable_qs; + reg_rdata_next[3] = status_sys_async_enable_qs; + end + + addr_hit[6]: begin + reg_rdata_next[3:0] = address_range_regwen_qs; + end + + addr_hit[7]: begin + reg_rdata_next[0] = address_range_valid_qs; + end + + addr_hit[8]: begin + reg_rdata_next[31:2] = inbound_base_address_qs; + end + + addr_hit[9]: begin + reg_rdata_next[31:2] = inbound_limit_address_qs; + end + + addr_hit[10]: begin + reg_rdata_next[31:2] = inbound_write_ptr_qs; + end + + addr_hit[11]: begin + reg_rdata_next[31:2] = outbound_base_address_qs; + end + + addr_hit[12]: begin + reg_rdata_next[31:2] = outbound_limit_address_qs; + end + + addr_hit[13]: begin + reg_rdata_next[31:2] = outbound_read_ptr_qs; + end + + addr_hit[14]: begin + reg_rdata_next[10:0] = outbound_object_size_qs; + end + + addr_hit[15]: begin + reg_rdata_next[31:0] = doe_intr_msg_addr_qs; + end + + addr_hit[16]: begin + reg_rdata_next[31:0] = doe_intr_msg_data_qs; + end + + default: begin + reg_rdata_next = '1; + end + endcase + end + + // shadow busy + logic shadow_busy; + assign shadow_busy = 1'b0; + + // register busy + assign reg_busy = shadow_busy; + + // Unused signal tieoff + + // wdata / byte enable are not always fully used + // add a blanket unused statement to handle lint waivers + logic unused_wdata; + logic unused_be; + assign unused_wdata = ^reg_wdata; + assign unused_be = ^reg_be; + + // Assertions for Register Interface + `ASSERT_PULSE(wePulse, reg_we, clk_i, !rst_ni) + `ASSERT_PULSE(rePulse, reg_re, clk_i, !rst_ni) + + `ASSERT(reAfterRv, $rose(reg_re || reg_we) |=> tl_o_pre.d_valid, clk_i, !rst_ni) + + `ASSERT(en2addrHit, (reg_we || reg_re) |-> $onehot0(addr_hit), clk_i, !rst_ni) + + // this is formulated as an assumption such that the FPV testbenches do disprove this + // property by mistake + //`ASSUME(reqParity, tl_reg_h2d.a_valid |-> tl_reg_h2d.a_user.chk_en == tlul_pkg::CheckDis) + +endmodule diff --git a/hw/ip/mbx/rtl/mbx_fsm.sv b/hw/ip/mbx/rtl/mbx_fsm.sv new file mode 100644 index 0000000000000..242cbd18719bf --- /dev/null +++ b/hw/ip/mbx/rtl/mbx_fsm.sv @@ -0,0 +1,177 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`include "prim_assert.sv" + +module mbx_fsm #( + parameter bit CfgOmbx = 1'b1 // 1'b1: Obmbx, 1'b0: Ibmbx +) ( + input logic clk_i, + input logic rst_ni, + // Control input signals + input logic mbx_range_valid_i, + input logic hostif_abort_ack_i, + input logic mbx_error_set_i, + input logic sysif_control_abort_set_i, + input logic sys_read_all_i, + input logic writer_close_mbx_i, + input logic writer_last_word_written_i, + input logic writer_write_valid_i, + // Status signals + output logic mbx_empty_o, + output logic mbx_write_o, + output logic mbx_read_o, + output logic mbx_sys_abort_o, + output logic mbx_ready_update_o, + output logic mbx_ready_o, + output logic mbx_irq_ready_o, + output logic mbx_irq_abort_o, + output logic mbx_state_error_o +); + typedef enum logic [2:0] { + MbxIdle = 3'b000, + MbxWrite = 3'b001, + MbxWaitFinalWord = 3'b010, + MbxRead = 3'b011, + MbxError = 3'b100, + MbxSysAbortHost = 3'b101 + } mbx_ctrl_state_e; + + mbx_ctrl_state_e ctrl_state_q, ctrl_state_d; + + // Following cast is needed to avoid conversion errors between mbx_ctrl_state_e <-> logic + logic [$bits(mbx_ctrl_state_e)-1:0] ctrl_state_logic; + assign ctrl_state_q = mbx_ctrl_state_e'(ctrl_state_logic); + + prim_flop #( + .Width($bits(mbx_ctrl_state_e)), + .ResetValue({MbxIdle}) + ) aff_ctrl_state_q ( + .clk_i ( clk_i ), + .rst_ni( rst_ni ), + .d_i ( ctrl_state_d ), + .q_o ( ctrl_state_logic ) + ); + + // Control signals for external usage + logic mbx_idle; + assign mbx_idle = (ctrl_state_q == MbxIdle); + assign mbx_empty_o = mbx_idle & mbx_range_valid_i; + assign mbx_write_o = (ctrl_state_q == MbxWrite); + assign mbx_read_o = (ctrl_state_q == MbxRead); + assign mbx_sys_abort_o = (ctrl_state_q == MbxSysAbortHost); + // The transition to the abort state marks the abort interrupt generation + assign mbx_irq_abort_o = (ctrl_state_q != MbxSysAbortHost) && (ctrl_state_d == MbxSysAbortHost); + // The transition to the read state marks the ready interrupt generation + assign mbx_irq_ready_o = (ctrl_state_q != MbxRead) && (ctrl_state_d == MbxRead); + + logic ombx_set_ready, ombx_clear_ready; + // Outbound mailbox is Ready, but only if not simultaneous with the exceptional conditions that + // demand clearing of the Ready status bit. + assign ombx_set_ready = CfgOmbx + & mbx_idle + & mbx_range_valid_i + & writer_close_mbx_i; + + // MbxRead is a common state for imbx and ombx + // Exit of MbxRead is used to clear imbx.Busy and ombx.Ready. + // This must also happen when an Error, Abort or FW-initiated reset occurs. + assign ombx_clear_ready = CfgOmbx & (mbx_error_set_i | + sysif_control_abort_set_i | + hostif_abort_ack_i | + mbx_read_o & sys_read_all_i); + + assign mbx_ready_update_o = CfgOmbx & (ombx_set_ready | ombx_clear_ready); // MUTEX(set,clr) + assign mbx_ready_o = !ombx_clear_ready; // Clearing overrules setting. + + always_comb begin + ctrl_state_d = ctrl_state_q; + mbx_state_error_o = 1'b0; + + // Acknowledgement of an Abort request may occur at any time, with the FSM in any state. + if (hostif_abort_ack_i) begin + ctrl_state_d = MbxIdle; + end else begin + unique case (ctrl_state_q) + MbxIdle: begin + if (CfgOmbx) begin + if (mbx_range_valid_i & writer_close_mbx_i) begin + ctrl_state_d = MbxRead; + end + end else begin + if (mbx_range_valid_i & writer_write_valid_i) begin + ctrl_state_d = MbxWrite; + end + end + + // If system wants to error or abort, it has the highest priority + if (mbx_error_set_i) begin + ctrl_state_d = MbxError; + end else if (sysif_control_abort_set_i) begin + ctrl_state_d = MbxSysAbortHost; + end + end + + // Inbound mailbox being written by the system = writer + // Outbound mailbox: not applicable + MbxWrite: begin + if (mbx_error_set_i) begin // Host asserts an error + ctrl_state_d = MbxError; + end else if (sysif_control_abort_set_i) begin // System wants to abort + ctrl_state_d = MbxSysAbortHost; + end else if (writer_close_mbx_i) begin // Writer decided to close the mailbox + if (writer_last_word_written_i) begin + ctrl_state_d = MbxRead; + end else begin + ctrl_state_d = MbxWaitFinalWord; + end + end + end + + // Inbound mailbox being written by the system = writer + // Outbound mailbox: not applicable + MbxWaitFinalWord: begin + if (mbx_error_set_i) begin // Host asserts an error + ctrl_state_d = MbxError; + end else if (sysif_control_abort_set_i) begin // System wants to abort + ctrl_state_d = MbxSysAbortHost; + end else if (writer_last_word_written_i) begin + ctrl_state_d = MbxRead; + end + end + + // Inbound mailbox being read by the reader = host + // Outbound mailbox being read by the reader = system + MbxRead: begin + if (mbx_error_set_i) begin // Host asserts an error + ctrl_state_d = MbxError; + end else if (sysif_control_abort_set_i) begin // System wants to abort + ctrl_state_d = MbxSysAbortHost; + end else if (sys_read_all_i) begin + // Inbound and outbound mailbox go back to idle after all data has + // been read by the sys requester + ctrl_state_d = MbxIdle; + end + end + + // Wait for the abort request to occur + MbxError: begin + if (sysif_control_abort_set_i) begin + ctrl_state_d = MbxSysAbortHost; + end + end + + MbxSysAbortHost: begin + // Wait for the host to acknowledge the abort; handled above. + end + + default: begin + // Should not reach this + ctrl_state_d = MbxIdle; + mbx_state_error_o = 1'b1; + end + endcase + end + end +endmodule diff --git a/hw/ip/mbx/rtl/mbx_hostif.sv b/hw/ip/mbx/rtl/mbx_hostif.sv new file mode 100644 index 0000000000000..c1df8e8f9c9ac --- /dev/null +++ b/hw/ip/mbx/rtl/mbx_hostif.sv @@ -0,0 +1,240 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +module mbx_hostif + import mbx_reg_pkg::*; +#( + parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}}, + parameter int unsigned CfgSramAddrWidth = 32, + parameter int unsigned CfgSramDataWidth = 32, + parameter int unsigned CfgObjectSizeWidth = 11 +) ( + input logic clk_i, + input logic rst_ni, + // Device port to the host side + input tlul_pkg::tl_h2d_t tl_host_i, + output tlul_pkg::tl_d2h_t tl_host_o, + // Generated interrupt event + input logic event_intr_ready_i, + input logic event_intr_abort_i, + input logic event_intr_error_i, + output logic intr_ready_o, + output logic intr_abort_o, + output logic intr_error_o, + // External errors + input logic intg_err_i, + input logic sram_err_i, + // Alerts + input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i, + output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o, + // Access to the control register + // Writing a 1 to control.abort register clears the abort condition + output logic hostif_control_abort_clear_o, + output logic hostif_control_error_set_o, + input logic hostif_control_error_i, + output logic hostif_control_async_msg_set_o, + output logic hostif_control_async_msg_clear_o, + // Access to the status register + input logic hostif_status_busy_i, + input logic hostif_status_sys_intr_en_i, + input logic hostif_status_sys_async_en_i, + input logic hostif_status_sys_intr_state_i, + // Access to the IB/OB RD/WR pointers + input logic [CfgSramAddrWidth-1:0] hostif_imbx_write_ptr_i, + input logic [CfgSramAddrWidth-1:0] hostif_ombx_read_ptr_i, + // Base/Limit for in/outbound mailbox + output logic hostif_address_range_valid_write_o, + output logic hostif_address_range_valid_o, + output logic [CfgSramAddrWidth-1:0] hostif_imbx_base_o, + output logic [CfgSramAddrWidth-1:0] hostif_imbx_limit_o, + output logic [CfgSramAddrWidth-1:0] hostif_ombx_base_o, + output logic [CfgSramAddrWidth-1:0] hostif_ombx_limit_o, + // Read/Write access for the object size register + output logic hostif_ombx_object_size_write_o, + output logic [CfgObjectSizeWidth-1:0] hostif_ombx_object_size_o, + input logic hostif_ombx_object_size_update_i, + input logic [CfgObjectSizeWidth-1:0] hostif_ombx_object_size_i, + // Alias of the interrupt address and data registers from the SYS interface + input logic [CfgSramAddrWidth-1:0] sysif_intr_msg_addr_i, + input logic [CfgSramDataWidth-1:0] sysif_intr_msg_data_i, + // Control inputs coming from the system registers interface + input logic sysif_control_abort_set_i +); + mbx_reg_pkg::mbx_core_reg2hw_t reg2hw; + mbx_reg_pkg::mbx_core_hw2reg_t hw2reg; + + ////////////////////////////////////////////////////////////////////////////// + // Assertions + ////////////////////////////////////////////////////////////////////////////// + logic tlul_intg_err; + logic [NumAlerts-1:0] alert_test, alerts; + + assign alert_test = { + reg2hw.alert_test.recov_fault.q & reg2hw.alert_test.recov_fault.qe, + reg2hw.alert_test.fatal_fault.q & reg2hw.alert_test.fatal_fault.qe + }; + + assign alerts[0] = tlul_intg_err | intg_err_i; + assign alerts[1] = sram_err_i; + + localparam logic [NumAlerts-1:0] IsFatal = {1'b0, 1'b1}; + for (genvar i = 0; i < NumAlerts; i++) begin : gen_alert_tx + prim_alert_sender #( + .AsyncOn ( AlertAsyncOn[i] ), + .IsFatal ( IsFatal[i] ) + ) u_prim_alert_sender ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .alert_test_i ( alert_test[i] ), + .alert_req_i ( alerts[i] ), + .alert_ack_o ( ), + .alert_state_o( ), + .alert_rx_i ( alert_rx_i[i] ), + .alert_tx_o ( alert_tx_o[i] ) + ); + end + + // SEC_CM: BUS.INTEGRITY + // SEC_CM: ADDRESS_RANGE.CONFIG.REGWEN_MUBI + mbx_core_reg_top u_regs( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .tl_i ( tl_host_i ), + .tl_o ( tl_host_o ), + .reg2hw ( reg2hw ), + .hw2reg ( hw2reg ), + .intg_err_o ( tlul_intg_err ), + .devmode_i ( 1'b1 ) + ); + + logic intr_ready_de, intr_abort_de, intr_error_de; + logic intr_ready_d, intr_abort_d, intr_error_d; + + // Instantiate interrupt hardware primitives for ready, abort, and error IRQ + prim_intr_hw #(.Width(1)) u_intr_ready ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .event_intr_i ( event_intr_ready_i ), + .reg2hw_intr_enable_q_i ( reg2hw.intr_enable.mbx_ready.q ), + .reg2hw_intr_test_q_i ( reg2hw.intr_test.mbx_ready.q ), + .reg2hw_intr_test_qe_i ( reg2hw.intr_test.mbx_ready.qe ), + .reg2hw_intr_state_q_i ( reg2hw.intr_state.mbx_ready.q ), + .hw2reg_intr_state_de_o ( intr_ready_de ), + .hw2reg_intr_state_d_o ( intr_ready_d ), + .intr_o ( intr_ready_o ) + ); + + prim_intr_hw #(.Width(1)) u_intr_abort ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .event_intr_i ( event_intr_abort_i ), + .reg2hw_intr_enable_q_i ( reg2hw.intr_enable.mbx_abort.q ), + .reg2hw_intr_test_q_i ( reg2hw.intr_test.mbx_abort.q ), + .reg2hw_intr_test_qe_i ( reg2hw.intr_test.mbx_abort.qe ), + .reg2hw_intr_state_q_i ( reg2hw.intr_state.mbx_abort.q ), + .hw2reg_intr_state_de_o ( intr_abort_de ), + .hw2reg_intr_state_d_o ( intr_abort_d ), + .intr_o ( intr_abort_o ) + ); + + prim_intr_hw #(.Width(1)) u_intr_error ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .event_intr_i ( event_intr_error_i ), + .reg2hw_intr_enable_q_i ( reg2hw.intr_enable.mbx_error.q ), + .reg2hw_intr_test_q_i ( reg2hw.intr_test.mbx_error.q ), + .reg2hw_intr_test_qe_i ( reg2hw.intr_test.mbx_error.qe ), + .reg2hw_intr_state_q_i ( reg2hw.intr_state.mbx_error.q ), + .hw2reg_intr_state_de_o ( intr_error_de ), + .hw2reg_intr_state_d_o ( intr_error_d ), + .intr_o ( intr_error_o ) + ); + + // Let acknowledging the abort from the host clear all pending interrupts + assign hw2reg.intr_state.mbx_ready.de = intr_ready_de | hostif_control_abort_clear_o; + assign hw2reg.intr_state.mbx_abort.de = intr_abort_de | hostif_control_abort_clear_o; + assign hw2reg.intr_state.mbx_error.de = intr_error_de | hostif_control_abort_clear_o; + + assign hw2reg.intr_state.mbx_ready.d = hostif_control_abort_clear_o ? 1'b0 : intr_ready_d; + assign hw2reg.intr_state.mbx_abort.d = hostif_control_abort_clear_o ? 1'b0 : intr_abort_d; + assign hw2reg.intr_state.mbx_error.d = hostif_control_abort_clear_o ? 1'b0 : intr_error_d; + + // Control Register + logic abort_d, abort_q; + + // Abort computation from system and host interface + always_comb begin + abort_d = abort_q; + + if (hostif_control_abort_clear_o) begin + abort_d = 1'b0; + end else if (sysif_control_abort_set_i) begin + abort_d = 1'b1; + end + end + + prim_flop #( + .Width(1) + ) u_abort ( + .clk_i ( clk_i ), + .rst_ni( rst_ni ), + .d_i ( abort_d ), + .q_o ( abort_q ) + ); + + // Writing a 1 to control.abort means clearing the abort condition + assign hostif_control_abort_clear_o = reg2hw.control.abort.qe & reg2hw.control.abort.q; + assign hw2reg.control.abort.d = abort_q; + + assign hostif_control_error_set_o = reg2hw.control.error.qe & reg2hw.control.error.q; + assign hw2reg.control.error.d = hostif_control_error_i; + + assign hostif_control_async_msg_set_o = reg2hw.control.sys_async_msg.qe & + reg2hw.control.sys_async_msg.q; + assign hostif_control_async_msg_clear_o = reg2hw.control.sys_async_msg.qe & + ~reg2hw.control.sys_async_msg.q; + + // Status Register + // It is implemented as hwext and implemented in a different hierarchy and only providing an + // alias. Thus manually assigning the external signals + + // External read logic + assign hw2reg.status.busy.d = hostif_status_busy_i; + assign hw2reg.status.sys_intr_state.d = hostif_status_sys_intr_state_i; + assign hw2reg.status.sys_intr_enable.d = hostif_status_sys_intr_en_i; + assign hw2reg.status.sys_async_enable.d = hostif_status_sys_async_en_i; + + // Address config valid + assign hostif_address_range_valid_write_o = reg2hw.address_range_valid.qe; + assign hostif_address_range_valid_o = reg2hw.address_range_valid.q; + + // Inbound Mailbox Base/Limit Register + assign hostif_imbx_base_o = { reg2hw.inbound_base_address.q, {2{1'b0}} }; + assign hostif_imbx_limit_o = { reg2hw.inbound_limit_address.q, {2{1'b0}} }; + + // Outbound Mailbox Base/Limit Register + assign hostif_ombx_base_o = { reg2hw.outbound_base_address.q, {2{1'b0}} }; + assign hostif_ombx_limit_o = { reg2hw.outbound_limit_address.q, {2{1'b0}} }; + + // Read/Write pointers + assign hw2reg.inbound_write_ptr.d = hostif_imbx_write_ptr_i[CfgSramAddrWidth-1:2]; + assign hw2reg.outbound_read_ptr.d = hostif_ombx_read_ptr_i[CfgSramAddrWidth-1:2]; + + // Outbound object size Register + // External read logic + assign hw2reg.outbound_object_size.d = hostif_ombx_object_size_i; + assign hw2reg.outbound_object_size.de = hostif_ombx_object_size_update_i; + // External write logic + assign hostif_ombx_object_size_write_o = reg2hw.outbound_object_size.qe; + assign hostif_ombx_object_size_o = reg2hw.outbound_object_size.q; + + // Alias of the IRQ addr and data register from the sys interface (RO) + assign hw2reg.doe_intr_msg_addr.d = sysif_intr_msg_addr_i; + assign hw2reg.doe_intr_msg_data.d = sysif_intr_msg_data_i; + + // Assertions + `ASSERT_PRIM_REG_WE_ONEHOT_ERROR_TRIGGER_ALERT(RegWeOnehotCheck_A, + u_regs, + alert_tx_o[0]) +endmodule diff --git a/hw/ip/mbx/rtl/mbx_imbx.sv b/hw/ip/mbx/rtl/mbx_imbx.sv new file mode 100644 index 0000000000000..8c7027f0245ed --- /dev/null +++ b/hw/ip/mbx/rtl/mbx_imbx.sv @@ -0,0 +1,214 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`include "prim_assert.sv" + +module mbx_imbx #( + parameter int unsigned CfgSramAddrWidth = 32, + parameter int unsigned CfgSramDataWidth = 32 +) ( + input logic clk_i, + input logic rst_ni, + output logic imbx_state_error_o, + output logic imbx_pending_o, + output logic imbx_irq_ready_o, + output logic imbx_irq_abort_o, + output logic imbx_status_busy_update_o, + output logic imbx_status_busy_o, + output logic imbx_overflow_error_set_o, + // Access to the control and status registers of host interface + // Writing a 1 to control.abort register clears the abort condition + input logic hostif_control_abort_clear_i, + input logic mbx_error_set_i, + // Range configuration for the private SRAM + input logic hostif_range_valid_write_i, + input logic hostif_range_valid_i, + input logic [CfgSramAddrWidth-1:0] hostif_base_i, + input logic [CfgSramAddrWidth-1:0] hostif_limit_i, + input logic sys_read_all_i, + // Device interface from the system side + input logic sysif_status_busy_i, + input logic sysif_control_go_set_i, + input logic sysif_control_abort_set_i, + input logic sysif_data_write_valid_i, + // Host interface to the private SRAM + output logic hostif_sram_write_req_o, + input logic hostif_sram_write_gnt_i, + input logic hostif_sram_all_vld_rcvd_i, + output logic [CfgSramAddrWidth-1:0] hostif_sram_write_ptr_o +); + localparam int unsigned LCFG_SRM_ADDRINC = CfgSramDataWidth / 8; + + logic [CfgSramAddrWidth-1:0] sram_write_ptr_d, sram_write_ptr_q; + + // Status signals from the FSM + logic mbx_empty, mbx_write, mbx_read, mbx_sys_abort; + + // hostif_sram_write_req_o is actually sticky because the sys-side TLUL_adapter_reg is + // NOT ack'ed until the command is granted by the host-side TLUL_adapter_host + // RW2A = sticky from DEC/RW-stage to (srm command) ACK + logic write_req; + assign write_req = (mbx_empty & sysif_data_write_valid_i) | + (mbx_write & sysif_data_write_valid_i & (sram_write_ptr_q <= hostif_limit_i)); + + // Waiting for a write request to be accepted onto the TL-UL bus; reset state if the host side + // is acknowledging an Abort request from the SoC side. + logic awaiting_gnt; + assign awaiting_gnt = hostif_sram_write_req_o & ~hostif_sram_write_gnt_i & + ~hostif_control_abort_clear_i; + + // Raise an error if the requester tries to write out of the limits + assign imbx_overflow_error_set_o = mbx_write & sysif_data_write_valid_i & + (sram_write_ptr_q > hostif_limit_i); + + // Create a sticky TLUL write request until its granted + logic req_q; + assign hostif_sram_write_req_o = write_req | req_q; + + prim_flop #( + .Width(1) + ) u_req_state ( + .clk_i ( clk_i ), + .rst_ni( rst_ni ), + .d_i ( awaiting_gnt ), + .q_o ( req_q ) + ); + + // The abort requested was handled by the host. This re-initialzes the write pointer + logic host_clear_abort; + assign host_clear_abort = hostif_control_abort_clear_i & mbx_sys_abort; + + logic load_write_ptr, advance_write_ptr; + + // Rewind the write pointer to the base + // Note: `mbx_empty` and `advance_write_ptr` can both be asserted if bus access is granted + // immediately on the initial word write of a message, and we must advance the write pointer. + assign load_write_ptr = (mbx_empty & ~advance_write_ptr) | host_clear_abort | + (mbx_read & sys_read_all_i); + + // Advance the write pointer when the valid write command is granted by the tlul_adapter_host + assign advance_write_ptr = hostif_sram_write_req_o & hostif_sram_write_gnt_i; + + always_comb begin + sram_write_ptr_d = sram_write_ptr_q; + + if (load_write_ptr) begin + sram_write_ptr_d = hostif_base_i; + end else if (advance_write_ptr) begin + sram_write_ptr_d = sram_write_ptr_q + LCFG_SRM_ADDRINC; + end + end + + prim_flop_en #( + .Width(CfgSramAddrWidth) + ) u_sram_write_ptr ( + .clk_i ( clk_i ), + .rst_ni( rst_ni ), + .en_i ( load_write_ptr | advance_write_ptr ), + .d_i ( sram_write_ptr_d ), + .q_o ( sram_write_ptr_q ) + ); + assign hostif_sram_write_ptr_o = sram_write_ptr_q; + + // Backpressure the next write data until the current write data is granted by the TLUL adapter + logic set_pending, clear_pending; + + // Block the request from TLUL until the SRAM write is complete. + // Reset state if the host side is acknowledging an Abort request. + assign set_pending = write_req; + assign clear_pending = hostif_sram_write_gnt_i | hostif_control_abort_clear_i; + + prim_flop #( + .Width(1) + ) u_pending ( + .clk_i ( clk_i ), + .rst_ni( rst_ni ), + .d_i ( ~clear_pending & (set_pending | imbx_pending_o) ), + .q_o ( imbx_pending_o ) + ); + + // Busy logic + logic imbx_set_busy, imbx_clear_busy; + // Busy is set when the requester asserts the go bit and we are not at the same time + // getting an abort or error request. Busy also gets set when there is an abort + // request, as this needs to be handled, and initially when the mailbox memory ranges + // are not yet configured + assign imbx_set_busy = + (mbx_write & sysif_control_go_set_i & ~mbx_error_set_i & ~sysif_control_abort_set_i) | + sysif_control_abort_set_i | ~hostif_range_valid_i; + + // Clear the busy signal if + // - the private SRAM range becomes valid + // - all data has been been read from the outbound mailbox + // - the host acknowledges an abort request from the sys + assign imbx_clear_busy = (hostif_range_valid_write_i & hostif_range_valid_i) | + sys_read_all_i | + hostif_control_abort_clear_i; + + // External busy update interface + assign imbx_status_busy_update_o = imbx_set_busy | imbx_clear_busy; + assign imbx_status_busy_o = imbx_set_busy; + + mbx_fsm #( + .CfgOmbx ( 0 ) + ) u_mbxfsm( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .mbx_range_valid_i ( hostif_range_valid_i ), + .hostif_abort_ack_i ( hostif_control_abort_clear_i ), + .mbx_error_set_i ( mbx_error_set_i ), + .sysif_control_abort_set_i ( sysif_control_abort_set_i ), + .sys_read_all_i ( sys_read_all_i ), + .writer_close_mbx_i ( sysif_control_go_set_i ), + .writer_last_word_written_i( hostif_sram_all_vld_rcvd_i ), + .writer_write_valid_i ( sysif_data_write_valid_i ), + // Status signals + .mbx_empty_o ( mbx_empty ), + .mbx_write_o ( mbx_write ), + .mbx_read_o ( mbx_read ), + .mbx_sys_abort_o ( mbx_sys_abort ), + .mbx_ready_update_o ( ), + .mbx_ready_o ( ), + .mbx_irq_ready_o ( imbx_irq_ready_o ), + .mbx_irq_abort_o ( imbx_irq_abort_o ), + .mbx_state_error_o ( imbx_state_error_o ) + ); + + ////////////////////////////////////////////////////////////////////////////// + // Assertions + ////////////////////////////////////////////////////////////////////////////// + + // Don't write the mailbox if it is full + `ASSERT_NEVER(NeverWriteMbxIfFull_A, hostif_sram_write_req_o & + (sram_write_ptr_q > hostif_limit_i)) + +`ifdef INC_ASSERT + logic[CfgSramAddrWidth-1:0] sram_write_ptr_assert_q; + prim_flop #( + .Width(CfgSramAddrWidth) + ) u_sram_write_ptr_assert ( + .clk_i ( clk_i ), + .rst_ni( rst_ni ), + .d_i ( sram_write_ptr_d ), + .q_o ( sram_write_ptr_assert_q ) + ); + // A granted write by the host adapter must advance the write pointer + `ASSERT_IF(GntMustAdvanceWritePtr_A, advance_write_ptr & + (sram_write_ptr_d == sram_write_ptr_assert_q + LCFG_SRM_ADDRINC), + hostif_sram_write_gnt_i) +`endif + + // Ready IRQ to core should not be asserted whilst there is still pending write traffic + `ASSERT_NEVER(WrEverythingBeforeReadyIRQ, imbx_irq_ready_o & + hostif_sram_write_req_o & ~hostif_sram_write_gnt_i) + + // The write pointer should not be advanced if the request has not yet been granted. + `ASSERT_IF(WrPtrShouldNotAdvanceIfNoAck_A, hostif_sram_write_gnt_i, + advance_write_ptr & imbx_pending_o) + + // When writing to the mailbox, DOE status busy must be low; it shall be set after the request + // writing is complete, and no further requests shall be received until it has been cleared. + `ASSERT_NEVER(WriteToMbxBusyMustBeLow_A, sysif_data_write_valid_i & sysif_status_busy_i) + +endmodule diff --git a/hw/ip/mbx/rtl/mbx_ombx.sv b/hw/ip/mbx/rtl/mbx_ombx.sv new file mode 100644 index 0000000000000..1436f73fd03c9 --- /dev/null +++ b/hw/ip/mbx/rtl/mbx_ombx.sv @@ -0,0 +1,316 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`include "prim_assert.sv" + +module mbx_ombx #( + parameter int unsigned CfgSramAddrWidth = 32, + parameter int unsigned CfgSramDataWidth = 32, + parameter int unsigned CfgObjectSizeWidth = 11 +) ( + input logic clk_i, + input logic rst_ni, + output logic ombx_state_error_o, + output logic ombx_doe_intr_ready_set_o, + output logic ombx_pending_o, + output logic ombx_status_ready_update_o, + output logic ombx_status_ready_o, + input logic hostif_range_valid_write_i, + input logic hostif_range_valid_i, + input logic [CfgSramAddrWidth-1:0] hostif_base_i, + input logic [CfgSramAddrWidth-1:0] hostif_limit_i, + output logic sys_read_all_o, + // Control and status signals from the host and system interface + // Writing a 1 to control.abort register clears the abort condition + input logic sysif_status_ready_i, + input logic hostif_control_abort_clear_i, + input logic mbx_error_set_i, + input logic sysif_control_abort_set_i, + input logic sysif_read_data_read_valid_i, + input logic sysif_read_data_write_valid_i, + // Interface for the object size register + input logic hostif_ombx_object_size_write_i, + input logic [CfgObjectSizeWidth-1:0] hostif_ombx_object_size_i, + output logic hostif_ombx_object_size_update_o, + output logic [CfgObjectSizeWidth-1:0] hostif_ombx_object_size_o, + // DOE data coming from the SRAM + output logic [CfgSramDataWidth-1:0] ombx_read_data_o, + // Host interface to the private SRAM + output logic ombx_sram_read_req_o, + input logic ombx_sram_read_gnt_i, + output logic [CfgSramAddrWidth-1:0] ombx_sram_read_ptr_o, + input logic ombx_sram_read_resp_valid_i, + input logic [CfgSramDataWidth-1:0] ombx_sram_read_resp_i +); + localparam int unsigned LCFG_SRM_ADDRINC = CfgSramDataWidth / 8; + + logic [CfgSramAddrWidth-1:0] sram_read_ptr_d, sram_read_ptr_q; + logic [CfgSramAddrWidth-1:0] sram_read_ptr_limit_d, sram_read_ptr_limit_q; + + // Status signals from the FSM + logic mbx_empty, mbx_read, mbx_sys_abort; + logic set_first_req, clear_first_req; + + // Generate the read request + // - Mbx reader ACK the current read data and initate the first request + // First is initiated by mbx owner writes object size register + // Note that pointer comparison with hostif_limit_i is inclusive, while comparison with internal + // limit of the object size is exclusive + logic read_req, first_req_q; + assign read_req = first_req_q | + (mbx_read & sysif_read_data_write_valid_i & + (sram_read_ptr_q <= hostif_limit_i) & + (sram_read_ptr_q < sram_read_ptr_limit_q)); + + // Waiting for a read request to be accepted onto the TL-UL bus; reset state if the host side + // is acknowledging an Abort request from the SoC side. + logic awaiting_gnt; + assign awaiting_gnt = ombx_sram_read_req_o & ~ombx_sram_read_gnt_i & + ~hostif_control_abort_clear_i; + + // Create a sticky TLUL read request until its granted + logic req_q; + assign ombx_sram_read_req_o = read_req | req_q; + + prim_flop #( + .Width(1) + ) u_req_state ( + .clk_i ( clk_i ), + .rst_ni( rst_ni ), + .d_i ( awaiting_gnt ), + .q_o ( req_q ) + ); + + // Backpressure the next read data until the current write data brings back the data from SRAM + // Exclude last ack + logic set_pending, clear_pending; + + // Block the request from TLUL until the SRAM read is complete + // Note that pointer comparison with hostif_limit_i is inclusive, while comparison with internal + // limit of the object size is exclusive + assign set_pending = mbx_read & sysif_read_data_write_valid_i & + (sram_read_ptr_q <= hostif_limit_i) & + (sram_read_ptr_q < sram_read_ptr_limit_q); + // Reset state if the host side is acknowledging an Abort request. + assign clear_pending = ombx_sram_read_resp_valid_i | hostif_control_abort_clear_i; + + prim_flop #( + .Width(1) + ) u_pending ( + .clk_i ( clk_i ), + .rst_ni( rst_ni ), + .d_i ( ~clear_pending & (set_pending | ombx_pending_o) ), + .q_o ( ombx_pending_o ) + ); + + logic writer_close_mbx; + // move FSM to MbxRead (Ready) after the 1st read comes back + assign writer_close_mbx = mbx_empty & ombx_sram_read_resp_valid_i; + + // Terminate mbx_read state (Ready = 1 -> 0) if ombx is already drained (sram_read is not issued) + assign sys_read_all_o = mbx_read & + sysif_read_data_write_valid_i & + (sram_read_ptr_q == sram_read_ptr_limit_q); + + // The abort requestet was handled by the host. This re-initialzes the read pointer + logic host_clear_abort; + assign host_clear_abort = hostif_control_abort_clear_i & mbx_sys_abort; + + // SRAM read pointer management + logic load_read_ptr, advance_read_ptr; + + // Determine when the range is set to valid + logic range_valid_set; + assign range_valid_set = hostif_range_valid_write_i & hostif_range_valid_i; + + // Rewind the read pointer to the base + assign load_read_ptr = range_valid_set | set_first_req | sys_read_all_o | host_clear_abort; + + // Advance the read pointer when one request went through + assign advance_read_ptr = ombx_sram_read_req_o & ombx_sram_read_gnt_i; + + always_comb begin + sram_read_ptr_d = sram_read_ptr_q; + + if (load_read_ptr) begin + sram_read_ptr_d = hostif_base_i; + end else if (advance_read_ptr) begin + sram_read_ptr_d = sram_read_ptr_q + LCFG_SRM_ADDRINC; + end + end + + prim_flop_en #( + .Width(CfgSramAddrWidth) + ) u_sram_read_ptr ( + .clk_i ( clk_i ), + .rst_ni( rst_ni ), + .en_i ( load_read_ptr | advance_read_ptr ), + .d_i ( sram_read_ptr_d ), + .q_o ( sram_read_ptr_q ) + ); + assign ombx_sram_read_ptr_o = sram_read_ptr_q; + + // Abort has been requested by the SoC but not yet acknowledged on the host side. + logic aborting; + assign aborting = sysif_control_abort_set_i | mbx_sys_abort; + + // Clear ombx read data register in case of all data is read, an error happens, or an Abort or + // FW-initiated reset occurs. + logic clear_read_data; + assign clear_read_data = sys_read_all_o | // Normal completion of Response + mbx_error_set_i | // Error raised by host side + aborting | // Abort requested by SoC side + hostif_control_abort_clear_i; // Abort ack or FW reset from host side + + // Advance the SRAM read response to read data + prim_flop_en #( + .Width(CfgSramDataWidth) + ) u_sram_read_data ( + .clk_i ( clk_i ), + .rst_ni( rst_ni ), + .en_i ( ombx_sram_read_resp_valid_i | clear_read_data ), + .d_i ( {CfgSramDataWidth{~clear_read_data}} & ombx_sram_read_resp_i ), + .q_o ( ombx_read_data_o ) + ); + + // The following logic creates the status signals to update the hostif.object_size register, + // which is part of the host interface. + + logic host_write_object_size_q; + logic ombx_object_size_update_valid_q; + + // The following flop creates an indicator that hostif.object_size has been written such that + // in the next cycle, the read pointer limit can be updated and the first transfer from the + // SRAM to the internal data flop can be initiated. + + prim_flop #( + .Width(1) + ) u_host_write_object_size ( + .clk_i ( clk_i ), + .rst_ni( rst_ni ), + // Ignore hardware-initiated changes to the OUTBOUND_OBJECT_SIZE register. + .d_i ( hostif_ombx_object_size_write_i & ~ombx_object_size_update_valid_q ), + .q_o ( host_write_object_size_q ) + ); + + logic [CfgObjectSizeWidth-1:0] hostif_ob_object_size_minus_one; + // Update the hostif.object_size register on every transaction or when aborting the transaction + assign hostif_ombx_object_size_update_o = (ombx_sram_read_req_o & ombx_sram_read_gnt_i) | + hostif_control_abort_clear_i; + // The updated value is the decremented by 1 size or zero-ed out if the transaction is aborted + assign hostif_ob_object_size_minus_one = hostif_ombx_object_size_i - 1; + assign hostif_ombx_object_size_o = {CfgObjectSizeWidth{~hostif_control_abort_clear_i}} & + hostif_ob_object_size_minus_one; + + prim_flop #( + .Width(1) + ) u_host_object_size_update_valid ( + .clk_i ( clk_i ), + .rst_ni( rst_ni ), + .d_i ( hostif_ombx_object_size_update_o ), + .q_o ( ombx_object_size_update_valid_q ) + ); + + // Compute the read pointer limit after object size is written + assign sram_read_ptr_limit_d = hostif_base_i + + CfgSramAddrWidth'({hostif_ombx_object_size_i, 2'b0}); + + prim_flop_en #( + .Width(CfgSramAddrWidth) + ) u_sram_read_ptr_limit ( + .clk_i ( clk_i ), + .rst_ni( rst_ni ), + // Factor in ~mbx_read because HW update can trigger host_write_object_size_q + .en_i ( (host_write_object_size_q & ~mbx_read) | range_valid_set ), + .d_i ( sram_read_ptr_limit_d ), + .q_o ( sram_read_ptr_limit_q ) + ); + + // Logic to initiate the first read (mbx_empty) from the SRAM to the requester + // Only starts the transmitting if the mailbox is configured properly + // (SRAM range is valid and the object size has been written to a non-zero) + // value. mbx_empty means there hasn't been read anything but range is valid. + assign set_first_req = mbx_empty & host_write_object_size_q & (|hostif_ombx_object_size_i); + assign clear_first_req = ombx_sram_read_gnt_i; + + prim_flop #( + .Width(1) + ) u_pop_entry ( + .clk_i ( clk_i ), + .rst_ni( rst_ni ), + .d_i ( set_first_req | (first_req_q & ~clear_first_req) ), + .q_o ( first_req_q ) + ); + + // Create a DOE interrupt request when the obmx FSM turns into the ready state + assign ombx_doe_intr_ready_set_o = (ombx_status_ready_o & ombx_status_ready_update_o); + + mbx_fsm #( + .CfgOmbx ( 1 ) + ) u_mbxfsm( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .mbx_range_valid_i ( hostif_range_valid_i ), + .hostif_abort_ack_i ( hostif_control_abort_clear_i ), + .mbx_error_set_i ( mbx_error_set_i ), + .sysif_control_abort_set_i ( sysif_control_abort_set_i ), + .sys_read_all_i ( sys_read_all_o ), + .writer_close_mbx_i ( writer_close_mbx ), + .writer_last_word_written_i( 1'b0 ), + .writer_write_valid_i ( 1'b0 ), + // Status signals + .mbx_empty_o ( mbx_empty ), + .mbx_write_o ( ), + .mbx_read_o ( mbx_read ), + .mbx_sys_abort_o ( mbx_sys_abort ), + .mbx_ready_update_o ( ombx_status_ready_update_o ), + .mbx_ready_o ( ombx_status_ready_o ), + .mbx_irq_ready_o ( ), + .mbx_irq_abort_o ( ), + .mbx_state_error_o ( ombx_state_error_o ) + ); + + ////////////////////////////////////////////////////////////////////////////// + // Unused signals + ////////////////////////////////////////////////////////////////////////////// + logic unused_signals; + assign unused_signals = sysif_status_ready_i; + + ////////////////////////////////////////////////////////////////////////////// + // Assertions + ////////////////////////////////////////////////////////////////////////////// + + // When reading from the ombx doe_status.ready must have been asserted + `ASSERT_NEVER(ReadyAssertedWhenRead_A, ombx_sram_read_req_o & + ~(first_req_q | sysif_status_ready_i)) + // System write-to-read data is non-posted. No subsequential read or write comes before the + // write is ACKed + `ASSERT_NEVER(NoReadBeforeWriteAcked_A, ombx_pending_o & + (sysif_read_data_read_valid_i | sysif_read_data_write_valid_i)) + // Never read the SRAM if it is empty + logic ombx_is_empty; + assign ombx_is_empty = (sram_read_ptr_q == sram_read_ptr_limit_q); + `ASSERT_NEVER(NeverReadWhenEmpty_A, ombx_sram_read_req_o & ombx_is_empty) + // Never let the read pointer run out of the limit, but allow the range to be redefined whilst + // the mailbox is not active + `ASSERT_NEVER(NeverRunOutOfLimit_A, |{first_req_q, mbx_read} & + (sram_read_ptr_q > sram_read_ptr_limit_q)) + +`ifdef INC_ASSERT + logic[CfgSramAddrWidth-1:0] sram_read_ptr_assert_q; + prim_flop #( + .Width(CfgSramAddrWidth) + ) u_sram_ptr_assert ( + .clk_i ( clk_i ), + .rst_ni( rst_ni ), + .d_i ( sram_read_ptr_d ), + .q_o ( sram_read_ptr_assert_q ) + ); + + // A granted read by the host adapter must advance the read pointer + `ASSERT_IF(GntMustAdvanceWritePtr_A, advance_read_ptr & + (sram_read_ptr_d == sram_read_ptr_assert_q + LCFG_SRM_ADDRINC), + ombx_sram_read_gnt_i) +`endif +endmodule diff --git a/hw/ip/mbx/rtl/mbx_reg_pkg.sv b/hw/ip/mbx/rtl/mbx_reg_pkg.sv new file mode 100644 index 0000000000000..57a48b8876a18 --- /dev/null +++ b/hw/ip/mbx/rtl/mbx_reg_pkg.sv @@ -0,0 +1,420 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Package auto-generated by `reggen` containing data structure + +package mbx_reg_pkg; + + // Param list + parameter int NumAlerts = 2; + + // Address widths within the block + parameter int CoreAw = 7; + parameter int SocAw = 5; + + /////////////////////////////////////////////// + // Typedefs for registers for core interface // + /////////////////////////////////////////////// + + typedef struct packed { + struct packed { + logic q; + } mbx_error; + struct packed { + logic q; + } mbx_abort; + struct packed { + logic q; + } mbx_ready; + } mbx_reg2hw_intr_state_reg_t; + + typedef struct packed { + struct packed { + logic q; + } mbx_error; + struct packed { + logic q; + } mbx_abort; + struct packed { + logic q; + } mbx_ready; + } mbx_reg2hw_intr_enable_reg_t; + + typedef struct packed { + struct packed { + logic q; + logic qe; + } mbx_error; + struct packed { + logic q; + logic qe; + } mbx_abort; + struct packed { + logic q; + logic qe; + } mbx_ready; + } mbx_reg2hw_intr_test_reg_t; + + typedef struct packed { + struct packed { + logic q; + logic qe; + } recov_fault; + struct packed { + logic q; + logic qe; + } fatal_fault; + } mbx_reg2hw_alert_test_reg_t; + + typedef struct packed { + struct packed { + logic q; + logic qe; + } sys_async_msg; + struct packed { + logic q; + logic qe; + } error; + struct packed { + logic q; + logic qe; + } abort; + } mbx_reg2hw_control_reg_t; + + typedef struct packed { + logic q; + logic qe; + } mbx_reg2hw_address_range_valid_reg_t; + + typedef struct packed { + logic [29:0] q; + } mbx_reg2hw_inbound_base_address_reg_t; + + typedef struct packed { + logic [29:0] q; + } mbx_reg2hw_inbound_limit_address_reg_t; + + typedef struct packed { + logic [29:0] q; + } mbx_reg2hw_outbound_base_address_reg_t; + + typedef struct packed { + logic [29:0] q; + } mbx_reg2hw_outbound_limit_address_reg_t; + + typedef struct packed { + logic [10:0] q; + logic qe; + } mbx_reg2hw_outbound_object_size_reg_t; + + typedef struct packed { + struct packed { + logic d; + logic de; + } mbx_ready; + struct packed { + logic d; + logic de; + } mbx_abort; + struct packed { + logic d; + logic de; + } mbx_error; + } mbx_hw2reg_intr_state_reg_t; + + typedef struct packed { + struct packed { + logic d; + } abort; + struct packed { + logic d; + } error; + } mbx_hw2reg_control_reg_t; + + typedef struct packed { + struct packed { + logic d; + } busy; + struct packed { + logic d; + } sys_intr_state; + struct packed { + logic d; + } sys_intr_enable; + struct packed { + logic d; + } sys_async_enable; + } mbx_hw2reg_status_reg_t; + + typedef struct packed { + logic [29:0] d; + } mbx_hw2reg_inbound_write_ptr_reg_t; + + typedef struct packed { + logic [29:0] d; + } mbx_hw2reg_outbound_read_ptr_reg_t; + + typedef struct packed { + logic [10:0] d; + logic de; + } mbx_hw2reg_outbound_object_size_reg_t; + + typedef struct packed { + logic [31:0] d; + } mbx_hw2reg_doe_intr_msg_addr_reg_t; + + typedef struct packed { + logic [31:0] d; + } mbx_hw2reg_doe_intr_msg_data_reg_t; + + // Register -> HW type for core interface + typedef struct packed { + mbx_reg2hw_intr_state_reg_t intr_state; // [155:153] + mbx_reg2hw_intr_enable_reg_t intr_enable; // [152:150] + mbx_reg2hw_intr_test_reg_t intr_test; // [149:144] + mbx_reg2hw_alert_test_reg_t alert_test; // [143:140] + mbx_reg2hw_control_reg_t control; // [139:134] + mbx_reg2hw_address_range_valid_reg_t address_range_valid; // [133:132] + mbx_reg2hw_inbound_base_address_reg_t inbound_base_address; // [131:102] + mbx_reg2hw_inbound_limit_address_reg_t inbound_limit_address; // [101:72] + mbx_reg2hw_outbound_base_address_reg_t outbound_base_address; // [71:42] + mbx_reg2hw_outbound_limit_address_reg_t outbound_limit_address; // [41:12] + mbx_reg2hw_outbound_object_size_reg_t outbound_object_size; // [11:0] + } mbx_core_reg2hw_t; + + // HW -> register type for core interface + typedef struct packed { + mbx_hw2reg_intr_state_reg_t intr_state; // [147:142] + mbx_hw2reg_control_reg_t control; // [141:140] + mbx_hw2reg_status_reg_t status; // [139:136] + mbx_hw2reg_inbound_write_ptr_reg_t inbound_write_ptr; // [135:106] + mbx_hw2reg_outbound_read_ptr_reg_t outbound_read_ptr; // [105:76] + mbx_hw2reg_outbound_object_size_reg_t outbound_object_size; // [75:64] + mbx_hw2reg_doe_intr_msg_addr_reg_t doe_intr_msg_addr; // [63:32] + mbx_hw2reg_doe_intr_msg_data_reg_t doe_intr_msg_data; // [31:0] + } mbx_core_hw2reg_t; + + // Register offsets for core interface + parameter logic [CoreAw-1:0] MBX_INTR_STATE_OFFSET = 7'h 0; + parameter logic [CoreAw-1:0] MBX_INTR_ENABLE_OFFSET = 7'h 4; + parameter logic [CoreAw-1:0] MBX_INTR_TEST_OFFSET = 7'h 8; + parameter logic [CoreAw-1:0] MBX_ALERT_TEST_OFFSET = 7'h c; + parameter logic [CoreAw-1:0] MBX_CONTROL_OFFSET = 7'h 10; + parameter logic [CoreAw-1:0] MBX_STATUS_OFFSET = 7'h 14; + parameter logic [CoreAw-1:0] MBX_ADDRESS_RANGE_REGWEN_OFFSET = 7'h 18; + parameter logic [CoreAw-1:0] MBX_ADDRESS_RANGE_VALID_OFFSET = 7'h 1c; + parameter logic [CoreAw-1:0] MBX_INBOUND_BASE_ADDRESS_OFFSET = 7'h 20; + parameter logic [CoreAw-1:0] MBX_INBOUND_LIMIT_ADDRESS_OFFSET = 7'h 24; + parameter logic [CoreAw-1:0] MBX_INBOUND_WRITE_PTR_OFFSET = 7'h 28; + parameter logic [CoreAw-1:0] MBX_OUTBOUND_BASE_ADDRESS_OFFSET = 7'h 2c; + parameter logic [CoreAw-1:0] MBX_OUTBOUND_LIMIT_ADDRESS_OFFSET = 7'h 30; + parameter logic [CoreAw-1:0] MBX_OUTBOUND_READ_PTR_OFFSET = 7'h 34; + parameter logic [CoreAw-1:0] MBX_OUTBOUND_OBJECT_SIZE_OFFSET = 7'h 38; + parameter logic [CoreAw-1:0] MBX_DOE_INTR_MSG_ADDR_OFFSET = 7'h 3c; + parameter logic [CoreAw-1:0] MBX_DOE_INTR_MSG_DATA_OFFSET = 7'h 40; + + // Reset values for hwext registers and their fields for core interface + parameter logic [2:0] MBX_INTR_TEST_RESVAL = 3'h 0; + parameter logic [0:0] MBX_INTR_TEST_MBX_READY_RESVAL = 1'h 0; + parameter logic [0:0] MBX_INTR_TEST_MBX_ABORT_RESVAL = 1'h 0; + parameter logic [0:0] MBX_INTR_TEST_MBX_ERROR_RESVAL = 1'h 0; + parameter logic [1:0] MBX_ALERT_TEST_RESVAL = 2'h 0; + parameter logic [0:0] MBX_ALERT_TEST_FATAL_FAULT_RESVAL = 1'h 0; + parameter logic [0:0] MBX_ALERT_TEST_RECOV_FAULT_RESVAL = 1'h 0; + parameter logic [3:0] MBX_CONTROL_RESVAL = 4'h 0; + parameter logic [0:0] MBX_CONTROL_ABORT_RESVAL = 1'h 0; + parameter logic [0:0] MBX_CONTROL_ERROR_RESVAL = 1'h 0; + parameter logic [0:0] MBX_CONTROL_SYS_ASYNC_MSG_RESVAL = 1'h 0; + parameter logic [3:0] MBX_STATUS_RESVAL = 4'h 1; + parameter logic [0:0] MBX_STATUS_BUSY_RESVAL = 1'h 1; + parameter logic [0:0] MBX_STATUS_SYS_INTR_STATE_RESVAL = 1'h 0; + parameter logic [0:0] MBX_STATUS_SYS_INTR_ENABLE_RESVAL = 1'h 0; + parameter logic [0:0] MBX_STATUS_SYS_ASYNC_ENABLE_RESVAL = 1'h 0; + parameter logic [31:0] MBX_INBOUND_WRITE_PTR_RESVAL = 32'h 0; + parameter logic [29:0] MBX_INBOUND_WRITE_PTR_INBOUND_READ_PTR_RESVAL = 30'h 0; + parameter logic [31:0] MBX_OUTBOUND_READ_PTR_RESVAL = 32'h 0; + parameter logic [29:0] MBX_OUTBOUND_READ_PTR_OUTBOUND_WRITE_PTR_RESVAL = 30'h 0; + parameter logic [31:0] MBX_DOE_INTR_MSG_ADDR_RESVAL = 32'h 0; + parameter logic [31:0] MBX_DOE_INTR_MSG_ADDR_DOE_INTR_MSG_ADDR_RESVAL = 32'h 0; + parameter logic [31:0] MBX_DOE_INTR_MSG_DATA_RESVAL = 32'h 0; + parameter logic [31:0] MBX_DOE_INTR_MSG_DATA_DOE_INTR_MSG_DATA_RESVAL = 32'h 0; + + // Register index for core interface + typedef enum int { + MBX_INTR_STATE, + MBX_INTR_ENABLE, + MBX_INTR_TEST, + MBX_ALERT_TEST, + MBX_CONTROL, + MBX_STATUS, + MBX_ADDRESS_RANGE_REGWEN, + MBX_ADDRESS_RANGE_VALID, + MBX_INBOUND_BASE_ADDRESS, + MBX_INBOUND_LIMIT_ADDRESS, + MBX_INBOUND_WRITE_PTR, + MBX_OUTBOUND_BASE_ADDRESS, + MBX_OUTBOUND_LIMIT_ADDRESS, + MBX_OUTBOUND_READ_PTR, + MBX_OUTBOUND_OBJECT_SIZE, + MBX_DOE_INTR_MSG_ADDR, + MBX_DOE_INTR_MSG_DATA + } mbx_core_id_e; + + // Register width information to check illegal writes for core interface + parameter logic [3:0] MBX_CORE_PERMIT [17] = '{ + 4'b 0001, // index[ 0] MBX_INTR_STATE + 4'b 0001, // index[ 1] MBX_INTR_ENABLE + 4'b 0001, // index[ 2] MBX_INTR_TEST + 4'b 0001, // index[ 3] MBX_ALERT_TEST + 4'b 0001, // index[ 4] MBX_CONTROL + 4'b 0001, // index[ 5] MBX_STATUS + 4'b 0001, // index[ 6] MBX_ADDRESS_RANGE_REGWEN + 4'b 0001, // index[ 7] MBX_ADDRESS_RANGE_VALID + 4'b 1111, // index[ 8] MBX_INBOUND_BASE_ADDRESS + 4'b 1111, // index[ 9] MBX_INBOUND_LIMIT_ADDRESS + 4'b 1111, // index[10] MBX_INBOUND_WRITE_PTR + 4'b 1111, // index[11] MBX_OUTBOUND_BASE_ADDRESS + 4'b 1111, // index[12] MBX_OUTBOUND_LIMIT_ADDRESS + 4'b 1111, // index[13] MBX_OUTBOUND_READ_PTR + 4'b 0011, // index[14] MBX_OUTBOUND_OBJECT_SIZE + 4'b 1111, // index[15] MBX_DOE_INTR_MSG_ADDR + 4'b 1111 // index[16] MBX_DOE_INTR_MSG_DATA + }; + + ////////////////////////////////////////////// + // Typedefs for registers for soc interface // + ////////////////////////////////////////////// + + typedef struct packed { + struct packed { + logic q; + logic qe; + } go; + struct packed { + logic q; + logic qe; + } doe_async_msg_en; + struct packed { + logic q; + logic qe; + } doe_intr_en; + struct packed { + logic q; + logic qe; + } abort; + } mbx_reg2hw_soc_control_reg_t; + + typedef struct packed { + struct packed { + logic q; + } ready; + struct packed { + logic q; + } error; + struct packed { + logic q; + } doe_intr_status; + struct packed { + logic q; + } busy; + } mbx_reg2hw_soc_status_reg_t; + + typedef struct packed { + logic [31:0] q; + } mbx_reg2hw_soc_doe_intr_msg_addr_reg_t; + + typedef struct packed { + logic [31:0] q; + } mbx_reg2hw_soc_doe_intr_msg_data_reg_t; + + typedef struct packed { + struct packed { + logic d; + } abort; + struct packed { + logic d; + } doe_intr_en; + struct packed { + logic d; + } doe_async_msg_en; + struct packed { + logic d; + } go; + } mbx_hw2reg_soc_control_reg_t; + + typedef struct packed { + struct packed { + logic d; + logic de; + } busy; + struct packed { + logic d; + logic de; + } doe_intr_status; + struct packed { + logic d; + logic de; + } error; + struct packed { + logic d; + logic de; + } doe_async_msg_status; + struct packed { + logic d; + logic de; + } ready; + } mbx_hw2reg_soc_status_reg_t; + + // Register -> HW type for soc interface + typedef struct packed { + mbx_reg2hw_soc_control_reg_t soc_control; // [75:68] + mbx_reg2hw_soc_status_reg_t soc_status; // [67:64] + mbx_reg2hw_soc_doe_intr_msg_addr_reg_t soc_doe_intr_msg_addr; // [63:32] + mbx_reg2hw_soc_doe_intr_msg_data_reg_t soc_doe_intr_msg_data; // [31:0] + } mbx_soc_reg2hw_t; + + // HW -> register type for soc interface + typedef struct packed { + mbx_hw2reg_soc_control_reg_t soc_control; // [13:10] + mbx_hw2reg_soc_status_reg_t soc_status; // [9:0] + } mbx_soc_hw2reg_t; + + // Register offsets for soc interface + parameter logic [SocAw-1:0] MBX_SOC_CONTROL_OFFSET = 5'h 8; + parameter logic [SocAw-1:0] MBX_SOC_STATUS_OFFSET = 5'h c; + parameter logic [SocAw-1:0] MBX_SOC_DOE_INTR_MSG_ADDR_OFFSET = 5'h 18; + parameter logic [SocAw-1:0] MBX_SOC_DOE_INTR_MSG_DATA_OFFSET = 5'h 1c; + + // Reset values for hwext registers and their fields for soc interface + parameter logic [31:0] MBX_SOC_CONTROL_RESVAL = 32'h 0; + parameter logic [0:0] MBX_SOC_CONTROL_ABORT_RESVAL = 1'h 0; + parameter logic [0:0] MBX_SOC_CONTROL_DOE_INTR_EN_RESVAL = 1'h 0; + parameter logic [0:0] MBX_SOC_CONTROL_DOE_ASYNC_MSG_EN_RESVAL = 1'h 0; + parameter logic [0:0] MBX_SOC_CONTROL_GO_RESVAL = 1'h 0; + + // Window parameters for soc interface + parameter logic [SocAw-1:0] MBX_WDATA_OFFSET = 5'h 10; + parameter int unsigned MBX_WDATA_SIZE = 'h 4; + parameter int unsigned MBX_WDATA_IDX = 0; + parameter logic [SocAw-1:0] MBX_RDATA_OFFSET = 5'h 14; + parameter int unsigned MBX_RDATA_SIZE = 'h 4; + parameter int unsigned MBX_RDATA_IDX = 1; + + // Register index for soc interface + typedef enum int { + MBX_SOC_CONTROL, + MBX_SOC_STATUS, + MBX_SOC_DOE_INTR_MSG_ADDR, + MBX_SOC_DOE_INTR_MSG_DATA + } mbx_soc_id_e; + + // Register width information to check illegal writes for soc interface + parameter logic [3:0] MBX_SOC_PERMIT [4] = '{ + 4'b 1111, // index[0] MBX_SOC_CONTROL + 4'b 1111, // index[1] MBX_SOC_STATUS + 4'b 1111, // index[2] MBX_SOC_DOE_INTR_MSG_ADDR + 4'b 1111 // index[3] MBX_SOC_DOE_INTR_MSG_DATA + }; + +endpackage diff --git a/hw/ip/mbx/rtl/mbx_soc_reg_top.sv b/hw/ip/mbx/rtl/mbx_soc_reg_top.sv new file mode 100644 index 0000000000000..c66f6340ced06 --- /dev/null +++ b/hw/ip/mbx/rtl/mbx_soc_reg_top.sv @@ -0,0 +1,573 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Top module auto-generated by `reggen` + +`include "prim_assert.sv" + +module mbx_soc_reg_top ( + input clk_i, + input rst_ni, + input tlul_pkg::tl_h2d_t tl_i, + output tlul_pkg::tl_d2h_t tl_o, + + // Output port for window + output tlul_pkg::tl_h2d_t tl_win_o [2], + input tlul_pkg::tl_d2h_t tl_win_i [2], + + // To HW + output mbx_reg_pkg::mbx_soc_reg2hw_t reg2hw, // Write + input mbx_reg_pkg::mbx_soc_hw2reg_t hw2reg, // Read + + // Integrity check errors + output logic intg_err_o +); + + import mbx_reg_pkg::* ; + + localparam int AW = 5; + localparam int DW = 32; + localparam int DBW = DW/8; // Byte Width + + // register signals + logic reg_we; + logic reg_re; + logic [AW-1:0] reg_addr; + logic [DW-1:0] reg_wdata; + logic [DBW-1:0] reg_be; + logic [DW-1:0] reg_rdata; + logic reg_error; + + logic addrmiss, wr_err; + + logic [DW-1:0] reg_rdata_next; + logic reg_busy; + + tlul_pkg::tl_h2d_t tl_reg_h2d; + tlul_pkg::tl_d2h_t tl_reg_d2h; + + + // incoming payload check + logic intg_err; + tlul_cmd_intg_chk u_chk ( + .tl_i(tl_i), + .err_o(intg_err) + ); + + // also check for spurious write enables + logic reg_we_err; + logic [3:0] reg_we_check; + prim_reg_we_check #( + .OneHotWidth(4) + ) u_prim_reg_we_check ( + .clk_i(clk_i), + .rst_ni(rst_ni), + .oh_i (reg_we_check), + .en_i (reg_we && !addrmiss), + .err_o (reg_we_err) + ); + + logic err_q; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + err_q <= '0; + end else if (intg_err || reg_we_err) begin + err_q <= 1'b1; + end + end + + // integrity error output is permanent and should be used for alert generation + // register errors are transactional + assign intg_err_o = err_q | intg_err | reg_we_err; + + // outgoing integrity generation + tlul_pkg::tl_d2h_t tl_o_pre; + tlul_rsp_intg_gen #( + .EnableRspIntgGen(1), + .EnableDataIntgGen(1) + ) u_rsp_intg_gen ( + .tl_i(tl_o_pre), + .tl_o(tl_o) + ); + + tlul_pkg::tl_h2d_t tl_socket_h2d [3]; + tlul_pkg::tl_d2h_t tl_socket_d2h [3]; + + logic [1:0] reg_steer; + + // socket_1n connection + assign tl_reg_h2d = tl_socket_h2d[2]; + assign tl_socket_d2h[2] = tl_reg_d2h; + + assign tl_win_o[0] = tl_socket_h2d[0]; + assign tl_socket_d2h[0] = tl_win_i[0]; + assign tl_win_o[1] = tl_socket_h2d[1]; + assign tl_socket_d2h[1] = tl_win_i[1]; + + // Create Socket_1n + tlul_socket_1n #( + .N (3), + .HReqPass (1'b1), + .HRspPass (1'b1), + .DReqPass ({3{1'b1}}), + .DRspPass ({3{1'b1}}), + .HReqDepth (4'h0), + .HRspDepth (4'h0), + .DReqDepth ({3{4'h0}}), + .DRspDepth ({3{4'h0}}), + .ExplicitErrs (1'b0) + ) u_socket ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .tl_h_i (tl_i), + .tl_h_o (tl_o_pre), + .tl_d_o (tl_socket_h2d), + .tl_d_i (tl_socket_d2h), + .dev_select_i (reg_steer) + ); + + // Create steering logic + always_comb begin + reg_steer = + tl_i.a_address[AW-1:0] inside {[16:19]} ? 2'd0 : + tl_i.a_address[AW-1:0] inside {[20:23]} ? 2'd1 : + // Default set to register + 2'd2; + + // Override this in case of an integrity error + if (intg_err) begin + reg_steer = 2'd2; + end + end + + tlul_adapter_reg #( + .RegAw(AW), + .RegDw(DW), + .EnableDataIntgGen(0) + ) u_reg_if ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + .tl_i (tl_reg_h2d), + .tl_o (tl_reg_d2h), + + .en_ifetch_i(prim_mubi_pkg::MuBi4False), + .intg_error_o(), + + .we_o (reg_we), + .re_o (reg_re), + .addr_o (reg_addr), + .wdata_o (reg_wdata), + .be_o (reg_be), + .busy_i (reg_busy), + .rdata_i (reg_rdata), + .error_i (reg_error) + ); + + // cdc oversampling signals + + assign reg_rdata = reg_rdata_next ; + assign reg_error = addrmiss | wr_err | intg_err; + + // Define SW related signals + // Format: __{wd|we|qs} + // or _{wd|we|qs} if field == 1 or 0 + logic soc_control_re; + logic soc_control_we; + logic soc_control_abort_wd; + logic soc_control_doe_intr_en_qs; + logic soc_control_doe_intr_en_wd; + logic soc_control_doe_async_msg_en_qs; + logic soc_control_doe_async_msg_en_wd; + logic soc_control_go_wd; + logic soc_status_we; + logic soc_status_busy_qs; + logic soc_status_doe_intr_status_qs; + logic soc_status_doe_intr_status_wd; + logic soc_status_error_qs; + logic soc_status_doe_async_msg_status_qs; + logic soc_status_ready_qs; + logic soc_doe_intr_msg_addr_we; + logic [31:0] soc_doe_intr_msg_addr_qs; + logic [31:0] soc_doe_intr_msg_addr_wd; + logic soc_doe_intr_msg_data_we; + logic [31:0] soc_doe_intr_msg_data_qs; + logic [31:0] soc_doe_intr_msg_data_wd; + + // Register instances + // R[soc_control]: V(True) + logic soc_control_qe; + logic [3:0] soc_control_flds_we; + assign soc_control_qe = &soc_control_flds_we; + // F[abort]: 0:0 + prim_subreg_ext #( + .DW (1) + ) u_soc_control_abort ( + .re (1'b0), + .we (soc_control_we), + .wd (soc_control_abort_wd), + .d (hw2reg.soc_control.abort.d), + .qre (), + .qe (soc_control_flds_we[0]), + .q (reg2hw.soc_control.abort.q), + .ds (), + .qs () + ); + assign reg2hw.soc_control.abort.qe = soc_control_qe; + + // F[doe_intr_en]: 1:1 + prim_subreg_ext #( + .DW (1) + ) u_soc_control_doe_intr_en ( + .re (soc_control_re), + .we (soc_control_we), + .wd (soc_control_doe_intr_en_wd), + .d (hw2reg.soc_control.doe_intr_en.d), + .qre (), + .qe (soc_control_flds_we[1]), + .q (reg2hw.soc_control.doe_intr_en.q), + .ds (), + .qs (soc_control_doe_intr_en_qs) + ); + assign reg2hw.soc_control.doe_intr_en.qe = soc_control_qe; + + // F[doe_async_msg_en]: 3:3 + prim_subreg_ext #( + .DW (1) + ) u_soc_control_doe_async_msg_en ( + .re (soc_control_re), + .we (soc_control_we), + .wd (soc_control_doe_async_msg_en_wd), + .d (hw2reg.soc_control.doe_async_msg_en.d), + .qre (), + .qe (soc_control_flds_we[2]), + .q (reg2hw.soc_control.doe_async_msg_en.q), + .ds (), + .qs (soc_control_doe_async_msg_en_qs) + ); + assign reg2hw.soc_control.doe_async_msg_en.qe = soc_control_qe; + + // F[go]: 31:31 + prim_subreg_ext #( + .DW (1) + ) u_soc_control_go ( + .re (1'b0), + .we (soc_control_we), + .wd (soc_control_go_wd), + .d (hw2reg.soc_control.go.d), + .qre (), + .qe (soc_control_flds_we[3]), + .q (reg2hw.soc_control.go.q), + .ds (), + .qs () + ); + assign reg2hw.soc_control.go.qe = soc_control_qe; + + + // R[soc_status]: V(False) + // F[busy]: 0:0 + prim_subreg #( + .DW (1), + .SwAccess(prim_subreg_pkg::SwAccessRO), + .RESVAL (1'h1), + .Mubi (1'b0) + ) u_soc_status_busy ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (1'b0), + .wd ('0), + + // from internal hardware + .de (hw2reg.soc_status.busy.de), + .d (hw2reg.soc_status.busy.d), + + // to internal hardware + .qe (), + .q (reg2hw.soc_status.busy.q), + .ds (), + + // to register interface (read) + .qs (soc_status_busy_qs) + ); + + // F[doe_intr_status]: 1:1 + prim_subreg #( + .DW (1), + .SwAccess(prim_subreg_pkg::SwAccessW1C), + .RESVAL (1'h0), + .Mubi (1'b0) + ) u_soc_status_doe_intr_status ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (soc_status_we), + .wd (soc_status_doe_intr_status_wd), + + // from internal hardware + .de (hw2reg.soc_status.doe_intr_status.de), + .d (hw2reg.soc_status.doe_intr_status.d), + + // to internal hardware + .qe (), + .q (reg2hw.soc_status.doe_intr_status.q), + .ds (), + + // to register interface (read) + .qs (soc_status_doe_intr_status_qs) + ); + + // F[error]: 2:2 + prim_subreg #( + .DW (1), + .SwAccess(prim_subreg_pkg::SwAccessRO), + .RESVAL (1'h0), + .Mubi (1'b0) + ) u_soc_status_error ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (1'b0), + .wd ('0), + + // from internal hardware + .de (hw2reg.soc_status.error.de), + .d (hw2reg.soc_status.error.d), + + // to internal hardware + .qe (), + .q (reg2hw.soc_status.error.q), + .ds (), + + // to register interface (read) + .qs (soc_status_error_qs) + ); + + // F[doe_async_msg_status]: 3:3 + prim_subreg #( + .DW (1), + .SwAccess(prim_subreg_pkg::SwAccessRO), + .RESVAL (1'h0), + .Mubi (1'b0) + ) u_soc_status_doe_async_msg_status ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (1'b0), + .wd ('0), + + // from internal hardware + .de (hw2reg.soc_status.doe_async_msg_status.de), + .d (hw2reg.soc_status.doe_async_msg_status.d), + + // to internal hardware + .qe (), + .q (), + .ds (), + + // to register interface (read) + .qs (soc_status_doe_async_msg_status_qs) + ); + + // F[ready]: 31:31 + prim_subreg #( + .DW (1), + .SwAccess(prim_subreg_pkg::SwAccessRO), + .RESVAL (1'h0), + .Mubi (1'b0) + ) u_soc_status_ready ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (1'b0), + .wd ('0), + + // from internal hardware + .de (hw2reg.soc_status.ready.de), + .d (hw2reg.soc_status.ready.d), + + // to internal hardware + .qe (), + .q (reg2hw.soc_status.ready.q), + .ds (), + + // to register interface (read) + .qs (soc_status_ready_qs) + ); + + + // R[soc_doe_intr_msg_addr]: V(False) + prim_subreg #( + .DW (32), + .SwAccess(prim_subreg_pkg::SwAccessRW), + .RESVAL (32'h0), + .Mubi (1'b0) + ) u_soc_doe_intr_msg_addr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (soc_doe_intr_msg_addr_we), + .wd (soc_doe_intr_msg_addr_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (), + .q (reg2hw.soc_doe_intr_msg_addr.q), + .ds (), + + // to register interface (read) + .qs (soc_doe_intr_msg_addr_qs) + ); + + + // R[soc_doe_intr_msg_data]: V(False) + prim_subreg #( + .DW (32), + .SwAccess(prim_subreg_pkg::SwAccessRW), + .RESVAL (32'h0), + .Mubi (1'b0) + ) u_soc_doe_intr_msg_data ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (soc_doe_intr_msg_data_we), + .wd (soc_doe_intr_msg_data_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (), + .q (reg2hw.soc_doe_intr_msg_data.q), + .ds (), + + // to register interface (read) + .qs (soc_doe_intr_msg_data_qs) + ); + + + + logic [3:0] addr_hit; + always_comb begin + addr_hit = '0; + addr_hit[0] = (reg_addr == MBX_SOC_CONTROL_OFFSET); + addr_hit[1] = (reg_addr == MBX_SOC_STATUS_OFFSET); + addr_hit[2] = (reg_addr == MBX_SOC_DOE_INTR_MSG_ADDR_OFFSET); + addr_hit[3] = (reg_addr == MBX_SOC_DOE_INTR_MSG_DATA_OFFSET); + end + + assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; + + // Check sub-word write is permitted + always_comb begin + wr_err = (reg_we & + ((addr_hit[0] & (|(MBX_SOC_PERMIT[0] & ~reg_be))) | + (addr_hit[1] & (|(MBX_SOC_PERMIT[1] & ~reg_be))) | + (addr_hit[2] & (|(MBX_SOC_PERMIT[2] & ~reg_be))) | + (addr_hit[3] & (|(MBX_SOC_PERMIT[3] & ~reg_be))))); + end + + // Generate write-enables + assign soc_control_re = addr_hit[0] & reg_re & !reg_error; + assign soc_control_we = addr_hit[0] & reg_we & !reg_error; + + assign soc_control_abort_wd = reg_wdata[0]; + + assign soc_control_doe_intr_en_wd = reg_wdata[1]; + + assign soc_control_doe_async_msg_en_wd = reg_wdata[3]; + + assign soc_control_go_wd = reg_wdata[31]; + assign soc_status_we = addr_hit[1] & reg_we & !reg_error; + + assign soc_status_doe_intr_status_wd = reg_wdata[1]; + assign soc_doe_intr_msg_addr_we = addr_hit[2] & reg_we & !reg_error; + + assign soc_doe_intr_msg_addr_wd = reg_wdata[31:0]; + assign soc_doe_intr_msg_data_we = addr_hit[3] & reg_we & !reg_error; + + assign soc_doe_intr_msg_data_wd = reg_wdata[31:0]; + + // Assign write-enables to checker logic vector. + always_comb begin + reg_we_check = '0; + reg_we_check[0] = soc_control_we; + reg_we_check[1] = soc_status_we; + reg_we_check[2] = soc_doe_intr_msg_addr_we; + reg_we_check[3] = soc_doe_intr_msg_data_we; + end + + // Read data return + always_comb begin + reg_rdata_next = '0; + unique case (1'b1) + addr_hit[0]: begin + reg_rdata_next[0] = '0; + reg_rdata_next[1] = soc_control_doe_intr_en_qs; + reg_rdata_next[3] = soc_control_doe_async_msg_en_qs; + reg_rdata_next[31] = '0; + end + + addr_hit[1]: begin + reg_rdata_next[0] = soc_status_busy_qs; + reg_rdata_next[1] = soc_status_doe_intr_status_qs; + reg_rdata_next[2] = soc_status_error_qs; + reg_rdata_next[3] = soc_status_doe_async_msg_status_qs; + reg_rdata_next[31] = soc_status_ready_qs; + end + + addr_hit[2]: begin + reg_rdata_next[31:0] = soc_doe_intr_msg_addr_qs; + end + + addr_hit[3]: begin + reg_rdata_next[31:0] = soc_doe_intr_msg_data_qs; + end + + default: begin + reg_rdata_next = '1; + end + endcase + end + + // shadow busy + logic shadow_busy; + assign shadow_busy = 1'b0; + + // register busy + assign reg_busy = shadow_busy; + + // Unused signal tieoff + + // wdata / byte enable are not always fully used + // add a blanket unused statement to handle lint waivers + logic unused_wdata; + logic unused_be; + assign unused_wdata = ^reg_wdata; + assign unused_be = ^reg_be; + + // Assertions for Register Interface + `ASSERT_PULSE(wePulse, reg_we, clk_i, !rst_ni) + `ASSERT_PULSE(rePulse, reg_re, clk_i, !rst_ni) + + `ASSERT(reAfterRv, $rose(reg_re || reg_we) |=> tl_o_pre.d_valid, clk_i, !rst_ni) + + `ASSERT(en2addrHit, (reg_we || reg_re) |-> $onehot0(addr_hit), clk_i, !rst_ni) + + // this is formulated as an assumption such that the FPV testbenches do disprove this + // property by mistake + //`ASSUME(reqParity, tl_reg_h2d.a_valid |-> tl_reg_h2d.a_user.chk_en == tlul_pkg::CheckDis) + +endmodule diff --git a/hw/ip/mbx/rtl/mbx_sramrwarb.sv b/hw/ip/mbx/rtl/mbx_sramrwarb.sv new file mode 100644 index 0000000000000..2844c4ec0aacc --- /dev/null +++ b/hw/ip/mbx/rtl/mbx_sramrwarb.sv @@ -0,0 +1,136 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +module mbx_sramrwarb + import tlul_pkg::*; +#( + parameter int unsigned CfgSramAddrWidth = 32, + parameter int unsigned CfgSramDataWidth = 32 +) ( + input logic clk_i, + input logic rst_ni, + // Host port for memory accesses to the OT private memory + output tlul_pkg::tl_h2d_t tl_host_o, + input tlul_pkg::tl_d2h_t tl_host_i, + output logic intg_err_o, + output logic sram_err_o, + + // Host-side acknowledgement of an Abort operation + input logic hostif_control_abort_clear_i, + + // Interface to the inbound mailbox + input logic imbx_sram_write_req_i, + output logic imbx_sram_write_gnt_o, + input logic [CfgSramAddrWidth-1:0] imbx_sram_write_ptr_i, + input logic [CfgSramDataWidth-1:0] imbx_write_data_i, + output logic imbx_sram_all_vld_rcvd_o, + // Interface to the outpbound mailbox + input logic ombx_sram_read_req_i, + output logic ombx_sram_read_gnt_o, + input logic [CfgSramAddrWidth-1:0] ombx_sram_read_ptr_i, + output logic ombx_sram_read_resp_vld_o, + output logic [CfgSramDataWidth-1:0] ombx_sram_read_resp_o +); + import prim_mubi_pkg::*; + // Maximum number of outstanding requests + localparam int unsigned LCFG_MAX_REQS = 4; + localparam int unsigned LCFG_MAX_REQS_LOG2 = $clog2(LCFG_MAX_REQS) + 1; + + // We prioritize the read request. + // Winner has an outstanding read request. + logic arb_read_winner; + assign arb_read_winner = ombx_sram_read_req_i; + + // Winner has an outstanding write request but there is no read request + logic arb_write_winner; + assign arb_write_winner = imbx_sram_write_req_i & ~arb_read_winner; + + // Granting logic. Mux it to the request + logic sram_gnt, sram_valid, max_outstanding_reqs_reached; + assign ombx_sram_read_gnt_o = arb_read_winner & (~max_outstanding_reqs_reached & sram_gnt); + assign imbx_sram_write_gnt_o = arb_write_winner & (~max_outstanding_reqs_reached & sram_gnt); + + // Mux the arbitration winner address + logic [CfgSramAddrWidth-1:0] sram_address; + assign sram_address = arb_read_winner? ombx_sram_read_ptr_i : + imbx_sram_write_ptr_i; + + // make sure the request FIFO is ready (ie not empty) + logic sram_req; + assign sram_req = (ombx_sram_read_req_i | imbx_sram_write_req_i); + + // FIFO Counting logic for maximum outstanding requests + logic [LCFG_MAX_REQS_LOG2-1:0] outstanding_req_count_d, outstanding_req_count_q; + logic inc_cnt, dec_cnt, update_cnt; + + // Do we have knowledge of any outstanding requests, including one currently being accepted? + // Note: a device may respond in the same cycle as accepting the request. + logic any_outstanding_reqs; + assign any_outstanding_reqs = inc_cnt || (outstanding_req_count_q != '0); + + // Increment the count of outstanding requests when a new request is accepted onto the bus, + // being sure not to drive out more requests than we can track. + assign inc_cnt = sram_req & ~max_outstanding_reqs_reached & sram_gnt; + // Decrement the count when a reply is received, being sure not to underflow if we have had to + // process an Abort operation whilst one or more requests was still outstanding. + assign dec_cnt = sram_valid & any_outstanding_reqs; + assign outstanding_req_count_d = hostif_control_abort_clear_i ? '0 : + (outstanding_req_count_q + inc_cnt - dec_cnt); + // Update the count of outstanding requests. + assign update_cnt = inc_cnt | dec_cnt | hostif_control_abort_clear_i; + + prim_flop_en #( + .Width(LCFG_MAX_REQS_LOG2) + ) u_outstanding_req_cnt ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .en_i ( update_cnt ), + .d_i ( outstanding_req_count_d ), + .q_o ( outstanding_req_count_q ) + ); + // Block SRAM requests if we reached the maximum outstanding number + assign max_outstanding_reqs_reached = (outstanding_req_count_q == LCFG_MAX_REQS); + // All outstanding requests responded, thus all transfers are written or read + assign imbx_sram_all_vld_rcvd_o = (outstanding_req_count_q == '0) & ~sram_req; + + tlul_adapter_host #( + .MAX_REQS ( LCFG_MAX_REQS ), + .EnableDataIntgGen ( 1 ), + .EnableRspDataIntgCheck( 1 ) + ) u_sram_host_adapter ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + // Request channel + .req_i ( sram_req & ~max_outstanding_reqs_reached ), + .gnt_o ( sram_gnt ), + .addr_i ( sram_address ), + .we_i ( arb_write_winner ), + .wdata_i ( imbx_write_data_i ), + .wdata_intg_i ( TL_A_USER_DEFAULT.data_intg ), + .be_i ( {top_pkg::TL_DBW{1'b1}} ), + .instr_type_i ( prim_mubi_pkg::MuBi4False ), + .user_rsvd_i ( '0 ), + // Response channel + .valid_o ( sram_valid ), + .rdata_o ( ombx_sram_read_resp_o ), + .rdata_intg_o ( ), + .err_o ( sram_err_o ), + .intg_err_o ( intg_err_o ), + // Bus interface + .tl_o ( tl_host_o ), + .tl_i ( tl_host_i ) + ); + + // Mux out response valid signal + // We cannot differentiate on directly on the response signal of the TLUL adapter. We need + // to look if the response was a response with data or not. It it's with data, it was a read + // request and we serve ombx_sram_read_resp_vld_o. If it was a response without data + // it was a write request. + // We also ensure that any responses are not propagated after an Abort operation. + assign ombx_sram_read_resp_vld_o = sram_valid & any_outstanding_reqs & + (tl_host_i.d_opcode == tlul_pkg::AccessAckData); + + // Functional Coverage + `COVER(MaxOutstandingRequestsReached_C, sram_req & max_outstanding_reqs_reached) +endmodule diff --git a/hw/ip/mbx/rtl/mbx_sysif.sv b/hw/ip/mbx/rtl/mbx_sysif.sv new file mode 100644 index 0000000000000..1360d16c396f7 --- /dev/null +++ b/hw/ip/mbx/rtl/mbx_sysif.sv @@ -0,0 +1,293 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +module mbx_sysif + import tlul_pkg::*; +#( + parameter int unsigned CfgSramAddrWidth = 32, + parameter int unsigned CfgSramDataWidth = 32, + // PCIe capabilities + parameter bit DoeIrqSupport = 1'b1, + parameter bit DoeAsyncMsgSupport = 1'b1 +) ( + input logic clk_i, + input logic rst_ni, + // Device port to the system fabric + input tlul_pkg::tl_h2d_t tl_sys_i, + output tlul_pkg::tl_d2h_t tl_sys_o, + output logic intg_err_o, + // Custom interrupt to the system requester + output logic doe_intr_support_o, + output logic doe_intr_en_o, + output logic doe_intr_o, + // Asynchronous message to the requester + output logic doe_async_msg_support_o, + output logic doe_async_msg_en_o, + input logic doe_async_msg_set_i, + input logic doe_async_msg_clear_i, + // Abort clearing from the host + input logic sysif_abort_ack_i, + // Access to the control register + output logic sysif_control_abort_set_o, + output logic sysif_control_go_set_o, + // Access to the status register + input logic sysif_status_busy_valid_i, + input logic sysif_status_busy_i, + output logic sysif_status_busy_o, + input logic sysif_status_doe_intr_ready_set_i, + input logic sysif_status_error_set_i, + output logic sysif_status_error_o, + input logic sysif_status_ready_valid_i, + input logic sysif_status_ready_i, + output logic sysif_status_ready_o, + // Alias of the interrupt address and data registers to the host interface + output logic [CfgSramAddrWidth-1:0] sysif_intr_msg_addr_o, + output logic [CfgSramDataWidth-1:0] sysif_intr_msg_data_o, + // Control lines for backpressuring the bus + input logic imbx_pending_i, + input logic ombx_pending_i, + // Data interface for inbound and outbound mailbox + output logic write_data_write_valid_o, + output logic [CfgSramDataWidth-1:0] write_data_o, + output logic read_data_read_valid_o, + output logic read_data_write_valid_o, + input logic [CfgSramDataWidth-1:0] read_data_i +); + import mbx_reg_pkg::*; + + mbx_soc_reg2hw_t reg2hw; + mbx_soc_hw2reg_t hw2reg; + + // Interface for the custom register interface with bus blocking support + tlul_pkg::tl_h2d_t tl_win_h2d[2]; + tlul_pkg::tl_d2h_t tl_win_d2h[2]; + + // SEC_CM: BUS.INTEGRITY + mbx_soc_reg_top u_soc_regs ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .tl_i ( tl_sys_i ), + .tl_o ( tl_sys_o ), + .tl_win_o ( tl_win_h2d ), + .tl_win_i ( tl_win_d2h ), + .reg2hw ( reg2hw ), + .hw2reg ( hw2reg ), + .intg_err_o ( intg_err_o ), + .devmode_i ( 1'b1 ) + ); + + // Straps for the external capability header registers + assign doe_intr_support_o = DoeIrqSupport; + assign doe_async_msg_support_o = DoeAsyncMsgSupport; + // DOE IRQ is generated when: + // - the host wrote a complete message to the outbound mailbox + // - there is an error + // - there is an asynchronous message + // request + assign doe_intr_o = DoeIrqSupport & reg2hw.soc_status.doe_intr_status.q; + + // Fiddle rising edge of writing the abort and go bit + assign sysif_control_abort_set_o = reg2hw.soc_control.abort.qe & reg2hw.soc_control.abort.q; + assign hw2reg.soc_control.abort.d = 1'b0; + + assign sysif_control_go_set_o = reg2hw.soc_control.go.qe & reg2hw.soc_control.go.q; + assign hw2reg.soc_control.go.d = 1'b0; + + // Manual implementation of the doe_intr_en bit + // Gate the data input with the feature flag + // SWAccess: RW + // HWAccess: RO + prim_subreg #( + .DW (1), + .SwAccess(prim_subreg_pkg::SwAccessRW), + .RESVAL (1'h0) + ) u_soc_control_doe_intr_en ( + .clk_i (clk_i), + .rst_ni (rst_ni), + // from register interface + .we (reg2hw.soc_control.doe_intr_en.qe), + .wd (reg2hw.soc_control.doe_intr_en.q & DoeIrqSupport), + // HWAccess: hro + .de (1'b0), + .d (1'b0), + // to internal hardware + .qe (), + .q (doe_intr_en_o), + .ds (hw2reg.soc_control.doe_intr_en.d), + .qs () + ); + + // Manual implementation of the doe_async_msg_en bit + // Gate the data input with the feature flag + // SWAccess: RW + // HWAccess: RO + prim_subreg #( + .DW (1), + .SwAccess(prim_subreg_pkg::SwAccessRW), + .RESVAL (1'h0) + ) u_soc_control_doe_async_msg_en ( + .clk_i (clk_i), + .rst_ni (rst_ni), + // from register interface + .we (reg2hw.soc_control.doe_async_msg_en.qe), + .wd (reg2hw.soc_control.doe_async_msg_en.q & DoeAsyncMsgSupport), + // HWAccess: hro + .de (1'b0), + .d (1'b0), + // to internal hardware + .qe (), + .q (doe_async_msg_en_o), + .ds (hw2reg.soc_control.doe_async_msg_en.d), + .qs () + ); + + // Fiddle out status register bits for external write logic + assign sysif_status_busy_o = reg2hw.soc_status.busy.q; + assign sysif_status_error_o = reg2hw.soc_status.error.q; + + // External read logic + assign hw2reg.soc_status.busy.de = sysif_status_busy_valid_i; + assign hw2reg.soc_status.busy.d = sysif_status_busy_i; + + // Gate the async msg setter with the feature strap and the enable bit + logic async_msg_set_gated; + assign async_msg_set_gated = DoeAsyncMsgSupport & doe_async_msg_en_o & doe_async_msg_set_i; + + // Interrupt is triggered by the outbound handler if the message has been written to + // the memory and can be read by the system, an error is raised, or if there is an asynchronous + // message request coming from the host. + // The interrupt is cleared by the SOC firmware via the RW1C behavior or when an abort is + // acknowledged by the host + assign hw2reg.soc_status.doe_intr_status.de = DoeIrqSupport & + (sysif_status_doe_intr_ready_set_i | + sysif_abort_ack_i | + sysif_status_error_set_i | + async_msg_set_gated); + assign hw2reg.soc_status.doe_intr_status.d = (sysif_status_doe_intr_ready_set_i | + sysif_status_error_set_i | + async_msg_set_gated) & + ~sysif_abort_ack_i; + + // Async message status is updated by the host interface when enabled + // and cleared in all cases on an abort and abort ack/FW reset + assign hw2reg.soc_status.doe_async_msg_status.de = (DoeAsyncMsgSupport & doe_async_msg_en_o & + (doe_async_msg_set_i | + doe_async_msg_clear_i)) | + sysif_control_abort_set_o | + sysif_abort_ack_i; + assign hw2reg.soc_status.doe_async_msg_status.d = doe_async_msg_set_i; + + // Error is cleared when writing the abort bit or on a FW-based reset + assign hw2reg.soc_status.error.de = sysif_status_error_set_i | + sysif_control_abort_set_o | + sysif_abort_ack_i; + assign hw2reg.soc_status.error.d = sysif_status_error_set_i; + + // Set by OT firmware (w1s) + // Cleared by SoC firmware (w1c) + assign hw2reg.soc_status.ready.de = sysif_status_ready_valid_i; + assign hw2reg.soc_status.ready.d = sysif_status_ready_i; + // Ready bit indication into hardware + assign sysif_status_ready_o = reg2hw.soc_status.ready.q; + + // Dedicated TLUL adapter for implementing the write data mailbox register via a register window. + // We use the register window to access the internal bus signals, allowing the mailbox to halt + // the bus if there are too many outstanding requests. + logic reg_wdata_we; + logic [top_pkg::TL_DW-1:0] reg_wdata_wdata; + tlul_adapter_reg #( + .RegAw ( SocAw ), + .RegDw ( top_pkg::TL_DW ), + .EnableDataIntgGen ( 0 ) + ) u_wdata_reg_if ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .tl_i ( tl_win_h2d[MBX_WDATA_IDX] ), + .tl_o ( tl_win_d2h[MBX_WDATA_IDX] ), + .en_ifetch_i ( prim_mubi_pkg::MuBi4False ), + .intg_error_o ( ), + .we_o ( reg_wdata_we ), + // No Reading of the write register. Always reads zero + .re_o ( ), + .addr_o ( ), + .wdata_o ( reg_wdata_wdata ), + .be_o ( ), + .busy_i ( imbx_pending_i ), + .rdata_i ( '0 ), + .error_i ( 1'b0 ) + ); + + // Dedicated TLUL adapter for implementing the read data mailbox register via a register window. + // We use the register window to access the internal bus signals, allowing the mailbox to halt + // the bus if there are too many outstanding requests. The register is implemented as hwext + // outside of this hierarchy + tlul_adapter_reg #( + .RegAw ( SocAw ), + .RegDw ( top_pkg::TL_DW ), + .EnableDataIntgGen ( 0 ) + ) u_rdata_reg_if ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .tl_i ( tl_win_h2d[MBX_RDATA_IDX] ), + .tl_o ( tl_win_d2h[MBX_RDATA_IDX] ), + .en_ifetch_i ( prim_mubi_pkg::MuBi4False ), + .intg_error_o ( ), + // No writing to the read register + .we_o ( read_data_write_valid_o ), + .re_o ( read_data_read_valid_o ), + .addr_o ( ), + // Write values are ignored. A Write simply means the read has occurred. + .wdata_o ( ), + .be_o ( ), + .busy_i ( ombx_pending_i ), + .rdata_i ( read_data_i ), + .error_i ( 1'b0 ) + ); + + // Manual implementation of the write read mailbox register. + // The manual implementation of the register via a register window is needed to expose the + // internal register interface of the TLUL bus to halt the bus if there too many outstanding + // requests. + logic mbx_wrdata_flds_we; + prim_flop #( + .Width(1), + .ResetValue(0) + ) u_mbxwrdat0_qe ( + .clk_i(clk_i), + .rst_ni(rst_ni), + .d_i(mbx_wrdata_flds_we), + .q_o(write_data_write_valid_o) + ); + prim_subreg #( + .DW (CfgSramDataWidth), + .SwAccess(prim_subreg_pkg::SwAccessWO), + .RESVAL (32'h0) + ) u_reg_wrdata ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (reg_wdata_we), + .wd (reg_wdata_wdata), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (mbx_wrdata_flds_we), + .q (write_data_o), + .ds (), + + // to register interface (read) + .qs () + ); + + // Forward IRQ addr and data register to the host interface + assign sysif_intr_msg_addr_o = reg2hw.soc_doe_intr_msg_addr.q; + assign sysif_intr_msg_data_o = reg2hw.soc_doe_intr_msg_data.q; + + // Assertions + `ASSERT(DataWidthCheck_A, CfgSramDataWidth == top_pkg::TL_DW) +endmodule