欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

宏定义(`define),常数(localparam),参数(parameter)三者的区别

程序员文章站 2022-06-28 19:00:55
在verilog中,好的设计是用符号常量代替固定文本,这使得代码清晰并有助于以后的维护和修改;在verilog中,可以用关键词localparam声明常数,例如声明数据总线的位宽和范围:localparam DATA_WIDTH = 8;DATA_RANGE = 2**DATA_WIDTH;或定义符号端口名称:UART_PORT = 4'b0001;LCD_PORT =......

在verilog中,好的设计是用符号常量代替固定文本,这使得代码清晰并有助于以后的维护和修改;在verilog中,可以用关键词localparam 声明常数,例如声明数据总线的位宽和范围:

localparam DATA_WIDTH = 8;

DATA_RANGE = 2**DATA_WIDTH;

或定义符号端口名称:

UART_PORT = 4'b0001;

LCD_PORT = 4'b0010;

MOUSE_PORT = 4'b0100;

 习惯用大写字母表示常数;

一个带进位的固定位宽加法器的例子:

module adder_carry_hard_lit(
    input [3:0] a,
    input [3:0] b,
    output [3:0] sum,
    output cout
    );
    wire [4:0] sum_ext;
    assign sum_ext = {1'b0,a} + {1'b0,b};
    assign sum = sum_ext[3:0];
    assign cout = sum_ext[4];
endmodule

如果改成8位加法器,这些位宽都要一个一个的手动更改,遇到大一点的项目,这将变得尤为困难;

对上述代码改进一下,使用局部常数localparam: 

module adder_carry_loacl_par
    (
   
    input [3:0] a,
    input [3:0] b,
    output [3:0] sum,
    input cout
    );
    localparam N = 4;
    localparam N1 = N-1;
    wire [N:0] sum_ext;
    assign sum_ext = {1'b0,a} + {1'b0,b};
    assign sum = sum_ext[N1:0];
    assign cout = sum_ext[N];
endmodule

上述代码使用了局部变量,这还不够优化,下面介绍参数的使用方法:

Verilog模块可以实例化为组件并成为更大设计模块的一部分。Verilog提供一种结构成为parameter,向模块传递信息,这种机制使得模块多功能化并能重复使用。参数在模块内不能改变,因此功能和常数类似.

在Verilog HDL 2001中,参数声明部分可以在模块的开头,即端口声明之前,如下:

module 模块名

#(parameter 参数名1 = 默认值1,

parameter 参数名2 = 默认值2)//没有分号

(

...

);

......//在模块内部声明parameter是一样的
parameter N = 12;

endmodule

为了增强可读性以及可维护性,可以采用符号参数N来表示加法器的位数:

module adder_carry
     #(parameter N = 4)
    (
   
    input [N-1:0] a,
    input [N-1:0] b,
    output [N-1:0] sum,
    input cout
    );
//常数声明
    localparam N1 = N-1;
    wire [N:0] sum_ext;
    assign sum_ext = {1'b0,a} + {1'b0,b};
    assign sum = sum_ext[N1:0];
    assign cout = sum_ext[N];
endmodul

如果之后,加法器在其他代码中作为组件使用,那么就可以在组件实例化中给参数指定所需的值并将原来的默认值覆盖;

如果省略参数赋值,则采用默认参数,如下:

module adder_insta(
    input [3:0] a4,
    input [3:0] b4,
    output [3:0] sum4,
    output cout4,
    input [7:0] a8,
    input [7:0] b8,
    output [7:0] sum8,
    output cout8
    );
    //实例化8位加法器
    adder_carry #(.N(8))  uu1(.a(a8),.b(b8),.sum(sum8),.cout(cout8));
    
    //实例化4位加法器
    adder_carry  uu2(.a(a4),.b(b4),.sum(sum4),.cout(cout4));
endmodule

总结1:parameter可用作在顶层模块中例化底层模块时传递参数的接口,localparam的作用域仅仅限于当前module,不能作为参数传递的接口!!

`define

语法格式

`define   M           12     //注意不加;不能忘记" ` "

作用区域  

在整个工程中均有效,因为它是可以跨模块的定义;在使用该宏定义值时,通常M应该表示为`M。之所以不是很提倡滥用宏定义,是因为它不像parameter那么“中规中矩”的作用有某几个特定的源代码文件中。一旦`define被编译,其在整个编译过程中都有效,只有当遇到`undef命令才能使之失效。也即它通常会影响工程的其他模块,尤其当多个同样宏名定义时,如果不注意有可能照成定义的混乱。 

总结2:

define:可以跨模块的定义;

parameter:本module内有效的定义,可用于参数传递;

localparam:关于localparam,这个关键字书上很少会讲到。但是大公司的代码里经常会看到;本module内有效的定义,不可用于参数传递;localparam cannot be used within the module port parameter list.

一般情况下,状态机的参数都是用localparam的。

用来生成循环,生成维数可扩展的模块,localparam是局部参数,但它不能被重定义,也就是说在实例化的时候不能通过层次引用进行重定义,例如parameter可以通过#(参数)来进行重新定义,但是localparam不可以,只能通过源代码来改变;

本文地址:https://blog.csdn.net/bleauchat/article/details/85986120

相关标签: verilog基础