Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(前导)
Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(一)
Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(三)
本次实验使用电脑上的网络调试助手,将命令帧通过以太网芯片RTL8211(RGMII接口)发送至ACZ7015开发板,提取UDP报文内容转换成控制命令,从而实现对ACM9238模块采样频率、数据采样个数以及采样通道的配置。
配置完成之后,ACM9238模块开始采集数据,将采集的数据存储至DDR3中,然后通过网口以UDP协议传输到电脑。用户可以在电脑上通过网口调试工具进行指令的下发,并以文件的形式保存接收到的数据,然后使用MATLAB软件进行进一步的数据处理分析。

PL 部分的模块说明如下:
两路,




采样上限50Msps,如果期望以1Msps 的采样速率采样, 则只需要每间隔 50 个采样数据取一个结果存储或使用, 其他 49 个数据直接舍弃。不建议降低ADC芯片的时钟信号。
将以太网接收到的数据进行解析, 得到控制命令。


锁相环模块,将 rgmii 接口时钟信号 rgmii_rx_clk_i 偏移90 °得到 rgmii_rx_clk 时钟信号。(为了在时钟信号的上升沿/下降沿取数据时,取得数据刚好是数据信号 rgmii_rxd 的正中间, 使得采样的数据处于最稳定的状态。)

锁相环IP配置

使用该 IP 核解决采集过程中会出现的跨时钟域数据交互问题(以太网125MHz,ACM9238-50MHz)。


接收转命令模块。




- //非空时产生FIFO读请求信号
- always@(posedge clk or negedge reset_n)
- if(!reset_n)
- fifo_rd_req <= 1'b0;
- else if(!rx_empty)
- fifo_rd_req <= 1'b1;
- else
- fifo_rd_req <= 1'b0;
-
-
- //获得帧命令数据
- always@(posedge clk)
- if(fifo_rd_req)begin
- data_0[7] <= #1 fifodout;
- data_0[6] <= #1 data_0[7];
- data_0[5] <= #1 data_0[6];
- data_0[4] <= #1 data_0[5];
- data_0[3] <= #1 data_0[4];
- data_0[2] <= #1 data_0[3];
- data_0[1] <= #1 data_0[2];
- data_0[0] <= #1 data_0[1];
- end
-
-
- //判断帧命令数据
- always@(posedge clk or negedge reset_n)
- if(!reset_n)begin
- address <= 0;
- cmd_data <= 32'd0;
- cmdvalid <= 1'b0;
- end
- else if(fifo_rx_done)begin
- if((data_0[0]==8'h55)&&(data_0[1]==8'hA5)&&(data_0[7]==8'hF0))
- begin
- cmd_data[7:0] <= #1 data_0[6];
- cmd_data[15:8] <= #1 data_0[5];
- cmd_data[23:16] <= #1 data_0[4];
- cmd_data[31:24] <= #1 data_0[3];
- address <= #1 data_0[2];
- cmdvalid <= #1 1;
- end
- else
- cmdvalid <= #1 0;
- end
- else
- cmdvalid <= #1 0;
接收数据转换为控制数据。
寄存器说明:




- always@(posedge clk or negedge reset_n)
- if(!reset_n)begin
- ChannelSel <= 2'b00;
- DataNum <= 32'd0;
- ADC_Speed_Set <= 32'd0;
- RestartReq <= 1'b0;
- end
- else if(cmdvalid)begin
- case(cmd_addr)
- 0: RestartReq <= 1'b1;
- 1: ChannelSel <= cmd_data[1:0];
- 2: DataNum <= cmd_data[31:0];
- 3: ADC_Speed_Set <= cmd_data[31:0];
- default:;
- endcase
- end
- else
- RestartReq <= 1'b0;
控制ADC的采样速率,将12位数据转换为16位数据。





- always@(posedge clk or negedge reset_n)
- if(!reset_n)
- div_cnt <= 0;
- else if(ad_sample_en)begin
- if(div_cnt >= div_set)
- div_cnt <= 0;
- else
- div_cnt <= div_cnt + 1'd1;
- end
- else
- div_cnt <= 0;
-
-
- always@(posedge clk or negedge reset_n)
- if(!reset_n)
- adc_data_en <= 0;
- else if(div_cnt == div_set)
- adc_data_en <= 1;
- else
- adc_data_en <= 0;


- always @(posedge clk)
- ad_out_valid <= ad_sample_en;
-
-
- assign s_ad_in1 = ad_in1 + 12'd2048;
- assign s_ad_in2 = ad_in2 + 12'd2048;
-
-
- always @(posedge clk)
- if(ad_sample_en && ch_sel == 2'b01)
- ad_out<={4'd0,s_ad_in1};//这样补 0 为了适应上位机
- else if(ad_sample_en && ch_sel == 2'b10)
- ad_out<={4'd0,s_ad_in2};//
- else if(ad_sample_en && ch_sel == 2'b00)
- ad_out<={4'd0,adc_test_data};
- else
- ad_out <= 16'd0;
控制信号的产生以及 ADC 何时启动数据传输。


- localparam IDLE = 4'd0; //空闲状态
- localparam DDR_WR_FIFO_CLEAR = 4'd1; //DDR 写 FIFO 清除状态
- localparam ADC_SAMPLE = 4'd2; //ADC 采样数据状态
- localparam DDR_RD_FIFO_CLEAR = 4'd3; //DDR 读 FIFO 清除状态
- localparam DATA_SEND_START = 4'd4; //数据发送状态
- localparam DATA_SEND_WORKING = 4'd5; //数据发送完成状态
(1)IDLE
- //ADC 模块开始采样标志信号寄存
- always@(posedge clk or posedge reset)begin
- if(reset)
- start_sample_rm <= 1'b0;
- else if(state==IDLE)
- start_sample_rm <= start_sample;
- else
- start_sample_rm <= 1'b0;
- end
-
- /*状态切换IDLE->DDR_WR_FIFO_CLEAR
- begin
- if(start_sample_rm) begin //DDR 初始化完成并且产生启动采样信号
- state <= DDR_WR_FIFO_CLEAR; //进入写 FIFO 清除状态
- end
- else begin
- state <= state;
- end
- end
- */
(2)DDR_WR_FIFO_CLEAR
- //延时10个节拍
- always@(posedge clk or posedge reset)begin
- if(reset)
- wrfifo_clr_cnt<=0;
- else if(state==DDR_WR_FIFO_CLEAR)//如果进入了清 fifo 状态
- begin
- if(wrfifo_clr_cnt==9)
- wrfifo_clr_cnt<=5'd9;
- else
- wrfifo_clr_cnt<=wrfifo_clr_cnt+1'b1;
- end
- else
- wrfifo_clr_cnt<=5'd0;
- end
-
- //给清FIFO信号足够的拉高时间
- always@(posedge clk or posedge reset)begin
- if (reset)
- wrfifo_clr<=0;
- else if(state==DDR_WR_FIFO_CLEAR)
- begin
- if(wrfifo_clr_cnt==0||wrfifo_clr_cnt==1||wrfifo_clr_cnt==2)
- wrfifo_clr<=1'b1;
- else
- wrfifo_clr<=1'b0;
- end
- else
- wrfifo_clr<=1'b0;
- end
-
- /*状态切换DDR_WR_FIFO_CLEAR->ADC_SAMPLE
- begin
- if(!wrfifo_full && (wrfifo_clr_cnt==9))
- state<=ADC_SAMPLE;
- else
- state<=DDR_WR_FIFO_CLEAR;
- end
- */
(3)ADC_SAMPLE
- //根据ADC输出使能信号计数
- always@(posedge clk or posedge reset)begin
- if(reset)
- adc_sample_cnt<=1'b0;
- else if(state==ADC_SAMPLE)begin
- if(adc_data_en)
- adc_sample_cnt<=adc_sample_cnt+1'b1;
- else
- adc_sample_cnt<=adc_sample_cnt;
- end
- else
- adc_sample_cnt<=1'b0;
- end
-
- //产生采样使能信号给其他模块
- always@(posedge clk or posedge reset)begin
- if(reset)
- ad_sample_en<=0;
- else if(state==ADC_SAMPLE)
- ad_sample_en<=1;
- else
- ad_sample_en<=0;
- end
-
- /*状态切换ADC_SAMPLE->DDR_RD_FIFO_CLEAR
- begin
- if((adc_sample_cnt>=set_sample_num-1'b1)&& adc_data_en)
- state<=DDR_RD_FIFO_CLEAR;
- else
- state<=state;
- end
- */
(4)DDR_RD_FIFO_CLEAR
- //延时10个节拍
- always@(posedge clk or posedge reset)begin
- if(reset)
- rdfifo_clr_cnt<=0;
- else if(state==DDR_RD_FIFO_CLEAR)//如果进入了清 fifo 状态
- begin
- if(rdfifo_clr_cnt==9)
- rdfifo_clr_cnt<=5'd9;
- else
- rdfifo_clr_cnt<=rdfifo_clr_cnt+1'b1;
- end
- else
- rdfifo_clr_cnt<=5'd0;
- end
-
- //给清FIFO信号足够的拉高时间
- always@(posedge clk or posedge reset)begin
- if (reset)
- rdfifo_clr<=0;
- else if(state==DDR_RD_FIFO_CLEAR)
- begin
- if(rdfifo_clr_cnt==0||rdfifo_clr_cnt==1||rdfifo_clr_cnt==2)
- rdfifo_clr<=1'b1;
- else
- rdfifo_clr<=1'b0;
- end
- else
- rdfifo_clr<=1'b0;
- end
-
- /*状态切换DDR_RD_FIFO_CLEAR->DATA_SEND_START
- begin
- if(!rdfifo_empty && (rdfifo_clr_cnt==9))begin
- state<=DATA_SEND_START;
- end
- else
- state<=state;
- end
- */
(5)DATA_SEND_START
- /*状态切换DATA_SEND_START->DATA_SEND_WORKING
- begin
- state <= DATA_SEND_WORKING;
- end
- */
(6)DATA_SEND_WORKING
- //发送数据计数
- always@(posedge clk or posedge reset)begin
- if(reset)
- send_data_cnt<=32'd0;
- else if(state==IDLE)
- send_data_cnt<=32'd0;
- else if(rdfifo_rden)
- send_data_cnt<=send_data_cnt+1;
- else
- send_data_cnt<=send_data_cnt;
- end
-
- //DDR数据存到以太网缓存
- always@(posedge clk or posedge reset)
- if(reset) begin
- eth_fifo_wrreq <= 1'b0;
- eth_fifo_wrdata <= 'd0;
- end
- else if(rdfifo_rden) begin
- eth_fifo_wrreq <= 1'b1;
- eth_fifo_wrdata <= rdfifo_dout;
- end
- else begin
- eth_fifo_wrreq <= 1'b0;
- eth_fifo_wrdata <= 'd0;
- end
- /*状态切换DATA_SEND_WORKING->IDLE、DATA_SEND_WORKING->DATA_SEND_WORKING
- begin
- if(send_data_cnt >= set_sample_num-1'b1) begin
- rdfifo_rden <= 1'b0;
- state <= IDLE;
- end
- else begin
- rdfifo_rden <= 1'b1;
- state <= DATA_SEND_WORKING;
- end
- end
- */
看文章开头:Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(一)。
- S_IDLE:
- begin
- if(start)
- next_state = S_ARB;
- else
- next_state = S_IDLE;
- end
- 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 = 28 ,
- parameter AXI_ID = 4'b0000,
- parameter AXI_BURST_LEN = 8'd31 //burst length = 32
- )
- (
- input start ,
- // 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 [3: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 [3:0] m_axi_bid ,
- input [1:0] m_axi_bresp ,
- input m_axi_bvalid ,
- output m_axi_bready ,
- // Master Interface Read Address Ports
- output [3: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 [3: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
- );
将DDR读出的ADC数据发送到电脑端。




First Word Fall Through( FWFT)可以不需要读命令, 自动将最新的数据放在 dout 上。




以太网帧最大长度 1518 字节(数据段 1500 字节) , 其中数据段 1500 字节还包括 20 字节 IP 报文头部和 8 字节 UDP 报文头部, 所以数据帧发送的ACM9238 采集的数据最大长度为 1472 字节。
- always@(posedge clk125M or negedge reset_n)
- if(!reset_n) begin
- pkt_tx_en <= 1'd0;
- pkt_length <= 16'd0;
- data_num <= 32'd0;
- state <= 0;
- cnt_dly_time <= 28'd0;
- end
- else begin
- case(state)
- 0://得到 pkt_length 信号的初始值
- begin
- if(restart_req)begin
- data_num <= total_data_num;
- if((total_data_num << 1) >= 16'd1472)begin
- pkt_length <= 16'd1472; //一个数据2个字节
- state <= 1;
- end
- else if((total_data_num << 1) > 0)begin
- pkt_length <= total_data_num << 1; //一个数据2个字节
- state <= 1;
- end
- else begin
- state <= 0;
- end
- end
- end
- 1:
- begin
- if(fifo_rd_cnt >= (pkt_length -2)) begin
- pkt_tx_en <= 1'd1;
- state <= 2;
- end
- else begin
- state <= 1;
- pkt_tx_en <= 1'd0;
- end
- end
- 2:
- begin
- pkt_tx_en <= 1'd0;
- if(eth_tx_done)begin
- data_num <= data_num - pkt_length/2;
- state <= 3;
- end
- end
-
- 3:
- if(cnt_dly_time >= cnt_dly_min)begin
- state <= 4;
- cnt_dly_time <= 28'd0;
- end
- else begin
- cnt_dly_time <= cnt_dly_time + 1'b1;
- state <= 3;
- end
- 4:
- begin
- if(data_num * 2 >= 16'd1472)begin
- pkt_length <= 16'd1472;
- state <= 1;
- end
- else if(data_num * 2 > 0)begin
- pkt_length <= data_num * 2;
- state <= 1;
- end
- else begin
- state <= 0;
- end
- end
-
- default:state <= 0;
-
- endcase
- end