首页专栏详情
打赏
FPGA逻辑设计回顾(3)多比特信号上升沿检测的设计方式与陷阱?
易百纳技术社区 李锐博恩 2020-12-27 18:30:21

前言

在总结本文最后的多比特上升沿检测之前,我们先把备用知识讲清楚,摊开来,以免造成模糊不清的默许!

逻辑运算符与位元运算符

从表面上来看,逻辑运算符与位元运算符之间的区别就是一个符号写法的问题,例如:&&,&,||,|,!,~ 事实上,我们应该真正的从含义上理解它们:

  • 所谓的逻辑运算符就是逻辑上的运算,那它的结果也就是真和假之分,即false and true,或者1'b1 and 1'b0,它是1比特的布尔值。
  • 而位元运算,其实就是单比特之间的逻辑运算而已,多比特的话就按位进行逻辑运算,例如按位与(&),按位或(|)以及按位非(~)。

有关这方面的语法,我以前有很多博客,例如: Verilog中的逻辑运算符与按位运算符的区分

或者之前的专栏: 数字设计基础教程

由于逻辑运算符与位元运算符之间的区别在此,但这也仅仅是针对多比特信号才有的区别,对于单比特信号,其实结果是一样的,我们认为都行。

下面给出几组测试,来区分逻辑运算符以及位元运算符之间的区别:

RTL原理图

下面给出仿真:

仿真波形

可见,逻辑运算符得到的结果总是1bit的,而位元运算符得到的结果和原输入数据位数一致,这和我们的认识也是相符合的。

下面给出上述设计以及仿真对应的RTL代码:

RTL设计代码:

module bitwise_test(
    input   [3:0]   in1,
    input   [3:0]   in2,
    output  [3:0]   out_bitwise_and,
    output  [3:0]   out_bitwise_or,
    output  [3:0]   out_bitwise_not,        
    output          out_logic_and,
    output          out_logic_or,
    output          out_logic_not           

    );

    assign out_bitwise_and = in1 & in2 ;
    assign out_logic_and = in1 && in2 ;

    assign out_bitwise_or = in1 | in2 ;
    assign out_logic_or = in1 || in2 ;

    assign out_bitwise_not = ~in1 ;
    assign out_logic_not = !in1 ;    

endmodule

Testbench代码:


module sim_bitwise_test(

    );

    reg [3:0] in1   ;
    reg [3:0] in2   ;

    wire  [3:0]   out_bitwise_and   ;
    wire  [3:0]   out_bitwise_or    ;
    wire  [3:0]   out_bitwise_not   ;        
    wire          out_logic_and     ;
    wire          out_logic_or      ;
    wire          out_logic_not      ;  

    initial begin
        in1 = 4'b1001;
        in2 = 4'b1100;

        #5

        in1 = 4'b1000;
        in2 = 4'b1101;
    end

    bitwise_test u_bitwise_test(
    .in1             ( in1             ),
    .in2             ( in2             ),
    .out_bitwise_and ( out_bitwise_and ),
    .out_bitwise_or  ( out_bitwise_or  ),
    .out_bitwise_not ( out_bitwise_not ),
    .out_logic_and   ( out_logic_and   ),
    .out_logic_or    ( out_logic_or    ),
    .out_logic_not   ( out_logic_not   )
);

endmodule

其实说起位元运算符以及逻辑运算符,在硬件描述语言中,还要一种不得不谈起,这几者算是同宗兄弟,只是完成者不同的使命,那便是缩位运算符:

HDLBits 系列(6)(Reduction)缩位运算符

其运算方式也十分简单,例如:

  • &a [3:0] // AND:a [3]&a [2]&a [1]&a [0]

  • | b [3:0] //或:b [3] | b [2] | b [1] | b [0]。 相当于(b [3:0]!= 4'h0)

  • ^ c [2:0] // XOR:c [2] ^ c [1] ^ c [0]

具体看我的其他博文吧,这里就不细讲了。

单比特信号上升沿检测

对于一个bit信号的上升沿检测,十分容易理解,实现方式也只有一种,那就是延迟,再逻辑运算,打拍得到结果。 例如,如下RTL原理图可以很清楚的说明问题: RTL原理图:

单比特信号上升沿检测原理图

下面是对上述电路的仿真:

仿真波形:

单比特上升沿检测仿真

对于上升沿检测,我们需要注意的是:FPGA设计心得(10)关于行为仿真的一点观点

我的观点只有一个,那就是我们在行为仿真的时候不要在正正好好时钟边沿处给数据,这是由于仿真工具对边沿处数据的不同处理方式,可能会导致得不到你想要的结果。具体还是看上面的链接吧,这里不再重提了。

下面附上上述设计以及仿真的RTL代码:

RTL设计:

module pos_detect(

    input   wire    clk     ,
    input   wire    rst     ,
    input   wire    in_a    ,
    output  reg     in_pos 

    );

    reg     reg_in_a    ;

    always@(posedge clk or posedge rst) begin
        if(rst) begin
            reg_in_a <= 1'b0    ;
        end
        else begin
            reg_in_a <= in_a    ;
        end
    end

    always@(posedge clk or posedge rst) begin
        if(rst) begin
            in_pos <= 1'b0    ;
        end
        else begin
            in_pos <= ~reg_in_a && in_a    ;
        end
    end

endmodule

Testbench仿真:


module pos_detect_tb(

    );

    reg     clk     ;
    reg     rst     ;
    reg     in_a    ;
    wire    in_pos  ;   

    always begin
        # 2 clk     = ~clk;
    end

    initial begin
        clk     = 1'b0;
        rst     = 1'b1;
        in_a    = 1'b0;
        #10
        rst     = 1'b0;
        #20
        in_a    = #(0.1) 1'b1;
        #20
        in_a    = #(0.1) 1'b0;
    end

    pos_detect u_pos_detect(
        .clk   ( clk   ),
        .rst   ( rst   ),
        .in_a  ( in_a  ),
        .in_pos  ( in_pos  )
    );

endmodule

多比特信号上升沿检测

对于多比特上升沿的检测,就要区分使用使用逻辑运算符和位元运算符了,如果用错了的话,肯定得不到想要的结果,这也就是所谓的小陷阱吧,专坑基础不牢或者马马虎虎的人。

除此之外,还有对于对比特的设计方式有如下几种,设计到for循环的使用,generate for循环的使用以及正常用法。 对于for循环以及generate for循环的含义,我在过去的博客上也多次写过,例如:

HDLBits 系列(7)对for循环以及generate for的各种实践

这篇文章对于generate for的使用提到一句比较关键的话就是generate for内部的语句要有一个名字:它的含义就是讲一个模块多次使用,形成多个模块的效果,属于并行的效果;可具体见博文,体会含义。 例如,使用generate for对全加器的多次例化,得到的文件结构是这样的:

generate for得到的文件结构

相应的RTL原理图也如此:

RTL原理图

使用三种方式对同一逻辑进行实现,部分RTL设计如下: 使用generate for形式:

    genvar j;
    generate 
        for(j = 0; j <= 3; j = j + 1) begin
            always@(posedge clk or posedge rst) begin:pos_detect3
                if(rst) begin
                    reg_in3[j] <= 1'b0;
                    out_pos3[j] <= 1'b0;
                end
                else begin
                    reg_in3[j] <= in1[j];
                    out_pos3[j] <= ~reg_in3[j] & in1[j];
                end
            end            
        end

至于使用for语句:

 always@(posedge clk or posedge rst) begin : pos_detect1
        if(rst) begin
            reg_in1 <= 4'h0;
            out_pos1 <= 4'b0;
        end 
        else begin
            for(i = 0; i <= 3; i = i + 1) begin : bits_pos_
                reg_in1[i] <= in1[i]    ;
                out_pos1[i] <= ~reg_in1[i] & in1[i]   ;
            end
        end       
    end

以及普通的实现方式:


    always@ (posedge clk or posedge rst) begin : pos_detect2
        if(rst) begin
            reg_in2 <= 4'b0;
            out_pos2 <= 4'b0;
        end
        else begin
            reg_in2 <= in1;
            out_pos2 <= ~reg_in2 & in1;
        end
    end

也能实现同样的功能,例如对其仿真示意图一致:

多种方式实现的仿真波形

生成的RTL原理图虽略有差异:

三种实现方式的RTL原理图

但本质上还是一致的,且看综合示意图:

三种实现方式的综合原理图

可见综合出来的结果,三者的结果采样了同一个中间过程,也就是最终在FPGA上的实现方式完全一致。

再给出implementation后的结果:

实现后的原理图

不用多想,结果也是一致的,虽然第2级触发器分开使用了不同的触发器,那是因为要分配给不同的输出,同时输出而非分时输出,也在意料之内,结构一致。

最终在FPGA中实现的电路也是这样的,实现是最接近底层的一级。

下面给出上述设计以及仿真的代码:

module bits_pos_detect(
    input   wire          clk,
    input   wire          rst,
    input   wire [3:0]    in1,
    output  reg  [3:0]    out_pos1,
    output  reg  [3:0]    out_pos2,
    output  reg  [3:0]    out_pos3   

    );

    reg     [3:0]   reg_in1 ;
    reg     [3:0]   reg_in2 ;
    reg     [3:0]   reg_in3 ;
    integer     i   ;

    always@(posedge clk or posedge rst) begin : pos_detect1
        if(rst) begin
            reg_in1 <= 4'h0;
            out_pos1 <= 4'b0;
        end 
        else begin
            for(i = 0; i <= 3; i = i + 1) begin : bits_pos_
                reg_in1[i] <= in1[i]    ;
                out_pos1[i] <= ~reg_in1[i] & in1[i]   ;
            end
        end       
    end

    always@ (posedge clk or posedge rst) begin : pos_detect2
        if(rst) begin
            reg_in2 <= 4'b0;
            out_pos2 <= 4'b0;
        end
        else begin
            reg_in2 <= in1;
            out_pos2 <= ~reg_in2 & in1;
        end
    end

    genvar j;
    generate 
        for(j = 0; j <= 3; j = j + 1) begin
            always@(posedge clk or posedge rst) begin:pos_detect3
                if(rst) begin
                    reg_in3[j] <= 1'b0;
                    out_pos3[j] <= 1'b0;
                end
                else begin
                    reg_in3[j] <= in1[j];
                    out_pos3[j] <= ~reg_in3[j] & in1[j];
                end
            end            
        end

    endgenerate

endmodule

测试平台:

module bits_pos_detect_tb(

    );

    reg                 clk;
    reg                 rst;
    reg     [3:0]       in1;
    wire    [3:0]       out_pos1;
    wire    [3:0]       out_pos2;
    wire    [3:0]       out_pos3;

    initial begin
        clk = 0;
        forever begin
            #5 clk = ~clk;
        end
    end   

    initial begin
        rst = 1;
        in1 = 4'h0;
        #18 
        rst = 0;
        in1 = #(0.5) 4'b1011;
        #20
        in1 = #(0.5) 4'b0000;
        #18
        in1 = #(0.5) 4'b1101;

        #20
        in1 = #(0.5) 4'b1001;
        #18
        in1 = #(0.5) 4'b1010;

    end

    bits_pos_detect u_bits_pos_detect(
        .clk      ( clk      ),
        .rst      ( rst      ),
        .in1      ( in1      ),
        .out_pos1 ( out_pos1 ),
        .out_pos2 ( out_pos2 ),
        .out_pos3  ( out_pos3  )
    );

endmodule
打赏
共2人已赏
一个努力写作的FPGA爱好者、从业者,CSDN博客专家,CSDN上万关注量,百万
评论
1个
内容存在敏感词
  • 易百纳技术社区
    给我一个支点,让我撬动地球~

    感谢分享

相关专栏
打赏作者
易百纳技术社区
李锐博恩
您的支持将鼓励我继续创作!
打赏金额:
¥1 易百纳技术社区
¥5 易百纳技术社区
¥10 易百纳技术社区
¥50 易百纳技术社区
¥100 易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区 微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

审核成功

发布时间设置
发布时间:

审核失败

失败原因
备注
Loading...
易百纳技术社区
确定要删除此文章、专栏、评论吗?
确定
取消
易百纳技术社区
易百纳技术社区
在专栏模块发布专栏,可获得其他E友的打赏
易百纳技术社区
回答悬赏问答,被题主采纳后即可获得悬赏金
易百纳技术社区
在上传资料时,有价值的资料可设置为付费资源
易百纳技术社区
达到一定金额,收益即可提现~
收益也可用来充值ebc,下载资料、兑换礼品更容易
易百纳技术社区
活动规则
  • 1.周任务为周期性任务,每周周一00:00刷新,上周完成的任务不会累计到本周,本周需要从头开始任务,当前任务完成后才可以完成下一个任务
  • 2.发布的专栏与资料需要与平台的板块有相关性,禁止注水,专栏/资料任务以审核通过的篇数为准
  • 3.任务完成后,现金奖励直接打款到微信账户;EBC/收益将自动发放到个人账户,可前往“我的钱包”查看;其他奖励请联系客服兑换
  • 4.每周最后三个任务将会有以下奖品掉落:社区热卖开发板、小米音响、视频年度会员、京东卡、华为手机等等
易百纳技术社区
升级提醒
易百纳技术社区

恭喜您由入门

社区送出礼品一份

请填写您的收件地址,礼品将在3个工作日寄出

易百纳技术社区