最近看到一个B站关注的UP主说最近看了一本书《数字IC设计入门》,说是工作之后看这本书对书中的内容有了更好的理解,于是我便看了一下这本书的目录,主要包含了一些基础知识以及SOC和总线的简要介绍,但是其中的内容都是十分关键的,于是便下单买了一本拿来学习。
自打接触数字电路和Verilog以来,就经常看到说Verilog是硬件描述语言,不是编程语言,在写Verilog的过程中,要做到心中有电路,可是在利用FPGA进行开发的过程中,却没能真正理解这句话的意思,依然没有考虑代码背后所对应的电路,大多数时候这样做也并没有不好的影响,直到后来有一次遇到严重的时许违例才对其中的道理略知一二。在看到本书第二章提取信号上升沿的时候,看到如下的代码:
assign a_fall = a_latch& (~a_latch_r)
然后联想起自己在做的时候采用的方法是
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
a_fall <= 0 ;
else if(a_latch_r==0 && a_latch==1)
a_fall <= 1 ;
else
a_fall <= a_fall ;
end
看似不大的差异实际对应的电路会是怎样的结果?接下来利用Vivado查看一下RTL级(寄存器传输级)以及综合后的电路结构。 我们采用如下两种编写方法,进行对比查看:
module syn(
input clk,
input rst_n,
input a,
input c,
output reg b,
output reg d,
input [1:0]addr,
output reg select,
input [1:0]addr2,
output reg select2
);
reg a_dl;
reg c_dl;
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
a_dl <= 0 ;
else
a_dl <= a ;
end
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
b <= 0 ;
else if(a_dl==0&&a==1)
b <= 1 ;
else
b <= b ;
end
第二种:
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
c_dl <= 0 ;
else
c_dl <= c ;
end
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
d <= 0 ;
else if(!c_dl&&c)
d <= 1 ;
else
d <= d ;
end
然后点击Open Elaborated Design, 便可以看到 RTL 层次结构如下所示:
我们可以发现,主要差距体现在触发器和与门之间的部分,一个采用了与非门,另一个采用了比较器。
但是Vivado综合后结构看起来是一样的,这是因为FPGA设计中的与门和很多其他电路都是有查找表LUT实现。
似乎这么看来差异并不是特比明显,接下来以下例来更加明显的展示二者区别:
第一种采用case语句实现:
always @(*)
begin
if(rst_n == 1'b0)
select <= 0 ;
else
case(addr)
2'b0:select<=0;
2'b01:select<=1;
2'b10:select<=0;
2'b11:select<=1;
default:select<=0;
endcase // addr
end
第二种采用if-else语句实现:
always @(*)
begin
if(rst_n == 1'b0)
select2 <= 0 ;
else if(addr2==2'd0)
select2 <= 0 ;
else if(addr2==2'd1)
select2 <= 1 ;
else if(addr2==2'd2)
select2 <= 0 ;
else if(addr2==2'd3)
select2 <= 1 ;
else
select2 <=0;
end
二者对应的RTL级结构图如下,在二者不同的部分中,第一种方法为一个四选一的选择器,第二种为四个二选一选择器级联,从这里就能非常明显的感受到其中的差异了,后者最后的面积是明显大于前者的。