最近拿到一本关于可编程数字逻辑方面(Verilog)的书。看了一部分,感觉写的一般。书的前半部分是关于FPGA一些原理性知识和开发流程的简介,这部分我觉得写得还不错。后面写Verilog部分基础语法,基本就是北航夏宇闻出的那本Verilog教程中Verilog基础语法的拷贝。语法介绍后面那章是3个简单模块设计实例。总体来说,我觉得这本书并没有什么让我觉得不同的地方。

  我觉得很多书写的都不是那么容易理解,不能让我明白Verilog言是怎么映射到具体的电路上,以及什么叫可综合的风格?比如在涉及一些与仿真相关的语法时,没有明确说明不能综合。所以我看的时候总是云里雾里,觉得这像C却不是C,难以搞清这些语法与实际生成电路之间的关系。

  Verilog这东西,大学时自学了点,断断续续地也搞了些,也心疼地花了不少大洋买了块板,不过收效甚微。上周日在天河公园,翻了下书,感觉这个东西跟C++、ARM程序的开发如此的相近!将一个数字模块划分为若干子模块,然后各个子模块用流程图详细设计其功能,最后用Verilog编码,再然后就是综合、布局布线,下载。不就相当于C++中将大模块划分为子模块,然后详细设计,用C++编码,然后编译、烧写?我想,反正这书看了这么久也没什么收获,索性不看了吧。既然对ARM开发比较熟悉,何不尝试将ARM的那套东西往这方面套套,然后随便搞点东西,再比较比较?

  于是我准备分别用Verilog设计一些比较简单的数字模块,同时也用C++写个虚拟的但同功能的模块。从中比较下二者的差异,掌握其中通用的方法。

  这篇文章先从8位流水灯开始,分别按不同的思路实现了两个版本。如果对比C++代码和Verilog代码的话,会发现他们是惊人的相似。后面对比可以发现,其实用Verilog写硬件模块的方法和流程和用C++开发基本类似的;C++中的一些概念可以移到Verilog上。

  下面的例子所用的平台为:Verilog(Quartus)、C++(Visual C++)。最终实现的硬件模块框如图下所示:

wps_clip_image-12299
 

基于查表法的流水灯实现

  这个例子实现的是一个基于查表方式的流水灯。不过由于我目前还不会在Verilog中定义ROM表,所以索性用case语句替代,二者本质上都一样。

  先看右图,右图用C++定义了RunningLEDModule1类,一个类就相当于完整的模块。类中定义了run()函数。该函数中包含了三个参数:

  • reset:外部输入参数,用于复位模块;
  • clk:外部时钟驱动参数,用于内部计数;
  • ledout:led输出。每一位代表一个LED灯驱动信号。

  run()函数的内部就是按照给定的输入参数,用标准C++代码编写方式实现的LED流水灯工作流程。

  再看左图,左图定义了LEDModule模块,输入、输出信号相当于RunningLEDModule1中输入输出参数。LEDModule在流程上和RunningLEDModule1非常相似。当然这并不意味着实际工作流程也是完全一样。Verilog语言执行上并非完全顺序执行,会包含一些并行执行的情况,如always块和assign语言就属于两条并行执行的语句。

wps_clip_image-12384

  接下来是进行测试。对于RunningLEDModule1,我用该类生成module1,首先施加复位信号,然后设置clk=1并反复调用run()以模拟该模块在时钟的上跳沿触发一次执行的情况。在执行过程中,将ledout的值打印出来,该8位值的各个位对应了实际每个流水灯的亮灭情况。

wps_clip_image-12521

wps_clip_image-14643

  而对于LEDModule,我则在Quatus环境中添加了一个波形文件,导入该模块的输入输出引脚,强制clk输出时钟信号。然后进行功能仿真,可以看到ledout引脚输出的流水灯波形。

wps_clip_image-12655
 

基于移位方式的流水灯实现

  下面是使用移位方式实现的流水灯。两种实现的流程也是基本相同的。唯一不同的仍然是如前面所提到的Verilog的实现中有些语句是并行执行的。

wps_clip_image-12766
 

小结

  上面的例子似乎很直接的说明了一个问题:如果抛去Verilog语句中的并行特点,那么无论是用Verilog、还是C++,设计思路上和算法是一样的,并且C++代码可以稍加修改便可移植到Verilog中。算法本身并没有变,变的只是最终实现方式;同样的,设计流程也没有变,变的只是采用什么工具。后面的文章,我觉得可以就这个例子研究下使用C++和Verilog具体有哪些东西是可以相互通用的,又有哪些是需要变通的。