• [转载][文档]. 艾米电子 参数与常量,Verilog


    来源:张亚峰的博客园 http://www.cnblogs.com/yuphone/archive/2010/12/18/1909772.html

    内容

    1 常量

    HDL代码经常在表达式和数组的边界使用常量。这些值在模块内是固定的,不可修改。一个很好的设计惯例是用符号常量取代这些hard literal,这样做可使代码清晰,便于后续维持及修改。在Verilog中,可以使用localparam(本地参数)来声明常量。比方说,我们可以声明一个数据总线的位宽及数据范围为:

    1 localparam DATA_WIDYH = 8,
    2            DATA_RANGE = 2**DATA_WIDYH - 1;

    或者定义一个符号端口名称:

    1 localparam UART_PORT  = 4'b0001,
    2            LCD_PORT   = 4'b0010,
    3            MOUSE_PORT = 4'b0100;

    声明中的表达式,如2**DATA_WIDTH-1,是在预编译时计算,因此它不会引用任何物理电路。一般来讲,我们使用大写字母来表示常量。

    常量的使用可用实例来说明。考虑一个带进位的加法器的代码。为了加法运算被正确执行,可将输入的值手动扩展1位,并取和的最高位为进位。代码如下:

    代码1 使用hard literal的加法器

    01 module adder_carry_hard_lit
    02 (
    03   input [3:0] a,
    04   input [3:0] b,
    05   output [3:0] sum,
    06   output cout // carry output
    07 );
    08   
    09 // signal declaration
    10 wire [4:0] sum_ext;
    11   
    12 // body
    13 assign sum_ext = {1'b0, a} + {1'b0, b};
    14 assign sum = sum_ext[3:0];
    15 assign cout = sum_ext[4];
    16   
    17 endmodule

    代码所示为4位加法器。hard literal,即硬文字,如用来表示数据范围的3和4,wire [4:]及sum_ext[3:0],以及最高位sum_ext[4]。如果我们需要把它修改为8位的加法器,那么久需要手动修改这些hard literal。如果代码很复杂且多处都引用这些hard literal,那么修改起来,将是件痛苦的事情,同时也有可能带来不必要的错误。

    为了提高代码的可读性,我们可以使用符号常量,比方说,用N来代表加法器的位数。修改后的代码如下所示:

    代码2 使用常量的加法器

    01 module adder_carry_local_par
    02 (
    03   input [3:0] a,
    04   input [3:0] b,
    05   output [3:0] sum,
    06   output cout // carry output
    07 );
    08   
    09 // constant declaration
    10 localparam N  = 4,
    11            N1 = N - 1;
    12              
    13 // signal declaration
    14 wire [4:0] sum_ext;
    15   
    16 // body
    17 assign sum_ext = {1'b0, a} + {1'b0, b};
    18 assign sum = sum_ext[N1:0];
    19 assign cout = sum_ext[N];
    20   
    21 endmodule

    常量使代码更易于被理解和维持。

    2 参数

    Verilog的模块可被例化以作为更大模块的一部分。Verilog提供了参数,来给模块传递信息。这种机制使得模块更加通用,也方便复用,因此它的功能很像常量。

    在Verilog-2001中,参数的定义区可以加在模块的头部,即端口申明之前。其简单语法如下:

    1 module [module_name]
    2 #(
    3   parameter [parameter_name]=[default_value],
    4             . . .,
    5             [parameter_name]=[default_value]
    6 )
    7 (
    8   . . .  // I/O port declaration
    9 );

    举个例子,前面的加法器代码中的加法器宽度可以修改为通过参数来指定的形式。

    代码3 使用参数的加法器

    01 module adder_carry_para
    02 #(parameter N=4)
    03 (
    04   input [N-1:0] a,
    05   input [N-1:0] b,
    06   output [N-1:0] sum,
    07   output cout // carry output
    08 );
    09   
    10 // constant declaration
    11 localparam N1 = N - 1;
    12              
    13 // signal declaration
    14 wire [N:0] sum_ext;
    15   
    16 // body
    17 assign sum_ext = {1'b0, a} + {1'b0, b};
    18 assign sum = sum_ext[N1:0];
    19 assign cout = sum_ext[N];
    20   
    21 endmodule

    N参数被声明的缺省值为4。当N被声明之后,它可以像常量一样,被用于端口声明和模块主体之中。

    如果之后,此加法器被用作其他代码的一个组件,那么我们就可以在组件例化的时候,指定想要的值给参数,以废除缺省值。参数可以通过名称或顺序列表方案来指定。一般情况下,本人都是通过名称来指定参数。请看代码:

    代码4 加法器例化样例

    01 module adder_inst
    02 (
    03   input  [3:0] a4, b4,
    04   output [3:0] sum4,
    05   output       c4,
    06   input  [7:0] a8, b8,
    07   output [7:0] sum8,
    08   output       c8
    09 );
    10   
    11 // instantiate 8-bit adder
    12 adder_carry_para #(.N(8)) a_inst1
    13 (
    14   .a(a8),
    15   .b(b8),
    16   .sum(sum8),
    17   .cout(c8)
    18 );
    19   
    20 // instantiate 4-bit adder
    21 adder_carry_para a_inst2
    22 (
    23   .a(a4),
    24   .b(b4),
    25   .sum(sum4),
    26   .cout(c4)
    27 );
    28   
    29 endmodule

    参数提供了创建可伸展代码的机制,使得电路的“宽度”可以按照指定需求来调整。这样写代码,可以使得设计被更好地复用。

    3 在Verilog-1995中使用参数

    localparam关键词、头部声明、通过名称指定参数给模块传递信息都是Verilog-2001的新特性。在Verilog-1995中,参数实在头部之后声明,而且只能通过顺序列表方案或defparam语句来重定义。进一步讲,常量必须被声明为参数,尽管它不应该被重新定义。上述的加法器使用Verilog-1995语法来描述的代码如下:

    代码5 使用Verilog-1995参数的加法器

    01 module adder_carry_95(a, b, sum, cout);
    02 parameter N = 4;     // parameter declared before the port
    03 parameter N1 = N - 1;// no localparam in Verilog-1995
    04 input [N1:0] a, b;
    05 output [N1:0] sum;
    06 output cout;
    07   
    08 // signal declaration
    09 wire [N:0] sum_ext;
    10   
    11 // body
    12 assign sum_ext = {1'b0, a} + {1'b0, b};
    13 assign sum = sum_ext[N1:0];
    14 assign cout = sum_ext[N];
    15   
    16 endmodule

    当一个组件被例化是,参数仅可以使用顺序列表方案来重定义,如:

    1 adder_carry_95 #(8,7) a_inst1
    2 (
    3   .a(a8),
    4   .b(b8),
    5   .sum(sum8),
    6   .cout(c8)
    7 );

    或者使用defparam语句,如:

    1 defparam a_inst2.N=8;
    2 defparam a_inst2.N1=7;
    3 adder_carry_95 a_inst2
    4 (
    5   .a(a8),
    6   .b(b8),
    7   .sum(sum8),
    8   .cout(c8)
    9 );

    Verilog-1995的方案比较冗长麻烦,有可能产生一些微妙的错误,因此不推荐使用。

    参考

    1 Pong P. Chu.FPGA Prototyping By Verilog Examples: Xilinx Spartan-3 Version.Wiley

    另见

    [与艾米一起学FPGA/SOPC].[逻辑实验文档连载计划]

  • 相关阅读:
    交叉编译OpenCV的教程——基于aarch64-linux-gnu的交叉编译器
    Day01:我的Python学习之路
    将中文库导入到ARM板子中以解决中文显示乱码的教程
    Linux环境下挂载SD卡的教程
    Ubuntu下压缩与解压各种文件的命令
    Ubuntu14.04环境下Qt5.5以上版本无法输入中文的解决教程
    编程之美:队列中的最大最小值
    leetcode:Compare Version Numbers
    leetcode:Search for a Range
    csapp:无符号数可能造成的程序bug
  • 原文地址:https://www.cnblogs.com/zlh840/p/2103975.html
Copyright © 2020-2023  润新知