It seems like all implementations here use binary-to-Gray conversion. For asynchronous FIFOs, this only works if the Gray code is latched just before crossing clock domains. What if you want a counter that actually counts Gray codes instead of converting binary values to Gray codes?
One option is to convert Gray to binary, add, then convert back to Gray and store the result. The other is to use custom arithmetic to calculate the next Gray value in the sequence. The typical sequence is a reflected-binary Gray code, but others exist.
The code below implements a Gray code counter using a reflected-binary Gray code. It was adapted from this blog post. It only counts up. It works like the Chisel Counter object, except it adds support for a synchronous reset and custom register name. It returns the counter and wrap status.
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)
}
}