https://mp.weixin.qq.com/s/q7R2Dn9p9cch_ABN4raReQ
介绍几种计数器的实现,以及其中的一点小细节。
1. ZCounter
a. value初始值为0;
b. 使用inc构建递增计数逻辑;
c. wrap: 如果value增加到n-1,则返回从0开始重新计数;如果n是2的幂,则不需要判断,一直加1即可。
创建ZCounter类的实例,只是创建了一个value寄存器:
在调用其inc()方法时,才会生成计数逻辑:
对Mux做一点小重构:
两种写法的区别在于isPow2(n)发生作用的时间点不同,构建出的硬件逻辑也不同。
原写法中,isPow2(n)虽然在软件执行构建期间确定值,但Bool(true)或者Bool(false)在硬件逻辑中生成了对应的线,并且要在硬件逻辑运行期间起作用。
重构之后的写法,isPow2(n)在软件执行构建期间确定值,并且也在构建期间起作用,根据其值的不同,生成不同的逻辑。最终生成的硬件逻辑中,不存在Bool(isPow2(n))对应的线。
2. TwoWayCounter
双向(可增可减)计数器。
3. WideCounter
位宽比较宽的计数器。思路是把位宽分成两部分:large,small。递增small部分,产生进位时才增加large部分。另外一个特点是,每次增加的值不一定是1,而是可以从外部输入(inc)。
其实现跟isWide息息相关,而isWide的值在构建期间可以确定,所以可以把这个类根据isWide的值拆成两个类:ReallyWideCounter 和 NotWideCounter。
1) NotWideCounter
当isWide == false时,WideCounter的实现简化为:
重构一下:
a. small:存储计数器值的寄存器,根据reset的值,决定是否初始化为0.
b. next:计数器的下一个值;
next = small + inc
small = next
c. carryOut:在加inc的过程中,产生了进位的位;
d. value:计数器的值:val value: UInt = small
e. width:计数器的位宽;
2) ReallyWideCounter
small的部分与NotWideCounter类似,这里介绍large的部分。
a. large
small是计数器的低位部分,large是计数器的高位部分,其位数是总位数(width),减去small的位数(smallWidth)。
只有当small部分在递增过程中产生进位(nextSmall(smallWidth))时,large才会加1:
nextSmall(smallWidth)代表进位是因为nextSmall比small多一位,而进位就存在这多的一位中:
同样的原因:small := nextSmall是丢失了进位的。
b. value:val value = Cat(large, small)
计数器的值是把large和small组合到一起。
c. carryOut
把small和large中产生了进位的位组合到一起。
3) 进一步重构
分成两个类之后,如何重新组织到一起呢?
a. 把WideCounter改成一个抽象类
b. 定义一个WideCounter伴生对象:
4. 附录
略