返回介绍

12.16.7 生成式间的值传递

发布于 2020-09-09 22:55:51 字数 3845 浏览 1072 评论 0 收藏 0

数据可以传递到将要产生的生成式,并且产生的生成式可以将数据返回到触发的非端接生成式。向生成式传递数据类似于一个任务调用,并且使用相同的语法。从一个生成式返回数据要求为生成式声明一个类型,这与函数声明使用相似的语法。

接收数据的生成式包含一个形式参数列表。为生成式声明参数的语法类似于一个任务原型;向生成式传递数据的语法与一个任务调用相同。

production ::= [function_data_type] production_name[(tf_port_list)]:rs_rule{| rs_rule};

production_item ::= production_identifier[(list_of_arguments)]

例如,前面的第一个例子可以按如下方式重写:

randsequence(main)
    main   : first second gen;
    first  : add | dec;
    second : pop | push;
    add    : gen("add");
    dec    : gen("dec");
    pop    : gen("pop");
    push   : gen("push");
    gen(string s = "done"):{$display(s);};
endsequence

在这个例子中,生成式gen接受一个字符串参数,其缺省值为"done"。五个其它的生成式产生这个生成式,每一个都具有一个不同的参数(main中的一个使用缺省知)。

一个生成式产生了一个范围,这个范围包含了这个生成式的所有规则和代码块。因此,传递给一个生成式的参数在整个生成式中有效。

返回数据的生成式要求一个类型声明。可选的返回类型位于生成式的前面。没有说明一个返回类型的生成式被假定为具有void类型。

我们使用带有一个表达式的return语句从一个生成式中返回一个值。当return语句被用于一个具有返回值的生成式中的时候,它必须说明一个具有正确类型的表达式,这就像一个非void函数一样。return语句将指定的表达式赋值到对应的生成式。返回值可以在触发返回一个值得生成式的产生的生成式的代码块中读取。在这些代码块中,返回值使用生成式名字加上一个可选的索引表达式来访问。在每一个生成式中,一个具有相同名字的变量会被隐含地为每一个返回一个值的生成式声明。

如果相同的生成式出现了多次,那么一个起始于1的一维数组会被隐含地声明。例如:

randsequence(bin_op)
    void bin_op : value operator value  // void类型是可选的
                  {$display("%s %b %b", operator, value[1], value[2]);}
                  ;
    bit [7:0] value : {return $urandom};
    string operator : add  := 5 {return "+";}
                    | dec  := 2 {return "-";}
                    | mult := 1 {return "*";}
                    ;
endsequence

在上面的例子中,operator和value生成式分别返回一个字符串和一个8位的值。生成式bin_op包含了这两个具有返回值的生成式。因此,与生成式bin_op相关联的代码块具有下列隐含的变量声明访问:

bit [7:0] value [1:2];
string operator;

访问这些隐含变量产生从对应的生成式返回的值。当执行的时候,上面的例子显示一个简单的三条目的随机序列:一个操作符跟着两个8位的值。操作符+、-、和*分别以5/8、2/8、和1/8的分布被选择。

只有已经被产生的生成式的返回值(也就是访问它们的代码块的左侧)可以被重新获取。试图读取一个还没有产生的生成式的返回值会导致一个为定义的值。例如:

X : A {int y = B;} B;                  // 对B的无效的访问
X : A {int y = A[2];} B A;             // 对A[2]的无效的访问
X : A {int y = A;} B {int j = A + B;}; // 有效

randsequence产生的序列可以作为生成式产生的边带效应被直接驱动到一个系统当中,或者这个序列可以被产生以便进行后续的处理。例如,下列的函数产生并返回一个位于其参数给定范围内的随机数队列。第一个和最后一个队列条目分别对应于下边界和上边界。而且,队列的尺寸根据生成式的权重被随机地选择。

function int[$] GenQueue(int low, int high);
    int[$] q;

    randsequence()
        TOP          : BOUND(low) LIST BOUND(high);
        LIST         : LIST ITEM := 8{q = {q, ITEM};}
                          | ITEM := 2{q = {q, ITEM};}
                          ;
        int ITEM     : {return $urandom_range(low, high);};
        BOUND(int b) : {q = {q, b};};
    endsequence

    GenQueue = q;
endfunction

当函数GenQueue中的randsequence执行的时候,它产生TOP生成式,TOP生成式会产生三个生成式:具有参数low的BOUND、LIST和具有参数high的BOUND。BOUND生成式简单地将它的参数附加到队列当中。LIST生成式包含了一个加权的LIST ITEM生成式和一个ITEM生成式。产生LIST ITEM生成式的概率为80%,它会引起LIST生成式被递归地产生,因此延缓了ITEM生成式的产生。LIST ITEM和ITEM的选择会被重复直到选择ITEM(它会中止LIST生成式)。每次产生ITEM生成式的时候,它会在指定的范围内产生一个随机数,这个随机数会在以后附加到队列。

下面的例子使用一个randsequence块为一个DSL包网络产生随机流量。

class DSL; ... endclass  // 产生有效DSL包的类

randsequence (STREAM)
    STREAM               : GAP DATA := 80
                         | DATA := 20;
    DATA                 : PACKET(0) := 94{transmit(PACKET);}
                         | PACKET(1) := 6{transmit(PACKET);};
    DSL PACKET (bit bad) : { DSL d = new;
                             if (bad) d.crc ^= 23;  // 损坏的crc
                             return d;
                           };
    GAP                  : {##{$urandom_range(1, 20)};};
endsequence

在这个例子中,流量包含了一个数据包(好的和坏的)和及其间隙的流。第一个生成式STREAM指出:在80%的时间里流量包含了一个GAP然后跟着一些DATA,在20%的时间里流量仅仅包含了DATA(没有GAP)。第二个生成式DATA指出:所有的数据包中有94%是好的数据包,另外余下的6%是坏的数据包。PACKET生成式实现了DSL包的产生;如果生成式参数是1,那么会通过损坏一个有效的DSL包的crc来产生一个坏包。最后,GAP生成式通过在1到20之间等待一个随机的周期数目来实现发送间隔。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文