Verilog高级知识点

一、阻塞和非阻塞

       阻塞和非阻塞也是FPGA经常会遇到的概念,不仅仅在信号的赋值时候会出现,也经常在Xilinx IP核配置中出现,所以笔者想在这里把这个概念阐述清楚,方便大家对后续程序编写和IP核配置上的理解。

阻塞赋值(Blocking)

        阻塞顾名思义,就是说后面的语句会受到前面的语句的影响,通俗的说就是如果在一条阻塞赋值语句还没有被执行,那么该语句后面的语句也不能被执行即被阻塞了,换而言之就是说在同一个always块内的语句有着串行顺序执行的特点,这点和C语言有点相似,比如C语言里同一个函数,里面的每句话都是顺序被执行调用的,这里阻塞赋值符号为“=”,如b=a,我们来看下面一个always块内的代码,在复位信号rst_n置0的时候,a = 8'h1、b = 8'h2、c = 8'h3,而复位信号rst_n置1,即没有被复位的时候,a的值应该被赋值为8'h0,但是由于always模块用的是时序逻辑,所以下一个时钟上升沿即第五个时钟上升沿a才被赋值,同时因为使用了阻塞赋值,a的8'h0值也被顺序执行赋值给b,b再顺序执行赋值给c,代码和对应的波形图如下所示。    

图1 always模块阻塞赋值的波形图

 

非阻塞赋值(Non Blocking)

       和阻塞赋值相对应,如果把阻塞赋值看成是串行赋值,那么则可以把非阻塞赋值看成是并行赋值,符号“<=”用于非阻塞赋值,如b<=a,非阻塞赋值是由时钟节拍决定的,在时钟上升沿到来的时刻,将begin … end之间所有的赋值语句同时从右边赋值到左边,每个时钟上升沿去执行一次,属于并行赋值操作,这点和C语言串行顺序执行有着很大的区别。

       我们也来看下面一个always块内的代码,在复位信号rst_n置0的时候,a <= 8'h1、b <= 8'h2、c <= 8'h3,如图3-3所示,这里因为使用了非阻塞赋值,在第四个时钟周期内复位信号rst_n置1,即没有被复位的时候,a的值应该被赋值为8'h0,但是由于always模块用的是时序逻辑,所以在第五个时钟上升沿到来的时候,a的值才被赋值为8'h0,而又因为在第四个时钟周期内a的值是8'h1,b的值是8'h2,所以在第五个时钟上升沿到来的时候,b的值被赋值为8'h1,c的值被赋值为8'h2 ,同样的道理大家可以看到在第六和第七两个时钟上升沿来到的时候,b和c又被重新赋值。

 

图2 always模块非阻塞赋值的波形图 

        那么刚接触到Verilog的朋友们可能会疑惑,到底什么时候用阻塞赋值,什么时候用非阻塞赋值呢?其实一般说来,在组合逻辑的时候,我们通常使用阻塞赋值,比如用assign赋值语句和用不带时钟的always赋值语句,这种组合逻辑只和当前输入电平有关系,非常适合阻塞赋值;而在时序逻辑的时候,我们通常使用非阻塞赋值,比如用带时钟的always赋值语句,这种时序逻辑和时钟上升沿有关系,只有在触发沿时才会发生赋值的变化,非常适合非阻塞赋值。

        类似于一个always块内的赋值代码,Xilinx官方也提供了一些常用到IP核方便用户进行二次开发,大家可以把IP核简单理解成官方封装好的一个模块,对外提供input输入接口和output输出接口,好像一个黑盒子一样,也有一些IP核配置中会有阻塞模式和非阻塞模式的选择,这里举个cordic计算三角函数和平方根的IP核的例子,以下是Xilinx官方手册对于阻塞模式和非阻塞模式的说明截图,对比两张截图大家可以清楚地看到如果配置成阻塞模式,在s_axis_cartesian_tready和s_axis_cartesian_tvalid为高时,即s_axis_cartesian_tdata有效,同样的在s_axis_phase_tready和s_axis_phase_tvalid为高时,即s_axis_phase_tdata有效,m_axis_dout_tdata会使用前面没使用到的A1:B1作为输入,代入IP核计算,而如果把IP核配置成非阻塞模式,那么就不会考虑这么多了,直接看当前周期内的值把A2:B1作为输入代入IP核内计算,其实这里和一个always模块的阻塞赋值和非阻塞赋值是一样的意思,阻塞模式和非阻塞模式则有时候会出现在一些IP核配置上。

图3 Cordic IP核对阻塞模式的说明截图

图4 Cordic IP核对非阻塞模式的说明截图

二、assign和always的区别

           assign和always是Verilog里面最基本的两个语句,经常被使用,区别在于assign不能带时钟,而always可以带时钟也可以不带时钟,在always不带时钟的时候,比如写成always  @(*)begin,和assign实现的逻辑功能完全一致,都是去产生组合逻辑;如果简单的组合逻辑,建议大家用assign语句去产生,规范好变量名代码显得清晰明了,如果复杂的组合逻辑,建议大家用always语句去产生,为了让代码的可读性更高,也举个上面提到的代码例子,大家可以去体会下,其实assign mcu_read_done = (~spi_cs_r1 & spi_cs_r0) ? 1'b1 : 1'b0; 也可以写成下面的always  @(*)begin组合逻辑,另外其中else begin mcu_read_done = 1'b0; end也可以省略不写,用always和assign语句去产生的组合逻辑,还有一点需要注意下,如果always语句中产生的信号需要定义成reg型,而assign语句产生的信号需要定义成wire型。

        这里也特意说明一下组合逻辑和时序逻辑的区别:1.组合逻辑在当前时钟做条件判断直接在当前周期变化,而时序逻辑在当前时钟做条件判断后在下个周期时钟上升沿才做变化;2.组合逻辑模块在所有的if条件均为假的时候,在当前周期会立刻被清零,所以用always  @(*) begin产生组合逻辑的时候可以不用写else begin … end中间的mcu_read_done = 1'b0,而时序逻辑模块在所有的if条件均为假的时候,则会去保留之前的值不被清零,所以用always  @(posedge clk or negedge rst_n)begin产生时序逻辑的时候,如果想清零某些标志信号一般需要去写else begin … end中间的mcu_read_done <= 1'b0。

 

三、模块例化

        通过前面的Verilog基础知识点介绍,大家也都知道了FPGA程序内部是由一个个模块组成的,这些模块可能是手写的不同功能的模块,也可能是调用芯片厂家提供的IP核,那么在整个工程中就需要把每个模块的信号相互连接起来,这里术语就叫作模块例化,举个实际的例子,不同按键控制不同LED灯亮灭,这也是开发板自带的一个简单入门例程,我们把按键检测写成一个接口模块命名为key_scan,具体信号列表如图5所示,顶层模块命名为key_led,然后来具体看下顶层模块key_led是如何例化子模块的,例化的方法如下图6所示,在这里简单地向大家说明下,子模块名是指被例化模块的模块名,而例化模块名相当于一种标识,当例化多个相同模块时,就可以通过例化模块名的不同去加以区分,通常人们都习惯写成“u” +“子模块名”,信号列表中“.”之后的信号都是key_scan模块定义的端口信号,括号内的信号则是顶层模块key_led声明的信号,这样就可以将顶层模块的信号和子模块的信号一一对应起来了,这时候另外需要注意信号的位宽要保持一致,此外还需要注意最后一个例化信号的括号后面不要加“,”,否则会报编译错误。

图5 key_scan子模块的信号列表 

图6 key_led模块例化key_scan子模块中的信号

四、FPGA模块化设计理念

        FPGA设计理念虽然有很多种,但实际应用上基本都是采用自顶向下的设计理念,因为随着需求上对功能的丰富和代码的增加,传统的自低向上的设计理念已经慢慢不太实用了,不利于工程升级和维护,实际项目工程中,一个设计往往从系统级开始,然后再把系统分为几大功能模块,功能模块再逐渐细分到基本单元,类似下面示意图的树状结构。

 图7 FPGA自顶向下模块化的设计理念

       可能大家刚开始猛地一听觉得有些抽象不太好理解,笔者举个“SD卡存放音频WAV播放”的例子来帮助大家理解FPGA自顶向下模块化的设计理念,整体软件上的需求是这样的:SD卡里事先存放了各类文件,包括bmp格式的图片和wav格式的音乐等等,然后用户按下按键,FPGA去遍历SD卡的每个扇区搜索带wav包头的文件,其中wav包头文件有特定字段表示该音频的长度,FPGA找到wav音频后,把SD卡所存储的wav音频数据去除包头再写入一个FIFO缓存中,音频编解码芯片WM8731再去从FIFO缓存中读这些音频数据,读出后WM8731芯片通过内部的DA转换输出音频音乐,用户插上开发板上的耳机即可听到动听的音乐,播放完一首歌后,按下按键,FPGA再从后面的扇区号开始搜索下一首歌曲,在播放歌曲的时候开发板上的LED灯也会来回闪烁,整个工程自顶向下模块化的设计如下图8所示。

图8 SD卡存放音频WAV播放自顶向下模块化的设计 

        我们可以拿这个例程去分析下FPGA自顶向下的设计理念,也把它看成个一个小的项目吧,大家可以类比下,这里也有些经典的FPGA设计理念,对于FPGA来说,SD卡是数据产生的地方,而WM8731音频编解码芯片是使用数据的地方这是一个程序设计上的主干,然后我们来看一看这些数据怎么去产生:1.FPGA用到SPI总线去读写SD卡;2.SD卡首先需要初始化才能正常读写操作;3.SD卡需要知道扇区号才能读出来对应扇区的数据512个字节的数据。一样的道理我们再去分析下这些从SD卡读出的音频数据怎么处理:1.FPGA用到IIC总线去配置WM8731芯片,对其进行初始化;2.初始化的数据配置按照芯片手册去操作;3.FPGA驱动WM8731芯片把SD卡读到的音频数据,按照一定的顺序组包再根据芯片手册上的时序送到其dac数据引脚。

      可能大家觉得这仅仅只是一个例程,但实际项目工程上其实在代码和设计方法也大同小异,只不过可能变换了应用场景功能并且细节上更加丰富了而已,举几个实际工程例子:串行SPI的adc去实时采集一个仪器的电压电流,其余模块再去根据一个周期采样到的点去计算平均值、有效值、最大值,对一个周期内的波形进行FFT快速傅里叶变化分析还原采集到的波形;类交换机产品SFP光口通信,协议层的组包和拆包,数据处理以后再去发送出去;驱动CMOS摄像头去实时采集视频,对视频进行压缩处理,多帧缓存,实时通过HDMI接口传输视频等等。站在FPGA的角度来看,其实说到底也是一端产生数据,而另一端去使用数据,当然可能数据产生和使用端具体情况要求去具体分析设计,可能产生数据端是SD卡内存着的数据;可能是adc采集到的波形数据;可能是各种高速接口比如Rapid IO,PCIE*4,SFP,SATA收到的数据;也可能是不同接口的摄像头实时采集到的视频。然后使用数据端则根据项目需求来具体设计,对于“SD卡存放音频WAV播放”的例程数据使用端就是WM8731音频编解码芯片;对于仪器仪表adc采样,数据使用端就是平均值、有效值、最大值等的计算模块和FFT快速傅里叶分析模块;对于高速接口应用数据使用端就是把从协议层取出的数据送到数据处理端或者数据应用端;对于视频处理,数据使用端可能就是图像压缩、图像卷积等等后续的图像处理模块。

  • 10
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[2\]:在Verilog,有一些高级功能可以用来增强设计的灵活性和复杂性。其包括阻塞赋值和非阻塞赋值、always语句和assign语句、if_else语句和case语句、状态机等。阻塞赋值和非阻塞赋值是用来给信号赋值的两种不同方式。阻塞赋值使用"="符号,表示在当前时间步骤,按照代码的顺序执行赋值操作。而非阻塞赋值使用"<="符号,表示在当前时间步骤结束后,按照代码的顺序执行赋值操作。always语句用于描述组合逻辑的行为,它可以根据输入信号的变化来更新输出信号。assign语句用于描述连续赋值的行为,它可以根据输入信号的变化实时更新输出信号。if_else语句和case语句用于描述条件分支的行为,它们可以根据不同的条件执行不同的操作。状态机是一种用于描述有限状态机的方法,它可以根据输入信号的变化来切换不同的状态,并执行相应的操作。这些高级功能可以帮助设计者更好地实现复杂的逻辑功能,并提高设计的可读性和可维护性。 #### 引用[.reference_title] - *1* *3* [Verilog学习笔记(5):Verilog高级程序设计](https://blog.csdn.net/KIDS333/article/details/127244643)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [【Verilog】三、Verilog高级知识点](https://blog.csdn.net/weixin_62912626/article/details/127863477)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值