一、前言

  最近忙于硕士毕业设计和论文,没有太多时间编写博客,现总结下之前在某个项目中用到的一个高速ADC接口设计部分。ADC这一器件经常用于无线通信、传感、测试测量等领域。目前数字系统对高速数据采集的需求与日俱增,本文使用了米联客的一款速率较高的AD/DA模块ADQ9481来阐述利用FPGA设计高速ADC接口的技术要点。

二、ADC硬件特性分析

  首先必须通过datasheet分析其核心参数、接口定义和时序要求。ADC9481的采样率为250MSPS,精度8bit。其原理结构图如下:

  主要引脚说明:

  CLK+-:差分时钟输入,信号频率为250MHz

  VIN+-:模拟信号输入,范围是1Vpp

  VREF:电压参考输入/输出,这里使用内部固定参考电压模式

  SENSE:参考模式选择

  D7A~D0A:通道A数字信号输出

   D7B~D0B:通道B数字信号输出

  DCO+-:数字差分时钟输出,信号频率为125MHz

  S1:数据格式选择,该接口电压决定数格式时原码还是补码

  PDWN:低功耗选通

  接下来看看接口时序:

  很容易看出A和B两个数字输出通道是交替输出的,通道A在DCO+上升沿输出,B在DCO-上升沿输出。DCO+-的频率仅是采样率250MHz的一半,也就是降低了对数字系统处理速率的要求。

三、ADC接口设计

   根据上述时序关系可知,FPGA端需要在DCO+上升沿采集通道B数据,在DCO-上升沿采集通道A数据。并且由于在DCO+-同一变化沿时刻,通道A为前一个数据,因此要注意数据的采集顺序。这类数据采集的普遍做法是将数据存入到RAM中,然后利用本地时钟同步。具体方法是:按照两通道的数据顺序对数据进行拼接,之后缓存到异步FIFO中。本地PLL生成的125MHz时钟作为读侧和后续处理时钟信号。这里就要利用Xilinx FPGA的“原语”中的IBUFDS+BUFG,依次是差分输入缓冲器和全局缓冲器。前者可将差分信号转变为单端信号,后者则可让时钟信号到达FPGA内部逻辑引脚的时延和抖动最小。综上,ADC接口硬件架构如图:

四、HDL代码编写

  根据前文所述的硬件架构,ADC接口HDL代码如下:

  1 `timescale 1ns / 1ps
  2
  3 module adc_interface#(parameter WIDTH = 8,
  4                                 FRAME_LEN = 512
  5                                 //WAIT_CYC = 125_000_000//1s = 1000_000_000ns   1000_000_000/8 = 125_000_000
  6               )
  7     (
  8     input                       clk0,     //125MHZ
  9     input                       clk1,
 10
 11     input       [WIDTH-1:0]     da,
 12     input       [WIDTH-1:0]     db,
 13     output                      adc_pd,//省电模式选择
 14
 15     output                      pll_ce,
 16     output                      pll_rst_n,
 17     output                      pen,
 18
 19     input                       user_clk,//125MHZ
 20     input                       rst_n,
 21     input                       en,
 22     output reg [WIDTH*2-1:0]    dout = 0,
 23     output reg                  dout_vld = 0
 24     );
 25
 26     function integer clogb2 (input integer bit_depth);
 27       begin
 28         for(clogb2=0; bit_depth>0; clogb2=clogb2+1)
 29           bit_depth = bit_depth >> 1;
 30       end
 31     endfunction
 32
 33     localparam DATA_CNT_W = clogb2(FRAME_LEN-1);
 34
 35 (*DONT_TOUCH = "true"*)reg setup_flag = 0;
 36 reg [WIDTH-1:0] data_a = 0,data_b = 0;
 37 reg data_a_vld = 0,data_b_vld = 0;
 38 reg wr_en = 0;
 39 reg [WIDTH*2-1:0] wr_data = 0;
 40 reg rd_en = 0;
 41 wire empty;
 42 wire full;
 43 wire [WIDTH*2-1:0] rd_data;
 44 (*DONT_TOUCH = "true"*)wire en_pos;
 45 (*DONT_TOUCH = "true"*)reg [ (DATA_CNT_W-1):0]  data_cnt  =0   ;
 46 wire        add_data_cnt ;
 47 wire        end_data_cnt ;
 48 reg en_r0 = 0,en_r1 = 0,en_r2 = 0,en_r3 = 0;
 49
 50     assign pll_ce     = 1'b1;
 51     assign pll_rst_n  = 1'b1;
 52     assign adc_pd     = 1'b0;
 53     assign pen        = 1'b1;
 54
 55     /***************************采集触发**************************************/
 56     //异步处理
 57     always@(posedge clk0)begin
 58         en_r0 <= en;
 59         en_r1 <= en_r0;
 60         en_r2 <= en_r1;
 61         en_r3 <= en_r2;
 62     end
 63
 64     assign en_pos = en_r2 == 1'b1 && en_r3 == 1'b0;
 65
 66 always  @(posedge clk0 or negedge rst_n)begin
 67     if(rst_n==1'b0)begin
 68         setup_flag <= 0;
 69     end
 70     else if(end_data_cnt)
 71         setup_flag <= 0;
 72     else if(en_pos)begin
 73         setup_flag <= 1'b1;
 74     end
 75 end
 76
 77 always @(posedge clk0 or negedge rst_n) begin
 78     if (rst_n==0) begin
 79         data_cnt <= 0;
 80     end
 81     else if(add_data_cnt) begin
 82         if(end_data_cnt)
 83             data_cnt <= 0;
 84         else
 85             data_cnt <= data_cnt+1 ;
 86    end
 87 end
 88 assign add_data_cnt = (setup_flag);
 89 assign end_data_cnt = add_data_cnt  && data_cnt == (FRAME_LEN)-1 ;
 90
 91
 92 /***************************clk0(dco_p)采集DB**************************************/
 93     always@(posedge clk0 or negedge rst_n)begin
 94         if(rst_n == 0)
 95             data_b <= 0;
 96         else
 97             data_b <= db;
 98     end
 99
100     always@(posedge clk0 or negedge rst_n)begin
101         if(rst_n == 0)
102             data_b_vld <= 0;
103         else if(setup_flag)
104             data_b_vld <= 1'b1;
105         else
106             data_b_vld <= 0;
107     end
108
109  /****************************clk1(dco_n)采集DA**************************************/
110     always  @(negedge clk0 or negedge rst_n)begin
111         if(rst_n==1'b0)begin
112             data_a <= 0;
113         end
114         else begin
115             data_a <= da;
116         end
117     end
118
119     always@(negedge clk0 or negedge rst_n)begin
120         if(rst_n == 0)begin
121             data_a_vld <= 0;
122         end
123         else if(setup_flag)begin
124             data_a_vld <= 1'b1;
125         end
126         else
127             data_a_vld <= 0;
128     end
129  /****************************FIFO写逻辑**************************************/
130 //FIFO:width 16bit  depth 16 async
131
132 always  @(negedge clk0 or negedge rst_n)begin
133     if(rst_n==1'b0)begin
134         wr_en <= 0;
135     end
136     else if(data_a_vld & data_b_vld)begin
137         wr_en <= 1'b1;
138     end
139     else
140         wr_en <= 0;
141 end
142
143 always  @(negedge clk0 or negedge rst_n)begin
144     if(rst_n==1'b0)begin
145         wr_data <= 0;
146     end
147     else begin
148         wr_data <= {data_b,data_a};//高字节为后一个数据
149     end
150 end
151
152     /****************************FIFO读侧逻辑**************************************/
153     //非空即读
154     always@(*)begin
155         if(~empty)
156             rd_en = 1'b1;
157         else
158             rd_en = 0;
159     end
160
161     always  @(posedge user_clk or negedge rst_n)begin
162         if(rst_n == 0)
163             dout <= 0;
164         else
165             dout <= rd_data;
166     end
167
168     always  @(posedge user_clk or negedge rst_n)begin
169         if(rst_n == 0)
170             dout_vld <= 0;
171         else if(rd_en)begin
172             dout_vld <= 1'b1;
173         end
174         else
175             dout_vld <= 0;
176     end
177
178
179 //FIFO instance
180 fifo_generator_2 interface_fifo (
181   .wr_clk(~clk0),                // input wire wr_clk
182   .rd_clk(user_clk),                // input wire rd_clk
183   .din(wr_data),                      // input wire [15 : 0] din
184   .wr_en(wr_en),                  // input wire wr_en
185   .rd_en(rd_en),                  // input wire rd_en
186   .dout(rd_data),                    // output wire [15 : 0] dout
187   .full(full),                    // output wire full
188   .empty(empty),                  // output wire empty
189   .rd_data_count(rd_data_count),  // output wire [3 : 0] rd_data_count
190   .wr_data_count(wr_data_count)  // output wire [3 : 0] wr_data_count
191 );
192
193
194 endmodule

adc_interface

  顶层模块代码:

  1 `timescale 1ns / 1ps
  2
  3 module top#(parameter DATA_W = 8,//改动参数需要重新配置IP核
  4                       CHANNEL_NUM = 2)
  5    (
  6     input                                       dco_p,//125MHZ
  7     input                                       dco_n,
  8     input       [DATA_W-1:0]                    adc_p1,//通道A
  9     input       [DATA_W-1:0]                    adc_p2,//通道B
 10     output                                      adc_pd,
 11     output                                      pll_ce,
 12     output                                      pll_rst_n,
 13     output                                      pen,
 14
 15     //user interface signals
 16
 17     input                                       clk,//100M
 18     input                                       rst_n,
 19     input                                       en//上升沿有效 有效一次则将之后采集到的一帧数据写入到FFT模块进行运算
 20     );
 21 /*********************************parameters*******************************************/
 22     function integer clogb2 (input integer bit_depth);
 23       begin
 24         for(clogb2=0; bit_depth>0; clogb2=clogb2+1)
 25           bit_depth = bit_depth >> 1;
 26       end
 27     endfunction
 28
 29     localparam  FFT_W = 20,
 30                 FFT_N = 1024,
 31                 DATA_EACH_CHANNEL = FFT_N/CHANNEL_NUM;
 32
 33     //log2
 34     localparam DECH_W = clogb2(DATA_EACH_CHANNEL-1);
 35
 36
 37 /*********************************variables*******************************************/
 38 genvar ii;
 39 wire clk_out0,locked0;
 40 reg locked0_r0 = 0,locked0_r1 = 0;
 41 wire clk_user;
 42 wire dco;
 43 wire dco_bufg;
 44
 45 wire [DATA_W*2-1:0] data_adc;
 46 wire data_adc_vld;
 47 (*DONT_TOUCH = "true"*)wire [DATA_W*2-1:0] din;
 48 (*DONT_TOUCH = "true"*)wire din_vld;
 49 wire din_sop,din_eop;
 50 reg [ (DECH_W-1):0]  data_cnt =0    ;
 51 wire        add_data_cnt ;
 52 wire        end_data_cnt ;
 53
 54 /******************************clock generators****************************************/
 55
 56 //user clock generator
 57  clk_wiz_0 user_clock_gen
 58    (
 59     // Clock out ports
 60     .clk_out1(clk_out0),     // output clk_out0  125MHZ
 61     // Status and control signals
 62     .locked(locked0),       // output locked
 63    // Clock in ports
 64     .clk_in1(clk));      // input clk_in1 100MHZ
 65
 66     //pll lock信号同步
 67     always@(posedge clk_out0)begin
 68         locked0_r0 <= locked0;
 69         locked0_r1 <= locked0_r0;
 70     end
 71
 72     assign clk_user = clk_out0 & locked0_r1;
 73
 74
 75 // ADC clock generator
 76 IBUFDS #(
 77     .DIFF_TERM("FALSE"),
 78     .IBUF_LOW_PWR("FALSE"),
 79     .IOSTANDARD("DEFAULT")
 80 ) IBUFDS_inst (
 81     .O(dco),
 82     .I(dco_p),
 83     .IB(dco_n)
 84 );
 85
 86 BUFG BUFG_inst(
 87     .O(dco_bufg),
 88     .I(dco)
 89 );
 90
 91 /**********************************ADC interface module***********************************/
 92 adc_interface#(.WIDTH(DATA_W),
 93                .FRAME_LEN(DATA_EACH_CHANNEL))
 94 u_adc_interface
 95 (
 96     //adc -> fpga
 97     . clk0        (dco_bufg)  ,     //125MHZ 与dco_p同相
 98     . clk1        (~dco_bufg),      //125MHZ 与dco_n反相
 99     . da          (adc_p1)  ,
100     . db          (adc_p2)  ,
101     //fpga -> adc
102     . adc_pd      (adc_pd)  ,//省点模式选择
103     . pll_ce      (pll_ce)  ,
104     . pll_rst_n   (pll_rst_n)  ,
105     . pen         (pen),
106     //user
107     . user_clk    (clk_user),//125MHZ
108     . rst_n       (rst_n),
109     . dout        (data_adc),//debug
110     . dout_vld    (data_adc_vld),
111     . en          (en)//上升沿有效
112     );
113 //data counter
114 always @(posedge clk_user or negedge rst_n) begin
115     if (rst_n==0) begin
116         data_cnt <= 0;
117     end
118     else if(add_data_cnt) begin
119         if(end_data_cnt)
120             data_cnt <= 0;
121         else
122             data_cnt <= data_cnt+1 ;
123    end
124 end
125
126 assign add_data_cnt = data_adc_vld ;
127 assign end_data_cnt = add_data_cnt  && data_cnt == (DATA_EACH_CHANNEL)-1 ;
128
129 //input data to the user defined logic
130     assign din_sop  = add_data_cnt && data_cnt == 0;
131     assign din_eop  = end_data_cnt;
132     assign din      = data_adc;
133     assign din_vld  = data_adc_vld;
134
135
136 //user logic end
137
138 endmodule

top

  上述代码是之前做ADC采集信号频谱分析的部分代码,因此adc_interface模块中每触发一次则连续采集一帧数据长度,用于FFT运算。用户可以根据项目需求自行改动。顶层模块中则例化IBUFDS+BUFG原语,以及后续的自定义处理模块。

五、板级调试

  行为仿真是FPGA开发中必不可少的重要环节,通过充分测试可节省很多调试时间,这里仅给出板级调试结果。信号发生器产生三角波,利用ILA抓取芯片内部实时数据,并以模拟形式显示:

  由于在接口模块中将两通道输入拼接为一个数据,这里拆分后观察数据数值。可见拼接后数据波形呈现三角波形状,且幅值增大过程中高字节较大,幅值降低过程中高字节较小,说明数据拼接顺序无误,高字节为当前节拍后一个采样数据。两路输出数据最高位为0,证明输出数据格式是自然二进制数。

  若想细致地观察数据的模拟形状,可以通过灵活的TCL脚本将ILA抓取数据导出,并在MATLAB中查看。TCL命令为:

write_hw_ila_data E:/fpga_files/wave_file.csv [upload_hw_ila_data hw_ila_1] -csv_file –forcewave

  命令格式是:write_hw_ila_data <文件路径及文件名> [upload_hw_ila_data <ILA名称>] -csv_file -forcewave。键入该命令后,指定路径下会产生CSV文件。如让信号发生器产生频率为1MHz,峰峰值是1Vpp,偏移幅值是0.7V的正弦波。导出ILA抓取数据,并在MATLAB中绘制曲线如图:

  整体来看还是比较简单的。后边如有机会接触采样率更高的ADC芯片,会总结基于Select I/O IP Core的LVDS接口设计。

转载于:https://www.cnblogs.com/moluoqishi/p/10641557.html

FPGA高速ADC接口实战——250MSPS采样率ADC9481相关推荐

  1. 读论文之《基于EV10AQ190的高速ADC接口设计》

    背景: 之前通过数据手册简单地认识了一下 EV10AQ190这款ADC芯片,见博文地址: EV10AQ190A(4核ADC)博客精选目录 这个博文目录里面收录了有关博文,可以作为初步参考. 回顾: 今 ...

  2. 基于FPGA的串行差分(LVDS)高速ADC接口

    低电压差分信号 - 维基百科 1.低电压差分信号(Low-Voltage Differential Signaling,LVDS)是一种电子信号系统,可满足现今对高性能数据传输英语的需求,同时系统供电 ...

  3. FPGA高速采集系统

    1.高速采集系统的功能和组成 2.高速ADC接口在FPGA中的实现 2.1总体设计 2.2 高速ADC的SPI接口 2.3高速ADC数据接收接口 下面所有用到的时钟,都是从AD_clk分频出来的同源时 ...

  4. 双通道14位、500 MSPS采样率的AD9684 高速并行LVDS ADC接口介绍

    双通道14位.500 MSPS采样率的AD9684 高速并行LVDS ADC接口 本文为明德扬原创文章,转载请注明出处! AD9684是一款双通道14位.500 MSPS采样率 ,并行LVDS接口的模 ...

  5. FPGA高速数据采集设计JESD204B接口

    FPGA高速数据采集设计JESD204B接口的应用场景 作者:陈刀刀 本文为明德扬原创文章,转载请注明出处! 一,JESD204B应用的优缺点 接触过FPGA高速数据采集设计的朋友,应该会听过新术语& ...

  6. 8通道250MSPS采样率16位AD采集FMC子卡-高速数据采集专家

    FMC128是一款8通道250MHz采样率16位分辨率AD采集FMC子卡,符合VITA57.1规范,可以作为一个理想的IO模块耦合至FPGA前端,8通道AD将模拟信号数字化后通过高带宽的FMC连接器( ...

  7. 16bit高精度、250MSPS采样率,支持4通道同步采集、2通道同步输出的高速数据采集卡

    N-LINX 是南科复华信息科技研发的一款同时具备交流耦合程控放大器和支持单.双极性宽带信号输入的高速数据采集卡:板载FPGA具备实时信号处理能力,这些特性使N-LINX成为激光雷达.光电倍增管应用以 ...

  8. 基于 Kintex-7 FPGA + Nvidia TX2 = 16通道高速ADC数据采集系统

    在之前接触的设计中如果涉及要实现ADC采样的话,往往会从精度和速率来考虑对性能的影响,一般来说精度是固定的或有一个最大精度设置,但是采样速率的话,过快会造成采样不准确,往往会对整个设计的性能造成限制, ...

  9. AM5728通过GPMC接口与FPGA高速数据通信实现

    硬件:AM5728开发板:Artix-7开发板 软件:Linux am57xx-evm 4.4.19:Vivado 2015.2 作者:杭州矢志信息科技有限公司 邮箱:admin@sysjoint.c ...

最新文章

  1. 初看Windows Media Center
  2. python3 print设置不换行
  3. WebService大讲堂之Axis2(2):复合类型数据的传递
  4. ssh作业批改系统_如何看待「全国至少十省份叫停家长批改作业」?能否从根源解决问题?...
  5. 译:9.使用Redis进行消息传递
  6. Firefox 10正式发布
  7. 机器学习前的热身(必备)
  8. 【贪心】奶酪厂(jzoj 1285)
  9. 漫谈边缘计算(一):边缘计算是大势所趋
  10. Thinkphp聚合支付整合第三方和四方支付系统带代理功能
  11. 苹果手机录屏软件_手机录屏高清软件下载-手机录屏高清 安卓版v1.0.0
  12. linux 小度wifi,树莓派2小度wifi(MT7601U)驱动
  13. 计算机三级信息安全技术试题与答案,计算机三级《信息安全技术》练习题与答案...
  14. 希尔伯特:数学世界的亚历山大(2009-06-11 21:33:43)
  15. 记一次src挖洞实战——逻辑漏洞
  16. 大数据实时处理第三周课
  17. Mac OS X 下 su 命令提示 sorry 的解决方法
  18. mysql的可视化工具使用推荐
  19. PD充电加数据OTG方案
  20. Python学习之确定性过程搜索随机性过程搜索

热门文章

  1. Abz-G-F(4NO2)-P-OH, 67482-93-3
  2. mmdetection训练自己的VOC数据集
  3. 江南,雷泽紫装任务收集帖1
  4. 场效应管(场效晶体管),绝缘栅型、结型。内部载流子的运动是怎么样的,控制过程、主要参数、特性曲线
  5. ChatGPT 如何应用于决策?Rationale 带你狂飙!
  6. 第六课,Extjs中常用表单介绍与应用
  7. Commons Logging 的使用方法
  8. 中职生计算机应用基础随堂练,计算机应用基础随堂练习.doc
  9. Dcat-Admim菜单栏[顶部横向]和[左侧纵向]随心设置--Dcat-Admin框架实战(十三)
  10. 手机号身份证号隐藏显示