FPGA拾忆_(3):调用IP 计数器&BCD计数器

  • 调用IP计数器:

每来一个cin(进位输入)信号,计数器输出值加一,当计数值为9且cin为1时,输出一个时钟长度的cout(进位输出)信号。

首先采用调用quartus种IP的方式,具体步骤:

Tools----IP Catalog:

然后会调出IP目录窗口:

6dee1fd27535494ca6f42a8d3138aeee.png

通过搜索counter来添加计数器模块,需要设置的内容有:bit位(几位输出寄存器)、计数值、 加一or减一、使能方式(clock enable ,count enable)、计数方式(时钟 or carry in)、清零,置数,预载等功能。

设置完成可以直接自己编写top模块,然后例化IP,eg:

971625d6edbb469999311252c10a77b2.png在顶层模块,右键点击 set as top-level....

c94368c5b8754a009ca902a4325c7862.png代码:

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型,如下图:

8423e426600c4b70a06b3a3b2700b1e1.png无论子模块的端口是reg还是wire型,因为子模块要迅速读取来自外部信号的变化情况,所以顶层模块例化时必须定义为wire类型!!

我自己在编译时候,就提示错误了,多个例化的中间变量or输入输出端口都报错了,因为未使用wire类型。9a372bf5700549e28df9c4f2bae1b06a.jpeg

  • 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:

97e27c45cad34152b6e2f8d6a10274ff.jpeg易错点:

在刚开始仿真的时候,发现出现了一个这样的错误:

b65d2a085036460ab4a3fb4146497b07.bmp

999之后再来cin信号,计数值错误,两个时钟之后,才恢复正常值,这是因为999+1=000,但是个位向十位传递进位时,需要一个周期,十位向百位传递进位时,也需要一个周期,所以出问题了,把cout的输出逻辑改为assign语句(即迅速传递进位信息),可以解决。

4c9192a1f07941b49e866f1a90cee45d.jpeg

 修改之后的仿真结果:

6debd69846e7440e9563aba3ae2c273b.png

自己编写的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

  • 小知识点扩展:

 1.仿真时,在modelsim窗口,可以将子模块的端口也添加进波形图,来观察子模块端口的仿真情况:

0f95672ede574db891adef84057350bb.bmp

2.为防止同名称无法分析,可以在信号窗口点击快捷键ctrl +G,进行快速分组。

3.像下面的延时语句,虽然是不可综合语句,但是会被综合器忽略,而且反应了电路实际延时情况,在自己测试时使用很方便(工作时不可以主动写这样的代码)。

6e232705fa2647278efaa494f6d916cf.bmp

 Vivado 调用其他项目中的代码(如38译码器模块中的38译码器代码)时,一定要勾选改选项,即创建一个拷贝存在现在的项目中,未勾选的话,仅仅是在现在的项目中添加了一个要调用的代码(如38译码器模块中的38译码器代码)的路径,这样在以后修改代码(如修改38译码器模块中的代码名称之类的改动)时,可能会将源代码直接修改!!

再强调一下易错点:首先是例化时要用wire类型,然后是testben中例化的元件名字不能落下,然后要注意计数器Q的输出逻辑的撰写,条件编写正确。

voer!

  • 28
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值