您的位置:首页 > 编程语言

FPGA综合过程中应该注意的几个代码风格的问题

2017-11-03 10:43 531 查看
决策树
优先级译码电路

避免滥用锁存器Latch

什么是可综合的HDL
阻塞赋值与阻塞赋值

for循环

组合逻辑环路

设计的项目配置
模块划分
数据链路与控制模块

时钟和复位模块

多重实例化

参数化设计
宏定义

可变参数

决策树

1.优先级译码电路

  HDL描述中的if/else结构在综合后会形成优先级电路,越先描述的条件有更高的优先级

module regwrite(
output reg rout,
input      clk,
input [3:0] in,
input [3:0] ctrl);
always @(posedge clk)
if(ctrl[0])      rout <= in[0];
else if(ctrl[1])   rout <= in[1];
else if(ctrl[2])   rout <= in[2];
else if(ctrl[3])   rout <= in[3];
endmodule




  然而如果用case结构,各条件就是平级的,比如下面描述的电路,所有的路径是并行的关系:

module regwrite(
output reg  rout,
input       clk,
input [3:0] in,
input [3:0] ctrl);
always @(posedge clk)
case(1)
ctrl[0]: rout <= in[0];
ctrl[1]: rout <= in[1];
ctrl[2]: rout <= in[2];
ctrl[3]: rout <= in[3];
endcase
endmodule




2.避免滥用锁存器Latch

  锁存器是由使能信号En控制透明还是不透明的,由于锁存器不利于静态时序分析而且可能产生亚稳态,这对整个设计都是灾难性的,所以在FPGA设计中同行避免使用锁存器,用触发器替代这一部分的功能。但是经常会因为不正确的变成风格无意间在设计中写出了锁存器。通常情况下是因为条件判断语句没有包含所有条件

  一种解决方法是在HDL的条件判断语句最后加一种default情况,可以确保所有情况都被描述到如何置位

module regwrite(
output reg  rout,
input       clk,
input [3:0] in,
input [3:0] ctrl);
always @(posedge clk)
case(1)
ctrl[0]: rout <= in[0];
ctrl[1]: rout <= in[1];
ctrl[2]: rout <= in[2];
ctrl[3]: rout <= in[3];
default: rout <= 0;  //描述默认情况避免出现锁存器
endcase
endmodule


  这种方法虽然有效避免了Latch产生,但是default条件导致了额外的逻辑资源浪费,为此可以把default条件提前进一步优化

module regwrite(
output reg  rout,
input       clk,
input [3:0] in,
input [3:0] ctrl);
always @(posedge clk) begin
rout <= 0;
case(1)
ctrl[0]: rout <= in[0];
ctrl[1]: rout <= in[1];
ctrl[2]: rout <= in[2];
ctrl[3]: rout <= in[3];
endcase
end
endmodule


## 3.多控制路径

  对于一个寄存器最好不要有多个控制路径对它同时赋值。下面的例子就是一个典型的错误示范

module separated(
output reg oDat,
input      iClk,
input      iDat1,iDat2,iCtrl11,iCtrl12);
always @(posedge iClk) begin
if(iCtrl12) oDat <= iDat2;
if(iCtrl11) oDat <= iDat1;
end
endmodule


什么是可综合的HDL

1.阻塞赋值与阻塞赋值

  阻塞赋值是指在一个模块内各赋值操作按顺序依次执行

  非阻塞赋值是指一个模块内所有赋值操作在下一个时钟周期同时执行。两种赋值操作在一个模块内不能混用

  通常建议在组合逻辑中用阻塞赋值,在时序逻辑中用非阻塞赋值

2.for循环

  不同于软件设计,HDL设计不能用for循环这样的循环结构,这样的设计往往是不能综合的,即使能综合也是以大量复制电路浪费资源为代价的,可以按下面的方法优化

module forloop(
output reg [7:0] PowerX,
output reg       Done,
input            Clk,Start,
input      [7:0] X,N);
integer          i;
always @(posedge Clk)
if(Start) begin
PowerX <= 1;
i      <= 0;
Done   <= 0;
end
else if (i < N) begin
PowerX <= PowerX*X;
i      <= i + 1;
end
else
Done   <= 1;
endmodule


3.组合逻辑环路

  在组合逻辑设计中带有环路也是典型的错误设计,由于没有时序期间延时,这种组合环路很可能在某种情况下出现震荡现象,比如下面一个设计

module combfeedback(
output out,
input  a);
reg    b;
assign out = b;
always @(a)
b = out ^ a;
endmodule


  可以在这种组合逻辑环路中插入时序器件,避免这种情况

设计的项目配置

  组织设计项目配置的目的是方面分模块管理项目,有利于提高代码的可读性和可重用性

模块划分

  模块划分是对项目进行模块和层级级别上的划分,这种划分往往是自底向上考虑的



数据链路与控制模块

  数据链路与控制模块最好被划分在不同的模块内,因为数据链路往往是设计的关键路径,需要floorplan布局优化达到最好的性能



时钟和复位模块

  好的设计风格在同一个模块内只有一种类型的时钟和一种类型的复位,但是由于实际应用的需要又会涉及到多时钟域和多种复位,所以需要用不同模块将多个时钟域和多种复位划分开

多重实例化

  多重实例化也是能提高代码可读和重用性的一种代码组织风格,允许程序员将顶层模块设计、架构设计和底层函数功能的设计分离开

参数化设计

宏定义

  参数和宏定义在很多情况下都是相似并且可以互相替换的。宏定义通常是用来定义全局变量,`define语句代表宏定义

`define CHIPID 8'bC9


  `ifdef的宏定义可以用来定义两种设计类型,然后在这两种设计类型中轻易的切换,比如ASIC设计和FPGA设计有略微的不同,可以用这种方式实现设计在这两种平台上的迁移

`ifdef ASIC
input TESETMODE
output TESTOUT
`endif

`ifdef FPGA
output DEBUGOUT
`endif


可变参数

  参数是用来定义不同的实例,可以再大小和位宽等方面对实例进行调整

module paramreg #(parameter WIDTH = 8)(
output reg [WIDTH-1:0] rout,
input                  clk,
input      [WIDTH-1:0] rin,
input                  rst);
always @(posedge clk)
if(!rst) rout <= 0;
else       rout <= rin;
endmodule


  这里的WIDTH是一个默认参数,在不对它进行特殊说明的时候,WIDTH就是8。可以用下面的方法调用这个带有默认参数的模块

paramreg #(2) r1(.clk(clk),.rin(rin),.rst(rst),.rout(rout));  //这个模块的WIDTH参数是2


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  综合 结构 fpga