Verilog语法基础
1. 格式
首先,要说明Verilog区分大小写。你可以在一行里写完,也可以跨多行来写。但是用一行来写并不明智,因为你的同事并不一定能快速读懂你的代码甚至来问你。
wire [2:0] output_signal;
assign output_signal = (x == 1'b1) ? 3'b100 :
(y == 1'b1) ? 3'b010 :
3'b001;
你注意到了:跟C语言一样,Verilog里使用分号来结束语句。
2. 注释
你可以用//
进行单行注释,也可以用/* */
进行跨行注释。
reg [4:0] counter ; // counter is a 5-bit register
/*
wire [2:0] output_signal;
assign output_signal = (x == 1'b1) ? 3'b100 :
*/
3. 标识符&关键字
你可以使用任何字母、数字、下划线和美元符$来命名标识符。但是,不能以数字和美元符开头。标识符区分大小写。
你不能使用关键字作为标识符,这篇文章告诉你哪些是关键字:
Verilog 中关键字全部为小写。
reg [4:0] counter ; // reg is a keyword, counter is an identifier
4. 数值表示方法
你可以使用十进制、二进制、八进制和十六进制来表示数值。
十进制('d 或 'D),十六进制('h 或 'H),二进制('b 或 'B),八进制('o 或 'O)。指明位宽是任意的。
你还可以指定负数,但负号不能放在基数和数字之间。
counter = 5'd10; // 5位宽的十进制数10
counter = -5'hA; // 5位宽的十六进制数A
counter = 5'b-101; // 非法说明
counter = 'b101; // 一般根据编译器自动分频位宽,常为32bit
实数表示方法:
reg [4:0] counter; // 5 位宽的寄存器定义
// 正确的整数赋值
counter = 5'd10; // 将 counter 赋值为十进制的 10
// 假设浮点数 10.5e-2 转换为整数
reg [4:0] counter;
real float_value;
integer int_value;
float_value = 10.5e-2; // 浮点数 0.105
int_value = $rtoi(float_value * 100); // 转换为整数 10.5 * 100 = 10.5,取整为 10
counter = int_value[4:0]; // 确保只取低 5 位
reg [4:0] counter;
// 假设要表示的数字是 10000
integer large_value = 10000;
counter = large_value[4:0]; // 需要确保大数在 5 位范围内
// 使用 5'd 语法定义整数时,确保只使用整数值,并且值在 0 到 31 之间。
counter = 5'd31; // 最大值
其中,四种基本的值表示电平逻辑:
- 逻辑零:0'b0 或 0'h0 或 0'd0
- 逻辑一:1'b1 或 1'h1 或 1'd1
- 逻辑高 impedance:1'bz 或 1'hz 或 1'dz
- 逻辑 don't care:1'bx 或 1'hx 或 1'dx
逻辑零表示逻辑低电平,逻辑一表示逻辑高电平。逻辑高 impedance 表示逻辑高阻抗,逻辑 don't care 表示逻辑不确定。
5. 字符串表示方法
Verilog中,字符串表示方法与C语言类似。字符串为一系列的单字节ASCII字符队列。因此,需要预留出存储单元。字符串不能多行,只能单行。不能包含回车符。
reg [0:12*8-1] str;
initial begin
str = "Hello, world!";
end
6. 数据类型
6.1 线网(wire)
线网(wire)类型表示硬件单元之间的物理连线,由其连接的器件输出端连续驱动。
但也存在没有驱动元件连接到wire型变量的情况,此时缺省值一般为Z(高阻)。
wire interrupt;
wire data1, data2;
wire gnd = 1b'0;
同时,线网型还有其他数据类型。您可以在这里查询。
6.2 寄存器(reg)
reg类型表示存储单元,保持数据原有的值,直到被改写。
reg clk;
reg data1, data2;
在always块中,寄存器可能被综合为边沿触发器,在组合逻辑中可能被综合为wire型变量。寄存器不需要驱动源,也不一定需要时钟信号。在仿真时,寄存器的值可在任意时刻通过赋值操作进行改写:
reg clk;
initial begin
clk = 1'b0;
# 101
clk = 1'b1;
end
6.3 向量
位宽大于1,wire/reg可声明为向量的形式:
wire [2:0] data; // 声明一个3位的wire型变量
wire [8:2] addr ;
reg [0:31] data1; // 声明一个32位的reg型变量
对于上面的向量,可以指定某一位或若干相邻位,作为其他逻辑使用,
wire [9:0] data_low = data1[0:9] ;
addr_temp[3:2] = addr[8:7] + 1'b1 ;
支持可变的向量域选择:
module variable_slice_example #(parameter WIDTH = 32, parameter START = 8, parameter STOP = 15) (
input wire [WIDTH-1:0] data_in, // 输入数据
output wire [STOP-START:0] data_out // 输出选择的域
);
// 使用可变的向量域选择
assign data_out = data_in[STOP:START];
endmodule
module top;
reg [31:0] data1; // 32位输入数据
wire [STOP-START:0] output_data; // 输出信号
// 定义选择参数
parameter START = 8;
parameter STOP = 15;
// 实例化 variable_slice_example,动态选择数据域
variable_slice_example #(.WIDTH(32), .START(START), .STOP(STOP)) slice_instance (
.data_in(data1),
.data_out(output_data)
);
initial begin
// 测试输入
data1 = 32'hAABBCCDD; // 输入测试数据
// 等待一段时间观察输出
#10;
// 打印结果
$display("data1 = %h", data1); // 显示输入数据
$display("output_data = %h", output_data); // 输出选择的域
// 结束仿真
$finish;
end
endmodule