01 module counter(Clk,Rst_n,led);
02
03 input Clk; //系统时钟
04 input Rst_n; //全局复位,低电平复位
05
06 output
reg led; //led输出
07
08 reg
[24:0]cnt; //定义计数器寄存器
09
10 parameter CNT_MAX =
25'd24_999_999;
11
12 //计数器计数进程
13 always@(posedge Clk or
negedge Rst_n)
14 if(Rst_n ==
1'b0)
15 cnt <=
25'd0;
16 else
if(cnt == CNT_MAX)
17 cnt <=
25'd0;
18 else
19 cnt <= cnt +
1'b1;
20
21 //led输出控制进程
22 always@(posedge Clk or
negedge Rst_n)
23 if(Rst_n ==
1'b0)
24 led <=
1'b1;
25 else
if(cnt == CNT_MAX)
26 led <=
~led;
27 else
28 led <= led;
29
行声明了一个参数化常量"CNT_MAX",在第16行和第25行进行条件判断时,直接判断cnt的值是否与该参数值相等,而不再是直接写具体数字的方式。到这里大家就可以看到,通过这样一种方式,首先就能避免一个模块中多次使用该常量而对修改设计带来的隐患。因为当我们需要修改该常量的值以适应不同的应用要求时,直接修改这个parameter的值即可,从而可以避免因为该参数使用的地方过多而在修改时容易出现的遗漏的问题。
另外,在我们的系统中,假如也需要三个这样的模块来对应驱动三个LED,大家可能首先想到的就是将该代码复制得到三个文件,并将其中的parameter值修改为对应需要的值即可。此种方式虽然能够避免一个常量在模块中被多次使用,修改时容易发生的遗漏问题,但仍然还是需要存在三个独立的子模块,并未大大减轻设计工作量。其实,修改parameter有更加快捷的方式,这种方式不需要我们针对每一个需求分别复制设计代码并修改对应参数,只需要在上层模块例化该模块时,直接在例化的过程中修改该值即可。具体实现方法,这里以实例代码的形式进行讲解。
在上层模块中,例化子模块时直接修改其参数有两种语法结构,这里我们首先看第一种:
01 module LED_flicker(
02 Clk,
03 Rst_n,
04 LED
05 );
06
07 input Clk;
08 input Rst_n;
09 output
[1:0]LED;
10
11 counter counter0(
12 .Clk(Clk),
13 .Rst_n(Rst_n),
14 .led(LED[0])
15 );
16
17 counter counter1(
18 .Clk(Clk),
19 .Rst_n(Rst_n),
20 .led(LED[1])
21 );
22
23 defparam counter0.CNT_MAX =
24_999_99;
24 defparam counter1.CNT_MAX =
24_999_9;
25
26 endmodule
|
行和24行分别使用"defparam"来重新定义CNT_MAX的值。这里以第23行修改counter0中的CNT_MAX的值来具体讲解此种语法的结构。
首先,使用关键字"defparam"来声明这里需要对某个参数的值进行重新定义,空格之后紧跟着的counter0为counter模块被例化的名字,counter0后紧随一个".","."后紧随着的就是counter模块中需要修改的参数的名字,然后使用"="将新的值赋给改参数即可。这里使用模块名+"."+参数名的方式,与C语言结构体中使用结构体成员的形式很类似,大家可以对比学习。通过这种方式,大家可以在不修改原有设计文件的情况下,例化后,在上层模块直接修改参数。虽然模块中设定了有默认值,但是使用defparam修改的值比原始设计文件中的值拥有更高的编译优先级。当使用defparam修改了原始文件中的参数值后,原始文件中的默认参数值即被忽略。
的计数值进行计数,则需要花费太多的仿真时间,严重影响设计效率,因此也可以在仿真时直接在testbench中使用defparam来对设计文件中的参数进行修改,从而降低仿真复杂度。上述LED_flicker系统的testbench代码如下所示:
01 `timescale
1ns/1ps
02 `define clk_period 20
03
04 module LED_flicker_tb;
05
06 //source define
07 reg Clk;
08 reg Rst_n;
09
10 //probe define
11 wire
[1:0]LED;
12
13 //instant user module
14 LED_flicker LED_flicker0(
15 .Clk(Clk),
16 .Rst_n(Rst_n),
17 .LED(LED)
18 );
19
20 defparam LED_flicker0.counter0.CNT_MAX =
24;
21 defparam LED_flicker0.counter1.CNT_MAX =
24;
22
23 //generater clock
24 initial Clk =
1;
25 always
#(`clk_period/2)Clk =
~Clk;
26
27 initial
begin
28 Rst_n =
1'b0;
29 #(`clk_period
*
20
+
1);
30 Rst_n =
1'b1;
31 #(`clk_period
*
2000);
32 $stop;
33 end
34
35 endmodule
|
行和第21行,使用defparam对设计模块中的CNT_MAX进行了重新定义,我们希望通过这种方式,来在仿真的时候,让计数值非常小,这样仿真便能很快的得出结果,通过这两句话大家也可以看到,结构体的形式能够实现多级调用。然而,当使用此testbench文件直接仿真LED_flicker模块时,却并不能实现预期的效果。在modelsim中仿真时,报告如下信息:
虽然并不影响仿真的执行,然而从仿真执行的结果来看,这种在testbench中再次修改设计模块中参数的方式无法与实体模块中例化子模块后进行参数定义同时存在,将实体模块中使用defparam修改参数的部分屏蔽,则仿真文件中的defparam生效,实现了预期效果。下图分别为不屏蔽LED_flicker中defparam内容和屏蔽LED_flicker中defparam内容后的仿真结果,从图中可知,不屏蔽,LED[0]和LED[1]的翻转频率分别为100ms和10ms,结果与LED_flicker中defparam定义的值一致,屏蔽掉后,LED[0]和LED[1]的翻转频率都是0.001ms,仿真结果与testench文件LED_flicker_tb中defparam定义的值一致。
不屏蔽LED_flicker中defparam内容时仿真结果
屏蔽LED_flicker中defparam内容时仿真结果
在实际使用中,我们往往希望实体设计模块中能够设定某参数值为符合实际板级运行要求的值,而在仿真中,为了节约仿真时间,我们又希望修改该参数值以配合仿真通过上面的例子我们发现,使用defparam语句难以同时满足实体设计和仿真的需求。那么有没有更好的方法来实现实体设计与仿真验证中对参数的修改能够共存且互不干扰呢?
为了实现此功能,我们调整在实体模块中例化子模块时对参数的修改方式,上面我们介绍的是使用defparam语句重新定义子模块参数内容。接下来我们使用在模块例化时,直接对参数进行例化的方式来修改子模块中的参数值。使用参数例化方式修改参数值的LED闪烁灯模块代码如下所示:
01 module LED_flicker_inst(
02 Clk,
03 Rst_n,
04 LED
05 );
06
07 input Clk;
08 input Rst_n;
09 output
[1:0]LED;
10
11 counter
12 #(
13 .CNT_MAX(24_999_99)
14 )
15 counter0(
16 .Clk(Clk),
17 .Rst_n(Rst_n),
18 .led(LED[0])
19 );
20
21 counter
22 #(
23 .CNT_MAX(24_999_9)
24 )
25 counter1(
26 .Clk(Clk),
27 .Rst_n(Rst_n),
28 .led(LED[1])
29 );
30
行到19行为例化counter0模块的代码。这里对于子模块中的参数,使用了和端口相类似的形式进行修改。通过这种方式,在例化每个功能模块的时候,直接将所需修改参数的值也同时通过例化的方式修改了。
下面对这种例化方式的代码结构进行简单介绍:
行为原始设计模块的名字,第12行和14行为一对小括号,对参数例化的内容就在此括号中进行。注意11号,即括号前面需要使用"#"符号来声明这是对参数进行例化。括号内,参数例化形式与端口例化形式一致,使用"."+原始参数名+新值的方式来修改新的值。通过这种方式,能够避开对于同一个参数多次使用defparam来重定义值而引发的冲突。使用上述testench文件仿真LED_flicker_inst模块的仿真结果如下图所示:
使用defparam重新修改CNT_MAX值的仿真结果
可以看到,LED[0]和LED[1]的翻转频率都是0.001ms,为通过testbench修改后的结果。
为了验证使用这种例化的方式是否能够达到预期的效果,这里将testbench中defparam内容屏蔽,再来观察仿真结果即可,屏蔽掉testbench文件中defparam内容后仿真结果如下图所示:
屏蔽testbench文件中defparam内容后仿真
可以看到,屏蔽掉testbench中队CNT_MAX值的修改内容后,LED[0]和LED[1]的翻转频率分别为100ms和10ms,仿真结果就与实际设计值一致。但是在我们实际板级使用的时候,testbench中的值并不会对实际设计内容产生影响,因此,即使不屏蔽testbench中对参数值的修改,也不会影响板级设计结果。
因此我们可以总结得出,为了同时保证实际逻辑设计和仿真验证时对参数的修改能够共存,互不影响,在实体模块设计中,使用参数例化的方式修改被例化模块中的参数。在仿真验证用的testbench中,使用defparam语句来修改对应的参数。
通过本教程内容,讲解了在Verilog中使用参数化设计的方法,展示了参数化设计在实际使用时候的巨大便利。介绍了两种修改参数值的方法,并通过实际的例子展示了两种修改方式的特点,并最终给出了能够同时兼容实际逻辑设计和仿真验证的参数修改方法。希望大家以后。在设计自己的代码的时候,多使用这种参数化的设计方式。
如有更多问题,欢迎加入芯航线FPGA技术支持群:472607506
小梅哥
2015年11月7日于芯航线电子工作室
- FPGA学习笔记(一)Verilog语法基础
一.变量类型 ①数值 数值表示采用 <二进制位数>'<数值表示的进制><数值>的结构. 其中进制可以为b.o.d.h分别代表二.八.十.十六进制. 例如22'd0代 ...
- Verilog HDL基础语法讲解之模块代码基本结构
Verilog HDL基础语法讲解之模块代码基本结构 本章主要讲解Verilog基础语法的内容,文章以一个最简单的例子"二选一多路器"来引入一个最简单的Verilog设计文件的 ...
- JAVA 入门第一章(语法基础)
本人初学java 博客分享记录一下自己的学习历程 java我的初步学习分为六章,有c和c++的基础学起来也简便了很多. 第一章 语法基础 第二章 面向对象 第三章 常用工具类 第四章 文件操纵 第五章 ...
- python基础入门一(语法基础)
作为自己正式接触并应用的第一门编程语言,在Alex和武sir两位大王的要求下,开始了写博客总结的日子.学习编程语言是很有趣的一件事情,但有2点请一定要谨记:1.做人靠自己,码代码也必须靠自己.能不能成 ...
- PHP语法基础
1.PHP语法基础 PHP标记符 <?php ?> 常亮与变量 $a = 10; 变量 可以在运行过程中修改 $a = 10; $a = 20; $b = 5; echo $a+$b; c ...
- C#语法基础和面向对象编程
1.C#语法基础 http://www.cnblogs.com/tonney/archive/2011/03/16/1986456.html 2.C#与面向对象基础 很棒的资源,简明扼要,介绍的非常清 ...
- Lua脚本之语法基础快速入门
要 1.基本数据类型 2.Lua中的常用语句结构以及函数 3.Lua中的常用语句结构介绍 4.Lua中的库函数 目录[-] 一.基本数据类型 二.Lua中的常用语句结构以及函数 1.Lua中的常用语句 ...
- Javascript语法基础
Javascript语法基础 一.基本数据类型 JavaScript中支持数字.字符串和布尔值三种基本数据类型: 1.数字 数字型是JavaScript中的基本数据类型.在JavaScript ...
- LinQ 语法基础
LINQ (Language-Integrated Query,语言集成查询). LINQ to Objects.LINQ to SQL.LINQ to DataSet和LINQ to XML,它们分 ...
随机推荐
- python_序列
1. python存在6中内建序列:列表.元组.字符串.Unicode字符串.buffer对象.xrange对象 列表可以修改,元组和字符串不可以修改. 2. 序列支持的操作: 索引 序列中所有的元素 ...
- SLAM学习笔记(3)相关概念
SIFT,即尺度不变特征变换(Scale-invariant feature transform,SIFT),是用于图像处理领域的一种描述子.这种描述具有尺度不变性,可在图像中检测出关键点,是一种局部 ...
- 虚拟化技术比较 PV HVM
很多人看到同样配置的VPS价格相差很大,甚是不理解,其实VPS使用的虚拟技术种类有很多,如OpenVZ.Xen.KVM.Xen和HVM与PV.在XEN中pv是半虚拟化,hvm是全虚拟化,pv只能用于L ...
- 使用Redis来实现LBS的应用
原文地址 微信.陌陌 架构方案分析 近两年.手机应用,莫过于微信.陌陌之类最受欢迎:但实现原理,分享文章甚少. 故,提出两种方案,供分享:不对之处,敬请留言学习. 目标 查找附近的某某某,由近到远返回 ...
- Git 使用的配置 常用命令
老文一篇 搬过来 1. git的部分配置 # 全局提交用户名与邮箱 git config --global user.name "simon" git config --globa ...
- mysql:sql行列转换
今天一个同学遇到一个问题问我了,由于本人平时学习的mysql比较基础,确实没解决,后来google了一下,才知道是sql的一种技法[行列转换],话不多说先上图: 想得到下面的结果: +------+- ...
- 如何用Java解析CSV文件
首先看一下csv文件的规则: csv(Comma Separate Values)文件即逗号分隔符文件,它是一种文本文件,可以直接以文本打开,以逗号分隔.windows默认用excel打开.它的格式包 ...
- HTML语言的一些元素(二)
3)表示元素:<b>,<i>,<u>,<s>,<tt>,<sup>,<sub>,<strike>,< ...
- C++Primer 第十四章
//1.当运算符作用于类类型运算对象时,可以通过运算符重载重新定义该运算符的含义.明智的使用运算符重载能令程序更加易于编写和阅读. //2.重载的运算符是具有特殊名字的函数,它们由关键字operato ...
- Struts2 标签库讲解
要使用Struts2的标签,只需要在JSP页面添加如下一行定义即可: <%@ taglib prefix="s" uri="/struts-tags"%& ...
| |