【Verilog HDL 训练】第 06 天(边沿检测)
1. 复习verilog语法
【选做题】
- reg和wire的区别
寄存器数据类型
Verilog中规定,凡是在程序块中被赋值的变量,都必须是寄存器类型的。(程序块:例如always块)
这里未免还是会让人产生疑惑?寄存器数据类型的变量最后一定会被综合成寄存器吗?
对应于实际的数字电路中,如果该程序块描述的是时序逻辑,则该寄存器变量对应为寄存器;如果该程序块描述的是组合逻辑,该寄存器变量对应为硬件逻辑;如果该程序块描述的是不完全组合逻辑,那么该寄存器变量也可以对应为锁存器。由此可见,寄存器类型的变量不一定会综合为寄存器。
线网数据类型
Verilog中规定,模块的input和inout端口必须是线网类型;连续赋值语句的被赋值对象必须是线网类型。对应于实际的数字电路,线网类型实际上就对应着硬件的连线,起到连接作用。
线网数据类型包括wire和tri等,wire最常见,不必多说,很多情况下直接声明为wire即可。
至于tri其实和wire在用法上是一模一样的,不过有时候,我们需要定义一些会被三态门驱动的硬件连线,用tri来命名会让代码更具有可读性,让人一看就知道这根连线上会出现Z状态,仅此而已!
---------------------
作者:李锐博恩(Reborn)
原文:https://blog.csdn.net/Reborn_Lee/article/details/82771503
这是我以前写过的博文:【 Verilog HDL 】寄存器数据类型(reg)与线网数据类型(wire,tri)
- 阻塞赋值与非阻塞赋值的区别
1、非阻塞(Non_Blocking)赋值方式(如 b <= a;)
块结束后才完成赋值操作;
b的值并不是立刻就改变;
这是一种比较常用的赋值方法。(特别在编写可综合模块时)
2、阻塞(Blocking)赋值方式(如: b = a;)
赋值语句执行完后,块才结束;
b的值在赋值语句执行完后立刻就改变;
可能产生意想不到的结果。
非阻塞赋值方式和阻塞赋值方式的区别常给设计人员带来问题。问题主要是给“always”块内的reg型信号的赋值方式不易把握。
如果“always”模块中的reg型信号采用非阻塞赋值方式:
b <= a;
这种方式的赋值并不是马上执行的,也就是说“always” 块内的下一条语句执行后,b并不等于a,而是保持原来的值。“always”块结束后,才进行赋值。
如果采用阻塞赋值方式:
b = a;
这种赋值方式是马上执行的。也就是说,执行下一条语句时,b已经等于a了。尽管这种方式看起来直观,但是可能引起麻烦。
下面对两种赋值方式举例分析:
例1:
非阻塞赋值
always @(posedge clk)
begin
b <= a;
c <= b;
end
上例中的"always"块中用了非阻塞赋值方式,定义了两个reg型信号b和c,clk信号的上升沿到来时,b就等于a,c就等于b,这里应该用到了两个触发器。请注意:赋值是在"always"块结束后执行的,c应为原来b的值,b为原来的a值。这个"always"块实际描述的电路功能如下图所示:
例2:
阻塞赋值
always @(posedge clk)
begin
b = a;
c = b;
end
上例中的 "always"块用了阻塞赋值方式。clk信号的上升沿到来时,将发生如下的变化:b马上取a的值,c马上取b的值(即等于a),生成的电路图如下所示只用了一个触发器来寄存器a的值,又输出给b和c。这大概不是设计者的初衷,如果采用[例1]所示的非阻塞赋值方式就可以避免这种错误。
---------------------
作者:李锐博恩(Reborn)
来源:CSDN
原文:https://blog.csdn.net/Reborn_Lee/article/details/82222665
这也是我以前写过的博文:【Verilog HDL】赋值语句之阻塞赋值方式与非阻塞赋值方式
其他博文:【 Verilog HDL 】进一步了解 Verilog HDL 的赋值运算符
- parameter与define的区别
作用域区别:
parameter 作用于声明的那个文件;`define 从编译器读到这条指令开始到编译结束都有效,或者遇到`undef命令使之失效。
如果想让parameter或`define作用于整个项目,可以将如下声明写于单独文件,并用`include让每个文件都包含声明文件:
`ifndef data
`define data 8’d14
或者
parameter data = 8‘d14;
`endif
`define也可以写在编译器最先编译的文件顶部。通常编译器都可以定义编译顺序,或者从最底层模块开始编译。因此写在最底层就可以了。
区别:
parameter可以用作例化时的参数传递。
在使用状态机时候区别挺大的。状态机的定义可以用parameter 定义,但是不推荐使用`define 宏定义的方式,因为'define 宏定义在编译时自动替换整个设计中所定义的宏,而parameter 仅仅定义模块内部的参数,定义的参数不会与模块外的其他状态机混淆。例如一个工程里面有两个module 各包含一个FSM,如果设计时都有IDLE 这一名称的状态,如果使用'define 宏定义就会混淆起来,如果使用parameter 则不会造成任何不良影响。
一旦`define指令被编译,其在整个编译过程中都有效。例如,通过另一个文件中的`define指令,定义的常量可以被其他文件中被调用。直到遇到`undef;parameter只在定义的文件中有效,在其它文件中无效。
这块内容参考:Verilog中parameter和define的区别
- task与function的区别
1.函数可以返回一个值而任务可以返回多个值
2.函数一经调用必须立即执行,里面不能包含任何的时序控制,而task中可以有时序控制
3.函数可以调用函数,但不可以调用任务,任务既可以调用函数也可以调用任务
4.函数必须要有一个输入参数,而任务可以没有参数输入。
4.任务输出的信号,在模块中必须定义为reg信号
2. 用verilog实现边沿检测电路:上升沿,下降沿,双沿(上升或下降沿)。
昨天刚刚写了一篇类似的博文,里面用到了边沿检测,边沿检测应用可算是十分广泛呀, 写flash控制器时候也用到了。
从这篇博文中节选出边沿检测的部分吧:
//req上升沿检测
reg reqr1, reqr2, reqr3; //定义reqrx代表延迟x拍
//--------------------------------------------------------
//第一种方法
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
reqr1 <= 1'b0;
reqr2 <= 1'b0;
reqr3 <= 1'b0;
end
else begin
reqr1 <= req;
reqr2 <= reqr1;
reqr3 <= reqr2;
end
end
//pos_req3比pos_req2延后一拍,确保数据被稳定锁存
wire pos_req1, pos_req2, pos_req3;
assign pos_req1 = ( { req, reqr1 } == 2'b10 ) ? 1'b1 : 1'b0;
assign pos_req2 = ( { reqr1, reqr2 } == 2'b10 ) ? 1'b1 : 1'b0;
assign pos_req3 = ( { reqr2, reqr3 } == 2'b10 ) ? 1'b1 : 1'b0;
//------------------------------------------------------------------
// 检测上升沿的第二种方法
// assign pos_req1 = ~reqr1 & req;
// assign pos_req2 = ~reqr2 & reqr1;
// assign pos_req3 = ~reqr3 & reqr2;---------------------
下面是仿真图,只看上面用到的变量部分:
更多细节,点开链接:https://blog.csdn.net/Reborn_Lee/article/details/89647526
上面的答案是昨天写的,所以优先附上,但之前也转载过类似的东西,看下之前的:
-
reg [1:0] signal_r;
-
//-------------------------------------------------------
-
//
-
always @(posedge clk or negedge rst_n)begin
-
if(rst_n == 1'b0)begin
-
signal_r <= 2'b00;
-
end
-
else begin
-
signal_r <= {signal_r[0], signal_in};
-
end
-
end
-
-
assign singal_posedge = ~signal_r[1] & signal_r[0];//检测上升沿
-
assign singal_negedge = signal_r[1] & ~signal_r[0];//检测下降沿
其实原理都是一样的。
3. 记录一下第2题中用到的工具,包括工具版本,操作步骤或命令选项,遇到的错误,提示信息等。
工具是:Vivado 2018
操作步骤:无非是建立工程,然后编写Verilog代码,之后编写Testbench文件,然后仿真即可。
遇到的问题:仿真发现,检测到边沿有时候并不能持续一个时钟,这是由于要检测的信号与时钟上升沿之间的位置关系。在应用边沿检测的时候,如果时间不够,可以多延迟几拍,见第二题的代码吧。
文章来源: reborn.blog.csdn.net,作者:李锐博恩,版权归原作者所有,如需转载,请联系作者。
原文链接:reborn.blog.csdn.net/article/details/89669193
- 点赞
- 收藏
- 关注作者
评论(0)