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.