• Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(二)


    Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(前导)

    Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(一)

    Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(三)


     五、实验目的

            本次实验使用电脑上的网络调试助手,将命令帧通过以太网芯片RTL8211(RGMII接口)发送至ACZ7015开发板,提取UDP报文内容转换成控制命令,从而实现对ACM9238模块采样频率、数据采样个数以及采样通道的配置。

             配置完成之后,ACM9238模块开始采集数据,将采集的数据存储至DDR3中,然后通过网口以UDP协议传输到电脑。用户可以在电脑上通过网口调试工具进行指令的下发,并以文件的形式保存接收到的数据,然后使用MATLAB软件进行进一步的数据处理分析。

    PL 部分的模块说明如下:

    • pll 模块: 锁相环模块, 输入时钟 50M, 由 PS 输出给 PL; 输出 100M 的时钟给到 DDR3 控制器使用; 输出 50M 的时钟给其它模块使用。
    • eth_receive_cmd 模块: 以太网接收命令模块, 对以太网接收到的数据进行分析, 将接收的数据转换成相应的控制数据并输出到对应的模块。
    • ad9238_ctrl 模块: ACM9238 控制器模块, 该模块内部包含速度控制模块,以及数据位宽转换模块。
    • state_ctrl 模块: ADC 采集数据 DDR3 缓存以太网发送状态控制模块, 协调各个模块的信号控制, 程序状态的总控制模块。
    • fifo_axi4_adapter 模块: fifo 接口到 AXI4 接口的转换模块(含 2 个 FIFO)。

    六、ACM9238模块

    两路,

    采样上限50Msps,如果期望以1Msps 的采样速率采样, 则只需要每间隔 50 个采样数据取一个结果存储或使用, 其他 49 个数据直接舍弃。不建议降低ADC芯片的时钟信号。

    七、设计实例

    7.1模块设计

    7.1.1 eth_receive_cmd模块

    将以太网接收到的数据进行解析, 得到控制命令。

     

    (1)eth_udp_rx_gmii模块和rgmii_to_gmii模块
    (2)mmcm模块

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

     锁相环IP配置

    (3)fifo_rx模块

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

    (4)eth_cmd模块

    接收转命令模块。

    1. //非空时产生FIFO读请求信号
    2. always@(posedge clk or negedge reset_n)
    3. if(!reset_n)
    4. fifo_rd_req <= 1'b0;
    5. else if(!rx_empty)
    6. fifo_rd_req <= 1'b1;
    7. else
    8. fifo_rd_req <= 1'b0;
    9. //获得帧命令数据
    10. always@(posedge clk)
    11. if(fifo_rd_req)begin
    12. data_0[7] <= #1 fifodout;
    13. data_0[6] <= #1 data_0[7];
    14. data_0[5] <= #1 data_0[6];
    15. data_0[4] <= #1 data_0[5];
    16. data_0[3] <= #1 data_0[4];
    17. data_0[2] <= #1 data_0[3];
    18. data_0[1] <= #1 data_0[2];
    19. data_0[0] <= #1 data_0[1];
    20. end
    21. //判断帧命令数据
    22. always@(posedge clk or negedge reset_n)
    23. if(!reset_n)begin
    24. address <= 0;
    25. cmd_data <= 32'd0;
    26. cmdvalid <= 1'b0;
    27. end
    28. else if(fifo_rx_done)begin
    29. if((data_0[0]==8'h55)&&(data_0[1]==8'hA5)&&(data_0[7]==8'hF0))
    30. begin
    31. cmd_data[7:0] <= #1 data_0[6];
    32. cmd_data[15:8] <= #1 data_0[5];
    33. cmd_data[23:16] <= #1 data_0[4];
    34. cmd_data[31:24] <= #1 data_0[3];
    35. address <= #1 data_0[2];
    36. cmdvalid <= #1 1;
    37. end
    38. else
    39. cmdvalid <= #1 0;
    40. end
    41. else
    42. cmdvalid <= #1 0;
    (5)cmd_rx模块

    接收数据转换为控制数据。

    寄存器说明:

    1. always@(posedge clk or negedge reset_n)
    2. if(!reset_n)begin
    3. ChannelSel <= 2'b00;
    4. DataNum <= 32'd0;
    5. ADC_Speed_Set <= 32'd0;
    6. RestartReq <= 1'b0;
    7. end
    8. else if(cmdvalid)begin
    9. case(cmd_addr)
    10. 0: RestartReq <= 1'b1;
    11. 1: ChannelSel <= cmd_data[1:0];
    12. 2: DataNum <= cmd_data[31:0];
    13. 3: ADC_Speed_Set <= cmd_data[31:0];
    14. default:;
    15. endcase
    16. end
    17. else
    18. RestartReq <= 1'b0;

    7.1.2 ad9238_ctrl 模块

    控制ADC的采样速率,将12位数据转换为16位数据。

    (1)speed_ctrl模块

    1. always@(posedge clk or negedge reset_n)
    2. if(!reset_n)
    3. div_cnt <= 0;
    4. else if(ad_sample_en)begin
    5. if(div_cnt >= div_set)
    6. div_cnt <= 0;
    7. else
    8. div_cnt <= div_cnt + 1'd1;
    9. end
    10. else
    11. div_cnt <= 0;
    12. always@(posedge clk or negedge reset_n)
    13. if(!reset_n)
    14. adc_data_en <= 0;
    15. else if(div_cnt == div_set)
    16. adc_data_en <= 1;
    17. else
    18. adc_data_en <= 0;
    (2)ad_12bit_to_16bit模块

    1. always @(posedge clk)
    2. ad_out_valid <= ad_sample_en;
    3. assign s_ad_in1 = ad_in1 + 12'd2048;
    4. assign s_ad_in2 = ad_in2 + 12'd2048;
    5. always @(posedge clk)
    6. if(ad_sample_en && ch_sel == 2'b01)
    7. ad_out<={4'd0,s_ad_in1};//这样补 0 为了适应上位机
    8. else if(ad_sample_en && ch_sel == 2'b10)
    9. ad_out<={4'd0,s_ad_in2};//
    10. else if(ad_sample_en && ch_sel == 2'b00)
    11. ad_out<={4'd0,adc_test_data};
    12. else
    13. ad_out <= 16'd0;

    7.1.3 state_ctrl 模块

    控制信号的产生以及 ADC 何时启动数据传输。

    1. localparam IDLE = 4'd0; //空闲状态
    2. localparam DDR_WR_FIFO_CLEAR = 4'd1; //DDR 写 FIFO 清除状态
    3. localparam ADC_SAMPLE = 4'd2; //ADC 采样数据状态
    4. localparam DDR_RD_FIFO_CLEAR = 4'd3; //DDR 读 FIFO 清除状态
    5. localparam DATA_SEND_START = 4'd4; //数据发送状态
    6. localparam DATA_SEND_WORKING = 4'd5; //数据发送完成状态

    (1)IDLE

    1. //ADC 模块开始采样标志信号寄存
    2. always@(posedge clk or posedge reset)begin
    3. if(reset)
    4. start_sample_rm <= 1'b0;
    5. else if(state==IDLE)
    6. start_sample_rm <= start_sample;
    7. else
    8. start_sample_rm <= 1'b0;
    9. end
    10. /*状态切换IDLE->DDR_WR_FIFO_CLEAR
    11. begin
    12. if(start_sample_rm) begin //DDR 初始化完成并且产生启动采样信号
    13. state <= DDR_WR_FIFO_CLEAR; //进入写 FIFO 清除状态
    14. end
    15. else begin
    16. state <= state;
    17. end
    18. end
    19. */

    (2)DDR_WR_FIFO_CLEAR

    1. //延时10个节拍
    2. always@(posedge clk or posedge reset)begin
    3. if(reset)
    4. wrfifo_clr_cnt<=0;
    5. else if(state==DDR_WR_FIFO_CLEAR)//如果进入了清 fifo 状态
    6. begin
    7. if(wrfifo_clr_cnt==9)
    8. wrfifo_clr_cnt<=5'd9;
    9. else
    10. wrfifo_clr_cnt<=wrfifo_clr_cnt+1'b1;
    11. end
    12. else
    13. wrfifo_clr_cnt<=5'd0;
    14. end
    15. //给清FIFO信号足够的拉高时间
    16. always@(posedge clk or posedge reset)begin
    17. if (reset)
    18. wrfifo_clr<=0;
    19. else if(state==DDR_WR_FIFO_CLEAR)
    20. begin
    21. if(wrfifo_clr_cnt==0||wrfifo_clr_cnt==1||wrfifo_clr_cnt==2)
    22. wrfifo_clr<=1'b1;
    23. else
    24. wrfifo_clr<=1'b0;
    25. end
    26. else
    27. wrfifo_clr<=1'b0;
    28. end
    29. /*状态切换DDR_WR_FIFO_CLEAR->ADC_SAMPLE
    30. begin
    31. if(!wrfifo_full && (wrfifo_clr_cnt==9))
    32. state<=ADC_SAMPLE;
    33. else
    34. state<=DDR_WR_FIFO_CLEAR;
    35. end
    36. */

    (3)ADC_SAMPLE

    1. //根据ADC输出使能信号计数
    2. always@(posedge clk or posedge reset)begin
    3. if(reset)
    4. adc_sample_cnt<=1'b0;
    5. else if(state==ADC_SAMPLE)begin
    6. if(adc_data_en)
    7. adc_sample_cnt<=adc_sample_cnt+1'b1;
    8. else
    9. adc_sample_cnt<=adc_sample_cnt;
    10. end
    11. else
    12. adc_sample_cnt<=1'b0;
    13. end
    14. //产生采样使能信号给其他模块
    15. always@(posedge clk or posedge reset)begin
    16. if(reset)
    17. ad_sample_en<=0;
    18. else if(state==ADC_SAMPLE)
    19. ad_sample_en<=1;
    20. else
    21. ad_sample_en<=0;
    22. end
    23. /*状态切换ADC_SAMPLE->DDR_RD_FIFO_CLEAR
    24. begin
    25. if((adc_sample_cnt>=set_sample_num-1'b1)&& adc_data_en)
    26. state<=DDR_RD_FIFO_CLEAR;
    27. else
    28. state<=state;
    29. end
    30. */

    (4)DDR_RD_FIFO_CLEAR

    1. //延时10个节拍
    2. always@(posedge clk or posedge reset)begin
    3. if(reset)
    4. rdfifo_clr_cnt<=0;
    5. else if(state==DDR_RD_FIFO_CLEAR)//如果进入了清 fifo 状态
    6. begin
    7. if(rdfifo_clr_cnt==9)
    8. rdfifo_clr_cnt<=5'd9;
    9. else
    10. rdfifo_clr_cnt<=rdfifo_clr_cnt+1'b1;
    11. end
    12. else
    13. rdfifo_clr_cnt<=5'd0;
    14. end
    15. //给清FIFO信号足够的拉高时间
    16. always@(posedge clk or posedge reset)begin
    17. if (reset)
    18. rdfifo_clr<=0;
    19. else if(state==DDR_RD_FIFO_CLEAR)
    20. begin
    21. if(rdfifo_clr_cnt==0||rdfifo_clr_cnt==1||rdfifo_clr_cnt==2)
    22. rdfifo_clr<=1'b1;
    23. else
    24. rdfifo_clr<=1'b0;
    25. end
    26. else
    27. rdfifo_clr<=1'b0;
    28. end
    29. /*状态切换DDR_RD_FIFO_CLEAR->DATA_SEND_START
    30. begin
    31. if(!rdfifo_empty && (rdfifo_clr_cnt==9))begin
    32. state<=DATA_SEND_START;
    33. end
    34. else
    35. state<=state;
    36. end
    37. */

    (5)DATA_SEND_START

    1. /*状态切换DATA_SEND_START->DATA_SEND_WORKING
    2. begin
    3. state <= DATA_SEND_WORKING;
    4. end
    5. */

    (6)DATA_SEND_WORKING

    1. //发送数据计数
    2. always@(posedge clk or posedge reset)begin
    3. if(reset)
    4. send_data_cnt<=32'd0;
    5. else if(state==IDLE)
    6. send_data_cnt<=32'd0;
    7. else if(rdfifo_rden)
    8. send_data_cnt<=send_data_cnt+1;
    9. else
    10. send_data_cnt<=send_data_cnt;
    11. end
    12. //DDR数据存到以太网缓存
    13. always@(posedge clk or posedge reset)
    14. if(reset) begin
    15. eth_fifo_wrreq <= 1'b0;
    16. eth_fifo_wrdata <= 'd0;
    17. end
    18. else if(rdfifo_rden) begin
    19. eth_fifo_wrreq <= 1'b1;
    20. eth_fifo_wrdata <= rdfifo_dout;
    21. end
    22. else begin
    23. eth_fifo_wrreq <= 1'b0;
    24. eth_fifo_wrdata <= 'd0;
    25. end
    26. /*状态切换DATA_SEND_WORKING->IDLE、DATA_SEND_WORKING->DATA_SEND_WORKING
    27. begin
    28. if(send_data_cnt >= set_sample_num-1'b1) begin
    29. rdfifo_rden <= 1'b0;
    30. state <= IDLE;
    31. end
    32. else begin
    33. rdfifo_rden <= 1'b1;
    34. state <= DATA_SEND_WORKING;
    35. end
    36. end
    37. */

    7.1.4 fifo_axi_adapter模块

    看文章开头:Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(一)。

    1. S_IDLE:
    2. begin
    3. if(start)
    4. next_state = S_ARB;
    5. else
    6. next_state = S_IDLE;
    7. end
    1. module fifo_axi4_adapter #(
    2. parameter FIFO_DW = 16 ,
    3. parameter WR_AXI_BYTE_ADDR_BEGIN = 0 ,
    4. parameter WR_AXI_BYTE_ADDR_END = 1023 ,
    5. parameter RD_AXI_BYTE_ADDR_BEGIN = 0 ,
    6. parameter RD_AXI_BYTE_ADDR_END = 1023 ,
    7. parameter AXI_DATA_WIDTH = 128 ,
    8. parameter AXI_ADDR_WIDTH = 28 ,
    9. parameter AXI_ID = 4'b0000,
    10. parameter AXI_BURST_LEN = 8'd31 //burst length = 32
    11. )
    12. (
    13. input start ,
    14. // clock reset
    15. input clk ,
    16. input reset ,
    17. // wr_fifo wr Interface
    18. input wrfifo_clr ,
    19. input wrfifo_clk ,
    20. input wrfifo_wren ,
    21. input [FIFO_DW-1:0] wrfifo_din ,
    22. output wrfifo_full ,
    23. output [15:0] wrfifo_wr_cnt ,
    24. // rd_fifo rd Interface
    25. input rdfifo_clr ,
    26. input rdfifo_clk ,
    27. input rdfifo_rden ,
    28. output [FIFO_DW-1:0] rdfifo_dout ,
    29. output rdfifo_empty ,
    30. output [15:0] rdfifo_rd_cnt ,
    31. // Master Interface Write Address Ports
    32. output [3:0] m_axi_awid ,
    33. output [AXI_ADDR_WIDTH-1:0] m_axi_awaddr ,
    34. output [7:0] m_axi_awlen ,
    35. output [2:0] m_axi_awsize ,
    36. output [1:0] m_axi_awburst ,
    37. output [0:0] m_axi_awlock ,
    38. output [3:0] m_axi_awcache ,
    39. output [2:0] m_axi_awprot ,
    40. output [3:0] m_axi_awqos ,
    41. output [3:0] m_axi_awregion,
    42. output m_axi_awvalid ,
    43. input m_axi_awready ,
    44. // Master Interface Write Data Ports
    45. output [AXI_DATA_WIDTH-1:0] m_axi_wdata ,
    46. output [AXI_DATA_WIDTH/8-1:0] m_axi_wstrb ,
    47. output m_axi_wlast ,
    48. output m_axi_wvalid ,
    49. input m_axi_wready ,
    50. // Master Interface Write Response Ports
    51. input [3:0] m_axi_bid ,
    52. input [1:0] m_axi_bresp ,
    53. input m_axi_bvalid ,
    54. output m_axi_bready ,
    55. // Master Interface Read Address Ports
    56. output [3:0] m_axi_arid ,
    57. output [AXI_ADDR_WIDTH-1:0] m_axi_araddr ,
    58. output [7:0] m_axi_arlen ,
    59. output [2:0] m_axi_arsize ,
    60. output [1:0] m_axi_arburst ,
    61. output [0:0] m_axi_arlock ,
    62. output [3:0] m_axi_arcache ,
    63. output [2:0] m_axi_arprot ,
    64. output [3:0] m_axi_arqos ,
    65. output [3:0] m_axi_arregion,
    66. output m_axi_arvalid ,
    67. input m_axi_arready ,
    68. // Master Interface Read Data Ports
    69. input [3:0] m_axi_rid ,
    70. input [AXI_DATA_WIDTH-1:0] m_axi_rdata ,
    71. input [1:0] m_axi_rresp ,
    72. input m_axi_rlast ,
    73. input m_axi_rvalid ,
    74. output m_axi_rready
    75. );

    7.1.5 eth_send_data 模块

    将DDR读出的ADC数据发送到电脑端。

    (1)fifo_tx模块

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

    (2)eth_send_ctrl模块

    以太网帧最大长度 1518 字节(数据段 1500 字节) , 其中数据段 1500 字节还包括 20 字节 IP 报文头部和 8 字节 UDP 报文头部, 所以数据帧发送的ACM9238 采集的数据最大长度为 1472 字节。

    1. always@(posedge clk125M or negedge reset_n)
    2. if(!reset_n) begin
    3. pkt_tx_en <= 1'd0;
    4. pkt_length <= 16'd0;
    5. data_num <= 32'd0;
    6. state <= 0;
    7. cnt_dly_time <= 28'd0;
    8. end
    9. else begin
    10. case(state)
    11. 0://得到 pkt_length 信号的初始值
    12. begin
    13. if(restart_req)begin
    14. data_num <= total_data_num;
    15. if((total_data_num << 1) >= 16'd1472)begin
    16. pkt_length <= 16'd1472; //一个数据2个字节
    17. state <= 1;
    18. end
    19. else if((total_data_num << 1) > 0)begin
    20. pkt_length <= total_data_num << 1; //一个数据2个字节
    21. state <= 1;
    22. end
    23. else begin
    24. state <= 0;
    25. end
    26. end
    27. end
    28. 1:
    29. begin
    30. if(fifo_rd_cnt >= (pkt_length -2)) begin
    31. pkt_tx_en <= 1'd1;
    32. state <= 2;
    33. end
    34. else begin
    35. state <= 1;
    36. pkt_tx_en <= 1'd0;
    37. end
    38. end
    39. 2:
    40. begin
    41. pkt_tx_en <= 1'd0;
    42. if(eth_tx_done)begin
    43. data_num <= data_num - pkt_length/2;
    44. state <= 3;
    45. end
    46. end
    47. 3:
    48. if(cnt_dly_time >= cnt_dly_min)begin
    49. state <= 4;
    50. cnt_dly_time <= 28'd0;
    51. end
    52. else begin
    53. cnt_dly_time <= cnt_dly_time + 1'b1;
    54. state <= 3;
    55. end
    56. 4:
    57. begin
    58. if(data_num * 2 >= 16'd1472)begin
    59. pkt_length <= 16'd1472;
    60. state <= 1;
    61. end
    62. else if(data_num * 2 > 0)begin
    63. pkt_length <= data_num * 2;
    64. state <= 1;
    65. end
    66. else begin
    67. state <= 0;
    68. end
    69. end
    70. default:state <= 0;
    71. endcase
    72. end
  • 相关阅读:
    java+ssm+vue基本微信小程序的高速公路服务区充电桩在线预订系统 uniapp小程序
    echarts自动播放tooltip提示框
    Unity Andriod调试
    数据结构与算法 -- 链表
    六、stm32-OLED
    AVS3:双向梯度修正BGC
    【云原生】阿里云 RocketMQ介绍
    做销售,如何实现快速初筛客户?
    RTC 技术的试金石:火山引擎视频会议场景技术实践
    SpringBoot 整合 Netty
  • 原文地址:https://blog.csdn.net/chinalihuanyu/article/details/136385897