文章目录
1、整数 integer
-
整数(正数 0 负数)是一种通用的寄存器数据类型,用于对数量进行操作,整数的默认位宽为宿主机的字的位数,与具体实现有关,最小为32位。
-
reg和integer:reg的寄存器类型变量为无符号数
integer的寄存器类型变量为有符号数 -
举例:
integer counter; //作为计数器定义 counter = -1; //将-1存储到计数器中 //说明:counter[8] 和 counter[16:2]是非法的
2、verilog的四值逻辑(0、1、x、z)
值的级别 | 硬件电路中的条件 |
---|---|
0 | 逻辑0,条件为假 |
1 | 逻辑1,条件为真 |
x | 逻辑值不确定 |
z | 高阻,浮动状态 |
3、task和function的区别
函数 | 任务 |
---|---|
函数能调用另一个函数,不能调用另一个任务 | 任务能调用另一个任务,也能调用另一个函数 |
函数在仿真0时刻就开始执行 | 任务可以在非0时刻执行 |
函数一定不能包含任何延迟、事件或者控制声明语句 | 任务可以包含任何延迟、事件或者控制声明语句 |
函数至少有一个输入变量,可以有多个输入变量 | 任务可以没有输入变量;或者有多个输入、输出、双向变量 |
函数只能返回一个值,函数不能有(output)和(inout)变量 | 任务不返回任何值,通过输出变量(output)和(inout)传递多个值 |
4、函数:关键字 function-----endfunction
使用函数的前提条件:
- 子程序内不含有延迟、时序或者控制结构
- 子程序只有一个返回值
- 至少有一个输入变量
- 没有输出或者双向变量
- 不含有非阻塞赋值语句
注意:
- 函数中不能调用任务
- 函数中可以调用其他函数
说明:关于函数function—endfunction有两种编码风格
基础语法:
function [返回值类型/位宽范围] [函数名称] ([参数1],[参数2]);
endfunction
//[函数名称]在verilog内部被隐含地声明为一个寄存器类型变量
//如果没有指定[返回值类型/位宽范围],则默认位宽为1
编码风格1:
module fun (
input integer data_in,
output [8:0] data_out
);
//定义一个计算二进制位宽的函数---可以用来计算模块中地址总线的宽度
function [8:0] clogb2;
input bit_depth ;
integer bit_depth ;
begin
for(clogb2 = 0; bit_depth > 0; clogb2 = clogb2 + 1)
bit_depth = bit_depth >> 1;
end
endfunction
assign data_out = clogb2(data_in); //函数的调用:指明函数名clogb2()和输入变量 i_in
endmodule
编码风格2:
module fun (
input integer data_in,
output [8:0] data_out
);
//定义一个计算二进制位宽的函数---可以用来计算模块中地址总线的宽度
function [8:0] clogb2(input integer bit_depth);
begin
for(clogb2 = 0; bit_depth > 0; clogb2 = clogb2 + 1)
bit_depth = bit_depth >> 1;
end
endfunction
assign data_out = clogb2(data_in); //函数的调用:指明函数名clogb2()和输入变量 i_in
endmodule
5、任务:关键字 task-----endtask
如果子程序满足下面任意一个条件,则必须使用任务而不是函数:
- 子程序中包含有延迟、时序或者事件控制结构
- 没有输出或者输出变量的数目大于1
- 没有输入变量
注意:
- 任务可以调用另一个任务,也可以调用另一个函数
基础语法:
task [函数名称] ([参数1],[参数2]);
endtask
举例: 以仿真apb slave总线上的外设为例
`timescale 1ns / 1ps
module apb_master_tb;
reg pclk;
reg presetn;
reg psel;
reg penable;
reg pwrite;
reg [31:0] paddr;
reg [31:0] pwdata;
reg [3:0] pstrb = 4'b1111;
wire [31:0] prdata;
wire pready;
// 时钟生成
always #50 pclk = ~pclk;
reg [31:0] rdata;
initial begin
pclk = 0;
presetn = 0;
#100;
presetn = 1;
#500;
apb_write(32'h0,32'h45af);
apb_write(32'h4,32'h45af);
apb_write(32'h8,32'h45af);
apb_write(32'hc,32'h45af);
#600;
apb_read(32'h0,rdata);
apb_read(32'h4,rdata);
apb_read(32'h8,rdata);
apb_read(32'hc,rdata);
#10000;
$finish;
end
// APB write
task apb_write(input [31:0] addr,input [31:0] data); begin
@(posedge pclk) begin
paddr <= addr; // 设置地址
pwdata <= data; // 设置写数据
pwrite <= 1; // 设置为写操作
psel <= 1; // 选中设备
penable <= 0; // 使能信号
end
@(posedge pclk) begin
pwrite <= 1; // 设置为写操作
psel <= 1; // 选中设备
penable <= 1; // 使能信号
end
@(posedge pclk) begin
pwrite <= 1; // 设置为写操作
psel <= 0; // 结束
penable <= 1; // 使能信号
end
end
endtask
// APB read
task apb_read(input [31:0] addr,output [31:0] data); begin
@(posedge pclk) begin
paddr <= addr; // 设置地址
pwrite <= 0; // 设置为读操作
psel <= 1; // 选中设备
penable <= 0; // 使能信号
end
@(posedge pclk) begin
pwrite <= 0; // 设置为读操作
psel <= 1; // 选中设备
penable <= 1; // 发起读操作
data <= prdata; // 设置读数据
end
@(posedge pclk) begin
pwrite <= 0; // 设置为读操作
psel <= 0; // 结束
penable <= 1; // 发起读操作
end
end
endtask
// APB 设备例化
apb_slave u_apb_slave (
.pclk (pclk),
.presetn (presetn),
.psel (psel),
.paddr (paddr),
.penable (penable),
.pstrb (pstrb),
.pwrite (pwrite),
.pwdata (pwdata),
.pready (pready),
.prdata (prdata)
);
endmodule
6、编译指令–形式:`keyword
(1) `timescale
用法: `timescale <reference_time_unit>/<time_precision>
说明: <reference_time_unit> (参考时间单位):指定时间和延迟的测量单位。
<time_precision>(时间精度):指定仿真过程中延迟值进位取整的精度。
只有1,10,100才是合法的说明时间单位和时间精度的整数。
举例:
`timescale 1ns / 1ps
#10 表示延迟10ns
#6.231678 因为精度为1ps,在6.232时赋值语句生效
(2) `define
说明: 定义Verilog中的文本宏,类似C语言中的#define
举例:
a. 规定子长的文本宏: `define WORD_SIZE 32 //代码中用`WORD_SIZE表示
b. 定义别名: `define S $stop //代码中用`S来代替$stop
c.定义字符串: `define WORD_REG reg [31:0] //用`WORD_REG reg 32来定义一个32位的寄存器变量
(3) `include
说明: 可以在编译期间将一个Verilog源文件包含在另一个Verilog文件中,作用类似于C语言中的#include结构
举例:
可以提前将VGA数据,比如颜色的数据存放在一个源文件中(起名为VGA_Para.v或者VGA_Para.h)在VGA的driver和display模块可以直接调用这个文件VGA_Para.v
`include "VGA_Para.v"
module VGA_Dispaly(
input port1,
output port2
);
endmodule
(4) `ifdef–条件编译
说明: 条件编译指令可以根据指定条件来生成对应的电路,可以减少电路面积并提高代码的复用性。
用法: 在Verilog文件中,条件编译标志可以用`define语句设置。
举例一:有条件的编译模块
`ifdef XOR //若设置NOR标志,则编译design_xor模块
module design_xor;
······
endmodule
`elsif AND //若设置AND标志,则编译design_and模块
module design_and;
······
endmodule
`else //默认执行design_or模块
module design_or;
······
endmodule
`endif
举例二:有条件的编译语句
`define XOR //执行该语句
//`define AND
//`define OR
module test(
input i_data_a,
input i_data_b,
output o_data_result
);
`ifdef XOR
assign o_data_result = i_data_a ^ i_data_b;
`elsif AND
assign o_data_result = i_data_a & i_data_b;
`else
assign o_data_result = i_data_a | i_data_b;
`endif
endmodule
举例三:`ifndef的用法
它的作用和`ifdef 是相反的----当其后的标识符未被定义时,则编译后续的代码段
//`define XOR //注释掉,相当于标识符未被定义
module test(
input i_data_a,
input i_data_b,
output o_data_result
);
`ifndef XOR
assign o_data_result = i_data_a ^ i_data_b; //XOR未被定义,执行该语句
`else
assign o_data_result = i_data_a | i_data_b;
`endif
endmodule
7、系统任务–形式:$keyword
(1) $display
用法: $display(P1,P2,P3,…,Pn);
说明: P1,P2,P3,…,Pn是双引号括起来的 字符串,变量或者表达式;同时$display会自动在字符串的结尾处插入一个换行符。
举例:
$display("Hello Verilog"); //显示小括号中的字符串
$display($time); //显示当前仿真时间
reg[0:40] virtual_addr;//在时间为200的时刻,显示41位虚拟地址1fe0000001c
$display("At time %d virtual address is %h", $time, virtual_addr);
---显示为:At time 200 virtual address is 1fe0000001c
$display("ID of the port is %d", port_id); //二进制数显示port_id 5
---显示为:ID of the port is 00101
字符串格式说明:
格式 | 显示 |
---|---|
%d或%D | 用十进制显示变量 |
%b或%B | 用二进制显示变量 |
%s或%S | 显示字符串 |
%h或%H | 用十六进制显示变量 |
%c或%C | 显示ASCII字符 |
%m或%M | 显示层次名 |
%v或%V | 显示强度 |
%o或%O | 用八进制显示变量 |
%t或%T | 显示当前时间格式 |
%e或%E | 用科学计数法格式显示实数(如3e10) |
%f或%F | 用十进制浮点数格式显示实数 |
%g或%G | 用科学计数法或十进制格式显示实数,显示较短的格式 |
(2) $stop
用法: $stop;
说明:$stop 暂停仿真,使仿真进入一种交互模式,设计者可以在此模式下对设计进行调试
(3) $finish
用法: $finish;
说明:$finish 结束仿真
(4) $random
随机数生成,满足随机测试向量集的需求
举例
$random; //返回一个32位带符号的随机整数,每次仿真。随机序列相同
$random(seed);//seed是一个整数,可指定随机数的取值范围。
//seed可以是reg、integer、time变量
//seed-种子,这一参数由仿真时间决定;seed参数不变,既同一时刻都在调用相同的seed,所以生成的随机序列是相同的。
$random % b; //得到(-b+1)~(b-1)范围内的随机数
$random % 60; //得到 -59 ~ 59范围内的随机数
{$random} % b; //得到 0 ~(b-1)范围内的随机数
{$random} % 60; //得到 0 ~ 59范围内的随机数
8、generate-----endgenerate
可综合的语法,有助于设计通用性强的逻辑电路,综合工具在对代码进行编译时,会将电路展开生成完整的设计代码,然后进行电路综合
举例一:模块设计
//Gray2Bin 格雷码转二进制(8bit)
generate
genvar i; //声明一个正整数变量
for(i=0;i<8;i=i+1) begin :gen_binary
assign binary_value[i] = binary_value[i+1] ^ gray_value[i];
end
endgenerate
举例二:多次例化
//Gray2Bin 格雷码转二进制(8bit)
generate
genvar i; //声明一个正整数变量
for(i=0;i<3;i=i+1) begin :port_inst
port u_port(
.port1 (in[i]),
.port2 (in[i]),
.port3 (in[i]),
.port4 (out[i]),
);
end
endgenerate
该博客为博主记录学习所用,如有错误,请评判指正。博客内容大多来自《Verilog HDL数字设计与综合(第二版)》 -夏宇闻译