(1)reg和wire的区别:
reg型数据保持最后一次的赋值,而wire型数据需要持续的驱动。wire用在连续赋值语句assign中;reg用于always过程赋值语句中。
wire对应于连续赋值,主要起信号间连接作用,用以构成信号的传递或者形成组合逻辑,如assign;reg对应于过程赋值,是寄存器的抽象表达,作用类似通常编程语言中的变量,可以储存数值,作为参与表达式的运算,通常负责时序逻辑,如always,initial。
Verilog中使用reg型,并不表示综合出来就是暂存器register:在组合电路中使用reg,组合后只是net;在时序电路中使用reg,合成后才是以Flip-Flop形式表示的register触发器。
(2)阻塞赋值和非阻塞赋值:
非阻塞(non-blocking) 赋值语句(b<= a):
1)块内的赋值语句同时赋值;
2)b的值被赋成新值a的操作, 是与块内其他赋值语句同时完成的;
3)建议在可综合风格的模块中使用非阻塞赋值。
阻塞(blocking) 赋值语句(b = a):
1)完成该赋值语句后才能做下一句的操作;
2)b的值立刻被赋成新值a;
3)硬件没有对应的电路,因而综合结果未知。
阻塞赋值和非阻塞赋值如果使用不当会存在冒险和竞争现象,必须按照下面两条准则:
1)在描述组合逻辑的always块中使用阻塞赋值,则综合组合逻辑的电路结构。
2) 在描述时序逻辑的always块中使用非阻塞赋值,则综合时序逻辑的电路结构。
在时钟沿触发的always块中,如果用非阻塞赋值语句对reg型变量赋值;或者当reg型变量经过多次循环其值仍保持不变,则会在综合中生成触发器。若不想生成触发器,而希望用reg型变量生成组合逻辑,则应使用电平触发。在组合逻辑中,阻塞赋值只与电平有关,往往和触发沿没有关系,可以将其看成并行执行的;在时序逻辑中,非阻塞赋值是并行执行的;因此,优秀的HDL设计,其内部语句也是并行执行的。
(3)parameter与define的区别,顺便说一下localparam:
`define:作用 -> 常用于定义常量可以跨模块、跨文件;
范围 -> 整个工程;
`define 从编译器读到这条指令开始到编译结束都有效,或者遇到`undef命令使之失效。
parameter:作用 -> 常用于模块间参数传递;
范围 -> 本module内有效的定义;
localparam:作用 -> 常用于状态机的参数定义;
范围 -> 本module内有效的定义,不可用于参数传递;
(4)task与function的区别,顺便说一下generate:
function的定义:
function
行为语句;
endfunction
定义function时,要注意以下几点:
1):function定义结构不能出现在任意一个过程块(always块或者initial块)的内部;
2):function定义不能包括有任何时间控制语句,即任何用#,@或wait来标识的语句;
3):定义function时至少要有一个输入参量;
4):定义function时,在function内部隐式地将函数名声明成一个寄存器变量,在函数体中必须有一条赋值语句对该寄存器变量赋以函数的结果值,以便调用function时能够得到返回的函数值。如果没有指定的返回值的宽度,function将缺省返回1位二进制数。
5):function的调用既可以出现在过程块中又可以出现在assign连续赋值语句之中。
function的调用:
(,...,) ;
task的定义
task;
端口与类型说明;
变量声明;
语句1;
。。。
语句n;
endtask
在定义一个task时,必须注意以下几点:
1):任务定义结构不能出现在任何一个过程块内;
2):一个task可以没有输入/输出端口,当然也可以有;
3): 一个task可以没有返回值,也可以通过输出端口或双向端口返回一个或多个值;
4):除任务参数外,task还能够引用说明任务的模块中定义的任何变量;
5):task调用是过程性语句,因此只能出现在always过程块和initial过程块中,调用task的输入与输出参数必须是寄存器类型的。在task中还可以调用其他的task和function。
task的调用:
task调用语句给出传入任务的参数值和接收结果的变量值,
(端口1,端口2,... ,端口n);
task和function的不同点:
1): function只能与主模块共用一个仿真时间单位,而task可以定义自己的仿真时间单位;
2): function不能调用任务,而task可以调用函数也可以调任务;
3): function至少需要一个输入变量,而task可以没有或者有很多个任意类型的变量;
4): function返回一个值,而task则不返回值。
generate生成块:
生成语句可以动态的生成verilog代码,当对矢量中的多个位进行重复操作时,或者当进行多个模块的实例引用的重复操作时,或者根据参数的定义来确定程序中是否应该包含某段Verilog代码的时候,使用生成语句能大大简化程序的编写过程。
生成语句生成的实例范围,关键字generate-endgenerate用来指定该范围。生成实例可以是以下的一个或多个类型:
1)模块;2)用户定义原语;3)门级语句;4)连续赋值语句;5)initial和always块。
generate语句有generate-for,generate-if,generate-case三种语句。以generate-for为例:
1)必须有genvar关键字定义for语句的变量。
2)for语句的内容必须加begin和end(即使就一句)。
3)for语句必须有个名字。
module test(bin,gray);
parameter SIZE=8;
output [SIZE-1:0] bin;
input [SIZE-1:0] gray;
genvar i; //genvar i;也可以定义到generate语句里面 generate
for(i=0;i
begin:bit
assign bin[i]=^gray[SIZE-1:i];
end
endgenerate
endmodule
等同于下面语句
assign bin[0]=^gray[SIZE-1:0];
assign bin[1]=^gray[SIZE-1:1];
assign bin[2]=^gray[SIZE-1:2];
assign bin[3]=^gray[SIZE-1:3];
assign bin[4]=^gray[SIZE-1:4];
assign bin[5]=^gray[SIZE-1:5];
assign bin[6]=^gray[SIZE-1:6];
assign bin[7]=^gray[SIZE-1:7];
另外一个例子
generate
genvar i;
for(i=0;i
begin:shifter
always@(posedge clk)
shifter[i]<=(i==0)?din:shifter[i-1];
end
endgenerate
相当于
always@(posedge clk)
shifter[0]<=din;
always@(posedge clk)
shifter[1]<=shifter[0];
always@(posedge clk)
shifter[2]<=shifter[1];
.................
......................
always@(posedge clk)
shifter[SIZE]<=shifter[SIZE-1];
(5) 用verilog实现边沿检测电路:上升沿,下降沿,双沿(上升或下降沿)。
module edge_detect(clk,rst,signal,pos_edge,neg_edge,both_edge);
input clk;
input rst;
input signal;
output pos_edge;
output neg_edge;
output both_edge;
reg sig_r0,sig_r1;//状态寄存器 always @(posedge clk)
begin
if(rst)
begin
sig_r0 <= 1'b0;
sig_r1 <= 1'b0;
end
else
begin
sig_r0 <= signal;
sig_r1 <= sig_r0;
end
end
assign pos_edge = ~sig_r1 & sig_r0;
assign neg_edge = sig_r1 & ~sig_r0;
assign both_edge = sig_r0 ^ sig_r1;
endmodule
『文末说明』
本文作者:小强子
知乎ID:Power小强
永远年轻,永远热泪盈眶,永远充满希望,一个嗜好太多能力又太小的普通人。
本文介绍了Verilog HDL中的基本概念,包括reg与wire的区别、阻塞赋值与非阻塞赋值的使用场景、parameter与`define及localparam的对比,以及task和function的功能和调用方式。同时,文章通过示例解释了generate生成块的用法,并提供了边沿检测电路的实现。
501

被折叠的 条评论
为什么被折叠?



