Verilog编码规范
Verilog编码规范
本文为自己研究实践与翻阅资料比如华为规范书等所得,主要关于编码问题以及综合电路映射问题,规范自己写出复用性更好、综合性能更好、便于仿真的代码和电路。
第一部分 Verilog RTL规范#
1 、Module#
-
顶层模块应该只是模块间的互联,尽量避免再做逻辑。如不能再出现对reg变量赋值等,这样做的目是为了更有效的综合。因为在顶层模块中出现中间逻辑,Synopsys 的design compiler 就不能把子模块中的逻辑综合到最优。
-
每一个模块应在开始处注明文件名功能描述设计者设计时间及版权信息等,下面为sublime模版
// ------------------------------------------------------------------------- // Copyright (c) 2014-{YEAR} All rights reserved // -------------------------------------------------------------------- // Author : HiDark 1173296519@qq.com // File : {FILE} // Create : {DATE} {TIME} // Description : // Editor : tab size ({TABS}) // -------------------------------------------------------------------- module mymodule ( // Inputs input clk, input rst_n, // Outputs ); //----------------------------------------------------------------- // A //----------------------------------------------------------------- endmodule
-
模块输出寄存器化
对所有模块的输出加以寄存如图1使得输出的驱动强度和输入的延迟可以预测从而使得模块的综合过程更简单.
输出驱动的强度都等于平均的触发器驱动强度
2、选择器(if-else、case、?:)#
- 优先级问题
-
if-else和?:都是生成优先级,多个2选1嵌套在一起,如下图左边。但是if 嵌套过多会导致速度很慢,路径延时很大,因此一般条件较少时适用,if else结构下最终综合的电路速度较慢,面积小。case综合为 n选1 mux电路,如下图右边。没有优先级,因此延时小,但占用面积较大。
-
通常使用case 语句要比if语句快。优先编码器的结构仅在信号的到达有先后时使用,优先级可以用来优化关键路径的时序。条件赋值语句也能综合成多路复用器,而case 语句仿真要比条件赋值语句快。
- 资源共享问题
- 条件算子中不存在 资源共享 ,如下,综合必须使用两个加法器;
z = (cond) ? (a + b) : (c + d);
- 而等效的条件if-then-else语句则可以资源共享 如下只要加法器的输入端复用,就可以实现加法器的共享,使用一个加法器实现。
if (Cond)
z = a + b;
else
z = c + d;
3、宏参数Macro#
- 把 ``define` 都放在一个单独文件
parameter
和localparam
4、时钟#
- 不要用为了起方便名字对时钟
assign
,否则可能会在综合时插入buf导致时序错误(吴斌老师讲的,但是没有试过) - 避免在RTL代码中使用门控时钟(Gated clock)。不利于移植,可能引起毛刺带来时序问题,同时对扫描链的形成带来问题。门控钟在低功耗设计中使用,并且借助工具生成。
- 避免在RTL级手工实例化时钟Buffer,后端物理设计才考虑
5、复位#
-
同步复位。 由于大多数触发器都没有同步复位端,因此同步复位会多引入一级逻辑,路径延时增大。而且同步复位依赖时钟,如果复位时间少一个时钟或时钟未使能、未工作就会失效。
-
异步复位。优点是不依赖时钟,但容易引入毛刺,因此为消除异步复位的缺陷,复位电路往往会采用“异步复位、同步释放”的设计方法,引入两级寄存器。
复位时与异步复位相同,都是立刻复位,在释放时经过两级触发器,确保不会产生由复位信号释放产生的亚稳态。典型代码如下
// 引自 https://zhuanlan.zhihu.com/p/405938699?utm_id=0 module areset_srelease( input rstn, //异步复位信号 input clk, //时钟 input din, //输入数据 output reg dout //输出数据 ); reg rstn_r1, rstn_r2; always @ (posedge clk or negedge rstn) begin if (!rstn) begin rstn_r1 <= 1'b0; //异步复位 rstn_r2 <= 1'b0; end else begin rstn_r1 <= 1'b1; //同步释放 rstn_r2 <= rstn_r1; //同步打拍,时序差可以多延迟几拍 end end //使用 rstn_r2 做同步复位,复位信号可以加到敏感列表中 always @ (posedge clk or negedge rstn_r2) begin if (!rstn_r2) dout <= 1'b0; //同步复位 else dout <= din; end endmodule
被表述为以下综合电路
时序图:
Wavedrom 代码
{ signal: [ { name: "clk", wave: "P.............................",period: 2}, { name: "rstn", wave: "1..0.1......................."}, { name: "din", wave: "1...........",}, { name: "rstn_r1", wave: "1..0..1....................."}, { name: "rstn_r2", wave: "1..0....1....................."}, { name: "dout", wave: "1..0......1.",}, ], config: { hscale: 1.5 }}
-
取消复位。
由于复位会引入逻辑,消耗面积和速度。因此在复位不影响功能的正确实现下,数据流或流水线级可取消复位,提高性能.
第二部分 设计方法规范#
1、设计优先#
由于工艺越来越先进,面积要求越来越不明显,速度逐渐成为设计的首要要求。
2、可复用性编码#
2.1 命名原则#
-
为开发指定命名,并以文档形式记录,应用到全局中
-
变量名、端口名和信号名小写
-
常量和用户定义类型大写
-
用有意义的名字,如地址
ram_addr
而不用ra
-
来自同一驱动器源的所有时钟信号使用相同名字(吴斌老师强调过)
-
对于低电平有效信号,加后缀
_n
,如rst_n
-
数字代表英文字母,例如 2 代表 to, 4 代表 for, 简化信号名。
-
尽可能用下表命名
约定 用途 _r 寄存器输出,延迟打拍用r1、r2 _a 异步信号 _pn 在第n步{phase}使用的信号 _next 锁存前信号 _z 三态
第三部分 Verilog仿真规范#
-
时钟生成用阻塞赋值 =
initial begin clk = '0; forever #(5) clk = ~clk; end
-
激励赋值用 <=
如果用=,并且是@(posedge clk) a = 1;实际结果会是立刻变化,即上升沿之前变化。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· 【全网最全教程】使用最强DeepSeekR1+联网的火山引擎,没有生成长度限制,DeepSeek本体