Monday, September 25, 2017

Example function to enhance Chisel bundle connection

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)