9

verilog笔记:频率计实现与verilog中timescale的解释

 2 years ago
source link: https://gsy00517.github.io/verilog20200107215404/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
verilog笔记:频率计实现与verilog中timescale的解释 | 高深远的博客

verilog笔记:频率计实现与verilog中timescale的解释

发表于 2020-01-07 | 更新于: 2020-01-19 | 分类于 程序与设计 | 0 | 阅读次数:
字数统计: 1.3k字 | 阅读时长 ≈ 5分钟

之前每次创建完一个新的文件,文件最上方总会显示一行timescale。当初觉得没什么作用,删了之后依旧没有任何问题便没把它当回事,直到后来做频率计数器的时候我才决定一探究竟。那么这篇文章也就一并谈一下这个问题。

References

电子文献:
https://blog.csdn.net/m0_37652453/article/details/90301902
https://blog.csdn.net/ciscomonkey/article/details/83661395
https://blog.csdn.net/qq_16923717/article/details/81099833


一个简易的频率计数器主要由分频器和计数器构成,其基本原理就是记录由分频器得到的一段时间内被测信号上升沿的个数,从而求得被测信号的频率。


控制信号转换模块

`timescale 1ns / 1ps //unit 1ns, precision 1ps
module control(
output reg Cnt_EN, //enable the counter count, so that the counting period can be controled
output wire Cnt_CR, //clear the counter every time when the measure begins
output wire Latch_Sig, //at its posedge, the value of the counter will be stored/latched
input nRST, //system reset signal
input CP //1Hz standard clock signal
);
always @ (posedge CP or negedge nRST)
begin
if(~nRST) //generate enable counting signal
Cnt_EN = 1'b0; //don't count
else
Cnt_EN = ~Cnt_EN; //two frequency divider for the clock signal
end
assign Latch_Sig = ~Cnt_EN; //generate latch signal
assign Cnt_CR = nRST & (~CP & Latch_Sig); //generate the clear signal for the counter
endmodule

`timescale 1ns / 1ps //unit 1ns, precision 1ps
module counter(
output reg [3:0] Q,
input CR, EN, CP
);
always @ (posedge CP or posedge CR)
begin
if(CR)
Q <= 4'b0000; //reset to zero
else if(~EN)
Q <= Q; //stop counting
else if(Q == 4'b1001)
Q <= 4'b0000;
else
Q <= Q + 1'b1; //counting, plus one
end
endmodule

`timescale 1ns / 1ps //unit 1ns, precision 1ps
module Latch(
output reg [15:0] Qout,
input [15:0] Din,
input Load, CR
);
always @ (posedge Load or posedge CR)
if(CR)
Qout <= 16'h0000; //reset to zero first
else
Qout <= Din;
endmodule

`timescale 1ns / 1ps //unit 1ns, precision 1ps
module Fre_Measure(
output wire [15:0] BCD, //transfer to display part
input _1HzIN, SigIN, nRST_Key,
output wire Cnt_EN, Cnt_CR, //control signals of the counter
output wire Latch_Sig, //latch singal
output wire [15:0] Cnt //8421BCDcode output
);

//call control block
control U0(Cnt_EN, Cnt_CR, Latch_Sig, nRST_Key, _1HzIN);

//measure counter
counter U1(Cnt[3:0], Cnt_CR, Cnt_EN, SigIN);
counter U2(Cnt[7:4], Cnt_CR, Cnt_EN, ~(Cnt[3:0] == 4'h9));
counter U3(Cnt[11:8], Cnt_CR, Cnt_EN, ~(Cnt[7:0] == 8'h99));
counter U4(Cnt[15:12], Cnt_CR, Cnt_EN, ~(Cnt[11:0] == 12'h999));

//call latch block
Latch U5(BCD, Cnt, Latch_Sig, ~nRST_Key);

endmodule

`timescale 1ns / 1ps //unit 1ns, precision 1ps
module simulateFile();

wire [15:0] BCD; //transfer to display part
wire [15:0] Cnt; //8421BCDcode output
reg CLK, RST, Signal;
parameter T1 = 0.1, //100Hz
T2 = 0.01, //1000Hz
T3 = 0.002; //5000Hz
wire Cnt_EN, Cnt_CR; //control signals of the counter
wire Latch_Sig; //latch singal

Fre_Measure Watch(BCD, CLK, Signal, RST, Cnt_EN, Cnt_CR, Latch_Sig, Cnt);

initial
begin
RST = 0;
CLK = 0;
Signal = 0;
end

always
forever #10 CLK = ~CLK; //generate clock signal

always #T1 Signal = ~Signal; //T1 or T2 or T3

always
begin
#10 RST = 1;
#200 RST = 0;
#10 RST = 1;
#200 RST = 0;
#10 RST = 1;
#200 RST = 0;
#10 RST = 1;
end

endmodule

以100Hz(T1)为例,输出的仿真结果如下。

其中CLK是固定的1Hz基准时钟信号;RST是系统需求的复位按键,当按下复位即RST为下降沿时,可以看到计数器Cnt被清零同时第一排译码输出的BCD码也被清零;Signal为输入的信号,这里我使用的是100Hz的,由我自己设定,由于频率较快,可以看到波形图非常密集;Cnt_EN是计数使能信号,可见在它为高电平时,Cnt随着输入信号一样快速计数;Cnt_CR是清零信号,在每次计数使能的上升沿或者复位的下降沿到来时Cnt_CR置零,也就是对Cnt清零操作;此外,当时钟信号到来时,假如系统不在计数(Cnt_EN=0),那么Latch_Sig将置1,也就是把记录数值存入锁存器。
实际上,这种设计方案会存在±1的计数误差,应为输入信号不一定与分频器同周期,即有可能每次测量的起始位置出于输入信号一个周期内的不同状态。


timescale

timescale是Verilog中的预编译指令,指定位于它后边的module的时间单位和时间精度,直到遇到新的timescale指令或者resetall指令。它的语法如下:

`timescale time_unit / time_precision

假如我们延时x个time_unit,那延时的总时间time = x * time_unit,但最后真正延时的时间是根据time_precision对time进行四舍五入后的结果。

  1. time_unit和time_precision只能是1、10和100这三种整数,单位有s、ms、us、ns、ps和fs。
  2. time_precision必须小于等于time_unit。
  3. timescale的时间精度设置是会影响仿真时间的,1ps精度可能是1ns精度仿真时间的一倍还多,并且占用更多的内存,所以如果没有必要,应尽量将时间精度设置得更大一些。

之前进行仿真时,我往往是让它自动运行至系统默认时间然后停止,这就会造成出现好几次重复循环的情况。
在Vivado中,窗口上方有三个类似播放器中的按钮,从左往右依次是:复位、不停运行、按指定时长(在后面的栏中设定)运行。
此外,如果计算不准时间,可以直接在仿真文件末尾或者想要结束的地方使用$stop或者$finifsh来终止仿真。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK