平台:ise14.7
仿真平台:modelsim se10.4d
最近学习AD测量相关的,把NI平台的相关计数器功能实现了一遍。具体参见NI手册,x series user manual chapter 7 counters。
在功能上实现了,边沿计数,脉冲宽度测量,脉冲计量,半周期测量,频率测量,周期测量,位置测量,以及双信号边缘分离测量。输出部分实现了,单脉冲输出,脉冲序列产生,分频器输出,以及产生ETS脉冲输出。
目录
整体verilog代码实现思路,使用一个选择寄存器选择实现的功能,使用一个计数器控制寄存器控制每种功能下的其他功能。
//计数器控制信号
input wire[7:0] reg_counter_choice ,//计数器方式选择
input wire[31:0] reg_counter_ctrl ,//计数器控制
//输入源
input wire source ,//
input wire source_trigger ,//输入源触发
input wire sample_clock ,//输入采样时钟
input wire gate ,//输入门信号
//
//输入系统信号
input wire clk ,//时钟
input wire rst_n //复位active—low
首先定义参数,定义了参数对应于每种不同的功能。
//--------------------------------------
// localparam
//--------------------------------------
localparam COUNTER_EDGE = 8'd1;// 1、边沿计数
localparam PULSE_WIDTH = 8'd2;// 2、脉冲宽度测量
localparam PULSE_MEAS = 8'd3;// 3、脉冲计量
localparam SEMI_PERIOD = 8'd4;// 4、半周期测量
localparam FRE_MEAS = 8'd5;// 5、频率测量
localparam PERIOD_MEAS = 8'd6;// 6、周期测量
localparam POSI_MEAS = 8'd7;// 7、位置测量
localparam TWO_EDGE = 8'd8;// 8、双信号边缘分离测量
思路中,将计数器方式选择寄存器和计数器功能参数对应起来,根据选择的不同功能,程序将实现不同的部分。
其次,为了保证源信号的采集,需要抓取源信号的上升沿或者下降沿。需要对源信号进行上升沿和下降沿的抓取,使用always语句循环执行就可以搞定。
在采集了输入信号源的上升沿下降沿后,我们就可以着手实现后续的功能了。
边沿计数
使用计数器对输入信号边沿进行计数,计数器可以配置为对源信号的上升沿或者下降沿计数。软件可以使用脉冲触发控制计数器开始和暂停。
对于这个计数,NI中说了有三种情况:
- 使用计数器对边沿直接计数。
- 使用触发信号来控制计数启动和停止
- 使用采样时钟来控制FIFO缓存计数值,随后可通过软件使用DMA读出FIFO中的数据
设计思路,直接对脉冲边沿计数时,使用我们的时钟信号来对源信号的边沿进行采集,这里逻辑需要实现对脉冲源的上升沿和下降沿都计数。所以使用时钟信号将源信号的上下都采集。通过软件配置计数器控制寄存器选择计数的边沿,同理在使用采样时钟进行对数据采样时,通过采集采样时钟的上下沿来产生有效信号,写入缓存。
下面是原NI手册的介绍。
直接看使用缓存采样时钟边沿计数。
如图所示,计数器计数源信号的上升沿,在采样时钟到来的时候将计数值缓存入FIFO。下面是使用verilog实现后的modelsim仿真示意图。
如图示例,采用了使用采样时钟sample_clock来控制计数器计数源信号的边沿的个数有效,在有效时,数据可以通过有效信号写入FIFO,之后上位机可以通过DMA读取出FIFO之中的数据。
脉宽测量
逻辑实现测量源信号的脉冲宽度,可以配置为测量源信号的高脉冲或者地脉冲。软件通过获取的测量值,乘以一个时钟周期的时间,就可以算出脉冲宽度。
- 单脉宽测量。
- 单脉宽测量后缓存。
- 采样时钟缓存脉冲宽度测量。
设计思路,同理我们使用时钟来采集源信号source的上升沿和下降沿。使用计数器方式选择寄存器选择为脉宽测量模式,在使用计数器控制寄存器控制计数器的模式为以上三种之一。操作时,将需要测量脉宽的信号接入source信号,使用我们固定的时钟来采集信号的
脉冲的宽度。下面直接看NI手册上的加入缓存buffer的采集模式。
根据选择的脉冲测量模式来确定测量高脉宽还是低脉宽。注意需要在计数器清零之前将采集的计数值锁存。
//锁存测量的脉宽
always@(posedge clk ,negedge rst_n)
begin
if(rst_n == 1'b0)
pulse_width_cnt_lock <= #U_DLY 32'h0;
else if(pulse_width_level == 1'b1 && source_negedge == 1'b1)//高下降沿锁存
pulse_width_cnt_lock <= #U_DLY pulse_width_cnt;
else if(pulse_width_level == 1'b0 && source_posedge == 1'b1)//低电平上升沿锁存
pulse_width_cnt_lock <= #U_DLY pulse_width_cnt;
else
pulse_width_cnt_lock <= #U_DLY pulse_width_cnt_lock;
end
分为上升沿锁存和下降沿锁存,锁存是为了保证不会由于计数器的清零而导致脉宽测量值清除。从而测量无效。最后产生数据有效信号,脉宽测量的三种方式,通过配置计数器控制寄存器来产生不同数据有效方式。
//2、单脉宽测量后缓存
//3、采样时钟缓冲脉冲宽度测量
always@(posedge clk ,negedge rst_n)
begin
if(rst_n == 1'b0)
pulse_width_cnt_vld <= #U_DLY 1'b0;
else if(pulse_width_buffer == 1'b1)
begin
if(pulse_width_level == 1'b1 && source_negedge == 1'b1)//高下降沿锁存
pulse_width_cnt_vld <= #U_DLY 1'b1;
else if(pulse_width_level == 1'b0 && source_posedge == 1'b1)//低电平上升沿锁存
pulse_width_cnt_vld <= #U_DLY 1'b1;
else
pulse_width_cnt_vld <= #U_DLY 1'b0;
end
else if(pulse_width_clock == 1'b1)
begin
if(sample_clock == 1'b1)
pulse_width_cnt_vld <= #U_DLY 1'b1;
else
pulse_width_cnt_vld <= #U_DLY 1'b0;
end
else
pulse_width_cnt_vld <= #U_DLY 1'b0;
end
结果与计算方法。如上图我们采集的第一个脉冲宽度计数值为N=3,在一个脉冲宽度中时钟计数器了三个周期。如CLK=100mhz,通过公式:
计算出:
从而可以得出脉冲宽度为:
下面是使用modelsim仿真波形。下图为测量高脉宽。
下图为测量低脉宽。
可以看到数据在测量完成后锁存。
脉冲计量
逻辑需要实现的功能,使用计数器计数脉冲的高电平时间和低电平时间,并根据其高低电平时间计数源信号的频率周期占空比。
- 单脉冲测量。
- 脉冲测量后加入缓存。测得的脉冲高电平时间和低电平时间合并后写入缓存。
- 采样时钟脉冲缓存测量。
设计思路,同样的将待测信号接入源信号source。计数器控制寄存器的低三位分别对应于脉冲计量的三种模式。测量脉冲,在信号的一个周期内有高电平也有低电平,需要使用计数器对着两段时间分别计数,在一次计数完成后,将两组数据写入缓存。逻辑上采用了两段式独立计数,即在高电平期间计数高电平的时间,在低电平期间计数低电平的时间。逻辑在源信号source的上升沿和下降沿分别锁存两处计数器的值。注意为了保证每次写入的第一个数据有效,在逻辑上将第一个数据有效点屏蔽了。
图示为NI手册上对这部分的描述。
//脉冲计量
reg [31:0] pulse_cnt ;//测得的高低周期的时间
reg [15:0] pulse_cnt_high ;//高电平时间
reg [15:0] pulse_cnt_low ;//低电平时间
reg pulse_cnt_vld ;//数据有效
reg pulse_signal ;//单测量模式
reg pulse_buffer ;//缓存模式
reg pulse_clock ;//采样时钟模式
reg pulse_start ;//脉冲计数器开始信号
//锁存
always@(posedge clk ,negedge rst_n)
begin
if(rst_n == 1'b0)
pulse_cnt <= #U_DLY 32'h0;
else if(pulse_signal == 1'b1)
begin
if(source_negedge == 1'b1)
pulse_cnt <= #U_DLY {pulse_cnt_high,pulse_cnt[15:0]};
else if(source_posedge == 1'b1)
pulse_cnt <= #U_DLY {pulse_cnt[31:16],pulse_cnt_low};
else
pulse_cnt <= #U_DLY pulse_cnt;
end
else
pulse_cnt <= #U_DLY 32'h0;
end
之后更加计数器控制寄存器来产生不同的数据有效信号写入FIFO。
计算公式和脉宽测量公式基本一致。时钟CLK继续使用100mhz时钟。周期为:
占空比Duty Ratio:
下面是使用modelsim仿真波形。
半周期测量
测量源信号的半个周期的时间,写入缓存。与脉冲测量基本一致。
逻辑需要实现对每个半周期计数,将计数值写入缓存。
下面是逻辑仿真截图。
在检测到源信号第一个上升沿source后,开始计数,每半个周期产生一次数据有效。同样将第一个脉冲产生的无效数据舍弃。
频率测量
根据NI的手册,频率测量由下列五种方法组成。
- 低频测量,使用一个计数器。
- 高频测量,使用两个计数器。
- 带有两个计数器的大范围频率测量。
- 采样时钟缓存频率测量。
- 硬件定时单点频率测量。
下面来分别对其使用verilog实现。首先是低频测量,工业上一般将小于3mhz的频率称作低频。将大于3mhz的频率定性为高频。
低频测量:
设计思路,输入的源信号。使用时钟clk计数其一个周期内时钟的上升沿的个数N。这里对其实现的难点是对计数器的清零。这里我想到了两种方式实现。第一种直接使用上升沿来对频率计数器清零,在后续电路中,一致使用源信号source上升沿来锁存计数器值和产生计数有效信号。
第一种方法如下:
通过采集Fx的上升沿来控制计数器数据的保存于舍弃。计数的值就是Fx两个上升沿之间的时间。
这时候通过公式就可以算出源信号source的周期和频率,周期。
频率。
测量和实际值之间的差距为一个时钟clk周期。
第二种测量方式:将输入源信号一个周期时间转换为一个正脉冲。
即通过逻辑转换,将输入源信号Fx的一个周期转换为一个正脉冲。测量这一段脉冲的时间。这种方式同样适用于测试正弦波的频率,只需要在正弦波进来后,通过逻辑将其先转换为同频率的方波。
计算方式和上述一样。
下面为使用modelsim仿真的波形示意图。
可以看到在每个周期后,都会锁存当前计数器的值,并写入fifo。
高频测量:
用已知长度的脉冲测量高频信号在这一个周期内上升沿的个数。逻辑设计时,设置一个已知脉冲宽度的源,计数在这一个脉冲期间待测高频信号的上升沿的个数。这样做的原因是在高频时,一个周期的时间可能非常短暂,一般为纳秒ns级别,这时候如果继续使用时钟clk去采集高频信号的一个周期,得出的结果将会不准确。所以一般使用一个固定时间的脉冲宽度,来计量这个脉冲期间高频信号上升沿的个数。
计算的公式如图中所示。
下面是modelsim仿真示意图。
在固定的脉冲宽度中计数高频源信号source的上升沿个数,并写入FIFO缓存。
带有两个计数器的大范围频率测量:
频率测量包括高频和低频。又叫互反频率测量。设计方法,当使用两个计数器来测量更大范围频率时。将待测的信号生成一个长脉冲,然后使用已知的时钟来测量这个脉冲的时间。
这里:使用计数器控制寄存器来设置用待测信号产生的门的脉宽。另一个计数来计数在门内,已知时钟的计数个数。
根据上图,可以得知待测频率的计算公式为:
待测周期
Verilog实现中。这里使用计数器控制寄存器的bit15-bit8来产生门脉冲宽度,预设最大门脉冲宽度为待测信号的256个周期。逻辑先需要根据设置的门脉冲长度产生相应长度的脉宽。然后利用门限的上下降沿上升沿来控制使用已知的时钟测量在此门限宽度内已知时钟的周期个数。
//产生相应的脉宽
always@(posedge source ,negedge rst_n)
begin
if(rst_n == 1'b0)
begin
frequency_large_cntx <= #U_DLY 8'h0;
frequency_large_gate <= #U_DLY 1'b0;
end
else if(frequency_large == 1'b1)
begin
frequency_large_cntx <= #U_DLY frequency_large_cntx + 1'b1;
if(frequency_large_cntx == frequency_large_cnt)
frequency_large_gate <= #U_DLY 1'b0;
else if(frequency_large_cntx == 8'hFF)
begin
frequency_large_cntx <= #U_DLY 8'h0;
frequency_large_gate <= #U_DLY 1'b1;
end
else
frequency_large_gate <= #U_DLY frequency_large_gate;
end
else
begin
frequency_large_cntx <= #U_DLY 8'h0;
frequency_large_gate <= #U_DLY 1'b0;
end
end
下面是modelsim仿真示意图。
仿真中添加激励产生32个周期待测信号source门限,使用已知100mhz时钟测量这个脉宽内的上升沿的个数Nfk。根据公式计算就可以得出source的频率。
待测周期
与仿真设置激励一致。
采样时钟缓存频率测量:
逻辑需要实现的功能,对待测信号fx进行周期计数,同时开始对时钟计数,等待采样时钟sample_clock来到的时候,缓存当前两个计数值。同时重新计数。
需要实现两种模式。
- 为求平均计数计数模式。
- 不平均计数模式。即源信号一个周期存一次数据,等待采样时钟到来写入buffer
逻辑实现,使用计数器控制寄存器来控制是否使用平均。
这是使用平均的。此模式下经过采样时钟sample_clock的脉冲来控制读出源信号source的上升沿个数和在此期间时钟clk时钟的上升沿个数。逻辑需要注意在不满一个上升沿的计数值时不保存。即每次保存一个完整的周期区间的数据。这时候就需要三个计数器。一个计数source上升沿个数,一个计数clk上升沿个数,一个缓存总数据。
reg [15:0] fre_sample_cnt_fx ;//输入待测信号周期计数fx=source
reg fre_sample_start ;//时钟采样计数开始
reg [15:0] fre_sample_cnt_fk ;//时钟周期计数
reg [31:0] fre_sample_cnt ;//高十六位为待测Nfx,低十六位为已知Nfk
reg fre_sample_cnt_vld ;//数据有效
//计数Nfk,在start为1开始计数始终少计数一个周期,
always@(posedge clk ,negedge rst_n)
begin
if(rst_n == 1'b0)
fre_sample_cnt_fk <= #U_DLY 16'h1;
else if(frequency_sample == 1'b1 && frequency_sample_ave == 1'b1)
begin
if(fre_sample_start == 1'b1)
fre_sample_cnt_fk <= #U_DLY fre_sample_cnt_fk + 1'b1;
else
fre_sample_cnt_fk <= #U_DLY 16'h1;
end
else if(frequency_sample == 1'b1 && frequency_sample_ave == 1'b0)
begin
if(source_posedge == 1'b1)
fre_sample_cnt_fk <= #U_DLY 16'h0;
else
fre_sample_cnt_fk <= #U_DLY fre_sample_cnt_fk + 1'b1;
end
else
fre_sample_cnt_fk <= #U_DLY 16'h1;
end
下面是使用modelsim仿真测出的周期数。
在开始测试之后,sample_clock在source计数5个周期后(未满六个周期)将前五个周期有效的数据拼接起来。保存到缓存。
计算出source周期:
待测周期
与仿真一致。
这是不使用平均的。不使用平均时,每次都保留一个完整周期的数据有效值,等待采样时钟sample_clock脉冲到来,将source一个周期的数据写入缓存。且每次只保留sample_clock之前的一个周期的有效数据。逻辑上就需要设置锁存器将每一个完整周期数据保留,并清除之前保留的数据。
下面是使用modelsim仿真截图。
#100;
reg_counter_choice = 8'd5;
reg_counter_ctrl = 32'h0000_0008;
#4000;
sample_clock = 1'b1;
#100;
sample_clock = 1'b0;
#1000;
sample_clock = 1'b1;
#100;
sample_clock = 1'b0;
#1000;
sample_clock = 1'b1;
#100;
sample_clock = 1'b0;
// Add stimulus here
end
always #10 clk = ~clk;
always #880 source = ~source;
计算结果与仿真值一致。
最后一种频率测量方式,硬件定时单点频率测量。
可以看到硬件定时单点频率测量和上述的采样时钟不平均测量基本一致。只保留一个周期数据写入缓存。这里就不进行设计了。
频率测量方式选择
选择合适的频率测量方式取决于以下几个因素,包括待测信号的预期频率,期望的精度,可以用多少计数器以及测量需要多长的时间。对于上述的测量方式,假设有如下几种情况。
fx | 如果没有误差,要测量的频率 |
fk | 已知源source或者门gate频率 |
Measurement time(T) | 测量一个样本所需要的时间 |
Divide down(N) | N这个整数用来区分被测频率,N仅仅用于(记录)大范围频率测量时的两个计数器的值。 |
fs | 样本时钟速率,仅仅使用在采样时钟测评 |
- 一个计数器,使用一个计数器测量低频信号。fx是待测源信号的频率,fk是已知时间clk的频率。Nfk为一个待测信号周期内计数值。
- 两个计数器,测量高频。一个计数器测量待测源信号周期数Nfx,一个计数器计数已知时钟源信号的周期数Nfk。
- 两个计数器大范围频率测量。可以测量高频或者低频。使用除法计算出测试结果,
- 采样时钟。使用采样时钟来测量待测频率。
频率测量的方法哪一种最好呢?
取决于要测量的频率,取决于你想测量频率的速率和准确性。下面是以50khz的待测频率为例子。
Variable | Sample clock | One counter | Two counter | |
High frequency | Large range | |||
fk | 50000 | 50000 | 50000 | 50000 |
fk | 100mhz | 100mhz | 1000 | 100mhz |
Measurement time (ms) | 1 | .02 | 1 | 1 |
N | ---- | ---- | ---- | 50 |
Max frequency error(HZ) | .512 | 25 | 1000 | .5 |
Max error% | .00102 | .05 | 2 | .001 |
从以上的结果中,使用一个计数器测量的时间最短,但是在sample clock和lange range中精度确是最高的。
在看以5MHZ的待测频率为例子。
Variable | Sample clock | One counter | Two counter | |
High frequency | Large range | |||
fk | 5m | 5m | 5m | 5m |
fk | 100mhz | 100mhz | 1000 | 100mhz |
Measurement time (ms) | 1 | .0002 | 1 | 1 |
N | ---- | ---- | ---- | 5000 |
Max frequency error(HZ) | 50.01 | 263k | 1000 | 50 |
Max error% | .001 | 5.26 | 0.02 | .001 |
同样使用一个计数器计算的结果最快,但是精度较低。注意sample clock和large range 测量精度和时间几乎相同。
Sample clock测量优点,即使频率发生变化,测量时间也没有变化,误差很小。
总结。
- 使用一个计数器时间快,精度较低。低频稳定,测量的精度随着频率的增加而降低。
- 带有两个计数器的高频测量测量高频信号十分准确。随着频率的降低,精度降低。在非常低频率时测量结果不准确。优点是,测量在已知的时间内完成。
- 带有两个计数器的频率测量。可以非常精确的测量高频和低频信号。但是他需要两个计数器,并且根据输入信号具有可变采样时间和可变误差%。
- 同样,一个计数器测量的时间最短,精度较低。Sample clock和large range 测量的时间和精度是相同的。使用sample clock优点是,即使被测频率发生变化,测量时间和误差也没有变化。缺点是,要测量的频率至少是时钟速率的两倍。用以确保在一个采样时钟周期内测量的频率完整。
下表总结了在频率测量的方法上的一些差异。
Variable | Number of measurements Used | Number of measurements Returned | Measures high frequency signals accurately | Measures low frequency signals accurately |
Low frequency with one counter | 1 | 1 | poor | good |
High frequency with two counters | 1 or 2 | 1 | Good | Poor |
Large range of frequencies with two counters | 2 | 1 | Good | Good |
Sample clocked (averaged) | 1 | 1 | Good | Good |
周期测量
使用上述频率测量的方式执行计算即可。
位置测量
您可以使用计数器执行正交编码器或两个脉冲编码器的位置测量。可以使用X1、X2和X4角度编码器测量角度位置。线性位置可以用两个脉冲编码器测量。您可以选择进行单点(按需)位置测量或缓冲(采样时钟)位置测量。
使用正交编码器测量measurements using quadrature encoders
之前没有了解过这个方面的知识,根据网上的解释为。
什么是编码器?
编码器(encoder)是将信号(如比特流)或数据进行编制、转换为可用以通讯、传输和存储的信号形式的设备。编码器把角位移或直线位移转换成电信号,前者称为码盘,后者称为码尺。按照读出方式编码器可以分为接触式和非接触式两种;按照工作原理编码器可分为增量式和绝对式两类。增量式编码器是将位移转换成周期性的电信号,再把这个电信号转变成计数脉冲,用脉冲的个数表示位移的大小。绝对式编码器的每一个位置对应一个确定的数字码,因此它的示值只与测量的起始和终止位置有关,而与测量的中间过程无关。
编码器信号输出,输出信号有正弦波(电流电压),方波(TTL,HTL),集电极开路(PNP,NPN)推拉式多种形式,其中TTL为长线差分驱动(对称A,A-;B,B-;Z,Z-),HTL也称为推拉式,推挽式输出,编码器的信号接收设备接口应该与编码器对应。
信号连接——编码器的脉冲信号一般连接有计数器,PLC,计算机,PLC和计算机连接的模块由低速模块与高速模块之分,开关频率有低有高。
如单相连接,用于单方向计数,单方向测速。
A.B两相连接,用于正反方向计数,判断正反反向和测速。
A.B.Z三相连接,用于带参考位修正的位置测量。
A、A-,B、B-,Z、Z-连接,由于带有对称负信号的连接,电流对于电缆贡献的电磁场为0,衰减最小,抗干扰最佳,可传输较远的距离。
对于TTL的带有对称负信号输出的编码器,信号传输距离可达150米。
对于HTL的带有对称负信号输出的编码器,信号传输距离可达300米。
以上来自百度百科。
这里我们注意设计对两相连接和三相连接的计数。
X1编码
NI手册如下。
在A的上升沿,b=0时,累加,a下降沿,b=0,累减。下面是使用modelsim仿真截图。
X2编码
NI手册如下,
下面是使用modelsim仿真截图。
X4编码
NI手册截图。
a上升沿,b=0,累加
b上升沿,a=1,累加
a下降沿,b=1,累加
b下降沿,a=0,累加
b上升沿,a=0,累减
a上升沿,b=1,累减
b下降沿,a=1,累减
a下降沿,b=0,累减
下面是使用modelsim仿真截图。
部分代码如下。
//位置测量
always@(posedge clk ,negedge rst_n)
begin
if(rst_n == 1'b0)
begin
ch_a_dly <= #U_DLY 1'b0;
ch_b_dly <= #U_DLY 1'b0;
ch_z_dly <= #U_DLY 1'b0;
end
else
begin
ch_a_dly <= #U_DLY ch_a;
ch_b_dly <= #U_DLY ch_b;
ch_z_dly <= #U_DLY ch_z;
end
end
//ch_a
always@(posedge clk ,negedge rst_n)
begin
if(rst_n == 1'b0)
begin
ch_a_posedge <= #U_DLY 1'b0;
ch_a_negedge <= #U_DLY 1'b0;
end
else if(ch_a == 1'b1 && ch_a_dly == 1'b0)
ch_a_posedge <= #U_DLY 1'b1;
else if(ch_a == 1'b0 && ch_a_dly == 1'b1)
ch_a_negedge <= #U_DLY 1'b1;
else
begin
ch_a_posedge <= #U_DLY 1'b0;
ch_a_negedge <= #U_DLY 1'b0;
end
end
//ch_b
always@(posedge clk ,negedge rst_n)
begin
if(rst_n == 1'b0)
begin
ch_b_posedge <= #U_DLY 1'b0;
ch_b_negedge <= #U_DLY 1'b0;
end
else if(ch_b == 1'b1 && ch_b_dly == 1'b0)
ch_b_posedge <= #U_DLY 1'b1;
else if(ch_b == 1'b0 && ch_b_dly == 1'b1)
ch_b_negedge <= #U_DLY 1'b1;
else
begin
ch_b_posedge <= #U_DLY 1'b0;
ch_b_negedge <= #U_DLY 1'b0;
end
end
//ch_z
always@(posedge clk ,negedge rst_n)
begin
if(rst_n == 1'b0)
begin
ch_z_posedge <= #U_DLY 1'b0;
ch_z_negedge <= #U_DLY 1'b0;
end
else if(ch_z == 1'b1 && ch_z_dly == 1'b0)
ch_z_posedge <= #U_DLY 1'b1;
else if(ch_z == 1'b0 && ch_z_dly == 1'b1)
ch_z_negedge <= #U_DLY 1'b1;
else
begin
ch_z_posedge <= #U_DLY 1'b0;
ch_z_negedge <= #U_DLY 1'b0;
end
end
双信号边沿分离测量
与脉冲宽度测量相似,只是有两个测量信号aux和edge,aux边沿开始计数,edge边沿停止计数。
1、单双信号边缘分离测量
2、隐式缓冲双信号边缘分离测量
3、采样时钟缓冲双信号分离测量
4、硬件定时单点双信号分离测量
单双信号边缘分离测量
Verilog处理测量一次后停止,使用了开始和停止来指示状态。
//单双信号边缘分离测量开始
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 1'b0)
two_sig_edge_start <= #U_DLY 1'b0;
else if(two_sig_edge == 1'b1 || two_sig_edge_bufer == 1'b1 || two_sig_edge_sample == 1'b1)
begin
if(aux_posedge == 1'b1)
two_sig_edge_start <= #U_DLY 1'b1;
else if(gate_posedge_d1 == 1'b1)
two_sig_edge_start <= #U_DLY 1'b0;
else
two_sig_edge_start <= #U_DLY two_sig_edge_start;
end
else
two_sig_edge_start <= #U_DLY 1'b0;
end
//单双信号边缘分离测量停止,用于单次测量
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 1'b0)
two_sig_edge_stop <= #U_DLY 1'b0;
else if(two_sig_edge == 1'b1)
begin
if(two_sig_edge_start == 1'b1 && gate_posedge_d1 == 1'b1)
two_sig_edge_stop <= #U_DLY 1'b1;
else
two_sig_edge_stop <= #U_DLY two_sig_edge_stop;
end
else
two_sig_edge_stop <= #U_DLY 1'b0;
end
下面是使用modelsim仿真截图。
如图,在aux上升沿开始计数,在gata上升沿停止计数,将gate门信号多次打拍。锁存数据。产生写使能。
隐式缓冲双信号边缘分离测量
下面是使用modelsim仿真截图。
采样时钟缓冲双信号分离测量
使用采样时钟,在采样时钟来到的时候,写入缓存。
下面是使用modelsim仿真截图。
计数器输入应用到此结束。
接下来做计数器输出应用。
输出部分
简单的脉冲发生器
使用计数器产生一个单脉冲输出。可以设置脉冲产生的宽度,为几个时钟周期。这里最大为0-65535。
- 产生一个单脉冲
- 带启动触发器的单脉冲生成
下面是使用modelsim仿真截图。
带启动触发器的单脉冲生成
下面是仿真截图
在门控信号后,输出预设的脉冲。
脉冲序列的产生
脉冲计量高低电平数,有两种实现方式,一种是直接通过寄存器设置,一种是将要设置的高低电平写入fifo,每次产生后都重新从缓冲中读出。脉冲产生的长度由num设置。Fifo设置需要预设的脉冲宽度。Fifo设计位宽位32位,高16位位低电平时间,低16位为高电平时间。
脉冲序列实现了两种控制方式
- 有限脉冲序列的产生
- 读缓冲有限脉冲序列产生
有限脉冲序列产生,通过设置需要脉冲时间到reg_counter_pluse,其中高16位为低电平持续时间,低16位持续时间为低电平持续时间。需要设置输出的脉冲个数使用reg_counter_ctrl高16位控制。
当选择了读缓存模式时,需要先将要写入的值通过reg_counter_pluse写入fifo缓存,然后每次产生完成后就重新读取出预设值并赋值。
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 1'b0)
begin
pluse_out_cnt_low <= #U_DLY 16'h0;
pluse_out_cnt_high <= #U_DLY 16'h0;
end
else if(finite_pluse_gen == 1'b1)
begin
pluse_out_cnt_low <= #U_DLY reg_counter_pluse[31:16];
pluse_out_cnt_high <= #U_DLY reg_counter_pluse[15:0];
end
else if(buffer_pluse_gen == 1'b1)
begin
pluse_out_cnt_low <= #U_DLY counter_out_dout[31:16];
pluse_out_cnt_high <= #U_DLY counter_out_dout[15:0];
end
else
begin
pluse_out_cnt_low <= #U_DLY 16'h0;
pluse_out_cnt_high <= #U_DLY 16'h0;
end
end
//计数器读fifo读使能
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 1'b0)
pluse_out_rd_en <= #U_DLY 1'b0;
else if(finite_pluse_gen == 1'b1)
pluse_out_rd_en <= #U_DLY 1'b0;
else if(buffer_pluse_gen == 1'b1)
begin
if(pluse_out_cnt_idle == pluse_out_cnt_low)
pluse_out_rd_en <= #U_DLY 1'b1;
else
pluse_out_rd_en <= #U_DLY 1'b0;
end
else
pluse_out_rd_en <= #U_DLY 1'b0;
end
下面是使用modelsim仿真截图。
有限脉冲序列的产生
可以看到输出的脉冲宽度一致。
读缓冲脉冲输出
在每一次产生完成脉冲后,在下一次脉冲产生的开始读取预设的脉冲宽度,并达到预设值后停止。
分频器输出
逻辑需要实现可以选择不同的时基,根据分频系数实现任意分频,实现百分之50%占空比,分频输出支持路由到任意PFI输出端口。
Verilog实现,需要先判断预设的分频系数是偶数还是奇数,如果是偶数产生的奇数和偶数计数器相等,如果是奇数,产生的奇数计数器等于偶数计数器加一。部分代码实现如下。
always@(posedge clk, negedge rst_n)
begin
if(rst_n == 1'b0)
begin
clk_cnt <= #U_DLY 12'h0;
clk_div_a <= #U_DLY 1'b0;
end
else if((clk_div_a == 1'b1) && (clk_cnt >= clk_cnt_h))
begin
clk_cnt <= #U_DLY 12'h1;
clk_div_a <= #U_DLY 1'b0;
end
else if(clk_cnt >= clk_cnt_l)
begin
clk_cnt <= #U_DLY 12'h1;
clk_div_a <= #U_DLY 1'b1;
end
else
begin
clk_cnt <= #U_DLY clk_cnt + 12'h1;
clk_div_a <= #U_DLY clk_div_a;
end
end
always@(negedge clk, negedge rst_n)
begin
if(rst_n == 1'b0)
clk_div_b <= #U_DLY 1'b0;
else if((clk_div_a == 1'b1) && (clk_cnt_odd == 1'b1))
clk_div_b <= #U_DLY 1'b1;
else
clk_div_b <= #U_DLY 1'b0;
end
assign clk_div_o = clk_div_a || clk_div_b;
下面是使用modelsim仿真截图。
从图中可以看到,在设置好分频系数后,输出时钟正常。
奇数分频。
设置奇数分频,输出的时钟为a和b的或,a使用时钟源上升沿产生,b使用时钟源下降沿产生。
ETS脉冲生成
计数器在门信号之后产生脉冲。延迟值可以设置为0-255。例如如果指定增量为10,则每次产生新的脉冲时,在门信号产生后产生的脉冲持续时间比前一次增加ets_cnt_add,此值根据reg_counter_ctrl寄存器bit[15:8]设置。
//ets产生
reg ets_pluse_gen ;//ets启动
reg [15:0] ets_cnt_high ;//ets高电平持续时间
reg [15:0] ets_cnt_low ;//ets低电平持续时间
reg [15:0] ets_cnt_high_add ;//ets累加
reg [15:0] ets_cnt_idle ;//ets空闲计数器
reg [15:0] ets_cnt_active ;//ets有效计数器
reg [7:0] ets_cnt_add ;//ets每次累加增加量
reg ets_start ;//ets开始
reg ets_low_start ;//ets低开始
reg ets_high_start ;//ets高信号开始
reg ets_pluse ;//ets脉冲
利用ets_start信号保证第一次产生的脉冲是当前设置值,在后续检测到门信号的上升沿后累加。
//每次计数后累加一次
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 1'b0)
ets_cnt_high_add <= #U_DLY 16'h0;
else if(ets_pluse_gen == 1'b1)
begin
if(ets_start == 1'b0)
ets_cnt_high_add <= #U_DLY ets_cnt_high;
else if(ets_start == 1'b1 && gate_posedge == 1'b1)
ets_cnt_high_add <= #U_DLY ets_cnt_high_add + ets_cnt_add;
else
ets_cnt_high_add <= #U_DLY ets_cnt_high_add;
end
else
ets_cnt_high_add <= #U_DLY 16'h0;
end
//ets开始用于控制累加信号
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 1'b0)
ets_start <= #U_DLY 1'h0;
else if(ets_pluse_gen == 1'b1)
begin
if(gate_posedge == 1'b1)
ets_start <= #U_DLY 1'b1;
else
ets_start <= #U_DLY ets_start;
end
else
ets_start <= #U_DLY 1'b0;
end
下面是ETS脉冲仿真截图
从图中可以看出设定高低电平持续时间后,在门信号来临后,计数器奇数到低电平时间后,产生ets脉冲输出。在一次结束后,下一次门信号来临时候,高电平时间累加。
到此计数器输出结束。