赞
踩
好久没更新了,搬运一篇verilog的文章吧。
来源:《RISC-V CPU设计——蜂鸟E203处理器核的RTL代码风格》
蜂鸟 E203 处理器核采用一套统一的 Verilog RTL 编码风格(coding style), 该编码风格来自严谨的工业级开发标准, 其要点如下。
下面分别予以详述。
寄存器是数字同步电路中基本的单元。当使用 Verilog 进行数字电路设计时, 最常见的方式是使用 always块语法生成寄存器。本节介绍蜂鸟 E203 处理器核推荐的原则, 本原则来自严谨的工业级开发标准。
对于寄存器,避免直接使用always块编写,而应该采用模块化的标准 DFF 模块进行例化。示例如下所示, 除时钟(clk)和复位信号(rst_n)之外, 一个名为flg_dfflr的寄存器还有使能信号 flg_ena和输入(flg_nxt) /输出信号(flg_r)。
- wire flg_r;
-
- wire flg_nxt= ~flg_r;
-
- 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 模块例化的好处以下:
标准 DFF 模块是一系列不同的模块,列举如下:
标准 DFF 模块内部则使用 Verilog语法的 always块进行编写, 以 sirv_gnrl_dfflr为例,代码如下所示。由于 Verilog if-else 语法不能传播不定态, 因此对于 if条件中 lden信号为不定态的非法情况使用断言(assertion) 进行捕捉。
- module sirv_gnrl_dfflr # (
-
- parameter DW= 32
-
- ) (
-
- input lden,
-
- input [DW-1:0] dnxt,
-
- output [DW-1:0] qout,
-
- input clk,
-
- input rst_n
-
- );
-
- reg [DW-1:0] qout_r;
-
- //使用always块编写寄存器逻辑
-
- always @(posedge clk or negedge rst_n)
-
- begin : DFFLR_PROC
-
- if (rst_n == 1'b0)
-
- qout_r <= {DW{1'b0}};
-
- else if (lden == 1'b1)
-
- qout_r <= dnxt;
-
- end
-
- assign qout = qout_r;
-
- //使用 assertion 捕捉 lden信号的不定态
-
- 'ifndef FPGA_SOURCE//{
- 'ifndef SYNTHESIS//{
-
- sirv_gnrl_xchecker # ( //该模块内部是使用SystemVerilog编写的断言
-
- . DW(1)
-
- ) u_sirv_gnrl_xchecker(
-
- . i_dat(lden),
-
- . clk (clk)
-
- );
-
- 'endif//}
- 'endif//}
-
- endmodule
-
- //sirv_gnrl_xchecker模块的代码片段
-
- //此模块专门捕捉不定态,一旦输入的i_dat出现不定态, 则会报错并终止仿真
-
- module sirv_gnrl_xchecker # (
-
- parameter DW= 32
-
- ) (
-
- input [DW-1:0] i_dat,
-
- input clk
-
- );
-
- CHECK_THE_X_VALUE:
-
- assert property (@(posedge clk)
- ((^(i_dat)) !== 1'bx)
- )
-
- else $fatal ("\n Error: Oops, detected a X value!!! This should never happen. \n");
-
- endmodule
Verilog中的 if-else 和 case 语法存在两大缺点。
为了规避这两大缺点, 蜂鸟E203 处理器核推荐使用 assign 语法进行代码编写, 本原则来自严谨的工业级开发标准。
Verilog的 if-else不能传播不定态, 以如下代码片段为例。假设a的值为X(不定态),按照 Verilog语法它会将等效于a==0,从而让 out等于in2, 最终没有将X(不定态) 传播出去。这种情况可能会在仿真阶段掩盖某些致命的bug, 造成芯片功能错误。
- if(a)
-
- out = inl;
-
- else
-
- 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 语法会被综合成优先级选择电路, 面积和时序均没有得到充分优化, 如下所示。
- if(sell)
-
- out = in1[3:0];
-
- else if (sel2)
-
- out = in2[3:0];
-
- else if (sel3)
-
- out = in3[3:0];
-
- else
-
- out = 4'b0;
如果此处确实要生成一种优先级选择逻辑, 则推荐使用 assign 语法等效地写成如下形式, 以规避X(不定态)传播的问题。
- assign out = sell ? in1[3:0] :
-
- sel2 ? in2[3:0] :
-
- sel3 ? in3[3:0] :
-
- 4'b0;
而如果此处本来要生成一种并行选择逻辑,则推荐使用 assign语法明确地使用“与或”逻辑, 代码如下。
- assign out = ({4{sel1}} & in1[3:0])
-
- | ({4{sel2}} & in2[3:0])
-
- | ({4{sel3}} & in3[3:0]) ;
使用明确的assign语法编写的“与或”逻辑一定能够保证综合成并行选择的电路。
同理, Verilog 的 case 语法也会被综合成优先级选择电路,面积和时序均未充分优化。有的EDA 综合工具可以提供注释(例如 synopsys parallel_case 和full_case)来使综合工具综出并行选择逻辑,但是这样可能会造成前后仿真不一致的严重问题,从而产生重大的 bug。因此在实际的工程开发中, 注意以下两点。
其他编码风格中的若干注意事项如下。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。