FPGA高速ADC接口实战——250MSPS采样率ADC9481
一、前言
最近忙于硕士毕业设计和论文,没有太多时间编写博客,现总结下之前在某个项目中用到的一个高速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相关推荐
- 读论文之《基于EV10AQ190的高速ADC接口设计》
背景: 之前通过数据手册简单地认识了一下 EV10AQ190这款ADC芯片,见博文地址: EV10AQ190A(4核ADC)博客精选目录 这个博文目录里面收录了有关博文,可以作为初步参考. 回顾: 今 ...
- 基于FPGA的串行差分(LVDS)高速ADC接口
低电压差分信号 - 维基百科 1.低电压差分信号(Low-Voltage Differential Signaling,LVDS)是一种电子信号系统,可满足现今对高性能数据传输英语的需求,同时系统供电 ...
- FPGA高速采集系统
1.高速采集系统的功能和组成 2.高速ADC接口在FPGA中的实现 2.1总体设计 2.2 高速ADC的SPI接口 2.3高速ADC数据接收接口 下面所有用到的时钟,都是从AD_clk分频出来的同源时 ...
- 双通道14位、500 MSPS采样率的AD9684 高速并行LVDS ADC接口介绍
双通道14位.500 MSPS采样率的AD9684 高速并行LVDS ADC接口 本文为明德扬原创文章,转载请注明出处! AD9684是一款双通道14位.500 MSPS采样率 ,并行LVDS接口的模 ...
- FPGA高速数据采集设计JESD204B接口
FPGA高速数据采集设计JESD204B接口的应用场景 作者:陈刀刀 本文为明德扬原创文章,转载请注明出处! 一,JESD204B应用的优缺点 接触过FPGA高速数据采集设计的朋友,应该会听过新术语& ...
- 8通道250MSPS采样率16位AD采集FMC子卡-高速数据采集专家
FMC128是一款8通道250MHz采样率16位分辨率AD采集FMC子卡,符合VITA57.1规范,可以作为一个理想的IO模块耦合至FPGA前端,8通道AD将模拟信号数字化后通过高带宽的FMC连接器( ...
- 16bit高精度、250MSPS采样率,支持4通道同步采集、2通道同步输出的高速数据采集卡
N-LINX 是南科复华信息科技研发的一款同时具备交流耦合程控放大器和支持单.双极性宽带信号输入的高速数据采集卡:板载FPGA具备实时信号处理能力,这些特性使N-LINX成为激光雷达.光电倍增管应用以 ...
- 基于 Kintex-7 FPGA + Nvidia TX2 = 16通道高速ADC数据采集系统
在之前接触的设计中如果涉及要实现ADC采样的话,往往会从精度和速率来考虑对性能的影响,一般来说精度是固定的或有一个最大精度设置,但是采样速率的话,过快会造成采样不准确,往往会对整个设计的性能造成限制, ...
- AM5728通过GPMC接口与FPGA高速数据通信实现
硬件:AM5728开发板:Artix-7开发板 软件:Linux am57xx-evm 4.4.19:Vivado 2015.2 作者:杭州矢志信息科技有限公司 邮箱:admin@sysjoint.c ...
最新文章
- 初看Windows Media Center
- python3 print设置不换行
- WebService大讲堂之Axis2(2):复合类型数据的传递
- ssh作业批改系统_如何看待「全国至少十省份叫停家长批改作业」?能否从根源解决问题?...
- 译:9.使用Redis进行消息传递
- Firefox 10正式发布
- 机器学习前的热身(必备)
- 【贪心】奶酪厂(jzoj 1285)
- 漫谈边缘计算(一):边缘计算是大势所趋
- Thinkphp聚合支付整合第三方和四方支付系统带代理功能
- 苹果手机录屏软件_手机录屏高清软件下载-手机录屏高清 安卓版v1.0.0
- linux 小度wifi,树莓派2小度wifi(MT7601U)驱动
- 计算机三级信息安全技术试题与答案,计算机三级《信息安全技术》练习题与答案...
- 希尔伯特:数学世界的亚历山大(2009-06-11 21:33:43)
- 记一次src挖洞实战——逻辑漏洞
- 大数据实时处理第三周课
- Mac OS X 下 su 命令提示 sorry 的解决方法
- mysql的可视化工具使用推荐
- PD充电加数据OTG方案
- Python学习之确定性过程搜索随机性过程搜索
热门文章
- Abz-G-F(4NO2)-P-OH, 67482-93-3
- mmdetection训练自己的VOC数据集
- 江南,雷泽紫装任务收集帖1
- 场效应管(场效晶体管),绝缘栅型、结型。内部载流子的运动是怎么样的,控制过程、主要参数、特性曲线
- ChatGPT 如何应用于决策?Rationale 带你狂飙!
- 第六课,Extjs中常用表单介绍与应用
- Commons Logging 的使用方法
- 中职生计算机应用基础随堂练,计算机应用基础随堂练习.doc
- Dcat-Admim菜单栏[顶部横向]和[左侧纵向]随心设置--Dcat-Admin框架实战(十三)
- 手机号身份证号隐藏显示