mirror of
https://github.com/fpganinja/taxi.git
synced 2026-02-07 17:50:20 -08:00
Compare commits
53 Commits
729bf79427
...
8328f50673
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8328f50673 | ||
|
|
9e8925de39 | ||
|
|
9e336fbdd5 | ||
|
|
bedd85d7f6 | ||
|
|
be40d3ac2d | ||
|
|
329fcf1f45 | ||
|
|
4b3a4b4059 | ||
|
|
108fb10735 | ||
|
|
3c4aecc859 | ||
|
|
711b268cb7 | ||
|
|
2987c8db71 | ||
|
|
d1bba66104 | ||
|
|
77b50c7f85 | ||
|
|
541f6a9ee6 | ||
|
|
eca44dc247 | ||
|
|
107238cce2 | ||
|
|
38d13f8337 | ||
|
|
847d47b71f | ||
|
|
b8cd443e01 | ||
|
|
d554f45b22 | ||
|
|
c18aed2074 | ||
|
|
3e421e3cdd | ||
|
|
a0e6ac2c35 | ||
|
|
0c0ac88462 | ||
|
|
fb93b62c50 | ||
|
|
ecf62c94e6 | ||
|
|
cc4e465462 | ||
|
|
96e24756de | ||
|
|
8ad1842b90 | ||
|
|
fc395b1596 | ||
|
|
8f60b25205 | ||
|
|
2c45420b52 | ||
|
|
d2f56bb932 | ||
|
|
3fcb32f232 | ||
|
|
74e49a77e2 | ||
|
|
4da6771603 | ||
|
|
245e71551b | ||
|
|
bfb96c677a | ||
|
|
2ada85105f | ||
|
|
9d8c5fce73 | ||
|
|
8bff361e12 | ||
|
|
2455b770fd | ||
|
|
900483d0cd | ||
|
|
ec7610754c | ||
|
|
bef82674d3 | ||
|
|
83c52e6744 | ||
|
|
dfe13db9f7 | ||
|
|
008e06ff48 | ||
|
|
ec00c2323c | ||
|
|
cb3538a0de | ||
|
|
cbe0fa730d | ||
|
|
7449dcfdc3 | ||
|
|
75d28d5adb |
62
README.md
62
README.md
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://github.com/fpganinja/taxi/actions/workflows/regression-tests.yml)
|
||||
|
||||
AXI, AXI stream, Ethernet, and PCIe components in System Verilog.
|
||||
The home of Corundum, Zircon, and XFCP, plus AXI, AXI stream, Ethernet, and PCIe components in System Verilog.
|
||||
|
||||
GitHub repository: https://github.com/fpganinja/taxi
|
||||
|
||||
@@ -10,7 +10,7 @@ Documentation: https://docs.fpga.taxi/
|
||||
|
||||
## Introduction
|
||||
|
||||
The goal of the Taxi transport library is to provide a set of performant, easy-to-use building blocks in modern System Verilog facilitating data transport and interfacing, both internally via AXI and AXI stream, and externally via Ethernet, PCI express, UART, and I2C. The building blocks are accompanied by testbenches and simulation models utilizing Cocotb and Verilator.
|
||||
The goal of the Taxi transport library is to provide a set of performant, easy-to-use building blocks in modern System Verilog facilitating data transport and interfacing, both internally via AXI, AXI stream, and APB, and externally via Ethernet, PCI express, UART, and I2C. This project is also the home of the next-generation version of the Corundum open-souce NIC and platform for in-network compute, as well as the Zircon open-source IP stack. The building blocks are accompanied by testbenches and simulation models utilizing Cocotb and Verilator.
|
||||
|
||||
This library is currently under development; more components will be added over time as they are developed.
|
||||
|
||||
@@ -22,7 +22,58 @@ Under the strongly-reciprocal CERN OHL, you must provide the source code of the
|
||||
|
||||
To facilitate the dual-license model, contributions to the project can only be accepted under a contributor license agreement.
|
||||
|
||||
## Components
|
||||
## Corundum NIC
|
||||
|
||||
Corundum is an open-source, high-performance FPGA-based NIC and platform for in-network compute. Features include a high performance datapath, 10G/25G/100G Ethernet, PCI express gen 3+, a custom, high performance, tightly-integrated PCIe DMA engine, many (1000+) transmit, receive, completion, and event queues, scatter/gather DMA, MSI/MSI-X interrupts, per-port transmit scheduling, flow hashing, RSS, checksum offloading, and native IEEE 1588 PTP timestamping. A Linux driver is included that integrates with the Linux networking stack. Development and debugging is facilitated by an extensive simulation framework that covers the entire system from a simulation model of the driver and PCI express interface on the host side and Ethernet interfaces on the network side.
|
||||
|
||||
Several variants of Corundum are planned, sharing the same host interface and device driver but targeting different optimization points:
|
||||
|
||||
* corundum-micro: size-optimized for applications like SoCs and low-bandwidth NICs, supporting several ports at 1 Gbps up to 10-25 Gbps
|
||||
* corundum-lite: middle of the road design, supporting multiple ports at 10G/25G or one port at 100G, up to around 100 Gbps aggregate
|
||||
* corundum-ng: intended for high-performance packet processing with deep pipelines and segmented internal interfaces, supporting operation at up to 400 Gbps aggregate
|
||||
* corundum-proto: simplified design with simplified driver, intended for educational purposes only
|
||||
|
||||
Planned features include a DPDK driver, SR-IOV, AF_XDP, white rabbit/IEEE 1588 HA, and Zircon stack integration.
|
||||
|
||||
Note that Corundum is still under active development and may not ready for production use; additional functionality and improvements to performance and flexibility will be made over time.
|
||||
|
||||
## Zircon IP stack
|
||||
|
||||
The Zircon IP stack implements IPv4, IPv6, and UDP support from 1 Gbps to 100 Gbps. It handles parsing and deparsing the packet headers, inserting and removing VLAN tags, computing and verifying header and payload checksums, matching RX packet fields against configured rules, and multiplexing between multiple internal application interfaces. The stack can be configured to pass packets to application logic either umodified, with VLAN tags stripped, with simplified headers, or with only the payloads.
|
||||
|
||||
Planned features include support for TCP, RDMA, and integration with Corundum.
|
||||
|
||||
Note that Zircon is still under active development and may not ready for production use; additional functionality and improvements to performance and flexibility will be made over time.
|
||||
|
||||
## Ethernet MAC and PHY
|
||||
|
||||
The Taxi transport library contains several Ethernet MAC and PCS variants, covering link rates from 10 Mbps to 25 Gbps. The MAC modules support LFC and PFC pause frames, PTP timestamping, frame length enforcement, FCS computation and verification, and statistics reporting. Wrappers for low-speed operation support MII, GMII, and RGMII PHY-attach protocols for use with an external PHY chip. Wrappers for 10G/25G include device-specific transceiver instances for a fully-integrated solution. Logic is available for a 10G/25G MAC, 10G/25G PCS, and 10G/25G "fused" MAC+PCS, with reduced latency and resource consumption.
|
||||
|
||||
The 10G/25G MAC/PHY/GT wrapper for 7-series/UltraScale/UltraScale+ supports GTX, GTH, and GTY transceivers. On UltraScale and UltraScale+ devices, it can be configured for either a 32-bit or 64-bit datapath via the DATA_W parameter. The 32-bit datapath supports 10G only, while the 64-bit datapath can be used for either 10G or 25G. TCL scripts for generating the GT cores are provided for both 10G and 25G and for several common reference clocks. The core supports operation in either a normal latency mode or a low latency mode via the CFG_LOW_LATENCY paremter, which affects the clock frequency and transceiver configuration (async gearbox vs. sync gearbox and buffer bypass). The low latency mode has a slightly higher clock frequency and resource consumption, so it is not recommended unless you really need to shave off a new nanoseconds of latency, or you need highest possible time sync precision. On 7-series, the core only supports 32-bit low-latency mode. The wrapper also provides an APB interface for configuring the transceivers and QPLLs.
|
||||
|
||||
The 10G/25G MAC and PCS logic is also highly optimized for both size and timing performance, with 60 instances fitting comfortably on an XCVU9P -2 on the HTG9200 board, fully utilizing 15 QSFP28 (9 on the board plus 6 via FMC+, 60 lanes total). With the low-latency MACs, statistics collection, loopback FIFOs, and XFCP, the footprint is about 15% of the device LUTs at 25G (about 3000 LUTs and 2500 FFs per channel) and about 10% of the device LUTs at 10G (about 2000 LUTs and 2100 FFs per channel). The 10G configuration closes timing on the KC705 (single SFP+, 1 lane total) with an XC7K325T -2 at 322.265625 MHz, and the 25G configuration closes timing on the XUSP3S (quad QSFP38, 16 lanes total) with an XCVU095 -2 at 402.83203125 MHz.
|
||||
|
||||
Planned features include 1000BASE-X, SGMII, USXGMII, dynamic rate switching, AN, integrated PTP TD logic, better integration of the PTP TD subsystem, and white rabbit/IEEE 1588 HA support.
|
||||
|
||||
## Statistics collection subsystem
|
||||
|
||||
The statistics collection subsystem provides a mechanism for aggregating statistical information from multiple components in a design. The statistics collector module accepts inputs in the form of increment values, accumulates those in an internal RAM, and periodically dumps the counter values via AXI stream where they can be merged and easily passed between clock domains. The statistics counter module accepts the counter values and accumulates them in a larger RAM (BRAM or URAM), providing an AXI-lite register interface to read the counters. The statistics collection subsystem can also distribute and collect informational strings along with the counter values, which can facilitate debugging and automatic discovery of implemented statistics channels. Statistics collector modules are integrated into several library components, like the Ethernet MACs, for ease of integration.
|
||||
|
||||
## PTP time distribution subsystem
|
||||
|
||||
The PTP time distribution subsystem provides a low-overhead method for precisely distributing and synchronizing a central PTP hardware clock (PHC) into multiple destination clock domains, potentially spread out across the target device. The PTP TD subsystem derives the 96-bit ToD clock from the 64-bit relative clock, enabling timestamp capture and manipulation to be done efficiently with a trucated relative timestamp which can be expanded to a full 96-bit ToD timestamp where the full resolution is needed. The PTP TD PHC module supports non-precision setting as well as precision atomic ofsetting of the clock, and uses a 32-bit extended fractional ns accumulator with an additional rational remainder to eliminate round-off error for common reference clock periods. The PTP TD PHC is connected to the leaf clocks through a single wire that carries serial data used to synchronize the leaf clocks, as well as a common reference clock. The leaf clocks can insert a configurable number pipeline registers and automatically compensate for the resulting delay. The leaf clock modules reconstruct the PTP time from the PHC locally in the PTP reference clock domain, then use a digital PLL to synthesize and deskew a new PTP time source in the destination clock domain.
|
||||
|
||||
Planned features include better integration into the MAC and PCS logic for ease of use and to compensate for transceiver gearbox delay variance, DDMTD for picosecond-level precision, and white rabbit/IEEE 1588 HA support.
|
||||
|
||||
## XFCP
|
||||
|
||||
The Extensible FPGA control platform (XFCP) is a framework that enables simple interfacing between an FPGA design in verilog and control software. XFCP uses a source-routed packet switched bus over AXI stream to interconnect components in an FPGA design, eliminating the need to assign and manage addresses, enabling simple bus enumeration, and vastly reducing dependencies between the FPGA design and the control software. XFCP currently supports operation over UART. XFCP includes an interface modules for UART, a parametrizable arbiter to enable simultaneous use of multiple interfaces, a parametrizable switch to connect multiple on-FPGA components, bridges for interfacing with various devices including AXI, AXI-lite, APB, and I2C, and a Python framework for enumerating XFCP buses and controlling connected devices.
|
||||
|
||||
Planned features include support for UDP via the Zircon stack.
|
||||
|
||||
## List of library components
|
||||
|
||||
The Taxi transport library contains many smaller components that can be composed to build larger designs.
|
||||
|
||||
* APB
|
||||
* SV interface for APB
|
||||
@@ -74,6 +125,9 @@ To facilitate the dual-license model, contributions to the project can only be a
|
||||
* DMA client for AXI stream
|
||||
* DMA interface for AXI
|
||||
* DMA interface for UltraScale PCIe
|
||||
* DMA descriptor mux
|
||||
* DMA RAM demux
|
||||
* DMA interface mux
|
||||
* Segmented SDP RAM
|
||||
* Segmented dual-clock SDP RAM
|
||||
* Ethernet
|
||||
@@ -118,6 +172,7 @@ To facilitate the dual-license model, contributions to the project can only be a
|
||||
* I2C master
|
||||
* I2C single register
|
||||
* I2C slave
|
||||
* I2C slave APB master
|
||||
* I2C slave AXI lite master
|
||||
* MDIO master
|
||||
* UART
|
||||
@@ -126,6 +181,7 @@ To facilitate the dual-license model, contributions to the project can only be a
|
||||
* PCI Express
|
||||
* PCIe AXI lite master
|
||||
* PCIe AXI lite master for Xilinx UltraScale
|
||||
* MSI shim for Xilinx UltraScale
|
||||
* Primitives
|
||||
* Arbiter
|
||||
* Priority encoder
|
||||
|
||||
3
src/axi/rtl/taxi_axi_crossbar_1s.f
Normal file
3
src/axi/rtl/taxi_axi_crossbar_1s.f
Normal file
@@ -0,0 +1,3 @@
|
||||
taxi_axi_crossbar_1s.sv
|
||||
taxi_axi_crossbar_1s_wr.f
|
||||
taxi_axi_crossbar_1s_rd.f
|
||||
152
src/axi/rtl/taxi_axi_crossbar_1s.sv
Normal file
152
src/axi/rtl/taxi_axi_crossbar_1s.sv
Normal file
@@ -0,0 +1,152 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2018-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* AXI4 crossbar
|
||||
*/
|
||||
module taxi_axi_crossbar_1s #
|
||||
(
|
||||
// Number of AXI outputs (master interfaces)
|
||||
parameter M_COUNT = 4,
|
||||
// Address width in bits for address decoding
|
||||
parameter ADDR_W = 32,
|
||||
// TODO fix parametrization once verilator issue 5890 is fixed
|
||||
// Number of concurrent unique IDs
|
||||
parameter S_THREADS = 2,
|
||||
// Number of concurrent operations for each slave interface
|
||||
// 1 concatenated fields of 32 bits
|
||||
parameter S_ACCEPT = 16,
|
||||
// Number of regions per master interface
|
||||
parameter M_REGIONS = 1,
|
||||
// Master interface base addresses
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_W bits
|
||||
// set to zero for default addressing based on M_ADDR_W
|
||||
parameter M_BASE_ADDR = '0,
|
||||
// Master interface address widths
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits
|
||||
parameter M_ADDR_W = {M_COUNT{{M_REGIONS{32'd24}}}},
|
||||
// Number of concurrent operations for each master interface
|
||||
// M_COUNT concatenated fields of 32 bits
|
||||
parameter M_ISSUE = {M_COUNT{32'd4}},
|
||||
// Secure master (fail operations based on awprot/arprot)
|
||||
// M_COUNT bits
|
||||
parameter M_SECURE = {M_COUNT{1'b0}},
|
||||
// Slave interface AW channel register type (input)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter S_AW_REG_TYPE = 2'd0,
|
||||
// Slave interface W channel register type (input)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter S_W_REG_TYPE = 2'd0,
|
||||
// Slave interface B channel register type (output)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter S_B_REG_TYPE = 2'd1,
|
||||
// Slave interface AR channel register type (input)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter S_AR_REG_TYPE = 2'd0,
|
||||
// Slave interface R channel register type (output)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter S_R_REG_TYPE = 2'd2,
|
||||
// Master interface AW channel register type (output)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter M_AW_REG_TYPE = {M_COUNT{2'd1}},
|
||||
// Master interface W channel register type (output)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter M_W_REG_TYPE = {M_COUNT{2'd2}},
|
||||
// Master interface B channel register type (input)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter M_B_REG_TYPE = {M_COUNT{2'd0}},
|
||||
// Master interface AR channel register type (output)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter M_AR_REG_TYPE = {M_COUNT{2'd1}},
|
||||
// Master interface R channel register type (input)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter M_R_REG_TYPE = {M_COUNT{2'd0}}
|
||||
)
|
||||
(
|
||||
input wire logic clk,
|
||||
input wire logic rst,
|
||||
|
||||
/*
|
||||
* AXI4 slave interface
|
||||
*/
|
||||
taxi_axi_if.wr_slv s_axi_wr,
|
||||
taxi_axi_if.rd_slv s_axi_rd,
|
||||
|
||||
/*
|
||||
* AXI4 master interfaces
|
||||
*/
|
||||
taxi_axi_if.wr_mst m_axi_wr[M_COUNT],
|
||||
taxi_axi_if.rd_mst m_axi_rd[M_COUNT]
|
||||
);
|
||||
|
||||
taxi_axi_crossbar_1s_wr #(
|
||||
.M_COUNT(M_COUNT),
|
||||
.ADDR_W(ADDR_W),
|
||||
.S_THREADS(S_THREADS),
|
||||
.S_ACCEPT(S_ACCEPT),
|
||||
.M_REGIONS(M_REGIONS),
|
||||
.M_BASE_ADDR(M_BASE_ADDR),
|
||||
.M_ADDR_W(M_ADDR_W),
|
||||
.M_ISSUE(M_ISSUE),
|
||||
.M_SECURE(M_SECURE),
|
||||
.S_AW_REG_TYPE(S_AW_REG_TYPE),
|
||||
.S_W_REG_TYPE(S_W_REG_TYPE),
|
||||
.S_B_REG_TYPE(S_B_REG_TYPE)
|
||||
)
|
||||
wr_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI slave interface
|
||||
*/
|
||||
.s_axi_wr(s_axi_wr),
|
||||
|
||||
/*
|
||||
* AXI master interfaces
|
||||
*/
|
||||
.m_axi_wr(m_axi_wr)
|
||||
);
|
||||
|
||||
taxi_axi_crossbar_1s_rd #(
|
||||
.M_COUNT(M_COUNT),
|
||||
.ADDR_W(ADDR_W),
|
||||
.S_THREADS(S_THREADS),
|
||||
.S_ACCEPT(S_ACCEPT),
|
||||
.M_REGIONS(M_REGIONS),
|
||||
.M_BASE_ADDR(M_BASE_ADDR),
|
||||
.M_ADDR_W(M_ADDR_W),
|
||||
.M_ISSUE(M_ISSUE),
|
||||
.M_SECURE(M_SECURE),
|
||||
.S_AR_REG_TYPE(S_AR_REG_TYPE),
|
||||
.S_R_REG_TYPE(S_R_REG_TYPE)
|
||||
)
|
||||
rd_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI slave interface
|
||||
*/
|
||||
.s_axi_rd(s_axi_rd),
|
||||
|
||||
/*
|
||||
* AXI master interfaces
|
||||
*/
|
||||
.m_axi_rd(m_axi_rd)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
3
src/axi/rtl/taxi_axi_crossbar_1s_rd.f
Normal file
3
src/axi/rtl/taxi_axi_crossbar_1s_rd.f
Normal file
@@ -0,0 +1,3 @@
|
||||
taxi_axi_crossbar_1s_rd.sv
|
||||
taxi_axi_crossbar_rd.f
|
||||
taxi_axi_tie_rd.sv
|
||||
130
src/axi/rtl/taxi_axi_crossbar_1s_rd.sv
Normal file
130
src/axi/rtl/taxi_axi_crossbar_1s_rd.sv
Normal file
@@ -0,0 +1,130 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2018-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* AXI4 crossbar
|
||||
*/
|
||||
module taxi_axi_crossbar_1s_rd #
|
||||
(
|
||||
// Number of AXI outputs (master interfaces)
|
||||
parameter M_COUNT = 4,
|
||||
// Address width in bits for address decoding
|
||||
parameter ADDR_W = 32,
|
||||
// TODO fix parametrization once verilator issue 5890 is fixed
|
||||
// Number of concurrent unique IDs
|
||||
parameter S_THREADS = 2,
|
||||
// Number of concurrent operations for each slave interface
|
||||
// 1 concatenated fields of 32 bits
|
||||
parameter S_ACCEPT = 16,
|
||||
// Number of regions per master interface
|
||||
parameter M_REGIONS = 1,
|
||||
// Master interface base addresses
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_W bits
|
||||
// set to zero for default addressing based on M_ADDR_W
|
||||
parameter M_BASE_ADDR = '0,
|
||||
// Master interface address widths
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits
|
||||
parameter M_ADDR_W = {M_COUNT{{M_REGIONS{32'd24}}}},
|
||||
// Number of concurrent operations for each master interface
|
||||
// M_COUNT concatenated fields of 32 bits
|
||||
parameter M_ISSUE = {M_COUNT{32'd4}},
|
||||
// Secure master (fail operations based on awprot/arprot)
|
||||
// M_COUNT bits
|
||||
parameter M_SECURE = {M_COUNT{1'b0}},
|
||||
// Slave interface AR channel register type (input)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter S_AR_REG_TYPE = 2'd0,
|
||||
// Slave interface R channel register type (output)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter S_R_REG_TYPE = 2'd2,
|
||||
// Master interface AR channel register type (output)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter M_AR_REG_TYPE = {M_COUNT{2'd1}},
|
||||
// Master interface R channel register type (input)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter M_R_REG_TYPE = {M_COUNT{2'd0}}
|
||||
)
|
||||
(
|
||||
input wire logic clk,
|
||||
input wire logic rst,
|
||||
|
||||
/*
|
||||
* AXI4 slave interface
|
||||
*/
|
||||
taxi_axi_if.rd_slv s_axi_rd,
|
||||
|
||||
/*
|
||||
* AXI4 master interfaces
|
||||
*/
|
||||
taxi_axi_if.rd_mst m_axi_rd[M_COUNT]
|
||||
);
|
||||
|
||||
taxi_axi_if #(
|
||||
.DATA_W(s_axi_rd.DATA_W),
|
||||
.ADDR_W(s_axi_rd.ADDR_W),
|
||||
.STRB_W(s_axi_rd.STRB_W),
|
||||
.ID_W(s_axi_rd.ID_W),
|
||||
.AWUSER_EN(s_axi_rd.AWUSER_EN),
|
||||
.AWUSER_W(s_axi_rd.AWUSER_W),
|
||||
.WUSER_EN(s_axi_rd.WUSER_EN),
|
||||
.WUSER_W(s_axi_rd.WUSER_W),
|
||||
.BUSER_EN(s_axi_rd.BUSER_EN),
|
||||
.BUSER_W(s_axi_rd.BUSER_W),
|
||||
.ARUSER_EN(s_axi_rd.ARUSER_EN),
|
||||
.ARUSER_W(s_axi_rd.ARUSER_W),
|
||||
.RUSER_EN(s_axi_rd.RUSER_EN),
|
||||
.RUSER_W(s_axi_rd.RUSER_W),
|
||||
.MAX_BURST_LEN(s_axi_rd.MAX_BURST_LEN),
|
||||
.NARROW_BURST_EN(s_axi_rd.NARROW_BURST_EN)
|
||||
)
|
||||
s_axi_rd_int[1]();
|
||||
|
||||
taxi_axi_tie_rd
|
||||
tie_inst (
|
||||
.s_axi_rd(s_axi_rd),
|
||||
.m_axi_rd(s_axi_rd_int[0])
|
||||
);
|
||||
|
||||
taxi_axi_crossbar_rd #(
|
||||
.S_COUNT(1),
|
||||
.M_COUNT(M_COUNT),
|
||||
.ADDR_W(ADDR_W),
|
||||
.S_THREADS(S_THREADS),
|
||||
.S_ACCEPT(S_ACCEPT),
|
||||
.M_REGIONS(M_REGIONS),
|
||||
.M_BASE_ADDR(M_BASE_ADDR),
|
||||
.M_ADDR_W(M_ADDR_W),
|
||||
.M_ISSUE(M_ISSUE),
|
||||
.M_SECURE(M_SECURE),
|
||||
.S_AR_REG_TYPE(S_AR_REG_TYPE),
|
||||
.S_R_REG_TYPE(S_R_REG_TYPE)
|
||||
)
|
||||
rd_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI slave interface
|
||||
*/
|
||||
.s_axi_rd(s_axi_rd_int),
|
||||
|
||||
/*
|
||||
* AXI master interfaces
|
||||
*/
|
||||
.m_axi_rd(m_axi_rd)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
3
src/axi/rtl/taxi_axi_crossbar_1s_wr.f
Normal file
3
src/axi/rtl/taxi_axi_crossbar_1s_wr.f
Normal file
@@ -0,0 +1,3 @@
|
||||
taxi_axi_crossbar_1s_wr.sv
|
||||
taxi_axi_crossbar_wr.f
|
||||
taxi_axi_tie_wr.sv
|
||||
137
src/axi/rtl/taxi_axi_crossbar_1s_wr.sv
Normal file
137
src/axi/rtl/taxi_axi_crossbar_1s_wr.sv
Normal file
@@ -0,0 +1,137 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2018-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* AXI4 crossbar
|
||||
*/
|
||||
module taxi_axi_crossbar_1s_wr #
|
||||
(
|
||||
// Number of AXI outputs (master interfaces)
|
||||
parameter M_COUNT = 4,
|
||||
// Address width in bits for address decoding
|
||||
parameter ADDR_W = 32,
|
||||
// TODO fix parametrization once verilator issue 5890 is fixed
|
||||
// Number of concurrent unique IDs
|
||||
parameter S_THREADS = 2,
|
||||
// Number of concurrent operations for each slave interface
|
||||
// 1 concatenated fields of 32 bits
|
||||
parameter S_ACCEPT = 16,
|
||||
// Number of regions per master interface
|
||||
parameter M_REGIONS = 1,
|
||||
// Master interface base addresses
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_W bits
|
||||
// set to zero for default addressing based on M_ADDR_W
|
||||
parameter M_BASE_ADDR = '0,
|
||||
// Master interface address widths
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits
|
||||
parameter M_ADDR_W = {M_COUNT{{M_REGIONS{32'd24}}}},
|
||||
// Number of concurrent operations for each master interface
|
||||
// M_COUNT concatenated fields of 32 bits
|
||||
parameter M_ISSUE = {M_COUNT{32'd4}},
|
||||
// Secure master (fail operations based on awprot/arprot)
|
||||
// M_COUNT bits
|
||||
parameter M_SECURE = {M_COUNT{1'b0}},
|
||||
// Slave interface AW channel register type (input)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter S_AW_REG_TYPE = 2'd0,
|
||||
// Slave interface W channel register type (input)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter S_W_REG_TYPE = 2'd0,
|
||||
// Slave interface B channel register type (output)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter S_B_REG_TYPE = 2'd1,
|
||||
// Master interface AW channel register type (output)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter M_AW_REG_TYPE = {M_COUNT{2'd1}},
|
||||
// Master interface W channel register type (output)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter M_W_REG_TYPE = {M_COUNT{2'd2}},
|
||||
// Master interface B channel register type (input)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter M_B_REG_TYPE = {M_COUNT{2'd0}}
|
||||
)
|
||||
(
|
||||
input wire logic clk,
|
||||
input wire logic rst,
|
||||
|
||||
/*
|
||||
* AXI4 slave interface
|
||||
*/
|
||||
taxi_axi_if.wr_slv s_axi_wr,
|
||||
|
||||
/*
|
||||
* AXI4 master interfaces
|
||||
*/
|
||||
taxi_axi_if.wr_mst m_axi_wr[M_COUNT]
|
||||
);
|
||||
|
||||
taxi_axi_if #(
|
||||
.DATA_W(s_axi_wr.DATA_W),
|
||||
.ADDR_W(s_axi_wr.ADDR_W),
|
||||
.STRB_W(s_axi_wr.STRB_W),
|
||||
.ID_W(s_axi_wr.ID_W),
|
||||
.AWUSER_EN(s_axi_wr.AWUSER_EN),
|
||||
.AWUSER_W(s_axi_wr.AWUSER_W),
|
||||
.WUSER_EN(s_axi_wr.WUSER_EN),
|
||||
.WUSER_W(s_axi_wr.WUSER_W),
|
||||
.BUSER_EN(s_axi_wr.BUSER_EN),
|
||||
.BUSER_W(s_axi_wr.BUSER_W),
|
||||
.ARUSER_EN(s_axi_wr.ARUSER_EN),
|
||||
.ARUSER_W(s_axi_wr.ARUSER_W),
|
||||
.RUSER_EN(s_axi_wr.RUSER_EN),
|
||||
.RUSER_W(s_axi_wr.RUSER_W),
|
||||
.MAX_BURST_LEN(s_axi_wr.MAX_BURST_LEN),
|
||||
.NARROW_BURST_EN(s_axi_wr.NARROW_BURST_EN)
|
||||
)
|
||||
s_axi_wr_int[1]();
|
||||
|
||||
taxi_axi_tie_wr
|
||||
tie_inst (
|
||||
.s_axi_wr(s_axi_wr),
|
||||
.m_axi_wr(s_axi_wr_int[0])
|
||||
);
|
||||
|
||||
taxi_axi_crossbar_wr #(
|
||||
.S_COUNT(1),
|
||||
.M_COUNT(M_COUNT),
|
||||
.ADDR_W(ADDR_W),
|
||||
.S_THREADS(S_THREADS),
|
||||
.S_ACCEPT(S_ACCEPT),
|
||||
.M_REGIONS(M_REGIONS),
|
||||
.M_BASE_ADDR(M_BASE_ADDR),
|
||||
.M_ADDR_W(M_ADDR_W),
|
||||
.M_ISSUE(M_ISSUE),
|
||||
.M_SECURE(M_SECURE),
|
||||
.S_AW_REG_TYPE(S_AW_REG_TYPE),
|
||||
.S_W_REG_TYPE(S_W_REG_TYPE),
|
||||
.S_B_REG_TYPE(S_B_REG_TYPE)
|
||||
)
|
||||
wr_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI slave interface
|
||||
*/
|
||||
.s_axi_wr(s_axi_wr_int),
|
||||
|
||||
/*
|
||||
* AXI master interfaces
|
||||
*/
|
||||
.m_axi_wr(m_axi_wr)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
3
src/axi/rtl/taxi_axi_interconnect_1s.f
Normal file
3
src/axi/rtl/taxi_axi_interconnect_1s.f
Normal file
@@ -0,0 +1,3 @@
|
||||
taxi_axi_interconnect_1s.sv
|
||||
taxi_axi_interconnect_1s_wr.f
|
||||
taxi_axi_interconnect_1s_rd.f
|
||||
103
src/axi/rtl/taxi_axi_interconnect_1s.sv
Normal file
103
src/axi/rtl/taxi_axi_interconnect_1s.sv
Normal file
@@ -0,0 +1,103 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2018-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* AXI4 interconnect
|
||||
*/
|
||||
module taxi_axi_interconnect_1s #
|
||||
(
|
||||
// Number of AXI outputs (master interfaces)
|
||||
parameter M_COUNT = 4,
|
||||
// Address width in bits for address decoding
|
||||
parameter ADDR_W = 32,
|
||||
// TODO fix parametrization once verilator issue 5890 is fixed
|
||||
// Number of regions per master interface
|
||||
parameter M_REGIONS = 1,
|
||||
// Master interface base addresses
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_W bits
|
||||
// set to zero for default addressing based on M_ADDR_W
|
||||
parameter M_BASE_ADDR = '0,
|
||||
// Master interface address widths
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits
|
||||
parameter M_ADDR_W = {M_COUNT{{M_REGIONS{32'd24}}}},
|
||||
// Secure master (fail operations based on awprot/arprot)
|
||||
// M_COUNT bits
|
||||
parameter M_SECURE = {M_COUNT{1'b0}}
|
||||
)
|
||||
(
|
||||
input wire logic clk,
|
||||
input wire logic rst,
|
||||
|
||||
/*
|
||||
* AXI4 slave interface
|
||||
*/
|
||||
taxi_axi_if.wr_slv s_axi_wr,
|
||||
taxi_axi_if.rd_slv s_axi_rd,
|
||||
|
||||
/*
|
||||
* AXI4 master interfaces
|
||||
*/
|
||||
taxi_axi_if.wr_mst m_axi_wr[M_COUNT],
|
||||
taxi_axi_if.rd_mst m_axi_rd[M_COUNT]
|
||||
);
|
||||
|
||||
taxi_axi_interconnect_1s_wr #(
|
||||
.M_COUNT(M_COUNT),
|
||||
.ADDR_W(ADDR_W),
|
||||
.M_REGIONS(M_REGIONS),
|
||||
.M_BASE_ADDR(M_BASE_ADDR),
|
||||
.M_ADDR_W(M_ADDR_W),
|
||||
.M_SECURE(M_SECURE)
|
||||
)
|
||||
wr_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI4 slave interface
|
||||
*/
|
||||
.s_axi_wr(s_axi_wr),
|
||||
|
||||
/*
|
||||
* AXI4 master interfaces
|
||||
*/
|
||||
.m_axi_wr(m_axi_wr)
|
||||
);
|
||||
|
||||
taxi_axi_interconnect_1s_rd #(
|
||||
.M_COUNT(M_COUNT),
|
||||
.ADDR_W(ADDR_W),
|
||||
.M_REGIONS(M_REGIONS),
|
||||
.M_BASE_ADDR(M_BASE_ADDR),
|
||||
.M_ADDR_W(M_ADDR_W),
|
||||
.M_SECURE(M_SECURE)
|
||||
)
|
||||
rd_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI4 slave interface
|
||||
*/
|
||||
.s_axi_rd(s_axi_rd),
|
||||
|
||||
/*
|
||||
* AXI4 master interfaces
|
||||
*/
|
||||
.m_axi_rd(m_axi_rd)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
6
src/axi/rtl/taxi_axi_interconnect_1s_rd.f
Normal file
6
src/axi/rtl/taxi_axi_interconnect_1s_rd.f
Normal file
@@ -0,0 +1,6 @@
|
||||
taxi_axi_interconnect_1s_rd.sv
|
||||
taxi_axi_interconnect_rd.sv
|
||||
taxi_axi_tie_rd.sv
|
||||
taxi_axi_if.sv
|
||||
../lib/taxi/src/prim/rtl/taxi_arbiter.sv
|
||||
../lib/taxi/src/prim/rtl/taxi_penc.sv
|
||||
105
src/axi/rtl/taxi_axi_interconnect_1s_rd.sv
Normal file
105
src/axi/rtl/taxi_axi_interconnect_1s_rd.sv
Normal file
@@ -0,0 +1,105 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2018-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* AXI4 interconnect
|
||||
*/
|
||||
module taxi_axi_interconnect_1s_rd #
|
||||
(
|
||||
// Number of AXI outputs (master interfaces)
|
||||
parameter M_COUNT = 4,
|
||||
// Address width in bits for address decoding
|
||||
parameter ADDR_W = 32,
|
||||
// TODO fix parametrization once verilator issue 5890 is fixed
|
||||
// Number of regions per master interface
|
||||
parameter M_REGIONS = 1,
|
||||
// Master interface base addresses
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_W bits
|
||||
// set to zero for default addressing based on M_ADDR_W
|
||||
parameter M_BASE_ADDR = '0,
|
||||
// Master interface address widths
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits
|
||||
parameter M_ADDR_W = {M_COUNT{{M_REGIONS{32'd24}}}},
|
||||
// Secure master (fail operations based on awprot/arprot)
|
||||
// M_COUNT bits
|
||||
parameter M_SECURE = {M_COUNT{1'b0}}
|
||||
)
|
||||
(
|
||||
input wire logic clk,
|
||||
input wire logic rst,
|
||||
|
||||
/*
|
||||
* AXI4 slave interface
|
||||
*/
|
||||
taxi_axi_if.rd_slv s_axi_rd,
|
||||
|
||||
/*
|
||||
* AXI4 master interfaces
|
||||
*/
|
||||
taxi_axi_if.rd_mst m_axi_rd[M_COUNT]
|
||||
);
|
||||
|
||||
taxi_axi_if #(
|
||||
.DATA_W(s_axi_rd.DATA_W),
|
||||
.ADDR_W(s_axi_rd.ADDR_W),
|
||||
.STRB_W(s_axi_rd.STRB_W),
|
||||
.ID_W(s_axi_rd.ID_W),
|
||||
.AWUSER_EN(s_axi_rd.AWUSER_EN),
|
||||
.AWUSER_W(s_axi_rd.AWUSER_W),
|
||||
.WUSER_EN(s_axi_rd.WUSER_EN),
|
||||
.WUSER_W(s_axi_rd.WUSER_W),
|
||||
.BUSER_EN(s_axi_rd.BUSER_EN),
|
||||
.BUSER_W(s_axi_rd.BUSER_W),
|
||||
.ARUSER_EN(s_axi_rd.ARUSER_EN),
|
||||
.ARUSER_W(s_axi_rd.ARUSER_W),
|
||||
.RUSER_EN(s_axi_rd.RUSER_EN),
|
||||
.RUSER_W(s_axi_rd.RUSER_W),
|
||||
.MAX_BURST_LEN(s_axi_rd.MAX_BURST_LEN),
|
||||
.NARROW_BURST_EN(s_axi_rd.NARROW_BURST_EN)
|
||||
)
|
||||
s_axi_rd_int[1]();
|
||||
|
||||
taxi_axi_tie_rd
|
||||
tie_inst (
|
||||
.s_axi_rd(s_axi_rd),
|
||||
.m_axi_rd(s_axi_rd_int[0])
|
||||
);
|
||||
|
||||
taxi_axi_interconnect_rd #(
|
||||
.S_COUNT(1),
|
||||
.M_COUNT(M_COUNT),
|
||||
.ADDR_W(ADDR_W),
|
||||
.M_REGIONS(M_REGIONS),
|
||||
.M_BASE_ADDR(M_BASE_ADDR),
|
||||
.M_ADDR_W(M_ADDR_W),
|
||||
.M_SECURE(M_SECURE)
|
||||
)
|
||||
rd_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI4 slave interface
|
||||
*/
|
||||
.s_axi_rd(s_axi_rd_int),
|
||||
|
||||
/*
|
||||
* AXI4 master interfaces
|
||||
*/
|
||||
.m_axi_rd(m_axi_rd)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
6
src/axi/rtl/taxi_axi_interconnect_1s_wr.f
Normal file
6
src/axi/rtl/taxi_axi_interconnect_1s_wr.f
Normal file
@@ -0,0 +1,6 @@
|
||||
taxi_axi_interconnect_1s_wr.sv
|
||||
taxi_axi_interconnect_wr.sv
|
||||
taxi_axi_tie_wr.sv
|
||||
taxi_axi_if.sv
|
||||
../lib/taxi/src/prim/rtl/taxi_arbiter.sv
|
||||
../lib/taxi/src/prim/rtl/taxi_penc.sv
|
||||
105
src/axi/rtl/taxi_axi_interconnect_1s_wr.sv
Normal file
105
src/axi/rtl/taxi_axi_interconnect_1s_wr.sv
Normal file
@@ -0,0 +1,105 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2018-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* AXI4 interconnect
|
||||
*/
|
||||
module taxi_axi_interconnect_1s_wr #
|
||||
(
|
||||
// Number of AXI outputs (master interfaces)
|
||||
parameter M_COUNT = 4,
|
||||
// Address width in bits for address decoding
|
||||
parameter ADDR_W = 32,
|
||||
// TODO fix parametrization once verilator issue 5890 is fixed
|
||||
// Number of regions per master interface
|
||||
parameter M_REGIONS = 1,
|
||||
// Master interface base addresses
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_W bits
|
||||
// set to zero for default addressing based on M_ADDR_W
|
||||
parameter M_BASE_ADDR = '0,
|
||||
// Master interface address widths
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits
|
||||
parameter M_ADDR_W = {M_COUNT{{M_REGIONS{32'd24}}}},
|
||||
// Secure master (fail operations based on awprot/arprot)
|
||||
// M_COUNT bits
|
||||
parameter M_SECURE = {M_COUNT{1'b0}}
|
||||
)
|
||||
(
|
||||
input wire logic clk,
|
||||
input wire logic rst,
|
||||
|
||||
/*
|
||||
* AXI4 slave interface
|
||||
*/
|
||||
taxi_axi_if.wr_slv s_axi_wr,
|
||||
|
||||
/*
|
||||
* AXI4 master interfaces
|
||||
*/
|
||||
taxi_axi_if.wr_mst m_axi_wr[M_COUNT]
|
||||
);
|
||||
|
||||
taxi_axi_if #(
|
||||
.DATA_W(s_axi_wr.DATA_W),
|
||||
.ADDR_W(s_axi_wr.ADDR_W),
|
||||
.STRB_W(s_axi_wr.STRB_W),
|
||||
.ID_W(s_axi_wr.ID_W),
|
||||
.AWUSER_EN(s_axi_wr.AWUSER_EN),
|
||||
.AWUSER_W(s_axi_wr.AWUSER_W),
|
||||
.WUSER_EN(s_axi_wr.WUSER_EN),
|
||||
.WUSER_W(s_axi_wr.WUSER_W),
|
||||
.BUSER_EN(s_axi_wr.BUSER_EN),
|
||||
.BUSER_W(s_axi_wr.BUSER_W),
|
||||
.ARUSER_EN(s_axi_wr.ARUSER_EN),
|
||||
.ARUSER_W(s_axi_wr.ARUSER_W),
|
||||
.RUSER_EN(s_axi_wr.RUSER_EN),
|
||||
.RUSER_W(s_axi_wr.RUSER_W),
|
||||
.MAX_BURST_LEN(s_axi_wr.MAX_BURST_LEN),
|
||||
.NARROW_BURST_EN(s_axi_wr.NARROW_BURST_EN)
|
||||
)
|
||||
s_axi_wr_int[1]();
|
||||
|
||||
taxi_axi_tie_wr
|
||||
tie_inst (
|
||||
.s_axi_wr(s_axi_wr),
|
||||
.m_axi_wr(s_axi_wr_int[0])
|
||||
);
|
||||
|
||||
taxi_axi_interconnect_wr #(
|
||||
.S_COUNT(1),
|
||||
.M_COUNT(M_COUNT),
|
||||
.ADDR_W(ADDR_W),
|
||||
.M_REGIONS(M_REGIONS),
|
||||
.M_BASE_ADDR(M_BASE_ADDR),
|
||||
.M_ADDR_W(M_ADDR_W),
|
||||
.M_SECURE(M_SECURE)
|
||||
)
|
||||
wr_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI4 slave interface
|
||||
*/
|
||||
.s_axi_wr(s_axi_wr_int),
|
||||
|
||||
/*
|
||||
* AXI4 master interfaces
|
||||
*/
|
||||
.m_axi_wr(m_axi_wr)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
4
src/axi/rtl/taxi_axi_tie.f
Normal file
4
src/axi/rtl/taxi_axi_tie.f
Normal file
@@ -0,0 +1,4 @@
|
||||
taxi_axi_tie.sv
|
||||
taxi_axi_tie_wr.sv
|
||||
taxi_axi_tie_rd.sv
|
||||
taxi_axi_if.sv
|
||||
61
src/axi/rtl/taxi_axi_tie.sv
Normal file
61
src/axi/rtl/taxi_axi_tie.sv
Normal file
@@ -0,0 +1,61 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* AXI4 tie
|
||||
*/
|
||||
module taxi_axi_tie
|
||||
(
|
||||
/*
|
||||
* AXI4 slave interface
|
||||
*/
|
||||
taxi_axi_if.wr_slv s_axi_wr,
|
||||
taxi_axi_if.rd_slv s_axi_rd,
|
||||
|
||||
/*
|
||||
* AXI4 master interface
|
||||
*/
|
||||
taxi_axi_if.wr_mst m_axi_wr,
|
||||
taxi_axi_if.rd_mst m_axi_rd
|
||||
);
|
||||
|
||||
taxi_axi_tie_wr
|
||||
wr_inst (
|
||||
/*
|
||||
* AXI4 slave interface
|
||||
*/
|
||||
.s_axi_wr(s_axi_wr),
|
||||
|
||||
/*
|
||||
* AXI4 master interface
|
||||
*/
|
||||
.m_axi_wr(m_axi_wr)
|
||||
);
|
||||
|
||||
taxi_axi_tie_rd
|
||||
rd_inst (
|
||||
/*
|
||||
* AXI4 slave interface
|
||||
*/
|
||||
.s_axi_rd(s_axi_rd),
|
||||
|
||||
/*
|
||||
* AXI4 master interface
|
||||
*/
|
||||
.m_axi_rd(m_axi_rd)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
72
src/axi/rtl/taxi_axi_tie_rd.sv
Normal file
72
src/axi/rtl/taxi_axi_tie_rd.sv
Normal file
@@ -0,0 +1,72 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* AXI4 tie (read)
|
||||
*/
|
||||
module taxi_axi_tie_rd
|
||||
(
|
||||
/*
|
||||
* AXI4 slave interface
|
||||
*/
|
||||
taxi_axi_if.rd_slv s_axi_rd,
|
||||
|
||||
/*
|
||||
* AXI4 master interface
|
||||
*/
|
||||
taxi_axi_if.rd_mst m_axi_rd
|
||||
);
|
||||
|
||||
// extract parameters
|
||||
localparam DATA_W = s_axi_rd.DATA_W;
|
||||
localparam ADDR_W = s_axi_rd.ADDR_W;
|
||||
localparam STRB_W = s_axi_rd.STRB_W;
|
||||
localparam ID_W = s_axi_rd.ID_W;
|
||||
localparam logic ARUSER_EN = s_axi_rd.ARUSER_EN && m_axi_rd.ARUSER_EN;
|
||||
localparam ARUSER_W = s_axi_rd.ARUSER_W;
|
||||
localparam logic RUSER_EN = s_axi_rd.RUSER_EN && m_axi_rd.RUSER_EN;
|
||||
localparam RUSER_W = s_axi_rd.RUSER_W;
|
||||
|
||||
// check configuration
|
||||
if (m_axi_rd.DATA_W != DATA_W)
|
||||
$fatal(0, "Error: Interface DATA_W parameter mismatch (instance %m)");
|
||||
|
||||
if (m_axi_rd.STRB_W != STRB_W)
|
||||
$fatal(0, "Error: Interface STRB_W parameter mismatch (instance %m)");
|
||||
|
||||
assign m_axi_rd.arid = s_axi_rd.arid;
|
||||
assign m_axi_rd.araddr = s_axi_rd.araddr;
|
||||
assign m_axi_rd.arlen = s_axi_rd.arlen;
|
||||
assign m_axi_rd.arsize = s_axi_rd.arsize;
|
||||
assign m_axi_rd.arburst = s_axi_rd.arburst;
|
||||
assign m_axi_rd.arlock = s_axi_rd.arlock;
|
||||
assign m_axi_rd.arcache = s_axi_rd.arcache;
|
||||
assign m_axi_rd.arprot = s_axi_rd.arprot;
|
||||
assign m_axi_rd.arqos = s_axi_rd.arqos;
|
||||
assign m_axi_rd.arregion = s_axi_rd.arregion;
|
||||
assign m_axi_rd.aruser = ARUSER_EN ? s_axi_rd.aruser : '0;
|
||||
assign m_axi_rd.arvalid = s_axi_rd.arvalid;
|
||||
assign s_axi_rd.arready = m_axi_rd.arready;
|
||||
|
||||
assign s_axi_rd.rid = m_axi_rd.rid;
|
||||
assign s_axi_rd.rdata = m_axi_rd.rdata;
|
||||
assign s_axi_rd.rresp = m_axi_rd.rresp;
|
||||
assign s_axi_rd.rlast = m_axi_rd.rlast;
|
||||
assign s_axi_rd.ruser = RUSER_EN ? m_axi_rd.ruser : '0;
|
||||
assign s_axi_rd.rvalid = m_axi_rd.rvalid;
|
||||
assign m_axi_rd.rready = s_axi_rd.rready;
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
80
src/axi/rtl/taxi_axi_tie_wr.sv
Normal file
80
src/axi/rtl/taxi_axi_tie_wr.sv
Normal file
@@ -0,0 +1,80 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* AXI4 tie (write)
|
||||
*/
|
||||
module taxi_axi_tie_wr
|
||||
(
|
||||
/*
|
||||
* AXI4 slave interface
|
||||
*/
|
||||
taxi_axi_if.wr_slv s_axi_wr,
|
||||
|
||||
/*
|
||||
* AXI4 master interface
|
||||
*/
|
||||
taxi_axi_if.wr_mst m_axi_wr
|
||||
);
|
||||
|
||||
// extract parameters
|
||||
localparam DATA_W = s_axi_wr.DATA_W;
|
||||
localparam ADDR_W = s_axi_wr.ADDR_W;
|
||||
localparam STRB_W = s_axi_wr.STRB_W;
|
||||
localparam ID_W = s_axi_wr.ID_W;
|
||||
localparam logic AWUSER_EN = s_axi_wr.AWUSER_EN && m_axi_wr.AWUSER_EN;
|
||||
localparam AWUSER_W = s_axi_wr.AWUSER_W;
|
||||
localparam logic WUSER_EN = s_axi_wr.WUSER_EN && m_axi_wr.WUSER_EN;
|
||||
localparam WUSER_W = s_axi_wr.WUSER_W;
|
||||
localparam logic BUSER_EN = s_axi_wr.BUSER_EN && m_axi_wr.BUSER_EN;
|
||||
localparam BUSER_W = s_axi_wr.BUSER_W;
|
||||
|
||||
// check configuration
|
||||
if (m_axi_wr.DATA_W != DATA_W)
|
||||
$fatal(0, "Error: Interface DATA_W parameter mismatch (instance %m)");
|
||||
|
||||
if (m_axi_wr.STRB_W != STRB_W)
|
||||
$fatal(0, "Error: Interface STRB_W parameter mismatch (instance %m)");
|
||||
|
||||
// bypass AW channel
|
||||
assign m_axi_wr.awid = s_axi_wr.awid;
|
||||
assign m_axi_wr.awaddr = s_axi_wr.awaddr;
|
||||
assign m_axi_wr.awlen = s_axi_wr.awlen;
|
||||
assign m_axi_wr.awsize = s_axi_wr.awsize;
|
||||
assign m_axi_wr.awburst = s_axi_wr.awburst;
|
||||
assign m_axi_wr.awlock = s_axi_wr.awlock;
|
||||
assign m_axi_wr.awcache = s_axi_wr.awcache;
|
||||
assign m_axi_wr.awprot = s_axi_wr.awprot;
|
||||
assign m_axi_wr.awqos = s_axi_wr.awqos;
|
||||
assign m_axi_wr.awregion = s_axi_wr.awregion;
|
||||
assign m_axi_wr.awuser = AWUSER_EN ? s_axi_wr.awuser : '0;
|
||||
assign m_axi_wr.awvalid = s_axi_wr.awvalid;
|
||||
assign s_axi_wr.awready = m_axi_wr.awready;
|
||||
|
||||
assign m_axi_wr.wdata = s_axi_wr.wdata;
|
||||
assign m_axi_wr.wstrb = s_axi_wr.wstrb;
|
||||
assign m_axi_wr.wlast = s_axi_wr.wlast;
|
||||
assign m_axi_wr.wuser = WUSER_EN ? s_axi_wr.wuser : '0;
|
||||
assign m_axi_wr.wvalid = s_axi_wr.wvalid;
|
||||
assign s_axi_wr.wready = m_axi_wr.wready;
|
||||
|
||||
assign s_axi_wr.bid = m_axi_wr.bid;
|
||||
assign s_axi_wr.bresp = m_axi_wr.bresp;
|
||||
assign s_axi_wr.buser = BUSER_EN ? m_axi_wr.buser : '0;
|
||||
assign s_axi_wr.bvalid = m_axi_wr.bvalid;
|
||||
assign m_axi_wr.bready = s_axi_wr.bready;
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
3
src/axi/rtl/taxi_axil_crossbar_1s.f
Normal file
3
src/axi/rtl/taxi_axil_crossbar_1s.f
Normal file
@@ -0,0 +1,3 @@
|
||||
taxi_axil_crossbar_1s.sv
|
||||
taxi_axil_crossbar_1s_wr.f
|
||||
taxi_axil_crossbar_1s_rd.f
|
||||
148
src/axi/rtl/taxi_axil_crossbar_1s.sv
Normal file
148
src/axi/rtl/taxi_axil_crossbar_1s.sv
Normal file
@@ -0,0 +1,148 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2021-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* AXI4 lite crossbar
|
||||
*/
|
||||
module taxi_axil_crossbar_1s #
|
||||
(
|
||||
// Number of AXI outputs (master interfaces)
|
||||
parameter M_COUNT = 4,
|
||||
// Address width in bits for address decoding
|
||||
parameter ADDR_W = 32,
|
||||
// TODO fix parametrization once verilator issue 5890 is fixed
|
||||
// Number of concurrent operations for each slave interface
|
||||
// 1 concatenated fields of 32 bits
|
||||
parameter S_ACCEPT = 32'd16,
|
||||
// Number of regions per master interface
|
||||
parameter M_REGIONS = 1,
|
||||
// Master interface base addresses
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_W bits
|
||||
// set to zero for default addressing based on M_ADDR_W
|
||||
parameter M_BASE_ADDR = '0,
|
||||
// Master interface address widths
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits
|
||||
parameter M_ADDR_W = {M_COUNT{{M_REGIONS{32'd24}}}},
|
||||
// Number of concurrent operations for each master interface
|
||||
// M_COUNT concatenated fields of 32 bits
|
||||
parameter M_ISSUE = {M_COUNT{32'd16}},
|
||||
// Secure master (fail operations based on awprot/arprot)
|
||||
// M_COUNT bits
|
||||
parameter M_SECURE = {M_COUNT{1'b0}},
|
||||
// Slave interface AW channel register type (input)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter S_AW_REG_TYPE = 2'd0,
|
||||
// Slave interface W channel register type (input)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter S_W_REG_TYPE = 2'd0,
|
||||
// Slave interface B channel register type (output)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter S_B_REG_TYPE = 2'd1,
|
||||
// Slave interface AR channel register type (input)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter S_AR_REG_TYPE = 2'd0,
|
||||
// Slave interface R channel register type (output)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter S_R_REG_TYPE = 2'd2,
|
||||
// Master interface AW channel register type (output)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter M_AW_REG_TYPE = {M_COUNT{2'd1}},
|
||||
// Master interface W channel register type (output)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter M_W_REG_TYPE = {M_COUNT{2'd2}},
|
||||
// Master interface B channel register type (input)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter M_B_REG_TYPE = {M_COUNT{2'd0}},
|
||||
// Master interface AR channel register type (output)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter M_AR_REG_TYPE = {M_COUNT{2'd1}},
|
||||
// Master interface R channel register type (input)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter M_R_REG_TYPE = {M_COUNT{2'd0}}
|
||||
)
|
||||
(
|
||||
input wire logic clk,
|
||||
input wire logic rst,
|
||||
|
||||
/*
|
||||
* AXI4-lite slave interface
|
||||
*/
|
||||
taxi_axil_if.wr_slv s_axil_wr,
|
||||
taxi_axil_if.rd_slv s_axil_rd,
|
||||
|
||||
/*
|
||||
* AXI4-lite master interfaces
|
||||
*/
|
||||
taxi_axil_if.wr_mst m_axil_wr[M_COUNT],
|
||||
taxi_axil_if.rd_mst m_axil_rd[M_COUNT]
|
||||
);
|
||||
|
||||
taxi_axil_crossbar_1s_wr #(
|
||||
.M_COUNT(M_COUNT),
|
||||
.ADDR_W(ADDR_W),
|
||||
.S_ACCEPT(S_ACCEPT),
|
||||
.M_REGIONS(M_REGIONS),
|
||||
.M_BASE_ADDR(M_BASE_ADDR),
|
||||
.M_ADDR_W(M_ADDR_W),
|
||||
.M_ISSUE(M_ISSUE),
|
||||
.M_SECURE(M_SECURE),
|
||||
.S_AW_REG_TYPE(S_AW_REG_TYPE),
|
||||
.S_W_REG_TYPE(S_W_REG_TYPE),
|
||||
.S_B_REG_TYPE(S_B_REG_TYPE)
|
||||
)
|
||||
wr_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI lite slave interface
|
||||
*/
|
||||
.s_axil_wr(s_axil_wr),
|
||||
|
||||
/*
|
||||
* AXI lite master interfaces
|
||||
*/
|
||||
.m_axil_wr(m_axil_wr)
|
||||
);
|
||||
|
||||
taxi_axil_crossbar_1s_rd #(
|
||||
.M_COUNT(M_COUNT),
|
||||
.ADDR_W(ADDR_W),
|
||||
.S_ACCEPT(S_ACCEPT),
|
||||
.M_REGIONS(M_REGIONS),
|
||||
.M_BASE_ADDR(M_BASE_ADDR),
|
||||
.M_ADDR_W(M_ADDR_W),
|
||||
.M_ISSUE(M_ISSUE),
|
||||
.M_SECURE(M_SECURE),
|
||||
.S_AR_REG_TYPE(S_AR_REG_TYPE),
|
||||
.S_R_REG_TYPE(S_R_REG_TYPE)
|
||||
)
|
||||
rd_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI lite slave interface
|
||||
*/
|
||||
.s_axil_rd(s_axil_rd),
|
||||
|
||||
/*
|
||||
* AXI lite master interfaces
|
||||
*/
|
||||
.m_axil_rd(m_axil_rd)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
3
src/axi/rtl/taxi_axil_crossbar_1s_rd.f
Normal file
3
src/axi/rtl/taxi_axil_crossbar_1s_rd.f
Normal file
@@ -0,0 +1,3 @@
|
||||
taxi_axil_crossbar_1s_rd.sv
|
||||
taxi_axil_crossbar_rd.f
|
||||
taxi_axil_tie_rd.sv
|
||||
124
src/axi/rtl/taxi_axil_crossbar_1s_rd.sv
Normal file
124
src/axi/rtl/taxi_axil_crossbar_1s_rd.sv
Normal file
@@ -0,0 +1,124 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2021-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* AXI4 lite crossbar
|
||||
*/
|
||||
module taxi_axil_crossbar_1s_rd #
|
||||
(
|
||||
// Number of AXI outputs (master interfaces)
|
||||
parameter M_COUNT = 4,
|
||||
// Address width in bits for address decoding
|
||||
parameter ADDR_W = 32,
|
||||
// TODO fix parametrization once verilator issue 5890 is fixed
|
||||
// Number of concurrent operations for each slave interface
|
||||
// 1 concatenated fields of 32 bits
|
||||
parameter S_ACCEPT = 32'd16,
|
||||
// Number of regions per master interface
|
||||
parameter M_REGIONS = 1,
|
||||
// Master interface base addresses
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_W bits
|
||||
// set to zero for default addressing based on M_ADDR_W
|
||||
parameter M_BASE_ADDR = '0,
|
||||
// Master interface address widths
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits
|
||||
parameter M_ADDR_W = {M_COUNT{{M_REGIONS{32'd24}}}},
|
||||
// Number of concurrent operations for each master interface
|
||||
// M_COUNT concatenated fields of 32 bits
|
||||
parameter M_ISSUE = {M_COUNT{32'd16}},
|
||||
// Secure master (fail operations based on awprot/arprot)
|
||||
// M_COUNT bits
|
||||
parameter M_SECURE = {M_COUNT{1'b0}},
|
||||
// Slave interface AR channel register type (input)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter S_AR_REG_TYPE = 2'd0,
|
||||
// Slave interface R channel register type (output)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter S_R_REG_TYPE = 2'd2,
|
||||
// Master interface AR channel register type (output)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter M_AR_REG_TYPE = {M_COUNT{2'd1}},
|
||||
// Master interface R channel register type (input)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter M_R_REG_TYPE = {M_COUNT{2'd0}}
|
||||
)
|
||||
(
|
||||
input wire logic clk,
|
||||
input wire logic rst,
|
||||
|
||||
/*
|
||||
* AXI4-lite slave interface
|
||||
*/
|
||||
taxi_axil_if.rd_slv s_axil_rd,
|
||||
|
||||
/*
|
||||
* AXI4-lite master interfaces
|
||||
*/
|
||||
taxi_axil_if.rd_mst m_axil_rd[M_COUNT]
|
||||
);
|
||||
|
||||
taxi_axil_if #(
|
||||
.DATA_W(s_axil_rd.DATA_W),
|
||||
.ADDR_W(s_axil_rd.ADDR_W),
|
||||
.STRB_W(s_axil_rd.STRB_W),
|
||||
.AWUSER_EN(s_axil_rd.AWUSER_EN),
|
||||
.AWUSER_W(s_axil_rd.AWUSER_W),
|
||||
.WUSER_EN(s_axil_rd.WUSER_EN),
|
||||
.WUSER_W(s_axil_rd.WUSER_W),
|
||||
.BUSER_EN(s_axil_rd.BUSER_EN),
|
||||
.BUSER_W(s_axil_rd.BUSER_W),
|
||||
.ARUSER_EN(s_axil_rd.ARUSER_EN),
|
||||
.ARUSER_W(s_axil_rd.ARUSER_W),
|
||||
.RUSER_EN(s_axil_rd.RUSER_EN),
|
||||
.RUSER_W(s_axil_rd.RUSER_W)
|
||||
)
|
||||
s_axil_rd_int[1]();
|
||||
|
||||
taxi_axil_tie_rd
|
||||
tie_inst (
|
||||
.s_axil_rd(s_axil_rd),
|
||||
.m_axil_rd(s_axil_rd_int[0])
|
||||
);
|
||||
|
||||
taxi_axil_crossbar_rd #(
|
||||
.S_COUNT(1),
|
||||
.M_COUNT(M_COUNT),
|
||||
.ADDR_W(ADDR_W),
|
||||
.S_ACCEPT(S_ACCEPT),
|
||||
.M_REGIONS(M_REGIONS),
|
||||
.M_BASE_ADDR(M_BASE_ADDR),
|
||||
.M_ADDR_W(M_ADDR_W),
|
||||
.M_ISSUE(M_ISSUE),
|
||||
.M_SECURE(M_SECURE),
|
||||
.S_AR_REG_TYPE(S_AR_REG_TYPE),
|
||||
.S_R_REG_TYPE(S_R_REG_TYPE)
|
||||
)
|
||||
rd_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI lite slave interface
|
||||
*/
|
||||
.s_axil_rd(s_axil_rd_int),
|
||||
|
||||
/*
|
||||
* AXI lite master interfaces
|
||||
*/
|
||||
.m_axil_rd(m_axil_rd)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
3
src/axi/rtl/taxi_axil_crossbar_1s_wr.f
Normal file
3
src/axi/rtl/taxi_axil_crossbar_1s_wr.f
Normal file
@@ -0,0 +1,3 @@
|
||||
taxi_axil_crossbar_1s_wr.sv
|
||||
taxi_axil_crossbar_wr.f
|
||||
taxi_axil_tie_wr.sv
|
||||
131
src/axi/rtl/taxi_axil_crossbar_1s_wr.sv
Normal file
131
src/axi/rtl/taxi_axil_crossbar_1s_wr.sv
Normal file
@@ -0,0 +1,131 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2021-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* AXI4 lite crossbar
|
||||
*/
|
||||
module taxi_axil_crossbar_1s_wr #
|
||||
(
|
||||
// Number of AXI outputs (master interfaces)
|
||||
parameter M_COUNT = 4,
|
||||
// Address width in bits for address decoding
|
||||
parameter ADDR_W = 32,
|
||||
// TODO fix parametrization once verilator issue 5890 is fixed
|
||||
// Number of concurrent operations for each slave interface
|
||||
// 1 concatenated fields of 32 bits
|
||||
parameter S_ACCEPT = 32'd16,
|
||||
// Number of regions per master interface
|
||||
parameter M_REGIONS = 1,
|
||||
// Master interface base addresses
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_W bits
|
||||
// set to zero for default addressing based on M_ADDR_W
|
||||
parameter M_BASE_ADDR = '0,
|
||||
// Master interface address widths
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits
|
||||
parameter M_ADDR_W = {M_COUNT{{M_REGIONS{32'd24}}}},
|
||||
// Number of concurrent operations for each master interface
|
||||
// M_COUNT concatenated fields of 32 bits
|
||||
parameter M_ISSUE = {M_COUNT{32'd16}},
|
||||
// Secure master (fail operations based on awprot/arprot)
|
||||
// M_COUNT bits
|
||||
parameter M_SECURE = {M_COUNT{1'b0}},
|
||||
// Slave interface AW channel register type (input)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter S_AW_REG_TYPE = 2'd0,
|
||||
// Slave interface W channel register type (input)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter S_W_REG_TYPE = 2'd0,
|
||||
// Slave interface B channel register type (output)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter S_B_REG_TYPE = 2'd1,
|
||||
// Master interface AW channel register type (output)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter M_AW_REG_TYPE = {M_COUNT{2'd1}},
|
||||
// Master interface W channel register type (output)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter M_W_REG_TYPE = {M_COUNT{2'd2}},
|
||||
// Master interface B channel register type (input)
|
||||
// 0 to bypass, 1 for simple buffer, 2 for skid buffer
|
||||
parameter M_B_REG_TYPE = {M_COUNT{2'd0}}
|
||||
)
|
||||
(
|
||||
input wire logic clk,
|
||||
input wire logic rst,
|
||||
|
||||
/*
|
||||
* AXI4-lite slave interface
|
||||
*/
|
||||
taxi_axil_if.wr_slv s_axil_wr,
|
||||
|
||||
/*
|
||||
* AXI4-lite master interfaces
|
||||
*/
|
||||
taxi_axil_if.wr_mst m_axil_wr[M_COUNT]
|
||||
);
|
||||
|
||||
taxi_axil_if #(
|
||||
.DATA_W(s_axil_wr.DATA_W),
|
||||
.ADDR_W(s_axil_wr.ADDR_W),
|
||||
.STRB_W(s_axil_wr.STRB_W),
|
||||
.AWUSER_EN(s_axil_wr.AWUSER_EN),
|
||||
.AWUSER_W(s_axil_wr.AWUSER_W),
|
||||
.WUSER_EN(s_axil_wr.WUSER_EN),
|
||||
.WUSER_W(s_axil_wr.WUSER_W),
|
||||
.BUSER_EN(s_axil_wr.BUSER_EN),
|
||||
.BUSER_W(s_axil_wr.BUSER_W),
|
||||
.ARUSER_EN(s_axil_wr.ARUSER_EN),
|
||||
.ARUSER_W(s_axil_wr.ARUSER_W),
|
||||
.RUSER_EN(s_axil_wr.RUSER_EN),
|
||||
.RUSER_W(s_axil_wr.RUSER_W)
|
||||
)
|
||||
s_axil_wr_int[1]();
|
||||
|
||||
taxi_axil_tie_wr
|
||||
tie_inst (
|
||||
.s_axil_wr(s_axil_wr),
|
||||
.m_axil_wr(s_axil_wr_int[0])
|
||||
);
|
||||
|
||||
taxi_axil_crossbar_wr #(
|
||||
.S_COUNT(1),
|
||||
.M_COUNT(M_COUNT),
|
||||
.ADDR_W(ADDR_W),
|
||||
.S_ACCEPT(S_ACCEPT),
|
||||
.M_REGIONS(M_REGIONS),
|
||||
.M_BASE_ADDR(M_BASE_ADDR),
|
||||
.M_ADDR_W(M_ADDR_W),
|
||||
.M_ISSUE(M_ISSUE),
|
||||
.M_SECURE(M_SECURE),
|
||||
.S_AW_REG_TYPE(S_AW_REG_TYPE),
|
||||
.S_W_REG_TYPE(S_W_REG_TYPE),
|
||||
.S_B_REG_TYPE(S_B_REG_TYPE)
|
||||
)
|
||||
wr_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI lite slave interface
|
||||
*/
|
||||
.s_axil_wr(s_axil_wr_int),
|
||||
|
||||
/*
|
||||
* AXI lite master interfaces
|
||||
*/
|
||||
.m_axil_wr(m_axil_wr)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
3
src/axi/rtl/taxi_axil_interconnect_1s.f
Normal file
3
src/axi/rtl/taxi_axil_interconnect_1s.f
Normal file
@@ -0,0 +1,3 @@
|
||||
taxi_axil_interconnect_1s.sv
|
||||
taxi_axil_interconnect_1s_wr.f
|
||||
taxi_axil_interconnect_1s_rd.f
|
||||
107
src/axi/rtl/taxi_axil_interconnect_1s.sv
Normal file
107
src/axi/rtl/taxi_axil_interconnect_1s.sv
Normal file
@@ -0,0 +1,107 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2021-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* AXI4 lite interconnect
|
||||
*/
|
||||
module taxi_axil_interconnect_1s #
|
||||
(
|
||||
// Number of AXI outputs (master interfaces)
|
||||
parameter M_COUNT = 4,
|
||||
// Address width in bits for address decoding
|
||||
parameter ADDR_W = 32,
|
||||
// TODO fix parametrization once verilator issue 5890 is fixed
|
||||
// Number of concurrent operations for each slave interface
|
||||
// 1 concatenated fields of 32 bits
|
||||
// Number of regions per master interface
|
||||
parameter M_REGIONS = 1,
|
||||
// Master interface base addresses
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_W bits
|
||||
// set to zero for default addressing based on M_ADDR_W
|
||||
parameter M_BASE_ADDR = '0,
|
||||
// Master interface address widths
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits
|
||||
parameter M_ADDR_W = {M_COUNT{{M_REGIONS{32'd24}}}},
|
||||
// Number of concurrent operations for each master interface
|
||||
// M_COUNT concatenated fields of 32 bits
|
||||
// Secure master (fail operations based on awprot/arprot)
|
||||
// M_COUNT bits
|
||||
parameter M_SECURE = {M_COUNT{1'b0}}
|
||||
)
|
||||
(
|
||||
input wire logic clk,
|
||||
input wire logic rst,
|
||||
|
||||
/*
|
||||
* AXI4-lite slave interface
|
||||
*/
|
||||
taxi_axil_if.wr_slv s_axil_wr,
|
||||
taxi_axil_if.rd_slv s_axil_rd,
|
||||
|
||||
/*
|
||||
* AXI4-lite master interfaces
|
||||
*/
|
||||
taxi_axil_if.wr_mst m_axil_wr[M_COUNT],
|
||||
taxi_axil_if.rd_mst m_axil_rd[M_COUNT]
|
||||
);
|
||||
|
||||
taxi_axil_interconnect_1s_wr #(
|
||||
.M_COUNT(M_COUNT),
|
||||
.ADDR_W(ADDR_W),
|
||||
.M_REGIONS(M_REGIONS),
|
||||
.M_BASE_ADDR(M_BASE_ADDR),
|
||||
.M_ADDR_W(M_ADDR_W),
|
||||
.M_SECURE(M_SECURE)
|
||||
)
|
||||
wr_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI4-lite slave interface
|
||||
*/
|
||||
.s_axil_wr(s_axil_wr),
|
||||
|
||||
/*
|
||||
* AXI4-lite master interfaces
|
||||
*/
|
||||
.m_axil_wr(m_axil_wr)
|
||||
);
|
||||
|
||||
taxi_axil_interconnect_1s_rd #(
|
||||
.M_COUNT(M_COUNT),
|
||||
.ADDR_W(ADDR_W),
|
||||
.M_REGIONS(M_REGIONS),
|
||||
.M_BASE_ADDR(M_BASE_ADDR),
|
||||
.M_ADDR_W(M_ADDR_W),
|
||||
.M_SECURE(M_SECURE)
|
||||
)
|
||||
rd_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI4-lite slave interface
|
||||
*/
|
||||
.s_axil_rd(s_axil_rd),
|
||||
|
||||
/*
|
||||
* AXI4-lite master interfaces
|
||||
*/
|
||||
.m_axil_rd(m_axil_rd)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
6
src/axi/rtl/taxi_axil_interconnect_1s_rd.f
Normal file
6
src/axi/rtl/taxi_axil_interconnect_1s_rd.f
Normal file
@@ -0,0 +1,6 @@
|
||||
taxi_axil_interconnect_1s_rd.sv
|
||||
taxi_axil_interconnect_rd.sv
|
||||
taxi_axil_tie_rd.sv
|
||||
taxi_axil_if.sv
|
||||
../lib/taxi/src/prim/rtl/taxi_arbiter.sv
|
||||
../lib/taxi/src/prim/rtl/taxi_penc.sv
|
||||
106
src/axi/rtl/taxi_axil_interconnect_1s_rd.sv
Normal file
106
src/axi/rtl/taxi_axil_interconnect_1s_rd.sv
Normal file
@@ -0,0 +1,106 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2021-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* AXI4 lite interconnect
|
||||
*/
|
||||
module taxi_axil_interconnect_1s_rd #
|
||||
(
|
||||
// Number of AXI outputs (master interfaces)
|
||||
parameter M_COUNT = 4,
|
||||
// Address width in bits for address decoding
|
||||
parameter ADDR_W = 32,
|
||||
// TODO fix parametrization once verilator issue 5890 is fixed
|
||||
// Number of concurrent operations for each slave interface
|
||||
// 1 concatenated fields of 32 bits
|
||||
// Number of regions per master interface
|
||||
parameter M_REGIONS = 1,
|
||||
// Master interface base addresses
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_W bits
|
||||
// set to zero for default addressing based on M_ADDR_W
|
||||
parameter M_BASE_ADDR = '0,
|
||||
// Master interface address widths
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits
|
||||
parameter M_ADDR_W = {M_COUNT{{M_REGIONS{32'd24}}}},
|
||||
// Number of concurrent operations for each master interface
|
||||
// M_COUNT concatenated fields of 32 bits
|
||||
// Secure master (fail operations based on awprot/arprot)
|
||||
// M_COUNT bits
|
||||
parameter M_SECURE = {M_COUNT{1'b0}}
|
||||
)
|
||||
(
|
||||
input wire logic clk,
|
||||
input wire logic rst,
|
||||
|
||||
/*
|
||||
* AXI4-lite slave interface
|
||||
*/
|
||||
taxi_axil_if.rd_slv s_axil_rd,
|
||||
|
||||
/*
|
||||
* AXI4-lite master interfaces
|
||||
*/
|
||||
taxi_axil_if.rd_mst m_axil_rd[M_COUNT]
|
||||
);
|
||||
|
||||
taxi_axil_if #(
|
||||
.DATA_W(s_axil_rd.DATA_W),
|
||||
.ADDR_W(s_axil_rd.ADDR_W),
|
||||
.STRB_W(s_axil_rd.STRB_W),
|
||||
.AWUSER_EN(s_axil_rd.AWUSER_EN),
|
||||
.AWUSER_W(s_axil_rd.AWUSER_W),
|
||||
.WUSER_EN(s_axil_rd.WUSER_EN),
|
||||
.WUSER_W(s_axil_rd.WUSER_W),
|
||||
.BUSER_EN(s_axil_rd.BUSER_EN),
|
||||
.BUSER_W(s_axil_rd.BUSER_W),
|
||||
.ARUSER_EN(s_axil_rd.ARUSER_EN),
|
||||
.ARUSER_W(s_axil_rd.ARUSER_W),
|
||||
.RUSER_EN(s_axil_rd.RUSER_EN),
|
||||
.RUSER_W(s_axil_rd.RUSER_W)
|
||||
)
|
||||
s_axil_rd_int[1]();
|
||||
|
||||
taxi_axil_tie_rd
|
||||
tie_inst (
|
||||
.s_axil_rd(s_axil_rd),
|
||||
.m_axil_rd(s_axil_rd_int[0])
|
||||
);
|
||||
|
||||
taxi_axil_interconnect_rd #(
|
||||
.S_COUNT(1),
|
||||
.M_COUNT(M_COUNT),
|
||||
.ADDR_W(ADDR_W),
|
||||
.M_REGIONS(M_REGIONS),
|
||||
.M_BASE_ADDR(M_BASE_ADDR),
|
||||
.M_ADDR_W(M_ADDR_W),
|
||||
.M_SECURE(M_SECURE)
|
||||
)
|
||||
rd_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI4-lite slave interface
|
||||
*/
|
||||
.s_axil_rd(s_axil_rd_int),
|
||||
|
||||
/*
|
||||
* AXI4-lite master interfaces
|
||||
*/
|
||||
.m_axil_rd(m_axil_rd)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
6
src/axi/rtl/taxi_axil_interconnect_1s_wr.f
Normal file
6
src/axi/rtl/taxi_axil_interconnect_1s_wr.f
Normal file
@@ -0,0 +1,6 @@
|
||||
taxi_axil_interconnect_1s_wr.sv
|
||||
taxi_axil_interconnect_wr.sv
|
||||
taxi_axil_tie_wr.sv
|
||||
taxi_axil_if.sv
|
||||
../lib/taxi/src/prim/rtl/taxi_arbiter.sv
|
||||
../lib/taxi/src/prim/rtl/taxi_penc.sv
|
||||
106
src/axi/rtl/taxi_axil_interconnect_1s_wr.sv
Normal file
106
src/axi/rtl/taxi_axil_interconnect_1s_wr.sv
Normal file
@@ -0,0 +1,106 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2021-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* AXI4 lite interconnect
|
||||
*/
|
||||
module taxi_axil_interconnect_1s_wr #
|
||||
(
|
||||
// Number of AXI outputs (master interfaces)
|
||||
parameter M_COUNT = 4,
|
||||
// Address width in bits for address decoding
|
||||
parameter ADDR_W = 32,
|
||||
// TODO fix parametrization once verilator issue 5890 is fixed
|
||||
// Number of concurrent operations for each slave interface
|
||||
// 1 concatenated fields of 32 bits
|
||||
// Number of regions per master interface
|
||||
parameter M_REGIONS = 1,
|
||||
// Master interface base addresses
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_W bits
|
||||
// set to zero for default addressing based on M_ADDR_W
|
||||
parameter M_BASE_ADDR = '0,
|
||||
// Master interface address widths
|
||||
// M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits
|
||||
parameter M_ADDR_W = {M_COUNT{{M_REGIONS{32'd24}}}},
|
||||
// Number of concurrent operations for each master interface
|
||||
// M_COUNT concatenated fields of 32 bits
|
||||
// Secure master (fail operations based on awprot/arprot)
|
||||
// M_COUNT bits
|
||||
parameter M_SECURE = {M_COUNT{1'b0}}
|
||||
)
|
||||
(
|
||||
input wire logic clk,
|
||||
input wire logic rst,
|
||||
|
||||
/*
|
||||
* AXI4-lite slave interface
|
||||
*/
|
||||
taxi_axil_if.wr_slv s_axil_wr,
|
||||
|
||||
/*
|
||||
* AXI4-lite master interfaces
|
||||
*/
|
||||
taxi_axil_if.wr_mst m_axil_wr[M_COUNT]
|
||||
);
|
||||
|
||||
taxi_axil_if #(
|
||||
.DATA_W(s_axil_wr.DATA_W),
|
||||
.ADDR_W(s_axil_wr.ADDR_W),
|
||||
.STRB_W(s_axil_wr.STRB_W),
|
||||
.AWUSER_EN(s_axil_wr.AWUSER_EN),
|
||||
.AWUSER_W(s_axil_wr.AWUSER_W),
|
||||
.WUSER_EN(s_axil_wr.WUSER_EN),
|
||||
.WUSER_W(s_axil_wr.WUSER_W),
|
||||
.BUSER_EN(s_axil_wr.BUSER_EN),
|
||||
.BUSER_W(s_axil_wr.BUSER_W),
|
||||
.ARUSER_EN(s_axil_wr.ARUSER_EN),
|
||||
.ARUSER_W(s_axil_wr.ARUSER_W),
|
||||
.RUSER_EN(s_axil_wr.RUSER_EN),
|
||||
.RUSER_W(s_axil_wr.RUSER_W)
|
||||
)
|
||||
s_axil_wr_int[1]();
|
||||
|
||||
taxi_axil_tie_wr
|
||||
tie_inst (
|
||||
.s_axil_wr(s_axil_wr),
|
||||
.m_axil_wr(s_axil_wr_int[0])
|
||||
);
|
||||
|
||||
taxi_axil_interconnect_wr #(
|
||||
.S_COUNT(1),
|
||||
.M_COUNT(M_COUNT),
|
||||
.ADDR_W(ADDR_W),
|
||||
.M_REGIONS(M_REGIONS),
|
||||
.M_BASE_ADDR(M_BASE_ADDR),
|
||||
.M_ADDR_W(M_ADDR_W),
|
||||
.M_SECURE(M_SECURE)
|
||||
)
|
||||
wr_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI4-lite slave interface
|
||||
*/
|
||||
.s_axil_wr(s_axil_wr_int),
|
||||
|
||||
/*
|
||||
* AXI4-lite master interfaces
|
||||
*/
|
||||
.m_axil_wr(m_axil_wr)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
4
src/axi/rtl/taxi_axil_tie.f
Normal file
4
src/axi/rtl/taxi_axil_tie.f
Normal file
@@ -0,0 +1,4 @@
|
||||
taxi_axil_tie.sv
|
||||
taxi_axil_tie_wr.sv
|
||||
taxi_axil_tie_rd.sv
|
||||
taxi_axil_if.sv
|
||||
61
src/axi/rtl/taxi_axil_tie.sv
Normal file
61
src/axi/rtl/taxi_axil_tie.sv
Normal file
@@ -0,0 +1,61 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* AXI4 lite tie
|
||||
*/
|
||||
module taxi_axil_tie
|
||||
(
|
||||
/*
|
||||
* AXI4 lite slave interface
|
||||
*/
|
||||
taxi_axil_if.wr_slv s_axil_wr,
|
||||
taxi_axil_if.rd_slv s_axil_rd,
|
||||
|
||||
/*
|
||||
* AXI4 lite master interface
|
||||
*/
|
||||
taxi_axil_if.wr_mst m_axil_wr,
|
||||
taxi_axil_if.rd_mst m_axil_rd
|
||||
);
|
||||
|
||||
taxi_axil_tie_wr
|
||||
wr_inst (
|
||||
/*
|
||||
* AXI4 lite slave interface
|
||||
*/
|
||||
.s_axil_wr(s_axil_wr),
|
||||
|
||||
/*
|
||||
* AXI4 lite master interface
|
||||
*/
|
||||
.m_axil_wr(m_axil_wr)
|
||||
);
|
||||
|
||||
taxi_axil_tie_rd
|
||||
rd_inst (
|
||||
/*
|
||||
* AXI4 lite slave interface
|
||||
*/
|
||||
.s_axil_rd(s_axil_rd),
|
||||
|
||||
/*
|
||||
* AXI4 lite master interface
|
||||
*/
|
||||
.m_axil_rd(m_axil_rd)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
61
src/axi/rtl/taxi_axil_tie_rd.sv
Normal file
61
src/axi/rtl/taxi_axil_tie_rd.sv
Normal file
@@ -0,0 +1,61 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* AXI4 lite tie (read)
|
||||
*/
|
||||
module taxi_axil_tie_rd
|
||||
(
|
||||
/*
|
||||
* AXI4 lite slave interface
|
||||
*/
|
||||
taxi_axil_if.rd_slv s_axil_rd,
|
||||
|
||||
/*
|
||||
* AXI4 lite master interface
|
||||
*/
|
||||
taxi_axil_if.rd_mst m_axil_rd
|
||||
);
|
||||
|
||||
// extract parameters
|
||||
localparam DATA_W = s_axil_rd.DATA_W;
|
||||
localparam ADDR_W = s_axil_rd.ADDR_W;
|
||||
localparam STRB_W = s_axil_rd.STRB_W;
|
||||
localparam logic ARUSER_EN = s_axil_rd.ARUSER_EN && m_axil_rd.ARUSER_EN;
|
||||
localparam ARUSER_W = s_axil_rd.ARUSER_W;
|
||||
localparam logic RUSER_EN = s_axil_rd.RUSER_EN && m_axil_rd.RUSER_EN;
|
||||
localparam RUSER_W = s_axil_rd.RUSER_W;
|
||||
|
||||
// check configuration
|
||||
if (m_axil_rd.DATA_W != DATA_W)
|
||||
$fatal(0, "Error: Interface DATA_W parameter mismatch (instance %m)");
|
||||
|
||||
if (m_axil_rd.STRB_W != STRB_W)
|
||||
$fatal(0, "Error: Interface STRB_W parameter mismatch (instance %m)");
|
||||
|
||||
assign m_axil_rd.araddr = s_axil_rd.araddr;
|
||||
assign m_axil_rd.arprot = s_axil_rd.arprot;
|
||||
assign m_axil_rd.aruser = ARUSER_EN ? s_axil_rd.aruser : '0;
|
||||
assign m_axil_rd.arvalid = s_axil_rd.arvalid;
|
||||
assign s_axil_rd.arready = m_axil_rd.arready;
|
||||
|
||||
assign s_axil_rd.rdata = m_axil_rd.rdata;
|
||||
assign s_axil_rd.rresp = m_axil_rd.rresp;
|
||||
assign s_axil_rd.ruser = RUSER_EN ? m_axil_rd.ruser : '0;
|
||||
assign s_axil_rd.rvalid = m_axil_rd.rvalid;
|
||||
assign m_axil_rd.rready = s_axil_rd.rready;
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
69
src/axi/rtl/taxi_axil_tie_wr.sv
Normal file
69
src/axi/rtl/taxi_axil_tie_wr.sv
Normal file
@@ -0,0 +1,69 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* AXI4 lite tie (write)
|
||||
*/
|
||||
module taxi_axil_tie_wr
|
||||
(
|
||||
/*
|
||||
* AXI4 lite slave interface
|
||||
*/
|
||||
taxi_axil_if.wr_slv s_axil_wr,
|
||||
|
||||
/*
|
||||
* AXI4 lite master interface
|
||||
*/
|
||||
taxi_axil_if.wr_mst m_axil_wr
|
||||
);
|
||||
|
||||
// extract parameters
|
||||
localparam DATA_W = s_axil_wr.DATA_W;
|
||||
localparam ADDR_W = s_axil_wr.ADDR_W;
|
||||
localparam STRB_W = s_axil_wr.STRB_W;
|
||||
localparam logic AWUSER_EN = s_axil_wr.AWUSER_EN && m_axil_wr.AWUSER_EN;
|
||||
localparam AWUSER_W = s_axil_wr.AWUSER_W;
|
||||
localparam logic WUSER_EN = s_axil_wr.WUSER_EN && m_axil_wr.WUSER_EN;
|
||||
localparam WUSER_W = s_axil_wr.WUSER_W;
|
||||
localparam logic BUSER_EN = s_axil_wr.BUSER_EN && m_axil_wr.BUSER_EN;
|
||||
localparam BUSER_W = s_axil_wr.BUSER_W;
|
||||
|
||||
// check configuration
|
||||
if (m_axil_wr.DATA_W != DATA_W)
|
||||
$fatal(0, "Error: Interface DATA_W parameter mismatch (instance %m)");
|
||||
|
||||
if (m_axil_wr.STRB_W != STRB_W)
|
||||
$fatal(0, "Error: Interface STRB_W parameter mismatch (instance %m)");
|
||||
|
||||
// bypass AW channel
|
||||
assign m_axil_wr.awaddr = s_axil_wr.awaddr;
|
||||
assign m_axil_wr.awprot = s_axil_wr.awprot;
|
||||
assign m_axil_wr.awuser = AWUSER_EN ? s_axil_wr.awuser : '0;
|
||||
assign m_axil_wr.awvalid = s_axil_wr.awvalid;
|
||||
assign s_axil_wr.awready = m_axil_wr.awready;
|
||||
|
||||
assign m_axil_wr.wdata = s_axil_wr.wdata;
|
||||
assign m_axil_wr.wstrb = s_axil_wr.wstrb;
|
||||
assign m_axil_wr.wuser = WUSER_EN ? s_axil_wr.wuser : '0;
|
||||
assign m_axil_wr.wvalid = s_axil_wr.wvalid;
|
||||
assign s_axil_wr.wready = m_axil_wr.wready;
|
||||
|
||||
assign s_axil_wr.bresp = m_axil_wr.bresp;
|
||||
assign s_axil_wr.buser = BUSER_EN ? m_axil_wr.buser : '0;
|
||||
assign s_axil_wr.bvalid = m_axil_wr.bvalid;
|
||||
assign m_axil_wr.bready = s_axil_wr.bready;
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
@@ -198,7 +198,7 @@ if getattr(cocotb, 'top', None) is not None:
|
||||
factory = TestFactory(test)
|
||||
factory.add_option("idle_inserter", [None, cycle_pause])
|
||||
factory.add_option("backpressure_inserter", [None, cycle_pause])
|
||||
# factory.add_option("size", [None]+list(range(max_burst_size)))
|
||||
factory.add_option("size", [None]+list(range(max_burst_size)))
|
||||
factory.add_option("s", range(min(s_count, 2)))
|
||||
factory.add_option("m", range(min(m_count, 2)))
|
||||
factory.generate_tests()
|
||||
|
||||
68
src/axi/tb/taxi_axi_crossbar_1s/Makefile
Normal file
68
src/axi/tb/taxi_axi_crossbar_1s/Makefile
Normal file
@@ -0,0 +1,68 @@
|
||||
# SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
#
|
||||
# Copyright (c) 2020-2025
|
||||
#
|
||||
# Authors:
|
||||
# - Alex Forencich
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= verilator
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
|
||||
RTL_DIR = ../../rtl
|
||||
LIB_DIR = ../../lib
|
||||
TAXI_SRC_DIR = $(LIB_DIR)/taxi/src
|
||||
|
||||
DUT = taxi_axi_crossbar_1s
|
||||
COCOTB_TEST_MODULES = test_$(DUT)
|
||||
COCOTB_TOPLEVEL = test_$(DUT)
|
||||
MODULE = $(COCOTB_TEST_MODULES)
|
||||
TOPLEVEL = $(COCOTB_TOPLEVEL)
|
||||
VERILOG_SOURCES += $(COCOTB_TOPLEVEL).sv
|
||||
VERILOG_SOURCES += $(RTL_DIR)/$(DUT).f
|
||||
|
||||
# handle file list files
|
||||
process_f_file = $(call process_f_files,$(addprefix $(dir $1),$(shell cat $1)))
|
||||
process_f_files = $(foreach f,$1,$(if $(filter %.f,$f),$(call process_f_file,$f),$f))
|
||||
uniq_base = $(if $1,$(call uniq_base,$(foreach f,$1,$(if $(filter-out $(notdir $(lastword $1)),$(notdir $f)),$f,))) $(lastword $1))
|
||||
VERILOG_SOURCES := $(call uniq_base,$(call process_f_files,$(VERILOG_SOURCES)))
|
||||
|
||||
# module parameters
|
||||
export PARAM_M_COUNT := 4
|
||||
export PARAM_DATA_W := 32
|
||||
export PARAM_ADDR_W := 32
|
||||
export PARAM_STRB_W := $(shell expr $(PARAM_DATA_W) / 8 )
|
||||
export PARAM_S_ID_W := 8
|
||||
export PARAM_M_ID_W := $(PARAM_S_ID_W)
|
||||
export PARAM_AWUSER_EN := 0
|
||||
export PARAM_AWUSER_W := 1
|
||||
export PARAM_WUSER_EN := 0
|
||||
export PARAM_WUSER_W := 1
|
||||
export PARAM_BUSER_EN := 0
|
||||
export PARAM_BUSER_W := 1
|
||||
export PARAM_ARUSER_EN := 0
|
||||
export PARAM_ARUSER_W := 1
|
||||
export PARAM_RUSER_EN := 0
|
||||
export PARAM_RUSER_W := 1
|
||||
export PARAM_M_REGIONS := 1
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(COCOTB_TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
VERILATOR_TRACE = 1
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
275
src/axi/tb/taxi_axi_crossbar_1s/test_taxi_axi_crossbar_1s.py
Normal file
275
src/axi/tb/taxi_axi_crossbar_1s/test_taxi_axi_crossbar_1s.py
Normal file
@@ -0,0 +1,275 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
"""
|
||||
|
||||
Copyright (c) 2020-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
|
||||
import cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge, Timer
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiBus, AxiMaster, AxiRam
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())
|
||||
|
||||
self.axi_master = AxiMaster(AxiBus.from_entity(dut.s_axi), dut.clk, dut.rst)
|
||||
self.axi_ram = [AxiRam(AxiBus.from_entity(ch), dut.clk, dut.rst, size=2**16) for ch in dut.m_axi]
|
||||
|
||||
for ram in self.axi_ram:
|
||||
# prevent X propagation from screwing things up - "anything but X!"
|
||||
# (X on bid and rid can propagate X to ready/valid)
|
||||
ram.write_if.b_channel.bus.bid.setimmediatevalue(0)
|
||||
ram.read_if.r_channel.bus.rid.setimmediatevalue(0)
|
||||
|
||||
def set_idle_generator(self, generator=None):
|
||||
if generator:
|
||||
self.axi_master.write_if.aw_channel.set_pause_generator(generator())
|
||||
self.axi_master.write_if.w_channel.set_pause_generator(generator())
|
||||
self.axi_master.read_if.ar_channel.set_pause_generator(generator())
|
||||
for ram in self.axi_ram:
|
||||
ram.write_if.b_channel.set_pause_generator(generator())
|
||||
ram.read_if.r_channel.set_pause_generator(generator())
|
||||
|
||||
def set_backpressure_generator(self, generator=None):
|
||||
if generator:
|
||||
self.axi_master.write_if.b_channel.set_pause_generator(generator())
|
||||
self.axi_master.read_if.r_channel.set_pause_generator(generator())
|
||||
for ram in self.axi_ram:
|
||||
ram.write_if.aw_channel.set_pause_generator(generator())
|
||||
ram.write_if.w_channel.set_pause_generator(generator())
|
||||
ram.read_if.ar_channel.set_pause_generator(generator())
|
||||
|
||||
async def cycle_reset(self):
|
||||
self.dut.rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst.value = 1
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst.value = 0
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
|
||||
async def run_test_write(dut, data_in=None, idle_inserter=None, backpressure_inserter=None, size=None, m=0):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
byte_lanes = tb.axi_master.write_if.byte_lanes
|
||||
max_burst_size = tb.axi_master.write_if.max_burst_size
|
||||
|
||||
if size is None:
|
||||
size = max_burst_size
|
||||
|
||||
await tb.cycle_reset()
|
||||
|
||||
tb.set_idle_generator(idle_inserter)
|
||||
tb.set_backpressure_generator(backpressure_inserter)
|
||||
|
||||
for length in list(range(1, byte_lanes*2))+[1024]:
|
||||
for offset in list(range(byte_lanes, byte_lanes*2))+list(range(4096-byte_lanes, 4096)):
|
||||
tb.log.info("length %d, offset %d, size %d", length, offset, size)
|
||||
ram_addr = offset+0x1000
|
||||
addr = ram_addr + m*0x1000000
|
||||
test_data = bytearray([x % 256 for x in range(length)])
|
||||
|
||||
tb.axi_ram[m].write(ram_addr-128, b'\xaa'*(length+256))
|
||||
|
||||
await tb.axi_master.write(addr, test_data, size=size)
|
||||
|
||||
tb.log.debug("%s", tb.axi_ram[m].hexdump_str((ram_addr & ~0xf)-16, (((ram_addr & 0xf)+length-1) & ~0xf)+48))
|
||||
|
||||
assert tb.axi_ram[m].read(ram_addr, length) == test_data
|
||||
assert tb.axi_ram[m].read(ram_addr-1, 1) == b'\xaa'
|
||||
assert tb.axi_ram[m].read(ram_addr+length, 1) == b'\xaa'
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
async def run_test_read(dut, data_in=None, idle_inserter=None, backpressure_inserter=None, size=None, m=0):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
byte_lanes = tb.axi_master.write_if.byte_lanes
|
||||
max_burst_size = tb.axi_master.write_if.max_burst_size
|
||||
|
||||
if size is None:
|
||||
size = max_burst_size
|
||||
|
||||
await tb.cycle_reset()
|
||||
|
||||
tb.set_idle_generator(idle_inserter)
|
||||
tb.set_backpressure_generator(backpressure_inserter)
|
||||
|
||||
for length in list(range(1, byte_lanes*2))+[1024]:
|
||||
for offset in list(range(byte_lanes, byte_lanes*2))+list(range(4096-byte_lanes, 4096)):
|
||||
tb.log.info("length %d, offset %d, size %d", length, offset, size)
|
||||
ram_addr = offset+0x1000
|
||||
addr = ram_addr + m*0x1000000
|
||||
test_data = bytearray([x % 256 for x in range(length)])
|
||||
|
||||
tb.axi_ram[m].write(ram_addr, test_data)
|
||||
|
||||
data = await tb.axi_master.read(addr, length, size=size)
|
||||
|
||||
assert data.data == test_data
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.cycle_reset()
|
||||
|
||||
tb.set_idle_generator(idle_inserter)
|
||||
tb.set_backpressure_generator(backpressure_inserter)
|
||||
|
||||
async def worker(master, offset, aperture, count=16):
|
||||
for k in range(count):
|
||||
m = random.randrange(len(tb.axi_ram))
|
||||
length = random.randint(1, min(512, aperture))
|
||||
addr = offset+random.randint(0, aperture-length) + m*0x1000000
|
||||
test_data = bytearray([x % 256 for x in range(length)])
|
||||
|
||||
await Timer(random.randint(1, 100), 'ns')
|
||||
|
||||
await master.write(addr, test_data)
|
||||
|
||||
await Timer(random.randint(1, 100), 'ns')
|
||||
|
||||
data = await master.read(addr, length)
|
||||
assert data.data == test_data
|
||||
|
||||
workers = []
|
||||
|
||||
for k in range(16):
|
||||
workers.append(cocotb.start_soon(worker(tb.axi_master, k*0x1000, 0x1000, count=16)))
|
||||
|
||||
while workers:
|
||||
await workers.pop(0).join()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
def cycle_pause():
|
||||
return itertools.cycle([1, 1, 1, 0])
|
||||
|
||||
|
||||
if getattr(cocotb, 'top', None) is not None:
|
||||
|
||||
m_count = len(cocotb.top.m_axi)
|
||||
|
||||
data_w = len(cocotb.top.s_axi.wdata)
|
||||
byte_lanes = data_w // 8
|
||||
max_burst_size = (byte_lanes-1).bit_length()
|
||||
|
||||
for test in [run_test_write, run_test_read]:
|
||||
|
||||
factory = TestFactory(test)
|
||||
factory.add_option("idle_inserter", [None, cycle_pause])
|
||||
factory.add_option("backpressure_inserter", [None, cycle_pause])
|
||||
factory.add_option("size", [None]+list(range(max_burst_size)))
|
||||
factory.add_option("m", range(min(m_count, 2)))
|
||||
factory.generate_tests()
|
||||
|
||||
factory = TestFactory(run_stress_test)
|
||||
factory.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
lib_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'lib'))
|
||||
taxi_src_dir = os.path.abspath(os.path.join(lib_dir, 'taxi', 'src'))
|
||||
|
||||
|
||||
def process_f_files(files):
|
||||
lst = {}
|
||||
for f in files:
|
||||
if f[-2:].lower() == '.f':
|
||||
with open(f, 'r') as fp:
|
||||
l = fp.read().split()
|
||||
for f in process_f_files([os.path.join(os.path.dirname(f), x) for x in l]):
|
||||
lst[os.path.basename(f)] = f
|
||||
else:
|
||||
lst[os.path.basename(f)] = f
|
||||
return list(lst.values())
|
||||
|
||||
|
||||
@pytest.mark.parametrize("data_w", [8, 16, 32])
|
||||
@pytest.mark.parametrize("m_count", [1, 4])
|
||||
def test_taxi_axi_crossbar_1s(request, m_count, data_w):
|
||||
dut = "taxi_axi_crossbar_1s"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = module
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(tests_dir, f"{toplevel}.sv"),
|
||||
os.path.join(rtl_dir, f"{dut}.f"),
|
||||
]
|
||||
|
||||
verilog_sources = process_f_files(verilog_sources)
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['M_COUNT'] = m_count
|
||||
parameters['DATA_W'] = data_w
|
||||
parameters['ADDR_W'] = 32
|
||||
parameters['STRB_W'] = parameters['DATA_W'] // 8
|
||||
parameters['S_ID_W'] = 8
|
||||
parameters['M_ID_W'] = parameters['S_ID_W']
|
||||
parameters['AWUSER_EN'] = 0
|
||||
parameters['AWUSER_W'] = 1
|
||||
parameters['WUSER_EN'] = 0
|
||||
parameters['WUSER_W'] = 1
|
||||
parameters['BUSER_EN'] = 0
|
||||
parameters['BUSER_W'] = 1
|
||||
parameters['ARUSER_EN'] = 0
|
||||
parameters['ARUSER_W'] = 1
|
||||
parameters['RUSER_EN'] = 0
|
||||
parameters['RUSER_W'] = 1
|
||||
parameters['M_REGIONS'] = 1
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir, "sim_build",
|
||||
request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
simulator="verilator",
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
||||
135
src/axi/tb/taxi_axi_crossbar_1s/test_taxi_axi_crossbar_1s.sv
Normal file
135
src/axi/tb/taxi_axi_crossbar_1s/test_taxi_axi_crossbar_1s.sv
Normal file
@@ -0,0 +1,135 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* AXI4 crossbar testbench
|
||||
*/
|
||||
module test_taxi_axi_crossbar_1s #
|
||||
(
|
||||
/* verilator lint_off WIDTHTRUNC */
|
||||
parameter M_COUNT = 4,
|
||||
parameter DATA_W = 32,
|
||||
parameter ADDR_W = 32,
|
||||
parameter STRB_W = (DATA_W/8),
|
||||
parameter S_ID_W = 8,
|
||||
parameter M_ID_W = S_ID_W,
|
||||
parameter logic AWUSER_EN = 1'b0,
|
||||
parameter AWUSER_W = 1,
|
||||
parameter logic WUSER_EN = 1'b0,
|
||||
parameter WUSER_W = 1,
|
||||
parameter logic BUSER_EN = 1'b0,
|
||||
parameter BUSER_W = 1,
|
||||
parameter logic ARUSER_EN = 1'b0,
|
||||
parameter ARUSER_W = 1,
|
||||
parameter logic RUSER_EN = 1'b0,
|
||||
parameter RUSER_W = 1,
|
||||
parameter S_THREADS = 2,
|
||||
parameter S_ACCEPT = 16,
|
||||
parameter M_REGIONS = 1,
|
||||
parameter M_BASE_ADDR = '0,
|
||||
parameter M_ADDR_W = {M_COUNT{{M_REGIONS{32'd24}}}},
|
||||
parameter M_ISSUE = {M_COUNT{32'd4}},
|
||||
parameter M_SECURE = {M_COUNT{1'b0}},
|
||||
parameter S_AW_REG_TYPE = 2'd0,
|
||||
parameter S_W_REG_TYPE = 2'd0,
|
||||
parameter S_B_REG_TYPE = 2'd1,
|
||||
parameter S_AR_REG_TYPE = 2'd0,
|
||||
parameter S_R_REG_TYPE = 2'd2,
|
||||
parameter M_AW_REG_TYPE = {M_COUNT{2'd1}},
|
||||
parameter M_W_REG_TYPE = {M_COUNT{2'd2}},
|
||||
parameter M_B_REG_TYPE = {M_COUNT{2'd0}},
|
||||
parameter M_AR_REG_TYPE = {M_COUNT{2'd1}},
|
||||
parameter M_R_REG_TYPE = {M_COUNT{2'd0}}
|
||||
/* verilator lint_on WIDTHTRUNC */
|
||||
)
|
||||
();
|
||||
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
taxi_axi_if #(
|
||||
.DATA_W(DATA_W),
|
||||
.ADDR_W(ADDR_W),
|
||||
.STRB_W(STRB_W),
|
||||
.ID_W(S_ID_W),
|
||||
.AWUSER_EN(AWUSER_EN),
|
||||
.AWUSER_W(AWUSER_W),
|
||||
.WUSER_EN(WUSER_EN),
|
||||
.WUSER_W(WUSER_W),
|
||||
.BUSER_EN(BUSER_EN),
|
||||
.BUSER_W(BUSER_W),
|
||||
.ARUSER_EN(ARUSER_EN),
|
||||
.ARUSER_W(ARUSER_W),
|
||||
.RUSER_EN(RUSER_EN),
|
||||
.RUSER_W(RUSER_W)
|
||||
) s_axi();
|
||||
|
||||
taxi_axi_if #(
|
||||
.DATA_W(DATA_W),
|
||||
.ADDR_W(ADDR_W),
|
||||
.STRB_W(STRB_W),
|
||||
.ID_W(M_ID_W),
|
||||
.AWUSER_EN(AWUSER_EN),
|
||||
.AWUSER_W(AWUSER_W),
|
||||
.WUSER_EN(WUSER_EN),
|
||||
.WUSER_W(WUSER_W),
|
||||
.BUSER_EN(BUSER_EN),
|
||||
.BUSER_W(BUSER_W),
|
||||
.ARUSER_EN(ARUSER_EN),
|
||||
.ARUSER_W(ARUSER_W),
|
||||
.RUSER_EN(RUSER_EN),
|
||||
.RUSER_W(RUSER_W)
|
||||
) m_axi[M_COUNT]();
|
||||
|
||||
taxi_axi_crossbar_1s #(
|
||||
.M_COUNT(M_COUNT),
|
||||
.ADDR_W(ADDR_W),
|
||||
.S_THREADS(S_THREADS),
|
||||
.S_ACCEPT(S_ACCEPT),
|
||||
.M_REGIONS(M_REGIONS),
|
||||
.M_BASE_ADDR(M_BASE_ADDR),
|
||||
.M_ADDR_W(M_ADDR_W),
|
||||
.M_ISSUE(M_ISSUE),
|
||||
.M_SECURE(M_SECURE),
|
||||
.S_AW_REG_TYPE(S_AW_REG_TYPE),
|
||||
.S_W_REG_TYPE(S_W_REG_TYPE),
|
||||
.S_B_REG_TYPE(S_B_REG_TYPE),
|
||||
.S_AR_REG_TYPE(S_AR_REG_TYPE),
|
||||
.S_R_REG_TYPE(S_R_REG_TYPE),
|
||||
.M_AW_REG_TYPE(M_AW_REG_TYPE),
|
||||
.M_W_REG_TYPE(M_W_REG_TYPE),
|
||||
.M_B_REG_TYPE(M_B_REG_TYPE),
|
||||
.M_AR_REG_TYPE(M_AR_REG_TYPE),
|
||||
.M_R_REG_TYPE(M_R_REG_TYPE)
|
||||
)
|
||||
uut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI4 slave interface
|
||||
*/
|
||||
.s_axi_wr(s_axi),
|
||||
.s_axi_rd(s_axi),
|
||||
|
||||
/*
|
||||
* AXI4 master interface
|
||||
*/
|
||||
.m_axi_wr(m_axi),
|
||||
.m_axi_rd(m_axi)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
@@ -192,7 +192,7 @@ if getattr(cocotb, 'top', None) is not None:
|
||||
factory = TestFactory(test)
|
||||
factory.add_option("idle_inserter", [None, cycle_pause])
|
||||
factory.add_option("backpressure_inserter", [None, cycle_pause])
|
||||
# factory.add_option("size", [None]+list(range(max_burst_size)))
|
||||
factory.add_option("size", [None]+list(range(max_burst_size)))
|
||||
factory.add_option("s", range(min(s_count, 2)))
|
||||
factory.add_option("m", range(min(m_count, 2)))
|
||||
factory.generate_tests()
|
||||
|
||||
68
src/axi/tb/taxi_axi_interconnect_1s/Makefile
Normal file
68
src/axi/tb/taxi_axi_interconnect_1s/Makefile
Normal file
@@ -0,0 +1,68 @@
|
||||
# SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
#
|
||||
# Copyright (c) 2020-2025
|
||||
#
|
||||
# Authors:
|
||||
# - Alex Forencich
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= verilator
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
|
||||
RTL_DIR = ../../rtl
|
||||
LIB_DIR = ../../lib
|
||||
TAXI_SRC_DIR = $(LIB_DIR)/taxi/src
|
||||
|
||||
DUT = taxi_axi_interconnect_1s
|
||||
COCOTB_TEST_MODULES = test_$(DUT)
|
||||
COCOTB_TOPLEVEL = test_$(DUT)
|
||||
MODULE = $(COCOTB_TEST_MODULES)
|
||||
TOPLEVEL = $(COCOTB_TOPLEVEL)
|
||||
VERILOG_SOURCES += $(COCOTB_TOPLEVEL).sv
|
||||
VERILOG_SOURCES += $(RTL_DIR)/$(DUT).f
|
||||
|
||||
# handle file list files
|
||||
process_f_file = $(call process_f_files,$(addprefix $(dir $1),$(shell cat $1)))
|
||||
process_f_files = $(foreach f,$1,$(if $(filter %.f,$f),$(call process_f_file,$f),$f))
|
||||
uniq_base = $(if $1,$(call uniq_base,$(foreach f,$1,$(if $(filter-out $(notdir $(lastword $1)),$(notdir $f)),$f,))) $(lastword $1))
|
||||
VERILOG_SOURCES := $(call uniq_base,$(call process_f_files,$(VERILOG_SOURCES)))
|
||||
|
||||
# module parameters
|
||||
export PARAM_M_COUNT := 4
|
||||
export PARAM_DATA_W := 32
|
||||
export PARAM_ADDR_W := 32
|
||||
export PARAM_STRB_W := $(shell expr $(PARAM_DATA_W) / 8 )
|
||||
export PARAM_S_ID_W := 8
|
||||
export PARAM_M_ID_W := $(PARAM_S_ID_W)
|
||||
export PARAM_AWUSER_EN := 0
|
||||
export PARAM_AWUSER_W := 1
|
||||
export PARAM_WUSER_EN := 0
|
||||
export PARAM_WUSER_W := 1
|
||||
export PARAM_BUSER_EN := 0
|
||||
export PARAM_BUSER_W := 1
|
||||
export PARAM_ARUSER_EN := 0
|
||||
export PARAM_ARUSER_W := 1
|
||||
export PARAM_RUSER_EN := 0
|
||||
export PARAM_RUSER_W := 1
|
||||
export PARAM_M_REGIONS := 1
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(COCOTB_TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
VERILATOR_TRACE = 1
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
@@ -0,0 +1,269 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
"""
|
||||
|
||||
Copyright (c) 2020-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
|
||||
import cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge, Timer
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiBus, AxiMaster, AxiRam
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())
|
||||
|
||||
self.axi_master = AxiMaster(AxiBus.from_entity(dut.s_axi), dut.clk, dut.rst)
|
||||
self.axi_ram = [AxiRam(AxiBus.from_entity(ch), dut.clk, dut.rst, size=2**16) for ch in dut.m_axi]
|
||||
|
||||
def set_idle_generator(self, generator=None):
|
||||
if generator:
|
||||
self.axi_master.write_if.aw_channel.set_pause_generator(generator())
|
||||
self.axi_master.write_if.w_channel.set_pause_generator(generator())
|
||||
self.axi_master.read_if.ar_channel.set_pause_generator(generator())
|
||||
for ram in self.axi_ram:
|
||||
ram.write_if.b_channel.set_pause_generator(generator())
|
||||
ram.read_if.r_channel.set_pause_generator(generator())
|
||||
|
||||
def set_backpressure_generator(self, generator=None):
|
||||
if generator:
|
||||
self.axi_master.write_if.b_channel.set_pause_generator(generator())
|
||||
self.axi_master.read_if.r_channel.set_pause_generator(generator())
|
||||
for ram in self.axi_ram:
|
||||
ram.write_if.aw_channel.set_pause_generator(generator())
|
||||
ram.write_if.w_channel.set_pause_generator(generator())
|
||||
ram.read_if.ar_channel.set_pause_generator(generator())
|
||||
|
||||
async def cycle_reset(self):
|
||||
self.dut.rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst.value = 1
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst.value = 0
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
|
||||
async def run_test_write(dut, data_in=None, idle_inserter=None, backpressure_inserter=None, size=None, m=0):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
byte_lanes = tb.axi_master.write_if.byte_lanes
|
||||
max_burst_size = tb.axi_master.write_if.max_burst_size
|
||||
|
||||
if size is None:
|
||||
size = max_burst_size
|
||||
|
||||
await tb.cycle_reset()
|
||||
|
||||
tb.set_idle_generator(idle_inserter)
|
||||
tb.set_backpressure_generator(backpressure_inserter)
|
||||
|
||||
for length in list(range(1, byte_lanes*2))+[1024]:
|
||||
for offset in list(range(byte_lanes, byte_lanes*2))+list(range(4096-byte_lanes, 4096)):
|
||||
tb.log.info("length %d, offset %d, size %d", length, offset, size)
|
||||
ram_addr = offset+0x1000
|
||||
addr = ram_addr + m*0x1000000
|
||||
test_data = bytearray([x % 256 for x in range(length)])
|
||||
|
||||
tb.axi_ram[m].write(ram_addr-128, b'\xaa'*(length+256))
|
||||
|
||||
await tb.axi_master.write(addr, test_data, size=size)
|
||||
|
||||
tb.log.debug("%s", tb.axi_ram[m].hexdump_str((ram_addr & ~0xf)-16, (((ram_addr & 0xf)+length-1) & ~0xf)+48))
|
||||
|
||||
assert tb.axi_ram[m].read(ram_addr, length) == test_data
|
||||
assert tb.axi_ram[m].read(ram_addr-1, 1) == b'\xaa'
|
||||
assert tb.axi_ram[m].read(ram_addr+length, 1) == b'\xaa'
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
async def run_test_read(dut, data_in=None, idle_inserter=None, backpressure_inserter=None, size=None, m=0):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
byte_lanes = tb.axi_master.write_if.byte_lanes
|
||||
max_burst_size = tb.axi_master.write_if.max_burst_size
|
||||
|
||||
if size is None:
|
||||
size = max_burst_size
|
||||
|
||||
await tb.cycle_reset()
|
||||
|
||||
tb.set_idle_generator(idle_inserter)
|
||||
tb.set_backpressure_generator(backpressure_inserter)
|
||||
|
||||
for length in list(range(1, byte_lanes*2))+[1024]:
|
||||
for offset in list(range(byte_lanes, byte_lanes*2))+list(range(4096-byte_lanes, 4096)):
|
||||
tb.log.info("length %d, offset %d, size %d", length, offset, size)
|
||||
ram_addr = offset+0x1000
|
||||
addr = ram_addr + m*0x1000000
|
||||
test_data = bytearray([x % 256 for x in range(length)])
|
||||
|
||||
tb.axi_ram[m].write(ram_addr, test_data)
|
||||
|
||||
data = await tb.axi_master.read(addr, length, size=size)
|
||||
|
||||
assert data.data == test_data
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.cycle_reset()
|
||||
|
||||
tb.set_idle_generator(idle_inserter)
|
||||
tb.set_backpressure_generator(backpressure_inserter)
|
||||
|
||||
async def worker(master, offset, aperture, count=16):
|
||||
for k in range(count):
|
||||
m = random.randrange(len(tb.axi_ram))
|
||||
length = random.randint(1, min(512, aperture))
|
||||
addr = offset+random.randint(0, aperture-length) + m*0x1000000
|
||||
test_data = bytearray([x % 256 for x in range(length)])
|
||||
|
||||
await Timer(random.randint(1, 100), 'ns')
|
||||
|
||||
await master.write(addr, test_data)
|
||||
|
||||
await Timer(random.randint(1, 100), 'ns')
|
||||
|
||||
data = await master.read(addr, length)
|
||||
assert data.data == test_data
|
||||
|
||||
workers = []
|
||||
|
||||
for k in range(16):
|
||||
workers.append(cocotb.start_soon(worker(tb.axi_master, k*0x1000, 0x1000, count=16)))
|
||||
|
||||
while workers:
|
||||
await workers.pop(0).join()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
def cycle_pause():
|
||||
return itertools.cycle([1, 1, 1, 0])
|
||||
|
||||
|
||||
if getattr(cocotb, 'top', None) is not None:
|
||||
|
||||
m_count = len(cocotb.top.m_axi)
|
||||
|
||||
data_w = len(cocotb.top.s_axi.wdata)
|
||||
byte_lanes = data_w // 8
|
||||
max_burst_size = (byte_lanes-1).bit_length()
|
||||
|
||||
for test in [run_test_write, run_test_read]:
|
||||
|
||||
factory = TestFactory(test)
|
||||
factory.add_option("idle_inserter", [None, cycle_pause])
|
||||
factory.add_option("backpressure_inserter", [None, cycle_pause])
|
||||
factory.add_option("size", [None]+list(range(max_burst_size)))
|
||||
factory.add_option("m", range(min(m_count, 2)))
|
||||
factory.generate_tests()
|
||||
|
||||
factory = TestFactory(run_stress_test)
|
||||
factory.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
lib_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'lib'))
|
||||
taxi_src_dir = os.path.abspath(os.path.join(lib_dir, 'taxi', 'src'))
|
||||
|
||||
|
||||
def process_f_files(files):
|
||||
lst = {}
|
||||
for f in files:
|
||||
if f[-2:].lower() == '.f':
|
||||
with open(f, 'r') as fp:
|
||||
l = fp.read().split()
|
||||
for f in process_f_files([os.path.join(os.path.dirname(f), x) for x in l]):
|
||||
lst[os.path.basename(f)] = f
|
||||
else:
|
||||
lst[os.path.basename(f)] = f
|
||||
return list(lst.values())
|
||||
|
||||
|
||||
@pytest.mark.parametrize("data_w", [8, 16, 32])
|
||||
@pytest.mark.parametrize("m_count", [1, 4])
|
||||
def test_taxi_axi_interconnect_1s(request, m_count, data_w):
|
||||
dut = "taxi_axi_interconnect_1s"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = module
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(tests_dir, f"{toplevel}.sv"),
|
||||
os.path.join(rtl_dir, f"{dut}.f"),
|
||||
]
|
||||
|
||||
verilog_sources = process_f_files(verilog_sources)
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['M_COUNT'] = m_count
|
||||
parameters['DATA_W'] = data_w
|
||||
parameters['ADDR_W'] = 32
|
||||
parameters['STRB_W'] = parameters['DATA_W'] // 8
|
||||
parameters['S_ID_W'] = 8
|
||||
parameters['M_ID_W'] = parameters['S_ID_W']
|
||||
parameters['AWUSER_EN'] = 0
|
||||
parameters['AWUSER_W'] = 1
|
||||
parameters['WUSER_EN'] = 0
|
||||
parameters['WUSER_W'] = 1
|
||||
parameters['BUSER_EN'] = 0
|
||||
parameters['BUSER_W'] = 1
|
||||
parameters['ARUSER_EN'] = 0
|
||||
parameters['ARUSER_W'] = 1
|
||||
parameters['RUSER_EN'] = 0
|
||||
parameters['RUSER_W'] = 1
|
||||
parameters['M_REGIONS'] = 1
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir, "sim_build",
|
||||
request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
simulator="verilator",
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
||||
@@ -0,0 +1,109 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* AXI4 interconnect testbench
|
||||
*/
|
||||
module test_taxi_axi_interconnect_1s #
|
||||
(
|
||||
/* verilator lint_off WIDTHTRUNC */
|
||||
parameter M_COUNT = 4,
|
||||
parameter DATA_W = 32,
|
||||
parameter ADDR_W = 32,
|
||||
parameter STRB_W = (DATA_W/8),
|
||||
parameter S_ID_W = 8,
|
||||
parameter M_ID_W = S_ID_W,
|
||||
parameter logic AWUSER_EN = 1'b0,
|
||||
parameter AWUSER_W = 1,
|
||||
parameter logic WUSER_EN = 1'b0,
|
||||
parameter WUSER_W = 1,
|
||||
parameter logic BUSER_EN = 1'b0,
|
||||
parameter BUSER_W = 1,
|
||||
parameter logic ARUSER_EN = 1'b0,
|
||||
parameter ARUSER_W = 1,
|
||||
parameter logic RUSER_EN = 1'b0,
|
||||
parameter RUSER_W = 1,
|
||||
parameter M_REGIONS = 1,
|
||||
parameter M_BASE_ADDR = '0,
|
||||
parameter M_ADDR_W = {M_COUNT{{M_REGIONS{32'd24}}}},
|
||||
parameter M_SECURE = {M_COUNT{1'b0}}
|
||||
/* verilator lint_on WIDTHTRUNC */
|
||||
)
|
||||
();
|
||||
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
taxi_axi_if #(
|
||||
.DATA_W(DATA_W),
|
||||
.ADDR_W(ADDR_W),
|
||||
.STRB_W(STRB_W),
|
||||
.ID_W(S_ID_W),
|
||||
.AWUSER_EN(AWUSER_EN),
|
||||
.AWUSER_W(AWUSER_W),
|
||||
.WUSER_EN(WUSER_EN),
|
||||
.WUSER_W(WUSER_W),
|
||||
.BUSER_EN(BUSER_EN),
|
||||
.BUSER_W(BUSER_W),
|
||||
.ARUSER_EN(ARUSER_EN),
|
||||
.ARUSER_W(ARUSER_W),
|
||||
.RUSER_EN(RUSER_EN),
|
||||
.RUSER_W(RUSER_W)
|
||||
) s_axi();
|
||||
|
||||
taxi_axi_if #(
|
||||
.DATA_W(DATA_W),
|
||||
.ADDR_W(ADDR_W),
|
||||
.STRB_W(STRB_W),
|
||||
.ID_W(M_ID_W),
|
||||
.AWUSER_EN(AWUSER_EN),
|
||||
.AWUSER_W(AWUSER_W),
|
||||
.WUSER_EN(WUSER_EN),
|
||||
.WUSER_W(WUSER_W),
|
||||
.BUSER_EN(BUSER_EN),
|
||||
.BUSER_W(BUSER_W),
|
||||
.ARUSER_EN(ARUSER_EN),
|
||||
.ARUSER_W(ARUSER_W),
|
||||
.RUSER_EN(RUSER_EN),
|
||||
.RUSER_W(RUSER_W)
|
||||
) m_axi[M_COUNT]();
|
||||
|
||||
taxi_axi_interconnect_1s #(
|
||||
.M_COUNT(M_COUNT),
|
||||
.ADDR_W(ADDR_W),
|
||||
.M_REGIONS(M_REGIONS),
|
||||
.M_BASE_ADDR(M_BASE_ADDR),
|
||||
.M_ADDR_W(M_ADDR_W),
|
||||
.M_SECURE(M_SECURE)
|
||||
)
|
||||
uut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI4 slave interface
|
||||
*/
|
||||
.s_axi_wr(s_axi),
|
||||
.s_axi_rd(s_axi),
|
||||
|
||||
/*
|
||||
* AXI4 master interface
|
||||
*/
|
||||
.m_axi_wr(m_axi),
|
||||
.m_axi_rd(m_axi)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
66
src/axi/tb/taxi_axil_crossbar_1s/Makefile
Normal file
66
src/axi/tb/taxi_axil_crossbar_1s/Makefile
Normal file
@@ -0,0 +1,66 @@
|
||||
# SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
#
|
||||
# Copyright (c) 2020-2025
|
||||
#
|
||||
# Authors:
|
||||
# - Alex Forencich
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= verilator
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
|
||||
RTL_DIR = ../../rtl
|
||||
LIB_DIR = ../../lib
|
||||
TAXI_SRC_DIR = $(LIB_DIR)/taxi/src
|
||||
|
||||
DUT = taxi_axil_crossbar_1s
|
||||
COCOTB_TEST_MODULES = test_$(DUT)
|
||||
COCOTB_TOPLEVEL = test_$(DUT)
|
||||
MODULE = $(COCOTB_TEST_MODULES)
|
||||
TOPLEVEL = $(COCOTB_TOPLEVEL)
|
||||
VERILOG_SOURCES += $(COCOTB_TOPLEVEL).sv
|
||||
VERILOG_SOURCES += $(RTL_DIR)/$(DUT).f
|
||||
|
||||
# handle file list files
|
||||
process_f_file = $(call process_f_files,$(addprefix $(dir $1),$(shell cat $1)))
|
||||
process_f_files = $(foreach f,$1,$(if $(filter %.f,$f),$(call process_f_file,$f),$f))
|
||||
uniq_base = $(if $1,$(call uniq_base,$(foreach f,$1,$(if $(filter-out $(notdir $(lastword $1)),$(notdir $f)),$f,))) $(lastword $1))
|
||||
VERILOG_SOURCES := $(call uniq_base,$(call process_f_files,$(VERILOG_SOURCES)))
|
||||
|
||||
# module parameters
|
||||
export PARAM_M_COUNT := 4
|
||||
export PARAM_DATA_W := 32
|
||||
export PARAM_ADDR_W := 32
|
||||
export PARAM_STRB_W := $(shell expr $(PARAM_DATA_W) / 8 )
|
||||
export PARAM_AWUSER_EN := 0
|
||||
export PARAM_AWUSER_W := 1
|
||||
export PARAM_WUSER_EN := 0
|
||||
export PARAM_WUSER_W := 1
|
||||
export PARAM_BUSER_EN := 0
|
||||
export PARAM_BUSER_W := 1
|
||||
export PARAM_ARUSER_EN := 0
|
||||
export PARAM_ARUSER_W := 1
|
||||
export PARAM_RUSER_EN := 0
|
||||
export PARAM_RUSER_W := 1
|
||||
export PARAM_M_REGIONS := 1
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(COCOTB_TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
VERILATOR_TRACE = 1
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
254
src/axi/tb/taxi_axil_crossbar_1s/test_taxi_axil_crossbar_1s.py
Normal file
254
src/axi/tb/taxi_axil_crossbar_1s/test_taxi_axil_crossbar_1s.py
Normal file
@@ -0,0 +1,254 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
"""
|
||||
|
||||
Copyright (c) 2020-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
|
||||
import cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge, Timer
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiLiteBus, AxiLiteMaster, AxiLiteRam
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())
|
||||
|
||||
self.axil_master = AxiLiteMaster(AxiLiteBus.from_entity(dut.s_axil), dut.clk, dut.rst)
|
||||
self.axil_ram = [AxiLiteRam(AxiLiteBus.from_entity(ch), dut.clk, dut.rst, size=2**16) for ch in dut.m_axil]
|
||||
|
||||
def set_idle_generator(self, generator=None):
|
||||
if generator:
|
||||
self.axil_master.write_if.aw_channel.set_pause_generator(generator())
|
||||
self.axil_master.write_if.w_channel.set_pause_generator(generator())
|
||||
self.axil_master.read_if.ar_channel.set_pause_generator(generator())
|
||||
for ram in self.axil_ram:
|
||||
ram.write_if.b_channel.set_pause_generator(generator())
|
||||
ram.read_if.r_channel.set_pause_generator(generator())
|
||||
|
||||
def set_backpressure_generator(self, generator=None):
|
||||
if generator:
|
||||
self.axil_master.write_if.b_channel.set_pause_generator(generator())
|
||||
self.axil_master.read_if.r_channel.set_pause_generator(generator())
|
||||
for ram in self.axil_ram:
|
||||
ram.write_if.aw_channel.set_pause_generator(generator())
|
||||
ram.write_if.w_channel.set_pause_generator(generator())
|
||||
ram.read_if.ar_channel.set_pause_generator(generator())
|
||||
|
||||
async def cycle_reset(self):
|
||||
self.dut.rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst.value = 1
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst.value = 0
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
|
||||
async def run_test_write(dut, data_in=None, idle_inserter=None, backpressure_inserter=None, m=0):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
byte_lanes = tb.axil_master.write_if.byte_lanes
|
||||
|
||||
await tb.cycle_reset()
|
||||
|
||||
tb.set_idle_generator(idle_inserter)
|
||||
tb.set_backpressure_generator(backpressure_inserter)
|
||||
|
||||
for length in range(1, byte_lanes*2):
|
||||
for offset in range(byte_lanes):
|
||||
tb.log.info("length %d, offset %d", length, offset)
|
||||
ram_addr = offset+0x1000
|
||||
addr = ram_addr + m*0x1000000
|
||||
test_data = bytearray([x % 256 for x in range(length)])
|
||||
|
||||
tb.axil_ram[m].write(ram_addr-128, b'\xaa'*(length+256))
|
||||
|
||||
await tb.axil_master.write(addr, test_data)
|
||||
|
||||
tb.log.debug("%s", tb.axil_ram[m].hexdump_str((ram_addr & ~0xf)-16, (((ram_addr & 0xf)+length-1) & ~0xf)+48))
|
||||
|
||||
assert tb.axil_ram[m].read(ram_addr, length) == test_data
|
||||
assert tb.axil_ram[m].read(ram_addr-1, 1) == b'\xaa'
|
||||
assert tb.axil_ram[m].read(ram_addr+length, 1) == b'\xaa'
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
async def run_test_read(dut, data_in=None, idle_inserter=None, backpressure_inserter=None, m=0):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
byte_lanes = tb.axil_master.write_if.byte_lanes
|
||||
|
||||
await tb.cycle_reset()
|
||||
|
||||
tb.set_idle_generator(idle_inserter)
|
||||
tb.set_backpressure_generator(backpressure_inserter)
|
||||
|
||||
for length in range(1, byte_lanes*2):
|
||||
for offset in range(byte_lanes):
|
||||
tb.log.info("length %d, offset %d", length, offset)
|
||||
ram_addr = offset+0x1000
|
||||
addr = ram_addr + m*0x1000000
|
||||
test_data = bytearray([x % 256 for x in range(length)])
|
||||
|
||||
tb.axil_ram[m].write(ram_addr, test_data)
|
||||
|
||||
data = await tb.axil_master.read(addr, length)
|
||||
|
||||
assert data.data == test_data
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.cycle_reset()
|
||||
|
||||
tb.set_idle_generator(idle_inserter)
|
||||
tb.set_backpressure_generator(backpressure_inserter)
|
||||
|
||||
async def worker(master, offset, aperture, count=16):
|
||||
for k in range(count):
|
||||
m = random.randrange(len(tb.axil_ram))
|
||||
length = random.randint(1, min(32, aperture))
|
||||
addr = offset+random.randint(0, aperture-length) + m*0x1000000
|
||||
test_data = bytearray([x % 256 for x in range(length)])
|
||||
|
||||
await Timer(random.randint(1, 100), 'ns')
|
||||
|
||||
await master.write(addr, test_data)
|
||||
|
||||
await Timer(random.randint(1, 100), 'ns')
|
||||
|
||||
data = await master.read(addr, length)
|
||||
assert data.data == test_data
|
||||
|
||||
workers = []
|
||||
|
||||
for k in range(16):
|
||||
workers.append(cocotb.start_soon(worker(tb.axil_master, k*0x1000, 0x1000, count=16)))
|
||||
|
||||
while workers:
|
||||
await workers.pop(0).join()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
def cycle_pause():
|
||||
return itertools.cycle([1, 1, 1, 0])
|
||||
|
||||
|
||||
if getattr(cocotb, 'top', None) is not None:
|
||||
|
||||
m_count = len(cocotb.top.m_axil)
|
||||
|
||||
for test in [run_test_write, run_test_read]:
|
||||
|
||||
factory = TestFactory(test)
|
||||
factory.add_option("idle_inserter", [None, cycle_pause])
|
||||
factory.add_option("backpressure_inserter", [None, cycle_pause])
|
||||
factory.add_option("m", range(min(m_count, 2)))
|
||||
factory.generate_tests()
|
||||
|
||||
factory = TestFactory(run_stress_test)
|
||||
factory.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
lib_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'lib'))
|
||||
taxi_src_dir = os.path.abspath(os.path.join(lib_dir, 'taxi', 'src'))
|
||||
|
||||
|
||||
def process_f_files(files):
|
||||
lst = {}
|
||||
for f in files:
|
||||
if f[-2:].lower() == '.f':
|
||||
with open(f, 'r') as fp:
|
||||
l = fp.read().split()
|
||||
for f in process_f_files([os.path.join(os.path.dirname(f), x) for x in l]):
|
||||
lst[os.path.basename(f)] = f
|
||||
else:
|
||||
lst[os.path.basename(f)] = f
|
||||
return list(lst.values())
|
||||
|
||||
|
||||
@pytest.mark.parametrize("data_w", [8, 16, 32])
|
||||
@pytest.mark.parametrize("m_count", [1, 4])
|
||||
def test_taxi_axil_crossbar_1s(request, m_count, data_w):
|
||||
dut = "taxi_axil_crossbar_1s"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = module
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(tests_dir, f"{toplevel}.sv"),
|
||||
os.path.join(rtl_dir, f"{dut}.f"),
|
||||
]
|
||||
|
||||
verilog_sources = process_f_files(verilog_sources)
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['M_COUNT'] = m_count
|
||||
parameters['DATA_W'] = data_w
|
||||
parameters['ADDR_W'] = 32
|
||||
parameters['STRB_W'] = parameters['DATA_W'] // 8
|
||||
parameters['AWUSER_EN'] = 0
|
||||
parameters['AWUSER_W'] = 1
|
||||
parameters['WUSER_EN'] = 0
|
||||
parameters['WUSER_W'] = 1
|
||||
parameters['BUSER_EN'] = 0
|
||||
parameters['BUSER_W'] = 1
|
||||
parameters['ARUSER_EN'] = 0
|
||||
parameters['ARUSER_W'] = 1
|
||||
parameters['RUSER_EN'] = 0
|
||||
parameters['RUSER_W'] = 1
|
||||
parameters['M_REGIONS'] = 1
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir, "sim_build",
|
||||
request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
simulator="verilator",
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
||||
129
src/axi/tb/taxi_axil_crossbar_1s/test_taxi_axil_crossbar_1s.sv
Normal file
129
src/axi/tb/taxi_axil_crossbar_1s/test_taxi_axil_crossbar_1s.sv
Normal file
@@ -0,0 +1,129 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* AXI4-lite crossbar testbench
|
||||
*/
|
||||
module test_taxi_axil_crossbar_1s #
|
||||
(
|
||||
/* verilator lint_off WIDTHTRUNC */
|
||||
parameter M_COUNT = 4,
|
||||
parameter DATA_W = 32,
|
||||
parameter ADDR_W = 32,
|
||||
parameter STRB_W = (DATA_W/8),
|
||||
parameter logic AWUSER_EN = 1'b0,
|
||||
parameter AWUSER_W = 1,
|
||||
parameter logic WUSER_EN = 1'b0,
|
||||
parameter WUSER_W = 1,
|
||||
parameter logic BUSER_EN = 1'b0,
|
||||
parameter BUSER_W = 1,
|
||||
parameter logic ARUSER_EN = 1'b0,
|
||||
parameter ARUSER_W = 1,
|
||||
parameter logic RUSER_EN = 1'b0,
|
||||
parameter RUSER_W = 1,
|
||||
parameter S_ACCEPT = 16,
|
||||
parameter M_REGIONS = 1,
|
||||
parameter M_BASE_ADDR = '0,
|
||||
parameter M_ADDR_W = {M_COUNT{{M_REGIONS{32'd24}}}},
|
||||
parameter M_ISSUE = {M_COUNT{32'd4}},
|
||||
parameter M_SECURE = {M_COUNT{1'b0}},
|
||||
parameter S_AW_REG_TYPE = 2'd0,
|
||||
parameter S_W_REG_TYPE = 2'd0,
|
||||
parameter S_B_REG_TYPE = 2'd1,
|
||||
parameter S_AR_REG_TYPE = 2'd0,
|
||||
parameter S_R_REG_TYPE = 2'd2,
|
||||
parameter M_AW_REG_TYPE = {M_COUNT{2'd1}},
|
||||
parameter M_W_REG_TYPE = {M_COUNT{2'd2}},
|
||||
parameter M_B_REG_TYPE = {M_COUNT{2'd0}},
|
||||
parameter M_AR_REG_TYPE = {M_COUNT{2'd1}},
|
||||
parameter M_R_REG_TYPE = {M_COUNT{2'd0}}
|
||||
/* verilator lint_on WIDTHTRUNC */
|
||||
)
|
||||
();
|
||||
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
taxi_axil_if #(
|
||||
.DATA_W(DATA_W),
|
||||
.ADDR_W(ADDR_W),
|
||||
.STRB_W(STRB_W),
|
||||
.AWUSER_EN(AWUSER_EN),
|
||||
.AWUSER_W(AWUSER_W),
|
||||
.WUSER_EN(WUSER_EN),
|
||||
.WUSER_W(WUSER_W),
|
||||
.BUSER_EN(BUSER_EN),
|
||||
.BUSER_W(BUSER_W),
|
||||
.ARUSER_EN(ARUSER_EN),
|
||||
.ARUSER_W(ARUSER_W),
|
||||
.RUSER_EN(RUSER_EN),
|
||||
.RUSER_W(RUSER_W)
|
||||
) s_axil();
|
||||
|
||||
taxi_axil_if #(
|
||||
.DATA_W(DATA_W),
|
||||
.ADDR_W(ADDR_W),
|
||||
.STRB_W(STRB_W),
|
||||
.AWUSER_EN(AWUSER_EN),
|
||||
.AWUSER_W(AWUSER_W),
|
||||
.WUSER_EN(WUSER_EN),
|
||||
.WUSER_W(WUSER_W),
|
||||
.BUSER_EN(BUSER_EN),
|
||||
.BUSER_W(BUSER_W),
|
||||
.ARUSER_EN(ARUSER_EN),
|
||||
.ARUSER_W(ARUSER_W),
|
||||
.RUSER_EN(RUSER_EN),
|
||||
.RUSER_W(RUSER_W)
|
||||
) m_axil[M_COUNT]();
|
||||
|
||||
taxi_axil_crossbar_1s #(
|
||||
.M_COUNT(M_COUNT),
|
||||
.ADDR_W(ADDR_W),
|
||||
.S_ACCEPT(S_ACCEPT),
|
||||
.M_REGIONS(M_REGIONS),
|
||||
.M_BASE_ADDR(M_BASE_ADDR),
|
||||
.M_ADDR_W(M_ADDR_W),
|
||||
.M_ISSUE(M_ISSUE),
|
||||
.M_SECURE(M_SECURE),
|
||||
.S_AW_REG_TYPE(S_AW_REG_TYPE),
|
||||
.S_W_REG_TYPE(S_W_REG_TYPE),
|
||||
.S_B_REG_TYPE(S_B_REG_TYPE),
|
||||
.S_AR_REG_TYPE(S_AR_REG_TYPE),
|
||||
.S_R_REG_TYPE(S_R_REG_TYPE),
|
||||
.M_AW_REG_TYPE(M_AW_REG_TYPE),
|
||||
.M_W_REG_TYPE(M_W_REG_TYPE),
|
||||
.M_B_REG_TYPE(M_B_REG_TYPE),
|
||||
.M_AR_REG_TYPE(M_AR_REG_TYPE),
|
||||
.M_R_REG_TYPE(M_R_REG_TYPE)
|
||||
)
|
||||
uut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI4-lite slave interface
|
||||
*/
|
||||
.s_axil_wr(s_axil),
|
||||
.s_axil_rd(s_axil),
|
||||
|
||||
/*
|
||||
* AXI4-lite master interface
|
||||
*/
|
||||
.m_axil_wr(m_axil),
|
||||
.m_axil_rd(m_axil)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
66
src/axi/tb/taxi_axil_interconnect_1s/Makefile
Normal file
66
src/axi/tb/taxi_axil_interconnect_1s/Makefile
Normal file
@@ -0,0 +1,66 @@
|
||||
# SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
#
|
||||
# Copyright (c) 2020-2025
|
||||
#
|
||||
# Authors:
|
||||
# - Alex Forencich
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= verilator
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
|
||||
RTL_DIR = ../../rtl
|
||||
LIB_DIR = ../../lib
|
||||
TAXI_SRC_DIR = $(LIB_DIR)/taxi/src
|
||||
|
||||
DUT = taxi_axil_interconnect_1s
|
||||
COCOTB_TEST_MODULES = test_$(DUT)
|
||||
COCOTB_TOPLEVEL = test_$(DUT)
|
||||
MODULE = $(COCOTB_TEST_MODULES)
|
||||
TOPLEVEL = $(COCOTB_TOPLEVEL)
|
||||
VERILOG_SOURCES += $(COCOTB_TOPLEVEL).sv
|
||||
VERILOG_SOURCES += $(RTL_DIR)/$(DUT).f
|
||||
|
||||
# handle file list files
|
||||
process_f_file = $(call process_f_files,$(addprefix $(dir $1),$(shell cat $1)))
|
||||
process_f_files = $(foreach f,$1,$(if $(filter %.f,$f),$(call process_f_file,$f),$f))
|
||||
uniq_base = $(if $1,$(call uniq_base,$(foreach f,$1,$(if $(filter-out $(notdir $(lastword $1)),$(notdir $f)),$f,))) $(lastword $1))
|
||||
VERILOG_SOURCES := $(call uniq_base,$(call process_f_files,$(VERILOG_SOURCES)))
|
||||
|
||||
# module parameters
|
||||
export PARAM_M_COUNT := 4
|
||||
export PARAM_DATA_W := 32
|
||||
export PARAM_ADDR_W := 32
|
||||
export PARAM_STRB_W := $(shell expr $(PARAM_DATA_W) / 8 )
|
||||
export PARAM_AWUSER_EN := 0
|
||||
export PARAM_AWUSER_W := 1
|
||||
export PARAM_WUSER_EN := 0
|
||||
export PARAM_WUSER_W := 1
|
||||
export PARAM_BUSER_EN := 0
|
||||
export PARAM_BUSER_W := 1
|
||||
export PARAM_ARUSER_EN := 0
|
||||
export PARAM_ARUSER_W := 1
|
||||
export PARAM_RUSER_EN := 0
|
||||
export PARAM_RUSER_W := 1
|
||||
export PARAM_M_REGIONS := 1
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(COCOTB_TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
VERILATOR_TRACE = 1
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
@@ -0,0 +1,254 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
"""
|
||||
|
||||
Copyright (c) 2020-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
|
||||
import cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge, Timer
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiLiteBus, AxiLiteMaster, AxiLiteRam
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())
|
||||
|
||||
self.axil_master = AxiLiteMaster(AxiLiteBus.from_entity(dut.s_axil), dut.clk, dut.rst)
|
||||
self.axil_ram = [AxiLiteRam(AxiLiteBus.from_entity(ch), dut.clk, dut.rst, size=2**16) for ch in dut.m_axil]
|
||||
|
||||
def set_idle_generator(self, generator=None):
|
||||
if generator:
|
||||
self.axil_master.write_if.aw_channel.set_pause_generator(generator())
|
||||
self.axil_master.write_if.w_channel.set_pause_generator(generator())
|
||||
self.axil_master.read_if.ar_channel.set_pause_generator(generator())
|
||||
for ram in self.axil_ram:
|
||||
ram.write_if.b_channel.set_pause_generator(generator())
|
||||
ram.read_if.r_channel.set_pause_generator(generator())
|
||||
|
||||
def set_backpressure_generator(self, generator=None):
|
||||
if generator:
|
||||
self.axil_master.write_if.b_channel.set_pause_generator(generator())
|
||||
self.axil_master.read_if.r_channel.set_pause_generator(generator())
|
||||
for ram in self.axil_ram:
|
||||
ram.write_if.aw_channel.set_pause_generator(generator())
|
||||
ram.write_if.w_channel.set_pause_generator(generator())
|
||||
ram.read_if.ar_channel.set_pause_generator(generator())
|
||||
|
||||
async def cycle_reset(self):
|
||||
self.dut.rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst.value = 1
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst.value = 0
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
|
||||
async def run_test_write(dut, data_in=None, idle_inserter=None, backpressure_inserter=None, m=0):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
byte_lanes = tb.axil_master.write_if.byte_lanes
|
||||
|
||||
await tb.cycle_reset()
|
||||
|
||||
tb.set_idle_generator(idle_inserter)
|
||||
tb.set_backpressure_generator(backpressure_inserter)
|
||||
|
||||
for length in range(1, byte_lanes*2):
|
||||
for offset in range(byte_lanes):
|
||||
tb.log.info("length %d, offset %d", length, offset)
|
||||
ram_addr = offset+0x1000
|
||||
addr = ram_addr + m*0x1000000
|
||||
test_data = bytearray([x % 256 for x in range(length)])
|
||||
|
||||
tb.axil_ram[m].write(ram_addr-128, b'\xaa'*(length+256))
|
||||
|
||||
await tb.axil_master.write(addr, test_data)
|
||||
|
||||
tb.log.debug("%s", tb.axil_ram[m].hexdump_str((ram_addr & ~0xf)-16, (((ram_addr & 0xf)+length-1) & ~0xf)+48))
|
||||
|
||||
assert tb.axil_ram[m].read(ram_addr, length) == test_data
|
||||
assert tb.axil_ram[m].read(ram_addr-1, 1) == b'\xaa'
|
||||
assert tb.axil_ram[m].read(ram_addr+length, 1) == b'\xaa'
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
async def run_test_read(dut, data_in=None, idle_inserter=None, backpressure_inserter=None, m=0):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
byte_lanes = tb.axil_master.write_if.byte_lanes
|
||||
|
||||
await tb.cycle_reset()
|
||||
|
||||
tb.set_idle_generator(idle_inserter)
|
||||
tb.set_backpressure_generator(backpressure_inserter)
|
||||
|
||||
for length in range(1, byte_lanes*2):
|
||||
for offset in range(byte_lanes):
|
||||
tb.log.info("length %d, offset %d", length, offset)
|
||||
ram_addr = offset+0x1000
|
||||
addr = ram_addr + m*0x1000000
|
||||
test_data = bytearray([x % 256 for x in range(length)])
|
||||
|
||||
tb.axil_ram[m].write(ram_addr, test_data)
|
||||
|
||||
data = await tb.axil_master.read(addr, length)
|
||||
|
||||
assert data.data == test_data
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.cycle_reset()
|
||||
|
||||
tb.set_idle_generator(idle_inserter)
|
||||
tb.set_backpressure_generator(backpressure_inserter)
|
||||
|
||||
async def worker(master, offset, aperture, count=16):
|
||||
for k in range(count):
|
||||
m = random.randrange(len(tb.axil_ram))
|
||||
length = random.randint(1, min(32, aperture))
|
||||
addr = offset+random.randint(0, aperture-length) + m*0x1000000
|
||||
test_data = bytearray([x % 256 for x in range(length)])
|
||||
|
||||
await Timer(random.randint(1, 100), 'ns')
|
||||
|
||||
await master.write(addr, test_data)
|
||||
|
||||
await Timer(random.randint(1, 100), 'ns')
|
||||
|
||||
data = await master.read(addr, length)
|
||||
assert data.data == test_data
|
||||
|
||||
workers = []
|
||||
|
||||
for k in range(16):
|
||||
workers.append(cocotb.start_soon(worker(tb.axil_master, k*0x1000, 0x1000, count=16)))
|
||||
|
||||
while workers:
|
||||
await workers.pop(0).join()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
def cycle_pause():
|
||||
return itertools.cycle([1, 1, 1, 0])
|
||||
|
||||
|
||||
if getattr(cocotb, 'top', None) is not None:
|
||||
|
||||
m_count = len(cocotb.top.m_axil)
|
||||
|
||||
for test in [run_test_write, run_test_read]:
|
||||
|
||||
factory = TestFactory(test)
|
||||
factory.add_option("idle_inserter", [None, cycle_pause])
|
||||
factory.add_option("backpressure_inserter", [None, cycle_pause])
|
||||
factory.add_option("m", range(min(m_count, 2)))
|
||||
factory.generate_tests()
|
||||
|
||||
factory = TestFactory(run_stress_test)
|
||||
factory.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
lib_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'lib'))
|
||||
taxi_src_dir = os.path.abspath(os.path.join(lib_dir, 'taxi', 'src'))
|
||||
|
||||
|
||||
def process_f_files(files):
|
||||
lst = {}
|
||||
for f in files:
|
||||
if f[-2:].lower() == '.f':
|
||||
with open(f, 'r') as fp:
|
||||
l = fp.read().split()
|
||||
for f in process_f_files([os.path.join(os.path.dirname(f), x) for x in l]):
|
||||
lst[os.path.basename(f)] = f
|
||||
else:
|
||||
lst[os.path.basename(f)] = f
|
||||
return list(lst.values())
|
||||
|
||||
|
||||
@pytest.mark.parametrize("data_w", [8, 16, 32])
|
||||
@pytest.mark.parametrize("m_count", [1, 4])
|
||||
def test_taxi_axil_interconnect_1s(request, m_count, data_w):
|
||||
dut = "taxi_axil_interconnect_1s"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = module
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(tests_dir, f"{toplevel}.sv"),
|
||||
os.path.join(rtl_dir, f"{dut}.f"),
|
||||
]
|
||||
|
||||
verilog_sources = process_f_files(verilog_sources)
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['M_COUNT'] = m_count
|
||||
parameters['DATA_W'] = data_w
|
||||
parameters['ADDR_W'] = 32
|
||||
parameters['STRB_W'] = parameters['DATA_W'] // 8
|
||||
parameters['AWUSER_EN'] = 0
|
||||
parameters['AWUSER_W'] = 1
|
||||
parameters['WUSER_EN'] = 0
|
||||
parameters['WUSER_W'] = 1
|
||||
parameters['BUSER_EN'] = 0
|
||||
parameters['BUSER_W'] = 1
|
||||
parameters['ARUSER_EN'] = 0
|
||||
parameters['ARUSER_W'] = 1
|
||||
parameters['RUSER_EN'] = 0
|
||||
parameters['RUSER_W'] = 1
|
||||
parameters['M_REGIONS'] = 1
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir, "sim_build",
|
||||
request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
simulator="verilator",
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
||||
@@ -0,0 +1,105 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* AXI4-lite interconnect testbench
|
||||
*/
|
||||
module test_taxi_axil_interconnect_1s #
|
||||
(
|
||||
/* verilator lint_off WIDTHTRUNC */
|
||||
parameter M_COUNT = 4,
|
||||
parameter DATA_W = 32,
|
||||
parameter ADDR_W = 32,
|
||||
parameter STRB_W = (DATA_W/8),
|
||||
parameter logic AWUSER_EN = 1'b0,
|
||||
parameter AWUSER_W = 1,
|
||||
parameter logic WUSER_EN = 1'b0,
|
||||
parameter WUSER_W = 1,
|
||||
parameter logic BUSER_EN = 1'b0,
|
||||
parameter BUSER_W = 1,
|
||||
parameter logic ARUSER_EN = 1'b0,
|
||||
parameter ARUSER_W = 1,
|
||||
parameter logic RUSER_EN = 1'b0,
|
||||
parameter RUSER_W = 1,
|
||||
parameter M_REGIONS = 1,
|
||||
parameter M_BASE_ADDR = '0,
|
||||
parameter M_ADDR_W = {M_COUNT{{M_REGIONS{32'd24}}}},
|
||||
parameter M_SECURE = {M_COUNT{1'b0}}
|
||||
/* verilator lint_on WIDTHTRUNC */
|
||||
)
|
||||
();
|
||||
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
taxi_axil_if #(
|
||||
.DATA_W(DATA_W),
|
||||
.ADDR_W(ADDR_W),
|
||||
.STRB_W(STRB_W),
|
||||
.AWUSER_EN(AWUSER_EN),
|
||||
.AWUSER_W(AWUSER_W),
|
||||
.WUSER_EN(WUSER_EN),
|
||||
.WUSER_W(WUSER_W),
|
||||
.BUSER_EN(BUSER_EN),
|
||||
.BUSER_W(BUSER_W),
|
||||
.ARUSER_EN(ARUSER_EN),
|
||||
.ARUSER_W(ARUSER_W),
|
||||
.RUSER_EN(RUSER_EN),
|
||||
.RUSER_W(RUSER_W)
|
||||
) s_axil();
|
||||
|
||||
taxi_axil_if #(
|
||||
.DATA_W(DATA_W),
|
||||
.ADDR_W(ADDR_W),
|
||||
.STRB_W(STRB_W),
|
||||
.AWUSER_EN(AWUSER_EN),
|
||||
.AWUSER_W(AWUSER_W),
|
||||
.WUSER_EN(WUSER_EN),
|
||||
.WUSER_W(WUSER_W),
|
||||
.BUSER_EN(BUSER_EN),
|
||||
.BUSER_W(BUSER_W),
|
||||
.ARUSER_EN(ARUSER_EN),
|
||||
.ARUSER_W(ARUSER_W),
|
||||
.RUSER_EN(RUSER_EN),
|
||||
.RUSER_W(RUSER_W)
|
||||
) m_axil[M_COUNT]();
|
||||
|
||||
taxi_axil_interconnect_1s #(
|
||||
.M_COUNT(M_COUNT),
|
||||
.ADDR_W(ADDR_W),
|
||||
.M_REGIONS(M_REGIONS),
|
||||
.M_BASE_ADDR(M_BASE_ADDR),
|
||||
.M_ADDR_W(M_ADDR_W),
|
||||
.M_SECURE(M_SECURE)
|
||||
)
|
||||
uut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI4-lite slave interface
|
||||
*/
|
||||
.s_axil_wr(s_axil),
|
||||
.s_axil_rd(s_axil),
|
||||
|
||||
/*
|
||||
* AXI4-lite master interface
|
||||
*/
|
||||
.m_axil_wr(m_axil),
|
||||
.m_axil_rd(m_axil)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
32
src/cndm/board/AS02MC04/fpga/README.md
Normal file
32
src/cndm/board/AS02MC04/fpga/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Corundum for Alibaba AS02MC04
|
||||
|
||||
## Introduction
|
||||
|
||||
This design targets the Alibaba AS02MC04 FPGA board.
|
||||
|
||||
* SFP+ cages
|
||||
* Looped-back 10GBASE-R or 25GBASE-R MAC via GTY transceiver
|
||||
|
||||
## Board details
|
||||
|
||||
* FPGA: xcku3p-ffvb676-1-e
|
||||
* PCIe: gen 3 x8 (~64 Gbps)
|
||||
* Reference oscillator: Fixed 156.25 MHz
|
||||
* 25GBASE-R PHY: Soft PCS with GTY transceiver
|
||||
|
||||
## Licensing
|
||||
|
||||
* Toolchain
|
||||
* Vivado Standard (enterprise license not required)
|
||||
* IP
|
||||
* No licensed vendor IP or 3rd party IP
|
||||
|
||||
## How to build
|
||||
|
||||
Run `make` in the appropriate `fpga*` subdirectory to build the bitstream. Ensure that the Xilinx Vivado toolchain components are in PATH.
|
||||
|
||||
On the host system, run `make` in `modules/cndm_proto` to build the driver. Ensure that the headers for the running kernel are installed, otherwise the driver cannot be compiled.
|
||||
|
||||
## How to test
|
||||
|
||||
Run `make program` to program the board with Vivado. Then, reboot the machine to re-enumerate the PCIe bus. Finally, load the driver on the host system with `insmod cndm_proto.ko`. Check `dmesg` for output from driver initialization. Run `cndm_proto_ddcmd.sh =p` to enable all debug messages.
|
||||
153
src/cndm/board/AS02MC04/fpga/common/vivado.mk
Normal file
153
src/cndm/board/AS02MC04/fpga/common/vivado.mk
Normal file
@@ -0,0 +1,153 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
###################################################################
|
||||
#
|
||||
# Xilinx Vivado FPGA Makefile
|
||||
#
|
||||
# Copyright (c) 2016-2025 Alex Forencich
|
||||
#
|
||||
###################################################################
|
||||
#
|
||||
# Parameters:
|
||||
# FPGA_TOP - Top module name
|
||||
# FPGA_FAMILY - FPGA family (e.g. VirtexUltrascale)
|
||||
# FPGA_DEVICE - FPGA device (e.g. xcvu095-ffva2104-2-e)
|
||||
# SYN_FILES - list of source files
|
||||
# INC_FILES - list of include files
|
||||
# XDC_FILES - list of timing constraint files
|
||||
# XCI_FILES - list of IP XCI files
|
||||
# IP_TCL_FILES - list of IP TCL files (sourced during project creation)
|
||||
# CONFIG_TCL_FILES - list of config TCL files (sourced before each build)
|
||||
#
|
||||
# Note: both SYN_FILES and INC_FILES support file list files. File list
|
||||
# files are files with a .f extension that contain a list of additional
|
||||
# files to include, one path relative to the .f file location per line.
|
||||
# The .f files are processed recursively, and then the complete file list
|
||||
# is de-duplicated, with later files in the list taking precedence.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# FPGA_TOP = fpga
|
||||
# FPGA_FAMILY = VirtexUltrascale
|
||||
# FPGA_DEVICE = xcvu095-ffva2104-2-e
|
||||
# SYN_FILES = rtl/fpga.v
|
||||
# XDC_FILES = fpga.xdc
|
||||
# XCI_FILES = ip/pcspma.xci
|
||||
# include ../common/vivado.mk
|
||||
#
|
||||
###################################################################
|
||||
|
||||
# phony targets
|
||||
.PHONY: fpga vivado tmpclean clean distclean
|
||||
|
||||
# prevent make from deleting intermediate files and reports
|
||||
.PRECIOUS: %.xpr %.bit %.bin %.ltx %.xsa %.mcs %.prm
|
||||
.SECONDARY:
|
||||
|
||||
CONFIG ?= config.mk
|
||||
-include $(CONFIG)
|
||||
|
||||
FPGA_TOP ?= fpga
|
||||
PROJECT ?= $(FPGA_TOP)
|
||||
XDC_FILES ?= $(PROJECT).xdc
|
||||
|
||||
# handle file list files
|
||||
process_f_file = $(call process_f_files,$(addprefix $(dir $1),$(shell cat $1)))
|
||||
process_f_files = $(foreach f,$1,$(if $(filter %.f,$f),$(call process_f_file,$f),$f))
|
||||
uniq_base = $(if $1,$(call uniq_base,$(foreach f,$1,$(if $(filter-out $(notdir $(lastword $1)),$(notdir $f)),$f,))) $(lastword $1))
|
||||
SYN_FILES := $(call uniq_base,$(call process_f_files,$(SYN_FILES)))
|
||||
INC_FILES := $(call uniq_base,$(call process_f_files,$(INC_FILES)))
|
||||
|
||||
###################################################################
|
||||
# Main Targets
|
||||
#
|
||||
# all: build everything (fpga)
|
||||
# fpga: build FPGA config
|
||||
# vivado: open project in Vivado
|
||||
# tmpclean: remove intermediate files
|
||||
# clean: remove output files and project files
|
||||
# distclean: remove archived output files
|
||||
###################################################################
|
||||
|
||||
all: fpga
|
||||
|
||||
fpga: $(PROJECT).bit
|
||||
|
||||
vivado: $(PROJECT).xpr
|
||||
vivado $(PROJECT).xpr
|
||||
|
||||
tmpclean::
|
||||
-rm -rf *.log *.jou *.cache *.gen *.hbs *.hw *.ip_user_files *.runs *.xpr *.html *.xml *.sim *.srcs *.str .Xil defines.v
|
||||
-rm -rf create_project.tcl update_config.tcl run_synth.tcl run_impl.tcl generate_bit.tcl
|
||||
|
||||
clean:: tmpclean
|
||||
-rm -rf *.bit *.bin *.ltx *.xsa program.tcl generate_mcs.tcl *.mcs *.prm flash.tcl
|
||||
-rm -rf *_utilization.rpt *_utilization_hierarchical.rpt
|
||||
|
||||
distclean:: clean
|
||||
-rm -rf rev
|
||||
|
||||
###################################################################
|
||||
# Target implementations
|
||||
###################################################################
|
||||
|
||||
# Vivado project file
|
||||
|
||||
# create fresh project if Makefile or IP files have changed
|
||||
create_project.tcl: Makefile $(XCI_FILES) $(IP_TCL_FILES)
|
||||
rm -rf defines.v
|
||||
touch defines.v
|
||||
for x in $(DEFS); do echo '`define' $$x >> defines.v; done
|
||||
echo "create_project -force -part $(FPGA_PART) $(PROJECT)" > $@
|
||||
echo "add_files -fileset sources_1 defines.v $(SYN_FILES)" >> $@
|
||||
echo "set_property top $(FPGA_TOP) [current_fileset]" >> $@
|
||||
echo "add_files -fileset constrs_1 $(XDC_FILES)" >> $@
|
||||
for x in $(XCI_FILES); do echo "import_ip $$x" >> $@; done
|
||||
for x in $(IP_TCL_FILES); do echo "source $$x" >> $@; done
|
||||
for x in $(CONFIG_TCL_FILES); do echo "source $$x" >> $@; done
|
||||
|
||||
# source config TCL scripts if any source file has changed
|
||||
update_config.tcl: $(CONFIG_TCL_FILES) $(SYN_FILES) $(INC_FILES) $(XDC_FILES)
|
||||
echo "open_project -quiet $(PROJECT).xpr" > $@
|
||||
for x in $(CONFIG_TCL_FILES); do echo "source $$x" >> $@; done
|
||||
|
||||
$(PROJECT).xpr: create_project.tcl update_config.tcl
|
||||
vivado -nojournal -nolog -mode batch $(foreach x,$?,-source $x)
|
||||
|
||||
# synthesis run
|
||||
$(PROJECT).runs/synth_1/$(PROJECT).dcp: create_project.tcl update_config.tcl $(SYN_FILES) $(INC_FILES) $(XDC_FILES) | $(PROJECT).xpr
|
||||
echo "open_project $(PROJECT).xpr" > run_synth.tcl
|
||||
echo "reset_run synth_1" >> run_synth.tcl
|
||||
echo "launch_runs -jobs 4 synth_1" >> run_synth.tcl
|
||||
echo "wait_on_run synth_1" >> run_synth.tcl
|
||||
vivado -nojournal -nolog -mode batch -source run_synth.tcl
|
||||
|
||||
# implementation run
|
||||
$(PROJECT).runs/impl_1/$(PROJECT)_routed.dcp: $(PROJECT).runs/synth_1/$(PROJECT).dcp
|
||||
echo "open_project $(PROJECT).xpr" > run_impl.tcl
|
||||
echo "reset_run impl_1" >> run_impl.tcl
|
||||
echo "launch_runs -jobs 4 impl_1" >> run_impl.tcl
|
||||
echo "wait_on_run impl_1" >> run_impl.tcl
|
||||
echo "open_run impl_1" >> run_impl.tcl
|
||||
echo "report_utilization -file $(PROJECT)_utilization.rpt" >> run_impl.tcl
|
||||
echo "report_utilization -hierarchical -file $(PROJECT)_utilization_hierarchical.rpt" >> run_impl.tcl
|
||||
vivado -nojournal -nolog -mode batch -source run_impl.tcl
|
||||
|
||||
# output files (including potentially bit, bin, ltx, and xsa)
|
||||
$(PROJECT).bit $(PROJECT).bin $(PROJECT).ltx $(PROJECT).xsa: $(PROJECT).runs/impl_1/$(PROJECT)_routed.dcp
|
||||
echo "open_project $(PROJECT).xpr" > generate_bit.tcl
|
||||
echo "open_run impl_1" >> generate_bit.tcl
|
||||
echo "write_bitstream -force -bin_file $(PROJECT).runs/impl_1/$(PROJECT).bit" >> generate_bit.tcl
|
||||
echo "write_debug_probes -force $(PROJECT).runs/impl_1/$(PROJECT).ltx" >> generate_bit.tcl
|
||||
echo "write_hw_platform -fixed -force -include_bit $(PROJECT).xsa" >> generate_bit.tcl
|
||||
vivado -nojournal -nolog -mode batch -source generate_bit.tcl
|
||||
ln -f -s $(PROJECT).runs/impl_1/$(PROJECT).bit .
|
||||
ln -f -s $(PROJECT).runs/impl_1/$(PROJECT).bin .
|
||||
if [ -e $(PROJECT).runs/impl_1/$(PROJECT).ltx ]; then ln -f -s $(PROJECT).runs/impl_1/$(PROJECT).ltx .; fi
|
||||
mkdir -p rev
|
||||
COUNT=100; \
|
||||
while [ -e rev/$(PROJECT)_rev$$COUNT.bit ]; \
|
||||
do COUNT=$$((COUNT+1)); done; \
|
||||
cp -pv $(PROJECT).runs/impl_1/$(PROJECT).bit rev/$(PROJECT)_rev$$COUNT.bit; \
|
||||
cp -pv $(PROJECT).runs/impl_1/$(PROJECT).bin rev/$(PROJECT)_rev$$COUNT.bin; \
|
||||
if [ -e $(PROJECT).runs/impl_1/$(PROJECT).ltx ]; then cp -pv $(PROJECT).runs/impl_1/$(PROJECT).ltx rev/$(PROJECT)_rev$$COUNT.ltx; fi; \
|
||||
if [ -e $(PROJECT).xsa ]; then cp -pv $(PROJECT).xsa rev/$(PROJECT)_rev$$COUNT.xsa; fi
|
||||
141
src/cndm/board/AS02MC04/fpga/fpga.xdc
Normal file
141
src/cndm/board/AS02MC04/fpga/fpga.xdc
Normal file
@@ -0,0 +1,141 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright (c) 2025 FPGA Ninja, LLC
|
||||
#
|
||||
# Authors:
|
||||
# - Alex Forencich
|
||||
#
|
||||
|
||||
# XDC constraints for the Alibaba AS02MC04
|
||||
# part: xcku3p-ffvb676-1-e
|
||||
|
||||
# General configuration
|
||||
set_property CFGBVS GND [current_design]
|
||||
set_property CONFIG_VOLTAGE 1.8 [current_design]
|
||||
set_property BITSTREAM.GENERAL.COMPRESS true [current_design]
|
||||
set_property BITSTREAM.CONFIG.UNUSEDPIN Pullup [current_design]
|
||||
set_property BITSTREAM.CONFIG.CONFIGRATE 72.9 [current_design]
|
||||
set_property CONFIG_MODE SPIx4 [current_design]
|
||||
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
|
||||
set_property BITSTREAM.CONFIG.SPI_FALL_EDGE Yes [current_design]
|
||||
set_property BITSTREAM.CONFIG.OVERTEMPSHUTDOWN Enable [current_design]
|
||||
|
||||
# 100 MHz system clock (Y2)
|
||||
set_property -dict {LOC E18 IOSTANDARD LVDS} [get_ports {clk_100mhz_p}]
|
||||
set_property -dict {LOC D18 IOSTANDARD LVDS} [get_ports {clk_100mhz_n}]
|
||||
create_clock -period 10 -name clk_100mhz [get_ports {clk_100mhz_p}]
|
||||
|
||||
# LEDs
|
||||
set_property -dict {LOC B12 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports {sfp_led[0]}] ;# DS3
|
||||
set_property -dict {LOC C12 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports {sfp_led[1]}] ;# DS2
|
||||
set_property -dict {LOC B11 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports {led[0]}] ;# DS6
|
||||
set_property -dict {LOC C11 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports {led[1]}] ;# DS7
|
||||
set_property -dict {LOC A10 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports {led[2]}] ;# DS8
|
||||
set_property -dict {LOC B10 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports {led[3]}] ;# DS9
|
||||
set_property -dict {LOC A13 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports {led_r}] ;# C1
|
||||
set_property -dict {LOC A12 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports {led_g}] ;# C1
|
||||
set_property -dict {LOC B9 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports {led_hb}] ;# DS5
|
||||
|
||||
set_false_path -to [get_ports {sfp_led[*] led[*] led_r led_g led_hb}]
|
||||
set_output_delay 0 [get_ports {sfp_led[*] led[*] led_r led_g led_hb}]
|
||||
|
||||
# Reset button
|
||||
set_property -dict {LOC F12 IOSTANDARD LVCMOS33} [get_ports reset] ;# SW1
|
||||
|
||||
set_false_path -from [get_ports {reset}]
|
||||
set_input_delay 0 [get_ports {reset}]
|
||||
|
||||
# GPIO
|
||||
#set_property -dict {LOC A14 IOSTANDARD LVCMOS33} [get_ports {gpio[0]}] ;# J5.3,4
|
||||
#set_property -dict {LOC E12 IOSTANDARD LVCMOS33} [get_ports {gpio[1]}] ;# J5.5,6
|
||||
#set_property -dict {LOC E13 IOSTANDARD LVCMOS33} [get_ports {gpio[2]}] ;# J5.7,8
|
||||
#set_property -dict {LOC F10 IOSTANDARD LVCMOS33} [get_ports {gpio[3]}] ;# J5.9,10
|
||||
#set_property -dict {LOC C9 IOSTANDARD LVCMOS33} [get_ports {gpio[4]}] ;# J5.11,12
|
||||
#set_property -dict {LOC D9 IOSTANDARD LVCMOS33} [get_ports {gpio[5]}] ;# J5.13,14
|
||||
|
||||
# SFP28 Interfaces
|
||||
set_property -dict {LOC A4 } [get_ports {sfp_rx_p[0]}] ;# MGTYRXP3_227 GTYE4_CHANNEL_X0Y15 / GTYE4_COMMON_X0Y3
|
||||
set_property -dict {LOC A3 } [get_ports {sfp_rx_n[0]}] ;# MGTYRXN3_227 GTYE4_CHANNEL_X0Y15 / GTYE4_COMMON_X0Y3
|
||||
set_property -dict {LOC B2 } [get_ports {sfp_rx_p[1]}] ;# MGTYRXP2_227 GTYE4_CHANNEL_X0Y14 / GTYE4_COMMON_X0Y3
|
||||
set_property -dict {LOC B1 } [get_ports {sfp_rx_n[1]}] ;# MGTYRXN2_227 GTYE4_CHANNEL_X0Y14 / GTYE4_COMMON_X0Y3
|
||||
set_property -dict {LOC B7 } [get_ports {sfp_tx_p[0]}] ;# MGTYTXP3_227 GTYE4_CHANNEL_X0Y15 / GTYE4_COMMON_X0Y3
|
||||
set_property -dict {LOC B6 } [get_ports {sfp_tx_n[0]}] ;# MGTYTXN3_227 GTYE4_CHANNEL_X0Y15 / GTYE4_COMMON_X0Y3
|
||||
set_property -dict {LOC D7 } [get_ports {sfp_tx_p[1]}] ;# MGTYTXP2_227 GTYE4_CHANNEL_X0Y14 / GTYE4_COMMON_X0Y3
|
||||
set_property -dict {LOC D6 } [get_ports {sfp_tx_n[1]}] ;# MGTYTXN2_227 GTYE4_CHANNEL_X0Y14 / GTYE4_COMMON_X0Y3
|
||||
set_property -dict {LOC K7 } [get_ports {sfp_mgt_refclk_p}] ;# MGTREFCLK0P_227 from Y1
|
||||
set_property -dict {LOC K6 } [get_ports {sfp_mgt_refclk_n}] ;# MGTREFCLK0N_227 from Y1
|
||||
set_property -dict {LOC D14 IOSTANDARD LVCMOS33 PULLUP true} [get_ports {sfp_npres[0]}]
|
||||
set_property -dict {LOC E11 IOSTANDARD LVCMOS33 PULLUP true} [get_ports {sfp_npres[1]}]
|
||||
set_property -dict {LOC B14 IOSTANDARD LVCMOS33 PULLUP true} [get_ports {sfp_tx_fault[0]}]
|
||||
set_property -dict {LOC F9 IOSTANDARD LVCMOS33 PULLUP true} [get_ports {sfp_tx_fault[1]}]
|
||||
set_property -dict {LOC D13 IOSTANDARD LVCMOS33 PULLUP true} [get_ports {sfp_los[0]}]
|
||||
set_property -dict {LOC E10 IOSTANDARD LVCMOS33 PULLUP true} [get_ports {sfp_los[1]}]
|
||||
#set_property -dict {LOC C13 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {sfp_i2c_scl[0]}]
|
||||
#set_property -dict {LOC D10 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {sfp_i2c_scl[1]}]
|
||||
#set_property -dict {LOC C14 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {sfp_i2c_sda[0]}]
|
||||
#set_property -dict {LOC D11 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {sfp_i2c_sda[1]}]
|
||||
|
||||
# 156.25 MHz MGT reference clock
|
||||
create_clock -period 6.4 -name sfp_mgt_refclk [get_ports {sfp_mgt_refclk_p}]
|
||||
|
||||
set_false_path -from [get_ports {sfp_npres[*] sfp_tx_fault[*] sfp_los[*]}]
|
||||
set_input_delay 0 [get_ports {sfp_npres[*] sfp_tx_fault[*] sfp_los[*]}]
|
||||
|
||||
#set_false_path -to [get_ports {sfp_i2c_sda[*] sfp_i2c_scl[*]}]
|
||||
#set_output_delay 0 [get_ports {sfp_i2c_sda[*] sfp_i2c_scl[*]}]
|
||||
#set_false_path -from [get_ports {sfp_i2c_sda[*] sfp_i2c_scl[*]}]
|
||||
#set_input_delay 0 [get_ports {sfp_i2c_sda[*] sfp_i2c_scl[*]}]
|
||||
|
||||
# I2C interface
|
||||
#set_property -dict {LOC G9 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {i2c_scl[0]}]
|
||||
#set_property -dict {LOC G10 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {i2c_sda[0]}]
|
||||
#set_property -dict {LOC J14 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {i2c_scl[1]}]
|
||||
#set_property -dict {LOC J15 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {i2c_sda[1]}]
|
||||
|
||||
#set_false_path -to [get_ports {i2c_sda[*] i2c_scl[*]}]
|
||||
#set_output_delay 0 [get_ports {i2c_sda[*] i2c_scl[*]}]
|
||||
#set_false_path -from [get_ports {i2c_sda[*] i2c_scl[*]}]
|
||||
#set_input_delay 0 [get_ports {i2c_sda[*] i2c_scl[*]}]
|
||||
|
||||
# PCIe Interface
|
||||
set_property -dict {LOC P2 } [get_ports {pcie_rx_p[0]}] ;# MGTYRXP3_225 GTYE4_CHANNEL_X0Y7 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC P1 } [get_ports {pcie_rx_n[0]}] ;# MGTYRXN3_225 GTYE4_CHANNEL_X0Y7 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC R5 } [get_ports {pcie_tx_p[0]}] ;# MGTYTXP3_225 GTYE4_CHANNEL_X0Y7 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC R4 } [get_ports {pcie_tx_n[0]}] ;# MGTYTXN3_225 GTYE4_CHANNEL_X0Y7 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC T2 } [get_ports {pcie_rx_p[1]}] ;# MGTYRXP2_225 GTYE4_CHANNEL_X0Y6 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC T1 } [get_ports {pcie_rx_n[1]}] ;# MGTYRXN2_225 GTYE4_CHANNEL_X0Y6 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC U5 } [get_ports {pcie_tx_p[1]}] ;# MGTYTXP2_225 GTYE4_CHANNEL_X0Y6 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC U4 } [get_ports {pcie_tx_n[1]}] ;# MGTYTXN2_225 GTYE4_CHANNEL_X0Y6 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC V2 } [get_ports {pcie_rx_p[2]}] ;# MGTYRXP1_225 GTYE4_CHANNEL_X0Y5 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC V1 } [get_ports {pcie_rx_n[2]}] ;# MGTYRXN1_225 GTYE4_CHANNEL_X0Y5 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC W5 } [get_ports {pcie_tx_p[2]}] ;# MGTYTXP1_225 GTYE4_CHANNEL_X0Y5 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC W4 } [get_ports {pcie_tx_n[2]}] ;# MGTYTXN1_225 GTYE4_CHANNEL_X0Y5 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC Y2 } [get_ports {pcie_rx_p[3]}] ;# MGTYRXP0_225 GTYE4_CHANNEL_X0Y4 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC Y1 } [get_ports {pcie_rx_n[3]}] ;# MGTYRXN0_225 GTYE4_CHANNEL_X0Y4 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC AA5 } [get_ports {pcie_tx_p[3]}] ;# MGTYTXP0_225 GTYE4_CHANNEL_X0Y4 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC AA4 } [get_ports {pcie_tx_n[3]}] ;# MGTYTXN0_225 GTYE4_CHANNEL_X0Y4 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC AB2 } [get_ports {pcie_rx_p[4]}] ;# MGTYRXP3_224 GTYE4_CHANNEL_X0Y3 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AB1 } [get_ports {pcie_rx_n[4]}] ;# MGTYRXN3_224 GTYE4_CHANNEL_X0Y3 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AC5 } [get_ports {pcie_tx_p[4]}] ;# MGTYTXP3_224 GTYE4_CHANNEL_X0Y3 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AC4 } [get_ports {pcie_tx_n[4]}] ;# MGTYTXN3_224 GTYE4_CHANNEL_X0Y3 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AD2 } [get_ports {pcie_rx_p[5]}] ;# MGTYRXP2_224 GTYE4_CHANNEL_X0Y2 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AD1 } [get_ports {pcie_rx_n[5]}] ;# MGTYRXN2_224 GTYE4_CHANNEL_X0Y2 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AD7 } [get_ports {pcie_tx_p[5]}] ;# MGTYTXP2_224 GTYE4_CHANNEL_X0Y2 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AD6 } [get_ports {pcie_tx_n[5]}] ;# MGTYTXN2_224 GTYE4_CHANNEL_X0Y2 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AE4 } [get_ports {pcie_rx_p[6]}] ;# MGTYRXP1_224 GTYE4_CHANNEL_X0Y1 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AE3 } [get_ports {pcie_rx_n[6]}] ;# MGTYRXN1_224 GTYE4_CHANNEL_X0Y1 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AE9 } [get_ports {pcie_tx_p[6]}] ;# MGTYTXP1_224 GTYE4_CHANNEL_X0Y1 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AE8 } [get_ports {pcie_tx_n[6]}] ;# MGTYTXN1_224 GTYE4_CHANNEL_X0Y1 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AF2 } [get_ports {pcie_rx_p[7]}] ;# MGTYRXP0_224 GTYE4_CHANNEL_X0Y0 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AF1 } [get_ports {pcie_rx_n[7]}] ;# MGTYRXN0_224 GTYE4_CHANNEL_X0Y0 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AF7 } [get_ports {pcie_tx_p[7]}] ;# MGTYTXP0_224 GTYE4_CHANNEL_X0Y0 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AF6 } [get_ports {pcie_tx_n[7]}] ;# MGTYTXN0_224 GTYE4_CHANNEL_X0Y0 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC T7 } [get_ports pcie_refclk_p] ;# MGTREFCLK1P_225
|
||||
set_property -dict {LOC T6 } [get_ports pcie_refclk_n] ;# MGTREFCLK1N_225
|
||||
set_property -dict {LOC A9 IOSTANDARD LVCMOS33 PULLUP true} [get_ports pcie_reset_n]
|
||||
|
||||
set_false_path -from [get_ports {pcie_reset_n}]
|
||||
set_input_delay 0 [get_ports {pcie_reset_n}]
|
||||
|
||||
# 100 MHz MGT reference clock
|
||||
create_clock -period 10 -name pcie_mgt_refclk [get_ports pcie_refclk_p]
|
||||
89
src/cndm/board/AS02MC04/fpga/fpga/Makefile
Normal file
89
src/cndm/board/AS02MC04/fpga/fpga/Makefile
Normal file
@@ -0,0 +1,89 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright (c) 2025 FPGA Ninja, LLC
|
||||
#
|
||||
# Authors:
|
||||
# - Alex Forencich
|
||||
#
|
||||
|
||||
# FPGA settings
|
||||
FPGA_PART = xcku3p-ffvb676-1-e
|
||||
FPGA_TOP = fpga
|
||||
FPGA_ARCH = kintexuplus
|
||||
|
||||
RTL_DIR = ../rtl
|
||||
LIB_DIR = ../lib
|
||||
TAXI_SRC_DIR = $(LIB_DIR)/taxi/src
|
||||
|
||||
# Files for synthesis
|
||||
SYN_FILES = $(RTL_DIR)/fpga.sv
|
||||
SYN_FILES += $(RTL_DIR)/fpga_core.sv
|
||||
SYN_FILES += $(TAXI_SRC_DIR)/corundum/rtl/cndm_micro_pcie_us.f
|
||||
SYN_FILES += $(TAXI_SRC_DIR)/eth/rtl/us/taxi_eth_mac_25g_us.f
|
||||
SYN_FILES += $(TAXI_SRC_DIR)/axis/rtl/taxi_axis_async_fifo.f
|
||||
SYN_FILES += $(TAXI_SRC_DIR)/sync/rtl/taxi_sync_reset.sv
|
||||
SYN_FILES += $(TAXI_SRC_DIR)/sync/rtl/taxi_sync_signal.sv
|
||||
|
||||
# XDC files
|
||||
XDC_FILES = ../fpga.xdc
|
||||
XDC_FILES += $(TAXI_SRC_DIR)/eth/syn/vivado/taxi_eth_mac_fifo.tcl
|
||||
XDC_FILES += $(TAXI_SRC_DIR)/axis/syn/vivado/taxi_axis_async_fifo.tcl
|
||||
XDC_FILES += $(TAXI_SRC_DIR)/sync/syn/vivado/taxi_sync_reset.tcl
|
||||
XDC_FILES += $(TAXI_SRC_DIR)/sync/syn/vivado/taxi_sync_signal.tcl
|
||||
|
||||
# IP
|
||||
IP_TCL_FILES = $(TAXI_SRC_DIR)/eth/rtl/us/taxi_eth_phy_25g_us_gty_25g_156.tcl
|
||||
IP_TCL_FILES += ../ip/pcie4_uscale_plus_0.tcl
|
||||
|
||||
# Configuration
|
||||
CONFIG_TCL_FILES = ./config.tcl
|
||||
|
||||
include ../common/vivado.mk
|
||||
|
||||
program: $(PROJECT).bit
|
||||
echo "open_hw_manager" > program.tcl
|
||||
echo "connect_hw_server" >> program.tcl
|
||||
echo "open_hw_target" >> program.tcl
|
||||
echo "current_hw_device [lindex [get_hw_devices] 0]" >> program.tcl
|
||||
echo "refresh_hw_device -update_hw_probes false [current_hw_device]" >> program.tcl
|
||||
echo "set_property PROGRAM.FILE {$(PROJECT).bit} [current_hw_device]" >> program.tcl
|
||||
echo "program_hw_devices [current_hw_device]" >> program.tcl
|
||||
echo "exit" >> program.tcl
|
||||
vivado -nojournal -nolog -mode batch -source program.tcl
|
||||
|
||||
$(PROJECT).mcs $(PROJECT).prm: $(PROJECT).bit
|
||||
echo "write_cfgmem -force -format mcs -size 32 -interface SPIx4 -loadbit {up 0x0000000 $*.bit} -checksum -file $*.mcs" > generate_mcs.tcl
|
||||
echo "exit" >> generate_mcs.tcl
|
||||
vivado -nojournal -nolog -mode batch -source generate_mcs.tcl
|
||||
mkdir -p rev
|
||||
COUNT=100; \
|
||||
while [ -e rev/$*_rev$$COUNT.bit ]; \
|
||||
do COUNT=$$((COUNT+1)); done; \
|
||||
COUNT=$$((COUNT-1)); \
|
||||
for x in .mcs .prm; \
|
||||
do cp $*$$x rev/$*_rev$$COUNT$$x; \
|
||||
echo "Output: rev/$*_rev$$COUNT$$x"; done;
|
||||
|
||||
flash: $(PROJECT).mcs $(PROJECT).prm
|
||||
echo "open_hw" > flash.tcl
|
||||
echo "connect_hw_server" >> flash.tcl
|
||||
echo "open_hw_target" >> flash.tcl
|
||||
echo "current_hw_device [lindex [get_hw_devices] 0]" >> flash.tcl
|
||||
echo "refresh_hw_device -update_hw_probes false [current_hw_device]" >> flash.tcl
|
||||
echo "create_hw_cfgmem -hw_device [current_hw_device] [lindex [get_cfgmem_parts {mt25qu256-spi-x1_x2_x4}] 0]" >> flash.tcl
|
||||
echo "current_hw_cfgmem -hw_device [current_hw_device] [get_property PROGRAM.HW_CFGMEM [current_hw_device]]" >> flash.tcl
|
||||
echo "set_property PROGRAM.FILES [list \"$(PROJECT).mcs\"] [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.PRM_FILES [list \"$(PROJECT).prm\"] [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.ERASE 1 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.CFG_PROGRAM 1 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.VERIFY 1 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.CHECKSUM 0 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.ADDRESS_RANGE {use_file} [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.UNUSED_PIN_TERMINATION {pull-none} [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "create_hw_bitstream -hw_device [current_hw_device] [get_property PROGRAM.HW_CFGMEM_BITFILE [current_hw_device]]" >> flash.tcl
|
||||
echo "program_hw_devices [current_hw_device]" >> flash.tcl
|
||||
echo "refresh_hw_device [current_hw_device]" >> flash.tcl
|
||||
echo "program_hw_cfgmem -hw_cfgmem [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "boot_hw_device [current_hw_device]" >> flash.tcl
|
||||
echo "exit" >> flash.tcl
|
||||
vivado -nojournal -nolog -mode batch -source flash.tcl
|
||||
22
src/cndm/board/AS02MC04/fpga/fpga/config.tcl
Normal file
22
src/cndm/board/AS02MC04/fpga/fpga/config.tcl
Normal file
@@ -0,0 +1,22 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright (c) 2025 FPGA Ninja, LLC
|
||||
#
|
||||
# Authors:
|
||||
# - Alex Forencich
|
||||
#
|
||||
|
||||
set params [dict create]
|
||||
|
||||
# 10G MAC configuration
|
||||
dict set params CFG_LOW_LATENCY "1"
|
||||
dict set params COMBINED_MAC_PCS "1"
|
||||
dict set params MAC_DATA_W "64"
|
||||
|
||||
# apply parameters to top-level
|
||||
set param_list {}
|
||||
dict for {name value} $params {
|
||||
lappend param_list $name=$value
|
||||
}
|
||||
|
||||
set_property generic $param_list [get_filesets sources_1]
|
||||
89
src/cndm/board/AS02MC04/fpga/fpga_10g/Makefile
Normal file
89
src/cndm/board/AS02MC04/fpga/fpga_10g/Makefile
Normal file
@@ -0,0 +1,89 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright (c) 2025 FPGA Ninja, LLC
|
||||
#
|
||||
# Authors:
|
||||
# - Alex Forencich
|
||||
#
|
||||
|
||||
# FPGA settings
|
||||
FPGA_PART = xcku3p-ffvb676-1-e
|
||||
FPGA_TOP = fpga
|
||||
FPGA_ARCH = kintexuplus
|
||||
|
||||
RTL_DIR = ../rtl
|
||||
LIB_DIR = ../lib
|
||||
TAXI_SRC_DIR = $(LIB_DIR)/taxi/src
|
||||
|
||||
# Files for synthesis
|
||||
SYN_FILES = $(RTL_DIR)/fpga.sv
|
||||
SYN_FILES += $(RTL_DIR)/fpga_core.sv
|
||||
SYN_FILES += $(TAXI_SRC_DIR)/cndm/rtl/cndm_micro_pcie_us.f
|
||||
SYN_FILES += $(TAXI_SRC_DIR)/eth/rtl/us/taxi_eth_mac_25g_us.f
|
||||
SYN_FILES += $(TAXI_SRC_DIR)/axis/rtl/taxi_axis_async_fifo.f
|
||||
SYN_FILES += $(TAXI_SRC_DIR)/sync/rtl/taxi_sync_reset.sv
|
||||
SYN_FILES += $(TAXI_SRC_DIR)/sync/rtl/taxi_sync_signal.sv
|
||||
|
||||
# XDC files
|
||||
XDC_FILES = ../fpga.xdc
|
||||
XDC_FILES += $(TAXI_SRC_DIR)/eth/syn/vivado/taxi_eth_mac_fifo.tcl
|
||||
XDC_FILES += $(TAXI_SRC_DIR)/axis/syn/vivado/taxi_axis_async_fifo.tcl
|
||||
XDC_FILES += $(TAXI_SRC_DIR)/sync/syn/vivado/taxi_sync_reset.tcl
|
||||
XDC_FILES += $(TAXI_SRC_DIR)/sync/syn/vivado/taxi_sync_signal.tcl
|
||||
|
||||
# IP
|
||||
IP_TCL_FILES = $(TAXI_SRC_DIR)/eth/rtl/us/taxi_eth_phy_10g_us_gty_156.tcl
|
||||
IP_TCL_FILES += ../ip/pcie4_uscale_plus_0.tcl
|
||||
|
||||
# Configuration
|
||||
CONFIG_TCL_FILES = ./config.tcl
|
||||
|
||||
include ../common/vivado.mk
|
||||
|
||||
program: $(PROJECT).bit
|
||||
echo "open_hw_manager" > program.tcl
|
||||
echo "connect_hw_server" >> program.tcl
|
||||
echo "open_hw_target" >> program.tcl
|
||||
echo "current_hw_device [lindex [get_hw_devices] 0]" >> program.tcl
|
||||
echo "refresh_hw_device -update_hw_probes false [current_hw_device]" >> program.tcl
|
||||
echo "set_property PROGRAM.FILE {$(PROJECT).bit} [current_hw_device]" >> program.tcl
|
||||
echo "program_hw_devices [current_hw_device]" >> program.tcl
|
||||
echo "exit" >> program.tcl
|
||||
vivado -nojournal -nolog -mode batch -source program.tcl
|
||||
|
||||
$(PROJECT).mcs $(PROJECT).prm: $(PROJECT).bit
|
||||
echo "write_cfgmem -force -format mcs -size 32 -interface SPIx4 -loadbit {up 0x0000000 $*.bit} -checksum -file $*.mcs" > generate_mcs.tcl
|
||||
echo "exit" >> generate_mcs.tcl
|
||||
vivado -nojournal -nolog -mode batch -source generate_mcs.tcl
|
||||
mkdir -p rev
|
||||
COUNT=100; \
|
||||
while [ -e rev/$*_rev$$COUNT.bit ]; \
|
||||
do COUNT=$$((COUNT+1)); done; \
|
||||
COUNT=$$((COUNT-1)); \
|
||||
for x in .mcs .prm; \
|
||||
do cp $*$$x rev/$*_rev$$COUNT$$x; \
|
||||
echo "Output: rev/$*_rev$$COUNT$$x"; done;
|
||||
|
||||
flash: $(PROJECT).mcs $(PROJECT).prm
|
||||
echo "open_hw" > flash.tcl
|
||||
echo "connect_hw_server" >> flash.tcl
|
||||
echo "open_hw_target" >> flash.tcl
|
||||
echo "current_hw_device [lindex [get_hw_devices] 0]" >> flash.tcl
|
||||
echo "refresh_hw_device -update_hw_probes false [current_hw_device]" >> flash.tcl
|
||||
echo "create_hw_cfgmem -hw_device [current_hw_device] [lindex [get_cfgmem_parts {mt25qu256-spi-x1_x2_x4}] 0]" >> flash.tcl
|
||||
echo "current_hw_cfgmem -hw_device [current_hw_device] [get_property PROGRAM.HW_CFGMEM [current_hw_device]]" >> flash.tcl
|
||||
echo "set_property PROGRAM.FILES [list \"$(PROJECT).mcs\"] [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.PRM_FILES [list \"$(PROJECT).prm\"] [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.ERASE 1 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.CFG_PROGRAM 1 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.VERIFY 1 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.CHECKSUM 0 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.ADDRESS_RANGE {use_file} [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.UNUSED_PIN_TERMINATION {pull-none} [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "create_hw_bitstream -hw_device [current_hw_device] [get_property PROGRAM.HW_CFGMEM_BITFILE [current_hw_device]]" >> flash.tcl
|
||||
echo "program_hw_devices [current_hw_device]" >> flash.tcl
|
||||
echo "refresh_hw_device [current_hw_device]" >> flash.tcl
|
||||
echo "program_hw_cfgmem -hw_cfgmem [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "boot_hw_device [current_hw_device]" >> flash.tcl
|
||||
echo "exit" >> flash.tcl
|
||||
vivado -nojournal -nolog -mode batch -source flash.tcl
|
||||
22
src/cndm/board/AS02MC04/fpga/fpga_10g/config.tcl
Normal file
22
src/cndm/board/AS02MC04/fpga/fpga_10g/config.tcl
Normal file
@@ -0,0 +1,22 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright (c) 2025 FPGA Ninja, LLC
|
||||
#
|
||||
# Authors:
|
||||
# - Alex Forencich
|
||||
#
|
||||
|
||||
set params [dict create]
|
||||
|
||||
# 10G MAC configuration
|
||||
dict set params CFG_LOW_LATENCY "1"
|
||||
dict set params COMBINED_MAC_PCS "1"
|
||||
dict set params MAC_DATA_W "32"
|
||||
|
||||
# apply parameters to top-level
|
||||
set param_list {}
|
||||
dict for {name value} $params {
|
||||
lappend param_list $name=$value
|
||||
}
|
||||
|
||||
set_property generic $param_list [get_filesets sources_1]
|
||||
28
src/cndm/board/AS02MC04/fpga/ip/pcie4_uscale_plus_0.tcl
Normal file
28
src/cndm/board/AS02MC04/fpga/ip/pcie4_uscale_plus_0.tcl
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
create_ip -name pcie4_uscale_plus -vendor xilinx.com -library ip -module_name pcie4_uscale_plus_0
|
||||
|
||||
set_property -dict [list \
|
||||
CONFIG.PL_LINK_CAP_MAX_LINK_SPEED {8.0_GT/s} \
|
||||
CONFIG.PL_LINK_CAP_MAX_LINK_WIDTH {X8} \
|
||||
CONFIG.AXISTEN_IF_RC_STRADDLE {false} \
|
||||
CONFIG.axisten_if_enable_client_tag {true} \
|
||||
CONFIG.axisten_if_width {256_bit} \
|
||||
CONFIG.extended_tag_field {true} \
|
||||
CONFIG.pf0_dev_cap_max_payload {1024_bytes} \
|
||||
CONFIG.axisten_freq {250} \
|
||||
CONFIG.PF0_CLASS_CODE {058000} \
|
||||
CONFIG.PF0_DEVICE_ID {C001} \
|
||||
CONFIG.PF0_SUBSYSTEM_ID {0009} \
|
||||
CONFIG.PF0_SUBSYSTEM_VENDOR_ID {1ded} \
|
||||
CONFIG.pf0_bar0_64bit {true} \
|
||||
CONFIG.pf0_bar0_prefetchable {true} \
|
||||
CONFIG.pf0_bar0_scale {Megabytes} \
|
||||
CONFIG.pf0_bar0_size {16} \
|
||||
CONFIG.pf0_msi_enabled {true} \
|
||||
CONFIG.PF0_MSI_CAP_MULTIMSGCAP {32_vectors} \
|
||||
CONFIG.en_msi_per_vec_masking {true} \
|
||||
CONFIG.vendor_id {1234} \
|
||||
CONFIG.mode_selection {Advanced} \
|
||||
CONFIG.en_gt_selection {true} \
|
||||
CONFIG.select_quad {GTY_Quad_225} \
|
||||
] [get_ips pcie4_uscale_plus_0]
|
||||
1
src/cndm/board/AS02MC04/fpga/lib/taxi
Symbolic link
1
src/cndm/board/AS02MC04/fpga/lib/taxi
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../../../
|
||||
598
src/cndm/board/AS02MC04/fpga/rtl/fpga.sv
Normal file
598
src/cndm/board/AS02MC04/fpga/rtl/fpga.sv
Normal file
@@ -0,0 +1,598 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
|
||||
Copyright (c) 2014-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* FPGA top-level module
|
||||
*/
|
||||
module fpga #
|
||||
(
|
||||
// simulation (set to avoid vendor primitives)
|
||||
parameter logic SIM = 1'b0,
|
||||
// vendor ("GENERIC", "XILINX", "ALTERA")
|
||||
parameter string VENDOR = "XILINX",
|
||||
// device family
|
||||
parameter string FAMILY = "kintexuplus",
|
||||
// 10G/25G MAC configuration
|
||||
parameter logic CFG_LOW_LATENCY = 1'b1,
|
||||
parameter logic COMBINED_MAC_PCS = 1'b1,
|
||||
parameter MAC_DATA_W = 64
|
||||
)
|
||||
(
|
||||
/*
|
||||
* Clock: 100MHz LVDS
|
||||
* Reset: Push button, active high
|
||||
*/
|
||||
input wire logic clk_100mhz_p,
|
||||
input wire logic clk_100mhz_n,
|
||||
input wire logic reset,
|
||||
|
||||
/*
|
||||
* GPIO
|
||||
*/
|
||||
output wire logic sfp_led[2],
|
||||
output wire logic [3:0] led,
|
||||
output wire logic led_r,
|
||||
output wire logic led_g,
|
||||
output wire logic led_hb,
|
||||
|
||||
/*
|
||||
* Ethernet: SFP+
|
||||
*/
|
||||
input wire logic sfp_rx_p[2],
|
||||
input wire logic sfp_rx_n[2],
|
||||
output wire logic sfp_tx_p[2],
|
||||
output wire logic sfp_tx_n[2],
|
||||
input wire logic sfp_mgt_refclk_p,
|
||||
input wire logic sfp_mgt_refclk_n,
|
||||
input wire logic [1:0] sfp_npres,
|
||||
input wire logic [1:0] sfp_tx_fault,
|
||||
input wire logic [1:0] sfp_los,
|
||||
|
||||
/*
|
||||
* PCIe
|
||||
*/
|
||||
input wire logic [7:0] pcie_rx_p,
|
||||
input wire logic [7:0] pcie_rx_n,
|
||||
output wire logic [7:0] pcie_tx_p,
|
||||
output wire logic [7:0] pcie_tx_n,
|
||||
input wire logic pcie_refclk_p,
|
||||
input wire logic pcie_refclk_n,
|
||||
input wire logic pcie_reset_n
|
||||
);
|
||||
|
||||
// Clock and reset
|
||||
|
||||
wire clk_100mhz_ibufg;
|
||||
|
||||
// Internal 125 MHz clock
|
||||
wire clk_125mhz_mmcm_out;
|
||||
wire clk_125mhz_int;
|
||||
wire rst_125mhz_int;
|
||||
|
||||
wire mmcm_rst = !reset;
|
||||
wire mmcm_locked;
|
||||
wire mmcm_clkfb;
|
||||
|
||||
IBUFGDS #(
|
||||
.DIFF_TERM("FALSE"),
|
||||
.IBUF_LOW_PWR("FALSE")
|
||||
)
|
||||
clk_100mhz_ibufg_inst (
|
||||
.O (clk_100mhz_ibufg),
|
||||
.I (clk_100mhz_p),
|
||||
.IB (clk_100mhz_n)
|
||||
);
|
||||
|
||||
// MMCM instance
|
||||
MMCME4_BASE #(
|
||||
// 100 MHz input
|
||||
.CLKIN1_PERIOD(10.0),
|
||||
.REF_JITTER1(0.010),
|
||||
// 100 MHz input / 1 = 100 MHz PFD (range 10 MHz to 500 MHz)
|
||||
.DIVCLK_DIVIDE(1),
|
||||
// 100 MHz PFD * 10 = 1000 MHz VCO (range 800 MHz to 1600 MHz)
|
||||
.CLKFBOUT_MULT_F(10),
|
||||
.CLKFBOUT_PHASE(0),
|
||||
// 1250 MHz / 8 = 125 MHz, 0 degrees
|
||||
.CLKOUT0_DIVIDE_F(8),
|
||||
.CLKOUT0_DUTY_CYCLE(0.5),
|
||||
.CLKOUT0_PHASE(0),
|
||||
// Not used
|
||||
.CLKOUT1_DIVIDE(1),
|
||||
.CLKOUT1_DUTY_CYCLE(0.5),
|
||||
.CLKOUT1_PHASE(0),
|
||||
// Not used
|
||||
.CLKOUT2_DIVIDE(1),
|
||||
.CLKOUT2_DUTY_CYCLE(0.5),
|
||||
.CLKOUT2_PHASE(0),
|
||||
// Not used
|
||||
.CLKOUT3_DIVIDE(1),
|
||||
.CLKOUT3_DUTY_CYCLE(0.5),
|
||||
.CLKOUT3_PHASE(0),
|
||||
// Not used
|
||||
.CLKOUT4_DIVIDE(1),
|
||||
.CLKOUT4_DUTY_CYCLE(0.5),
|
||||
.CLKOUT4_PHASE(0),
|
||||
.CLKOUT4_CASCADE("FALSE"),
|
||||
// Not used
|
||||
.CLKOUT5_DIVIDE(1),
|
||||
.CLKOUT5_DUTY_CYCLE(0.5),
|
||||
.CLKOUT5_PHASE(0),
|
||||
// Not used
|
||||
.CLKOUT6_DIVIDE(1),
|
||||
.CLKOUT6_DUTY_CYCLE(0.5),
|
||||
.CLKOUT6_PHASE(0),
|
||||
|
||||
// optimized bandwidth
|
||||
.BANDWIDTH("OPTIMIZED"),
|
||||
// don't wait for lock during startup
|
||||
.STARTUP_WAIT("FALSE")
|
||||
)
|
||||
clk_mmcm_inst (
|
||||
// 100 MHz input
|
||||
.CLKIN1(clk_100mhz_ibufg),
|
||||
// direct clkfb feeback
|
||||
.CLKFBIN(mmcm_clkfb),
|
||||
.CLKFBOUT(mmcm_clkfb),
|
||||
.CLKFBOUTB(),
|
||||
// 125 MHz, 0 degrees
|
||||
.CLKOUT0(clk_125mhz_mmcm_out),
|
||||
.CLKOUT0B(),
|
||||
// Not used
|
||||
.CLKOUT1(),
|
||||
.CLKOUT1B(),
|
||||
// Not used
|
||||
.CLKOUT2(),
|
||||
.CLKOUT2B(),
|
||||
// Not used
|
||||
.CLKOUT3(),
|
||||
.CLKOUT3B(),
|
||||
// Not used
|
||||
.CLKOUT4(),
|
||||
// Not used
|
||||
.CLKOUT5(),
|
||||
// Not used
|
||||
.CLKOUT6(),
|
||||
// reset input
|
||||
.RST(mmcm_rst),
|
||||
// don't power down
|
||||
.PWRDWN(1'b0),
|
||||
// locked output
|
||||
.LOCKED(mmcm_locked)
|
||||
);
|
||||
|
||||
BUFG
|
||||
clk_125mhz_bufg_inst (
|
||||
.I(clk_125mhz_mmcm_out),
|
||||
.O(clk_125mhz_int)
|
||||
);
|
||||
|
||||
taxi_sync_reset #(
|
||||
.N(4)
|
||||
)
|
||||
sync_reset_125mhz_inst (
|
||||
.clk(clk_125mhz_int),
|
||||
.rst(~mmcm_locked),
|
||||
.out(rst_125mhz_int)
|
||||
);
|
||||
|
||||
// PCIe
|
||||
localparam AXIS_PCIE_DATA_W = 256;
|
||||
localparam AXIS_PCIE_KEEP_W = (AXIS_PCIE_DATA_W/32);
|
||||
localparam AXIS_PCIE_RC_USER_W = 75;
|
||||
localparam AXIS_PCIE_RQ_USER_W = 62;
|
||||
localparam AXIS_PCIE_CQ_USER_W = 85;
|
||||
localparam AXIS_PCIE_CC_USER_W = 33;
|
||||
localparam RC_STRADDLE = 1'b0; // AXIS_PCIE_DATA_W >= 256;
|
||||
|
||||
localparam RQ_SEQ_NUM_W = AXIS_PCIE_RQ_USER_W == 60 ? 4 : 6;
|
||||
localparam RQ_SEQ_NUM_EN = 1;
|
||||
|
||||
localparam PCIE_TAG_CNT = 64;
|
||||
localparam BAR0_APERTURE = 24;
|
||||
|
||||
logic pcie_user_clk;
|
||||
logic pcie_user_rst;
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(AXIS_PCIE_DATA_W),
|
||||
.KEEP_EN(1),
|
||||
.KEEP_W(AXIS_PCIE_KEEP_W),
|
||||
.USER_EN(1),
|
||||
.USER_W(AXIS_PCIE_CQ_USER_W)
|
||||
) axis_pcie_cq();
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(AXIS_PCIE_DATA_W),
|
||||
.KEEP_EN(1),
|
||||
.KEEP_W(AXIS_PCIE_KEEP_W),
|
||||
.USER_EN(1),
|
||||
.USER_W(AXIS_PCIE_CC_USER_W)
|
||||
) axis_pcie_cc();
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(AXIS_PCIE_DATA_W),
|
||||
.KEEP_EN(1),
|
||||
.KEEP_W(AXIS_PCIE_KEEP_W),
|
||||
.USER_EN(1),
|
||||
.USER_W(AXIS_PCIE_RQ_USER_W)
|
||||
) axis_pcie_rq();
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(AXIS_PCIE_DATA_W),
|
||||
.KEEP_EN(1),
|
||||
.KEEP_W(AXIS_PCIE_KEEP_W),
|
||||
.USER_EN(1),
|
||||
.USER_W(AXIS_PCIE_RC_USER_W)
|
||||
) axis_pcie_rc();
|
||||
|
||||
wire [RQ_SEQ_NUM_W-1:0] pcie_rq_seq_num0;
|
||||
wire pcie_rq_seq_num_vld0;
|
||||
wire [RQ_SEQ_NUM_W-1:0] pcie_rq_seq_num1;
|
||||
wire pcie_rq_seq_num_vld1;
|
||||
|
||||
wire [2:0] cfg_max_payload;
|
||||
wire [2:0] cfg_max_read_req;
|
||||
wire [3:0] cfg_rcb_status;
|
||||
|
||||
wire [9:0] cfg_mgmt_addr;
|
||||
wire [7:0] cfg_mgmt_function_number;
|
||||
wire cfg_mgmt_write;
|
||||
wire [31:0] cfg_mgmt_write_data;
|
||||
wire [3:0] cfg_mgmt_byte_enable;
|
||||
wire cfg_mgmt_read;
|
||||
wire [31:0] cfg_mgmt_read_data;
|
||||
wire cfg_mgmt_read_write_done;
|
||||
|
||||
wire [7:0] cfg_fc_ph;
|
||||
wire [11:0] cfg_fc_pd;
|
||||
wire [7:0] cfg_fc_nph;
|
||||
wire [11:0] cfg_fc_npd;
|
||||
wire [7:0] cfg_fc_cplh;
|
||||
wire [11:0] cfg_fc_cpld;
|
||||
wire [2:0] cfg_fc_sel;
|
||||
|
||||
// wire [3:0] cfg_interrupt_msix_enable;
|
||||
// wire [3:0] cfg_interrupt_msix_mask;
|
||||
// wire [251:0] cfg_interrupt_msix_vf_enable;
|
||||
// wire [251:0] cfg_interrupt_msix_vf_mask;
|
||||
// wire [63:0] cfg_interrupt_msix_address;
|
||||
// wire [31:0] cfg_interrupt_msix_data;
|
||||
// wire cfg_interrupt_msix_int;
|
||||
// wire [1:0] cfg_interrupt_msix_vec_pending;
|
||||
// wire cfg_interrupt_msix_vec_pending_status;
|
||||
// wire cfg_interrupt_msix_sent;
|
||||
// wire cfg_interrupt_msix_fail;
|
||||
// wire [7:0] cfg_interrupt_msi_function_number;
|
||||
|
||||
wire [3:0] cfg_interrupt_msi_enable;
|
||||
wire [11:0] cfg_interrupt_msi_mmenable;
|
||||
wire cfg_interrupt_msi_mask_update;
|
||||
wire [31:0] cfg_interrupt_msi_data;
|
||||
wire [1:0] cfg_interrupt_msi_select;
|
||||
wire [31:0] cfg_interrupt_msi_int;
|
||||
wire [31:0] cfg_interrupt_msi_pending_status;
|
||||
wire cfg_interrupt_msi_pending_status_data_enable;
|
||||
wire [1:0] cfg_interrupt_msi_pending_status_function_num;
|
||||
wire cfg_interrupt_msi_sent;
|
||||
wire cfg_interrupt_msi_fail;
|
||||
wire [2:0] cfg_interrupt_msi_attr;
|
||||
wire cfg_interrupt_msi_tph_present;
|
||||
wire [1:0] cfg_interrupt_msi_tph_type;
|
||||
wire [7:0] cfg_interrupt_msi_tph_st_tag;
|
||||
wire [7:0] cfg_interrupt_msi_function_number;
|
||||
|
||||
wire stat_err_cor;
|
||||
wire stat_err_uncor;
|
||||
|
||||
wire pcie_sys_clk;
|
||||
wire pcie_sys_clk_gt;
|
||||
|
||||
IBUFDS_GTE4 #(
|
||||
.REFCLK_HROW_CK_SEL(2'b00)
|
||||
)
|
||||
ibufds_gte4_pcie_refclk_inst (
|
||||
.I (pcie_refclk_p),
|
||||
.IB (pcie_refclk_n),
|
||||
.CEB (1'b0),
|
||||
.O (pcie_sys_clk_gt),
|
||||
.ODIV2 (pcie_sys_clk)
|
||||
);
|
||||
|
||||
pcie4_uscale_plus_0
|
||||
pcie4_uscale_plus_inst (
|
||||
.pci_exp_txn(pcie_tx_n),
|
||||
.pci_exp_txp(pcie_tx_p),
|
||||
.pci_exp_rxn(pcie_rx_n),
|
||||
.pci_exp_rxp(pcie_rx_p),
|
||||
.user_clk(pcie_user_clk),
|
||||
.user_reset(pcie_user_rst),
|
||||
.user_lnk_up(),
|
||||
|
||||
.s_axis_rq_tdata(axis_pcie_rq.tdata),
|
||||
.s_axis_rq_tkeep(axis_pcie_rq.tkeep),
|
||||
.s_axis_rq_tlast(axis_pcie_rq.tlast),
|
||||
.s_axis_rq_tready(axis_pcie_rq.tready),
|
||||
.s_axis_rq_tuser(axis_pcie_rq.tuser),
|
||||
.s_axis_rq_tvalid(axis_pcie_rq.tvalid),
|
||||
|
||||
.m_axis_rc_tdata(axis_pcie_rc.tdata),
|
||||
.m_axis_rc_tkeep(axis_pcie_rc.tkeep),
|
||||
.m_axis_rc_tlast(axis_pcie_rc.tlast),
|
||||
.m_axis_rc_tready(axis_pcie_rc.tready),
|
||||
.m_axis_rc_tuser(axis_pcie_rc.tuser),
|
||||
.m_axis_rc_tvalid(axis_pcie_rc.tvalid),
|
||||
|
||||
.m_axis_cq_tdata(axis_pcie_cq.tdata),
|
||||
.m_axis_cq_tkeep(axis_pcie_cq.tkeep),
|
||||
.m_axis_cq_tlast(axis_pcie_cq.tlast),
|
||||
.m_axis_cq_tready(axis_pcie_cq.tready),
|
||||
.m_axis_cq_tuser(axis_pcie_cq.tuser),
|
||||
.m_axis_cq_tvalid(axis_pcie_cq.tvalid),
|
||||
|
||||
.s_axis_cc_tdata(axis_pcie_cc.tdata),
|
||||
.s_axis_cc_tkeep(axis_pcie_cc.tkeep),
|
||||
.s_axis_cc_tlast(axis_pcie_cc.tlast),
|
||||
.s_axis_cc_tready(axis_pcie_cc.tready),
|
||||
.s_axis_cc_tuser(axis_pcie_cc.tuser),
|
||||
.s_axis_cc_tvalid(axis_pcie_cc.tvalid),
|
||||
|
||||
.pcie_rq_seq_num0(pcie_rq_seq_num0),
|
||||
.pcie_rq_seq_num_vld0(pcie_rq_seq_num_vld0),
|
||||
.pcie_rq_seq_num1(pcie_rq_seq_num1),
|
||||
.pcie_rq_seq_num_vld1(pcie_rq_seq_num_vld1),
|
||||
.pcie_rq_tag0(),
|
||||
.pcie_rq_tag1(),
|
||||
.pcie_rq_tag_av(),
|
||||
.pcie_rq_tag_vld0(),
|
||||
.pcie_rq_tag_vld1(),
|
||||
|
||||
.pcie_tfc_nph_av(),
|
||||
.pcie_tfc_npd_av(),
|
||||
|
||||
.pcie_cq_np_req(1'b1),
|
||||
.pcie_cq_np_req_count(),
|
||||
|
||||
.cfg_phy_link_down(),
|
||||
.cfg_phy_link_status(),
|
||||
.cfg_negotiated_width(),
|
||||
.cfg_current_speed(),
|
||||
.cfg_max_payload(cfg_max_payload),
|
||||
.cfg_max_read_req(cfg_max_read_req),
|
||||
.cfg_function_status(),
|
||||
.cfg_function_power_state(),
|
||||
.cfg_vf_status(),
|
||||
.cfg_vf_power_state(),
|
||||
.cfg_link_power_state(),
|
||||
|
||||
.cfg_mgmt_addr(cfg_mgmt_addr),
|
||||
.cfg_mgmt_function_number(cfg_mgmt_function_number),
|
||||
.cfg_mgmt_write(cfg_mgmt_write),
|
||||
.cfg_mgmt_write_data(cfg_mgmt_write_data),
|
||||
.cfg_mgmt_byte_enable(cfg_mgmt_byte_enable),
|
||||
.cfg_mgmt_read(cfg_mgmt_read),
|
||||
.cfg_mgmt_read_data(cfg_mgmt_read_data),
|
||||
.cfg_mgmt_read_write_done(cfg_mgmt_read_write_done),
|
||||
.cfg_mgmt_debug_access(1'b0),
|
||||
|
||||
.cfg_err_cor_out(),
|
||||
.cfg_err_nonfatal_out(),
|
||||
.cfg_err_fatal_out(),
|
||||
.cfg_local_error_valid(),
|
||||
.cfg_local_error_out(),
|
||||
.cfg_ltssm_state(),
|
||||
.cfg_rx_pm_state(),
|
||||
.cfg_tx_pm_state(),
|
||||
.cfg_rcb_status(cfg_rcb_status),
|
||||
.cfg_obff_enable(),
|
||||
.cfg_pl_status_change(),
|
||||
.cfg_tph_requester_enable(),
|
||||
.cfg_tph_st_mode(),
|
||||
.cfg_vf_tph_requester_enable(),
|
||||
.cfg_vf_tph_st_mode(),
|
||||
|
||||
.cfg_msg_received(),
|
||||
.cfg_msg_received_data(),
|
||||
.cfg_msg_received_type(),
|
||||
.cfg_msg_transmit(1'b0),
|
||||
.cfg_msg_transmit_type(3'd0),
|
||||
.cfg_msg_transmit_data(32'd0),
|
||||
.cfg_msg_transmit_done(),
|
||||
|
||||
.cfg_fc_ph(cfg_fc_ph),
|
||||
.cfg_fc_pd(cfg_fc_pd),
|
||||
.cfg_fc_nph(cfg_fc_nph),
|
||||
.cfg_fc_npd(cfg_fc_npd),
|
||||
.cfg_fc_cplh(cfg_fc_cplh),
|
||||
.cfg_fc_cpld(cfg_fc_cpld),
|
||||
.cfg_fc_sel(cfg_fc_sel),
|
||||
|
||||
.cfg_dsn(64'd0),
|
||||
|
||||
.cfg_bus_number(),
|
||||
|
||||
.cfg_power_state_change_ack(1'b1),
|
||||
.cfg_power_state_change_interrupt(),
|
||||
|
||||
.cfg_err_cor_in(stat_err_cor),
|
||||
.cfg_err_uncor_in(stat_err_uncor),
|
||||
.cfg_flr_in_process(),
|
||||
.cfg_flr_done(4'd0),
|
||||
.cfg_vf_flr_in_process(),
|
||||
.cfg_vf_flr_func_num(8'd0),
|
||||
.cfg_vf_flr_done(8'd0),
|
||||
|
||||
.cfg_link_training_enable(1'b1),
|
||||
|
||||
.cfg_interrupt_int(4'd0),
|
||||
.cfg_interrupt_pending(4'd0),
|
||||
.cfg_interrupt_sent(),
|
||||
// .cfg_interrupt_msix_enable(cfg_interrupt_msix_enable),
|
||||
// .cfg_interrupt_msix_mask(cfg_interrupt_msix_mask),
|
||||
// .cfg_interrupt_msix_vf_enable(cfg_interrupt_msix_vf_enable),
|
||||
// .cfg_interrupt_msix_vf_mask(cfg_interrupt_msix_vf_mask),
|
||||
// .cfg_interrupt_msix_address(cfg_interrupt_msix_address),
|
||||
// .cfg_interrupt_msix_data(cfg_interrupt_msix_data),
|
||||
// .cfg_interrupt_msix_int(cfg_interrupt_msix_int),
|
||||
// .cfg_interrupt_msix_vec_pending(cfg_interrupt_msix_vec_pending),
|
||||
// .cfg_interrupt_msix_vec_pending_status(cfg_interrupt_msix_vec_pending_status),
|
||||
// .cfg_interrupt_msi_sent(cfg_interrupt_msix_sent),
|
||||
// .cfg_interrupt_msi_fail(cfg_interrupt_msix_fail),
|
||||
// .cfg_interrupt_msi_function_number(cfg_interrupt_msi_function_number),
|
||||
.cfg_interrupt_msi_enable(cfg_interrupt_msi_enable),
|
||||
.cfg_interrupt_msi_mmenable(cfg_interrupt_msi_mmenable),
|
||||
.cfg_interrupt_msi_mask_update(cfg_interrupt_msi_mask_update),
|
||||
.cfg_interrupt_msi_data(cfg_interrupt_msi_data),
|
||||
.cfg_interrupt_msi_select(cfg_interrupt_msi_select),
|
||||
.cfg_interrupt_msi_int(cfg_interrupt_msi_int),
|
||||
.cfg_interrupt_msi_pending_status(cfg_interrupt_msi_pending_status),
|
||||
.cfg_interrupt_msi_pending_status_data_enable(cfg_interrupt_msi_pending_status_data_enable),
|
||||
.cfg_interrupt_msi_pending_status_function_num(cfg_interrupt_msi_pending_status_function_num),
|
||||
.cfg_interrupt_msi_sent(cfg_interrupt_msi_sent),
|
||||
.cfg_interrupt_msi_fail(cfg_interrupt_msi_fail),
|
||||
.cfg_interrupt_msi_attr(cfg_interrupt_msi_attr),
|
||||
.cfg_interrupt_msi_tph_present(cfg_interrupt_msi_tph_present),
|
||||
.cfg_interrupt_msi_tph_type(cfg_interrupt_msi_tph_type),
|
||||
.cfg_interrupt_msi_tph_st_tag(cfg_interrupt_msi_tph_st_tag),
|
||||
.cfg_interrupt_msi_function_number(cfg_interrupt_msi_function_number),
|
||||
|
||||
.cfg_pm_aspm_l1_entry_reject(1'b0),
|
||||
.cfg_pm_aspm_tx_l0s_entry_disable(1'b0),
|
||||
|
||||
.cfg_hot_reset_out(),
|
||||
|
||||
.cfg_config_space_enable(1'b1),
|
||||
.cfg_req_pm_transition_l23_ready(1'b0),
|
||||
.cfg_hot_reset_in(1'b0),
|
||||
|
||||
.cfg_ds_port_number(8'd0),
|
||||
.cfg_ds_bus_number(8'd0),
|
||||
.cfg_ds_device_number(5'd0),
|
||||
|
||||
.sys_clk(pcie_sys_clk),
|
||||
.sys_clk_gt(pcie_sys_clk_gt),
|
||||
.sys_reset(pcie_reset_n),
|
||||
|
||||
.phy_rdy_out()
|
||||
);
|
||||
|
||||
fpga_core #(
|
||||
.SIM(SIM),
|
||||
.VENDOR(VENDOR),
|
||||
.FAMILY(FAMILY),
|
||||
.CFG_LOW_LATENCY(CFG_LOW_LATENCY),
|
||||
.COMBINED_MAC_PCS(COMBINED_MAC_PCS),
|
||||
.MAC_DATA_W(MAC_DATA_W)
|
||||
)
|
||||
core_inst (
|
||||
/*
|
||||
* Clock: 125 MHz
|
||||
* Synchronous reset
|
||||
*/
|
||||
.clk_125mhz(clk_125mhz_int),
|
||||
.rst_125mhz(rst_125mhz_int),
|
||||
|
||||
/*
|
||||
* GPIO
|
||||
*/
|
||||
.sfp_led(sfp_led),
|
||||
.led(led),
|
||||
.led_r(led_r),
|
||||
.led_g(led_g),
|
||||
.led_hb(led_hb),
|
||||
|
||||
/*
|
||||
* Ethernet: SFP+
|
||||
*/
|
||||
.sfp_rx_p(sfp_rx_p),
|
||||
.sfp_rx_n(sfp_rx_n),
|
||||
.sfp_tx_p(sfp_tx_p),
|
||||
.sfp_tx_n(sfp_tx_n),
|
||||
.sfp_mgt_refclk_p(sfp_mgt_refclk_p),
|
||||
.sfp_mgt_refclk_n(sfp_mgt_refclk_n),
|
||||
.sfp_mgt_refclk_out(),
|
||||
.sfp_npres(sfp_npres),
|
||||
.sfp_tx_fault(sfp_tx_fault),
|
||||
.sfp_los(sfp_los),
|
||||
|
||||
/*
|
||||
* PCIe
|
||||
*/
|
||||
.pcie_clk(pcie_user_clk),
|
||||
.pcie_rst(pcie_user_rst),
|
||||
.s_axis_pcie_cq(axis_pcie_cq),
|
||||
.m_axis_pcie_cc(axis_pcie_cc),
|
||||
.m_axis_pcie_rq(axis_pcie_rq),
|
||||
.s_axis_pcie_rc(axis_pcie_rc),
|
||||
|
||||
.pcie_rq_seq_num0(pcie_rq_seq_num0),
|
||||
.pcie_rq_seq_num_vld0(pcie_rq_seq_num_vld0),
|
||||
.pcie_rq_seq_num1(pcie_rq_seq_num1),
|
||||
.pcie_rq_seq_num_vld1(pcie_rq_seq_num_vld1),
|
||||
|
||||
.cfg_max_payload(cfg_max_payload),
|
||||
.cfg_max_read_req(cfg_max_read_req),
|
||||
.cfg_rcb_status(cfg_rcb_status),
|
||||
|
||||
.cfg_mgmt_addr(cfg_mgmt_addr),
|
||||
.cfg_mgmt_function_number(cfg_mgmt_function_number),
|
||||
.cfg_mgmt_write(cfg_mgmt_write),
|
||||
.cfg_mgmt_write_data(cfg_mgmt_write_data),
|
||||
.cfg_mgmt_byte_enable(cfg_mgmt_byte_enable),
|
||||
.cfg_mgmt_read(cfg_mgmt_read),
|
||||
.cfg_mgmt_read_data(cfg_mgmt_read_data),
|
||||
.cfg_mgmt_read_write_done(cfg_mgmt_read_write_done),
|
||||
|
||||
.cfg_fc_ph(cfg_fc_ph),
|
||||
.cfg_fc_pd(cfg_fc_pd),
|
||||
.cfg_fc_nph(cfg_fc_nph),
|
||||
.cfg_fc_npd(cfg_fc_npd),
|
||||
.cfg_fc_cplh(cfg_fc_cplh),
|
||||
.cfg_fc_cpld(cfg_fc_cpld),
|
||||
.cfg_fc_sel(cfg_fc_sel),
|
||||
|
||||
// .cfg_interrupt_msix_enable(cfg_interrupt_msix_enable),
|
||||
// .cfg_interrupt_msix_mask(cfg_interrupt_msix_mask),
|
||||
// .cfg_interrupt_msix_vf_enable(cfg_interrupt_msix_vf_enable),
|
||||
// .cfg_interrupt_msix_vf_mask(cfg_interrupt_msix_vf_mask),
|
||||
// .cfg_interrupt_msix_address(cfg_interrupt_msix_address),
|
||||
// .cfg_interrupt_msix_data(cfg_interrupt_msix_data),
|
||||
// .cfg_interrupt_msix_int(cfg_interrupt_msix_int),
|
||||
// .cfg_interrupt_msix_vec_pending(cfg_interrupt_msix_vec_pending),
|
||||
// .cfg_interrupt_msix_vec_pending_status(cfg_interrupt_msix_vec_pending_status),
|
||||
// .cfg_interrupt_msix_sent(cfg_interrupt_msix_sent),
|
||||
// .cfg_interrupt_msix_fail(cfg_interrupt_msix_fail),
|
||||
// .cfg_interrupt_msi_function_number(cfg_interrupt_msi_function_number),
|
||||
|
||||
.cfg_interrupt_msi_enable(cfg_interrupt_msi_enable),
|
||||
.cfg_interrupt_msi_mmenable(cfg_interrupt_msi_mmenable),
|
||||
.cfg_interrupt_msi_mask_update(cfg_interrupt_msi_mask_update),
|
||||
.cfg_interrupt_msi_data(cfg_interrupt_msi_data),
|
||||
.cfg_interrupt_msi_select(cfg_interrupt_msi_select),
|
||||
.cfg_interrupt_msi_int(cfg_interrupt_msi_int),
|
||||
.cfg_interrupt_msi_pending_status(cfg_interrupt_msi_pending_status),
|
||||
.cfg_interrupt_msi_pending_status_data_enable(cfg_interrupt_msi_pending_status_data_enable),
|
||||
.cfg_interrupt_msi_pending_status_function_num(cfg_interrupt_msi_pending_status_function_num),
|
||||
.cfg_interrupt_msi_sent(cfg_interrupt_msi_sent),
|
||||
.cfg_interrupt_msi_fail(cfg_interrupt_msi_fail),
|
||||
.cfg_interrupt_msi_attr(cfg_interrupt_msi_attr),
|
||||
.cfg_interrupt_msi_tph_present(cfg_interrupt_msi_tph_present),
|
||||
.cfg_interrupt_msi_tph_type(cfg_interrupt_msi_tph_type),
|
||||
.cfg_interrupt_msi_tph_st_tag(cfg_interrupt_msi_tph_st_tag),
|
||||
.cfg_interrupt_msi_function_number(cfg_interrupt_msi_function_number)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
515
src/cndm/board/AS02MC04/fpga/rtl/fpga_core.sv
Normal file
515
src/cndm/board/AS02MC04/fpga/rtl/fpga_core.sv
Normal file
@@ -0,0 +1,515 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
|
||||
Copyright (c) 2014-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* FPGA core logic
|
||||
*/
|
||||
module fpga_core #
|
||||
(
|
||||
// simulation (set to avoid vendor primitives)
|
||||
parameter logic SIM = 1'b0,
|
||||
// vendor ("GENERIC", "XILINX", "ALTERA")
|
||||
parameter string VENDOR = "XILINX",
|
||||
// device family
|
||||
parameter string FAMILY = "kintexuplus",
|
||||
parameter RQ_SEQ_NUM_W = 6,
|
||||
// 10G/25G MAC configuration
|
||||
parameter logic CFG_LOW_LATENCY = 1'b1,
|
||||
parameter logic COMBINED_MAC_PCS = 1'b1,
|
||||
parameter MAC_DATA_W = 64
|
||||
)
|
||||
(
|
||||
/*
|
||||
* Clock: 125MHz
|
||||
* Synchronous reset
|
||||
*/
|
||||
input wire logic clk_125mhz,
|
||||
input wire logic rst_125mhz,
|
||||
|
||||
/*
|
||||
* GPIO
|
||||
*/
|
||||
output wire logic sfp_led[2],
|
||||
output wire logic [3:0] led,
|
||||
output wire logic led_r,
|
||||
output wire logic led_g,
|
||||
output wire logic led_hb,
|
||||
|
||||
/*
|
||||
* Ethernet: SFP+
|
||||
*/
|
||||
input wire logic sfp_rx_p[2],
|
||||
input wire logic sfp_rx_n[2],
|
||||
output wire logic sfp_tx_p[2],
|
||||
output wire logic sfp_tx_n[2],
|
||||
input wire logic sfp_mgt_refclk_p,
|
||||
input wire logic sfp_mgt_refclk_n,
|
||||
output wire logic sfp_mgt_refclk_out,
|
||||
input wire logic [1:0] sfp_npres,
|
||||
input wire logic [1:0] sfp_tx_fault,
|
||||
input wire logic [1:0] sfp_los,
|
||||
|
||||
/*
|
||||
* PCIe
|
||||
*/
|
||||
input wire logic pcie_clk,
|
||||
input wire logic pcie_rst,
|
||||
taxi_axis_if.snk s_axis_pcie_cq,
|
||||
taxi_axis_if.src m_axis_pcie_cc,
|
||||
taxi_axis_if.src m_axis_pcie_rq,
|
||||
taxi_axis_if.snk s_axis_pcie_rc,
|
||||
|
||||
input wire [RQ_SEQ_NUM_W-1:0] pcie_rq_seq_num0,
|
||||
input wire pcie_rq_seq_num_vld0,
|
||||
input wire [RQ_SEQ_NUM_W-1:0] pcie_rq_seq_num1,
|
||||
input wire pcie_rq_seq_num_vld1,
|
||||
|
||||
input wire [2:0] cfg_max_payload,
|
||||
input wire [2:0] cfg_max_read_req,
|
||||
input wire [3:0] cfg_rcb_status,
|
||||
|
||||
output wire [9:0] cfg_mgmt_addr,
|
||||
output wire [7:0] cfg_mgmt_function_number,
|
||||
output wire cfg_mgmt_write,
|
||||
output wire [31:0] cfg_mgmt_write_data,
|
||||
output wire [3:0] cfg_mgmt_byte_enable,
|
||||
output wire cfg_mgmt_read,
|
||||
output wire [31:0] cfg_mgmt_read_data,
|
||||
input wire cfg_mgmt_read_write_done,
|
||||
|
||||
input wire [7:0] cfg_fc_ph,
|
||||
input wire [11:0] cfg_fc_pd,
|
||||
input wire [7:0] cfg_fc_nph,
|
||||
input wire [11:0] cfg_fc_npd,
|
||||
input wire [7:0] cfg_fc_cplh,
|
||||
input wire [11:0] cfg_fc_cpld,
|
||||
output wire [2:0] cfg_fc_sel,
|
||||
|
||||
input wire [3:0] cfg_interrupt_msi_enable,
|
||||
input wire [11:0] cfg_interrupt_msi_mmenable,
|
||||
input wire cfg_interrupt_msi_mask_update,
|
||||
input wire [31:0] cfg_interrupt_msi_data,
|
||||
output wire [1:0] cfg_interrupt_msi_select,
|
||||
output wire [31:0] cfg_interrupt_msi_int,
|
||||
output wire [31:0] cfg_interrupt_msi_pending_status,
|
||||
output wire cfg_interrupt_msi_pending_status_data_enable,
|
||||
output wire [1:0] cfg_interrupt_msi_pending_status_function_num,
|
||||
input wire cfg_interrupt_msi_sent,
|
||||
input wire cfg_interrupt_msi_fail,
|
||||
output wire [2:0] cfg_interrupt_msi_attr,
|
||||
output wire cfg_interrupt_msi_tph_present,
|
||||
output wire [1:0] cfg_interrupt_msi_tph_type,
|
||||
output wire [7:0] cfg_interrupt_msi_tph_st_tag,
|
||||
output wire [7:0] cfg_interrupt_msi_function_number
|
||||
);
|
||||
|
||||
// SFP+
|
||||
wire sfp_tx_clk[2];
|
||||
wire sfp_tx_rst[2];
|
||||
wire sfp_rx_clk[2];
|
||||
wire sfp_rx_rst[2];
|
||||
|
||||
wire sfp_rx_status[2];
|
||||
|
||||
assign sfp_led[0] = !sfp_rx_status[0];
|
||||
assign sfp_led[1] = !sfp_rx_status[1];
|
||||
assign led = '1;
|
||||
assign led_r = 1'b1;
|
||||
assign led_g = 1'b1;
|
||||
|
||||
localparam HB_COUNT = 62500000;
|
||||
localparam CL_HB_COUNT = $clog2(HB_COUNT);
|
||||
logic [CL_HB_COUNT-1:0] hb_count_reg = '0;
|
||||
logic hb_reg = 1'b0;
|
||||
|
||||
assign led_hb = !hb_reg;
|
||||
|
||||
always_ff @(posedge clk_125mhz) begin
|
||||
if (hb_count_reg == 0) begin
|
||||
hb_count_reg <= HB_COUNT;
|
||||
hb_reg <= !hb_reg;
|
||||
end else begin
|
||||
hb_count_reg <= hb_count_reg - 1;
|
||||
end
|
||||
|
||||
if (rst_125mhz) begin
|
||||
hb_count_reg <= '0;
|
||||
hb_reg <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
wire sfp_gtpowergood;
|
||||
|
||||
wire sfp_mgt_refclk;
|
||||
wire sfp_mgt_refclk_int;
|
||||
wire sfp_mgt_refclk_bufg;
|
||||
|
||||
assign sfp_mgt_refclk_out = sfp_mgt_refclk_bufg;
|
||||
|
||||
wire sfp_rst;
|
||||
|
||||
taxi_axis_if #(.DATA_W(MAC_DATA_W), .ID_W(8), .USER_EN(1), .USER_W(1)) axis_sfp_tx[2]();
|
||||
taxi_axis_if #(.DATA_W(96), .KEEP_W(1), .ID_W(8)) axis_sfp_tx_cpl[2]();
|
||||
taxi_axis_if #(.DATA_W(MAC_DATA_W), .ID_W(8), .USER_EN(1), .USER_W(1)) axis_sfp_rx[2]();
|
||||
taxi_axis_if #(.DATA_W(16), .KEEP_W(1), .KEEP_EN(0), .LAST_EN(0), .USER_EN(1), .USER_W(1), .ID_EN(1), .ID_W(8)) axis_sfp_stat();
|
||||
|
||||
if (SIM) begin
|
||||
|
||||
assign sfp_mgt_refclk = sfp_mgt_refclk_p;
|
||||
assign sfp_mgt_refclk_int = sfp_mgt_refclk_p;
|
||||
assign sfp_mgt_refclk_bufg = sfp_mgt_refclk_int;
|
||||
|
||||
end else begin
|
||||
|
||||
IBUFDS_GTE4 ibufds_gte4_sfp_mgt_refclk_inst (
|
||||
.I (sfp_mgt_refclk_p),
|
||||
.IB (sfp_mgt_refclk_n),
|
||||
.CEB (1'b0),
|
||||
.O (sfp_mgt_refclk),
|
||||
.ODIV2 (sfp_mgt_refclk_int)
|
||||
);
|
||||
|
||||
BUFG_GT bufg_gt_sfp_mgt_refclk_inst (
|
||||
.CE (sfp_gtpowergood),
|
||||
.CEMASK (1'b1),
|
||||
.CLR (1'b0),
|
||||
.CLRMASK (1'b1),
|
||||
.DIV (3'd0),
|
||||
.I (sfp_mgt_refclk_int),
|
||||
.O (sfp_mgt_refclk_bufg)
|
||||
);
|
||||
|
||||
end
|
||||
|
||||
taxi_sync_reset #(
|
||||
.N(4)
|
||||
)
|
||||
sfp_sync_reset_inst (
|
||||
.clk(sfp_mgt_refclk_bufg),
|
||||
.rst(rst_125mhz),
|
||||
.out(sfp_rst)
|
||||
);
|
||||
|
||||
taxi_apb_if #(
|
||||
.ADDR_W(18),
|
||||
.DATA_W(16)
|
||||
)
|
||||
gt_apb_ctrl();
|
||||
|
||||
taxi_eth_mac_25g_us #(
|
||||
.SIM(SIM),
|
||||
.VENDOR(VENDOR),
|
||||
.FAMILY(FAMILY),
|
||||
|
||||
.CNT(2),
|
||||
|
||||
// GT config
|
||||
.CFG_LOW_LATENCY(CFG_LOW_LATENCY),
|
||||
|
||||
// GT type
|
||||
.GT_TYPE("GTY"),
|
||||
|
||||
// GT parameters
|
||||
.GT_TX_POLARITY('0),
|
||||
.GT_RX_POLARITY('0),
|
||||
|
||||
// MAC/PHY config
|
||||
.COMBINED_MAC_PCS(COMBINED_MAC_PCS),
|
||||
.DATA_W(MAC_DATA_W),
|
||||
.PADDING_EN(1'b1),
|
||||
.DIC_EN(1'b1),
|
||||
.MIN_FRAME_LEN(64),
|
||||
.PTP_TS_EN(1'b0),
|
||||
.PTP_TS_FMT_TOD(1'b1),
|
||||
.PTP_TS_W(96),
|
||||
.PRBS31_EN(1'b0),
|
||||
.TX_SERDES_PIPELINE(1),
|
||||
.RX_SERDES_PIPELINE(1),
|
||||
.COUNT_125US(125000/6.4),
|
||||
.STAT_EN(1'b0)
|
||||
)
|
||||
sfp_mac_inst (
|
||||
.xcvr_ctrl_clk(clk_125mhz),
|
||||
.xcvr_ctrl_rst(sfp_rst),
|
||||
|
||||
/*
|
||||
* Transceiver control
|
||||
*/
|
||||
.s_apb_ctrl(gt_apb_ctrl),
|
||||
|
||||
/*
|
||||
* Common
|
||||
*/
|
||||
.xcvr_gtpowergood_out(sfp_gtpowergood),
|
||||
.xcvr_gtrefclk00_in(sfp_mgt_refclk),
|
||||
.xcvr_qpll0pd_in(1'b0),
|
||||
.xcvr_qpll0reset_in(1'b0),
|
||||
.xcvr_qpll0pcierate_in(3'd0),
|
||||
.xcvr_qpll0lock_out(),
|
||||
.xcvr_qpll0clk_out(),
|
||||
.xcvr_qpll0refclk_out(),
|
||||
.xcvr_gtrefclk01_in(sfp_mgt_refclk),
|
||||
.xcvr_qpll1pd_in(1'b0),
|
||||
.xcvr_qpll1reset_in(1'b0),
|
||||
.xcvr_qpll1pcierate_in(3'd0),
|
||||
.xcvr_qpll1lock_out(),
|
||||
.xcvr_qpll1clk_out(),
|
||||
.xcvr_qpll1refclk_out(),
|
||||
|
||||
/*
|
||||
* Serial data
|
||||
*/
|
||||
.xcvr_txp(sfp_tx_p),
|
||||
.xcvr_txn(sfp_tx_n),
|
||||
.xcvr_rxp(sfp_rx_p),
|
||||
.xcvr_rxn(sfp_rx_n),
|
||||
|
||||
/*
|
||||
* MAC clocks
|
||||
*/
|
||||
.rx_clk(sfp_rx_clk),
|
||||
.rx_rst_in('{2{1'b0}}),
|
||||
.rx_rst_out(sfp_rx_rst),
|
||||
.tx_clk(sfp_tx_clk),
|
||||
.tx_rst_in('{2{1'b0}}),
|
||||
.tx_rst_out(sfp_tx_rst),
|
||||
.ptp_sample_clk('{2{1'b0}}),
|
||||
|
||||
/*
|
||||
* Transmit interface (AXI stream)
|
||||
*/
|
||||
.s_axis_tx(axis_sfp_tx),
|
||||
.m_axis_tx_cpl(axis_sfp_tx_cpl),
|
||||
|
||||
/*
|
||||
* Receive interface (AXI stream)
|
||||
*/
|
||||
.m_axis_rx(axis_sfp_rx),
|
||||
|
||||
/*
|
||||
* PTP clock
|
||||
*/
|
||||
.tx_ptp_ts('{2{'0}}),
|
||||
.tx_ptp_ts_step('{2{1'b0}}),
|
||||
.rx_ptp_ts('{2{'0}}),
|
||||
.rx_ptp_ts_step('{2{1'b0}}),
|
||||
|
||||
/*
|
||||
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
|
||||
*/
|
||||
.tx_lfc_req('{2{1'b0}}),
|
||||
.tx_lfc_resend('{2{1'b0}}),
|
||||
.rx_lfc_en('{2{1'b0}}),
|
||||
.rx_lfc_req(),
|
||||
.rx_lfc_ack('{2{1'b0}}),
|
||||
|
||||
/*
|
||||
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
|
||||
*/
|
||||
.tx_pfc_req('{2{'0}}),
|
||||
.tx_pfc_resend('{2{1'b0}}),
|
||||
.rx_pfc_en('{2{'0}}),
|
||||
.rx_pfc_req(),
|
||||
.rx_pfc_ack('{2{'0}}),
|
||||
|
||||
/*
|
||||
* Pause interface
|
||||
*/
|
||||
.tx_lfc_pause_en('{2{1'b0}}),
|
||||
.tx_pause_req('{2{1'b0}}),
|
||||
.tx_pause_ack(),
|
||||
|
||||
/*
|
||||
* Statistics
|
||||
*/
|
||||
.stat_clk(clk_125mhz),
|
||||
.stat_rst(rst_125mhz),
|
||||
.m_axis_stat(axis_sfp_stat),
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
.tx_start_packet(),
|
||||
.stat_tx_byte(),
|
||||
.stat_tx_pkt_len(),
|
||||
.stat_tx_pkt_ucast(),
|
||||
.stat_tx_pkt_mcast(),
|
||||
.stat_tx_pkt_bcast(),
|
||||
.stat_tx_pkt_vlan(),
|
||||
.stat_tx_pkt_good(),
|
||||
.stat_tx_pkt_bad(),
|
||||
.stat_tx_err_oversize(),
|
||||
.stat_tx_err_user(),
|
||||
.stat_tx_err_underflow(),
|
||||
.rx_start_packet(),
|
||||
.rx_error_count(),
|
||||
.rx_block_lock(),
|
||||
.rx_high_ber(),
|
||||
.rx_status(sfp_rx_status),
|
||||
.stat_rx_byte(),
|
||||
.stat_rx_pkt_len(),
|
||||
.stat_rx_pkt_fragment(),
|
||||
.stat_rx_pkt_jabber(),
|
||||
.stat_rx_pkt_ucast(),
|
||||
.stat_rx_pkt_mcast(),
|
||||
.stat_rx_pkt_bcast(),
|
||||
.stat_rx_pkt_vlan(),
|
||||
.stat_rx_pkt_good(),
|
||||
.stat_rx_pkt_bad(),
|
||||
.stat_rx_err_oversize(),
|
||||
.stat_rx_err_bad_fcs(),
|
||||
.stat_rx_err_bad_block(),
|
||||
.stat_rx_err_framing(),
|
||||
.stat_rx_err_preamble(),
|
||||
.stat_rx_fifo_drop('{2{1'b0}}),
|
||||
.stat_tx_mcf(),
|
||||
.stat_rx_mcf(),
|
||||
.stat_tx_lfc_pkt(),
|
||||
.stat_tx_lfc_xon(),
|
||||
.stat_tx_lfc_xoff(),
|
||||
.stat_tx_lfc_paused(),
|
||||
.stat_tx_pfc_pkt(),
|
||||
.stat_tx_pfc_xon(),
|
||||
.stat_tx_pfc_xoff(),
|
||||
.stat_tx_pfc_paused(),
|
||||
.stat_rx_lfc_pkt(),
|
||||
.stat_rx_lfc_xon(),
|
||||
.stat_rx_lfc_xoff(),
|
||||
.stat_rx_lfc_paused(),
|
||||
.stat_rx_pfc_pkt(),
|
||||
.stat_rx_pfc_xon(),
|
||||
.stat_rx_pfc_xoff(),
|
||||
.stat_rx_pfc_paused(),
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
*/
|
||||
.cfg_tx_max_pkt_len('{2{16'd9218}}),
|
||||
.cfg_tx_ifg('{2{8'd12}}),
|
||||
.cfg_tx_enable('{2{1'b1}}),
|
||||
.cfg_rx_max_pkt_len('{2{16'd9218}}),
|
||||
.cfg_rx_enable('{2{1'b1}}),
|
||||
.cfg_tx_prbs31_enable('{2{1'b0}}),
|
||||
.cfg_rx_prbs31_enable('{2{1'b0}}),
|
||||
.cfg_mcf_rx_eth_dst_mcast('{2{48'h01_80_C2_00_00_01}}),
|
||||
.cfg_mcf_rx_check_eth_dst_mcast('{2{1'b1}}),
|
||||
.cfg_mcf_rx_eth_dst_ucast('{2{48'd0}}),
|
||||
.cfg_mcf_rx_check_eth_dst_ucast('{2{1'b0}}),
|
||||
.cfg_mcf_rx_eth_src('{2{48'd0}}),
|
||||
.cfg_mcf_rx_check_eth_src('{2{1'b0}}),
|
||||
.cfg_mcf_rx_eth_type('{2{16'h8808}}),
|
||||
.cfg_mcf_rx_opcode_lfc('{2{16'h0001}}),
|
||||
.cfg_mcf_rx_check_opcode_lfc('{2{1'b1}}),
|
||||
.cfg_mcf_rx_opcode_pfc('{2{16'h0101}}),
|
||||
.cfg_mcf_rx_check_opcode_pfc('{2{1'b1}}),
|
||||
.cfg_mcf_rx_forward('{2{1'b0}}),
|
||||
.cfg_mcf_rx_enable('{2{1'b0}}),
|
||||
.cfg_tx_lfc_eth_dst('{2{48'h01_80_C2_00_00_01}}),
|
||||
.cfg_tx_lfc_eth_src('{2{48'h80_23_31_43_54_4C}}),
|
||||
.cfg_tx_lfc_eth_type('{2{16'h8808}}),
|
||||
.cfg_tx_lfc_opcode('{2{16'h0001}}),
|
||||
.cfg_tx_lfc_en('{2{1'b0}}),
|
||||
.cfg_tx_lfc_quanta('{2{16'hffff}}),
|
||||
.cfg_tx_lfc_refresh('{2{16'h7fff}}),
|
||||
.cfg_tx_pfc_eth_dst('{2{48'h01_80_C2_00_00_01}}),
|
||||
.cfg_tx_pfc_eth_src('{2{48'h80_23_31_43_54_4C}}),
|
||||
.cfg_tx_pfc_eth_type('{2{16'h8808}}),
|
||||
.cfg_tx_pfc_opcode('{2{16'h0101}}),
|
||||
.cfg_tx_pfc_en('{2{1'b0}}),
|
||||
.cfg_tx_pfc_quanta('{2{'{8{16'hffff}}}}),
|
||||
.cfg_tx_pfc_refresh('{2{'{8{16'h7fff}}}}),
|
||||
.cfg_rx_lfc_opcode('{2{16'h0001}}),
|
||||
.cfg_rx_lfc_en('{2{1'b0}}),
|
||||
.cfg_rx_pfc_opcode('{2{16'h0101}}),
|
||||
.cfg_rx_pfc_en('{2{1'b0}})
|
||||
);
|
||||
|
||||
cndm_micro_pcie_us #(
|
||||
.SIM(SIM),
|
||||
.VENDOR(VENDOR),
|
||||
.FAMILY(FAMILY),
|
||||
.PORTS(2),
|
||||
.RQ_SEQ_NUM_W(RQ_SEQ_NUM_W),
|
||||
.BAR0_APERTURE(24)
|
||||
)
|
||||
cndm_inst (
|
||||
/*
|
||||
* PCIe
|
||||
*/
|
||||
.pcie_clk(pcie_clk),
|
||||
.pcie_rst(pcie_rst),
|
||||
.s_axis_pcie_cq(s_axis_pcie_cq),
|
||||
.m_axis_pcie_cc(m_axis_pcie_cc),
|
||||
.m_axis_pcie_rq(m_axis_pcie_rq),
|
||||
.s_axis_pcie_rc(s_axis_pcie_rc),
|
||||
|
||||
.pcie_rq_seq_num0(pcie_rq_seq_num0),
|
||||
.pcie_rq_seq_num_vld0(pcie_rq_seq_num_vld0),
|
||||
.pcie_rq_seq_num1(pcie_rq_seq_num1),
|
||||
.pcie_rq_seq_num_vld1(pcie_rq_seq_num_vld1),
|
||||
|
||||
.cfg_max_payload(cfg_max_payload),
|
||||
.cfg_max_read_req(cfg_max_read_req),
|
||||
.cfg_rcb_status(cfg_rcb_status),
|
||||
|
||||
.cfg_mgmt_addr(cfg_mgmt_addr),
|
||||
.cfg_mgmt_function_number(cfg_mgmt_function_number),
|
||||
.cfg_mgmt_write(cfg_mgmt_write),
|
||||
.cfg_mgmt_write_data(cfg_mgmt_write_data),
|
||||
.cfg_mgmt_byte_enable(cfg_mgmt_byte_enable),
|
||||
.cfg_mgmt_read(cfg_mgmt_read),
|
||||
.cfg_mgmt_read_data(cfg_mgmt_read_data),
|
||||
.cfg_mgmt_read_write_done(cfg_mgmt_read_write_done),
|
||||
|
||||
.cfg_fc_ph(cfg_fc_ph),
|
||||
.cfg_fc_pd(cfg_fc_pd),
|
||||
.cfg_fc_nph(cfg_fc_nph),
|
||||
.cfg_fc_npd(cfg_fc_npd),
|
||||
.cfg_fc_cplh(cfg_fc_cplh),
|
||||
.cfg_fc_cpld(cfg_fc_cpld),
|
||||
.cfg_fc_sel(cfg_fc_sel),
|
||||
|
||||
.cfg_interrupt_msi_enable(cfg_interrupt_msi_enable),
|
||||
.cfg_interrupt_msi_mmenable(cfg_interrupt_msi_mmenable),
|
||||
.cfg_interrupt_msi_mask_update(cfg_interrupt_msi_mask_update),
|
||||
.cfg_interrupt_msi_data(cfg_interrupt_msi_data),
|
||||
.cfg_interrupt_msi_select(cfg_interrupt_msi_select),
|
||||
.cfg_interrupt_msi_int(cfg_interrupt_msi_int),
|
||||
.cfg_interrupt_msi_pending_status(cfg_interrupt_msi_pending_status),
|
||||
.cfg_interrupt_msi_pending_status_data_enable(cfg_interrupt_msi_pending_status_data_enable),
|
||||
.cfg_interrupt_msi_pending_status_function_num(cfg_interrupt_msi_pending_status_function_num),
|
||||
.cfg_interrupt_msi_sent(cfg_interrupt_msi_sent),
|
||||
.cfg_interrupt_msi_fail(cfg_interrupt_msi_fail),
|
||||
.cfg_interrupt_msi_attr(cfg_interrupt_msi_attr),
|
||||
.cfg_interrupt_msi_tph_present(cfg_interrupt_msi_tph_present),
|
||||
.cfg_interrupt_msi_tph_type(cfg_interrupt_msi_tph_type),
|
||||
.cfg_interrupt_msi_tph_st_tag(cfg_interrupt_msi_tph_st_tag),
|
||||
.cfg_interrupt_msi_function_number(cfg_interrupt_msi_function_number),
|
||||
|
||||
/*
|
||||
* Ethernet
|
||||
*/
|
||||
.mac_tx_clk(sfp_tx_clk),
|
||||
.mac_tx_rst(sfp_tx_rst),
|
||||
.mac_axis_tx(axis_sfp_tx),
|
||||
.mac_axis_tx_cpl(axis_sfp_tx_cpl),
|
||||
|
||||
.mac_rx_clk(sfp_rx_clk),
|
||||
.mac_rx_rst(sfp_rx_rst),
|
||||
.mac_axis_rx(axis_sfp_rx)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
60
src/cndm/board/AS02MC04/fpga/tb/fpga_core/Makefile
Normal file
60
src/cndm/board/AS02MC04/fpga/tb/fpga_core/Makefile
Normal file
@@ -0,0 +1,60 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright (c) 2020-2025 FPGA Ninja, LLC
|
||||
#
|
||||
# Authors:
|
||||
# - Alex Forencich
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= verilator
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
|
||||
RTL_DIR = ../../rtl
|
||||
LIB_DIR = ../../lib
|
||||
TAXI_SRC_DIR = $(LIB_DIR)/taxi/src
|
||||
|
||||
DUT = fpga_core
|
||||
COCOTB_TEST_MODULES = test_$(DUT)
|
||||
COCOTB_TOPLEVEL = test_$(DUT)
|
||||
MODULE = $(COCOTB_TEST_MODULES)
|
||||
TOPLEVEL = $(COCOTB_TOPLEVEL)
|
||||
VERILOG_SOURCES += $(COCOTB_TOPLEVEL).sv
|
||||
VERILOG_SOURCES += $(RTL_DIR)/$(DUT).sv
|
||||
VERILOG_SOURCES += $(TAXI_SRC_DIR)/cndm/rtl/cndm_micro_pcie_us.f
|
||||
VERILOG_SOURCES += $(TAXI_SRC_DIR)/eth/rtl/us/taxi_eth_mac_25g_us.f
|
||||
VERILOG_SOURCES += $(TAXI_SRC_DIR)/axis/rtl/taxi_axis_async_fifo.f
|
||||
VERILOG_SOURCES += $(TAXI_SRC_DIR)/sync/rtl/taxi_sync_reset.sv
|
||||
VERILOG_SOURCES += $(TAXI_SRC_DIR)/sync/rtl/taxi_sync_signal.sv
|
||||
|
||||
# handle file list files
|
||||
process_f_file = $(call process_f_files,$(addprefix $(dir $1),$(shell cat $1)))
|
||||
process_f_files = $(foreach f,$1,$(if $(filter %.f,$f),$(call process_f_file,$f),$f))
|
||||
uniq_base = $(if $1,$(call uniq_base,$(foreach f,$1,$(if $(filter-out $(notdir $(lastword $1)),$(notdir $f)),$f,))) $(lastword $1))
|
||||
VERILOG_SOURCES := $(call uniq_base,$(call process_f_files,$(VERILOG_SOURCES)))
|
||||
|
||||
# module parameters
|
||||
export PARAM_SIM := "1'b1"
|
||||
export PARAM_VENDOR := "\"XILINX\""
|
||||
export PARAM_FAMILY := "\"kintexuplus\""
|
||||
export PARAM_CFG_LOW_LATENCY := "1'b1"
|
||||
export PARAM_COMBINED_MAC_PCS := "1'b1"
|
||||
export PARAM_MAC_DATA_W := "64"
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(COCOTB_TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
VERILATOR_TRACE = 1
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
1
src/cndm/board/AS02MC04/fpga/tb/fpga_core/baser.py
Symbolic link
1
src/cndm/board/AS02MC04/fpga/tb/fpga_core/baser.py
Symbolic link
@@ -0,0 +1 @@
|
||||
../../lib/taxi/src/eth/tb/baser.py
|
||||
1
src/cndm/board/AS02MC04/fpga/tb/fpga_core/cndm.py
Symbolic link
1
src/cndm/board/AS02MC04/fpga/tb/fpga_core/cndm.py
Symbolic link
@@ -0,0 +1 @@
|
||||
../../lib/taxi/src/cndm/tb/cndm.py
|
||||
507
src/cndm/board/AS02MC04/fpga/tb/fpga_core/test_fpga_core.py
Normal file
507
src/cndm/board/AS02MC04/fpga/tb/fpga_core/test_fpga_core.py
Normal file
@@ -0,0 +1,507 @@
|
||||
#!/usr/bin/env python
|
||||
# SPDX-License-Identifier: MIT
|
||||
"""
|
||||
|
||||
Copyright (c) 2020-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
import cocotb_test.simulator
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge, FallingEdge, Timer
|
||||
|
||||
from cocotbext.axi import AxiStreamBus
|
||||
from cocotbext.eth import XgmiiFrame
|
||||
from cocotbext.pcie.core import RootComplex
|
||||
from cocotbext.pcie.xilinx.us import UltraScalePlusPcieDevice
|
||||
|
||||
try:
|
||||
from baser import BaseRSerdesSource, BaseRSerdesSink
|
||||
import cndm
|
||||
except ImportError:
|
||||
# attempt import from current directory
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__)))
|
||||
try:
|
||||
from baser import BaseRSerdesSource, BaseRSerdesSink
|
||||
import cndm
|
||||
finally:
|
||||
del sys.path[0]
|
||||
|
||||
|
||||
class TB:
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
# PCIe
|
||||
self.rc = RootComplex()
|
||||
|
||||
self.rc.max_payload_size = 0x1 # 256 bytes
|
||||
self.rc.max_read_request_size = 0x2 # 512 bytes
|
||||
|
||||
self.dev = UltraScalePlusPcieDevice(
|
||||
# configuration options
|
||||
pcie_generation=3,
|
||||
pcie_link_width=8,
|
||||
user_clk_frequency=250e6,
|
||||
alignment="dword",
|
||||
cq_straddle=False,
|
||||
cc_straddle=False,
|
||||
rq_straddle=False,
|
||||
rc_straddle=False,
|
||||
rc_4tlp_straddle=False,
|
||||
pf_count=1,
|
||||
max_payload_size=1024,
|
||||
enable_client_tag=True,
|
||||
enable_extended_tag=True,
|
||||
enable_parity=False,
|
||||
enable_rx_msg_interface=False,
|
||||
enable_sriov=False,
|
||||
enable_extended_configuration=False,
|
||||
|
||||
pf0_msi_enable=True,
|
||||
pf0_msi_count=32,
|
||||
pf1_msi_enable=False,
|
||||
pf1_msi_count=1,
|
||||
pf2_msi_enable=False,
|
||||
pf2_msi_count=1,
|
||||
pf3_msi_enable=False,
|
||||
pf3_msi_count=1,
|
||||
pf0_msix_enable=False,
|
||||
pf0_msix_table_size=31,
|
||||
pf0_msix_table_bir=4,
|
||||
pf0_msix_table_offset=0x00000000,
|
||||
pf0_msix_pba_bir=4,
|
||||
pf0_msix_pba_offset=0x00008000,
|
||||
pf1_msix_enable=False,
|
||||
pf1_msix_table_size=0,
|
||||
pf1_msix_table_bir=0,
|
||||
pf1_msix_table_offset=0x00000000,
|
||||
pf1_msix_pba_bir=0,
|
||||
pf1_msix_pba_offset=0x00000000,
|
||||
pf2_msix_enable=False,
|
||||
pf2_msix_table_size=0,
|
||||
pf2_msix_table_bir=0,
|
||||
pf2_msix_table_offset=0x00000000,
|
||||
pf2_msix_pba_bir=0,
|
||||
pf2_msix_pba_offset=0x00000000,
|
||||
pf3_msix_enable=False,
|
||||
pf3_msix_table_size=0,
|
||||
pf3_msix_table_bir=0,
|
||||
pf3_msix_table_offset=0x00000000,
|
||||
pf3_msix_pba_bir=0,
|
||||
pf3_msix_pba_offset=0x00000000,
|
||||
|
||||
# signals
|
||||
# Clock and Reset Interface
|
||||
user_clk=dut.pcie_clk,
|
||||
user_reset=dut.pcie_rst,
|
||||
# user_lnk_up
|
||||
# sys_clk
|
||||
# sys_clk_gt
|
||||
# sys_reset
|
||||
# phy_rdy_out
|
||||
|
||||
# Requester reQuest Interface
|
||||
rq_bus=AxiStreamBus.from_entity(dut.m_axis_pcie_rq),
|
||||
pcie_rq_seq_num0=dut.pcie_rq_seq_num0,
|
||||
pcie_rq_seq_num_vld0=dut.pcie_rq_seq_num_vld0,
|
||||
pcie_rq_seq_num1=dut.pcie_rq_seq_num1,
|
||||
pcie_rq_seq_num_vld1=dut.pcie_rq_seq_num_vld1,
|
||||
# pcie_rq_tag0
|
||||
# pcie_rq_tag1
|
||||
# pcie_rq_tag_av
|
||||
# pcie_rq_tag_vld0
|
||||
# pcie_rq_tag_vld1
|
||||
|
||||
# Requester Completion Interface
|
||||
rc_bus=AxiStreamBus.from_entity(dut.s_axis_pcie_rc),
|
||||
|
||||
# Completer reQuest Interface
|
||||
cq_bus=AxiStreamBus.from_entity(dut.s_axis_pcie_cq),
|
||||
# pcie_cq_np_req
|
||||
# pcie_cq_np_req_count
|
||||
|
||||
# Completer Completion Interface
|
||||
cc_bus=AxiStreamBus.from_entity(dut.m_axis_pcie_cc),
|
||||
|
||||
# Transmit Flow Control Interface
|
||||
# pcie_tfc_nph_av=dut.pcie_tfc_nph_av,
|
||||
# pcie_tfc_npd_av=dut.pcie_tfc_npd_av,
|
||||
|
||||
# Configuration Management Interface
|
||||
# cfg_mgmt_addr=dut.cfg_mgmt_addr,
|
||||
# cfg_mgmt_function_number=dut.cfg_mgmt_function_number,
|
||||
# cfg_mgmt_write=dut.cfg_mgmt_write,
|
||||
# cfg_mgmt_write_data=dut.cfg_mgmt_write_data,
|
||||
# cfg_mgmt_byte_enable=dut.cfg_mgmt_byte_enable,
|
||||
# cfg_mgmt_read=dut.cfg_mgmt_read,
|
||||
# cfg_mgmt_read_data=dut.cfg_mgmt_read_data,
|
||||
# cfg_mgmt_read_write_done=dut.cfg_mgmt_read_write_done,
|
||||
# cfg_mgmt_debug_access
|
||||
|
||||
# Configuration Status Interface
|
||||
# cfg_phy_link_down
|
||||
# cfg_phy_link_status
|
||||
# cfg_negotiated_width
|
||||
# cfg_current_speed
|
||||
# cfg_max_payload=dut.cfg_max_payload,
|
||||
# cfg_max_read_req=dut.cfg_max_read_req,
|
||||
# cfg_function_status
|
||||
# cfg_vf_status
|
||||
# cfg_function_power_state
|
||||
# cfg_vf_power_state
|
||||
# cfg_link_power_state
|
||||
# cfg_err_cor_out
|
||||
# cfg_err_nonfatal_out
|
||||
# cfg_err_fatal_out
|
||||
# cfg_local_error_out
|
||||
# cfg_local_error_valid
|
||||
# cfg_rx_pm_state
|
||||
# cfg_tx_pm_state
|
||||
# cfg_ltssm_state
|
||||
# cfg_rcb_status=dut.cfg_rcb_status,
|
||||
# cfg_obff_enable
|
||||
# cfg_pl_status_change
|
||||
# cfg_tph_requester_enable
|
||||
# cfg_tph_st_mode
|
||||
# cfg_vf_tph_requester_enable
|
||||
# cfg_vf_tph_st_mode
|
||||
|
||||
# Configuration Received Message Interface
|
||||
# cfg_msg_received
|
||||
# cfg_msg_received_data
|
||||
# cfg_msg_received_type
|
||||
|
||||
# Configuration Transmit Message Interface
|
||||
# cfg_msg_transmit
|
||||
# cfg_msg_transmit_type
|
||||
# cfg_msg_transmit_data
|
||||
# cfg_msg_transmit_done
|
||||
|
||||
# Configuration Flow Control Interface
|
||||
cfg_fc_ph=dut.cfg_fc_ph,
|
||||
cfg_fc_pd=dut.cfg_fc_pd,
|
||||
cfg_fc_nph=dut.cfg_fc_nph,
|
||||
cfg_fc_npd=dut.cfg_fc_npd,
|
||||
cfg_fc_cplh=dut.cfg_fc_cplh,
|
||||
cfg_fc_cpld=dut.cfg_fc_cpld,
|
||||
cfg_fc_sel=dut.cfg_fc_sel,
|
||||
|
||||
# Configuration Control Interface
|
||||
# cfg_hot_reset_in
|
||||
# cfg_hot_reset_out
|
||||
# cfg_config_space_enable
|
||||
# cfg_dsn
|
||||
# cfg_bus_number
|
||||
# cfg_ds_port_number
|
||||
# cfg_ds_bus_number
|
||||
# cfg_ds_device_number
|
||||
# cfg_ds_function_number
|
||||
# cfg_power_state_change_ack
|
||||
# cfg_power_state_change_interrupt
|
||||
# cfg_err_cor_in=dut.status_error_cor,
|
||||
# cfg_err_uncor_in=dut.status_error_uncor,
|
||||
# cfg_flr_in_process
|
||||
# cfg_flr_done
|
||||
# cfg_vf_flr_in_process
|
||||
# cfg_vf_flr_func_num
|
||||
# cfg_vf_flr_done
|
||||
# cfg_pm_aspm_l1_entry_reject
|
||||
# cfg_pm_aspm_tx_l0s_entry_disable
|
||||
# cfg_req_pm_transition_l23_ready
|
||||
# cfg_link_training_enable
|
||||
|
||||
# Configuration Interrupt Controller Interface
|
||||
# cfg_interrupt_int
|
||||
# cfg_interrupt_sent
|
||||
# cfg_interrupt_pending
|
||||
cfg_interrupt_msi_enable=dut.cfg_interrupt_msi_enable,
|
||||
cfg_interrupt_msi_mmenable=dut.cfg_interrupt_msi_mmenable,
|
||||
cfg_interrupt_msi_mask_update=dut.cfg_interrupt_msi_mask_update,
|
||||
cfg_interrupt_msi_data=dut.cfg_interrupt_msi_data,
|
||||
cfg_interrupt_msi_select=dut.cfg_interrupt_msi_select,
|
||||
cfg_interrupt_msi_int=dut.cfg_interrupt_msi_int,
|
||||
cfg_interrupt_msi_pending_status=dut.cfg_interrupt_msi_pending_status,
|
||||
cfg_interrupt_msi_pending_status_data_enable=dut.cfg_interrupt_msi_pending_status_data_enable,
|
||||
cfg_interrupt_msi_pending_status_function_num=dut.cfg_interrupt_msi_pending_status_function_num,
|
||||
cfg_interrupt_msi_sent=dut.cfg_interrupt_msi_sent,
|
||||
cfg_interrupt_msi_fail=dut.cfg_interrupt_msi_fail,
|
||||
# cfg_interrupt_msix_enable=dut.cfg_interrupt_msix_enable,
|
||||
# cfg_interrupt_msix_mask=dut.cfg_interrupt_msix_mask,
|
||||
# cfg_interrupt_msix_vf_enable=dut.cfg_interrupt_msix_vf_enable,
|
||||
# cfg_interrupt_msix_vf_mask=dut.cfg_interrupt_msix_vf_mask,
|
||||
# cfg_interrupt_msix_address=dut.cfg_interrupt_msix_address,
|
||||
# cfg_interrupt_msix_data=dut.cfg_interrupt_msix_data,
|
||||
# cfg_interrupt_msix_int=dut.cfg_interrupt_msix_int,
|
||||
# cfg_interrupt_msix_vec_pending=dut.cfg_interrupt_msix_vec_pending,
|
||||
# cfg_interrupt_msix_vec_pending_status=dut.cfg_interrupt_msix_vec_pending_status,
|
||||
# cfg_interrupt_msix_sent=dut.cfg_interrupt_msix_sent,
|
||||
# cfg_interrupt_msix_fail=dut.cfg_interrupt_msix_fail,
|
||||
cfg_interrupt_msi_attr=dut.cfg_interrupt_msi_attr,
|
||||
cfg_interrupt_msi_tph_present=dut.cfg_interrupt_msi_tph_present,
|
||||
cfg_interrupt_msi_tph_type=dut.cfg_interrupt_msi_tph_type,
|
||||
cfg_interrupt_msi_tph_st_tag=dut.cfg_interrupt_msi_tph_st_tag,
|
||||
cfg_interrupt_msi_function_number=dut.cfg_interrupt_msi_function_number,
|
||||
|
||||
# Configuration Extend Interface
|
||||
# cfg_ext_read_received
|
||||
# cfg_ext_write_received
|
||||
# cfg_ext_register_number
|
||||
# cfg_ext_function_number
|
||||
# cfg_ext_write_data
|
||||
# cfg_ext_write_byte_enable
|
||||
# cfg_ext_read_data
|
||||
# cfg_ext_read_data_valid
|
||||
)
|
||||
|
||||
# self.dev.log.setLevel(logging.DEBUG)
|
||||
|
||||
self.rc.make_port().connect(self.dev)
|
||||
|
||||
self.dev.functions[0].configure_bar(0, 2**int(dut.uut.cndm_inst.axil_ctrl_bar.ADDR_W))
|
||||
|
||||
# Ethernet
|
||||
cocotb.start_soon(Clock(dut.clk_125mhz, 8, units="ns").start())
|
||||
cocotb.start_soon(Clock(dut.sfp_mgt_refclk_p, 6.4, units="ns").start())
|
||||
|
||||
self.sfp_sources = []
|
||||
self.sfp_sinks = []
|
||||
|
||||
for ch in dut.uut.sfp_mac_inst.ch:
|
||||
gt_inst = ch.ch_inst.gt.gt_inst
|
||||
|
||||
if ch.ch_inst.DATA_W.value == 64:
|
||||
if ch.ch_inst.CFG_LOW_LATENCY.value:
|
||||
clk = 2.482
|
||||
gbx_cfg = (66, [64, 65])
|
||||
else:
|
||||
clk = 2.56
|
||||
gbx_cfg = None
|
||||
else:
|
||||
if ch.ch_inst.CFG_LOW_LATENCY.value:
|
||||
clk = 3.102
|
||||
gbx_cfg = (66, [64, 65])
|
||||
else:
|
||||
clk = 3.2
|
||||
gbx_cfg = None
|
||||
|
||||
cocotb.start_soon(Clock(gt_inst.tx_clk, clk, units="ns").start())
|
||||
cocotb.start_soon(Clock(gt_inst.rx_clk, clk, units="ns").start())
|
||||
|
||||
self.sfp_sources.append(BaseRSerdesSource(
|
||||
data=gt_inst.serdes_rx_data,
|
||||
data_valid=gt_inst.serdes_rx_data_valid,
|
||||
hdr=gt_inst.serdes_rx_hdr,
|
||||
hdr_valid=gt_inst.serdes_rx_hdr_valid,
|
||||
clock=gt_inst.rx_clk,
|
||||
slip=gt_inst.serdes_rx_bitslip,
|
||||
reverse=True,
|
||||
gbx_cfg=gbx_cfg
|
||||
))
|
||||
self.sfp_sinks.append(BaseRSerdesSink(
|
||||
data=gt_inst.serdes_tx_data,
|
||||
data_valid=gt_inst.serdes_tx_data_valid,
|
||||
hdr=gt_inst.serdes_tx_hdr,
|
||||
hdr_valid=gt_inst.serdes_tx_hdr_valid,
|
||||
gbx_sync=gt_inst.serdes_tx_gbx_sync,
|
||||
clock=gt_inst.tx_clk,
|
||||
reverse=True,
|
||||
gbx_cfg=gbx_cfg
|
||||
))
|
||||
|
||||
dut.sfp_npres.setimmediatevalue(0)
|
||||
dut.sfp_tx_fault.setimmediatevalue(0)
|
||||
dut.sfp_los.setimmediatevalue(0)
|
||||
|
||||
self.loopback_enable = False
|
||||
cocotb.start_soon(self._run_loopback())
|
||||
|
||||
async def init(self):
|
||||
|
||||
self.dut.rst_125mhz.setimmediatevalue(0)
|
||||
|
||||
await FallingEdge(self.dut.pcie_rst)
|
||||
await Timer(100, 'ns')
|
||||
|
||||
for k in range(10):
|
||||
await RisingEdge(self.dut.clk_125mhz)
|
||||
|
||||
self.dut.rst_125mhz.value = 1
|
||||
|
||||
for k in range(10):
|
||||
await RisingEdge(self.dut.clk_125mhz)
|
||||
|
||||
self.dut.rst_125mhz.value = 0
|
||||
|
||||
for k in range(10):
|
||||
await RisingEdge(self.dut.clk_125mhz)
|
||||
|
||||
await self.rc.enumerate()
|
||||
|
||||
async def _run_loopback(self):
|
||||
while True:
|
||||
await RisingEdge(self.dut.pcie_clk)
|
||||
|
||||
if self.loopback_enable:
|
||||
for src, snk in zip(self.sfp_sources, self.sfp_sinks):
|
||||
while not snk.empty():
|
||||
await src.send(await snk.recv())
|
||||
|
||||
@cocotb.test()
|
||||
async def run_test(dut):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.init()
|
||||
|
||||
tb.log.info("Init driver model")
|
||||
driver = cndm.Driver()
|
||||
await driver.init_pcie_dev(tb.rc.find_device(tb.dev.functions[0].pcie_id))
|
||||
|
||||
tb.log.info("Init complete")
|
||||
|
||||
tb.log.info("Wait for block lock")
|
||||
for k in range(1200):
|
||||
await RisingEdge(tb.dut.clk_125mhz)
|
||||
|
||||
for snk in tb.sfp_sinks:
|
||||
snk.clear()
|
||||
|
||||
tb.log.info("Send and receive single packet on each port")
|
||||
|
||||
for k in range(len(driver.ports)):
|
||||
data = f"Corundum rocks on port {k}!".encode('ascii')
|
||||
|
||||
await driver.ports[k].start_xmit(data)
|
||||
|
||||
pkt = await tb.sfp_sinks[k].recv()
|
||||
tb.log.info("Got TX packet: %s", pkt)
|
||||
|
||||
assert pkt.get_payload() == data.ljust(60, b'\x00')
|
||||
assert pkt.check_fcs()
|
||||
|
||||
await tb.sfp_sources[k].send(pkt)
|
||||
|
||||
pkt = await driver.ports[k].recv()
|
||||
tb.log.info("Got RX packet: %s", pkt)
|
||||
|
||||
assert bytes(pkt) == data.ljust(60, b'\x00')
|
||||
|
||||
tb.log.info("Multiple small packets")
|
||||
|
||||
count = 64
|
||||
pkts = [bytearray([(x+k) % 256 for x in range(60)]) for k in range(count)]
|
||||
|
||||
tb.loopback_enable = True
|
||||
|
||||
for p in pkts:
|
||||
await driver.ports[0].start_xmit(p)
|
||||
|
||||
for k in range(count):
|
||||
pkt = await driver.ports[0].recv()
|
||||
|
||||
tb.log.info("Got RX packet: %s", pkt)
|
||||
|
||||
assert bytes(pkt) == pkts[k].ljust(60, b'\x00')
|
||||
|
||||
tb.loopback_enable = False
|
||||
|
||||
tb.log.info("Multiple large packets")
|
||||
|
||||
count = 64
|
||||
pkts = [bytearray([(x+k) % 256 for x in range(1514)]) for k in range(count)]
|
||||
|
||||
tb.loopback_enable = True
|
||||
|
||||
for p in pkts:
|
||||
await driver.ports[0].start_xmit(p)
|
||||
|
||||
for k in range(count):
|
||||
pkt = await driver.ports[0].recv()
|
||||
|
||||
tb.log.info("Got RX packet: %s", pkt)
|
||||
|
||||
assert bytes(pkt) == pkts[k].ljust(60, b'\x00')
|
||||
|
||||
tb.loopback_enable = False
|
||||
|
||||
await RisingEdge(dut.clk_125mhz)
|
||||
await RisingEdge(dut.clk_125mhz)
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
lib_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'lib'))
|
||||
taxi_src_dir = os.path.abspath(os.path.join(lib_dir, 'taxi', 'src'))
|
||||
|
||||
|
||||
def process_f_files(files):
|
||||
lst = {}
|
||||
for f in files:
|
||||
if f[-2:].lower() == '.f':
|
||||
with open(f, 'r') as fp:
|
||||
l = fp.read().split()
|
||||
for f in process_f_files([os.path.join(os.path.dirname(f), x) for x in l]):
|
||||
lst[os.path.basename(f)] = f
|
||||
else:
|
||||
lst[os.path.basename(f)] = f
|
||||
return list(lst.values())
|
||||
|
||||
|
||||
@pytest.mark.parametrize("mac_data_w", [32, 64])
|
||||
def test_fpga_core(request, mac_data_w):
|
||||
dut = "fpga_core"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = module
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(tests_dir, f"{toplevel}.sv"),
|
||||
os.path.join(rtl_dir, f"{dut}.sv"),
|
||||
os.path.join(taxi_src_dir, "cndm", "rtl", "cndm_micro_pcie_us.f"),
|
||||
os.path.join(taxi_src_dir, "eth", "rtl", "us", "taxi_eth_mac_25g_us.f"),
|
||||
os.path.join(taxi_src_dir, "axis", "rtl", "taxi_axis_async_fifo.f"),
|
||||
os.path.join(taxi_src_dir, "sync", "rtl", "taxi_sync_reset.sv"),
|
||||
os.path.join(taxi_src_dir, "sync", "rtl", "taxi_sync_signal.sv"),
|
||||
]
|
||||
|
||||
verilog_sources = process_f_files(verilog_sources)
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['SIM'] = "1'b1"
|
||||
parameters['VENDOR'] = "\"XILINX\""
|
||||
parameters['FAMILY'] = "\"kintexuplus\""
|
||||
parameters['CFG_LOW_LATENCY'] = "1'b1"
|
||||
parameters['COMBINED_MAC_PCS'] = "1'b1"
|
||||
parameters['MAC_DATA_W'] = mac_data_w
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir, "sim_build",
|
||||
request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
simulator="verilator",
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
||||
232
src/cndm/board/AS02MC04/fpga/tb/fpga_core/test_fpga_core.sv
Normal file
232
src/cndm/board/AS02MC04/fpga/tb/fpga_core/test_fpga_core.sv
Normal file
@@ -0,0 +1,232 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* FPGA core logic testbench
|
||||
*/
|
||||
module test_fpga_core #
|
||||
(
|
||||
/* verilator lint_off WIDTHTRUNC */
|
||||
parameter logic SIM = 1'b0,
|
||||
parameter string VENDOR = "XILINX",
|
||||
parameter string FAMILY = "kintexuplus",
|
||||
parameter AXIS_PCIE_DATA_W = 256,
|
||||
parameter AXIS_PCIE_RC_USER_W = AXIS_PCIE_DATA_W < 512 ? 75 : 161,
|
||||
parameter AXIS_PCIE_RQ_USER_W = AXIS_PCIE_DATA_W < 512 ? 62 : 137,
|
||||
parameter AXIS_PCIE_CQ_USER_W = AXIS_PCIE_DATA_W < 512 ? 85 : 183,
|
||||
parameter AXIS_PCIE_CC_USER_W = AXIS_PCIE_DATA_W < 512 ? 33 : 81,
|
||||
// 10G/25G MAC configuration
|
||||
parameter logic CFG_LOW_LATENCY = 1'b1,
|
||||
parameter logic COMBINED_MAC_PCS = 1'b1,
|
||||
parameter MAC_DATA_W = 64
|
||||
/* verilator lint_on WIDTHTRUNC */
|
||||
)
|
||||
();
|
||||
|
||||
localparam AXIS_PCIE_KEEP_W = (AXIS_PCIE_DATA_W/32);
|
||||
localparam RQ_SEQ_NUM_W = AXIS_PCIE_RQ_USER_W == 60 ? 4 : 6;
|
||||
|
||||
logic clk_125mhz;
|
||||
logic rst_125mhz;
|
||||
|
||||
logic sfp_led[2];
|
||||
logic [3:0] led;
|
||||
logic led_r;
|
||||
logic led_g;
|
||||
logic led_hb;
|
||||
|
||||
logic sfp_mgt_refclk_p;
|
||||
logic sfp_mgt_refclk_n;
|
||||
logic sfp_mgt_refclk_out;
|
||||
|
||||
logic [1:0] sfp_npres;
|
||||
logic [1:0] sfp_tx_fault;
|
||||
logic [1:0] sfp_los;
|
||||
|
||||
logic pcie_clk;
|
||||
logic pcie_rst;
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(AXIS_PCIE_DATA_W),
|
||||
.KEEP_EN(1),
|
||||
.KEEP_W(AXIS_PCIE_KEEP_W),
|
||||
.USER_EN(1),
|
||||
.USER_W(AXIS_PCIE_CQ_USER_W)
|
||||
) s_axis_pcie_cq();
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(AXIS_PCIE_DATA_W),
|
||||
.KEEP_EN(1),
|
||||
.KEEP_W(AXIS_PCIE_KEEP_W),
|
||||
.USER_EN(1),
|
||||
.USER_W(AXIS_PCIE_CC_USER_W)
|
||||
) m_axis_pcie_cc();
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(AXIS_PCIE_DATA_W),
|
||||
.KEEP_EN(1),
|
||||
.KEEP_W(AXIS_PCIE_KEEP_W),
|
||||
.USER_EN(1),
|
||||
.USER_W(AXIS_PCIE_RQ_USER_W)
|
||||
) m_axis_pcie_rq();
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(AXIS_PCIE_DATA_W),
|
||||
.KEEP_EN(1),
|
||||
.KEEP_W(AXIS_PCIE_KEEP_W),
|
||||
.USER_EN(1),
|
||||
.USER_W(AXIS_PCIE_RC_USER_W)
|
||||
) s_axis_pcie_rc();
|
||||
|
||||
logic [RQ_SEQ_NUM_W-1:0] pcie_rq_seq_num0;
|
||||
logic pcie_rq_seq_num_vld0;
|
||||
logic [RQ_SEQ_NUM_W-1:0] pcie_rq_seq_num1;
|
||||
logic pcie_rq_seq_num_vld1;
|
||||
|
||||
logic [2:0] cfg_max_payload;
|
||||
logic [2:0] cfg_max_read_req;
|
||||
logic [3:0] cfg_rcb_status;
|
||||
|
||||
logic [9:0] cfg_mgmt_addr;
|
||||
logic [7:0] cfg_mgmt_function_number;
|
||||
logic cfg_mgmt_write;
|
||||
logic [31:0] cfg_mgmt_write_data;
|
||||
logic [3:0] cfg_mgmt_byte_enable;
|
||||
logic cfg_mgmt_read;
|
||||
logic [31:0] cfg_mgmt_read_data;
|
||||
logic cfg_mgmt_read_write_done;
|
||||
|
||||
logic [7:0] cfg_fc_ph;
|
||||
logic [11:0] cfg_fc_pd;
|
||||
logic [7:0] cfg_fc_nph;
|
||||
logic [11:0] cfg_fc_npd;
|
||||
logic [7:0] cfg_fc_cplh;
|
||||
logic [11:0] cfg_fc_cpld;
|
||||
logic [2:0] cfg_fc_sel;
|
||||
|
||||
logic [3:0] cfg_interrupt_msi_enable;
|
||||
logic [11:0] cfg_interrupt_msi_mmenable;
|
||||
logic cfg_interrupt_msi_mask_update;
|
||||
logic [31:0] cfg_interrupt_msi_data;
|
||||
logic [1:0] cfg_interrupt_msi_select;
|
||||
logic [31:0] cfg_interrupt_msi_int;
|
||||
logic [31:0] cfg_interrupt_msi_pending_status;
|
||||
logic cfg_interrupt_msi_pending_status_data_enable;
|
||||
logic [1:0] cfg_interrupt_msi_pending_status_function_num;
|
||||
logic cfg_interrupt_msi_sent;
|
||||
logic cfg_interrupt_msi_fail;
|
||||
logic [2:0] cfg_interrupt_msi_attr;
|
||||
logic cfg_interrupt_msi_tph_present;
|
||||
logic [1:0] cfg_interrupt_msi_tph_type;
|
||||
logic [7:0] cfg_interrupt_msi_tph_st_tag;
|
||||
logic [7:0] cfg_interrupt_msi_function_number;
|
||||
|
||||
fpga_core #(
|
||||
.SIM(SIM),
|
||||
.VENDOR(VENDOR),
|
||||
.FAMILY(FAMILY),
|
||||
.RQ_SEQ_NUM_W(RQ_SEQ_NUM_W),
|
||||
// 10G/25G MAC configuration
|
||||
.CFG_LOW_LATENCY(CFG_LOW_LATENCY),
|
||||
.COMBINED_MAC_PCS(COMBINED_MAC_PCS),
|
||||
.MAC_DATA_W(MAC_DATA_W)
|
||||
)
|
||||
uut (
|
||||
/*
|
||||
* Clock: 125MHz
|
||||
* Synchronous reset
|
||||
*/
|
||||
.clk_125mhz(clk_125mhz),
|
||||
.rst_125mhz(rst_125mhz),
|
||||
|
||||
/*
|
||||
* GPIO
|
||||
*/
|
||||
.sfp_led(sfp_led),
|
||||
.led(led),
|
||||
.led_r(led_r),
|
||||
.led_g(led_g),
|
||||
.led_hb(led_hb),
|
||||
|
||||
/*
|
||||
* PCIe
|
||||
*/
|
||||
.pcie_clk(pcie_clk),
|
||||
.pcie_rst(pcie_rst),
|
||||
.s_axis_pcie_cq(s_axis_pcie_cq),
|
||||
.m_axis_pcie_cc(m_axis_pcie_cc),
|
||||
.m_axis_pcie_rq(m_axis_pcie_rq),
|
||||
.s_axis_pcie_rc(s_axis_pcie_rc),
|
||||
|
||||
.pcie_rq_seq_num0(pcie_rq_seq_num0),
|
||||
.pcie_rq_seq_num_vld0(pcie_rq_seq_num_vld0),
|
||||
.pcie_rq_seq_num1(pcie_rq_seq_num1),
|
||||
.pcie_rq_seq_num_vld1(pcie_rq_seq_num_vld1),
|
||||
|
||||
.cfg_max_payload(cfg_max_payload),
|
||||
.cfg_max_read_req(cfg_max_read_req),
|
||||
.cfg_rcb_status(cfg_rcb_status),
|
||||
|
||||
.cfg_mgmt_addr(cfg_mgmt_addr),
|
||||
.cfg_mgmt_function_number(cfg_mgmt_function_number),
|
||||
.cfg_mgmt_write(cfg_mgmt_write),
|
||||
.cfg_mgmt_write_data(cfg_mgmt_write_data),
|
||||
.cfg_mgmt_byte_enable(cfg_mgmt_byte_enable),
|
||||
.cfg_mgmt_read(cfg_mgmt_read),
|
||||
.cfg_mgmt_read_data(cfg_mgmt_read_data),
|
||||
.cfg_mgmt_read_write_done(cfg_mgmt_read_write_done),
|
||||
|
||||
.cfg_fc_ph(cfg_fc_ph),
|
||||
.cfg_fc_pd(cfg_fc_pd),
|
||||
.cfg_fc_nph(cfg_fc_nph),
|
||||
.cfg_fc_npd(cfg_fc_npd),
|
||||
.cfg_fc_cplh(cfg_fc_cplh),
|
||||
.cfg_fc_cpld(cfg_fc_cpld),
|
||||
.cfg_fc_sel(cfg_fc_sel),
|
||||
|
||||
.cfg_interrupt_msi_enable(cfg_interrupt_msi_enable),
|
||||
.cfg_interrupt_msi_mmenable(cfg_interrupt_msi_mmenable),
|
||||
.cfg_interrupt_msi_mask_update(cfg_interrupt_msi_mask_update),
|
||||
.cfg_interrupt_msi_data(cfg_interrupt_msi_data),
|
||||
.cfg_interrupt_msi_select(cfg_interrupt_msi_select),
|
||||
.cfg_interrupt_msi_int(cfg_interrupt_msi_int),
|
||||
.cfg_interrupt_msi_pending_status(cfg_interrupt_msi_pending_status),
|
||||
.cfg_interrupt_msi_pending_status_data_enable(cfg_interrupt_msi_pending_status_data_enable),
|
||||
.cfg_interrupt_msi_pending_status_function_num(cfg_interrupt_msi_pending_status_function_num),
|
||||
.cfg_interrupt_msi_sent(cfg_interrupt_msi_sent),
|
||||
.cfg_interrupt_msi_fail(cfg_interrupt_msi_fail),
|
||||
.cfg_interrupt_msi_attr(cfg_interrupt_msi_attr),
|
||||
.cfg_interrupt_msi_tph_present(cfg_interrupt_msi_tph_present),
|
||||
.cfg_interrupt_msi_tph_type(cfg_interrupt_msi_tph_type),
|
||||
.cfg_interrupt_msi_tph_st_tag(cfg_interrupt_msi_tph_st_tag),
|
||||
.cfg_interrupt_msi_function_number(cfg_interrupt_msi_function_number),
|
||||
|
||||
/*
|
||||
* Ethernet: SFP+
|
||||
*/
|
||||
.sfp_rx_p(),
|
||||
.sfp_rx_n(),
|
||||
.sfp_tx_p('{2{1'b0}}),
|
||||
.sfp_tx_n('{2{1'b0}}),
|
||||
.sfp_mgt_refclk_p(sfp_mgt_refclk_p),
|
||||
.sfp_mgt_refclk_n(sfp_mgt_refclk_n),
|
||||
.sfp_mgt_refclk_out(sfp_mgt_refclk_out),
|
||||
.sfp_npres(sfp_npres),
|
||||
.sfp_tx_fault(sfp_tx_fault),
|
||||
.sfp_los(sfp_los)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
1
src/cndm/lib/taxi
Symbolic link
1
src/cndm/lib/taxi
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../
|
||||
35
src/cndm/modules/cndm/Makefile
Normal file
35
src/cndm/modules/cndm/Makefile
Normal file
@@ -0,0 +1,35 @@
|
||||
# SPDX-License-Identifier: GPL
|
||||
# Copyright (c) 2025 FPGA Ninja
|
||||
|
||||
ifneq ($(KERNELRELEASE),)
|
||||
|
||||
obj-m += cndm.o
|
||||
cndm-y += cndm_main.o
|
||||
cndm-y += cndm_devlink.o
|
||||
cndm-y += cndm_irq.o
|
||||
cndm-y += cndm_dev.o
|
||||
cndm-y += cndm_netdev.o
|
||||
cndm-y += cndm_ethtool.o
|
||||
cndm-y += cndm_tx.o
|
||||
cndm-y += cndm_rx.o
|
||||
|
||||
ifneq ($(DEBUG),)
|
||||
ccflags-y += -DDEBUG
|
||||
endif
|
||||
|
||||
else
|
||||
|
||||
ifneq ($(KERNEL_SRC),)
|
||||
KDIR ?= $(KERNEL_SRC)
|
||||
endif
|
||||
|
||||
KDIR ?= /lib/modules/$(shell uname -r)/build
|
||||
|
||||
all: modules
|
||||
|
||||
help modules modules_install clean:
|
||||
$(MAKE) -C $(KDIR) M=$(shell pwd) $@
|
||||
|
||||
install: modules_install
|
||||
|
||||
endif
|
||||
175
src/cndm/modules/cndm/cndm.h
Normal file
175
src/cndm/modules/cndm/cndm.h
Normal file
@@ -0,0 +1,175 @@
|
||||
/* SPDX-License-Identifier: GPL */
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
#ifndef CNDM_H
|
||||
#define CNDM_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ptp_clock_kernel.h>
|
||||
#include <net/devlink.h>
|
||||
|
||||
#define DRIVER_VERSION "0.1"
|
||||
|
||||
#define CNDM_MAX_IRQ 256
|
||||
|
||||
struct cndm_irq {
|
||||
int index;
|
||||
int irqn;
|
||||
char name[16+3];
|
||||
struct atomic_notifier_head nh;
|
||||
};
|
||||
|
||||
struct cndm_dev {
|
||||
struct pci_dev *pdev;
|
||||
struct device *dev;
|
||||
|
||||
unsigned int id;
|
||||
char name[16];
|
||||
|
||||
struct miscdevice misc_dev;
|
||||
|
||||
int irq_count;
|
||||
struct cndm_irq *irq;
|
||||
|
||||
struct net_device *ndev[32];
|
||||
|
||||
resource_size_t hw_regs_size;
|
||||
phys_addr_t hw_regs_phys;
|
||||
void __iomem *hw_addr;
|
||||
|
||||
u32 port_count;
|
||||
u32 port_offset;
|
||||
u32 port_stride;
|
||||
|
||||
void __iomem *ptp_regs;
|
||||
struct ptp_clock *ptp_clock;
|
||||
struct ptp_clock_info ptp_clock_info;
|
||||
};
|
||||
|
||||
struct cndm_tx_info {
|
||||
struct sk_buff *skb;
|
||||
dma_addr_t dma_addr;
|
||||
u32 len;
|
||||
};
|
||||
|
||||
struct cndm_rx_info {
|
||||
struct page *page;
|
||||
dma_addr_t dma_addr;
|
||||
u32 len;
|
||||
};
|
||||
|
||||
struct cndm_priv {
|
||||
struct device *dev;
|
||||
struct net_device *ndev;
|
||||
struct cndm_dev *cdev;
|
||||
|
||||
bool registered;
|
||||
bool port_up;
|
||||
|
||||
void __iomem *hw_addr;
|
||||
|
||||
size_t txq_region_len;
|
||||
void *txq_region;
|
||||
dma_addr_t txq_region_addr;
|
||||
|
||||
struct cndm_irq *irq;
|
||||
struct notifier_block irq_nb;
|
||||
|
||||
struct cndm_tx_info *tx_info;
|
||||
struct cndm_rx_info *rx_info;
|
||||
|
||||
struct netdev_queue *tx_queue;
|
||||
|
||||
struct napi_struct tx_napi;
|
||||
struct napi_struct rx_napi;
|
||||
|
||||
u32 txq_log_size;
|
||||
u32 txq_size;
|
||||
u32 txq_mask;
|
||||
u32 txq_prod;
|
||||
u32 txq_cons;
|
||||
|
||||
size_t rxq_region_len;
|
||||
void *rxq_region;
|
||||
dma_addr_t rxq_region_addr;
|
||||
|
||||
u32 rxq_log_size;
|
||||
u32 rxq_size;
|
||||
u32 rxq_mask;
|
||||
u32 rxq_prod;
|
||||
u32 rxq_cons;
|
||||
|
||||
size_t txcq_region_len;
|
||||
void *txcq_region;
|
||||
dma_addr_t txcq_region_addr;
|
||||
|
||||
u32 txcq_log_size;
|
||||
u32 txcq_size;
|
||||
u32 txcq_mask;
|
||||
u32 txcq_prod;
|
||||
u32 txcq_cons;
|
||||
|
||||
size_t rxcq_region_len;
|
||||
void *rxcq_region;
|
||||
dma_addr_t rxcq_region_addr;
|
||||
|
||||
u32 rxcq_log_size;
|
||||
u32 rxcq_size;
|
||||
u32 rxcq_mask;
|
||||
u32 rxcq_prod;
|
||||
u32 rxcq_cons;
|
||||
};
|
||||
|
||||
struct cndm_desc {
|
||||
__u8 rsvd[4];
|
||||
__le32 len;
|
||||
__le64 addr;
|
||||
};
|
||||
|
||||
struct cndm_cpl {
|
||||
__u8 rsvd[4];
|
||||
__le32 len;
|
||||
__u8 rsvd2[7];
|
||||
__u8 phase;
|
||||
};
|
||||
|
||||
// cndm_devlink.c
|
||||
struct devlink *cndm_devlink_alloc(struct device *dev);
|
||||
void cndm_devlink_free(struct devlink *devlink);
|
||||
|
||||
// cndm_irq.c
|
||||
int cndm_irq_init_pcie(struct cndm_dev *cdev);
|
||||
void cndm_irq_deinit_pcie(struct cndm_dev *cdev);
|
||||
|
||||
// cndm_netdev.c
|
||||
struct net_device *cndm_create_netdev(struct cndm_dev *cdev, int port, void __iomem *hw_addr);
|
||||
void cndm_destroy_netdev(struct net_device *ndev);
|
||||
|
||||
// cndm_dev.c
|
||||
extern const struct file_operations cndm_fops;
|
||||
|
||||
// cndm_ethtool.c
|
||||
extern const struct ethtool_ops cndm_ethtool_ops;
|
||||
|
||||
// cndm_tx.c
|
||||
int cndm_free_tx_buf(struct cndm_priv *priv);
|
||||
int cndm_poll_tx_cq(struct napi_struct *napi, int budget);
|
||||
int cndm_start_xmit(struct sk_buff *skb, struct net_device *ndev);
|
||||
|
||||
// cndm_rx.c
|
||||
int cndm_free_rx_buf(struct cndm_priv *priv);
|
||||
int cndm_refill_rx_buffers(struct cndm_priv *priv);
|
||||
int cndm_poll_rx_cq(struct napi_struct *napi, int budget);
|
||||
|
||||
#endif
|
||||
28
src/cndm/modules/cndm/cndm_ddcmd.sh
Executable file
28
src/cndm/modules/cndm/cndm_ddcmd.sh
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
|
||||
module=cndm
|
||||
|
||||
control=/proc/dynamic_debug/control
|
||||
|
||||
if ! test -f $control; then
|
||||
control=/sys/kernel/debug/dynamic_debug/control
|
||||
fi
|
||||
|
||||
if ! test -f $control; then
|
||||
>&2 echo "Error: dynamic debug control file not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
>&2 echo "Error: no argument provided"
|
||||
>&2 echo "usage: $0 [stmt]"
|
||||
>&2 echo "Disable all debug print statements: $0 =_"
|
||||
>&2 echo "Enable all debug print statements: $0 =p"
|
||||
>&2 echo "More verbose: $0 =pflmt"
|
||||
>&2 echo "Pattern match: $0 format \"some-string\" =p"
|
||||
>&2 echo "Current configuration:"
|
||||
grep "\[$module\]" $control >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo module $module "${@@Q}" > $control
|
||||
136
src/cndm/modules/cndm/cndm_dev.c
Normal file
136
src/cndm/modules/cndm/cndm_dev.c
Normal file
@@ -0,0 +1,136 @@
|
||||
// SPDX-License-Identifier: GPL
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
#include "cndm.h"
|
||||
#include "cndm_ioctl.h"
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
static int cndm_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
// struct miscdevice *miscdev = file->private_data;
|
||||
// struct cndm_dev *cdev = container_of(miscdev, struct cndm_dev, misc_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cndm_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
// struct miscdevice *miscdev = file->private_data;
|
||||
// struct cndm_dev *cdev = container_of(miscdev, struct cndm_dev, misc_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cndm_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct miscdevice *miscdev = file->private_data;
|
||||
struct cndm_dev *cdev = container_of(miscdev, struct cndm_dev, misc_dev);
|
||||
int index;
|
||||
u64 pgoff, req_len, req_start;
|
||||
|
||||
index = vma->vm_pgoff >> (40 - PAGE_SHIFT);
|
||||
req_len = vma->vm_end - vma->vm_start;
|
||||
pgoff = vma->vm_pgoff & ((1U << (40 - PAGE_SHIFT)) - 1);
|
||||
req_start = pgoff << PAGE_SHIFT;
|
||||
|
||||
if (vma->vm_end < vma->vm_start)
|
||||
return -EINVAL;
|
||||
|
||||
if ((vma->vm_flags & VM_SHARED) == 0)
|
||||
return -EINVAL;
|
||||
|
||||
switch (index) {
|
||||
case 0:
|
||||
if (req_start + req_len > cdev->hw_regs_size)
|
||||
return -EINVAL;
|
||||
|
||||
return io_remap_pfn_range(vma, vma->vm_start,
|
||||
(cdev->hw_regs_phys >> PAGE_SHIFT) + pgoff,
|
||||
req_len, pgprot_noncached(vma->vm_page_prot));
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static long cndm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct miscdevice *miscdev = file->private_data;
|
||||
struct cndm_dev *cdev = container_of(miscdev, struct cndm_dev, misc_dev);
|
||||
size_t minsz;
|
||||
|
||||
if (cmd == CNDM_IOCTL_GET_API_VERSION) {
|
||||
// Get API version
|
||||
return CNDM_IOCTL_API_VERSION;
|
||||
} else if (cmd == CNDM_IOCTL_GET_DEVICE_INFO) {
|
||||
// Get device information
|
||||
struct cndm_ioctl_device_info info;
|
||||
|
||||
minsz = offsetofend(struct cndm_ioctl_device_info, num_irqs);
|
||||
|
||||
if (copy_from_user(&info, (void __user *)arg, minsz))
|
||||
return -EFAULT;
|
||||
|
||||
if (info.argsz < minsz)
|
||||
return -EINVAL;
|
||||
|
||||
info.flags = 0;
|
||||
info.num_regions = 1;
|
||||
info.num_irqs = 0;
|
||||
|
||||
return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0;
|
||||
} else if (cmd == CNDM_IOCTL_GET_REGION_INFO) {
|
||||
// Get region information
|
||||
struct cndm_ioctl_region_info info;
|
||||
|
||||
minsz = offsetofend(struct cndm_ioctl_region_info, name);
|
||||
|
||||
if (copy_from_user(&info, (void __user *)arg, minsz))
|
||||
return -EFAULT;
|
||||
|
||||
if (info.argsz < minsz)
|
||||
return -EINVAL;
|
||||
|
||||
info.flags = 0;
|
||||
info.type = CNDM_REGION_TYPE_UNIMPLEMENTED;
|
||||
info.next = 0;
|
||||
info.child = 0;
|
||||
info.size = 0;
|
||||
info.offset = ((u64)info.index) << 40;
|
||||
info.name[0] = 0;
|
||||
|
||||
switch (info.index) {
|
||||
case 0:
|
||||
info.type = CNDM_REGION_TYPE_NIC_CTRL;
|
||||
info.next = 0;
|
||||
info.child = 0;
|
||||
info.size = cdev->hw_regs_size;
|
||||
info.offset = ((u64)info.index) << 40;
|
||||
strscpy(info.name, "ctrl", sizeof(info.name));
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
const struct file_operations cndm_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = cndm_open,
|
||||
.release = cndm_release,
|
||||
.mmap = cndm_mmap,
|
||||
.unlocked_ioctl = cndm_ioctl,
|
||||
};
|
||||
76
src/cndm/modules/cndm/cndm_devlink.c
Normal file
76
src/cndm/modules/cndm/cndm_devlink.c
Normal file
@@ -0,0 +1,76 @@
|
||||
// SPDX-License-Identifier: GPL
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
#include "cndm.h"
|
||||
|
||||
#include <linux/version.h>
|
||||
|
||||
static int cndm_devlink_info_get(struct devlink *devlink,
|
||||
struct devlink_info_req *req, struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct cndm_dev *cdev = devlink_priv(devlink);
|
||||
char str[32];
|
||||
int ret = 0;
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0)
|
||||
ret = devlink_info_driver_name_put(req, KBUILD_MODNAME);
|
||||
if (ret)
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
snprintf(str, sizeof(str), "%08x", 0); // TODO
|
||||
|
||||
ret = devlink_info_version_fixed_put(req, "fpga.id", str);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
snprintf(str, sizeof(str), "%08x", 0); // TODO
|
||||
|
||||
ret = devlink_info_version_fixed_put(req, DEVLINK_INFO_VERSION_GENERIC_BOARD_ID, str);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
snprintf(str, sizeof(str), "%08x", 0); // TODO
|
||||
|
||||
ret = devlink_info_version_fixed_put(req, DEVLINK_INFO_VERSION_GENERIC_BOARD_REV, str);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
snprintf(str, sizeof(str), "%08x", 0); // TODO
|
||||
|
||||
ret = devlink_info_version_running_put(req, "fw.id", str);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
snprintf(str, sizeof(str), "%08x", 0); // TODO
|
||||
|
||||
ret = devlink_info_version_running_put(req, DEVLINK_INFO_VERSION_GENERIC_FW, str);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct devlink_ops cndm_devlink_ops = {
|
||||
.info_get = cndm_devlink_info_get,
|
||||
};
|
||||
|
||||
struct devlink *cndm_devlink_alloc(struct device *dev)
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
|
||||
return devlink_alloc(&cndm_devlink_ops, sizeof(struct cndm_dev), dev);
|
||||
#else
|
||||
return devlink_alloc(&cndm_devlink_ops, sizeof(struct cndm_dev));
|
||||
#endif
|
||||
}
|
||||
|
||||
void cndm_devlink_free(struct devlink *devlink) {
|
||||
devlink_free(devlink);
|
||||
}
|
||||
29
src/cndm/modules/cndm/cndm_ethtool.c
Normal file
29
src/cndm/modules/cndm/cndm_ethtool.c
Normal file
@@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: GPL
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
#include "cndm.h"
|
||||
|
||||
#include <linux/ethtool.h>
|
||||
|
||||
static void cndm_get_drvinfo(struct net_device *ndev,
|
||||
struct ethtool_drvinfo *drvinfo)
|
||||
{
|
||||
struct cndm_priv *priv = netdev_priv(ndev);
|
||||
struct cndm_dev *cdev = priv->cdev;
|
||||
|
||||
strscpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver));
|
||||
strscpy(drvinfo->version, DRIVER_VERSION, sizeof(drvinfo->version));
|
||||
snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "TODO"); // TODO
|
||||
strscpy(drvinfo->bus_info, dev_name(cdev->dev), sizeof(drvinfo->bus_info));
|
||||
}
|
||||
|
||||
const struct ethtool_ops cndm_ethtool_ops = {
|
||||
.get_drvinfo = cndm_get_drvinfo,
|
||||
};
|
||||
63
src/cndm/modules/cndm/cndm_ioctl.h
Normal file
63
src/cndm/modules/cndm/cndm_ioctl.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/* SPDX-License-Identifier: GPL */
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
#ifndef CNDM_IOCTL_H
|
||||
#define CNDM_IOCTL_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define CNDM_IOCTL_API_VERSION 0
|
||||
|
||||
#define CNDM_IOCTL_TYPE 0x63
|
||||
#define CNDM_IOCTL_BASE 0xC0
|
||||
|
||||
enum {
|
||||
CNDM_REGION_TYPE_UNIMPLEMENTED = 0x00000000,
|
||||
CNDM_REGION_TYPE_CTRL = 0x00001000,
|
||||
CNDM_REGION_TYPE_NIC_CTRL = 0x00001001,
|
||||
CNDM_REGION_TYPE_APP_CTRL = 0x00001002,
|
||||
};
|
||||
|
||||
// get API version
|
||||
#define CNDM_IOCTL_GET_API_VERSION _IO(CNDM_IOCTL_TYPE, CNDM_IOCTL_BASE + 0)
|
||||
|
||||
// get device information
|
||||
struct cndm_ioctl_device_info {
|
||||
__u32 argsz;
|
||||
__u32 flags;
|
||||
__u32 fw_id;
|
||||
__u32 fw_ver;
|
||||
__u32 board_id;
|
||||
__u32 board_ver;
|
||||
__u32 build_date;
|
||||
__u32 git_hash;
|
||||
__u32 rel_info;
|
||||
__u32 num_regions;
|
||||
__u32 num_irqs;
|
||||
};
|
||||
|
||||
#define CNDM_IOCTL_GET_DEVICE_INFO _IO(CNDM_IOCTL_TYPE, CNDM_IOCTL_BASE + 1)
|
||||
|
||||
// get region information
|
||||
struct cndm_ioctl_region_info {
|
||||
__u32 argsz;
|
||||
__u32 flags;
|
||||
__u32 index;
|
||||
__u32 type;
|
||||
__u32 next;
|
||||
__u32 child;
|
||||
__u32 size;
|
||||
__u64 offset;
|
||||
__u8 name[32];
|
||||
};
|
||||
|
||||
#define CNDM_IOCTL_GET_REGION_INFO _IO(CNDM_IOCTL_TYPE, CNDM_IOCTL_BASE + 2)
|
||||
|
||||
#endif
|
||||
86
src/cndm/modules/cndm/cndm_irq.c
Normal file
86
src/cndm/modules/cndm/cndm_irq.c
Normal file
@@ -0,0 +1,86 @@
|
||||
// SPDX-License-Identifier: GPL
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
#include "cndm.h"
|
||||
|
||||
static irqreturn_t cndm_irq_handler(int irqn, void *data)
|
||||
{
|
||||
struct cndm_irq *irq = data;
|
||||
|
||||
atomic_notifier_call_chain(&irq->nh, 0, NULL);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int cndm_irq_init_pcie(struct cndm_dev *cdev)
|
||||
{
|
||||
struct pci_dev *pdev = cdev->pdev;
|
||||
struct device *dev = cdev->dev;
|
||||
int ret = 0;
|
||||
int irq_count;
|
||||
int k;
|
||||
|
||||
cdev->irq_count = 0;
|
||||
|
||||
irq_count = pci_alloc_irq_vectors(pdev, 1, CNDM_MAX_IRQ, PCI_IRQ_MSI | PCI_IRQ_MSIX);
|
||||
if (irq_count < 0) {
|
||||
dev_err(dev, "Failed to allocate IRQs");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
cdev->irq = kvzalloc(sizeof(*cdev->irq) * irq_count, GFP_KERNEL);
|
||||
if (!cdev->irq) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(dev, "Failed to allocate memory");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (k = 0; k < irq_count; k++) {
|
||||
struct cndm_irq *irq = &cdev->irq[k];
|
||||
|
||||
ATOMIC_INIT_NOTIFIER_HEAD(&irq->nh);
|
||||
|
||||
ret = pci_request_irq(pdev, k, cndm_irq_handler, NULL,
|
||||
irq, "%s-%d", cdev->name, k);
|
||||
if (ret < 0) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(dev, "Failed to request IRQ %d", k);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
irq->index = k;
|
||||
irq->irqn = pci_irq_vector(pdev, k);
|
||||
cdev->irq_count++;
|
||||
}
|
||||
|
||||
dev_info(dev, "Configured %d IRQs", cdev->irq_count);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
cndm_irq_deinit_pcie(cdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void cndm_irq_deinit_pcie(struct cndm_dev *cdev)
|
||||
{
|
||||
struct pci_dev *pdev = cdev->pdev;
|
||||
int k;
|
||||
|
||||
for (k = 0; k < cdev->irq_count; k++)
|
||||
pci_free_irq(pdev, k, &cdev->irq[k]);
|
||||
|
||||
cdev->irq_count = 0;
|
||||
|
||||
if (cdev->irq)
|
||||
kvfree(cdev->irq);
|
||||
cdev->irq = NULL;
|
||||
|
||||
pci_free_irq_vectors(pdev);
|
||||
}
|
||||
309
src/cndm/modules/cndm/cndm_main.c
Normal file
309
src/cndm/modules/cndm/cndm_main.c
Normal file
@@ -0,0 +1,309 @@
|
||||
// SPDX-License-Identifier: GPL
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
#include "cndm.h"
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
MODULE_DESCRIPTION("Corundum device driver");
|
||||
MODULE_AUTHOR("FPGA Ninja");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
|
||||
static DEFINE_IDA(cndm_instance_ida);
|
||||
|
||||
static int cndm_assign_id(struct cndm_dev *cdev)
|
||||
{
|
||||
int ret = ida_alloc(&cndm_instance_ida, GFP_KERNEL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
cdev->id = ret;
|
||||
snprintf(cdev->name, sizeof(cdev->name), KBUILD_MODNAME "%d", cdev->id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cndm_free_id(struct cndm_dev *cdev)
|
||||
{
|
||||
ida_free(&cndm_instance_ida, cdev->id);
|
||||
}
|
||||
|
||||
static void cndm_common_remove(struct cndm_dev *cdev);
|
||||
|
||||
static int cndm_common_probe(struct cndm_dev *cdev)
|
||||
{
|
||||
struct devlink *devlink = priv_to_devlink(cdev);
|
||||
struct device *dev = cdev->dev;
|
||||
int ret = 0;
|
||||
int k;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
|
||||
devlink_register(devlink);
|
||||
#else
|
||||
devlink_register(devlink, dev);
|
||||
#endif
|
||||
|
||||
cdev->port_count = ioread32(cdev->hw_addr + 0x0100);
|
||||
cdev->port_offset = ioread32(cdev->hw_addr + 0x0104);
|
||||
cdev->port_stride = ioread32(cdev->hw_addr + 0x0108);
|
||||
|
||||
dev_info(dev, "Port count: %d", cdev->port_count);
|
||||
dev_info(dev, "Port offset: 0x%x", cdev->port_offset);
|
||||
dev_info(dev, "Port stride: 0x%x", cdev->port_stride);
|
||||
|
||||
if (cdev->port_count > ARRAY_SIZE(cdev->ndev))
|
||||
cdev->port_count = ARRAY_SIZE(cdev->ndev);
|
||||
|
||||
for (k = 0; k < cdev->port_count; k++) {
|
||||
struct net_device *ndev;
|
||||
|
||||
ndev = cndm_create_netdev(cdev, k, cdev->hw_addr + cdev->port_offset + (cdev->port_stride*k));
|
||||
if (IS_ERR_OR_NULL(ndev)) {
|
||||
ret = PTR_ERR(ndev);
|
||||
goto fail_netdev;
|
||||
}
|
||||
|
||||
cdev->ndev[k] = ndev;
|
||||
}
|
||||
|
||||
fail_netdev:
|
||||
cdev->misc_dev.minor = MISC_DYNAMIC_MINOR;
|
||||
cdev->misc_dev.name = cdev->name;
|
||||
cdev->misc_dev.fops = &cndm_fops;
|
||||
cdev->misc_dev.parent = dev;
|
||||
|
||||
ret = misc_register(&cdev->misc_dev);
|
||||
if (ret) {
|
||||
cdev->misc_dev.this_device = NULL;
|
||||
dev_err(dev, "misc_register failed: %d", ret);
|
||||
goto fail;
|
||||
|
||||
}
|
||||
|
||||
dev_info(dev, "Registered device %s", cdev->name);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
cndm_common_remove(cdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cndm_common_remove(struct cndm_dev *cdev)
|
||||
{
|
||||
struct devlink *devlink = priv_to_devlink(cdev);
|
||||
int k;
|
||||
|
||||
if (cdev->misc_dev.this_device)
|
||||
misc_deregister(&cdev->misc_dev);
|
||||
|
||||
for (k = 0; k < ARRAY_SIZE(cdev->ndev); k++) {
|
||||
if (cdev->ndev[k]) {
|
||||
cndm_destroy_netdev(cdev->ndev[k]);
|
||||
cdev->ndev[k] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
devlink_unregister(devlink);
|
||||
}
|
||||
|
||||
static int cndm_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct devlink *devlink;
|
||||
struct cndm_dev *cdev;
|
||||
struct pci_dev *bridge = pci_upstream_bridge(pdev);
|
||||
int ret = 0;
|
||||
|
||||
dev_info(dev, KBUILD_MODNAME " PCI probe");
|
||||
dev_info(dev, "Corundum device driver");
|
||||
dev_info(dev, "Version " DRIVER_VERSION);
|
||||
dev_info(dev, "Copyright (c) 2025 FPGA Ninja, LLC");
|
||||
dev_info(dev, "https://fpga.ninja/");
|
||||
dev_info(dev, "PCIe configuration summary:");
|
||||
|
||||
if (pdev->pcie_cap) {
|
||||
u16 devctl;
|
||||
u32 lnkcap;
|
||||
u16 lnkctl;
|
||||
u16 lnksta;
|
||||
|
||||
pci_read_config_word(pdev, pdev->pcie_cap + PCI_EXP_DEVCTL, &devctl);
|
||||
pci_read_config_dword(pdev, pdev->pcie_cap + PCI_EXP_LNKCAP, &lnkcap);
|
||||
pci_read_config_word(pdev, pdev->pcie_cap + PCI_EXP_LNKCTL, &lnkctl);
|
||||
pci_read_config_word(pdev, pdev->pcie_cap + PCI_EXP_LNKSTA, &lnksta);
|
||||
|
||||
dev_info(dev, " Max payload size: %d bytes",
|
||||
128 << ((devctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5));
|
||||
dev_info(dev, " Max read request size: %d bytes",
|
||||
128 << ((devctl & PCI_EXP_DEVCTL_READRQ) >> 12));
|
||||
dev_info(dev, " Read completion boundary: %d bytes",
|
||||
lnkctl & PCI_EXP_LNKCTL_RCB ? 128 : 64);
|
||||
dev_info(dev, " Link capability: gen %d x%d",
|
||||
lnkcap & PCI_EXP_LNKCAP_SLS, (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4);
|
||||
dev_info(dev, " Link status: gen %d x%d",
|
||||
lnksta & PCI_EXP_LNKSTA_CLS, (lnksta & PCI_EXP_LNKSTA_NLW) >> 4);
|
||||
dev_info(dev, " Relaxed ordering: %s",
|
||||
devctl & PCI_EXP_DEVCTL_RELAX_EN ? "enabled" : "disabled");
|
||||
dev_info(dev, " Phantom functions: %s",
|
||||
devctl & PCI_EXP_DEVCTL_PHANTOM ? "enabled" : "disabled");
|
||||
dev_info(dev, " Extended tags: %s",
|
||||
devctl & PCI_EXP_DEVCTL_EXT_TAG ? "enabled" : "disabled");
|
||||
dev_info(dev, " No snoop: %s",
|
||||
devctl & PCI_EXP_DEVCTL_NOSNOOP_EN ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NUMA
|
||||
dev_info(dev, " NUMA node: %d", pdev->dev.numa_node);
|
||||
#endif
|
||||
|
||||
if (bridge) {
|
||||
dev_info(dev, " Bridge PCI ID: %04x:%02x:%02x.%d", pci_domain_nr(bridge->bus),
|
||||
bridge->bus->number, PCI_SLOT(bridge->devfn), PCI_FUNC(bridge->devfn));
|
||||
}
|
||||
|
||||
if (bridge && bridge->pcie_cap) {
|
||||
u32 lnkcap;
|
||||
u16 lnksta;
|
||||
|
||||
pci_read_config_dword(bridge, bridge->pcie_cap + PCI_EXP_LNKCAP, &lnkcap);
|
||||
pci_read_config_word(bridge, bridge->pcie_cap + PCI_EXP_LNKSTA, &lnksta);
|
||||
|
||||
dev_info(dev, " Bridge link capability: gen %d x%d",
|
||||
lnkcap & PCI_EXP_LNKCAP_SLS, (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4);
|
||||
dev_info(dev, " Bridge link status: gen %d x%d",
|
||||
lnksta & PCI_EXP_LNKSTA_CLS, (lnksta & PCI_EXP_LNKSTA_NLW) >> 4);
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
|
||||
pcie_print_link_status(pdev);
|
||||
#endif
|
||||
|
||||
devlink = cndm_devlink_alloc(dev);
|
||||
if (!devlink)
|
||||
return -ENOMEM;
|
||||
|
||||
cdev = devlink_priv(devlink);
|
||||
cdev->pdev = pdev;
|
||||
cdev->dev = dev;
|
||||
pci_set_drvdata(pdev, cdev);
|
||||
|
||||
ret = cndm_assign_id(cdev);
|
||||
if (ret)
|
||||
goto fail_assign_id;
|
||||
|
||||
ret = pci_enable_device_mem(pdev);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable device");
|
||||
goto fail_enable_device;
|
||||
}
|
||||
|
||||
pci_set_master(pdev);
|
||||
|
||||
ret = pci_request_regions(pdev, cdev->name);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to reserve regions");
|
||||
goto fail_regions;
|
||||
}
|
||||
|
||||
cdev->hw_regs_size = pci_resource_len(pdev, 0);
|
||||
cdev->hw_regs_phys = pci_resource_start(pdev, 0);
|
||||
|
||||
dev_info(dev, "Control BAR size: %llu", cdev->hw_regs_size);
|
||||
cdev->hw_addr = pci_ioremap_bar(pdev, 0);
|
||||
if (!cdev->hw_addr) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(dev, "Failed to map control BAR");
|
||||
goto fail_map_bars;
|
||||
}
|
||||
|
||||
if (ioread32(cdev->hw_addr + 0x0000) == 0xffffffff) {
|
||||
ret = -EIO;
|
||||
dev_err(dev, "Device needs to be reset");
|
||||
goto fail_map_bars;
|
||||
}
|
||||
|
||||
ret = cndm_irq_init_pcie(cdev);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to set up interrupts");
|
||||
goto fail_init_irq;
|
||||
}
|
||||
|
||||
ret = cndm_common_probe(cdev);
|
||||
if (ret)
|
||||
goto fail_common;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_common:
|
||||
cndm_irq_deinit_pcie(cdev);
|
||||
fail_init_irq:
|
||||
fail_map_bars:
|
||||
if (cdev->hw_addr)
|
||||
pci_iounmap(pdev, cdev->hw_addr);
|
||||
pci_release_regions(pdev);
|
||||
fail_regions:
|
||||
pci_clear_master(pdev);
|
||||
pci_disable_device(pdev);
|
||||
fail_enable_device:
|
||||
cndm_free_id(cdev);
|
||||
fail_assign_id:
|
||||
cndm_devlink_free(devlink);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cndm_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct cndm_dev *cdev = pci_get_drvdata(pdev);
|
||||
struct devlink *devlink = priv_to_devlink(cdev);
|
||||
|
||||
dev_info(dev, KBUILD_MODNAME " PCI remove");
|
||||
|
||||
cndm_common_remove(cdev);
|
||||
|
||||
cndm_irq_deinit_pcie(cdev);
|
||||
if (cdev->hw_addr)
|
||||
pci_iounmap(pdev, cdev->hw_addr);
|
||||
pci_release_regions(pdev);
|
||||
pci_clear_master(pdev);
|
||||
pci_disable_device(pdev);
|
||||
cndm_free_id(cdev);
|
||||
cndm_devlink_free(devlink);
|
||||
}
|
||||
|
||||
static const struct pci_device_id cndm_pci_id_table[] = {
|
||||
{PCI_DEVICE(0x1234, 0xC001)},
|
||||
{0}
|
||||
};
|
||||
|
||||
static struct pci_driver cndm_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.id_table = cndm_pci_id_table,
|
||||
.probe = cndm_pci_probe,
|
||||
.remove = cndm_pci_remove
|
||||
};
|
||||
|
||||
static int __init cndm_init(void)
|
||||
{
|
||||
return pci_register_driver(&cndm_driver);
|
||||
}
|
||||
|
||||
static void __exit cndm_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&cndm_driver);
|
||||
|
||||
ida_destroy(&cndm_instance_ida);
|
||||
}
|
||||
|
||||
module_init(cndm_init);
|
||||
module_exit(cndm_exit);
|
||||
286
src/cndm/modules/cndm/cndm_netdev.c
Normal file
286
src/cndm/modules/cndm/cndm_netdev.c
Normal file
@@ -0,0 +1,286 @@
|
||||
// SPDX-License-Identifier: GPL
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
#include "cndm.h"
|
||||
|
||||
#include <linux/version.h>
|
||||
|
||||
static int cndm_open(struct net_device *ndev)
|
||||
{
|
||||
struct cndm_priv *priv = netdev_priv(ndev);
|
||||
|
||||
cndm_refill_rx_buffers(priv);
|
||||
|
||||
priv->tx_queue = netdev_get_tx_queue(ndev, 0);
|
||||
|
||||
netif_napi_add_tx(ndev, &priv->tx_napi, cndm_poll_tx_cq);
|
||||
napi_enable(&priv->tx_napi);
|
||||
netif_napi_add(ndev, &priv->rx_napi, cndm_poll_rx_cq);
|
||||
napi_enable(&priv->rx_napi);
|
||||
|
||||
netif_tx_start_all_queues(ndev);
|
||||
netif_carrier_on(ndev);
|
||||
netif_device_attach(ndev);
|
||||
|
||||
priv->port_up = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cndm_close(struct net_device *ndev)
|
||||
{
|
||||
struct cndm_priv *priv = netdev_priv(ndev);
|
||||
|
||||
priv->port_up = 0;
|
||||
|
||||
napi_disable(&priv->tx_napi);
|
||||
netif_napi_del(&priv->tx_napi);
|
||||
napi_disable(&priv->rx_napi);
|
||||
netif_napi_del(&priv->rx_napi);
|
||||
|
||||
netif_tx_stop_all_queues(ndev);
|
||||
netif_carrier_off(ndev);
|
||||
netif_tx_disable(ndev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cndm_set_mac(struct net_device *ndev, void *addr)
|
||||
{
|
||||
struct sockaddr *saddr = addr;
|
||||
|
||||
if (!is_valid_ether_addr(saddr->sa_data))
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
netif_addr_lock_bh(ndev);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
|
||||
eth_hw_addr_set(ndev, saddr->sa_data);
|
||||
#else
|
||||
memcpy(ndev->dev_addr, saddr->sa_data, ETH_ALEN);
|
||||
#endif
|
||||
netif_addr_unlock_bh(ndev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct net_device_ops cndm_netdev_ops = {
|
||||
.ndo_open = cndm_open,
|
||||
.ndo_stop = cndm_close,
|
||||
.ndo_start_xmit = cndm_start_xmit,
|
||||
.ndo_validate_addr = eth_validate_addr,
|
||||
.ndo_set_mac_address = cndm_set_mac,
|
||||
};
|
||||
|
||||
static int cndm_netdev_irq(struct notifier_block *nb, unsigned long action, void *data)
|
||||
{
|
||||
struct cndm_priv *priv = container_of(nb, struct cndm_priv, irq_nb);
|
||||
|
||||
netdev_dbg(priv->ndev, "Interrupt");
|
||||
|
||||
if (priv->port_up) {
|
||||
napi_schedule_irqoff(&priv->tx_napi);
|
||||
napi_schedule_irqoff(&priv->rx_napi);
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
struct net_device *cndm_create_netdev(struct cndm_dev *cdev, int port, void __iomem *hw_addr)
|
||||
{
|
||||
struct device *dev = cdev->dev;
|
||||
struct net_device *ndev;
|
||||
struct cndm_priv *priv;
|
||||
int ret = 0;
|
||||
|
||||
ndev = alloc_etherdev_mqs(sizeof(*priv), 1, 1);
|
||||
if (!ndev) {
|
||||
dev_err(dev, "Failed to allocate net_device");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
SET_NETDEV_DEV(ndev, dev);
|
||||
ndev->dev_port = port;
|
||||
|
||||
priv = netdev_priv(ndev);
|
||||
memset(priv, 0, sizeof(*priv));
|
||||
|
||||
priv->dev = dev;
|
||||
priv->ndev = ndev;
|
||||
priv->cdev = cdev;
|
||||
|
||||
priv->hw_addr = hw_addr;
|
||||
|
||||
netif_set_real_num_tx_queues(ndev, 1);
|
||||
netif_set_real_num_rx_queues(ndev, 1);
|
||||
|
||||
ndev->addr_len = ETH_ALEN;
|
||||
|
||||
eth_hw_addr_random(ndev);
|
||||
|
||||
ndev->netdev_ops = &cndm_netdev_ops;
|
||||
ndev->ethtool_ops = &cndm_ethtool_ops;
|
||||
|
||||
ndev->hw_features = 0;
|
||||
ndev->features = 0;
|
||||
|
||||
ndev->min_mtu = ETH_MIN_MTU;
|
||||
ndev->max_mtu = 1500;
|
||||
|
||||
priv->rxq_log_size = ilog2(256);
|
||||
priv->rxq_size = 1 << priv->rxq_log_size;
|
||||
priv->rxq_mask = priv->rxq_size-1;
|
||||
priv->rxq_prod = 0;
|
||||
priv->rxq_cons = 0;
|
||||
|
||||
priv->txq_log_size = ilog2(256);
|
||||
priv->txq_size = 1 << priv->txq_log_size;
|
||||
priv->txq_mask = priv->txq_size-1;
|
||||
priv->txq_prod = 0;
|
||||
priv->txq_cons = 0;
|
||||
|
||||
priv->rxcq_log_size = ilog2(256);
|
||||
priv->rxcq_size = 1 << priv->rxcq_log_size;
|
||||
priv->rxcq_mask = priv->rxcq_size-1;
|
||||
priv->rxcq_prod = 0;
|
||||
priv->rxcq_cons = 0;
|
||||
|
||||
priv->txcq_log_size = ilog2(256);
|
||||
priv->txcq_size = 1 << priv->txcq_log_size;
|
||||
priv->txcq_mask = priv->txcq_size-1;
|
||||
priv->txcq_prod = 0;
|
||||
priv->txcq_cons = 0;
|
||||
|
||||
// allocate DMA buffers
|
||||
priv->txq_region_len = priv->txq_size*16;
|
||||
priv->txq_region = dma_alloc_coherent(dev, priv->txq_region_len, &priv->txq_region_addr, GFP_KERNEL | __GFP_ZERO);
|
||||
if (!priv->txq_region) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
priv->rxq_region_len = priv->rxq_size*16;
|
||||
priv->rxq_region = dma_alloc_coherent(dev, priv->rxq_region_len, &priv->rxq_region_addr, GFP_KERNEL | __GFP_ZERO);
|
||||
if (!priv->rxq_region) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
priv->txcq_region_len = priv->txcq_size*16;
|
||||
priv->txcq_region = dma_alloc_coherent(dev, priv->txcq_region_len, &priv->txcq_region_addr, GFP_KERNEL | __GFP_ZERO);
|
||||
if (!priv->txcq_region) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
priv->rxcq_region_len = priv->rxcq_size*16;
|
||||
priv->rxcq_region = dma_alloc_coherent(dev, priv->rxcq_region_len, &priv->rxcq_region_addr, GFP_KERNEL | __GFP_ZERO);
|
||||
if (!priv->rxcq_region) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// allocate info rings
|
||||
priv->tx_info = kvzalloc(sizeof(*priv->tx_info) * priv->txq_size, GFP_KERNEL);
|
||||
if (!priv->tx_info) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
priv->rx_info = kvzalloc(sizeof(*priv->rx_info) * priv->rxq_size, GFP_KERNEL);
|
||||
if (!priv->tx_info) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
iowrite32(0x00000000, priv->hw_addr + 0x200);
|
||||
iowrite32(priv->rxq_prod & 0xffff, priv->hw_addr + 0x204);
|
||||
iowrite32(priv->rxq_region_addr & 0xffffffff, priv->hw_addr + 0x208);
|
||||
iowrite32(priv->rxq_region_addr >> 32, priv->hw_addr + 0x20c);
|
||||
iowrite32(0x00000001 | (priv->rxq_log_size << 16), priv->hw_addr + 0x200);
|
||||
|
||||
iowrite32(0x00000000, priv->hw_addr + 0x100);
|
||||
iowrite32(priv->txq_prod & 0xffff, priv->hw_addr + 0x104);
|
||||
iowrite32(priv->txq_region_addr & 0xffffffff, priv->hw_addr + 0x108);
|
||||
iowrite32(priv->txq_region_addr >> 32, priv->hw_addr + 0x10c);
|
||||
iowrite32(0x00000001 | (priv->txq_log_size << 16), priv->hw_addr + 0x100);
|
||||
|
||||
iowrite32(0x00000000, priv->hw_addr + 0x400);
|
||||
iowrite32(priv->rxcq_region_addr & 0xffffffff, priv->hw_addr + 0x408);
|
||||
iowrite32(priv->rxcq_region_addr >> 32, priv->hw_addr + 0x40c);
|
||||
iowrite32(0x00000001 | (priv->rxcq_log_size << 16), priv->hw_addr + 0x400);
|
||||
|
||||
iowrite32(0x00000000, priv->hw_addr + 0x300);
|
||||
iowrite32(priv->txcq_region_addr & 0xffffffff, priv->hw_addr + 0x308);
|
||||
iowrite32(priv->txcq_region_addr >> 32, priv->hw_addr + 0x30c);
|
||||
iowrite32(0x00000001 | (priv->txcq_log_size << 16), priv->hw_addr + 0x300);
|
||||
|
||||
netif_carrier_off(ndev);
|
||||
|
||||
ret = register_netdev(ndev);
|
||||
if (ret) {
|
||||
dev_err(dev, "netdev registration failed");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
priv->registered = 1;
|
||||
|
||||
priv->irq_nb.notifier_call = cndm_netdev_irq;
|
||||
priv->irq = &cdev->irq[port % cdev->irq_count];
|
||||
ret = atomic_notifier_chain_register(&priv->irq->nh, &priv->irq_nb);
|
||||
if (ret) {
|
||||
priv->irq = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
return ndev;
|
||||
|
||||
fail:
|
||||
cndm_destroy_netdev(ndev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
void cndm_destroy_netdev(struct net_device *ndev)
|
||||
{
|
||||
struct cndm_priv *priv = netdev_priv(ndev);
|
||||
struct device *dev = priv->dev;
|
||||
|
||||
iowrite32(0x00000000, priv->hw_addr + 0x200);
|
||||
iowrite32(0x00000000, priv->hw_addr + 0x100);
|
||||
iowrite32(0x00000000, priv->hw_addr + 0x400);
|
||||
iowrite32(0x00000000, priv->hw_addr + 0x300);
|
||||
|
||||
if (priv->irq)
|
||||
atomic_notifier_chain_unregister(&priv->irq->nh, &priv->irq_nb);
|
||||
|
||||
priv->irq = NULL;
|
||||
|
||||
if (priv->registered)
|
||||
unregister_netdev(ndev);
|
||||
|
||||
if (priv->tx_info) {
|
||||
cndm_free_tx_buf(priv);
|
||||
kvfree(priv->tx_info);
|
||||
}
|
||||
if (priv->rx_info) {
|
||||
cndm_free_rx_buf(priv);
|
||||
kvfree(priv->rx_info);
|
||||
}
|
||||
if (priv->txq_region)
|
||||
dma_free_coherent(dev, priv->txq_region_len, priv->txq_region, priv->txq_region_addr);
|
||||
if (priv->rxq_region)
|
||||
dma_free_coherent(dev, priv->rxq_region_len, priv->rxq_region, priv->rxq_region_addr);
|
||||
if (priv->txcq_region)
|
||||
dma_free_coherent(dev, priv->txcq_region_len, priv->txcq_region, priv->txcq_region_addr);
|
||||
if (priv->rxcq_region)
|
||||
dma_free_coherent(dev, priv->rxcq_region_len, priv->rxcq_region, priv->rxcq_region_addr);
|
||||
|
||||
free_netdev(ndev);
|
||||
}
|
||||
193
src/cndm/modules/cndm/cndm_rx.c
Normal file
193
src/cndm/modules/cndm/cndm_rx.c
Normal file
@@ -0,0 +1,193 @@
|
||||
// SPDX-License-Identifier: GPL
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
#include "cndm.h"
|
||||
|
||||
static void cndm_free_rx_desc(struct cndm_priv *priv, int index)
|
||||
{
|
||||
struct device *dev = priv->dev;
|
||||
struct cndm_rx_info *rx_info = &priv->rx_info[index];
|
||||
|
||||
netdev_dbg(priv->ndev, "Free RX desc index %d", index);
|
||||
|
||||
if (!rx_info->page)
|
||||
return;
|
||||
|
||||
dma_unmap_page(dev, rx_info->dma_addr, rx_info->len, DMA_FROM_DEVICE);
|
||||
rx_info->dma_addr = 0;
|
||||
__free_pages(rx_info->page, 0);
|
||||
rx_info->page = NULL;
|
||||
}
|
||||
|
||||
int cndm_free_rx_buf(struct cndm_priv *priv)
|
||||
{
|
||||
u32 index;
|
||||
int cnt = 0;
|
||||
|
||||
while (priv->rxq_prod != priv->rxq_cons) {
|
||||
index = priv->rxq_cons & priv->rxq_mask;
|
||||
cndm_free_rx_desc(priv, index);
|
||||
priv->rxq_cons++;
|
||||
cnt++;
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static int cndm_prepare_rx_desc(struct cndm_priv *priv, int index)
|
||||
{
|
||||
struct device *dev = priv->dev;
|
||||
struct cndm_rx_info *rx_info = &priv->rx_info[index];
|
||||
struct cndm_desc *rx_desc = (struct cndm_desc *)(priv->rxq_region + index*16);
|
||||
struct page *page;
|
||||
u32 len = PAGE_SIZE;
|
||||
dma_addr_t dma_addr;
|
||||
|
||||
netdev_dbg(priv->ndev, "Prepare RX desc index %d", index);
|
||||
|
||||
page = dev_alloc_pages(0);
|
||||
if (unlikely(!page)) {
|
||||
netdev_err(priv->ndev, "Failed to allocate page");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dma_addr = dma_map_page(dev, page, 0, len, DMA_FROM_DEVICE);
|
||||
|
||||
if (unlikely(dma_mapping_error(dev, dma_addr))) {
|
||||
netdev_err(priv->ndev, "Mapping failed");
|
||||
__free_pages(page, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rx_desc->len = cpu_to_le32(len);
|
||||
rx_desc->addr = cpu_to_le64(dma_addr);
|
||||
|
||||
rx_info->page = page;
|
||||
rx_info->len = len;
|
||||
rx_info->dma_addr = dma_addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cndm_refill_rx_buffers(struct cndm_priv *priv)
|
||||
{
|
||||
u32 missing = 128 - (priv->rxq_prod - priv->rxq_cons); // TODO
|
||||
int ret = 0;
|
||||
|
||||
if (missing < 8)
|
||||
return 0;
|
||||
|
||||
for (; missing-- > 0;) {
|
||||
ret = cndm_prepare_rx_desc(priv, priv->rxq_prod & priv->rxq_mask);
|
||||
if (ret)
|
||||
break;
|
||||
priv->rxq_prod++;
|
||||
}
|
||||
|
||||
dma_wmb();
|
||||
iowrite32(priv->rxq_prod & 0xffff, priv->hw_addr + 0x204);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cndm_process_rx_cq(struct net_device *ndev, int napi_budget)
|
||||
{
|
||||
struct cndm_priv *priv = netdev_priv(ndev);
|
||||
struct cndm_cpl *cpl;
|
||||
struct cndm_rx_info *rx_info;
|
||||
struct sk_buff *skb;
|
||||
struct page *page;
|
||||
int done = 0;
|
||||
u32 len;
|
||||
|
||||
u32 cq_cons_ptr;
|
||||
u32 cq_index;
|
||||
u32 cons_ptr;
|
||||
u32 index;
|
||||
|
||||
cq_cons_ptr = priv->rxcq_cons;
|
||||
cons_ptr = priv->rxq_cons;
|
||||
|
||||
while (done < napi_budget) {
|
||||
cq_index = cq_cons_ptr & priv->rxcq_mask;
|
||||
cpl = (struct cndm_cpl *)(priv->rxcq_region + cq_index * 16);
|
||||
|
||||
if (!!(cpl->phase & 0x80) == !!(cq_cons_ptr & priv->rxcq_size))
|
||||
break;
|
||||
|
||||
dma_rmb();
|
||||
|
||||
index = cons_ptr & priv->rxq_mask;
|
||||
|
||||
rx_info = &priv->rx_info[index];
|
||||
page = rx_info->page;
|
||||
len = min_t(u32, le16_to_cpu(cpl->len), rx_info->len);
|
||||
|
||||
netdev_dbg(priv->ndev, "Process RX cpl index %d", index);
|
||||
|
||||
if (!page) {
|
||||
netdev_err(priv->ndev, "Null page at index %d", index);
|
||||
break;
|
||||
}
|
||||
|
||||
dma_unmap_page(priv->dev, rx_info->dma_addr, rx_info->len, DMA_FROM_DEVICE);
|
||||
rx_info->dma_addr = 0;
|
||||
rx_info->page = NULL;
|
||||
|
||||
if (len < ETH_HLEN) {
|
||||
netdev_warn(priv->ndev, "Dropping short frame (len %d)", len);
|
||||
goto rx_drop;
|
||||
}
|
||||
|
||||
skb = napi_get_frags(&priv->rx_napi);
|
||||
if (!skb) {
|
||||
netdev_err(priv->ndev, "Failed to allocate skb %d", index);
|
||||
break;
|
||||
}
|
||||
|
||||
__skb_fill_page_desc(skb, 0, page, 0, len);
|
||||
|
||||
skb_shinfo(skb)->nr_frags = 1;
|
||||
skb->len = len;
|
||||
skb->data_len = len;
|
||||
skb->truesize = rx_info->len;
|
||||
|
||||
napi_gro_frags(&priv->rx_napi);
|
||||
|
||||
rx_drop:
|
||||
done++;
|
||||
cq_cons_ptr++;
|
||||
cons_ptr++;
|
||||
}
|
||||
|
||||
priv->rxcq_cons = cq_cons_ptr;
|
||||
priv->rxq_cons = cons_ptr;
|
||||
|
||||
cndm_refill_rx_buffers(priv);
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
int cndm_poll_rx_cq(struct napi_struct *napi, int budget)
|
||||
{
|
||||
struct cndm_priv *priv = container_of(napi, struct cndm_priv, rx_napi);
|
||||
int done;
|
||||
|
||||
done = cndm_process_rx_cq(priv->ndev, budget);
|
||||
|
||||
if (done == budget)
|
||||
return done;
|
||||
|
||||
napi_complete(napi);
|
||||
|
||||
// TODO re-enable interrupts
|
||||
|
||||
return done;
|
||||
}
|
||||
159
src/cndm/modules/cndm/cndm_tx.c
Normal file
159
src/cndm/modules/cndm/cndm_tx.c
Normal file
@@ -0,0 +1,159 @@
|
||||
// SPDX-License-Identifier: GPL
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
#include "cndm.h"
|
||||
|
||||
static void cndm_free_tx_desc(struct cndm_priv *priv, int index, int napi_budget)
|
||||
{
|
||||
struct device *dev = priv->dev;
|
||||
struct cndm_tx_info *tx_info = &priv->tx_info[index];
|
||||
struct sk_buff *skb = tx_info->skb;
|
||||
|
||||
netdev_dbg(priv->ndev, "Free TX desc index %d", index);
|
||||
|
||||
dma_unmap_single(dev, tx_info->dma_addr, tx_info->len, DMA_TO_DEVICE);
|
||||
tx_info->dma_addr = 0;
|
||||
|
||||
napi_consume_skb(skb, napi_budget);
|
||||
tx_info->skb = NULL;
|
||||
}
|
||||
|
||||
int cndm_free_tx_buf(struct cndm_priv *priv)
|
||||
{
|
||||
u32 index;
|
||||
int cnt = 0;
|
||||
|
||||
while (priv->txq_prod != priv->txq_cons) {
|
||||
index = priv->txq_cons & priv->txq_mask;
|
||||
cndm_free_tx_desc(priv, index, 0);
|
||||
priv->txq_cons++;
|
||||
cnt++;
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static int cndm_process_tx_cq(struct net_device *ndev, int napi_budget)
|
||||
{
|
||||
struct cndm_priv *priv = netdev_priv(ndev);
|
||||
struct cndm_cpl *cpl;
|
||||
int done = 0;
|
||||
|
||||
u32 cq_cons_ptr;
|
||||
u32 cq_index;
|
||||
u32 cons_ptr;
|
||||
u32 index;
|
||||
|
||||
cq_cons_ptr = priv->txcq_cons;
|
||||
cons_ptr = priv->txq_cons;
|
||||
|
||||
while (done < napi_budget) {
|
||||
cq_index = cq_cons_ptr & priv->txcq_mask;
|
||||
cpl = (struct cndm_cpl *)(priv->txcq_region + cq_index * 16);
|
||||
|
||||
if (!!(cpl->phase & 0x80) == !!(cq_cons_ptr & priv->txcq_size))
|
||||
break;
|
||||
|
||||
dma_rmb();
|
||||
|
||||
index = cons_ptr & priv->txq_mask;
|
||||
|
||||
cndm_free_tx_desc(priv, index, napi_budget);
|
||||
|
||||
done++;
|
||||
cq_cons_ptr++;
|
||||
cons_ptr++;
|
||||
}
|
||||
|
||||
priv->txcq_cons = cq_cons_ptr;
|
||||
priv->txq_cons = cons_ptr;
|
||||
|
||||
if (netif_tx_queue_stopped(priv->tx_queue) && (done != 0 || priv->txq_prod == priv->txq_cons))
|
||||
netif_tx_wake_queue(priv->tx_queue);
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
int cndm_poll_tx_cq(struct napi_struct *napi, int budget)
|
||||
{
|
||||
struct cndm_priv *priv = container_of(napi, struct cndm_priv, tx_napi);
|
||||
int done;
|
||||
|
||||
done = cndm_process_tx_cq(priv->ndev, budget);
|
||||
|
||||
if (done == budget)
|
||||
return done;
|
||||
|
||||
napi_complete(napi);
|
||||
|
||||
// TODO re-enable interrupts
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
int cndm_start_xmit(struct sk_buff *skb, struct net_device *ndev)
|
||||
{
|
||||
struct cndm_priv *priv = netdev_priv(ndev);
|
||||
struct device *dev = priv->dev;
|
||||
u32 index;
|
||||
u32 cons_ptr;
|
||||
u32 len;
|
||||
dma_addr_t dma_addr;
|
||||
struct cndm_desc *tx_desc;
|
||||
struct cndm_tx_info *tx_info;
|
||||
|
||||
netdev_dbg(ndev, "Got packet for TX");
|
||||
|
||||
if (skb->len < ETH_HLEN) {
|
||||
netdev_warn(ndev, "Dropping short frame");
|
||||
goto tx_drop;
|
||||
}
|
||||
|
||||
cons_ptr = READ_ONCE(priv->txq_cons);
|
||||
|
||||
index = priv->txq_prod & priv->txq_mask;
|
||||
|
||||
tx_desc = (struct cndm_desc *)(priv->txq_region + index*16);
|
||||
tx_info = &priv->tx_info[index];
|
||||
|
||||
len = skb_headlen(skb);
|
||||
|
||||
dma_addr = dma_map_single(dev, skb->data, len, DMA_TO_DEVICE);
|
||||
|
||||
if (unlikely(dma_mapping_error(dev, dma_addr))) {
|
||||
netdev_err(ndev, "Mapping failed");
|
||||
goto tx_drop;
|
||||
}
|
||||
|
||||
tx_desc->len = cpu_to_le32(len);
|
||||
tx_desc->addr = cpu_to_le64(dma_addr);
|
||||
|
||||
tx_info->skb = skb;
|
||||
tx_info->len = len;
|
||||
tx_info->dma_addr = dma_addr;
|
||||
|
||||
netdev_dbg(ndev, "Write desc index %d len %d", index, len);
|
||||
|
||||
priv->txq_prod++;
|
||||
|
||||
if (priv->txq_prod - priv->txq_cons >= 128) {
|
||||
netdev_dbg(ndev, "TX ring full");
|
||||
netif_tx_stop_queue(priv->tx_queue);
|
||||
}
|
||||
|
||||
dma_wmb();
|
||||
iowrite32(priv->txq_prod & 0xffff, priv->hw_addr + 0x104);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
tx_drop:
|
||||
dev_kfree_skb_any(skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
16
src/cndm/rtl/cndm_micro_core.f
Normal file
16
src/cndm/rtl/cndm_micro_core.f
Normal file
@@ -0,0 +1,16 @@
|
||||
cndm_micro_core.sv
|
||||
cndm_micro_port.sv
|
||||
cndm_micro_rx.sv
|
||||
cndm_micro_tx.sv
|
||||
cndm_micro_desc_rd.sv
|
||||
cndm_micro_cpl_wr.sv
|
||||
../lib/taxi/src/dma/rtl/taxi_dma_client_axis_source.sv
|
||||
../lib/taxi/src/dma/rtl/taxi_dma_client_axis_sink.sv
|
||||
../lib/taxi/src/dma/rtl/taxi_dma_if_mux.f
|
||||
../lib/taxi/src/dma/rtl/taxi_dma_psdpram.sv
|
||||
../lib/taxi/src/axi/rtl/taxi_axil_interconnect_1s.f
|
||||
../lib/taxi/src/axis/rtl/taxi_axis_async_fifo.f
|
||||
../lib/taxi/src/axis/rtl/taxi_axis_arb_mux.f
|
||||
../lib/taxi/src/axis/rtl/taxi_axis_demux.sv
|
||||
../lib/taxi/src/ptp/rtl/taxi_ptp_td_phc.sv
|
||||
../lib/taxi/src/ptp/rtl/taxi_ptp_td_leaf.sv
|
||||
369
src/cndm/rtl/cndm_micro_core.sv
Normal file
369
src/cndm/rtl/cndm_micro_core.sv
Normal file
@@ -0,0 +1,369 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* Corundum-micro core logic
|
||||
*/
|
||||
module cndm_micro_core #(
|
||||
parameter PORTS = 2//,
|
||||
// parameter logic PTP_TS_EN = 1'b1,
|
||||
// parameter PTP_CLK_PER_NS_NUM = 512,
|
||||
// parameter PTP_CLK_PER_NS_DENOM = 165
|
||||
)
|
||||
(
|
||||
input wire logic clk,
|
||||
input wire logic rst,
|
||||
|
||||
/*
|
||||
* Control register interface
|
||||
*/
|
||||
taxi_axil_if.wr_slv s_axil_wr,
|
||||
taxi_axil_if.rd_slv s_axil_rd,
|
||||
|
||||
/*
|
||||
* DMA
|
||||
*/
|
||||
taxi_dma_desc_if.req_src dma_rd_desc_req,
|
||||
taxi_dma_desc_if.sts_snk dma_rd_desc_sts,
|
||||
taxi_dma_desc_if.req_src dma_wr_desc_req,
|
||||
taxi_dma_desc_if.sts_snk dma_wr_desc_sts,
|
||||
taxi_dma_ram_if.wr_slv dma_ram_wr,
|
||||
taxi_dma_ram_if.rd_slv dma_ram_rd,
|
||||
|
||||
output wire logic [PORTS-1:0] irq,
|
||||
|
||||
/*
|
||||
* PTP
|
||||
*/
|
||||
// input wire logic ptp_clk = 1'b0,
|
||||
// input wire logic ptp_rst = 1'b0,
|
||||
// input wire logic ptp_sample_clk = 1'b0,
|
||||
// output wire logic ptp_td_sdo,
|
||||
// output wire logic ptp_pps,
|
||||
// output wire logic ptp_pps_str,
|
||||
// output wire logic ptp_sync_locked,
|
||||
// output wire logic [63:0] ptp_sync_ts_rel,
|
||||
// output wire logic ptp_sync_ts_rel_step,
|
||||
// output wire logic [95:0] ptp_sync_ts_tod,
|
||||
// output wire logic ptp_sync_ts_tod_step,
|
||||
// output wire logic ptp_sync_pps,
|
||||
// output wire logic ptp_sync_pps_str,
|
||||
|
||||
/*
|
||||
* Ethernet
|
||||
*/
|
||||
input wire logic mac_tx_clk[PORTS],
|
||||
input wire logic mac_tx_rst[PORTS],
|
||||
taxi_axis_if.src mac_axis_tx[PORTS],
|
||||
taxi_axis_if.snk mac_axis_tx_cpl[PORTS],
|
||||
|
||||
input wire logic mac_rx_clk[PORTS],
|
||||
input wire logic mac_rx_rst[PORTS],
|
||||
taxi_axis_if.snk mac_axis_rx[PORTS]
|
||||
);
|
||||
|
||||
localparam CL_PORTS = $clog2(PORTS);
|
||||
|
||||
localparam AXIL_ADDR_W = s_axil_wr.ADDR_W;
|
||||
localparam AXIL_DATA_W = s_axil_wr.DATA_W;
|
||||
|
||||
localparam RAM_SEGS = dma_ram_wr.SEGS;
|
||||
localparam RAM_SEG_ADDR_W = dma_ram_wr.SEG_ADDR_W;
|
||||
localparam RAM_SEG_DATA_W = dma_ram_wr.SEG_DATA_W;
|
||||
localparam RAM_SEG_BE_W = dma_ram_wr.SEG_BE_W;
|
||||
localparam RAM_SEL_W = dma_ram_wr.SEL_W;
|
||||
|
||||
localparam PORT_OFFSET = 1;
|
||||
// localparam PORT_OFFSET = PTP_TS_EN ? 2 : 1;
|
||||
|
||||
taxi_axil_if #(
|
||||
.DATA_W(s_axil_wr.DATA_W),
|
||||
.ADDR_W(16),
|
||||
.STRB_W(s_axil_wr.STRB_W),
|
||||
.AWUSER_EN(s_axil_wr.AWUSER_EN),
|
||||
.AWUSER_W(s_axil_wr.AWUSER_W),
|
||||
.WUSER_EN(s_axil_wr.WUSER_EN),
|
||||
.WUSER_W(s_axil_wr.WUSER_W),
|
||||
.BUSER_EN(s_axil_wr.BUSER_EN),
|
||||
.BUSER_W(s_axil_wr.BUSER_W),
|
||||
.ARUSER_EN(s_axil_wr.ARUSER_EN),
|
||||
.ARUSER_W(s_axil_wr.ARUSER_W),
|
||||
.RUSER_EN(s_axil_wr.RUSER_EN),
|
||||
.RUSER_W(s_axil_wr.RUSER_W)
|
||||
)
|
||||
s_axil_ctrl[PORTS+PORT_OFFSET]();
|
||||
|
||||
taxi_axil_interconnect_1s #(
|
||||
.M_COUNT($size(s_axil_ctrl)),
|
||||
.ADDR_W(s_axil_wr.ADDR_W),
|
||||
.M_REGIONS(1),
|
||||
.M_BASE_ADDR('0),
|
||||
.M_ADDR_W({$size(s_axil_ctrl){{1{32'd16}}}}),
|
||||
.M_SECURE({$size(s_axil_ctrl){1'b0}})
|
||||
)
|
||||
port_intercon_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI4-lite slave interface
|
||||
*/
|
||||
.s_axil_wr(s_axil_wr),
|
||||
.s_axil_rd(s_axil_rd),
|
||||
|
||||
/*
|
||||
* AXI4-lite master interfaces
|
||||
*/
|
||||
.m_axil_wr(s_axil_ctrl),
|
||||
.m_axil_rd(s_axil_ctrl)
|
||||
);
|
||||
|
||||
logic s_axil_awready_reg = 1'b0;
|
||||
logic s_axil_wready_reg = 1'b0;
|
||||
logic s_axil_bvalid_reg = 1'b0;
|
||||
|
||||
logic s_axil_arready_reg = 1'b0;
|
||||
logic [AXIL_DATA_W-1:0] s_axil_rdata_reg = '0;
|
||||
logic s_axil_rvalid_reg = 1'b0;
|
||||
|
||||
assign s_axil_ctrl[0].awready = s_axil_awready_reg;
|
||||
assign s_axil_ctrl[0].wready = s_axil_wready_reg;
|
||||
assign s_axil_ctrl[0].bresp = '0;
|
||||
assign s_axil_ctrl[0].buser = '0;
|
||||
assign s_axil_ctrl[0].bvalid = s_axil_bvalid_reg;
|
||||
|
||||
assign s_axil_ctrl[0].arready = s_axil_arready_reg;
|
||||
assign s_axil_ctrl[0].rdata = s_axil_rdata_reg;
|
||||
assign s_axil_ctrl[0].rresp = '0;
|
||||
assign s_axil_ctrl[0].ruser = '0;
|
||||
assign s_axil_ctrl[0].rvalid = s_axil_rvalid_reg;
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
s_axil_awready_reg <= 1'b0;
|
||||
s_axil_wready_reg <= 1'b0;
|
||||
s_axil_bvalid_reg <= s_axil_bvalid_reg && !s_axil_ctrl[0].bready;
|
||||
|
||||
s_axil_arready_reg <= 1'b0;
|
||||
s_axil_rvalid_reg <= s_axil_rvalid_reg && !s_axil_ctrl[0].rready;
|
||||
|
||||
if (s_axil_ctrl[0].awvalid && s_axil_ctrl[0].wvalid && !s_axil_bvalid_reg) begin
|
||||
s_axil_awready_reg <= 1'b1;
|
||||
s_axil_wready_reg <= 1'b1;
|
||||
s_axil_bvalid_reg <= 1'b1;
|
||||
|
||||
case ({s_axil_ctrl[0].awaddr[15:2], 2'b00})
|
||||
// 16'h0100: begin
|
||||
// txq_en_reg <= s_axil_ctrl[0].wdata[0];
|
||||
// txq_size_reg <= s_axil_ctrl[0].wdata[19:16];
|
||||
// end
|
||||
// 16'h0104: txq_prod_reg <= s_axil_ctrl[0].wdata[15:0];
|
||||
// 16'h0108: txq_base_addr_reg[31:0] <= s_axil_ctrl[0].wdata;
|
||||
// 16'h010c: txq_base_addr_reg[63:32] <= s_axil_ctrl[0].wdata;
|
||||
default: begin end
|
||||
endcase
|
||||
end
|
||||
|
||||
if (s_axil_ctrl[0].arvalid && !s_axil_rvalid_reg) begin
|
||||
s_axil_rdata_reg <= '0;
|
||||
|
||||
s_axil_arready_reg <= 1'b1;
|
||||
s_axil_rvalid_reg <= 1'b1;
|
||||
|
||||
case ({s_axil_ctrl[0].araddr[15:2], 2'b00})
|
||||
16'h0100: s_axil_rdata_reg <= PORTS; // port count
|
||||
16'h0104: s_axil_rdata_reg <= 32'h00010000; // port offset
|
||||
// 16'h0104: s_axil_rdata_reg <= PTP_TS_EN ? 32'h00020000 : 32'h00010000; // port offset
|
||||
16'h0108: s_axil_rdata_reg <= 32'h00010000; // port stride
|
||||
default: begin end
|
||||
endcase
|
||||
end
|
||||
|
||||
if (rst) begin
|
||||
s_axil_awready_reg <= 1'b0;
|
||||
s_axil_wready_reg <= 1'b0;
|
||||
s_axil_bvalid_reg <= 1'b0;
|
||||
|
||||
s_axil_arready_reg <= 1'b0;
|
||||
s_axil_rvalid_reg <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
// if (PTP_TS_EN) begin : ptp
|
||||
|
||||
// cndm_micro_ptp #(
|
||||
// .PTP_CLK_PER_NS_NUM(PTP_CLK_PER_NS_NUM),
|
||||
// .PTP_CLK_PER_NS_DENOM(PTP_CLK_PER_NS_DENOM)
|
||||
// )
|
||||
// ptp_inst (
|
||||
// .clk(clk),
|
||||
// .rst(rst),
|
||||
|
||||
// /*
|
||||
// * Control register interface
|
||||
// */
|
||||
// .s_axil_wr(s_axil_ctrl[1]),
|
||||
// .s_axil_rd(s_axil_ctrl[1]),
|
||||
|
||||
// /*
|
||||
// * PTP
|
||||
// */
|
||||
// .ptp_clk(ptp_clk),
|
||||
// .ptp_rst(ptp_rst),
|
||||
// .ptp_sample_clk(ptp_sample_clk),
|
||||
// .ptp_td_sdo(ptp_td_sdo),
|
||||
// .ptp_pps(ptp_pps),
|
||||
// .ptp_pps_str(ptp_pps_str),
|
||||
// .ptp_sync_locked(ptp_sync_locked),
|
||||
// .ptp_sync_ts_rel(ptp_sync_ts_rel),
|
||||
// .ptp_sync_ts_rel_step(ptp_sync_ts_rel_step),
|
||||
// .ptp_sync_ts_tod(ptp_sync_ts_tod),
|
||||
// .ptp_sync_ts_tod_step(ptp_sync_ts_tod_step),
|
||||
// .ptp_sync_pps(ptp_sync_pps),
|
||||
// .ptp_sync_pps_str(ptp_sync_pps_str)
|
||||
// );
|
||||
|
||||
// end else begin : ptp
|
||||
|
||||
// assign ptp_td_sdo = 1'b0;
|
||||
// assign ptp_pps = 1'b0;
|
||||
// assign ptp_pps_str = 1'b0;
|
||||
|
||||
// end
|
||||
|
||||
taxi_dma_desc_if #(
|
||||
.SRC_ADDR_W(dma_rd_desc_req.SRC_ADDR_W),
|
||||
.SRC_SEL_EN(dma_rd_desc_req.SRC_SEL_EN),
|
||||
.SRC_SEL_W(dma_rd_desc_req.SRC_SEL_W),
|
||||
.SRC_ASID_EN(dma_rd_desc_req.SRC_ASID_EN),
|
||||
.DST_ADDR_W(dma_rd_desc_req.DST_ADDR_W),
|
||||
.DST_SEL_EN(dma_rd_desc_req.DST_SEL_EN),
|
||||
.DST_SEL_W(dma_rd_desc_req.DST_SEL_W-CL_PORTS),
|
||||
.DST_ASID_EN(dma_rd_desc_req.DST_ASID_EN),
|
||||
.IMM_EN(dma_rd_desc_req.IMM_EN),
|
||||
.LEN_W(dma_rd_desc_req.LEN_W),
|
||||
.TAG_W(dma_rd_desc_req.TAG_W-CL_PORTS),
|
||||
.ID_EN(dma_rd_desc_req.ID_EN),
|
||||
.DEST_EN(dma_rd_desc_req.DEST_EN),
|
||||
.USER_EN(dma_rd_desc_req.USER_EN)
|
||||
) dma_rd_desc_int[PORTS]();
|
||||
|
||||
taxi_dma_desc_if #(
|
||||
.SRC_ADDR_W(dma_wr_desc_req.SRC_ADDR_W),
|
||||
.SRC_SEL_EN(dma_wr_desc_req.SRC_SEL_EN),
|
||||
.SRC_SEL_W(dma_wr_desc_req.SRC_SEL_W-CL_PORTS),
|
||||
.SRC_ASID_EN(dma_wr_desc_req.SRC_ASID_EN),
|
||||
.DST_ADDR_W(dma_wr_desc_req.DST_ADDR_W),
|
||||
.DST_SEL_EN(dma_wr_desc_req.DST_SEL_EN),
|
||||
.DST_SEL_W(dma_wr_desc_req.DST_SEL_W),
|
||||
.DST_ASID_EN(dma_wr_desc_req.DST_ASID_EN),
|
||||
.IMM_EN(dma_wr_desc_req.IMM_EN),
|
||||
.IMM_W(dma_wr_desc_req.IMM_W),
|
||||
.LEN_W(dma_wr_desc_req.LEN_W),
|
||||
.TAG_W(dma_wr_desc_req.TAG_W-CL_PORTS),
|
||||
.ID_EN(dma_wr_desc_req.ID_EN),
|
||||
.DEST_EN(dma_wr_desc_req.DEST_EN),
|
||||
.USER_EN(dma_wr_desc_req.USER_EN)
|
||||
) dma_wr_desc_int[PORTS]();
|
||||
|
||||
taxi_dma_ram_if #(
|
||||
.SEGS(RAM_SEGS),
|
||||
.SEG_ADDR_W(RAM_SEG_ADDR_W),
|
||||
.SEG_DATA_W(RAM_SEG_DATA_W),
|
||||
.SEG_BE_W(RAM_SEG_BE_W),
|
||||
.SEL_W(RAM_SEL_W-CL_PORTS)
|
||||
) dma_ram_int[PORTS]();
|
||||
|
||||
taxi_dma_if_mux #(
|
||||
.PORTS(PORTS),
|
||||
.ARB_ROUND_ROBIN(1),
|
||||
.ARB_LSB_HIGH_PRIO(1)
|
||||
)
|
||||
dma_mux_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* DMA descriptors from clients
|
||||
*/
|
||||
.client_rd_req(dma_rd_desc_int),
|
||||
.client_rd_sts(dma_rd_desc_int),
|
||||
.client_wr_req(dma_wr_desc_int),
|
||||
.client_wr_sts(dma_wr_desc_int),
|
||||
|
||||
/*
|
||||
* DMA descriptors to DMA engines
|
||||
*/
|
||||
.dma_rd_req(dma_rd_desc_req),
|
||||
.dma_rd_sts(dma_rd_desc_sts),
|
||||
.dma_wr_req(dma_wr_desc_req),
|
||||
.dma_wr_sts(dma_wr_desc_sts),
|
||||
|
||||
/*
|
||||
* RAM interface (from DMA interface)
|
||||
*/
|
||||
.dma_ram_wr(dma_ram_wr),
|
||||
.dma_ram_rd(dma_ram_rd),
|
||||
|
||||
/*
|
||||
* RAM interface (towards RAM)
|
||||
*/
|
||||
.client_ram_wr(dma_ram_int),
|
||||
.client_ram_rd(dma_ram_int)
|
||||
);
|
||||
|
||||
for (genvar p = 0; p < PORTS; p = p + 1) begin : port
|
||||
|
||||
cndm_micro_port #(
|
||||
// .PTP_TS_EN(PTP_TS_EN)
|
||||
)
|
||||
port_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* Control register interface
|
||||
*/
|
||||
.s_axil_wr(s_axil_ctrl[PORT_OFFSET+p]),
|
||||
.s_axil_rd(s_axil_ctrl[PORT_OFFSET+p]),
|
||||
|
||||
/*
|
||||
* DMA
|
||||
*/
|
||||
.dma_rd_desc_req(dma_rd_desc_int[p]),
|
||||
.dma_rd_desc_sts(dma_rd_desc_int[p]),
|
||||
.dma_wr_desc_req(dma_wr_desc_int[p]),
|
||||
.dma_wr_desc_sts(dma_wr_desc_int[p]),
|
||||
.dma_ram_wr(dma_ram_int[p]),
|
||||
.dma_ram_rd(dma_ram_int[p]),
|
||||
|
||||
.irq(irq[p]),
|
||||
|
||||
/*
|
||||
* Ethernet
|
||||
*/
|
||||
.mac_tx_clk(mac_tx_clk[p]),
|
||||
.mac_tx_rst(mac_tx_rst[p]),
|
||||
.mac_axis_tx(mac_axis_tx[p]),
|
||||
.mac_axis_tx_cpl(mac_axis_tx_cpl[p]),
|
||||
|
||||
.mac_rx_clk(mac_rx_clk[p]),
|
||||
.mac_rx_rst(mac_rx_rst[p]),
|
||||
.mac_axis_rx(mac_axis_rx[p])
|
||||
);
|
||||
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
227
src/cndm/rtl/cndm_micro_cpl_wr.sv
Normal file
227
src/cndm/rtl/cndm_micro_cpl_wr.sv
Normal file
@@ -0,0 +1,227 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* Corundum-micro completion write module
|
||||
*/
|
||||
module cndm_micro_cpl_wr
|
||||
(
|
||||
input wire logic clk,
|
||||
input wire logic rst,
|
||||
|
||||
/*
|
||||
* DMA
|
||||
*/
|
||||
taxi_dma_desc_if.req_src dma_wr_desc_req,
|
||||
taxi_dma_desc_if.sts_snk dma_wr_desc_sts,
|
||||
taxi_dma_ram_if.rd_slv dma_ram_rd,
|
||||
|
||||
input wire logic txcq_en,
|
||||
input wire logic [3:0] txcq_size,
|
||||
input wire logic [63:0] txcq_base_addr,
|
||||
output wire logic [15:0] txcq_prod,
|
||||
input wire logic rxcq_en,
|
||||
input wire logic [3:0] rxcq_size,
|
||||
input wire logic [63:0] rxcq_base_addr,
|
||||
output wire logic [15:0] rxcq_prod,
|
||||
|
||||
taxi_axis_if.snk axis_cpl[2],
|
||||
output wire logic irq
|
||||
);
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(axis_cpl[0].DATA_W),
|
||||
.KEEP_EN(axis_cpl[0].KEEP_EN),
|
||||
.KEEP_W(axis_cpl[0].KEEP_W),
|
||||
.STRB_EN(axis_cpl[0].STRB_EN),
|
||||
.LAST_EN(axis_cpl[0].LAST_EN),
|
||||
.ID_EN(1),
|
||||
.ID_W(1),
|
||||
.DEST_EN(axis_cpl[0].DEST_EN),
|
||||
.DEST_W(axis_cpl[0].DEST_W),
|
||||
.USER_EN(axis_cpl[0].USER_EN),
|
||||
.USER_W(axis_cpl[0].USER_W)
|
||||
) cpl_comb();
|
||||
|
||||
localparam [2:0]
|
||||
STATE_IDLE = 0,
|
||||
STATE_RX_CPL = 1,
|
||||
STATE_WRITE_DATA = 2;
|
||||
|
||||
logic [2:0] state_reg = STATE_IDLE;
|
||||
|
||||
logic [15:0] txcq_prod_ptr_reg = '0;
|
||||
logic [15:0] rxcq_prod_ptr_reg = '0;
|
||||
|
||||
logic phase_tag_reg = 1'b0;
|
||||
|
||||
logic irq_reg = 1'b0;
|
||||
|
||||
assign txcq_prod = txcq_prod_ptr_reg;
|
||||
assign rxcq_prod = rxcq_prod_ptr_reg;
|
||||
|
||||
assign irq = irq_reg;
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
cpl_comb.tready <= 1'b0;
|
||||
|
||||
dma_wr_desc_req.req_src_sel <= '0;
|
||||
dma_wr_desc_req.req_src_asid <= '0;
|
||||
dma_wr_desc_req.req_dst_sel <= '0;
|
||||
dma_wr_desc_req.req_dst_asid <= '0;
|
||||
dma_wr_desc_req.req_imm <= '0;
|
||||
dma_wr_desc_req.req_imm_en <= '0;
|
||||
dma_wr_desc_req.req_len <= 16;
|
||||
dma_wr_desc_req.req_tag <= '0;
|
||||
dma_wr_desc_req.req_id <= '0;
|
||||
dma_wr_desc_req.req_dest <= '0;
|
||||
dma_wr_desc_req.req_user <= '0;
|
||||
dma_wr_desc_req.req_valid <= dma_wr_desc_req.req_valid && !dma_wr_desc_req.req_ready;
|
||||
|
||||
if (!txcq_en) begin
|
||||
txcq_prod_ptr_reg <= '0;
|
||||
end
|
||||
|
||||
if (!rxcq_en) begin
|
||||
rxcq_prod_ptr_reg <= '0;
|
||||
end
|
||||
|
||||
irq_reg <= 1'b0;
|
||||
|
||||
case (state_reg)
|
||||
STATE_IDLE: begin
|
||||
dma_wr_desc_req.req_src_addr <= '0;
|
||||
|
||||
if (cpl_comb.tid == 0) begin
|
||||
dma_wr_desc_req.req_dst_addr <= txcq_base_addr + 64'(16'(txcq_prod_ptr_reg & ({16{1'b1}} >> (16 - txcq_size))) * 16);
|
||||
phase_tag_reg <= !txcq_prod_ptr_reg[txcq_size];
|
||||
if (cpl_comb.tvalid && !cpl_comb.tready) begin
|
||||
txcq_prod_ptr_reg <= txcq_prod_ptr_reg + 1;
|
||||
if (txcq_en) begin
|
||||
dma_wr_desc_req.req_valid <= 1'b1;
|
||||
state_reg <= STATE_WRITE_DATA;
|
||||
end else begin
|
||||
state_reg <= STATE_IDLE;
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
dma_wr_desc_req.req_dst_addr <= rxcq_base_addr + 64'(16'(rxcq_prod_ptr_reg & ({16{1'b1}} >> (16 - rxcq_size))) * 16);
|
||||
phase_tag_reg <= !rxcq_prod_ptr_reg[rxcq_size];
|
||||
if (cpl_comb.tvalid && !cpl_comb.tready) begin
|
||||
rxcq_prod_ptr_reg <= rxcq_prod_ptr_reg + 1;
|
||||
if (rxcq_en) begin
|
||||
dma_wr_desc_req.req_valid <= 1'b1;
|
||||
state_reg <= STATE_WRITE_DATA;
|
||||
end else begin
|
||||
state_reg <= STATE_IDLE;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
STATE_WRITE_DATA: begin
|
||||
if (dma_wr_desc_sts.sts_valid) begin
|
||||
cpl_comb.tready <= 1'b1;
|
||||
irq_reg <= 1'b1;
|
||||
state_reg <= STATE_IDLE;
|
||||
end
|
||||
end
|
||||
default: begin
|
||||
state_reg <= STATE_IDLE;
|
||||
end
|
||||
endcase
|
||||
|
||||
if (rst) begin
|
||||
state_reg <= STATE_IDLE;
|
||||
txcq_prod_ptr_reg <= '0;
|
||||
rxcq_prod_ptr_reg <= '0;
|
||||
irq_reg <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
taxi_axis_arb_mux #(
|
||||
.S_COUNT(2),
|
||||
.UPDATE_TID(1),
|
||||
.ARB_ROUND_ROBIN(1),
|
||||
.ARB_LSB_HIGH_PRIO(1)
|
||||
)
|
||||
mux_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI4-Stream input (sink)
|
||||
*/
|
||||
.s_axis(axis_cpl),
|
||||
|
||||
/*
|
||||
* AXI4-Stream output (source)
|
||||
*/
|
||||
.m_axis(cpl_comb)
|
||||
);
|
||||
|
||||
// extract parameters
|
||||
localparam SEGS = dma_ram_rd.SEGS;
|
||||
localparam SEG_ADDR_W = dma_ram_rd.SEG_ADDR_W;
|
||||
localparam SEG_DATA_W = dma_ram_rd.SEG_DATA_W;
|
||||
localparam SEG_BE_W = dma_ram_rd.SEG_BE_W;
|
||||
|
||||
if (SEGS*SEG_DATA_W < 128)
|
||||
$fatal(0, "Total segmented interface width must be at least 128 (instance %m)");
|
||||
|
||||
wire [SEGS-1:0][SEG_DATA_W-1:0] ram_data = (SEG_DATA_W*SEGS)'({phase_tag_reg, cpl_comb.tdata[126:0]});
|
||||
|
||||
for (genvar n = 0; n < SEGS; n = n + 1) begin
|
||||
|
||||
logic [0:0] rd_resp_valid_pipe_reg = '0;
|
||||
logic [SEG_DATA_W-1:0] rd_resp_data_pipe_reg[1];
|
||||
|
||||
initial begin
|
||||
for (integer i = 0; i < 1; i = i + 1) begin
|
||||
rd_resp_data_pipe_reg[i] = '0;
|
||||
end
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (dma_ram_rd.rd_resp_ready[n]) begin
|
||||
rd_resp_valid_pipe_reg[0] <= 1'b0;
|
||||
end
|
||||
|
||||
for (integer j = 0; j > 0; j = j - 1) begin
|
||||
if (dma_ram_rd.rd_resp_ready[n] || (1'(~rd_resp_valid_pipe_reg) >> j) != 0) begin
|
||||
rd_resp_valid_pipe_reg[j] <= rd_resp_valid_pipe_reg[j-1];
|
||||
rd_resp_data_pipe_reg[j] <= rd_resp_data_pipe_reg[j-1];
|
||||
rd_resp_valid_pipe_reg[j-1] <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
if (dma_ram_rd.rd_cmd_valid[n] && dma_ram_rd.rd_cmd_ready[n]) begin
|
||||
rd_resp_valid_pipe_reg[0] <= 1'b1;
|
||||
rd_resp_data_pipe_reg[0] <= ram_data[0];
|
||||
end
|
||||
|
||||
if (rst) begin
|
||||
rd_resp_valid_pipe_reg <= '0;
|
||||
end
|
||||
end
|
||||
|
||||
assign dma_ram_rd.rd_cmd_ready[n] = dma_ram_rd.rd_resp_ready[n] || &rd_resp_valid_pipe_reg == 0;
|
||||
|
||||
assign dma_ram_rd.rd_resp_valid[n] = rd_resp_valid_pipe_reg[0];
|
||||
assign dma_ram_rd.rd_resp_data[n] = rd_resp_data_pipe_reg[0];
|
||||
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
266
src/cndm/rtl/cndm_micro_desc_rd.sv
Normal file
266
src/cndm/rtl/cndm_micro_desc_rd.sv
Normal file
@@ -0,0 +1,266 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* Corundum-micro descriptor read module
|
||||
*/
|
||||
module cndm_micro_desc_rd
|
||||
(
|
||||
input wire logic clk,
|
||||
input wire logic rst,
|
||||
|
||||
/*
|
||||
* DMA
|
||||
*/
|
||||
taxi_dma_desc_if.req_src dma_rd_desc_req,
|
||||
taxi_dma_desc_if.sts_snk dma_rd_desc_sts,
|
||||
taxi_dma_ram_if.wr_slv dma_ram_wr,
|
||||
|
||||
input wire logic txq_en,
|
||||
input wire logic [3:0] txq_size,
|
||||
input wire logic [63:0] txq_base_addr,
|
||||
input wire logic [15:0] txq_prod,
|
||||
output wire logic [15:0] txq_cons,
|
||||
input wire logic rxq_en,
|
||||
input wire logic [3:0] rxq_size,
|
||||
input wire logic [63:0] rxq_base_addr,
|
||||
input wire logic [15:0] rxq_prod,
|
||||
output wire logic [15:0] rxq_cons,
|
||||
|
||||
input wire logic [1:0] desc_req,
|
||||
taxi_axis_if.src axis_desc[2]
|
||||
);
|
||||
|
||||
localparam RAM_ADDR_W = 16;
|
||||
|
||||
taxi_dma_desc_if #(
|
||||
.SRC_ADDR_W(RAM_ADDR_W),
|
||||
.SRC_SEL_EN(1'b0),
|
||||
.SRC_ASID_EN(1'b0),
|
||||
.DST_ADDR_W(RAM_ADDR_W),
|
||||
.DST_SEL_EN(1'b0),
|
||||
.DST_ASID_EN(1'b0),
|
||||
.IMM_EN(1'b0),
|
||||
.LEN_W(5),
|
||||
.TAG_W(1),
|
||||
.ID_EN(0),
|
||||
.DEST_EN(1),
|
||||
.DEST_W(1),
|
||||
.USER_EN(1),
|
||||
.USER_W(1)
|
||||
) dma_desc();
|
||||
|
||||
localparam [2:0]
|
||||
STATE_IDLE = 0,
|
||||
STATE_READ_DESC = 1,
|
||||
STATE_READ_DATA = 2,
|
||||
STATE_TX_DESC = 3;
|
||||
|
||||
logic [2:0] state_reg = STATE_IDLE;
|
||||
|
||||
logic [1:0] desc_req_reg = '0;
|
||||
|
||||
logic [15:0] txq_cons_ptr_reg = '0;
|
||||
logic [15:0] rxq_cons_ptr_reg = '0;
|
||||
|
||||
assign txq_cons = txq_cons_ptr_reg;
|
||||
assign rxq_cons = rxq_cons_ptr_reg;
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
// axis_desc.tready <= 1'b0;
|
||||
|
||||
dma_rd_desc_req.req_src_sel <= '0;
|
||||
dma_rd_desc_req.req_src_asid <= '0;
|
||||
dma_rd_desc_req.req_dst_sel <= '0;
|
||||
dma_rd_desc_req.req_dst_asid <= '0;
|
||||
dma_rd_desc_req.req_imm <= '0;
|
||||
dma_rd_desc_req.req_imm_en <= '0;
|
||||
dma_rd_desc_req.req_len <= 16;
|
||||
dma_rd_desc_req.req_tag <= '0;
|
||||
dma_rd_desc_req.req_id <= '0;
|
||||
dma_rd_desc_req.req_dest <= '0;
|
||||
dma_rd_desc_req.req_user <= '0;
|
||||
dma_rd_desc_req.req_valid <= dma_rd_desc_req.req_valid && !dma_rd_desc_req.req_ready;
|
||||
|
||||
dma_desc.req_src_sel <= '0;
|
||||
dma_desc.req_src_asid <= '0;
|
||||
dma_desc.req_dst_addr <= '0;
|
||||
dma_desc.req_dst_sel <= '0;
|
||||
dma_desc.req_dst_asid <= '0;
|
||||
dma_desc.req_imm <= '0;
|
||||
dma_desc.req_imm_en <= '0;
|
||||
dma_desc.req_len <= 16;
|
||||
dma_desc.req_tag <= '0;
|
||||
dma_desc.req_id <= '0;
|
||||
dma_desc.req_user <= '0;
|
||||
dma_desc.req_valid <= dma_desc.req_valid && !dma_desc.req_ready;
|
||||
|
||||
desc_req_reg <= desc_req_reg | desc_req;
|
||||
|
||||
if (!txq_en) begin
|
||||
txq_cons_ptr_reg <= '0;
|
||||
end
|
||||
|
||||
if (!rxq_en) begin
|
||||
rxq_cons_ptr_reg <= '0;
|
||||
end
|
||||
|
||||
case (state_reg)
|
||||
STATE_IDLE: begin
|
||||
if (desc_req_reg[1]) begin
|
||||
dma_rd_desc_req.req_src_addr <= rxq_base_addr + 64'(16'(rxq_cons_ptr_reg & ({16{1'b1}} >> (16 - rxq_size))) * 16);
|
||||
dma_desc.req_dest <= 1'b1;
|
||||
desc_req_reg[1] <= 1'b0;
|
||||
if (rxq_cons_ptr_reg == rxq_prod || !rxq_en) begin
|
||||
dma_desc.req_user <= 1'b1;
|
||||
dma_desc.req_valid <= 1'b1;
|
||||
state_reg <= STATE_TX_DESC;
|
||||
end else begin
|
||||
dma_desc.req_user <= 1'b0;
|
||||
dma_rd_desc_req.req_valid <= 1'b1;
|
||||
rxq_cons_ptr_reg <= rxq_cons_ptr_reg + 1;
|
||||
state_reg <= STATE_READ_DESC;
|
||||
end
|
||||
end else if (desc_req_reg[0]) begin
|
||||
dma_rd_desc_req.req_src_addr <= txq_base_addr + 64'(16'(txq_cons_ptr_reg & ({16{1'b1}} >> (16 - txq_size))) * 16);
|
||||
dma_desc.req_dest <= 1'b0;
|
||||
desc_req_reg[0] <= 1'b0;
|
||||
if (txq_cons_ptr_reg == txq_prod || !txq_en) begin
|
||||
dma_desc.req_user <= 1'b1;
|
||||
dma_desc.req_valid <= 1'b1;
|
||||
state_reg <= STATE_TX_DESC;
|
||||
end else begin
|
||||
dma_desc.req_user <= 1'b0;
|
||||
dma_rd_desc_req.req_valid <= 1'b1;
|
||||
txq_cons_ptr_reg <= txq_cons_ptr_reg + 1;
|
||||
state_reg <= STATE_READ_DESC;
|
||||
end
|
||||
end
|
||||
end
|
||||
STATE_READ_DESC: begin
|
||||
if (dma_rd_desc_sts.sts_valid) begin
|
||||
dma_desc.req_valid <= 1'b1;
|
||||
state_reg <= STATE_TX_DESC;
|
||||
end
|
||||
end
|
||||
STATE_TX_DESC: begin
|
||||
if (dma_desc.sts_valid) begin
|
||||
state_reg <= STATE_IDLE;
|
||||
end
|
||||
end
|
||||
default: begin
|
||||
state_reg <= STATE_IDLE;
|
||||
end
|
||||
endcase
|
||||
|
||||
if (rst) begin
|
||||
state_reg <= STATE_IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
taxi_dma_ram_if #(
|
||||
.SEGS(dma_ram_wr.SEGS),
|
||||
.SEG_ADDR_W(dma_ram_wr.SEG_ADDR_W),
|
||||
.SEG_DATA_W(dma_ram_wr.SEG_DATA_W),
|
||||
.SEG_BE_W(dma_ram_wr.SEG_BE_W)
|
||||
) dma_ram_rd();
|
||||
|
||||
taxi_dma_psdpram #(
|
||||
.SIZE(1024),
|
||||
.PIPELINE(2)
|
||||
)
|
||||
ram_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* Write port
|
||||
*/
|
||||
.dma_ram_wr(dma_ram_wr),
|
||||
|
||||
/*
|
||||
* Read port
|
||||
*/
|
||||
.dma_ram_rd(dma_ram_rd)
|
||||
);
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(axis_desc[0].DATA_W),
|
||||
.KEEP_EN(axis_desc[0].KEEP_EN),
|
||||
.KEEP_W(axis_desc[0].KEEP_W),
|
||||
.LAST_EN(axis_desc[0].LAST_EN),
|
||||
.ID_EN(axis_desc[0].ID_EN),
|
||||
.ID_W(axis_desc[0].ID_W),
|
||||
.DEST_EN(1),
|
||||
.DEST_W(1),
|
||||
.USER_EN(axis_desc[0].USER_EN),
|
||||
.USER_W(axis_desc[0].USER_W)
|
||||
) m_axis_rd_data();
|
||||
|
||||
taxi_dma_client_axis_source
|
||||
dma_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* DMA descriptor
|
||||
*/
|
||||
.desc_req(dma_desc),
|
||||
.desc_sts(dma_desc),
|
||||
|
||||
/*
|
||||
* AXI stream read data output
|
||||
*/
|
||||
.m_axis_rd_data(m_axis_rd_data),
|
||||
|
||||
/*
|
||||
* RAM interface
|
||||
*/
|
||||
.dma_ram_rd(dma_ram_rd),
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
*/
|
||||
.enable(1'b1)
|
||||
);
|
||||
|
||||
taxi_axis_demux #(
|
||||
.M_COUNT(2),
|
||||
.TDEST_ROUTE(1)
|
||||
)
|
||||
demux_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* AXI4-Stream input (sink)
|
||||
*/
|
||||
.s_axis(m_axis_rd_data),
|
||||
|
||||
/*
|
||||
* AXI4-Stream output (source)
|
||||
*/
|
||||
.m_axis(axis_desc),
|
||||
|
||||
/*
|
||||
* Control
|
||||
*/
|
||||
.enable(1'b1),
|
||||
.drop(1'b0),
|
||||
.select('0)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
5
src/cndm/rtl/cndm_micro_pcie_us.f
Normal file
5
src/cndm/rtl/cndm_micro_pcie_us.f
Normal file
@@ -0,0 +1,5 @@
|
||||
cndm_micro_pcie_us.sv
|
||||
cndm_micro_core.f
|
||||
../lib/taxi/src/pcie/rtl/taxi_pcie_us_axil_master.sv
|
||||
../lib/taxi/src/pcie/rtl/taxi_pcie_us_msi.sv
|
||||
../lib/taxi/src/dma/rtl/taxi_dma_if_pcie_us.f
|
||||
465
src/cndm/rtl/cndm_micro_pcie_us.sv
Normal file
465
src/cndm/rtl/cndm_micro_pcie_us.sv
Normal file
@@ -0,0 +1,465 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* Corundum-micro core logic for UltraScale PCIe
|
||||
*/
|
||||
module cndm_micro_pcie_us #(
|
||||
// simulation (set to avoid vendor primitives)
|
||||
parameter logic SIM = 1'b0,
|
||||
// vendor ("GENERIC", "XILINX", "ALTERA")
|
||||
parameter string VENDOR = "XILINX",
|
||||
// device family
|
||||
parameter string FAMILY = "virtexuplus",
|
||||
parameter PORTS = 2,
|
||||
parameter RQ_SEQ_NUM_W = 6,
|
||||
parameter BAR0_APERTURE = 24
|
||||
)
|
||||
(
|
||||
/*
|
||||
* PCIe
|
||||
*/
|
||||
input wire logic pcie_clk,
|
||||
input wire logic pcie_rst,
|
||||
taxi_axis_if.snk s_axis_pcie_cq,
|
||||
taxi_axis_if.src m_axis_pcie_cc,
|
||||
taxi_axis_if.src m_axis_pcie_rq,
|
||||
taxi_axis_if.snk s_axis_pcie_rc,
|
||||
|
||||
input wire [RQ_SEQ_NUM_W-1:0] pcie_rq_seq_num0,
|
||||
input wire pcie_rq_seq_num_vld0,
|
||||
input wire [RQ_SEQ_NUM_W-1:0] pcie_rq_seq_num1,
|
||||
input wire pcie_rq_seq_num_vld1,
|
||||
|
||||
input wire [2:0] cfg_max_payload,
|
||||
input wire [2:0] cfg_max_read_req,
|
||||
input wire [3:0] cfg_rcb_status,
|
||||
|
||||
output wire [9:0] cfg_mgmt_addr,
|
||||
output wire [7:0] cfg_mgmt_function_number,
|
||||
output wire cfg_mgmt_write,
|
||||
output wire [31:0] cfg_mgmt_write_data,
|
||||
output wire [3:0] cfg_mgmt_byte_enable,
|
||||
output wire cfg_mgmt_read,
|
||||
input wire [31:0] cfg_mgmt_read_data,
|
||||
input wire cfg_mgmt_read_write_done,
|
||||
|
||||
input wire [7:0] cfg_fc_ph,
|
||||
input wire [11:0] cfg_fc_pd,
|
||||
input wire [7:0] cfg_fc_nph,
|
||||
input wire [11:0] cfg_fc_npd,
|
||||
input wire [7:0] cfg_fc_cplh,
|
||||
input wire [11:0] cfg_fc_cpld,
|
||||
output wire [2:0] cfg_fc_sel,
|
||||
|
||||
input wire [3:0] cfg_interrupt_msi_enable,
|
||||
input wire [11:0] cfg_interrupt_msi_mmenable,
|
||||
input wire cfg_interrupt_msi_mask_update,
|
||||
input wire [31:0] cfg_interrupt_msi_data,
|
||||
output wire [1:0] cfg_interrupt_msi_select,
|
||||
output wire [31:0] cfg_interrupt_msi_int,
|
||||
output wire [31:0] cfg_interrupt_msi_pending_status,
|
||||
output wire cfg_interrupt_msi_pending_status_data_enable,
|
||||
output wire [1:0] cfg_interrupt_msi_pending_status_function_num,
|
||||
input wire cfg_interrupt_msi_sent,
|
||||
input wire cfg_interrupt_msi_fail,
|
||||
output wire [2:0] cfg_interrupt_msi_attr,
|
||||
output wire cfg_interrupt_msi_tph_present,
|
||||
output wire [1:0] cfg_interrupt_msi_tph_type,
|
||||
output wire [7:0] cfg_interrupt_msi_tph_st_tag,
|
||||
output wire [7:0] cfg_interrupt_msi_function_number,
|
||||
|
||||
/*
|
||||
* Ethernet
|
||||
*/
|
||||
input wire logic mac_tx_clk[PORTS],
|
||||
input wire logic mac_tx_rst[PORTS],
|
||||
taxi_axis_if.src mac_axis_tx[PORTS],
|
||||
taxi_axis_if.snk mac_axis_tx_cpl[PORTS],
|
||||
|
||||
input wire logic mac_rx_clk[PORTS],
|
||||
input wire logic mac_rx_rst[PORTS],
|
||||
taxi_axis_if.snk mac_axis_rx[PORTS]
|
||||
);
|
||||
|
||||
localparam CL_PORTS = $clog2(PORTS);
|
||||
|
||||
localparam AXIL_DATA_W = 32;
|
||||
localparam AXIL_ADDR_W = BAR0_APERTURE;
|
||||
|
||||
taxi_axil_if #(
|
||||
.DATA_W(AXIL_DATA_W),
|
||||
.ADDR_W(AXIL_ADDR_W),
|
||||
.AWUSER_EN(1'b0),
|
||||
.WUSER_EN(1'b0),
|
||||
.BUSER_EN(1'b0),
|
||||
.ARUSER_EN(1'b0),
|
||||
.RUSER_EN(1'b0)
|
||||
) axil_ctrl_bar();
|
||||
|
||||
taxi_pcie_us_axil_master
|
||||
pcie_axil_master_inst (
|
||||
.clk(pcie_clk),
|
||||
.rst(pcie_rst),
|
||||
|
||||
/*
|
||||
* UltraScale PCIe interface
|
||||
*/
|
||||
.s_axis_cq(s_axis_pcie_cq),
|
||||
.m_axis_cc(m_axis_pcie_cc),
|
||||
|
||||
/*
|
||||
* AXI Lite Master output
|
||||
*/
|
||||
.m_axil_wr(axil_ctrl_bar),
|
||||
.m_axil_rd(axil_ctrl_bar),
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
*/
|
||||
.completer_id('0),
|
||||
.completer_id_en(1'b0),
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
.stat_err_cor(),
|
||||
.stat_err_uncor()
|
||||
);
|
||||
|
||||
localparam AXIS_PCIE_DATA_W = m_axis_pcie_rq.DATA_W;
|
||||
|
||||
localparam PCIE_ADDR_W = 64;
|
||||
|
||||
// TODO
|
||||
localparam logic RQ_SEQ_NUM_EN = 1'b1;
|
||||
localparam RAM_SEL_W = 2+CL_PORTS;
|
||||
localparam RAM_ADDR_W = 16;
|
||||
localparam RAM_SEGS = 2;//AXIS_PCIE_DATA_W > 256 ? AXIS_PCIE_DATA_W / 128 : 2;
|
||||
localparam PCIE_TAG_CNT = 64;//AXIS_PCIE_RQ_USER_W == 60 ? 64 : 256,
|
||||
localparam logic IMM_EN = 1'b0;
|
||||
localparam IMM_W = 32;
|
||||
localparam LEN_W = 20;
|
||||
localparam TAG_W = 8;
|
||||
localparam RD_OP_TBL_SIZE = PCIE_TAG_CNT;
|
||||
localparam RD_TX_LIMIT = 2**(RQ_SEQ_NUM_W-1);
|
||||
localparam logic RD_TX_FC_EN = 1'b1;
|
||||
localparam RD_CPLH_FC_LIMIT = 512;
|
||||
localparam RD_CPLD_FC_LIMIT = RD_CPLH_FC_LIMIT*4;
|
||||
localparam WR_OP_TBL_SIZE = 2**(RQ_SEQ_NUM_W-1);
|
||||
localparam WR_TX_LIMIT = 2**(RQ_SEQ_NUM_W-1);
|
||||
localparam logic WR_TX_FC_EN = 1'b1;
|
||||
|
||||
localparam RAM_DATA_W = AXIS_PCIE_DATA_W*2;
|
||||
localparam RAM_SEG_DATA_W = RAM_DATA_W / RAM_SEGS;
|
||||
localparam RAM_SEG_BE_W = RAM_SEG_DATA_W / 8;
|
||||
localparam RAM_SEG_ADDR_W = RAM_ADDR_W - $clog2(RAM_SEGS*RAM_SEG_BE_W);
|
||||
|
||||
logic [RQ_SEQ_NUM_W-1:0] s_axis_rq_seq_num_0;
|
||||
logic s_axis_rq_seq_num_valid_0;
|
||||
logic [RQ_SEQ_NUM_W-1:0] s_axis_rq_seq_num_1;
|
||||
logic s_axis_rq_seq_num_valid_1;
|
||||
|
||||
logic [7:0] pcie_tx_fc_nph_av;
|
||||
logic [7:0] pcie_tx_fc_ph_av;
|
||||
logic [11:0] pcie_tx_fc_pd_av;
|
||||
|
||||
assign cfg_fc_sel = 3'b100;
|
||||
|
||||
taxi_dma_desc_if #(
|
||||
.SRC_ADDR_W(PCIE_ADDR_W),
|
||||
.SRC_SEL_EN(1'b0),
|
||||
.SRC_ASID_EN(1'b0),
|
||||
.DST_ADDR_W(RAM_ADDR_W),
|
||||
.DST_SEL_EN(1'b1),
|
||||
.DST_SEL_W(RAM_SEL_W),
|
||||
.DST_ASID_EN(1'b0),
|
||||
.IMM_EN(1'b0),
|
||||
.LEN_W(LEN_W),
|
||||
.TAG_W(TAG_W),
|
||||
.ID_EN(1'b0),
|
||||
.DEST_EN(1'b0),
|
||||
.USER_EN(1'b0)
|
||||
) dma_rd_desc();
|
||||
|
||||
taxi_dma_desc_if #(
|
||||
.SRC_ADDR_W(RAM_ADDR_W),
|
||||
.SRC_SEL_EN(1'b1),
|
||||
.SRC_SEL_W(RAM_SEL_W),
|
||||
.SRC_ASID_EN(1'b0),
|
||||
.DST_ADDR_W(PCIE_ADDR_W),
|
||||
.DST_SEL_EN(1'b0),
|
||||
.DST_ASID_EN(1'b0),
|
||||
.IMM_EN(IMM_EN),
|
||||
.IMM_W(IMM_W),
|
||||
.LEN_W(LEN_W),
|
||||
.TAG_W(TAG_W),
|
||||
.ID_EN(1'b0),
|
||||
.DEST_EN(1'b0),
|
||||
.USER_EN(1'b0)
|
||||
) dma_wr_desc();
|
||||
|
||||
taxi_dma_ram_if #(
|
||||
.SEGS(RAM_SEGS),
|
||||
.SEG_ADDR_W(RAM_SEG_ADDR_W),
|
||||
.SEG_DATA_W(RAM_SEG_DATA_W),
|
||||
.SEG_BE_W(RAM_SEG_BE_W),
|
||||
.SEL_W(RAM_SEL_W)
|
||||
) dma_ram();
|
||||
|
||||
logic stat_rd_busy;
|
||||
logic stat_wr_busy;
|
||||
logic stat_err_cor;
|
||||
logic stat_err_uncor;
|
||||
|
||||
logic [$clog2(RD_OP_TBL_SIZE)-1:0] stat_rd_op_start_tag;
|
||||
logic stat_rd_op_start_valid;
|
||||
logic [$clog2(RD_OP_TBL_SIZE)-1:0] stat_rd_op_finish_tag;
|
||||
logic [3:0] stat_rd_op_finish_status;
|
||||
logic stat_rd_op_finish_valid;
|
||||
logic [$clog2(PCIE_TAG_CNT)-1:0] stat_rd_req_start_tag;
|
||||
logic [12:0] stat_rd_req_start_len;
|
||||
logic stat_rd_req_start_valid;
|
||||
logic [$clog2(PCIE_TAG_CNT)-1:0] stat_rd_req_finish_tag;
|
||||
logic [3:0] stat_rd_req_finish_status;
|
||||
logic stat_rd_req_finish_valid;
|
||||
logic stat_rd_req_timeout;
|
||||
logic stat_rd_op_tbl_full;
|
||||
logic stat_rd_no_tags;
|
||||
logic stat_rd_tx_limit;
|
||||
logic stat_rd_tx_stall;
|
||||
logic [$clog2(WR_OP_TBL_SIZE)-1:0] stat_wr_op_start_tag;
|
||||
logic stat_wr_op_start_valid;
|
||||
logic [$clog2(WR_OP_TBL_SIZE)-1:0] stat_wr_op_finish_tag;
|
||||
logic [3:0] stat_wr_op_finish_status;
|
||||
logic stat_wr_op_finish_valid;
|
||||
logic [$clog2(WR_OP_TBL_SIZE)-1:0] stat_wr_req_start_tag;
|
||||
logic [12:0] stat_wr_req_start_len;
|
||||
logic stat_wr_req_start_valid;
|
||||
logic [$clog2(WR_OP_TBL_SIZE)-1:0] stat_wr_req_finish_tag;
|
||||
logic [3:0] stat_wr_req_finish_status;
|
||||
logic stat_wr_req_finish_valid;
|
||||
logic stat_wr_op_tbl_full;
|
||||
logic stat_wr_tx_limit;
|
||||
logic stat_wr_tx_stall;
|
||||
|
||||
// register to break timing path from PCIe HIP 500 MHz clock domain
|
||||
logic [RQ_SEQ_NUM_W-1:0] pcie_rq_seq_num0_reg = '0;
|
||||
logic pcie_rq_seq_num_vld0_reg = 1'b0;
|
||||
logic [RQ_SEQ_NUM_W-1:0] pcie_rq_seq_num1_reg = '0;
|
||||
logic pcie_rq_seq_num_vld1_reg = 1'b0;
|
||||
|
||||
always_ff @(posedge pcie_clk) begin
|
||||
pcie_rq_seq_num0_reg <= pcie_rq_seq_num0;
|
||||
pcie_rq_seq_num_vld0_reg <= pcie_rq_seq_num_vld0;
|
||||
pcie_rq_seq_num1_reg <= pcie_rq_seq_num1;
|
||||
pcie_rq_seq_num_vld1_reg <= pcie_rq_seq_num_vld1;
|
||||
|
||||
if (pcie_rst) begin
|
||||
pcie_rq_seq_num_vld0_reg <= 1'b0;
|
||||
pcie_rq_seq_num_vld1_reg <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
taxi_dma_if_pcie_us #(
|
||||
.RQ_SEQ_NUM_W(RQ_SEQ_NUM_W),
|
||||
.RQ_SEQ_NUM_EN(RQ_SEQ_NUM_EN),
|
||||
.PCIE_TAG_CNT(PCIE_TAG_CNT),
|
||||
.RD_OP_TBL_SIZE(RD_OP_TBL_SIZE),
|
||||
.RD_TX_LIMIT(RD_TX_LIMIT),
|
||||
.RD_TX_FC_EN(RD_TX_FC_EN),
|
||||
.RD_CPLH_FC_LIMIT(RD_CPLH_FC_LIMIT),
|
||||
.RD_CPLD_FC_LIMIT(RD_CPLD_FC_LIMIT),
|
||||
.WR_OP_TBL_SIZE(WR_OP_TBL_SIZE),
|
||||
.WR_TX_LIMIT(WR_TX_LIMIT),
|
||||
.WR_TX_FC_EN(WR_TX_FC_EN)
|
||||
)
|
||||
dma_if_inst (
|
||||
.clk(pcie_clk),
|
||||
.rst(pcie_rst),
|
||||
|
||||
/*
|
||||
* UltraScale PCIe interface
|
||||
*/
|
||||
.m_axis_rq(m_axis_pcie_rq),
|
||||
.s_axis_rc(s_axis_pcie_rc),
|
||||
|
||||
/*
|
||||
* Transmit sequence number input
|
||||
*/
|
||||
.s_axis_rq_seq_num_0(pcie_rq_seq_num0_reg),
|
||||
.s_axis_rq_seq_num_valid_0(pcie_rq_seq_num_vld0_reg),
|
||||
.s_axis_rq_seq_num_1(pcie_rq_seq_num1_reg),
|
||||
.s_axis_rq_seq_num_valid_1(pcie_rq_seq_num_vld1_reg),
|
||||
|
||||
/*
|
||||
* Transmit flow control
|
||||
*/
|
||||
.pcie_tx_fc_nph_av(cfg_fc_nph),
|
||||
.pcie_tx_fc_ph_av(cfg_fc_ph),
|
||||
.pcie_tx_fc_pd_av(cfg_fc_pd),
|
||||
|
||||
/*
|
||||
* Read descriptor
|
||||
*/
|
||||
.rd_desc_req(dma_rd_desc),
|
||||
.rd_desc_sts(dma_rd_desc),
|
||||
|
||||
/*
|
||||
* Write descriptor
|
||||
*/
|
||||
.wr_desc_req(dma_wr_desc),
|
||||
.wr_desc_sts(dma_wr_desc),
|
||||
|
||||
/*
|
||||
* RAM interface
|
||||
*/
|
||||
.dma_ram_wr(dma_ram),
|
||||
.dma_ram_rd(dma_ram),
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
*/
|
||||
.read_enable(1'b1),
|
||||
.write_enable(1'b1),
|
||||
.ext_tag_en(1'b0), // TODO
|
||||
.rcb_128b(cfg_rcb_status[0]),
|
||||
.requester_id('0),
|
||||
.requester_id_en(1'b0),
|
||||
.max_rd_req_size(cfg_max_read_req),
|
||||
.max_payload_size(cfg_max_payload),
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
.stat_rd_busy(stat_rd_busy),
|
||||
.stat_wr_busy(stat_wr_busy),
|
||||
.stat_err_cor(stat_err_cor),
|
||||
.stat_err_uncor(stat_err_uncor),
|
||||
|
||||
/*
|
||||
* Statistics
|
||||
*/
|
||||
.stat_rd_op_start_tag(stat_rd_op_start_tag),
|
||||
.stat_rd_op_start_valid(stat_rd_op_start_valid),
|
||||
.stat_rd_op_finish_tag(stat_rd_op_finish_tag),
|
||||
.stat_rd_op_finish_status(stat_rd_op_finish_status),
|
||||
.stat_rd_op_finish_valid(stat_rd_op_finish_valid),
|
||||
.stat_rd_req_start_tag(stat_rd_req_start_tag),
|
||||
.stat_rd_req_start_len(stat_rd_req_start_len),
|
||||
.stat_rd_req_start_valid(stat_rd_req_start_valid),
|
||||
.stat_rd_req_finish_tag(stat_rd_req_finish_tag),
|
||||
.stat_rd_req_finish_status(stat_rd_req_finish_status),
|
||||
.stat_rd_req_finish_valid(stat_rd_req_finish_valid),
|
||||
.stat_rd_req_timeout(stat_rd_req_timeout),
|
||||
.stat_rd_op_tbl_full(stat_rd_op_tbl_full),
|
||||
.stat_rd_no_tags(stat_rd_no_tags),
|
||||
.stat_rd_tx_limit(stat_rd_tx_limit),
|
||||
.stat_rd_tx_stall(stat_rd_tx_stall),
|
||||
.stat_wr_op_start_tag(stat_wr_op_start_tag),
|
||||
.stat_wr_op_start_valid(stat_wr_op_start_valid),
|
||||
.stat_wr_op_finish_tag(stat_wr_op_finish_tag),
|
||||
.stat_wr_op_finish_status(stat_wr_op_finish_status),
|
||||
.stat_wr_op_finish_valid(stat_wr_op_finish_valid),
|
||||
.stat_wr_req_start_tag(stat_wr_req_start_tag),
|
||||
.stat_wr_req_start_len(stat_wr_req_start_len),
|
||||
.stat_wr_req_start_valid(stat_wr_req_start_valid),
|
||||
.stat_wr_req_finish_tag(stat_wr_req_finish_tag),
|
||||
.stat_wr_req_finish_status(stat_wr_req_finish_status),
|
||||
.stat_wr_req_finish_valid(stat_wr_req_finish_valid),
|
||||
.stat_wr_op_tbl_full(stat_wr_op_tbl_full),
|
||||
.stat_wr_tx_limit(stat_wr_tx_limit),
|
||||
.stat_wr_tx_stall(stat_wr_tx_stall)
|
||||
);
|
||||
|
||||
wire [PORTS-1:0] irq;
|
||||
wire [31:0] msi_irq = 32'(irq);
|
||||
|
||||
taxi_pcie_us_msi #(
|
||||
.MSI_CNT(32)
|
||||
)
|
||||
msi_inst (
|
||||
.clk(pcie_clk),
|
||||
.rst(pcie_rst),
|
||||
|
||||
/*
|
||||
* Interrupt request inputs
|
||||
*/
|
||||
.msi_irq(msi_irq),
|
||||
|
||||
/*
|
||||
* Interface to UltraScale PCIe IP core
|
||||
*/
|
||||
/* verilator lint_off WIDTHEXPAND */
|
||||
.cfg_interrupt_msi_enable(cfg_interrupt_msi_enable),
|
||||
.cfg_interrupt_msi_vf_enable(),
|
||||
.cfg_interrupt_msi_mmenable(cfg_interrupt_msi_mmenable),
|
||||
.cfg_interrupt_msi_mask_update(cfg_interrupt_msi_mask_update),
|
||||
.cfg_interrupt_msi_data(cfg_interrupt_msi_data),
|
||||
.cfg_interrupt_msi_select(cfg_interrupt_msi_select),
|
||||
.cfg_interrupt_msi_int(cfg_interrupt_msi_int),
|
||||
.cfg_interrupt_msi_pending_status(cfg_interrupt_msi_pending_status),
|
||||
.cfg_interrupt_msi_pending_status_data_enable(cfg_interrupt_msi_pending_status_data_enable),
|
||||
.cfg_interrupt_msi_pending_status_function_num(cfg_interrupt_msi_pending_status_function_num),
|
||||
.cfg_interrupt_msi_sent(cfg_interrupt_msi_sent),
|
||||
.cfg_interrupt_msi_fail(cfg_interrupt_msi_fail),
|
||||
.cfg_interrupt_msi_attr(cfg_interrupt_msi_attr),
|
||||
.cfg_interrupt_msi_tph_present(cfg_interrupt_msi_tph_present),
|
||||
.cfg_interrupt_msi_tph_type(cfg_interrupt_msi_tph_type),
|
||||
.cfg_interrupt_msi_tph_st_tag(cfg_interrupt_msi_tph_st_tag),
|
||||
.cfg_interrupt_msi_function_number(cfg_interrupt_msi_function_number)
|
||||
/* verilator lint_on WIDTHEXPAND */
|
||||
);
|
||||
|
||||
cndm_micro_core #(
|
||||
.PORTS(PORTS)
|
||||
)
|
||||
core_inst (
|
||||
.clk(pcie_clk),
|
||||
.rst(pcie_rst),
|
||||
|
||||
/*
|
||||
* Control register interface
|
||||
*/
|
||||
.s_axil_wr(axil_ctrl_bar),
|
||||
.s_axil_rd(axil_ctrl_bar),
|
||||
|
||||
/*
|
||||
* DMA
|
||||
*/
|
||||
.dma_rd_desc_req(dma_rd_desc),
|
||||
.dma_rd_desc_sts(dma_rd_desc),
|
||||
.dma_wr_desc_req(dma_wr_desc),
|
||||
.dma_wr_desc_sts(dma_wr_desc),
|
||||
.dma_ram_wr(dma_ram),
|
||||
.dma_ram_rd(dma_ram),
|
||||
|
||||
.irq(irq),
|
||||
|
||||
/*
|
||||
* Ethernet
|
||||
*/
|
||||
.mac_tx_clk(mac_tx_clk),
|
||||
.mac_tx_rst(mac_tx_rst),
|
||||
.mac_axis_tx(mac_axis_tx),
|
||||
.mac_axis_tx_cpl(mac_axis_tx_cpl),
|
||||
|
||||
.mac_rx_clk(mac_rx_clk),
|
||||
.mac_rx_rst(mac_rx_rst),
|
||||
.mac_axis_rx(mac_axis_rx)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
606
src/cndm/rtl/cndm_micro_port.sv
Normal file
606
src/cndm/rtl/cndm_micro_port.sv
Normal file
@@ -0,0 +1,606 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* Corundum-micro port module
|
||||
*/
|
||||
module cndm_micro_port #(
|
||||
parameter logic PTP_TS_EN = 1'b1
|
||||
)
|
||||
(
|
||||
input wire logic clk,
|
||||
input wire logic rst,
|
||||
|
||||
/*
|
||||
* Control register interface
|
||||
*/
|
||||
taxi_axil_if.wr_slv s_axil_wr,
|
||||
taxi_axil_if.rd_slv s_axil_rd,
|
||||
|
||||
/*
|
||||
* DMA
|
||||
*/
|
||||
taxi_dma_desc_if.req_src dma_rd_desc_req,
|
||||
taxi_dma_desc_if.sts_snk dma_rd_desc_sts,
|
||||
taxi_dma_desc_if.req_src dma_wr_desc_req,
|
||||
taxi_dma_desc_if.sts_snk dma_wr_desc_sts,
|
||||
taxi_dma_ram_if.wr_slv dma_ram_wr,
|
||||
taxi_dma_ram_if.rd_slv dma_ram_rd,
|
||||
|
||||
output wire logic irq,
|
||||
|
||||
/*
|
||||
* Ethernet
|
||||
*/
|
||||
input wire logic mac_tx_clk,
|
||||
input wire logic mac_tx_rst,
|
||||
taxi_axis_if.src mac_axis_tx,
|
||||
taxi_axis_if.snk mac_axis_tx_cpl,
|
||||
|
||||
input wire logic mac_rx_clk,
|
||||
input wire logic mac_rx_rst,
|
||||
taxi_axis_if.snk mac_axis_rx
|
||||
);
|
||||
|
||||
localparam AXIL_ADDR_W = s_axil_wr.ADDR_W;
|
||||
localparam AXIL_DATA_W = s_axil_wr.DATA_W;
|
||||
|
||||
localparam RAM_SEGS = dma_ram_wr.SEGS;
|
||||
localparam RAM_SEG_ADDR_W = dma_ram_wr.SEG_ADDR_W;
|
||||
localparam RAM_SEG_DATA_W = dma_ram_wr.SEG_DATA_W;
|
||||
localparam RAM_SEG_BE_W = dma_ram_wr.SEG_BE_W;
|
||||
localparam RAM_SEL_W = dma_ram_wr.SEL_W;
|
||||
|
||||
logic txq_en_reg = '0;
|
||||
logic [3:0] txq_size_reg = '0;
|
||||
logic [63:0] txq_base_addr_reg = '0;
|
||||
logic [15:0] txq_prod_reg = '0;
|
||||
wire [15:0] txq_cons;
|
||||
logic rxq_en_reg = '0;
|
||||
logic [3:0] rxq_size_reg = '0;
|
||||
logic [63:0] rxq_base_addr_reg = '0;
|
||||
logic [15:0] rxq_prod_reg = '0;
|
||||
wire [15:0] rxq_cons;
|
||||
|
||||
logic txcq_en_reg = '0;
|
||||
logic [3:0] txcq_size_reg = '0;
|
||||
logic [63:0] txcq_base_addr_reg = '0;
|
||||
wire [15:0] txcq_prod;
|
||||
logic rxcq_en_reg = '0;
|
||||
logic [3:0] rxcq_size_reg = '0;
|
||||
logic [63:0] rxcq_base_addr_reg = '0;
|
||||
wire [15:0] rxcq_prod;
|
||||
|
||||
logic s_axil_awready_reg = 1'b0;
|
||||
logic s_axil_wready_reg = 1'b0;
|
||||
logic s_axil_bvalid_reg = 1'b0;
|
||||
|
||||
logic s_axil_arready_reg = 1'b0;
|
||||
logic [AXIL_DATA_W-1:0] s_axil_rdata_reg = '0;
|
||||
logic s_axil_rvalid_reg = 1'b0;
|
||||
|
||||
assign s_axil_wr.awready = s_axil_awready_reg;
|
||||
assign s_axil_wr.wready = s_axil_wready_reg;
|
||||
assign s_axil_wr.bresp = '0;
|
||||
assign s_axil_wr.buser = '0;
|
||||
assign s_axil_wr.bvalid = s_axil_bvalid_reg;
|
||||
|
||||
assign s_axil_rd.arready = s_axil_arready_reg;
|
||||
assign s_axil_rd.rdata = s_axil_rdata_reg;
|
||||
assign s_axil_rd.rresp = '0;
|
||||
assign s_axil_rd.ruser = '0;
|
||||
assign s_axil_rd.rvalid = s_axil_rvalid_reg;
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
s_axil_awready_reg <= 1'b0;
|
||||
s_axil_wready_reg <= 1'b0;
|
||||
s_axil_bvalid_reg <= s_axil_bvalid_reg && !s_axil_wr.bready;
|
||||
|
||||
s_axil_arready_reg <= 1'b0;
|
||||
s_axil_rvalid_reg <= s_axil_rvalid_reg && !s_axil_rd.rready;
|
||||
|
||||
if (s_axil_wr.awvalid && s_axil_wr.wvalid && !s_axil_bvalid_reg) begin
|
||||
s_axil_awready_reg <= 1'b1;
|
||||
s_axil_wready_reg <= 1'b1;
|
||||
s_axil_bvalid_reg <= 1'b1;
|
||||
|
||||
case ({s_axil_wr.awaddr[15:2], 2'b00})
|
||||
16'h0100: begin
|
||||
txq_en_reg <= s_axil_wr.wdata[0];
|
||||
txq_size_reg <= s_axil_wr.wdata[19:16];
|
||||
end
|
||||
16'h0104: txq_prod_reg <= s_axil_wr.wdata[15:0];
|
||||
16'h0108: txq_base_addr_reg[31:0] <= s_axil_wr.wdata;
|
||||
16'h010c: txq_base_addr_reg[63:32] <= s_axil_wr.wdata;
|
||||
|
||||
16'h0200: begin
|
||||
rxq_en_reg <= s_axil_wr.wdata[0];
|
||||
rxq_size_reg <= s_axil_wr.wdata[19:16];
|
||||
end
|
||||
16'h0204: rxq_prod_reg <= s_axil_wr.wdata[15:0];
|
||||
16'h0208: rxq_base_addr_reg[31:0] <= s_axil_wr.wdata;
|
||||
16'h020c: rxq_base_addr_reg[63:32] <= s_axil_wr.wdata;
|
||||
|
||||
16'h0300: begin
|
||||
txcq_en_reg <= s_axil_wr.wdata[0];
|
||||
txcq_size_reg <= s_axil_wr.wdata[19:16];
|
||||
end
|
||||
16'h0308: txcq_base_addr_reg[31:0] <= s_axil_wr.wdata;
|
||||
16'h030c: txcq_base_addr_reg[63:32] <= s_axil_wr.wdata;
|
||||
|
||||
16'h0400: begin
|
||||
rxcq_en_reg <= s_axil_wr.wdata[0];
|
||||
rxcq_size_reg <= s_axil_wr.wdata[19:16];
|
||||
end
|
||||
16'h0408: rxcq_base_addr_reg[31:0] <= s_axil_wr.wdata;
|
||||
16'h040c: rxcq_base_addr_reg[63:32] <= s_axil_wr.wdata;
|
||||
default: begin end
|
||||
endcase
|
||||
end
|
||||
|
||||
if (s_axil_rd.arvalid && !s_axil_rvalid_reg) begin
|
||||
s_axil_rdata_reg <= '0;
|
||||
|
||||
s_axil_arready_reg <= 1'b1;
|
||||
s_axil_rvalid_reg <= 1'b1;
|
||||
|
||||
case ({s_axil_rd.araddr[15:2], 2'b00})
|
||||
16'h0100: begin
|
||||
s_axil_rdata_reg[0] <= txq_en_reg;
|
||||
s_axil_rdata_reg[19:16] <= txq_size_reg;
|
||||
end
|
||||
16'h0104: begin
|
||||
s_axil_rdata_reg[15:0] <= txq_prod_reg;
|
||||
s_axil_rdata_reg[31:16] <= txq_cons;
|
||||
end
|
||||
16'h0108: s_axil_rdata_reg <= txq_base_addr_reg[31:0];
|
||||
16'h010c: s_axil_rdata_reg <= txq_base_addr_reg[63:32];
|
||||
|
||||
16'h0200: begin
|
||||
s_axil_rdata_reg[0] <= rxq_en_reg;
|
||||
s_axil_rdata_reg[19:16] <= rxq_size_reg;
|
||||
end
|
||||
16'h0204: begin
|
||||
s_axil_rdata_reg[15:0] <= rxq_prod_reg;
|
||||
s_axil_rdata_reg[31:16] <= rxq_cons;
|
||||
end
|
||||
16'h0208: s_axil_rdata_reg <= rxq_base_addr_reg[31:0];
|
||||
16'h020c: s_axil_rdata_reg <= rxq_base_addr_reg[63:32];
|
||||
|
||||
16'h0300: begin
|
||||
s_axil_rdata_reg[0] <= txcq_en_reg;
|
||||
s_axil_rdata_reg[19:16] <= txcq_size_reg;
|
||||
end
|
||||
16'h0304: s_axil_rdata_reg[15:0] <= txcq_prod;
|
||||
16'h0308: s_axil_rdata_reg <= txcq_base_addr_reg[31:0];
|
||||
16'h030c: s_axil_rdata_reg <= txcq_base_addr_reg[63:32];
|
||||
|
||||
16'h0400: begin
|
||||
s_axil_rdata_reg[0] <= rxcq_en_reg;
|
||||
s_axil_rdata_reg[19:16] <= rxcq_size_reg;
|
||||
end
|
||||
16'h0404: s_axil_rdata_reg[15:0] <= rxcq_prod;
|
||||
16'h0408: s_axil_rdata_reg <= rxcq_base_addr_reg[31:0];
|
||||
16'h040c: s_axil_rdata_reg <= rxcq_base_addr_reg[63:32];
|
||||
default: begin end
|
||||
endcase
|
||||
end
|
||||
|
||||
if (rst) begin
|
||||
s_axil_awready_reg <= 1'b0;
|
||||
s_axil_wready_reg <= 1'b0;
|
||||
s_axil_bvalid_reg <= 1'b0;
|
||||
|
||||
s_axil_arready_reg <= 1'b0;
|
||||
s_axil_rvalid_reg <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
taxi_dma_desc_if #(
|
||||
.SRC_ADDR_W(dma_rd_desc_req.SRC_ADDR_W),
|
||||
.SRC_SEL_EN(dma_rd_desc_req.SRC_SEL_EN),
|
||||
.SRC_SEL_W(dma_rd_desc_req.SRC_SEL_W),
|
||||
.SRC_ASID_EN(dma_rd_desc_req.SRC_ASID_EN),
|
||||
.DST_ADDR_W(dma_rd_desc_req.DST_ADDR_W),
|
||||
.DST_SEL_EN(dma_rd_desc_req.DST_SEL_EN),
|
||||
.DST_SEL_W(dma_rd_desc_req.DST_SEL_W-1),
|
||||
.DST_ASID_EN(dma_rd_desc_req.DST_ASID_EN),
|
||||
.IMM_EN(dma_rd_desc_req.IMM_EN),
|
||||
.LEN_W(dma_rd_desc_req.LEN_W),
|
||||
.TAG_W(dma_rd_desc_req.TAG_W-1),
|
||||
.ID_EN(dma_rd_desc_req.ID_EN),
|
||||
.DEST_EN(dma_rd_desc_req.DEST_EN),
|
||||
.USER_EN(dma_rd_desc_req.USER_EN)
|
||||
) dma_rd_desc_int[2]();
|
||||
|
||||
taxi_dma_ram_if #(
|
||||
.SEGS(RAM_SEGS),
|
||||
.SEG_ADDR_W(RAM_SEG_ADDR_W),
|
||||
.SEG_DATA_W(RAM_SEG_DATA_W),
|
||||
.SEG_BE_W(RAM_SEG_BE_W),
|
||||
.SEL_W(RAM_SEL_W-1)
|
||||
) dma_ram_wr_int[2]();
|
||||
|
||||
taxi_dma_if_mux_rd #(
|
||||
.PORTS(2),
|
||||
.ARB_ROUND_ROBIN(1),
|
||||
.ARB_LSB_HIGH_PRIO(1)
|
||||
)
|
||||
rd_dma_mux_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* DMA descriptors from clients
|
||||
*/
|
||||
.client_req(dma_rd_desc_int),
|
||||
.client_sts(dma_rd_desc_int),
|
||||
|
||||
/*
|
||||
* DMA descriptors to DMA engines
|
||||
*/
|
||||
.dma_req(dma_rd_desc_req),
|
||||
.dma_sts(dma_rd_desc_sts),
|
||||
|
||||
/*
|
||||
* RAM interface (from DMA interface)
|
||||
*/
|
||||
.dma_ram_wr(dma_ram_wr),
|
||||
|
||||
/*
|
||||
* RAM interface (towards RAM)
|
||||
*/
|
||||
.client_ram_wr(dma_ram_wr_int)
|
||||
);
|
||||
|
||||
taxi_dma_desc_if #(
|
||||
.SRC_ADDR_W(dma_wr_desc_req.SRC_ADDR_W),
|
||||
.SRC_SEL_EN(dma_wr_desc_req.SRC_SEL_EN),
|
||||
.SRC_SEL_W(dma_wr_desc_req.SRC_SEL_W-1),
|
||||
.SRC_ASID_EN(dma_wr_desc_req.SRC_ASID_EN),
|
||||
.DST_ADDR_W(dma_wr_desc_req.DST_ADDR_W),
|
||||
.DST_SEL_EN(dma_wr_desc_req.DST_SEL_EN),
|
||||
.DST_SEL_W(dma_wr_desc_req.DST_SEL_W),
|
||||
.DST_ASID_EN(dma_wr_desc_req.DST_ASID_EN),
|
||||
.IMM_EN(dma_wr_desc_req.IMM_EN),
|
||||
.IMM_W(dma_wr_desc_req.IMM_W),
|
||||
.LEN_W(dma_wr_desc_req.LEN_W),
|
||||
.TAG_W(dma_wr_desc_req.TAG_W-1),
|
||||
.ID_EN(dma_wr_desc_req.ID_EN),
|
||||
.DEST_EN(dma_wr_desc_req.DEST_EN),
|
||||
.USER_EN(dma_wr_desc_req.USER_EN)
|
||||
) dma_wr_desc_int[2]();
|
||||
|
||||
taxi_dma_ram_if #(
|
||||
.SEGS(RAM_SEGS),
|
||||
.SEG_ADDR_W(RAM_SEG_ADDR_W),
|
||||
.SEG_DATA_W(RAM_SEG_DATA_W),
|
||||
.SEG_BE_W(RAM_SEG_BE_W),
|
||||
.SEL_W(RAM_SEL_W-1)
|
||||
) dma_ram_rd_int[2]();
|
||||
|
||||
taxi_dma_if_mux_wr #(
|
||||
.PORTS(2),
|
||||
.ARB_ROUND_ROBIN(1),
|
||||
.ARB_LSB_HIGH_PRIO(1)
|
||||
)
|
||||
wr_dma_mux_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* DMA descriptors from clients
|
||||
*/
|
||||
.client_req(dma_wr_desc_int),
|
||||
.client_sts(dma_wr_desc_int),
|
||||
|
||||
/*
|
||||
* DMA descriptors to DMA engines
|
||||
*/
|
||||
.dma_req(dma_wr_desc_req),
|
||||
.dma_sts(dma_wr_desc_sts),
|
||||
|
||||
/*
|
||||
* RAM interface (from DMA interface)
|
||||
*/
|
||||
.dma_ram_rd(dma_ram_rd),
|
||||
|
||||
/*
|
||||
* RAM interface (towards RAM)
|
||||
*/
|
||||
.client_ram_rd(dma_ram_rd_int)
|
||||
);
|
||||
|
||||
wire [1:0] desc_req;
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(16*8),
|
||||
.KEEP_EN(1),
|
||||
.LAST_EN(1),
|
||||
.ID_EN(0),
|
||||
.DEST_EN(1), // TODO
|
||||
.USER_EN(1),
|
||||
.USER_W(1)
|
||||
) axis_desc[2]();
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(16*8),
|
||||
.KEEP_EN(1),
|
||||
.LAST_EN(1),
|
||||
.ID_EN(1), // TODO
|
||||
.DEST_EN(0),
|
||||
.USER_EN(0)
|
||||
) axis_cpl[2]();
|
||||
|
||||
cndm_micro_desc_rd
|
||||
desc_rd_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* DMA
|
||||
*/
|
||||
.dma_rd_desc_req(dma_rd_desc_int[0]),
|
||||
.dma_rd_desc_sts(dma_rd_desc_int[0]),
|
||||
.dma_ram_wr(dma_ram_wr_int[0]),
|
||||
|
||||
.txq_en(txq_en_reg),
|
||||
.txq_size(txq_size_reg),
|
||||
.txq_base_addr(txq_base_addr_reg),
|
||||
.txq_prod(txq_prod_reg),
|
||||
.txq_cons(txq_cons),
|
||||
.rxq_en(rxq_en_reg),
|
||||
.rxq_size(rxq_size_reg),
|
||||
.rxq_base_addr(rxq_base_addr_reg),
|
||||
.rxq_prod(rxq_prod_reg),
|
||||
.rxq_cons(rxq_cons),
|
||||
|
||||
.desc_req(desc_req),
|
||||
.axis_desc(axis_desc)
|
||||
);
|
||||
|
||||
cndm_micro_cpl_wr
|
||||
cpl_wr_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* DMA
|
||||
*/
|
||||
.dma_wr_desc_req(dma_wr_desc_int[0]),
|
||||
.dma_wr_desc_sts(dma_wr_desc_int[0]),
|
||||
.dma_ram_rd(dma_ram_rd_int[0]),
|
||||
|
||||
.txcq_en(txcq_en_reg),
|
||||
.txcq_size(txcq_size_reg),
|
||||
.txcq_base_addr(txcq_base_addr_reg),
|
||||
.txcq_prod(txcq_prod),
|
||||
.rxcq_en(rxcq_en_reg),
|
||||
.rxcq_size(rxcq_size_reg),
|
||||
.rxcq_base_addr(rxcq_base_addr_reg),
|
||||
.rxcq_prod(rxcq_prod),
|
||||
|
||||
.axis_cpl(axis_cpl),
|
||||
.irq(irq)
|
||||
);
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(mac_axis_tx.DATA_W),
|
||||
.USER_EN(1),
|
||||
.USER_W(1)
|
||||
) mac_tx_int();
|
||||
|
||||
taxi_axis_async_fifo #(
|
||||
.DEPTH(16384),
|
||||
.RAM_PIPELINE(2),
|
||||
.FRAME_FIFO(1),
|
||||
.USER_BAD_FRAME_VALUE(1'b1),
|
||||
.USER_BAD_FRAME_MASK(1'b1),
|
||||
.DROP_OVERSIZE_FRAME(1),
|
||||
.DROP_BAD_FRAME(1),
|
||||
.DROP_WHEN_FULL(1)
|
||||
)
|
||||
tx_fifo (
|
||||
/*
|
||||
* AXI4-Stream input (sink)
|
||||
*/
|
||||
.s_clk(clk),
|
||||
.s_rst(rst),
|
||||
.s_axis(mac_tx_int),
|
||||
|
||||
/*
|
||||
* AXI4-Stream output (source)
|
||||
*/
|
||||
.m_clk(mac_tx_clk),
|
||||
.m_rst(mac_tx_rst),
|
||||
.m_axis(mac_axis_tx),
|
||||
|
||||
/*
|
||||
* Pause
|
||||
*/
|
||||
.s_pause_req(1'b0),
|
||||
.s_pause_ack(),
|
||||
.m_pause_req(1'b0),
|
||||
.m_pause_ack(),
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
.s_status_depth(),
|
||||
.s_status_depth_commit(),
|
||||
.s_status_overflow(),
|
||||
.s_status_bad_frame(),
|
||||
.s_status_good_frame(),
|
||||
.m_status_depth(),
|
||||
.m_status_depth_commit(),
|
||||
.m_status_overflow(),
|
||||
.m_status_bad_frame(),
|
||||
.m_status_good_frame()
|
||||
);
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(mac_axis_tx_cpl.DATA_W),
|
||||
.KEEP_EN(mac_axis_tx_cpl.KEEP_EN),
|
||||
.KEEP_W(mac_axis_tx_cpl.KEEP_W),
|
||||
.USER_EN(1),
|
||||
.USER_W(1)
|
||||
)
|
||||
mac_tx_cpl_int();
|
||||
|
||||
taxi_axis_async_fifo #(
|
||||
.DEPTH(256),
|
||||
.RAM_PIPELINE(2),
|
||||
.FRAME_FIFO(0),
|
||||
.USER_BAD_FRAME_VALUE(1'b1),
|
||||
.USER_BAD_FRAME_MASK(1'b1),
|
||||
.DROP_OVERSIZE_FRAME(0),
|
||||
.DROP_BAD_FRAME(0),
|
||||
.DROP_WHEN_FULL(0)
|
||||
)
|
||||
tx_cpl_fifo (
|
||||
/*
|
||||
* AXI4-Stream input (sink)
|
||||
*/
|
||||
.s_clk(mac_tx_clk),
|
||||
.s_rst(mac_tx_rst),
|
||||
.s_axis(mac_axis_tx_cpl),
|
||||
|
||||
/*
|
||||
* AXI4-Stream output (source)
|
||||
*/
|
||||
.m_clk(clk),
|
||||
.m_rst(rst),
|
||||
.m_axis(mac_tx_cpl_int),
|
||||
|
||||
/*
|
||||
* Pause
|
||||
*/
|
||||
.s_pause_req(1'b0),
|
||||
.s_pause_ack(),
|
||||
.m_pause_req(1'b0),
|
||||
.m_pause_ack(),
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
.s_status_depth(),
|
||||
.s_status_depth_commit(),
|
||||
.s_status_overflow(),
|
||||
.s_status_bad_frame(),
|
||||
.s_status_good_frame(),
|
||||
.m_status_depth(),
|
||||
.m_status_depth_commit(),
|
||||
.m_status_overflow(),
|
||||
.m_status_bad_frame(),
|
||||
.m_status_good_frame()
|
||||
);
|
||||
|
||||
cndm_micro_tx #(
|
||||
.PTP_TS_EN(PTP_TS_EN)
|
||||
)
|
||||
tx_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* DMA
|
||||
*/
|
||||
.dma_rd_desc_req(dma_rd_desc_int[1]),
|
||||
.dma_rd_desc_sts(dma_rd_desc_int[1]),
|
||||
.dma_ram_wr(dma_ram_wr_int[1]),
|
||||
|
||||
.desc_req(desc_req[0]),
|
||||
.axis_desc(axis_desc[0]),
|
||||
.tx_data(mac_tx_int),
|
||||
.tx_cpl(mac_tx_cpl_int),
|
||||
.axis_cpl(axis_cpl[0])
|
||||
);
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(mac_axis_rx.DATA_W),
|
||||
.USER_EN(1),
|
||||
.USER_W(1)
|
||||
) mac_rx_int();
|
||||
|
||||
taxi_axis_async_fifo #(
|
||||
.DEPTH(16384),
|
||||
.RAM_PIPELINE(2),
|
||||
.FRAME_FIFO(1),
|
||||
.USER_BAD_FRAME_VALUE(1'b1),
|
||||
.USER_BAD_FRAME_MASK(1'b1),
|
||||
.DROP_OVERSIZE_FRAME(1),
|
||||
.DROP_BAD_FRAME(1),
|
||||
.DROP_WHEN_FULL(1)
|
||||
)
|
||||
rx_fifo (
|
||||
/*
|
||||
* AXI4-Stream input (sink)
|
||||
*/
|
||||
.s_clk(mac_rx_clk),
|
||||
.s_rst(mac_rx_rst),
|
||||
.s_axis(mac_axis_rx),
|
||||
|
||||
/*
|
||||
* AXI4-Stream output (source)
|
||||
*/
|
||||
.m_clk(clk),
|
||||
.m_rst(rst),
|
||||
.m_axis(mac_rx_int),
|
||||
|
||||
/*
|
||||
* Pause
|
||||
*/
|
||||
.s_pause_req(1'b0),
|
||||
.s_pause_ack(),
|
||||
.m_pause_req(1'b0),
|
||||
.m_pause_ack(),
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
.s_status_depth(),
|
||||
.s_status_depth_commit(),
|
||||
.s_status_overflow(),
|
||||
.s_status_bad_frame(),
|
||||
.s_status_good_frame(),
|
||||
.m_status_depth(),
|
||||
.m_status_depth_commit(),
|
||||
.m_status_overflow(),
|
||||
.m_status_bad_frame(),
|
||||
.m_status_good_frame()
|
||||
);
|
||||
|
||||
cndm_micro_rx #(
|
||||
.PTP_TS_EN(PTP_TS_EN)
|
||||
)
|
||||
rx_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* DMA
|
||||
*/
|
||||
.dma_wr_desc_req(dma_wr_desc_int[1]),
|
||||
.dma_wr_desc_sts(dma_wr_desc_int[1]),
|
||||
.dma_ram_rd(dma_ram_rd_int[1]),
|
||||
|
||||
.rx_data(mac_rx_int),
|
||||
.desc_req(desc_req[1]),
|
||||
.axis_desc(axis_desc[1]),
|
||||
.axis_cpl(axis_cpl[1])
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
213
src/cndm/rtl/cndm_micro_rx.sv
Normal file
213
src/cndm/rtl/cndm_micro_rx.sv
Normal file
@@ -0,0 +1,213 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* Corundum-micro receive datapath
|
||||
*/
|
||||
module cndm_micro_rx #(
|
||||
parameter logic PTP_TS_EN = 1'b1
|
||||
)
|
||||
(
|
||||
input wire logic clk,
|
||||
input wire logic rst,
|
||||
|
||||
/*
|
||||
* DMA
|
||||
*/
|
||||
taxi_dma_desc_if.req_src dma_wr_desc_req,
|
||||
taxi_dma_desc_if.sts_snk dma_wr_desc_sts,
|
||||
taxi_dma_ram_if.rd_slv dma_ram_rd,
|
||||
|
||||
taxi_axis_if.snk rx_data,
|
||||
output wire logic desc_req,
|
||||
taxi_axis_if.snk axis_desc,
|
||||
taxi_axis_if.src axis_cpl
|
||||
);
|
||||
|
||||
localparam RAM_ADDR_W = 16;
|
||||
|
||||
taxi_dma_desc_if #(
|
||||
.SRC_ADDR_W(RAM_ADDR_W),
|
||||
.SRC_SEL_EN(1'b0),
|
||||
.SRC_ASID_EN(1'b0),
|
||||
.DST_ADDR_W(RAM_ADDR_W),
|
||||
.DST_SEL_EN(1'b0),
|
||||
.DST_ASID_EN(1'b0),
|
||||
.IMM_EN(1'b0),
|
||||
.LEN_W(16),
|
||||
.TAG_W(1),
|
||||
.ID_EN(0),
|
||||
.DEST_EN(0),
|
||||
.USER_EN(1),
|
||||
.USER_W(1)
|
||||
) dma_desc();
|
||||
|
||||
localparam [2:0]
|
||||
STATE_IDLE = 0,
|
||||
STATE_RX_DATA = 1,
|
||||
STATE_READ_DESC = 2,
|
||||
STATE_WRITE_DATA = 3;
|
||||
|
||||
logic [2:0] state_reg = STATE_IDLE;
|
||||
|
||||
logic desc_req_reg = 1'b0;
|
||||
|
||||
assign desc_req = desc_req_reg;
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
desc_req_reg <= 1'b0;
|
||||
|
||||
axis_desc.tready <= 1'b0;
|
||||
|
||||
dma_wr_desc_req.req_src_sel <= '0;
|
||||
dma_wr_desc_req.req_src_asid <= '0;
|
||||
dma_wr_desc_req.req_dst_sel <= '0;
|
||||
dma_wr_desc_req.req_dst_asid <= '0;
|
||||
dma_wr_desc_req.req_imm <= '0;
|
||||
dma_wr_desc_req.req_imm_en <= '0;
|
||||
dma_wr_desc_req.req_tag <= '0;
|
||||
dma_wr_desc_req.req_id <= '0;
|
||||
dma_wr_desc_req.req_dest <= '0;
|
||||
dma_wr_desc_req.req_user <= '0;
|
||||
dma_wr_desc_req.req_valid <= dma_wr_desc_req.req_valid && !dma_wr_desc_req.req_ready;
|
||||
|
||||
dma_desc.req_src_addr <= '0;
|
||||
dma_desc.req_src_sel <= '0;
|
||||
dma_desc.req_src_asid <= '0;
|
||||
dma_desc.req_dst_addr <= '0;
|
||||
dma_desc.req_dst_sel <= '0;
|
||||
dma_desc.req_dst_asid <= '0;
|
||||
dma_desc.req_imm <= '0;
|
||||
dma_desc.req_imm_en <= '0;
|
||||
dma_desc.req_len <= 4096;
|
||||
dma_desc.req_tag <= '0;
|
||||
dma_desc.req_id <= '0;
|
||||
dma_desc.req_dest <= '0;
|
||||
dma_desc.req_user <= '0;
|
||||
dma_desc.req_valid <= dma_desc.req_valid && !dma_desc.req_ready;
|
||||
|
||||
axis_cpl.tkeep <= '0;
|
||||
axis_cpl.tid <= '0;
|
||||
axis_cpl.tdest <= '0;
|
||||
axis_cpl.tuser <= '0;
|
||||
axis_cpl.tlast <= 1'b1;
|
||||
axis_cpl.tvalid <= axis_cpl.tvalid && !axis_cpl.tready;
|
||||
|
||||
case (state_reg)
|
||||
STATE_IDLE: begin
|
||||
dma_desc.req_valid <= 1'b1;
|
||||
state_reg <= STATE_RX_DATA;
|
||||
end
|
||||
STATE_RX_DATA: begin
|
||||
dma_wr_desc_req.req_len <= 20'(dma_desc.sts_len);
|
||||
axis_cpl.tdata[47:32] <= 16'(dma_desc.sts_len);
|
||||
if (dma_desc.sts_valid) begin
|
||||
desc_req_reg <= 1'b1;
|
||||
state_reg <= STATE_READ_DESC;
|
||||
end
|
||||
end
|
||||
STATE_READ_DESC: begin
|
||||
axis_desc.tready <= 1'b1;
|
||||
|
||||
dma_wr_desc_req.req_src_addr <= '0;
|
||||
dma_wr_desc_req.req_dst_addr <= axis_desc.tdata[127:64];
|
||||
|
||||
if (axis_desc.tvalid && axis_desc.tready) begin
|
||||
if (dma_wr_desc_req.req_len > 20'(axis_desc.tdata[47:32])) begin
|
||||
dma_wr_desc_req.req_len <= 20'(axis_desc.tdata[47:32]);
|
||||
end
|
||||
|
||||
if (axis_desc.tuser) begin
|
||||
// failed to read desc
|
||||
state_reg <= STATE_IDLE;
|
||||
end else begin
|
||||
dma_wr_desc_req.req_valid <= 1'b1;
|
||||
state_reg <= STATE_WRITE_DATA;
|
||||
end
|
||||
end
|
||||
end
|
||||
STATE_WRITE_DATA: begin
|
||||
if (dma_wr_desc_sts.sts_valid) begin
|
||||
axis_cpl.tvalid <= 1'b1;
|
||||
state_reg <= STATE_IDLE;
|
||||
end
|
||||
end
|
||||
default: begin
|
||||
state_reg <= STATE_IDLE;
|
||||
end
|
||||
endcase
|
||||
|
||||
if (rst) begin
|
||||
state_reg <= STATE_IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
taxi_dma_ram_if #(
|
||||
.SEGS(dma_ram_rd.SEGS),
|
||||
.SEG_ADDR_W(dma_ram_rd.SEG_ADDR_W),
|
||||
.SEG_DATA_W(dma_ram_rd.SEG_DATA_W),
|
||||
.SEG_BE_W(dma_ram_rd.SEG_BE_W)
|
||||
) dma_ram_wr();
|
||||
|
||||
taxi_dma_psdpram #(
|
||||
.SIZE(4096),
|
||||
.PIPELINE(2)
|
||||
)
|
||||
ram_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* Write port
|
||||
*/
|
||||
.dma_ram_wr(dma_ram_wr),
|
||||
|
||||
/*
|
||||
* Read port
|
||||
*/
|
||||
.dma_ram_rd(dma_ram_rd)
|
||||
);
|
||||
|
||||
taxi_dma_client_axis_sink
|
||||
dma_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* DMA descriptor
|
||||
*/
|
||||
.desc_req(dma_desc),
|
||||
.desc_sts(dma_desc),
|
||||
|
||||
/*
|
||||
* AXI stream write data input
|
||||
*/
|
||||
.s_axis_wr_data(rx_data),
|
||||
|
||||
/*
|
||||
* RAM interface
|
||||
*/
|
||||
.dma_ram_wr(dma_ram_wr),
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
*/
|
||||
.enable(1),
|
||||
.abort(0)
|
||||
);
|
||||
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
208
src/cndm/rtl/cndm_micro_tx.sv
Normal file
208
src/cndm/rtl/cndm_micro_tx.sv
Normal file
@@ -0,0 +1,208 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* Corundum-micro transmit datapath
|
||||
*/
|
||||
module cndm_micro_tx #(
|
||||
parameter logic PTP_TS_EN = 1'b1
|
||||
)
|
||||
(
|
||||
input wire logic clk,
|
||||
input wire logic rst,
|
||||
|
||||
/*
|
||||
* DMA
|
||||
*/
|
||||
taxi_dma_desc_if.req_src dma_rd_desc_req,
|
||||
taxi_dma_desc_if.sts_snk dma_rd_desc_sts,
|
||||
taxi_dma_ram_if.wr_slv dma_ram_wr,
|
||||
|
||||
output wire logic desc_req,
|
||||
taxi_axis_if.snk axis_desc,
|
||||
taxi_axis_if.src tx_data,
|
||||
taxi_axis_if.snk tx_cpl,
|
||||
taxi_axis_if.src axis_cpl
|
||||
);
|
||||
|
||||
localparam RAM_ADDR_W = 16;
|
||||
|
||||
taxi_dma_desc_if #(
|
||||
.SRC_ADDR_W(RAM_ADDR_W),
|
||||
.SRC_SEL_EN(1'b0),
|
||||
.SRC_ASID_EN(1'b0),
|
||||
.DST_ADDR_W(RAM_ADDR_W),
|
||||
.DST_SEL_EN(1'b0),
|
||||
.DST_ASID_EN(1'b0),
|
||||
.IMM_EN(1'b0),
|
||||
.LEN_W(16),
|
||||
.TAG_W(1),
|
||||
.ID_EN(0),
|
||||
.DEST_EN(0),
|
||||
.USER_EN(1),
|
||||
.USER_W(1)
|
||||
) dma_desc();
|
||||
|
||||
localparam [2:0]
|
||||
STATE_IDLE = 0,
|
||||
STATE_READ_DESC = 1,
|
||||
STATE_READ_DATA = 2,
|
||||
STATE_TX_DATA = 3;
|
||||
|
||||
logic [2:0] state_reg = STATE_IDLE;
|
||||
|
||||
logic desc_req_reg = 1'b0;
|
||||
|
||||
assign desc_req = desc_req_reg;
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
desc_req_reg <= 1'b0;
|
||||
|
||||
axis_desc.tready <= 1'b0;
|
||||
|
||||
dma_rd_desc_req.req_src_sel <= '0;
|
||||
dma_rd_desc_req.req_src_asid <= '0;
|
||||
dma_rd_desc_req.req_dst_sel <= '0;
|
||||
dma_rd_desc_req.req_dst_asid <= '0;
|
||||
dma_rd_desc_req.req_imm <= '0;
|
||||
dma_rd_desc_req.req_imm_en <= '0;
|
||||
dma_rd_desc_req.req_tag <= '0;
|
||||
dma_rd_desc_req.req_id <= '0;
|
||||
dma_rd_desc_req.req_dest <= '0;
|
||||
dma_rd_desc_req.req_user <= '0;
|
||||
dma_rd_desc_req.req_valid <= dma_rd_desc_req.req_valid && !dma_rd_desc_req.req_ready;
|
||||
|
||||
dma_desc.req_src_sel <= '0;
|
||||
dma_desc.req_src_asid <= '0;
|
||||
dma_desc.req_dst_addr <= '0;
|
||||
dma_desc.req_dst_sel <= '0;
|
||||
dma_desc.req_dst_asid <= '0;
|
||||
dma_desc.req_imm <= '0;
|
||||
dma_desc.req_imm_en <= '0;
|
||||
dma_desc.req_tag <= '0;
|
||||
dma_desc.req_id <= '0;
|
||||
dma_desc.req_dest <= '0;
|
||||
dma_desc.req_user <= '0;
|
||||
dma_desc.req_valid <= dma_desc.req_valid && !dma_desc.req_ready;
|
||||
|
||||
axis_cpl.tkeep <= '0;
|
||||
axis_cpl.tid <= '0;
|
||||
axis_cpl.tdest <= '0;
|
||||
axis_cpl.tuser <= '0;
|
||||
axis_cpl.tlast <= 1'b1;
|
||||
axis_cpl.tvalid <= axis_cpl.tvalid && !axis_cpl.tready;
|
||||
|
||||
case (state_reg)
|
||||
STATE_IDLE: begin
|
||||
desc_req_reg <= 1'b1;
|
||||
state_reg <= STATE_READ_DESC;
|
||||
end
|
||||
STATE_READ_DESC: begin
|
||||
axis_desc.tready <= 1'b1;
|
||||
|
||||
dma_rd_desc_req.req_src_addr <= axis_desc.tdata[127:64];
|
||||
dma_rd_desc_req.req_dst_addr <= '0;
|
||||
dma_rd_desc_req.req_len <= 20'(axis_desc.tdata[47:32]);
|
||||
|
||||
dma_desc.req_src_addr <= '0;
|
||||
dma_desc.req_len <= axis_desc.tdata[47:32];
|
||||
|
||||
if (axis_desc.tvalid && axis_desc.tready) begin
|
||||
if (axis_desc.tuser) begin
|
||||
// failed to read desc
|
||||
state_reg <= STATE_IDLE;
|
||||
end else begin
|
||||
dma_rd_desc_req.req_valid <= 1'b1;
|
||||
state_reg <= STATE_READ_DATA;
|
||||
end
|
||||
end
|
||||
end
|
||||
STATE_READ_DATA: begin
|
||||
if (dma_rd_desc_sts.sts_valid) begin
|
||||
dma_desc.req_valid <= 1'b1;
|
||||
state_reg <= STATE_TX_DATA;
|
||||
end
|
||||
end
|
||||
STATE_TX_DATA: begin
|
||||
if (dma_desc.sts_valid) begin
|
||||
axis_cpl.tvalid <= 1'b1;
|
||||
state_reg <= STATE_IDLE;
|
||||
end
|
||||
end
|
||||
default: begin
|
||||
state_reg <= STATE_IDLE;
|
||||
end
|
||||
endcase
|
||||
|
||||
if (rst) begin
|
||||
state_reg <= STATE_IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
taxi_dma_ram_if #(
|
||||
.SEGS(dma_ram_wr.SEGS),
|
||||
.SEG_ADDR_W(dma_ram_wr.SEG_ADDR_W),
|
||||
.SEG_DATA_W(dma_ram_wr.SEG_DATA_W),
|
||||
.SEG_BE_W(dma_ram_wr.SEG_BE_W)
|
||||
) dma_ram_rd();
|
||||
|
||||
taxi_dma_psdpram #(
|
||||
.SIZE(4096),
|
||||
.PIPELINE(2)
|
||||
)
|
||||
ram_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* Write port
|
||||
*/
|
||||
.dma_ram_wr(dma_ram_wr),
|
||||
|
||||
/*
|
||||
* Read port
|
||||
*/
|
||||
.dma_ram_rd(dma_ram_rd)
|
||||
);
|
||||
|
||||
taxi_dma_client_axis_source
|
||||
dma_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
|
||||
/*
|
||||
* DMA descriptor
|
||||
*/
|
||||
.desc_req(dma_desc),
|
||||
.desc_sts(dma_desc),
|
||||
|
||||
/*
|
||||
* AXI stream read data output
|
||||
*/
|
||||
.m_axis_rd_data(tx_data),
|
||||
|
||||
/*
|
||||
* RAM interface
|
||||
*/
|
||||
.dma_ram_rd(dma_ram_rd),
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
*/
|
||||
.enable(1'b1)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
276
src/cndm/tb/cndm.py
Normal file
276
src/cndm/tb/cndm.py
Normal file
@@ -0,0 +1,276 @@
|
||||
# SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
"""
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import struct
|
||||
from collections import deque
|
||||
|
||||
from cocotb.queue import Queue
|
||||
|
||||
class Port:
|
||||
def __init__(self, driver, index, hw_regs):
|
||||
self.driver = driver
|
||||
self.log = driver.log
|
||||
self.index = index
|
||||
self.hw_regs = hw_regs
|
||||
|
||||
self.rxq_log_size = (256).bit_length()-1
|
||||
self.rxq_size = 2**self.rxq_log_size
|
||||
self.rxq_mask = self.rxq_size-1
|
||||
self.rxq = None
|
||||
self.rxq_prod = 0
|
||||
self.rxq_cons = 0
|
||||
|
||||
self.rx_info = [None] * self.rxq_size
|
||||
|
||||
self.rxcq_log_size = (256).bit_length()-1
|
||||
self.rxcq_size = 2**self.rxcq_log_size
|
||||
self.rxcq_mask = self.rxcq_size-1
|
||||
self.rxcq = None
|
||||
self.rxcq_prod = 0
|
||||
self.rxcq_cons = 0
|
||||
|
||||
self.txq_log_size = (256).bit_length()-1
|
||||
self.txq_size = 2**self.txq_log_size
|
||||
self.txq_mask = self.txq_size-1
|
||||
self.txq = None
|
||||
self.txq_prod = 0
|
||||
self.txq_cons = 0
|
||||
|
||||
self.tx_info = [None] * self.txq_size
|
||||
|
||||
self.txcq_log_size = (256).bit_length()-1
|
||||
self.txcq_size = 2**self.txcq_log_size
|
||||
self.txcq_mask = self.txcq_size-1
|
||||
self.txcq = None
|
||||
self.txcq_prod = 0
|
||||
self.txcq_cons = 0
|
||||
|
||||
self.rx_queue = Queue()
|
||||
|
||||
async def init(self):
|
||||
|
||||
self.rxq = self.driver.pool.alloc_region(self.rxq_size*16)
|
||||
addr = self.rxq.get_absolute_address(0)
|
||||
await self.hw_regs.write_dword(0x0200, 0x00000000)
|
||||
await self.hw_regs.write_dword(0x0204, 0x00000000)
|
||||
await self.hw_regs.write_dword(0x0208, addr & 0xffffffff)
|
||||
await self.hw_regs.write_dword(0x020c, addr >> 32)
|
||||
await self.hw_regs.write_dword(0x0200, 0x00000001 | (self.rxq_log_size << 16))
|
||||
|
||||
self.rxcq = self.driver.pool.alloc_region(self.rxcq_size*16)
|
||||
addr = self.rxcq.get_absolute_address(0)
|
||||
await self.hw_regs.write_dword(0x0400, 0x00000000)
|
||||
await self.hw_regs.write_dword(0x0408, addr & 0xffffffff)
|
||||
await self.hw_regs.write_dword(0x040c, addr >> 32)
|
||||
await self.hw_regs.write_dword(0x0400, 0x00000001 | (self.rxcq_log_size << 16))
|
||||
|
||||
self.txq = self.driver.pool.alloc_region(self.txq_size*16)
|
||||
addr = self.txq.get_absolute_address(0)
|
||||
await self.hw_regs.write_dword(0x0100, 0x00000000)
|
||||
await self.hw_regs.write_dword(0x0104, 0x00000000)
|
||||
await self.hw_regs.write_dword(0x0108, addr & 0xffffffff)
|
||||
await self.hw_regs.write_dword(0x010c, addr >> 32)
|
||||
await self.hw_regs.write_dword(0x0100, 0x00000001 | (self.txq_log_size << 16))
|
||||
|
||||
self.txcq = self.driver.pool.alloc_region(self.txcq_size*16)
|
||||
addr = self.txcq.get_absolute_address(0)
|
||||
await self.hw_regs.write_dword(0x0300, 0x00000000)
|
||||
await self.hw_regs.write_dword(0x0308, addr & 0xffffffff)
|
||||
await self.hw_regs.write_dword(0x030c, addr >> 32)
|
||||
await self.hw_regs.write_dword(0x0300, 0x00000001 | (self.txcq_log_size << 16))
|
||||
|
||||
# wait for writes to complete
|
||||
await self.hw_regs.read_dword(0)
|
||||
|
||||
await self.refill_rx_buffers()
|
||||
|
||||
async def start_xmit(self, data):
|
||||
headroom = 10
|
||||
tx_buf = self.driver.alloc_pkt()
|
||||
await tx_buf.write(headroom, data)
|
||||
index = self.txq_prod & self.txq_mask
|
||||
ptr = tx_buf.get_absolute_address(0)
|
||||
struct.pack_into('<xxxxLQ', self.txq.mem, 16*index, len(data), ptr+headroom)
|
||||
self.tx_info[index] = tx_buf
|
||||
self.txq_prod += 1
|
||||
await self.hw_regs.write_dword(0x0104, self.txq_prod & 0xffff)
|
||||
|
||||
async def recv(self):
|
||||
return await self.rx_queue.get()
|
||||
|
||||
async def recv_nowait(self):
|
||||
return self.rx_queue.get_nowait()
|
||||
|
||||
def free_tx_desc(self, index):
|
||||
pkt = self.tx_info[index]
|
||||
self.driver.free_pkt(pkt)
|
||||
self.tx_info[index] = None
|
||||
|
||||
def free_tx_buf(self):
|
||||
while self.txq_cons != self.txq_prod:
|
||||
index = self.txq_cons & self.txq_mask
|
||||
self.free_tx_desc(index)
|
||||
self.txq_cons += 1
|
||||
|
||||
async def process_tx_cq(self):
|
||||
|
||||
cq_cons_ptr = self.txcq_cons
|
||||
cons_ptr = self.txq_cons
|
||||
|
||||
while True:
|
||||
cq_index = cq_cons_ptr & self.txcq_mask
|
||||
index = cons_ptr & self.txq_mask
|
||||
|
||||
cpl_data = struct.unpack_from("<LLLL", self.txcq.mem, cq_index*16)
|
||||
|
||||
self.log.info("TX CQ index %d data %s", cq_index, cpl_data)
|
||||
|
||||
if bool(cpl_data[-1] & 0x80000000) == bool(cq_cons_ptr & self.txcq_size):
|
||||
self.log.info("CQ empty")
|
||||
break
|
||||
|
||||
pkt = self.tx_info[index]
|
||||
|
||||
self.free_tx_desc(index)
|
||||
|
||||
cq_cons_ptr += 1
|
||||
cons_ptr += 1
|
||||
|
||||
self.txcq_cons = cq_cons_ptr
|
||||
self.txq_cons = cons_ptr
|
||||
|
||||
def free_rx_desc(self, index):
|
||||
pkt = self.rx_info[index]
|
||||
self.driver.free_pkt(pkt)
|
||||
self.rx_info[index] = None
|
||||
|
||||
def free_rx_buf(self):
|
||||
while self.rxq_cons != self.rxq_prod:
|
||||
index = self.rxq_cons & self.rxq_mask
|
||||
self.free_rx_desc(index)
|
||||
self.rxq_cons += 1
|
||||
|
||||
def prepare_rx_desc(self, index):
|
||||
pkt = self.driver.alloc_pkt()
|
||||
self.rx_info[index] = pkt
|
||||
|
||||
length = pkt.size
|
||||
ptr = pkt.get_absolute_address(0)
|
||||
|
||||
struct.pack_into('<xxxxLQ', self.rxq.mem, 16*index, length, ptr)
|
||||
|
||||
async def refill_rx_buffers(self):
|
||||
missing = self.rxq_size - (self.rxq_prod - self.rxq_cons)
|
||||
|
||||
if missing < 8:
|
||||
return
|
||||
|
||||
for k in range(missing):
|
||||
self.prepare_rx_desc(self.rxq_prod & self.rxq_mask)
|
||||
self.rxq_prod += 1
|
||||
|
||||
await self.hw_regs.write_dword(0x0204, self.rxq_prod & 0xffff)
|
||||
|
||||
async def process_rx_cq(self):
|
||||
|
||||
cq_cons_ptr = self.rxcq_cons
|
||||
cons_ptr = self.rxq_cons
|
||||
|
||||
while True:
|
||||
cq_index = cq_cons_ptr & self.rxcq_mask
|
||||
index = cons_ptr & self.rxq_mask
|
||||
|
||||
cpl_data = struct.unpack_from("<LLLL", self.rxcq.mem, cq_index*16)
|
||||
|
||||
self.log.info("RX CQ index %d data %s", cq_index, cpl_data)
|
||||
|
||||
if bool(cpl_data[-1] & 0x80000000) == bool(cq_cons_ptr & self.rxcq_size):
|
||||
self.log.info("CQ empty")
|
||||
break
|
||||
|
||||
pkt = self.rx_info[index]
|
||||
length = cpl_data[1]
|
||||
|
||||
data = pkt[:length]
|
||||
|
||||
self.log.info("Packet: %s", data)
|
||||
|
||||
self.rx_queue.put_nowait(data)
|
||||
|
||||
self.free_rx_desc(index)
|
||||
|
||||
cq_cons_ptr += 1
|
||||
cons_ptr += 1
|
||||
|
||||
self.rxcq_cons = cq_cons_ptr
|
||||
self.rxq_cons = cons_ptr
|
||||
|
||||
await self.refill_rx_buffers()
|
||||
|
||||
async def interrupt_handler(self):
|
||||
self.log.info("Interrupt")
|
||||
await self.process_rx_cq()
|
||||
await self.process_tx_cq()
|
||||
|
||||
|
||||
class Driver:
|
||||
def __init__(self):
|
||||
self.log = logging.getLogger("cocotb.cndm")
|
||||
|
||||
self.dev = None
|
||||
self.pool = None
|
||||
self.hw_regs = None
|
||||
|
||||
self.ports = []
|
||||
|
||||
self.free_packets = deque()
|
||||
self.allocated_packets = []
|
||||
|
||||
async def init_pcie_dev(self, dev):
|
||||
self.dev = dev
|
||||
self.pool = dev.rc.mem_pool
|
||||
|
||||
await dev.enable_device()
|
||||
await dev.set_master()
|
||||
await dev.alloc_irq_vectors(32, 32)
|
||||
|
||||
self.hw_regs = dev.bar_window[0]
|
||||
|
||||
await self.init_common()
|
||||
|
||||
async def init_common(self):
|
||||
self.port_count = await self.hw_regs.read_dword(0x0100)
|
||||
self.port_offset = await self.hw_regs.read_dword(0x0104)
|
||||
self.port_stride = await self.hw_regs.read_dword(0x0108)
|
||||
|
||||
self.log.info("Port count: %d", self.port_count)
|
||||
self.log.info("Port offset: 0x%x", self.port_offset)
|
||||
self.log.info("Port stride: 0x%x", self.port_stride)
|
||||
|
||||
for k in range(self.port_count):
|
||||
port = Port(self, k, self.hw_regs.create_window(self.port_offset + self.port_stride*k))
|
||||
await port.init()
|
||||
self.dev.request_irq(k, port.interrupt_handler)
|
||||
|
||||
self.ports.append(port)
|
||||
|
||||
def alloc_pkt(self):
|
||||
if self.free_packets:
|
||||
return self.free_packets.popleft()
|
||||
|
||||
pkt = self.pool.alloc_region(4096)
|
||||
self.allocated_packets.append(pkt)
|
||||
return pkt
|
||||
|
||||
def free_pkt(self, pkt):
|
||||
assert pkt is not None
|
||||
assert pkt in self.allocated_packets
|
||||
self.free_packets.append(pkt)
|
||||
59
src/cndm/tb/cndm_micro_pcie_us/Makefile
Normal file
59
src/cndm/tb/cndm_micro_pcie_us/Makefile
Normal file
@@ -0,0 +1,59 @@
|
||||
# SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
#
|
||||
# Copyright (c) 2020-2025 FPGA Ninja, LLC
|
||||
#
|
||||
# Authors:
|
||||
# - Alex Forencich
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= verilator
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
|
||||
RTL_DIR = ../../rtl
|
||||
LIB_DIR = ../../lib
|
||||
TAXI_SRC_DIR = $(LIB_DIR)/taxi/src
|
||||
|
||||
DUT = cndm_micro_pcie_us
|
||||
COCOTB_TEST_MODULES = test_$(DUT)
|
||||
COCOTB_TOPLEVEL = test_$(DUT)
|
||||
MODULE = $(COCOTB_TEST_MODULES)
|
||||
TOPLEVEL = $(COCOTB_TOPLEVEL)
|
||||
VERILOG_SOURCES += $(COCOTB_TOPLEVEL).sv
|
||||
VERILOG_SOURCES += $(RTL_DIR)/$(DUT).f
|
||||
VERILOG_SOURCES += $(TAXI_SRC_DIR)/axis/rtl/taxi_axis_async_fifo.f
|
||||
VERILOG_SOURCES += $(TAXI_SRC_DIR)/sync/rtl/taxi_sync_reset.sv
|
||||
VERILOG_SOURCES += $(TAXI_SRC_DIR)/sync/rtl/taxi_sync_signal.sv
|
||||
|
||||
# handle file list files
|
||||
process_f_file = $(call process_f_files,$(addprefix $(dir $1),$(shell cat $1)))
|
||||
process_f_files = $(foreach f,$1,$(if $(filter %.f,$f),$(call process_f_file,$f),$f))
|
||||
uniq_base = $(if $1,$(call uniq_base,$(foreach f,$1,$(if $(filter-out $(notdir $(lastword $1)),$(notdir $f)),$f,))) $(lastword $1))
|
||||
VERILOG_SOURCES := $(call uniq_base,$(call process_f_files,$(VERILOG_SOURCES)))
|
||||
|
||||
# module parameters
|
||||
export PARAM_SIM := "1'b1"
|
||||
export PARAM_VENDOR := "\"XILINX\""
|
||||
export PARAM_FAMILY := "\"virtexuplus\""
|
||||
export PARAM_PORTS := 2
|
||||
export PARAM_MAC_DATA_W := 32
|
||||
export PARAM_AXIS_PCIE_DATA_W := 256
|
||||
export PARAM_BAR0_APERTURE := 24
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(COCOTB_TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
VERILATOR_TRACE = 1
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
1
src/cndm/tb/cndm_micro_pcie_us/cndm.py
Symbolic link
1
src/cndm/tb/cndm_micro_pcie_us/cndm.py
Symbolic link
@@ -0,0 +1 @@
|
||||
../cndm.py
|
||||
520
src/cndm/tb/cndm_micro_pcie_us/test_cndm_micro_pcie_us.py
Normal file
520
src/cndm/tb/cndm_micro_pcie_us/test_cndm_micro_pcie_us.py
Normal file
@@ -0,0 +1,520 @@
|
||||
#!/usr/bin/env python
|
||||
# SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
"""
|
||||
|
||||
Copyright (c) 2020-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
import cocotb_test.simulator
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge, FallingEdge, Timer
|
||||
|
||||
from cocotbext.axi import AxiStreamBus
|
||||
from cocotbext.eth import EthMac
|
||||
from cocotbext.pcie.core import RootComplex
|
||||
from cocotbext.pcie.xilinx.us import UltraScalePlusPcieDevice
|
||||
|
||||
try:
|
||||
import cndm
|
||||
except ImportError:
|
||||
# attempt import from current directory
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__)))
|
||||
try:
|
||||
import cndm
|
||||
finally:
|
||||
del sys.path[0]
|
||||
|
||||
|
||||
class TB:
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
# PCIe
|
||||
self.rc = RootComplex()
|
||||
|
||||
self.rc.max_payload_size = 0x1 # 256 bytes
|
||||
self.rc.max_read_request_size = 0x2 # 512 bytes
|
||||
|
||||
self.dev = UltraScalePlusPcieDevice(
|
||||
# configuration options
|
||||
pcie_generation=3,
|
||||
pcie_link_width=8,
|
||||
user_clk_frequency=250e6,
|
||||
alignment="dword",
|
||||
cq_straddle=False,
|
||||
cc_straddle=False,
|
||||
rq_straddle=False,
|
||||
rc_straddle=False,
|
||||
rc_4tlp_straddle=False,
|
||||
pf_count=1,
|
||||
max_payload_size=1024,
|
||||
enable_client_tag=True,
|
||||
enable_extended_tag=True,
|
||||
enable_parity=False,
|
||||
enable_rx_msg_interface=False,
|
||||
enable_sriov=False,
|
||||
enable_extended_configuration=False,
|
||||
|
||||
pf0_msi_enable=True,
|
||||
pf0_msi_count=32,
|
||||
pf1_msi_enable=False,
|
||||
pf1_msi_count=1,
|
||||
pf2_msi_enable=False,
|
||||
pf2_msi_count=1,
|
||||
pf3_msi_enable=False,
|
||||
pf3_msi_count=1,
|
||||
pf0_msix_enable=False,
|
||||
pf0_msix_table_size=31,
|
||||
pf0_msix_table_bir=4,
|
||||
pf0_msix_table_offset=0x00000000,
|
||||
pf0_msix_pba_bir=4,
|
||||
pf0_msix_pba_offset=0x00008000,
|
||||
pf1_msix_enable=False,
|
||||
pf1_msix_table_size=0,
|
||||
pf1_msix_table_bir=0,
|
||||
pf1_msix_table_offset=0x00000000,
|
||||
pf1_msix_pba_bir=0,
|
||||
pf1_msix_pba_offset=0x00000000,
|
||||
pf2_msix_enable=False,
|
||||
pf2_msix_table_size=0,
|
||||
pf2_msix_table_bir=0,
|
||||
pf2_msix_table_offset=0x00000000,
|
||||
pf2_msix_pba_bir=0,
|
||||
pf2_msix_pba_offset=0x00000000,
|
||||
pf3_msix_enable=False,
|
||||
pf3_msix_table_size=0,
|
||||
pf3_msix_table_bir=0,
|
||||
pf3_msix_table_offset=0x00000000,
|
||||
pf3_msix_pba_bir=0,
|
||||
pf3_msix_pba_offset=0x00000000,
|
||||
|
||||
# signals
|
||||
# Clock and Reset Interface
|
||||
user_clk=dut.pcie_clk,
|
||||
user_reset=dut.pcie_rst,
|
||||
# user_lnk_up
|
||||
# sys_clk
|
||||
# sys_clk_gt
|
||||
# sys_reset
|
||||
# phy_rdy_out
|
||||
|
||||
# Requester reQuest Interface
|
||||
rq_bus=AxiStreamBus.from_entity(dut.m_axis_pcie_rq),
|
||||
pcie_rq_seq_num0=dut.pcie_rq_seq_num0,
|
||||
pcie_rq_seq_num_vld0=dut.pcie_rq_seq_num_vld0,
|
||||
pcie_rq_seq_num1=dut.pcie_rq_seq_num1,
|
||||
pcie_rq_seq_num_vld1=dut.pcie_rq_seq_num_vld1,
|
||||
# pcie_rq_tag0
|
||||
# pcie_rq_tag1
|
||||
# pcie_rq_tag_av
|
||||
# pcie_rq_tag_vld0
|
||||
# pcie_rq_tag_vld1
|
||||
|
||||
# Requester Completion Interface
|
||||
rc_bus=AxiStreamBus.from_entity(dut.s_axis_pcie_rc),
|
||||
|
||||
# Completer reQuest Interface
|
||||
cq_bus=AxiStreamBus.from_entity(dut.s_axis_pcie_cq),
|
||||
# pcie_cq_np_req
|
||||
# pcie_cq_np_req_count
|
||||
|
||||
# Completer Completion Interface
|
||||
cc_bus=AxiStreamBus.from_entity(dut.m_axis_pcie_cc),
|
||||
|
||||
# Transmit Flow Control Interface
|
||||
# pcie_tfc_nph_av=dut.pcie_tfc_nph_av,
|
||||
# pcie_tfc_npd_av=dut.pcie_tfc_npd_av,
|
||||
|
||||
# Configuration Management Interface
|
||||
# cfg_mgmt_addr=dut.cfg_mgmt_addr,
|
||||
# cfg_mgmt_function_number=dut.cfg_mgmt_function_number,
|
||||
# cfg_mgmt_write=dut.cfg_mgmt_write,
|
||||
# cfg_mgmt_write_data=dut.cfg_mgmt_write_data,
|
||||
# cfg_mgmt_byte_enable=dut.cfg_mgmt_byte_enable,
|
||||
# cfg_mgmt_read=dut.cfg_mgmt_read,
|
||||
# cfg_mgmt_read_data=dut.cfg_mgmt_read_data,
|
||||
# cfg_mgmt_read_write_done=dut.cfg_mgmt_read_write_done,
|
||||
# cfg_mgmt_debug_access
|
||||
|
||||
# Configuration Status Interface
|
||||
# cfg_phy_link_down
|
||||
# cfg_phy_link_status
|
||||
# cfg_negotiated_width
|
||||
# cfg_current_speed
|
||||
# cfg_max_payload=dut.cfg_max_payload,
|
||||
# cfg_max_read_req=dut.cfg_max_read_req,
|
||||
# cfg_function_status
|
||||
# cfg_vf_status
|
||||
# cfg_function_power_state
|
||||
# cfg_vf_power_state
|
||||
# cfg_link_power_state
|
||||
# cfg_err_cor_out
|
||||
# cfg_err_nonfatal_out
|
||||
# cfg_err_fatal_out
|
||||
# cfg_local_error_out
|
||||
# cfg_local_error_valid
|
||||
# cfg_rx_pm_state
|
||||
# cfg_tx_pm_state
|
||||
# cfg_ltssm_state
|
||||
# cfg_rcb_status=dut.cfg_rcb_status,
|
||||
# cfg_obff_enable
|
||||
# cfg_pl_status_change
|
||||
# cfg_tph_requester_enable
|
||||
# cfg_tph_st_mode
|
||||
# cfg_vf_tph_requester_enable
|
||||
# cfg_vf_tph_st_mode
|
||||
|
||||
# Configuration Received Message Interface
|
||||
# cfg_msg_received
|
||||
# cfg_msg_received_data
|
||||
# cfg_msg_received_type
|
||||
|
||||
# Configuration Transmit Message Interface
|
||||
# cfg_msg_transmit
|
||||
# cfg_msg_transmit_type
|
||||
# cfg_msg_transmit_data
|
||||
# cfg_msg_transmit_done
|
||||
|
||||
# Configuration Flow Control Interface
|
||||
cfg_fc_ph=dut.cfg_fc_ph,
|
||||
cfg_fc_pd=dut.cfg_fc_pd,
|
||||
cfg_fc_nph=dut.cfg_fc_nph,
|
||||
cfg_fc_npd=dut.cfg_fc_npd,
|
||||
cfg_fc_cplh=dut.cfg_fc_cplh,
|
||||
cfg_fc_cpld=dut.cfg_fc_cpld,
|
||||
cfg_fc_sel=dut.cfg_fc_sel,
|
||||
|
||||
# Configuration Control Interface
|
||||
# cfg_hot_reset_in
|
||||
# cfg_hot_reset_out
|
||||
# cfg_config_space_enable
|
||||
# cfg_dsn
|
||||
# cfg_bus_number
|
||||
# cfg_ds_port_number
|
||||
# cfg_ds_bus_number
|
||||
# cfg_ds_device_number
|
||||
# cfg_ds_function_number
|
||||
# cfg_power_state_change_ack
|
||||
# cfg_power_state_change_interrupt
|
||||
# cfg_err_cor_in=dut.status_error_cor,
|
||||
# cfg_err_uncor_in=dut.status_error_uncor,
|
||||
# cfg_flr_in_process
|
||||
# cfg_flr_done
|
||||
# cfg_vf_flr_in_process
|
||||
# cfg_vf_flr_func_num
|
||||
# cfg_vf_flr_done
|
||||
# cfg_pm_aspm_l1_entry_reject
|
||||
# cfg_pm_aspm_tx_l0s_entry_disable
|
||||
# cfg_req_pm_transition_l23_ready
|
||||
# cfg_link_training_enable
|
||||
|
||||
# Configuration Interrupt Controller Interface
|
||||
# cfg_interrupt_int
|
||||
# cfg_interrupt_sent
|
||||
# cfg_interrupt_pending
|
||||
cfg_interrupt_msi_enable=dut.cfg_interrupt_msi_enable,
|
||||
cfg_interrupt_msi_mmenable=dut.cfg_interrupt_msi_mmenable,
|
||||
cfg_interrupt_msi_mask_update=dut.cfg_interrupt_msi_mask_update,
|
||||
cfg_interrupt_msi_data=dut.cfg_interrupt_msi_data,
|
||||
cfg_interrupt_msi_select=dut.cfg_interrupt_msi_select,
|
||||
cfg_interrupt_msi_int=dut.cfg_interrupt_msi_int,
|
||||
cfg_interrupt_msi_pending_status=dut.cfg_interrupt_msi_pending_status,
|
||||
cfg_interrupt_msi_pending_status_data_enable=dut.cfg_interrupt_msi_pending_status_data_enable,
|
||||
cfg_interrupt_msi_pending_status_function_num=dut.cfg_interrupt_msi_pending_status_function_num,
|
||||
cfg_interrupt_msi_sent=dut.cfg_interrupt_msi_sent,
|
||||
cfg_interrupt_msi_fail=dut.cfg_interrupt_msi_fail,
|
||||
# cfg_interrupt_msix_enable=dut.cfg_interrupt_msix_enable,
|
||||
# cfg_interrupt_msix_mask=dut.cfg_interrupt_msix_mask,
|
||||
# cfg_interrupt_msix_vf_enable=dut.cfg_interrupt_msix_vf_enable,
|
||||
# cfg_interrupt_msix_vf_mask=dut.cfg_interrupt_msix_vf_mask,
|
||||
# cfg_interrupt_msix_address=dut.cfg_interrupt_msix_address,
|
||||
# cfg_interrupt_msix_data=dut.cfg_interrupt_msix_data,
|
||||
# cfg_interrupt_msix_int=dut.cfg_interrupt_msix_int,
|
||||
# cfg_interrupt_msix_vec_pending=dut.cfg_interrupt_msix_vec_pending,
|
||||
# cfg_interrupt_msix_vec_pending_status=dut.cfg_interrupt_msix_vec_pending_status,
|
||||
# cfg_interrupt_msix_sent=dut.cfg_interrupt_msix_sent,
|
||||
# cfg_interrupt_msix_fail=dut.cfg_interrupt_msix_fail,
|
||||
cfg_interrupt_msi_attr=dut.cfg_interrupt_msi_attr,
|
||||
cfg_interrupt_msi_tph_present=dut.cfg_interrupt_msi_tph_present,
|
||||
cfg_interrupt_msi_tph_type=dut.cfg_interrupt_msi_tph_type,
|
||||
cfg_interrupt_msi_tph_st_tag=dut.cfg_interrupt_msi_tph_st_tag,
|
||||
cfg_interrupt_msi_function_number=dut.cfg_interrupt_msi_function_number,
|
||||
|
||||
# Configuration Extend Interface
|
||||
# cfg_ext_read_received
|
||||
# cfg_ext_write_received
|
||||
# cfg_ext_register_number
|
||||
# cfg_ext_function_number
|
||||
# cfg_ext_write_data
|
||||
# cfg_ext_write_byte_enable
|
||||
# cfg_ext_read_data
|
||||
# cfg_ext_read_data_valid
|
||||
)
|
||||
|
||||
# self.dev.log.setLevel(logging.DEBUG)
|
||||
|
||||
self.rc.make_port().connect(self.dev)
|
||||
|
||||
self.dev.functions[0].configure_bar(0, 2**int(dut.uut.axil_ctrl_bar.ADDR_W))
|
||||
|
||||
# Ethernet
|
||||
self.port_mac = []
|
||||
|
||||
eth_clock_period = 3.2
|
||||
eth_speed = 10e9
|
||||
|
||||
for k in range(len(dut.mac_axis_tx)):
|
||||
cocotb.start_soon(Clock(dut.mac_tx_clk[k], eth_clock_period, units="ns").start())
|
||||
cocotb.start_soon(Clock(dut.mac_rx_clk[k], eth_clock_period, units="ns").start())
|
||||
|
||||
dut.mac_tx_rst[k].setimmediatevalue(0)
|
||||
dut.mac_rx_rst[k].setimmediatevalue(0)
|
||||
|
||||
mac = EthMac(
|
||||
tx_clk=dut.mac_tx_clk[k],
|
||||
tx_rst=dut.mac_tx_rst[k],
|
||||
tx_bus=AxiStreamBus.from_entity(dut.mac_axis_tx[k]),
|
||||
rx_clk=dut.mac_rx_clk[k],
|
||||
rx_rst=dut.mac_rx_rst[k],
|
||||
rx_bus=AxiStreamBus.from_entity(dut.mac_axis_rx[k]),
|
||||
ifg=12, speed=eth_speed
|
||||
)
|
||||
self.port_mac.append(mac)
|
||||
|
||||
# cocotb.start_soon(Clock(dut.sfp_mgt_refclk_p, 6.4, units="ns").start())
|
||||
|
||||
# self.sfp_sources = []
|
||||
# self.sfp_sinks = []
|
||||
|
||||
# for ch in dut.uut.sfp_mac_inst.ch:
|
||||
# gt_inst = ch.ch_inst.gt.gt_inst
|
||||
|
||||
# if ch.ch_inst.DATA_W.value == 64:
|
||||
# if ch.ch_inst.CFG_LOW_LATENCY.value:
|
||||
# clk = 2.482
|
||||
# gbx_cfg = (66, [64, 65])
|
||||
# else:
|
||||
# clk = 2.56
|
||||
# gbx_cfg = None
|
||||
# else:
|
||||
# if ch.ch_inst.CFG_LOW_LATENCY.value:
|
||||
# clk = 3.102
|
||||
# gbx_cfg = (66, [64, 65])
|
||||
# else:
|
||||
# clk = 3.2
|
||||
# gbx_cfg = None
|
||||
|
||||
# cocotb.start_soon(Clock(gt_inst.tx_clk, clk, units="ns").start())
|
||||
# cocotb.start_soon(Clock(gt_inst.rx_clk, clk, units="ns").start())
|
||||
|
||||
# self.sfp_sources.append(BaseRSerdesSource(
|
||||
# data=gt_inst.serdes_rx_data,
|
||||
# data_valid=gt_inst.serdes_rx_data_valid,
|
||||
# hdr=gt_inst.serdes_rx_hdr,
|
||||
# hdr_valid=gt_inst.serdes_rx_hdr_valid,
|
||||
# clock=gt_inst.rx_clk,
|
||||
# slip=gt_inst.serdes_rx_bitslip,
|
||||
# reverse=True,
|
||||
# gbx_cfg=gbx_cfg
|
||||
# ))
|
||||
# self.sfp_sinks.append(BaseRSerdesSink(
|
||||
# data=gt_inst.serdes_tx_data,
|
||||
# data_valid=gt_inst.serdes_tx_data_valid,
|
||||
# hdr=gt_inst.serdes_tx_hdr,
|
||||
# hdr_valid=gt_inst.serdes_tx_hdr_valid,
|
||||
# gbx_sync=gt_inst.serdes_tx_gbx_sync,
|
||||
# clock=gt_inst.tx_clk,
|
||||
# reverse=True,
|
||||
# gbx_cfg=gbx_cfg
|
||||
# ))
|
||||
#
|
||||
self.loopback_enable = False
|
||||
cocotb.start_soon(self._run_loopback())
|
||||
|
||||
async def init(self):
|
||||
|
||||
for mac in self.port_mac:
|
||||
mac.rx.reset.setimmediatevalue(0)
|
||||
mac.tx.reset.setimmediatevalue(0)
|
||||
|
||||
await FallingEdge(self.dut.pcie_rst)
|
||||
await Timer(100, 'ns')
|
||||
|
||||
for k in range(10):
|
||||
await RisingEdge(self.dut.pcie_clk)
|
||||
|
||||
for mac in self.port_mac:
|
||||
mac.rx.reset.value = 1
|
||||
mac.tx.reset.value = 1
|
||||
|
||||
for k in range(10):
|
||||
await RisingEdge(self.dut.pcie_clk)
|
||||
|
||||
for mac in self.port_mac:
|
||||
mac.rx.reset.value = 0
|
||||
mac.tx.reset.value = 0
|
||||
|
||||
for k in range(10):
|
||||
await RisingEdge(self.dut.pcie_clk)
|
||||
|
||||
await self.rc.enumerate()
|
||||
|
||||
async def _run_loopback(self):
|
||||
while True:
|
||||
await RisingEdge(self.dut.pcie_clk)
|
||||
|
||||
if self.loopback_enable:
|
||||
for mac in self.port_mac:
|
||||
while not mac.tx.empty():
|
||||
await mac.rx.send(await mac.tx.recv())
|
||||
|
||||
@cocotb.test()
|
||||
async def run_test(dut):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.init()
|
||||
|
||||
tb.log.info("Init driver model")
|
||||
driver = cndm.Driver()
|
||||
await driver.init_pcie_dev(tb.rc.find_device(tb.dev.functions[0].pcie_id))
|
||||
|
||||
tb.log.info("Init complete")
|
||||
|
||||
tb.log.info("Send and receive single packet on each port")
|
||||
|
||||
for k in range(len(driver.ports)):
|
||||
data = f"Corundum rocks on port {k}!".encode('ascii')
|
||||
|
||||
await driver.ports[k].start_xmit(data)
|
||||
|
||||
pkt = await tb.port_mac[k].tx.recv()
|
||||
tb.log.info("Got TX packet: %s", pkt)
|
||||
|
||||
assert bytes(pkt) == data
|
||||
|
||||
await tb.port_mac[k].rx.send(pkt)
|
||||
|
||||
pkt = await driver.ports[k].recv()
|
||||
tb.log.info("Got RX packet: %s", pkt)
|
||||
|
||||
assert bytes(pkt) == data
|
||||
|
||||
tb.log.info("Multiple small packets")
|
||||
|
||||
count = 64
|
||||
pkts = [bytearray([(x+k) % 256 for x in range(60)]) for k in range(count)]
|
||||
|
||||
tb.loopback_enable = True
|
||||
|
||||
for p in pkts:
|
||||
await driver.ports[0].start_xmit(p)
|
||||
|
||||
for k in range(count):
|
||||
pkt = await driver.ports[0].recv()
|
||||
|
||||
tb.log.info("Got RX packet: %s", pkt)
|
||||
|
||||
assert bytes(pkt) == pkts[k]
|
||||
|
||||
tb.loopback_enable = False
|
||||
|
||||
tb.log.info("Multiple large packets")
|
||||
|
||||
count = 64
|
||||
pkts = [bytearray([(x+k) % 256 for x in range(1514)]) for k in range(count)]
|
||||
|
||||
tb.loopback_enable = True
|
||||
|
||||
for p in pkts:
|
||||
await driver.ports[0].start_xmit(p)
|
||||
|
||||
for k in range(count):
|
||||
pkt = await driver.ports[0].recv()
|
||||
|
||||
tb.log.info("Got RX packet: %s", pkt)
|
||||
|
||||
assert bytes(pkt) == pkts[k]
|
||||
|
||||
tb.loopback_enable = False
|
||||
|
||||
await RisingEdge(dut.pcie_clk)
|
||||
await RisingEdge(dut.pcie_clk)
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
lib_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'lib'))
|
||||
taxi_src_dir = os.path.abspath(os.path.join(lib_dir, 'taxi', 'src'))
|
||||
|
||||
|
||||
def process_f_files(files):
|
||||
lst = {}
|
||||
for f in files:
|
||||
if f[-2:].lower() == '.f':
|
||||
with open(f, 'r') as fp:
|
||||
l = fp.read().split()
|
||||
for f in process_f_files([os.path.join(os.path.dirname(f), x) for x in l]):
|
||||
lst[os.path.basename(f)] = f
|
||||
else:
|
||||
lst[os.path.basename(f)] = f
|
||||
return list(lst.values())
|
||||
|
||||
|
||||
@pytest.mark.parametrize("mac_data_w", [32, 64])
|
||||
def test_cndm_micro_pcie_us(request, mac_data_w):
|
||||
dut = "cndm_micro_pcie_us"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = module
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(tests_dir, f"{toplevel}.sv"),
|
||||
os.path.join(rtl_dir, f"{dut}.f"),
|
||||
os.path.join(taxi_src_dir, "axis", "rtl", "taxi_axis_async_fifo.f"),
|
||||
os.path.join(taxi_src_dir, "sync", "rtl", "taxi_sync_reset.sv"),
|
||||
os.path.join(taxi_src_dir, "sync", "rtl", "taxi_sync_signal.sv"),
|
||||
]
|
||||
|
||||
verilog_sources = process_f_files(verilog_sources)
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['SIM'] = "1'b1"
|
||||
parameters['VENDOR'] = "\"XILINX\""
|
||||
parameters['FAMILY'] = "\"virtexuplus\""
|
||||
parameters['PORTS'] = 2
|
||||
parameters['MAC_DATA_W'] = mac_data_w
|
||||
parameters['AXIS_PCIE_DATA_W'] = 256
|
||||
parameters['BAR0_APERTURE'] = 24
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir, "sim_build",
|
||||
request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
simulator="verilator",
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
||||
228
src/cndm/tb/cndm_micro_pcie_us/test_cndm_micro_pcie_us.sv
Normal file
228
src/cndm/tb/cndm_micro_pcie_us/test_cndm_micro_pcie_us.sv
Normal file
@@ -0,0 +1,228 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* FPGA core logic testbench
|
||||
*/
|
||||
module test_cndm_micro_pcie_us #
|
||||
(
|
||||
/* verilator lint_off WIDTHTRUNC */
|
||||
parameter logic SIM = 1'b0,
|
||||
parameter string VENDOR = "XILINX",
|
||||
parameter string FAMILY = "virtexuplus",
|
||||
parameter PORTS = 2,
|
||||
parameter MAC_DATA_W = 32,
|
||||
parameter AXIS_PCIE_DATA_W = 256,
|
||||
parameter AXIS_PCIE_RC_USER_W = AXIS_PCIE_DATA_W < 512 ? 75 : 161,
|
||||
parameter AXIS_PCIE_RQ_USER_W = AXIS_PCIE_DATA_W < 512 ? 62 : 137,
|
||||
parameter AXIS_PCIE_CQ_USER_W = AXIS_PCIE_DATA_W < 512 ? 85 : 183,
|
||||
parameter AXIS_PCIE_CC_USER_W = AXIS_PCIE_DATA_W < 512 ? 33 : 81,
|
||||
parameter BAR0_APERTURE = 24
|
||||
/* verilator lint_on WIDTHTRUNC */
|
||||
)
|
||||
();
|
||||
|
||||
localparam AXIS_PCIE_KEEP_W = (AXIS_PCIE_DATA_W/32);
|
||||
localparam RQ_SEQ_NUM_W = AXIS_PCIE_RQ_USER_W == 60 ? 4 : 6;
|
||||
|
||||
logic sfp_mgt_refclk_p;
|
||||
logic sfp_mgt_refclk_n;
|
||||
logic sfp_mgt_refclk_out;
|
||||
|
||||
logic [1:0] sfp_npres;
|
||||
logic [1:0] sfp_tx_fault;
|
||||
logic [1:0] sfp_los;
|
||||
|
||||
logic pcie_clk;
|
||||
logic pcie_rst;
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(AXIS_PCIE_DATA_W),
|
||||
.KEEP_EN(1),
|
||||
.KEEP_W(AXIS_PCIE_KEEP_W),
|
||||
.USER_EN(1),
|
||||
.USER_W(AXIS_PCIE_CQ_USER_W)
|
||||
) s_axis_pcie_cq();
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(AXIS_PCIE_DATA_W),
|
||||
.KEEP_EN(1),
|
||||
.KEEP_W(AXIS_PCIE_KEEP_W),
|
||||
.USER_EN(1),
|
||||
.USER_W(AXIS_PCIE_CC_USER_W)
|
||||
) m_axis_pcie_cc();
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(AXIS_PCIE_DATA_W),
|
||||
.KEEP_EN(1),
|
||||
.KEEP_W(AXIS_PCIE_KEEP_W),
|
||||
.USER_EN(1),
|
||||
.USER_W(AXIS_PCIE_RQ_USER_W)
|
||||
) m_axis_pcie_rq();
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(AXIS_PCIE_DATA_W),
|
||||
.KEEP_EN(1),
|
||||
.KEEP_W(AXIS_PCIE_KEEP_W),
|
||||
.USER_EN(1),
|
||||
.USER_W(AXIS_PCIE_RC_USER_W)
|
||||
) s_axis_pcie_rc();
|
||||
|
||||
logic [RQ_SEQ_NUM_W-1:0] pcie_rq_seq_num0;
|
||||
logic pcie_rq_seq_num_vld0;
|
||||
logic [RQ_SEQ_NUM_W-1:0] pcie_rq_seq_num1;
|
||||
logic pcie_rq_seq_num_vld1;
|
||||
|
||||
logic [2:0] cfg_max_payload;
|
||||
logic [2:0] cfg_max_read_req;
|
||||
logic [3:0] cfg_rcb_status;
|
||||
|
||||
logic [9:0] cfg_mgmt_addr;
|
||||
logic [7:0] cfg_mgmt_function_number;
|
||||
logic cfg_mgmt_write;
|
||||
logic [31:0] cfg_mgmt_write_data;
|
||||
logic [3:0] cfg_mgmt_byte_enable;
|
||||
logic cfg_mgmt_read;
|
||||
logic [31:0] cfg_mgmt_read_data;
|
||||
logic cfg_mgmt_read_write_done;
|
||||
|
||||
logic [7:0] cfg_fc_ph;
|
||||
logic [11:0] cfg_fc_pd;
|
||||
logic [7:0] cfg_fc_nph;
|
||||
logic [11:0] cfg_fc_npd;
|
||||
logic [7:0] cfg_fc_cplh;
|
||||
logic [11:0] cfg_fc_cpld;
|
||||
logic [2:0] cfg_fc_sel;
|
||||
|
||||
logic [3:0] cfg_interrupt_msi_enable;
|
||||
logic [11:0] cfg_interrupt_msi_mmenable;
|
||||
logic cfg_interrupt_msi_mask_update;
|
||||
logic [31:0] cfg_interrupt_msi_data;
|
||||
logic [1:0] cfg_interrupt_msi_select;
|
||||
logic [31:0] cfg_interrupt_msi_int;
|
||||
logic [31:0] cfg_interrupt_msi_pending_status;
|
||||
logic cfg_interrupt_msi_pending_status_data_enable;
|
||||
logic [1:0] cfg_interrupt_msi_pending_status_function_num;
|
||||
logic cfg_interrupt_msi_sent;
|
||||
logic cfg_interrupt_msi_fail;
|
||||
logic [2:0] cfg_interrupt_msi_attr;
|
||||
logic cfg_interrupt_msi_tph_present;
|
||||
logic [1:0] cfg_interrupt_msi_tph_type;
|
||||
logic [7:0] cfg_interrupt_msi_tph_st_tag;
|
||||
logic [7:0] cfg_interrupt_msi_function_number;
|
||||
|
||||
logic mac_tx_clk[PORTS];
|
||||
logic mac_tx_rst[PORTS];
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(MAC_DATA_W),
|
||||
.ID_W(8),
|
||||
.USER_EN(1),
|
||||
.USER_W(1)
|
||||
) mac_axis_tx[PORTS]();
|
||||
|
||||
logic mac_rx_clk[PORTS];
|
||||
logic mac_rx_rst[PORTS];
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(96),
|
||||
.KEEP_W(1),
|
||||
.ID_W(8)
|
||||
) mac_axis_tx_cpl[PORTS]();
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(MAC_DATA_W),
|
||||
.ID_W(8),
|
||||
.USER_EN(1),
|
||||
.USER_W(1)
|
||||
) mac_axis_rx[PORTS]();
|
||||
|
||||
cndm_micro_pcie_us #(
|
||||
.SIM(SIM),
|
||||
.VENDOR(VENDOR),
|
||||
.FAMILY(FAMILY),
|
||||
.PORTS(PORTS),
|
||||
.RQ_SEQ_NUM_W(RQ_SEQ_NUM_W),
|
||||
.BAR0_APERTURE(BAR0_APERTURE)
|
||||
)
|
||||
uut (
|
||||
/*
|
||||
* PCIe
|
||||
*/
|
||||
.pcie_clk(pcie_clk),
|
||||
.pcie_rst(pcie_rst),
|
||||
.s_axis_pcie_cq(s_axis_pcie_cq),
|
||||
.m_axis_pcie_cc(m_axis_pcie_cc),
|
||||
.m_axis_pcie_rq(m_axis_pcie_rq),
|
||||
.s_axis_pcie_rc(s_axis_pcie_rc),
|
||||
|
||||
.pcie_rq_seq_num0(pcie_rq_seq_num0),
|
||||
.pcie_rq_seq_num_vld0(pcie_rq_seq_num_vld0),
|
||||
.pcie_rq_seq_num1(pcie_rq_seq_num1),
|
||||
.pcie_rq_seq_num_vld1(pcie_rq_seq_num_vld1),
|
||||
|
||||
.cfg_max_payload(cfg_max_payload),
|
||||
.cfg_max_read_req(cfg_max_read_req),
|
||||
.cfg_rcb_status(cfg_rcb_status),
|
||||
|
||||
.cfg_mgmt_addr(cfg_mgmt_addr),
|
||||
.cfg_mgmt_function_number(cfg_mgmt_function_number),
|
||||
.cfg_mgmt_write(cfg_mgmt_write),
|
||||
.cfg_mgmt_write_data(cfg_mgmt_write_data),
|
||||
.cfg_mgmt_byte_enable(cfg_mgmt_byte_enable),
|
||||
.cfg_mgmt_read(cfg_mgmt_read),
|
||||
.cfg_mgmt_read_data(cfg_mgmt_read_data),
|
||||
.cfg_mgmt_read_write_done(cfg_mgmt_read_write_done),
|
||||
|
||||
.cfg_fc_ph(cfg_fc_ph),
|
||||
.cfg_fc_pd(cfg_fc_pd),
|
||||
.cfg_fc_nph(cfg_fc_nph),
|
||||
.cfg_fc_npd(cfg_fc_npd),
|
||||
.cfg_fc_cplh(cfg_fc_cplh),
|
||||
.cfg_fc_cpld(cfg_fc_cpld),
|
||||
.cfg_fc_sel(cfg_fc_sel),
|
||||
|
||||
.cfg_interrupt_msi_enable(cfg_interrupt_msi_enable),
|
||||
.cfg_interrupt_msi_mmenable(cfg_interrupt_msi_mmenable),
|
||||
.cfg_interrupt_msi_mask_update(cfg_interrupt_msi_mask_update),
|
||||
.cfg_interrupt_msi_data(cfg_interrupt_msi_data),
|
||||
.cfg_interrupt_msi_select(cfg_interrupt_msi_select),
|
||||
.cfg_interrupt_msi_int(cfg_interrupt_msi_int),
|
||||
.cfg_interrupt_msi_pending_status(cfg_interrupt_msi_pending_status),
|
||||
.cfg_interrupt_msi_pending_status_data_enable(cfg_interrupt_msi_pending_status_data_enable),
|
||||
.cfg_interrupt_msi_pending_status_function_num(cfg_interrupt_msi_pending_status_function_num),
|
||||
.cfg_interrupt_msi_sent(cfg_interrupt_msi_sent),
|
||||
.cfg_interrupt_msi_fail(cfg_interrupt_msi_fail),
|
||||
.cfg_interrupt_msi_attr(cfg_interrupt_msi_attr),
|
||||
.cfg_interrupt_msi_tph_present(cfg_interrupt_msi_tph_present),
|
||||
.cfg_interrupt_msi_tph_type(cfg_interrupt_msi_tph_type),
|
||||
.cfg_interrupt_msi_tph_st_tag(cfg_interrupt_msi_tph_st_tag),
|
||||
.cfg_interrupt_msi_function_number(cfg_interrupt_msi_function_number),
|
||||
|
||||
/*
|
||||
* Ethernet: SFP+
|
||||
*/
|
||||
.mac_tx_clk(mac_tx_clk),
|
||||
.mac_tx_rst(mac_tx_rst),
|
||||
.mac_axis_tx(mac_axis_tx),
|
||||
.mac_axis_tx_cpl(mac_axis_tx_cpl),
|
||||
|
||||
.mac_rx_clk(mac_rx_clk),
|
||||
.mac_rx_rst(mac_rx_rst),
|
||||
.mac_axis_rx(mac_axis_rx)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
32
src/cndm_proto/board/AS02MC04/fpga/README.md
Normal file
32
src/cndm_proto/board/AS02MC04/fpga/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Corundum-proto for Alibaba AS02MC04
|
||||
|
||||
## Introduction
|
||||
|
||||
This design targets the Alibaba AS02MC04 FPGA board.
|
||||
|
||||
* SFP+ cages
|
||||
* Looped-back 10GBASE-R or 25GBASE-R MAC via GTY transceiver
|
||||
|
||||
## Board details
|
||||
|
||||
* FPGA: xcku3p-ffvb676-1-e
|
||||
* PCIe: gen 3 x8 (~64 Gbps)
|
||||
* Reference oscillator: Fixed 156.25 MHz
|
||||
* 25GBASE-R PHY: Soft PCS with GTY transceiver
|
||||
|
||||
## Licensing
|
||||
|
||||
* Toolchain
|
||||
* Vivado Standard (enterprise license not required)
|
||||
* IP
|
||||
* No licensed vendor IP or 3rd party IP
|
||||
|
||||
## How to build
|
||||
|
||||
Run `make` in the appropriate `fpga*` subdirectory to build the bitstream. Ensure that the Xilinx Vivado toolchain components are in PATH.
|
||||
|
||||
On the host system, run `make` in `modules/cndm_proto` to build the driver. Ensure that the headers for the running kernel are installed, otherwise the driver cannot be compiled.
|
||||
|
||||
## How to test
|
||||
|
||||
Run `make program` to program the board with Vivado. Then, reboot the machine to re-enumerate the PCIe bus. Finally, load the driver on the host system with `insmod cndm_proto.ko`. Check `dmesg` for output from driver initialization. Run `cndm_proto_ddcmd.sh =p` to enable all debug messages.
|
||||
153
src/cndm_proto/board/AS02MC04/fpga/common/vivado.mk
Normal file
153
src/cndm_proto/board/AS02MC04/fpga/common/vivado.mk
Normal file
@@ -0,0 +1,153 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
###################################################################
|
||||
#
|
||||
# Xilinx Vivado FPGA Makefile
|
||||
#
|
||||
# Copyright (c) 2016-2025 Alex Forencich
|
||||
#
|
||||
###################################################################
|
||||
#
|
||||
# Parameters:
|
||||
# FPGA_TOP - Top module name
|
||||
# FPGA_FAMILY - FPGA family (e.g. VirtexUltrascale)
|
||||
# FPGA_DEVICE - FPGA device (e.g. xcvu095-ffva2104-2-e)
|
||||
# SYN_FILES - list of source files
|
||||
# INC_FILES - list of include files
|
||||
# XDC_FILES - list of timing constraint files
|
||||
# XCI_FILES - list of IP XCI files
|
||||
# IP_TCL_FILES - list of IP TCL files (sourced during project creation)
|
||||
# CONFIG_TCL_FILES - list of config TCL files (sourced before each build)
|
||||
#
|
||||
# Note: both SYN_FILES and INC_FILES support file list files. File list
|
||||
# files are files with a .f extension that contain a list of additional
|
||||
# files to include, one path relative to the .f file location per line.
|
||||
# The .f files are processed recursively, and then the complete file list
|
||||
# is de-duplicated, with later files in the list taking precedence.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# FPGA_TOP = fpga
|
||||
# FPGA_FAMILY = VirtexUltrascale
|
||||
# FPGA_DEVICE = xcvu095-ffva2104-2-e
|
||||
# SYN_FILES = rtl/fpga.v
|
||||
# XDC_FILES = fpga.xdc
|
||||
# XCI_FILES = ip/pcspma.xci
|
||||
# include ../common/vivado.mk
|
||||
#
|
||||
###################################################################
|
||||
|
||||
# phony targets
|
||||
.PHONY: fpga vivado tmpclean clean distclean
|
||||
|
||||
# prevent make from deleting intermediate files and reports
|
||||
.PRECIOUS: %.xpr %.bit %.bin %.ltx %.xsa %.mcs %.prm
|
||||
.SECONDARY:
|
||||
|
||||
CONFIG ?= config.mk
|
||||
-include $(CONFIG)
|
||||
|
||||
FPGA_TOP ?= fpga
|
||||
PROJECT ?= $(FPGA_TOP)
|
||||
XDC_FILES ?= $(PROJECT).xdc
|
||||
|
||||
# handle file list files
|
||||
process_f_file = $(call process_f_files,$(addprefix $(dir $1),$(shell cat $1)))
|
||||
process_f_files = $(foreach f,$1,$(if $(filter %.f,$f),$(call process_f_file,$f),$f))
|
||||
uniq_base = $(if $1,$(call uniq_base,$(foreach f,$1,$(if $(filter-out $(notdir $(lastword $1)),$(notdir $f)),$f,))) $(lastword $1))
|
||||
SYN_FILES := $(call uniq_base,$(call process_f_files,$(SYN_FILES)))
|
||||
INC_FILES := $(call uniq_base,$(call process_f_files,$(INC_FILES)))
|
||||
|
||||
###################################################################
|
||||
# Main Targets
|
||||
#
|
||||
# all: build everything (fpga)
|
||||
# fpga: build FPGA config
|
||||
# vivado: open project in Vivado
|
||||
# tmpclean: remove intermediate files
|
||||
# clean: remove output files and project files
|
||||
# distclean: remove archived output files
|
||||
###################################################################
|
||||
|
||||
all: fpga
|
||||
|
||||
fpga: $(PROJECT).bit
|
||||
|
||||
vivado: $(PROJECT).xpr
|
||||
vivado $(PROJECT).xpr
|
||||
|
||||
tmpclean::
|
||||
-rm -rf *.log *.jou *.cache *.gen *.hbs *.hw *.ip_user_files *.runs *.xpr *.html *.xml *.sim *.srcs *.str .Xil defines.v
|
||||
-rm -rf create_project.tcl update_config.tcl run_synth.tcl run_impl.tcl generate_bit.tcl
|
||||
|
||||
clean:: tmpclean
|
||||
-rm -rf *.bit *.bin *.ltx *.xsa program.tcl generate_mcs.tcl *.mcs *.prm flash.tcl
|
||||
-rm -rf *_utilization.rpt *_utilization_hierarchical.rpt
|
||||
|
||||
distclean:: clean
|
||||
-rm -rf rev
|
||||
|
||||
###################################################################
|
||||
# Target implementations
|
||||
###################################################################
|
||||
|
||||
# Vivado project file
|
||||
|
||||
# create fresh project if Makefile or IP files have changed
|
||||
create_project.tcl: Makefile $(XCI_FILES) $(IP_TCL_FILES)
|
||||
rm -rf defines.v
|
||||
touch defines.v
|
||||
for x in $(DEFS); do echo '`define' $$x >> defines.v; done
|
||||
echo "create_project -force -part $(FPGA_PART) $(PROJECT)" > $@
|
||||
echo "add_files -fileset sources_1 defines.v $(SYN_FILES)" >> $@
|
||||
echo "set_property top $(FPGA_TOP) [current_fileset]" >> $@
|
||||
echo "add_files -fileset constrs_1 $(XDC_FILES)" >> $@
|
||||
for x in $(XCI_FILES); do echo "import_ip $$x" >> $@; done
|
||||
for x in $(IP_TCL_FILES); do echo "source $$x" >> $@; done
|
||||
for x in $(CONFIG_TCL_FILES); do echo "source $$x" >> $@; done
|
||||
|
||||
# source config TCL scripts if any source file has changed
|
||||
update_config.tcl: $(CONFIG_TCL_FILES) $(SYN_FILES) $(INC_FILES) $(XDC_FILES)
|
||||
echo "open_project -quiet $(PROJECT).xpr" > $@
|
||||
for x in $(CONFIG_TCL_FILES); do echo "source $$x" >> $@; done
|
||||
|
||||
$(PROJECT).xpr: create_project.tcl update_config.tcl
|
||||
vivado -nojournal -nolog -mode batch $(foreach x,$?,-source $x)
|
||||
|
||||
# synthesis run
|
||||
$(PROJECT).runs/synth_1/$(PROJECT).dcp: create_project.tcl update_config.tcl $(SYN_FILES) $(INC_FILES) $(XDC_FILES) | $(PROJECT).xpr
|
||||
echo "open_project $(PROJECT).xpr" > run_synth.tcl
|
||||
echo "reset_run synth_1" >> run_synth.tcl
|
||||
echo "launch_runs -jobs 4 synth_1" >> run_synth.tcl
|
||||
echo "wait_on_run synth_1" >> run_synth.tcl
|
||||
vivado -nojournal -nolog -mode batch -source run_synth.tcl
|
||||
|
||||
# implementation run
|
||||
$(PROJECT).runs/impl_1/$(PROJECT)_routed.dcp: $(PROJECT).runs/synth_1/$(PROJECT).dcp
|
||||
echo "open_project $(PROJECT).xpr" > run_impl.tcl
|
||||
echo "reset_run impl_1" >> run_impl.tcl
|
||||
echo "launch_runs -jobs 4 impl_1" >> run_impl.tcl
|
||||
echo "wait_on_run impl_1" >> run_impl.tcl
|
||||
echo "open_run impl_1" >> run_impl.tcl
|
||||
echo "report_utilization -file $(PROJECT)_utilization.rpt" >> run_impl.tcl
|
||||
echo "report_utilization -hierarchical -file $(PROJECT)_utilization_hierarchical.rpt" >> run_impl.tcl
|
||||
vivado -nojournal -nolog -mode batch -source run_impl.tcl
|
||||
|
||||
# output files (including potentially bit, bin, ltx, and xsa)
|
||||
$(PROJECT).bit $(PROJECT).bin $(PROJECT).ltx $(PROJECT).xsa: $(PROJECT).runs/impl_1/$(PROJECT)_routed.dcp
|
||||
echo "open_project $(PROJECT).xpr" > generate_bit.tcl
|
||||
echo "open_run impl_1" >> generate_bit.tcl
|
||||
echo "write_bitstream -force -bin_file $(PROJECT).runs/impl_1/$(PROJECT).bit" >> generate_bit.tcl
|
||||
echo "write_debug_probes -force $(PROJECT).runs/impl_1/$(PROJECT).ltx" >> generate_bit.tcl
|
||||
echo "write_hw_platform -fixed -force -include_bit $(PROJECT).xsa" >> generate_bit.tcl
|
||||
vivado -nojournal -nolog -mode batch -source generate_bit.tcl
|
||||
ln -f -s $(PROJECT).runs/impl_1/$(PROJECT).bit .
|
||||
ln -f -s $(PROJECT).runs/impl_1/$(PROJECT).bin .
|
||||
if [ -e $(PROJECT).runs/impl_1/$(PROJECT).ltx ]; then ln -f -s $(PROJECT).runs/impl_1/$(PROJECT).ltx .; fi
|
||||
mkdir -p rev
|
||||
COUNT=100; \
|
||||
while [ -e rev/$(PROJECT)_rev$$COUNT.bit ]; \
|
||||
do COUNT=$$((COUNT+1)); done; \
|
||||
cp -pv $(PROJECT).runs/impl_1/$(PROJECT).bit rev/$(PROJECT)_rev$$COUNT.bit; \
|
||||
cp -pv $(PROJECT).runs/impl_1/$(PROJECT).bin rev/$(PROJECT)_rev$$COUNT.bin; \
|
||||
if [ -e $(PROJECT).runs/impl_1/$(PROJECT).ltx ]; then cp -pv $(PROJECT).runs/impl_1/$(PROJECT).ltx rev/$(PROJECT)_rev$$COUNT.ltx; fi; \
|
||||
if [ -e $(PROJECT).xsa ]; then cp -pv $(PROJECT).xsa rev/$(PROJECT)_rev$$COUNT.xsa; fi
|
||||
141
src/cndm_proto/board/AS02MC04/fpga/fpga.xdc
Normal file
141
src/cndm_proto/board/AS02MC04/fpga/fpga.xdc
Normal file
@@ -0,0 +1,141 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright (c) 2025 FPGA Ninja, LLC
|
||||
#
|
||||
# Authors:
|
||||
# - Alex Forencich
|
||||
#
|
||||
|
||||
# XDC constraints for the Alibaba AS02MC04
|
||||
# part: xcku3p-ffvb676-1-e
|
||||
|
||||
# General configuration
|
||||
set_property CFGBVS GND [current_design]
|
||||
set_property CONFIG_VOLTAGE 1.8 [current_design]
|
||||
set_property BITSTREAM.GENERAL.COMPRESS true [current_design]
|
||||
set_property BITSTREAM.CONFIG.UNUSEDPIN Pullup [current_design]
|
||||
set_property BITSTREAM.CONFIG.CONFIGRATE 72.9 [current_design]
|
||||
set_property CONFIG_MODE SPIx4 [current_design]
|
||||
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
|
||||
set_property BITSTREAM.CONFIG.SPI_FALL_EDGE Yes [current_design]
|
||||
set_property BITSTREAM.CONFIG.OVERTEMPSHUTDOWN Enable [current_design]
|
||||
|
||||
# 100 MHz system clock (Y2)
|
||||
set_property -dict {LOC E18 IOSTANDARD LVDS} [get_ports {clk_100mhz_p}]
|
||||
set_property -dict {LOC D18 IOSTANDARD LVDS} [get_ports {clk_100mhz_n}]
|
||||
create_clock -period 10 -name clk_100mhz [get_ports {clk_100mhz_p}]
|
||||
|
||||
# LEDs
|
||||
set_property -dict {LOC B12 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports {sfp_led[0]}] ;# DS3
|
||||
set_property -dict {LOC C12 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports {sfp_led[1]}] ;# DS2
|
||||
set_property -dict {LOC B11 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports {led[0]}] ;# DS6
|
||||
set_property -dict {LOC C11 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports {led[1]}] ;# DS7
|
||||
set_property -dict {LOC A10 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports {led[2]}] ;# DS8
|
||||
set_property -dict {LOC B10 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports {led[3]}] ;# DS9
|
||||
set_property -dict {LOC A13 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports {led_r}] ;# C1
|
||||
set_property -dict {LOC A12 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports {led_g}] ;# C1
|
||||
set_property -dict {LOC B9 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12} [get_ports {led_hb}] ;# DS5
|
||||
|
||||
set_false_path -to [get_ports {sfp_led[*] led[*] led_r led_g led_hb}]
|
||||
set_output_delay 0 [get_ports {sfp_led[*] led[*] led_r led_g led_hb}]
|
||||
|
||||
# Reset button
|
||||
set_property -dict {LOC F12 IOSTANDARD LVCMOS33} [get_ports reset] ;# SW1
|
||||
|
||||
set_false_path -from [get_ports {reset}]
|
||||
set_input_delay 0 [get_ports {reset}]
|
||||
|
||||
# GPIO
|
||||
#set_property -dict {LOC A14 IOSTANDARD LVCMOS33} [get_ports {gpio[0]}] ;# J5.3,4
|
||||
#set_property -dict {LOC E12 IOSTANDARD LVCMOS33} [get_ports {gpio[1]}] ;# J5.5,6
|
||||
#set_property -dict {LOC E13 IOSTANDARD LVCMOS33} [get_ports {gpio[2]}] ;# J5.7,8
|
||||
#set_property -dict {LOC F10 IOSTANDARD LVCMOS33} [get_ports {gpio[3]}] ;# J5.9,10
|
||||
#set_property -dict {LOC C9 IOSTANDARD LVCMOS33} [get_ports {gpio[4]}] ;# J5.11,12
|
||||
#set_property -dict {LOC D9 IOSTANDARD LVCMOS33} [get_ports {gpio[5]}] ;# J5.13,14
|
||||
|
||||
# SFP28 Interfaces
|
||||
set_property -dict {LOC A4 } [get_ports {sfp_rx_p[0]}] ;# MGTYRXP3_227 GTYE4_CHANNEL_X0Y15 / GTYE4_COMMON_X0Y3
|
||||
set_property -dict {LOC A3 } [get_ports {sfp_rx_n[0]}] ;# MGTYRXN3_227 GTYE4_CHANNEL_X0Y15 / GTYE4_COMMON_X0Y3
|
||||
set_property -dict {LOC B2 } [get_ports {sfp_rx_p[1]}] ;# MGTYRXP2_227 GTYE4_CHANNEL_X0Y14 / GTYE4_COMMON_X0Y3
|
||||
set_property -dict {LOC B1 } [get_ports {sfp_rx_n[1]}] ;# MGTYRXN2_227 GTYE4_CHANNEL_X0Y14 / GTYE4_COMMON_X0Y3
|
||||
set_property -dict {LOC B7 } [get_ports {sfp_tx_p[0]}] ;# MGTYTXP3_227 GTYE4_CHANNEL_X0Y15 / GTYE4_COMMON_X0Y3
|
||||
set_property -dict {LOC B6 } [get_ports {sfp_tx_n[0]}] ;# MGTYTXN3_227 GTYE4_CHANNEL_X0Y15 / GTYE4_COMMON_X0Y3
|
||||
set_property -dict {LOC D7 } [get_ports {sfp_tx_p[1]}] ;# MGTYTXP2_227 GTYE4_CHANNEL_X0Y14 / GTYE4_COMMON_X0Y3
|
||||
set_property -dict {LOC D6 } [get_ports {sfp_tx_n[1]}] ;# MGTYTXN2_227 GTYE4_CHANNEL_X0Y14 / GTYE4_COMMON_X0Y3
|
||||
set_property -dict {LOC K7 } [get_ports {sfp_mgt_refclk_p}] ;# MGTREFCLK0P_227 from Y1
|
||||
set_property -dict {LOC K6 } [get_ports {sfp_mgt_refclk_n}] ;# MGTREFCLK0N_227 from Y1
|
||||
set_property -dict {LOC D14 IOSTANDARD LVCMOS33 PULLUP true} [get_ports {sfp_npres[0]}]
|
||||
set_property -dict {LOC E11 IOSTANDARD LVCMOS33 PULLUP true} [get_ports {sfp_npres[1]}]
|
||||
set_property -dict {LOC B14 IOSTANDARD LVCMOS33 PULLUP true} [get_ports {sfp_tx_fault[0]}]
|
||||
set_property -dict {LOC F9 IOSTANDARD LVCMOS33 PULLUP true} [get_ports {sfp_tx_fault[1]}]
|
||||
set_property -dict {LOC D13 IOSTANDARD LVCMOS33 PULLUP true} [get_ports {sfp_los[0]}]
|
||||
set_property -dict {LOC E10 IOSTANDARD LVCMOS33 PULLUP true} [get_ports {sfp_los[1]}]
|
||||
#set_property -dict {LOC C13 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {sfp_i2c_scl[0]}]
|
||||
#set_property -dict {LOC D10 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {sfp_i2c_scl[1]}]
|
||||
#set_property -dict {LOC C14 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {sfp_i2c_sda[0]}]
|
||||
#set_property -dict {LOC D11 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {sfp_i2c_sda[1]}]
|
||||
|
||||
# 156.25 MHz MGT reference clock
|
||||
create_clock -period 6.4 -name sfp_mgt_refclk [get_ports {sfp_mgt_refclk_p}]
|
||||
|
||||
set_false_path -from [get_ports {sfp_npres[*] sfp_tx_fault[*] sfp_los[*]}]
|
||||
set_input_delay 0 [get_ports {sfp_npres[*] sfp_tx_fault[*] sfp_los[*]}]
|
||||
|
||||
#set_false_path -to [get_ports {sfp_i2c_sda[*] sfp_i2c_scl[*]}]
|
||||
#set_output_delay 0 [get_ports {sfp_i2c_sda[*] sfp_i2c_scl[*]}]
|
||||
#set_false_path -from [get_ports {sfp_i2c_sda[*] sfp_i2c_scl[*]}]
|
||||
#set_input_delay 0 [get_ports {sfp_i2c_sda[*] sfp_i2c_scl[*]}]
|
||||
|
||||
# I2C interface
|
||||
#set_property -dict {LOC G9 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {i2c_scl[0]}]
|
||||
#set_property -dict {LOC G10 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {i2c_sda[0]}]
|
||||
#set_property -dict {LOC J14 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {i2c_scl[1]}]
|
||||
#set_property -dict {LOC J15 IOSTANDARD LVCMOS33 SLEW SLOW DRIVE 12 PULLUP true} [get_ports {i2c_sda[1]}]
|
||||
|
||||
#set_false_path -to [get_ports {i2c_sda[*] i2c_scl[*]}]
|
||||
#set_output_delay 0 [get_ports {i2c_sda[*] i2c_scl[*]}]
|
||||
#set_false_path -from [get_ports {i2c_sda[*] i2c_scl[*]}]
|
||||
#set_input_delay 0 [get_ports {i2c_sda[*] i2c_scl[*]}]
|
||||
|
||||
# PCIe Interface
|
||||
set_property -dict {LOC P2 } [get_ports {pcie_rx_p[0]}] ;# MGTYRXP3_225 GTYE4_CHANNEL_X0Y7 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC P1 } [get_ports {pcie_rx_n[0]}] ;# MGTYRXN3_225 GTYE4_CHANNEL_X0Y7 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC R5 } [get_ports {pcie_tx_p[0]}] ;# MGTYTXP3_225 GTYE4_CHANNEL_X0Y7 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC R4 } [get_ports {pcie_tx_n[0]}] ;# MGTYTXN3_225 GTYE4_CHANNEL_X0Y7 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC T2 } [get_ports {pcie_rx_p[1]}] ;# MGTYRXP2_225 GTYE4_CHANNEL_X0Y6 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC T1 } [get_ports {pcie_rx_n[1]}] ;# MGTYRXN2_225 GTYE4_CHANNEL_X0Y6 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC U5 } [get_ports {pcie_tx_p[1]}] ;# MGTYTXP2_225 GTYE4_CHANNEL_X0Y6 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC U4 } [get_ports {pcie_tx_n[1]}] ;# MGTYTXN2_225 GTYE4_CHANNEL_X0Y6 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC V2 } [get_ports {pcie_rx_p[2]}] ;# MGTYRXP1_225 GTYE4_CHANNEL_X0Y5 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC V1 } [get_ports {pcie_rx_n[2]}] ;# MGTYRXN1_225 GTYE4_CHANNEL_X0Y5 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC W5 } [get_ports {pcie_tx_p[2]}] ;# MGTYTXP1_225 GTYE4_CHANNEL_X0Y5 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC W4 } [get_ports {pcie_tx_n[2]}] ;# MGTYTXN1_225 GTYE4_CHANNEL_X0Y5 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC Y2 } [get_ports {pcie_rx_p[3]}] ;# MGTYRXP0_225 GTYE4_CHANNEL_X0Y4 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC Y1 } [get_ports {pcie_rx_n[3]}] ;# MGTYRXN0_225 GTYE4_CHANNEL_X0Y4 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC AA5 } [get_ports {pcie_tx_p[3]}] ;# MGTYTXP0_225 GTYE4_CHANNEL_X0Y4 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC AA4 } [get_ports {pcie_tx_n[3]}] ;# MGTYTXN0_225 GTYE4_CHANNEL_X0Y4 / GTYE4_COMMON_X0Y1
|
||||
set_property -dict {LOC AB2 } [get_ports {pcie_rx_p[4]}] ;# MGTYRXP3_224 GTYE4_CHANNEL_X0Y3 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AB1 } [get_ports {pcie_rx_n[4]}] ;# MGTYRXN3_224 GTYE4_CHANNEL_X0Y3 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AC5 } [get_ports {pcie_tx_p[4]}] ;# MGTYTXP3_224 GTYE4_CHANNEL_X0Y3 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AC4 } [get_ports {pcie_tx_n[4]}] ;# MGTYTXN3_224 GTYE4_CHANNEL_X0Y3 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AD2 } [get_ports {pcie_rx_p[5]}] ;# MGTYRXP2_224 GTYE4_CHANNEL_X0Y2 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AD1 } [get_ports {pcie_rx_n[5]}] ;# MGTYRXN2_224 GTYE4_CHANNEL_X0Y2 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AD7 } [get_ports {pcie_tx_p[5]}] ;# MGTYTXP2_224 GTYE4_CHANNEL_X0Y2 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AD6 } [get_ports {pcie_tx_n[5]}] ;# MGTYTXN2_224 GTYE4_CHANNEL_X0Y2 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AE4 } [get_ports {pcie_rx_p[6]}] ;# MGTYRXP1_224 GTYE4_CHANNEL_X0Y1 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AE3 } [get_ports {pcie_rx_n[6]}] ;# MGTYRXN1_224 GTYE4_CHANNEL_X0Y1 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AE9 } [get_ports {pcie_tx_p[6]}] ;# MGTYTXP1_224 GTYE4_CHANNEL_X0Y1 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AE8 } [get_ports {pcie_tx_n[6]}] ;# MGTYTXN1_224 GTYE4_CHANNEL_X0Y1 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AF2 } [get_ports {pcie_rx_p[7]}] ;# MGTYRXP0_224 GTYE4_CHANNEL_X0Y0 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AF1 } [get_ports {pcie_rx_n[7]}] ;# MGTYRXN0_224 GTYE4_CHANNEL_X0Y0 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AF7 } [get_ports {pcie_tx_p[7]}] ;# MGTYTXP0_224 GTYE4_CHANNEL_X0Y0 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC AF6 } [get_ports {pcie_tx_n[7]}] ;# MGTYTXN0_224 GTYE4_CHANNEL_X0Y0 / GTYE4_COMMON_X0Y0
|
||||
set_property -dict {LOC T7 } [get_ports pcie_refclk_p] ;# MGTREFCLK1P_225
|
||||
set_property -dict {LOC T6 } [get_ports pcie_refclk_n] ;# MGTREFCLK1N_225
|
||||
set_property -dict {LOC A9 IOSTANDARD LVCMOS33 PULLUP true} [get_ports pcie_reset_n]
|
||||
|
||||
set_false_path -from [get_ports {pcie_reset_n}]
|
||||
set_input_delay 0 [get_ports {pcie_reset_n}]
|
||||
|
||||
# 100 MHz MGT reference clock
|
||||
create_clock -period 10 -name pcie_mgt_refclk [get_ports pcie_refclk_p]
|
||||
89
src/cndm_proto/board/AS02MC04/fpga/fpga/Makefile
Normal file
89
src/cndm_proto/board/AS02MC04/fpga/fpga/Makefile
Normal file
@@ -0,0 +1,89 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright (c) 2025 FPGA Ninja, LLC
|
||||
#
|
||||
# Authors:
|
||||
# - Alex Forencich
|
||||
#
|
||||
|
||||
# FPGA settings
|
||||
FPGA_PART = xcku3p-ffvb676-1-e
|
||||
FPGA_TOP = fpga
|
||||
FPGA_ARCH = kintexuplus
|
||||
|
||||
RTL_DIR = ../rtl
|
||||
LIB_DIR = ../lib
|
||||
TAXI_SRC_DIR = $(LIB_DIR)/taxi/src
|
||||
|
||||
# Files for synthesis
|
||||
SYN_FILES = $(RTL_DIR)/fpga.sv
|
||||
SYN_FILES += $(RTL_DIR)/fpga_core.sv
|
||||
SYN_FILES += $(TAXI_SRC_DIR)/cndm_proto/rtl/cndm_proto_pcie_us.f
|
||||
SYN_FILES += $(TAXI_SRC_DIR)/eth/rtl/us/taxi_eth_mac_25g_us.f
|
||||
SYN_FILES += $(TAXI_SRC_DIR)/axis/rtl/taxi_axis_async_fifo.f
|
||||
SYN_FILES += $(TAXI_SRC_DIR)/sync/rtl/taxi_sync_reset.sv
|
||||
SYN_FILES += $(TAXI_SRC_DIR)/sync/rtl/taxi_sync_signal.sv
|
||||
|
||||
# XDC files
|
||||
XDC_FILES = ../fpga.xdc
|
||||
XDC_FILES += $(TAXI_SRC_DIR)/eth/syn/vivado/taxi_eth_mac_fifo.tcl
|
||||
XDC_FILES += $(TAXI_SRC_DIR)/axis/syn/vivado/taxi_axis_async_fifo.tcl
|
||||
XDC_FILES += $(TAXI_SRC_DIR)/sync/syn/vivado/taxi_sync_reset.tcl
|
||||
XDC_FILES += $(TAXI_SRC_DIR)/sync/syn/vivado/taxi_sync_signal.tcl
|
||||
|
||||
# IP
|
||||
IP_TCL_FILES = $(TAXI_SRC_DIR)/eth/rtl/us/taxi_eth_phy_25g_us_gty_25g_156.tcl
|
||||
IP_TCL_FILES += ../ip/pcie4_uscale_plus_0.tcl
|
||||
|
||||
# Configuration
|
||||
CONFIG_TCL_FILES = ./config.tcl
|
||||
|
||||
include ../common/vivado.mk
|
||||
|
||||
program: $(PROJECT).bit
|
||||
echo "open_hw_manager" > program.tcl
|
||||
echo "connect_hw_server" >> program.tcl
|
||||
echo "open_hw_target" >> program.tcl
|
||||
echo "current_hw_device [lindex [get_hw_devices] 0]" >> program.tcl
|
||||
echo "refresh_hw_device -update_hw_probes false [current_hw_device]" >> program.tcl
|
||||
echo "set_property PROGRAM.FILE {$(PROJECT).bit} [current_hw_device]" >> program.tcl
|
||||
echo "program_hw_devices [current_hw_device]" >> program.tcl
|
||||
echo "exit" >> program.tcl
|
||||
vivado -nojournal -nolog -mode batch -source program.tcl
|
||||
|
||||
$(PROJECT).mcs $(PROJECT).prm: $(PROJECT).bit
|
||||
echo "write_cfgmem -force -format mcs -size 32 -interface SPIx4 -loadbit {up 0x0000000 $*.bit} -checksum -file $*.mcs" > generate_mcs.tcl
|
||||
echo "exit" >> generate_mcs.tcl
|
||||
vivado -nojournal -nolog -mode batch -source generate_mcs.tcl
|
||||
mkdir -p rev
|
||||
COUNT=100; \
|
||||
while [ -e rev/$*_rev$$COUNT.bit ]; \
|
||||
do COUNT=$$((COUNT+1)); done; \
|
||||
COUNT=$$((COUNT-1)); \
|
||||
for x in .mcs .prm; \
|
||||
do cp $*$$x rev/$*_rev$$COUNT$$x; \
|
||||
echo "Output: rev/$*_rev$$COUNT$$x"; done;
|
||||
|
||||
flash: $(PROJECT).mcs $(PROJECT).prm
|
||||
echo "open_hw" > flash.tcl
|
||||
echo "connect_hw_server" >> flash.tcl
|
||||
echo "open_hw_target" >> flash.tcl
|
||||
echo "current_hw_device [lindex [get_hw_devices] 0]" >> flash.tcl
|
||||
echo "refresh_hw_device -update_hw_probes false [current_hw_device]" >> flash.tcl
|
||||
echo "create_hw_cfgmem -hw_device [current_hw_device] [lindex [get_cfgmem_parts {mt25qu256-spi-x1_x2_x4}] 0]" >> flash.tcl
|
||||
echo "current_hw_cfgmem -hw_device [current_hw_device] [get_property PROGRAM.HW_CFGMEM [current_hw_device]]" >> flash.tcl
|
||||
echo "set_property PROGRAM.FILES [list \"$(PROJECT).mcs\"] [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.PRM_FILES [list \"$(PROJECT).prm\"] [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.ERASE 1 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.CFG_PROGRAM 1 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.VERIFY 1 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.CHECKSUM 0 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.ADDRESS_RANGE {use_file} [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.UNUSED_PIN_TERMINATION {pull-none} [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "create_hw_bitstream -hw_device [current_hw_device] [get_property PROGRAM.HW_CFGMEM_BITFILE [current_hw_device]]" >> flash.tcl
|
||||
echo "program_hw_devices [current_hw_device]" >> flash.tcl
|
||||
echo "refresh_hw_device [current_hw_device]" >> flash.tcl
|
||||
echo "program_hw_cfgmem -hw_cfgmem [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "boot_hw_device [current_hw_device]" >> flash.tcl
|
||||
echo "exit" >> flash.tcl
|
||||
vivado -nojournal -nolog -mode batch -source flash.tcl
|
||||
22
src/cndm_proto/board/AS02MC04/fpga/fpga/config.tcl
Normal file
22
src/cndm_proto/board/AS02MC04/fpga/fpga/config.tcl
Normal file
@@ -0,0 +1,22 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright (c) 2025 FPGA Ninja, LLC
|
||||
#
|
||||
# Authors:
|
||||
# - Alex Forencich
|
||||
#
|
||||
|
||||
set params [dict create]
|
||||
|
||||
# 10G MAC configuration
|
||||
dict set params CFG_LOW_LATENCY "1"
|
||||
dict set params COMBINED_MAC_PCS "1"
|
||||
dict set params MAC_DATA_W "64"
|
||||
|
||||
# apply parameters to top-level
|
||||
set param_list {}
|
||||
dict for {name value} $params {
|
||||
lappend param_list $name=$value
|
||||
}
|
||||
|
||||
set_property generic $param_list [get_filesets sources_1]
|
||||
89
src/cndm_proto/board/AS02MC04/fpga/fpga_10g/Makefile
Normal file
89
src/cndm_proto/board/AS02MC04/fpga/fpga_10g/Makefile
Normal file
@@ -0,0 +1,89 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright (c) 2025 FPGA Ninja, LLC
|
||||
#
|
||||
# Authors:
|
||||
# - Alex Forencich
|
||||
#
|
||||
|
||||
# FPGA settings
|
||||
FPGA_PART = xcku3p-ffvb676-1-e
|
||||
FPGA_TOP = fpga
|
||||
FPGA_ARCH = kintexuplus
|
||||
|
||||
RTL_DIR = ../rtl
|
||||
LIB_DIR = ../lib
|
||||
TAXI_SRC_DIR = $(LIB_DIR)/taxi/src
|
||||
|
||||
# Files for synthesis
|
||||
SYN_FILES = $(RTL_DIR)/fpga.sv
|
||||
SYN_FILES += $(RTL_DIR)/fpga_core.sv
|
||||
SYN_FILES += $(TAXI_SRC_DIR)/cndm_proto/rtl/cndm_proto_pcie_us.f
|
||||
SYN_FILES += $(TAXI_SRC_DIR)/eth/rtl/us/taxi_eth_mac_25g_us.f
|
||||
SYN_FILES += $(TAXI_SRC_DIR)/axis/rtl/taxi_axis_async_fifo.f
|
||||
SYN_FILES += $(TAXI_SRC_DIR)/sync/rtl/taxi_sync_reset.sv
|
||||
SYN_FILES += $(TAXI_SRC_DIR)/sync/rtl/taxi_sync_signal.sv
|
||||
|
||||
# XDC files
|
||||
XDC_FILES = ../fpga.xdc
|
||||
XDC_FILES += $(TAXI_SRC_DIR)/eth/syn/vivado/taxi_eth_mac_fifo.tcl
|
||||
XDC_FILES += $(TAXI_SRC_DIR)/axis/syn/vivado/taxi_axis_async_fifo.tcl
|
||||
XDC_FILES += $(TAXI_SRC_DIR)/sync/syn/vivado/taxi_sync_reset.tcl
|
||||
XDC_FILES += $(TAXI_SRC_DIR)/sync/syn/vivado/taxi_sync_signal.tcl
|
||||
|
||||
# IP
|
||||
IP_TCL_FILES = $(TAXI_SRC_DIR)/eth/rtl/us/taxi_eth_phy_10g_us_gty_156.tcl
|
||||
IP_TCL_FILES += ../ip/pcie4_uscale_plus_0.tcl
|
||||
|
||||
# Configuration
|
||||
CONFIG_TCL_FILES = ./config.tcl
|
||||
|
||||
include ../common/vivado.mk
|
||||
|
||||
program: $(PROJECT).bit
|
||||
echo "open_hw_manager" > program.tcl
|
||||
echo "connect_hw_server" >> program.tcl
|
||||
echo "open_hw_target" >> program.tcl
|
||||
echo "current_hw_device [lindex [get_hw_devices] 0]" >> program.tcl
|
||||
echo "refresh_hw_device -update_hw_probes false [current_hw_device]" >> program.tcl
|
||||
echo "set_property PROGRAM.FILE {$(PROJECT).bit} [current_hw_device]" >> program.tcl
|
||||
echo "program_hw_devices [current_hw_device]" >> program.tcl
|
||||
echo "exit" >> program.tcl
|
||||
vivado -nojournal -nolog -mode batch -source program.tcl
|
||||
|
||||
$(PROJECT).mcs $(PROJECT).prm: $(PROJECT).bit
|
||||
echo "write_cfgmem -force -format mcs -size 32 -interface SPIx4 -loadbit {up 0x0000000 $*.bit} -checksum -file $*.mcs" > generate_mcs.tcl
|
||||
echo "exit" >> generate_mcs.tcl
|
||||
vivado -nojournal -nolog -mode batch -source generate_mcs.tcl
|
||||
mkdir -p rev
|
||||
COUNT=100; \
|
||||
while [ -e rev/$*_rev$$COUNT.bit ]; \
|
||||
do COUNT=$$((COUNT+1)); done; \
|
||||
COUNT=$$((COUNT-1)); \
|
||||
for x in .mcs .prm; \
|
||||
do cp $*$$x rev/$*_rev$$COUNT$$x; \
|
||||
echo "Output: rev/$*_rev$$COUNT$$x"; done;
|
||||
|
||||
flash: $(PROJECT).mcs $(PROJECT).prm
|
||||
echo "open_hw" > flash.tcl
|
||||
echo "connect_hw_server" >> flash.tcl
|
||||
echo "open_hw_target" >> flash.tcl
|
||||
echo "current_hw_device [lindex [get_hw_devices] 0]" >> flash.tcl
|
||||
echo "refresh_hw_device -update_hw_probes false [current_hw_device]" >> flash.tcl
|
||||
echo "create_hw_cfgmem -hw_device [current_hw_device] [lindex [get_cfgmem_parts {mt25qu256-spi-x1_x2_x4}] 0]" >> flash.tcl
|
||||
echo "current_hw_cfgmem -hw_device [current_hw_device] [get_property PROGRAM.HW_CFGMEM [current_hw_device]]" >> flash.tcl
|
||||
echo "set_property PROGRAM.FILES [list \"$(PROJECT).mcs\"] [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.PRM_FILES [list \"$(PROJECT).prm\"] [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.ERASE 1 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.CFG_PROGRAM 1 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.VERIFY 1 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.CHECKSUM 0 [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.ADDRESS_RANGE {use_file} [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "set_property PROGRAM.UNUSED_PIN_TERMINATION {pull-none} [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "create_hw_bitstream -hw_device [current_hw_device] [get_property PROGRAM.HW_CFGMEM_BITFILE [current_hw_device]]" >> flash.tcl
|
||||
echo "program_hw_devices [current_hw_device]" >> flash.tcl
|
||||
echo "refresh_hw_device [current_hw_device]" >> flash.tcl
|
||||
echo "program_hw_cfgmem -hw_cfgmem [current_hw_cfgmem]" >> flash.tcl
|
||||
echo "boot_hw_device [current_hw_device]" >> flash.tcl
|
||||
echo "exit" >> flash.tcl
|
||||
vivado -nojournal -nolog -mode batch -source flash.tcl
|
||||
22
src/cndm_proto/board/AS02MC04/fpga/fpga_10g/config.tcl
Normal file
22
src/cndm_proto/board/AS02MC04/fpga/fpga_10g/config.tcl
Normal file
@@ -0,0 +1,22 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright (c) 2025 FPGA Ninja, LLC
|
||||
#
|
||||
# Authors:
|
||||
# - Alex Forencich
|
||||
#
|
||||
|
||||
set params [dict create]
|
||||
|
||||
# 10G MAC configuration
|
||||
dict set params CFG_LOW_LATENCY "1"
|
||||
dict set params COMBINED_MAC_PCS "1"
|
||||
dict set params MAC_DATA_W "32"
|
||||
|
||||
# apply parameters to top-level
|
||||
set param_list {}
|
||||
dict for {name value} $params {
|
||||
lappend param_list $name=$value
|
||||
}
|
||||
|
||||
set_property generic $param_list [get_filesets sources_1]
|
||||
@@ -0,0 +1,28 @@
|
||||
|
||||
create_ip -name pcie4_uscale_plus -vendor xilinx.com -library ip -module_name pcie4_uscale_plus_0
|
||||
|
||||
set_property -dict [list \
|
||||
CONFIG.PL_LINK_CAP_MAX_LINK_SPEED {8.0_GT/s} \
|
||||
CONFIG.PL_LINK_CAP_MAX_LINK_WIDTH {X8} \
|
||||
CONFIG.AXISTEN_IF_RC_STRADDLE {false} \
|
||||
CONFIG.axisten_if_enable_client_tag {true} \
|
||||
CONFIG.axisten_if_width {256_bit} \
|
||||
CONFIG.extended_tag_field {true} \
|
||||
CONFIG.pf0_dev_cap_max_payload {1024_bytes} \
|
||||
CONFIG.axisten_freq {250} \
|
||||
CONFIG.PF0_CLASS_CODE {058000} \
|
||||
CONFIG.PF0_DEVICE_ID {C070} \
|
||||
CONFIG.PF0_SUBSYSTEM_ID {0009} \
|
||||
CONFIG.PF0_SUBSYSTEM_VENDOR_ID {1ded} \
|
||||
CONFIG.pf0_bar0_64bit {true} \
|
||||
CONFIG.pf0_bar0_prefetchable {true} \
|
||||
CONFIG.pf0_bar0_scale {Megabytes} \
|
||||
CONFIG.pf0_bar0_size {16} \
|
||||
CONFIG.pf0_msi_enabled {true} \
|
||||
CONFIG.PF0_MSI_CAP_MULTIMSGCAP {32_vectors} \
|
||||
CONFIG.en_msi_per_vec_masking {true} \
|
||||
CONFIG.vendor_id {1234} \
|
||||
CONFIG.mode_selection {Advanced} \
|
||||
CONFIG.en_gt_selection {true} \
|
||||
CONFIG.select_quad {GTY_Quad_225} \
|
||||
] [get_ips pcie4_uscale_plus_0]
|
||||
1
src/cndm_proto/board/AS02MC04/fpga/lib/taxi
Symbolic link
1
src/cndm_proto/board/AS02MC04/fpga/lib/taxi
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../../../
|
||||
598
src/cndm_proto/board/AS02MC04/fpga/rtl/fpga.sv
Normal file
598
src/cndm_proto/board/AS02MC04/fpga/rtl/fpga.sv
Normal file
@@ -0,0 +1,598 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
|
||||
Copyright (c) 2014-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* FPGA top-level module
|
||||
*/
|
||||
module fpga #
|
||||
(
|
||||
// simulation (set to avoid vendor primitives)
|
||||
parameter logic SIM = 1'b0,
|
||||
// vendor ("GENERIC", "XILINX", "ALTERA")
|
||||
parameter string VENDOR = "XILINX",
|
||||
// device family
|
||||
parameter string FAMILY = "kintexuplus",
|
||||
// 10G/25G MAC configuration
|
||||
parameter logic CFG_LOW_LATENCY = 1'b1,
|
||||
parameter logic COMBINED_MAC_PCS = 1'b1,
|
||||
parameter MAC_DATA_W = 64
|
||||
)
|
||||
(
|
||||
/*
|
||||
* Clock: 100MHz LVDS
|
||||
* Reset: Push button, active high
|
||||
*/
|
||||
input wire logic clk_100mhz_p,
|
||||
input wire logic clk_100mhz_n,
|
||||
input wire logic reset,
|
||||
|
||||
/*
|
||||
* GPIO
|
||||
*/
|
||||
output wire logic sfp_led[2],
|
||||
output wire logic [3:0] led,
|
||||
output wire logic led_r,
|
||||
output wire logic led_g,
|
||||
output wire logic led_hb,
|
||||
|
||||
/*
|
||||
* Ethernet: SFP+
|
||||
*/
|
||||
input wire logic sfp_rx_p[2],
|
||||
input wire logic sfp_rx_n[2],
|
||||
output wire logic sfp_tx_p[2],
|
||||
output wire logic sfp_tx_n[2],
|
||||
input wire logic sfp_mgt_refclk_p,
|
||||
input wire logic sfp_mgt_refclk_n,
|
||||
input wire logic [1:0] sfp_npres,
|
||||
input wire logic [1:0] sfp_tx_fault,
|
||||
input wire logic [1:0] sfp_los,
|
||||
|
||||
/*
|
||||
* PCIe
|
||||
*/
|
||||
input wire logic [7:0] pcie_rx_p,
|
||||
input wire logic [7:0] pcie_rx_n,
|
||||
output wire logic [7:0] pcie_tx_p,
|
||||
output wire logic [7:0] pcie_tx_n,
|
||||
input wire logic pcie_refclk_p,
|
||||
input wire logic pcie_refclk_n,
|
||||
input wire logic pcie_reset_n
|
||||
);
|
||||
|
||||
// Clock and reset
|
||||
|
||||
wire clk_100mhz_ibufg;
|
||||
|
||||
// Internal 125 MHz clock
|
||||
wire clk_125mhz_mmcm_out;
|
||||
wire clk_125mhz_int;
|
||||
wire rst_125mhz_int;
|
||||
|
||||
wire mmcm_rst = !reset;
|
||||
wire mmcm_locked;
|
||||
wire mmcm_clkfb;
|
||||
|
||||
IBUFGDS #(
|
||||
.DIFF_TERM("FALSE"),
|
||||
.IBUF_LOW_PWR("FALSE")
|
||||
)
|
||||
clk_100mhz_ibufg_inst (
|
||||
.O (clk_100mhz_ibufg),
|
||||
.I (clk_100mhz_p),
|
||||
.IB (clk_100mhz_n)
|
||||
);
|
||||
|
||||
// MMCM instance
|
||||
MMCME4_BASE #(
|
||||
// 100 MHz input
|
||||
.CLKIN1_PERIOD(10.0),
|
||||
.REF_JITTER1(0.010),
|
||||
// 100 MHz input / 1 = 100 MHz PFD (range 10 MHz to 500 MHz)
|
||||
.DIVCLK_DIVIDE(1),
|
||||
// 100 MHz PFD * 10 = 1000 MHz VCO (range 800 MHz to 1600 MHz)
|
||||
.CLKFBOUT_MULT_F(10),
|
||||
.CLKFBOUT_PHASE(0),
|
||||
// 1250 MHz / 8 = 125 MHz, 0 degrees
|
||||
.CLKOUT0_DIVIDE_F(8),
|
||||
.CLKOUT0_DUTY_CYCLE(0.5),
|
||||
.CLKOUT0_PHASE(0),
|
||||
// Not used
|
||||
.CLKOUT1_DIVIDE(1),
|
||||
.CLKOUT1_DUTY_CYCLE(0.5),
|
||||
.CLKOUT1_PHASE(0),
|
||||
// Not used
|
||||
.CLKOUT2_DIVIDE(1),
|
||||
.CLKOUT2_DUTY_CYCLE(0.5),
|
||||
.CLKOUT2_PHASE(0),
|
||||
// Not used
|
||||
.CLKOUT3_DIVIDE(1),
|
||||
.CLKOUT3_DUTY_CYCLE(0.5),
|
||||
.CLKOUT3_PHASE(0),
|
||||
// Not used
|
||||
.CLKOUT4_DIVIDE(1),
|
||||
.CLKOUT4_DUTY_CYCLE(0.5),
|
||||
.CLKOUT4_PHASE(0),
|
||||
.CLKOUT4_CASCADE("FALSE"),
|
||||
// Not used
|
||||
.CLKOUT5_DIVIDE(1),
|
||||
.CLKOUT5_DUTY_CYCLE(0.5),
|
||||
.CLKOUT5_PHASE(0),
|
||||
// Not used
|
||||
.CLKOUT6_DIVIDE(1),
|
||||
.CLKOUT6_DUTY_CYCLE(0.5),
|
||||
.CLKOUT6_PHASE(0),
|
||||
|
||||
// optimized bandwidth
|
||||
.BANDWIDTH("OPTIMIZED"),
|
||||
// don't wait for lock during startup
|
||||
.STARTUP_WAIT("FALSE")
|
||||
)
|
||||
clk_mmcm_inst (
|
||||
// 100 MHz input
|
||||
.CLKIN1(clk_100mhz_ibufg),
|
||||
// direct clkfb feeback
|
||||
.CLKFBIN(mmcm_clkfb),
|
||||
.CLKFBOUT(mmcm_clkfb),
|
||||
.CLKFBOUTB(),
|
||||
// 125 MHz, 0 degrees
|
||||
.CLKOUT0(clk_125mhz_mmcm_out),
|
||||
.CLKOUT0B(),
|
||||
// Not used
|
||||
.CLKOUT1(),
|
||||
.CLKOUT1B(),
|
||||
// Not used
|
||||
.CLKOUT2(),
|
||||
.CLKOUT2B(),
|
||||
// Not used
|
||||
.CLKOUT3(),
|
||||
.CLKOUT3B(),
|
||||
// Not used
|
||||
.CLKOUT4(),
|
||||
// Not used
|
||||
.CLKOUT5(),
|
||||
// Not used
|
||||
.CLKOUT6(),
|
||||
// reset input
|
||||
.RST(mmcm_rst),
|
||||
// don't power down
|
||||
.PWRDWN(1'b0),
|
||||
// locked output
|
||||
.LOCKED(mmcm_locked)
|
||||
);
|
||||
|
||||
BUFG
|
||||
clk_125mhz_bufg_inst (
|
||||
.I(clk_125mhz_mmcm_out),
|
||||
.O(clk_125mhz_int)
|
||||
);
|
||||
|
||||
taxi_sync_reset #(
|
||||
.N(4)
|
||||
)
|
||||
sync_reset_125mhz_inst (
|
||||
.clk(clk_125mhz_int),
|
||||
.rst(~mmcm_locked),
|
||||
.out(rst_125mhz_int)
|
||||
);
|
||||
|
||||
// PCIe
|
||||
localparam AXIS_PCIE_DATA_W = 256;
|
||||
localparam AXIS_PCIE_KEEP_W = (AXIS_PCIE_DATA_W/32);
|
||||
localparam AXIS_PCIE_RC_USER_W = 75;
|
||||
localparam AXIS_PCIE_RQ_USER_W = 62;
|
||||
localparam AXIS_PCIE_CQ_USER_W = 85;
|
||||
localparam AXIS_PCIE_CC_USER_W = 33;
|
||||
localparam RC_STRADDLE = 1'b0; // AXIS_PCIE_DATA_W >= 256;
|
||||
|
||||
localparam RQ_SEQ_NUM_W = AXIS_PCIE_RQ_USER_W == 60 ? 4 : 6;
|
||||
localparam RQ_SEQ_NUM_EN = 1;
|
||||
|
||||
localparam PCIE_TAG_CNT = 64;
|
||||
localparam BAR0_APERTURE = 24;
|
||||
|
||||
logic pcie_user_clk;
|
||||
logic pcie_user_rst;
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(AXIS_PCIE_DATA_W),
|
||||
.KEEP_EN(1),
|
||||
.KEEP_W(AXIS_PCIE_KEEP_W),
|
||||
.USER_EN(1),
|
||||
.USER_W(AXIS_PCIE_CQ_USER_W)
|
||||
) axis_pcie_cq();
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(AXIS_PCIE_DATA_W),
|
||||
.KEEP_EN(1),
|
||||
.KEEP_W(AXIS_PCIE_KEEP_W),
|
||||
.USER_EN(1),
|
||||
.USER_W(AXIS_PCIE_CC_USER_W)
|
||||
) axis_pcie_cc();
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(AXIS_PCIE_DATA_W),
|
||||
.KEEP_EN(1),
|
||||
.KEEP_W(AXIS_PCIE_KEEP_W),
|
||||
.USER_EN(1),
|
||||
.USER_W(AXIS_PCIE_RQ_USER_W)
|
||||
) axis_pcie_rq();
|
||||
|
||||
taxi_axis_if #(
|
||||
.DATA_W(AXIS_PCIE_DATA_W),
|
||||
.KEEP_EN(1),
|
||||
.KEEP_W(AXIS_PCIE_KEEP_W),
|
||||
.USER_EN(1),
|
||||
.USER_W(AXIS_PCIE_RC_USER_W)
|
||||
) axis_pcie_rc();
|
||||
|
||||
wire [RQ_SEQ_NUM_W-1:0] pcie_rq_seq_num0;
|
||||
wire pcie_rq_seq_num_vld0;
|
||||
wire [RQ_SEQ_NUM_W-1:0] pcie_rq_seq_num1;
|
||||
wire pcie_rq_seq_num_vld1;
|
||||
|
||||
wire [2:0] cfg_max_payload;
|
||||
wire [2:0] cfg_max_read_req;
|
||||
wire [3:0] cfg_rcb_status;
|
||||
|
||||
wire [9:0] cfg_mgmt_addr;
|
||||
wire [7:0] cfg_mgmt_function_number;
|
||||
wire cfg_mgmt_write;
|
||||
wire [31:0] cfg_mgmt_write_data;
|
||||
wire [3:0] cfg_mgmt_byte_enable;
|
||||
wire cfg_mgmt_read;
|
||||
wire [31:0] cfg_mgmt_read_data;
|
||||
wire cfg_mgmt_read_write_done;
|
||||
|
||||
wire [7:0] cfg_fc_ph;
|
||||
wire [11:0] cfg_fc_pd;
|
||||
wire [7:0] cfg_fc_nph;
|
||||
wire [11:0] cfg_fc_npd;
|
||||
wire [7:0] cfg_fc_cplh;
|
||||
wire [11:0] cfg_fc_cpld;
|
||||
wire [2:0] cfg_fc_sel;
|
||||
|
||||
// wire [3:0] cfg_interrupt_msix_enable;
|
||||
// wire [3:0] cfg_interrupt_msix_mask;
|
||||
// wire [251:0] cfg_interrupt_msix_vf_enable;
|
||||
// wire [251:0] cfg_interrupt_msix_vf_mask;
|
||||
// wire [63:0] cfg_interrupt_msix_address;
|
||||
// wire [31:0] cfg_interrupt_msix_data;
|
||||
// wire cfg_interrupt_msix_int;
|
||||
// wire [1:0] cfg_interrupt_msix_vec_pending;
|
||||
// wire cfg_interrupt_msix_vec_pending_status;
|
||||
// wire cfg_interrupt_msix_sent;
|
||||
// wire cfg_interrupt_msix_fail;
|
||||
// wire [7:0] cfg_interrupt_msi_function_number;
|
||||
|
||||
wire [3:0] cfg_interrupt_msi_enable;
|
||||
wire [11:0] cfg_interrupt_msi_mmenable;
|
||||
wire cfg_interrupt_msi_mask_update;
|
||||
wire [31:0] cfg_interrupt_msi_data;
|
||||
wire [1:0] cfg_interrupt_msi_select;
|
||||
wire [31:0] cfg_interrupt_msi_int;
|
||||
wire [31:0] cfg_interrupt_msi_pending_status;
|
||||
wire cfg_interrupt_msi_pending_status_data_enable;
|
||||
wire [1:0] cfg_interrupt_msi_pending_status_function_num;
|
||||
wire cfg_interrupt_msi_sent;
|
||||
wire cfg_interrupt_msi_fail;
|
||||
wire [2:0] cfg_interrupt_msi_attr;
|
||||
wire cfg_interrupt_msi_tph_present;
|
||||
wire [1:0] cfg_interrupt_msi_tph_type;
|
||||
wire [7:0] cfg_interrupt_msi_tph_st_tag;
|
||||
wire [7:0] cfg_interrupt_msi_function_number;
|
||||
|
||||
wire stat_err_cor;
|
||||
wire stat_err_uncor;
|
||||
|
||||
wire pcie_sys_clk;
|
||||
wire pcie_sys_clk_gt;
|
||||
|
||||
IBUFDS_GTE4 #(
|
||||
.REFCLK_HROW_CK_SEL(2'b00)
|
||||
)
|
||||
ibufds_gte4_pcie_refclk_inst (
|
||||
.I (pcie_refclk_p),
|
||||
.IB (pcie_refclk_n),
|
||||
.CEB (1'b0),
|
||||
.O (pcie_sys_clk_gt),
|
||||
.ODIV2 (pcie_sys_clk)
|
||||
);
|
||||
|
||||
pcie4_uscale_plus_0
|
||||
pcie4_uscale_plus_inst (
|
||||
.pci_exp_txn(pcie_tx_n),
|
||||
.pci_exp_txp(pcie_tx_p),
|
||||
.pci_exp_rxn(pcie_rx_n),
|
||||
.pci_exp_rxp(pcie_rx_p),
|
||||
.user_clk(pcie_user_clk),
|
||||
.user_reset(pcie_user_rst),
|
||||
.user_lnk_up(),
|
||||
|
||||
.s_axis_rq_tdata(axis_pcie_rq.tdata),
|
||||
.s_axis_rq_tkeep(axis_pcie_rq.tkeep),
|
||||
.s_axis_rq_tlast(axis_pcie_rq.tlast),
|
||||
.s_axis_rq_tready(axis_pcie_rq.tready),
|
||||
.s_axis_rq_tuser(axis_pcie_rq.tuser),
|
||||
.s_axis_rq_tvalid(axis_pcie_rq.tvalid),
|
||||
|
||||
.m_axis_rc_tdata(axis_pcie_rc.tdata),
|
||||
.m_axis_rc_tkeep(axis_pcie_rc.tkeep),
|
||||
.m_axis_rc_tlast(axis_pcie_rc.tlast),
|
||||
.m_axis_rc_tready(axis_pcie_rc.tready),
|
||||
.m_axis_rc_tuser(axis_pcie_rc.tuser),
|
||||
.m_axis_rc_tvalid(axis_pcie_rc.tvalid),
|
||||
|
||||
.m_axis_cq_tdata(axis_pcie_cq.tdata),
|
||||
.m_axis_cq_tkeep(axis_pcie_cq.tkeep),
|
||||
.m_axis_cq_tlast(axis_pcie_cq.tlast),
|
||||
.m_axis_cq_tready(axis_pcie_cq.tready),
|
||||
.m_axis_cq_tuser(axis_pcie_cq.tuser),
|
||||
.m_axis_cq_tvalid(axis_pcie_cq.tvalid),
|
||||
|
||||
.s_axis_cc_tdata(axis_pcie_cc.tdata),
|
||||
.s_axis_cc_tkeep(axis_pcie_cc.tkeep),
|
||||
.s_axis_cc_tlast(axis_pcie_cc.tlast),
|
||||
.s_axis_cc_tready(axis_pcie_cc.tready),
|
||||
.s_axis_cc_tuser(axis_pcie_cc.tuser),
|
||||
.s_axis_cc_tvalid(axis_pcie_cc.tvalid),
|
||||
|
||||
.pcie_rq_seq_num0(pcie_rq_seq_num0),
|
||||
.pcie_rq_seq_num_vld0(pcie_rq_seq_num_vld0),
|
||||
.pcie_rq_seq_num1(pcie_rq_seq_num1),
|
||||
.pcie_rq_seq_num_vld1(pcie_rq_seq_num_vld1),
|
||||
.pcie_rq_tag0(),
|
||||
.pcie_rq_tag1(),
|
||||
.pcie_rq_tag_av(),
|
||||
.pcie_rq_tag_vld0(),
|
||||
.pcie_rq_tag_vld1(),
|
||||
|
||||
.pcie_tfc_nph_av(),
|
||||
.pcie_tfc_npd_av(),
|
||||
|
||||
.pcie_cq_np_req(1'b1),
|
||||
.pcie_cq_np_req_count(),
|
||||
|
||||
.cfg_phy_link_down(),
|
||||
.cfg_phy_link_status(),
|
||||
.cfg_negotiated_width(),
|
||||
.cfg_current_speed(),
|
||||
.cfg_max_payload(cfg_max_payload),
|
||||
.cfg_max_read_req(cfg_max_read_req),
|
||||
.cfg_function_status(),
|
||||
.cfg_function_power_state(),
|
||||
.cfg_vf_status(),
|
||||
.cfg_vf_power_state(),
|
||||
.cfg_link_power_state(),
|
||||
|
||||
.cfg_mgmt_addr(cfg_mgmt_addr),
|
||||
.cfg_mgmt_function_number(cfg_mgmt_function_number),
|
||||
.cfg_mgmt_write(cfg_mgmt_write),
|
||||
.cfg_mgmt_write_data(cfg_mgmt_write_data),
|
||||
.cfg_mgmt_byte_enable(cfg_mgmt_byte_enable),
|
||||
.cfg_mgmt_read(cfg_mgmt_read),
|
||||
.cfg_mgmt_read_data(cfg_mgmt_read_data),
|
||||
.cfg_mgmt_read_write_done(cfg_mgmt_read_write_done),
|
||||
.cfg_mgmt_debug_access(1'b0),
|
||||
|
||||
.cfg_err_cor_out(),
|
||||
.cfg_err_nonfatal_out(),
|
||||
.cfg_err_fatal_out(),
|
||||
.cfg_local_error_valid(),
|
||||
.cfg_local_error_out(),
|
||||
.cfg_ltssm_state(),
|
||||
.cfg_rx_pm_state(),
|
||||
.cfg_tx_pm_state(),
|
||||
.cfg_rcb_status(cfg_rcb_status),
|
||||
.cfg_obff_enable(),
|
||||
.cfg_pl_status_change(),
|
||||
.cfg_tph_requester_enable(),
|
||||
.cfg_tph_st_mode(),
|
||||
.cfg_vf_tph_requester_enable(),
|
||||
.cfg_vf_tph_st_mode(),
|
||||
|
||||
.cfg_msg_received(),
|
||||
.cfg_msg_received_data(),
|
||||
.cfg_msg_received_type(),
|
||||
.cfg_msg_transmit(1'b0),
|
||||
.cfg_msg_transmit_type(3'd0),
|
||||
.cfg_msg_transmit_data(32'd0),
|
||||
.cfg_msg_transmit_done(),
|
||||
|
||||
.cfg_fc_ph(cfg_fc_ph),
|
||||
.cfg_fc_pd(cfg_fc_pd),
|
||||
.cfg_fc_nph(cfg_fc_nph),
|
||||
.cfg_fc_npd(cfg_fc_npd),
|
||||
.cfg_fc_cplh(cfg_fc_cplh),
|
||||
.cfg_fc_cpld(cfg_fc_cpld),
|
||||
.cfg_fc_sel(cfg_fc_sel),
|
||||
|
||||
.cfg_dsn(64'd0),
|
||||
|
||||
.cfg_bus_number(),
|
||||
|
||||
.cfg_power_state_change_ack(1'b1),
|
||||
.cfg_power_state_change_interrupt(),
|
||||
|
||||
.cfg_err_cor_in(stat_err_cor),
|
||||
.cfg_err_uncor_in(stat_err_uncor),
|
||||
.cfg_flr_in_process(),
|
||||
.cfg_flr_done(4'd0),
|
||||
.cfg_vf_flr_in_process(),
|
||||
.cfg_vf_flr_func_num(8'd0),
|
||||
.cfg_vf_flr_done(8'd0),
|
||||
|
||||
.cfg_link_training_enable(1'b1),
|
||||
|
||||
.cfg_interrupt_int(4'd0),
|
||||
.cfg_interrupt_pending(4'd0),
|
||||
.cfg_interrupt_sent(),
|
||||
// .cfg_interrupt_msix_enable(cfg_interrupt_msix_enable),
|
||||
// .cfg_interrupt_msix_mask(cfg_interrupt_msix_mask),
|
||||
// .cfg_interrupt_msix_vf_enable(cfg_interrupt_msix_vf_enable),
|
||||
// .cfg_interrupt_msix_vf_mask(cfg_interrupt_msix_vf_mask),
|
||||
// .cfg_interrupt_msix_address(cfg_interrupt_msix_address),
|
||||
// .cfg_interrupt_msix_data(cfg_interrupt_msix_data),
|
||||
// .cfg_interrupt_msix_int(cfg_interrupt_msix_int),
|
||||
// .cfg_interrupt_msix_vec_pending(cfg_interrupt_msix_vec_pending),
|
||||
// .cfg_interrupt_msix_vec_pending_status(cfg_interrupt_msix_vec_pending_status),
|
||||
// .cfg_interrupt_msi_sent(cfg_interrupt_msix_sent),
|
||||
// .cfg_interrupt_msi_fail(cfg_interrupt_msix_fail),
|
||||
// .cfg_interrupt_msi_function_number(cfg_interrupt_msi_function_number),
|
||||
.cfg_interrupt_msi_enable(cfg_interrupt_msi_enable),
|
||||
.cfg_interrupt_msi_mmenable(cfg_interrupt_msi_mmenable),
|
||||
.cfg_interrupt_msi_mask_update(cfg_interrupt_msi_mask_update),
|
||||
.cfg_interrupt_msi_data(cfg_interrupt_msi_data),
|
||||
.cfg_interrupt_msi_select(cfg_interrupt_msi_select),
|
||||
.cfg_interrupt_msi_int(cfg_interrupt_msi_int),
|
||||
.cfg_interrupt_msi_pending_status(cfg_interrupt_msi_pending_status),
|
||||
.cfg_interrupt_msi_pending_status_data_enable(cfg_interrupt_msi_pending_status_data_enable),
|
||||
.cfg_interrupt_msi_pending_status_function_num(cfg_interrupt_msi_pending_status_function_num),
|
||||
.cfg_interrupt_msi_sent(cfg_interrupt_msi_sent),
|
||||
.cfg_interrupt_msi_fail(cfg_interrupt_msi_fail),
|
||||
.cfg_interrupt_msi_attr(cfg_interrupt_msi_attr),
|
||||
.cfg_interrupt_msi_tph_present(cfg_interrupt_msi_tph_present),
|
||||
.cfg_interrupt_msi_tph_type(cfg_interrupt_msi_tph_type),
|
||||
.cfg_interrupt_msi_tph_st_tag(cfg_interrupt_msi_tph_st_tag),
|
||||
.cfg_interrupt_msi_function_number(cfg_interrupt_msi_function_number),
|
||||
|
||||
.cfg_pm_aspm_l1_entry_reject(1'b0),
|
||||
.cfg_pm_aspm_tx_l0s_entry_disable(1'b0),
|
||||
|
||||
.cfg_hot_reset_out(),
|
||||
|
||||
.cfg_config_space_enable(1'b1),
|
||||
.cfg_req_pm_transition_l23_ready(1'b0),
|
||||
.cfg_hot_reset_in(1'b0),
|
||||
|
||||
.cfg_ds_port_number(8'd0),
|
||||
.cfg_ds_bus_number(8'd0),
|
||||
.cfg_ds_device_number(5'd0),
|
||||
|
||||
.sys_clk(pcie_sys_clk),
|
||||
.sys_clk_gt(pcie_sys_clk_gt),
|
||||
.sys_reset(pcie_reset_n),
|
||||
|
||||
.phy_rdy_out()
|
||||
);
|
||||
|
||||
fpga_core #(
|
||||
.SIM(SIM),
|
||||
.VENDOR(VENDOR),
|
||||
.FAMILY(FAMILY),
|
||||
.CFG_LOW_LATENCY(CFG_LOW_LATENCY),
|
||||
.COMBINED_MAC_PCS(COMBINED_MAC_PCS),
|
||||
.MAC_DATA_W(MAC_DATA_W)
|
||||
)
|
||||
core_inst (
|
||||
/*
|
||||
* Clock: 125 MHz
|
||||
* Synchronous reset
|
||||
*/
|
||||
.clk_125mhz(clk_125mhz_int),
|
||||
.rst_125mhz(rst_125mhz_int),
|
||||
|
||||
/*
|
||||
* GPIO
|
||||
*/
|
||||
.sfp_led(sfp_led),
|
||||
.led(led),
|
||||
.led_r(led_r),
|
||||
.led_g(led_g),
|
||||
.led_hb(led_hb),
|
||||
|
||||
/*
|
||||
* Ethernet: SFP+
|
||||
*/
|
||||
.sfp_rx_p(sfp_rx_p),
|
||||
.sfp_rx_n(sfp_rx_n),
|
||||
.sfp_tx_p(sfp_tx_p),
|
||||
.sfp_tx_n(sfp_tx_n),
|
||||
.sfp_mgt_refclk_p(sfp_mgt_refclk_p),
|
||||
.sfp_mgt_refclk_n(sfp_mgt_refclk_n),
|
||||
.sfp_mgt_refclk_out(),
|
||||
.sfp_npres(sfp_npres),
|
||||
.sfp_tx_fault(sfp_tx_fault),
|
||||
.sfp_los(sfp_los),
|
||||
|
||||
/*
|
||||
* PCIe
|
||||
*/
|
||||
.pcie_clk(pcie_user_clk),
|
||||
.pcie_rst(pcie_user_rst),
|
||||
.s_axis_pcie_cq(axis_pcie_cq),
|
||||
.m_axis_pcie_cc(axis_pcie_cc),
|
||||
.m_axis_pcie_rq(axis_pcie_rq),
|
||||
.s_axis_pcie_rc(axis_pcie_rc),
|
||||
|
||||
.pcie_rq_seq_num0(pcie_rq_seq_num0),
|
||||
.pcie_rq_seq_num_vld0(pcie_rq_seq_num_vld0),
|
||||
.pcie_rq_seq_num1(pcie_rq_seq_num1),
|
||||
.pcie_rq_seq_num_vld1(pcie_rq_seq_num_vld1),
|
||||
|
||||
.cfg_max_payload(cfg_max_payload),
|
||||
.cfg_max_read_req(cfg_max_read_req),
|
||||
.cfg_rcb_status(cfg_rcb_status),
|
||||
|
||||
.cfg_mgmt_addr(cfg_mgmt_addr),
|
||||
.cfg_mgmt_function_number(cfg_mgmt_function_number),
|
||||
.cfg_mgmt_write(cfg_mgmt_write),
|
||||
.cfg_mgmt_write_data(cfg_mgmt_write_data),
|
||||
.cfg_mgmt_byte_enable(cfg_mgmt_byte_enable),
|
||||
.cfg_mgmt_read(cfg_mgmt_read),
|
||||
.cfg_mgmt_read_data(cfg_mgmt_read_data),
|
||||
.cfg_mgmt_read_write_done(cfg_mgmt_read_write_done),
|
||||
|
||||
.cfg_fc_ph(cfg_fc_ph),
|
||||
.cfg_fc_pd(cfg_fc_pd),
|
||||
.cfg_fc_nph(cfg_fc_nph),
|
||||
.cfg_fc_npd(cfg_fc_npd),
|
||||
.cfg_fc_cplh(cfg_fc_cplh),
|
||||
.cfg_fc_cpld(cfg_fc_cpld),
|
||||
.cfg_fc_sel(cfg_fc_sel),
|
||||
|
||||
// .cfg_interrupt_msix_enable(cfg_interrupt_msix_enable),
|
||||
// .cfg_interrupt_msix_mask(cfg_interrupt_msix_mask),
|
||||
// .cfg_interrupt_msix_vf_enable(cfg_interrupt_msix_vf_enable),
|
||||
// .cfg_interrupt_msix_vf_mask(cfg_interrupt_msix_vf_mask),
|
||||
// .cfg_interrupt_msix_address(cfg_interrupt_msix_address),
|
||||
// .cfg_interrupt_msix_data(cfg_interrupt_msix_data),
|
||||
// .cfg_interrupt_msix_int(cfg_interrupt_msix_int),
|
||||
// .cfg_interrupt_msix_vec_pending(cfg_interrupt_msix_vec_pending),
|
||||
// .cfg_interrupt_msix_vec_pending_status(cfg_interrupt_msix_vec_pending_status),
|
||||
// .cfg_interrupt_msix_sent(cfg_interrupt_msix_sent),
|
||||
// .cfg_interrupt_msix_fail(cfg_interrupt_msix_fail),
|
||||
// .cfg_interrupt_msi_function_number(cfg_interrupt_msi_function_number),
|
||||
|
||||
.cfg_interrupt_msi_enable(cfg_interrupt_msi_enable),
|
||||
.cfg_interrupt_msi_mmenable(cfg_interrupt_msi_mmenable),
|
||||
.cfg_interrupt_msi_mask_update(cfg_interrupt_msi_mask_update),
|
||||
.cfg_interrupt_msi_data(cfg_interrupt_msi_data),
|
||||
.cfg_interrupt_msi_select(cfg_interrupt_msi_select),
|
||||
.cfg_interrupt_msi_int(cfg_interrupt_msi_int),
|
||||
.cfg_interrupt_msi_pending_status(cfg_interrupt_msi_pending_status),
|
||||
.cfg_interrupt_msi_pending_status_data_enable(cfg_interrupt_msi_pending_status_data_enable),
|
||||
.cfg_interrupt_msi_pending_status_function_num(cfg_interrupt_msi_pending_status_function_num),
|
||||
.cfg_interrupt_msi_sent(cfg_interrupt_msi_sent),
|
||||
.cfg_interrupt_msi_fail(cfg_interrupt_msi_fail),
|
||||
.cfg_interrupt_msi_attr(cfg_interrupt_msi_attr),
|
||||
.cfg_interrupt_msi_tph_present(cfg_interrupt_msi_tph_present),
|
||||
.cfg_interrupt_msi_tph_type(cfg_interrupt_msi_tph_type),
|
||||
.cfg_interrupt_msi_tph_st_tag(cfg_interrupt_msi_tph_st_tag),
|
||||
.cfg_interrupt_msi_function_number(cfg_interrupt_msi_function_number)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user