一、注意:
1,大多数FPGA内部的触发器数目相当多,又加上独热码状态机的译码逻辑最为简单,所以在设计采用FPGA实现的状态机时,往往采用独热码状态机(即每个状态只有一个寄存器置位的状态机)。
2,设计异步状态机。如果设计要求必须有不同的时钟触发的状态机,采用方法:
(1)编写另一个模块,在那个模块内使用另一个时钟;
(2)用实例引用的方法在另一个模块中把它们连接起来。
(3)为使设计比较简单、调试比较容易,应该尽量使这两个状态机的时钟有一定的关系;例如甲模块的时钟是乙模块时钟同步计数器的输出。
3,千万不要使用综合工具来设计异步状态机。因为目前大多数综合工具在对异步状态机进行逻辑优化时会胡乱地简化逻辑,使综合后的异步状态机不能正常工作。
二、综合的一般原则:
1,综合之前一定要进行仿真,这是因为仿真会暴露逻辑错误。如果不做仿真,没有发现的逻辑错误会进入综合器,使综合的结果产生同样的逻辑错误。
2,每一次布局布线之后都要进行仿真,在器件编程或流片之前要做最后的仿真。
3,用Verilog HDL描述的异步状态机是不能综合的,因此应该避免用综合器来设计;如果一定要设计异步状态机,则可用电路图输入的方法来设计。
4,如果要为电平敏感的锁存器建模,使用连续赋值语句是最简单的方法。
三、语言指导原则:
1,always块:
(1)每个always块只能有一个事件控制“@(event-expression)”,而且要紧跟在always关键字的后面。
(2)always块可以表示时序逻辑或者组合逻辑;但不建议使用always块既表示电平敏感的透明锁存器又同时表示组合逻辑,虽然always块这样使用不会有语法错误,但是这容易产生电路功能的错误和多余电平敏感的透明锁存器。
(3)带有posedge或negedge
关键字的事件表达式表示沿触发的时序逻辑,没有posedge或negedge关键字的表示组合逻辑或电平敏感的锁存器,或者两种都表示。
(4)每个表示时序always块只能由一个时钟跳变沿触发,置位或复位最好也由该时钟跳变沿触发。
(5)每个在always块中赋值的信号都必须定义成reg型或整型。
整型变量默认是32位,使用Verilog操作符可对其进行二进制求补的算术运算。综合器还支持整型变量的范围说明,这样就允许产生不是32位的整型量。句法结构是:integer [ : ]。
(6)always块中应该避免组合反馈回路。
每次执行always块时,在生成组合逻辑的always块中赋值的所有信号必须有明确的值;否则,需要设计者在设计中加入电平敏感的锁存器来保持赋值前的最后一个值。只有这样,综合器才能正常生成电路,如果不这样做,综合器会发出警告,提示设计中插入了锁存器。如果在设计中存在综合器认为不是电平敏感锁存器的组合回路时,综合器会发出错误信息(例如在设计异步状态机时)。
四、对上述原则的简化解释:
用always块设计纯组合逻辑电路时,在生成组合逻辑的always块中参与赋值的所有信号都必须有明确的值,即在赋值表达式右端参与赋值的信号都必须在
always@(敏感电平列表)
中列出。如果赋值表达式右端参与赋值的信号没在always@(敏感电平列表)
中列出,这就会形成一个透明的锁存器。但是,该信号的变化不会立刻显现出来,而必须等到敏感电平列表中某个信号变化时,它的作用才会显现出来,也就是相当于存在着一个透明锁存器,即把该信号的变化暂存起来,待敏感电平列表中某个信号变化再起作用,纯组合逻辑不可能做到这一点。
这样,综合后所得的电路已经不是纯组合逻辑电路了,这时综合器又会发出警告,提示设计中插入了锁存器。
// 1,8位带进位端的加法器的设计实例
module adder_8(
input [7:0] a,
input [7:0] b,
input cin,
output [7:0] sum,
output cout
);
assign {cout, sum} = a + b +cin;
endmodule
// 测试信号, tb
module tb_adder_8;
reg [7:0] a;
reg [7:0] b;
reg cin;
wire [7:0] sum;
wire cout;
adder_8 u1_adder_8(
.a (a ),
.b (b ),
.cin (cin ),
.sum (sum ),
.cout (cout )
);
initial begin
#60;
a = 8'b0000_1111;
b = 8'b0001_0000;
cin = 1'b1;
#100
a = 8'b1010_1010;
b = 8'b0101_0101;
cin = 1'b1;
end
endmodule
// 2,指令译码电路的设计实例(利用电平敏感的always块来设计组合逻辑)
// 操作码的定义
`define plus 3'd0
`define minus 3'd1
`define band 3'd2
`define bor 3'd3
`define unegate 3'd4
module alu(
input [7:0] a,
input [7:0] b,
input [2:0] opcode,
output reg [7:0] out
);
// 用电平敏感的always块描述组合逻辑
always@(opcode or a or b) begin
case(opcode)
// 算术运算
`plus: out = a + b;
`minus: out = a - b;
// 位运算
`band: out = a & b;
`bor: out = a | b;
// 单目运算
`unegate: out = ~a;
default: out = 8'hx;
endcase
end
endmodule
// 2,
module tb_alu;
reg [7:0] a;
reg [7:0] b;
reg [2:0] opcode;
wire [7:0] out;
initial begin
#60;
opcode = 3'd0;
#10;
opcode = 3'd1;
#10;
opcode = 3'd2;
#20;
#10;
opcode = 3'd3;
#10;
opcode = 3'd4;
#10;
opcode = 3'd6;
end
initial begin
#60;
a = 8'b0000_1111;
b = 8'b0001_0000;
#40;
a = 8'b1010_1010;
b = 8'b0101_0101;
#100
$stop;
end
alu u1_alu(
.a (a ),
.b (b ),
.opcode (opcode ),
.out (out )
);
endmodule
// 3,利用 task 和电平敏感的 always 块设计经比较后重组信号的组合逻辑
module sort4(ra, rb, rc, rd, a, b, c, d);
parameter t = 3;
input [3:0] a;
input [3:0] b;
input [3:0] c;
input [3:0] d;
output reg [t:0] ra;
output reg [t:0] rb;
output reg [t:0] rc;
output reg [t:0] rd;
always@(a or b or c or d) begin:local
// 用电平敏感的 always 块描述组合逻辑
reg [t:0] va, vb, vc, vd;
{va, vb, vc, vd} = {a, b, c, d};
sort2(va, vc); // 实现交换,第一个是小指,第二个是大值。
sort2(vb, vd); // 实现交换,第一个是小指,第二个是大值。
sort2(va, vb); // 实现交换,第一个是小指,第二个是大值。
sort2(vc, vd); // 实现交换,第一个是小指,第二个是大值。
sort2(vb, vc); // 实现交换,第一个是小指,第二个是大值。
{ra, rb, rc, rd} = {va, vb, vc, vd}; // 按照:从小到大 输出。
end
task sort2; // 调用任务,如果满足条件,
inout [t:0] x, y; // 则按照最终得到,小、大值,返回!
reg [t:0] tmp;
if(x > y) begin
tmp = x;
x = y;
y = tmp;
end
endtask
endmodule
// 3,
module tb_sort4;
parameter t = 3;
reg [3:0] a;
reg [3:0] b;
reg [3:0] c;
reg [3:0] d;
wire [t:0] ra;
wire [t:0] rb;
wire [t:0] rc;
wire [t:0] rd;
initial begin
a = 0; b = 0; c = 0; d = 0;
repeat(5) begin
#100
a = {$random} % 15;
b = {$random} % 15;
c = {$random} % 15;
d = {$random} % 15;
end
#700;
$stop;
end
sort4 u1_sort4(
.a (a ),
.b (b ),
.c (c ),
.d (d ),
.ra (ra ),
.rb (rb ),
.rc (rc ),
.rd (rd )
);
endmodule
// 4,比较器的设计实例(利用赋值语句设计组合逻辑)
module compare(equal, a, b);
parameter size = 4;
input [size-1 : 0] a, b;
output equal;
assign equal = (a == b) ? 1 : 0;
endmodule
// 4
// 测试信号
module tb_compare;
parameter size = 4;
reg [size-1:0] a, b;
wire equal;
initial begin
a = 4'd0; b = 4'd0;
repeat(5) begin
#20;
a = {$random} % 15;
b = {$random} % 15;
end
#700;
$stop;
end
compare u1_compare(
.a (a ),
.b (b ),
.equal (equal )
);
endmodule
// 5,3-8译码器设计实例(利用赋值语句设计组合逻辑)
module decoder(out, in);
input [2:0] in;
output [7:0] out;
assign out = 1'b1 << in;
// 把最低位的 1 左移 in(根据从 in 口输入的值)位,并赋予 out.
endmodule
// 5,
module tb_decoder;
reg [2:0] in;
wire [7:0] out;
initial begin
in = 3'd0;
repeat(8) begin
#100;
in = {$random}%8;
end
#400;
$stop;
end
decoder u1_decoder(
.in (in ),
.out (out )
);
endmodule
// 6,8-3编码器的设计实例,(1)
module encoder1(none_on, out, in);
input [7:0] in;
output reg none_on;
output reg [2:0] out;
always@(in) begin:local // 此处 begin-end 块必须有一模块名,因为块中定义了局部变量
integer i;
out = 0;
none_on = 1;
/* 返回输入信号 in 的 8位 中,为 1 的最高位数 */
for(i=0; i<8; i=i+1) begin // 取最高位是1,先有效
if(in[i]) begin
out = i;
none_on = 0;
end
end
end
endmodule
// 6,
module tb_encoder1;
reg [7:0] in; // 编码器
wire [2:0] out;
wire none_on;
initial begin
in = 8'b0000_0000;
#100;
in = 8'b0000_0001;
#100;
in = 8'b0000_0010;
#100;
in = 8'b0000_0100;
#100;
in = 8'b0000_1000;
#200;
in = 8'b0001_0000;
#100;
in = 8'b0010_0000;
#100;
in = 8'b0100_0000;
#100;
in = 8'b1000_0000;
#200;
in = 8'b1010_0000;
#100;
in = 8'b0001_0100;
#200;
$stop;
end
encoder1 u1_encoder1(
.in (in ),
.out (out ),
.none_on (none_on )
);
endmodule
// 编码器设计方案,(2)
module encoder2(none_on, out2, out1, out0, h, g, f, e, d, c, b, a);
input h, g, f, e, d, c, b, a;
output none_on, out2, out1, out0;
wire [3:0] outvec;
assign outvec = h ? 4'b0111 : g ? 4'b0110 : f ? 4'b0101 :
e ? 4'b0100 : d ? 4'b0011 : c ? 4'b0010 :
b ? 4'b0001 : a ? 4'b0000 : 4'b1000;
assign none_on = outvec[3];
assign out2 = outvec[2];
assign out1 = outvec[1];
assign out0 = outvec[0];
endmodule
// 6,(2)
module tb_encoder2;
reg h; // 7
reg g; // 6
reg f; // 5
reg e; // 4
reg d; // 3
reg c; // 2
reg b; // 1
reg a; // 0
wire none_on;
wire out2;
wire out1;
wire out0;
initial begin
// 0
{h, g, f, e, d, c, b, a} = {1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0};
#20; // 7
{h, g, f, e, d, c, b, a} = {1'b1, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0};
#20; // 6
{h, g, f, e, d, c, b, a} = {1'b0, 1'b1, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0};
#20; // 5
{h, g, f, e, d, c, b, a} = {1'b0, 1'b0, 1'b1, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0};
#20; // 4
{h, g, f, e, d, c, b, a} = {1'b0, 1'b0, 1'b0, 1'b1, 1'b0, 1'b0, 1'b0, 1'b0};
#20; // 3
{h, g, f, e, d, c, b, a} = {1'b0, 1'b0, 1'b0, 1'b0, 1'b1, 1'b0, 1'b0, 1'b0};
#20; // 2
{h, g, f, e, d, c, b, a} = {1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b1, 1'b0, 1'b0};
#20; // 1
{h, g, f, e, d, c, b, a} = {1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b1, 1'b0};
#20; // 0
{h, g, f, e, d, c, b, a} = {1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b1};
#200; // 0
{h, g, f, e, d, c, b, a} = {1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0};
#20; // 5
{h, g, f, e, d, c, b, a} = {1'b0, 1'b0, 1'b1, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0};
#300;
$stop;
end
encoder2 u1_encoder2(
.h (h ),
.g (g ),
.f (f ),
.e (e ),
.d (d ),
.c (c ),
.b (b ),
.a (a ),
.none_on (none_on ),
.out2 (out2 ),
.out1 (out1 ),
.out0 (out0 )
);
endmodule
// 编码器设计方案,(3)
module encoder3(none_on, out2, out1, out0, h, g, f, e, d, c, b, a);
input h, g, f, e, d, c, b, a;
output out2, out1, out0;
output none_on;
reg [3:0] outvec;
assign {none_on, out2, out1, out0} = outvec;
always@(a or b or c or d or e or f or g or h) begin
if(h) outvec = 4'b0111;
else if(g) outvec = 4'b0110;
else if(f) outvec = 4'b0101;
else if(e) outvec = 4'b0100;
else if(d) outvec = 4'b0011;
else if(c) outvec = 4'b0010;
else if(b) outvec = 4'b0001;
else if(a) outvec = 4'b0000;
else outvec = 4'b1000;
end
endmodule
//
module tb_encoder3;
reg h; // 7
reg g; // 6
reg f; // 5
reg e; // 4
reg d; // 3
reg c; // 2
reg b; // 1
reg a; // 0
wire none_on;
wire out2;
wire out1;
wire out0;
initial begin
// 0
{h, g, f, e, d, c, b, a} = {1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0};
#20; // 7
{h, g, f, e, d, c, b, a} = {1'b1, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0};
#20; // 6
{h, g, f, e, d, c, b, a} = {1'b0, 1'b1, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0};
#20; // 5
{h, g, f, e, d, c, b, a} = {1'b0, 1'b0, 1'b1, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0};
#20; // 4
{h, g, f, e, d, c, b, a} = {1'b0, 1'b0, 1'b0, 1'b1, 1'b0, 1'b0, 1'b0, 1'b0};
#20; // 3
{h, g, f, e, d, c, b, a} = {1'b0, 1'b0, 1'b0, 1'b0, 1'b1, 1'b0, 1'b0, 1'b0};
#20; // 2
{h, g, f, e, d, c, b, a} = {1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b1, 1'b0, 1'b0};
#20; // 1
{h, g, f, e, d, c, b, a} = {1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b1, 1'b0};
#20; // 0
{h, g, f, e, d, c, b, a} = {1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b1};
#200; // 0
{h, g, f, e, d, c, b, a} = {1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0};
#20; // 5
{h, g, f, e, d, c, b, a} = {1'b0, 1'b0, 1'b1, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0};
#300;
$stop;
end
encoder3 u1_encoder3(
.h (h ),
.g (g ),
.f (f ),
.e (e ),
.d (d ),
.c (c ),
.b (b ),
.a (a ),
.none_on (none_on ),
.out2 (out2 ),
.out1 (out1 ),
.out0 (out0 )
);
endmodule
使用连续赋值、case语句 或 if-else语句 可以生成多路器电路。
如果条件语句(case 或 if-else)中分支条件是互斥的话,综合器
能自动地生成并行的路由器。
// 7,多路器的设计实例,(1)
module emux1(out, a, b, sel);
input a, b, sel;
output out;
assign out = sel ? a : b;
endmodule
// 7
module tb_emux1;
reg a;
reg b;
reg sel;
wire out;
initial begin
a = 1'b0; b = 1'b0; sel = 1'b0;
repeat(5) begin
#100
sel = {$random} % 2;
a = {$random} % 2;
b = {$random} % 2;
end
#700;
$stop;
end
emux1 u1_emux1(
.a (a ),
.b (b ),
.sel (sel ),
.out (out )
);
endmodule
// 多路器设计方案,(2)
module mux2(out, a, b, sel);
input a, b, sel;
output reg out;
// 用电平触发的 always 块来设计多路器的组合逻辑
always@(a or b or sel) begin
/* 检查输入信号 sel 的值,如是 1,输出 out 是 a;如是 0,输出 out 是 b.*/
case(sel)
1'b1: out = a;
1'b0: out = b;
default: out = 'bx;
endcase
end
endmodule
// 7,(2)
module tb_mux2;
reg a;
reg b;
reg sel;
wire out;
initial begin
a = 1'b0; b = 1'b0; sel = 1'b0;
repeat(5) begin
#100
sel = {$random} % 2;
a = {$random} % 2;
b = {$random} % 2;
end
#700;
$stop;
end
mux2 u1_mux2(
.a (a ),
.b (b ),
.sel (sel ),
.out (out )
);
endmodule
// 多路器设计方案,(3)
module mux3(out, a, b, sel);
input a, b, sel;
output reg out;
always@(a or b or sel) begin
if(sel)
out = a;
else
out = b;
end
endmodule
// 7,(2)
module tb_mux3;
reg a;
reg b;
reg sel;
wire out;
initial begin
a = 1'b0; b = 1'b0; sel = 1'b0;
repeat(5) begin
#100
sel = {$random} % 2;
a = {$random} % 2;
b = {$random} % 2;
end
#700;
$stop;
end
mux3 u1_mux3(
.a (a ),
.b (b ),
.sel (sel ),
.out (out )
);
endmodule
// 8,奇偶校验位生成器设计实例
module parity(even_numbits, odd_numbits, input_bus);
input [7:0] input_bus;
output even_numbits, odd_numbits;
assign odd_numbits = ^input_bus;
assign even_numbits = ~odd_numbits;
endmodule
// 8
module tb_parity;
reg [7:0] input_bus;
wire even_numbits;
wire odd_numbits;
initial begin
input_bus = 8'd0;
repeat(8) begin
#100;
input_bus = {$random} % 8;
end
#400;
input_bus = 8'd0;
end
parity u1_parity(
.input_bus (input_bus ),
.even_numbits (even_numbits ),
.odd_numbits (odd_numbits )
);
endmodule
// 9,三态输出驱动器设计实例(用连续赋值语句建立三态门模型)
// 三态输出驱动器设计方案,(1)
module trist1(out, in, enable);
input in, enable;
output out;
assign out = enable ? in : 'bz;
endmodule
// 9,(1)
module tb_trist1;
reg in;
reg enable;
wire out;
initial begin
enable = 1'b0; in = 1'b0;
repeat(8) begin
#100;
enable = {$random} % 2; in = {$random} % 2;
end
#400;
enable = {$random} % 2; in = {$random} % 2;
end
trist1 u1_trist1(
.in (in ),
.enable (enable ),
.out (out )
);
endmodule
// 三态输出驱动器设计方案,(2)
module trist2(out, in, enable);
input in, enable;
output out;
// bufif1 是一个Verilog门级原语(primitive)
bufif1 mybuf1(out, in, enable);
endmodule
// 9,(2)
module tb_trist2;
reg in;
reg enable;
wire out;
initial begin
enable = 1'b0; in = 1'b0;
repeat(8) begin
#100;
enable = {$random} % 2; in = {$random} % 2;
end
#400;
enable = {$random} % 2; in = {$random} % 2;
end
trist2 u1_trist2(
.in (in ),
.enable (enable ),
.out (out )
);
endmodule
// 10,三态双向驱动器设计实例
module bidir(tri_inout, out, in, en, b);
input en, in, b;
output out;
inout tri_inout;
assign tri_inout = en ? in : 'bz;
assign out = tri_inout ^ b;
endmodule
// 10
module tb_bidir;
reg en;
reg in;
reg b;
wire tri_inout;
wire out;
initial begin
en = 1'b0; in = 1'b0; b = 1'b0;
repeat(8) begin
#100;
en = {$random}%2; in = {$random}%2; b = {$random}%2;
end
#400;
$stop;
end
bidir u1_bidir(
.en (en ),
.in (in ),
.b (b ),
.tri_inout (tri_inout ),
.out (out )
);
endmodule
// 11,触发器设计实例
module dff(q, data, clk);
input clk, data;
output reg q;
always@(posedge clk) begin
q <= data;
end
endmodule
// 11,时序逻辑
module tb_dff;
reg clk;
reg data;
wire q;
// clk
always #5 clk = ~clk;
initial begin
clk = 1'b0;
end
//
initial begin
data = 1'b0;
forever begin
#10;
data = {$random} % 2;
end
end
dff u1_dff(
.clk (clk ),
.data (data ),
.q (q )
);
endmodule
// 12,电平敏感型锁存器设计实例,(1)
module latch1(q, data, clk);
input clk, data;
output q;
assign q = clk ? data : q;
endmodule
//
// 12
module tb_latch1;
reg clk;
reg data;
wire q;
initial begin
clk = 1'b1;
data = 1'b0;
forever begin
#10
clk = ~clk;
data = {$random} % 2;
end
end
latch1 u1_latch1(
.clk (clk ),
.data (data ),
.q (q )
);
endmodule
// 13,带置位和复位端的电平敏感型锁存器设计实例,(2)
module latch2(q, data, clk, set, reset);
input clk, data, set, reset;
output q;
assign q = reset ? 0 : (set ? 1 : (clk ? data : q));
endmodule
//
// 13,
module tb_latch2;
reg clk; //
reg reset; //
reg set; //
reg data; //
wire q; //
initial begin
clk = 1'b1;
reset = 1'b1;
set = 1'b1;
data = 1'b0;
forever begin
#10
clk = ~clk;
reset = {$random} % 2;
set = {$random} % 2;
data = {$random} % 2;
end
end
latch2 u1_latch2(
.clk (clk ),
.reset (reset ),
.set (set ),
.data (data ),
.q (q )
);
endmodule
// 14,电平敏感型锁存器设计实例,(3)
module latch3(q, data, clk);
input clk, data;
output reg q;
always@(clk or data) begin
if(clk)
q <= data;
end
endmodule
//
// 14
module tb_latch3;
reg clk;
reg data;
wire q;
initial begin
clk = 1'b1;
data = 1'b0;
forever begin
#10
clk = ~clk;
data = {$random} % 2;
end
end
latch3 u1_latch3(
.clk (clk ),
.data (data ),
.q (q )
);
endmodule
注意:
的综合器会产生一警告信息,告诉你产生了一个电平敏感型锁存器,因为我们设计的就是一个电平敏感型锁存器,所以不用管这个警告信息。
// 15,移位寄存器设计实例
module shifter(din, clk, i_rst, dout);
input clk, i_rst, din;
output reg [7:0] dout;
always@(posedge clk) begin
if(i_rst) // 清零
dout <= 8'b0;
else begin
dout <= dout << 1; // 左移一位
dout[0] <= din; // 把输入信号放入寄存器的最低位
end
end
endmodule
// tb,测试信号
module tb_shifter;
reg clk;
reg i_rst;
reg din;
wire [7:0] dout;
initial begin
i_rst = 1'b1;
#5;
i_rst = 1'b0;
end
initial begin
clk = 1'b1;
din = 1'b0;
forever begin
#10;
clk = ~clk;
din = {$random} % 2;
end
end
shifter u1_shifter(
.clk (clk ),
.i_rst (i_rst ),
.din (din ),
.dout (dout )
);
endmodule
// 16,8位计数器设计实例,(1)
module counter1(out, cout, data, load, cin, clk);
input clk, cin, load;
input [7:0] data;
output reg [7:0] out;
output cout;
always@(posedge clk) begin
if(load)
out <= data;
else
out <= out + cin;
end
assign cout = (& out) & cin;
// 只有当 out[7:0] 的所有各位都是1, 并且进位 cin 也是1时才能产生进位 cout。
endmodule
// 16,(1)
// 测试代码
module tb_counter1;
reg clk;
reg cin;
reg load;
reg [7:0] data;
wire [7:0] out;
wire cout;
always #10 clk = ~clk;
initial begin
clk = 1'b1;
load = 1'b0; // 不置数
#10;
load = 1'b1; // 置数
#20;
load = 1'b0; // 不置数
end
// 输入数据
initial begin // 顺序执行块
data = 8'b0000_0000;
cin = 1'b1;
#40;
data = 8'b0000_0001;
cin = 1'b1;
// repeat(256) begin
// #20;
// data = 8'b0000_0001 + 1'b1;
// end
#5400;
data = 8'b0000_0000;
cin = 1'b0;
end
counter1 u1_counter1(
.clk (clk ),
.cin (cin ),
.load (load ),
.data (data ),
.out (out ),
.cout (cout )
);
endmodule
// 17,8位计数器设计实例,(2)
module counter2(out, cout, data, load, cin, clk);
input load, cin, clk;
input [7:0] data;
output reg [7:0] out;
output reg cout;
reg [7:0] preout;
// 创建 8位 寄存器
always@(posedge clk) begin
out <= preout;
end
/* 计算计数器和进位的下一个状态。注意:为提高性能不希望加载影响到进位。 */
always@(out or data or load or cin) begin
{cout, preout} = out + cin;
if(load)
preout = data;
end
endmodule
// 17,(2)
// 测试代码
module tb_counter2;
reg clk;
reg cin;
reg load;
reg [7:0] data;
wire [7:0] out;
wire cout;
initial begin
load = 1'b0; // 不置位
data = 8'b0000_0000;
cin = 1'b1;
#40;
load = 1'b1; // 置位
data = 8'b0000_0001;
// cin = 1'b1;
#20; // 不置位
load = 1'b0;
#6000; // 置位
load = 1'b1;
data = 8'b1000_0000;
cin = 1'b0;
end
always #10 clk = ~clk;
initial begin
clk = 1'b1;
end
counter2 u1_counter2(
.clk (clk ),
.cin (cin ),
.load (load ),
.data (data ),
.out (out ),
.cout (cout )
);
endmodule
// 13.6.1 状态机的异步置位与复位是用 always 块和事件控制实现的。
// 带异步高电平有效的 置/复位端 的 D触发器实例
module dff1(q, qb, d, clk, set, reset);
input clk, d, set, reset;
output reg q, qb;
// 声明 q 和 qb 为 reg类型,因为它需要在 always 块内赋值
always@(posedge clk or posedge set or posedge reset) begin
if(reset) begin
q <= 0;
qb <= 1;
end
else
if(set) begin
q <= 1;
qb <= 0;
end
else begin
q <= d;
qb <= ~d;
end
end
endmodule
// 18,异步
module tb_dff1;
reg clk;
reg reset;
reg set;
reg d;
wire q;
wire qb;
initial begin
reset = 1'b0; // no vld.
set = 1'b0;
d = 1'b0;
#10;
reset = 1'b1; // vld.
set = 1'b0;
d = 1'b1;
#20;
reset = 1'b1; // vld
set = 1'b1;
d = 1'b0;
#40;
reset = 1'b0;
set = 1'b1;
d = 1'b0;
#400; // no vld.
reset = 1'b0;
set = 1'b0;
d = 1'd1;
#20;
d = 1'd0;
#20;
d = 1'd1;
#20;
d = 1'd0;
end
always #10 clk = ~clk;
initial begin
clk = 1'b1;
end
dff1 u1_dff1(
.clk (clk ),
.reset (reset ),
.set (set ),
.d (d ),
.q (q ),
.qb (qb )
);
endmodule
// 13.6.2 状态机的同步置位与复位
// 同步的具有高电平有效的 置位/复位 的 D触发器
module dff2(q, qb, d, clk, set, reset);
input clk, d, set, reset;
output reg q, qb;
always@(posedge clk) begin
if(reset) begin
q <= 0;
qb <= 1;
end
else begin
if(set) begin
q <= 1;
qb <= 0;
end
else begin
q <= d;
qb <= ~d;
end
end
end
endmodule
// 19,同步
module tb_dff2;
reg clk;
reg reset;
reg set;
reg d;
wire q;
wire qb;
initial begin
reset = 1'b0;
set = 1'b0;
d = 1'b0; // vld.
#10;
reset = 1'b1; // vld.
set = 1'b0;
d = 1'b1;
#20;
reset = 1'b1; // vld,
set = 1'b1;
d = 1'b0;
#40;
reset = 1'b0;
set = 1'b1; // vld.
d = 1'b0;
#400;
reset = 1'b0;
set = 1'b0;
d = 1'd1; // vld.
#20;
d = 1'd0; // vld.
#20;
d = 1'd1; // vld.
#20;
d = 1'd0; // vld.
end
always #10 clk = ~clk;
initial begin
clk = 1'b1;
end
dff2 u1_dff2(
.clk (clk ),
.reset (reset ),
.set (set ),
.d (d ),
.q (q ),
.qb (qb )
);
endmodule
本文阐述了什么样的风格的Verilog模块是可以综合成电路结构的,以及综合的一般原则。此外,对于一般的综合工具而言,可以借助于工具自动综合成电路结构的Verilog模块风格非常有限,如1.1节的组合逻辑和3.2节同步状态机的标准写法。
注意:
当系统比较复杂时,需要通过仔细的分析,把一个具体系统分解为数据流和控制流。构思哪些部分用组合逻辑,哪些部分的资源可以共享而不影响系统的性能,需要设置哪些开关逻辑来控制数据的流动,需要一个或几个同步有限状态机来正确有序地控制这些开关逻辑,以便有效地利用有限的硬件资源,才能编写出真正有价值的RTL级源代码,从而综合出有实用价值的高性能的数字逻辑电路系统。
因此,认真学习并掌握数字电路基础和计算机体系结构这两门学科的真谛是Verilog数字系统设计的基础。