Verilog编写规范(华为FPGA)

Verilog编写规范(华为FPGA)

1.命名规则

  • 用有意义而有效的名字

    有效的命名有时并不是要求将功能描述出来,如:

for(i = 0;i < 1024;i = i + 1)
    Mem[i] <= #1 32'b0;    
  • 用连贯的缩写
    采用缩写时应该主义同一信号在模块中的一致性.如:
    Addr	address
    Pntr	pointer
    Clk		clock
    Rst		reset
  • 用最右边的字符下划线代表低电平有效,高电平有效的信号不得以下划线表示,短暂的有效信号建议采用高电平有效.如:
    Rst_
    Trdy_
    Irdy_
  • 大小写原则
    名字一般首字符大写,其余小写(但是parameter/integer定义的数值名可以全部大写),两个单词之间用下划线连接.如:
    Data_in
    Mem_wr
    Rd_req
    Sensor_ctrl
  • 全局信号名中应包含信号来源的信息.

    如:D_addr[7:2],这里的"D"指明了地址是解码模块(Decoder module)的地址.

  • 同一信号在不同层次应保持一致性

  • 自己定义的常数和类型等用大写表示.如:

    parameter CYCLE = 100;
  • 避免使用保留字

    如:in,out,x,z等不能够作为变量,端口或模块名

  • 添加有意义的后缀,使信号名更加明确,常用的后缀有:

    _Clk    时钟信号
    _next   寄存前的信号
    _z      连到三态输出的信号
    _f      下降沿有效的寄存器
    _xi     芯片原始输入信号
    _xo     芯片原始输出信号
    _xod    芯片的漏极开路输出
    _xz     芯片的三态输出
    -xbio   芯片的双向信号

2.Modules

  • 顶层模块应知识内部模块间的互联

    Verilog设计一般都是层次性的设计,也就是在设计中会出现一个或多个模块,模块间的调用在所难免.可把设计比喻成树,被调用的模块就是输液,没被调用的模块就是树根,那么在这个树根模块中,除了内部的互联和模块的调用外,尽量避免再做逻辑,如不能再出现对reg变量赋值等.这样做的目的是为了更有效的综合,因为在顶层模块VS出现中间逻辑,Synopsys的Design Compiler就不能把子模块中的逻辑综合到最优.

  • 每个模块应该在开始处注明文件名,功能描述,引用模块,设计者,设计时间,以及版权信息等.如:

    /*==============================================================================================*/
        Filename            :   RX_MUX.v
        Author              :   Dongyi Lin
        Description         :   
        Called by           :   Top module
        Revision History    :   22-05-27
        Revision            :   1.0
        Email               :   lindongyi@163.com
        Company             :   Hunan Institute of Advanced Sensing and Information Technology,
                                Xiangtan Univeristy
        Copyright           :   2022, Xiangtan University, All right reserved
    /*==============================================================================================*/
  • 不要对Input进行驱动,在module内不要存在没有驱动的信号,更不能在端口模块出现没有驱动的输出信号,避免再仿真或综合时产生warning,干扰错误定位.

  • 每行应限制在80个字符以内,以保持代码的清晰,没关和层次感.

    一条语句占用一行,如果超过80个字符则要换行.

  • 电路中调用的module名用Uxx表示.向量大小要表示清晰,采用基于名字的(name_based)调用,而非基于顺序的(order_based).

Instance    Uinstance2(
                .DataOut        (DOUT       ),
                .DataIn         (DIN        ),
                .Cs_            (Cs_		),	
            );
  • 时钟的上升沿或下降沿采样信号,不能一会上升沿,一会用下降沿.如果既要用上升沿又要用下降沿,应该分成两个模块设计.建议在顶层模块中对Clock做一个not门,
    在层次模块中如果要用时钟下降沿就可以用not门产生的Posedge Clk_,这样的好处是在整个设计中采用同一种时钟出发,有利于综合.

  • 在模块中增加注释.

    对信号,参量,引脚,模块,函数及进程等加以说明,便于阅读与维护.

  • Module名称要用大写表示,且应该与文件名保持一致.如:

    module DFF_ADSYNC_RST(
                            Reset,
                            Clk,
                            Data,
                            Qout
    );
  • 要严格芯片级模块的划分

    只有顶层包括IO引脚(pads),中间层是时钟产生模块,JTAG,芯片的内核(CORE),这样便于对每个模块加以约束仿真,对时钟也可以仔细仿真.

  • 模块输出寄存器化

    对所有的模块的数据加以寄存,使得输出的驱动强度和输入的延迟可以预测,从而使得模块的综合过程更简单.

3.Net and Register

  • 一个reg变量只能在一个always语句中赋值

  • 向量有效位顺序一边为从大到小
    推荐Data[4:0]这种格式的定义.

  • 对net和register类型的数据要做声明(在PORT中).

4.Expressions

  • 用括号表示执行的优先级,对读者更情绪,更有意义,如:
    if(alpha < beta && gamma >= delta)...就不如下面的更好
    if((alpha < beta) && (gamma >= delta))
  • 用函数(function)来代替表达式的多次重复

    这样在以后的版本升级时更便利,而且经常使用的一组描述可以写到一个任务(task)中

5.IF语句

  • 向量比较时,比较的向量要相等

    在向量比较时,verilog将位数小的向量做0扩展以使得他们的长度相匹配,它的自动扩展是隐式的.建议采用显式扩展.如:

        reg Abc [7:0];
        reg Bca [3:0];
        ......
        if(Abc == {4'b0,Bca})begin
        ......
        if(Abc == 8'b0)begin
    
  • 每一个if都应该有一个else与之对应
    没有else可能会使得综合出的逻辑和RTL级的逻辑不同,如果条件为假时不尽兴任何操作,则使用一条空语句,如:

        always@(Cond)begin
            if(Cond)
                DataOut <= DataIn;
        end
        //以上语句DataOut会综合出锁存器.
    
  • 如果变量在if-else语句或case语句中没有被完全赋值,则应该提前给变量一个缺省值,即:

    V1 = 2'b00;
    V2 = 2'b00;
    V3 = 2'b00; //给V1,V2,V3缺省值,在后面赋值变量时有一个默认值在
    if(a == b)begin
        V1 = 2'b01;
        V2 = 2'b10; //V3 is not assigned, so the default value of V3 is 2'b00;
    end
    else if(a == c)begin
        V2 = 2'b10;
        V3 = 2'b11; //V1 is not assigned, so the default value of V1 is 2'b00;
    end
    ...

6.case语句

  • case语句通常被综合为一级多路复用器,而if-then-else语句则综合为优先编码的串接的多个多路复用器.通常case语句比if语句快,有限编码结果在信号到达时有先后.case语句仿真比条件语句快.

  • 所有的case语句都应该有一个default case,且允许空语句出现如:

default:	;
	

7.Function

  • 在function的最后给function赋值,如:
    function CompareVectors;
	    input	[199:0]	Vector1;
	    input	[199:0]	Vector2;
	    input	[31:0]	Length;
	    //local variables
	    integer	i;
	    reg		Equal;
	    
	    begin
	        i = 0;
	        Equal = 1;	//给Equal赋初值
	        while((i < Length)&& Equal)begin
	            if(Vector2[i] !== 1'bx)begin
	                if(Vector1[i] !== Vector2[i])
	                    Equal = 0;
	                else;
	            end
	            i = i + 1;
	        end
	        CompareVectors = Equal; //赋值放在function的最后
	    end
	endfunction
  • 在function中避免使用全局变量

    否则容易引起HDL行为及仿真和门级仿真的差异.如:

    function ByteCompare;
        input   [15:0]  Vector1;
        input   [15:0]  Vector2;
        input   [7:0]   Length;

        begin
            if(ByteSel) //ByteSel是全局变量,如果在其他位置无意修改了,可能导致函数结果错误,
                        //所以最好在端口加以定义
                ...
            else
                ...
        end
    endfunction

注意,函数与任务的调用均为静态调用.

8.Assignment

  • Verilog有两种赋值方式:过程赋值(procedural)和连续赋值(continuous).过程复制用于过程代码(initial,always,task,function)中给reg和integer变量,time\realtime\real复制,而连续赋值一般给wire变量赋值.

  • always@(敏感表),敏感表要完整,如果不完整,将会引起仿真和综合结果不一致,如:

    always@(d or Clr)
        if(Clr)
            q = 1'b0;
        else if(e)
            q = d;
    //以上语句在行为及仿真时e的变化不会使仿真器进入该always块,导致仿真结果错误.
  • assign/deassign仅用于仿真加速,仅对寄存器有用.

  • force/release仅用于debug,对寄存器和线网型都有用.

  • 避免使用disable

  • 对任何reg赋值,都用非阻塞赋值(<=)代替阻塞赋值(=),reg的非阻塞赋值要加单位延迟,但异步复位可加可不加.如:

    always@(posedge Clk or negedge Rst_)begin
        if(!Rst_)begin
            Rega <= 0;  //non_blocking assignment
            Regb <= 0;
        end
        else if(Soft_rst_all)begin
            Rega <= #u_dly  0;  //add unit delay
            Regb <= #u_dly  0;
        end
        else if(Load_init)begin
            Rega <= #u_dly  init_rega;
            Regb <= #u_dly  init_rega;
        end
        else begin
            Rega <= #u_dly  Rega << 1;    
            Rega <= #u_dly  St_1;    
        end
    end //end Rega,Regb assignment

9.Combinatorial vs Sequential Logic

  • 如果一个事件持续几个时钟周期,设计时就用时序逻辑代替组合逻辑.如:
    wire Ct_24_e4;  //Ct_24_e4 last over several clock cycles
    assign  Ct_24_e4 = (count8bit[7:0] >= 8'h24) & (count8bit[7:0] <= 8'he4);

这种设计将综合处两个8bit加法器,而且会产生毛刺,对于这种电路,要采用时序设计,代码如下:

    reg Ct_24_e4;
    
    always@(posedge Clk or negedge Rst_)begin
        if(!Rst_)
            Ct_24_e4 <= 1'b0;
        else if(count8bit[7:0] > 8'he4)
            Ct_24_e4 <= #u_dly  1'b0;
        else if(count8bit[7:0] > 8'h23)
            Ct_24_e4 <= #u_dly  1'b1;
        else    ;
    end
  • 内部总线不要悬空.在default状态,要把他上拉或下拉.
    wire    OE_default;
    assign  OE_default = !(oe1 | oe2 | oe3);
    assign  bus[31:0] = oe1 ? Data1[31:0]:
                        oe2 ? Data2[31:0]:
                        oe3 ? Data3[31:0]:
                        OE_default ? 32'h0000_0000: //如果bus不等于oe1,oe2,oe3中的任何一个,
                                                    //若等于OE_default,则bus为32'h0即拉低,否则拉高为高阻态.
                        32'hzzzz_zzzz;

10.Macros 宏指令

  • 为了保持代码的可读性常用`define做常数声明

  • 把`define放在一个独立的文件中

    参数(parameter)必须在一个模块中定义,不要传递参数到模块(仿真测试向量例外);

    define可以在任何地方定义,要把所有的define定义在一个文件中(极少的一个两个define就不用了吧),在编译源代码时首先把这个文件读入.

    如果希望宏的作用于仅在一个模块中,就用参数来代替.

11.Comments

  • 对更新的内容要做注释

  • 在语法块的结尾做标记

  • 每一个模块都应该在模块开始处做模块级的注释(参考前面的标准模块头)

  • 在端口列表中出现的端口信号,都应该做简要的功能描述.

12.FSM 状态机

  • 状态机的状态分配

    Verilog描述状态机时必须有parameter分配好状态.

  • 组合逻辑和时序逻辑分开用不同的进程

    组合逻辑包括状态译码和输出,时序逻辑则是状态寄存器的切换

  • 必须对所有状态都处理,不能出现无法处理的状态,使状态机时空

  • Mealy状态机输出不仅取决于当前状态,还与输入有关;Moore状态机输出仅与当前状态有关.

    Mealy状态机的例子如下:

        ...
        reg CurrentState,NextState,Out1;
    
        parameter S0 = 0, S1 = 1;
    
        always@(posedge Clk or negedge Rst_)
            if(!Rst_)
                CurrentState <= S0;
            else
                CurrentState <= #u_dly NextState;
    
        always@(In1 or In2 or CurrentState)
            case(CurrentState)
                S0:begin
                    NextState <= #u_dly S1;
                    Out1 <= #u_dly 1'b0;
                end
                S1:begin
                    if(In1)begin
                        NextState <= #u_dly S0;
                        Out1 <= #u_dly !In2;
                    end
                end
            endcase
    

13.Module 编写示例

/*==============================================================================================*/
    Filename            :   module_name.v
    Author              :   Dongyi Lin
    Description         :   
    Called by           :   Top module
    Revision History    :   22-05-27
    Revision            :   1.0
    Email               :   lindongyi@163.com
    Company             :   Hunan Institute of Advanced Sensing and Information Technology,
                            Xiangtan Univeristy
    Copyright           :   2022, Xiangtan University, All right reserved
/*==============================================================================================*/

module module_name(
                    Output_ports,       //comment:port description
                    Input_ports,        //comment:port description
                    Io_ports,           //comment:port description
                    Clk_port,           //comment:port description
                    Rst_port            //comment:port description
);


//port declarations
    output      [31:0]      Dataout;
    input       [31:0]      Datain;
    inout                   Bi_dir_siginal;
    input                   input1;
                            input2;
    
    
//interrnal wire/reg declarations
    wire        [31:0]      internal_data;
    reg                     output_enable;
    
    
//module instantiations, Self-build module
    module_name1 Uinstance_name1(
                                    .port1(...);
                                    .port2(...);
    );
    
    module_name2 Uinstance_name2(
                                    .port1(...);
                                    .port2(...);
    );
    
    //TSC4000 cell
    DTC12V1(
            .Clk(Clk),
            .CLRZ(Clr),
            .D(Data),
            .Q(Qout)
    );


//always block
    always@(input2)begin
        ...
    end


//function and task definitions
    function [function_type] function_name;
        declarations_of_inputs;
        [declarations_of_local_variables];

        begin
            behavirol_statement;
            function_name = function_express;
        end
    endfunction

    
endmodule

14.Testbench 编写示例

下面是一个格雷码的测试模块:

    module TB_GRAY;

    reg             Clock;
    reg             Reset;

    wire    [7:0]   Qout;

    integer fout;           //输出文件指针
    parameter CYC = 20;

    GRAY DUT(
                .Clock(Clock),
                .Reset(Reset),
                .Qout(Qout)
    );

    initial begin
        Clock = 1'b0;
        Reset = 1'b1;
        #(5 * CYC)  Reset = 1'b0;
        #(5 * CYC)  Reset = 1'b1;
        #(5000 * CYC);
        $fclose(fout);
        $finish;
    end

    initial begin
        $shm_open("GRAY.shm");
        $shm_probe("AS");
        fout = $fopen("gray.dat");
    end

    always #(CYC) Clock = ~Clock;

    //输出数据到文件gray.dat
    always@(posedge Clock)begin
        $fwrite(fout,"%d %b\n", Qout, Qout);
    end

endmodule
  • 在testbench中避免使用绝对的时间,如#20,#15或#(CYC + 15)等,应该在文件前端使用parameter定义一些常量,使得时间的定义像#(CYC + OFF0)这样的形式,便于修改;

  • 观测结果可以输出到波形文件GRAY.shm,或数据文件gray.dat.生成波形文件可以使用simwave或gtkwave观测结果,比较直观;而生成数据文件则既可以快速定位,也可以通过编写的小程序工具对它进行进一步的处理;

  • 对大的设计的顶层方针,一般不要对所有信号进行跟踪,大的设计波形文件会很大,仿真时间也会延长,可以有选择的观测一些信号.

  • 1
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
华为verilog编程规范是一种用于指导和规范在华为公司进行verilog编程的规范文件。这个规范涵盖了verilog语言的各个方面,旨在提高代码的可读性、可维护性和可重用性。 首先,华为verilog编程规范明确了命名规范。在这个规范中,变量、模块、端口和信号的命名必须具有清晰、简洁和有意义的名称,以增加代码的可读性和理解性。 其次,规范指导了代码的结构和格式。它强调使用适当的缩进、注释和空格来增加代码的可读性,并提醒程序员不要使用过长的行或复杂的语句。 此外,华为verilog编程规范还规定了在编写代码时应遵循的规则。例如,规范要求程序员使用合适的数据类型、操作符、条件语句和循环结构,并强调避免使用过于复杂或技巧性的编程技巧,以确保代码的可维护性和可靠性。 另外,在设计验证方面,规范还提供了一些指导原则。例如,它鼓励使用模块化的设计方法,将功能划分为独立的模块,并使用规定的接口定义和进行模块间的通信。 最后,华为verilog编程规范还包括了一些特定的最佳实践和建议,帮助程序员编写高质量的代码。例如,规范建议使用有意义的信号命名和注释,以及进行适当的代码复用和模块化设计。 总之,华为verilog编程规范是一份详细的指南,旨在提高华为公司内部verilog代码的质量和一致性,确保代码的可读性、可维护性和可重用性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值