Here's a real world example that demonstrates the flexibility of
Chisel (or DSLs in general) compared to traditional languages for describing hardware.
It is my opinion that SystemVerilog's promise of making module connection more user (and verification) friendly has not been realized. The
interface construct was a good first step, so were interface modports. The .* connection operator or parameterizable interfaces, not so much. Traditional teams send their best (aka. quickest) Perl monkey to the rescue and, just like that, a new target for feature creep and "I'm busy, fix the script yourself" is created.
You have access to an API when using an internal DSL approach (such as Chisel). If you wanted to write your own function to connect to arbitrary hardware interfaces - you do it. This doesn't directly eliminate feature creep, but provides a better framework for development than Perl/RegEx can offer. Consider this tradeoff. How many lines of parsing, data structure definition, testing and software regression strategies are needed compared to a few lines of Scala.
Here's an example from my private repo. It takes two
Bundles as input and connects fields with matching names.
final def connect(left: Bundle, right: Bundle)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Unit = {
(left.elements.toSeq ++ right.elements.toSeq) // Map to Seq, then combine lhs and rhs
.groupBy(_._1) // group by name of
.filter(_._2.length == 2) // filter out non matches
.foreach {
case (k, v) => {
(lhs.dir, rhs.dir) match {
case (NODIR, NODIR) => attach(lhs.asInstanceOf[Analog], rhs.asInstanceOf[Analog]) // to support INOUT ports
case (INPUT, OUTPUT) => rhs := lhs
case (OUTPUT, INPUT) => lhs := rhs
case (INPUT, INPUT) => rhs := lhs // TODO: verify more
case (OUTPUT, OUTPUT) => lhs := rhs // TODO: verify mode
}
}
}
PS. Slowly, but surely, I'm
becoming functional.
Its difficult for the time being, especially without a IDE.
IntelliJ IDEA is terrific. Still can't properly explain a Monad. The ability to highlight a val, then hit "Alt-Enter", select "Add Type annotation to value definition", provides immeasurable value. Here's an example. By breaking up different parts of the above function, I can better determine (before compile time) the type of data structure that will be output.
val a: Map[String, Data] = left.elements
val b: Map[String, Data] = right.elements
val c: Seq[(String, Data)] = a.toSeq
val d: Seq[(String, Data)] = a.toSeq ++ b.toSeq
val e: Map[String, Seq[(String, Data)]] = d.groupBy(_._1)