入门级,学Verilog必须要懂的语法
目录
内部信号声明 在模块内用到的和与端口有关的wire和reg声明
基本词汇元素
标识符 标识符赋予了一个对象唯一的名字,如eq1、i0等等。由字母、数字、下划线和符号$,$一般与系统任务或者函数一起使用
关键字 关键字是指用于描述语言结构的预定义标识符。比如module和wire。
间隔符 间隔符在Verilog代码中用于分隔标识符,包括空格、制表符及换行符。
注释 注释仅用于记录,与C语言注释一样,有两种表达方式
数据类型
四值系统
- 0:表示逻辑0,或条件为假
- 1:表示逻辑1,或条件为真
- Z:表示高阻态
- X:表示不确定值
Z值相当于三态缓冲器的输出。X值通常用于建模和仿真。
数据类型分类 (wire and reg)
Verilog有两个数据类型:线网和变量。
线网 该数据类型表示硬件元件间的物理连线。用于作为连续赋值的输出及不同模块间的连接信号。
最常用的数据类型是 wire 型
wire 数据类型表示1bit的信号,如
wire p0,p1; //两个1bit的信号
当信号的集合划为一条总线时,我们使用一维数组来表示,如:
wire [7:0] data1,data2; //8bit数据
wire [31:0] addr; //32bit地址
wire [0:7] revers_data; //应避免升序索引
注意:索引范围可以是[0:7],也可以是[7:0]
有时候需要使用二维数组表示一个存储器。例如一个32×4的存储器(就是一个有32个字而每个字为4bit位宽)可以表示为
wire [3:0] meal [31:0]; //乘4 寄存器
线网类中另一些数据类型表示特定的逻辑行为或者功能,例如 wand(用于线与连接)及 supply0(用于电路接地连接)。
变量类 变量类中的数据类型表示行为模型中的抽象储存,用于过程赋值的输出。变量类包含五种数据类型:reg、integer、real、time以及realtime。最常用的数据类型是 reg ,它是可综合的。推断出来的电路可能有也可能没有物理储存元件。
数字表示
在Verilog中,一个整型常数可以用多种格式表示。通常表示为
[符号][位宽]’[基数][数值]
[基数]指定的是数字的进制:
- b或B:二进制
- 0或O:八进制
- d或D:十进制
- h或H:十六进制
[数值]是指定对应基数下的数值。
[位宽]指明了数字的比特位数,它是可选的,[位宽]**存在时,数字是有具体位宽限制的
指定位宽数字 一个指定位宽的数字明确指定了比特位宽。如果数值本身所需的位宽比[位宽] 指明的小,就在前端填充0.
运算符
Verilog语言有二十多种运算符。对于门级描述,我们只需要使用下列按位运算符:
~(非)、&(与)、|(或)、^(异或)
程序结构
端口声明
module [module-name] //这里没有逗号
(
[mode] [data-type] [port-names],
[mode] [data-type] [port-names],
……
[mode] [data-type] [port-names],
);
endmodule
[mode]项可以为输入型、输出型、或者输入/输出型。
Verilog-1995 端口声明 在Verilog-1995中,端口名称、类型、数据类型是分别声明的。
module eq1 (i0,i1,eq); //括号里面只有端口名字
//声明方式
input i0,i1;
output eq;
//声明数据类型
wire i0,i1;
wire eq;
程序体 (assign)
Verilog不像C语言一样顺序执行,而是并行操作并发执行。
- 描述一个线路的第一种方法是使用连续赋值。这对简单的组合电路很有帮助
assign [signal_name] = [expression];
每一个连续赋值都可以作为一个电路部件。左手边的信号为输出,右手边表达式中的信号为输入。
assign eq = p0|p1
这里执行逻辑或操作的电路。当p0或p1发生变化,则激活该条语句的值,在传播延迟后eq重新赋值。
- 描述一个电路部件的第二种方法是使用always块
- 描述一个电路部件的第三种方法是使用模块例化。
信号声明
声明部分指定了模块使用的内部信号和参数。内部信号可以认为是电路部分间的互接连线。
简单的信号声明部分为:[数据类型] [端口名称]|
例如 wire p0, p1;
隐式线网 在Verilog中,标识符不需要明确声明。如果一个标识符是可忽略的,那么它就被定义为隐式线网。错误的类型是 wire 型。
module eq1_implicit
(
input i0,i1; //无数据类型声明
output eq
);
//无内部信号声明
//乘积项必须在前面放置
assign p0 = ~10 & ~1; //隐式线网
assign p1 = i0 & i1; //隐式线网
assign eq = p0|p1;
endmodule
结构描述
module eq2
{
input wire [1:0] a, b,
output wire aeqb
);
//内部信号声明
wire e0, e1;
//实体
//例化两个1bit 比较器
eq1 eq_bit0_uint (.i0(a[0]),.i1(b[0]),.eq(e0);
eq1 eq_bit1_unit (.eq(e1),.i0(a[1]),.i1(b[1]);
//若每位都相等则a 与b 相等
assign aeqb = e0&e1;
endmodule
代码包含两个模块例化语句。
[模块名称][例化名称]
(
.端口名称,
.端口名称,
……
);
语句第一部分指定使用哪一个元件。[模块名称]指出的是模块的名字,而[例化名称]给出了一个例化的独有身份。第二部分是端口连接,显示一个例化模块I/O端口间的连接及当前模块中使用的外部信号。这种映射的形式是依据名称的连接。
通过规则列表连接 联系端口的外部信号的可选择的方式是通过规则列表。在这种方式中,低级模块的端口名称可以忽略而高级模块的信号用和低级模块的端口定义时的相同顺序列出。
eq1 eq_bit0_uint (a(0),b(0),e0);
eq1 eq_bit1_unit (a(1),b(1),e1);
尽管这种方式使代码更简洁,但是它可能产生错误,特别是对一个有很多I/0端口的模块。
Verilog原语 Verilog包含了一系列可以作为模块例化的预先设定的原语。这些原语符合简单的门级功能,如and,or和not单元。
module eq1-primitive
(
input wire i0,i1,
output wire eq
)
//内部信号声明
wire i0_n, i1_n, p0, p1;
//原语门实例化
not unit1 (i0_n, i0); //i0_n = ~i0;
not unit2 (i1_0, i1); //i1_n = ~i1;
and unit3 (p0, i0_n, i1_n); //p0=i0_n & i1_n;
and unit4 (p1, i0, i1); //p1 = i0 & i1;
or unit5 (eq, p0, p1); //eq = p0 | p1
endmodule
模块的框架
I/O声明 模块的端口声明了模块的输入/输出端口
module name(port1,port2,port3......)
I/O说明 I/O说明的格式如下
input(port1,port2......);
output(port1,port2.....);
内部信号声明 在模块内用到的和与端口有关的wire和reg声明
reg[width-1:0]
wire[width-1:0]
功能定义 模块中最重要的部分是逻辑功能定义部分
1> 用"assign"声明语句
assign a = b&c;
2> 用实例元件
and and_inst(q,a,b) // output q; input a,b;
3> 用"always”块
always @( posedge clk or posedge clr ) //时钟上升沿触发,异步清零
begin
if( clr ) q<=0; //清零
else if(en) q<=d; //使能赋值
end
采用"assign"语句是描述组合逻辑最常用的方法之一,而"always”块既可以用于描述组合逻辑,也可以用来描述时序逻辑。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY