似乎这里的所有实现都使用二进制到灰色的转换。对于异步 FIFO,这仅在格雷码在跨越时钟域之前被锁存时才有效。如果您想要一个实际计数格雷码而不是将二进制值转换为格雷码的计数器怎么办?
一种选择是将灰度转换为二进制,相加,然后再转换回灰度并存储结果。另一种是使用自定义算法来计算序列中的下一个灰度值。典型的序列是反射二进制格雷码,但也存在其他序列。
下面的代码使用反射二进制格雷码实现格雷码计数器。它改编自这篇博文。它只会计数。它的工作方式与 Chisel Counter 对象类似,但它添加了对同步重置和自定义寄存器名称的支持。它返回计数器和包装状态。
import chisel3._
import chisel3.util._
// a Gray counter counts in Gray code
object GrayCounter {
// Gray unit cell
// b is the current state of this bit
// returns (t, z_o) where t is the next state of this bit
def grayCell(b: Bool, q_i: Bool, z_i: Bool, enable: Bool, parity: Bool): (Bool, Bool) = {
(b ^ (enable && q_i && z_i && parity), (!q_i) && z_i)
}
// cond = counts when true
// n = count value, must be a power of 2
// synchronousReset = resets counter to 0
// name = name for this counter
def apply(cond: Bool, n: Int, synchronousReset: Bool = false.B, name: String = null) = {
require(isPow2(n), s"Gray counter must have power-of-2 length (you asked for $n)")
require(n > 2, s"Gray counter minimum count is 4 (you asked for $n)")
val counter = RegInit(0.U(log2Ceil(n).W))
if (name != null) {
counter.suggestName(name)
}
val counterNext = Wire(Vec(log2Ceil(n), Bool()))
counter := counterNext.asUInt
val z_wires = Wire(Vec(log2Ceil(n), Bool()))
val parity = counter.xorR
for (i <- 0 until log2Ceil(n)) {
if (i == 0) {
val grayCellOut = grayCell(counter(i), true.B, true.B, cond, !parity)
counterNext(i) := grayCellOut._1
z_wires(i) := grayCellOut._2
} else {
val grayCellOut = grayCell(counter(i), counter(i-1) || (i == log2Ceil(n)-1).B,
z_wires(i-1) || (i == 1).B, cond, parity)
counterNext(i) := grayCellOut._1
z_wires(i) := grayCellOut._2
}
}
when (synchronousReset) {
counter := 0.U
}
val wrap = counter === (n/2).U && cond
(counter, wrap)
}
}