基础内容
module 模块名(【端口列表】)
端口信号声明
信号数据类型有wire,reg
信号位宽
模块把输入的input转化为output
数据类型默认为wire,wire表电路间的连线
assign赋值目标必须是wire,始终激活,连续赋值语句
过程赋值语句,always赋值目标必须是reg型
激活条件由敏感信号条件表决定,当敏感条件满足时,过程块被激活
有两种,一种是边沿敏感,一种是电平敏感
边沿敏感,posedge 信号名,信号上升沿到来,negedge,信号下降沿到来
电平敏感,信号列表中任一个信号有变化(a,b,c)
上升沿0->1,
always@(posedge CLK)
Q=D;
当CLK上升时,把D赋值给Q,这样Q始终为D的MAX
always@(D)
Q=D
只要D发生改变,Q就被赋值为D,即Q与D始终相同
=为阻塞赋值,是计算完后立即赋值,对后续赋值结果产生影响
>=为非阻塞赋值,是全部计算完后再赋值,计算时用的都是老数据
设计组合电路时常用阻塞赋值
时序电路常用非阻塞赋值
一个always中只能用一种赋值方式
always中开始赋值时要用Begin,end包起来
内部信号声明类型
数据类型 位宽 信号类型 元素个数
reg [3:0] Q
output [3:0]Q1
门原语调用,输出在前,输入在后
input型信号必为wire
output可为reg或wire
<位宽>`<进制><数字>
2` b00
5` d8
o八进制b二进制h十六进制d十进制
有符号数为<sb>,第一位为符号位
8` sb1 011 1011
RST表复位信号
没有定义位数时,都是wire,即默认类型都是wire一位,0或1
只有定义为reg[],才会有多位数
{s1,s0}表把s1,s0两个一位的wire型,拼接为两位的二进制数
module pratice1(a,b,c,d,s1,s0,y);
input a,b,c,d,s1,s0;
output reg y;
always@(a,b,c,d,s1,s0,y)
begin
case({s1,s0})
2`b00 : y=a;
2`b01 : y=b;
2`b10 :y=c;
2`b11:y=d;
default:y=0;
endcase
end
endmodule
begin,end就像左右括号,if,else,等都要用
题
信号顺序调整
一个16位信号in包含四个四位数[3:0]a[3:0]b[3:0]c[3:0]d,将它们顺序倒置为dcba输出,输出out
`timescale 1ns/1ns
module top_module(
input [15:0]in,
output [15:0]out
);
wire[3:0]a,b,c,d;
assign {a,b,c,d}=in;
assign out={d,c,b,a};
endmodule
位运算与逻辑运算
1 2 3 4 5 6 7 8 9 10 11 12 |
|
与:& 按位与;&& 逻辑与;
或:| 按位或;|| 逻辑或;
非:~ 按位非;! 逻辑非;
#对信号按位操作#
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
信号级联合并
将6个输入信号串联转为四个信号输出,输入信号为[4:0] a[4:0] b[4:0]c [4:0]d [4:0]e [4:0]f,末尾增加一个宽度为两位的3,形成32位长度后,按照从前到后的顺序输出[7:0]w [7:0]x [7:0]y [7:0]z
`timescale 1ns/1ns
module top_module(
input [4:0] a, b, c, d, e, f,
output [7:0] w, x, y, z );
// wire [31:0]data;
assign {w,x,y,z}={a,b,c,d,e,f,2'b11};
endmodule
#信号反转输出#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
多位信号异或
给定五个1bit信号(a、b、c、d 和 e),生成两种25位的数据: 一种是将信号复制五次后连接起来aaaaabbbbb...,一种是将信号连接起来复制五次成为abcdeabcde... 。比较两个25位信号,如果两个信号的同样位置的位相等,则输出1。
`timescale 1ns/1ns
module top_module(
input a, b, c, d, e,
output [24:0] out
);
wire [24:0] data1,data2;
assign data1={{5{a}},{5{b}},{5{c}},{5{d}},{5{e}}};
assign data2={5{a,b,c,d,e}};
genvar i;
generate
for(i=0;i<=24;i=i+1)
begin:loop
assign out[i]=(data1[i]==data2[i])?1'b1:1'b0;
end
endgenerate
endmodule
制作一个四选一的多路选择器,要求输出定义上为线网类型
`timescale 1ns/1ns
module mux4_1(
input [1:0]d1,d2,d3,d0,
input [1:0]sel,
output[1:0]mux_out
);
//*************code***********//
reg[1:0]mux_out_tmp;
always@(*)begin
case(sel)
2'b00:mux_out_tmp=d3;
2'b01:mux_out_tmp=d2;
2'b10:mux_out_tmp=d1;
2'b11:mux_out_tmp=d0;
endcase
end
assign mux_out=mux_out_tmp;
//*************code***********//
endmodule
`timescale 1ns/1ns
module mux4_1(
input [1:0]d1,d2,d3,d0,
input [1:0]sel,
output[1:0]mux_out
);
assign mux_out = sel[0]?(sel[1]?d0:d2):(sel[1]?d1:d3);
endmodule
思考
always与assign
always里赋值的必须为reg型,而不能是wire型,需要满足条件才会触发,
相当于寄存器,如果不用reg,修改的值出always就被消了,只有reg才能记忆保存
assign里赋值的必须是wire型,始终触发并赋值
reg与wire
本质上为什么要有reg和wire,因为电路需要有记忆性的单元(比如FF)以及无记忆性的单元(比如导线)。
reg a ;
reg b;
always@ (*) begin
if (<condition>)
a = tmp ;
b = a ;
end
在这段代码中a是不会综合出寄存器的,而b却会,原因在于a是中间变量,就是一条线,所以综合出来的不是寄存器,而是一条线。而在condtion不满足的情况下b要保值,所以b会综合成寄存器。绝不是 综合工具对代码描述的硬件行为的一种解读。
Verilog作为一门行为描述语言,你可以把每一段程序都理解为“用语言描述一个模块”,而实际上,一个模块的组成无非就是“引脚”+“内部的各种电路”。
有个这种准备之后就好理解wire 和 reg 了。Verilog中,把没有定义类型的信号默认设置为wire,除非特殊声明一次。如:
input[7:0] a, b;
reg [7:0] a;
以上例子,由于“input”就是模块管脚的声明,他有两个管脚a,b,用作输入。而再把a定义成寄存器类型,方便功能定义中进行操作。所以此时a 为reg型 b 为 默认wire 型。
所以说,可以粗略地把wire型理解为“从管脚输入的、原始的信号序列”,他在导体上以电信号的形式流动,而把reg型理解为“用寄存器存着从管脚输入的信号”,因为之后在寄存器中存住的数字信息,才能进行诸如移位、赋值等操作。
很经典的解读:
wire表示直通,即输入有变化,输出马上无条件地反映(如与、非门的简单连接)。
reg表示一定要有触发,输出才会反映输入的状态。
reg相当于存储单元,wire相当于物理连线。reg表示一定要有触发,没有输入的时候可以保持原来的值,但不直接实际的硬件电路对应。
两者的区别是:寄存器型数据保持最后一次的赋值,而线型数据需要持续的驱动。wire使用在连续赋值语句中,而reg使用在过程赋值语句(initial ,always)中。wire若无驱动器连接,其值为z,reg默认初始值为不定值 x 。
在连续赋值语句中,表达式右侧的计算结果可以立即更新表达式的左侧。在理解上,相当于一个逻辑之后直接连了一条线,这个逻辑对应于表达式的右侧,而这条线就对应于wire。在过程赋值语句中,表达式右侧的计算结果在某种条件的触发下放到一个变量当中,而这个变量可以声明成reg类型的。根据触发条件的不同,过程赋值语句可以建模不同的硬件结构:如果这个条件是时钟的上升沿或下降沿,那么这个硬件模型就是一个触发器;如果这个条件是某一信号的高电平或低电平,那么这个硬件模型就是一个锁存器;如果这个条件是赋值语句右侧任意操作数的变化,那么这个硬件模型就是一个组合逻辑。
对组合逻辑输出变量,可以直接用assign。即如果不指定为reg类型,那么就默认为1位wire类型,故无需指定1位wire类型的变量。当然专门指定出wire类型,可能是多位或为使程序易读。wire只能被assign连续赋值,reg只能在initial和always中赋值。
输入端口可以由wire/reg驱动,但输入端口只能是wire;输出端口可以是wire/reg类型,输出端口只能驱动wire;若输出端口在过程块中赋值则为reg型,若在过程块外赋值则为net型(wire/tri)。用关键词inout声明一个双向端口, inout端口不能声明为reg类型,只能是wire类型。
默认信号是wire类型,reg类型要申明。这里所说的默认是指输出信号申明成output时为wire。如果是模块内部信号,必须申明成wire或者reg.
对于always语句而言,赋值要申明成reg,连续赋值assign的时候要用wire。
模块调用时 信号类型确定方法总结如下:
•信号可以分为端口信号和内部信号。出现在端口列表中的信号是端口信号,其它的信号为内部信号。
•对于端口信号,输入端口只能是net类型。输出端口可以是net类型,也可以是register类型。若输出端口在过程块中赋值则为register类型;若在过程块外赋值(包括实例化语句),则为net类型。
•内部信号类型与输出端口相同,可以是net或register类型。判断方法也与输出端口相同。若在过程块中赋值,则为register类型;若在过程块外赋值,则为net类型。
•若信号既需要在过程块中赋值,又需要在过程块外赋值。这种情况是有可能出现的,如决断信号。这时需要一个中间信号转换。
下面所列是常出的错误及相应的错误信息(error message)
•用过程语句给一个net类型的或忘记声明类型的信号赋值。
信息:illegal …… assignment.
•将实例的输出连接到声明为register类型的信号上。
信息:<name> has illegal output port specification.
•将模块的输入信号声明为register类型。
信息:incompatible declaration, <signal name>
……