Verilog学习的重点和难点总结

一、阻塞赋值和非阻塞赋值问题;
1.1 阻塞赋值,即“=”。用于组合逻辑的设计中,例如:
1)连续赋值语句:

assign a=b;     //(a必须是wire型,b可以是wire型也可以是reg型)

2)在always模块中设计的组合逻辑电路:

module test1(clk,a,b,c);
input clk;
input a;
output b,c;
reg b,c;
always@( posedge clk)  //端口定义省略
 begin
  b=a;
  c=b;
end</font>`</td></tr></table>

1.2 非阻塞赋值,即“<=”。用于时序逻辑设计。例如:

module test2(clk,a,b,c);
input clk;
input a;
output b,c;
reg b,c;
always@(posedge clk)  //端口定义省略
 begin
  b<=a;
  c<=b;
end
endmodule

1.3 两者的区别:
a.阻塞赋值先赋值,always块才会结束,即在阻塞赋值过程中不允许其他指令的插入,直到完成赋值为止;
b. 而非阻塞赋值在赋值的过程中可以插入其他指令,在块结束之后才赋值;
导致结果的不同:
a. test1模块得到的结果是当时钟的上升沿到来时b的值为a的值,而c的值也为a的值,因为a的值送给b之后,才实行c=b操作,此时b已经变为a的,所以c也为a的值;
b. test2中的结果为b等于a,而c等于b原来的值,因为在模块结束后才将a值送给b,而c=b操作在模块结束之前就完成了,所以c为b的值。

所以可以看到在相同的环境下,不同的幅值方式产生了很大的差别,所以为了达到设计的要求,避免冒险竞争问题,应按下列要求进行设计:
1)在设计时序电路建模和锁存器建模时,用非阻塞赋值;
2)在电平触发的always块和连续赋值语句设计的组合逻辑电路时,用阻塞赋值;
3)在时钟边沿触发的always块中设计组合和时序逻辑电路时,用非阻塞赋值;
4)不能在同一个always块中同时出现阻塞赋值和非阻塞赋值。
.
.
.
二、同步复位和异步复位的问题
2.1 首先是同步复位,详细如下:

always@(posedge clk)
begin 
if(rst) 
过程1…
else 
过程2
end 

具体为:在always块的条件中不加入复位信号rst,此时只有当clk的上升沿到来时,才能进入always块中实现复位,即与时钟同步,所以称为同步复位。

2.2 其次是异步复位,详细如下:

always@(posedge clk or negedge rst)
begin 
if(rst) 
过程1…
else 
过程2
end 

具体为:在always块的条件中加入复位信号rst,此时不只有当clk的上升沿到来时,才能进入always块中实现复位,rst的下降沿到来时也能实现复位,即可以不与时钟同步,所以称为异步复位。

2.3 各自的优缺点
1)同步复位的优点:
a. 可以实现系统的100%的同步,方便时钟分析;
b. 可以防止复位信号的突变,以为即使复位信号有突变,此时时钟信号的上升沿没有到来,也不能实现复位;
2)同步复位的缺点:
a. 复位信号的有效时间必须大于一个时钟周期才能反映,如果复位信号延时过短就不能实现复位;
b. 在逻辑器件中的所有触发器都是异步复位的,所以在使用同步复位时,在每一个触发器的输入端都需添加一个寄存器,这样就无形中消耗了资源;
3)异步复位的优点:
a. 可以实现全局随时复位,操作简单;
b. 在每个触发器前不需要多余的寄存器,减少资源消耗;
4)异步复位的缺点:
a. 如果复位信号出现尖刺,容易导致异常复位,影响系统正常运作;
所以各有千秋,推荐使用异步复位,因为尖刺并不会容易产生,不必过于担心,而且能够实现随时手动复位,减少资源消耗。
.
.
.

三、状态机的设计流程及其注意事项
3.1 状态机设计步骤
a. 捋清事件的来龙去脉,事件原因即为逻辑输入,结果即为逻辑输出,输入输出的关系即为逻辑关系;
b. 将实际的逻辑关系用逻辑函数表示,然后将函数化简,函数越简单,电路实现就越简单,利用的资源也就越少;
c. 列出状态转换表或状态转换图;
d. 状态编码,即选择gray编码或独热编码,如果FPGA器件的触发器资源丰富,推荐使用独热编码,即使电路的复杂度会增高,但能提高电路的性能;
e. 捋清每一个状态下,相应值的赋值情况,即将某些状态符置高或置低;
f. 捋清状态转换的条件;
g. 选择case语句或if-else语句实现代码描述。

3.2 状态机设计注意事项
a. 在描述多状态转换时,推荐利用case语句实现代码描述,并且添加default选项,让状态机回到初始状态,防止出现锁存状态;
b. 为了能综合出有效地电路,状态机中必须明确的由唯一时钟触发,即同步状态机;
c. 状态机应该有一个同步或异步复位端,推荐使用异步复位端;
d. 在设置编码状态的名称时,要按相应的功能命名,方便代码理解与维护。
.
.
.
四、task和function说明语句
1)任务和函数的不同点:
a. 任务可以调取其它的任务或函数,函数不能调取任务;
b. 任务可以具有自己的仿真时间,但是函数只能和主module的仿真时间相同;
c. 任务可以有任何类型的变量或者没有,但是函数至少有一个输入变量;
d. 任务不返回值,只是执行某操作,而函数有返回值。

2)task的说明
a. 任务的定义:

task <任务名>
<端口及数据类型说明语句>
<语句1>
<语句2>
……………..
<语句n>
endtask

b. 任务的调用:
直接在需要执行任务中的功能的地方写上任务名即可,如果有端口,把端口写上:
<任务名>(端口1,端口2,…,端口n);
下面是一个具体的例子用来说明怎样在模块的设计中使用任务,使程序容易读懂:

module traffic_lights;
reg clock, red, amber, green;
parameter on=1, off=0, red_sec=350,
amber_sec=30,green_sec=200;
//initial traffic lights
initial  red=off;
initial  amber=off;
initial  green=off;
//control the time of traffic lights 
always
 begin
 red=on; //open red lights
 light(red,red_sec); //delay task
 green=on; //open green lights
 light(green,green_sec);  // delay task
 amber=on; //open amber lights
 light(amber,amber_sec);  // delay task
end
//define start task of traffic lights
task light(color,sec);
 output color;
 input[31:0] sec;
 begin
  repeat(sec) @(posedge clock);//wait sec up time
  color=off;//close lights 
 end
endtask
//generate clock block
always
 begin
  #80 clock=0;
  #80 clock=1;
 end
endmodule

这个例子描述了一个简单的交通灯的时序控制,并且该交通灯有它自己的时钟产生器。

3) function说明语句
函数的目的是返回一个用于表达式的值。
a. 定义函数的语法:

function <返回值的类型或范围> (函数名);
<端口说明语句>
<变量类型说明语句>
 begin
  <语句>
  ........
 end
endfunction

其中<返回值的类型或范围>可以缺省,缺省状态下默认为一位的寄存器类型。例子如下所示:

function [7:0] getaddr;
 input [3:0] a,b;
  begin 
   getaddr={a,b};  //函数内说明语句,把结果直接赋予函数的返回字节
   end
  endfunction

从上例子中可以看出,函数的返回值就是函数名,函数名同时代表着一个寄存器,此寄存器的类型或范围与定义的<返回值的类型或范围>相同。

b. 函数的调用
函数的调用是通过将函数作为表达式中的操作数来实现的。
其调用格式如下:
<函数名> (<表达式>,<表达式>)
其中函数名作为确认符。
下面这句表达式调用了getaddr函数实现地址高低位拼接功能。

addr=control? getaddr(addr_high,addr_low):0;

c. 函数的使用规则
除了前面与任务比较时提到的函数规则之外,另添加如下两条:

  1. 函数的定义不能包含有任何的时间控制语句,即任何用#、@、或wait来标识的语句。
  2. 在函数的定义中必须有一条赋值语句给函数中的一个内部变量(即函数名)赋以函数的结果值。

d. 举例说明
下面的例子中定义了一个可进行阶乘运算的名为fact的函数,该函数返回一个32位的寄存器类型的值,该函数可后向调用自身,并且打印出部分结果值。

module test2;
//函数的定义-------------------------------
function[31:0] fact;
input[3:0] opcode;
reg[3:0] inc;
 begin
  fact = opcode? 1 : 0;
  for(inc=2;inc<=opcode;inc=inc+1)
   fact = inc * fact;
 end
endfunction
//函数的测试-------------------------------------
reg[31:0]result;
reg[3:0] i;
initial
 begin
 result=1;
 for(i=2;i<=9;i=i+1)
  begin
   $display("Partial result i= %d result= %d", i, result);
   result = i * fact (i)/((i*2)+1);
  end
 $display("Finalresult=%d",result);
 end
endmodule//模块结束
  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值