示例引入
//行为级HDL代码
// 整数乘法器
always @ (posedge multiply)
begin
product <= a*b // 非阻塞赋值 相当于等号
end
//RTL级HDL代码
input [4:1] a,b; //位宽是4,位权从4到1 输入变量
output[4:1] count; // 输出变量
output[16:1] product;
reg[4:1] count; // 寄存器变量
reg[4:1] product;
always @ (posedge clk)
begin
if (multiply_en == 1) begin
count <= 4;
product <= 0;
end
if (count) begin
if(b[count]) begin // b[count] == 1
product <= (product << 1) + a;
end
else begin // b[count] == 0
product <= product << 1;
end
count <= count - 1;
end
end
// 门电路级HDL代码
module UnsignedMultiply(clk,a,b,multiply_en,product);
input clk,multiply_en;
input[3:0] a,b;
output[7:0] product;
wire clk;
wire un1_count5_axb_1;
......
endmodule
模块的基本概念
选择器
实现上述元件(二选一数据选择器)的功能:
module mux2(out,a,b,sel); // module name(端口列表) 注意分号
input a,b,sel; // 输入
output out; // 输出
reg out; // 寄存器类型 always被赋值的对象需要为寄存器类型
always @(a or b or sel) // 括号里为信号,在接收到这些信号时,进行之后的操作
if (!sel) out = a;
else out = b;
endmodule
由与门和非门和或门组成,sel=1,则nsel=0,此时选择b,sel=1,则nsel=1,此时选择a。
// `timescale 1ns/1ns 声明仿真时间单位
module mux2(out,a,b,sel);
input a,b,sel;
output out;
wire nsel.sela.selb; //wire是连线型变量,为中间变量
//not,and,or 为元语,表示非门、与门、或门
//端口参数列表为:第一个是输出,其余全是输入
not u1(nsel,sel); // not是一个声明,变成具体的电路还需要名称
// not #1 u1(nsel,sel) 在进入门之前有一个仿真时间单位的延迟
and u2(sela,a,nsel);
and u3(selb,b,sel);
or u4(out,sela,selb);
endmodule
全加器
行为级描述
module adder4(cout,sum,a,b,cin)
input [3:0] a,b; //[3:0]是位宽表达式
input cin;
output cout;
output [3:0] sum;
assign {cout,sum} = a+b+cin
endmodule
4位全加器,根据两个4bit的加数a、b和进位输入cin计算sum和进位输出cout。
assign属于连续赋值语句,属于行为描述方式。
assign的变量需要声明位连线型(wire),一般而言,不给output额外声明reg形式,它就是连线型。
值得一提的是,全部程序是位于module和endmodule之间的。
比较器
module compare2(equal,a,b);
input[1:0] a,b;
output equal;
assign equal = (a==b) ?1:0; //条件运算符
endmodule
Verilog和C语言的注释方式是一致的。
如果要将条件运算符表达式表示用if-else
表示需要使用always @ ()
,因为Verilog中不允许表达式语句独立存在,除了条件表达式。
always
语句中的变量需要声明位reg类型。
三态缓冲器
通过调用Verilog语言提供的原语库中三态驱动器元件bufif1实现,也可以自定义模块使用。
module trist2(sout,sin,ena);
input sin,ena;
output sout;
mytri tri_inst(.out(sout),.in(sin),.en(ena));
// .表示被引用模块的端口,名称必须一致,()表示本模块中与之连接的端口
// 也可以直接写本模块中的模块,但需要顺序对上,不建议使用
endmodule
module mytri(out,in,en);
input in,en;
output out;
assign out = en ? in : 1'bz; // 1‘bz表示1bit的数值
endmodule
综上所述,实现功能的方式有三种:
- 元语言;
- always;
- 过程块;
模块的测试
`include "mux2.v" //Verilog HDL默认扩展名 .v
module tstmux; //没有端口参数列表,这是测试向量的名称
reg ain,bin,sel_in;
reg clock;
wire outw; // 就是一根线,将输出信号引到被测模块
// 寄存器中的初始值是不确定的,因此要赋值
initial // 专门用来做变量初始化
begin
ain = 0;
bin = 0;
sel_in = 0;
clock = 0;
end
// 产生一个不断重复的,周期为100个单位时间的时钟信号
always #50 clock = ~clock;
always @ (posedge clock)
begin
ain = {$random} % 2; //-32768~32767 随机数函数 ,目的是防止人为规定模式,导致测试覆盖范围不全
#3 bin = $random % 2; // 取余
end
always @(posedge clock)
begin
ain = {$random} % 2;
#3 bin = $random % 2;
end
always #10000 sel_in = ! sel_in;
// 将上述信号传递给编辑的module
mux2 m(.out(outw),.a(ain),.b(bin),.sel(sel_in));
endmodule