同步时序电路设计风格下建议所有的输出都是reg型,也就是最后的输出要有一个寄存器(边沿触发)。
常用的寄存器有不带置位和重置的简单DFF,异步置位同步释放的DFF,异步置位重置同步释放的DFF。
module FlipFlops(
input D, clk, rst, pst,
output reg Qsimple, Qasyncrst, Qasyncpst
);
// simple DFF
always @(posedge clk)
Qsimple <= D;
// asynchronous reset, synchronous release
always @(posedge clk, posedge rst)begin
if (rst == 1'b1)
Qasyncrst <= 1'b0;
else
Qasyncrst <= D;
end
// asynchronous reset/preset, synchronous release
always @(posedge clk, posedge rst, posedge pst)begin
if (rst == 1'b1)
Qasyncpst <= 1'b0;
else if (pst == 1'b1)
Qasyncpst <= 1'b1;
else
Qasyncpst <= D;
end
endmodule
`timescale 1ns/100ps
module FlipFlops_tb;
reg D, clk, rst, pst;
wire Qsimple, Qasyncrst, Qasyncpst;
FlipFlops FF_U1(
.D(D),
.clk(clk),
.rst(rst),
.pst(pst),
.Qsimple(Qsimple),
.Qasyncrst(Qasyncrst),
.Qasyncpst(Qasyncpst)
);
always #1 clk = ~clk;
initial begin
#0
clk = 1'b1;
D = 1'b0;
rst = 1'b0;
pst = 1'b0;
#1
rst = 1'b1;
pst = 1'b1;
#2.5
D = 1'b1;
#5
rst = 1'b0;
#7.5
pst = 1'b0;
#10
$finish;
end
endmodule
好的设计风格应该尽量避免锁存器的出现,一般在always块的敏感列表是电平敏感,但是没有把所有组合逻辑需要的信号都列出来的情况就会出现锁存器。见下例
input a,b,c;
reg e,d;
always @(a,b,c)begin
// 因为d没有出现在敏感列表,所以d变化时,e不能立刻变化,需要等到abc某个信号变化的时候才能体现,实际上是产生了一个锁存器把d的信号锁存了
e = d&a&b;
end
避免出现锁存的方法:
1、使用完备的 if …else,或 case 语句;
2、避免组合逻辑反馈环路;
3、为每个输入条件,定义输出操作;
4、事件控制表达式中的敏感信号必须完整;
5、状态机设计中,定义default 的状态转移;
6、定义缺省值(default值) 。
常用的Dlatch的代码如下
module DLatch(
input D, ena, rst, pst,
output reg Qsimple, Qasyncrst, Qasyncpst
);
always @(D, ena)
if (ena == 1'b1)
Qsimple <= D;
always @(D, ena, rst)begin
if (rst == 1'b1)
Qasyncrst <= 1'b0;
else if (ena == 1'b1)
Qasyncrst <= D;
end
always @(D, ena, rst, pst)begin
if (rst == 1'b1)
Qasyncpst <= 1'b0;
else if (pst == 1'b1)
Qasyncpst <= 1'b1;
else if (ena == 1'b1)
Qasyncpst <= D;
end
endmodule
`timescale 1ns/100ps
module Dlatch_tb;
reg D, ena, rst, pst;
wire Qsimple, Qasyncrst, Qasyncpst;
DLatch Latch_U1(
.D(D),
.ena(ena),
.rst(rst),
.pst(pst),
.Qsimple(Qsimple),
.Qasyncrst(Qasyncrst),
.Qasyncpst(Qasyncpst)
);
always #1 ena = ~ena;
initial begin
#0
ena = 1'b1;
D = 1'b0;
rst = 1'b0;
pst = 1'b0;
#1
rst = 1'b1;
pst = 1'b1;
#2.5
D = 1'b1;
#5
rst = 1'b0;
#7.5
pst = 1'b0;
#10
$finish;
end
endmodule
module ShiftReg(
input Din, shift_ena, clk, rst,
output Qout
);
reg [4:0] Qreg;
always @(posedge clk or posedge rst) begin
if (rst) begin
Qreg <= 5'b0; // reset
end
else begin
Qreg[0] <= (shift_ena == 1'b1)? Din : Qreg[0];
Qreg[1] <= (shift_ena == 1'b1)? Qreg[0] : Qreg[1];
Qreg[2] <= (shift_ena == 1'b1)? Qreg[1] : Qreg[2];
Qreg[3] <= (shift_ena == 1'b1)? Qreg[2] : Qreg[3];
Qreg[4] <= (shift_ena == 1'b1)? Qreg[3] : Qreg[4];
end
end
assign Qout = Qreg[4];
endmodule
`timescale 1ns/100ps
module ShiftReg_tb;
reg Din, shift_ena, clk, rst;
wire Qout;
ShiftReg shift_U1(
.Din(Din),
.shift_ena(shift_ena),
.clk(clk),
.rst(rst),
.Qout(Qout)
);
always #1 clk = ~clk;
always begin
#3.2 Din = ~Din;
#1.8 Din = 0;
end
initial begin
Din = 1'b0;
shift_ena = 1'b0;
clk = 1'b1;
rst = 1'b1;
#5 shift_ena = 1'b1;
#180 rst = 1'b0;
#200 $finish;
end
endmodule
计数器主要用于分频,任务控制等,计数器有门级模型,状态机,行为级三种verilog写法,推荐使用行为级写法。以下是使用计数器实现一个分频器。
module fdiv
#(parameter Ndiv = 2)(
input fin, rst,
output reg fout
);
reg [7:0] cnt;
always @(posedge fin or posedge rst) begin
if (rst) begin
// fout <= 1'b0;
cnt <= 8'b0;
end
else if (cnt == Ndiv) begin
cnt <= 8'b0;
// fout <= ~fout;
end
else cnt <= cnt+1;
end
always @(posedge fin or posedge rst) begin
if (rst) begin
fout <= 1'b0;
end
else if (cnt == Ndiv) begin
fout <= ~fout;
end
end
endmodule
`timescale 1ns/100ps
module fdiv_tb;
reg fin, rst;
wire fout;
fdiv #(
.Ndiv(4)
)
U1 (
.fin(fin),
.rst(rst),
.fout(fout)
);
initial begin
rst = 1'b1;
fin = 1'b0;
#20 rst = 1'b0;
#500 $finish;
end
always #5 fin = ~fin;
endmodule