思路:要找到最低位的1,可以求补
比如这个例子:
假设输入 Original = "1101"
1. ~inBits = "0010" 取反
2. ~inBits + 1 = "0011" 加一。现在的效果是最低有效位和原来一样,但更高的位都是反的。
3. Original & (inverted + 1) = "1101" & "0011" = "0001" 更高位都是反的,所以求与可以消掉。剩下 LSB
代码:
import spinal.core._
import spinal.lib._
object Encoders {
def PriorityEncoderOH(inBits: Bits): Bits = {
val uInput = U(inBits)
val lsbIsolator = U(~inBits) + U(1)
(uInput & lsbIsolator).asBits
}
def PriorityEncoderMSBOH(inBits: Bits): Bits = {
val reversedIn = inBits.reversed
val lsbOHOfReversed = PriorityEncoderOH(reversedIn)
lsbOHOfReversed.reversed
}
}
单测如下:
import spinal.core._
import spinal.core.sim._
import spinal.lib._
import spinal.tester.SpinalSimFunSuite
import boson.demo2.utils.Encoders.PriorityEncoderOH
case class PriorityEncoderOHTestBench(width: Int) extends Component {
val io = new Bundle {
val input = in Bits(width bits)
val output = out Bits(width bits)
}
io.output := PriorityEncoderOH(io.input)
}
class PriorityEncoderOHSpec extends SpinalSimFunSuite {
def runCase(width: Int, testNameSuffix: String = ""): Unit = {
test(s"PriorityEncoderOH $width-bit ${testNameSuffix}") {
SimConfig.withWave.compile(PriorityEncoderOHTestBench(width)).doSim { dut =>
dut.clockDomain.forkStimulus(10)
def getExpected(inputValue: BigInt, w: Int): BigInt = {
if (inputValue == 0) return BigInt(0)
var i = 0
while (i < w) {
if (((inputValue >> i) & 1) == 1) {
return BigInt(1) << i
}
i += 1
}
BigInt(0)
}
dut.io.input #= 0
sleep(1)
assert(dut.io.output.toBigInt == 0, "All zeros input failed")
for (i <- 0 until width) {
val inputVal = BigInt(1) << i
dut.io.input #= inputVal
sleep(1)
assert(dut.io.output.toBigInt == inputVal, s"Single bit at $i input failed (input: ${inputVal.toString(16)})")
}
if (width >= 4) {
val testInputs = Seq(
BigInt("0110", 2),
BigInt("1110", 2),
BigInt("1001", 2),
(BigInt(1) << width) -1
)
for (inputVal <- testInputs) {
dut.io.input #= inputVal
sleep(1)
val expectedVal = getExpected(inputVal, width)
assert(dut.io.output.toBigInt == expectedVal, s"Multi-bit input ${inputVal.toString(2)} failed, expected ${expectedVal.toString(2)}, got ${dut.io.output.toBigInt.toString(2)}")
}
}
val maxInput = (BigInt(1) << width) - 1
if (maxInput > 0) {
dut.io.input #= maxInput
sleep(1)
assert(dut.io.output.toBigInt == 1, s"All ones input (0x${maxInput.toString(16)}) failed")
}
val random = new scala.util.Random(42)
for (_ <- 0 until 50) {
val randomInput = BigInt(width, random)
dut.io.input #= randomInput
sleep(1)
val expectedVal = getExpected(randomInput, width)
assert(dut.io.output.toBigInt == expectedVal, s"Random input ${randomInput.toString(16)} failed, expected ${expectedVal.toString(16)}, got ${dut.io.output.toBigInt.toString(16)}")
}
dut.clockDomain.waitSampling(5)
}
}
}
runCase(1, "1-bit")
runCase(4, "4-bit")
runCase(8, "8-bit")
runCase(16, "16-bit")
}
PS:实际上开发的时候不用自己写,用 OHMasking.last
或者 OHMasking.first
就行。只是觉得这个思路挺有意思的。