opsero-ethernet-mac
opsero-ethernet-mac (omac) is a collection of open-source Ethernet MAC + PCS cores for FPGAs, written from scratch in vendor-neutral SystemVerilog from IEEE 802.3 (clauses 46, 49, 81, 82 and annex 31B) and released under the MIT license. No licensed MAC, PCS or evaluation IP is involved: the transceiver is reached through a small per-family wrapper around the vendor’s free transceiver wizard.
Every core presents the same user-side boundary, the interface contract: AXI-Stream transmit and receive on one user clock, FCS handled inside, PAUSE as levels, and a common status, control and event bundle. A design can therefore swap one MAC for another (or for a hard MAC behind a thin wrapper) without touching the logic around it.
Source code, releases and issues:
github.com/fpgadeveloper/opsero-ethernet-mac.
The collection was created for Opsero’s reference designs; the designs that use it, with their
measured results, are on the projects page. omac_40g and omac_10g are
validated on a ZCU106 against an NVIDIA ConnectX-6 Dx (Hardware validation). The cores are not
tied to a board: any device with a supported transceiver wrapper (today
UltraScale+ GTH) can use them; the board-specific part (which GT channels, which reference
clock) is the design’s (Board placement).
IP cores
IP |
Standard |
User side |
Lanes |
Maturity |
|---|---|---|---|---|
10GBASE-R; 25GBASE-R with |
64-bit AXI-Stream at 156.25 MHz (25G: 390.625 MHz) |
1 x 10.3125 Gb/s (25G: 1 x 25.78125) |
10GBASE-R: hardware-proven on a ZCU106 against a ConnectX-6 Dx (Hardware validation). 25GBASE-R: simulated, timing closed at 390.625 MHz on xcku5p-2, not yet on hardware (needs a GTY wrapper) |
|
40GBASE-R4 (multi-lane distribution, alignment markers, BIP, deskew, lane reorder) |
256-bit AXI-Stream at 156.25 MHz |
4 x 10.3125 Gb/s |
hardware-proven on a ZCU106 against a ConnectX-6 Dx at 40GBASE-CR4 (Hardware validation) |
|
|
1000BASE-X / SGMII |
8-bit |
1 x 1.25 Gb/s |
planned (Roadmap) |
|
100GBASE-R4 with RS-FEC |
512-bit |
4 x 25.78125 Gb/s |
planned (Roadmap) |
Transceiver wrapper |
Family |
Maturity |
|---|---|---|
UltraScale+ GTH, 64B/66B asynchronous gearbox (AMD |
hardware-proven with both IPs (ZCU106, Hardware validation) |
|
|
UltraScale+ GTY |
planned |
Every IP comes with a self-checking simulation regression against an independent Python model
of the standard (Simulation) and out-of-context implementation results
(Resources and timing). One tag series covers the whole repository (vX.Y.Z); the maturity of each
IP is the table above and its entry in the
CHANGELOG.
License
MIT, see LICENSE.
Copyright (c) 2026 Opsero Electronic Design Inc. The transceiver wizard IP that the gt/
generator scripts create (gtwizard_ultrascale) is AMD’s and needs no license key.
Using the cores
As a git submodule
The repository is meant to be pulled into a design as a git submodule. Each IP lists its
sources in compile order in ip/<ip>/rtl/<ip>.f (paths relative to the repository root),
including the common/ blocks it needs:
git submodule add https://github.com/fpgadeveloper/opsero-ethernet-mac submodules/opsero-ethernet-mac
# Vivado: read the IP's source list
set omac submodules/opsero-ethernet-mac
foreach f [split [read [open $omac/ip/omac_40g/rtl/omac_40g.f]] "\n"] {
if {$f ne "" && [string index $f 0] ne "#"} { read_verilog -sv $omac/$f } ;# or add_files
}
# the transceiver: generate the free gtwizard IP and add the wrapper
source $omac/gt/omac_gt_usp_gth/gen_gt.tcl
# the board's GT channel sites, QSFP lane 0 first (omac_10g: omac_gen_gth ./ip <channel-site>)
omac_gen_gth_x4 ./ip {<lane0> <lane1> <lane2> <lane3>}
read_verilog -sv $omac/gt/omac_gt_usp_gth/omac_gt_usp_gth_x4.sv
When the design synthesises in memory with synth_design (no out-of-context IP runs), set the
generated transceiver IP to global synthesis; see Global synthesis.
In RTL
Instantiate the IP and its transceiver wrapper and connect the GT side; each IP page shows the
connection, and ip/<ip>/synth/<ip>_gth_top.sv is a complete example (the synthesis check of
the pair). All user logic runs on the wrapper’s tx_usrclk2 (the IP’s clk).
In an IP integrator block design (module reference)
Add ip/<ip>/rtl/<ip>_bd.v as a module reference: a generated Verilog wrapper around the
SystemVerilog top (module references need a Verilog top file), whose AXI-Streams, clock and
reset IPI recognises as interfaces. Alternatively, put the IP and its transceiver wrapper in
one small Verilog module and add that as the module reference. Several such modules (one per
MAC, or a hard-MAC adapter) with the same ports let one block design take any of them.
Bring-up checklist
Make the 156.25 MHz reference clock stable first (for example, a programmable clock generator on the FMC must be programmed first), then release the wrapper’s
reset_all.Constrain the reference clock (
create_clock -period 6.400) and the clock crossings in an implementation-only XDC (Clock constraints).Force the link partner to the line rate with auto-negotiation off and FEC off (omac has no clause 73 auto-negotiation and no FEC), for example on a ConnectX NIC:
ethtool -s <if> speed 40000 duplex full autoneg offandethtool --set-fec <if> encoding off.Watch
stat_link_up(allow up to 10 s: the partner’s own link-up dominates); if it does not come, follow the debug order of the IP page: Bring-up and software, Bring-up and software.
No drivers, by design
The cores ship without a driver: no register block inside the IPs, no Linux netdev, no bare-metal library. A design folds the MAC’s static control and status ports into its own register block (each design puts something different behind the MAC), and its software brings the link up through those registers. What the registers must expose, the clock-domain rule, the counters, the interrupts and a worked example are the software contract; the sequence, timing and debug order of each IP are on its page.