《SystemVerilog硬件设计及建模》笔记6

第6章 SystemVerilog过程块、任务和函数

        Verilog中的always过程块是通用的,不能反映准确的设计意图,SystemVerilog增加了硬件专用类型的过程块,能够准确反映设计意图,使模拟、综合、形式检查、lint检查等EDA工具精确地完成任务,并使不同的EDA工具保持一致性;SystemVerilog对任务和函数进行了许多改进,增强了任务和函数对大规模、复杂设计建模能力。

        本章内容包括

        组合逻辑过程块

        锁存逻辑过程块

        时序逻辑过程块

        任务与函数的改进

6.1 Verilog通用目的always过程块

        always过程块是一个能重复执行的语句块的无限循环,循环包括了时间控制或事件控制以使模拟时间向前推进。

敏感表

always过程块的开头所写的边沿敏感的事件控制条件通常被认为是这个过程块的敏感表,只有敏感表中的信号发生变化时,过程块才能被触发执行。

   always @(a, b) // sensitivity list
        begin
            sum = a + b;
            diff = a - b;
            prod = a * b;
        end

always过程块的一般用法

Verilog的always过程块用于对象建模。在RTL级always过程块可以用作组合逻辑、锁存逻辑和时序逻辑的建模。在更为抽象的建模中,也可以使用always过程块对算法逻辑进行行为建模而不必准确表达出该行为的具体实现方法。此外,always过程块还可以在测试平台中对时钟振荡器和需要在验证过程中重复执行的其他验证任务进行建模。

从always过程块推断具体实现

由于always过程块是通用的,加大软件工具特别是综合编译器和形式验证工具的负担。这些工具仅仅执行过程块中的语言是不够的。综合编译器和形式验证工具还必须推断该块描述的是硬件的类型--是组合逻辑。锁存逻辑,还是时序逻辑。

Verilog RTL综合的IEEE1364.1标准中给出了always过程块的组合逻辑、锁存逻辑和时序逻辑的综合指导方针,但并不要求强制执行,且always的模拟与综合不依照同样的语法规则,使得模拟与综合结果不一致!

使用通用always过程块。其中主要限制和规则有:

使用通用alway过程块表示组合逻辑的情况:

• Always关键字之后必须接一个边沿敏感的事件控制(@符号)。

 事件控件的敏感度列表中不能包含posedge或negedge约束。

• 敏感表必须列出过程块的所有输入。输入是指在该块读入并且在块外赋值的信号。

• 过程块不能包含任何其他的事件控件。

• 所有在过程块中赋值的变量必须随所有可能的输入组合变化而更新。

• 所有在此过程块中赋值的变量不能在其他过程块中再次赋值。

使用通用alway过程块表示锁存逻辑的情况:

• Always关键字之后必须接一个边沿敏感的事件控制(@符号)。

• 事件控件的敏感度列表中不能包含posedge或negedge约束。

• 敏感表必须列出过程块的所有输入。输入是指在该块读入并且在块外赋值的信号。

• 过程块不能包含任何其他的事件控件。

• 在过程块中赋值的变量至少一个变量不能被某些输入条件更新。

• 所有在此过程块中赋值的变量不能在其他过程块中再次赋值。

使用通用alway过程块表示时序逻辑的情况:

• Always关键字之后必须接一个边沿敏感的事件控制(@符号)。

• 事件控件的敏感度列表中包含posedge或negedge约束。

• 敏感表必须列出过程块的所有输入。输入是指在该块读入并且在块外赋值的信号。

• 过程块不能包含任何其他的事件控件。

• 所有在此过程块中赋值的变量不能在其他过程块中再次赋值。

6.2 SystemVerilog特有的过程块

        SystemVerilog增加了3个能够明确表示设计意图的过程块:always_comb、always_latch和always_ff;如果特有的过程块的内容与其相应逻辑不匹配,软件工具会发出警告信息;特有过程块与always一样是无限循环的,在特有块中加入了限制建模类型的句法和语义的规则,从而与IEEE1364.1综合标准一致。

        通过使用always_comb、always_latch和always_ff过程块,不仅是软件工具,其它查看或维护此模型的设计人员也能够很清楚地了解设计思路,提高了代码的文档性。

        注意:并不是使用了always_comb、always_latch、always_ff过程块就一定会综合出对应的组合、锁存、触发电路!


6.2.1 组合逻辑过程块

        always_comb过程块表示建立组合逻辑模型。always_comb能推断出其敏感表,推断的敏感表包括所有被过程块读取并在块外赋值的信号及过程块中调用函数的有所信号(但只被函数赋值和读取的临时变量除外)。

    always_comb

        if (!mode)
               y = a + b;
        else
              y = a - b;

always_comb禁止出现共享变量,赋值的变量不能在其他过程块中再次赋值。

无歧义的设计意图

alwags_comb过程块的主要优点是设计意图明确

    always @(a, en)
        if (en) y = a;   //锁存逻辑

    always_comb
        if (en) y = a;   //组合逻辑

零时刻自动求值

        always_comb与always过程块的另一个不同之处是在所有initial和always过程块启动后,always_comb过程块会在仿真的零时刻自动触发,不过敏感列表中的信号是否发生变化。always_comb这种特殊的语义确保了组合逻辑在零时刻产生与输入相对应的输出结果。特别是在使用缺省值为逻辑0的两态逻辑变量建模时,零时刻,复位信号很可能不会引起组合逻辑的敏感表中的信号变化。

例6.1 State和NextStae缺省默认值都是WAITE。always @(State)中State值一直不变。

这个锁定问题时由于Verilog敏感表工作方式造成的一个仿真异常现象,在实际硬件甚至硬件的门级模型中都不会出现。

always_comb和always@*的比较

always@*没有组合逻辑语义,只能是在敏感表有变化时才执行。

always@*仅仅是一种推断事件控制列表信号的语句,可在过程块内部使用,但这种用法不能综合。

always@*推断的敏感表可能不完整,只能对任务/函数调用的参变量敏感,不能推断出函数调用引用的模块级信号。

 6.2.2 锁存逻辑过程块

        always_latch过程块表示描述的是基于锁存器的逻辑。always_latch过程块的语义规则与always_comb一样,推断敏感表的规则一样;在锁存逻辑中,过程块的输出变量不需要对所有可能的输入条件响应;同样always_latch中赋值的变量不能再次在其它过程块被赋值;always_latch过程块也会在0时刻自动执行一次。

6.2.3 时序逻辑过程块

        always_ff过程块描述时序逻辑,always_ff过程块的敏感表必须列出,以确定时序逻辑的置位/复位是同步还是异步的,如果代码不能综合出时序逻辑时,软件工具能够报告警告信息。

        always_ff过程块要求明确指定敏感表中的信号是posedge还是negedge,这是对时序逻辑敏感列表的综合要求,以确保模拟结果与综合结果一致。

        always_ff过程块禁止在块开头以外的地方使用事件控制。

6.2.4 综合指导

        always_comb、always_latch和always_ff过程块都是可以综合的。无论是仿真还是综合,使用这些过程块优于使用always过程块,并且这些过程块会要求仿真器和其他软件工具按照综合编译器要求的规则进行检查。这样使用always_comb、always_latch和always_ff过程块可以在实际综合开始前,即在设计流程的较早器避免一些建模的潜在错误。

6.3 对任务和函数的改进

6.3.1 任务和函数的隐式语句组

        Verilog中,如果任务或函数包含多条语句时必须写在begin … end之间,对任务也允许使用fork … join结构。对SystemVerilog则可以省略begin … end,省略后,任务或函数中的语句将会顺序执行。

        function states_t NextState(states_t State);

            NextState = State; // default next state

            case (State)

                WAITE: if (start) NextState = LOAD;

                LOAD: if (done) NextState = STORE;

                STORE: NextState = WAITE;

            endcase

        endfunction

6.3.2 返回函数值

SystemVerilog增加了return语句,用于返回数值。 return语句的优先级高于用函数名返回值。

        function int add_and_inc (input int a, b);
                add_and_inc = a + b;
                return ++add_and_inc;
        endfunction

6.3.3 在任务和函数结束前返回

Verilog必须到达任务或函数结尾才能退出。

在SystemVerilog中,使用return语句可以在执行流的任何时刻推迟任务或函数

Verilog:

        function automatic int log2 (input int n);
            if (n <=1)
                log2 = 1;
            else begin // skip this code when n<=1

                        log2 = 0;
                        while (n > 1) begin
                            n = n/2;
                            log2 = log2+1;
                    end
            end
        endfunction

SystemVerilog:

        function automatic int log2 (input int n);
            if (n <=1) return 1; // abort function
            log2 = 0;
            while (n > 1) begin
                n = n/2;
                log2++;
              end
        endfunction

6.3.4 空函数

 在verilog中,函数必须有返回值。SystemVerilog增加了一个类似C语言的类型--viod。函数可以声明为viod数据类型,表示函数没有返回值。

与通常函数语法和语义限制一样,如不能包含任何类型的延迟或事件控制,也不能使用非阻塞赋值语句。

void函数可以象任务一样被调用。

void函数克服了函数不能调用任务的缺陷。

void函数可以有output和inout的形式参数。

    typedef struct {
        logic valid;
        logic [ 7:0] check;
        logic [63:0] data;
     } packet_t;


    function void fill_packet (
        input logic [63:0] data_in,
        output packet_t data_out );
        data_out.data = data_in;
        for (int i=0; i<=7; i++)
            data_out.check[i] = ^data_in[(8*i)+:8];
            data_out.valid = 1;
    endfunction

综合指导原则

提示 在可综合的模块中,使用空函数来替代任务。

空函数的优势在用可以像任务一样被调用。

6.3.5 使用名称传递任务/函数的参数

任务或函数调用时,Verilog只允许按照任务或函数的形式参数的顺序来传递参数,否则就会出现编码错误。

SystemVerilog可以使用形式参数的名称而不是顺序来传递参数值,以减少错误。

    always @(posedge clock)
            result <= divide(b, a);              //Verilog调用风格
    function int divide (input int numerator,denominator);
        if (denominator == 0) begin
            $display("Error! divide by zero");
            return 0;
        end
        else
        return numerator / denominator;
     endfunction

-----------------------------

    always @(posedge clock)
        result <= divide(.denominator(b),.numerator(a) );  // SystemVerilog style function call

6.3.6 增强型函数形式参数

Verilog的函数只允许有input,唯一的输出就是它的返回值。

    // Verilog style function formal arguments
    function [63:0] add (input [63:0] a, b);
    ...
    endfunction

SystemVerilog的函数可以象任务一样声明input、output和inout类型的形式参数,扩展了函数的建模范围。

    // SystemVerilog style function formal args

    function [63:0] add (input [63:0] a, b, output overflow);

        {overflow,add} = a + b;

    endfunction

对带输出的函数调用限制

从综合角度考虑,有output或inout参数的函数不能在以下情况中调用:

  • 事件表达式
  • 使用过程持续赋值的表达式
  • 不在过程语句内的表达式

6.3.7 无形式参数的函数

 Verilog允许任务有零个或任意个形式参数,Verilog要求函数至少有一个输入形式参数,即使参数不使用。

SystemVerilog允许函数没有形式参数。

6.3.8 形式参数的缺省方向和类型

Verilog中函数的每个形式参数必须显式声明为input。任务的每个形式参数必须显式声明为input、output或inout,方向声明后面可以跟着逗号分隔的参数列表。任务或函数中所有的形式参数默认是reg型,除非显示声明为其他的数据类型。

SystemVerilog简化了任务和函数声明语法,形式参数的缺省方向为input,缺省类型为logic

        function int compare (int a, b);
            ...
        endfunction
        // a and b are inputs, y1 and y2 are outputs
        task mytask (a, b, output y1, y2);
        ...
        endtask

6.3.9 缺省的形式参数

SystemVerilog允许任务与函数为每个形式参数设置一个可选的缺省值,设定缺省值的语法与设置变量初始值的语法类似。

    function int incrementer(int count=0, step=1);
        incrementer = count + step;
    endfunction

    always @(posedge clock)
        result = incrementer( data_bus );

缺省的形式参数允许任务或函数调用时只传递与该次调用相关的参数。

Verliog要求任务或函数调用时的参数表达式数目必须和任务或函数的形式参数数目相符。

SystemVerilog允许任务或函数调用时的参数表达式数目小于任务或函数的形式参数的数目。如果对没有缺省形式参数值的任务或函数调用时不传递值给形式参数,系统会报告一个错误。

6.3.10 数组、结构体和联合体作为形式参数

 SystemVerilog允许压缩或非压缩数组、结构体或联合体传递进/出任务和函数。

对于结构体和联合体,形式参数必须定义为用户自定义类型。

压缩数组在传递任务或函数时被看做向量。如果调用的压缩数组参数宽度和形式参数不匹配,该向量就会按照Verilog向量赋值的规则进行截断或扩展。

非压缩数组传递给任务或函数的调用数组参数必须与数组形式参数定义的结构和元素类型精准匹配。

    typedef struct {
        logic valid;
        logic [ 7:0] check;
        logic [63:0] data;
    } packet_t;

    function void fill_packet (
                                            input   logic [7:0] data_in [0:7],  // array arg
                                            output packet_t data_out );       // structure arg
        for (int i=0; i<=7; i++) begin
                data_out.data[(8*i)+:8] = data_in[i];
                data_out.check[i] = ^data_in[i];
        end
        data_out.valid = 1;
    endfunction

6.3.11 用引用取代复制来传递参数值

        当调动任务或函数时,系统会将输入值复制到任务或函数中。于是这些值变为任务或函数的局部数值。当任务或函数执行到末尾返回时,系统在将所有的输出复制到任务或函数调用程序。

        Verilog通过使用外部名称方式隐含传递参数给任务或函数。Verilog的任务或函数可以引用没有传递进来的信号。对于函数,不必指定形式参数,对函数的调用也不必列出传递给函数的信号。对于任务,引用的外部的信号使任务在外部信号的值发生变化时能够被察觉到,而且使任务内的变化能在任务执行完成前被任务外部感知。

        SystemVerilog可以显式地通过引用的方法传递参数给任务或函数,在形式参数说明中,用ref代替input、output和inout方向关键词。

ref形式参数是实际值的别名,通过引用传递,变量只需要在调用部分声明,而不需要在任务或函数描述时再次声明,这样,任务或函数只在它调用的范围内引用这个变量。

注意:只有自动函数或任务才可以具有ref参数。

下例中,形式参数data_in是函数调用模块chip中数组raw_data在任务中的一个别名,而形式参数data_out是chip中的data_packet结构体的一个别名。

只读引用参数

        通过将形式参数说明为const ref类型,只允许对引用对象进行读操作,而禁止任务或函数改动对象内容。

            function automatic void fill_packet (
                    const ref logic [7:0] data_in [0:7],
                    ref packet_t data_out );
                    ...
            endfunction

任务ref参数对变化敏感

        任务ref参数的一个重要特点是任务中的逻辑会对信号在调用程序内发生的变化敏感,由于函数必须零延时执行,所有函数ref参数不能包含对变化敏感的时间控制

        任务ref参数可以读取当前值。

        任务ref参数可以立即传播变化而不是等到任务结束时才反映。

对使用ref参数进行函数调用的限制

与带有输出形参函数调用限制相同,不能被以下几种情况调用:

  • 事件表达式
  • 使用过程持续赋值的表达式
  • 不在过程语句内的表达式 

6.3.12 命名的任务和函数结尾

SystemVerilog允许在任务和函数的结尾处用关键字endtask和endfunction后面跟任务名函数名。结尾处指定的名称必须与对应的任务或函数的名称一致。

        function int add_and_inc (int a, b);
            return a + b + 1;
        endfunction : add_and_inc


        task automatic check_results (
                                                        input packet_t sent,
                                                        ref packet_t received,
                                                        ref logic done );
                    static int error_count;
                    ...
        endtask: check_results

6.3.13 空任务和函数

        Verilog要求任务和函数至少要包含一条语句(即使空的begin...end语句组也可以)。

        SystemVerilog允许任务和函数不包含任何语句。空函数将返回表示函数名的隐含变量的当前值。

        空任务和空函数在非完整代码中预留出了空间。对于自顶向下的设计流程而言,可以在模块的描述中创建空任务和函数作为模型文件,在后面的设计中逐步将内容填写完整。

6.4小结

         本章介绍了SystemVerilog在Verilog标准的基础上,新增了三个特殊的过程块always_comb、always_latch和always_ff。这些特殊的过程块提高了硬件建模,特别是对可综合的RTL级建模的准确性和简易性。另一方面,使设计意图--过程块描述哪种逻辑类型更加明确。

        SystemVerilog对Verilog的任务和函数进行了一系列的改进,包括对Verilog语义和语法规则的简化级一些新的使用方式。所有的这些改进都使规模更大、复杂程度更高的设计建模变得更为快捷,代码量也变得更少。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值