声明:本篇文章部分参考
https://www.cnblogs.com/protogenoi/p/8926993.html Verilog笔记.1.基本语法 作者:Protogenoi
一、Verilog语言整体印象
1、模块是基本描述单位。端口默认是线网数据类型。端口长度声明,默认1位。output [0:3] Z;4位
实例化:指调用软件自带的模块(如内置门级元件)和自己编写的模块。
2、描述设计:有四种方式
1. 数据流描述方式:assign:连续赋值语句,语句并发。编译器指令:` timescale 1ns /100ps 时间单位、时间精度
2. 行为描述方式:过程语句initial、always。只有reg(寄存器)类型能在两种语句中赋值。顺序执行。
always=事 件控制(判断条件)+顺序过程。always语句块可以理解为while语句吗?
3. 结构化描述方式:原语,硬件级, xor、and和or的实例语句
4. 混合设计描述方式:门实例语句,模块实例语句、连续赋值语句,过程语句。
过程赋值vs连续赋值:
1.过程赋值改变寄存器的状态,是时序逻辑,用在initial和always里面;
2.连续赋值是组合逻辑,驱动线型变量(wire)
3、设计模拟:激励、控制、存储响应、设计验证
1. 激励和控制可以由初始化语句产生
4、抽象模型分级
系统级:用高级语言结构实现设计模块的外部性能的模型。
算法级:用高级语言结构实现设计算法的模型。
RTL级:描述数据在存储器之间流动和如何处理这些数据的模型。
门级:描述逻辑门与逻辑门之间的连接的模型。
开关级:描述器件中三极管和存储节点以及它们连接的模型。
二、Verilog语言要素
1、Verilog内置的12个门级元件,模块可以实例引用门级模型, 对结构进行实例化描述。
2、运算符
(1) 算术运算符 +,-,*,/,%
(2) 赋值运算符 =,<=
赋值语句:组合逻辑,顺序执行,阻塞赋值,使用=赋值。时序逻辑,并行执行,非阻塞赋值,使用<=赋值。
(3) 关系运算符> ,<,>=,<=
(4) 逻辑运算符 &&, ||, ! (与或非)
(5) 条件运算符 ?:
(6) 位运算符 ~, | , ^ ,& ,^~ (取反、或、异或、与、同或)
(7) 移位运算符 << ,>>
(8) 拼接运算符 {}
3、特殊标识符
$开始的标识符表示系统任务/函数 。
ps: 函数vs任务。任务可以返回0或多个值,函数返回一个值。函数在0时刻执行,不允许延迟,任务可以带有延迟。
'开始的标识符是编译器指令。编译器指令在整个编译过程中有效(类似于宏定义)
4、操作数
四种基本值:0,1,x,z:假,真,未知,高阻态。
三类常量:整形、实数型、字符串型。
整形表示法:
(1)十进制表示:<数字> 默认采用十进制
(2)基数表示法:<位宽>'<进制><数字> 位宽表示占用二进制位数 (bodh:二八十十六)
5、数据类型
(1)线网和寄存器
Verilog两大类数据类型:线网和寄存器,线网有11钟类型,寄存器有5钟类型。
线网类型:表示结构化元件间的物理连线,值由驱动元件决定。
寄存器类型:在always和initial语句赋值过程中的存储单元。五种寄存器类型:reg,integer,time,real,realtime。
区别:wire只能由assign连续赋值语句中赋值,reg只能在initial/always过程赋值语句中赋值。
更多参考:https://www.cnblogs.com/wzd5230/p/3847481.html verilog中wire与reg类型的区别
reg [msb:lsb] reg1 msb:lsb表示位数范围,大小方向都可以。
reg [0:3] MyMem [0:64] 存储器(寄存器数组)MyMem为64个4位寄存器的数组。线网类型没有对应点的存储器类型。
(2)参数
定义一个标志符代表一个常量(三种类型,如上),常用于定义延迟时间和变量宽度。
parameter <标志符> = <常量>
三、语句、子程序
1、赋值语句:Verilog中信号有两种赋值方式
(1)连续赋值语句:assign 用于对线网进行赋值,等价于门级描述。
- 左值必须为一个线网类型的变量或向量,不能是寄存器类型。
- 输出值随输入值变化而随时变化。
- 操作数可以是线网或寄存器或函数调用。
- 必须用“=”阻塞赋值进行赋值。
(2)过程赋值语句:=,<=
- 非阻塞赋值方式( 如 b <= a; )
- 块结束后才完成赋值操作。
- b的值并不是立刻就改变的。
- 这是一种比较常用的赋值方法。(特别在编写可综合模块时)
- 阻塞赋值方式( 如 b = a; )
- 赋值语句执行完后,块才结束。
- b的值在赋值语句执行完后立刻就改变的。
- 可能会产生意想不到的结果。
2、块语句:将多条语句结合在一起。块语句有两种,一种是begin_end语句,通常用来标识顺序执行的语句,用它来标识的块称为顺序块。一种是fork_join语句,通常用来标识并行执行的语句,用它来标识的块称为并行块。
3、条件语句:if_else语句、case语句。略。
4、过程语句:initial、always。
initial语句常用于仿真中的初始化,initial过程块中的语句仅执行一次;always语句则是不断重复执行的。
每一条initial语句和always语句都是独立的执行过程,彼此并行执行,执行顺序于在模块内的书写顺序无关,并且,每条initial和always语句过程语句都是在仿真时间0时刻同时开始的。
5、编译预处理 `与系统指令#
1)宏定义 `define
2)“文件包含”处理`include
3)时间尺度 `timescale
4)条件编译命令`ifdef、`else、`endif
6、循环语句:forever,repeat,while,for
参考文献:https://www.cnblogs.com/SYoong/p/5857367.html Verilog学习笔记基本语法篇(六)········ 循环语句
7、子程序:task、function
参考文献:https://blog.csdn.net/HengZo/article/details/49688677 Verilog之function使用说明
参考文献:https://www.cnblogs.com/SYoong/p/5865546.html Verilog学习笔记基本语法篇(九)任务和函数
8、实例语句
模块实例语句:<模块名> <标志符>(<对应端口1>,<对应端口2>...)
内置门实例语句:<关键字> <标志符>(<输出端口>,<输入端口1>...) 这里的关键字也叫内置门原语xor等,标志符也叫实例名称,括号里是信号列表。信号列表有位置管理和名称关联两种。
四、demo
1、全加器:逻辑:A加数 B被加数 Cin低位进位 S和 Cout高位进位
2、行为描述方式:
// 行为描述,全加器
module FA(a,b,ci,s,co);
input a,b,ci;
output s,co;
reg s,co,t1,t2,t3;
always @(a or b or ci) begin
s=(a ^ b)^ci;
t1=a & b;
t2=a & ci;
t3=b & ci;
co=(t1|t2)|t3;
end
endmodule
`timescale 1 ns/ 1 ns
module FA_test();
reg Pa,Pb,Pci;
wire Ps,Pco;
FA F1(Pa,Pb,Pci,Ps,Pco); //模块实例语句:待测模块名 引用名(信号端口一一对应,位置关联)
initial begin: ONLY_ONCE
reg [3:0] i;
for (i=0;i<8;i=i+1)
begin
#20 {Pa,Pb,Pci}=i;
end
end
endmodule
3、流水灯
module waterled(
input clk,
input rst,
output reg [3:0] led
);
reg [7:0] times;
always @ (posedge clk or negedge rst) // 时钟信号计数
begin
if(!rst)
times<=1'b0; // 电路未复位,计数器锁定为0
else
if(times<8'd10) // 电路复位开始计数,计数到10重置
times<=times+1'b1;
else
times<=8'd0;
end
always @ (posedge clk or negedge rst) // 计数满10,四位数组中的1进位一次
begin
if(!rst)
led<=4'b0001; // 电路未复位 led[0]=1
else
if(times==8'd10) // 计数满10,led高位到低位,1向前进位
led<={led[2:0],led[3]};
else
led<=led;
end
endmodule
`timescale 1 ns/ 1 ns
module waterled_test;
wire [3:0] led;
reg rst,clk10;
waterled i1 (clk10,rst,led); //例化语句
always #10 clk10=~clk10; // 产生时钟信号
initial begin
clk10=1'b0;
rst=1'b0;
#100 rst=1'b1; // 100ns时,电路复位
#1000 $stop; // 1000ns时,仿真暂停
end
endmodule
4、function demo
module comb15;
function ADD;
input A, B;
ADD = A ^ B;
endfunction
initial begin
$display("Hello %b",ADD(1'b1,1'b0));
end
endmodule
输出1
module comb15;
function signed [1:0] ADD;
input A, B;
reg S;
S = A ^ B;
endfunction
reg [31:0] N,M;
reg H;
initial begin
N = 100;
M = 100;
H = 32'd100;
$display("Hello",100);
$display("Hello",32'd100);
$display("Hello%b",32'd100);
$display("Hello%b",8'd100);
$display("Hello\n",100);
$display("Hello",N);
$display("Hello",M);
$display("Hello",H);
end
endmodule
display需要begin..and, 超过1句也需要begin..and。
其他参考资料:
https://wenku.baidu.com/view/0d8a1bd4195f312b3169a59c.html verilog中reg和wire类型的区别
https://zhidao.baidu.com/question/135703718.html 请Verilog高手帮助!wire赋值问题
http://bbs.eetop.cn/thread-313513-1-1.html signed的用法
https://vlab.ustc.edu.cn/guide/doc_verilog.html Verilog语法
https://zhidao.baidu.com/question/437300882386090484.html 综合和仿真
Verilog这门语言真的是。。。语法臃肿代码效率低。。。这把设计和验证分开是为啥子。。。不能断点调试看变量的值。。。没有ide。。。 不重视缩进。。。函数名与返回值是一个。。。线网和寄存器类型奇奇怪怪。。。魔!鬼!啊!