组合逻辑设计,时序逻辑设计,状态机设计,设计优化
整理复习一下常用的Verilog相关知识。共分为三节进行更新。
不同抽象级别的Verilog模型
- Verilog HDL是一种能够在多个级别对数字电路和数字系统进行描述的高级语言,Verilog HDL模型可以是对实际电路的不同级别的抽象。这些抽象级别一般可分为5级:
- 系统级(System Level)
- 算法级(Algorithm Level)
- 寄存器传输级(RTL,Register Transfer Level)
- 门级(Gate Level)
- 开关级(Switch Level)
- 其中,前三种属于高级别的描述方法,又称为行为级描述。门级模型是描述逻辑门以及逻辑门之间连接关系的模型。而开关级的模型则是描述器件中三极管和存储节点以及它们之间连接关系的模型。Verilog HDL在开关级提供了一整套完整的组合型原语(primitive),可以精确地建立MOS器件的底层模型。
门级描述
- Verilog HDL有关门类型的关键字共有26个,比较常用的有下面几个:not:非门;and:与门;nand:与非门;or:或门;nor:或非门;xor:异或门;xnor:异或非门(同或门);buf:缓冲器;bufif1,bufif0,notif1,notif0:各种三态门。
module flop(data,clock,clear,q,qb);
input data,clock,clear;
output q,qb;
nand #10 nd1(a,data,clock,clear);
nd2(b,ndata,clock);
nd4(d,c,b,clear);
nd5(e,c,nclock);
nd6(f,d,nclock);
nd8(qb,q,f,clear);
nand #9 nd3(c,a,d);
nd7(q,e,qb);
not #10 iv1(ndata,data);
iv2(nclock,clock);
endmodule
行为级(RTL)描述
- 一般的Verilog HDL设计中,很少采用门级描述,而使用较多的是行为级描述。对于设计者而言,采用的描述级别越高,设计越容易。但对于特定的综合器而言,有可能无法将某些抽象级别高的描述转化成电路。
module gen_clk (
output clk,
output reset
);
reg clk, reset;
initial begin
reset = 1; //初始状态
clk=0;
#3 reset = 0;
#5 reset = 1;
end
always #5 clk = ~clk; //每隔5个单位时间翻转一次
endmodul
- 可以看出,行为级描述能够更加直观的看出所设计的逻辑功能,但需要使用综合器将代码转换为电路逻辑。
组合逻辑设计
- Verilog HDL程序是由模块构成的。每个模块的内容都是嵌在module和endmodule两个语句之间,每个模块实现特定的功能,模块是可以进行层次嵌套的。
- 每个模块首先要进行端口定义,并说明输入(input)和输出(output),然后对模块的功能进行逻辑描述。
- Verilog HDL程序的书写格式自由,一行可以写几个语句,一个语句也可以分多行写。
- 除了endmodule语句外,每个语句的最后必须有分号。
二选一多路选择器
- 写法1(与或非运算):
module mux2_1(a,b,s,y); //mux2_1是器件的名称
input a,b,s; //输入接口a,b,s
output y; //输出接口y
assign y = (a& (~s)) | (b&s); //通过与或非门实现逻辑功能
endmodule
- 写法2(门级描述):
module mux2_1(a,b,s,y); //mux2_1是器件的名称
input a,b,s; //输入接口a,b,s
output y; //输出接口y
wire ns,as,bs;
not(ns,s);
and(as,a,ns);
and(bs,b,s);
or(y,as,bs);
endmodule
- 写法3(RTL):
module mux2_1(a,b,s,y); //mux2_1是器件的名称
input a,b,s; //输入接口a,b,s
output y; //输出接口y
assign y = (s ==0) ? a:b; //s等于0,输出a,反之,输出b
endmodule
全加器
- 可先实现一个半加器,再通过顶层文件镜像元件例化实现全加器。
- 先编写一个半加器模块:
module half_adder(a,b,so,co); //编写半加器模块
input a,b;
output so,co;
assign so = a ^ b;
assign co = a & b;
endmodule
- 再通过元件例化实现全加器:
module adder_top (a,b,ci,so,co)
input a,b,ci;
output so,co;
wire c1,c2,s1;
half_adder u1( //元件例化一个半加器处理ab求和
.a (a),
.b (b),
.so (s1),
.co (c1)
);
half_adder u2( //原件例化一个全加器处理进位
.a (s1),
.b (ci),
.so (so),
.co (c2)
);
assign co = c1|c2;
endmodule
42编码器设计
- 写法1(case语句):
module encoder(a0,a1,a2,a3,y0,y1); //定义编码器模块,输入为a输出为b
input a0,a1,a2,a3;
output y0,y1;
wire [3:0] a;
reg [1:0] y;
assign a = {a3,a2,a1,a0} //将输入的a3至a0进行位拼接操作
assign y0 = y[0]; //将寄存器的y值进行输出
assign y1 = y[1];
always @(a) begin
case(a)
4'b0001 : y = 2'b00; //当输入是0001时,编码输出00
4'b0010 : y = 2'b01;//当输入是0010时,编码输出01
4'b0100 : y = 2'b10;//当输入是0100时,编码输出10
4'b1000 : y = 2'b11;//当输入是1000时,编码输出11
default :y = 2'bxx;
endcase
end
endmodule
- 写法2(if语句):
module encoder(a0,a1,a2,a3,y0,y1); //定义编码器模块,输入为a输出为b
input a0,a1,a2,a3;
output y0,y1;
reg [1:0] y;
assign y0 = y[0]; //将寄存器的y值进行输出
assign y1 = y[1];
always @(a0,a1,a2,a3) begin
if (a0) y = 2'b00; //使用if-else if-else语句对输入a进行分类
else if (a1) y = 2'b01;
else if (a2) y = 2'b10;
else y = 2'b11;
end
endmodule
时序逻辑设计
- Verilog HDL语言中触发器的生成:首先需要定义一个reg型的变量,而这个变量的名字可以是输出端,也可以是内部信号。通过一个always语句块来描述电路的时序行为,这里的always语句块的敏感信号是边沿敏感的,程序中的clk是一个上升沿敏感信号,每次clk信号的一个上升沿到来时,都将触发always语句块的一次执行。
- 敏感信号类型以及同步、异步操作:使用posedge和negedge关键字来描述一个边沿敏感型信号,分别表示上升沿(positive edge)和下降沿(negative edge)。posedge和negedge关键字除了用于描边沿敏感型信号以外,还可以用在异步事件的描述中,这时posedge一般表示一个异步事件在信号高电平时有效,而negedge则表示异步事件在信号低电平时有效。异步操作控制信号出现在敏感信号列表里面,这时是异步操作;否则为同步操作。
D触发器设计
module d_flip_flop(d,clk,q);
input d,clk;
output q;
reg q;
always @(posedge clk) q<=d; //当上升沿到来时,将d的值赋值给q
endmodule
带有异步清零和异步置位的D触发器
module d_flip_flop(aclr,aset,d,ck,q);
input aclr,aset,d,ck;
output q;
reg q;
/*当任何一个信号发生变化时,均会在同一时刻改变输出,不与时钟同步,故称异步复位*/
always @(posedge aclr or posedge asset or posedge ck) begin
if(aclr) q <=1'b1;
else if(aset) q<=1'b1;
else q<=d;
end
endmodule
带有同步清零和同步置位的D触发器
module d_flip_flop(aclr,aset,d,ck,q);
input aclr,aset,d,ck;
output q;
reg q;
/*只有当时钟上升沿到来时,才对输出q进行改变,复位和清零操作与时间同步,故称同步复位*/
always @(posedge ck) begin
if(aclr) q <=1'b1;
else if(aset) q<=1'b1;
else q<=d;
end
endmodule
有限状态机设计
- 组合逻辑的输出仅仅是其当前输入的瞬时函数,而时序逻辑的输出还依赖于输入信号的历史,这种依赖性可以用“状态”的概念来表述。时序机的将来行为特征完全可以用它的输入及其当前状态来描述,任意时刻系统的状态,是指连同系统的输入在内的、足以确定系统将来行为的最少信息。例如,若仅仅知道串行比特流中1计数器输入端出现的1的个数,并不足以确定今后任意时刻计数器的计数值,还必须知道当前的计数值,因此计数器的状态就是它当前的计数值。
- 对于基于边沿触发的触发器的同步时序机,其状态转移是通过一个公共时钟的有效沿(即上升沿或下降沿)来进行同步的。状态的变化会引起确定下一状态及输出的组合逻辑的输出发生变化。时钟波形可以是对称的,也可以是非对称的,即时钟为低电平的持续间隔与时钟为高电平的持续间隔不相等。寄存器数据的传输都是在时钟的上升沿时刻或下降沿时刻完成的,并且将使输入数据与有效沿之间的变化在时间上保持一致。
- 有限状态机有两种基本类型:米利(Mealy)机和摩尔(Moore)机,米利机的下一状态和输出取决于当前状态和当前输入;摩尔机的下一状态取决于当前状态和当前输入,但其输出仅取决于当前状态。这两类有限状态机的下一状态和输出都是由组合逻辑电路形成的。
描述一个有限状态机
module fsm (Clock, Reset, A, F, G);
input Clock, Reset, A;
output F,G;
reg F,G;
reg [3:0] state ;
parameter Idle = 4’b1000 , Start = 4’b0100 , Stop = 4’b0010 , Clear = 4’b0001;
always @(posedge clock) begin
if (!Reset) begin
state <= Idle; F<=0; G<=0;
end
else begin
case (state)
Idle: begin
if (A) begin
state <= Start;
G<=0;
end
else state <= Idle;
end
Start:
if (!A) state <= Stop;
else state <= Start;
Stop: begin
if (A) begin
state <= Clear;
F <= 1;
end
else state <= Stop;
end
Clear: begin
if (!A) begin
state <=Idle;
F<=0; G<=1;
end
else state <= Clear;
end
default: state <=Idle;
endcase
end
end
endmodule
设计优化的几个思路
资源优化
- 资源共享,即将公用的计算模块放在设计系统的后级,让多个模块通过选择器共用后级通用模块。
- 串行化,将原来耗用资源巨大、单时钟周期内完成的并行执行逻辑块分割开来,提取出相同的逻辑模块(一般为组合逻辑块),在时间上复用该逻辑模块。
速度优化
- 流水线设计,采用n级流水线时,可以将速度提高将近n倍。在设计中加入流水线并不会减少原设计中的总延时,有时甚至还会增加插入的寄存器延时及信号同步的时间差,但却可以提高总体的运行速度。
- 寄存器配平,一项设计中,如果其中的两个组合逻辑块的延时差别过大,如T1大于T2,于是其总体的工作频率Fmax取决于T1,即最大的延时模块,从而导致设计的整体性能受到限制。把组合逻辑1的部分逻辑转移到组合逻辑2中,以减小组合逻辑1的延时T1,使t1 ≈ t2,且T1 + T2 = t1 + t2。
- 关键路径,关键路径是指设计中从输入到输出经过的延时最长的逻辑路径。减少关键路径上的延时,从输入到输出的总延时就能得到改善。
参考文献:
《Verilog经典教程(第三版)》——夏闻宇