(1)Verilog 语言四种逻辑:
逻辑 0:表示低电平,也就对应我们电路 GND;
逻辑 1:表示高电平,也就是对应我们电路的 VCC;
逻辑 X:表示未知,有可能是高电平,也有可能是低电平;
逻辑 Z:表示高阻态,外部没有激励信号,是一个悬空状态。
(2)数字表示:不给位宽默认32位,不给进制默认d:如1020=32’d1020
(3)标识符:
标识符区分大小写,第一个字符必须是字母或者下划线;
标识符推荐写法:
不建议大小写混合使用;
普通内部信号建议全部小写;
信号命名最好体现信号的含义,简洁、清晰、易懂;
以下是一些推荐的写法:
1、用有意义的有效的名字如 sum 、cpu_addr等。
2、用下划线区分词,如cpu_addr。
3、采用一些前缀或后缀,比如时钟采用clk前缀:clk_50,clk_cpu;
(4)在 Verilog 语言中,主要有三大类数据类型:
从名称中,我们可以看出,真正在数字电路中起作用的数据类型应该是:寄存器数据类型和线网数据类型。
一.寄存器数据类型reg:
(1)表示一个抽象的数据存储单元,通过赋值语句可以改变寄存器储存的值
(2)初值不确定,默认X。
(3)reg类型的数据只能在 always 语句和 initial 语句中被赋值。
(4)reg的位宽:[高位,低位]
如果不给位宽,默认为1
(5)reg的物理意义:触发器/硬件连线
二.线网数据类型wire/tri(tri很少用但用法一致):
(1)线网数据类型表示结构实体(例如门)之间的物理连线,不能储存值,它的值是由驱动它的元件所决定的。
(2)驱动的元件有门、连续赋值语句、assign等,如果没有驱动元件连接到线网类型的变量上,则该变量就是高阻的,即其值为z
三.参数数据类型parameter:
(1)参数其实就是一个常量
(2)在模块调用时,可通过参数传递来改变被调用模块中已定义的参数。
(5)移位运算符:
两种移位运算都用0来填补移出的空位。
左移时,位宽增加;右移时,位宽不变。
4’b1001 << 2 = 6’b100100;
4’b1001 >> 1 = 4’b0100;
(6)拼接运算符:
{a,b} 将 a 和 b 拼接起来,作为一个新信号
比如:c = { a, b[3:0] };
(2)Verilog 的基本设计单元是“模块”(block)。
一个模块是由两部分组成的,一部分描述接口,另一部分描述逻辑功能。
(3)注意:输入端可以是wire类型的,也可以是reg类型的
但输出端只能是wire类型
(4)参数传递:注意看其中的MAX_NUM ,尽管在模块中已经有值了,但仍然可以在外面重赋值。
高级知识点:
(1)#number 的意思:
上一条语句执行完毕后延时number时间执行这一条语句
(2)没有posedge 和negedge修饰,就是电平触发方式,电平触发常用于描述组合逻辑
(3)如果组合逻辑块语句的输入变量很多,那么编写敏感列表会很烦琐并且容易出错。
@( * )表示对后面语句块中所有输入变量的变化都是敏感的。
(4)根据逻辑功能的不同特点,可以将数字电路分成两大类:
组合逻辑电路 和 时序逻辑电路。
组合逻辑电路中,任意时刻的输出仅仅取决于该时刻的输入,与电路原来的状态无关。
时序逻辑电路中,任一时刻的输出不仅取决于当时的输入信号,而且还取决于电路原来的状态。或者说还与以前的输入有关,因此时序逻辑必须具备记忆功能。
组合逻辑:
时序逻辑:
(5)阻塞赋值与非阻塞赋值:
可以这样理解:
阻塞赋值(=):有点类似于使用导线将两个变量连起来,不cpoy,直接赋值,一个变全变。
只有一个步骤:计算所有阻塞赋值右侧值,并将右赋左,同时所赋值即时影响其它。
或者这样理解:后面的变量如果想用之前变量的值,他一定用到的是之前变量更新过后(执行完赋值后)的值。
非阻塞赋值(<=):
有点类似于先cpoy,再赋值,一个变其它下次变。
赋值分两个步骤:
Step1:取所有阻塞赋值右侧值(注意,注意,这里已经取出来了)
Step2:将右赋左,变量值更新。
两种赋值的使用:
状态机:
Verilog中特别适合顺序执行逻辑
有限状态机:FSM
在有限个状态之间按一定规律转换的时序电路
重点:四段论法写三段式状态机:
(1)写状态空间和相关寄存器定义:
//定义状态
parameter STATE_WID = 2;
parameter IDLE = 2'b00;//0HZ
parameter S1 = 2'b01;//1HZ
parameter S2 = 2'b10;//2HZ
parameter S3 = 2'b11;//5HZ
//定义相关寄存器
reg [STATE_WID-1:0] state_c;
reg [STATE_WID-1:0] state_n;
(2)写状态跳转:(这个进程写法基本不会变):
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
state_c <= IDLE;
else
state_c <= state_n;
end
(3)写下个状态判断:
always @(*) begin
case(state_c)
IDLE:begin
if(key==1'b0)begin
state_n = S1;
end
else begin
state_n = IDLE;
end
end
S1:begin
if(key==1'b0)begin
state_n = S2;
end
else begin
state_n = S1;
end
end
S2:begin
if(key==1'b0)begin
state_n = S3;
end
else begin
state_n = S2;
end
end
S3:begin
if(key==1'b0)begin
state_n = IDLE;
end
else begin
state_n = S3;
end
end
endcase
end
(4)写各个状态下的动作:
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
counter_comp <= 28'd0;
else if(state_c==IDLE) begin
counter_comp <= 28'd0;
end
else if(state_c==S1) begin
counter_comp <= 28'd500_0000;
end
else if(state_c==S2) begin
counter_comp <= 24'd60331648;
end
else if(state_c==S3) begin
counter_comp <= 28'd2000_0000;
end
end
讲解:
(1)两种状态空间定义表达方式:
(2)
两种写法功能完全一样
(3)
(4)必须注意!!!!
Verilog数值表达位宽问题:
Verilog并不会检查如下的数字表达问题,下面的两端代码仍然可以综合。
counter_comp <= 8’d1000_0000;
此时d1000_0000中[7,0]赋值到counter_comp中,其它位被丢弃。
也就是说:
24’b1001_1000_1001_0110_1000_0000
与24’b0011_1001_1000_1001_0110_1000_0000相同
所以:数字一定要确认位宽够用,否则可能带来莫名其妙的问题
(5)注意:要注意default的书写,看看有必要写defult不。
(写状态机可参考这篇讲解,讲的真的好:
https://blog.csdn.net/fengyuwuzu0519/article/details/72571740
再去看一下正点原子的状态机视频,就更好了)