设计可综合状态机的指导原则
一、注意:
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,组合逻辑电路设计
1.1,8位带进位端的加法器模块设计
1.1.1,RTL代码设计
// 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
1.1.2,tb测试信号
// 测试信号, 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
1.1.3,生成原理图
1.1.4,SIM输出波形
1.2,指令译码模块设计
1.2.1,RTL代码设计
// 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
1.2.2,tb测试代码
// 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
1.2.3,生成原理图
1.2.4,SIM输出波形
1.3,利用 task 和 电平敏感的 always 块设计经比较后重组信号的组合逻辑
1.3.1,RTL代码设计
// 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
1.3.2,tb测试代码
// 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
1.3.3,生成原理图
1.3.4,SIM输出波形
1.4,比较器模块设计
1.4.1,RTL代码设计
// 4,比较器的设计实例(利用赋值语句设计组合逻辑)
module compare(equal, a, b);
parameter size = 4;
input [size-1 : 0] a, b;
output equal;
assign equal = (a == b) ? 1 : 0;
endmodule
1.4.2,tb测试代码
// 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
1.4.3,生成原理图
1.4.4,SIM输出波形
1.5,3-8译码器模块设计
1.5.1,RTL代码设计
// 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
1.5.2,tb测试代码
// 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