How do I express a ROM in Chisel 3 so that a ROM is inferred by the Altera tools

602 views
Skip to first unread message

Øyvind Harboe

unread,
Mar 22, 2017, 7:44:49 PM3/22/17
to chisel-users
I'd like to have a ROM inferred for my Chisel code on my Altera FPGA: https://github.com/ucb-bar/chisel3/wiki/Memories

Q: Which backend should I use for Chisel 3? I thought I saw an 'fpga' backend in Chisel 2, but there isn't one for Chisel 3?

Q: am I barking up the wrong tree? Should I go for blackboxes? Inference worked like a charm for RAM's and multipliers, so I figured it was the path for ROM too.

Currently I'm putting the top-level .v file generated for the Verilator backend into my FPGA design and I'm able to get RAM and multiplier blocks inferred, but no such luck with ROMs.

Altera wants something that looks like a case statement: http://quartushelp.altera.com/15.0/mergedProjects/hdl/vlog/vlog_file_dir_romstyle.htm

(* romstyle = "M9K" *) output reg [7:0] q;

   reg [4:0] addr_reg;

   always@(posedge clk) begin
      addr_reg <= addr;
      case(addr_reg)
          0: q <= 8'h01;
          1: q <= 8'h02;
          2: q <= 8'h03;
          3: q <= 8'h04;
          4: q <= 8'h05;
          5: q <= 8'h06;
          6: q <= 8'h07;
          …

           endcase
   end
  



But Chisel generates something that looks like a mux:

  assign _GEN_1 = 4'h1 == _T_124 ? simpleConstROM_1 : simpleConstROM_0;
  assign _GEN_2 = 4'h2 == _T_124 ? simpleConstROM_2 : _GEN_1;
  assign _GEN_3 = 4'h3 == _T_124 ? simpleConstROM_3 : _GEN_2;
  assign _GEN_4 = 4'h4 == _T_124 ? simpleConstROM_4 : _GEN_3;
  assign _GEN_5 = 4'h5 == _T_124 ? simpleConstROM_5 : _GEN_4;
...


Martin Schoeberl

unread,
Mar 23, 2017, 8:22:38 AM3/23/17
to chisel...@googlegroups.com
If you have a  logic table and the input in a register you are up for an implementation in a ROM in the FPGA (that’s roughly what you show in the example). The same functionality might also be implemented with LUTs. It depends on the size of the table. Simply let the synthesize tool decide how to implement it. I would not use blackboxes or any FPGA vendor specific components.

That said, as an example (still Chisel 2, but that should not matter) I read in a file from Scala and generate a Vec for the ROM (for boot code of a processor). Depending on the size that ROM is sometimes implemented in a on-chip memory block or in logic:

--
You received this message because you are subscribed to the Google Groups "chisel-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chisel-users...@googlegroups.com.
To post to this group, send email to chisel...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/chisel-users/a43f8358-aacd-475c-9e27-cf44935e858b%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Øyvind Harboe

unread,
Mar 23, 2017, 9:27:49 AM3/23/17
to chisel-users
Ah, I see. Thanks for the advice. I've got a 16 bit wide 16 entry 'rom' and I guess the FPGA tool prefers not to infer a ROM from that(because it's so small and it's better to use generic resources that are close and easier to route??).

Silly question: how do I tell Chisel to generate the Verilog?

Currently I fire up a dummy test that runs the Verilator backend, which generates Verilog as a side-effect. This is a bit annoying because running Verilator and the c++ compiler takes a long time on my machine.

There has to be a Chisel 3 way to tell Chisel to do nothing but to generate the Verilog. The only thing I've found, would be to hack and slash setupVerilatorBackend() so that it only generates the Verilog and doesn't do any of the other stuff, but it feels wrong as there has to be some way to simply *only* generate the Verilog without running tests?

Steve Burns

unread,
Mar 23, 2017, 2:07:49 PM3/23/17
to chisel...@googlegroups.com
I would suggest blackboxes. With the online Blackbox feature you can have arbitrary scala code generate the verilog description on the fly (very easy to automatically produce the verilog style you want from, say, an IndexedSeq [BigInt]). And you can add hooks to the firrtl-interpreter so that you use that instead or verilator/vcs for faster development iterations.

--
You received this message because you are subscribed to the Google Groups "chisel-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chisel-users+unsubscribe@googlegroups.com.

Øyvind Harboe

unread,
Mar 23, 2017, 3:35:07 PM3/23/17
to chisel-users
I assume by "online" BlackBox, you're referring to https://github.com/ucb-bar/chisel3/wiki/BlackBoxes which is present in Chisel 3 now.

Do you have an example of how to generate  verilog from firrtl? It sounds like what I need to do to generate the ROMs.

It also sounds like I could use this to solve my problem of how to quickly initialize RAM modules by using Verilog features like $readmemh(MEM_INIT_FILE, ram);


To unsubscribe from this group and stop receiving emails from it, send an email to chisel-users...@googlegroups.com.

Steve Burns

unread,
Mar 24, 2017, 6:49:34 PM3/24/17
to chisel...@googlegroups.com
Meant inline. Sorry. I have an example. I'll send it soon. It is just chisel code. No manipulation of firrtl-interpreter necessary.

Steve Burns

unread,
Mar 28, 2017, 8:58:56 PM3/28/17
to chisel...@googlegroups.com
Here is a standalone implementation: put it in a file in src/test/scala. I'm using an initial block to initialize the ROM, but you could use $readme instead.

//====================

package rom.tests

import org.scalatest.{ Matchers, FlatSpec, GivenWhenThen}
import org.scalacheck.{ Properties, Gen, Arbitrary}
import org.scalacheck.Prop.{ forAll, forAllNoShrink, AnyOperators, collect}

import chisel3._
import chisel3.util._
import chisel3.iotesters._
import firrtl_interpreter.InterpretiveTester

import chisel3.experimental._

trait MParams {
  val addrWidth = 20
  val dataWidth = 40
}

class RomIfc extends Module with MParams {
  val io = IO{
    new Bundle{
      val addr = Input(UInt(addrWidth.W))
      val data = Output(UInt(dataWidth.W))
    }
  }
}

import firrtl.ir.Type
import firrtl_interpreter._

class RomFirrtlInterpreterBlackBox( val name : String) extends BlackBoxImplementation with MParams {

  val tbl : IndexedSeq[BigInt] = for( i <- 0 until (1 << addrWidth)) yield BigInt(i).pow(2)

  def outputDependencies(outputName: String): Seq[(String)] = {
    outputName match {
      case "data" => Seq(fullName("addr"))
      case _ => Seq.empty
    }
  }

  def cycle(): Unit = {}

  def execute(inputValues: Seq[Concrete], tpe: Type): Concrete = {
    val result = tbl(inputValues(0).value.intValue)
    val poisoned = inputValues(0).poisoned
    TypeInstanceFactory(tpe, result, poisoned)
  }

}

class RomBlackBoxFactory extends BlackBoxFactory {

  def createInstance(instanceName: String, blackBoxName: String): Option[BlackBoxImplementation] = {
    println( s"AddReduceBlackBoxFactory: ${blackBoxName}")
    blackBoxName match {
      case "RomBlackBox"    => Some(add(new RomFirrtlInterpreterBlackBox(instanceName)))
      case _                => None
    }
  }
}

class RomBlackBox extends BlackBox with MParams with HasBlackBoxInline {
  val io = IO{
    new Bundle{
      val addr = Input(UInt(addrWidth.W))
      val data = Output(UInt(dataWidth.W))
    }
  }

  setInline("RomBlackBox.v",
  s"""|module RomBlackBox( input [${addrWidth-1}:0] addr, output [${dataWidth-1}:0] data);
      |  reg [${dataWidth-1}:0] rom[0:${(1<<addrWidth)-1}];
      |  assign data = rom[addr];
      |  initial begin
      |     for(integer i=0; i<${1<<addrWidth}; i=i+1) begin
      |       rom[i] = i*i;
      |     end
      |  end
      |endmodule // RomBlackBox""".stripMargin)
}


class scalaCheckTester( factory : () => Module) extends MParams { 

  val optionsManager = new TesterOptionsManager {
    interpreterOptions = interpreterOptions.copy(
      blackBoxFactories = interpreterOptions.blackBoxFactories :+ new RomBlackBoxFactory)
  }

  val s = chisel3.Driver.emit( factory)
  val tester = new InterpretiveTester( s, optionsManager)

  def run( a : BigInt) = {
    tester.poke( s"io_addr", a)
    tester.peek( s"io_data") ?= a*a
  }

}

class RomBlackBoxWrapper extends RomIfc {
// Can't have black box at top level
  val m = Module(new RomBlackBox)
  m.io.addr := io.addr
  io.data := m.io.data
}

class RomSpec( factory : () => RomIfc) extends MParams {
  val gen = Gen.choose( 0L, (1L << addrWidth) - 1L)
  val t = new scalaCheckTester( factory)
}

object RomSpec_BlackBox extends Properties("RomSpec_BlackBox") with MParams {
  val spec = new RomSpec( () => new RomBlackBoxWrapper)

  property("looks up") = forAllNoShrink( spec.gen) {
    case a => spec.t.run( a)
  }
}

class Rom_PeekPokeTester[T <: RomIfc](c:T) extends PeekPokeTester(c) with MParams {

  poke( c.io.addr, 25)
  expect( c.io.data, 25*25)

}

class RomBlackBox_PeekPokeTest extends FlatSpec with Matchers {

  val optionsManager = new TesterOptionsManager {
    interpreterOptions = interpreterOptions.copy(
      blackBoxFactories = interpreterOptions.blackBoxFactories :+ new RomBlackBoxFactory)
  }

  behavior of "RomBlackBox" 

  it should "work" in {
    chisel3.iotesters.Driver.execute( () => new RomBlackBoxWrapper, optionsManager){ c => new Rom_PeekPokeTester(c)} should be (true)
  }

}

class RomBlackBox_PeekPokeTest_Verilator extends FlatSpec with Matchers {

  val optionsManager = new TesterOptionsManager {
    interpreterOptions = interpreterOptions.copy(
      blackBoxFactories = interpreterOptions.blackBoxFactories :+ new RomBlackBoxFactory)
  }

  behavior of "RomBlackBox" 

  it should "work" in {
    chisel3.iotesters.Driver( () => new RomBlackBoxWrapper, "verilator"){ c => new Rom_PeekPokeTester(c)} should be (true)
  }

}

Øyvind Harboe

unread,
Mar 29, 2017, 1:15:42 AM3/29/17
to chisel-users
Thanks!

Examples with unit-tests is gold!

Hmm.... could we have a chisel-cookbook/samples repository for such things?

To unsubscribe from this group and stop receiving emails from it, send an email to chisel-users...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages