黑金例程 AD9238 例程解析(一)
AD9238_sample IP解析
从IP核来看,有 AXI_Lite总线接口,AXI_Stream总线,还有adc信号接口(单独的ADC采样时钟,复位,data信号线)
提供可配置的参数 AXIdata信号线宽度、AXI 地址宽度
头部文件
基本上是IP核的接口定义
`timescale 1 ns / 1 ps
module ad9238_sample_v1_0 #
(
// Users to add parameters here
// User parameters ends
// Do not modify the parameters beyond this line
// Parameters of Axi Slave Bus Interface S00_AXI
parameter integer C_S00_AXI_DATA_WIDTH = 32,
parameter integer C_S00_AXI_ADDR_WIDTH = 4
)
(
// Users to add ports here
input adc_clk,
input adc_rst_n,
input[11:0] adc_data,
output [15:0] m00_axis_tdata,
output [1:0] m00_axis_tkeep,
output m00_axis_tlast,
input m00_axis_tready,
output m00_axis_tvalid,
input m00_axis_aresetn,
input m00_axis_aclk,
// User ports ends
// Do not modify the ports beyond this line
// Ports of Axi Slave Bus Interface S00_AXI
input wire s00_axi_aclk,
input wire s00_axi_aresetn,
input wire [C_S00_AXI_ADDR_WIDTH-1 : 0] s00_axi_awaddr,
input wire [2 : 0] s00_axi_awprot,
input wire s00_axi_awvalid,
output wire s00_axi_awready,
input wire [C_S00_AXI_DATA_WIDTH-1 : 0] s00_axi_wdata,
input wire [(C_S00_AXI_DATA_WIDTH/8)-1 : 0] s00_axi_wstrb,
input wire s00_axi_wvalid,
output wire s00_axi_wready,
output wire [1 : 0] s00_axi_bresp,
output wire s00_axi_bvalid,
input wire s00_axi_bready,
input wire [C_S00_AXI_ADDR_WIDTH-1 : 0] s00_axi_araddr,
input wire [2 : 0] s00_axi_arprot,
input wire s00_axi_arvalid,
output wire s00_axi_arready,
output wire [C_S00_AXI_DATA_WIDTH-1 : 0] s00_axi_rdata,
output wire [1 : 0] s00_axi_rresp,
output wire s00_axi_rvalid,
input wire s00_axi_rready
);
// Instantiation of Axi Bus Interface S00_AXI
ad9238_sample_v1_0_S00_AXI # (
.C_S_AXI_DATA_WIDTH(C_S00_AXI_DATA_WIDTH),
.C_S_AXI_ADDR_WIDTH(C_S00_AXI_ADDR_WIDTH)
) ad9238_sample_v1_0_S00_AXI_inst (
.adc_clk (adc_clk ),
.adc_rst_n (adc_rst_n ),
.adc_data (adc_data ),
.m00_axis_tdata (m00_axis_tdata ),
.m00_axis_tkeep (m00_axis_tkeep ),
.m00_axis_tlast (m00_axis_tlast ),
.m00_axis_tready (m00_axis_tready ),
.m00_axis_tvalid (m00_axis_tvalid ),
.m00_axis_aresetn (m00_axis_aresetn),
.m00_axis_aclk (m00_axis_aclk ),
.S_AXI_ACLK(s00_axi_aclk),
.S_AXI_ARESETN(s00_axi_aresetn),
.S_AXI_AWADDR(s00_axi_awaddr),
.S_AXI_AWPROT(s00_axi_awprot),
.S_AXI_AWVALID(s00_axi_awvalid),
.S_AXI_AWREADY(s00_axi_awready),
.S_AXI_WDATA(s00_axi_wdata),
.S_AXI_WSTRB(s00_axi_wstrb),
.S_AXI_WVALID(s00_axi_wvalid),
.S_AXI_WREADY(s00_axi_wready),
.S_AXI_BRESP(s00_axi_bresp),
.S_AXI_BVALID(s00_axi_bvalid),
.S_AXI_BREADY(s00_axi_bready),
.S_AXI_ARADDR(s00_axi_araddr),
.S_AXI_ARPROT(s00_axi_arprot),
.S_AXI_ARVALID(s00_axi_arvalid),
.S_AXI_ARREADY(s00_axi_arready),
.S_AXI_RDATA(s00_axi_rdata),
.S_AXI_RRESP(s00_axi_rresp),
.S_AXI_RVALID(s00_axi_rvalid),
.S_AXI_RREADY(s00_axi_rready)
);
// Add user logic here
// User logic ends
endmodule
采样模块代码解析
首先定义AXI_Lite总线接口
总线所需要信号,我们要注意AXI_lite和AXI_full的区别
// Global Clock Signal
input wire S_AXI_ACLK,
// Global Reset Signal. This Signal is Active LOW
input wire S_AXI_ARESETN,
// Write address (issued by master, acceped by Slave)
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,
// Write channel Protection type. This signal indicates the
// privilege and security level of the transaction, and whether
// the transaction is a data access or an instruction access.
input wire [2 : 0] S_AXI_AWPROT,
// Write address valid. This signal indicates that the master signaling
// valid write address and control information.
input wire S_AXI_AWVALID,
// Write address ready. This signal indicates that the slave is ready
// to accept an address and associated control signals.
output wire S_AXI_AWREADY,
// Write data (issued by master, acceped by Slave)
input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA,
// Write strobes. This signal indicates which byte lanes hold
// valid data. There is one write strobe bit for each eight
// bits of the write data bus.
input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB,
// Write valid. This signal indicates that valid write
// data and strobes are available.
input wire S_AXI_WVALID,
// Write ready. This signal indicates that the slave
// can accept the write data.
output wire S_AXI_WREADY,
// Write response. This signal indicates the status
// of the write transaction.
output wire [1 : 0] S_AXI_BRESP,
// Write response valid. This signal indicates that the channel
// is signaling a valid write response.
output wire S_AXI_BVALID,
// Response ready. This signal indicates that the master
// can accept a write response.
input wire S_AXI_BREADY,
// Read address (issued by master, acceped by Slave)
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR,
// Protection type. This signal indicates the privilege
// and security level of the transaction, and whether the
// transaction is a data access or an instruction access.
input wire [2 : 0] S_AXI_ARPROT,
// Read address valid. This signal indicates that the channel
// is signaling valid read address and control information.
input wire S_AXI_ARVALID,
// Read address ready. This signal indicates that the slave is
// ready to accept an address and associated control signals.
output wire S_AXI_ARREADY,
// Read data (issued by slave)
output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA,
// Read response. This signal indicates the status of the
// read transfer.
output wire [1 : 0] S_AXI_RRESP,
// Read valid. This signal indicates that the channel is
// signaling the required read data.
output wire S_AXI_RVALID,
// Read ready. This signal indicates that the master can
// accept the read data and response information.
input wire S_AXI_RREADY
AXI_LITE源码学习笔记 - 沉默改良者 - 博客园 (cnblogs.com)
这篇博客详细的介绍了AXI_lite模块以及如何修改他的源码
这篇博客详细的介绍了AXI_lite总线的使用和其信号线种类
模块内特定寄存器定义
在我的理解,AXI_lite总线就是控制模块内寄存器的,我们在做AXI_lite接口的时候需要特定的设置相应的寄存器
这就是设置了四个寄存器的原因
// Example-specific design signals
// local parameter for addressing 32 bit / 64 bit C_S_AXI_DATA_WIDTH
// ADDR_LSB is used for addressing 32/64 bit registers/memories
// ADDR_LSB = 2 for 32 bits (n downto 2)
// ADDR_LSB = 3 for 64 bits (n downto 3)
//设置地址偏移,找到相应的地址
localparam integer ADDR_LSB = (C_S_AXI_DATA_WIDTH/32) + 1;
localparam integer OPT_MEM_ADDR_BITS = 1;
//----------------------------------------------
//-- Signals for user logic register space example
//------------------------------------------------
//-- Number of Slave Registers 4
reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg0;
reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg1;
reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg2;
reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg3;
wire slv_reg_rden;
wire slv_reg_wren;
reg [C_S_AXI_DATA_WIDTH-1:0] reg_data_out;
// Implement memory mapped register select and write logic generation
// The write data is accepted and written to memory mapped registers when
// axi_awready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted. Write strobes are used to
// select byte enables of slave registers while writing.
// These registers are cleared when reset (active low) is applied.
// Slave register write enable is asserted when valid address and data are available
// and the slave is ready to accept the write address and write data.
assign slv_reg_wren = axi_wready && S_AXI_WVALID && axi_awready && S_AXI_AWVALID;
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
slv_reg0 <= 0;
slv_reg1 <= 0;
slv_reg2 <= 0;
slv_reg3 <= 0;
end
else begin
if (slv_reg_wren)
begin
case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
2'h0:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) //for 在一个时钟周期内进行
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 0
slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
2'h1:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 1
slv_reg1[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
2'h2:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 2
slv_reg2[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
2'h3:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 3
slv_reg3[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
default : begin
slv_reg0 <= slv_reg0;
slv_reg1 <= slv_reg1;
slv_reg2 <= slv_reg2;
slv_reg3 <= slv_reg3;
end
endcase
end
else if (start_clr_d2)
slv_reg0 <= {C_S_AXI_DATA_WIDTH{1'b0}} ; //控制采样开始
end
end
// Implement memory mapped register select and read logic generation
// Slave register read enable is asserted when valid address is available
// and the slave is ready to accept the read address.
assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;
always @(*)
begin
// Address decoding for reading registers
case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
2'h0 : reg_data_out <= slv_reg0;
2'h1 : reg_data_out <= slv_reg1;
2'h2 : reg_data_out <= slv_reg2;
2'h3 : reg_data_out <= slv_reg3;
default : reg_data_out <= 0;
endcase
end
虽然设置了四个但是应该只使用了reg0和reg1。作为采样开启信号 和 采样长度
需要注意的一点是for循环的用法,在此程序中for在一个时钟内完成,也就是for被综合成多个相同的块。
AXI_lite总线交互
写地址
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_awready <= 1'b0;
aw_en <= 1'b1;
end
else
begin
if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en) //当满足输入信号 S_AXI_AWVALID写地址有效,S_AXI_WVALID 写数据有效 都为‘1’时,axi_awready被置‘1’ ~axi_awready作为触发条件,互锁的设计
begin
// slave is ready to accept write address when
// there is a valid write address and write data
// on the write address and data bus. This design
// expects no outstanding transactions.
axi_awready <= 1'b1;
aw_en <= 1'b0;
end
else if (S_AXI_BREADY && axi_bvalid) //等待发送完写完反馈指令后使能aw_en,等待下次写指令
begin
aw_en <= 1'b1;
axi_awready <= 1'b0;
end
else
begin
axi_awready <= 1'b0;
end
end
end
// Implement axi_awaddr latching
// This process is used to latch the address when both
// S_AXI_AWVALID and S_AXI_WVALID are valid.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_awaddr <= 0;
end
else
begin
if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
begin
// Write Address latching
axi_awaddr <= S_AXI_AWADDR;
end
end
end
写数据 注意于AXI_lite总线不同的是写地址和写数据同时进行
//AXI_lite总线的addr和data同时写入
// Implement axi_wready generation
// axi_wready is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_wready is
// de-asserted when reset is low.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_wready <= 1'b0;
end
else
begin
if (~axi_wready && S_AXI_WVALID && S_AXI_AWVALID && aw_en )
begin
// slave is ready to accept write data when
// there is a valid write address and write data
// on the write address and data bus. This design
// expects no outstanding transactions.
axi_wready <= 1'b1;
end
else
begin
axi_wready <= 1'b0;
end
end
end
写应答
// Implement write response logic generation
// The write response and response valid signals are asserted by the slave
// when axi_wready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted.
// This marks the acceptance of address and indicates the status of
// write transaction.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_bvalid <= 0;
axi_bresp <= 2'b0;
end
else
begin
if (axi_awready && S_AXI_AWVALID && ~axi_bvalid && axi_wready && S_AXI_WVALID)
begin
// indicates a valid write response is available
axi_bvalid <= 1'b1;
axi_bresp <= 2'b0; // 'OKAY' response
end // work error responses in future
else
begin
if (S_AXI_BREADY && axi_bvalid)
//check if bready is asserted while bvalid is high)
//(there is a possibility that bready is always asserted high)
begin
axi_bvalid <= 1'b0;
end
end
end
end
读地址写入
// axi_arready is asserted for one S_AXI_ACLK clock cycle when
// S_AXI_ARVALID is asserted. axi_awready is
// de-asserted when reset (active low) is asserted.
// The read address is also latched when S_AXI_ARVALID is
// asserted. axi_araddr is reset to zero on reset assertion.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_arready <= 1'b0;
axi_araddr <= 32'b0;
end
else
begin
if (~axi_arready && S_AXI_ARVALID)
begin
// indicates that the slave has acceped the valid read address
axi_arready <= 1'b1;
// Read address latching
axi_araddr <= S_AXI_ARADDR;
end
else
begin
axi_arready <= 1'b0;
end
end
end
读数据+加应答
// Implement axi_arvalid generation
// axi_rvalid is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_ARVALID and axi_arready are asserted. The slave registers
// data are available on the axi_rdata bus at this instance. The
// assertion of axi_rvalid marks the validity of read data on the
// bus and axi_rresp indicates the status of read transaction.axi_rvalid
// is deasserted on reset (active low). axi_rresp and axi_rdata are
// cleared to zero on reset (active low).
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_rvalid <= 0;
axi_rresp <= 0;
end
else
begin
if (axi_arready && S_AXI_ARVALID && ~axi_rvalid)
begin
// Valid read data is available at the read data bus
axi_rvalid <= 1'b1; //可读
axi_rresp <= 2'b0; // 'OKAY' response
end
else if (axi_rvalid && S_AXI_RREADY)
begin
// Read data is accepted by the master
axi_rvalid <= 1'b0; //只持续一个时钟
end
end
end
//读寄存器
// Implement memory mapped register select and read logic generation
// Slave register read enable is asserted when valid address is available
// and the slave is ready to accept the read address.
assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;
always @(*)
begin
// Address decoding for reading registers
case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
2'h0 : reg_data_out <= slv_reg0;
2'h1 : reg_data_out <= slv_reg1;
2'h2 : reg_data_out <= slv_reg2;
2'h3 : reg_data_out <= slv_reg3;
default : reg_data_out <= 0;
endcase
end
// Output register or memory read data
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_rdata <= 0;
end
else
begin
// When there is a valid read address (S_AXI_ARVALID) with
// acceptance of read address by the slave (axi_arready),
// output the read dada
if (slv_reg_rden)
begin
axi_rdata <= reg_data_out; // register read data
end
end
end
注意always和always(*)的区别
组合逻辑和时序逻辑
一、组合逻辑
always@(敏感信号)或者always@*,组合逻辑相当于组合电路,与或非门组成的电路,其输出只与当前状态有关,与其他输入状态的函数无关,不涉及信号跳变处理(组合逻辑竞争冒险:只要输入信号同时变化,组合逻辑就必然产生毛刺);
二、时序逻辑
always@(跳变时钟)是时序电路,其输出不仅仅与当前状态有关,只有在时钟跳变的时候才会变化,其最简单的模型相当于DFF,D触发器。
AD9238采样模块
12位数据扩展方便储存
//扩展数据至16位
/* Expand adc data to 16 bits */
always@(posedge adc_clk or negedge adc_rst_n)
begin
if(adc_rst_n == 1'b0)
adc_buf_data <= 16'd0;
else
adc_buf_data <= {4'b0000, adc_data};
end
采样状态机
always@(posedge adc_clk or negedge adc_rst_n)
begin
if(adc_rst_n == 1'b0)
begin
state <= S_IDLE;
wait_cnt <= 8'd0;
sample_cnt <= 32'd0;
adc_buf_wr <= 1'b0;
start_clr <= 1'b0 ;
end
else
case(state)
S_IDLE:
begin
if (sample_start_d2)
begin
state <= S_SAMP_WAIT ;
start_clr <= 1'b1 ; //clear start register
end
end
S_SAMP_WAIT :
begin
if(start_clr_ack_d2) //wait ack signal assert
begin
state <= S_SAMPLE;
wait_cnt <= 32'd0;
start_clr <= 1'b0 ;
end
else
begin
wait_cnt <= wait_cnt + 32'd1;
end
end
S_SAMPLE:
begin
if(sample_cnt == sample_len_d2)
begin
sample_cnt <= 32'd0;
adc_buf_wr <= 1'b0;
state <= S_IDLE;
end
else
begin
sample_cnt <= sample_cnt + 32'd1;
adc_buf_wr <= 1'b1;
end
end
default:
state <= S_IDLE;
endcase
end
- IDLE: 初始状态,若收到开启信号,进入采样等待阶段。开启信号来自AXI_lite控制的reg0
- WAIT: 等待状态,需要等待start_clr_ack_d2信号,start_clr信号先传出去,经过三次AXI_ACLK的打拍后,控制reg0清零,同时传入start_clk_ack信号,经过adc_clk的三次打拍后,控制进入采样阶段。(打拍好像是为了不同时钟域的同步?我感觉打多了吧,两拍一拍应该也可以吧)
- SAMPLE:采样阶段,上各阶段取消了清零信号,打开FIFO可读,进行采样计数,将数据写入FIFO中,若是采样数据达到设定阈值,就关闭FIFO使能,恢复到IDLE阶段,等待下一次开启
FIFO设置,以及数据流传输
/*
* Instantiate async fifo by using Xilinx parameterized Macros. For ultrasclae, refer to ug974, for 7 series ug953
* write and read depth is 1024, write and read data width is 16
*/
xpm_fifo_async #(
.CDC_SYNC_STAGES (2),
.DOUT_RESET_VALUE ("1"),
.ECC_MODE ("no_ecc"),
.FIFO_MEMORY_TYPE ("auto"),
.FIFO_READ_LATENCY (1), //读出数据延迟一个时钟
.FIFO_WRITE_DEPTH (1024), //FIFO深度
.FULL_RESET_VALUE (0),
.PROG_EMPTY_THRESH (10),
.PROG_FULL_THRESH (10),
.RD_DATA_COUNT_WIDTH (11),
.READ_DATA_WIDTH (16),
.READ_MODE ("std"),
.RELATED_CLOCKS (0),
.USE_ADV_FEATURES ("0707"),
.WAKEUP_TIME (0),
.WRITE_DATA_WIDTH (16),
.WR_DATA_COUNT_WIDTH (11)
)
xpm_fifo_async_inst (
.rst (~adc_rst_n),
.wr_clk (adc_clk),
.wr_en (adc_buf_wr), //使能写
.din (adc_buf_data),
.rd_clk (M_AXIS_CLK),
.rd_en (adc_buf_rd),
.dout (M_AXIS_tdata),
.empty (empty),
.full (),
.almost_empty (),
.almost_full (),
.wr_data_count (),
.rd_data_count (),
.prog_empty (),
.prog_full (),
.data_valid (),
.dbiterr (),
.sbiterr (),
.overflow (),
.underflow (),
.wr_ack (),
.wr_rst_busy (),
.rd_rst_busy (),
.injectdbiterr (1'b0),
.injectsbiterr (1'b0),
.sleep (1'b0)
);
/* When axis slave interface ready and fifo is not empty, read fifo data */
assign adc_buf_rd = M_AXIS_tready & ~empty ; //不空且接收端准备好,开启读信号
always@(posedge M_AXIS_CLK or negedge M_AXIS_RSTN)
begin
if(M_AXIS_RSTN == 1'b0)
adc_buf_rd_d0 <= 1'b0;
else
adc_buf_rd_d0 <= adc_buf_rd ; //读出数据比读使能延迟一个时钟周期
end
/* fifo data is one clock cycle later than read enable,
When read signal and axis ready signal valid at the same time,
enable fifo read data */
//这一步没看懂在搞什么
always@(posedge M_AXIS_CLK or negedge M_AXIS_RSTN)
begin
if(M_AXIS_RSTN == 1'b0)
tvalid_en <= 1'b0;
else if (adc_buf_rd_d0 & ~M_AXIS_tready)
tvalid_en <= 1'b1 ;
else if (M_AXIS_tready)
tvalid_en <= 1'b0;
end
/* Async data to sync clock domain */
always@(posedge M_AXIS_CLK or negedge M_AXIS_RSTN)
begin
if(M_AXIS_RSTN == 1'b0)
begin
dma_len_d0 <= 32'd0 ;
dma_len_d1 <= 32'd0 ;
dma_len_d2 <= 32'd0 ;
end
else
begin
dma_len_d0 <= sample_len ;
dma_len_d1 <= dma_len_d0 ;
dma_len_d2 <= dma_len_d1 ;
end
end
/* axis interface data counter */
always@(posedge M_AXIS_CLK or negedge M_AXIS_RSTN)
begin
if(M_AXIS_RSTN == 1'b0)
dma_cnt <= 32'd0;
else if (M_AXIS_tvalid & ~M_AXIS_tlast)
dma_cnt <= dma_cnt + 1'b1 ;
else if (M_AXIS_tvalid & M_AXIS_tlast)
dma_cnt <= 32'd0 ;
end
assign M_AXIS_tvalid = M_AXIS_tready & (tvalid_en | adc_buf_rd_d0) ;
assign M_AXIS_tkeep = 2'b11 ;
assign M_AXIS_tlast = M_AXIS_tvalid & (dma_cnt == dma_len_d2 - 1) ; //last信号
keep信号和strb信号作用