本文主要是介绍Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
ACM9238 高速双通道ADC模块自助服务手册AD9238
一、实验目的
本次实验通过电脑上的网络调试助手,将命令帧进行发送,然后通过ACZ7015开发板上的以太网芯片接收,随后将接收到的数据转换成命令,从而实现对ACM9238模块采样频率、数据采样个数以及采样通道的配置。配置完成之后,ACM9238模块开始采集数据,将采集的数据存储至ddr中,然后通过网口以UDP协议传输到电脑。用户可以在电脑上通过网口调试工具进行指令的下发,并以文件的形式保存接收到的数据,然后使用MATLAB软件进行进一步的数据处理分析。
二、AD9238
模块在各方面参数性能上与AD9226保持一致。但是在设计上优化了信号调理电路,将单端信号先转成差分信号,再送入ADC转换,已获得更小的采样误差。
功能参数
1、±5V电压输入范围
2、每通道65Msps最高采样速率
3、每通道12位输出
三、DDR3(大容量存储器件)
DDR=Double Data Rate双倍速率,DDR SDRAM=双倍速率同步动态随机存储器,人们习惯称为DDR,其中,SDRAM 是Synchronous Dynamic Random Access Memory的缩写,即同步动态随机存取存储器。而DDR SDRAM是Double Data Rate SDRAM的缩写,是双倍速率同步动态随机存储器。(摘录:ddr(双倍数据速率)_百度百科 (baidu.com))
1.相关知识
PL:通用可编程逻辑FPGA。
PS:两个 Cortex-A9 核、 IO 外设、各类硬核控制器等资源在内的 SOC 处理系统。
PS部分的DDR3主要用作ARM CPU的内存系统,ARM CPU的程序可以在该DDR3中运行,PS部分的DDR3存储器使用,不需要,也不能调用MIG IP来控制,只需要在设计中添加ZYNQ7 Processing System 组件,并配置好DDR相关的参数,即可使用。ACZ7015开发板的DDR型号为MT41K256M16 RE-125。
PL部分如果要使用DDR3存储器:
第一种:首先在PL部分要设计DDR3部分电路,并且需要通过在IP Catlog中调用MIG IP来创建DDR3控制器。
第二种:PL通过AXI4(4个高性能数据交互端口,High-Performance Ports)接口使用PS的DDR3存储器空间。
2.架构优势
PL通过HP端口写入到PS侧DDR3中的数据,不仅PL可以再读回来,同时PS侧的ARM CPU也可以读写这些数据。相同的道理,PS侧在某些区域写入的数据,PL侧也可以从该区域读出来使用。
3.直接使用DDR控制器可能会遇到的实际问题
如何确保读写DDR时数据的有效性以及读写传输的高效性?
3.1读写时钟频率不同跨时钟域怎么办?单一Ui时钟能满足数据读写侧速率需求吗?
DDR控制器的时钟频率恒定,但是大部分硬件(读写)有自身的固定工作频率、数据读写频率,并且有可能与DDR时钟频率不一致。有可能导致DDR控制器无法满足读写硬件的速率需求,进而数据交互存在跨时钟域,还会导致DDR读写错误。
3.2读写数据不连续怎么办?
在速率满足的情况下,还可能存在外设发送接收数据并不连续的问题。
3.3读写数据位宽不同怎么办?
DDR典型数据位宽128位,但外设一般为8位或16位。
以上问题通过在读写段各添加一个FIFO解决,进而引出AXI转化模块。
四、AXI转FIFO接口设计
1.AXI接口知识
AXI协议是基于 burst的传输,并且定义了以下 5 个独立的传输通道:
- 读地址通道(Read Address Channel, AR)
- 读数据通道(Read Data Channel, R)
- 写地址通道(Write Address Channel, AW)
- 写数据通道(Write Data Channel, W)
- 写响应通道(Write Response Channel, B)
- 这 5 条独立的通道都包含一个双路的 VALD、 READY 握手机制。信息源通过 VALID 信号来指示通道中的数据和控制信息什么时候有效。目地源用READY 信号来表示何时准备好接收数据。传输地址信息和数据都是在 VALID和 READY 同时为高时有效。
- 读数据和写数据通道都包括一个 LAST 信号,用来指明一个事物传输的最后一个数据。
- 读/写事务都有自己的地址通道,地址通道携带着传输事务所必需的地址和控制信息。
- 读数据通道传送着从设备到主机的读数据和读响应信息。读响应信息指明读事务的完成状态。
- 写数据通路传送着主机向从设备的写数据。写响应通道提供了设备响应写事务的一种方式。在每一次突发式写会产生一个完成信号。
写事务时序
读事务时序
AXI 协议支持乱序传输。每一个通过接口的事务有一个 IDtag。协议要求相同 ID tag 的事务必须有序完成,而不同 ID tag 可以乱序完成
1.1写地址通道信号
1.2写数据通道信号
1.3写响应通道信号
1.4读地址通道信号
1.5读数据通道信号
2.ZynqAXI接口
红绿蓝三种颜色的箭头代表了几种不同位宽的 AXI 总线。 红色线条框是 PL 端访问 DDR控制器的路径,其中, High Performamce AXI 32/64b Slave Ports 便是我们所说的HP 接口, 该接口为 AXI 接口,通常用于大量数据的高速传输。
PS 端通过硬件电路实现 AXI 总线,而 PL 端则需要用户通过逻辑资源搭建 AXI 总线。
3.AXI转换模块(fifo_axi4_adapter)
目的:为了解决上下游模块常常处于不同时钟域的问题。
fifo_axi4_adapter 负责FIFO接口到AXI4接口的转换,FIFO中的数据可以通过该模块写入PS端,PS端的数据也可以由该模块接收写入FIFO。通过这种方式,用户只需要操作FIFO,就能实现PL端数据对PS端DDR的读写。
wr_ddr3_fifo 和 rd_ddr3_fifo 为上文中我们提到的读写 FIFO,用于存储读写数据,同时解决时钟域等问题。fifo2axi4 为 AXI4 接口转换模块,负责fifo 与 axi4 接口间的转换, 将写侧 FIFO 里的数据读出然后存储在 DDR 存储器以及将 DDR 存储器读出的数据存放到读侧 FIFO 缓存。
3.1接口转换模块设计(fifo2axi4)
写流程:主机向写地址通道写入地址和控制信息→写数据通道突发写入数据→收到设备的写数据响应。
读流程:主机向读地址通道写入地址和控制信息→ 收到设备的读数据响应和读的数据。
根据写时序进行状态机设计:
//写信号状态机
always@(posedge clk or posedge reset)
beginif(reset)curr_wr_state <= S_IDLE;elsecurr_wr_state <= next_wr_state;endalways@(*)
begincase(curr_wr_state)S_IDLE:beginif(wr_ddr3_req == 1'b1)next_wr_state = S_WR_ADDR;elsenext_wr_state = S_IDLE;endS_WR_ADDR:beginif(m_axi_awready && m_axi_awvalid)next_wr_state = S_WR_DATA;elsenext_wr_state = S_WR_ADDR;endS_WR_DATA:beginif(m_axi_wready && m_axi_wvalid && m_axi_wlast)next_wr_state = S_WR_RESP;elsenext_wr_state = S_WR_DATA;endS_WR_RESP:beginif(m_axi_bready && m_axi_bvalid && (m_axi_bresp == 2'b00) &&(m_axi_bid == AXI_ID[AXI_ID_WIDTH-1:0]))next_wr_state = S_IDLE;elsenext_wr_state = S_WR_RESP;endendcase
end//awaddr信号
always@(posedge clk or posedge reset)
beginif(reset)m_axi_awaddr <= WR_AXI_BYTE_ADDR_BEGIN;else if(wr_addr_clr || axi_awaddr_clr)m_axi_awaddr <= WR_AXI_BYTE_ADDR_BEGIN;else if(m_axi_awaddr >= WR_AXI_BYTE_ADDR_END)m_axi_awaddr <= WR_AXI_BYTE_ADDR_BEGIN;else if((curr_wr_state == S_WR_RESP) && m_axi_bready && m_axi_bvalid && (m_axi_bresp == 2'b00) && (m_axi_bid == AXI_ID[AXI_ID_WIDTH-1:0]))//每次地址增加量应该是突发写数据个数*每个数据的字节数m_axi_awaddr <= m_axi_awaddr + ((m_axi_awlen + 1'b1)*(AXI_DATA_WIDTH/8));elsem_axi_awaddr <= m_axi_awaddr;
end//awvalid信号
always@(posedge clk or posedge reset)
beginif(reset)m_axi_awvalid <= 1'b0;else if((curr_wr_state == S_WR_ADDR) && m_axi_awready && m_axi_awvalid)m_axi_awvalid <= 1'b0;else if(curr_wr_state == S_WR_ADDR)m_axi_awvalid <= 1'b1;elsem_axi_awvalid <= m_axi_awvalid;
end//wvalid信号
always@(posedge clk or posedge reset)
beginif(reset)m_axi_wvalid <= 1'b0;else if((curr_wr_state == S_WR_DATA) && m_axi_wready && m_axi_wvalid && m_axi_wlast)
m_axi_wvalid <= 1'b0;else if(curr_wr_state == S_WR_DATA)m_axi_wvalid <= 1'b1;elsem_axi_wvalid <= m_axi_wvalid;
end//传输计数
always@(posedge clk or posedge reset)
beginif(reset)wr_data_cnt <= 1'b0;else if(curr_wr_state == S_IDLE)wr_data_cnt <= 1'b0;else if(curr_wr_state == S_WR_DATA && m_axi_wready && m_axi_wvalid)wr_data_cnt <= wr_data_cnt + 1'b1;elsewr_data_cnt <= wr_data_cnt;
end//wlast信号:
always@(posedge clk or posedge reset)
beginif(reset)m_axi_wlast <= 1'b0;else if(curr_wr_state == S_WR_DATA && m_axi_wready && m_axi_wvalid && m_axi_wlast)m_axi_wlast <= 1'b0;else if(curr_wr_state == S_WR_DATA && m_axi_awlen == 8'd0)//数据个数为1m_axi_wlast <= 1'b1;else if(curr_wr_state == S_WR_DATA && m_axi_wready && m_axi_wvalid && (wr_data_cnt == m_axi_awlen -1'b1))//传输完倒数第二个数m_axi_wlast <= 1'b1;elsem_axi_wlast <= m_axi_wlast;
end
根据读时序进行状态机设计:
//读状态机
always@(posedge clk or posedge reset)
beginif(reset)curr_rd_state <= S_IDLE;elsecurr_rd_state <= next_rd_state;
end
always@(*)
begincase(curr_rd_state)S_IDLE:beginif(rd_ddr3_req == 1'b1)next_rd_state = S_RD_ADDR;elsenext_rd_state = S_IDLE;endS_RD_ADDR:beginif(m_axi_arready && m_axi_arvalid)next_rd_state = S_RD_RESP;elsenext_rd_state = S_RD_ADDR;endS_RD_RESP:beginif(m_axi_rready && m_axi_rvalid && m_axi_rlast && (m_axi_rresp == 2'b00)&& (m_axi_rid == AXI_ID[AXI_ID_WIDTH-1:0]))next_rd_state = S_IDLE;elsenext_rd_state = S_RD_RESP;endendcase
end//araddr
always@(posedge clk or posedge reset)
beginif(reset)m_axi_araddr <= RD_AXI_BYTE_ADDR_BEGIN;else if(rd_addr_clr || axi_araddr_clr)m_axi_araddr <= RD_AXI_BYTE_ADDR_BEGIN;else if(m_axi_araddr >= RD_AXI_BYTE_ADDR_END)m_axi_araddr <= RD_AXI_BYTE_ADDR_BEGIN;else if((curr_rd_state == S_RD_RESP) && m_axi_rready && m_axi_rvalid && m_axi_rlast && (m_axi_rresp == 2'b00) && (m_axi_rid == AXI_ID[AXI_ID_WIDTH-1:0]))m_axi_araddr <= m_axi_araddr + ((m_axi_arlen + 1'b1)*(AXI_DATA_WIDTH/8));elsem_axi_araddr <= m_axi_araddr;
end//m_axi_arvalid
always@(posedge clk or posedge reset)
beginif(reset)m_axi_arvalid <= 1'b0;else if((curr_rd_state == S_RD_ADDR) && m_axi_arready && m_axi_arvalid)m_axi_arvalid <= 1'b0;else if(curr_rd_state == S_RD_ADDR)m_axi_arvalid <= 1'b1;elsem_axi_arvalid <= m_axi_arvalid;
end
其他信号定义:
assign m_axi_awid = AXI_ID[AXI_ID_WIDTH-1:0];
assign m_axi_awsize = DATA_SIZE;
assign m_axi_awburst = 2'b01;
assign m_axi_awlock = 1'b0;
assign m_axi_awcache = 4'b0000;
assign m_axi_awprot = 3'b000;
assign m_axi_awqos = 4'b0000;
assign m_axi_awregion= 4'b0000;
assign m_axi_awlen = AXI_BURST_LEN[7:0];
assign m_axi_wstrb = 16'hffff;
assign m_axi_wdata = wr_fifo_rddata;
assign m_axi_bready = 1'b1;
assign m_axi_arid = AXI_ID[AXI_ID_WIDTH-1:0];
assign m_axi_arsize = DATA_SIZE;
assign m_axi_arburst = 2'b01;
assign m_axi_arlock = 1'b0;
assign m_axi_arcache = 4'b0000;
assign m_axi_arprot = 3'b000;
assign m_axi_arqos = 4'b0000;
assign m_axi_arregion= 4'b0000;
assign m_axi_arlen = AXI_BURST_LEN[7:0];
assign m_axi_rready = ~rd_fifo_alfull;assign wr_fifo_rdreq = (~axi_awaddr_clr) && m_axi_wvalid && m_axi_wready;
assign rd_fifo_wrreq = (~axi_araddr_clr) && m_axi_rvalid && m_axi_rready;
assign rd_fifo_wrdata = m_axi_rdata;assign wr_req_cnt_thresh = (m_axi_awlen == 'd0)? 1'b1 : (AXI_BURST_LEN[7:0]+1'b1-2'd2);//计数比实际数量少 2
assign rd_req_cnt_thresh = AXI_BURST_LEN[7:0];
assign wr_ddr3_req = (wr_fifo_rst_busy == 1'b0) && (wr_fifo_rd_cnt >= wr_req_cnt_thresh) ? 1'b1:1'b0;
assign rd_ddr3_req = (rd_fifo_rst_busy == 1'b0) && (rd_fifo_wr_cnt <= rd_req_cnt_thresh) ? 1'b1:1'b0;
3.2FIFO IP创建(wr_ddr3_fifo、rd_ddr3_fifo)
wr_ddr3_fifo:
设置写FIFO接口类型
设置写FIFO端口
rd_ddr3_fifo:
设置读FIFO接口类型
设置读 FIFO 端口
设置读FIFO数据计数
3.3 封装接口转换模块
module fifo_axi4_adapter #(parameter FIFO_DW = 16 ,parameter WR_AXI_BYTE_ADDR_BEGIN = 0 ,parameter WR_AXI_BYTE_ADDR_END = 1023 ,parameter RD_AXI_BYTE_ADDR_BEGIN = 0 ,parameter RD_AXI_BYTE_ADDR_END = 1023 ,parameter AXI_DATA_WIDTH = 128 ,parameter AXI_ADDR_WIDTH = 29 ,parameter AXI_ID_WIDTH = 4 ,parameter AXI_ID = 4'b0000,parameter AXI_BURST_LEN = 8'd31 //burst length = 32
)
(
// clock reset
input clk ,
input reset ,// wr_fifo wr Interface
input wrfifo_clr ,
input wrfifo_clk ,
input wrfifo_wren ,
input [FIFO_DW-1:0] wrfifo_din ,
output wrfifo_full ,
output [15:0] wrfifo_wr_cnt ,// rd_fifo rd Interface
input rdfifo_clr ,
input rdfifo_clk ,
input rdfifo_rden ,
output [FIFO_DW-1:0] rdfifo_dout ,
output rdfifo_empty ,
output [15:0] rdfifo_rd_cnt ,// Master Interface Write Address Ports
output [AXI_ID_WIDTH-1:0] m_axi_awid ,
output [AXI_ADDR_WIDTH-1:0] m_axi_awaddr ,
output [7:0] m_axi_awlen ,
output [2:0] m_axi_awsize ,
output [1:0] m_axi_awburst ,
output [0:0] m_axi_awlock ,
output [3:0] m_axi_awcache ,
output [2:0] m_axi_awprot ,
output [3:0] m_axi_awqos ,
output [3:0] m_axi_awregion,
output m_axi_awvalid ,
input m_axi_awready ,// Master Interface Write Data Ports
output [AXI_DATA_WIDTH-1:0] m_axi_wdata ,
output [AXI_DATA_WIDTH/8-1:0] m_axi_wstrb ,
output m_axi_wlast ,
output m_axi_wvalid ,
input m_axi_wready ,// Master Interface Write Response Ports
input [AXI_ID_WIDTH-1:0] m_axi_bid ,
input [1:0] m_axi_bresp ,
input m_axi_bvalid ,
output m_axi_bready ,// Master Interface Read Address Ports
output [AXI_ID_WIDTH-1:0] m_axi_arid ,
output [AXI_ADDR_WIDTH-1:0] m_axi_araddr ,
output [7:0] m_axi_arlen ,
output [2:0] m_axi_arsize ,
output [1:0] m_axi_arburst ,
output [0:0] m_axi_arlock ,
output [3:0] m_axi_arcache ,
output [2:0] m_axi_arprot ,
output [3:0] m_axi_arqos ,
output [3:0] m_axi_arregion,
output m_axi_arvalid ,
input m_axi_arready ,// Master Interface Read Data Ports
input [AXI_ID_WIDTH-1:0] m_axi_rid ,
input [AXI_DATA_WIDTH-1:0] m_axi_rdata ,
input [1:0] m_axi_rresp ,
input m_axi_rlast ,
input m_axi_rvalid ,
output m_axi_rready
);wire wrfifo_rden;
wire [AXI_DATA_WIDTH-1:0] wrfifo_dout;
wire [5 : 0] wrfifo_rd_cnt;
wire wrfifo_empty;
wire wrfifo_wr_rst_busy;
wire wrfifo_rd_rst_busy;
wire rdfifo_wren;
wire [AXI_DATA_WIDTH-1:0] rdfifo_din;
wire [5 : 0] rdfifo_wr_cnt;
wire rdfifo_full;
wire rdfifo_wr_rst_busy;
wire rdfifo_rd_rst_busy;
reg wrfifo_clr_sync_clk;
reg wr_addr_clr;
reg rdfifo_clr_sync_clk;
reg rd_addr_clr;wr_ddr3_fifo wr_ddr3_fifo
(.rst (wrfifo_clr ),.wr_clk (wrfifo_clk ),.rd_clk (clk ),.din (wrfifo_din ),.wr_en (wrfifo_wren ),.rd_en (wrfifo_rden ),.dout (wrfifo_dout ),.full (wrfifo_full ),.empty (wrfifo_empty ),.rd_data_count (wrfifo_rd_cnt ),.wr_data_count (wrfifo_wr_cnt ),.wr_rst_busy (wrfifo_wr_rst_busy ),.rd_rst_busy (wrfifo_rd_rst_busy )
);rd_ddr3_fifo rd_ddr3_fifo
(.rst (rdfifo_clr ),.wr_clk (clk ),.rd_clk (rdfifo_clk ),.din (rdfifo_din ),.wr_en (rdfifo_wren ),.rd_en (rdfifo_rden ),.dout (rdfifo_dout ),.full (rdfifo_full ),.empty (rdfifo_empty ),.rd_data_count (rdfifo_rd_cnt ),.wr_data_count (rdfifo_wr_cnt ),.wr_rst_busy (rdfifo_wr_rst_busy ),.rd_rst_busy (rdfifo_rd_rst_busy )
);always@(posedge clk)
beginwrfifo_clr_sync_clk <= wrfifo_clr;wr_addr_clr <= wrfifo_clr_sync_clk;
endalways@(posedge clk)
beginrdfifo_clr_sync_clk <= rdfifo_clr;rd_addr_clr <= rdfifo_clr_sync_clk;
endfifo2axi4
#(.WR_AXI_BYTE_ADDR_BEGIN (WR_AXI_BYTE_ADDR_BEGIN ),.WR_AXI_BYTE_ADDR_END (WR_AXI_BYTE_ADDR_END ),.RD_AXI_BYTE_ADDR_BEGIN (RD_AXI_BYTE_ADDR_BEGIN ),.RD_AXI_BYTE_ADDR_END (RD_AXI_BYTE_ADDR_END ),.AXI_DATA_WIDTH (AXI_DATA_WIDTH ),.AXI_ADDR_WIDTH (AXI_ADDR_WIDTH ),.AXI_ID_WIDTH (AXI_ID_WIDTH ),.AXI_ID (AXI_ID ),.AXI_BURST_LEN (AXI_BURST_LEN )//burst length = 32
)fifo2axi4_inst
(
//clock reset.clk (clk ),.reset (reset ),//FIFO Interface ports.wr_addr_clr (wr_addr_clr ), //1:clear, sync clk.wr_fifo_rdreq (wrfifo_rden ),.wr_fifo_rddata (wrfifo_dout ),.wr_fifo_empty (wrfifo_empty ),.wr_fifo_rd_cnt (wrfifo_rd_cnt ),.wr_fifo_rst_busy (wrfifo_rd_rst_busy ),.rd_addr_clr (rd_addr_clr ), //1:clear, sync clk.rd_fifo_wrreq (rdfifo_wren ),.rd_fifo_wrdata (rdfifo_din ),.rd_fifo_alfull (rdfifo_full ),.rd_fifo_wr_cnt (rdfifo_wr_cnt ),.rd_fifo_rst_busy (rdfifo_wr_rst_busy ),// Slave Interface Write Address Ports.m_axi_awid (m_axi_awid ),.m_axi_awaddr (m_axi_awaddr ),.m_axi_awlen (m_axi_awlen ),.m_axi_awsize (m_axi_awsize ),.m_axi_awburst (m_axi_awburst ),.m_axi_awlock (m_axi_awlock ),.m_axi_awcache (m_axi_awcache ),.m_axi_awprot (m_axi_awprot ),.m_axi_awqos (m_axi_awqos ),.m_axi_awregion (m_axi_awregion ),.m_axi_awvalid (m_axi_awvalid ),.m_axi_awready (m_axi_awready ),// Slave Interface Write Data Ports.m_axi_wdata (m_axi_wdata ),.m_axi_wstrb (m_axi_wstrb ),.m_axi_wlast (m_axi_wlast ),.m_axi_wvalid (m_axi_wvalid ),.m_axi_wready (m_axi_wready ),// Slave Interface Write Response Ports.m_axi_bid (m_axi_bid ),.m_axi_bresp (m_axi_bresp ),.m_axi_bvalid (m_axi_bvalid ),.m_axi_bready (m_axi_bready ),// Slave Interface Read Address Ports.m_axi_arid (m_axi_arid ),.m_axi_araddr (m_axi_araddr ),.m_axi_arlen (m_axi_arlen ),.m_axi_arsize (m_axi_arsize ),.m_axi_arburst (m_axi_arburst ),.m_axi_arlock (m_axi_arlock ),.m_axi_arcache (m_axi_arcache ),.m_axi_arprot (m_axi_arprot ),.m_axi_arqos (m_axi_arqos ),.m_axi_arregion (m_axi_arregion ),.m_axi_arvalid (m_axi_arvalid ),.m_axi_arready (m_axi_arready ),// Slave Interface Read Data Ports.m_axi_rid (m_axi_rid ),.m_axi_rdata (m_axi_rdata ),.m_axi_rresp (m_axi_rresp ),.m_axi_rlast (m_axi_rlast ),.m_axi_rvalid (m_axi_rvalid ),.m_axi_rready (m_axi_rready )
);
endmodule
这篇关于Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!