本文主要讲述日常在RTL Coding 时高频出现的几个关键词,熟练掌握不仅可以使得代码简洁,而且效率也可以大大提升。本文介绍的关键词有:parameter、localparam、`define、`ifdef `else `endif generate、for、function和`include。
一、parameter
作用 : 常用于模块间参数传递,参数化设计。
范围 : 本module内有效的定义,且定义未声明位宽时系统一般默认32bit。
dram#(
.RAM_WIDTH (RAM_WIDTH ),
.RAM_ADDR (RAM_ADDR )
)
dram_u0(
.clk ( clk ),
.rst_n ( rst_n ),
.wr ( wr ),
.din ( din ),
.addr ( addr ),
.rd ( rd ),
.rd_data ( rd_data )
);
二、localparam
作用:常用于状态机的参数定义,参数化设计
范围:本module内有效的定义,不可用于参数传递
module dram#(
parameter RAM_WIDTH = 8,
//parameter RAM_WIDTH = 8'd8,
parameter RAM_ADDR = 4
//parameter RAM_ADDR = 8'd4
)
(
input clk,
input rst_n,
input wr,
input [RAM_WIDTH-1:0] din,
input [RAM_ADDR-1:0] addr,
input rd,
output reg[RAM_WIDTH-1:0] rd_data
);
localparam RAM_DEPTH = (1<<RAM_ADDR);
三、`define
作用:宏定义,常配合条件编译指令进行代码/模块隔离或注释
范围:可以跨模块的定义,写在模块名称上面,在整个设计工程都有效。一旦define指令被编译,其在整个编译过程中都有效。
禁忌:不能滥用。在芯片设计中代码规模一般比较庞大,每人负责一部分模块,如果不加约束话,宏名定义很容易撞到,又由于其作用整个工程的,就会造成定义混乱.特别注意`define 定义常值变量时,使用时必须也要点`(`NUM).
四、`ifdef `else `endif
作用:条件编译关键词,如上述3所述,常配合`define 使用。
常见场景:IC设计工程代码常包含FPGA代码分支、仿真代码分支和ASIC代码分支,根据需要开启各个分支宏定义即可,不需要分别建立3个不同的工程避免不必要的麻烦。多个条件时使用:`ifdef `elsif `elsif `else `endif .
`dedine NUM 666
`define SW_SIM
module cnt_exa(
input clk,
input rst_n,
output reg [(DW-1):0] cnt
);
`ifdef SW_SIM
parameter DW = 8;
`else
parameter DW = 16;
`endif
always@(posedge clk)
begin
if(!rst_n)
cnt <= {DW{1'b0}};
else if(cnt == `NUM)
cnt <={DW{1'b0}};
else
cnt <= cnt + 1'b1;
end
endmodule
五、generate
在设计中,很多情况下我们需要编写很多结构相同但参数不同的赋值语句或逻辑语句块,当参数量很大的情况下,原始的列举就会变得很笨拙甚至是不可行的。而verilog 语言为我们提供了generate语句块来帮助我们完成这些过程generate 语句有 generate-for、generate-if 和 generate-case 三种语句。此外,generate 条件用法在同一个文件里可以"隔离/注释"代码.比如在某种场景启用不同的代码,类似于上述条件编译指令作用。
5.1 generate for循环
genvar i;
generate for(i=0;i<n;i=i+1)
begin :xxx
// logic
end
endgenerate
5.2 generate if条件
generate允许对语句进行条件选择,即将条件选择加入到generate中的for循环中,只例化条件成立时对应的语句或者module,多种场景时可以使用
genvar i;
generate for(i=0;i<n;i=i+1)
begin :xxx
if( 条件1)
else if(条件2)
else
end
endgenerate
5.3 generate case条件
generate-case分支语句与generate-条件语句类似,只不过将原来的分支语句换做了case语句。值得注意的是,不需要 genvar i 变量;也没有for循环,多种场景时可以使用。
generate
begin :xxx
case (N)
1: begin end
2: begin end
default: begin end
end
endgenerate
5.4 generate 嵌套for循环
genvar i,k;
generate
for(i=0;i<n;i=i+1)
begin :xxx
for(j=0;j<n;j=j+1)
begin :xxx
data[i][j] = src_data[j];
end
end
endgenerate
六、for循环
作用:for循环是可综合的,因为其在预编译过程中,已经把电路复制多份。在Verilog中,for循环一般用作输入多次有一定规律的赋值语句,以提高设计效率。
实例代码片段:给reg搭建的存储器赋初值
integer i;
reg [7:0] mem [0:15];
always@(posedge clk)begin
if(reset)begin
for(i=0;i<15;i=i+1)
mem[i] <=8'd0;
end
else if(wr)begin
mem[wr_addr] <= wr_data;
end
end
七、function
作用:Verilog的函数,类似C语言的函数,目的是返回一个用于表达式的值,可简化代码
使用规则:
1)函数定义不能包含有任何的时间控制语句,即任何用#、@、wait来标识的语句。
2) 函数不能调用“task”。
3) 定义函数时至少要有一个输入参数。
4) 在函数的定义中必须有一条赋值语句给函数中与函数名同名、位宽相同的内部寄存器赋值。
5) verilog中的function只能用于组合逻辑
标准写法:
function <返回值的类型或范围>(函数名);
<端口说明语句>// input XXX
<变量类型说明语句>// reg YYY
......
begin
<语句>
......
函数名 = ZZZ;// 函数名就相当于输出变量;
end
endfunction
实例:
function [31:0] factorial;
input [3:0] operand;
reg[3:0] index;
begin
factorial =1;
for(index=2;index<operand;index=index+1)
factorial = index* factorial;
end
endfunction
八、`include
作用:Verilog HDL语言提供了`include命令用来实现"文件包含"的操作。所谓"文件包含"处理是一个源文件可以将另外一个源文件的全部内容包含进来,即将另外的文件包含到本文件之中.因其也是在编译时进行的预处理,所以说是可综合的。
使用场景:常用于公共的文件包含而且可能会频繁修改,使得代码简洁化。这里的文件可以是任何形式的,可以是一个参数定义List或者一个代码片段,也可以是一个完整的Module.
如上图所示,在编译的时候,需要对`include命令进行"文件包含"预处理:将File2.v的全部内容复制插入到`include "File2.v"命令出现的地方,即将File2.v被包含到File1.v中,在接着往下进行编译中,将"包含"以后的File1.v作为一个源文件单位进行编译。
使用注意:
1) 一个`include命令只能指定一个被包含的文件,如果要包含n个文件,要用n个`include命令。include可以理解为“插入”处理,因此需要注意文件上下文顺序关系。
错误写法:`include"aaa.v""bbb.v"
正确写法:`include"aaa.v" `include"bbb.v"
或 `include"aaa.v"
`include"bbb.v"
2) `include命令可以出现在Verilog HDL源程序的任何地方,被包含文件名可以是相对路径名,也可以是绝对路径名。例如:'include"./common/image_param.v"