当前位置:   article > 正文

FPGA学习历程(一):Verilog基础_verilog 赋值x

verilog 赋值x

以下内容学习自黑金 FPGA 教程以及正点原子 FPGA 教程。

一、数据类型

1.1 常量

  • 0 和 1 :0 表示低电平,即对应电路的 GND;1 表示高电平,即对应电路的 VCC。

  • x 和 z :x 代表不定值,有可能是低电平也有可能是高电平;z 代表高阻值,外部没有激励信号,是一个悬空状态。

5‘b00x11,第三位不定值;3’b00z,最低位为高阻值。

  • 整数:可用二进制 b 或 B,八进制 o 或 O,十进制 d 或 D,十六进制 h 或 H 表示。

8‘b00001111 表示 8 位位宽的二进制整数,4’ha 表示 4 位位宽的十六进制整数。

  • 下划线:仅在位数过长时用来分割位数,提高程序可读性。

前面的 8‘b00001111 可以写成 8‘b0000_1111。

  • 标识符:用于定义模块名、端口名和信号名等(这东西存在于任何描述语言中)。同样是必须以字母或者下划线开头,且区分大小写。

含义清晰、简洁易懂的标识符,clk_50,clk_cpu,sdram_addr。

  • 参数 parameter:parameter 可以用标识符定义常量,运用时只使用标识符即可。

定义 parameter width = 8; 又定义寄存器 reg [width - 1 : 0] a; 就是定义了 8 位宽度的寄存器

  parameter 就和 C 语言中的函数形参类似,是支持传递参数和修改参数值的,具体形式记录在后面,这里先以增加认知为主。

1.2 变量

  • Wire型:也叫网络类型变量,用于结构实体之间的物理连接,如门与门之间,不能储存值,其值由驱动它的元件所决定,用连续赋值语句 assign 赋值,定义为 wire [n - 1 : 0] a; 其中 n 代表位宽,如定义 wire a; assign a = b; 是将 b 的结点连接到连线 a 上。
    在这里插入图片描述

  • Reg型:也叫寄存器变量,可用来储存值,其默认初始值为不定值 x,只能在 always 语句里和 initial 语句中被赋值。其定义为 reg [n - 1 : 0] a; 表示 n 位位宽的寄存器。对于 reg 的赋值过程的描述,如果是时序逻辑,则 always 语句中带有时钟信号,该 reg 变量对应为触发器;如果是组合逻辑,则 always 语句中不带时钟信号,该 reg 变量对应为硬件连线。比如下面的 reg q 就是 D 触发器的输出,reg Mux 则是数据选择器的输出。
    在这里插入图片描述
    在这里插入图片描述

  • Memory型:用来定义 RAM,ROM 等存储器,其结构为 reg [n - 1 : 0] 存储器名 [m - 1 : 0] ,表示 m 个 n 位宽度的寄存器。比如 reg [7:0] ram [255:0] 定义了 256 个 8 位寄存器,256 即存储器深度,8 为数据宽度。

二、运算符

2.1 算术运算符(+ 、- 、* 、/ 、%)

  + 、- 、* 、/ 、% 这些符号的定义和 C 语言完全一致,即加、减、乘、除、取余。

2.2 赋值运算符( = 、<=)

  Verilog 中有两种赋值方式,分别为 =(阻塞赋值)和 <=(非阻塞赋值)。阻塞赋值为执行完一条赋值语句,再执行下一条(顺序执行),且赋值是立即执行;非阻塞赋值则不考虑赋值顺序(并行执行),在 always 块语句执行完成后,才进行赋值。说得更具体一些,就是:阻塞赋值的赋值语句是在前一条赋值语句结束后开始赋值的,而非阻塞赋值的赋值语句则是先计算出语句块内部所有右边表达式的值,然后并行完成对左边寄存器变量的赋值操作
在这里插入图片描述
  一般情况下,在时序逻辑电路中使用非阻塞赋值,可以避免仿真时出现竞争冒险现象;在组合逻辑电路中使用阻塞赋值,执行赋值语句后立即改变;在 assign 语句中必须使用阻塞赋值

2.3 关系运算符(> 、< 、>= 、<= 、== 、!=)

  这里的运算符也可以视作 C 语言中的符号(大于、小于、大于等于、小于等于、等于、不等于)。

2.4 逻辑运算符(&& 、|| 、!)

  这里对应的是逻辑与、或、非,与 C 语言相同。

2.5 条件运算符(?:)

  与 C 语言相同,属于条件判断的简化写法。

2.6 位运算符(~ 、& 、| 、^)

  按位取反、按位与、按位或、按位异或。

2.7 移位运算符(<< 、>> )

  左移和右移。

2.8 拼接运算符({ })

  这个算是特有的,作用是将多个信号按位拼接。例如 {a [3 : 0] , b [1 : 0]} 表示将 a 的低 4 位,b 的低 2 位拼接成 6 位数据;{n{a [3 : 0]}} 表示将 n 个 a [3 : 0] 拼接。

2.9 运算符优先级

  这个也是老规矩了,大概记一个 位操作 > 算术 > 移位 > 关系运算 > 逻辑运算 > 条件运算 就成。
在这里插入图片描述

三、程序框架

3.1 注释

  Verilog 和 C 语言一样有两种注释方式,即 “//” 单行注释与 “/* */” 多行注释。

3.2 关键字

  常用的关键字见下表(这里列出的并非全部):

关键字含义
module模块开始定义
input输入端口定义
output输出端口定义
inout双向端口定义
parameter信号参数定义
wirewire 信号定义
regreg 信号定义
always产生 reg 信号语句的关键字
assign产生 wire 信号语句的关键字
begin语句起始标志
end语句结束标志
edge/posedge/negedge时序电路标志
casecase 语句起始标志
defaultcase 语句默认分支标志
endcasecase 语句结束标志
ifif/else 语句标志
elseif/else 语句标志
forfor 语句标志
endmodule模块结束定义

3.3 模块

  Verilog 的基本设计单元就是 “模块”。一个模块由两部分组成,一部分描述接口,另一部分描述逻辑功能。如之前在介绍 Reg 型变量时引用的 D 触发器中,第一句定义了模块名称及信号接口的名称与个数,第二句到第四句定义了信号接口的输入输出属性,这四句为前半部分;后半部分则由 always 语句描述该模块的逻辑功能是 D 触发器。
在这里插入图片描述
  模块例化:前面提到的模块还只是一个很小很简单的模块,而 FGPA 逻辑设计中通常是一个大的模块中包含了一个或多个功能子模块,verilog 通过模块调用或称为模块实例化的方式来实现这些子模块与高层模块的连接,有利于简化每一个模块的代码,易于维护和修改(翻译:类似于 C++ 中类的实例化)。模块例化方法如下图所示:
在这里插入图片描述

3.4 结构语句

  这里正式介绍一下 initial 语句和 always 语句。

  • initial 语句在模块中只会执行一次。它常用于测试文件的编写,用来产生仿真测试信号(激励信号),或者用于对存储器变量赋初值。
  • always 语句则一直在不断地重复活动。但是只有和一定的时间控制结合在一起才有作用。结合之前的时序电路标志关键字说明,always 的时间控制可以是沿触发(描述时序逻辑行为),也可以是电平触发(描述组合逻辑行为);可以是单个信号,也可以是多个信号,多个信号中间要用关键字 or 连接。always 语句后紧跟的过程块是否运行,要看它的触发条件是否满足。always @(*) 表示对后面语句块中所有输入变量的变化都是敏感的。
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述

四、有限状态机

4.1 状态机概念理解

  有限状态机(Finite State Machine,FSM)是指在有限个状态之间按一定规律转换的时序电路。一图理解:
在这里插入图片描述

PS:状态机思想实际上在编程算法中很常见,只是没有像 Verilog 这样明确定义出来而已。

4.2 状态机设计——四段论

  • 状态空间定义:确定好要划分出来的状态数量,定义状态名称;
  • 状态跳转:确定各状态之间的跳转递进关系;
  • 下个状态判断:判断各状态下进行状态跳转的时间点;
  • 各个状态下的动作:每个不同的状态下都有不同的动作(判断状态跳转时间点 + 其他事情)。

  以一个四状态的 Moore 有限状态机举例说明。
在这里插入图片描述
  在程序中设计了 8 位的移位寄存器,在 Idle 状态下,判断 shift_start 信号是否为高,如果为高,进入 Start 状态,在 Start 状态延迟 100 个周期,进入 Run 状态,进行移位处理,如果 shift_stop 信号有效了,进入 Stop 状态,在 Stop 状态,清零 q 的值,再跳转到 Idle 状态。
  首先定义好模块名称以及状态机的状态:

module top
(
	input shift_start,
	input shift_stop,
	input rst,
	input clk,
	input d,
	output reg [7:0] q
);

parameter Idle = 2'd0 ; //Idle state 
parameter Start = 2'd1 ; //Start state 
parameter Run = 2'd2 ; //Run state 
parameter Stop = 2'd3 ; //Stop state 

reg [1:0] current_state ; //statement
reg [1:0] next_state ;
reg [4:0] delay_cnt ; //delay counter
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

  之后开始逐个状态的跳转设计。首先是状态机复位设计如下:

//First part: statement transition
always @(posedge clk or negedge rst)
begin
	if (!rst)
		current_state <= Idle ;
	else
		current_state <= next_state ;
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

  然后是四大状态之间的跳转设计如下:

//Second part: combination logic, judge statement transition condition
always @(*)
begin
	case(current_state)
		Idle : begin
			if (shift_start)
				next_state <= Start ;
			else
				next_state <= Idle ;
		end
		Start : begin
			if (delay_cnt == 5'd99)
				next_state <= Run ;
			else
				next_state <= Start ;
			end
		Run : begin
			if (shift_stop)
				next_state <= Stop ;
			else
				next_state <= Run ;
			end
		Stop : next_state <= Idle ;
		default: next_state <= Idle ;
	endcase
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

  最后状态机的输出设计如下:

//Last part: output data
always @(posedge clk or negedge rst)
begin
	if (!rst)
		delay_cnt <= 0 ;
	else if (current_state == Start)
		delay_cnt <= delay_cnt + 1'b1 ;
	else
		delay_cnt <= 0 ;
end

always @(posedge clk or negedge rst)
begin
	if (!rst)
		q <= 0 ;
	else if (current_state == Run)
		q <= {q[6:0], d} ;
	else
	q <= 0 ;
end

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/462685
推荐阅读
相关标签
  

闽ICP备14008679号