电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
FPGA的Veilog HDL语法、框架总结
分 享
扫描二维码分享
FPGA的Veilog HDL语法、框架总结
FPGA
Veilog
果果小师弟
关注
发布时间: 2021-07-20
丨
阅读: 907
**摘要**:Verilog HDL硬件描述语言是在用途最广泛的C语言的基础上发展起来的一种硬件描述语言,具有灵活性高、易学易用等特点。Verilog HDL可以在较短的时间内学习和掌握,FPGA的Veilog HDL基础语法总结,看完这些,FPGA的基本语法应该就没啥问题了! # 一、基础知识 ## 1、逻辑值 逻辑0:表示低电平,也就对应我们电路`GND`; 逻辑1:表示高电平,也就是对应我们电路的`VCC`; 逻辑X:表示未知,有可能是高电平,也有可能是低电平; 逻辑Z:表示高阻态,外部没有激励信号,是一个`悬空状态`。 ## 2、进制格式 Verilog数字进制格式包括二进制、八进制、十进制和十六进制。 一般常用的为二进制、十进制和十六进制。 二进制表示如下:4b0101表示4位二进制数字0101 十进制表示如下:4'd2表示4位十进制数字2(二进制0010) 十六进制表示如下:4ha表示4位十六进制数字a(二进制1010) **16'b1001 1010 1010 1001=16'h9AA9** ## 3、标识符 标识符(identifier)用于定义模块名、端口名、信号名等。 标识符可以是任意一组字母、数字、$符号和(下划线)符号的组合; 但标识符的第一个字符必须是字母或者下划线; 标识符是区分大小写的; ## 4、标识符推荐写法 不建议大小写混合使用; 普通内部信号建议全部小写; 信号命名最好体现信号的含义,简洁、清晰、易懂; 以下是一些推荐的写法: * 1、用有意义的有效的名字如`sum`、`cpu_addr`等。 * 2、用下划线区分词,如`cpu addr`。 * 3、采用一些前缀或后缀,比如时钟采用clk前缀:`clk_50`,`clk_cpu`; # 二、数据类型 在Verilog 语言中,主要有三大类数据类型。 **寄存器数据类型、线网数据类型和参数数据类型**。 从名称中,我们可以看出,真正在数字电路中起作用的数据类型应该是**寄存器数据类型**和**线网数据类型**。 ## 1、寄存器类型 寄存器表示一个抽象的数据存储单元,通过赋值语句可以改变寄存器储存的值寄存器数据类型的关键字是reg,reg类型数据的默认初始值为不定值x。 ![](https://files.mdnice.com/user/3658/67c09e3f-ae27-4fa2-a798-5d1fc6b2bcbc.png) reg类型的数据只能在`always语句`和`initial语句`中被赋值。 如果该过程语句描述的是**时序逻辑**,
即always语句带有时钟信号,则该寄存器变量对应为触发器
; 如果该过程语句描述的是**组合逻辑**,
即always语句不带有时钟信号,则该寄存器变量对应为硬件连线
; ```c //计数器对系统时钟计数,计时0.2秒 always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) counter <= 24'd0; else if (counter < 24'd999_9999) counter <= counter + 1'b1; else counter <= 24'd0; end //通过移位寄存器控制IO口的高低电平,从而改变LED的显示状态 always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) led <= 4'b0001; else if(counter == 24'd999_9999) led[3:0] <= {led[2:0],led[3]}; else led <= led; end ``` ## 2、线网类型 线网数据类型表示**结构实体(例如门)之间的物理连线**。 **线网类型的变量不能储存值**,它的值是由驱动它的元件所决定的。驱动线网类型变量的元件有**门**、**连续赋值语句**、**assign**等。 如果没有驱动元件连接到线网类型的变量上,则该变量就是高阻的,即其值为z。 线网数据类型包括`wire型`和`tri型`,其中最常用的就是wire类型。 ![](https://files.mdnice.com/user/3658/bfe4aae6-7366-4eff-89cf-90c7672b1dd4.png) ## 3、参数类型 参数其实就是一个常量,在Verilog HDL中用parameter定义常量。 我们可以一次定义多个参数,参数与参数之间需要用**逗号隔开**。 **每个参数定义的右边必须是一个常数表达式**。 ![](https://files.mdnice.com/user/3658/ec1c8dad-7daf-44ee-8532-01bd029b3cd5.png) 参数型数据常用于**定义状态机的状态**、**数据位宽**和**延迟大小**等。 采用标识符来代表一个常量**可以提高程序的可读性和可维护性**。 在模块调用时,可通过参数传递来**改变被调用模块中已定义的参数**。 # 三、运算符 ## 1、算数运算符 ![](https://img-blog.csdnimg.cn/20210621213527801.png) ## 2、关系运算符 ![](https://img-blog.csdnimg.cn/20210621213550746.png) ## 3、逻辑运算符 ![](https://img-blog.csdnimg.cn/20210621213609896.png) ## 4、条件操作符 ![](https://img-blog.csdnimg.cn/202106212136403.png) **result=(a>=b)?a:b;** ## 5、位运算符 ![](https://img-blog.csdnimg.cn/20210621213703724.png) ## 6、移位运算符 ![](https://img-blog.csdnimg.cn/20210621213724565.png) 两种移位运算都用0来填补移出的空位。 左移时,位宽增加;右移时,位宽不变。 **4b1001 <<2 = 6'b100100;** **4b1001 >>1 = 4b0100;** ## 7、拼接运算符 ![](https://img-blog.csdnimg.cn/20210621213748559.png) **c={a,b[3:0];** ## 8、优先级运算符 ![ ](https://img-blog.csdnimg.cn/2021062121381472.png) # 四、模块结构 Verilog的基本设计单元是“模块"(block)。 一个模块是由两部分组成的,一部分描述接口,另一部分描述逻辑功能。 ![](https://img-blog.csdnimg.cn/20210621190251289.png) 使用quartusii软件编写出上图左边的硬件描述代码,通过软件编译,就能生成最右边组合逻辑电路图来。每个Verilog程序包括4个主要的部分:**端口定义、I0说明、内部信号声明、功能定义**。 ![流水灯代码](https://files.mdnice.com/user/3658/1868a9d2-6c6c-4adb-a87f-172f53d687d3.png) 上图时流水灯的代码,第一个always块代码的意思: 如果`时钟信号的上升沿`或者`复位信号的下降沿到`来,就执行`begin`与`end`之间的代码。 如果产生了复位信号(低电平),计数器清0,如果计数器的值小于10000000,计数器的值就+1,如果没有产生复位信号和计数值不小于10000000,计数器的值就为0。在这个always块中,逻辑是顺序执行的。 第二个always块代码的意思: 如果`时钟信号的上升沿`或者`复位信号的下降沿到`来,就执行`begin`与`end`之间的代码。如果产生了复位信号(低电平),led0点亮,如果计数器的值小于10000000,led0-3顺序点亮,如果没有产生复位信号和计数值不小于10000000,led灯状态保持不变。在这个always块中,逻辑是顺序执行的。 但是这个always块代码是并行执行的,也就是说时钟信号一直在产生。 功能定义部分有三种方法: * 1、assign语句描述组合逻辑 * 2、always语句描述组合/时序逻辑 * 3、例化实例元件 上述三种逻辑功能是并行执行的。 # 五、结构语句 ## 1、initial和always语句 initial语句它在模块中只执行一次。 它常用于测试文件的编写,用来产生仿真测试信号(激励信号),或者用于对存储器变量赋初值。 always 语句一直在不断地重复活动。但是只有和一定的时间控制结合在一起才有作用。 ![](https://img-blog.csdnimg.cn/20210621194331815.png) 一般`initial语句`常用于测试文件,在测试文件中初始化使用。比如上面的代码首先始终信号初始化为0,之后在always语句中让其10个时钟周期翻转一次,就达到了时钟的要求。 复位信号最开始为低电平,然后延时20个时钟周期就拉高。触摸按键信号最开始为低电平,延时10和时钟周期后拉高,再延时30个时钟周期再拉低,延时110个时钟周期再拉高,再延时30个时钟周期再拉低。 always的时间控制可以是**沿触发**,也可以是**电平触发**;可以是**单个信号**,也可以是**多个信号**,**多个信号中间要用关键字or连**接。always 语句后紧跟的过程块是否运行,要看它的触发条件是否满足。 ![](https://img-blog.csdnimg.cn/20210621200821836.png) **
沿触发的always块常常描述时序逻辑行为
**。由**关键词or**连接的多个事件名或信号名组成的列表称为“**敏感列表**”。 **
电平触发的always块常常描述组合逻辑行为。
** ![](https://img-blog.csdnimg.cn/20210621205942879.png) ## 2、组合逻辑和时序逻辑电路 根据逻辑功能的不同特点,可以将数字电路分成两大类: **组合逻辑电路和时序逻辑电路。** *
组合逻辑电路中
,任意时刻的输出仅仅取决于该时刻的输入,与电路原来的状态无关。 *
时序逻辑电路中
,任时刻的输出不仅取决于当时的输入信号,而且还取决于电路原来的状态。或者说还与以前的输入有关,因此时序逻辑必须具备记忆功能。 ![](https://img-blog.csdnimg.cn/20210621204015721.png) ## 3、赋值语句 Verilog HDL 语言中,信号有两种赋值方式 1、阻塞赋值(blocking),如b=a 2、非阻塞赋值(Non_Blocking),如b<=a ### 3.1、阻塞赋值 阻塞赋值可以认为只有一个步骤的操作:即计算RHS(左侧)并更新LHS(右侧)。 **所谓阻塞的概念是指,在同一个always块中,后面的赋值语句是在前一句赋值语句结束后才开始赋值的。** ```c module block_nonblock(Clk,Rst_n,a,b,c,out) input Clk; input Rst_n; input a; input b; input c; output reg [1:0] out; // out a + b + c;最大值为3,所以应该定义为2位的位宽 // d = a+b; // out = d+c; reg [1:0]d;//定义一个中间变量 always @(posedge Clk or negedge Rst_n) if (!Rst_n) out = 2'b0; else begin d = a+b; out = d+c; end endmodule ``` ![](https://files.mdnice.com/user/3658/70007d34-b0fd-4e12-ae16-448c92f05774.png) 现在我们改变一下d= a+b;out = d+c;的顺序,就会发现综合出来的电路是完全不同的。 ```c module block_nonblock(Clk,Rst_n,a,b,c,out) input Clk; input Rst_n; input a; input b; input c; output reg [1:0] out; reg [1:0]d;//定义一个中间变量 always @(posedge Clk or negedge Rst_n) if (!Rst_n) out = 2'b0; else begin out = d+c; d = a+b; end endmodule ``` ![](https://files.mdnice.com/user/3658/a0100307-9864-4694-8577-d34f7ef93293.png) ![](https://img-blog.csdnimg.cn/20210621204525977.png) ### 3.2、非阻塞赋值 非阻塞赋值的操作过程可以看作两个步骤 (1)赋值开始的时候,计算RHS(左侧); (2)赋值结束的时候,更新LHS(右侧)。 所谓非阻塞的概念是指,在计算非阻塞赋值的RHS以及更新LHS期间,允许其他的非阻塞赋值语句同时计算RHS和更新LHS。 **
非阻塞赋值只能用于对寄存器类型的变量进行赋值,因此只能用在initial块和always块等过程块中
**。 还是用上面的例子 ```c module block_nonblock(Clk,Rst_n,a,b,c,out) input Clk; input Rst_n; input a; input b; input c; output reg [1:0] out; reg [1:0]d;//定义一个中间变量 always @(posedge Clk or negedge Rst_n) if (!Rst_n) out = 2'b0; else begin d <= a+b; out <= d+c; end endmodule ``` 生成效果如下: ![](https://files.mdnice.com/user/3658/5f41f86d-0ca1-4845-b172-7d3b25d961d1.png) 现在我们改变一下d= a+b;out = d+c;的顺序,就会发现综合出来的电路是完全相同的。这里由于采用的非阻塞赋值,因此交换语句的前后顺序并不会对最终生成的逻辑电路有实际影响。 ```c module block_nonblock(Clk,Rst_n,a,b,c,out) input Clk; input Rst_n; input a; input b; input c; output reg [1:0] out; reg [1:0]d;//定义一个中间变量 always @(posedge Clk or negedge Rst_n) if (!Rst_n) out = 2'b0; else begin out <= d+c; d <= a+b; end endmodule ``` ![](https://files.mdnice.com/user/3658/00213f57-f811-4a44-a391-ad4190c83392.png) ![](https://img-blog.csdnimg.cn/20210621205037593.png) 1、在描述`组合逻辑`(电平触发)的always 块中用`阻塞赋值=`,综合成组合逻辑的电路结构;这种电路结构只与输入电平的变化有关系。 2、在描述`时序逻辑`(沿触发)的always 块中用`非阻塞赋值=`,综合成时序逻辑的电路结构;这种电路结构往往与触发沿有关系,只有在触发沿时才可能发生赋值的变化。 >**注意**:在同一个always块中不要既用非阻塞赋值又用阻塞赋值 >不充许在多个always块中对同一个变量进行赋值!因为在多个always块中代码时并行执行的。 一般在设计中掌握以下六个原则,可解决在综合后仿真中出现绝大多数的冒险竞争问题。 1)时序电路(沿触发的always块)建模时,用非阻塞赋值; 2)锁存器电路建模时,用非阻塞赋值; 3)用always块建立组合逻辑(电平触发的always块)模型时,用阻塞赋值; 4)在同一个always块中建立时序和组合逻辑电路时,用非阻塞赋值: 5)在同一个always块中不要既用非阻塞赋值又用阻塞赋值; 6)不要在一个以上的always块中为同一个变量赋值。 ## 4、条件语句 条件语句必须在**过程块**中使用。**过程块语**句是指
由initial语句和always语句引导的块语句
。 ### 4.1 if_else语句 ![](https://img-blog.csdnimg.cn/20210621210100855.png) 1、允许一定形式的简写,如: ```bash if(a) 等同于if(a==1) if(la)等同于if(a!=1) ``` 2、`if语句`对表达式的值进行判断,若为0,x,z,则按假处理;若为1,按真处理。 3、`if和else`后面的操作语句可以用`begin和end`包含多个语句。 4、允许if语句的`嵌套`。 ![](https://img-blog.csdnimg.cn/20210621210300912.png) ### 4.1 case语句 case语句(多分支选择语句) 1、分支表达式的值互不相同; 2、所有表达式的**位宽必须相等**;不能用`’bx`来代替`n'bx` 3、casez比较时,不考虑表达式中的**高阻值** 4、casex不考虑**高阻值z**和**不定值x** ![](https://files.mdnice.com/user/3658/92c8c323-1e97-46ef-8357-2720a0e8a93d.png) # 注意 `if_else`需要配对,一个if语句就应该必须有一个else语句。好处是避免latch产生。`latch`是一个锁存器,在数字电路中latch是一个电平触发的存储器,触发器是一个边沿触发的存储器。在编写veilog语句中应避免产生无畏锁存器,锁存器只在组合逻辑电路中产成,而锁存器会导致电路生成的毛刺比较多,还会影响我们对整个电路的时序分析。 **什么样的情况下会产生这个锁存器呢?** 首先在组合逻辑电路中,如果我们有`if语句`但是没有相应的`else语句`,他就有可能产生锁存器。第二点,比如case语句,如果我们的case语句没有给完全,没有列举完所有应该的产生的case语句,就应该写一个`default`,否则也会生成一个锁存器。
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
0
)
果果小师弟
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交