FPGA学习笔记---时序逻辑与组合逻辑分析比较

       在学习FPGA使用Verilog HDL语言编程时,开始遇到时序逻辑和组合逻辑时概念一看就明白,但是实际使用时还是不清楚到底要用哪个。现在用就一个例子来体会一下这两者的区别。

     首先先看组合逻辑和时序逻辑的定义。

FPGA学习笔记---时序逻辑与组合逻辑分析比较_第1张图片

看完以后还是感觉云里雾里搞不清楚,那么就不用管它了,直接用例子来说明。

FPGA学习笔记---时序逻辑与组合逻辑分析比较_第2张图片

        在这里设计一个0---9计数器,clk为输入时钟信号,cin为计数有效信号,也就是说只有当cin为高电平时,计数器才计数一次。cout为计数进位信号,当计数值为9时,计数值再加1的话,就输出一个进位信号,同时计数值清零。q输出计数值,输出值的范围是0--9。这个计数器类似于数码管显示数字时每一个数码管的显示范围,每个数码管显示范围为0--9,当低位满10之后,向前一位进1,同时低位清零。

下面开始编写代码

首先定义输入输出端口

module bcd_counter(
    input   clk,        //时钟
    input   rst_n,      //复位
    input   cin,        //计数使能
    
    output  cout,       //进位输出
    output [3:0] q      //计数输出
);
endmodule

输入信号有三个 时钟 clk、复位 rst_n、计数使能 cin。

输出信号有两个 进位输出cout、计数值输出q,q的计数范围是0--9,所以q设置为4位计数器。

下面编写计数代码

reg [3:0] cnt;
//BCD码计数
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)
        cnt <= 4'd0;
    else if(cin == 1'b1) begin      //计数使能信号有效时 计数器加1
        if(cnt < 4'd9)
            cnt <= cnt + 1'b1;
        else
            cnt <= 4'd0;
     end
     else 
        cnt <= cnt;
end

         cnt存储计数值,复位后默认值为0,每次当cin为高电平时,计数值加1,当计数到9时,计数值清零。cin为低电平时,计数值保持不变。这样计数寄存器的值就在0-到9之间循环。

下面编写进位代码

always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)
        cout <= 1'b0;
    else if(cnt == 4'd9 && cin == 1'b1)
        cout <= 1'b1;
    else
        cout <= 1'b0;
end

当计数寄存器的值为9,同时计数使能信号为1时,说明已经计够10次了,需要进位一次,这时cout输出1。其余情况下输出为0。

最后将寄存器的值连接到输出端口上

assign q = cnt; 

整体代码如下

module bcd_counter(
    input   clk,        //时钟
    input   rst_n,      //复位
    input   cin,        //计数使能
    
    output  cout,       //进位输出
    output [3:0] q      //计数输出
);

reg [3:0] cnt;
//BCD码计数
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)
        cnt <= 4'd0;
    else if(cin == 1'b1) begin      //计数使能信号有效时 计数器加1
        if(cnt < 4'd9)
            cnt <= cnt + 1'b1;
        else
            cnt <= 4'd0;
     end
     else 
        cnt <= cnt;
end
//进位信号输出
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)
        cout <= 1'b0;
    else if(cnt == 4'd9 && cin == 1'b1)
        cout <= 1'b1;
    else
        cout <= 1'b0;
end

assign q = cnt;        //输出计数值

endmodule

代码的功能比较简单,一个always语句产生计数信号,一个always语句产生进位信号。

下面编写测试文件

`timescale 1ns/1ns
module bcd_counter_tb;
parameter T = 20;

reg sys_clk;
reg sys_rst_n;
reg cin;

wire cout;
wire [3:0] q;

bcd_counter bcd_counter0(
    .clk        (sys_clk),        //时钟
    .rst_n      (sys_rst_n),      //复位
    .cin        (cin),            //计数使能
    
    .cout       (cout),           //进位输出
    .q          (q)               //计数输出
);

initial begin
    sys_rst_n = 1'b0;
    sys_clk = 1'b1;
    #200;
    sys_rst_n = 1'b1;
    repeat(100) begin      
        cin <= 1'b0;              //输出4个周期低电平
        #(T * 4);   
        cin <= 1'b1;              //输出1个周期高电平
        #(T);        
    end
    cin <= 1'b0;
    #(200 * T);
    $stop;
end

always #(T/2) sys_clk = ~sys_clk;

endmodule

       测试文件产生一个时钟信号 sys_clk 和一个计数使能信号 cin,cin信号为4个时钟的低电平,然后1个时钟的高电平.也是说没5个时钟周期计数器就会计数一次。

      下来仿真一下,看看输出波形。

FPGA学习笔记---时序逻辑与组合逻辑分析比较_第3张图片

放大波形看看cin和计数值关系

FPGA学习笔记---时序逻辑与组合逻辑分析比较_第4张图片

可以看到每个cin信号为高时,计数值加1,当计数值为9时,输出cout信号输出一个高脉冲。

再放大波形看看cout和cin的输出时序

FPGA学习笔记---时序逻辑与组合逻辑分析比较_第5张图片

可以看到当计数值为9时,cin信号出现高电平,cout延时一个时钟周期才输出的一个高电平。按照正常计数逻辑来说,当低位9再加1时,低位变为0,同时向高位进1,计数和进位是同时发生的。而这个却出现了计数和进位不同步的情况。出现这种情况是为什么呢?那就要分析分析代码中的进位信号。

always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)
        cout <= 1'b0;
    else if(cnt == 4'd9 && cin == 1'b1)
        cout <= 1'b1;
    else
        cout <= 1'b0;
end

进位的always语句执行块,是在时钟的上升沿或者复位信号的下降沿才会进入。

FPGA学习笔记---时序逻辑与组合逻辑分析比较_第6张图片

在波形中可以看出,在蓝色光标处,时钟的上升沿cin信号到来,计数值加1,此时计数值为9,同时cin信号为1,此时cout信号应该要输出高电平了,但是由于cout是由时序逻辑控制的,只有在时钟的上升边沿always语句才会执行,所以只有等到下一个时钟上升沿cout才有机会输出高电平。

说明cout信号通过时序逻辑来实现的话,会有延时,不符合设计的实时变化要求,那么就把cout的实现改成用组合逻辑来实现,看看是什么效果。

在代码中将cout改为组合逻辑

/*
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)
        cout <= 1'b0;
    else if(cnt == 4'd9 && cin == 1'b1)
        cout <= 1'b1;
    else
        cout <= 1'b0;
end
*/
将时序逻辑改为组合逻辑

assign  cout = (cnt == 4'd9 && cin == 1'b1) ? 1'b1 : 1'b0;  

将cout改为组合逻辑实现,当计数值cnt为9,同时cin为高电平时,cout值为1,否则cout值为0。

这样当cin和cnt的值由任何变化时,cout值也跟着会变化,不受到时钟上升沿的影响。

注意将cout由时序逻辑改为组合逻辑时,要在初始化中将cout由寄存器类型改为线网类型。

重新编译代码后,查看波形。

FPGA学习笔记---时序逻辑与组合逻辑分析比较_第7张图片

这时可以看到cout信号和cin信号会同时变为高电平,不会有一个时钟周期的延迟。

FPGA学习笔记---时序逻辑与组合逻辑分析比较_第8张图片

当计数值变为9时,cin由低电平变为高电平继续计数时,计数值清0,同时进位输出也变为高电平。符合设计的要求。

通过上面的例子可以看到,当输出信号需要实时跟随输入信号变化时,就必须用组合逻辑来实现。其余情况下用时序逻辑来实现。

 

你可能感兴趣的:(FPGA学习笔记)