问题总结区
1、如何理解 module ..... endmodule ?
module 指模块是verilog 的一个基本单元,这个基本单元可以是描述一个简单的求和行为,也可以用来表达一个功能器件(逻辑门、反向器),对具体功能或部件的描述,类似与C语言中的函数;
module 模块中的描述方式有三种:数据流描述、行为描述、结构化描述
数据流描述:用assign语句进行描述
行为流描述:用always语句进行描述
结构化描述:实例化已经存在的模块,简单理解就是模块的调用;
如何理解 数据流描述 与 行为流描述?
初步理解:
数据流描述:只是简单的 数据流向,我把它称为简单语句,指赋值
行为流描述:较为复杂的过程语句,逻辑选择、判断、赋值的总过程
2、 如何理解 testbranch ?
仿真测试:就是给已经设计好的模块加入输入激励,从而观察结果是否与预期结果相同,起到一个测试作用。
TestBench文件中,包括了很多不可综合的Verilog代码,这些代码可以用于生成测试激励,并且用于检查待测模块是否满足了设计的要求。
3、如何理解 initial ?
initial 是一种 行为描述语言,可以把他理解为 初始化模块,主要在仿真测试中经常运用。从begin 开始后,一直到end,都是 给予变量 初始化。
4、如何理解 verilog ?
是一种硬件描述语言,他可以以文本的形式描述 硬件逻辑电路、逻辑功能;
语法加载处:
(1)Verilog 的 标识符 与 C语言 相同:
由 字母 下划线 数字 组成,不能以数字开头,不能是关键字。
(2)verilog 的 数据类型:
net 线网 用于器件的连接,通常为 wire 类型
reg 寄存器存储
paramester : 常数
一、硬件描述语言HDL
具有特殊结构能够对硬件逻辑电路的功能进行描述的一种高级编程语言。
HDL主要分为两种:Verilog 与 VHDL
Verilog HDL是硬件描述语言的一种,用于数字电子系统设计。
Verilog 用于描述:系统级、算法级、寄存器传输级(RTL级)、逻辑、门级、电路开关级的设计
设计理念:自顶向下;
MUX(多路选择器):可以任意选择一个行为。
寄存器级描述:用语言描述内部的部件,语法上的一种说法,如下所示:
module muxtwo(out, a, b, sl);
input a,b,sl;
output out;
reg out;
always @(sl or a or b)
if (!sl) out = a;
else out = b;
endmodule
二、基本逻辑门代码的设计
1、数据选择器
功能描述:
输入信号:in0 、in1均为8位二进制数; sel为地址信号(1位)
输出信号:out 为8位二进制数
输出、输入关系:当 sel = 0 时:out = in0; 当 sel = 1 时:out = in1;
原代码如下:
代码描述:
程序为模块结构,代码包含在module 与 endmodule 之间;
mux 为 模块名称,()小括号中包含的是 输出、输入端口的列表
parameter 代表参数; N = 8 表示数据的位数
output[N:1] out; 输出端口的描述,[ ] 括号内部填写 位数 由高到低
input : 输入端口的描述
assign :逻辑功能的描述
module mux(out, in0, in1, sel);
parameter N = 8;
output[N:1] out;
input[N:1] in0,in1;
input sel;
assign out = sel?in1:in0;
endmodule
2、反向器的设计
module 模块结构;inv 名称;A Y 输入、输出参数;assign 逻辑描述;
module inv(A, Y);
input A;
ouput Y;
assign Y = ~A;
endmodule
测试用例的编写:
`timescale 1ns/10ps
module inv_tb;
reg aa;
wire yy;
inv inv(.A(aa), .Y(yy));
initial begin
aa <= 0;
#10 aa <= 1;
#10 aa <= 0;
#10 aa <= 1;
#10 $stop;
end
endmodule
8位反向器的设计:(在反向器的基础上进行改动)
module inv(A, Y);
input[7:0] A;
ouput[7:0] Y;
assign Y = ~A;
endmodule
3、与非门的设计
module nand_gate(A, B, Y);
input A;
input B;
output Y;
assign Y = ~(A&B);
endmodule
测试用例的编写:
`timescale 1ns/10ps
module nand_gate_tb;
reg aa,bb;
wire yy;
nand_gate nand_gate(
.A(aa),
.B(bb),
.Y(yy)
);
initial begin
aa <= 0; bb <= 0;
#10 aa <= 0; bb <= 1;
#10 aa <= 1; bb <= 0;
#10 aa <= 1; bb <= 1;
#10 $stop;
end
endmodule
4位的与非门:
module nand_gate_4bits(A, B, Y);
input[3:0] A;
input[3:0] B;
output[3:0] Y;
assign Y = ~(A&B);
endmodule
测试用例的编写:
`timescale 1ns/10ps
module nand_gate_4bits_tb;
reg[3:0] aa,bb;
wire[3:0] yy;
nand_gate_4bits nand_gate_4bits(
.A(aa),
.B(bb),
.Y(yy)
);
initial begin
aa <= 4'b0000; bb <= 4'b1111;
#10 aa <= 4'b0110; bb <= 4'b1001;
#10 aa <= 4'b0111; bb <= 4'b1010;
#10 aa <= 4'b1100; bb <= 4'b1011;
#10 $stop;
end
endmodule
总结:
基本逻辑门代码结构
简单testbench编写
异名例化
$stop系统任务
Verilog位操作逻辑符号
assign组合逻辑赋值语句
用initial语句块 写testbench
多位宽电路符号图画法
三、组合逻辑代码设计(多路选择器逻辑设计)
1、二选一逻辑
代码逻辑功能:sel 为 1位:sel 为 1 时,a 和 b 异或; sel 为 0 时,a 和 b 与;
assign 语句实现 二选一 逻辑
module fn_sw(
a,
b,
sel,
y
);
input a;
input b;
input sel;
output y;
assign y = sel?(a^b):(a&b);
endmodule
always 语句实现 二选一逻辑
代码说明:
always 语句块,括号内部是 敏感变量,组合的逻辑输入
begin end 语句 开始 与 结束标志;
if else 语句;
always 语句块里 赋值的变量需要 是 reg 型,reg 型变量赋值用带箭头的等号。
reg为寄存器数据,wire为连线型数据。
在程序设计中,正确的使用wire和reg需要牢记以下几点:
1)连续赋值语句(assign)只能使用wire类型;
2)在过程语句块(initial,always)中,只能对reg数据进行赋值,赋值时需要加带箭头的等号。
3)结构化描述时,模块的输出信号只能使用wire,一般默认为 wire;
4)在定义模块的端口时,默认为wire类型,如果输出是reg,则需要重新定义;
reg y;
always@(a or b or sel)begin
if (sel == 1)begin
y <= a^b;
end
else begin
y <= a&b;
end
end
always 语句 与 assign 语句 一样 可以嵌入在 module 模块结构当中
module fn_sw(
a,
b,
sel,
y
);
input a;
input b;
input sel;
output y;
//assign y = sel?(a^b):(a&b);
reg y;
always@(a or b or sel)begin
if (sel == 1)begin
y <= a^b;
end
else begin
y <= a&b;
end
end
endmodule
逻辑设计加测试(仿真)分支的编写:
`timescale 1ns/10ps
module fn_sw(
a,
b,
sel,
y
);
input a;
input b;
input sel;
output y;
//assign y = sel?(a ^ b):(a & b);
reg y;
always@(a or b or sel)begin
if(sel == 1)begin
y <= a^b;
end
else begin
y <= a&b;
end
end
endmodule
---------testbranch---------
module fn_sw_tb;
reg a,b,sel;
wrie y;
fn_sw fn_sw(
.a(a),
.b(b),
.sel(sel),
.y(y)
);
initial begin
a <= 0;b <= 0; sel <= 0;
#10 a <= 0;b <= 0; sel <= 1;
#10 a <= 0;b <= 1; sel <= 0;
#10 a <= 0;b <= 1; sel <= 1;
#10 a <= 1;b <= 0; sel <= 0;
#10 a <= 1;b <= 0; sel <= 1;
#10 a <= 1;b <= 1; sel <= 0;
#10 a <= 1;b <= 1; sel <= 1;
#10 $shop;
end
endmodule
2、多路选择逻辑
代码功能描述:sel 为 2 位;sel 有四种 可能性(00,01,10,11);
当 sel 为 00 时,y 是 a 和 b 的与;
当 sel 为 01 时,y 是 a 和 b 的或;
当 sel 为 10 时,y 是 a 和 b 的异或;
当 sel 为 11 时,y 是 a 和 b 的同或;
代码设计和仿真:
`timescale 1ns/10ps
module fn_sw_4(
a,
b,
sel,
y
);
input a;
input b;
input[1:0] sel;
output y;
reg y;
always@(a or b or sel)begin
case(sel)
2'b00:begin y <= a&b; end
2'b01:begin y <= a|b; end
2'b10:begin y <= a^b; end
2'b11:begin y <= ~(a^b); end
endcase
end
endmodule
----testbench----
module fn_sw_4_tb;
reg[3:0] absel;
wrie y;
fn_sw_4 fn_sw_4(
.a(absel[0]),
.b(absel[1]),
.sel(absel[3:2]),
.y(y)
);
initial begin
absel <= 0;
#200 $shop;
end
always #10 absel <= absel + 1;
endmodule
总结:
用问号冒号语句实现二选一
always语句块写组合逻辑
用 if else 语句实现二选一
用case 语句实现多路选择
用 always # 语句遍历逻辑值
四、组合逻辑代码设计与仿真(补码转换和七段译码逻辑设计)
1、补码转换
功能描述:正数 补码 不变; 负数 符号位 不变,其他位数 按位取反 再加 1;
选择:二选一逻辑
module comp_conv(
a,
a_comp
);
input[7:0] a;
output[7:0] a_comp;
wire[6:0] b;
wire[7:0] y;
assign b = ~a[6:0];
assign y[6:0] = b + 1;
assign y[7] = a[7];
assign a_comp = a[7]?y:a; // a_comp = a[7]?{a[7],~a[6:0] + 1}:a; // {} 表示位拼接
endmodule
-----testtench-----
`timescale 1ns/10ps
module comp_conv_tb;
reg[7:0] a;
wire[7:0] a_comp;
comp_conv comp_conv(
.a(a),
.a_comp(a_comp)
);
initial begin
a <= 8'b00000000;
#3000 $stop
always #10 a <= a + 1;
end
endmodule
2、7段数码管译码器
module seg_dec(
num,
a_g
);
inpput[3:0] num;
output[6:0] a_g;
reg[6:0] a_g;
always@(num)begin
case(num)
4'b0000:begin a_g <= 7'b1111110;end
4'b0001:begin a_g <= 7'b0110000;end
4'b0010:begin a_g <= 7'b1101101;end
4'b0011:begin a_g <= 7'b1111001;end
4'b0100:begin a_g <= 7'b0110011;end
4'b0101:begin a_g <= 7'b1011011;end
4'b0110:begin a_g <= 7'b1011111;end
4'b0111:begin a_g <= 7'b1110000;end
4'b1000:begin a_g <= 7'b1111111;end
4'b1001:begin a_g <= 7'b1111011;end
default:begin a_g <= 7'b0000001;end //中杠表示其他情况
endcase
end
endmodule
总结:
补码转换器设计
七段译码代码设计
位拼接语句
case 的 default 处理
代码规整性训练
五、时序逻辑代码设计和仿真
时序逻辑电路:是由 组合逻辑 加 触发器 组成的。
1、计数器
功能描述;每次时钟信号到达后,y 就会加 1 起到 计数的作用。
module counter(
clk,
res,
y
);
input clk;
input res;
output[7:0] y;
reg[7:0] y;
wire[7:0] sum;
assign sum = y + 1;
always@(posedge clk or negedge res)begin
if(~res)begin
y <= 0;
end
else begin
y <= sum;
end
end
endmodule
----testbench----
`timescale 1ns/10ps
module counter_tb;
reg clk,res;
wire[7:0] y;
counter counter(
.clk(clk),
.res(res),
.y(y)
);
initial begin
clk <= 0;res <= 0;
#17 res <= 1;
#6000 $stop;
end
always #5 clk <=~clk;
endmodule
2、4级伪随机码发生器
伪随机码发生器:可以得到最大理论重复周期
功能描述:D0 与 D3 要模2加;高位 向 低位 右移 1 位,D3 给 D2,D2 给 D1,D1 给 D0;触发器 触发后 全为 1;
module m_gen(clk,res,y);
input clk;
input res;
output y;
reg[3:0] d; //定义4位寄存器
assign y = d[0]; //输出连接
always@(posedge clk or negedge res)
if(~res)begin
d <= 4'b1111;
end
else begin
d[2:0] <= d[3:1]; //右移一位
d[3] <= d[3] + d[0] //模2加
end
endmodule
----testbench---
module m_gen_tb;
reg clk;
reg res;
wire[7:0] y;
m_gen m_gen(
.clk(clk),
.res(res),
.y(y)
);
initial begin
clk <= 0;res <= 0;
#17 res <= 1;
#600 $stop;
end
always #5 clk <= ~clk;
endmodule
总结:
计数器设计
伪随机码发生器设计
触发器的敏感变量为时钟沿和复位沿
时序逻辑的代码结构
寄存器右移
模2加
testbench中的时钟和复位写法