xdma 回环
vivado 里有官方示例
fpga:pcie rx – axi-stream master – axi-stream slave – pcie tx
流程:电脑启动读取,然后电脑再在超时时间内写入。或者电脑启动写入,然后电脑再在超时时间内读出。只读取或只写入会报超时,所以需要双线程进行
细节:只有电脑启动读取,pcie tx – axi-stream slave 才会使能 ready 信号,然后 pcie rx – axi-stream master 才能向 pcie tx – axi-stream slave 写数据
pcie xdma 配置
verilog hdl
work.v:
module work #(
parameter C_DATA_WIDTH = 64
) (
// AXI streaming
output wire [C_DATA_WIDTH-1:0] s_axis_c2h_tdata_0,
output wire s_axis_c2h_tlast_0,
output wire s_axis_c2h_tvalid_0,
input wire s_axis_c2h_tready_0,
output wire [C_DATA_WIDTH/8-1:0] s_axis_c2h_tkeep_0,
input wire [C_DATA_WIDTH-1:0] m_axis_h2c_tdata_0,
input wire m_axis_h2c_tlast_0,
input wire m_axis_h2c_tvalid_0,
output wire m_axis_h2c_tready_0,
input wire [C_DATA_WIDTH/8-1:0] m_axis_h2c_tkeep_0,
// clk、rst
input wire sys_rst_n,
input wire user_resetn,
input wire user_clk,
input wire user_lnk_up,
// led
output wire [3:0] leds
);
reg [25:0] user_clk_heartbeat;
assign sys_resetn = sys_rst_n;
// led
assign leds[0] = sys_resetn;
assign leds[1] = user_resetn;
assign leds[2] = user_lnk_up;
assign leds[3] = user_clk_heartbeat[25];
// AXI streaming
assign s_axis_c2h_tdata_0 = m_axis_h2c_tdata_0;
assign s_axis_c2h_tlast_0 = m_axis_h2c_tlast_0;
assign s_axis_c2h_tvalid_0 = m_axis_h2c_tvalid_0;
assign s_axis_c2h_tkeep_0 = m_axis_h2c_tkeep_0;
assign m_axis_h2c_tready_0 = s_axis_c2h_tready_0;
// led blink
always @(posedge user_clk) begin
if(!sys_resetn) begin
user_clk_heartbeat <= 26'd0;
end else begin
user_clk_heartbeat <= user_clk_heartbeat + 1'b1;
end
end
endmodule
main.v:
module main #(
parameter PL_LINK_CAP_MAX_LINK_WIDTH = 2, // 1- X1; 2 - X2; 4 - X4; 8 - X8
parameter C_DATA_WIDTH = 64
) (
// pcie
output [(PL_LINK_CAP_MAX_LINK_WIDTH - 1) : 0] pci_exp_txp,
output [(PL_LINK_CAP_MAX_LINK_WIDTH - 1) : 0] pci_exp_txn,
input [(PL_LINK_CAP_MAX_LINK_WIDTH - 1) : 0] pci_exp_rxp,
input [(PL_LINK_CAP_MAX_LINK_WIDTH - 1) : 0] pci_exp_rxn,
// clk、rst
input sys_clk_p,
input sys_clk_n,
input sys_rst_n,
// led
output [3:0] leds
);
localparam C_NUM_USR_IRQ = 1;
// pcie clk、rst
wire sys_clk;
wire sys_rst_n_c;
IBUFDS_GTE2 refclk_ibuf (.O(sys_clk), .ODIV2(), .I(sys_clk_p), .CEB(1'b0), .IB(sys_clk_n));
IBUF sys_reset_n_ibuf (.O(sys_rst_n_c), .I(sys_rst_n));
// user
wire user_lnk_up;
wire user_clk;
wire user_resetn;
// irq
reg [C_NUM_USR_IRQ-1:0] usr_irq_req = 0;
// AXI streaming
wire [C_DATA_WIDTH-1:0] m_axis_h2c_tdata_0;
wire m_axis_h2c_tlast_0;
wire m_axis_h2c_tvalid_0;
wire m_axis_h2c_tready_0;
wire [C_DATA_WIDTH/8-1:0] m_axis_h2c_tkeep_0;
wire [C_DATA_WIDTH-1:0] s_axis_c2h_tdata_0;
wire s_axis_c2h_tlast_0;
wire s_axis_c2h_tvalid_0;
wire s_axis_c2h_tready_0;
wire [C_DATA_WIDTH/8-1:0] s_axis_c2h_tkeep_0;
xdma_0 xdma_0_i (
// clk、rst
.sys_rst_n(sys_rst_n_c),
.sys_clk (sys_clk),
// pcie
.pci_exp_txn(pci_exp_txn),
.pci_exp_txp(pci_exp_txp),
.pci_exp_rxn(pci_exp_rxn),
.pci_exp_rxp(pci_exp_rxp),
// AXI streaming
.s_axis_c2h_tdata_0 (s_axis_c2h_tdata_0),
.s_axis_c2h_tlast_0 (s_axis_c2h_tlast_0),
.s_axis_c2h_tvalid_0(s_axis_c2h_tvalid_0),
.s_axis_c2h_tready_0(s_axis_c2h_tready_0),
.s_axis_c2h_tkeep_0 (s_axis_c2h_tkeep_0),
.m_axis_h2c_tdata_0 (m_axis_h2c_tdata_0),
.m_axis_h2c_tlast_0 (m_axis_h2c_tlast_0),
.m_axis_h2c_tvalid_0(m_axis_h2c_tvalid_0),
.m_axis_h2c_tready_0(m_axis_h2c_tready_0),
.m_axis_h2c_tkeep_0 (m_axis_h2c_tkeep_0),
.axi_aclk (user_clk),
.axi_aresetn(user_resetn),
// led
.user_lnk_up(user_lnk_up),
// irq
.usr_irq_req(usr_irq_req),
.usr_irq_ack(),
.msix_enable(),
// Config managemnet interface
.cfg_mgmt_addr (19'b0),
.cfg_mgmt_write (1'b0),
.cfg_mgmt_write_data (32'b0),
.cfg_mgmt_byte_enable(4'b0),
.cfg_mgmt_read (1'b0),
.cfg_mgmt_read_data(),
.cfg_mgmt_read_write_done(),
.cfg_mgmt_type1_cfg_reg_access(1'b0)
);
work #(
.C_DATA_WIDTH(C_DATA_WIDTH)
) work_i (
// AXI streaming
.s_axis_c2h_tdata_0 (s_axis_c2h_tdata_0),
.s_axis_c2h_tlast_0 (s_axis_c2h_tlast_0),
.s_axis_c2h_tvalid_0(s_axis_c2h_tvalid_0),
.s_axis_c2h_tready_0(s_axis_c2h_tready_0),
.s_axis_c2h_tkeep_0 (s_axis_c2h_tkeep_0),
.m_axis_h2c_tdata_0 (m_axis_h2c_tdata_0),
.m_axis_h2c_tlast_0 (m_axis_h2c_tlast_0),
.m_axis_h2c_tvalid_0(m_axis_h2c_tvalid_0),
.m_axis_h2c_tready_0(m_axis_h2c_tready_0),
.m_axis_h2c_tkeep_0 (m_axis_h2c_tkeep_0),
// clk、rst
.user_clk(user_clk),
.user_resetn(user_resetn),
.sys_rst_n(sys_rst_n_c),
.user_lnk_up(user_lnk_up),
// led
.leds(leds)
);
endmodule
constr.xdc:
led 高电平亮。正常情况下,板卡上电待 fpga 程序加载完毕后,第四个灯会一直闪烁,亮灭切换间隔大约为 500ms;第一个灯也会亮,等待约3秒钟就会灭然后等待约 500ms 第一、二、三号灯一起长亮
## configure
set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]
set_property CONFIG_MODE SPIx4 [current_design]
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
set_property BITSTREAM.CONFIG.SPI_FALL_EDGE YES [current_design]
set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]
set_property BITSTREAM.CONFIG.UNUSEDPIN PULLNONE [current_design]
## pcie
# clock
set_property PACKAGE_PIN F6 [get_ports sys_clk_p]
# data
set_property PACKAGE_PIN D9 [get_ports {pci_exp_rxp[0]}]
set_property PACKAGE_PIN B10 [get_ports {pci_exp_rxp[1]}]
set_property PACKAGE_PIN D7 [get_ports {pci_exp_txp[0]}]
set_property PACKAGE_PIN B6 [get_ports {pci_exp_txp[1]}]
# rst
set_property -dict {PACKAGE_PIN N15 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]
## led
set_property -dict {PACKAGE_PIN V9 IOSTANDARD LVCMOS15} [get_ports {leds[0]}]
set_property -dict {PACKAGE_PIN Y8 IOSTANDARD LVCMOS15} [get_ports {leds[1]}]
set_property -dict {PACKAGE_PIN Y7 IOSTANDARD LVCMOS15} [get_ports {leds[2]}]
set_property -dict {PACKAGE_PIN W7 IOSTANDARD LVCMOS15} [get_ports {leds[3]}]
linux 下 rust 测试 xdma stream 的回环
main.rs:
fn main() {
use rand::Rng;
use std::fs::OpenOptions;
use std::io::{Read, Write};
let (read_tx, read_rx) = mpsc::channel();
thread::spawn(move || {
let mut buffer = vec![0u8; 200];
let mut file = OpenOptions::new()
.read(true)
.open("/dev/xdma0_c2h_0")
.unwrap();
file.read(buffer.as_mut_slice()).unwrap();
read_tx.send(buffer).unwrap();
});
thread::spawn(move || {
let mut buffer = vec![0u8; 200];
for index in 0..(buffer.len() / 2) {
let rng: u16 = rand::thread_rng().gen_range(0..=1023);
buffer[index * 2] = (rng >> 8) as u8;
buffer[index * 2 + 1] = rng as u8;
}
let mut file = OpenOptions::new()
.write(true)
.open("/dev/xdma0_h2c_0")
.unwrap();
file.write(buffer.as_slice()).unwrap();
});
let buffer = read_rx.recv().unwrap();
let mut data = vec![0u16; 100];
for index in 0..(buffer.len() / 2) {
data[index] = ((buffer[index * 2] as u16) << 8) | (buffer[index * 2 + 1] as u16);
}
printfln!("xdma: {}", data);
}
Cargo.toml:
[dependencies]
rand = "*"