【IEEE_Verilog-12.4】generate的用法

12.4 Generate construct

generate构造用于在模型中有条件地或实例化的生成块。生成块是一个或多个模块项的集合。一个生成块不能包含端口声明、参数声明、指定块或specparam声明。所有其他模块项,包括其他的generate结构,都允许在一个generate块中。generate结构为参数值提供了影响模型结构的能力。它们还允许描述具有重复结构的模块,使递归模块实例化成为可能。

有两种类型的循环结构:loops和conditionals。loop生成结构允许在一个模型中实例化一个生成块多次。conditionals生成结构,包括if-generate和case-generate结构,最多从一组可选的生成块中实例化一个生成块。这个术语generate scheme指的是决定哪一个或多少生成块被实例化的方法。它包括出现在生成结构中的条件语句、case选择语句或loop控制语句。

在模型细化elabration过程中,对生成方案进行评估。细化发生在语法分析parse HDL之后和仿真simulation之前;它涉及到扩展模块实例化、计算参数值、解析层次名称,建立net连接和一般情况下为仿真做准备。尽管生成方案使用和行为语句相似的语法,但是认识到它们不会在仿真时执行是重要的。它们在细化阶段进行评估,并在模拟开始之前确定结果。因此,generate scheme中的所有表达式都应该是常量表达式,在细化时是确定的。

generate结构的细化导致产生零个或多个generate实例块。一个generate块实例在某些方面类似于模块实例。它创建了一个新的层次。它使块中的对象、行为结构和模块实例产生。除了可以引用内部作用域的对象声明外,这些构造的行为与它们在模块实例化时的行为是一样的。实例化的命名生成块中的名称可以像12.5中描述的那样分层引用。

关键词generate和endgenerate可以在模块中使用,从而定义一个genetate区域。generate区域是在模块描述中可能出现generate结构的文本区域。使用generate区域是可选的。当使用generate区域时,在模块中没有语义差异。解析器可以选择识别生成区域为误用的generate关键词生成不同的错误信息。生成区域不会嵌套,它可能仅仅直接出现在模块中。如果使用了generate关键词,应该使用endgenerate关键词进行匹配。

generate结构语法如下:

12.4.1 循环生成结构loop generate construct

一个循环生成构造允许使用类似于for循环语句的语法多次实例化一个生成块。在循环生成方案中使用循环索引变量之前,应该在genvar声明中声明它。在细化过程中,genvar用作一个整数来计算生成循环并创建生成块的实例,但在仿真时它不存在。一个genvar不能被引用到任何地方,除了在一个循环生成方案。

在循环生成方案中,初始化和迭代赋值都应该赋值给同一个genvar。初始化赋值不能引用右侧的循环索引变量。

在循环生成构造中的生成块,有一个隐式的localparam变量声明。这是一个有相同名字和类型的整数参数作为循环索引变量,在每一个生成块的实例中,它的值是创建实例时索引变量的值。这个参数可以在生成块中的任何地方使用,可以使用带有整数值得普通参数。可以用层次名称引用它。

因为这个隐式的localparam有和genvar相同的名字,在循环生成块中,对这个名字的任何引用将是对localparam的应用,不是genvar。因此,不可能在两个嵌套的循环生成块中使用相同的genvar。

可以对在循环生成结构中的生成块进行命名或不命名,它们只能包含一个项,这个项不需要使用begin/end包裹。即使没有begin/end关键词,它仍然是一个生成块,这个生成块像所有生成块一样,当实例化时,由一个独立的作用域和一个新的层次结构组成。

如果对生成块命名,它是一个生成块实例数组的声明。在这个数组中的索引值是在细化过程中genvar假定的值。这可以是一个稀疏数组,因为genvar的值不必形成一个连续整数范围。即使循环generate方案没有生成generate块的实例,该数组也被认为是已声明的。如果generate块没有命名,那么它里面的声明不能被引用,只能从generate块本身实例化的层次结构中引用。

如果一个generate块实例数组的名称与任何其他声明(包括任何其他generate块实例数组)冲突,则会出现错误。如果循环生成方案没有中止,则会出现错误。在循环生成方案的评估过程中,如果genvar值重复,则为错误。在循环生成方案的评估过程中,如果genvar的任何位被设置为x或z,那将是一个错误。

例子:
例子1—合法和不合法的生成循环

module mod_a;
genvar i;
//generate, endgenerate关键词不需要
for (i=0; i<5; i=i+1) begin:a
  for(i=0; i<5; i=i+1) begin:b
    ...                        //错误-两个嵌套的生成块中都使用i作为循环索引值
    ...
  end
end
endmodule
module mod_b;
genvar i;
reg a;
for (i=1; i<0; i=i+1) begin: a
  ...                  //错误-寄存器a的名字和块a的名字冲突
end
endmodule
module mod_c;
genvar i;
for (i=1; i<5; i=i+1) begin: a
  ...
end
for (i=10; i<15; i=i+1) begin: a
  ...                  //错误-即使索引是唯一的,但是块a的名字和前一个块的名字a冲突
end
endmodule

例子2—
使用循环的参数化灰码到二进制码转换器模块,从而生成连续赋值

module gray2bin1 (bin, gray);
  parameter SIZE = 8;  //这个模块是可参数化的
  output [SIZE-1:0] bin;
  input [SIZE-1:0] gray;
  genvar i;
  generate
    for (i=0; i<SIZE; i=i+1) begin: bit
      assign bin[i] = ^gray[SIZE-1:i];
        //i指的隐式定义的localparam,当它建立的时候,在每一个生成块实例中,它的值是genvar的值。
    end
  endgenerate
endmodule

例3和例4的模型是使用循环的脉动加法器的参数化模型,从而生成Verilog门级原语。例3在循环生成之外使用了一个二维net声明使门级原语连接,而例4在generate循环内部做了net声明来生成连接gate原语所需的导线,用于循环的每次迭代。

例3—具有在循环生成之外的二维net声明的脉动加法器

module addergen1 (co, sum, a, b, ci);
  parameter SIZE = 4;
  output [SIZE-1:0] sum;
  output            co;
  input [SIZE-1:0]  a, b;
  input             ci;
  wire [SIZE:0]     c;
  wire [SIZE-1:0]   t [1:3];
  genvar            i;
  
  assign c[0] = ci;
  //分级的门的实例如下:
  //xor 门:bit[0].g1  bit[1].g1  bit[2].g1  bit[3].g1
  //        bit[0].g2  bit[1].g2  bit[2].g2  bit[3].g2
  //and 门:bit[0].g3  bit[1].g3  bit[2].g3  bit[3].g3
  //        bit[0].g4  bit[1].g4  bit[2].g4  bit[3].g4
  //or 门: bit[0].g5  bit[1].g5  bit[2].g5  bit[3].g5
  //生成实例用多维net t[1][3:0] t[2][3:0] t[3][3:0]连接(总共12个net)
  for (i=0; i<SIZE; i=i+1) begin: bit
    xor g1 (t[1][i], a[i], b[i]);
    xor g2 (sum[i], t[1][i], c[i]);
    and g3 (t[2][i], a[i], b[i]);
    and g4 (t[3][i], t[1][i], c[i]);
    or g5 (c[i+1], t[2][i], t[3][i]);
  end
assgin co = c[SIZE];
endmodule

在例5中显示了多级生成循环的分层生成块实例名字。对生成循环创建的每一个块实例,循环的生成块识别符通过增加【genvar value】到生成块识别符的末尾的方式进行索引。这些名字可以在分层路径名字中使用。
例5—多级生成循环

parameter SIZE = 2;
genvar i, j, k, m;
generate
  for (i=0; i<SIZE; i=i+1) begin: B1  //B1[i]域
    M1 N1();  //实例化B1[i].N1
    for (j=0; j<SIZE; j=j+1) begin: B2 //B1[i].B2[j]域
      M2 N2(); //实例化B1[i].B2[j].N2
      for (k=0; k<SIZE; k=k+1) begin: B3 //B1[i].B2[j].B3[k]域
        M3 N3(); //实例化B1[i].B2[j].B3[k].N3
      end
    end
    if (i>0) begin: B4  //B1[i].B4域
      for (m=0; m<SIZE; m=m+1) begin: B5  //B1[i].B4.B5[m]
        M4 N4();  //B1[i].B4.B5[m].N4
      end
    end
  end
endgenerate
//下面是一些模块实例的层次名称示例:
// B1[0].N1                       B1[1].N1
// B1[0].B2[0].N2                 B1[0].B2[1].N2
// B1[0].B2[0].B3[0].N3           B1[0].B2[0].B3[1].N3
// B1[0].B2[1].B3[0].N3
// B1[1].B4.B5[0].N4              B1[1].B4.B5[1].N4

12.4.2 条件生成结构Conditional generate constructs

条件生成结构,if-generate和case-genetate,最多从根据在细化过程中计算的常量表达式的可选生成块集合中选择一个生成块。被选择的生成块,如果有的话,被被实例化到模型中。

可以对在条件生成块结构中的生成块进行命名或不命名,它们可能只由一个项组成,这个项不需要使用begin/end包裹。即使没有begin/end关键词,当实例化时,它仍然是一个生成块,这个生成块像所有生成块一样,由一个单独域和一个新的层次结构。

因为最多只能实例化一个可选的生成块,所以在一个条件生成构造中可以有多个名称相同的块。不允许任何已命名的generate块与在相同作用域中的任何其他条件或循环generate构造中的generate块具有相同的名称,即使具有相同名称的块没有被选中进行实例化。不允许任何已命名的生成块与同一作用域中的任何其他声明具有相同的名称,即使该块没有被选中进行实例化。

如果选择用于实例化的生成块已经被命名,那么这个名字声明一个生成块实例,并且是它创建作用域的名称。适用层次命名的一般规则。如果选择用于实例化的生成块没有被命名,它仍然创建一个域;但是其中的声明不能使用分层名称引用,只能从generate块本身实例化的分层中引用。

如果在条件生产结构中一个生成块只有一个项组成,这个项本身是一个条件生成结构,如果这个项没有被begin/end关键词包裹,那么这个生成块不会当做一个独立域。这个块中的生成结构被认为是直接嵌套。直接嵌套结构的生成块被视为属于外部构造。因此,它们可以具有与外部构造的generate块相同的名称,而不能具有与外部构造外围作用域中的任何声明相同的名称(包括该作用域中其他generate构造中的其他generate块)。这允许在不创建不必要的生成块层次结构的情况下表达复杂的条件生成方案。

最常见的用法是用任意数量的else-if子句创建一个if-else-if generate scheme,所有子句都可以有相同名称的generate块,因为只有一个会被选中进行实例化。在相同的复杂生成方案中,可以结合if-generate和case-generate构造。直接嵌套只适用于嵌套在条件生成结构中的条件生成结构。它不以任何方式应用于循环生成结构。

例1:

module test;
parameter p = 0, q = 0;
wire a, b, c;
//生成a u1.g1实例的代码或没有实例
//以下门的一个u1.g1实例
//{and, or, xor, xnor}生成如果
//{p, q} == {1, 0}, {1, 2}, {2, 0}, {2, 1}, {2, 2}, {2, default}
if (p == 1)
  if (q == 0)
    begin : u1
      and g1(a, b, c);  //如果p==1且q==0, 那么实例化AND,这个AND有层次名称test.u1.g1
    end
  else if (q == 2)
    begin : u1
      or g1(a, b, c);  //如果p==1且q==2, 那么实例化OR,这个OR具有层次名称test.u1.g1
    end
  else;       //增加到if (q == 2)语句的else
              //如果p == 1且q != 0或2, 那么不实例化
else if (p == 2) 
  case (q)
  0, 1, 2:
    begin : u1  //如果p == 2且q == 0, 1或2,那么实例化XOR, 有层次名称test.u1.g1
      xor g1(a, b, c); 
    end
  default:
    begin : u1
      xnor g1(a, b, c); //如果p == 2且q != 0 , 1或2,那么实例化XNOR,具有层次名称test1.u1.g1
    end
endmodule

这个generate构造将最多选择一个名为u1的generate块。该块中gate实例化的层次名称是test.u1.g1。嵌套if-generate构造时,else总是属于最近的if构造。

注意—像上面的例子一样,带有空generate块的else可以被插入,以使后续的else属于外部的if构造。开始/结束关键字也可以用来消除歧义。然而,这将违反直接嵌套的标准,并将创建一个额外的生成块层次结构。

条件生成结构使模块能够包含自身的实例化。循环生成结构也是如此,但使用条件生成更容易实现。通过正确使用参数,可以终止生成的递归,从而得到一个合法的模型层次结构。由于决定顶级模块的规则,包含自身实例化的模块将不是顶级模块。

例2—参数化乘法器模块的实现

module multiplier (a, b, product);
parameter a_width = 8, b_width = 8;
localparam product_width = a_width+b_width;
//不能用defparam语句或模块实例化语句直接修改
input [a_width-1:0] a;
input [b_width-1:0] b;
output [product_width-1:0] product;
generate
  if ((a_width<8) || (b_width<8)) begin: mult
    CLA_multiplier #(a_width, b_width) u1(a, b, product);
    //实例化CLA_multiplier
  end
  else begin : mult
    WALLACE_multiplier #(a_width, b_width) u1(a, b, product);
    //实例化Wallace-tree乘法器
  end
endgenerate
//层次实例名称是mult.u1
endmodule

例3—生成一个case来处理小于3的宽度

generate
  case (WIDTH)
    1: begin: adder  //实现一位加法器
        adder_1bit x1(co, sum, a, b, ci);
       end
    2: begin : adder //实现二位加法器
         adder_2bit x1(co, sum, a, b, ci); 
       end
    default:
      begin : adder //其它——超前进位加法器
        adder_cla #(WIDHT) x1(co, sum, a, b, ci);
      end
  endcase
//层次化名称是adder.x1
endgenerate

例4—内存模块

module dimm(addr, ba, rasx, casx, csx, wex, cke, clk, dqm, data, dev_id);
  parameter [31:0] MEM_WIDTH = 16, MEM_SIZE = 8; // in mbytes
  input [10:0] addr;
  input ba, rasx, casx, csx, wex, cke, clk;
  input [ 7:0] dqm;
  inout [63:0] data;
  input [ 4:0] dev_id;
  genvar i;
    case ({MEM_SIZE, MEM_WIDTH})
      {32'd8, 32'd16}: // 8Meg x 16 bits wide
      begin: memory
        for (i=0; i<4; i=i+1) begin:word
          sms_08b216t0 p(.clk(clk), .csb(csx), .cke(cke),.ba(ba),
                         .addr(addr), .rasb(rasx), .casb(casx),
                         .web(wex), .udqm(dqm[2*i+1]), .ldqm(dqm[2*i]),
                         .dqi(data[15+16*i:16*i]), .dev_id(dev_id));
        // 层次化名称 memory.word[3].p,
        // memory.word[2].p, memory.word[1].p, memory.word[0].p,
        // and 任务 memory.read_mem
        end
        task read_mem;
          input [31:0] address;
          output [63:0] data;
          begin // call read_mem in sms module
            word[3].p.read_mem(address, data[63:48]);
            word[2].p.read_mem(address, data[47:32]);
            word[1].p.read_mem(address, data[31:16]);
            word[0].p.read_mem(address, data[15: 0]);
          end
          endtask
        end
        {32'd16, 32'd8}: // 16Meg x 8 bits wide
          begin: memory
            for (i=0; i<8; i=i+1) begin:byte
              sms_16b208t0 p(.clk(clk), .csb(csx), .cke(cke),.ba(ba),
                             .addr(addr), .rasb(rasx), .casb(casx),
                             .web(wex), .dqm(dqm[i]),
                             .dqi(data[7+8*i:8*i]), .dev_id(dev_id));
            // 层次化名称 memory.byte[7].p,
            // memory.byte[6].p, ... , memory.byte[1].p, memory.byte[0].p,
            //任务 memory.read_mem
          end
          task read_mem;
            input [31:0] address;
            output [63:0] data;
            begin // call read_mem in sms module
              byte[7].p.read_mem(address, data[63:56]);
              byte[6].p.read_mem(address, data[55:48]);
              byte[5].p.read_mem(address, data[47:40]);
              byte[4].p.read_mem(address, data[39:32]);
              byte[3].p.read_mem(address, data[31:24]);
              byte[2].p.read_mem(address, data[23:16]);
              byte[1].p.read_mem(address, data[15: 8]);
              byte[0].p.read_mem(address, data[ 7: 0]);
            end
          endtask
        end
      // 其它内存情况
    endcase
endmodule
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值