草庐IT

Verilog Tutorial(10)如何实现可复用的Verilog设计?

孤独的单刀 2023-04-09 原文

本文将讨论可以用来实现代码可复用性的参数parameter和generate语句(生成语句)。

与大多数编程语言一样,设计者也应该尽量使verilog代码尽可能地具备可复用性----这能够减少未来项目的开发时间,因为设计者可以更轻松地将代码从一个设计移植到另一个设计。

在 verilog 中有两种语法可以帮助设计者编写可复用的代码——参数parameter和generate语句。这两种语法都允许设计者创建更通用的代码,以便在例化组件时可以通过修改代码的方式来满足其他的设计需求。

参数Parameter

参数parameter是常量(constant)的局部形式,它可以在例化模块时为其赋值。由于参数的作用范围有限,设计者可以多次调用同一个模块,并为参数赋不同的值。

在编写模块时必须定义一个模块接口,然后可以使用这个接口在FPGA 设计中与许多不同的模块实现互连。接口可以声明参数以及模块的输入和输出。

下面的 verilog 代码片段展示了在模块中声明参数的方法。当这样在 verilog 模块中声明参数时,我们称其为参数化模块(a parameterized module)。

module <module_name> #(
  parameter <parameter_name> = <default_value>    //声明参数
)
(
  //端口定义
);

上面代码中的 <parameter_name> 用于为参数提供标识符。设计者可以使用此标识符在代码中调用参数值--就像使用普通变量一样。

代码中的<default_value> 将为参数分配一个默认值,这很有用,因为它允许设计者例化组件而无需专门为参数分配值。

在例化一个模块时可以使用命名关联(named association)或位置关联(positional association)的方法来为参数赋值,这与直接将信号分配给模块上的输入或输出的作用完全相同。但是在使用verilog-1995标准编写代码时,只能使用位置关联方法来为参数赋值。

下面的 verilog 代码片段展示了在例化模块时用于为参数赋值的方法。

//名称关联
<module_name> # (
  .<parameter_name> (<parameter_value>)
)
<instance_name> (
  //端口连接
);
 
//位置关联
<module_name> # (<parameter_values>)
<instance_name> (
  //端口连接
);

示例

为了更好地理解如何在 verilog 中使用参数,请考虑一个基本示例:设计两个同步计数器,一个是 8 位宽,另一个是 12 位宽。

为了实现这个电路可以设计两个具有不同宽度的不同计数器,但这显然是一种低效的编码方式。相反,如果设计一个计数器并使用一个参数来更改输出中的位数,则会更加高效。

下面的 verilog 代码片段展示了如何为参数化计数器模块编写接口。

module counter #(
  parameter BITS = 8;
)
(
  input wire clock,
  input wire reset,
  output reg [BITS-1 : 0] count
);

这个例子展示了如何使用参数来调整 verilog 中信号的位宽。示例并不是使用固定数字来声明端口信号的位宽,而是将参数值替换到端口声明中。这是 verilog 中一个最常见的参数示例。

上面的verilog代码中定义了BITS参数的默认值为8。因此,如果想要一个不是 8 位的输出时,设计者只需要为参数重新赋值。

下面的代码片段展示了当需要 12 位输出时如何例化此模块。这种情况必须在例化 verilog 模块时重写参数的默认值。

counter # (
  .BITS (12)
) count_12 (
  .clock  (clock),
  .reset  (reset),
  .count  (count_out)
);

虽然上面的例子中只使用了命名关联方法,但其实也可以使用位置关联方法来为参数赋值。

下面的代码片段就展示了如何使用位置关联方法来将 12 赋给参数 BITS 。

counter # (12) count_12 (clock, reset, count_out);

Generate语句

generate 语句可以被用来在设计中生成条件的(Conditional)或迭代的(iterative)代码块。这允许设计可以有选择地包含或排除代码块或创建特定代码块的多个实例。

只能在并发verilog代码块(concurrent verilog code blocks)中使用 generate 语句,这意味着它不能被包含在always blocks或initial blocks中。此外,generate 关键字还必须结合if语句、case语句或者for循环语句来使用。

generate if 和 generate case 语句被用来来有条件地生成代码,而 generate for 语句则迭代地生成代码。generate块中可以编写需要的任何有效的 verilog 代码,包括always blocks、模块例化和其他generate语句。

generate语句是在verilog-2001标准中引入的,所以设计者不能在基于verilog-1995标准的设计中使用它。

Generate For 语句

设计者可以在generate块中使用verilog中的for循环来迭代地创建一段代码的多个实例。 generate for loop 语法通常被用来使用描述有规则的和重复的结构的硬件。

例如,设计者可能想要使用单个总线控制多个RAM模块。如果使用generate块而不是手动例化所有模块,那么就可以有效地减少代码行数。

下面的代码片段展示了generate for 块的一般语法。

//声明循环变量
genvar <name>;
 
//generate块的代码
generate
  for (<initial_condition>; <stop_condition>; <increment>) begin
    //要重复执行的代码
  end
endgenerate

从这里可以看出来,这种语句实际上可for循环语句是很类似的,但是两者之间仍有两个很大的区别。首先,设计者必须使用 genvar 类型来声明循环变量。其次,设计者应在 generate 块中声明循环,而不是像在always块这样的普通过程块中,这种差异很重要,因为它改变了代码的基本行为。

编写一个 generate for block 实际上是让编译工具创建代码的多个实例。相反,使用普通的 for 循环则是让编译工具创建代码的单个实例但要多次执行它。

作为示例,请看一个非常简单的用例:将数据分配给 2 bits向量。

下面的 verilog 代码展示了如何使用 generate for 和 for 循环来做到这一点。在这两种情况下,代码的功能相同,但生成的结构却大不相同。

//使用for循环的方法
always @(posedge clock) begin
  for (i = 0; i < 2; i = i + 1) begin
    sig_a[i] = 1'b0;
  end
end

//使用generate for循环的方法
generate
  for (i = 0; i < 2; i = i + 1) begin
    always @(posedge clock) begin
      sig_a[i] = 1'b0;
    end
  end
endgenerate

如果将for循环的代码展开,那么将得到如下所示的代码。

always @(posedge clock) begin
  sig_a[0] = 1'b0;
  sig_a[1] = 1'b0;
end

相反,将generate for的代码展开将如下所示。

always @(posedge clock) begin
  sig_a[0] = 1'b0;
end

always @(posedge clock) begin
  sig_a[1] = 1'b0;
end

由此可以看出 generate for 与 for 循环有何根本不同。

示例

为了更好地演示 verilog generate for 语句的工作原理,请考虑一个基本示例:连接到同一总线的 3 个 RAM 模块的数组,每个 RAM 模块都有一个写使能端口、一个 4 位地址输入和 4 位数据输入。这些信号都被连接到同一总线。此外,每个 RAM 都有一个 4 位数据输出总线和一个使能信号,它们对于每个 RAM 块都是独立的。下面的电路图展示了将要设计的电路。

为此需要声明一个 3 位的向量,可以使用它来连接到 RAM 使能端口,庵后可以根据循环变量的值将不同的位连接到每个 RAM。

对于数据输出总线,可以创建一个 12 位向量并将读取的数据输出连接到该向量的不同 4 位向量。然而,更高效的解决方案是使用由 3 个 4 位向量组成的数组。同样也可以使用循环变量根据需要赋值该数组的不同元素。

下面的 verilog 代码片段展示了如何使用 for generate 语句对这个电路进行设计。


wire [3:0] rd_data [2:0];    //读数据数组
wire [2:0] enable;           //使能信号构建的向量


genvar i;        //循环变量
generate
  for (i=0; i<=2; i=i+1) begin
    ram ram_i (
      .clock    (clock),
      .enable   (enable[i]),
      .wr_en    (wr_en),
      .addr     (addr),
      .wr_data  (wr_data),
      .rd_data  (rd_data[i])
    );
  end
endgenerate

综合这段代码后,将得到如下所示的电路。

Generate If语句

generate if语句可以在设计中有条件地生成 verilog 代码。当设计者只想将代码在特定条件下使用时,可以使用 generate if 语句。与此有关的一个例子是当设计者想在设计中包含一个专门用于测试的功能时,为此可以使用 generate if 语句来确保仅将此函数包含在调试版本的代码中,而不会包含在生产版本代码中。

下面的代码片段展示了 generate if 语句的一般语法。

generate
  if (<condition1>) begin
    //要执行的代码
  end
  else if (<condition2>) begin
    //要执行的代码
  end
  else begin
    //要执行的代码
  end
endgenerate

从这个示例可以看到,该语法虽然与verilog中的if语法是类似的,但这两种方法之间存在根本区别。

编写 generate if 语句时,实际上是让编译工具根据某些条件创建代码块的实例。这意味着只有一个分支会被编译,而任何其他分支都将被排除在编译之外。

相反,使用 if 语句时,整个 if 语句将被编译并且可以执行语句的每个分支。每次在仿真期间触发 if 语句时,都会判断条件以确定执行哪个分支。

示例

为了更好地演示generate if 语句的工作原理,请考虑一个基本示例:编写一个输出 4 位计数器值的测试电路。由于这是一个测试功能,所以只需要在使用调试版本时激活它;构建生产版本代码时,将计数器输出接地。为此将使用一个参数来确定何时应该构建调试版本的电路。

下面的代码片段展示了如何实现这个示例。

parameter debug_build = 0;

generate
  if (debug_build) begin
    always @(posedge clock, posedge reset) begin
      if (reset) begin
        count <= 4'h0;
      end
      else begin
        count <= count + 1;
      end
    end
  end
  else begin
    initial begin
      count <= 4'h0;
    end
  end
endgenerate

当 debug_build 被设置为 1 时,综合工具会生成如下所示的电路----四位计数器电路。

但当 debug_build 被设置为 0 时,综合工具则会生成如下所示的电路----计数信号的所有位均接地。

Generate Case语句

generate case 语句可以被用来有条件地在设计中包含 verilog 代码块。generate case 语句基本上与 generate if 语句功能相同,但语法不同。这意味着如果只想在特定条件下将某部分包含在设计中时,设计者也可以使用 generate case 语句。

例如,如果设计者想要设计一个只想包含在调试版本中的测试电路,就可以使用 generate case 语句来确定构建哪个版本的代码。

下面的代码片段展示了generate case 语句的一般语法。

generate
  case (<variable>)
    <value1> : begin
      //当<variable> = <value1>执行这个分支语句
    end
    <value2> : begin
      //当<variable> = <value2>执行这个分支语句
    end
    default : begin
    //其他情况执行这个分支语句
    end
  endcase
endgenerate

从这个示例可以看到,该语法虽然与verilog中的case语法是类似的,但这两种方法之间存在根本区别。

编写 generate case 语句时,实际上是让编译工具根据某些条件创建代码块的实例。这意味着只有一个分支会被编译,而任何其他分支都将被排除在编译之外。

相反,使用 case 语句时,整个 case 语句将被编译并且可以执行语句的每个分支。每次在仿真期间触发 case语句时,都会判断条件以确定执行哪个分支。

示例

由于generate case语句与 if generate 语句功能类似,所以仍以generate if章节中使用的示例为例。下面的 verilog 代码展示了如何使用 generate case 语句来实现这个例子。

parameter debug_build = 0;

generate
  case (debug_build)
    1 : begin
      always @(posedge clock, posedge reset) begin
        if (reset) begin
          count <= 4'h0;
        end
        else begin
          count <= count + 1;
        end
      end
    end
    default : begin
      initial begin
        count <= 4'h0;
      end
    end
  endcase
endgenerate

当 debug_build 被设置为 1 时,综合工具会生成如下所示的电路----四位计数器电路。

但当 debug_build 被设置为 0 时,综合工具则会生成如下所示的电路----计数信号的所有位均接地。


  • 📣您有任何问题,都可以在评论区和我交流📃

  • 📣您的支持是我持续创作的最大动力!如果本文对您有帮助,还请多多点赞👍、评论💬和收藏


有关Verilog Tutorial(10)如何实现可复用的Verilog设计?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  4. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  5. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  6. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  7. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  8. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  9. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  10. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

随机推荐