当前位置:   article > 正文

【Verilog】工业级RTL代码风格推荐_蜂鸟verilog编码风格

蜂鸟verilog编码风格

好久没更新了,搬运一篇verilog的文章吧。

来源:《RISC-V  CPU设计——蜂鸟E203处理器核的RTL代码风格》


        蜂鸟 E203 处理器核采用一套统一的 Verilog RTL 编码风格(coding style), 该编码风格来自严谨的工业级开发标准, 其要点如下。

  1. 使用标准 DFF 模块例化、生成寄存器。
  2. 推荐使用Verilog中的 assign语法替代if-else 和case语法。

下面分别予以详述。

1. 使用标准 DFF 模块例化生成寄存器

        寄存器是数字同步电路中基本的单元。当使用 Verilog 进行数字电路设计时, 最常见的方式是使用 always块语法生成寄存器。本节介绍蜂鸟 E203 处理器核推荐的原则, 本原则来自严谨的工业级开发标准。

        对于寄存器,避免直接使用always块编写,而应该采用模块化的标准 DFF 模块进行例化。示例如下所示, 除时钟(clk)和复位信号(rst_n)之外, 一个名为flg_dfflr的寄存器还有使能信号 flg_ena和输入(flg_nxt) /输出信号(flg_r)。

  1. wire flg_r;
  2. wire flg_nxt= ~flg_r;
  3. wire flg_ena = (ptr_r == ('E203_OITF_DEPTH-1)) & ptr_ena;

//此处使用例化 sirv_gnrl_dfflr 的方式实现寄存器, 而不使用显式的 always块

sirv_gnrl_dfflr #(1) flg_dfflrs(flg_ena, flg_nxt, flg_r, clk, rst_n);

使用标准DFF 模块例化的好处以下:

  1. 便于全局替换寄存器类型。
  2. 便于在寄存器中全局插入延迟。
  3. 明确的load-enable 使能信号(如下例中的 flg _ena)可方便综合工具自动插入寄存器级别的门控时钟以降低动态功耗。
  4. 便于规避 Verilog 语法中if-else 不能传播不定态的问题。(避免bug在仿真过程中被掩盖)

标准 DFF 模块是一系列不同的模块,列举如下:

  1. sirv_gnrl_dfflrs: 带 load-enable使能信号、带异步 reset信号、复位默认值为1 的寄存器。
  2. sirv_gnrl_dfflr:带load-enable使能信号、带异步 reset信号、复位默认值为0的寄存器。
  3. sirv_gnrl_dffl: 带 load-enable使能信号、不带 reset信号的寄存器。
  4. sirv_gnrl_dffrs:不带 load-enable使能信号、带异步 reset信号、复位默认值为1 的寄存器。
  5. sirv_gnrl_dffr:不带load-enable使能信号、带异步 reset信号、复位默认值为0的寄存器。
  6. sirv_gnrl_ltch: Latch模块。

        标准 DFF 模块内部则使用 Verilog语法的 always块进行编写, 以 sirv_gnrl_dfflr为例,代码如下所示。由于 Verilog if-else 语法不能传播不定态, 因此对于 if条件中 lden信号为不定态的非法情况使用断言(assertion) 进行捕捉。

  1. module sirv_gnrl_dfflr # (
  2. parameter DW= 32
  3. ) (
  4. input lden,
  5. input [DW-1:0] dnxt,
  6. output [DW-1:0] qout,
  7. input clk,
  8. input rst_n
  9. );
  10. reg [DW-1:0] qout_r;
  11. //使用always块编写寄存器逻辑
  12. always @(posedge clk or negedge rst_n)
  13. begin : DFFLR_PROC
  14. if (rst_n == 1'b0)
  15. qout_r <= {DW{1'b0}};
  16. else if (lden == 1'b1)
  17. qout_r <= dnxt;
  18. end
  19. assign qout = qout_r;
  20. //使用 assertion 捕捉 lden信号的不定态
  21. 'ifndef FPGA_SOURCE//{
  22. 'ifndef SYNTHESIS//{
  23. sirv_gnrl_xchecker # ( //该模块内部是使用SystemVerilog编写的断言
  24. . DW(1)
  25. ) u_sirv_gnrl_xchecker(
  26. . i_dat(lden),
  27. . clk (clk)
  28. );
  29. 'endif//}
  30. 'endif//}
  31. endmodule
  32. //sirv_gnrl_xchecker模块的代码片段
  33. //此模块专门捕捉不定态,一旦输入的i_dat出现不定态, 则会报错并终止仿真
  34. module sirv_gnrl_xchecker # (
  35. parameter DW= 32
  36. ) (
  37. input [DW-1:0] i_dat,
  38. input clk
  39. );
  40. CHECK_THE_X_VALUE:
  41. assert property (@(posedge clk)
  42. ((^(i_dat)) !== 1'bx)
  43. )
  44. else $fatal ("\n Error: Oops, detected a X value!!! This should never happen. \n");
  45. endmodule

2. 推荐使用 assign语法替代 if-else 和case语法

Verilog中的 if-else 和 case 语法存在两大缺点。

  1. 不能传播不定态。
  2. 会产生优先级的选择电路而非并行选择电路, 从而不利于优化时序和面积。

        为了规避这两大缺点, 蜂鸟E203 处理器核推荐使用 assign 语法进行代码编写, 本原则来自严谨的工业级开发标准。

        Verilog的 if-else不能传播不定态, 以如下代码片段为例。假设a的值为X(不定态),按照 Verilog语法它会将等效于a==0,从而让 out等于in2, 最终没有将X(不定态) 传播出去。这种情况可能会在仿真阶段掩盖某些致命的bug, 造成芯片功能错误。

  1. if(a)
  2. out = inl;
  3. else
  4. out = in2;

        而使用功能等效的 assign语法,如下所示, 假设a的值为X(不定态), 按照 Verilog语法,则会将X(不定态) 传播出去, 从而让out也等于X。通过对X(不定态) 的传播,开发人员可以在仿真阶段将bug彻底暴露出来。

assign out = a ? in1 : in2;

        虽然现在有的EDA 工具提供的专有选项(例如 Synopsys VCS 提供的 xprop 选项)可以将 Verilog 原始语法中定义的“不传播不定态”的情形强行传播出来, 但是一方面, 不是所有的EDA 工具支持此功能; 另一方面,在操作中此选项也时常被忽视, 从而造成疏漏。

          Verilog 的 Case语法也不能传播不定态,与问题一中的 if-else 同理。而使用等效的 assign 语法即可规避此缺陷。

          Verilog 的if-else 语法会被综合成优先级选择电路, 面积和时序均没有得到充分优化, 如下所示。

  1. if(sell)
  2. out = in1[3:0];
  3. else if (sel2)
  4. out = in2[3:0];
  5. else if (sel3)
  6. out = in3[3:0];
  7. else
  8. out = 4'b0;

         如果此处确实要生成一种优先级选择逻辑, 则推荐使用 assign 语法等效地写成如下形式, 以规避X(不定态)传播的问题。

  1. assign out = sell ? in1[3:0] :
  2. sel2 ? in2[3:0] :
  3. sel3 ? in3[3:0] :
  4. 4'b0;

  而如果此处本来要生成一种并行选择逻辑,则推荐使用 assign语法明确地使用“与或”逻辑, 代码如下。

  1. assign out  = ({4{sel1}} & in1[3:0])
  2. |  ({4{sel2}} & in2[3:0])
  3.   |  ({4{sel3}} & in3[3:0]) ;

使用明确的assign语法编写的“与或”逻辑一定能够保证综合成并行选择的电路。

          同理, Verilog 的 case 语法也会被综合成优先级选择电路,面积和时序均未充分优化。有的EDA 综合工具可以提供注释(例如 synopsys parallel_case 和full_case)来使综合工具综出并行选择逻辑,但是这样可能会造成前后仿真不一致的严重问题,从而产生重大的 bug。因此在实际的工程开发中, 注意以下两点。

  1. 应该明令禁止使用EDA 综合工具提供的注释(例如 synopsys parallel_case 和 full_case)。
  2. 应该使用等效的 assign 语法设计电路。

3.其他若干注意事项

其他编码风格中的若干注意事项如下。

  1.  由于带 reset信号的寄存器面积略大,时序稍微差一点, 因此在数据通路上可以使用不带reset信号的寄存器, 而只在控制通路上使用带 reset信号的寄存器。
  2.  信号名应该避免使用拼音,使用英语缩写, 信号名不可过长, 但是也不可过短。代码即注释, 应该尽量让开发人员能够从信号名中看出其功能。
  3.   Clock和 Reset信号应禁止用于任何其他的逻辑功能, Clock 和 Reset信号只能接入DFF,作为其时钟和复位信号。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/807910
推荐阅读
相关标签
  

闽ICP备14008679号