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 the author refers to coding the arbiter as a big blog.  The results of the paper 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 it'd be interesting to try the big blob approach using Chisel.

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 somewhat expected that I could look at the generated Verilog code (link) and visually check whether my Chisel implementation matched the reference implemented in Verilog.  That is not true.

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 needed 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.