Verilog 语法 (一)

有意义的名称:
确保命名能够直观表达信号的用途和功能。例如,sum
应该表示一个和,cpu_addr
应该表示 CPU 的地址线。
下划线风格:
使用下划线(_
)来分隔不同的词。这样可以提高可读性,并避免单词混淆。例如,data_in
,address_out
,clk_50m
等。
前缀与后缀:
时钟信号:如你所说,使用 clk_
作为前缀,如 clk_50m
,clk_cpu
。
低电平信号:使用 _n
后缀标识低电平有效的信号,如 reset_n
,enable_n
。
有效信号:信号名可以使用 _valid
后缀,如 data_valid
表示数据有效信号。
使能信号:使用 _en
后缀,如 write_en
,read_en
。
统一缩写:
使用标准和一致的缩写。例如,rst
一般表示复位信号,en
表示使能信号。
避免自定义缩写,除非它们能增加可读性且广泛被使用。
信号一致性:
在同一个模块或不同模块中保持信号命名的一致性,尤其是在多层次设计中。例如,同一时钟信号可以保持命名为 clk_50m
,而在模块中使用相同的名称。
在层次化的设计中,层级之间的信号可以有统一的命名规则,比如 top_clk
和 submodule_clk
。
避免保留字冲突:
确保自定义的标识符与 Verilog 语言的保留字(如 if
, else
, module
, input
, output
等)不冲突。
如果有疑问,可以参考 Verilog 的保留字列表。
参数命名规范:
参数使用全大写字母,如 SIZE
,WIDTH
,DEPTH
,这可以让它们区别于信号名或变量。
如果参数有多个单词,可以使用下划线分隔,如 MAX_DEPTH
,ADDR_WIDTH
。
模块名称和实例化命名:
模块名通常采用首字母大写的驼峰命名法,如 DataProcessor
,ControlUnit
。
实例化时,可以使用类似 data_processor_0
或 control_unit_top
的命名方式,明确表示该实例的作用和层级。
信号类型和位宽标识:
对于带宽的信号,可以在名称中包含位宽信息,如 data_in_8
表示 8 位数据输入。
对于多维信号(如多位总线或数组),可以使用带宽或索引信息,如 addr_bus[7:0]
。
4'b0101 // 4 位二进制,表示 5 (十进制)
4'd2 // 4 位十进制,表示 2 (二进制 0010)
4'ha // 4 位十六进制,表示 10 (十进制) -> 二进制 1010
32'd100 // 默认 32 位十进制,表示 100
8'o17 // 8 位八进制,表示 15 (十进制) -> 二进制 00001111
//reg define
reg [31:0] delay_cnt; //延时计数器
reg key_flag ; //按键标志
reg
是最常用的寄存器类型,它用于存储逻辑值(0
、1
或 X
等)。
它只能在 always
语句(带时钟)或 initial
语句中被赋值。
reg
的大小可以由方括号 []
来指定,如 reg [31:0]
表示一个 32 位的寄存器。
//wire define
wire data_en; //数据使能信号
wire [7:0] data ; //数据
wire
类型用于表示 连接信号 或 传输信号,它是 线网类型 的一部分。你给出的示例是两个 wire
类型的信号:data_en
和 data
。
wire data_en;
这个 wire
类型的信号 data_en
被定义为一个单比特(1 位)信号,用于表示 数据使能信号。它通常用来控制某个数据传输操作的启用或禁用。例如,data_en
可以用来控制 data
是否有效。
用途示例:
数据使能信号:当 data_en
为高电平(1
)时,表示数据有效,可以进行传输或操作;当 data_en
为低电平(0
)时,数据可能被忽略或不传输。
wire [7:0] data;
这个 wire
类型的信号 data
被定义为 8 位宽的信号([7:0]
),用于表示 数据。这个信号通常用来传输实际的数据值,例如一个 8 位的字节数据。
用途示例:
数据传输:data
可以传递一个 8 位的数据单元,可以是从一个模块传到另一个模块,或者用于在不同寄存器间传输数据。
parameter
的定义和作用
parameter
类型的常量在模块中定义,并且可以在模块实例化时进行修改。这样可以使得模块具有灵活的配置能力,而无需修改模块内部的实现代码。定义方法:parameter
可以在模块中定义并赋予默认值。例如:
parameter DATA_WIDTH = 8; // 数据位宽为 8 位
数据位宽:可以用 parameter
来定义数据总线的宽度,便于模块复用。
状态机状态数量:在设计状态机时,可以使用 parameter
来定义状态的数量或者状态的编码值。
延迟大小:在某些模块中,可以用 parameter
来定义延迟的大小,以便根据实际需求进行调整。
关键词 | 描述 |
---|---|
module | 模块开始定义 |
input | 输入端口定义 |
output | 输出端口定义 |
inout | 双向端口定义 |
parameter | 信号的参数定义 |
wire | 信号定义(用于线网类型信号) |
reg | 信号定义(用于寄存器类型信号) |
always | 产生 reg 信号语句的关键字 |
assign | 产生 wire 信号语句的关键字 |
begin | 语句的起始标志 |
end | 语句的结束标志 |
posedge /negedge | 时序电路的标志 |
case | Case 语句起始标记 |
default | Case 语句的默认分支标志 |
endcase | Case 语句结束标记 |
if | if 语句标记 |
else /else if | else / else if 语句标记 |
for | for 语句标记 |
endmodule | 模块结束定义 |
接下来呢,我们就通过实际的一个例子,来将上述的知识点融会贯通。
module led(input sys_clk , //系统时钟input sys_rst_n, //系统复位,低电平有效output reg [3:0] led //4 位 LED 灯
);//parameter define
parameter WIDTH = 25 ;
parameter COUNT_MAX = 25_000_000; //板载 50M 时钟=20ns,0.5s/20ns=25000000,需要 25bit
//位宽//reg define
reg [WIDTH-1:0] counter ;
reg [1:0] led_ctrl_cnt;//wire define
wire counter_en ;//***********************************************************************************
//** main code
//***********************************************************************************//计数到最大值时产生高电平使能信号
assign counter_en = (counter == (COUNT_MAX - 1'b1)) ? 1'b1 : 1'b0; //用于产生 0.5 秒使能信号的计数器
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0)counter <= 1'b0;
else if (counter_en)counter <= 1'b0;
elsecounter <= counter + 1'b1;
end//led 流水控制计数器
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0)led_ctrl_cnt <= 2'b0;
else if (counter_en)led_ctrl_cnt <= led_ctrl_cnt + 2'b1;
end//通过控制 IO 口的高低电平实现发光二极管的亮灭
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0)led <= 4'b0;
else begincase (led_ctrl_cnt) 2'd0 : led <= 4'b0001;2'd1 : led <= 4'b0010;2'd2 : led <= 4'b0100;2'd3 : led <= 4'b1000;default : ;endcase
end
endendmodule
因为是第一次笔者讲verilog代码,详细讲一下,我们开始。
第一部分内容就是模块的定义,如下所示:
module led(input sys_clk, // 系统时钟input sys_rst_n, // 系统复位,低电平有效output reg [3:0] led // 4 位 LED 灯输出
);
该模块名为 led
,包含一个系统时钟 sys_clk
、复位信号 sys_rst_n
,和一个 4 位的 LED 输出端口 led
。
参数定义
parameter WIDTH = 25 ;
parameter COUNT_MAX = 25_000_000; // 计数最大值
WIDTH
定义了计数器的位宽,这里设置为 25 位。
COUNT_MAX
设定了计数器的最大值,使用板载 50 MHz 时钟,经过 25 位计数器和 0.5 秒定时的计算结果为 25_000_000。
寄存器定义
reg [WIDTH-1:0] counter ; // 用于计数的寄存器
reg [1:0] led_ctrl_cnt; // 用于控制 LED 流水灯状态的计数器
counter
用于记录时钟周期计数,并达到 COUNT_MAX
后触发 LED 切换。
led_ctrl_cnt
控制流水灯的状态,在 counter_en
使能信号触发时改变。
信号定义
wire counter_en ; // 使能信号
counter_en
为计数器使能信号,当 counter
达到 COUNT_MAX
时,该信号为高电平,表示计数周期已完成。
计数器使能信号的生成
assign counter_en = (counter == (COUNT_MAX - 1'b1)) ? 1'b1 : 1'b0;
counter_en
当 counter
达到最大值时,产生高电平信号,表示计数周期已完成。
计数器模块
always @(posedge sys_clk or negedge sys_rst_n) beginif (sys_rst_n == 1'b0)counter <= 1'b0; // 复位时,计数器清零else if (counter_en)counter <= 1'b0; // 计数完成时,计数器清零elsecounter <= counter + 1'b1; // 每个时钟周期加 1
end
counter
计数器每次在时钟上升沿累加,计数到 COUNT_MAX
后复位。
LED 控制计数器
always @(posedge sys_clk or negedge sys_rst_n) beginif (sys_rst_n == 1'b0)led_ctrl_cnt <= 2'b0; // 复位时,LED 控制计数器清零else if (counter_en)led_ctrl_cnt <= led_ctrl_cnt + 2'b1; // 每次计数完成后加 1
end
led_ctrl_cnt
用于控制 LED 灯的流水显示,每次计数周期完成时自增,改变流水灯显示的顺序。
LED 灯控制
always @(posedge sys_clk or negedge sys_rst_n) beginif (sys_rst_n == 1'b0)led <= 4'b0; // 复位时,LED 灯关闭else begincase (led_ctrl_cnt) 2'd0 : led <= 4'b0001; // LED 灯显示第一个2'd1 : led <= 4'b0010; // LED 灯显示第二个2'd2 : led <= 4'b0100; // LED 灯显示第三个2'd3 : led <= 4'b1000; // LED 灯显示第四个default : ;endcaseend
end
根据 led_ctrl_cnt
控制 LED 灯的显示状态,形成一个 4 位 LED 的流水效果。每次计数周期完成后,led
的值依次切换。