文章目录
- 一、模块和设计风格
-
- 1. 模块
- 2. 描述设计的四种风格
-
- (1)数据流风格
- (2)行为风格
- (3)结构风格描述
- (4)混合设计风格的描述
- 二、各种模块的示例和testbench
-
- (1)一位加法器
- (2)16位计数器
- (3)3-8译码器
- (4)可复位寄存器
- (5)(带参数)二选一多选器
- (6)四选一多选器
- (7)寄存器堆示例(计算机组成原理)
- (8)ALU示例1(计算机组成原理)
- (9)ALU示例2(计算机组成原理)
一、模块和设计风格
1. 模块
- verilog中的基本描述单位是模块
- 模块的描述包括以下两个部分
- 某个设计的功能或结构
- 模块与其他外部模块的通信端口
- 描述模块的方法
- 使用开关原语、门级原语等,对设计的结构进行描述
- 使用连续赋值语句(assign)对设计的数据流进行描述
- 使用过程性结构(always、initial等)对设计的时序行为进行描述
- 在模块内可以引用另一个模块
2. 描述设计的四种风格
(1)数据流风格
- 使用数据流风格对设计进行建模的基本机制是采用连续赋值语句
assgin
- 语法:
assign [delay] LHS_net = RHS_expression;
- 右边表达式的使用的操作数无论何时发生变化,右值都将被重新计算,并在指定的延迟后赋予左边的线网类型变量。如果没有规定延时,默认延时为0
- 可以把这理解为一个连接的导线,导线右边任意时刻的变化都会传递到左边
(2)行为风格
- 设计的行为功能通过以下过程性结构进行描述
-
语句:此语句内仅在程序最初运行一次initial
-
语句:此语句总是在循环执行中always
-
- 所有的
和initial
语句都在0时刻并发执行always
- 只有变量类型能在这两种语句中被赋值。这种类型的变量数据在被赋值前保持原有值不变。
- 可以把这理解为一个时序部件,需要时钟信号控制
- 这种风格比较抽象
(3)结构风格描述
- 在verilog中,可以用以下四种构造对电路结构进行描述
- 内建门级原语(基元):在门级描述电路(如
、and
、xor
等内建基元门)or
- 开关级原语(基元):在晶体管级描述电路
- 用户定义的原语(基元):在门级描述电路
- 模块实例:创建层次结构描述电路
- 内建门级原语(基元):在门级描述电路(如
- 用线网可以连接各个基元和模块
- 这个层面上,有点像画电路图,最贴近底层
(4)混合设计风格的描述
- 在模块中,结构的构造和行为的构造可以自由混合
- 模块描述中可以包括
- 门示实例的引用 (触发always和initial)
- 模块实例的引用
- 连续赋值语句assign(触发always和initial)
- always语句、initial语句(驱动门和开关)
- 其他语句
二、各种模块的示例和testbench
(1)一位加法器
- 模块
module add1(a,b,cin,sum,cout);
input a,b,cin;
output sum,cout;
wire q = a&b;
wire g = a^b;
//assign {cout,sum} = a + b + cin;
assign sum = cin ^ g;
assign cout = cin & g | q;
endmodule
- testbench
module add1_tb;
reg a,b,cin;
wire sum,cout;
initial begin
a = 1'b0; b = 1'b0; cin = 1'b0; #100
a = 1'b0; b = 1'b0; cin = 1'b1; #100
a = 1'b0; b = 1'b1; cin = 1'b0; #100
a = 1'b0; b = 1'b1; cin = 1'b1; #100
a = 1'b1; b = 1'b0; cin = 1'b0; #100
a = 1'b1; b = 1'b0; cin = 1'b1; #100
a = 1'b1; b = 1'b1; cin = 1'b0; #100
a = 1'b1; b = 1'b1; cin = 1'b1;
end
add1 myAdd1(
.a(a),
.b(b),
.cin(cin),
.sum(sum),
.cout(cout)
);
endmodule
(2)16位计数器
- 模块
module counter16(clk,reset,d);
input clk,reset;
output reg[0:3] d;
always@(posedge clk)
if(reset)
d <= 0;
else
d <= d+1;
endmodule
- testbench
`timescale 1ns/1ns
module counter16_tb;
reg clk,rst;
wire[3:0] out;
integer i;
initial begin
i = 0;
clk = 1'b0;
rst = 1'b1;
#100
rst = 1'b0;
end
always begin
#10
clk = ~clk;
i = i+1;
if(i == 10) begin
rst = ~rst;
i = 0;
end
end
counter16 myCounter(clk,rst,out);
endmodule
(3)3-8译码器
- 模块
module dec3_8(a,y);
input [2:0] a;
output[7:0] y;
assign y[0] = (a==3'b000);
assign y[1] = (a==3'b001);
assign y[2] = (a==3'b010);
assign y[3] = (a==3'b011);
assign y[4] = (a==3'b100);
assign y[5] = (a==3'b101);
assign y[6] = (a==3'b110);
assign y[7] = (a==3'b111);
endmodule
/*-----------------其他实现方式------------------
1. 使用case语句
module dec3_8(a,y);
input [2:0] a;
output[7:0] y;
reg[7:0] yy;
assign y = yy; //yy在always块种被赋值,必须是reg型变量
always@(a) begin //电平触发,a变化就会触发执行
case(a)
3'b000: yy = 8'b0000_0001;
3'b001: yy = 8'b0000_0010;
3'b010: yy = 8'b0000_0100;
3'b011: yy = 8'b0000_1000;
3'b100: yy = 8'b0001_0000;
3'b101: yy = 8'b0010_0000;
3'b110: yy = 8'b0100_0000;
3'b111: yy = 8'b1000_0000;
endcase
end
endmodule
2. 行为抽象级别更高
module dec3_8(a,y);
input[2:0] a;
output[7:0] y;
assign y = 1<<a;
endmodule
*/
- testbench
module dec3_8_tb;
reg[2:0] s;
wire[7:0] y;
initial begin
s = 3'b0;
end
always #10 s=s+1;
dec3_8 dec(
.a(s),
.y(y)
);
endmodule
(4)可复位寄存器
- 模块
//可复位寄存器
module floper(clk,rst,d,q);
input clk,rst;
input[3:0] d;
output reg[3:0] q;
always @(posedge clk or negedge rst)
if(!rst)
q <= 4'b0;
else
q <= d;
endmodule
- testbench
module floper_tb;
reg clk,rst;
reg[3:0] d;
wire[3:0] q;
initial begin
clk = 1'b0;
rst = 1'b0;
d = 4'b1010;
#30 rst = 1'b1; //30ns后取消复位,这时正好clk上升沿,直接写入1010
end
initial #60 d = 4'b0101; //60ns后写入值改变,此时clk是下降沿不写,等到70ns时写入0101
always #10 clk = ~clk;
floper f(
.clk(clk),
.rst(rst),
.d(d),
.q(q)
);
endmodule
(5)(带参数)二选一多选器
- 模块
module mux2(a,b,s,y);
input s;
input [7:0]a,b;
output[7:0]y;
assign y = (s==0)?a:b;
endmodule
/*------------ 带位宽参数的写法------------------
module mux2_para #(parameter WIDTH=8)(a,b,s,y);
input s;
input[WIDTH-1:0] a,b;
output[WIDTH-1:0] y;
assign y = (s==0) ? a:b;
endmodule
*/
- testbench
`timescale 1ns/1ns
module mux2_tb;
reg clk;
reg[7:0] a,b;
wire[7:0] out;
initial begin
a = 8'b0;
b = 8'b1111_1111;
clk = 1'b0;
end
always #10 clk=~clk;
mux2 myMux2(
.a(a),
.b(b),
.s(clk),
.y(out)
);
endmodule
(6)四选一多选器
- 模块
module mux4(d0,d1,d2,d3,s,y);
input[3:0] d0,d1,d2,d3;
input[1:0] s;
output[3:0] y;
wire[3:0] low,high;
//先选出低位为s[0]的
mux2 lowmux(d0,d1,s[0],low);
mux2 highmux(d2,d3,s[0],high);
//再选出高位为s[1]的
mux2 finalmux(low,high,s[1],y);
endmodule
- testbench
module mux4_tb;
integer i;
reg[1:0] s;
reg[3:0] i0,i1,i2,i3;
wire[3:0] out;
initial begin
i0 = 4'b0;
i1 = 4'b0101;
i2 = 4'b1010;
i3 = 4'b1111;
i = 0;
s = 2'b0;
end
always begin
#10
i = i + 1;
if(i > 3)
i = 0;
s = i;
end
mux4 myMux4(
.d0(i0),
.d1(i1),
.d2(i2),
.d3(i3),
.s(s),
.y(out)
);
endmodule
(7)寄存器堆示例(计算机组成原理)
- 模块
module regFile(clk,reset,s,wEn,din,dout);
input clk,reset,wEn; //时钟,复位,写使能
input[1:0] s; //从regFile的四个单位中选择
input[7:0] din;
output[7:0] dout;
reg[7:0] R[0:3]; //regFile的存储空间,4个字节
assign dout = R[s]; //把R[s]连接到dout,内部信号传出来通常用assign,非时序电路,用=
always @(posedge clk or negedge reset)
begin
if(!reset) begin
R[0] <= 0; //给寄存器写值,这是时序电路,用<=
R[1] <= 1;
R[2] <= 2;
R[3] <= 3;
end
else if (wEn)
R[s] <= din;
end
endmodule
- testbench
module regFile_tb;
reg clk,rst,wEn;
reg[1:0] s;
reg[7:0] in;
wire[7:0] out;
initial begin
rst = 1'b0;
wEn = 1'b0;
clk = 1'b0;
s = 2'b0;
in = 8'b1111_1111;
end
always #10 clk = ~clk;
always @(posedge clk) begin
in = in+1;
s = in % 4;
//从第四个clk上升沿开始写入数据,由于读是非时序电路assign,写是时序电路<=,每次都是读出旧数据,写入新数据(下个周期读出)
if(in>=4) begin
rst = 1'b1;
wEn = 1'b1;
end
end
regFile rf(
.clk(clk),
.reset(rst),
.s(s),
.wEn(wEn),
.din(in),
.dout(out)
);
endmodule
(8)ALU示例1(计算机组成原理)
- 模块
module ALU_1(op,A,B,result);
input[2:0] op;
input[7:0] A,B;
output[7:0] result;
reg[7:0] result;
always @(op or A or B) begin
if(op == 3'b000) result = A+B;
else if(op == 3'b001) result = A-B;
else if(op == 3'b010) result = A&B;
else if(op == 3'b011) result = A|B;
else if(op == 3'b100) result = ~A;
else result = 8'b0;
end
endmodule
- testbench
module ALU_1_tb;
reg[7:0] A,B;
reg[2:0] op;
wire[7:0] res;
initial begin
A = 8'b1010_1010;
B = 8'b0101_0101;
op = 3'b0;
end
always #10 op = op+1;
ALU_1 alu(
.op(op),
.A(A),
.B(B),
.result(res)
);
endmodule
(9)ALU示例2(计算机组成原理)
- 模块
module ALU_2(op,A,B,ci,result,co);
input[2:0] op;
input[7:0] A,B;
input ci;
output[7:0] result;
output co;
reg[7:0] result;
reg co;
always @(op or A or B) begin
case(op)
3'd0: {co,result} = A + B;
3'd1: {co,result} = A + B + ci;
3'd2: {co,result} = A - B - ci;
3'd3: result = A & B;
3'd4: result = A | B;
3'd5: result = A ^ B;
3'd6: result = ~A;
default: begin
co = 0;
result = 8'd0;
end
endcase
end
endmodule
- testbench
module ALU_2_tb;
reg[7:0] A,B;
reg[2:0] op;
reg ci;
wire[7:0] res;
wire co;
initial begin
A = 8'b1010_1010;
B = 8'b0101_0101;
ci = 1;
op = 0;
end
always #10 op = op+1;
ALU_2 alu(
.op(op),
.A(A),
.B(B),
.ci(ci),
.result(res),
.co(co)
);
endmodule