- 调用IP计数器:
每来一个cin(进位输入)信号,计数器输出值加一,当计数值为9且cin为1时,输出一个时钟长度的cout(进位输出)信号。
首先采用调用quartus种IP的方式,具体步骤:
Tools----IP Catalog:
然后会调出IP目录窗口:
通过搜索counter来添加计数器模块,需要设置的内容有:bit位(几位输出寄存器)、计数值、 加一or减一、使能方式(clock enable ,count enable)、计数方式(时钟 or carry in)、清零,置数,预载等功能。
设置完成可以直接自己编写top模块,然后例化IP,eg:
在顶层模块,右键点击 set as top-level....
代码:
module counter_mytop(clk,cin,cout,q);
input wire clk;
input wire cin;
output wire cout; //这里都报错了,原因均是错误使用了reg型来例化,
output wire [11:0]q; //应该使用wire来例化
wire cout1;
wire cout2;
wire [3:0] q1;
wire [3:0] q2;
wire [3:0] q3;
assign q = {q3,q2,q1};
counter_myip U1(
.cin(cin),
.clock(clk),
.cout(cout1),
.q(q1)
);
counter_myip U2(
.cin(cout1),
.clock(clk),
.cout(cout2),
.q(q2)
);
counter_myip U3(
.cin(cout2),
.clock(clk),
.cout(cout),
.q(q3)
);
endmodule
易错点:
在顶层模块例化的时候,一定要注意,例化端口的数据类型必须是wire型,如下图:
无论子模块的端口是reg还是wire型,因为子模块要迅速读取来自外部信号的变化情况,所以顶层模块例化时必须定义为wire类型!!
我自己在编译时候,就提示错误了,多个例化的中间变量or输入输出端口都报错了,因为未使用wire类型。
- BCD计数器
BCD码的巨大优势:
(BCD码,使用4位2进制来表示一位10进制,可以粗浅理解为将16进制直接读为10进制)
示例:
取出1158这个数的每一位来分别进行操作:
(1)C语言的做法:
1158%10 = 8;//个位
1158/10 =115;
115%10 = 5;//十位
115/10 =11;
11%10 = 1;//百位
11/10 =1;//千位
显然,这种方式对于硬件资源的浪费极大,而且硬件对于除法运算会舍去精度,因此对于“需要对数据进行取各位运算、非整体运算时”,采用BCD码可以大幅度减少运算资源占用以及计算误差。
如十进制的1158,我们直接把它用16'h1158来代替,那么
16'h1_1_5_8
=16'b0001_0001_1001_1000
在硬件中,只需要读出高四位数(0001),即代表千位,低四位(1000),即代表个位。
这样完全避免了上面的转化运算,可以直接读取十进制的各个位数据!!!
BCD计数器的最大计数值为10,也就是每次记到9,并且来了一个新的cin信号时,cout输出1:
易错点:
在刚开始仿真的时候,发现出现了一个这样的错误:
999之后再来cin信号,计数值错误,两个时钟之后,才恢复正常值,这是因为999+1=000,但是个位向十位传递进位时,需要一个周期,十位向百位传递进位时,也需要一个周期,所以出问题了,把cout的输出逻辑改为assign语句(即迅速传递进位信息),可以解决。
修改之后的仿真结果:
自己编写的10进制计数器:
仔细感悟一下q的always块的条件书写,体会条件书写时的包含与被包含关系,理清思路
代码:
module counter_my(clk,rst,cin,cout,q);
input clk;
input rst;
input cin;
output cout; //进位输出
output reg [3:0]q; //计数输出
//q的输出逻辑
always@(negedge rst or posedge clk)begin
if(!rst)
q<=4'b0;
else if(cin ==1) //计数到10也复位
begin
if(q==4'b1001)
q<=0;
else
q<=q+1;
end
else
q<=q;
end
//cout的输出逻辑
assign cout = (q==4'b1001&&cin==1)?1:0;
endmodule
testbench:
`timescale 1ns/1ns
`define clockperiod 20 //时钟周期20ns
module counter_myip_testbench;
//激励信号
reg clk;
reg cin;
//检测信号
wire [11:0] q;
wire cout;
//例化待检测模块,这里犯了错误,例化时没加例化名tb1,编译不报错但是仿真无法进行!!
counter_mytop tb1( .clk(clk),
.cin(cin),
.cout(cout),
.q(q)
);
//激励施加过程
initial clk = 1;
always begin
#(`clockperiod/2) clk = ~clk;
end
//
initial begin
repeat(1500)begin
#(`clockperiod*10);
cin = 1;
#(`clockperiod*10);
cin = 0;
end
#1000;
$stop;
end
endmodule
文章来源地址https://www.toymoban.com/news/detail-775210.html
- 小知识点扩展:
1.仿真时,在modelsim窗口,可以将子模块的端口也添加进波形图,来观察子模块端口的仿真情况:
2.为防止同名称无法分析,可以在信号窗口点击快捷键ctrl +G,进行快速分组。
3.像下面的延时语句,虽然是不可综合语句,但是会被综合器忽略,而且反应了电路实际延时情况,在自己测试时使用很方便(工作时不可以主动写这样的代码)。
文章来源:https://www.toymoban.com/news/detail-775210.html
再强调一下易错点:首先是例化时要用wire类型,然后是testben中例化的元件名字不能落下,然后要注意计数器Q的输出逻辑的撰写,条件编写正确。
voer!
到了这里,关于FPGA拾忆_(3):调用IP 计数器&BCD计数器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!