赞
踩
always模块有两种:
组合:always@(*)
时序:always@(posedge clk)
组合逻辑always模块和assign模块赋值是等价的,使用哪一种完全看哪一种更为方便。always模块内可有更丰富的状态,如if-then,case等,但是不能有连续赋值语句assign。
assign赋值语句的左边一般为wire型,always赋值语句的左边一般为reg型。
always是一个过程块,后面通常接@再接敏感列表。时序逻辑,一般写作always@(posedge clk)也就是上升沿敏感,每次时钟上升沿的时候开始执行always当中的过程块,当然并不是都是时钟敏感的,比如说组合逻辑电路通常可以写成always@(**),只要always块中的任意变量发生变化都会触发always块。
题目:使用赋值语句assign和组合always块两种方式构建与门。
答案:
module top_module(a, b, out_assign, out_alwaysblock);
input a,
input b,
output wire out_assign,
output reg out_alwaysblock
assign out_assign = a & b;//连续赋值语句
always @(*) begin //组合always赋值
out_alwaysblock = a & b;
end
endmodule
时序always块可像组合always块那样生成电路,同时也会生成一系列的触发器,寄存器等,因为输出要等到下个时钟延才能输出。
阻塞赋值 与 非阻塞赋值
verilog中有三种赋值方式:
1)连续赋值(assign x=y;),只能在always块外使用。
2)阻塞赋值(x=y;),只能在always块内使用。
3)非阻塞赋值(x<=y;)只能在always块内使用。
在组合逻辑的always块中(always @(*))使用阻塞赋值语句;在时序逻辑的always块中(always @(posedge clk))使用非阻塞赋值语句。
题目:使用赋值语句、组合always块和时序always块三种方式构建一个异或门。
答案:
module top_module(clk, a, b, out_assign, out_always_comb,out_always_ff);
input clk,
input a,
input b,
output wire out_assign,
output reg out_always_comb,
output reg out_always_ff
assign out_assign = a ^ b; //连续赋值语句
always @(*) begin //组合always赋值语句
out_always_comb = a ^ b;
end
always @(posedge clk) begin //时序always赋值语句
out_always_ff <= a ^ b;
end
endmodule
一个if语句会产生一个2选1的数据选择器,需注意的是,并不是if选择的那路数据才被实现成电路模式,而是if和else两路都被实现为电路形式然后用选择器选择输出。
条件(if)语句用于控制执行语句要根据条件判断来确定是否执行。
条件语句用关键字 if 和 else 来声明,条件表达式必须在圆括号中。
if语句使用结构如下:
if(congdition) ture_statement;
else if (congdition1) ture_statement1;
else if (congdition2) ture_statement2;
else default_statement;
if 语句执行时,如果 condition为真,则执行true_statement ;如果 condition1为假,condition2为真,则执行 true_statement2;依次类推。
else if 与 else 结构可以省略,即可以只有一个 if 条件判断和一组执行语句 ture_statement1 就可以构成一个执行过程。
else if 可以叠加多个,不仅限于 1 或 2 个。
ture_statement等执行语句可以是一条语句,也可以是多条。如果是多条执行语句,则需要用 begin 与 end 关键字进行说明。
if的两种方式:
always @(*) begin
if (condition) begin
out = x;
end
else begin
out = y;
end
end
assign out = (condition) ? x : y;
锁存器(Latch)是数字逻辑电路中很重要的一种基本电路,常见的锁存器包括三个端口:数据输入口、数据输出口、使能端。当使能端为高电平时,输入口的数据直接送到输出口,此时输入输出口可以看成是直接连通的;当使能端为低电平时,输出口的数据保持之前的数据不变,无论输入口的数据怎么变化,输出都保持不变,就是把原来的状态锁存下来了(所以才叫锁存器)。锁存器与触发器的区别在于:锁存器是电平触发,而触发器是边沿触发。锁存器在不锁存数据时,输出随输入变化;但一旦数据锁存时,输入对输出不产生任何影响。
在FPGA电路设计中,不规范的描述语言可能会产生意想不到的锁存器,而设计者往往并没有注意到自己的设计会被综合出锁存器,导致综合出的电路出现逻辑错误。
产生锁存器的情况一般有两种:
1)if…else…结构中缺少else
2)case结构中的分支没有包含所有情况并且没有default语句
所以当我们使用if语句或者case语句时,我们必须考虑到所有情况并给对应情况的输出进行赋值,就意味着我们要为else或者default中的输出赋值。
在Verilog中,case语句与if-elseif-else相近,与c语言中的switch差别较大,如下:
always @(*) begin // This is a combinational circuit
case (in)
1'b1: begin
out = 1'b1; // begin-end if statement >1
end
1'b0: out = 1'b0;
default: out = 1'bx;
endcase
end
1)case语句以case开始,每个case的选项以分号结束,没有开关。
2)每个case项只能执行一条语句。这使得在C中使用的“break”是不必要的。但这意味着如果你需要不止一个语句,你必须使用begin…end。
3)允许重复(部分重叠)案例项目。使用第一个匹配的。C不允许重复的case项。
题目:6选1选择器
答案:
module top_module (sel, data0, data1, data2, data3, data4, data5, out); input [2:0] sel, input [3:0] data0, input [3:0] data1, input [3:0] data2, input [3:0] data3, input [3:0] data4, input [3:0] data5, output reg [3:0] out always@(*) begin // This is a combinational circuit case(sel) 3'b000:out=data0; 3'b001:out=data1; 3'b010:out=data2; 3'b011:out=data3; 3'b100:out=data4; 3'b101:out=data5; default:out=3'b0; endcase end endmodule
允许同时在几个输入端有输入信号,编码器按输入信号排定的优先顺序,只对同时输入的几个信号中优先权最高的一个进行编码。
题目:构建一个4位优先级编码器。 对于此问题,如果所有输入位都不为高(即输入为零),则输出零。 请注意,一个4位数字具有16种可能的组合。
答案:按照常规思路,可以列出每一种情况,分别进行赋值,该方法对于位数不多的编码器而言,可行。但对于多位的编码器不适合,代码太多且繁琐。
因为case允许重复(部分重叠)案例项目。使用第一个匹配的。 从低位到高位去比较in中是否有数据位为1,但只执行首次匹配的操作。很大程度上简化了代码。
module top_module (
input [3:0] in,
output reg [1:0] pos );
always @(*) begin
case(1)
in[0]:pos = 0;
in[1]:pos = 1;
in[2]:pos = 2;
in[3]:pos = 3;
default:pos = 0;
endcase
end
endmodule
casez可以减少需要比较的案例项目,在比较中,将值z的位看作无关位。进一步简化了代码。
例子,实现上一练习中的4输入优先级编码器:
always @(*) begin
casez (in[3:0])
4'bzzz1: out = 0; // in[3:1] can be anything
4'bzz1z: out = 1;
4'bz1zz: out = 2;
4'b1zzz: out = 3;
default: out = 0;
endcase
end
case语句的行为就好像每个项都是按顺序检查的(实际上,它更像是生成一个巨大的真值表,然后生成gates)。注意如何有某些输入(例如,4’b1111)将匹配多个case项。选择第一个匹配项(因此4’b1111匹配第一个项,out=0,但不匹配后面的任何项)。
还有一个类似的casex,它将x和z都视为不在乎。我觉得用它来代替凯斯没什么用。
数字?是z的同义词。所以2’bz0和2’b?0是一样的。
为避免产生锁存,必须在所有可能的条件下为所有输出分配一个值。仅仅有一个默认案例是不够的。在所有四种情况和默认情况下,必须为所有四个输出指定一个值。这可能涉及许多不必要的输入。解决这个问题的一个简单方法是在case语句之前为输出分配一个“默认值”:
always @(*) begin
up = 1'b0; down = 1'b0; left = 1'b0; right = 1'b0;
case (scancode)
... // Set to 1 as necessary.
endcase
end
这种类型的代码确保在所有可能的情况下为输出赋值(0),除非case语句重写赋值。这也意味着default:case项变得不必要。
以上是HDLBits Verilog语言刷题网站中的Procedures部分,后续部分会继续更新。对一些基础性知识点进行归纳总结,有错误请指正。仅供学习参考,谢谢!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。