这一篇在干嘛?
前一个实验是 FPGA「凭空」生成正弦波(DA),这一个则把整条模拟链路走通:外部信号源输入一个模拟波形,12 位高速 ADC(ADS807)把它采样成数字量送进 FPGA,FPGA 不做任何处理、原样直通,再交给 12 位 DAC(DA902)还原成模拟信号输出。示波器同时看输入和输出,两条波形几乎重合——这就是最简单的「AD→DA 直通」链路,也是一切数字采集/回放系统(示波器、声卡、软件无线电)的最小模型。
实验目标与原理
实验目的
了解通过 12 位 ADS807 采样转成数据信号,再通过 12 位 DA 输出模拟信号,接示波器观察。
ADC 采样原理速览
模拟信号是时间和幅度都连续的曲线,而 FPGA 只认数字。ADC(模数转换器)干两件事:
- 时间上离散化(采样):每隔一个时钟周期测一次输入电压。根据奈奎斯特采样定理,采样率必须大于信号最高频率的 2 倍,采样后的数字序列才能不失真地代表原信号。本实验的 ADS807 并行采样速率高达 50MHz,而测试信号只有 2.5MHz,余量非常充足。
- 幅度上离散化(量化):把测到的电压除以最小分辨率,四舍五入成 12 位整数(0~4095)。位数越多,台阶越细,还原越逼真。
DAC(DA902)则反过来:把 12 位整数按比例还原成电压。AD 与 DA 的工作时钟都由 FPGA 提供,两边节拍一致,数据才能「无缝接力」。
为什么直通也有意义
有人会问:FPGA 中间什么都没做,这个实验是不是「白做」了?恰恰相反,直通是所有信号处理系统的第一块试金石:
- 它验证了时序——AD 数据在 50MHz 下能否被 FPGA 正确接收、DA 能否正确输出,任何一环时序不满足,示波器上波形立刻失真;
- 它验证了幅度链路——从 AD_IN 到 DA_OUT 的增益、偏置是否匹配,直通时输入输出应一比一对应;
- 它给出了基准——以后在中间插入滤波器、算法模块,只要拿直通的示波器对比图一比,新增处理的得失一目了然。
工程上把这叫「管道先行」:先打通最简单的通路,再逐级往里加功能,每加一级都能和上一级的基准对比。
直通架构与代码解析
本示例通过 AD 对外围提供的模拟信号进行采集,采集完的数据输送给 DA,转换成模拟信号输出。AD/DA 的工作时钟由 FPGA 提供。ADS807/DA902 使用手册已提供 pdf 文档,供参考使用。
完整 Verilog 实现如下:
module adda_direct(clk_50m, reset_n, ad_dat, da_dat, da_clk, ad_clk
);
input clk_50m;
input reset_n;
input [11:0] ad_dat;
output da_clk;
output ad_clk;
output[11:0] da_dat;
reg [11:0] da_dat;
wire clk_50m;
assign da_clk = ~clk_50m;
assign ad_clk = ~clk_50m;
always @ (posedge clk_50m or negedge reset_n)
begin
if(!reset_n)
begin
da_dat <= 12'b0;
end
else
da_dat <= ad_dat;
end
endmodule整个模块只有一个 always 块,但每行都有讲究:
- assign ad_clk = ~clk_50m;:AD/DA 的转换时钟不是直接用 50M 时钟,而是取反后输出。50MHz 时钟的上升沿采样 AD 数据时,取反时钟正好处于相对稳定的区间,让 ADC 内部采样与 FPGA 读数在时序上错开半个周期,避免采样「撞车」。这是高速并行接口设计中常见的时序配合技巧。
- da_dat <= ad_dat;:所谓「直通」,就是在 50M 时钟每个上升沿把 AD 送来的 12 位数据打一拍寄存器,再原样送往 DA。虽然只延迟了一个时钟周期(20ns),但从示波器看,输出波形与输入几乎同步,所以叫直通。这一拍寄存器非常重要——它把 AD 引脚来的异步数据先「收进」FPGA 内部寄存器再输出,保证了信号的建立/保持余量。
- 异步复位:复位低电平有效(negedge reset_n 触发、!reset_n 判断),把输出清零;正常工作时 reset_n 保持高电平即可。
数据链路总结:外部模拟信号 → AD_IN → ADS807(50MHz 采样、12 位并行输出 ad_dat[11:0])→ FPGA 打一拍 → da_dat[11:0] → DA902(165MHz 并行转换)→ DA_OUT → 示波器。
硬件准备
图 11-1 为 ADDA 板介绍:

扩展板上同时集成了 AD807(12 位并行 ADC,采样率 50MHz)和 DA902(12 位并行 DAC,转换率 165MHz),AD_IN 是模拟输入端口,DA_OUT 是模拟输出端口,AGND 为模拟地。
引脚分配
图 11-2、图 11-3 为管脚分配示意:

图 11-2

图 11-3
IO 管脚约束:
set_pin_assignment { ad_dat[10] } { LOCATION = K12; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; }
set_pin_assignment { ad_dat[11] } { LOCATION = J13; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; }
set_pin_assignment { ad_dat[1] } { LOCATION = F15; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; }
set_pin_assignment { ad_dat[2] } { LOCATION = F14; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; }
set_pin_assignment { ad_dat[3] } { LOCATION = E15; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; }
set_pin_assignment { ad_dat[4] } { LOCATION = D16; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; }
set_pin_assignment { ad_dat[5] } { LOCATION = M10; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; }
set_pin_assignment { ad_dat[6] } { LOCATION = L10; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; }
set_pin_assignment { ad_dat[7] } { LOCATION = P11; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; }
set_pin_assignment { ad_dat[8] } { LOCATION = M14; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; }
set_pin_assignment { ad_dat[9] } { LOCATION = L14; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; }
set_pin_assignment { clk_50m } { LOCATION = R7; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; }
set_pin_assignment { clk_out } { LOCATION = P4; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da_clk } { LOCATION = K15; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da_dat[0] } { LOCATION = R15; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da_dat[10] } { LOCATION = H15; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da_dat[11] } { LOCATION = J16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da_dat[1] } { LOCATION = P15; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da_dat[2] } { LOCATION = P16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da_dat[3] } { LOCATION = N14; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da_dat[4] } { LOCATION = N16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da_dat[5] } { LOCATION = L16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da_dat[6] } { LOCATION = M15; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da_dat[7] } { LOCATION = M16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da_dat[8] } { LOCATION = K16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { da_dat[9] } { LOCATION = H16; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 8; PULLTYPE = NONE; }
set_pin_assignment { reset_n } { LOCATION = A2; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; }信号含义:ad_dat[11:0] 是 AD807 采样输出的 12 位数据(输入方向),da_dat[11:0] 是送往 DA902 的 12 位数据(输出方向),ad_clk/da_clk 是给两颗芯片的转换时钟,clk_50m(R7)是系统时钟,reset_n(A2)是复位,clk_out(P4)是时钟输出引脚。
输入信号幅度必须限制
AD 模块的测试条件是输入峰峰值 500mV 的波形,不用偏置;实测示波器观察到的波形峰值不超过 500mV。使用信号源时一定要先把幅度调到规定范围内再接入 AD_IN,输入过大会导致 ADC 饱和削顶,甚至损坏模拟前端。接线时注意 AGND(模拟地)与信号源共地,否则波形会飘。
上板流程
把 12 位高速 ADDA 模块插在开发板 7A75C 的 J1 口,下载程序后,通过信号源输入如 2.5MHz 信号通过 AD_IN 接口,通过示波器接 DA_OUT 观察转换波形。
现象与总结
示波器观察波形如 11-5,峰值不超过 500mV:

示波器观察波形,外围信号 2.5M:

可以看到示波器上输出的波形与输入的 2.5MHz 信号形状一致——数据被原封不动地「搬运」了一轮。虽然本实验的 FPGA 中间什么都没做,但它搭好了「AD 进数字、DA 出模拟」的完整管道;在这条管道中间插入 FIR 滤波、FFT、调制解调等任何数字处理模块,就变成了真正的数字信号处理系统。直通实验正是验证时序链路是否可靠的「管道试压」。
自测
自测
- 本实验中 AD807 和 DA902 的采样/转换速率分别是多少?测试输入信号频率是多少?是否满足奈奎斯特采样定理?
- 代码中 assign ad_clk = ~clk_50m; 为什么要把时钟取反后再送给 AD/DA?
- 「数据直通」具体指什么操作?中间那一拍寄存器(da_dat <= ad_dat)有什么作用?
- 复位信号 reset_n 是高电平有效还是低电平有效?复位后输出是什么值?
- AD 模块对输入信号有什么要求?输入幅度过大会出现什么后果?