基于FPGA的Σ-△ADC控制信号同步方法
技术领域
本发明涉及医疗电子或数字医疗设备领域中产品使用的高精度△-Σ模数转换器(下称Σ-△ADC)控制信号与时钟信号同步的方法,特别是涉及一种基于FPGA(现场可编程门阵列)实现的模数转换器控制信号与工作时钟之间稳定同步的处理方法。
背景技术
基于Σ-△调制器的模数转换器(简称Σ-△ADC),是一种高分辨率的数据采样器件,通常可以实现20-bit~24-bit的高分辨率数据采样和转换,随着各种应用场合对数据高分辨率采样的需求增长,这类器件已经广泛应用于医疗电子和数字医疗设备、航天、精密仪器等尖端科技领域。电流检测型Σ-△ADC前端的模拟输入通道含有2个电流积分器(Current Integrator,CI),其作用是把输入的模拟电流通过线性积分,转换成模拟电压输出,CI可以接收微弱的输入电流,输入电流的动态范围为几十pA~几百mA。Σ-△ADC的结构原理如图1所示,电压检测型Σ-△ADC的工作原理与之类似。另外不同厂商基于Σ-△调制器的ADC芯片工作原理也基本相似。本发明以电流检测型Σ-△ADC介绍控制信号的同步方法。
图1所示Σ-△ADC芯片的转换控制信号CONV和时钟信号CLK,由片外的相应管脚施加,时钟CLK为方波,典型的工作频率为10MHz。芯片是通过控制信号CONV来驱动采样和转换过程。当CONV=1时,ADC进行采样,采样过程分两步,其中第一步是电流积分器的积分过程,将微弱的模拟输入电流转换为模拟电压,第二步是Σ-△调制器把模拟电压值变换成与之成正比的时间宽度信号。当CONV=0时,ADC停止采样,进行转换,转换过程中ADC芯片根据时间宽度生成数字脉冲序列,由于数字脉冲序列使原始信号的噪声频带被右移,这样通过数字滤波以后,噪声对输入信号的影响被大大削弱。在连续数据转换模式下,控制信号CONV高、低电平的持续时间保持一致,且大于芯片完成转换的时间,从而使得CONV看上去如同一个方波。可以看出,控制信号CONV从一次“0”到“1”的状态切换迁移至下一次“0”到“1”的状态切换,则意味着Σ-△ADC芯片已经完成一次模拟量的采样和转换,因此CONV信号高低电平切换的次数/2,即是芯片的采样频率。
为确保Σ-△ADC芯片获得最佳的噪声性能,要求其转换控制信号CONV从1到0切换的下降沿或从0到1切换的上升沿(统称为CONV的切换沿),与时钟信号CLK的上升沿保持较高精度的同步,例如有些芯片要求CONV切换沿与CLK上升沿保持±10ns同步(1ns = 10-9s)。
对于控制信号的高低电平切换沿与时钟信号同步精度要求在±10ns以内,如果选择使用分立的IC器件搭建逻辑和时序电路来实现控制信号的同步,其难度非常大,甚至是不可能实现,原因在于一个普通的逻辑门延迟就可能有10ns数量级甚至更高。针对分立IC实现的困难,考虑到FPGA主要以查找表(LUT)和D触发器(DFF)分别实现逻辑和时序功能,传输延迟较小,因此本发明提出了一种采用FPGA实现控制信号与时钟同步的方法,完全满足芯片最佳噪声性能对控制信号同步的要求,并且可以获得极高的同步精度。
鉴于目前FPGA的价格相对较低,故基于FPGA的同步方法不会增加产品的设计成本,但在产品中使用FPGA提高了设计的技术门槛;另一方面,Σ-△ADC芯片厂商为降低用户在产品设计过程中的难度,推出了片内同步解决方案的Σ-△ADC芯片,但片内同步的Σ-△ADC相对于图1所示的片外同步Σ-△ADC,价格高出很多。因此,本发明基于FPGA的控制信号与时钟同步的方法,同步精度高,成本相对低、又能使芯片工作在最佳噪声性能状态。
参考文献:
《关于数字逻辑门电路平均延迟时间的实验测量》,百度文库。
发明内容
为克服分立元件实现控制信号同步的困难,确保控制信号同步使Σ-△ADC工作在最佳噪声性能的状态,本发明提出了一种基于FPGA的Σ-△ADC控制信号与时钟信号同步的方法。这种方法的基本原理和思路如下:
FPGA芯片外接一定频率的有源晶振(Oscillator),通过VHDL文件对FPGA芯片的片内资源进行描述、编译、综合,使FPGA芯片片内的一部分或全部资源按照VHD文件的描述,连接构成逻辑和时序电路,其输出信号之间是同步的,且同步精度满足Σ-△ADC芯片的要求。这时这种逻辑和时序电路依赖输入的有源晶振时钟生成新的时钟和控制信号,将新时钟和控制信号输出连接至Σ-△ADC即可驱动Σ-△ADC工作在最佳噪声性能状态。
利用VHDL(源代码文件存储的文件名格式为*.VHD)进行FPGA的设计,是分顶层(Top Level)和下层(Level 1)两层进行的,顶层VHD文件和下层VHD文件分别单独定义自己的实体和输入、输出信号。下层VHD文件实体的输出信号在类型和宽度方面与顶层VHD文件实体的输出信号一致,同时应确保顶层VHD在调用下层VHD实体时,二者的端口信号映射(Port Map)关系正确。如图2所示。
顶层VHD文件描述的实体,其实现的功能是:利用外部频率较高的有源晶振(如50MHz),通过FPGA内置的PLL进行倍频和分频,产生一个频率相对较低的时钟C0(如20MHz),这个时钟C0就是FPGA内部的一种全局时钟资源,具有零畸变、零漂移的特点,因此使用它作为新的时钟和其他控制信号生成的基准时钟。如果Σ-△ADC需要N-1个控制信号和1个时钟CLK(N>=2),那么以C0作为时钟CLK和N-1个控制信号的生成基准。在本发明实施的具体例子中使用的Σ-△ADC芯片只有一个转换控制信号CONV和一个工作时钟CLK,满足N=2的条件。如图2所示。
下层VHD文件描述的实体,包含了N(N>=2)个并行工作的“信号单元”,每个“信号单元”由一个D触发器(D Flip-Flop,DFF)及分频所需的计数器组成,每个“信号单元”由一个具有相同VHDL原语的进程(process)描述,每个进程均由全局时钟C0触发,如图3所示。该实体实现的功能是:以全局时钟C0作为基准,将时钟C0同时连接至N个DFF,使这N个DFF并行工作,每个DFF结合各自分频所需的计数器,完成一路控制信号CONVi(i=1,2,…n-1)或时钟CLK的生成。基于并行工作的结构模式,使得每个DFF输出的控制信号或时钟相对于全局时钟C0的延迟在理论上是一样的,因为FPGA的制造工艺使每个DFF具有几乎完全相同的物理性质和传输延迟。于是N个并行工作的DFF生成和输出的控制信号CONVi(i=1,2,…n-1)与时钟CLK之间,可以形成较高精度的同步。
由上文所述,时钟CLK和控制信号CONVi(i=1,2,…n-1)的生成和同步,首先采用了以FPGA的内部全局时钟C0作为基准。其次,为了实现时钟CLK相对全局时钟C0具有任意非零偶数(2,4,6,…)的分频系数,本发明提出了一种“计数器逻辑”单元,即在计数器溢出时,C0时钟上升沿处,使生成的输出时钟CLK的电平产生状态翻转,如图3所示。这种驱动机制,可以使“信号单元”生成的时钟CLK相对输入时钟C0的分频系数是任意非零偶数。描述DFF和计数器构成电路的进程原语如下:
p1: process(clkin) --p1为进程号
variable cnt: integer range 1 to m :=1; --计数器及初始化,m为整数且m>=1
begin
if(clkin='1' and clkin'event) then
if(cnt=1) then --计数器逻辑部分 开始。当计数器溢出时将输出信号状态翻转
cnt := 1;
if(clkout='0') then
clkout <= '1';
elsif(clkout='1') then
clkout <= '0';
end if;
else
cnt := cnt+1;
end if; --计数器逻辑部分 结束
end if;
end process p1;
这个进程生成的输出时钟clkout相对输入时钟clkin具有2m的分频系数(m为整数且m>=1)。
同理,当使用与上述“信号单元”生成控制电平信号CONV时,那么CONV切换的频率相对输入时钟C0的分频系数,同样具有任意非零偶数(2,4,6,…)的特点。可见通过“计数器逻辑”单元功能所获得的任意非零偶数的分频系数,使应用的灵活性得到增强。
本发明提出的控制信号同步方法,分别通过Quartus II 9.0软件仿真和产品单板实测:
在仿真情况下,控制信号CONV高低电平切换的上升沿与时钟CLK上升沿同步的仿真波形如图4所示,同步精度约为1ns;控制信号CONV高低电平切换的下降沿与时钟CLK上升沿同步的仿真波形如图5所示,同步精度约为1ns。
在实测情况下,使用数字示波器测量的控制信号CONV高低电平切换的上升沿与时钟CLK上升沿同步波形如图6所示,同步精度小于1ns。控制信号CONV高低电平切换的下降沿与时钟CLK上升沿同步波形如图7所示,同步精度亦小于1ns。
结论:本发明提出的基于FPGA实现的Σ-△ADC控制信号CONV高低电平切换的上升沿和下降沿与时钟CLK同步的方法,同步精度小于<1ns。
附图说明
图1为Σ-△ADC的结构原理示意图;
图2为本发明采用的VHDL分层设计理念示意图;
图3为N并行D触发器结构原理示意图;
图4为控制信号CONV高低电平切换上升沿与时钟CLK上升沿同步仿真波形示意图;
图5为控制信号CONV高低电平切换下降沿与时钟CLK上升沿同步仿真波形示意图;
图6为控制信号CONV高低电平切换上升沿与时钟CLK上升沿同步实测波形示意图;
图7为控制信号CONV高低电平切换下降沿与时钟CLK上升沿同步实测波形示意图;
图8为具体实施的系统示意图;
图9为下层VHD文件经Quartus II软件综合生成的RTL视图。
本发明设计过程中用到的器件和工具说明如下:
DDC112:德州仪器(TI)公司生产的一种电流输入型Σ-△ADC,具有20-bit分辨率的,工作时钟CLK频率为1MHz~12MHz,厂商推荐的工作时钟频率为10MHz,采样频率最大为2KHz。该芯片的输入电流范围为20pA~750mA。该芯片控制信号CONV对时钟CLK的同精度的要求是±10ns。
EP1C3T100C8:ALTERA公司生产的一种型号的FPGA器件,属于Cyclone I家族,为目前市场上价格低廉的小规模FPGA器件。I/O管脚的电平标准选择为3.3V。
JTAG:一种用于FPGA器件编程和下载代码的硬件连接工具,连接PC端采用USB接口,连接目标FPGA芯片采用IDC10的排线插头。
Quartus II 9.0:ALTERA公司FPGA的开发工具软件,对于已开发的Project,可以方便地实现仿真。
数字示波器:带宽500M的数字示波器,用于测量和观察单板信号同步波形和同步精度。
具体实施方式
本发明完全使用VHDL硬件描述语言,通过Quartus II 9.0集成开发环境,实现对VHDL源文件及工程文件的编译,综合,以及下载和编程Flash。设计任务是以FPGA的50MHz有源晶振输入时钟为基础,通过FPGA芯片生成一个10MHz的工作时钟CLK和一个1KHz的控制信号CONV,供DDC112芯片使用,根据DDC112的Datasheet,其控制信号CONV的高低电平切换沿与时钟CLK上升沿的同步精度为±10ns。组成的硬件系统如图8所示。图9是下列VHDL源码经编译、综合所生成的RTL(Register Transfer Level,寄存器传输层)视图,可以看出下层的VHD文件确实是定义和描述了一个具有并行结构的DFF和相关的计数器逻辑。
具体实施的源代码如下(分为顶层文件和下层文件):
一、顶层文件
LIBRARY ieee;
USE ieee.std_logic_1164.all;
LIBRARY altera_mf;
USE altera_mf.all;
ENTITY pll IS
PORT
(
inclk0 : IN STD_LOGIC := '0';
c0 : buffer STD_LOGIC;
CLK,CONV : out std_logic
);
END pll;
ARCHITECTURE SYN OF pll IS
component ddc112_conv is
port(clkin: in std_logic;
clkout: buffer std_logic;
conv: buffer std_logic);
end component;
SIGNAL sub_wire0 : STD_LOGIC_VECTOR (5 DOWNTO 0);
SIGNAL sub_wire1 : STD_LOGIC ;
SIGNAL sub_wire2 : STD_LOGIC ;
SIGNAL sub_wire3 : STD_LOGIC_VECTOR (1 DOWNTO 0);
SIGNAL sub_wire4_bv : BIT_VECTOR (0 DOWNTO 0);
SIGNAL sub_wire4 : STD_LOGIC_VECTOR (0 DOWNTO 0);
COMPONENT altpll
GENERIC (
clk0_divide_by : NATURAL;
clk0_duty_cycle : NATURAL;
clk0_multiply_by : NATURAL;
clk0_phase_shift : STRING;
compensate_clock : STRING;
inclk0_input_frequency : NATURAL;
intended_device_family : STRING;
lpm_hint : STRING;
lpm_type : STRING;
operation_mode : STRING;
pll_type : STRING;
port_activeclock : STRING;
port_areset : STRING;
port_clkbad0 : STRING;
port_clkbad1 : STRING;
port_clkloss : STRING;
port_clkswitch : STRING;
port_configupdate : STRING;
port_fbin : STRING;
port_inclk0 : STRING;
port_inclk1 : STRING;
port_locked : STRING;
port_pfdena : STRING;
port_phasecounterselect : STRING;
port_phasedone : STRING;
port_phasestep : STRING;
port_phaseupdown : STRING;
port_pllena : STRING;
port_scanaclr : STRING;
port_scanclk : STRING;
port_scanclkena : STRING;
port_scandata : STRING;
port_scandataout : STRING;
port_scandone : STRING;
port_scanread : STRING;
port_scanwrite : STRING;
port_clk0 : STRING;
port_clk1 : STRING;
port_clk3 : STRING;
port_clk4 : STRING;
port_clk5 : STRING;
port_clkena0 : STRING;
port_clkena1 : STRING;
port_clkena3 : STRING;
port_clkena4 : STRING;
port_clkena5 : STRING;
port_extclk0 : STRING;
port_extclk1 : STRING;
port_extclk2 : STRING;
port_extclk3 : STRING
);
PORT (
inclk : IN STD_LOGIC_VECTOR (1 DOWNTO 0);
clk : OUT STD_LOGIC_VECTOR (5 DOWNTO 0)
);
END COMPONENT;
BEGIN
sub_wire4_bv(0 DOWNTO 0) <= "0";
sub_wire4 <= To_stdlogicvector(sub_wire4_bv);
sub_wire1 <= sub_wire0(0);
c0 <= sub_wire1;
sub_wire2 <= inclk0;
sub_wire3 <= sub_wire4(0 DOWNTO 0) & sub_wire2;
pll: ddc112_conv
port map(
clkin => c0,
clkout => CLK,
conv => CONV
);
altpll_component : altpll
GENERIC MAP (
clk0_divide_by => 5,
clk0_duty_cycle => 50,
clk0_multiply_by => 2,
clk0_phase_shift => "0",
compensate_clock => "CLK0",
inclk0_input_frequency => 20000,
intended_device_family => "Cyclone",
lpm_hint => "CBX_MODULE_PREFIX=pll",
lpm_type => "altpll",
operation_mode => "NORMAL",
pll_type => "AUTO",
port_activeclock => "PORT_UNUSED",
port_areset => "PORT_UNUSED",
port_clkbad0 => "PORT_UNUSED",
port_clkbad1 => "PORT_UNUSED",
port_clkloss => "PORT_UNUSED",
port_clkswitch => "PORT_UNUSED",
port_configupdate => "PORT_UNUSED",
port_fbin => "PORT_UNUSED",
port_inclk0 => "PORT_USED",
port_inclk1 => "PORT_UNUSED",
port_locked => "PORT_UNUSED",
port_pfdena => "PORT_UNUSED",
port_phasecounterselect => "PORT_UNUSED",
port_phasedone => "PORT_UNUSED",
port_phasestep => "PORT_UNUSED",
port_phaseupdown => "PORT_UNUSED",
port_pllena => "PORT_UNUSED",
port_scanaclr => "PORT_UNUSED",
port_scanclk => "PORT_UNUSED",
port_scanclkena => "PORT_UNUSED",
port_scandata => "PORT_UNUSED",
port_scandataout => "PORT_UNUSED",
port_scandone => "PORT_UNUSED",
port_scanread => "PORT_UNUSED",
port_scanwrite => "PORT_UNUSED",
port_clk0 => "PORT_USED",
port_clk1 => "PORT_UNUSED",
port_clk3 => "PORT_UNUSED",
port_clk4 => "PORT_UNUSED",
port_clk5 => "PORT_UNUSED",
port_clkena0 => "PORT_UNUSED",
port_clkena1 => "PORT_UNUSED",
port_clkena3 => "PORT_UNUSED",
port_clkena4 => "PORT_UNUSED",
port_clkena5 => "PORT_UNUSED",
port_extclk0 => "PORT_UNUSED",
port_extclk1 => "PORT_UNUSED",
port_extclk2 => "PORT_UNUSED",
port_extclk3 => "PORT_UNUSED"
)
PORT MAP (
inclk => sub_wire3,
clk => sub_wire0
);
END SYN;
二、下层文件
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity ddc112_conv is
port(clkin: in std_logic;
clkout: buffer std_logic;
ctl,conv: buffer std_logic);
end ddc112_conv;
architecture beh of ddc112_conv is
begin
p1: process(clkin)
variable cnt: integer range 1 to 1 :=1; --定义计数器实现2分频
begin
if(clkin='1' and clkin'event) then
if(cnt=1) then
cnt := 1;
if(clkout='0') then
clkout <= '1';
elsif(clkout='1') then
clkout <= '0';
end if;
else
cnt := cnt+1;
end if;
end if;
end process p1;
p2: process(clkin)
variable cnt: integer range 1 to 10000 :=1; --定义计数器实现20000分频
begin
if(clkin='1' and clkin'event) then
if(cnt=10000) then
cnt := 1;
if(ctl='0') then
ctl <= '1';
elsif(ctl='1') then
ctl <= '0';
end if;
else
cnt := cnt + 1;
end if;
end if;
end process p2;
p3: process(conv)
begin
conv <= not ctl;
end process p3;
end architecture beh;