赞
踩
例如:一个半加器的Verilog HDL程序
module halfadder(A, B, CO, S);
input A, B;
output S, CO;
wire S, CO;
assign S = A ^ B;
assign CO = A & B;
endmodule
一个模块的五个部分:
描述 | 举例 | 备注 |
---|---|---|
端口名的模块声明 | module halfadder(A, B, CO, S); | mudule和模块名是必须的,端口列表非必须 |
端口声明 | input A, B; output S, CO; | 端口声明非必须 |
数据类型声明 | wire S, CO; (如:线网型(wire)和寄存器型(reg)) | 数据类型声明非必须 |
模块功能说明 | assign S = A ^ B; assign CO = A & B; (如:assign语句,always语句,initial语句) | 模块功能说明非必须 |
模块结束声明 | endmodule | endmodule是必须的 |
例如:通过调用半加器模块和或门模块实现一位全加器
//半加器模块
module halfadder(
input A,
input B,
output S,
output CO
);
assign S = A ^ B;
assign CO = A & B;
endmodule
//或门模块
module and2(
input A,
input B,
output C
);
assign S = A | B;
endmodule
调用方法1:按端口顺序连接,低层模块定义声明时的端口顺序应与上层模块相应的连接端口顺序一致
//全加器模块
module fulladder(
input a,
input b,
input co_in,
output co_out,
output s
);
halfadder u1(a, b, co_temp1, s_temp);
halfadder u2(s_temp, co_in, co_temp2, s);
and2 u3(co_temp1, co_temp2, co_out);
endmodule
调用方法2:按端口名称连接,低层模块定义声明时的端口顺序可以与上层模块相应的连接端口顺序不一致
//全加器模块
module fulladder(
input a,
input b,
input co_in,
output co_out,
output s
);
halfadder u1(.A(a), .B(b), .CO(co_temp1), .S(s_temp);
halfadder u2(.A(s_temp), .B(co_in), .CO(co_temp2), .S(s);
and2 u3(.A(co_temp1), .B(co_temp2), .C(co_out));
endmodule
单行注释:以"//"开始
多行注释:以"/*“开始,以”*/"结束
数字的表达方式:<位宽>‘<进制><数值>
说明 | |
---|---|
位宽 | 用十进制数表示数字的位数,不写默认为机器默认位宽 |
进制 | b/B表示二进制,o/O表示八进制,d/D表示十进制,h/H表示十六进制,不写默认为十进制 |
数值 | 所选进制下的有效数字,当数值的位宽大于指定的位宽时,截去高位 |
4’b1000 //位宽为4的二进制数8
3'b1000 //位宽不够截去高位,相当于3'b000
注意:数字之间可用"_"进行分隔,如 8b’1100_1100
" "内的字符为字符串,字符串不能分多行书写
用于定义模块名、端口名、连线、信号名等。
标识符为字母,数字,$和_的组合,但标识符的首字符必须为字母或_,且不超过1024个
用于分隔标识符
Verilog语言内部已使用的词
wire型变量用于表示硬件单元的连接,常用于以assign为关键字的组合逻辑
格式:wire[width-1:0] 变量名1,变量名2……
说明:
①[width-1:0]表示位宽,不写默认为1
②wire的默认值为高阻值z
③模块的输入输出信号默认为wire型
//举例
wire a; //定义位宽为1的wire型变量a
wire[2:0] a; //定义位宽为3的wire型变量a,分别为a[0],a[1],a[2]
wire[2:1] a; //定义位宽为2的wire型变量a,分别为a[1],a[2]
寄存器位数据存储单元的抽象,寄存器中的数据可以保存直到被赋值语句赋予新的值。reg型变量只能在initial语句和always语句中被赋值
格式:reg[width-1:0] 变量名1,变量名2……
说明:
①[width-1:0]表示位宽,不写默认为1
②wire的默认值为不定值x
//举例
reg a; //定义位宽为1的reg型变量a
reg[2:0] a; //定义位宽为3的reg型变量a,分别为a[0],a[1],a[2]
reg[2:1] a; //定义位宽为2的reg型变量a,分别为a[1],a[2]
用parameter关键字可指定一个标识符来代表一个常量,常用于信号位宽定义,延迟时间定义
格式:paramter 标识符1 = 表达式1,标识符2 = 表达式2……
(表达式可以是常量也可以是之前定义过的标识符)
//举例
parameter width = 8; //定义一个常数参数
input[width-1:0] data; //定义位宽为8的输入信号
parameter a = 1, b = 2; //定义两个常数参数
parameter c = a + b; //表达式可以是前面定义过的标识符
算术运算符:加(+),减(-),乘(*),除(/),取模(%)
说明:若有一个操作数为不确定值x,算术运算的结果全为不定值x
a = 4'b0101, b = 4'b0010;
a + b //结果为4b'0111
a - b //结果为4b'0011
a * b //结果为4b'1010
a / b //结果为4b'0010,a除b的整数部分
a % b //结果为1, a除b的余数部分
逻辑运算符:与(&&),或(||),非(!)
说明:
①逻辑运算的结果为:逻辑假(0),逻辑真(1),不确定(x)三种情况下1位的值
②当操作数为具体数值时,若该数不为0,则视为1,否则视为0
③当操作数为z或x时,视为x
a = 2, b = 0;
a && b //等价于1 && 0,结果为0
a || b //等价于1 || 0,结果为1
!a //等价于!1,结果为0
按位运算符:取反(~),与(&),或(|),异或(^),同或( ~^)
说明:
①按位运算是指对操作数的每一位按位操作,若两个数的位宽不等,则将两个数右对齐并在较短的数前面补0后再进行运算
②按位运算产生的是与较长操作数等位宽的数值
③当操作数为z或x时,视为x
a = 4'b0011, b = 3'b010 //操作时将b看成4b'0010
~a //结果为4b'1100
a & b //结果为4b'0010
a | b //结果为4b'0011
a ^ b //结果为4b'0001
a ~^ b //结果为4b'0010
关系运算符:大于(>),小于(<),大于等于(>=),小于等于(<=)
说明:
①若表达式成立,结果为1,反之为0
②若操作数有一位为x,表达式结果为x
a = 4'b1010, b = 4'b0001
a > b //结果为1
a < b //结果为0
a >= b //结果为1
a <= b //结果为0
等式运算符:逻辑等(==),逻辑不等(!=),case等(===),case不等(!==)
说明:
①若两个数的位宽不等,则将两个数右对齐并在较短的数前面补0后再进行运算
②对逻辑等(==)与 逻辑不等(!=),若两个书中某一位为不确定值x,结果为x,若两个数相同,结果为1,反之为0
③对case等(===) 与 case不等(!==),若两个数完全相同(x所在的位置也相同),结果为1,反之为0
a = 4b'1010, b = 4b'1011, c = 4b'110x
a == b //结果为0
a != b //结果为1
a == c //结果为x
d = 4b'1011, e = 4b'101x
d === e //结果为0
d !== e //结果为1
缩减运算符:缩减与(&),缩减与非(~&),缩减或(|),缩减或非( ~|),缩减异或(^),缩减同或( ~^)
说明:
①缩减运算符对操作数从左向右操作
②缩减运算符的操作数只有一个,而按位运算符的操作数有两个
a = 4b'1010
&a //等价于1&0&1&0,结果为0
|a //等价于1|0|1|0,结果为1
^a //等价于1^0^1^0, 结果为0
移位运算符:右移(>>),左移(<<)
说明:移位运算符将操作数向右或向左移动指定的位数,空出的位置补0
a = 4b'1010
b = a>>1 //右移一位,结果为4b'0101
b = a<<2 //左移两位,结果为4b'1000
拼接运算符:{}
说明:拼接运算符可嵌套
{1'b1, 1'b0} //结果为2b'10
{1'b1, 3{1'b0}} //结果为4b'1000
格式:条件表达式? 表达式1 :表达式2
说明:
①判断时先计算条件表达式的内容
②若条件表达式为真,则计算表达式1的值;若条件表达式为假,则计算表达式2的值;若条件表达式的结果为不确定值x且表达式1和表达式2的值不相等,结果为不确定值x
assign c = a > b? a : b; //若a > b,输出a,反之,输出b
按位运算符 > 算术运算符 > 移位运算符 > 关系运算符 > 等价运算符 > 缩减 运算符> 逻辑运算符 > 条件运算符 > 拼接运算符
说明:可以用( )改变运算的次序
赋值语句中,赋值符号左边是赋值目标,右边是表达式,赋值语句分过程赋值与连续赋值
赋值对象:寄存器,实数,整数等,这些变量赋值后保持不变直到再次赋予新值
过程赋值常出现在initial和always语句中
过程赋值分阻塞赋值(=)和非阻塞赋值(<=)
过程赋值操作符:=(阻塞赋值)和(非阻塞赋值)
//举例1:阻塞赋值
always @(posedge clk) //当时钟上升沿来到时,触发always块执行
begin
a = b + 1; //先求b+1再赋给a
c = a; //将a赋给c,最终c=a=b+1
end
//举例2:非阻塞赋值
always @(posedge clk) //当时钟上升沿来到时,触发always块执行
begin
a <= b + 1; //时钟上升沿来到时,采样到a,b的值并计算b+1的值
c <= a; //always块结束前把b+1赋给a,把a(时钟上升沿采样的a)赋给c
end
阻塞赋值和非阻塞赋值的区别:
阻塞赋值 | 非阻塞赋值 |
---|---|
阻塞赋值时赋值语句是一条一条执行的,执行完一条语句就更新一次被赋值变量(赋值立即生效) | 非阻塞赋值时先计算所有赋值语句表达式右边的值,到该次仿真周期结束时再更新被赋值变量(赋值不立即生效) |
阻塞赋值语句执行期间不允许其他语句干扰 | 非阻塞赋值执行期间允许其他语句共同执行 |
连续赋值常用于数据流行为建模,常以assign为关键字
连续赋值操作符:=
//格式
assign 赋值目标线网 = 表达式;
//举例
assign c = a | b; //描述一个两输入的或门
assign {a, sum[3:0]} = a[3:0] + b[3:0] + c_in; //描述一个加法器
assign c = max(a, b); //调用求最大值函数
assign data_out = sel? a : b //可进行条件判断后再赋值
说明:
Ⅰ表达式左边只能是线网型变量
Ⅱ表达式右边可以是线网型变量,寄存器型变量,函数
过程赋值 | 连续赋值 |
---|---|
无关键字 | 关键字assign |
用"=","<="赋值 | 用"="赋值 |
只能出现在initial和always语句中 | 不能出现在initial和always语句中 |
用于驱动寄存器 | 用于驱动网线 |
语句按书写顺序执行,由begin-end标识,仿真开始时执行第一条语句,下一条语句的开始执行时间为上一条语句的结束执行时间
//顺序块格式
begin
执行语句1;
执行语句2;
……
end
//顺序块举例
begin
s = 0
#2 s = 1; //过了2秒,即第2秒s = 1
#2 s = 0; //过了2秒,即第4秒s = 0
#3 s = 1; //过了3秒,即第7秒s = 1
#1 s = 0; //过了1秒,即第8秒s = 0
end
语句是同时执行的,每一条语句都可以看成一个独立的进程,仿真开始时每条语句均开始执行
//并行块格式
fork
执行语句1;
执行语句2;
……
join
//顺序块举例
fork
s = 0
#2 s = 1; //第2秒s = 1
#4 s = 0; //第4秒s = 0
#7 s = 1; //第7秒s = 1
#8 s = 0; //第8秒s = 0
join
//格式——只有一条执行语句
initial
语句1;
//格式——有多条执行语句
initial
begin
语句1;
语句2;
……
end
说明:
Ⅰ一个模块中可以含有多个initial语句,所有的initial语句都同时从0时刻开始并行执行,且只执行一次
Ⅱinitial语句常用于测试文本中对信号的初始化,生成输入仿真波形,监测信号变化等
Ⅲinitial语句中也可以组合fork···join语句
//举例
reg[1:0] a, b;
reg c;
initial //多条执行语句
begin
a = 1, b = 0; //第0个单位时间,信号初始化不占用仿真时间
#10 begin a = 2; b = 3; end //10个单位时间后对a,b重新赋值
#10 begin a = 0; b = 2; end //20个单位时间后对a,b重新赋值
end
initial //一条执行语句
c = 1;
//格式
always @<触发事件> 语句或语句组
说明:
Ⅰalways的触发事件可以是控制信号变化,时钟边沿跳变等。always的触发控制信号可以是一个或多个,其间用or连接
//举例
always @(a or b or c or d) //当a,b,c,d四个信号的任意一个电平发生变化时,触发always块
//举例
always @(posedge clock or posedge reset) //当clock上升沿或reset上升沿时,触发always块
Ⅱalways的触发事件产生一次,always就执行一次,若触发事件不断产生,则always中的语句反复执行
//举例
reg q;
always @(posedge clock)
q <= d; //当时钟上升沿到来时,将d的值赋给q
Ⅲ一个模块中可以含有多个always语句,所有的always语句只要相应的触发事件产生了,对应的语句就会被执行,执行顺序与always语句的书写顺序无关
//格式
task 任务名;
<输入输出端口声明>
<任务中数据类型声明>
语句1;
语句2;
……
endtask
//格式
function<返回值的位宽, 类型说明> 函数名;
<输入端口与类型说明>
<局部变量说明>
begin
语句;
end
endfunction
//格式一 if (表达式) <语句> //格式二 if (表达式) <语句1> else <语句2> //格式三 if (表达式) <语句1> else if (表达式2) <语句2> …… else if (表达式n) <语句n> else <语句n + 1>
说明:
Ⅰif语句的表达式可以是逻辑表达式、关系表达式或操作数
Ⅱif语句可嵌套
//格式
case (控制表达式)
分支表达式1: 语句1;
分支表达式2: 语句2;
……
分支表达式n: 语句n;
default: 默认语句
endcase
说明:
Ⅰcase语句的各分支表达式语句必须不同
Ⅱ若控制表达式的值和case语句中分支表达式的值的位宽不同,在左边补0后再比较
Ⅲ若多个不同的状态有相同的执行语句,则用","将其分隔开
//格式
for (初始条件表达式; 循环终止条件; 改变循环控制变量的赋值语句)
//格式
while (循环终止条件)
//格式
forever 语句;
说明:
Ⅰforever将无限循环执行语句,直到遇到$finish 或$stop
Ⅱforever必须写在initial块中
//格式
repeat (表达式) 语句;
说明:
Ⅰ表达式确定了语句循环执行的次数,表达式通常为常数、变量或信号
Ⅱ若表达式为变量或信号,循环次数为循环开始时变量或信号的值
系统任务和系统函数前面都有标识符$,执行中若有返回值则无系统函数,若无返回值则为系统任务
系统任务和函数 | 功能 |
---|---|
$display | 模块信息的屏幕显示 |
$monitor | 信号的动态监控 |
$stop | 暂停仿真 |
$finish | 结束仿真 |
$readmemb | 读入二进制数据 |
$readmemh | 读入十六进制数据 |
$fopen | 文件打开 |
$fclose | 文件关闭 |
实验内容1:
编写Verilog 程序,验证3输入的与门、或门、与非门、或非门、异或门、同或门。
实验程序:
module gate( input in1, input in2, input in3, output out1, output out2, output out3, output out4, output out5, output out6 ); and(out1,in1,in2,in3); or(out2,in1,in2,in3); nand(out3,in1,in2,in3); nor(out4,in1,in2,in3); xor(out5,in1,in2,in3); xnor(out6,in1,in2,in3); endmodule
仿真程序:
module sim( ); reg[2:0] a=3'b000; wire[5:0] result; always #10 begin a=a+1'b1; end initial #100 $finish; gate mysim(.in1(a[0]), .in2(a[1]), .in3(a[2]), .out1(result[0]), .out2(result[1]), .out3(result[2]), .out4(result[3]), .out5(result[4]), .out6(result[5]) ); endmodule
实验内容2:
编写Verilog 程序,用与非门实现异或运算。
实验程序:
module gates(
input in1,
input in2,
output out1
);
wire temp1;
wire temp2;
wire temp3;
nand(temp1, in1, in2);
nand(temp2, in1, temp1);
nand(temp3, in2, temp1);
nand(out1, temp2, temp3);
endmodule
仿真程序:
module sim( ); reg[1:0] a = 3'b000; wire result; always #10 begin a = a + 1'b1; end initial #100 $finish; gates mysim(.in1(a[0]), .in2(a[1]), .out1(result) ); endmodule
实验内容:
用Verilog HDL语言基于数据流建模实现四选一的2位数据选择器。
实验程序:
module selector(
input [1:0] op,
input [1:0] d0,
input [1:0] d1,
input [1:0] d2,
input [1:0] d3,
output [1:0] res
);
assign res = (op[1] == 0? (op[0] == 0? d0: d1): (op[0] == 0? d2: d3));
endmodule
仿真程序:
module sim( ); reg[1:0] option = 2'b00; reg[1:0] data0 = 2'b00; reg[1:0] data1 = 2'b01; reg[1:0] data2 = 2'b10; reg[1:0] data3 = 2'b11; wire[1:0] result; always #10 begin option = option + 1'b1; end initial #40 $finish; selector mysim(.op(option), .d0(data0), .d1(data1), .d2(data2), .d3(data3), .res(result) ); endmodule
实验内容:
用Verilog HDL 语言实现74LS138的功能。
实验程序:
module decoder(
input [2:0] E,
input [2:0] A,
output [7:0] Y
);
wire EN = (~E[2]) & (~E[1]) & (E[0]);
assign Y[0] = ~((~A[2]) & (~A[1]) & (~A[0]) & EN);
assign Y[1] = ~((~A[2]) & (~A[1]) & A[0] & EN);
assign Y[2] = ~((~A[2]) & A[1] & (~A[0]) & EN);
assign Y[3] = ~((~A[2]) & A[1] & A[0] & EN);
assign Y[4] = ~(A[2] & (~A[1]) & (~A[0]) & EN);
assign Y[5] = ~(A[2] & (~A[1]) & A[0] & EN);
assign Y[6] = ~(A[2] & A[1] & (~A[0]) & EN);
assign Y[7] = ~(A[2] & A[1] & A[0] & EN);
endmodule
仿真程序:
module sim( ); reg[2:0] E = 3'b100; reg[2:0] A = 3'b000; wire[7:0] res; always #10 begin A = A + 1'b1; end initial begin #80 E = 3'b001; #80 $finish; end decoder mysim(.E(E), .A(A), .Y(res[7:0]) ); endmodule
实验内容:
编写Verilog 程序,使得在七段数码管中显示09,AF这16进制数。
实验程序:
module led( input [3:0] data, output an, output reg[6:0] out ); assign an = 1'b1; always @(*) case(data) 4'h0: out = 7'b1111110; 4'h1: out = 7'b0110000; 4'h2: out = 7'b1101101; 4'h3: out = 7'b1111001; 4'h4: out = 7'b0110011; 4'h5: out = 7'b1011011; 4'h6: out = 7'b1011111; 4'h7: out = 7'b1110000; 4'h8: out = 7'b1111111; 4'h9: out = 7'b1111011; 4'hA: out = 7'b1110111; 4'hB: out = 7'b0011111; 4'hC: out = 7'b1001110; 4'hD: out = 7'b0111101; 4'hE: out = 7'b1001111; 4'hF: out = 7'b1000111; default: out = 7'b1111110; endcase endmodule
仿真程序:
module sim( ); reg[3:0] simData = 4'h0; wire simAn; wire[6:0] simOut; always #10 begin simData = simData + 4'h1; end initial begin #160 $finish; end led mysim(.data(simData), .an(simAn), .out(simOut) ); endmodule
实验内容:
用Verilog HDL 实现1位的十六进制计数器,并用七段数码管来显示十六进制数0~F。
实验程序:
module seven( input [3:0] data, output an, output reg[6:0] out ); assign an = 1'b1; always @(*) case(data) 4'h0: out = 7'b1111110; 4'h1: out = 7'b0110000; 4'h2: out = 7'b1101101; 4'h3: out = 7'b1111001; 4'h4: out = 7'b0110011; 4'h5: out = 7'b1011011; 4'h6: out = 7'b1011111; 4'h7: out = 7'b1110000; 4'h8: out = 7'b1111111; 4'h9: out = 7'b1111011; 4'hA: out = 7'b1110111; 4'hB: out = 7'b0011111; 4'hC: out = 7'b1001110; 4'hD: out = 7'b0111101; 4'hE: out = 7'b1001111; 4'hF: out = 7'b1000111; default: out = 7'b1111110; endcase endmodule module counter( input clk, input clr, output reg[3:0] data ); always @(posedge clk) begin if (clr == 1'b1) data = 4'b0000; else data = data + 1'b1; end endmodule module div( input clk, output clk_3 ); reg[24:0] q = 25'b0; always @(posedge clk) begin q = q + 1'b1; end assign clk_3 = q[24]; endmodule module top( input clk, input clr, output an, output [6:0] out ); //仿真用 // wire[3:0] dt; // counter myc(.clk(clk), // .clr(clr), // .data(dt) // ); // seven mys(.data(dt), // .an(an), // .out(out) // ); //实例化用 wire clk_new; div myd(.clk(clk), .clk_3(clk_new) ); wire[3:0] dt; counter myc(.clk(clk_new), .clr(clr), .data(dt) ); seven mys(.data(dt), .an(an), .out(out) ); endmodule
仿真程序:
module sim( ); parameter clk_period = 10; reg clk; reg clr; wire an; wire[6:0] out; initial begin clk = 1'b0; forever #(clk_period / 2) clk = ~clk; end initial begin clr = 1'b1; #(clk_period / 2 + 1) clr = 1'b0; end top mysim(.clk(clk), .clr(clr), .an(an), .out(out) ); endmodule
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。