zynq中axi_dma的sg模式采集--PL搭建

参考

BD

axidma_sg_system.tcl_xc7z020.tcl

axidma_three_step_system.tcl_xc7z010.tcl

AXI 地址映射表

模块 接口 Slave Interface Address Segment Base Address Range High Address
axi_dma_0 Data_S2MM S_AXI_HP0 HP0_DDR_LOWOCM 0x0000_0000 1G 0x3FFF_FFFF
axi_dma_0 Data_SG S_AXI_HP0 HP0_DDR_LOWOCM 0x0000_0000 1G 0x3FFF_FFFF
processing_system7_0 axi_dma_0/S_AXI_LITE S_AXI_LITE Reg 0x4040_0000 64K 0x4040_FFFF
processing_system7_0 top_0/s_axi s_axi reg0 0x43C0_0000 4K 0x43C0_0FFF

PIN.xdc

xc7z020

bash 复制代码
## IIC
set_property -dict {PACKAGE_PIN T11 IOSTANDARD LVCMOS33} [get_ports IIC_EMIO_scl_io]
set_property -dict {PACKAGE_PIN V5 IOSTANDARD LVCMOS33} [get_ports IIC_EMIO_sda_io]


## GPIO_EMIO
set_property -dict {PACKAGE_PIN U5 IOSTANDARD LVCMOS33} [get_ports GPIO_EMIO_tri_io[0]]
set_property -dict {PACKAGE_PIN V7 IOSTANDARD LVCMOS33} [get_ports GPIO_EMIO_tri_io[1]]
set_property -dict {PACKAGE_PIN W8 IOSTANDARD LVCMOS33} [get_ports GPIO_EMIO_tri_io[2]]
set_property -dict {PACKAGE_PIN U9 IOSTANDARD LVCMOS33} [get_ports GPIO_EMIO_tri_io[3]]
set_property -dict {PACKAGE_PIN U10 IOSTANDARD LVCMOS33} [get_ports GPIO_EMIO_tri_io[4]]
set_property -dict {PACKAGE_PIN W6 IOSTANDARD LVCMOS33} [get_ports GPIO_EMIO_tri_io[5]]
set_property -dict {PACKAGE_PIN Y7 IOSTANDARD LVCMOS33} [get_ports GPIO_EMIO_tri_io[6]]
set_property -dict {PACKAGE_PIN Y9 IOSTANDARD LVCMOS33} [get_ports GPIO_EMIO_tri_io[7]]

# led
set_property -dict {PACKAGE_PIN H15 IOSTANDARD LVCMOS33} [get_ports O_LED]

top.v

verilog 复制代码
`timescale 1ns/1ps
module top #(
    // AXI-Lite 地址宽度,常见 FPGA SoC 系统中一般为 32 位。
    parameter P_S_AXI_ADDR_WIDTH = 32,

    // AXI-Lite 数据宽度,本项目使用 32 位寄存器访问。
    parameter P_S_AXI_DATA_WIDTH = 32,

    // AXI-Stream 输出宽度,可设为 32 或 64。
    parameter P_M_AXIS_DATA_WIDTH = 32,

    // AXI-Lite 可访问的寄存器窗口大小,单位是字节。
    // 1024 字节可覆盖 slot0 的 129 个 32 位寄存器和后续 slot。
    parameter P_S_MEM_SIZE       = 1024,

    // 内部 SMMR 字节地址宽度。
    // 10 位地址可以覆盖 1024 字节空间。
    parameter P_REG_ADDR_WIDTH   = 10,

    // 系统时钟频率。
    // 当前顶层主要把 AXI 时钟传给各级模块,此参数预留给需要时序计算的模块使用。
    parameter P_CLK_FREQ         = 50_000_000
)(
    // AXI-Lite 全局时钟和低有效复位。
    input  wire                              s_axi_aclk,
    input  wire                              s_axi_aresetn,

    // AXI-Lite 写地址通道。
    input  wire [P_S_AXI_ADDR_WIDTH-1:0]     s_axi_awaddr,
    input  wire                              s_axi_awvalid,
    output wire                              s_axi_awready,

    // AXI-Lite 写数据通道。
    input  wire [P_S_AXI_DATA_WIDTH-1:0]     s_axi_wdata,
    input  wire [(P_S_AXI_DATA_WIDTH/8)-1:0] s_axi_wstrb,
    input  wire                              s_axi_wvalid,
    output wire                              s_axi_wready,

    // AXI-Lite 写响应通道。
    output wire [1:0]                        s_axi_bresp,
    output wire                              s_axi_bvalid,
    input  wire                              s_axi_bready,

    // AXI-Lite 读地址通道。
    input  wire [P_S_AXI_ADDR_WIDTH-1:0]     s_axi_araddr,
    input  wire                              s_axi_arvalid,
    output wire                              s_axi_arready,

    // AXI-Lite 读数据通道。
    output wire [P_S_AXI_DATA_WIDTH-1:0]     s_axi_rdata,
    output wire [1:0]                        s_axi_rresp,
    output wire                              s_axi_rvalid,
    input  wire                              s_axi_rready,

    // slot0 AXI-Stream 数据输出,接 AXI DMA S2MM 或 AXI-Stream FIFO。
    output wire [P_M_AXIS_DATA_WIDTH-1:0]     m_axis_tdata,
    output wire                              m_axis_tvalid,
    input  wire                              m_axis_tready,
    output wire                              m_axis_tlast,
    output wire [(P_M_AXIS_DATA_WIDTH/8)-1:0] m_axis_tkeep,


    // 示例外设输出。
    // 该信号由 slot1 内部的控制寄存器驱动。
    output wire                              o_led
);

    localparam C_SLOT_ADDR_WIDTH = P_REG_ADDR_WIDTH - 2;

    ////////////////////////////////////////////////////////////////////////////
    // AXI-Lite 桥接模块到 SMMR 路由模块之间的信号
    ////////////////////////////////////////////////////////////////////////////

    // AXI-Lite 写事务完成后产生的单周期写脉冲。
    wire                              w_reg_wr_en;

    // SMMR 写地址和读地址。
    // 这里仍然是字节地址,后面由 smmr_slot_router 转换成寄存器索引。
    wire [P_REG_ADDR_WIDTH-1:0]       w_reg_wr_addr;
    wire [P_REG_ADDR_WIDTH-1:0]       w_reg_rd_addr;

    // SMMR 写数据和读数据。
    wire [P_S_AXI_DATA_WIDTH-1:0]     w_reg_wr_data;
    wire [P_S_AXI_DATA_WIDTH-1:0]     w_reg_rd_data;

    ////////////////////////////////////////////////////////////////////////////
    // SMMR 路由模块到各个 slot 外设之间的信号
    ////////////////////////////////////////////////////////////////////////////

    // slot 地址以 32 位寄存器为单位,因此比字节地址少 2 位。
    // 例如字节地址 20 右移 2 位后变成寄存器地址 5。
    wire [C_SLOT_ADDR_WIDTH-1:0]      w_slot_wr_addr;
    wire [C_SLOT_ADDR_WIDTH-1:0]      w_slot_rd_addr;

    // 写数据会同时送到所有 slot,真正写入哪个 slot 由对应写使能决定。
    wire [P_S_AXI_DATA_WIDTH-1:0]     w_slot_wr_data;

    // slot0 的写使能和读数据。
    wire                              w_slot0_wr_en;
    wire [P_S_AXI_DATA_WIDTH-1:0]     w_slot0_rd_data;

    // slot1 的写使能和读数据。
    wire                              w_slot1_wr_en;
    wire [P_S_AXI_DATA_WIDTH-1:0]     w_slot1_rd_data;

    // AXIS 生成器输出的是一整帧宽数据,这里再拆成 32/64 位送 DMA。
    localparam integer C_AXIS_GEN_WORDS      = 22;
    localparam integer C_AXIS_GEN_DATA_WIDTH = C_AXIS_GEN_WORDS * 32;
    wire [C_AXIS_GEN_DATA_WIDTH-1:0]         w_axis_gen_tdata;
    wire                                     w_axis_gen_tvalid;
    wire                                     w_axis_gen_tready;

    ////////////////////////////////////////////////////////////////////////////
    // AXI-Lite 从接口转 SMMR 桥接模块
    //
    // 该模块负责处理 AXI-Lite 的地址、数据、响应和握手信号。
    // 对内部模块来说,它只输出简单的 SMMR 寄存器读写接口。
    ////////////////////////////////////////////////////////////////////////////
    axilite_slave_smmr_bridge #(
        .P_S_AXI_ADDR_WIDTH(P_S_AXI_ADDR_WIDTH),
        .P_S_AXI_DATA_WIDTH(P_S_AXI_DATA_WIDTH),
        .P_S_MEM_SIZE      (P_S_MEM_SIZE),
        .P_REG_ADDR_WIDTH  (P_REG_ADDR_WIDTH)
    ) u_axilite_slave_smmr_bridge (
        .s_axi_aclk     (s_axi_aclk),
        .s_axi_aresetn  (s_axi_aresetn),

        .s_axi_awaddr   (s_axi_awaddr),
        .s_axi_awvalid  (s_axi_awvalid),
        .s_axi_awready  (s_axi_awready),

        .s_axi_wdata    (s_axi_wdata),
        .s_axi_wstrb    (s_axi_wstrb),
        .s_axi_wvalid   (s_axi_wvalid),
        .s_axi_wready   (s_axi_wready),

        .s_axi_bresp    (s_axi_bresp),
        .s_axi_bvalid   (s_axi_bvalid),
        .s_axi_bready   (s_axi_bready),

        .s_axi_araddr   (s_axi_araddr),
        .s_axi_arvalid  (s_axi_arvalid),
        .s_axi_arready  (s_axi_arready),

        .s_axi_rdata    (s_axi_rdata),
        .s_axi_rresp    (s_axi_rresp),
        .s_axi_rvalid   (s_axi_rvalid),
        .s_axi_rready   (s_axi_rready),

        .o_reg_wr_en    (w_reg_wr_en),
        .o_reg_wr_addr  (w_reg_wr_addr),
        .o_reg_wr_data  (w_reg_wr_data),
        .o_reg_rd_addr  (w_reg_rd_addr),
        .i_reg_rd_data  (w_reg_rd_data)
    );

    ////////////////////////////////////////////////////////////////////////////
    // SMMR slot 路由模块
    //
    // 该模块根据 SMMR 地址范围选择目标 slot。
    // 写操作时,它给对应 slot 产生写使能。
    // 读操作时,它把被选中 slot 的读数据返回给 AXI-Lite 桥接模块。
    //
    // 当前地址映射:
    //   slot0:寄存器地址 0 到 128
    //          0 保留,1..64 为读区,65..128 为写区
    //   slot1:寄存器地址 129 到 132
    ////////////////////////////////////////////////////////////////////////////
    smmr_slot_router #(
        .P_ADDR_WIDTH (P_REG_ADDR_WIDTH),
        .P_DATA_WIDTH (P_S_AXI_DATA_WIDTH),
        .P_SLOT0_BASE (0),
        .P_SLOT0_SIZE (129),
        .P_SLOT1_BASE (129),
        .P_SLOT1_SIZE (4)
    ) u_smmr_slot_router (
        .i_clk          (s_axi_aclk),
        .i_rst_n        (s_axi_aresetn),

        .i_reg_wr_en    (w_reg_wr_en),
        .i_reg_wr_addr  (w_reg_wr_addr),
        .i_reg_wr_data  (w_reg_wr_data),
        .i_reg_rd_addr  (w_reg_rd_addr),
        .o_reg_rd_data  (w_reg_rd_data),

        .o_slot_wr_addr (w_slot_wr_addr),
        .o_slot_wr_data (w_slot_wr_data),
        .o_slot_rd_addr (w_slot_rd_addr),

        .o_slot0_wr_en  (w_slot0_wr_en),
        .i_slot0_rd_data(w_slot0_rd_data),

        .o_slot1_wr_en  (w_slot1_wr_en),
        .i_slot1_rd_data(w_slot1_rd_data)
    );



  //模拟生成采集流
   axis_generator#(
      .P_CHANNEL_COUNT(C_AXIS_GEN_WORDS-1)
   ) u_axis_generator(
        .i_clk(s_axi_aclk),
        .i_rst_n(s_axi_aresetn),
        .m_axis_tdata(w_axis_gen_tdata),
        .m_axis_tvalid(w_axis_gen_tvalid),
        .m_axis_tready(w_axis_gen_tready)
   );

   //采集流位宽转换,8行数据1个bd(生成m_axis_tlast)
    axis_width_adapter #(
        .P_S_AXIS_DATA_WIDTH(C_AXIS_GEN_DATA_WIDTH),
        .P_M_AXIS_DATA_WIDTH(P_M_AXIS_DATA_WIDTH)
    ) u_axis_width_adapter (
        .i_clk          (s_axi_aclk),
        .i_rst_n        (s_axi_aresetn),
        .s_axis_tdata   (w_axis_gen_tdata),
        .s_axis_tvalid  (w_axis_gen_tvalid),
        .s_axis_tready  (w_axis_gen_tready),
        .m_axis_tdata   (m_axis_tdata),
        .m_axis_tvalid  (m_axis_tvalid),
        .m_axis_tready  (m_axis_tready),
        .m_axis_tlast   (m_axis_tlast),
        .m_axis_tkeep   (m_axis_tkeep)
    );


    slot0 #(
        .P_ADDR_WIDTH (C_SLOT_ADDR_WIDTH),
        .P_DATA_WIDTH (P_S_AXI_DATA_WIDTH),
        .P_SLOT_BASE  (0),
        .P_REG_COUNT  (129)
    ) u_slot0 (
        .i_clk          (s_axi_aclk),
        .i_rst_n        (s_axi_aresetn),
        .i_reg_wr_en    (w_slot0_wr_en),
        .i_reg_wr_addr  (w_slot_wr_addr),
        .i_reg_wr_data  (w_slot_wr_data),
        .i_reg_rd_addr  (w_slot_rd_addr),
        .o_reg_rd_data  (w_slot0_rd_data)
    );


endmodule

axilite_slave_smmr_bridge.v

verilog 复制代码
//AXI-Lite Slave 接口转 SMMR 简单寄存器接口 桥接模块
module axilite_slave_smmr_bridge #(
    parameter P_S_AXI_ADDR_WIDTH = 32,
    parameter P_S_AXI_DATA_WIDTH = 32,
    parameter P_S_MEM_SIZE       = 512, //内部存储器大小(单位:字节),对应128个32位寄存器
    parameter P_REG_ADDR_WIDTH   = 9
)(
    // =========================
    // AXI-Lite (PS -> PL)
    // =========================
    input  wire                              s_axi_aclk,
    input  wire                              s_axi_aresetn,
    input  wire [P_S_AXI_ADDR_WIDTH-1:0]     s_axi_awaddr,
    input  wire                              s_axi_awvalid,
    output reg                               s_axi_awready,
    input  wire [P_S_AXI_DATA_WIDTH-1:0]     s_axi_wdata,
    input  wire [(P_S_AXI_DATA_WIDTH/8)-1:0] s_axi_wstrb,
    input  wire                              s_axi_wvalid,
    output reg                               s_axi_wready,
    output reg  [1:0]                        s_axi_bresp,
    output reg                               s_axi_bvalid,
    input  wire                              s_axi_bready,
    input  wire [P_S_AXI_ADDR_WIDTH-1:0]     s_axi_araddr,
    input  wire                              s_axi_arvalid,
    output reg                               s_axi_arready,
    output reg  [P_S_AXI_DATA_WIDTH-1:0]     s_axi_rdata,
    output reg  [1:0]                        s_axi_rresp,
    output reg                               s_axi_rvalid,
    input  wire                              s_axi_rready,

    // =========================
    // Simple register bus
    // =========================
    output reg                               o_reg_wr_en,
    output reg  [P_REG_ADDR_WIDTH-1:0]       o_reg_wr_addr,
    output reg  [P_S_AXI_DATA_WIDTH-1:0]     o_reg_wr_data,
    output reg  [P_REG_ADDR_WIDTH-1:0]       o_reg_rd_addr,
    input  wire [P_S_AXI_DATA_WIDTH-1:0]     i_reg_rd_data
);


    reg [P_S_AXI_ADDR_WIDTH-1:0] r_write_addr;   // 写地址锁存:AW 通道先到、W 通道后到时,暂存本次写地址。
    reg [P_S_AXI_DATA_WIDTH-1:0] r_write_data;   // 写数据锁存:W 通道先到、AW 通道后到时,暂存本次写数据。
    reg                          r_aw_valid;     // 写地址锁存有效标志:AWADDR 已锁存,等待 WDATA 到齐后写寄存器。
    reg                          r_w_valid;      // 写数据锁存有效标志:WDATA 已锁存,等待 AWADDR 到齐后写寄存器。
    reg                          r_read_pending;   // 读请求处理中标志:ARADDR 发给下游后一拍,再采样读数据返回 AXI R 通道。

    wire w_aw_fire = s_axi_awvalid && s_axi_awready; // 写地址通道握手完成,本拍接收 AWADDR。
    wire w_w_fire  = s_axi_wvalid  && s_axi_wready;  // 写数据通道握手完成,本拍接收 WDATA。
    wire w_ar_fire = s_axi_arvalid && s_axi_arready; // 读地址通道握手完成,本拍接收 ARADDR。
    // 写地址和写数据可能不同拍到达;这里先判断本拍是否已经具备完整写事务。
    wire w_write_addr_ready = r_aw_valid || w_aw_fire;
    wire w_write_data_ready = r_w_valid  || w_w_fire;
    wire w_write_ready      = !s_axi_bvalid && w_write_addr_ready && w_write_data_ready;
    wire [P_S_AXI_ADDR_WIDTH-1:0] w_write_addr = w_aw_fire ? s_axi_awaddr : r_write_addr;
    wire [P_S_AXI_DATA_WIDTH-1:0] w_write_data = w_w_fire  ? s_axi_wdata  : r_write_data;
    // 读写通道独立仲裁。AXI-Lite 允许读写同时发起,这里不让读事务等待写事务结束。
    always @(*) begin
        s_axi_awready = s_axi_aresetn && !r_aw_valid && !s_axi_bvalid;
        s_axi_wready  = s_axi_aresetn && !r_w_valid  && !s_axi_bvalid;
        s_axi_arready = s_axi_aresetn && !r_read_pending && !s_axi_rvalid;
    end

    always @(posedge s_axi_aclk or negedge s_axi_aresetn) begin
        if (!s_axi_aresetn) begin
            s_axi_bresp   <= 2'b00;
            s_axi_bvalid  <= 1'b0;
            s_axi_rdata   <= {P_S_AXI_DATA_WIDTH{1'b0}};
            s_axi_rresp   <= 2'b00;
            s_axi_rvalid  <= 1'b0;
            r_write_addr  <= {P_S_AXI_ADDR_WIDTH{1'b0}};
            r_write_data  <= {P_S_AXI_DATA_WIDTH{1'b0}};
            r_aw_valid <= 1'b0;
            r_w_valid  <= 1'b0;
            r_read_pending <= 1'b0;
            o_reg_wr_en     <= 1'b0;
            o_reg_wr_addr   <= {P_REG_ADDR_WIDTH{1'b0}};
            o_reg_wr_data   <= {P_S_AXI_DATA_WIDTH{1'b0}};
            o_reg_rd_addr   <= {P_REG_ADDR_WIDTH{1'b0}};
        end else begin
            o_reg_wr_en <= 1'b0;
            if (w_aw_fire) begin
                r_aw_valid <= 1'b1;
                r_write_addr <= s_axi_awaddr;
            end

            if (w_w_fire) begin
                r_w_valid <= 1'b1;
                r_write_data <= s_axi_wdata;
            end

            if (w_write_ready) begin
                r_aw_valid <= 1'b0;
                r_w_valid  <= 1'b0;
                s_axi_bresp  <= 2'b00;
                s_axi_bvalid <= 1'b1;
                o_reg_wr_en   <= 1'b1;
                o_reg_wr_addr <= w_write_addr[P_REG_ADDR_WIDTH-1:0];
                o_reg_wr_data <= w_write_data;
            end else if (s_axi_bvalid && s_axi_bready) begin
                s_axi_bvalid <= 1'b0;
            end

            if (w_ar_fire) begin
                r_read_pending <= 1'b1;
                o_reg_rd_addr  <= s_axi_araddr[P_REG_ADDR_WIDTH-1:0];
            end else if (r_read_pending) begin
                r_read_pending <= 1'b0;
                s_axi_rdata    <= i_reg_rd_data;
                s_axi_rresp    <= 2'b00;
                s_axi_rvalid   <= 1'b1;
            end else if (s_axi_rvalid && s_axi_rready) begin
                s_axi_rvalid <= 1'b0;
            end
        end
    end

endmodule

smmr_slot_router.v

verilog 复制代码
////////////////////////////////////////////////////////////////////////////////
// 简单内存映射寄存器路由模块
// - 将字节地址转换成 32 位寄存器索引
// - 将一路 SMMR 接口路由到多个寄存器槽
////////////////////////////////////////////////////////////////////////////////
module smmr_slot_router #(
    // AXI-Lite 侧使用字节地址,寄存器槽侧使用寄存器索引。
    parameter P_ADDR_WIDTH = 10,
    parameter P_DATA_WIDTH = 32,
    // 寄存器槽地址范围以 32 位寄存器为单位。
    parameter P_SLOT0_BASE = 0,
    parameter P_SLOT0_SIZE = 129,
    parameter P_SLOT1_BASE = 129,
    parameter P_SLOT1_SIZE = 4
)(
    input  wire                    i_clk,
    input  wire                    i_rst_n,

    // 上游简单寄存器总线,地址为字节地址。
    input  wire                    i_reg_wr_en,
    input  wire [P_ADDR_WIDTH-1:0] i_reg_wr_addr,
    input  wire [P_DATA_WIDTH-1:0] i_reg_wr_data,
    input  wire [P_ADDR_WIDTH-1:0] i_reg_rd_addr,
    output reg  [P_DATA_WIDTH-1:0] o_reg_rd_data,

    // 下游寄存器槽总线,地址为 32 位寄存器索引。
    output wire [P_ADDR_WIDTH-3:0] o_slot_wr_addr,
    output wire [P_DATA_WIDTH-1:0] o_slot_wr_data,
    output wire [P_ADDR_WIDTH-3:0] o_slot_rd_addr,
    output wire                    o_slot0_wr_en,
    input  wire [P_DATA_WIDTH-1:0] i_slot0_rd_data,
    output wire                    o_slot1_wr_en,
    input  wire [P_DATA_WIDTH-1:0] i_slot1_rd_data
);
    wire [P_ADDR_WIDTH-3:0] w_reg_wr_addr_u32 = i_reg_wr_addr >> 2;
    wire [P_ADDR_WIDTH-3:0] w_reg_rd_addr_u32 = i_reg_rd_addr >> 2;

    reg r_slot0_wr_sel;
    reg r_slot1_wr_sel;
    reg r_slot0_rd_sel;
    reg r_slot1_rd_sel;

    always @(*) begin
        r_slot0_wr_sel = 1'b0;
        r_slot1_wr_sel = 1'b0;

        if ((w_reg_wr_addr_u32 >= P_SLOT0_BASE) && (w_reg_wr_addr_u32 < (P_SLOT0_BASE + P_SLOT0_SIZE))) begin
            r_slot0_wr_sel = 1'b1;
        end else if ((w_reg_wr_addr_u32 >= P_SLOT1_BASE) && (w_reg_wr_addr_u32 < (P_SLOT1_BASE + P_SLOT1_SIZE))) begin
            r_slot1_wr_sel = 1'b1;
        end
    end

    always @(*) begin
        r_slot0_rd_sel = 1'b0;
        r_slot1_rd_sel = 1'b0;

        if ((w_reg_rd_addr_u32 >= P_SLOT0_BASE) && (w_reg_rd_addr_u32 < (P_SLOT0_BASE + P_SLOT0_SIZE))) begin
            r_slot0_rd_sel = 1'b1;
        end else if ((w_reg_rd_addr_u32 >= P_SLOT1_BASE) && (w_reg_rd_addr_u32 < (P_SLOT1_BASE + P_SLOT1_SIZE))) begin
            r_slot1_rd_sel = 1'b1;
        end
    end

    assign o_slot0_wr_en  = i_reg_wr_en && r_slot0_wr_sel;
    assign o_slot1_wr_en  = i_reg_wr_en && r_slot1_wr_sel;
    assign o_slot_wr_addr = w_reg_wr_addr_u32;
    assign o_slot_wr_data = i_reg_wr_data;
    assign o_slot_rd_addr = w_reg_rd_addr_u32;

    always @(*) begin
        if (r_slot0_rd_sel) begin
            o_reg_rd_data = i_slot0_rd_data;
        end else if (r_slot1_rd_sel) begin
            o_reg_rd_data = i_slot1_rd_data;
        end else begin
            o_reg_rd_data = {P_DATA_WIDTH{1'b0}};
        end
    end

endmodule

slot0.v

verilog 复制代码
module slot0 #(
    // 槽内部寄存器索引宽度。当前顶层传入 8bit,可覆盖 0..255 个 32bit 寄存器索引。
    parameter P_ADDR_WIDTH = 8,
    // 数据宽度。当前工程使用 32bit,与 AXI-Lite 数据宽度和 AXI-Stream tdata 一致。
    parameter P_DATA_WIDTH = 32,
    // 本 slot 在全局寄存器索引空间中的起始地址。slot0 当前固定从 0 开始。
    parameter [P_ADDR_WIDTH-1:0] P_SLOT_BASE = 0,
    // slot0 寄存器数量:0..128,共 129 个 32bit 寄存器索引。
    parameter P_REG_COUNT  = 129
)(
    // 与 AXI-Lite/SMMR 和 AXI-Stream 共用同一时钟域。
    input  wire                    i_clk,
    // 低有效复位。
    input  wire                    i_rst_n,

    // 简单寄存器写接口:上游 AXI-Lite 桥在完成一次写事务时给一个写使能脉冲。
    input  wire                    i_reg_wr_en,
    // 寄存器索引地址,不是字节地址。比如 slot0[65] 的地址就是 65。
    input  wire [P_ADDR_WIDTH-1:0] i_reg_wr_addr,
    input  wire [P_DATA_WIDTH-1:0] i_reg_wr_data,
    // 简单寄存器读接口:组合读,地址命中时直接返回 r_mem 中的数据。
    input  wire [P_ADDR_WIDTH-1:0] i_reg_rd_addr,
    output reg  [P_DATA_WIDTH-1:0] o_reg_rd_data
);

    reg [P_DATA_WIDTH-1:0] r_mem [0:P_REG_COUNT-1];

    integer i;
    always @(posedge i_clk or negedge i_rst_n) begin
        if (!i_rst_n) begin
            for (i = 0; i < P_REG_COUNT; i = i + 1) begin
                r_mem[i] <= {P_DATA_WIDTH{1'b0}};
            end
        end else if (i_reg_wr_en && (i_reg_wr_addr >= P_SLOT_BASE) && (i_reg_wr_addr < (P_SLOT_BASE + P_REG_COUNT))) begin
            r_mem[i_reg_wr_addr-P_SLOT_BASE] <= i_reg_wr_data;
        end
    end
    always @(*) begin
        if ((i_reg_rd_addr >= P_SLOT_BASE) && (i_reg_rd_addr < (P_SLOT_BASE + P_REG_COUNT))) begin
            o_reg_rd_data = r_mem[i_reg_rd_addr-P_SLOT_BASE];
        end else begin
            o_reg_rd_data = {P_DATA_WIDTH{1'b0}};
        end
    end

endmodule

axis_generator.v

verilog 复制代码
`timescale 1ns / 1ps

//
// 示例:
//   P_CHANNEL_COUNT = 21
//   seq    ad0    ad1    ad2    ad3    ad4    ad5    ad6    ad7    ad8    ad9    ad10   ad11   ad12   ad13   ad14   ad15   ad16   ad17   ad18   ad19   ad20
//   0000   0001   0002   0003   0004   0005   0006   0007   0008   0009   0010   0011   0012   0013   0014   0015   0016   0017   0018   0019   0020   0021
//   0001   0022   0023   0024   0025   0026   0027   0028   0029   0030   0031   0032   0033   0034   0035   0036   0037   0038   0039   0040   0041   0042

module axis_generator #(
    // 时钟频率
    parameter P_CLK_FREQ = 100_000_000,
    // 采样周期
    //10ms采样1次 10ms*50=500ms 写1次ddr
    parameter P_SAMPLE_PERIOD = 1000000,
    // AD数量
    parameter P_CHANNEL_COUNT = 21
)(
    input wire i_clk,
    input wire i_rst_n,
    //(P_CHANNEL_COUNT+1)个u32
    output reg [(P_CHANNEL_COUNT+1)*32-1:0] m_axis_tdata,
    output reg                         m_axis_tvalid,
    input  wire                        m_axis_tready
);

localparam P_AXIS_WIDTH = (P_CHANNEL_COUNT+1)*32;
// 采样计时器
reg [31:0] r_timer;
// 行号
reg [31:0] r_seq;
// AD数据计数
reg [31:0] r_ad_counter;
integer i;
////////////////////////////////////////////////////////
// AXIS DATA GENERATOR
////////////////////////////////////////////////////////
always @(posedge i_clk or negedge i_rst_n) begin
    if(!i_rst_n) begin
        r_timer <= 0;
        r_seq <= 0;
        r_ad_counter <= 0;
        m_axis_tdata <= 0;
        m_axis_tvalid <= 0;
    end
    else begin
        //--------------------------------------------------
        // AXIS握手
        //--------------------------------------------------
        if(m_axis_tvalid) begin
            if(m_axis_tready) m_axis_tvalid <= 0;
        end
        //--------------------------------------------------
        //旧数据收到后,才生成新数据
        //--------------------------------------------------
        else if(r_timer >= P_SAMPLE_PERIOD-1) begin
            r_timer <= 0;
            // 清空
            m_axis_tdata <= 0;
            // seq
            m_axis_tdata[P_AXIS_WIDTH-1 -:32] <= r_seq;
            //AD
            for(i=0;i<P_CHANNEL_COUNT;i=i+1) begin
                m_axis_tdata[P_AXIS_WIDTH-33-i*32 -:32]<= r_ad_counter+i+1;
            end
            //数据有效
            m_axis_tvalid <= 1'b1;
            // 行号+1
            r_seq <= r_seq + 1;
            // AD计数增加AD数量
            r_ad_counter <= r_ad_counter + P_CHANNEL_COUNT;
        end
        else begin
            r_timer <= r_timer + 1;
        end
    end
end
endmodule

axis_width_adapter.v

verilog 复制代码
`timescale 1ns / 1ps

// -----------------------------------------------------------------------------
// 功能说明
// 这个模块把一行宽总线输入拆成多拍窄总线输出。
// 例如:704bit 输入、32bit 输出时,一行会拆成 22 个 beat。
// 数据顺序采用 MSB 先出,与 axis_generator 的打包顺序一致。
// m_axis_tlast 不再按"每行都拉高",而是按"每 P_LINES_PER_TLAST 行拉高一次"。
// 默认 P_LINES_PER_TLAST=8,即 8 行产生 1 个 last。
// -----------------------------------------------------------------------------
module axis_width_adapter #(
    parameter P_S_AXIS_DATA_WIDTH = 704,   // 输入 AXIS 宽度,通常对应一整行数据
    parameter P_M_AXIS_DATA_WIDTH = 32,    // 输出 AXIS 宽度,通常对应单个 beat
    parameter P_LINES_PER_TLAST   = 8      // 每多少行输出一次 tlast,最小值为 1
)(
    input  wire                              i_clk,
    input  wire                              i_rst_n,
    input  wire [P_S_AXIS_DATA_WIDTH-1:0]    s_axis_tdata,
    input  wire                              s_axis_tvalid,
    output wire                              s_axis_tready,
    output wire [P_M_AXIS_DATA_WIDTH-1:0]    m_axis_tdata,
    output wire                              m_axis_tvalid,
    input  wire                              m_axis_tready,
    output wire                              m_axis_tlast,
    output wire [(P_M_AXIS_DATA_WIDTH/8)-1:0] m_axis_tkeep
);

    // 一行输入可以拆出的 beat 数。
    localparam integer P_BEAT_COUNT = P_S_AXIS_DATA_WIDTH / P_M_AXIS_DATA_WIDTH;
    // 用于检查输入宽度是否能被输出宽度整除。
    localparam integer P_REMAINDER  = P_S_AXIS_DATA_WIDTH % P_M_AXIS_DATA_WIDTH;
    // 0 基准下,分组最后一行的索引。
    localparam integer P_LAST_LINE   = P_LINES_PER_TLAST - 1;

    // 当前缓存的一整行输入数据。
    reg [P_S_AXIS_DATA_WIDTH-1:0] r_frame_data;
    // 当前行已经输出到第几个 beat。
    reg [31:0]                    r_beat_idx;
    // 当前处在第几行,用于控制 tlast 的行分组。
    reg [31:0]                    r_line_idx;
    // 当前缓存里是否还有未输出完的一整行数据。
    reg                           r_frame_valid;

    // 输出侧握手成功,表示当前 beat 被下游真正接收。
    wire w_axis_fire = m_axis_tvalid && m_axis_tready;
    // 当前行是否已经输出到最后一个 beat。
    wire w_last_beat = (r_beat_idx == P_BEAT_COUNT-1);
    // 当前行是否正好是本轮 tlast 分组中的最后一行。
    wire w_last_line  = (r_line_idx == P_LAST_LINE);
    // 只有当前一整行已经输出完,模块才允许接收下一行。
    wire w_accept_new = !r_frame_valid || (w_axis_fire && w_last_beat);

    // 输入端始终以"一行"为单位握手。
    assign s_axis_tready = w_accept_new;
    // 只要有一整行缓存,输出就保持 valid。
    assign m_axis_tvalid = r_frame_valid;
    // 只有在"最后一行"的"最后一个 beat"上,才拉高 tlast。
    assign m_axis_tlast  = r_frame_valid && w_last_beat && w_last_line;
    // 本模块输出的是纯数据拍,没有字节裁剪,所有 byte lane 都有效。
    assign m_axis_tkeep  = {(P_M_AXIS_DATA_WIDTH/8){1'b1}};
    // 从缓存的一整行里,按 MSB 先出的顺序切出当前 beat。
    assign m_axis_tdata  = r_frame_data[P_S_AXIS_DATA_WIDTH-1 - r_beat_idx*P_M_AXIS_DATA_WIDTH -: P_M_AXIS_DATA_WIDTH];

    always @(posedge i_clk or negedge i_rst_n) begin
        if (!i_rst_n) begin
            // 复位后先把行索引放到"最后一行"的位置,
            // 这样第一次接收新行时会回卷到 0,逻辑写法更统一。
            r_frame_data  <= {P_S_AXIS_DATA_WIDTH{1'b0}};
            r_beat_idx    <= 32'd0;
            r_line_idx    <= P_LAST_LINE;
            r_frame_valid <= 1'b0;
        end else begin
            if (s_axis_tvalid && s_axis_tready) begin
                // 新的一行输入被接受:
                // 1) 锁存整行数据
                // 2) beat 索引清零,准备从最高位开始输出
                // 3) 行计数加一/回卷,用来决定这一行是否触发 tlast
                r_frame_data  <= s_axis_tdata;
                r_beat_idx    <= 32'd0;
                if (r_line_idx == P_LAST_LINE) begin
                    r_line_idx <= 32'd0;
                end else begin
                    r_line_idx <= r_line_idx + 32'd1;
                end
                // 标记当前有一整行数据等待输出。
                r_frame_valid <= 1'b1;
            end else if (w_axis_fire) begin
                // 当前 beat 已被下游接收,继续输出下一拍。
                if (w_last_beat) begin
                    // 一整行已经输出结束,清空 valid,等待下一行。
                    r_beat_idx    <= 32'd0;
                    r_frame_valid <= 1'b0;
                end else begin
                    // 还没到最后一个 beat,beat 索引加一继续拆包。
                    r_beat_idx <= r_beat_idx + 32'd1;
                end
            end
        end
    end

    generate
        if (P_LINES_PER_TLAST < 1) begin : g_tlast_error
            // 参数非法时直接报错,避免出现负索引或死逻辑。
            initial begin
                $fatal(1, "axis_width_adapter: P_LINES_PER_TLAST must be >= 1");
            end
        end

        if ((P_S_AXIS_DATA_WIDTH < P_M_AXIS_DATA_WIDTH) || (P_REMAINDER != 0)) begin : g_width_error
            // 输入宽度必须是输出宽度的整数倍,否则无法整拍拆分。
            initial begin
                $fatal(1, "axis_width_adapter: source width must be an integer multiple of output width");
            end
        end
    endgenerate

endmodule
相关推荐
传奇开心果编程1 小时前
【Rust入门知识点学与练】第3课:String 与 &str 的区别
开发语言·学习·rust
小雨笙笙2 小时前
机器学习:线性回归学习
学习·机器学习·线性回归
HugoStudio_SWAN2 小时前
洛谷 P1420 / P1179 / B4262 最长连号、数字统计与词频统计——统计的三种面孔
c++·学习·程序人生·算法
是隼人3 小时前
buuctf-pwn [HarekazeCTF2019]baby_rop2(64位ret2libc)题解(学习过程持续更新)
c语言·学习·安全·pwn入门·ctf入门
aprilaaaaa4 小时前
(TC397 学习笔记)二、ICU和TIM的时钟计算
笔记·学习·fpga开发
xian_wwq5 小时前
【学习笔记】-深度认知系列-第8讲主流AI模型横向评测——GPT、Claude、Gemini、盘古、文心、通义
人工智能·笔记·学习
大牧师5 小时前
TypeORM 学习教程
数据库·sql·学习·node.js·orm·nest.js·typeorm
晨欣6 小时前
NVIDIA GPU 架构演进学习笔记(GPT-5.6 Terra 生成)
笔记·学习·gpu·显卡·nvidia·英伟达
小雪崩6 小时前
嵌入式学习 day40:数据库
数据库·学习