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

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整个工程文件中都会用到的参数

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

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: FPGA是一种可编程逻辑器件,可以通过编程实现各种功能。而Verilog是一种硬件描述语言,用于描述FPGA中的电路。Verilog基础语法包括模块定义、端口定义、信号定义、赋值语句、条件语句、循环语句等。模块定义是Verilog中最基本的语法,用于定义一个电路模块。端口定义用于定义模块的输入输出端口。信号定义用于定义模块内部的信号。赋值语句用于给信号赋值。条件语句用于根据条件执行不同的代码。循环语句用于重复执行一段代码。掌握Verilog基础语法是学习FPGA编程的基础。 ### 回答2: FPGA是一种可编程逻辑器件,它可以通过硬件描述语言来编写自定义的逻辑电路来实现各种功能。其中,Verilog是一种常用的硬件描述语言,有助于实现FPGA的功能。 Verilog语言主要由模块、端口、信号、语句和运算符五个基本部分组成。模块是Verilog中的最基本的语法单元,类似于传统程序语言中的函数,模块之间可以通过实例化进行连接。端口则是与外部世界进行通信的接口,可以分为输入端口(input)和输出端口(output)两种类型。信号是Verilog中表示数字信号的基本单元,可以是位向量、整数或实数。语句则是Verilog中描述操作和行为的语言,常用的语句包括赋值语句、分支语句和循环语句。运算符则是Verilog中用于进行操作的符号,包括算数运算符、逻辑运算符和位运算符。 在Verilog语言中,还有一些常用的结构体和命令可以帮助我们更方便地实现FPGA的功能。其中,常用的结构体包括always语句、case语句和module归档,常用的命令包括initial语句、wire语句和reg语句。always语句可以在指定的触发条件下执行某一段代码,case语句可以根据不同的条件执行不同的代码段,module归档则可以将多个模块合并为一个模块。initial语句可用于在仿真开始前初始化某些信号,wire语句则用于声明并连接信号,reg语句则用于声明并存储信号。 总体而言,了解FPGAVerilog基础语法可以帮助我们更好地理解FPGA技术的应用和实现。然而,理论知识只有结合实际操作和实验才能更好地掌握。所以,我们还需要结合实际项目来进行练习和实践,从而更好地掌握FPGAVerilog基础语法。 ### 回答3: FPGA(可编程门阵列)是一种可编程逻辑器件,可以用来创建定制的数字电路。Verilog是一种硬件描述语言,用于描述数字电路的结构和行为。 Verilog基础语法有以下几个部分: 1.模块定义:Verilog代码以模块的形式进行组织,每个模块都有一个名称和端口列表。模块定义以module关键字开始,以endmodule关键字结束。 2.端口声明:模块的端口是输入和输出连接到其他模块或FPGA芯片的引脚。端口可以是输入(input)、输出(output)或双向(inout)。端口声明在模块定义中。 3.信号声明:信号是描述数字电路中状态的变量。可以是单个位(wire)或多位(reg),在模块中声明。 4.赋值语句:用来为信号赋值,包括非阻塞赋值(<=)、阻塞赋值(=)和连续赋值(assign)。 5.条件分支语句:if, else if和else语句是用来控制程序流程,实现条件判断。 6.循环语句:Verilog支持for、while、do while和forever等类型的循环语句,可以在程序中实现迭代操作。 7.模块实例化:用来将其他模块作为子模块嵌入到当前模块中,从而实现复杂的数字电路。 除上述基本语法外,Verilog还有其他常用语法,如always块、initial块和function定义等。需要深入了解和使用Verilog,可以参考相关资料和教程。掌握了Verilog语法,可以使用FPGA搭建各种个性化的数字电路,用于嵌入式系统、数字信号处理、计算机网络等各种应用领域。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值