Skip to content

Instantly share code, notes, and snippets.

@jackkoenig
Last active August 4, 2021 00:02
Show Gist options
  • Save jackkoenig/6e8d8c92012e0992f0c7bb1093c0bcce to your computer and use it in GitHub Desktop.
Save jackkoenig/6e8d8c92012e0992f0c7bb1093c0bcce to your computer and use it in GitHub Desktop.
DataView Cookbook (after mdoc generation) - Updated 3 Aug 2021
layout title section
docs
DataView Cookbook
chisel3

DataView Cookbook

How do I view a Data as a UInt or vice versa?

Subword viewing (using concatenations or bit extractions in DataViews) is not yet supported. We intend to implement this in the future, but for the time being, use regular casts (.asUInt and .asTypeOf).

How do I create a DataView for a Bundle has a type parameter?

Instead of using a val, use a def which can have type parameters:

import chisel3._
import chisel3.experimental.dataview._

class Foo[T <: Data](val foo: T) extends Bundle
class Bar[T <: Data](val bar: T) extends Bundle

object Foo {
  implicit def view[T <: Data]: DataView[Foo[T], Bar[T]] = {
    DataView(f => new Bar(f.foo.cloneType), _.foo -> _.bar)
    // .cloneType is necessary because the f passed to this function will be bound hardware
  }
}

If you think about type parameterized classes as really being a family of different classes (one for each type parameter), you can think about the implicit def as a generator of DataViews for each type parameter.

How do I create a DataView for a Bundle with optional fields?

Instead of using the default DataView apply method, use DataView.mapping:

import chisel3._
import chisel3.experimental.dataview._

class Foo(val w: Option[Int]) extends Bundle {
  val foo = UInt(8.W)
  val opt = w.map(x => UInt(x.W))
}
class Bar(val w: Option[Int]) extends Bundle {
  val bar = UInt(8.W)
  val opt = w.map(x => UInt(x.W))
}

object Foo {
  implicit val view: DataView[Foo, Bar] =
    DataView.mapping(
      // First argument is always the function to make the view from the target
      f => new Bar(f.w),
      // Now instead of a varargs of tuples of individual mappings, we have a single function that
      // takes a target and a view and returns an Iterable of tuple
      (f, b) =>  List(f.foo -> b.bar) ++ f.opt.map(_ -> b.opt.get)
                                   // ^ Note that we can append options since they are Iterable!

    )
}

How do I connect a subset of Bundle fields?

Chisel 3 requires types to match exactly for connections. DataView provides a mechanism for "viewing" one Bundle object as if it were the type of another, which allows them to be connected.

How do I view a Bundle as a parent type (superclass)?

For viewing Bundles as the type of the parent, it is as simple as using viewAsSupertype and providing a template object of the parent type:

import chisel3._
import chisel3.experimental.dataview._

class Foo extends Bundle {
  val foo = UInt(8.W)
}
class Bar extends Foo {
  val bar = UInt(8.W)
}
class MyModule extends Module {
  val foo = IO(Input(new Foo))
  val bar = IO(Output(new Bar))
  bar.viewAsSupertype(new Foo) := foo // bar.foo := foo.foo
  bar.bar := 123.U           // all fields need to be connected
}
module MyModule(
  input        clock,
  input        reset,
  input  [7:0] foo_foo,
  output [7:0] bar_foo,
  output [7:0] bar_bar
);
  assign bar_foo = foo_foo; // @[dataview.md 120:32]
  assign bar_bar = 8'h7b; // @[dataview.md 121:11]
endmodule

How do I view a Bundle as a parent type when the parent type is abstract (like a trait)?

Given the following Bundles that share a common trait:

import chisel3._
import chisel3.experimental.dataview._

trait Super extends Bundle {
  def bitwidth: Int
  val a = UInt(bitwidth.W)
}
class Foo(val bitwidth: Int) extends Super {
  val foo = UInt(8.W)
}
class Bar(val bitwidth: Int) extends Super {
  val bar = UInt(8.W)
}

Foo and Bar cannot be connected directly, but they could be connected by viewing them both as if they were instances of their common supertype, Super. A straightforward approach might run into an issue like the following:

class MyModule extends Module {
  val foo = IO(Input(new Foo(8)))
  val bar = IO(Output(new Bar(8)))
  bar.viewAsSupertype(new Super) := foo.viewAsSupertype(new Super)
}
// error: trait Super is abstract; cannot be instantiated
//   bar.viewAsSupertype(new Super) := foo.viewAsSupertype(new Super)
//                       ^^^^^^^^^
// error: trait Super is abstract; cannot be instantiated
//   bar.viewAsSupertype(new Super) := foo.viewAsSupertype(new Super)
//                                                         ^^^^^^^^^

The problem is that viewAs requires an object to use as a type template (so that it can be cloned), but traits are abstract and cannot be instantiated. The solution is to create an instance of an anonymous class and use that object as the argument to viewAs. We can do this like so:

class MyModule extends Module {
  val foo = IO(Input(new Foo(8)))
  val bar = IO(Output(new Bar(8)))
  val tpe = new Super { // Adding curly braces creates an anonymous class
    def bitwidth = 8 // We must implement any abstract methods
  }
  bar.viewAsSupertype(tpe) := foo.viewAsSupertype(tpe)
}

By adding curly braces after the name of the trait, we're telling Scala to create a new concrete subclass of the trait, and create an instance of it. As indicated in the comment, abstract methods must still be implemented. This is the same that happens when one writes new Bundle {}, the curly braces create a new concrete subclass; however, because Bundle has no abstract methods, the contents of the body can be empty.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment