FPGA逻辑设计回顾(3)多比特信号上升沿检测的设计方式与陷阱?

举报
李锐博恩 发表于 2021/07/15 00:28:19 2021/07/15
【摘要】 前言 注:本文首发自FPGA逻辑设计回顾(3)多比特信号上升沿检测的设计方式与陷阱? 在总结本文最后的多比特上升沿检测之前,我们先把备用知识讲清楚,摊开来,以免造成模糊不清的默许! 逻辑运算符与位元运算符 从表面上来看,逻辑运算符与位元运算符之间的区别就是一个符号写法的问题,例如:&&,&,||,|,!,~ 事实上,我们应该真正的从含义上理...

前言

注:本文首发自FPGA逻辑设计回顾(3)多比特信号上升沿检测的设计方式与陷阱?
在总结本文最后的多比特上升沿检测之前,我们先把备用知识讲清楚,摊开来,以免造成模糊不清的默许!

逻辑运算符与位元运算符

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

  • 所谓的逻辑运算符就是逻辑上的运算,那它的结果也就是真和假之分,即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

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

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

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

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

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

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

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

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

多比特信号上升沿检测

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

除此之外,还有对于对比特的设计方式有如下几种,设计到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

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

至于使用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

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

以及普通的实现方式:

 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

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

也能实现同样的功能,例如对其仿真示意图一致:
多种方式实现的仿真波形

生成的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

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

测试平台:

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

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

文章来源: reborn.blog.csdn.net,作者:李锐博恩,版权归原作者所有,如需转载,请联系作者。

原文链接:reborn.blog.csdn.net/article/details/111794561

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。