Verilog HDL 语言基础语法
2024年1月18日13点56分
发现了一个写的较好的verilog语言总结:
标识符
Verilog 语言区分大小写,也就是说同一个名称,用大写和用小写就代表了两个不同的符号。
在 Verilog 语言中,所有的关键字(又叫保留字)都为小写。
//类似于C语言中的 int if while 等都属于关键字
逻辑值
在二进制计数中,单比特逻辑值只有“0”和“1”两种状态,而在 Verilog 语言中,为了对电路了进行精确的建模,又增加了两种逻辑状态,即“X”和“Z”。
这里主要说一下X 和 Z
当“X”用作信号状态时表示未知,当用作条件判断时(在 casex 或 casez)表示不关心;
“Z”表示高阻状态,也就是没有任何驱动,通常用来对三态总线进行建模。
问:什么是高阻态?
答:FPGA中的高阻态Z是指某个信号线上的电平处于高阻状态。在FPGA中,每个逻辑单元都有能力将其输出引脚设置为高阻态。当一个信号线处于高阻态时,它既不会驱动其他逻辑单元,也不会被其他逻辑单元所驱动。
常量
(1) 整数型;
整数型常量也可以采用基数表示法表示,这种写法清晰明了,所以更推荐这种表示方
法,例如:
(1) 8’hab 表示 8bit 的十六进制数,换算成二进制是 1010_1011;
(2) 8’d171 表示 8bit 的十进制数,换算成二进制是 1010_1011;
(3) 8’o253 表示 8bit 的八进制数,换算成二进制是 1010_1011;
(4) 8’b1010_1011 表示 8bit 的二进制数,二进制就是 1010_1011。
(2) 实数型;
Verilog 语言中的实数型变量可以采用十进制,也可以采用科学计数法,例如:13_2.18e2 表示 13218
(3) 字符串型。
字符串是指双引号中的字符序列,是 8 位 ASCII 码值的序列,例如:
“Hello World”,该字符串包含 11 个 ASCII 符号(两个单词共 10 个符号,单词之间的空格位一个符号,共 11 个 ASCII 符号),一个 ASCII 符号需要 1 个byte 存储,所以共需要 11 个 byte 存储。
变量
Verilog 语言中主要的两种变量类型
- 线网型:表示电路间的物理连接;
- 寄存器型:Verilog 中一个抽象的数据存储单元。
参数
参数是一种常量,通常出现在 module 内部,常被用于定义状态机的状态、数据位宽和
计数器计数个数大小等,例如:
parameter IDLE = 3'b001;
parameter CNT_1S_WIDTH = 4'd15
Parameter CNT_MAX = 25'd24_999_999
parameter 是出现在模块内部的局部定义,只作用于声明的那个文件,可以被灵活改变,这是 parameter 的一个重要特征。
赋值语句
赋值语句的赋值方式有两种,分别为“<=”(非阻塞赋值)和“=”(阻塞赋值)
以下是一个简单的Verilog代码例子,用于说明阻塞赋值和非阻塞赋值的区别:
module test;
reg a, b, c;
initial begin
a = 0; b = 0; c = 0;
#1 a = 1; b <= 2; c = 3;
#1 $display("a=%b, b=%b, c=%b", a, b, c);
end
endmodule
在上述代码中,首先将变量a、b、c都初始化为0。然后,在第1个时间单位,将a赋值为1,执行完该赋值语句后,a的值变为1。然后执行非阻塞赋值语句b <= 2,此时并不会立即将2赋给b,而是在下一个时间单位开始时,b的值才会变为2。然后执行阻塞赋值语句c = 3,此时c的值立刻变为3。因此,在执行完这三条赋值语句后,a的值为1,b的值为2,c的值为3。
这句话说的很到位:仿真进程在遇到阻塞型过程赋值语句时将计算表达式的值并立即将其结果赋给等式左边的被赋值变量;在串行语句块中,下一条语句
的执行会被本条阻塞型过程赋值语句所阻塞,只有在当前这条阻塞型过程赋值语句所对应的赋值操作执行完后下一条语句才能开始执行,如果在阻塞赋值过程中插入非阻塞赋值,则即使本条语句未立即执行,也可执行下一条语句。
系统函数
Verilog 语言中预先定义了一些任务和函数,用于完成一些特殊的功能,它们被称为系统任务和系统函数,这些函数大多数都是只能在 Testbench 仿真中使用的,使我们更方便的进行验证。
$display //打印信息,自动换行
$write //打印信息
$strobe //打印信息,自动换行,最后执行
$monitor //监测变量
$stop //暂停仿真
$finish //结束仿真
$time //时间函数
$random //随机函数
$readmemb //读文件函数
`timescale 1ns/1ns //时间尺度预编译指令 时间单位/时间精度
下面是文心一言的回答:
在FPGA中,时间单位和时间精度是两个重要的概念,它们用于描述仿真时间和延迟时间的精度和尺度。
时间单位是指用于描述仿真时间的基准单位,例如秒(s)、毫秒(ms)、微秒(us)、纳秒(ns)、皮秒(ps)和飞秒(fs)。在FPGA仿真中,时间单位通常用于定义模块中仿真时间和延迟时间的基准单位。
时间精度是指在仿真过程中用来定义模块中仿真时间和延迟时间的精确程度。它通常用时间单位和时间精度的组合来表示。例如,如果时间单位为纳秒(ns),时间精度为10纳秒(ns),则每个时间单位代表10纳秒的仿真时间。在FPGA仿真中,时间精度通常用于声明该模块的仿真时间的精确程度。
在FPGA中,不同的时间单位和时间精度组合可以用于不同的应用场景。例如,对于需要高精度仿真的模块,可以使用较小的时间单位和较高的时间精度来描述仿真时间和延迟时间。而对于需要较大时间尺度的仿真,可以使用较大的时间单位和较低的时间精度来描述仿真时间和延迟时间。
总之,在FPGA中,时间单位和时间精度是两个重要的概念,它们用于描述仿真时间和延迟时间的精度和尺度。根据不同的应用场景选择合适的时间单位和时间精度组合可以更好地控制FPGA仿真的时间和延迟时间,从而更好地模拟电路的行为。
以下是一个使用Verilog语言的FPGA设计例子,其中包含一个数字滤波器模块和一个信号发生器模块。该例子中使用了时间单位和时间精度来描述这两个模块的行为。
module digital_filter(input clk, input rst, input signed [7:0] data_in, output signed [7:0] data_out);
timescale 1ns/1ps; // 时间单位为纳秒(ns),时间精度为皮秒(ps)
reg signed [7:0] shift_reg [0:7]; // 移位寄存器,用于存储输入数据和进行滤波处理
reg signed [7:0] tap_data; // 用于存储移位寄存器中的当前数据
reg signed [7:0] output_data; // 用于存储滤波器的输出数据
always @(posedge clk or posedge rst) begin
if (rst) begin
shift_reg <= 8'b0;
tap_data <= 8'b0;
output_data <= 8'b0;
end else begin
// 将移位寄存器中的数据移入tap_data中
tap_data <= shift_reg[0];
for (int i = 0; i < 7; i = i + 1) begin
shift_reg[i] <= shift_reg[i + 1];
end
shift_reg[7] <= data_in;
// 进行数字滤波处理,并将结果存储在output_data中
output_data <= tap_data + tap_data - tap_data + data_in;
end
end
assign data_out = output_data; // 将滤波器的输出数据分配给输出端口
endmodule
module signal_generator(input clk, output reg signed [7:0] data_out);
timescale 1us/1ps; // 时间单位为微秒(us),时间精度为皮秒(ps)
reg signed [7:0] counter; // 计数器,用于产生时钟信号的频率
reg clk_out; // 时钟信号输出寄存器
initial begin
counter = 8'hFF; // 初始值为255,即产生一个频率为1MHz的时钟信号
clk_out = 1'b0;
end
always @(posedge clk) begin
if (counter == 8'hFF) begin
clk_out <= ~clk_out; // 当时钟计数器达到最大值时取反,以产生频率为1MHz的时钟信号
counter <= 8'h00; // 重置计数器,开始下一个时钟周期
end else begin
counter <= counter + 1; // 当时钟计数器没有达到最大值时递增,以产生不同频率的时钟信号
end
end
assign data_out = clk_out; // 将时钟信号输出分配给输出端口
endmodule
在这个例子中,数字滤波器模块的时间单位为纳秒(ns),时间精度为皮秒(ps),用于描述其仿真时间和延迟时间。信号发生器模块的时间单位为微秒(us),时间精度为皮秒(ps),用于描述其仿真时间和延迟时间。这两个模块的仿真时间和延迟时间尺度不同,因此使用不同的时间单位和时间精度组合可以更好地控制它们的仿真行为。