HAL Architecture Specification

Introduction

The Hardware Abstraction Layer (HAL) provides a software-visible representation of openENOC hardware components and defines a standardized mechanism for accessing their Control and Status Registers (CSRs). The HAL serves as the primary interface between software and hardware, enabling configuration, monitoring, diagnostics, and runtime control of openENOC subsystems.

This document specifies the HAL architecture and CSR organization for the openENOC Switch and the openENOC Endpoint Interface. The described CSR definitions establish a common programming model for software components and serve as the authoritative reference for hardware/software integration.

The application-facing C API, its compatibility contract, and runtime usage are documented in Runtime API Reference.

CSR Specification Methodology

Within this flow, Control and Status Registers are defined using Accellera’s SystemRDL 2.0 as the authoritative specification format for all openENOC hardware components.

For artefact generation, the openENOC toolchain is based on PeakRDL, which processes the SystemRDL description and produces the corresponding CSR-related outputs used throughout the build and verification flow. Since the current PeakRDL implementation does not support all features of the SystemRDL 2.0 standard, CSR definitions are written with this limitation in mind to ensure that the complete register specification can be translated into all required generated artefacts without manual intervention or post-processing.

openENOC Switch Interface HAL Architecture

The openENOC Switch provides frame-forwarding and management functionality within the openENOC system. Software access to switch resources is provided through the openENOC Switch Interface, which exposes a memory-mapped CSR space.

The switch interface HAL defines the software-visible representation of switch functionality and provides a consistent programming model independent of implementation-specific details.

Architecture

The switch interface HAL is organized around a CSR-based management interface that exposes configuration, status, monitoring, and diagnostic functionality. Software components interact with switch resources through register accesses performed over the openENOC Switch Interface. The HAL architecture establishes a clear separation between software-visible behavior and the underlying frame-forwarding implementation, allowing internal switch architectures to evolve while maintaining software compatibility.

A central configuration aspect of the switch interface HAL is the selection of the switch operating mode. In unmanaged mode, the switch operates autonomously and forwarding state is maintained by internal hardware logic. In this mode, the switch may learn forwarding table entries from observed traffic and update its forwarding state without software intervention. In managed mode, forwarding behavior is controlled through software-visible configuration mechanisms exposed by the openENOC Switch Interface. This allows an external processor to populate, update, inspect, or invalidate forwarding table entries according to system-specific requirements.

The operating mode is controlled through switch configuration registers. These registers define whether the switch operates autonomously or under software control, and they provide the foundation for additional management functions such as forwarding table updates, status inspection, diagnostics, and event handling. Status registers expose the current operational state of the switch and allow software to determine whether the switch is active, idle, paused, or operating under a specific management mode.

The default forwarding behavior for frames that do not match any enabled forwarding table entry is defined through a dedicated register containing a destination interface bitmap. This register specifies the set of output interfaces to which unmatched frames shall be forwarded. In managed mode, this mechanism allows unmatched frames to be redirected toward a switch controller implemented on one of the openENOC endpoints. Such frames are delivered to the controller using the standard openENOC Endpoint Interface, preserving the same data-plane abstraction used by other endpoint-to-network communication.

Since forwarding table updates may affect the active forwarding behavior of the switch, the HAL includes a switch-level flow control mechanism used during managed reconfiguration. This mechanism is exposed through a Flow Control Register and allows software to temporarily stop or pause the flow of frames through the switch before modifying forwarding state. After the pause request is issued, software can observe the corresponding status indication to determine when the switch has reached a safe state for table modification.

Once the switch is paused, software may update forwarding table entries through the CSR space without interfering with active frame forwarding. After the update sequence is complete, software releases the pause condition and normal forwarding operation resumes. This mechanism provides a simple and deterministic way to perform controlled forwarding table reconfiguration while avoiding inconsistent lookup behavior during partial updates.

The flow control mechanism described here is local to switch management and should not be confused with link-level or protocol-level flow control. Its purpose is to coordinate software-driven CSR updates with the internal forwarding pipeline of the switch.

In managed mode, the HAL therefore provides both configuration access and operational control over the switch forwarding behavior. In unmanaged mode, the same hardware may operate without software intervention, and the openENOC Switch Interface may be omitted if no external configuration or monitoring functionality is required.

Forwarding Table

A central software-visible structure of the openENOC Switch is the forwarding table, which defines how frames are forwarded based on their destination MAC address. The table is exposed through the CSR space and can be configured by software using the openENOC Switch Interface.

The forwarding table contains entries aligned to a 32-bit data bus. Each forwarding table entry is represented in the CSR space by fields containing a destination MAC address, an N-bit destination interface bitmap, a single enable bit, and padding bits required for alignment to the 32-bit bus width. The destination interface bitmap defines the set of output interfaces to which a matching frame shall be forwarded.

_images/openENOC-SwitchForwardingTable.svg

Example forwarding table for an 8-port openENOC Switch

The figure illustrates an example forwarding table for an openENOC Switch with 8 ports. The table contains several representative forwarding rules. Rule (1) defines unicast forwarding of frames with destination address 0x020E0C000011 to the first output interface. Rule (2) represents a disabled unicast rule that remains present in the table but is not applied during forwarding lookup. Rule (3) defines multicast forwarding of frames with destination address 0x030E0C330000 to the first, third, fifth, and seventh output interfaces. Rule (4) defines frame dropping for destination address 0x020E0C123456 by means of an enabled entry with an empty destination interface bitmap. Rule (M) defines the broadcast forwarding rule for frames with destination address 0xFFFFFFFFFFFF.

Register Definitions and CSR Memory Map

The SystemRDL source shown below defines the CSR structure of the openENOC Switch. This source file is the maintained register specification used by the PeakRDL-based generation flow.

SystemRDL specification of the openENOC Switch CSR map
  1// SPDX-FileCopyrightText: 2026 Enio Kaljic
  2// SPDX-License-Identifier: CERN-OHL-S-2.0
  3
  4regfile openenoc_switch_interface #(
  5    longint unsigned NUM_OF_INTERFACES = 1,
  6    longint unsigned TABLE_DEPTH = 1,
  7    string INST_NAME = "openenoc_switch_interface"
  8) {
  9    name = INST_NAME;
 10    desc = "Control and status register file for an openENOC Switch instance. It includes configuration registers and a forwarding table used to map destination MAC address keys to output interface selections for frame forwarding.";
 11
 12    default accesswidth = 32;
 13    default regwidth = 32;
 14    default alignment = 4;
 15
 16    reg info {
 17        name = {INST_NAME, ".info"};
 18        desc = "Read-only information register for this openENOC Switch instance.";
 19
 20        field {
 21            name = {INST_NAME, ".info.num_of_interfaces[21:16]"};
 22            desc = "Number of interfaces in this openENOC Switch instance. This field reflects the NUM_OF_INTERFACES parameter value.";
 23
 24            sw = r;
 25            hw = r;
 26        } num_of_interfaces[21:16] = NUM_OF_INTERFACES;
 27
 28        field {
 29            name = {INST_NAME, ".info.table_depth[15:0]"};
 30            desc = "Depth of the forwarding table in this openENOC Switch instance. This field reflects the TABLE_DEPTH parameter value.";
 31
 32            sw = r;
 33            hw = r;
 34        } table_depth[15:0] = TABLE_DEPTH;
 35    } info;
 36
 37    reg forwarding_control {
 38        name = {INST_NAME, ".forwarding_control"};
 39        desc = "Forwarding control register for the openENOC Switch instance.";
 40
 41        field {
 42            name = {INST_NAME, ".forwarding_control.operation_mode[0:0]"};
 43            desc = "Mode of operation for the openENOC Switch instance. When set to 1, the switch operates in managed mode, allowing software to configure the forwarding table and control forwarding operations. When set to 0, the switch operates in unmanaged mode, where forwarding state is maintained autonomously by internal hardware logic without software intervention.";
 44
 45            sw = rw;
 46            hw = r;
 47        } operation_mode[0:0] = 0;
 48
 49        field {
 50            name = {INST_NAME, ".forwarding_control.pause_request[7:7]"};
 51            desc = "Pause request for the forwarding logic. When set, this field requests the switch to pause frame forwarding and clear its internal pipeline before forwarding table updates are performed.";
 52
 53
 54            sw = rw;
 55            hw = r;
 56        } pause_request[7:7] = 0;
 57
 58        field {
 59            name = {INST_NAME, ".forwarding_control.pause_done[15:15]"};
 60            desc = "Pause done status. When set, this field indicates that the switch has paused frame forwarding and reached a safe state for forwarding table modification.";
 61
 62            sw = r;
 63            hw = w;
 64        } pause_done[15:15];
 65    } forwarding_control;
 66
 67    reg default_forwarding {
 68        name = {INST_NAME, ".default_forwarding"};
 69        desc = "Defines the destination interface or interfaces for frames that do not match any enabled forwarding table entry.";
 70
 71        field {
 72            name = {INST_NAME, ".default_forwarding.bitmap[NUM_OF_INTERFACES-1:0]"};
 73            desc = "Bitmap selecting the output interface or interfaces to which frames that do not match any enabled forwarding table entry are forwarded. Bit NUM_OF_INTERFACES-1, the MSB, corresponds to the first interface; bit 0 corresponds to the last interface.";
 74
 75            sw = rw;
 76            hw = r;
 77        } bitmap[NUM_OF_INTERFACES-1:0] = 0;
 78    } default_forwarding;
 79
 80    external regfile forwarding_table {
 81        name = {INST_NAME, ".forwarding_table"};
 82        desc = "Forwarding table used to map MAC addresses to output interface selections for frame forwarding.";
 83
 84        regfile entry {
 85            name = {INST_NAME, ".forwarding_table.entry[0..TABLE_DEPTH-1]"};
 86            desc = "Forwarding table entry containing the MAC address key, output interface selection, and entry configuration.";
 87
 88            reg mac_address {
 89                name = {INST_NAME, ".forwarding_table.entry[0..TABLE_DEPTH-1].mac_address"};
 90                desc = "48-bit destination MAC address used as the key for this forwarding table entry.";
 91
 92                regwidth = 64;
 93
 94                field {
 95                    name = {INST_NAME, ".forwarding_table.entry[0..TABLE_DEPTH-1].mac_address.hi_word[47:32]"};
 96                    desc = "Upper 16 bits [47:32] of the 48-bit MAC address stored in this forwarding table entry.";
 97
 98                    sw = rw;
 99                    hw = rw;
100                } hi_word[47:32];
101
102                field {
103                    name = {INST_NAME, ".forwarding_table.entry[0..TABLE_DEPTH-1].mac_address.lo_word[31:0]"};
104                    desc = "Lower 32 bits [31:0] of the 48-bit MAC address stored in this forwarding table entry.";
105
106                    sw = rw;
107                    hw = rw;
108                } lo_word[31:0];
109            } mac_address;
110
111            reg iface {
112                name = {INST_NAME, ".forwarding_table.entry[0..TABLE_DEPTH-1].iface"};
113                desc = "Forwarding interface information associated with this forwarding table entry.";
114
115                field {
116                    name = {INST_NAME, ".forwarding_table.entry[0..TABLE_DEPTH-1].iface.bitmap[NUM_OF_INTERFACES-1:0]"};
117                    desc = "Bitmap selecting the output interface or interfaces to which a matching frame is forwarded. Bit NUM_OF_INTERFACES-1, the MSB, corresponds to the first interface; bit 0 corresponds to the last interface.";
118
119                    sw = rw;
120                    hw = rw;
121                } bitmap[NUM_OF_INTERFACES-1:0];
122            } iface;
123
124            reg conf {
125                name = {INST_NAME, ".forwarding_table.entry[0..TABLE_DEPTH-1].conf"};
126                desc = "Configuration information associated with this forwarding table entry.";
127
128                field {
129                    name = {INST_NAME, ".forwarding_table.entry[0..TABLE_DEPTH-1].conf.enabled"};
130                    desc = "Enables this forwarding table entry. When cleared, the entry is ignored during forwarding table lookup.";
131
132                    sw = rw;
133                    hw = rw;
134                } enabled;
135            } conf;
136        } entry[TABLE_DEPTH];
137    } forwarding_table;
138};
139
140addrmap openenoc_switch_interface_top {
141    name = "openenoc_switch_interface_top";
142    desc = "Control and status address map for an openENOC Switch instance.";
143
144    littleendian;
145    default accesswidth = 32;
146    default regwidth = 32;
147    default alignment = 4;
148    addressing = regalign;
149
150    openenoc_switch_interface #(
151        .NUM_OF_INTERFACES(1),
152        .TABLE_DEPTH(1),
153        .INST_NAME("openenoc_switch_interface")
154    ) openenoc_switch_interface;
155};

The openENOC Switch CSR memory map and detailed register documentation are derived directly from this SystemRDL specification. The generated CSR documentation provides the corresponding human-readable description of the register hierarchy, address offsets, field layouts, access permissions, reset values, and associated register semantics.

The complete generated CSR documentation is available in openenoc_switch_interface_top address map.

openENOC Endpoint Interface HAL Architecture

The openENOC Endpoint Interface provides the connection between processing elements and the openENOC network. It exposes endpoint control, status monitoring, and communication management functionality through a dedicated CSR space.

The endpoint interface HAL establishes a uniform software interface for accessing endpoint resources and controlling interactions with the network.

Architecture

The endpoint interface HAL is organized around a CSR-based management interface that exposes endpoint configuration, software-visible stream access, remote peer configuration, DMA control, and a virtual remote-memory region. Software components interact with endpoint resources through register accesses performed over the openENOC Switch Interface. The HAL architecture establishes a clear separation between software-visible endpoint behavior and the underlying oETP frame handling, allowing internal buffering, DMA scheduling, and protocol processing logic to evolve while maintaining software compatibility.

A central configuration aspect of the endpoint interface HAL is the description of the local endpoint instance and the set of remote peers that it can access. The read-only information register exposes implementation parameters such as the total depth of the virtual remote-memory region and the number of supported remote peers. These values allow software to discover the size and structure of the endpoint address space without relying on hard-coded assumptions.

The endpoint configuration registers define the local unicast MAC address and a separate multicast destination address. The oETP receive filter compares an individual destination against conf.mac_address and a group destination against conf.multicast_address. All endpoints in a replication group configure the same multicast address while retaining individual unicast identities; outgoing oETP frames use the unicast address as their source. A zero multicast address disables oETP group reception. The multicast register follows the unicast MAC register, with both response-timeout registers following the non-oETP receive policy. This destination filtering will be implemented in the oETP engine.

Remote communication targets are described through a peer table, where each entry contains the MAC address of a remote peer, the offset of the corresponding virtual remote-memory region, the local memory base address, the remote memory base address, and the size of the region. This structure allows software to describe how local memory resources are related to remote memory regions visible through the endpoint.

The HAL supports two complementary access models. The first model is a software-visible AXI4-Stream access path exposed through source and sink register files. The source register file allows software to provide stream data words, assert the corresponding valid indication, and mark the last word of a frame. Software observes the ready status to determine when the endpoint can accept the next transfer. Conversely, the sink register file allows hardware to present received stream data to software, together with valid and last indications, while software acknowledges reception through the ready control field.

This CSR-mapped AXI4-Stream interface mirrors the basic AXI4-Stream handshake semantics in software-visible form. It is useful for low-bandwidth communication, diagnostics, initialization sequences, and simple software-driven frame exchange. The mechanism does not prescribe the internal implementation of the endpoint datapath; it only defines how stream-oriented transfers are exposed through the HAL.

The second access model is memory-oriented communication through the virtual remote-memory region. This region provides a software-visible address space representing memory associated with one or more remote peers. The mapping between a remote peer and its corresponding portion of the virtual memory space is defined by the peer configuration registers. Accesses to this region may be handled directly or used as the basis for DMA-driven transfers, depending on the configured DMA mode for the selected peer.

The response-timeout configuration uses two 32-bit cycle counts: conf.rmem_timeout.cycles for transparent RMEM and conf.dma_timeout.cycles for individual unicast peer DMA fragments. The DMA setting is shared by all peers; there is no separate whole-transfer timeout. Both reset to zero, which permits an indefinite response wait. A DMA fragment timeout aborts the remaining fragments and reports peer DMA error code 8 (TIMEOUT). Software controls recovery and any restart; hardware does not retransmit automatically. Multicast writes complete locally without waiting for responses. These CSRs define the oETP timeout policy; runtime enforcement is part of the forthcoming oETP engine implementation.

An RMEM read timeout returns 0xFFFFFFFF with cpuif_rd_ack; an RMEM write timeout asserts cpuif_wr_ack. The existing generated external-RMEM boundary carries ACK and read data and remains unchanged without ERR signals. Transparent RMEM (peer mode 1) and bulk DMA share peers.entry[].dma.error and the four-bit error_code. Local timeout records code 8; a received ERROR_RSP requires an explicit wire-to-CSR code mapping. Starting or successfully completing another operation preserves the error. Writing one to the peer’s dma.clear_error clears the flag and code; hardware acknowledges by clearing the command field. New failures take precedence over a simultaneous clear. The command does not abort active work, clear done, or complete an IRQ claim.

Failure reporting also covers received requests. The responder identifies the peer by source MAC and records the servicing failure before exposing its failed completion to the oETP engine. The initiator maps the valid ERROR_RSP cause to remote code 9..15. An AXI write SLVERR therefore records local code 6 at the responder and remote code 14 at the initiator. Received failures preserve the responder’s locally initiated dma.request, idle, and done state; successful incoming requests preserve sticky errors and generate no IRQ. An incomplete received DMA_WRITE_REQ or DMA_READ_RSP, including missing or incorrect EndOfData, records remote code 10 directly. A truncated received write returns ERROR_RSP wire cause 2, while its receiver retains CSR code 10. Both endpoints can independently notify software that destination memory may be partially updated. Multicast receivers retain local diagnostics and enabled events while suppressing responses.

RMEM and bulk DMA retain separate IRQ sources: irq.event_enable.rmem_error enables source 5 for RMEM failures, while bulk DMA retains PEER_DMA_COMPLETE, source 0, gated by the per-peer dma.irq_enable and global irq.event_enable.peer_dma_complete. For a received bulk failure, the per-peer enable is captured when recording the error and the global enable is sampled at IRQ admission. Received RMEM failures use the separate global RMEM gate at admission. Error recording is independent of IRQ enable; neither releasing an RMEM access nor returning an error completion waits for IRQ FIFO capacity. The responder retains a pending notification and defers its next incoming request until the notification is admitted and committed. Clearing the CSR error and completing an IRQ claim remain independent operations.

The DMA engine owns the shared status and receives tagged RMEM error completions on the common transfer interface. Both RMEM CPUIF directions and the RMEM IRQ producer belong to that engine. The locally initiating CPUIF transaction controller and its event generation remain to be implemented; the existing scalar CSR stub is retained. The responder already records incoming failures and emits the appropriate error event, including unsupported RMEM requests. The oETP engine owns protocol framing and response timeouts; its parser and ERROR_RSP assembly remain to be implemented.

For non-oETP DMA, AXIS between the engines carries the complete raw Ethernet frame without FCS. For oETP bulk DMA, it streams exactly the requested memory bytes without headers, EndOfData, or padding, starting on the first matching TVALID. The oETP engine appends the 32-bit 0xE0D0E0D0 EndOfData only after successful final read completion; failures finish the frame without that marker. The common oETP fragment maximum is 8160 bytes at the unchanged 8192-byte raw Ethernet frame limit. Final TX and late RX protocol outcomes must be added to the internal transfer contract when implementing the parser. Address and Length are command metadata, and RMEM data travels directly on command/completion fields. Internal AXIS TUSER[0] retains the final-fragment flag; TUSER[1] distinguishes initiator (0) from responder (1) when both command paths use the same peer and sequence. This adds no software-visible mode or wire field. The full contract is described in oETP Engine – openenoc_endpoint_oetp_engine.

MAX_RAW_FRAME_SIZE remains a synthesis-time endpoint RDL parameter and defaults to 8192 bytes without FCS. The read-only info.max_dma_frame_size_bytes advertises that raw frame ceiling. The global conf.dma_max_fragment_size.bytes register follows conf.dma_timeout and selects the maximum memory bytes per locally initiated peer DMA fragment. Its reset and upper bound are 4 * floor((MAX_RAW_FRAME_SIZE - 32) / 4), or 8160 bytes in the default build. Hardware rounds the written value down to a multiple of four, then requires an effective MFS from four through that bound. The CSR retains the original value: 31 selects 28, 7 selects four, and 8163 selects 8160. Values zero through three or a rounded value above the bound reject a new DMA block with local error code 1. The final fragment retains the exact remaining Length, even if it is shorter than four bytes. The DMA engine snapshots the effective MFS at whole-transfer acceptance, so writes during a transfer affect only later blocks. Received requests use the physical fragment ceiling; raw non-oETP DMA, direct CSR streams, and RMEM are unaffected.

DMA behavior is controlled independently for each peer. When DMA operation is disabled, the peer entry remains configured but no automatic transfer is performed. In transparent mode, accesses to the virtual remote-memory region are translated into corresponding accesses to the remote peer memory region on a word-by-word basis. This mode is suitable when software requires a direct memory-mapped view of remote resources and when simple access semantics are preferred over bulk synchronization.

The endpoint interface HAL also supports mirrored transfer modes. In mirror-to-local mode, the local memory region is used as the software-visible representation of the remote peer memory, and the state of the remote memory region is fetched from the remote peer on demand or periodically. In mirror-to-remote mode, the remote memory region is used as the destination representation, and the state of the local memory region is sent to the remote peer on demand or periodically. These modes allow software to configure endpoint-to-endpoint memory synchronization without directly managing individual transport frames.

DMA transfers are initiated through a peer-specific request field. The corresponding status fields indicate whether the DMA engine is idle, whether the requested transfer has completed successfully, or whether an error has occurred. A typical software sequence therefore consists of configuring the peer address mapping, selecting the DMA mode, issuing a transfer request, and observing the idle, done, and error status indications until the operation reaches a terminal state.

Peer DMA error status distinguishes locally detected causes 1..7, local TIMEOUT = 8, and remote causes 9..15 reported through ERROR_RSP. Code 10 also directly identifies an incomplete received DMA data frame or absent/incorrect EndOfData, without requiring ERROR_RSP first. Wire causes 1..7 map to the remote CSR range by adding 8 after validating the complete 32-bit value. Local AXI read/write SLVERR and DECERR use codes 4..7; their remote equivalents use 12..15. Invalid wire causes report local invalid-parameter code 1. The initiator completion interface carries this final encoding, which the DMA engine preserves for both RMEM and bulk DMA. Raw non-oETP DMA error status describes local work. RMEM failure handling is described in oETP Engine – openenoc_endpoint_oetp_engine.

Non-oETP TX and RX each expose command_status.clear_errors at bit 9. Writing one clears that channel’s error and error_code; hardware clears the command after accepting it. Starting or successfully completing another transfer preserves a recorded error, and a new failure takes precedence over a simultaneous clear. Clearing leaves an active or armed transfer, done, byte counts, and IRQ events intact. TX, RX, and per-peer error records clear independently.

The DMA control mechanism described here is local to endpoint management and should not be confused with Ethernet link-level or protocol-level flow control. Its purpose is to coordinate software-visible memory mappings and transfer requests with the internal endpoint datapath, DMA engine, and oETP processing logic.

The endpoint interface HAL therefore provides both a stream-oriented and a memory-oriented abstraction for communication with the openENOC network. The AXI4-Stream register interface offers a simple CSR-accessible path for direct frame exchange, while the peer table, virtual remote-memory region, and DMA controls provide a scalable mechanism for accessing and synchronizing memory resources across endpoints.

Register Definitions and CSR Memory Map

The SystemRDL source shown below defines the CSR structure of the openENOC Endpoint Interface. This source file is the maintained register specification used by the PeakRDL-based generation flow.

SystemRDL specification of the openENOC Endpoint Interface CSR map
   1// SPDX-FileCopyrightText: 2026 Enio Kaljic
   2// SPDX-License-Identifier: CERN-OHL-S-2.0
   3
   4regfile openenoc_endpoint_interface #(
   5    longint unsigned RMEM_TOTAL_DEPTH = 256,
   6    longint unsigned NUM_OF_PEERS = 1,
   7    longint unsigned MAX_RAW_FRAME_SIZE = 8192,
   8    longint unsigned HAS_PEER_DMA = 1,
   9    longint unsigned HAS_NON_OETP_DMA = 1,
  10    longint unsigned HAS_DIRECT_AXIS = 1,
  11    longint unsigned HAS_RMEM = 1,
  12    longint unsigned HAS_IRQ = 1,
  13    string INST_NAME = "openenoc_endpoint_interface"
  14) {
  15    name = INST_NAME;
  16    desc = "Control and status register file for an openENOC Endpoint Interface instance.";
  17
  18    default accesswidth = 32;
  19    default regwidth = 32;
  20    default alignment = 4;
  21
  22    reg info {
  23        name = {INST_NAME, ".info"};
  24        desc = "Read-only information register for this openENOC Endpoint Interface instance.";
  25
  26        regwidth = 64;
  27
  28        field {
  29            name = {INST_NAME, ".info.rmem_total_depth[31:0]"};
  30            desc = "Total depth of the shared memory region for all remote peers. This field
  31                reflects the RMEM_TOTAL_DEPTH parameter value.";
  32
  33            sw = r;
  34            hw = r;
  35        } rmem_total_depth[31:0] = RMEM_TOTAL_DEPTH;
  36
  37        field {
  38            name = {INST_NAME, ".info.num_of_peers[42:32]"};
  39            desc = "Number of remote peers supported by this openENOC Endpoint Interface instance,
  40                from 0 to 2047. This field reflects the NUM_OF_PEERS parameter value.";
  41
  42            sw = r;
  43            hw = r;
  44        } num_of_peers[42:32] = NUM_OF_PEERS;
  45
  46        field {
  47            name = {INST_NAME, ".info.peer_dma_supported"};
  48            desc = "Indicates whether DMA transfers associated with configured remote peers are
  49                supported.";
  50
  51            sw = r;
  52            hw = r;
  53        } peer_dma_supported[43:43] = HAS_PEER_DMA;
  54
  55        field {
  56            name = {INST_NAME, ".info.non_oetp_dma_supported"};
  57            desc = "Indicates whether endpoint-level DMA transfers of complete non-oETP Ethernet
  58                frames are supported.";
  59
  60            sw = r;
  61            hw = r;
  62        } non_oetp_dma_supported[44:44] = HAS_NON_OETP_DMA;
  63
  64        field {
  65            name = {INST_NAME, ".info.direct_axis_supported"};
  66            desc = "Indicates whether direct CSR-driven AXI4-Stream access is supported for
  67                non-oETP Ethernet frames.";
  68
  69            sw = r;
  70            hw = r;
  71        } direct_axis_supported[45:45] = HAS_DIRECT_AXIS;
  72
  73        field {
  74            name = {INST_NAME, ".info.rmem_supported"};
  75            desc = "Indicates whether the transparent Remote Memory (RMEM) interface is supported.";
  76
  77            sw = r;
  78            hw = r;
  79        } rmem_supported[46:46] = HAS_RMEM;
  80
  81        field {
  82            name = {INST_NAME, ".info.irq_supported"};
  83            desc = "Indicates whether the endpoint interrupt output and interrupt-control logic
  84                are implemented.";
  85
  86            sw = r;
  87            hw = r;
  88        } irq_supported[47:47] = HAS_IRQ;
  89
  90        field {
  91            name = {INST_NAME, ".info.max_dma_frame_size_bytes[63:48]"};
  92            desc = "Synthesis-time maximum Ethernet frame size in bytes, excluding FCS. This
  93                field reflects the MAX_RAW_FRAME_SIZE parameter value and bounds raw
  94                non-oETP DMA frames. The peer DMA memory-fragment ceiling is
  95                4 * floor((MAX_RAW_FRAME_SIZE - 32) / 4), accounting for the Ethernet
  96                header, oETP write metadata, data-word padding and EndOfData. An 8192-byte
  97                frame limit permits 8160-byte memory fragments. A value of zero indicates
  98                that DMA is not supported.";
  99
 100            sw = r;
 101            hw = r;
 102        } max_dma_frame_size_bytes[63:48] = MAX_RAW_FRAME_SIZE;
 103    } info;
 104
 105    regfile conf {
 106        name = {INST_NAME, ".conf"};
 107        desc = "Configuration register file for this openENOC Endpoint Interface instance.";
 108
 109        reg mac_address {
 110            name = {INST_NAME, ".conf.mac_address"};
 111            desc = "Local endpoint 48-bit unicast MAC address. The oETP engine uses this address
 112                as its source MAC and compares individual-addressed incoming oETP frames against
 113                it. The I/G bit in the first MAC octet must be zero. Group-addressed oETP frames
 114                are compared against conf.multicast_address instead.";
 115
 116            regwidth = 64;
 117
 118            field {
 119                name = {INST_NAME, ".conf.mac_address.lo_word[31:0]"};
 120                desc = "Lower 32 bits [31:0] of the 48-bit MAC address.";
 121
 122                sw = rw;
 123                hw = r;
 124            } lo_word[31:0] = 0;
 125
 126            field {
 127                name = {INST_NAME, ".conf.mac_address.hi_word[47:32]"};
 128                desc = "Upper 16 bits [47:32] of the 48-bit MAC address.";
 129
 130                sw = rw;
 131                hw = r;
 132            } hi_word[47:32] = 0;
 133        } mac_address;
 134
 135        reg multicast_address {
 136            name = {INST_NAME, ".conf.multicast_address"};
 137            desc = "Local endpoint 48-bit multicast destination MAC address. For an incoming
 138                oETP frame whose destination I/G bit is one, the engine accepts the destination
 139                only when it exactly matches this address. All slave endpoints in a replication
 140                group use the same value. This address is not used as a source MAC. Zero is the
 141                reset value and matches no group-addressed destination, disabling oETP group
 142                reception. Broadcast is accepted only when this address is all ones. This field
 143                does not change the separate non-oETP receive-mode policy.";
 144
 145            regwidth = 64;
 146
 147            field {
 148                name = {INST_NAME, ".conf.multicast_address.lo_word[31:0]"};
 149                desc = "Lower 32 bits [31:0] of the 48-bit multicast MAC address.";
 150
 151                sw = rw;
 152                hw = r;
 153            } lo_word[31:0] = 0;
 154
 155            field {
 156                name = {INST_NAME, ".conf.multicast_address.hi_word[47:32]"};
 157                desc = "Upper 16 bits [47:32] of the 48-bit multicast MAC address.";
 158
 159                sw = rw;
 160                hw = r;
 161            } hi_word[47:32] = 0;
 162        } multicast_address;
 163
 164        reg non_oetp_control {
 165            name = {INST_NAME, ".conf.non_oetp_control"};
 166            desc = "Receive policy for Ethernet frames that do not carry oETP traffic.";
 167
 168            field {
 169                name = {INST_NAME, ".conf.non_oetp_control.receive_mode[1:0]"};
 170                desc = "
 171                    Receive mode for non-oETP Ethernet frames:<ul>
 172                    <li>0: Drop all non-oETP Ethernet frames.</li>
 173                    <li>1: Filtered mode. Accept frames addressed to the configured local MAC
 174                    address and Ethernet broadcast frames.</li>
 175                    <li>2: Multicast mode. Accept the same frames as filtered mode, plus all
 176                    multicast-addressed frames.</li>
 177                    <li>3: Promiscuous mode. Accept all non-oETP Ethernet frames.</li>
 178                    </ul>
 179                    An accepted frame is directed to the non-oETP RX DMA channel when that channel
 180                    is armed; otherwise it is directed to the CSR AXI4-Stream sink.
 181                ";
 182
 183                sw = rw;
 184                hw = r;
 185            } receive_mode[1:0] = 1;
 186        } non_oetp_control;
 187
 188        reg rmem_timeout {
 189            name = {INST_NAME, ".conf.rmem_timeout"};
 190            desc = "Timeout configuration for transparent RMEM operations.";
 191
 192            field {
 193                name = {INST_NAME, ".conf.rmem_timeout.cycles[31:0]"};
 194                desc = "Maximum wait for an RMEM response in endpoint clock cycles. Zero disables
 195                    the timeout and permits an indefinite response wait. Hardware samples this
 196                    value when it accepts an RMEM operation; subsequent writes apply to later
 197                    operations. The response timer starts after the complete request frame has
 198                    been accepted by the Ethernet-facing transmit stream and runs until the
 199                    complete matching response is received and validated through Ethernet TLAST.
 200                    RX backpressure counts toward the timeout; remaining local memory completion
 201                    does not. A valid response completing on the expiry edge takes priority,
 202                    including a valid ERROR_RSP with its reported cause. Hardware does not
 203                    retry; timeout handling and retry policy belong to software. Multicast
 204                    writes do not wait for a response and do not use this response timeout.
 205                    An RMEM read timeout terminates the access with all-ones read data and read
 206                    ACK; a write timeout terminates with write ACK. The external-RMEM boundary
 207                    uses no ERR signals. Failures set error and error_code in the associated
 208                    peers.entry[].dma register and may generate a separate RMEM_ERROR IRQ event.";
 209
 210                sw = rw;
 211                hw = r;
 212            } cycles[31:0] = 0;
 213        } rmem_timeout;
 214
 215        reg dma_timeout {
 216            name = {INST_NAME, ".conf.dma_timeout"};
 217            desc = "Common response timeout for locally initiated unicast peer DMA fragments.";
 218
 219            field {
 220                name = {INST_NAME, ".conf.dma_timeout.cycles[31:0]"};
 221                desc = "Maximum wait for a response to one peer DMA fragment, in endpoint clock
 222                    cycles. This value is shared by all peers. Zero disables the timeout and
 223                    permits an indefinite response wait. Hardware samples this value when the
 224                    oETP engine accepts the fragment request; later writes apply to later
 225                    fragments. The response timer starts after the complete request frame has
 226                    been accepted by the Ethernet-facing transmit stream and runs until the
 227                    complete matching response is received and validated through Ethernet TLAST,
 228                    including EndOfData where present. RX backpressure counts toward the timeout;
 229                    remaining local memory completion does not. A valid response completing on
 230                    the expiry edge takes priority, including a valid ERROR_RSP with its reported
 231                    cause. On expiry, hardware
 232                    aborts the remaining fragments of that DMA transfer and reports error code
 233                    8 (timeout) in the peer DMA status. Hardware does not retry; software decides
 234                    whether to start another transfer. This timeout does not apply to multicast
 235                    writes, non-oETP DMA, or transparent RMEM operations.";
 236
 237                sw = rw;
 238                hw = r;
 239            } cycles[31:0] = 0;
 240        } dma_timeout;
 241
 242        reg dma_max_fragment_size {
 243            name = {INST_NAME, ".conf.dma_max_fragment_size"};
 244            desc = "Common software-selected maximum memory fragment size for locally
 245                initiated peer DMA transfers. The synthesis-time frame limit remains fixed.";
 246
 247            field {
 248                name = {INST_NAME, ".conf.dma_max_fragment_size.bytes[31:0]"};
 249                desc = "Maximum meaningful memory bytes in one locally initiated DMA fragment,
 250                    excluding Ethernet/oETP headers, word padding, EndOfData and FCS. The
 251                    hardware rounds the written value down to a multiple of four before
 252                    validating it. The effective MFS must be at least four bytes and at most
 253                    4 * floor((MAX_RAW_FRAME_SIZE - 32) / 4). With an 8192-byte frame ceiling,
 254                    the effective range is 4 through 8160 bytes in steps of four, with reset
 255                    value 8160. For example, 31 selects 28, 7 selects 4, and 8161 through 8163
 256                    select 8160. Hardware snapshots the effective value when it accepts the
 257                    whole peer DMA transfer; later writes apply only to subsequent transfers.
 258                    The final fragment uses the exact remaining byte length and may be shorter
 259                    than four bytes. A written value of 0 through 3, or a rounded value above
 260                    the synthesized ceiling, rejects a new transfer with local error code 1
 261                    before issuing any memory or protocol operation. The CSR retains the
 262                    unrounded written value. This setting does not
 263                    restrict received peer requests, which use the synthesized fragment
 264                    ceiling, and does not affect RMEM, direct CSR streams or non-oETP DMA.";
 265
 266                sw = rw;
 267                hw = r;
 268            } bytes[31:0] = (MAX_RAW_FRAME_SIZE > 32) ?
 269                ((MAX_RAW_FRAME_SIZE - 32) / 4) * 4 : 0;
 270        } dma_max_fragment_size;
 271    } conf;
 272
 273    regfile axis_if {
 274        name = {INST_NAME, ".axis_if"};
 275        desc = "Register file for the AXI4-Stream source and sink interfaces.";
 276
 277        regfile source {
 278            name = {INST_NAME, ".axis_if.source"};
 279            desc = "Register file for the AXI4-Stream source interface.";
 280
 281            reg data {
 282                name = {INST_NAME, ".axis_if.source.data"};
 283                desc = "Data register for the AXI4-Stream source interface.";
 284
 285                field {
 286                    name = {INST_NAME, ".axis_if.source.data.tdata[31:0]"};
 287                    desc = "32-bit data value for the AXI4-Stream source interface.";
 288
 289                    sw = rw;
 290                    hw = r;
 291                } tdata[31:0] = 0;
 292            } data;
 293
 294            reg control {
 295                name = {INST_NAME, ".axis_if.source.control"};
 296                desc = "Control register for the AXI4-Stream source interface.";
 297
 298                field {
 299                    name = {INST_NAME, ".axis_if.source.control.tvalid"};
 300                    desc = "Indicates that the AXI4-Stream source interface has valid data to send.
 301                        Once asserted by software, the field remains asserted until the transfer is
 302                        accepted by the destination.";
 303
 304                    sw = rw;
 305                    hw = r;
 306                    hwclr = true;
 307                } tvalid[0:0] = 0;
 308
 309                field {
 310                    name = {INST_NAME, ".axis_if.source.control.tlast"};
 311                    desc = "Indicates the last data word of a frame on the AXI4-Stream source
 312                        interface.";
 313
 314                    sw = rw;
 315                    hw = r;
 316                } tlast[8:8] = 0;
 317
 318                field {
 319                    name = {INST_NAME, ".axis_if.source.control.tkeep[3:0]"};
 320                    desc = "Indicates which byte lanes contain valid data on the AXI4-Stream source
 321                        interface.";
 322
 323                    sw = rw;
 324                    hw = r;
 325                } tkeep[19:16] = 0;
 326            } control;
 327
 328            reg status {
 329                name = {INST_NAME, ".axis_if.source.status"};
 330                desc = "Status register for the AXI4-Stream source interface.";
 331
 332                field {
 333                    name = {INST_NAME, ".axis_if.source.status.tready"};
 334                    desc = "Registered readiness at the input of the direct TX AXI4-Stream
 335                        register slice. With the non_oetp_direct_tx_complete event disabled,
 336                        follows downstream backpressure through the slice's registered READY
 337                        path; CSR acknowledgements and frame boundaries do not force it low.
 338                        With the event enabled, additionally requires frame admission, which
 339                        reserves space for the TX completion event. Acceptance of the final
 340                        word (tlast) into the slice deasserts this field until the next frame
 341                        receives admission. Writing the first word and setting
 342                        source.control.tvalid requests admission even while this field is low.
 343                        The active frame retains its admission decision if event_enable changes.
 344                        Software must retain data, KEEP and LAST until source.control.tvalid
 345                        is cleared by hardware, independently of this readiness status.";
 346
 347                    sw = r;
 348                    hw = w;
 349                } tready[0:0] = 0;
 350            } status;
 351        } source;
 352
 353        regfile sink {
 354            name = {INST_NAME, ".axis_if.sink"};
 355            desc = "Register file for the AXI4-Stream sink interface.";
 356
 357            reg data {
 358                name = {INST_NAME, ".axis_if.sink.data"};
 359                desc = "Data register for the AXI4-Stream sink interface.";
 360
 361                field {
 362                    name = {INST_NAME, ".axis_if.sink.data.tdata[31:0]"};
 363                    desc = "32-bit data value for the AXI4-Stream sink interface.";
 364
 365                    sw = r;
 366                    hw = w;
 367                } tdata[31:0];
 368            } data;
 369
 370            reg control {
 371                name = {INST_NAME, ".axis_if.sink.control"};
 372                desc = "Control register for the AXI4-Stream sink interface.";
 373
 374                field {
 375                    name = {INST_NAME, ".axis_if.sink.control.tready"};
 376                    desc = "Indicates that the AXI4-Stream sink interface is ready to accept a
 377                        data transfer. Once asserted by software, the field remains asserted until
 378                        a transfer occurs.";
 379
 380                    sw = rw;
 381                    hw = r;
 382                    hwclr = true;
 383                } tready[0:0] = 0;
 384            } control;
 385
 386            reg status {
 387                name = {INST_NAME, ".axis_if.sink.status"};
 388                desc = "Status register for the AXI4-Stream sink interface.";
 389
 390                field {
 391                    name = {INST_NAME, ".axis_if.sink.status.tvalid"};
 392                    desc = "Indicates that the AXI4-Stream sink interface has valid data
 393                        to receive.";
 394
 395                    sw = r;
 396                    hw = w;
 397                } tvalid[0:0];
 398
 399                field {
 400                    name = {INST_NAME, ".axis_if.sink.status.tlast"};
 401                    desc = "Indicates the last data word of a frame on the AXI4-Stream
 402                        sink interface.";
 403
 404                    sw = r;
 405                    hw = w;
 406                } tlast[8:8];
 407
 408                field {
 409                    name = {INST_NAME, ".axis_if.sink.status.tkeep[3:0]"};
 410                    desc = "Indicates which byte lanes contain valid data on the AXI4-Stream
 411                        sink interface.";
 412
 413                    sw = r;
 414                    hw = w;
 415                } tkeep[19:16];
 416            } status;
 417        } sink;
 418    } axis_if;
 419
 420    regfile non_oetp_dma {
 421        name = {INST_NAME, ".non_oetp_dma"};
 422        desc = "Endpoint-level DMA control and status for complete non-oETP Ethernet frames.
 423            Frame data includes the Ethernet header and payload, but excludes the preamble,
 424            Start Frame Delimiter (SFD), and Frame Check Sequence (FCS).";
 425
 426        regfile tx {
 427            name = {INST_NAME, ".non_oetp_dma.tx"};
 428            desc = "Transmit DMA channel for complete non-oETP Ethernet frames.";
 429
 430            reg buffer_address {
 431                name = {INST_NAME, ".non_oetp_dma.tx.buffer_address"};
 432                desc = "Local memory address of the non-oETP Ethernet frame to transmit.";
 433
 434                field {
 435                    name = {INST_NAME, ".non_oetp_dma.tx.buffer_address.base[31:0]"};
 436                    desc = "32-bit byte address of the first byte of the transmit buffer.";
 437
 438                    sw = rw;
 439                    hw = r;
 440                } base[31:0] = 0;
 441            } buffer_address;
 442
 443            reg frame_length {
 444                name = {INST_NAME, ".non_oetp_dma.tx.frame_length"};
 445                desc = "Length of the complete non-oETP Ethernet frame to transmit.";
 446
 447                field {
 448                    name = {INST_NAME, ".non_oetp_dma.tx.frame_length.bytes[31:0]"};
 449                    desc = "Frame length in bytes. Valid non-zero values shall not exceed
 450                        info.max_dma_frame_size_bytes.";
 451
 452                    sw = rw;
 453                    hw = r;
 454                } bytes[31:0] = 0;
 455            } frame_length;
 456
 457            reg command_status {
 458                name = {INST_NAME, ".non_oetp_dma.tx.command_status"};
 459                desc = "Command and completion status for the non-oETP transmit DMA channel.";
 460
 461                field {
 462                    name = {INST_NAME, ".non_oetp_dma.tx.command_status.request"};
 463                    desc = "Writing one requests transmission of the configured frame. The field
 464                        remains asserted until the DMA engine accepts the request. Hardware clears
 465                        it upon acceptance; while the channel is busy, a newly asserted request
 466                        remains pending. Software or an RTL controller shall read the completion
 467                        status and transferred length of the previous request before asserting this
 468                        field for the next request.";
 469
 470                    sw = rw;
 471                    hw = r;
 472                    hwclr = true;
 473                } request[8:8] = 0;
 474
 475                field {
 476                    name = {INST_NAME, ".non_oetp_dma.tx.command_status.clear_errors"};
 477                    desc = "Writing one clears this channel's error flag and error code.
 478                        Hardware clears the command after accepting it. The command does not
 479                        abort an active transfer, clear done or transferred length, or complete
 480                        an IRQ claim. A new failure takes precedence over a simultaneous clear.
 481                        Starting or successfully completing a transfer preserves a recorded error.";
 482
 483                    sw = rw;
 484                    hw = r;
 485                    hwclr = true;
 486                } clear_errors[9:9] = 0;
 487
 488                field {
 489                    name = {INST_NAME, ".non_oetp_dma.tx.command_status.idle"};
 490                    desc = "Indicates that the channel has no accepted transfer in progress.
 491                        Hardware deasserts this field when a request is accepted and asserts it
 492                        when the transfer completes.";
 493
 494                    sw = r;
 495                    hw = w;
 496                } idle[16:16];
 497
 498                field {
 499                    name = {INST_NAME, ".non_oetp_dma.tx.command_status.done"};
 500                    desc = "Sticky successful-completion flag. Hardware sets this field after the
 501                        accepted transfer completes successfully and clears it when the next request
 502                        is accepted.";
 503
 504                    sw = r;
 505                    hw = w;
 506                } done[24:24];
 507
 508                field {
 509                    name = {INST_NAME, ".non_oetp_dma.tx.command_status.error"};
 510                    desc = "Sticky error-completion flag. Hardware sets this field when an
 511                        accepted transfer fails. Only command_status.clear_errors or endpoint
 512                        reset clears it; starting or successfully completing another transfer
 513                        preserves a recorded error.";
 514
 515                    sw = r;
 516                    hw = w;
 517                } error[25:25];
 518
 519                field {
 520                    name = {INST_NAME, ".non_oetp_dma.tx.command_status.error_code[31:28]"};
 521                    desc = "
 522                        Sticky error code for the most recent transmit failure:<ul>
 523                        <li>0: No error.</li>
 524                        <li>1: Invalid DMA configuration or descriptor.</li>
 525                        <li>2: AXI4-Stream length or TLAST error.</li>
 526                        <li>3: Frame exceeds the supported size or configured buffer capacity.</li>
 527                        <li>4: AXI read SLVERR response.</li>
 528                        <li>5: AXI read DECERR response.</li>
 529                        <li>6: AXI write SLVERR response.</li>
 530                        <li>7: AXI write DECERR response.</li>
 531                        <li>8-15: Reserved.</li>
 532                        </ul>
 533                        These errors describe local non-oETP DMA work. Only
 534                        command_status.clear_errors or endpoint reset clears this field.
 535                        A new failure replaces the code and takes precedence over a simultaneous
 536                        clear; successful transfers preserve the previous failure.
 537                    ";
 538
 539                    sw = r;
 540                    hw = w;
 541                } error_code[31:28];
 542            } command_status;
 543
 544            reg transferred_length {
 545                name = {INST_NAME, ".non_oetp_dma.tx.transferred_length"};
 546                desc = "Number of bytes transferred for the most recently accepted transmit
 547                    request.";
 548
 549                field {
 550                    name = {INST_NAME, ".non_oetp_dma.tx.transferred_length.bytes[31:0]"};
 551                    desc = "Actual number of bytes transferred. Hardware clears this field when
 552                        the next request is accepted.";
 553
 554                    sw = r;
 555                    hw = w;
 556                } bytes[31:0];
 557            } transferred_length;
 558        } tx;
 559
 560        regfile rx {
 561            name = {INST_NAME, ".non_oetp_dma.rx"};
 562            desc = "Receive DMA channel for complete non-oETP Ethernet frames.";
 563
 564            reg buffer_address {
 565                name = {INST_NAME, ".non_oetp_dma.rx.buffer_address"};
 566                desc = "Local memory address of the receive buffer for a non-oETP Ethernet frame.";
 567
 568                field {
 569                    name = {INST_NAME, ".non_oetp_dma.rx.buffer_address.base[31:0]"};
 570                    desc = "32-bit byte address of the first byte of the receive buffer.";
 571
 572                    sw = rw;
 573                    hw = r;
 574                } base[31:0] = 0;
 575            } buffer_address;
 576
 577            reg buffer_capacity {
 578                name = {INST_NAME, ".non_oetp_dma.rx.buffer_capacity"};
 579                desc = "Capacity of the receive buffer for one complete non-oETP Ethernet frame.";
 580
 581                field {
 582                    name = {INST_NAME, ".non_oetp_dma.rx.buffer_capacity.bytes[31:0]"};
 583                    desc = "Receive-buffer capacity in bytes. Valid non-zero values shall not exceed
 584                        info.max_dma_frame_size_bytes.";
 585
 586                    sw = rw;
 587                    hw = r;
 588                } bytes[31:0] = 0;
 589            } buffer_capacity;
 590
 591            reg command_status {
 592                name = {INST_NAME, ".non_oetp_dma.rx.command_status"};
 593                desc = "Command and completion status for the non-oETP receive DMA channel.";
 594
 595                field {
 596                    name = {INST_NAME, ".non_oetp_dma.rx.command_status.request"};
 597                    desc = "Writing one arms reception into the configured buffer. The field remains
 598                        asserted until the DMA engine accepts the request. Hardware clears it upon
 599                        acceptance; while the channel is busy, a newly asserted request remains
 600                        pending. Software or an RTL controller shall read the completion status and
 601                        received length of the previous request before asserting this field for the
 602                        next request.";
 603
 604                    sw = rw;
 605                    hw = r;
 606                    hwclr = true;
 607                } request[8:8] = 0;
 608
 609                field {
 610                    name = {INST_NAME, ".non_oetp_dma.rx.command_status.clear_errors"};
 611                    desc = "Writing one clears this channel's error flag and error code.
 612                        Hardware clears the command after accepting it. The command does not
 613                        abort an active or armed receive, clear done or received length, or
 614                        complete an IRQ claim. A new failure takes precedence over a simultaneous
 615                        clear. Starting or successfully completing a transfer preserves a
 616                        recorded error.";
 617
 618                    sw = rw;
 619                    hw = r;
 620                    hwclr = true;
 621                } clear_errors[9:9] = 0;
 622
 623                field {
 624                    name = {INST_NAME, ".non_oetp_dma.rx.command_status.idle"};
 625                    desc = "Indicates that the channel has no accepted receive request in progress.
 626                        After acceptance, the channel may be armed and waiting for an eligible
 627                        non-oETP frame or may be writing a received frame to memory.";
 628
 629                    sw = r;
 630                    hw = w;
 631                } idle[16:16];
 632
 633                field {
 634                    name = {INST_NAME, ".non_oetp_dma.rx.command_status.armed"};
 635                    desc = "Indicates that the accepted receive request is waiting for an eligible
 636                        non-oETP Ethernet frame. Hardware sets this field when it accepts a request
 637                        and clears it when the first beat of the selected frame is accepted by the
 638                        RX DMA datapath. The frame-routing logic uses this field to select the
 639                        RX DMA path; otherwise an accepted non-oETP frame is directed to the
 640                        CSR AXI4-Stream sink.";
 641
 642                    sw = r;
 643                    hw = w;
 644                } armed[17:17];
 645
 646                field {
 647                    name = {INST_NAME, ".non_oetp_dma.rx.command_status.done"};
 648                    desc = "Sticky successful-completion flag. Hardware sets this field after a
 649                        received frame has been written successfully and clears it when the next
 650                        request is accepted.";
 651
 652                    sw = r;
 653                    hw = w;
 654                } done[24:24];
 655
 656                field {
 657                    name = {INST_NAME, ".non_oetp_dma.rx.command_status.error"};
 658                    desc = "Sticky error-completion flag. Hardware sets this field when an
 659                        accepted receive fails. Only command_status.clear_errors or endpoint
 660                        reset clears it; starting or successfully completing another receive
 661                        preserves a recorded error.";
 662
 663                    sw = r;
 664                    hw = w;
 665                } error[25:25];
 666
 667                field {
 668                    name = {INST_NAME, ".non_oetp_dma.rx.command_status.error_code[31:28]"};
 669                    desc = "
 670                        Sticky error code for the most recent receive failure:<ul>
 671                        <li>0: No error.</li>
 672                        <li>1: Invalid DMA configuration or descriptor.</li>
 673                        <li>2: AXI4-Stream length or TLAST error.</li>
 674                        <li>3: Received frame exceeds the configured buffer capacity.</li>
 675                        <li>4: AXI read SLVERR response.</li>
 676                        <li>5: AXI read DECERR response.</li>
 677                        <li>6: AXI write SLVERR response.</li>
 678                        <li>7: AXI write DECERR response.</li>
 679                        <li>8-15: Reserved.</li>
 680                        </ul>
 681                        Only command_status.clear_errors or endpoint reset clears this field.
 682                        A new failure replaces the code and takes precedence over a simultaneous
 683                        clear; successful transfers preserve the previous failure.
 684                    ";
 685
 686                    sw = r;
 687                    hw = w;
 688                } error_code[31:28];
 689            } command_status;
 690
 691            reg received_length {
 692                name = {INST_NAME, ".non_oetp_dma.rx.received_length"};
 693                desc = "Length of the most recently received non-oETP Ethernet frame.";
 694
 695                field {
 696                    name = {INST_NAME, ".non_oetp_dma.rx.received_length.bytes[31:0]"};
 697                    desc = "Actual number of frame bytes written to the receive buffer. Hardware
 698                        clears this field when the next request is accepted.";
 699
 700                    sw = r;
 701                    hw = w;
 702                } bytes[31:0];
 703            } received_length;
 704        } rx;
 705    } non_oetp_dma;
 706
 707    regfile irq {
 708        name = {INST_NAME, ".irq"};
 709        desc = "Endpoint-level interrupt control and claim interface. Interrupt events from peer
 710            DMA, non-oETP DMA, direct AXI4-Stream transfers, and RMEM failures are serialized
 711            through a shared event FIFO.";
 712
 713        reg control {
 714            name = {INST_NAME, ".irq.control"};
 715            desc = "Global interrupt-output control and interrupt-controller maintenance requests.";
 716
 717            field {
 718                name = {INST_NAME, ".irq.control.global_enable"};
 719                desc = "Enables the physical endpoint IRQ output. Clearing this field masks the
 720                    output but does not prevent enabled events from being queued. Event capture is
 721                    controlled by irq.event_enable and, for peer DMA, by the selected peer's
 722                    dma.irq_enable field.";
 723
 724                sw = rw;
 725                hw = r;
 726            } global_enable[0:0] = 0;
 727
 728            field {
 729                name = {INST_NAME, ".irq.control.clear_errors"};
 730                desc = "Writing one requests clearing of the sticky irq.status.overflow and
 731                    irq.status.invalid_complete flags. The field remains asserted until hardware
 732                    accepts the request and clears it.";
 733
 734                sw = rw;
 735                hw = r;
 736                hwclr = true;
 737            } clear_errors[8:8] = 0;
 738        } control;
 739
 740        reg event_enable {
 741            name = {INST_NAME, ".irq.event_enable"};
 742            desc = "Enables generation of individual endpoint IRQ event classes. These fields
 743                control event capture; irq.control.global_enable only masks the physical
 744                IRQ output.";
 745
 746            field {
 747                name = {INST_NAME, ".irq.event_enable.peer_dma_complete"};
 748                desc = "Enables PEER_DMA_COMPLETE events. A peer event is queued only when this
 749                    field and the selected peer's dma.irq_enable field were both set when the DMA
 750                    request was accepted. A failed incoming bulk DMA request also generates this
 751                    event for the peer resolved from the source MAC. For incoming failures,
 752                    hardware captures the per-peer enable when recording the failure and samples
 753                    this field at IRQ admission. Successful incoming requests generate no event.
 754                    CSR error recording and the error response do not wait for IRQ FIFO capacity.";
 755
 756                sw = rw;
 757                hw = r;
 758            } peer_dma_complete[0:0] = 0;
 759
 760            field {
 761                name = {INST_NAME, ".irq.event_enable.non_oetp_dma_tx_complete"};
 762                desc = "Enables NON_OETP_DMA_TX_COMPLETE events. Hardware samples this field when
 763                    it accepts a non-oETP transmit DMA request.";
 764
 765                sw = rw;
 766                hw = r;
 767            } non_oetp_dma_tx_complete[1:1] = 0;
 768
 769            field {
 770                name = {INST_NAME, ".irq.event_enable.non_oetp_dma_rx_complete"};
 771                desc = "Enables NON_OETP_DMA_RX_COMPLETE events. Hardware samples this field when
 772                    it accepts a non-oETP receive DMA request.";
 773
 774                sw = rw;
 775                hw = r;
 776            } non_oetp_dma_rx_complete[2:2] = 0;
 777
 778            field {
 779                name = {INST_NAME, ".irq.event_enable.non_oetp_direct_tx_complete"};
 780                desc = "Enables NON_OETP_DIRECT_TX_COMPLETE events. Hardware samples this field when
 781                    the first beat of a direct transmit frame is accepted from the CSR-facing
 782                    AXI4-Stream interface. When enabled, the frame start is accepted only after an
 783                    IRQ FIFO credit has been reserved. The event is generated when the final beat
 784                    is accepted by the oETP engine.";
 785
 786                sw = rw;
 787                hw = r;
 788            } non_oetp_direct_tx_complete[3:3] = 0;
 789
 790            field {
 791                name = {INST_NAME, ".irq.event_enable.non_oetp_direct_rx_available"};
 792                desc = "Enables NON_OETP_DIRECT_RX_AVAILABLE events. One event is generated when
 793                    the first beat of a new direct receive frame becomes valid on the CSR-facing
 794                    AXI4-Stream interface. When enabled, routing logic does not expose that first
 795                    TVALID until an IRQ FIFO credit is available, so the event cannot be lost.
 796                    The event does not depend on TLAST and therefore supports both cut-through and
 797                    frame-FIFO operation.";
 798
 799                sw = rw;
 800                hw = r;
 801            } non_oetp_direct_rx_available[4:4] = 0;
 802
 803            field {
 804                name = {INST_NAME, ".irq.event_enable.rmem_error"};
 805                desc = "Enables RMEM_ERROR events for locally initiated RMEM failures, including
 806                    timeout and an accepted ERROR_RSP, and failed incoming RMEM requests from a
 807                    resolved peer. Hardware records the associated peer's
 808                    dma.error and dma.error_code before
 809                    exposing the event and terminates the failed RMEM access without waiting
 810                    for IRQ FIFO capacity. For locally initiated failures the enable is sampled
 811                    when the failure is recorded; incoming failures sample it at IRQ admission.
 812                    The bulk DMA per-peer irq_enable does not gate RMEM_ERROR.
 813                    Successful RMEM accesses generate no event.";
 814
 815                sw = rw;
 816                hw = r;
 817            } rmem_error[5:5] = 0;
 818        } event_enable;
 819
 820        reg status {
 821            name = {INST_NAME, ".irq.status"};
 822            desc = "Status of the endpoint IRQ event FIFO and its reservation mechanism.";
 823
 824            field {
 825                name = {INST_NAME, ".irq.status.claim_pending"};
 826                desc = "Indicates that at least one valid event is available in irq.claim.";
 827
 828                sw = r;
 829                hw = w;
 830            } claim_pending[0:0];
 831
 832            field {
 833                name = {INST_NAME, ".irq.status.credit_full"};
 834                desc = "Indicates that all event FIFO credits are occupied by queued claims or
 835                    reserved for admitted operations. While no credit is available, new
 836                    interrupt-enabled DMA requests are not accepted and the start of an
 837                    interrupt-enabled direct AXI4-Stream frame is backpressured.";
 838
 839                sw = r;
 840                hw = w;
 841            } credit_full[1:1];
 842
 843            field {
 844                name = {INST_NAME, ".irq.status.overflow"};
 845                desc = "Sticky internal-error flag indicating that an enabled event could not be
 846                    retained. Correct credit reservation, admission control, and AXI4-Stream
 847                    backpressure make this condition unreachable during normal operation. Clear with
 848                    irq.control.clear_errors.";
 849
 850                sw = r;
 851                hw = w;
 852            } overflow[2:2];
 853
 854            field {
 855                name = {INST_NAME, ".irq.status.invalid_complete"};
 856                desc = "Sticky protocol-error flag indicating that irq.complete.valid was accepted
 857                    while no claim was pending or that the completion token did not match the
 858                    current claim. No claim is removed on a mismatch. Clear with
 859                    irq.control.clear_errors.";
 860
 861                sw = r;
 862                hw = w;
 863            } invalid_complete[3:3];
 864
 865            field {
 866                name = {INST_NAME, ".irq.status.irq_asserted"};
 867                desc = "Reflects the current value of the physical endpoint IRQ output after
 868                    application of irq.control.global_enable.";
 869
 870                sw = r;
 871                hw = w;
 872            } irq_asserted[4:4];
 873
 874            field {
 875                name = {INST_NAME, ".irq.status.fifo_level[7:0]"};
 876                desc = "Number of valid claims currently queued in the IRQ event FIFO. Values
 877                    greater than 255 are reported as 255.";
 878
 879                sw = r;
 880                hw = w;
 881            } fifo_level[15:8];
 882
 883            field {
 884                name = {INST_NAME, ".irq.status.reserved_count[7:0]"};
 885                desc = "Number of event FIFO credits reserved for admitted DMA operations or direct
 886                    transmit frames whose events have not yet been queued. Values greater than 255
 887                    are reported as 255.";
 888
 889                sw = r;
 890                hw = w;
 891            } reserved_count[23:16];
 892        } status;
 893
 894        reg claim {
 895            name = {INST_NAME, ".irq.claim"};
 896            desc = "Read-only view of the event at the head of the IRQ event FIFO. Reading this
 897                register has no side effect, and all fields remain stable until a matching
 898                irq.complete request removes the claim.";
 899
 900            field {
 901                name = {INST_NAME, ".irq.claim.peer_idx[10:0]"};
 902                desc = "Zero-based peer index for a PEER_DMA_COMPLETE or RMEM_ERROR event, in the range 0 through
 903                    NUM_OF_PEERS-1. The field is not applicable to other event sources and is driven
 904                    to zero for deterministic readback.";
 905
 906                sw = r;
 907                hw = w;
 908            } peer_idx[10:0];
 909
 910            field {
 911                name = {INST_NAME, ".irq.claim.source[3:0]"};
 912                desc = "
 913                    IRQ event source:<ul>
 914                    <li>0: PEER_DMA_COMPLETE. A peer DMA request completed with either success or
 915                    error; peer_idx identifies the peer.</li>
 916                    <li>1: NON_OETP_DMA_TX_COMPLETE. A non-oETP transmit DMA request completed with
 917                    either success or error.</li>
 918                    <li>2: NON_OETP_DMA_RX_COMPLETE. A non-oETP receive DMA request completed with
 919                    either success or error.</li>
 920                    <li>3: NON_OETP_DIRECT_TX_COMPLETE. The final beat of a direct non-oETP transmit
 921                    frame was accepted by the oETP engine.</li>
 922                    <li>4: NON_OETP_DIRECT_RX_AVAILABLE. The first beat of a new direct non-oETP
 923                    receive frame is available on the CSR-facing AXI4-Stream interface.</li>
 924                    <li>5: RMEM_ERROR. A locally initiated RMEM operation failed. The cause is
 925                    recorded in peers.entry[peer_idx].dma; peer_idx identifies the associated peer.</li>
 926                    <li>6-15: Reserved.</li>
 927                    </ul>
 928                    This field is meaningful only when valid is set.
 929                ";
 930
 931                sw = r;
 932                hw = w;
 933            } source[14:11];
 934
 935            field {
 936                name = {INST_NAME, ".irq.claim.seq[15:0]"};
 937                desc = "Monotonically increasing event sequence number, modulo 65536. The sequence
 938                    number distinguishes otherwise identical claims and protects against stale or
 939                    repeated completion requests.";
 940
 941                sw = r;
 942                hw = w;
 943            } seq[30:15];
 944
 945            field {
 946                name = {INST_NAME, ".irq.claim.valid"};
 947                desc = "Indicates that this register contains the valid event at the head of the
 948                    IRQ event FIFO. When clear, all other claim fields shall be ignored.";
 949
 950                sw = r;
 951                hw = w;
 952            } valid[31:31];
 953        } claim;
 954
 955        reg complete {
 956            name = {INST_NAME, ".irq.complete"};
 957            desc = "Completion request for the current IRQ claim. Software acknowledges an event by
 958                copying the complete 32-bit irq.claim value into this register.";
 959
 960            field {
 961                name = {INST_NAME, ".irq.complete.peer_idx[10:0]"};
 962                desc = "Peer-index portion of the claim token.";
 963
 964                sw = rw;
 965                hw = r;
 966            } peer_idx[10:0] = 0;
 967
 968            field {
 969                name = {INST_NAME, ".irq.complete.source[3:0]"};
 970                desc = "Event-source portion of the claim token.";
 971
 972                sw = rw;
 973                hw = r;
 974            } source[14:11] = 0;
 975
 976            field {
 977                name = {INST_NAME, ".irq.complete.seq[15:0]"};
 978                desc = "Sequence-number portion of the claim token.";
 979
 980                sw = rw;
 981                hw = r;
 982            } seq[30:15] = 0;
 983
 984            field {
 985                name = {INST_NAME, ".irq.complete.valid"};
 986                desc = "Writing one submits the completion token. The field remains asserted until
 987                    hardware validates the token and clears it. A matching token removes the current
 988                    claim and releases its FIFO credit; an invalid token leaves the claim unchanged
 989                    and sets irq.status.invalid_complete.";
 990
 991                sw = rw;
 992                hw = r;
 993                hwclr = true;
 994            } valid[31:31] = 0;
 995        } complete;
 996    } irq;
 997
 998    regfile peers {
 999        name = {INST_NAME, ".peers"};
1000        desc = "Register file for remote peer configuration and memory region information.";
1001
1002        regfile entry {
1003            name = {INST_NAME, ".peers.entry[0..NUM_OF_PEERS-1]"};
1004            desc = "Register file for a single remote peer configuration and memory region
1005                information.";
1006
1007            reg mac_address {
1008                name = {INST_NAME, ".peers.entry[0..NUM_OF_PEERS-1].mac_address"};
1009                desc = "Remote peer 48-bit destination MAC address.";
1010
1011                regwidth = 64;
1012
1013                field {
1014                    name = {INST_NAME, ".peers.entry[0..NUM_OF_PEERS-1].mac_address.lo_word[31:0]"};
1015                    desc = "Lower 32 bits [31:0] of the 48-bit MAC address.";
1016
1017                    sw = rw;
1018                    hw = r;
1019                } lo_word[31:0];
1020
1021                field {
1022                    name = {INST_NAME, ".peers.entry[0..NUM_OF_PEERS-1].mac_address.hi_word[47:32]"};
1023                    desc = "Upper 16 bits [47:32] of the 48-bit MAC address.";
1024
1025                    sw = rw;
1026                    hw = r;
1027                } hi_word[47:32];
1028            } mac_address;
1029
1030            reg rmem_address {
1031                name = {INST_NAME, ".peers.entry[0..NUM_OF_PEERS-1].rmem_address"};
1032                desc = "Address offset of the virtual memory region corresponding to the remote
1033                    peer's memory.";
1034
1035                field {
1036                    name = {INST_NAME, ".peers.entry[0..NUM_OF_PEERS-1].rmem_address.offset[31:0]"};
1037                    desc = "32-bit byte offset of the virtual memory region corresponding to the
1038                        remote peer's memory. The value shall be aligned to a 32-bit word boundary.";
1039
1040                    sw = rw;
1041                    hw = r;
1042                } offset[31:0];
1043            } rmem_address;
1044
1045            reg local_address {
1046                name = {INST_NAME, ".peers.entry[0..NUM_OF_PEERS-1].local_address"};
1047                desc = "Start address of the local memory region for DMA transfers.";
1048
1049                field {
1050                    name = {INST_NAME, ".peers.entry[0..NUM_OF_PEERS-1].local_address.base[31:0]"};
1051                    desc = "Word-aligned 32-bit start address of the local memory region for
1052                        DMA transfers.";
1053
1054                    sw = rw;
1055                    hw = r;
1056                } base[31:0];
1057            } local_address;
1058
1059            reg remote_address {
1060                name = {INST_NAME, ".peers.entry[0..NUM_OF_PEERS-1].remote_address"};
1061                desc = "Start address of the remote peer's memory region.";
1062
1063                field {
1064                    name = {INST_NAME, ".peers.entry[0..NUM_OF_PEERS-1].remote_address.base[31:0]"};
1065                    desc = "Word-aligned 32-bit start address of the remote peer's memory region.";
1066
1067                    sw = rw;
1068                    hw = r;
1069                } base[31:0];
1070            } remote_address;
1071
1072            reg size {
1073                name = {INST_NAME, ".peers.entry[0..NUM_OF_PEERS-1].size"};
1074                desc = "Size of the remote peer's memory region.";
1075
1076                field {
1077                    name = {INST_NAME, ".peers.entry[0..NUM_OF_PEERS-1].size.bytes[31:0]"};
1078                    desc = "32-bit size of the remote peer's memory region in bytes.";
1079
1080                    sw = rw;
1081                    hw = r;
1082                } bytes[31:0];
1083            } size;
1084
1085            reg dma {
1086                name = {INST_NAME, ".peers.entry[0..NUM_OF_PEERS-1].dma"};
1087                desc = "DMA/RMEM configuration and control for the remote peer, with shared
1088                    sticky error reporting. RMEM and bulk DMA retain separate IRQ event classes.";
1089
1090                field {
1091                    name = {INST_NAME, ".peers.entry[0..NUM_OF_PEERS-1].dma.mode[1:0]"};
1092                    desc = "
1093                        DMA mode and responder access policy for the remote peer:<ul>
1094
1095                        <li>0: Disabled. Locally initiated DMA and transparent RMEM
1096                        operations are disabled, and incoming oETP memory requests from
1097                        this peer are rejected.</li>
1098
1099                        <li>1: Transparent RMEM mode. Accesses to the virtual RMEM region
1100                        are translated into individual remote memory accesses. Incoming
1101                        transparent RMEM read and write requests from this peer are
1102                        permitted. Bulk DMA read and write requests are rejected.</li>
1103
1104                        <li>2: Mirror-to-local mode. A locally initiated DMA request fetches
1105                        the remote memory region into the configured local memory region
1106                        using remote DMA reads. On the responder side, incoming bulk DMA
1107                        write requests from this peer are permitted and target the
1108                        configured local memory region. Incoming bulk DMA read requests
1109                        are rejected.</li>
1110
1111                        <li>3: Mirror-to-remote mode. A locally initiated DMA request sends
1112                        the configured local memory region to the remote memory region
1113                        using remote DMA writes. On the responder side, incoming bulk DMA
1114                        read requests from this peer are permitted and source data from
1115                        the configured local memory region. Incoming bulk DMA write
1116                        requests are rejected.</li>
1117
1118                        </ul>
1119
1120                        Consequently, a mirror relationship uses complementary modes:
1121                        an initiator operating in mirror-to-local mode communicates with
1122                        a responder entry configured as mirror-to-remote, while an
1123                        initiator operating in mirror-to-remote mode communicates with a
1124                        responder entry configured as mirror-to-local.
1125                    ";
1126
1127                    sw = rw;
1128                    hw = r;
1129                } mode[1:0];
1130
1131                field {
1132                    name = {INST_NAME, ".peers.entry[0..NUM_OF_PEERS-1].dma.irq_enable"};
1133                    desc = "Enables generation of a PEER_DMA_COMPLETE IRQ event for this peer.
1134                        Hardware samples this field together with irq.event_enable.peer_dma_complete
1135                        when it accepts the peer DMA request. Changing the field while a transfer is
1136                        active does not affect that transfer. Disabling the field does not affect
1137                        DMA execution or the done, error, and error_code status fields. For a
1138                        failed incoming bulk DMA request, this field is captured when recording
1139                        the failure and irq.event_enable.peer_dma_complete is sampled at IRQ
1140                        admission. The event identifies this peer by the received source MAC.
1141                        Successful incoming requests generate no event. RMEM errors use their
1142                        separate irq.event_enable.rmem_error path.";
1143
1144                    sw = rw;
1145                    hw = r;
1146                } irq_enable[2:2] = 0;
1147
1148                field {
1149                    name = {INST_NAME, ".peers.entry[0..NUM_OF_PEERS-1].dma.request[8:8]"};
1150                    desc = "Writing one requests a DMA transfer to or from the remote peer,
1151                        according to dma.mode. The field remains asserted until the DMA engine
1152                        accepts and snapshots the request. Hardware clears it upon acceptance;
1153                        while a transfer for this peer is active, a newly asserted request remains
1154                        pending. Software or an RTL controller shall read the completion status of
1155                        the previous request before asserting this field for the next request and
1156                        shall keep the peer configuration stable while this field is asserted.";
1157
1158                    sw = rw;
1159                    hw = r;
1160                    hwclr = true;
1161                } request[8:8] = 0;
1162
1163                field {
1164                    name = {INST_NAME, ".peers.entry[0..NUM_OF_PEERS-1].dma.clear_error"};
1165                    desc = "Writing one clears this peer's shared RMEM/DMA error flag and error
1166                        code. The field remains asserted until hardware accepts and clears the
1167                        command. It does not abort an active operation, clear done, or complete
1168                        an IRQ claim. A new failure takes precedence over a simultaneous clear.
1169                        Starting or successfully completing an operation does not clear a
1170                        previously recorded error.";
1171
1172                    sw = rw;
1173                    hw = r;
1174                    hwclr = true;
1175                } clear_error[9:9] = 0;
1176
1177                field {
1178                    name = {INST_NAME, ".peers.entry[0..NUM_OF_PEERS-1].dma.idle[16:16]"};
1179                    desc = "Indicates whether this peer has no accepted DMA request in progress.
1180                        Hardware deasserts this field when a request is accepted and asserts it
1181                        after all fragments of the requested block have completed.";
1182
1183                    sw = r;
1184                    hw = w;
1185                } idle[16:16];
1186
1187                field {
1188                    name = {INST_NAME, ".peers.entry[0..NUM_OF_PEERS-1].dma.done[24:24]"};
1189                    desc = "Sticky successful-completion flag for this peer. Hardware sets this
1190                        field after all fragments of the accepted block transfer complete
1191                        successfully and clears it when the next request is accepted.";
1192
1193                    sw = r;
1194                    hw = w;
1195                } done[24:24];
1196
1197                field {
1198                    name = {INST_NAME, ".peers.entry[0..NUM_OF_PEERS-1].dma.error[25:25]"};
1199                    desc = "Shared sticky RMEM/DMA error flag for this peer. Hardware sets this
1200                        field when a locally initiated RMEM access or bulk DMA transfer fails,
1201                        or when servicing an incoming RMEM or bulk DMA request from this peer
1202                        fails. Received requests are associated by source MAC. An incoming failure
1203                        records its code before exposing its error response, independently of
1204                        IRQ enable; it does not change the locally initiated dma.request, idle,
1205                        or done state. An incomplete incoming DMA data frame records remote
1206                        stream code 10. Multicast response suppression does not suppress this
1207                        local error record or an enabled event.
1208                        Only dma.clear_error or endpoint reset clears a latched error; starting
1209                        or successfully completing another operation does not clear it. The
1210                        flag is independent of the IRQ event-enable fields.";
1211
1212                    sw = r;
1213                    hw = w;
1214                } error[25:25];
1215
1216                field {
1217                    name = {INST_NAME, ".peers.entry[0..NUM_OF_PEERS-1].dma.error_code[31:28]"};
1218                    desc = "
1219                        Shared sticky code for the most recent RMEM or bulk DMA failure for this peer:<ul>
1220                        <li>0: No error.</li>
1221                        <li>1: Local invalid request, configuration, descriptor, or parameters.</li>
1222                        <li>2: Local stream/PDU length or TLAST error.</li>
1223                        <li>3: Local supported-size or buffer-capacity overflow.</li>
1224                        <li>4: Local AXI read SLVERR response.</li>
1225                        <li>5: Local AXI read DECERR response.</li>
1226                        <li>6: Local AXI write SLVERR response.</li>
1227                        <li>7: Local AXI write DECERR response.</li>
1228                        <li>8: Local peer response timeout or Request ID wrap collision. An RMEM access
1229                            terminates through its ACK-only boundary; a bulk DMA timeout aborts
1230                            the remaining fragments. Hardware does not retry.</li>
1231                        <li>9: Remote invalid request, configuration, descriptor, or parameters.</li>
1232                        <li>10: Remote stream/PDU length or TLAST error. Also used directly by
1233                            the receiver of an incomplete DMA_WRITE_REQ or DMA_READ_RSP,
1234                            including missing or incorrect EndOfData.</li>
1235                        <li>11: Remote supported-size or buffer-capacity overflow.</li>
1236                        <li>12: Remote AXI read SLVERR response.</li>
1237                        <li>13: Remote AXI read DECERR response.</li>
1238                        <li>14: Remote AXI write SLVERR response.</li>
1239                        <li>15: Remote AXI write DECERR response.</li>
1240                        </ul>
1241                        Codes 1-8 describe errors detected by this endpoint, including
1242                        malformed responses, local memory failures, and failures while servicing
1243                        received requests. Codes 9-15 describe
1244                        failures reported by the responding peer through a valid ERROR_RSP.
1245                        Code 10 additionally reports an incomplete incoming DMA data frame
1246                        detected at this endpoint, without requiring an ERROR_RSP first.
1247                        A missing or incorrect EndOfData uses this same code even when
1248                        Ethernet padding masks the short data length. A receiver of an
1249                        incomplete unicast DMA_WRITE_REQ records CSR code 10 but sends
1250                        ERROR_RSP wire cause 2; multicast writes generate no response.
1251                        ERROR_RSP carries a 32-bit cause in the range 1-7, which the initiating
1252                        oETP engine validates and maps to CSR code = 8 + wire code. Values
1253                        outside 1-7 are invalid response parameters and record local code 1;
1254                        they must not be truncated or mistaken for local TIMEOUT = 8.
1255                        The oETP initiator completion channel carries this final CSR encoding
1256                        for both RMEM and bulk DMA. The responder completion channel reports
1257                        its cause 1-7 for serialization as ERROR_RSP; cause 2 on an incomplete
1258                        received bulk write corresponds to receiver CSR code 10. Hardware
1259                        clears this field only on dma.clear_error or endpoint reset. Successful
1260                        operations preserve a recorded failure. A new failure replaces the code
1261                        and takes precedence over a simultaneous clear. A failed streaming
1262                        transfer may have partially modified memory; error reporting does not
1263                        provide rollback or an exact count of modified bytes. Software manages
1264                        buffer synchronization and recovery.
1265                    ";
1266
1267                    sw = r;
1268                    hw = w;
1269                } error_code[31:28];
1270            } dma;
1271        } entry[NUM_OF_PEERS];
1272    } peers;
1273
1274    external regfile rmem {
1275        name = {INST_NAME, ".rmem"};
1276        desc = "Virtual memory region for all remote peers, with offsets and sizes defined in the
1277            peers regfile.";
1278
1279        reg word {
1280            name = {INST_NAME, ".rmem.word[0..RMEM_TOTAL_DEPTH-1]"};
1281            desc = "32-bit word in the virtual memory region.";
1282
1283            field {
1284                name = {INST_NAME, ".rmem.word[0..RMEM_TOTAL_DEPTH-1].data[31:0]"};
1285                desc = "Data stored in this virtual memory word.";
1286
1287                sw = rw;
1288                hw = rw;
1289            } data[31:0];
1290        } word[RMEM_TOTAL_DEPTH];
1291    } rmem;
1292};
1293
1294addrmap openenoc_endpoint_interface_top {
1295    name = "openenoc_endpoint_interface_top";
1296    desc = "Control and status address map for an openENOC Endpoint Interface instance.";
1297
1298    littleendian;
1299    default accesswidth = 32;
1300    default regwidth = 32;
1301    default alignment = 4;
1302    addressing = regalign;
1303
1304    openenoc_endpoint_interface #(
1305        .RMEM_TOTAL_DEPTH(1),
1306        .NUM_OF_PEERS(1),
1307        .MAX_RAW_FRAME_SIZE(8192),
1308        .HAS_PEER_DMA(1),
1309        .HAS_NON_OETP_DMA(1),
1310        .HAS_DIRECT_AXIS(1),
1311        .HAS_RMEM(1),
1312        .HAS_IRQ(1),
1313        .INST_NAME("openenoc_endpoint_interface")
1314    ) openenoc_endpoint_interface;
1315};

The openENOC Endpoint Interface CSR memory map and detailed register documentation are derived directly from this SystemRDL specification. The generated CSR documentation provides the corresponding human-readable description of the register hierarchy, address offsets, field layouts, access permissions, reset values, and associated register semantics.

The complete generated CSR documentation is available in openenoc_endpoint_interface_top address map.

Summary

This document defines the HAL architecture and CSR organization for the openENOC Switch and the openENOC Endpoint Interface.

The HAL establishes a consistent software-visible programming model based on memory-mapped Control and Status Registers, while the CSR specification provides the authoritative description of hardware/software interactions. Together, these mechanisms form the foundation for software development, system integration, and verification activities within the openENOC project.