Saturday, October 21, 2017

Script to generate Verilog ports from Chisel source

Here's a script I wrote that will (naively) parse a Verilog file and output it's corresponding ports in Chisel format.  This is useful to import a Verilog module as a blackbox in a Chisel design.


#!/bin/sh
exec scala "$0" "$@"
!#

import scala.util.{Success, Try}
import scala.util.matching.Regex

val port = raw"\s*(input|output|inout)\s+(wire\s*|reg\s*)?\s*(\[(\d+):(\d+)\])?\s*(\w+)?\s*;?".r

val p = scala.io.Source.fromFile(args(0)).getLines.foreach { f: String =>
  val b = f match {
    case port(d, tzpe, _, msb, lsb, name) =>
      val dir = d match {
        case "input" => "Input"
        case "output" => "Output"
        case "inout" => "Analog"
        case _ =>
      }
      val w = Try({msb.toInt - lsb.toInt + 1}) match {
        case Success(s) => s"%d".W.format(s)
        case _ => "1.W"
      }
      s"val $name = $dir(UInt($w))"
    case _ => ""
  }
  if (b.nonEmpty) println(b)
}

The chosen regular expression was testing using regex101.com, a populate online regular expression tester.

Monday, October 16, 2017

My Clocking and Reset Strategy with RawModule

This post is originally a reply to: https://groups.google.com/forum/#!topic/chisel-users/LTujWW6DtI4

The following snippet of code illustrates my top level clocking and reset strategy.  I opt to use LazyRawModuleImp and explicitly specify the clocks and reset.

class LazyTop(implicit p: Parameters) extends LazyModule {
  lazy val module = new LazyRawModuleImp(this) {
    val io = IO(new Bundle {      
      val ref_clk_p = Input(Clock())
      val rst_sw_n = Input(Bool()) // async reset, debounced on board?
      val led_n = Output(Bits(8.W))
    })

    val GSR_INST = Module(new GSR()) // this is blackbox module
    GSR_INST.io.GSR := io.rst_sw_n

    val global_reset = Wire(Bool())
    val global_reset_n = Wire(Bool())
    global_reset_n := GSR_INST.io.GSR // appears GSR is active low
    global_reset := !GSR_INST.io.GSR

    // use active high clock
    withClockAndReset(ref_clk_p, global_reset) {
      io.led_n := RegNext("h5A".asUInt(8.W), init = 0.U)
    }
  }
}

Module GSR is a Chisel BlackBox for my FPGA's GSR (global set reset) library cell.


class GSR extends BlackBox {
  val io = IO(new Bundle {
    val GSR = Input(Bool())
  })
}

Wednesday, October 4, 2017

To link Chisel annotations and circuit info

Here's another snippet of Chisel code.  It was well worth the (ashamed to admit) 4+ hrs to write.  Admittedly, I am still getting a hand of functional programming.  I am particularly proud of the recursive function call to populate an immutable val.  The function's use case is to return Chisel circuit level information from an Annotation object.  It is necessary because Annotation does not contain (do best of my knowledge) a reference to a Data object.


  private def getDirection(circuit: Circuit, a: Annotation): ActualDirection = {
    // get net info from annotation object
    val thisModName = a.target.asInstanceOf[ComponentName].module.name
    val thisPortName = a.target.name.replace("io.", "") // FIXME: this is a HACK ..

    // find matching module in Chisel circuit
    val module = circuit.components.flatMap {
      case m: DefModule => Some(m)
      case b: DefBlackBox => None
    }.find {
      _.name == thisModName
    }.head

    // recursively return Element objects
    def elemsFromData(d: Data): Seq[Element] = d match {
      case e: Element => Seq(e)
      case r: Record =>
        r.elements.flatMap((e: (String, Data)) => {
          elemsFromData(e._2)
        }).toSeq
    }

    // all elements for all module port
    val elems: Seq[Element] = module.ports.flatMap((p: Port) => {
      elemsFromData(p.id)
    })

    // use DataMirror to find direction, have yet to test whether fully accurate
    // FIXME: obviously not optimal, should filter above
    val dir = DataMirror.directionOf(elems.find(_.name == thisPortName).head)
    dir
  }

Wednesday, September 27, 2017

Reflecting on my birthday

Reflecting on my 36th birthday this morning.  I feel incredibly grateful both in my personal and professional life.

Focusing on the latter, this is a professional blog after all, the news that Sun/Oracle's SPARC program was shut down brought up some thoughts.  I started my career on that team.  They were doing interesting work.  The challenges were both technical and non technical.  Getting things done required less coding and more engineering; the aspects of the profession that are not taught in school (e.g. sourcing requirements, gathering data, engaging with partners, and establishing consensus).  I recall the exit interview with my then director.  What stuck in the conversation was the level of pride that the director had in having assembled a team family, many of which had already been working together for 10+ years.  Maybe it was a retention tactic (ok, of course it was!), but I was extended a very genuine statement that I was valued in the team family and a promise, that within this environment, I would continue to grow, etc.  Obviously, I didn't recognize any of this at the time.  Many of the my ex-colleagues did remain in that team and, post layoff, I think back to the growth potential I would of had in that organization.  I've since been happy to refer a handful of these people to my connections.

My life (including personal life) may have been very different if I continued on that path.  Alas, I made a different decision.  For a short while, I chased happiness at work, but over the past 5+ years, my focus changed to becoming my best self and doing my best work.  This has never been more true than today.  I am grateful that this endeavor is the third most important area in my life (following my self care, and wife/family).

Tuesday, September 26, 2017

Generate custom Chisel bundle using meta programming

I found the example to extend a Bundle's functionality limiting.  You need to pass a varargs of (String,Data) tuple to the function.  Here's a snippet:

 final class CustomBundle(elts: (String, Data)*) extends Record {
    val elements = ListMap(elts map { case (field, elt) => field -> elt.chiselCloneType }: _*)
    def apply(elt: String): Data = elements(elt)
    override def cloneType = (new CustomBundle(elements.toList: _*)).asInstanceOf[this.type]
  }

I could have found a solution using the above (not sure, is it possible to convert ListMap to varargs?), but I dug into Scala meta programming for the purpose of ramp.  Behold!  Here's an alternative (though incomplete) method of generating a bundle.

object MetaBundle {
  import reflect.runtime.currentMirror
  import tools.reflect.ToolBox
  val toolbox = currentMirror.mkToolBox()
  import toolbox.u._

  def apply(): Bundle = {
    val tree =
      q"""
          import chisel3.core._
          val b = new Bundle {
            // placeholder for your interface
            val i = Input(UInt(1.W))
            val o = Output(UInt(1.W))
          }
          b
       """    
     toolbox.eval(tree).asInstanceOf[Bundle]
  }
}

The code above returns an instance of a typeless bundle.  The result isn't equivalent to above, but serves my purpose.  Have yet to try, but suspect the technique can be extended to other cases.  There's likely implications wrt. statically typed nature of Scala.  Use at your own risk.  I am only posting this because it's a neat hack.