Verilog基础语法——parameter、localparam与`define

本文详细介绍了Verilog中localparam、parameter和`define三种参数定义方法,强调了它们的作用范围、可传递性以及在实际设计中的应用场景。localparam限于模块内部,parameter可用于模块间传递,而`define适用于整个工程文件的全局参数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Verilog基础语法——parameter、localparam与`define

写在前面

  在使用Verilog编写RTL代码时,如果需要定义一个常量,可以使用`define、parameter和localparam三种进行定义与赋值。

一、localparam

  localparam是一种局部常量,只在声明该常量的模块中有效,不可用于模块与模块之间的参数传递。一般在定义仅用于模块内部的参数时使用localparam,比如状态机状态的定义声明。例如:

// FSM Sate
localparam IDLE         = 4'b0001;
localparam INPUT        = 4'b0010;
localparam DECODE       = 4'b0100;
localparam COMPLETE     = 4'b1000;

// FSM
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        curr_state <= IDLE;
        //...
    end
    else begin
        case(curr_state) 
            IDLE:
            begin
            	if(start)
                    curr_state <= INPUT;
                else
                	curr_state <= IDLE;
                //...             
            end

            INPUT   :
            begin
                curr_state <= DECODE;
                //...
            end

            DECODE      :        
            begin
            	curr_state <= COMPLETE;
                //... 
            end

            ALL_COMPLETE:
            begin
            	curr_state <= IDLE;
            end

            default :;
        endcase
    end
end

二、parameter

  parameter与localparam相同的是,其作用范围仅仅是声明该参数的模块内部,而不同的是,parameter可以用于模块之间的参数传递,一般用于参数化设计。参数化设计是指对于所设计的功能子模块,通过修改其内部参数值即可使得该模块适用于其他场景。
  这里,parameter可以分为在模块头部中声明与在模块内部定义声明,两种定义方式需要不同的方式来进行参数传递。

(1)在模块头部中定义
  在模块头部中定义参数是一种常用的做法,其格式如下:

module counter
#(
    parameter CNT_NUM    = 8'd128 ,
    parameter DIN_WIDTH  = 4'd8   ,
    parameter DOUT_WIDTH = 5'd16   
)
(
    input    wire    [DIN_WIDTH-1:0]    din , 
    output   wire    [DOUT_WIDTH-1:0]   dout
);
    // 模块内部代码
    // ...

endmodule

  而在模块例化时,需要与例化模块输入输出端口一样,给参数接入不同的数值即可,其格式如下:

module top
(
    // 输出输入声明
    // ...
);

    localparam CNT_NUM = 8'd100;
    localparam DIN_WIDTH  = 4'd6;
    localparam DOUT_WIDTH = 5'd12;   

    wire    [DIN_WIDTH-1:0]    counter_din ; 
    wire    [DOUT_WIDTH-1:0]   counter_dout;


    // 模块例化
    counter 
    #(
        .CNT_NUM   (CNT_NUM   ), // 参数传递
        .DIN_WIDTH (DIN_WIDTH ), // 参数传递
        .DOUT_WIDTH(DOUT_WIDTH)  // 参数传递
    )
    counter_inst
    (
        .din (counter_din ), 
        .dout(counter_dout)
    )
    // ...

endmodule

(2)在模块内部定义
  在模块内部定义的paramter其格式如下:

module counter
(
    input    wire    [15:0]   din , 
    output   wire    [7:0]    dout
);

    parameter CNT_NUM    = 8'd128 ;
    parameter DIN_WIDTH  = 4'd8   ;
    parameter DOUT_WIDTH = 5'd16  ;

    // 模块内部代码
    // ...

endmodule

  在上层模块的例化中可以通过defparam进行修改所例化模块中定义参数的值,其格式如下:

module top
(
    // 输出输入声明
    // ...
) ;

    wire    [15:0]    counter_din ; 
    wire    [7:0]     counter_dout;

    // 模块例化
    counter counter_inst
    (
        .din (counter_din ), 
        .dout(counter_dout)
    );
	
	// 格式:
	// defparam 模块例化名称 参数名称 = 重新设定的参数值
	// 如果是多层嵌套子模块,在模块3中例化模块2,在模块2中例化模块1,则格式为:
	// defparam 模块3例化名称 模块2例化名称 模块1例化名称 参数名称 = 重新设定的参数值
    defparam counter_inst CNT_NUM = 8'd100;
    defparam counter_inst DIN_WIDTH  = 4'd6;
    defparam counter_inst DOUT_WIDTH = 5'd12; 

endmodule

  这种方式的缺点在于:该方式声明的parameter无法用于模块输入输出信号位宽的控制,因为参数定义声明在模块的内部。

三、`define

  通过`define定义的参数作用范围是整个设计工程文件,遇到`undef则失效,其格式如下:

`define CNT_NUM 8'd128
module counter
(
    // 输出输入声明
    // ...
)

    // 模块内部代码
    // ...

endmodule

  也可以将所以`define定义声明的参数放在一个单独文件中,并在每个模块中使用`include包含声明文件,以作用于整个工程项目。其格式如下:

// para_def.vh
// 独立参数声明文件
`define CNT_NUM 8'd128
`define DIN_WIDTH  4'd6;
`define DOUT_WIDTH 5'd12; 

  然后再每个模块的前面使用`include包含该参数声明文件即可使用,格式如下:

// `include "路径/参数声明文件名"
`include "F/xxx/RTL/para_def.vh"
module counter
(
    // 输出输入声明
    // ...
)
 
    // 模块内部代码
    // ...
	
	// 使用格式:`参数名
	always @(posedge clk) begin
		if(cnt == `CNT_NUM)
			
		else
		
	end

endmodule

写在最后

  在本文中,我们学习了Verilog基础语法中三种不同的参数定义方式——localparam、parameter与`define,其中,`define定义的参数作用范围最广,且支持用于模块之间的参数传递;localparam作用范围仅为模块内部,且不支持参数传递;而parameter是两者的折中,作用范围为模块内部,但是支持参数传递。在实际代码编写过程中,应选择合适的方式对不同参数进行声明,使用时可以参考下表。

关键字适用场景
localparam仅用于模块内部的参数,且在不同场景下无需进行修改
parameter仅用于模块内部的参数,但是在不同场景下需要进行修改
`define整个工程文件中都会用到的参数

🧐:以上为个人学习笔记,如有疑问,欢迎评论区交流探讨 !!!

module der ( rst, key_in, clk, rw, rs, en, data, led, led1, led2, led3, bee, ); input clk,rst; //CLK 50M,RSTw为复位信号 /***********************给变量赋初值************************/ initial A<=8'd48;//初始密码 0000——0000,d48为0的ASCALL值 initial B<=8'd48; initial C<=8'd48; initial D<=8'd48; initial E<=8'd48; initial F<=8'd48; initial G<=8'd48; initial H<=8'd48; initial bee=0; initial bee_flag=0; initial p=0; initial i<=1; initial led=0; initial led1=0; initial led2=0; initial led3=0; initial flag=0; initial xianshi<=8'd16; //让初始化完以后,就显示空字符 initial add<=8'h80; //让初始化显示的地址 initial shuzi<=8'd57; //让初始化完以后,数字变量为9,ascll码值为57, initial zimu<=8'd122; //为什么是9,因为按键按下后,数字会加一,9加一变为10,10以后跳会0, //所以第一按按键显示0 //让初始化字母为小写z /**************************LCD的初始化函数********************************/ /*******************计数器函数用于在状态机中做延时使用********************/ reg [31:0]count; always @(posedge clk ) begin if(!rst) count<=0; else begin count<=count+1; if(count==2_000_001) begin count<=0; end end end /********************************状态定义********************************/ parameter state0 =6'h00, //设置8位格式,2行显示,5*7 8'h38; state1 =6'h01, //开显示,开光标,闪烁 8'h0e 关光标,不闪烁 8'h0c state2 =6'h02, //输入数据光标右移,显示字符不移动 8'h06 state3 =6'h03, //清屏 8'h01 state4 =6'h04, //显示的地址 add state5 =6'h05; //显示的字符 output rs,en,rw; //LCD控制使能端,rs=0时为写命令,rs=1;为写数据,en为LCD的E引脚,rw=0时,为写,rw=1为读数据(读数据没有用到,所以rw始终为0) output [7:0] data;//输出LCD显示的字符//LCD的D0到D7引脚 reg rs,en_sel; //OUTput的变量要为定义为reg类型,en_sel为LCD E引脚的寄存器变量,最终为E=en_sel reg [7:0]data; reg [7:0]add; //LCD字符显示的地址 reg [7:0] next; //next为状态定义 reg[3:0]lcd_flag; // lcd_flag=0表示LCD正在写入字符,lcd_flag=1;表示LCD写入数据完成 always @(posedge clk,negedge rst) begin if(!rst) begin next<=state0; lcd_flag=0; end else begin case(next) state0 :
03-11
### Verilog 中 `parameter`、`localparam` 和 `define` 的使用方法 #### 定义常量的方法及其特点 在 Verilog 中,`parameter`、`localparam` 和 `define` 均可用于定义常量,但各自具有不同的特性和适用场景。 #### Parameter 参数化模块中的常量 `parameter` 关键字允许创建参数化的模块,在实例化时可由外部指定其值。这使得设计更加灵活并易于配置: ```verilog module example #(parameter WIDTH=8)(input [WIDTH-1:0] a, output reg [WIDTH-1:0] b); always @(a) begin b = ~a; end endmodule ``` 此代码片段展示了如何通过 `parameter` 来设置输入输出端口宽度[^1]。 #### Localparam 局部参数 `localparam` 类似于 `parameter`,但它仅限于当前模块内部可见,不允许被其他地方覆盖或更改。这种特性有助于保护某些关键数值免受意外篡改: ```verilog module counter ( input wire clk, input wire reset, output reg [7:0] count ); localparam MAX_COUNT = 255; always @(posedge clk or posedge reset) if (reset) count <= 0; else if (count >= MAX_COUNT) count <= 0; else count <= count + 1'b1; endmodule ``` 上述例子中,`MAX_COUNT` 被设为不可改变的最大计数界限[^2]。 #### Define 文本替换宏 不同于前两者基于数据类型的声明方式,`define` 是一种预处理器指令,用于实现简单的文本替换功能。它不受作用域限制,并且可以在编译之前完成所有替换操作: ```c // 文件顶部添加如下行即可全局生效 `define PI 3.14159265358979323846 /* 圆周率 */ // 后续可以直接使用PI代替具体数值 wire real area = radius * radius * `PI ; ``` 这里展示了一个利用 `define` 将圆周率作为宏定义的例子[^3]。 综上所述,选择合适的工具取决于实际需求——如果希望提供给用户自定义选项,则应考虑采用 `parameter`; 若是为了确保特定变量的安全性而不愿让外界访问,则推荐运用 `localparam`; 对于那些纯粹为了简化编码过程而存在的固定值来说,`define` 或许是最好的解决方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值