HDL verilog 常用编程知识点

1. 在Verilog语言中,所有的变量都是静态的,即所有变量都只有唯一一个存储地址。

2. 前(RTL)仿真:功能(行为级)上进行验证。
 后仿真:对布局布线后的门级电路进行验证。

3. 测试时钟必须要用initial模块产生,否则不知道初始值。

4. 在always块中,逻辑是按照指定的顺序执行的。“always”块中的语句称为“顺序语句”,因为它们是顺序执行的。所以“always”块也称为过程块。“initial”块也是过程块。

5. 只有连续赋值语句assign和实例引用语句可以独立于“过程块”而存在于模块的功能定义部分。

6. 四种基本数据类型reg、wire、integer、parameter。

7. x和z值,在数字电路中,x代表不定值,z代表高阻值。z还有一种表达方式可以写成“?”。

8. 下划线可以用来分隔开数的表达以提高程序可读性。例如:8'b0011_0010。

9. 常量不说明位数时默认是32位。1=32'd1=32'b1。

10. parameter可以定义一个标识符代表一个常量,称为符号常量。

11. 网络数据类型表示结构实体(例如:门)之间的物理连接。网络类型的变量不能存储值。而且它必须收到驱动器(例如:门或连续赋值语句)驱动。

12. wire型数据常用来表示用以assign关键字指定的组合逻辑信号。Verilog程序模块中输入、输出信号类型默认时自动定义为wire型。wire型信号可以用作任何方程式的输入,也可以被用做“assign” 语句或实例元件的输出。

13. 在“always”模块内被赋值的每一个信号都必须定义成reg型。

14. reg [7:0] mema [255:0]; //定义一个名为mema的存储器,该存储器有256个8位存储器。

15. 位运算符
(1)~ :取反   (2)&:按位与    (3)|:按位或
(4)^ :按位异或 (5)^~:按位同或

16. 缩减运算符
 a = &A;   //将A的所有bit递归进行与操作,结果为1bit。
 同样的操作还有:|A 、~A。

17. 非阻塞赋值(<=)
(1)在语句块中,非赋值语句所赋的变量值不能立即就位下面的语句所用。
(2)块结束后才能完成这次赋值操作,而所赋的变量值是上一次赋值得到的。
(3)在编写可综合的时序逻辑模块时,这是最常用的赋值方法。

18. 阻塞赋值(=)
(1)赋值语句执行完成后,块才结束。
(2)被赋值的变量在赋值语句完成后,立刻发生改变。
(3)在时序逻辑使用中,可能产生意想不到的结果。

19. begin_end语句通常用来标识顺序执行的语句,用它标识的块称为顺序块。fork_join语句,通常标识并行执行的语句,用它来表示的块称为并行块。

20. 在Verilog HDL语言中,可以给每个块取一个名字,只需将名字加载begin或fork后面即可,这样做的原因有几点:
(1)可以在块内定义局部变量。
(2)可以允许被其他语句调用,如disable语句。例如:disable blockname;(相当于C语言中 的break)。
(3)在Verilog语言里,所有变量都是静态的,即所有的变量都只有唯一一个存储地址。因此, 进入或跳出块并不影响存储在变量内的值。(即块内的局部变量不会消失)。

21. 循环语句有四种:forever、repeat、while、for。

22. forever语句
    forever  语句;
 或 forever  begin
 		...
 		...
	end
forever语句常用于产生周期性的波形,用来作为仿真测试信号。它与always的不同之处在于不能独立写在程序中,而必须写在initial块中。

23. repeat语句
   repeat(表达式)  语句;
或 repeat(表达式)  begin
   ...
   ...
   end
24. 生成变量genvar
genvar i;        //生成变量。声明一个临时循环变量,该变量只用于生成块的循环计算,
                 //在仿真时,该变量在设计中并不存在
generate
    for(i=0; i<N; i=i+1) begin: name
        ...
        module u1(...);        //可以循环实例化
    end
endgenerate
25. 电平敏感时序控制
always wait(count_enable)          //只有当count_enable=1时才执行后面的语句
    #20 count = count + 1;
26. task和function说明语句分别用来定义任务和函数,利用任务和函数可以把一个很大的module分解成许多较小任务和函数便于理解,调试。(task和function在模块内定义使用)。使用task和function语句可以简化程序的结构,使程序明白易懂,是编写大型模块的基本功。
任务的定义语法如下:
task  任务名;
    端口及数据类型声明语句;
    ...
    ...
endtask
函数的定义语法如下:
function  返回值的类型或范围  函数名;
    端口说明语句;
    变量类型说明语句;
    begin
        ...
        ...
        ...
    end
endfunction
函数的定义蕴含声明了与函数同名的,函数内部的寄存器。

27. task和function说明语句的不同点
任务和函数的主要不同有以下四点:
(1)函数只能和主模块共用同一个仿真时间单位,而任务可以定义自己的仿真时间单位。
(2)函数不能启动任务,但可以调用其它函数。任务能启动其他任务和函数。
(3)函数至少要有一个输入变量,而任务可以没有或有多个任何类型的变量。
(4)函数返回1个值,而任务不返回值。
根据以上考量,task较为常用,因为限制较少。
例如,定义一个任务或函数,功能是将一个16bits字变成一个新字。
任务调用源码:switch_bytes(old_word, new_word);
函数调用源码:new_word = switch_bytes(old_word);

28. 函数的使用规则
(1)函数的定义不能包含有任何的时间控制语句,即任何用#、@或wait来标识的语句。
(2)函数不能启动任务。
(3)函数只能有1个返回值,并且至少要有1个输入变量。
(4)在函数的定义中必须有一条赋值语句给函数中的一个内部变量赋以函数的结果值,该内部 变量具有和函数名相同的名字。

29. 自动(递归)函数
Verilog中的函数是不能递归调用的。设计模块中若某函数在两个不同的地方被同时并发调用,由于这两个调用同时对同一地址空间进行操作,那么计算结果将是不确定的。
若在函数声明时使用了关键字automatic,那么该函数将成为自动的或可递归的,即仿真器为每一次函数调用动态地分配新的地址空间。
例如:function automatic interger factorical;
   function:关键字   integer:返回值类型   factorical:函数名

30. 带符号函数
源代码如:function signed[63:0] compute_signed(input [63:0] vector);

31. 任务和函数都包含在设计层次中,可以通过层次名对它们进行调用。

32. $display和$write任务
格式:$display("输出格式",p1,p2,...,pm);     //按格式输出,自动在输出后换行
$write的格式和作用与$display相同,但$write输出时不换行。
        输出格式                     说明

          %c                   以ASCII码字符形式输出

          %v                   输出网络型数据信号强度

          %m                   输出等级层次的名字(可以区分哪个模块实例在输出)

          %s                   以字符串形式输出

          %t                   以当前的时间格式输出

          %e                   以指数形式输出实数型

          %f                   以十进制形式输出实数型
33. $fopen 打开文件
用法:文件句柄 = $fopen("文件路径");
任务$fopen返回一个被称为多通道描述符的32位值。多通道描述符中只有1位被置为1。标准输出最低位为1。

34. 写文件$display和fmonitor
用法:$display(文件描述符,p1,p2,...,pm)
用法:$fmonitor(文件描述符,p1,p2,...,pm)
文件描述符是一个多通道描述符,它可以是一个文件句柄或多个文件句柄按位的组合。Verilog会把输出写到与文件描述符中值为1的位相关联的所有文件中。

35. 关闭文件$fclose
用法:fclose("文件描述符")

36. 选通显示$strobe
$strobe与$display除了一点差异外,其他非常相似。
如果许多其他语句与$display任务在同一个时间单位执行,那么这些语句与$display任务的执行顺序是不确定的,如果使用$strobe,该语句总是在同时刻的其他赋值语句执行完成之后才执行。

37. $monitor
$monitor提供了监控和输出参数列表中的表达式或变量值的功能。每当参数列表中的变量或表达式的值发生变化时,整个参数列表中变量或表达式的值都将输出显示。
$monitoron   //控制启动监控
$monitoroff   //控制关闭监控
$monitor往往在initial块中调用,只要不调用monitoroff,$monitor便不间断地对所设定的信号进行监视。

38. $stop
$stop多用于仿真调试,类似于软件语言中的断点。

39. $readmemb和$readmemh
用法:从文件中读取数据到存储器中。
格式:$readmemb("数据文件名","存储器名");
   $readmemb("数据文件名","存储器名","起始地址");
   $readmemb("数据文件名","存储器名","起始地址","结束地址");
这里的地址指的是对存储器数组的寻址地址(下标)。给定起始地址和结束地址时就从起始地址读取到结束地址结束。
在这两个系统任务中,被读取的数据文件内容只能包含:空白位置,注释行(//形式或/*...*/),二进制或十六进制的数字,数字中不能包含位宽说明和格式说明。
   $readmemb:文件中每个数字必须是二进制。
   $readmemh:文件中每个数字必须是十六进制。

40. $random
$random,产生一个带符号的32位的整形随机数。 一般用法:$random % b,其中b>0
利用这个函数可以产生随机脉冲序列或宽度随机的脉冲序列,以用于电路的测试。

41. 宏定义`define
用一个指定的标识符(即名字)来代表一个字符串,它的一般形式为:
`define 标识符 字符串   例如:`define WORD_SIZE 8
关于宏定义的几点说明:
(1)宏名(标识符)建议用大写字母,以与变量名相区别。
(2)`define命令可以出现在模块定义里面,也可以出现在模块定义外面。宏名的有效范围为定义命令之后到原文件结束。
(3)在引用已定义的宏名时,必须在宏名的前面加上符号“ ` ”,表示该名字是一个经过宏定义的名字。
   例如reg[1:Word_SIZE] data;
(4)当需要改变某一个变量时,可以只改变`define命令行,一改全改。
(5)宏定义不是Verilog HDL语句,不必在行末加分号。
(6)在进行宏定义时,可以引用已定义的宏名。

42. 文件包含处理`include
  一般形式:`include"文件名"

43. 时间尺度`timescale
·timescale命令用来说明跟在该命令后的模块的时间单位和时间精度。
格式:`timescale 时间单位/时间精度
时间单位参量是用来定义模块中仿真时间和延迟时间的基准单位的。
时间精度参量是用来声明该模块的仿真时间的精确程度的。
时间单位和时间精度的有效数字为1,10,1000。

44. Verilog 门级结构描述8个基本的门类型关键字
  and——与门   nand——与非门
     or——或门      nor——或非门
   not——非门
   xor——异或门   nand——同或门
   buf——缓冲器
关于门类型的引用:门的类型 驱动能力 延时 实例名(输出,输入)。 //驱动能力和延时属于可选项
例如:nand #10 nd1(a, data, clock);

45. 流水线设计
流水线设计实际上是把规模较大、层次较多的组合逻辑电路分为几个级,在每一级插入寄存器组并暂存中间数据。K级流水线就是从组合逻辑的输入到输出恰好有K个寄存器组(分为K级,每一级都有一个寄存器组)。上一级的输出是下一级的输入而又无反馈的电路。

46. 组合逻辑
输出只是当前输入逻辑电平的函数(有延时),与电路的原始状态无关的逻辑电路。也就是说,当输入信号中的任何一个发生变化时,输出都有可能会根据其变化而变化,但与目前所处的状态没有任何关系(即逻辑电路没有记忆部件)。

47. 时序逻辑
输出不只是当前输入的逻辑电平的函数,还与电路目前所处的状态有关的逻辑电路(即逻辑电路有记忆部件)。

48. Mealy状态机和Moore状态机 如果时序逻辑的输出不但取决于状态,还取决于输入,称为Mealy状态机。而有些时序逻辑电路的输出只取决于当前状态,这样的电路称为Moore状态机。在设计工作中,大部分状态机都属于Mealy状态机。

49. 在Verilog HDL中,常用always语句和case语句描述有限状态机。
在表示状态机时,状态可以用不同的编码表示。例如,假设有4个状态IDLE,start,stop,clear。
用Gray编码:
output [1:0] state;
reg [1:0] state;
parameter  IDLE=2'b00,
          start=2'b01,
           stop=2'b10,
          clear=2'b11;
用独热码编码
output [3:0] state;
reg [13:0] state;
parameter  IDLE=4'b1000,
          start=4'b0100,
           stop=4'b0010,
          clear=4'b0001;
实际中采用哪一种编码好要看具体情况而定。对于用FPGA实现的有限状态机建议采用独热码,因为虽然独热码多用了两个触发器。但所用组合电路可省一些,因为使电路的速度和可靠性有所提高,而总的单玉数并无显著增加。采用独热编码后有了多余的状态,就有一些不可到达的状态。因此,在case语句的最后需要增加default分支项,可以用默认项表示该项,也可以用确定项表示,以确保回到IDLE状态。一般综合器都可以通过综合指令的控制来合理地处理默认项。

50. 综合的一般原则
(1)综合之前一定要进行仿真,否则综合的结果可能产生逻辑错误。
(2)每一次布局布线之后都要进行仿真,在器件编程或流片之前要做最后的仿真。
(3)用Verilog HDL描述的异步状态机是不能综合的。如果一定要设计异步状态机,则可用电路图输入的方法来设计。
(4)如果要为电平敏感的锁存器建模,使用连续赋值语句是最简单的方法。

51. 用always块设计纯组合逻辑电路时,在生成组合逻辑的always块中参与赋值的所有信号都必须有明确的值,即在赋值表达式右端参与赋值的信号都必须在always @(敏感电平列表)中列出。

52. 对一个寄存器型(reg)和整型(integer)变量给定位的赋值,只允许在一个always块内进行。如果在另一个always块中也对其赋值,这是非法的。

53. 把某一信号赋值为'bx,综合器就把它解释成无关状态,因为综合器为其生成的硬件电路最简洁。

54. 异步置位与复位
异步置位与复位是与时钟无关的。当异步置位与复位到来时他们立即分别置触发器的输出为1或0,不需要等到时钟沿到来才置位或复位。把他们列入always块的事件控制括号内就能出发always块的执行。
事件控制语法:
always @(沿关键词  时钟信号  or  沿关键词   复位信号  or  沿关键词  置位信号)

55. 同步置位与复位
同步置位与复位是指只有在时钟的有效跳变沿时刻置位或复位,信号才能使触发器置位或复位。因此不能把set或reset信号名列入always块的事件控制表达式。
事件控制语法:
always @(沿关键词  时钟信号)

56. 在描述组合逻辑的always块中用阻塞赋值(=),则综合成组合逻辑的电路结构。在描述时序逻辑的always块中用非阻塞赋值(<=),则综合成时序逻辑的电路结构。
RHS——赋值等号右边的表达式或变量
LHS——赋值等号左边的表达式或变量
阻塞赋值的行为可以认为是只有一个步骤的操作,即计算RHS并更新LHS,此时不能允许(即阻塞)来自任何其他Verilog语句的干扰。
非阻塞赋值的执行可以看做两个步骤:(1)在赋值开始时,计算非阻塞赋值RHS的表达式。(2)在赋值结束时更新LHS表达式。此时允许(非阻塞)其他Verilog语句同时进行操作。
非阻塞赋值操作只能用于对寄存器型(reg)变量进行赋值,因此只能用在initinal块和always块等过程块中。

57. Verilog模块编程的8个要点
在编写Verilog代码时必须牢记这8个要点,才能在综合布局布线后的仿真中避免出现冒险竞争现象。
(1)时序电路建模时,用非阻塞赋值(<=)
(2)锁存器电路建模时,用非阻塞赋值(<=)
(3)用always块建立组合逻辑模型时,用阻塞赋值(=)
(4)在同一个always块中建立时序和组合逻辑电路时,用非阻塞赋值(<=)
(5)在同一个always块中不要即用非阻塞赋值又用阻塞赋值
(6)不要在一个以上的always块中为同一个变量赋值
(7)用$strobe系统任务来显示用非阻塞赋值的变量值
(8)在赋值时不要使用#0延迟

58. 非阻塞语句的赋值在所有的$display命令执行以后才更新数值。

59. 在同一个always块中,可以对某同一变量进行多次非阻塞赋值,但在多次赋值中,只有最后一次赋值对该变量起作用。

60. always和模块实例化是并行的,模块实例化不能写到always块内。

61. Verilog中如果要遍历vector中的值,注意程序写法
result[i] = data[15+16*i,16*i];   //这是错误的,此时会报错 i is not constant
result[i] = data[(16*i)+,16];      //这是正确写法,它的含义是从16*i开始,向上读16位。当i=0时,result[0] = data[15:0];
  • 30
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值