基于语言的设计易于移植(设计团队可以重用或修改以前的设计,以保持更先进工艺的一致性)且不依赖于工艺(随着器件物理尺寸的缩小,电路密度的提高,基于原有HDL模型进行综合生成的电路同样具有更高的性能)。
目录
数值表示
数值种类:0、1、x(未知)、z(高阻)
- 整数数值表示方法:十进制'd/'D,十六进制'h/'H,二进制'b/'B,八进制'o/'O
- 负数表示:-6'd15,-15
- 实数表示:
- 十进制:30.123,6.0,0.01
- 科学计数法:
- 1.2e4//大小为12000
- 1_0001e4//大小为10010000
- 1E-3//大小为0.00
- 字符串表示
reg[0:14*8-1] str;
initial begin
str="www.runoob.com";
end
数据类型
- 线网wire
- 寄存器reg:无符号数。
- 向量(当位宽大于1时,wire或reg即可声明为向量形式)
//1.verilog支持bit位后固定位宽的向量域选择访问
[bit+:width] 从起始位bit开始递增,位宽位width
[bit-:width] 从起始位bit开始递减,位宽位width
//下面两种赋值等效
A=data1[31-:8];
A=data1[31:24];
//2.对信号重新进行组合成新的向量时,需要借助大括号
wire[31:0] temp1,temp2;
assign temp1={byte1[0][7:0],data1[31:8]};//数据拼接
assign temp2={32{1'b0}};//赋值32位的数值0
- 整数integer:声明时不用指明位宽,位宽和编译器有关,一般为32bit。有符号数。
- 实数real:可用十进制或科学计数法来表示,实数声明不能带有范围,默认值为0。如果将一个实数赋值给一个整数,则只有实数的整数部分会赋值给整数。
real data1;
integer temp;
initial begin
data1 = 2e3;
data2 = 3.75;
end
initial begin
temp = data1;//temp大小为3
end
- 时间time:Verilog使用特殊的时间寄存器time型变量,对仿真时间进行保存,其宽度一般为64bit,通过调用系统函数$time获取当前仿真时间。
time current_time;
initial begin
#100
current_time=$time;//current_time大小为100
end
- 数组
integer flag [7:0] ; //8个整数组成的数组
reg [3:0] counter [3:0] ; //由4个4bit计数器组成的数组
wire [7:0] addr_bus [3:0] ; //由4个8bit wire型变量组成的数组
wire data_bit[7:0][5:0] ; //声明1bit wire型变量的二维数组
reg [31:0] data_4d[11:0][3:0][3:0][255:0] ; //声明4维的32bit数据变量数组
flag [1] = 32'd0 ; //将flag数组中第二个元素赋值为32bit的0值
counter[3] = 4'hF ; //将数组counter中第4个元素的值赋值为4bit 十六进制数F,等效于counter[3][3:0] = 4'hF,即可省略宽度;
assign addr_bus[0] = 8'b0 ; //将数组addr_bus中第一个元素的值赋值为0
assign data_bit[0][1] = 1'b1; //将数组data_bit的第1行第2列的元素赋值为1,这里不能省略第二个访问标号,即 assign data_bit[0] = 1'b1; 是非法的。
data_4d[0][0][0][0][15:0] = 15'd3 ; //将数组data_4d中标号为[0][0][0][0]的寄存器单元的15~0bit赋值为3
- 存储器:本质是寄存器数组,可用来描述RAM或ROM的行为。
reg membit[0:255];//256个的1bit存储器
reg[7:0] mem[0:1023];1kbit存储器,有1024个位宽8bit的存储器
mem[511]=8'b0;//令第512个8bit的存储单元值为0
- 参数parameter
- 字符串
表达式与操作符
参考https://www.runoob.com/w3cnote/verilog-expression.html
function与task
在verilog中可以利用task或者function将重复性的行为级设计进行提取,并在多个地方调用,来避免重复代码的多次编写,使得代码更加的简洁易懂。
| function | task |
| 不可以在module外定义 | 同function |
| 不能含有时序逻辑(时延、非阻塞赋值、时序逻辑等) | 可以含有时序逻辑,但不能有always块 |
| 至少一个input,有返回值,没有输出 | 可没有或有多个输入输出,可存在inout类型 |
| 不可以做为语句单独出现,只能出现在赋值语句右侧 | 可以作为语句单独出现 |
| 不可以调用task,可调用function | 可以调用task与function |
function
//返回值为宽度range的function_id,默认宽度为1
function[range-1:0] function_id;
input_declaration;
other_declaration;
procedural_statement;
endfunction
task
进行task的逻辑设计时可以把input看成wire型,output看成reg型,但是不需要用reg对output端口进行再次说明。
对output信号赋值时也不要使用关键字assign,为避免时序错乱,建议output信号采用阻塞赋值。
任务调用时,端口必须按顺序对应。输入端连接的模块内信号可以是 wire 型,也可以是 reg 型。输出端连接的模块内信号要求一定是 reg 型,这点需要注意。
task task_id;
port_declaration;
procedural_statement;
endtask
//实例1
task xor_poer_iner;
input[N-1:0] numa;
input[N-1:0] numb;
output[N-1:0] numo;
//output reg [N-1:0] numco ; //无需再注明 reg 类型,虽然注明也可能没错
#3 numco=numa^numb;
//assign #3 numco = numa ^ numb ; //不用assign,因为输出默认是reg
endtask
//实例2
task xor_oper_iner(
input [N-1:0] numa,
input [N-1:0] numb,
output [N-1:0] numco ) ;
#3 numco = numa ^ numb ;
endtask
常数函数
仿真开始之前,在编译期间就计算出结果为常数的函数。
常数函数不允许访问全局变量或者调用系统函数,但是可以调用另一个常数函数。
//计算模块中地址总线的宽度
parameter MEM_DEPTH = 256 ;
reg [logb2(MEM_DEPTH)-1: 0] addr ; //可得addr的宽度为8bit
function integer logb2;
input integer depth ;
//256为9bit,我们最终数据应该是8,所以需depth=2时提前停止循环
for(logb2=0; depth>1; logb2=logb2+1) begin
depth = depth >> 1 ;
end
endfunction
automatic函数
一般函数的局部变量是静态的,即函数每次调用时,函数的局部变量都会使用同一个存储空间,若某个函数在两个不同的地方同时并发的调用,那么两个函数调用行为同时对同一块地址进行操作,会导致不确定的函数结果。
使用automatic关键字来对函数进行说明,此类函数在调用时可以自动分配新的内存空间,也可以理解为是可递归的。因此,automatic 函数中声明的局部变量不能通过层次命名进行访问,但是 automatic 函数本身可以通过层次名进行调用。
wire [31:0] results3 = factorial(4);
function automatic integer factorial ;
input integer data ;
integer i ;
begin
factorial = (data>=2)? data * factorial(data-1) : 1 ;
end
endfunction // factorial
循环语句
- while
while(condition)begin
end
- for
for(;;)begin
end
- repeat
repeat(loop_times固定次数)begin
//执行期间loops发生变化,repeat次数也不会改变
end
- forever
forever begin
//$finish可以退出forever
end
- generate
通过generate循环可以产生一个对象(比如一个元件或者一个模块)的多次例化,一般在循环和条件语句中使用。
同一个文件中genvar循环变量名不能重复,也就是说,有几个循环就需要几个不同的genvar变量;
genvar定义的变量不要在always语句中使用,可以用integer变量代替
moudule buffer_1(
input in,
output out);
assign out=~in;
endmodule
module buffer_8(
input[7:0] din,
output[7:0] dout);
//循环变量i必须是genvar类型的,不可以是reg或integer
genvar i;
generate
for(i=0;i<8;i=i+1)begin:BLOCK1//for要有名字
buffer_1 buffer_1_1(.in(din[i]),.out(dout[i]));
end
endgenerate
endmodule
//testbench
module buffer_8_tb;
reg[7:0] din;
wire[7:0] dout;
//Instance the DUT
buffer_8 buffer_8_1(.din(din),.dout(dout));
//generate the stimulate
initial begin
din=8'd7;
#10;din=8'd6;
#10;din=8'd4;
#10;din=8'd7;
#10;$finish();
end
endmodule
在generate语句中可以出现以下三种语句:
generate
//generate...loop循环语句
//generate...case分支语句
case(condition)
endcase
//generate...conditional条件语句
if(condition) statements//condition必须是静态条件
else statements
//嵌套的generate语句
endgenerate
本文详细介绍了Verilog HDL中的数值表示,包括不同类型的数值(0、1、x、z)、数据类型(如wire、reg、integer、real、time等)、表达式与操作符、以及function和task的使用。function和task用于提取重复性设计,提高代码可读性。此外,还讨论了常数函数和automatic函数的特点,以及各种循环语句的用法,如while、for、repeat、forever和generate。
4697

被折叠的 条评论
为什么被折叠?



