Skip to content

Commit

Permalink
More Circt intrinsic wrappers (IsX, PlusArgsTest, PlusArgsValue) (#2958)
Browse files Browse the repository at this point in the history
This extends the set of circt intrinsics with Chisel wrappers. These have no impact on code not using them.
  • Loading branch information
darthscsi authored Apr 8, 2023
1 parent 4d1f15e commit 024cbfc
Show file tree
Hide file tree
Showing 8 changed files with 266 additions and 67 deletions.
32 changes: 32 additions & 0 deletions src/main/scala/chisel3/util/circt/IsX.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: Apache-2.0

package chisel3.util.circt

import chisel3._
import chisel3.experimental.IntrinsicModule
import chisel3.internal.Builder

import circt.Intrinsic

/** Create a module with a parameterized type which returns whether the input
* is a verilog 'x'.
*/
private class IsXIntrinsic[T <: Data](gen: T) extends IntrinsicModule("circt_isX") {
val i = IO(Input(gen))
val found = IO(Output(Bool()))
}

object IsX {

/** Creates an intrinsic which returns whether the input is a verilog 'x'.
*
* @example {{{
* b := IsX(a)
* }}}
*/
def apply[T <: Data](gen: T): Bool = {
val inst = Module(new IsXIntrinsic(chiselTypeOf(gen)))
inst.i := gen
inst.found
}
}
35 changes: 35 additions & 0 deletions src/main/scala/chisel3/util/circt/PlusArgsTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: Apache-2.0

package chisel3.util.circt

import chisel3._
import chisel3.experimental.IntrinsicModule
import chisel3.internal.Builder

import circt.Intrinsic

/** Create a module with a parameterized type which calls the verilog function
* \$test\$plusargs to test for the existence of the string str in the
* simulator command line.
*/
private class PlusArgsTestIntrinsic[T <: Data](gen: T, str: String)
extends IntrinsicModule("circt_plusargs_test", Map("FORMAT" -> str)) {
val found = IO(Output(Bool()))
}

object PlusArgsTest {

/** Creates an intrinsic which calls \$test\$plusargs.
*
* @example {{{
* b := PlusArgsTest(UInt<32.W>, "FOO")
* }}}
*/
def apply[T <: Data](gen: T, str: String): Bool = {
Module(if (gen.isSynthesizable) {
new PlusArgsTestIntrinsic(chiselTypeOf(gen), str)
} else {
new PlusArgsTestIntrinsic(gen, str)
}).found
}
}
54 changes: 54 additions & 0 deletions src/main/scala/chisel3/util/circt/PlusArgsValue.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: Apache-2.0

package chisel3.util.circt

import scala.language.reflectiveCalls

import chisel3._
import chisel3.experimental.{FlatIO, IntrinsicModule}
import chisel3.internal.Builder

import circt.Intrinsic

/** Create a module which generates a verilog \$value\$plusargs. This returns a
* value as indicated by the format string and a flag for whether the value
* was found.
*/
private class PlusArgsValueIntrinsic[T <: Data](gen: T, str: String)
extends IntrinsicModule("circt_plusargs_value", Map("FORMAT" -> str)) {
val io = FlatIO(new Bundle {
val found = Output(Bool())
val result = Output(gen)
})
}

object PlusArgsValue {

/** Creates an intrinsic which calls \$value\$plusargs.
*
* @example {{{
* b := PlusArgsValue(UInt(32.W), "FOO=%d")
* b.found
* b.value
* }}}
*/
def apply[T <: Data](gen: T, str: String) = {
Module(if (gen.isSynthesizable) {
new PlusArgsValueIntrinsic(chiselTypeOf(gen), str)
} else {
new PlusArgsValueIntrinsic(gen, str)
}).io
}

/** Creates an intrinsic which calls \$value\$plusargs and returns a default
* value if the specified pattern is not found.
*
* @example {{{
* v := PlusArgsValue(UInt(32.W), "FOO=%d", 42.U)
* }}}
*/
def apply[T <: Data](gen: T, str: String, default: T): T = {
val result = apply(gen, str)
Mux(result.found, result.result, default)
}
}
32 changes: 6 additions & 26 deletions src/main/scala/chisel3/util/circt/SizeOf.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,17 @@

package chisel3.util.circt

import scala.util.hashing.MurmurHash3

import chisel3._
import chisel3.experimental.{annotate, ChiselAnnotation, ExtModule}
import chisel3.experimental.IntrinsicModule
import chisel3.internal.{Builder, BuilderContextCache}

import circt.Intrinsic

// We have to unique designedName per type, be we can't due to name conflicts
// on bundles. Thus we use a globally unique id.
private object SizeOfGlobalIDGen {
private case object CacheKey extends BuilderContextCache.Key[Int]
def getID() = {
val oldID = Builder.contextCache.getOrElse(CacheKey, 0)
Builder.contextCache.put(CacheKey, oldID + 1)
oldID
}
}

/** Create a module with a parameterized type which returns the size of the type
* as a compile-time constant. This lets you write code which depends on the
* results of type inference.
*/
private class SizeOfIntrinsic[T <: Data](gen: T) extends ExtModule {
val i = IO(Input(gen));
private class SizeOfIntrinsic[T <: Data](gen: T) extends IntrinsicModule("circt_sizeof") {
val i = IO(Input(gen))
val size = IO(Output(UInt(32.W)))
annotate(new ChiselAnnotation {
override def toFirrtl =
Intrinsic(toTarget, "circt.sizeof")
})
override val desiredName = "SizeOf_" + SizeOfGlobalIDGen.getID()
}

object SizeOf {
Expand All @@ -47,8 +27,8 @@ object SizeOf {
* }}}
*/
def apply[T <: Data](gen: T): Data = {
val sizeOfInst = Module(new SizeOfIntrinsic(chiselTypeOf(gen)))
sizeOfInst.i := gen
sizeOfInst.size
val inst = Module(new SizeOfIntrinsic(chiselTypeOf(gen)))
inst.i := gen
inst.size
}
}
47 changes: 47 additions & 0 deletions src/test/scala/chiselTests/util/IsXSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: Apache-2.0

package chiselTests.util

import chisel3._
import chisel3.stage.ChiselGeneratorAnnotation
import chisel3.testers.BasicTester
import chisel3.util.circt.IsX
import circt.stage.ChiselStage

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

import scala.io.Source

private class IsXBundle extends Bundle {
val a = UInt()
val b = SInt()
}

private class IsXTop extends Module {
val w = IO(Input(UInt(65.W)))
val x = IO(Input(new IsXBundle))
val y = IO(Input(UInt(65.W)))
val outw = IO(Output(UInt(1.W)))
val outx = IO(Output(UInt(1.W)))
val outy = IO(Output(UInt(1.W)))
outw := IsX(w)
outx := IsX(x)
outy := IsX(y)
}

class IsXSpec extends AnyFlatSpec with Matchers {
it should "Should work for types" in {
val fir = ChiselStage.emitCHIRRTL(new IsXTop)
println(fir)
(
(fir.split('\n').map(_.trim) should contain).allOf(
"intmodule IsXIntrinsic :",
"input i : UInt<65>",
"output found : UInt<1>",
"intrinsic = circt_isX",
"input i : { a : UInt, b : SInt}"
)
)
}
}
35 changes: 35 additions & 0 deletions src/test/scala/chiselTests/util/PlusArgsTestSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: Apache-2.0

package chiselTests.util

import chisel3._
import chisel3.testers.BasicTester
import chisel3.util.circt.PlusArgsTest
import circt.stage.ChiselStage

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

import scala.io.Source

private class PlusArgsTestTop extends Module {
val w = IO(Output(UInt(1.W)))
val x = IO(Output(UInt(1.W)))
val z = IO(Input(UInt(32.W)))
w := PlusArgsTest(UInt(32.W), "FOO")
x := PlusArgsTest(z, "BAR")
}

class PlusArgsTestSpec extends AnyFlatSpec with Matchers {
it should "Should work for types" in {
val fir = ChiselStage.emitCHIRRTL(new PlusArgsTestTop)
println(fir)
(fir.split('\n').map(_.trim) should contain).allOf(
"intmodule PlusArgsTestIntrinsic :",
"output found : UInt<1>",
"intrinsic = circt_plusargs_test",
"parameter FORMAT = \"FOO\"",
"parameter FORMAT = \"BAR\""
)
}
}
44 changes: 44 additions & 0 deletions src/test/scala/chiselTests/util/PlusArgsValueSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: Apache-2.0

package chiselTests.util

import chisel3._
import chisel3.testers.BasicTester
import chisel3.util.circt.PlusArgsValue
import circt.stage.ChiselStage

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

import scala.io.Source

private class PlusArgsValueTop extends Module {
val wf = IO(Output(UInt(1.W)))
val wv = IO(Output(UInt(32.W)))
val xf = IO(Output(UInt(1.W)))
val xv = IO(Output(UInt(32.W)))
val zv = IO(Output(UInt(32.W)))
val tmpw = PlusArgsValue(UInt(32.W), "FOO=%d")
val tmpx = PlusArgsValue(xv, "BAR=%d")
zv := PlusArgsValue(xv, "BAR=%d", 42.U)
wf := tmpw.found
wv := tmpw.result
xf := tmpx.found
xv := tmpx.result
}

class PlusArgsValueSpec extends AnyFlatSpec with Matchers {
it should "Should work for types" in {
val fir = ChiselStage.emitCHIRRTL(new PlusArgsValueTop)
println(fir)
(fir.split('\n').map(_.trim.takeWhile(_ != '@')) should contain).inOrder(
"intmodule PlusArgsValueIntrinsic :",
"output found : UInt<1>",
"output result : UInt<32>",
"intrinsic = circt_plusargs_value",
"parameter FORMAT = \"FOO=%d\"",
"parameter FORMAT = \"BAR=%d\"",
"node _zv_T = mux(PlusArgsValueIntrinsic_2.found, PlusArgsValueIntrinsic_2.result, UInt<6>(\"h2a\")) "
)
}
}
54 changes: 13 additions & 41 deletions src/test/scala/chiselTests/util/SizeOfSpec.scala
Original file line number Diff line number Diff line change
@@ -1,64 +1,36 @@
// SPDX-License-Identifier: Apache-2.0

package chiselTests.util

import chisel3._
import chisel3.stage.ChiselGeneratorAnnotation
import chisel3.testers.BasicTester
import chisel3.util.circt.SizeOf

import circt.stage.ChiselStage

import firrtl.stage.FirrtlCircuitAnnotation

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

import scala.io.Source

class MyBundle extends Bundle {
private class SizeOfBundle extends Bundle {
val a = UInt()
val b = SInt()
}

class SizeOfTop extends Module {
val io = IO(new Bundle {
val w = Input(UInt(65.W))
val x = Input(new MyBundle)
val outw = UInt(32.W)
val outx = UInt(32.W)
})
io.outw := SizeOf(io.w)
io.outx := SizeOf(io.x)
private class SizeOfTop extends Module {
val w = IO(Input(UInt(65.W)))
val x = IO(Input(new SizeOfBundle))
val outw = IO(Output(UInt(32.W)))
val outx = IO(Output(UInt(32.W)))
outw := SizeOf(w)
outx := SizeOf(x)
}

/** A test for intrinsics. Since chisel is producing intrinsics as tagged
* extmodules (for now), we explicitly test the chirrtl and annotations rather
* than the processed firrtl or verilog. It is worth noting that annotations
* are implemented (for now) in a way which makes the output valid for all
* firrtl compilers, hence we write a localized, not end-to-end test
*/
class SizeOfSpec extends AnyFlatSpec with Matchers {
it should "Should work for types" in {
val fir = ChiselStage.emitCHIRRTL(new SizeOfTop)
val a1 = """extmodule SizeOf_0""".r
(fir should include).regex(a1)
val b1 = """defname = SizeOf_0""".r
(fir should include).regex(b1)
val a2 = """extmodule SizeOf_1""".r
(fir should include).regex(a2)
val b2 = """defname = SizeOf_1""".r
(fir should include).regex(b2)

// The second elaboration uses a unique name since the Builder is reused (?)
val c = """Intrinsic\(~SizeOfTop\|SizeOf.*,circt.sizeof\)"""
((new ChiselStage)
.execute(
args = Array("--target", "chirrtl"),
annotations = Seq(chisel3.stage.ChiselGeneratorAnnotation(() => new SizeOfTop))
)
.flatMap {
case FirrtlCircuitAnnotation(circuit) => circuit.annotations
case _ => None
}
.mkString("\n") should include).regex(c)
println(fir)
(fir.split('\n').map(_.trim) should contain)
.allOf("intmodule SizeOfIntrinsic :", "input i : UInt<65>", "output size : UInt<32>", "intrinsic = circt_sizeof")
}
}

0 comments on commit 024cbfc

Please sign in to comment.