DMA数据发送模块实现
DMA数据发送模块实现
发送模块的数据接口
- 发送模块使用AXI Stream接口与DMA控制器通信,AXI Stream是一种简单的点对点数据流协议,主要包含以下三个信号:
- valid:表示发送方是否有有效的数据。
- ready:表示接收方是否准备好接收数据。
- data:表示数据本身,可以是任意位宽。
- 另外,AXI Stream接口还有一个可选的信号:
- last:表示当前的数据是否是一帧数据的最后一个字节,用于标识数据边界。
- 发送方是FPGA(PL),接收方是DMA控制器(PS),因此valid和last信号由FPGA控制,ready信号由DMA控制器控制。
- 只有当valid和ready信号同时为高时,才表示一次有效的数据传输。
graph TD
A[发送读开始命令] --> B[主控模块跳转到读状态并拉高value信号]
B --> C{PS端是否ready?}
C --是--> D[产生FIFO读使能信号并从FIFO中读取数据]
D --> E{是否发送完所有数据?}
E --是--> F[拉高last信号并跳回idle状态]
E --否--> D
C --否--> B
- FIFO的读使能信号(buffer_rd_en)
- 需要用value和ready的与运算来产生,因为只有当两个信号都为高时,数据才能正常发送出去
- 这里假设FIFO用的是fast forward模式,即读使能来了之后,数据马上就能出来,没有延时
- last信号的产生
- 需要用一个寄存器(register)来记录FIFO内部的数据量,在每次read start为高时更新这个值
- 需要用一个计数器(counter)来记录已经发送的数据量,在每次FIFO读使能为高时加1,在value为0时清零
- 当计数器的值等于寄存器的值减1时,就把last信号拉高
1. 当read start信号为高时,跳转到read状态,拉高value信号,表示数据有效
2. 当value和ready信号同时为高时,产生FIFO的读使能信号,从FIFO里读出数据,并用一个计数器记录读出的数据个数
3. 当计数器的值等于FIFO内部的实时数据量(data count)时,拉高last信号,表示最后一个数据
4. 当last信号为高时,产生read finish信号,表示读取数据完成,跳转回idle状态,清零计数器
例子
- 假设FIFO里有1000个数据,data count为1000
- 发出read start信号,主控模块跳转到read状态,拉高value信号
- PS端给出ready信号,FIFO产生读使能信号,从FIFO里读出第一个数据,并把计数器加一
- 重复上述过程,直到计数器达到999时,拉高last信号,表示最后一个数据
- PS端收到last信号后,给出read finish信号,主控模块跳转回idle状态,清零计数器
代码清单:
stream_tx
module stream_tx(
// system siganls
input sclk ,
input s_rst_n ,
//
output wire [63:0] s_axis_s2mm_tdata ,
output wire [ 7:0] s_axis_s2mm_tkeep ,
output wire s_axis_s2mm_tvalid ,
input s_axis_s2mm_tready ,
output reg s_axis_s2mm_tlast ,
//
output wire buffer_rd_en ,
input [63:0] buffer_rd_data ,
input [ 5:0] state ,
input [12:0] buffer_data_count ,
output wire read_finish
);
//========================================================================\
// =========== Define Parameter and Internal signals ===========
//========================================================================/
reg [12:0] tx_cnt ;
//=============================================================================
//************** Main Code **************
//=============================================================================
assign s_axis_s2mm_tvalid = state[2];
assign s_axis_s2mm_tkeep = 8'hff;
assign buffer_rd_en = s_axis_s2mm_tvalid & s_axis_s2mm_tready;
assign s_axis_s2mm_tdata = buffer_rd_data;
assign read_finish = s_axis_s2mm_tlast;
always @(posedge sclk or negedge s_rst_n) begin
if(s_rst_n == 1'b0)
tx_cnt <= 'd0;
else if(s_axis_s2mm_tvalid == 1'b0)
tx_cnt <= 'd0;
else if(buffer_rd_en == 1'b1)
tx_cnt <= tx_cnt + 1'b1;
end
always @(posedge sclk or negedge s_rst_n) begin
if(s_rst_n == 1'b0)
s_axis_s2mm_tlast <= 1'b0;
else if(s_axis_s2mm_tvalid == 1'b1 && tx_cnt == (data_count-2))
s_axis_s2mm_tlast <= 1'b1;
else
s_axis_s2mm_tlast <= 1'b0;
end
endmodule