Initial Chisel Template
This commit is contained in:
73
src/main/scala/gcd/DecoupledGCD.scala
Normal file
73
src/main/scala/gcd/DecoupledGCD.scala
Normal file
@ -0,0 +1,73 @@
|
||||
// See README.md for license details.
|
||||
|
||||
package gcd
|
||||
|
||||
import chisel3._
|
||||
import chisel3.util.Decoupled
|
||||
|
||||
class GcdInputBundle(val w: Int) extends Bundle {
|
||||
val value1 = UInt(w.W)
|
||||
val value2 = UInt(w.W)
|
||||
}
|
||||
|
||||
class GcdOutputBundle(val w: Int) extends Bundle {
|
||||
val value1 = UInt(w.W)
|
||||
val value2 = UInt(w.W)
|
||||
val gcd = UInt(w.W)
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute Gcd using subtraction method.
|
||||
* Subtracts the smaller from the larger until register y is zero.
|
||||
* value input register x is then the Gcd.
|
||||
* Unless first input is zero then the Gcd is y.
|
||||
* Can handle stalls on the producer or consumer side
|
||||
*/
|
||||
class DecoupledGcd(width: Int) extends Module {
|
||||
val input = IO(Flipped(Decoupled(new GcdInputBundle(width))))
|
||||
val output = IO(Decoupled(new GcdOutputBundle(width)))
|
||||
|
||||
val xInitial = Reg(UInt())
|
||||
val yInitial = Reg(UInt())
|
||||
val x = Reg(UInt())
|
||||
val y = Reg(UInt())
|
||||
val busy = RegInit(false.B)
|
||||
val resultValid = RegInit(false.B)
|
||||
|
||||
input.ready := ! busy
|
||||
output.valid := resultValid
|
||||
output.bits := DontCare
|
||||
|
||||
when(busy) {
|
||||
when(x > y) {
|
||||
x := x - y
|
||||
}.otherwise {
|
||||
y := y - x
|
||||
}
|
||||
when(x === 0.U || y === 0.U) {
|
||||
when(x === 0.U) {
|
||||
output.bits.gcd := y
|
||||
}.otherwise {
|
||||
output.bits.gcd := x
|
||||
}
|
||||
|
||||
output.bits.value1 := xInitial
|
||||
output.bits.value2 := yInitial
|
||||
resultValid := true.B
|
||||
|
||||
when(output.ready && resultValid) {
|
||||
busy := false.B
|
||||
resultValid := false.B
|
||||
}
|
||||
}
|
||||
}.otherwise {
|
||||
when(input.valid) {
|
||||
val bundle = input.deq()
|
||||
x := bundle.value1
|
||||
y := bundle.value2
|
||||
xInitial := bundle.value1
|
||||
yInitial := bundle.value2
|
||||
busy := true.B
|
||||
}
|
||||
}
|
||||
}
|
||||
46
src/main/scala/gcd/GCD.scala
Normal file
46
src/main/scala/gcd/GCD.scala
Normal file
@ -0,0 +1,46 @@
|
||||
// See README.md for license details.
|
||||
|
||||
package gcd
|
||||
|
||||
import chisel3._
|
||||
// _root_ disambiguates from package chisel3.util.circt if user imports chisel3.util._
|
||||
import _root_.circt.stage.ChiselStage
|
||||
|
||||
/**
|
||||
* Compute GCD using subtraction method.
|
||||
* Subtracts the smaller from the larger until register y is zero.
|
||||
* value in register x is then the GCD
|
||||
*/
|
||||
class GCD extends Module {
|
||||
val io = IO(new Bundle {
|
||||
val value1 = Input(UInt(16.W))
|
||||
val value2 = Input(UInt(16.W))
|
||||
val loadingValues = Input(Bool())
|
||||
val outputGCD = Output(UInt(16.W))
|
||||
val outputValid = Output(Bool())
|
||||
})
|
||||
|
||||
val x = Reg(UInt())
|
||||
val y = Reg(UInt())
|
||||
|
||||
when(x > y) { x := x - y }
|
||||
.otherwise { y := y - x }
|
||||
|
||||
when(io.loadingValues) {
|
||||
x := io.value1
|
||||
y := io.value2
|
||||
}
|
||||
|
||||
io.outputGCD := x
|
||||
io.outputValid := y === 0.U
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Verilog sources and save it in file GCD.v
|
||||
*/
|
||||
object GCD extends App {
|
||||
ChiselStage.emitSystemVerilogFile(
|
||||
new GCD,
|
||||
firtoolOpts = Array("-disable-all-randomization", "-strip-debug-info")
|
||||
)
|
||||
}
|
||||
68
src/test/scala/gcd/GCDSpec.scala
Normal file
68
src/test/scala/gcd/GCDSpec.scala
Normal file
@ -0,0 +1,68 @@
|
||||
// See README.md for license details.
|
||||
|
||||
package gcd
|
||||
|
||||
import chisel3._
|
||||
import chisel3.experimental.BundleLiterals._
|
||||
import chisel3.simulator.EphemeralSimulator._
|
||||
import org.scalatest.freespec.AnyFreeSpec
|
||||
import org.scalatest.matchers.must.Matchers
|
||||
|
||||
/**
|
||||
* This is a trivial example of how to run this Specification
|
||||
* From within sbt use:
|
||||
* {{{
|
||||
* testOnly gcd.GCDSpec
|
||||
* }}}
|
||||
* From a terminal shell use:
|
||||
* {{{
|
||||
* sbt 'testOnly gcd.GCDSpec'
|
||||
* }}}
|
||||
* Testing from mill:
|
||||
* {{{
|
||||
* mill %NAME%.test.testOnly gcd.GCDSpec
|
||||
* }}}
|
||||
*/
|
||||
class GCDSpec extends AnyFreeSpec with Matchers {
|
||||
|
||||
"Gcd should calculate proper greatest common denominator" in {
|
||||
simulate(new DecoupledGcd(16)) { dut =>
|
||||
val testValues = for { x <- 0 to 10; y <- 0 to 10} yield (x, y)
|
||||
val inputSeq = testValues.map { case (x, y) => (new GcdInputBundle(16)).Lit(_.value1 -> x.U, _.value2 -> y.U) }
|
||||
val resultSeq = testValues.map { case (x, y) =>
|
||||
(new GcdOutputBundle(16)).Lit(_.value1 -> x.U, _.value2 -> y.U, _.gcd -> BigInt(x).gcd(BigInt(y)).U)
|
||||
}
|
||||
|
||||
dut.reset.poke(true.B)
|
||||
dut.clock.step()
|
||||
dut.reset.poke(false.B)
|
||||
dut.clock.step()
|
||||
|
||||
var sent, received, cycles: Int = 0
|
||||
while (sent != 100 && received != 100) {
|
||||
assert(cycles <= 1000, "timeout reached")
|
||||
|
||||
if (sent < 100) {
|
||||
dut.input.valid.poke(true.B)
|
||||
dut.input.bits.value1.poke(testValues(sent)._1.U)
|
||||
dut.input.bits.value2.poke(testValues(sent)._2.U)
|
||||
if (dut.input.ready.peek().litToBoolean) {
|
||||
sent += 1
|
||||
}
|
||||
}
|
||||
|
||||
if (received < 100) {
|
||||
dut.output.ready.poke(true.B)
|
||||
if (dut.output.valid.peekValue().asBigInt == 1) {
|
||||
dut.output.bits.gcd.expect(BigInt(testValues(received)._1).gcd(testValues(received)._2))
|
||||
received += 1
|
||||
}
|
||||
}
|
||||
|
||||
// Step the simulation forward.
|
||||
dut.clock.step()
|
||||
cycles += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user