fpga: lib: add scalability to pcie host datapath components

This commit is contained in:
Javier Valenzuela 2026-07-23 10:59:46 -05:00 committed by Marian Koop
parent d4b827abea
commit 2cde73ab6f
10 changed files with 396 additions and 214 deletions

View File

@ -9,6 +9,8 @@ IOPORT2_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/io_port2/, \
./LvFpga_Chinch_Interface.v \
./LvFpga_Chinch_Interface.ngc \
./ioport2_msg_codec.v \
./ioport2_msg_pkg.sv \
./ctrlport_to_ioport2_stream.sv \
./pcie_pkt_route_specifier.v \
./pcie_axi_wb_conv.v \
./pcie_wb_reg_core.v \

View File

@ -0,0 +1,94 @@
//
// Copyright 2026 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: ctrlport_to_ioport2_stream
//
// Description:
//
// Bridges a CtrlPort slave interface to the ioport2 AXI-Stream message
// interface.
//
//
module ctrlport_to_ioport2_stream (
// CtrlPort slave interface
ctrlport_if.slave s_ctrlport,
// ioport2 AXI-Stream request port (to ioport2 slave)
output logic [63:0] regi_tdata,
output logic regi_tvalid,
input logic regi_tready,
// ioport2 AXI-Stream response port (from ioport2 slave)
input logic [63:0] rego_tdata,
input logic rego_tvalid,
output logic rego_tready
);
import ioport2_msg_pkg::*;
// ioport2 message encoding (see ioport2_msg_codec.v):
// msg[63] = rd_response = 0 (this is a request)
// msg[62] = wr_request
// msg[61] = rd_request
// msg[60] = half_word = 0 (always 32-bit)
// msg[59:52] = reserved
// msg[51:32] = address
// msg[31:0] = data
ioport2_msg_t encoded_msg;
assign encoded_msg = '{
rd_response: 1'b0,
wr_request : s_ctrlport.req.wr,
rd_request : s_ctrlport.req.rd,
half_word : 1'b0,
reserved : 8'h00,
address : s_ctrlport.req.addr,
data : s_ctrlport.req.data
};
// Latch the incoming one-cycle ctrlport request pulse into a held valid
// that drives regi_tvalid until the slave accepts it.
logic pending;
ioport2_msg_t msg_r;
logic held_rd;
logic accepted;
assign held_rd = msg_r.rd_request;
assign accepted = regi_tvalid & regi_tready & (!held_rd || rego_tvalid);
// Handle incoming requests and hold until accepted. Ignore any new request
// pulses while pending is high so the held message is preserved until the
// downstream side accepts it.
always_ff @(posedge s_ctrlport.clk) begin
if (s_ctrlport.rst) begin
pending <= 1'b0;
msg_r <= '0;
end else begin
if (accepted) begin
pending <= 1'b0;
end else if (!pending && (s_ctrlport.req.wr || s_ctrlport.req.rd)) begin
pending <= 1'b1;
msg_r <= encoded_msg;
end
end
end
assign regi_tdata = msg_r;
assign regi_tvalid = pending;
// regi_tready is expected to be asserted combinatorially as soon as
// regi_tvalid is seen. Reads also wait for rego_tvalid before the bridge
// treats the transaction as accepted.
assign rego_tready = 1'b1;
// Ack fires the same cycle the slave consumes the message. Reads additionally
// require rego_tvalid so resp.data is only returned with valid response data.
// For writes: rego_tvalid=0; resp_data is a don't-care (ctrlport master
// ignores resp_data for writes).
assign s_ctrlport.resp.ack = accepted;
assign s_ctrlport.resp.status = ctrlport_pkg::STS_OKAY;
assign s_ctrlport.resp.data = rego_tdata[31:0];
endmodule: ctrlport_to_ioport2_stream

View File

@ -0,0 +1,25 @@
//
// Copyright 2026 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Package: ioport2_msg_pkg
//
// Description:
//
// Shared packed-struct definition for ioport2 message encoding.
//
package ioport2_msg_pkg;
typedef struct packed {
logic rd_response;
logic wr_request;
logic rd_request;
logic half_word;
logic [ 7:0] reserved;
logic [19:0] address;
logic [31:0] data;
} ioport2_msg_t;
endpackage : ioport2_msg_pkg

View File

@ -1,29 +1,47 @@
//
// Copyright 2013 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
// Copyright 2025 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: pcie_lossy_samp_gate
//
// Description:
//
// This module implements a gate that can be used to drop samples from a
// stream. When 'drop' is high, the module will drop all incoming samples by
// not asserting o_tvalid and by asserting i_tready.
//
// Parameters:
// DATA_WIDTH : Width of the data bus
//
`default_nettype none
module pcie_lossy_samp_gate
#(
parameter DATA_WIDTH = 64
)
(
input [63:0] i_tdata,
input i_tvalid,
output i_tready,
output [63:0] o_tdata,
output o_tvalid,
input o_tready,
input drop,
output dropping
input wire [DATA_WIDTH-1:0] i_tdata,
input wire i_tvalid,
output wire i_tready,
output wire [DATA_WIDTH-1:0] o_tdata,
output wire o_tvalid,
input wire o_tready,
input wire drop,
output wire dropping
);
assign o_tdata = i_tdata;
assign o_tvalid = i_tvalid & ~drop;
assign i_tready = o_tready | drop;
assign dropping = drop & i_tvalid;
assign o_tdata = i_tdata;
assign o_tvalid = i_tvalid & ~drop;
assign i_tready = o_tready | drop;
assign dropping = drop & i_tvalid;
endmodule // pcie_lossy_samp_gate
`default_nettype wire

View File

@ -2,132 +2,149 @@
// Copyright 2013 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
// Copyright 2019 Ettus Research, a National Instruments Company
// Copyright 2025 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Quantize chdr packets to a configurable quantum value. o_tlast and
// i_tready will be held off until the entire quantized packet is xferred.
// If quantum is changed, it is the responsibility of the client to clear
// this module. error is asserted if a packet is larger than the quantum
// error can be reset by asserting reset or clear.
// Module: chdr_chunker
//
// Description:
//
// Quantize chdr packets to a configurable quantum value. o_tlast and
// i_tready will be held off until the entire quantized packet is xferred.
// If quantum is changed, it is the responsibility of the client to clear
// this module. error is asserted if a packet is larger than the quantum.
// error can be reset by asserting reset or clear.
//
// Parameters:
// DATA_WIDTH : Width of the data bus
// PAD_VALUE : Value used for padding
// HOLD_ERROR : If high, hold error until reset, else pulse
//
`default_nettype none
module chdr_chunker # (
parameter PAD_VALUE = 64'hFFFFFFFF_FFFFFFFF,
HOLD_ERROR = 1'b1 // If high, hold error until reset, else pulse
parameter DATA_WIDTH = 64,
parameter PAD_VALUE = {DATA_WIDTH{1'b1}},
parameter HOLD_ERROR = 1'b1 // If high, hold error until reset, else pulse
) (
input wire clk,
input wire reset,
input wire clear,
input wire [15:0] frame_size,
input wire clk,
input wire reset,
input wire clear,
input wire [15:0] frame_size,
input wire [63:0] i_tdata,
input wire i_tlast,
input wire i_tvalid,
output reg i_tready,
input wire [DATA_WIDTH-1:0] i_tdata,
input wire i_tlast,
input wire i_tvalid,
output reg i_tready,
output wire [63:0] o_tdata,
output wire o_tlast,
output reg o_tvalid,
input wire o_tready,
output wire [DATA_WIDTH-1:0] o_tdata,
output wire o_tlast,
output reg o_tvalid,
input wire o_tready,
output wire error
output wire error
);
localparam ST_HEADER = 2'd0;
localparam ST_DATA = 2'd1;
localparam ST_PADDING = 2'd2;
localparam ST_ERROR = 2'd3;
localparam ST_HEADER = 2'd0;
localparam ST_DATA = 2'd1;
localparam ST_PADDING = 2'd2;
localparam ST_ERROR = 2'd3;
reg [1:0] state;
reg [15:0] frame_rem;
reg [1:0] state;
reg [15:0] frame_rem;
// axi_len = ceil(length / 8)
wire [15:0] chdr_len_ceil = i_tdata[31:16] + 16'd7;
wire [15:0] axi_len = {3'b000, chdr_len_ceil[15:3]};
always @(posedge clk) begin
if (reset | clear) begin
state <= ST_HEADER;
frame_rem <= 16'd0;
end else if ((state == ST_ERROR) & i_tlast & i_tvalid & !HOLD_ERROR) begin
state <= ST_HEADER;
frame_rem <= 16'd0;
end else if (o_tready) begin
case (state)
ST_HEADER: begin
if (i_tvalid) begin
if ((axi_len > frame_size) | (axi_len == 16'd0))
state <= ST_ERROR;
else if (i_tlast)
state <= ST_PADDING;
else
state <= ST_DATA;
localparam BYTES_IN_DATA = DATA_WIDTH / 8;
localparam LEN_WIDTH = $clog2(BYTES_IN_DATA);
frame_rem <= frame_size - 16'd1;
end
end
// axi_len = ceil(length / BYTES_IN_DATA)
wire [15:0] cvita_len_ceil = i_tdata[31:16] + (BYTES_IN_DATA - 1);
wire [15:0] axi_len = {{LEN_WIDTH{1'b0}}, cvita_len_ceil[15:LEN_WIDTH]};
ST_DATA: begin
if (i_tvalid) begin
if (i_tlast) begin
state <= o_tlast ? ST_HEADER : ST_PADDING;
frame_rem <= o_tlast ? 16'd0 : (frame_rem - 16'd1);
end else begin
state <= ST_DATA;
frame_rem <= frame_rem - 16'd1;
end
end
end
ST_PADDING: begin
if (o_tlast) begin
state <= ST_HEADER;
frame_rem <= 16'd0;
end else begin
state <= ST_PADDING;
frame_rem <= frame_rem - 16'd1;
end
end
endcase
end
end
always @(*) begin
always @(posedge clk) begin
if (reset | clear) begin
state <= ST_HEADER;
frame_rem <= 16'd0;
end else if ((state == ST_ERROR) & i_tlast & i_tvalid & !HOLD_ERROR) begin
state <= ST_HEADER;
frame_rem <= 16'd0;
end else if (o_tready) begin
case (state)
ST_HEADER: begin
i_tready = o_tready;
o_tvalid = (axi_len <= frame_size) & (axi_len > 16'd0) & i_tvalid;
end
ST_HEADER: begin
if (i_tvalid) begin
if ((axi_len > frame_size) | (axi_len == 16'd0)) begin
state <= ST_ERROR;
end else if (i_tlast) begin
state <= ST_PADDING;
end else begin
state <= ST_DATA;
end
ST_DATA: begin
i_tready = o_tready;
o_tvalid = i_tvalid;
end
frame_rem <= frame_size - 16'd1;
end
end
ST_PADDING: begin
i_tready = 1'b0;
o_tvalid = 1'b1;
end
ST_DATA: begin
if (i_tvalid) begin
if (i_tlast) begin
state <= o_tlast ? ST_HEADER : ST_PADDING;
frame_rem <= o_tlast ? 16'd0 : (frame_rem - 16'd1);
end else begin
state <= ST_DATA;
frame_rem <= frame_rem - 16'd1;
end
end
end
ST_ERROR: begin
i_tready = 1'b1;
o_tvalid = 1'b0;
end
ST_PADDING: begin
if (o_tlast) begin
state <= ST_HEADER;
frame_rem <= 16'd0;
end else begin
state <= ST_PADDING;
frame_rem <= frame_rem - 16'd1;
end
end
default: begin
i_tready = 1'b0;
o_tvalid = 1'b0;
end
endcase
end
end
end
assign o_tlast = (frame_rem != 16'd0) ? (frame_rem == 16'd1) : (axi_len == 16'd1);
assign o_tdata = (state == ST_PADDING) ? PAD_VALUE : i_tdata;
always @(*) begin
case (state)
ST_HEADER: begin
i_tready = o_tready;
o_tvalid = (axi_len <= frame_size) & (axi_len > 16'd0) & i_tvalid;
end
assign error = (state == ST_ERROR);
ST_DATA: begin
i_tready = o_tready;
o_tvalid = i_tvalid;
end
ST_PADDING: begin
i_tready = 1'b0;
o_tvalid = 1'b1;
end
ST_ERROR: begin
i_tready = 1'b1;
o_tvalid = 1'b0;
end
default: begin
i_tready = 1'b0;
o_tvalid = 1'b0;
end
endcase
end
assign o_tlast = (frame_rem != 16'd0) ? (frame_rem == 16'd1) : (axi_len == 16'd1);
assign o_tdata = (state == ST_PADDING) ? PAD_VALUE : i_tdata;
assign error = (state == ST_ERROR);
endmodule // chdr_chunker

View File

@ -1,101 +1,123 @@
//
// Copyright 2013 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
// Copyright 2025 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: chdr_dechunker
//
// Description:
//
// Reconstruct chdr packets from quantized chdr packets. o_tlast and
// i_tready will be held off until the entire quantized packet is transferred.
// If quantum is changed, it is the responsibility of the client to clear
// this module. 'error' is asserted if a packet is larger than the quantum.
//
// Parameters:
// DATA_WIDTH : Width of the data bus
// PAD_VALUE : Value used for padding
//
`default_nettype none
module chdr_dechunker # (
parameter PAD_VALUE = 64'hFFFFFFFF_FFFFFFFF
parameter DATA_WIDTH = 64,
parameter PAD_VALUE = {DATA_WIDTH{1'b1}}
) (
input clk,
input reset,
input clear,
input [15:0] frame_size,
input wire clk,
input wire reset,
input wire clear,
input wire [15:0] frame_size,
input [63:0] i_tdata,
input i_tvalid,
output i_tready,
input wire [DATA_WIDTH-1:0] i_tdata,
input wire i_tvalid,
output wire i_tready,
output [63:0] o_tdata,
output o_tlast,
output o_tvalid,
input o_tready,
output wire [DATA_WIDTH-1:0] o_tdata,
output wire o_tlast,
output wire o_tvalid,
input wire o_tready,
output error
output wire error
);
localparam ST_HEADER = 2'd0;
localparam ST_DATA = 2'd1;
localparam ST_PADDING = 2'd2;
localparam ST_ERROR = 2'd3;
localparam ST_HEADER = 2'd0;
localparam ST_DATA = 2'd1;
localparam ST_PADDING = 2'd2;
localparam ST_ERROR = 2'd3;
reg [1:0] state;
reg [15:0] frame_rem, pkt_rem;
wire i_tlast;
reg [1:0] state;
reg [15:0] frame_rem, pkt_rem;
wire i_tlast;
// axi_len = ceil(length / 8)
wire [15:0] cvita_len_ceil = i_tdata[31:16] + 7;
wire [15:0] axi_len = {3'b000, cvita_len_ceil[15:3]};
localparam BYTES_IN_DATA = DATA_WIDTH / 8;
localparam LEN_WIDTH = $clog2(BYTES_IN_DATA);
always @(posedge clk) begin
if (reset | clear) begin
state <= ST_HEADER;
frame_rem <= 16'd0;
pkt_rem <= 16'd0;
end else if (i_tvalid & i_tready) begin
case (state)
ST_HEADER: begin
if (axi_len > frame_size)
state <= ST_ERROR;
else if (~o_tlast)
state <= ST_DATA;
else
state <= ST_PADDING;
// axi_len = ceil(length / BYTES_IN_DATA)
wire [15:0] cvita_len_ceil = i_tdata[31:16] + (BYTES_IN_DATA - 1);
wire [15:0] axi_len = {{LEN_WIDTH{1'b0}}, cvita_len_ceil[15:LEN_WIDTH]};
frame_rem <= frame_size - 16'd1;
pkt_rem <= axi_len - 16'd1;
end
always @(posedge clk) begin
if (reset | clear) begin
state <= ST_HEADER;
frame_rem <= 16'd0;
pkt_rem <= 16'd0;
end else if (i_tvalid & i_tready) begin
case (state)
ST_HEADER: begin
if (axi_len > frame_size) begin
state <= ST_ERROR;
end else if (~o_tlast) begin
state <= ST_DATA;
end else begin
state <= ST_PADDING;
end
ST_DATA: begin
if (o_tlast) begin
state <= i_tlast ? ST_HEADER : ST_PADDING;
pkt_rem <= 16'd0;
end else begin
state <= ST_DATA;
pkt_rem <= pkt_rem - 16'd1;
end
frame_rem <= frame_rem - 16'd1;
end
frame_rem <= frame_size - 16'd1;
pkt_rem <= axi_len - 16'd1;
end
ST_PADDING: begin
if (i_tlast) begin
state <= ST_HEADER;
frame_rem <= 16'd0;
end else begin
state <= ST_PADDING;
frame_rem <= frame_rem - 16'd1;
end
end
ST_DATA: begin
if (o_tlast) begin
state <= i_tlast ? ST_HEADER : ST_PADDING;
pkt_rem <= 16'd0;
end else begin
state <= ST_DATA;
pkt_rem <= pkt_rem - 16'd1;
end
frame_rem <= frame_rem - 16'd1;
end
ST_ERROR: begin
// We never leave the error state. However, we can't reach it
// with PCIe if we configure our transport according to the
// NI-RIO configuration.
state <= ST_ERROR;
end
endcase
end
end
ST_PADDING: begin
if (i_tlast) begin
state <= ST_HEADER;
frame_rem <= 16'd0;
end else begin
state <= ST_PADDING;
frame_rem <= frame_rem - 16'd1;
end
end
assign i_tready = o_tready | (state == ST_PADDING);
assign i_tlast = (frame_rem == 16'd1); //Temp signal
ST_ERROR: begin
// We never leave the error state. However, we can't reach it
// with PCIe if we configure our transport according to the
// NI-RIO configuration.
state <= ST_ERROR;
end
endcase
end
end
assign o_tvalid = i_tvalid & (state != ST_PADDING);
assign o_tlast = (pkt_rem != 0) ? (pkt_rem == 16'd1) : (axi_len == 16'd1);
assign o_tdata = i_tdata;
assign i_tready = o_tready | (state == ST_PADDING);
assign i_tlast = (frame_rem == 16'd1); //Temp signal
assign error = (state == ST_ERROR);
assign o_tvalid = i_tvalid & (state != ST_PADDING);
assign o_tlast = (pkt_rem != 0) ? (pkt_rem == 16'd1) : (axi_len == 16'd1);
assign o_tdata = i_tdata;
assign error = (state == ST_ERROR);
endmodule // chdr_dechunker
`default_nettype wire

View File

@ -13,4 +13,5 @@ eth_ipv4_chdr64_adapter.v \
eth_ipv4_chdr64_dispatch.v \
eth_interface.v \
eth_internal.v \
nirio_chdr_adapter.v \
))

View File

@ -1,12 +1,13 @@
//
// Copyright 2019 Ettus Research, A National Instruments Brand
// Copyright 2025 Ettus Research, A National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: nirio_chdr64_adapter
// Module: nirio_chdr_adapter
//
// Description: A transport adapter specific to connecting an NI-RIO streaming
// interface to CHDR. It assumes to be connected to x300_pcie_int.
// interface to CHDR. It assumes to be connected to a single AXI stream
// (as generate by x300_pcie_int and b310_pcie_int).
// See also chdr_xport_adapter_generic.
//
// The tuser inputs/outputs used for routing are the index of the DMA channel.
@ -14,10 +15,11 @@
// bits wide.
//
// Parameters:
// - PROTOVER: RFNoC protocol version {8'd<major>, 8'd<minor>}
// - MTU: Log2 of the MTU of the packet in 64-bit words
// - PROTOVER: RFNoC protocol version {8'd<major>, 8'd<minor>}
// - DATA_W: Word size for elements in stream to/from the MAC
// - MTU: Log2 of the MTU of the packet in DATA_W-bit words
// - RT_TBL_SIZE: Log2 of the depth of the return-address routing table
// - NODE_INST: The node type to return for a node-info discovery
// - NODE_INST: The node type to return for a node-info discovery
//
// Signals:
// - device_id : The ID of the device that has instantiated this module
@ -27,8 +29,9 @@
// - m_chdr_*: The output CHDR stream to the rfnoc infrastructure
//
module nirio_chdr64_adapter #(
module nirio_chdr_adapter #(
parameter [15:0] PROTOVER = {8'd1, 8'd0},
parameter DATA_W = 64,
parameter MTU = 10,
parameter RT_TBL_SIZE = 6,
parameter NODE_INST = 0,
@ -40,30 +43,30 @@ module nirio_chdr64_adapter #(
// Device info
input wire [15:0] device_id,
// AXI-Stream interface to/from DMA engines
input wire [63:0] s_dma_tdata,
input wire [DATA_W-1:0] s_dma_tdata,
input wire [DMA_ID_WIDTH-1:0] s_dma_tuser,
input wire s_dma_tlast,
input wire s_dma_tvalid,
output wire s_dma_tready,
output wire [63:0] m_dma_tdata,
output wire [DATA_W-1:0] m_dma_tdata,
output wire [DMA_ID_WIDTH-1:0] m_dma_tuser,
output wire m_dma_tlast,
output wire m_dma_tvalid,
input wire m_dma_tready,
// AXI-Stream interface to/from CHDR infrastructure
input wire [63:0] s_chdr_tdata,
input wire s_chdr_tlast,
input wire s_chdr_tvalid,
output wire s_chdr_tready,
output wire [63:0] m_chdr_tdata,
output wire m_chdr_tlast,
output wire m_chdr_tvalid,
input wire m_chdr_tready
input wire [DATA_W-1:0] s_chdr_tdata,
input wire s_chdr_tlast,
input wire s_chdr_tvalid,
output wire s_chdr_tready,
output wire [DATA_W-1:0] m_chdr_tdata,
output wire m_chdr_tlast,
output wire m_chdr_tvalid,
input wire m_chdr_tready
);
`include "../../lib/rfnoc/core/rfnoc_chdr_utils.vh"
`include "../../lib/rfnoc/core/rfnoc_chdr_internal_utils.vh"
`include "../../lib/rfnoc/xport/rfnoc_xport_types.vh"
`include "../core/rfnoc_chdr_utils.vh"
`include "../core/rfnoc_chdr_internal_utils.vh"
`include "./rfnoc_xport_types.vh"
//---------------------------------------
// CHDR Transport Adapter
@ -71,7 +74,7 @@ module nirio_chdr64_adapter #(
chdr_xport_adapter_generic #(
.PROTOVER (PROTOVER),
.CHDR_W (64),
.CHDR_W (DATA_W),
.USER_W (DMA_ID_WIDTH),
.TBL_SIZE (RT_TBL_SIZE),
.NODE_SUBTYPE (NODE_SUBTYPE_XPORT_NIRIO_CHDR64),

View File

@ -59,7 +59,6 @@ x300_core.v \
x300_sfpp_io_core.v \
x300_zpu_config.vhd \
x300_eth_interface.v \
nirio_chdr64_adapter.v \
soft_ctrl.v \
capture_ddrlvds.v \
gen_ddrlvds.v \

View File

@ -746,8 +746,9 @@ module bus_int #(
wire [63:0] c2p_tdata;
wire c2p_tlast, c2p_tvalid, c2p_tready;
// Transport adapter with the management interface
nirio_chdr64_adapter #(
nirio_chdr_adapter #(
.PROTOVER(RFNOC_PROTOVER),
.DATA_W(64),
.DMA_ID_WIDTH(DMA_RX_DEST_WIDTH)
) nirio_xport_adapter
(