今天和大侠聊一聊FPGA研发设计相关规范,养成良好的个人习惯,代码设计风格等,都有助于日后发展。有哪些设计规范,从文档到工程建立等,聊一聊也许你会学到很多东西,少走很多弯路哦。话不多说,上货。
在团队项目开发中,为了使开发的高效性、一致性、正确性,团队应当要有一个规范的设计流程。按照规范来完成项目的设计开发工作,归类清晰明了的工程文件夹级别;项目应拥有良好风格和完整的文档,如设计思路与调试记录及器件选型等;代码书写高效,即统一的书写规范,文件头包含的信息完整,无论自己还是团队他人阅读便一目了然。
一、文档命名:
清晰的文档命名能够让我们思路非常的清晰,所以FPGA工程文件夹的目录要求层次鲜明,归类清晰。一个工程必须要有一个严整的框架结构,用来存放相关的文档、设计,不仅方便自己查看,也提高了项目的团队工作效率。
下面我们来举例说明:
一级文件夹为工程名<project>
二级文件夹多个:用以存放源文件<src>用以存放Testbench文件<sim>
用来存放设计思路相关类的文件<doc> 用来存放IP 核的文件<core>等等…
二、设计文档化:
将自己对设计的思路和调试记录在文档中,有利于以后对模块功能的添加和维护,并且在项目联调时方便项目组其他人员读代码。也方便不同厂家的FPGA之间移植,以及FPGA到ASIC的移植。如下图就是设计文档化的举例说明,文档介绍清晰,功能分析明确,有利于以后对模块功能的添加和维护。
三、编码风格:
每个module应存在于单独的源文件中,源文件名应与其所包含的模块名相同。每个设计都应该有一个完善的文件头,包含公司名称、设计者、设计时间、文件名、所属项目、模块名称及功能、修改记录及版本信息等内容。代码中的标识符采用传统C语言的命名方法,在单词之间用下划线分开,采用有意义,能反应对象特征、作用和性质的单词命名标识符,以此来增强程序的可读性。为避免标识符过于冗长,较长的单词可以适当的缩写。
四、代码规范:
低电平有效的信号,后缀名要用“_n”,比如低电平有效的复位信号“rst_n”
模块名和信号名统一小写
变量名要小写,如wire、reg、input、output等定义的
变量命名应按照变量的功能用英文简洁表示出来“xxx_xxx_xxx”,避免过长
采用大写字母定义常量参数,参数名小于20个字母,如parameter TIME=20
时钟信号应前缀“clk”,复位信号应前缀“rst”
对于顶层模块的输出信号尽量被寄存
三态逻辑避免在子模块使用,可以在顶层模块使用
到其它模块的接口信号按:输入、(双向)、输出的顺序定义端口
一个模块至少要有一个输入、输出,避免书写空模块
时钟事件的表达式用“posedge”或“negedge”的形式
If语句嵌套不能太多
建议不要使用include语句
建议每个模块添加timescale
代码中给出必要的注释
每个文件有个一头文件
每个文件只包含一个模块
模块名和文件名保持一致
异步复位,用if(xxx==1’b1) 或 if(xxx==1’b0)
同步时序逻辑的always block中有且只有一个时钟信号,并且在同一个沿动作
采用同步设计,避免使用异步逻辑
一般不要将时钟信号作为数据信号的输入
不要在时钟路径上添加任何buffer
在顶层模块中,时钟信号必须可见
不要采用向量定义的方式定义一组时钟信号
不要在模块内部生成时钟信号,使用pll产生
尽量不使用任务
不使用事件变量
不使用系统函数
不使用disable语句
尽量不使用forever、repeat、while等循环语句
不使用不可综合的运算符
在一个always语句中有且只能有一个事件列表
移位变量必须是一个常数
时序逻辑语块中统一使用非阻塞型赋值
组合逻辑语块中使用阻塞型赋值
五、注释规则
1、每个文件有一个文件头,文件头中注明文件名、功能描述、引用模块、设计者、设计时间、版权信息以及修改信息等;
2、对信号、参量、引脚、模块、函数及进程等加以说明,便于阅读与维护,如信号的作用、频率、占空比、高低电平宽度等。用“//”做小于1行的注释,用“/* */”做多于1行的注释。更新的内容要做注释,记录修改原因,修改日期和修改人。
六、模块规则
1、module例化名用u_xx_x标示;
2、建议给每个模块要加timescale;
3、不要书写空的模块,即:一个模块至少要有一个输入和一个输出;
4、为了保持代码的清晰、美观和层次感,一条语句应占用一行,每行限制在80个字符以内,如果较长(超出80个字符)则换行;
5、采用基于名字(name_based)的调用而不是基于顺序的(order_based)的调用;
6、模块的接口信号按输入、双向、输出顺序定义;
7、使用降序定义向量有效位顺序,最低位为0;
8、管脚和信号说明部分:一个管脚和一组总线占用一行,说明要清晰;
9、不要采用向量的方式定义一组时钟信号;
10、逻辑内部不对input进行驱动,在module内不存在没有驱动源的信号,更不能在模块端口存在没有驱动的输出信号,避免在elabarate和compile时产生warning;
11、在顶层模块中,除了内部的互连和module的例化外,避免在做其他逻辑;
12、出于层次设计和同步设计的考虑,子模块输出信号建议用寄存器;
13、内部模块端口避免inout,最好在最顶层模块处理双向总线;
14、子模块中禁止使用三态逻辑,可以在顶层模块使用;
15、禁止出现未连接的端口;
16、为逻辑升级保留的无用端口和信号要注释;对于层次化设计的逻辑,在升级中采用增量编译;建议采用层次化设计,模块之间相对独立。
七、线网和寄存器规则
1、锁存器和触发器不允许在不同的always块中赋值,造成多重驱动;
2、出于功能仿真考虑,非阻塞赋值应该增加单位延时,对于寄存器类型的变量赋值时,尤其要注意这一点;阻塞赋值不允许使用单位延时;
3、always语句实现时序逻辑采用非阻塞赋值;always语句实现的组合逻辑和assign语句块中使用阻塞赋值;4、同一信号赋值不能同时使用阻塞和非阻塞两种方式;
5、不允许出现定义了parameter、wire、reg却没有使用的情况;
6、不建议使用integer类型寄存器;
7、寄存器类型的信号要初始化;
8、除移位寄存器外,每个always语句只对一个变量赋值,尽量避免在一个always语句出现多个变量进行运算或赋值。
八、表达式规则
1、在表达式内使用括号表示运算的优先级,一行中不能出现多个表达式;
2、不要给信号赋“x”态,以免x值传递;
3、设计中使用到的0,1,z等常数采用基数表示法书写(即表示为1'b0,1'b1,1'bz或十六进制);
4、端口申明、比较、赋值等操作时,数据位宽要匹配。
九、条件语句规则
1、if 都有else和它对应,变量在if-else或case语句中所有变量在所有分支中都赋值;
7、不推荐嵌套使用5级以上if…else if…结构。
十、可综合部分规则
1、不要使用include语句;
十一、可重用部分规则
1、考虑未使用的输入信号power_down,避免传入不稳定态;
十二、同步设计规则
1、同一个module中,要在时钟信号的同一个沿动作;
十三、循环语句规则
1、在设计中不推荐使用循环语句;
十四、约束规则
1、对所有时钟频率和占空比都进行约束;
1、如果使用FPGA内部DCM和PLL时,应该保证输入时钟的抖动小于300ps,防止DCM/PLL失锁;如果输入时钟瞬断后必须复位PLL/DCM。
十六、代码编辑
由于不同编辑器处理不同,对齐代码使用空格,而不是tab键。
***可综合和不可综合详解***
(2)所有综合工具都不支持的结构:time,defparam,$finish,fork,join,initial,delays,UDP,wait。
(3)有些工具支持有些工具不支持的结构:casex,casez,wand,triand,wor,trior,real,disable,forever,arrays,memories,repeat,task,while。
建立可综合模型的原则
要保证Verilog HDL赋值语句的可综合性,在建模时应注意以下要点:
(1)不使用initial。
1、initial
2、events
3、real
不支持real数据类型的综合。
4、time
不支持time数据类型的综合。
5、force 和release
不支持force和release的综合。
6、assign 和deassign
不支持对reg 数据类型的assign或deassign进行综合,支持对wire数据类型的assign或deassign进行综合。
7、fork join
支持门级原语的综合,不支持非门级原语的综合。
9、table
不支持UDP 和table的综合。
10、敏感列表里同时带有posedge和negedge
如:always @(posedge clk or negedge clk) begin...end这个always块不可综合。
11、同一个reg变量被多个always块驱动
12、延时
以#开头的延时不可综合成硬件电路延时,综合工具会忽略所有延时代码,但不会报错。
如:a=#10 b;
这里的#10是用于仿真时的延时,在综合的时候综合工具会忽略它。也就是说,在综合的时候上式等同于a=b;
13、与X、Z的比较
可能会有人喜欢在条件表达式中把数据和X(或Z)进行比较,殊不知这是不可综合的,综合工具同样会忽略。所以要确保信号只有两个状态:0或1。