Wednesday, May 20, 2015

Wednesday Night Hack #14 (Part 2) - Chisel Blob Arbiter

Part 2 of my Wednesday Night Hack!

I remember reading an old article about arbiter coding styles.  There is a section where he refers to coding the arbiter as a big blog.  The results state that the big blog coding style offers better timing, area, and compile time than the novel approaches he presents.  I quote: "a sixty-four requester version of this coding style was not implemented due to the amount of tedious typing it would have required".  I thought this to be the perfect example to try with a chip generator tool.  Chisel in this case.

Here is the 4 requester version of his code :


always@(pointer_reg or req) begin
  case (pointer_reg)
  2'b00 :
    if (req[0]) grant = 4'b0001;
    else if (req[1]) grant = 4'b0010;
    else if (req[2]) grant = 4'b0100;
    else if (req[3]) grant = 4'b1000;
    else grant = 4'b0000;
  2'b01 :
    if (req[1]) grant = 4'b0010;
    else if (req[2]) grant = 4'b0100;
    else if (req[3]) grant = 4'b1000;
    else if (req[0]) grant = 4'b0001;
    else grant = 4'b0000;
  2'b10 :
    if (req[2]) grant = 4'b0100;
    else if (req[3]) grant = 4'b1000;
    else if (req[0]) grant = 4'b0001;
    else if (req[1]) grant = 4'b0010;
    else grant = 4'b0000;
  2'b11 :
    if (req[3]) grant = 4'b1000;
    else if (req[0]) grant = 4'b0001;
    else if (req[1]) grant = 4'b0010;
    else if (req[2]) grant = 4'b0100;
    else grant = 4'b0000;
  endcase
end

Nothing special here.  It is clear that code would explode with larger input vectors.

Here is my untested Chisel implementation :


class BlobArb(width: Int) extends Module {
  val io = new Bundle {
    val request = UInt(INPUT, width)
    val pointer = UInt(INPUT, log2Up(width))
    val grant = UInt(OUTPUT, width)
  }

  io.grant := UInt(0)  // Chisel complains if no default value is specified

  for (i <- 0 until width) {
    when(io.pointer === UInt(i)) {
      for (j <- 0 until width) {
        var x = (i + j) % width
        when(io.request(x)) {
          io.grant := UInt(1 << x)
        }
      }
    }
  }
}

I'll re-iterate.  This isn't tested.  I simply don't have the time.  But, I do have time for some quick commentary.

I kind of expected that I could look at the generated Verilog code (link) and visually check whether my Chisel implementation matched the reference implemented in Verilog.  I did not find it to be the case.

From the perspective of a seasoned verification engineer, I observe the following major flaw.  There will come a time where the Chisel-based design will need to be simulated at the full-chip level.  When (not if) there is a bug, I am not sure how one correlates a node the generated Verilog netlist to Chisel source.  I would assume this has been solved because the great folks at Cal have done multiple chip tapeouts using Chisel as their primary HDL.

As interesting of an HDL that Chisel is... Gosh... I have mixed feelings.  I am intimately familiar with Verilog coding guidelines for synthesis.  I barely wrote any code and I found myself asking whether the final Verilog output would match my expectations.  There is an order of magnitude of additional freedom.  Freedom in describing hardware does not come without consequences. More hands on experience would obviously help here.

To the key issue of chip generators.  Verilog parameters aren't nearly enough.  Perl and Verilog can take you pretty far and Genesis2 (Perl and Verilog on steroids, though unsupported) can take you even further.  My crystal ball is that some design-by-committee solution will result in to improvements to SystemVerilog.  The improvements, of course, will take a decade to roll out.

Next steps for Chisel and I? I don't know.  My blog is means to get creative juices flowing, not necessarily for deep dives.  Hoping that one day some topic will call for more a move in-depth investigation.

Wednesday Night Hack #14 (Part 1) - Home Automation Fail!

I recently purchased a Raspberry Pi and companion Razberry daughter board for the purposes of building up my home automation network (HAN).  The daughter board hosts a Sigma Designs ZM5202 Z-Wave transceiver module with custom (read: proprietary) firmware.  A well featured low-level software stack (middleware) is also provided.  This software provides different ways for users to access the devices on their HAN.  In addition to a web service, there is a C API and JSON API available.  The web service allows users to setup and debug their network without writing a single line of code.  Sample programs (and Android, IOS app) are also provided.

All in all, I am impressed.  I was not expecting this level of software support.  My one caveat is that the board's firmware appears to be closed source.  This would mean that any software written using the provided API would be bound to the vendor's product.  For the average hobbyist who is looking to get something done, quickly, I guess there is no harm.  I would think twice about using this platform to develop a commercial product.  Luckily, there appears to be a open source alternative.

Given that I do not have any commercial intentions, I'll continue on... or not..

I couldn't include the device on the network.  I tried for a good hour.  I'll be returning deadbolt I purchased and try again another time.  Next time, I will make sure to pair the device before installing the deadbolt on my front door, oops!

Wednesday, May 13, 2015

Wednesday Night Hack #13 - Features of Scala language for Chisel

I have zero experience with Scala and little experience with functional programming languages.  This small guide was written to help me (and only me) understand the understand the syntactic sugar in Chisel.  I hope it can help others.

Let's start with a simple class, annotated with comments :

class Point(val xc: Int, val yc: Int) { // constructor arguments are here
   var x: Int = xc // x is explicitly typed, set to value xc upon construction
   var y: Int = yc

   def move(dx: Int, dy: Int) { // def is a function call
      x = x + dx
      y = y + dy
      println ("Point x location : " + x);
      println ("Point y location : " + y);
   }
}

An object can be constructed using new operation (as one would expect).

There are three potentially new keywords: def, val, and var.  To summarize this post on stackoverflow.com: def defines a method, val defines a fixed value (which cannot be modified), and var defines a variable (which can be modified).

Once this is understood, you can refer to this example that explains class extensions and singleton classes (e.g. object).

Next closures.  Not sure how applicable they are to describing hardware.

val multiplier = (i:Int) => i * 10

This creates a anonymous function with parameter list: i:Int.  The body of the function is i * 10.  The function can be used in current scope by calling multiplier(6), for which it will return 60.

Next match operator.  Again, not sure how applicable this is within the context of Chisel.

object Test { // singleton class
   def main(args: Array[String]) {
      println(matchTest("two"))  // prints 2
      println(matchTest("test")) // prints many
      println(matchTest(1))      // prints one
   }
   def matchTest(x: Any): Any = x match {
   // Any is the root of Scala class hierarchy
      case 1      => "one"
      case "two"  => 2
      case y: Int => "scala.Int" // matches against any integer
                                 // binds selector value to var y
      case _      => "many" // default case
   }
}

Regular expressions.  This example is interesting because it demonstrates the functional nature of the language.

// need to include a library for regex functionality
import scala.util.matching.Regex 

object Test {
   def main(args: Array[String]) {
      // argument to Regex constructor is the regular expression
      val pattern = new Regex("(S|s)cala") 
      val str = "Scala is scalable and cool"

      // create instance object of type Regex
      // method findAllIn(str) of instance is called
      // this returns a list ...
      // which we convert into a comma delimited string ...
      // and then printed
      println((pattern findAllIn str).mkString(","))
   }
}

Arrays.  From this example.
Syntax for arrays will take some getting used to.

// declare empty of 3 strings
var z = new Array[String](3) // 3,3 would be a 3x3 2D array

// initiatlize the 3 strings
z(0) = "Zara"; z(1) = "Nuha"; z(4/2) = "Ayan"

// alternate way to initialize string
var z = Array("Zara", "Nuha", "Ayan")

// simple array iteration
for (x <- z) {
    println(x)
}

As one would expect, Scala offers built-in support maps, tuples, lists and other data structures.  They are called collections.

// define List of integers
val x = List(1,2,3,4)

// define a map
val x = Map("one" -> 1, "two" -> 2, "three" -> 3)

// create a tuple of two elements
val x = (10, "Scala")

These collections can be accessed by using iterators.  The iterator API is fully featured.

Turning to the Chisel examples. This is the first example that confused me.  The example is creating a class extension.  Inside the class extension there is a bundle object instance.  The := operator threw me off.

class Mux2 extends Module {
    val io = new Bundle {
        val sel = UInt(INPUT, 1)
        val in0 = UInt(INPUT, 1)
        val in1 = UInt(INPUT, 1)
        val out = UInt(OUTPUT, 1)
    }
    io.out := (io.sel & io.in1) | (~io.sel & io.in0) // ???
}

As best I can tell, the operator is defined as part of Data.scala (and other locations in Chisel source and it is not part of pure Scala.

Here is another example that stumped me.  Specifically it was the :: operator.  I could not find mention of in Scala documentation.  Mux.scala in the Chisel source appears implements the operator.

class Parity extends Module {
    val io = new Bundle {
    val in = Bool(dir = INPUT)
    val out = Bool(dir = OUTPUT) }
    val s_even :: s_odd :: Nil = Enum(UInt(), 2) // ???
    val state = Reg(init = s_even)
    when (io.in) {
        when (state === s_even) { state := s_odd }
        when (state === s_odd) { state := s_even }
    }
    io.out := (state === s_odd)
}

Documentation states that Enum(UInt(), 2) generates 2 UInt literals and, I guess, assigns then to s_even, s_odd.

The <> is implemented by Chisel library.  The example below is from Bulk connections in the Chisel paper.

class Block extends Module {
    val io = new FilterIO()
    val f1 = Module(new Filter())
    val f2 = Module(new Filter())
    f1.io.x <> io.x // // ???
    f1.io.y <> f2.io.x
    f2.io.y <> io.y
}

Enough theory for now.  Having worked through this short exercise to study Scala and Chisel, next step (next week?) is to try and build some hardware.

Tuesday, May 12, 2015

Wednesday Night (Pre-)Hack #13 - Chisel and IntelliJ Integration

Before getting started.  Make sure the following is complete.

IntelliJ is downloaded and installed.  The Scala plugin for IntelliJ is also installed.

It is unbelievable how simple the process was.  I had never used IntelliJ before, but I am a user and fan of their PyCharm and CLion tools.

1. Create a new Scala project



2. Make sure Project SDK and Scala SDK are properly set



3. Write, compile, and test a small Scala program


For basic testing, use the following shortcuts :

Ctrl-Shift-F9 to compile
Ctrl-Shift-F10 to run


4. Download Chisel from Maven repository


Use: File.. Project Structure.. Global Libraries..

I picked up the following version: edu.berkeley.cs:chisel_2.11:2.2.26


5. Write, compile, and test a small Scala program

One gotcha I encountered was the name of the source file (HelloWorld.scala) needed to match the name of the object containing main function call.



Monday, May 4, 2015

Comments about MITx 6.00.2x

This was my first massively online course that I finished, to completion.

What were my thoughts?  Mixed.

To be honest, it was hard to stay motivated throughout.  The course also suffered from a difficulty ramp, first 2/3 was easy, then the last 1/3 hits you hard.  Difficulty level overall was pretty for low me.

The finger exercises were great because they give you immediate feedback on whether what you learned in the lecture was sufficient.

The problem sets suffered from one major problem.  In addition to the difficulty ramp, if for some reason, you have trouble passing the input vectors for the first part of the problem set, then you were toast for the rest. That was frustrating.  A frustration which could have been mitigated if I had spent time connecting with other students in the discussion forums.

Honestly, the greatest thing about this course is that I gave no concern whatsoever about the grade I'd eventually receive.  Instead, I focused on learning what I could.  What a refreshing and liberating feeling.

What I got out from the course was a introduction (or re-introduction) to some CS topics that weren't properly covered during my EE/CE studies.  The subject material was presented using Python, which I thought to be a wonderful.

The course succeeded in it's goal of teaching the basics of data science.  I now have a better understanding of stochastic programs / monte carlo simulations (using randomness in computations), data visualization and curve fitting using pylab, knapsack and graph optimization problems, and machine learning - well, sort of for that one.

I personally learn best from working on open ended problems, not contrived examples.  The course could be improved by adding such a component.

As a final note, because of the lack of an individual project component, there's a good chance I'll forget most of the details of what I learned :)  The redeeming factor is, without question, the wonderful textbook that I can go back and refer to.  The process of sitting through all the lectures and trying (but not completing) all the problem sets made it such that my copy of the textbook is nicely highlighted throughout.  This will make it easier for me if I ever needed to write code to, say..., apply machine learning to making sense of functional coverage data.