RTL(Register Transfer Level),指的是寄存器传输级别的硬件描述,那么关于寄存器Register的硬件描述自然是我们写代码时候最日常的操作了。在verilog语言中,使用always语句块便能描述最基本的d寄存器。如果该寄存器的更新条件和更新值复杂一点,配合if-else也可以轻易完成。
对于寄存器来说,分为控制寄存器和数据寄存器。一般而言数据寄存器不需要复位,bit数也较大。在描述的时候略为不同。
1 控制寄存器
举一个简单的例子。一个用来表示数据有效的单bit valid信号,复位值为1’b0,在新数据到来的时刻条件A成立时需要被设置成1’b1,在数据清除B条件成立时要被清为1’b0,如果某时刻A和B同时成立,则数据依然有效。根据条件,很轻易地可以写出以下verilog寄存器描述:
这么写肯定是没问题的。通过if-else的描述来保证AB同时有效时刻的优先级,功能描述也符合需求。但有个点,这么写综合出来的电路是什么样的,很难通过代码直观感受。很明显这应该综合出一个1bit的d寄存器,但它的d端,en端具体和A,B这些条件是什么连接关系呢?只看代码不好推测。我们来看另外一种代码风格描述同一个功能:
很明显,这种方式描述的d寄存器,每个pin所连接的逻辑都一目了然。
依旧举一个简单的例子。一个多bit的数据,A条件成立时被赋值成data_a,B条件成立时被赋值成data_b,C条件成立时被赋值成data_c,优先级A>B>C。依旧可以有两种不同的描述方式:
第一种:
第二种:
以上两个例子中,前一种写法本质上是行为级的描述,用偏软件风格的描述,if-else更加直观容易理解,但综合实现结果取决于工具。而后一种写法本质上是门级的描述,逻辑看着不那么直观明了,但是确实门级电路最终实现的样子。一方面可以最大程度人为控制综合结果(比起行为级描述),另一方面便于寄存器批量替换。在蜂鸟E203开源RISCV项目中便使用了这种寄存器描述来实例化标准寄存器:
如果使用行为级描述的if-else来描述寄存器,那么无法实现这样的寄存器模块化。
此外,对于数据寄存器的d端,如果使用if-else,没有特别对综合工具下约束,只能生成优先级选择逻辑,而使用门级描述,可以轻易选择使用优先级选择逻辑或者是并行选择逻辑,以此来优化时序(当然前提是A,B,C条件互斥)。
这篇文章举的这些例子,提到了寄存器的两种不同代码风格:行为级描述和门级描述。其实也适用于组合逻辑。组合逻辑可以使用always@(*)的方式进行行为级描述,也可以直接使用assign加上与或非来进行门级描述。两种不同的风格,因人而异,只要保证个人全篇代码风格的统一就可以,并没有孰优孰劣。相比于行为级,门级描述能让硬件工程师更加清晰地认识到自己所写的代码最后将是什么样的电路,在做ppa考量的时候更好进行一些粗略估算和分析。而行为级的抽象是硬件描述语言不断进步的一种体现,让写代码的人能更加注重算法的实现,至于硬件的实现则更多地交给EDA工具。
但是不管是哪种代码风格,都要尽量做到“眼前是代码,心中是电路”。毕竟目前我们搬的是硬件的砖,自称攻城狮而不是程序猿..
今天就到这里,欢迎大家继续关注我们后续文章。