Skip to content

Commit

Permalink
Merge branch 'master' into 3.6-release
Browse files Browse the repository at this point in the history
  • Loading branch information
jackkoenig committed Dec 6, 2022
2 parents d1b65eb + 9f080d5 commit 67c7e12
Show file tree
Hide file tree
Showing 16 changed files with 478 additions and 32 deletions.
19 changes: 11 additions & 8 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ val defaultVersions = Map(
)

lazy val commonSettings = Seq(
resolvers ++= Seq(
Resolver.sonatypeRepo("snapshots"),
Resolver.sonatypeRepo("releases")
),
resolvers ++= Resolver.sonatypeOssRepos("snapshots"),
resolvers ++= Resolver.sonatypeOssRepos("releases"),
organization := "edu.berkeley.cs",
version := "3.6.0-M1",
autoAPIMappings := true,
Expand All @@ -39,6 +37,7 @@ lazy val commonSettings = Seq(
)

lazy val publishSettings = Seq(
versionScheme := Some("pvp"),
publishMavenStyle := true,
Test / publishArtifact := false,
pomIncludeRepository := { x => false },
Expand Down Expand Up @@ -71,9 +70,9 @@ lazy val publishSettings = Seq(
lazy val chiselSettings = Seq(
name := "chisel3",
libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "3.2.12" % "test",
"org.scalatest" %% "scalatest" % "3.2.14" % "test",
"org.scalatestplus" %% "scalacheck-1-14" % "3.2.2.0" % "test",
"com.lihaoyi" %% "os-lib" % "0.8.1"
"com.lihaoyi" %% "upickle" % "2.0.0"
)
) ++ (
// Tests from other projects may still run concurrently
Expand All @@ -94,7 +93,7 @@ lazy val pluginScalaVersions = Seq(
// scalamacros paradise version used is not published for 2.12.0 and 2.12.1
"2.12.2",
"2.12.3",
"2.12.4",
// 2.12.4 is broken in newer versions of Zinc: https://github.com/sbt/sbt/issues/6838
"2.12.5",
"2.12.6",
"2.12.7",
Expand All @@ -118,7 +117,7 @@ lazy val pluginScalaVersions = Seq(
"2.13.7",
"2.13.8",
"2.13.9",
"2.13.10",
"2.13.10"
)

lazy val plugin = (project in file("plugin"))
Expand Down Expand Up @@ -174,6 +173,10 @@ lazy val core = (project in file("core"))
.settings(mimaPreviousArtifacts := Set())
.settings(
name := "chisel3-core",
libraryDependencies ++= Seq(
"com.lihaoyi" %% "upickle" % "2.0.0",
"com.lihaoyi" %% "os-lib" % "0.8.1"
),
scalacOptions := scalacOptions.value ++ Seq(
"-deprecation",
"-explaintypes",
Expand Down
6 changes: 4 additions & 2 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ object v {
val firrtl = getVersion("firrtl")
val treadle = getVersion("treadle")
val chiseltest = ivy"edu.berkeley.cs::chiseltest:0.6-SNAPSHOT"
val scalatest = ivy"org.scalatest::scalatest:3.2.12"
val scalatest = ivy"org.scalatest::scalatest:3.2.14"
val scalacheck = ivy"org.scalatestplus::scalacheck-1-14:3.2.2.0"
val osLib = ivy"com.lihaoyi::os-lib:0.8.1"
val upickle = ivy"com.lihaoyi::upickle:2.0.0"
val macroParadise = ivy"org.scalamacros:::paradise:2.1.1"
}

Expand Down Expand Up @@ -61,7 +62,8 @@ trait CommonModule extends CrossSbtModule with PublishModule with ScalafmtModule
override def moduleDeps = super.moduleDeps ++ firrtlModule

override def ivyDeps = super.ivyDeps() ++ Agg(
v.osLib
v.osLib,
v.upickle
) ++ firrtlIvyDeps

def publishVersion = "3.6.0-M1"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package chisel3.experimental

import upickle.default._

import scala.reflect.runtime.universe

/** Parameter for SerializableModule, it should be serializable via upickle API.
* For more information, please refer to [[https://com-lihaoyi.github.io/upickle/]]
*/
trait SerializableModuleParameter

/** Mixin this trait to let chisel auto serialize module, it has these constraints:
* 1. Module should not be any inner class of other class, since serializing outer class is impossible.
* 2. Module should have and only have one parameter with type `T`:
* {{{
* class FooSerializableModule[FooSerializableModuleParameter](val parameter: FooSerializableModuleParameter)
* }}}
* 3. user should guarantee the module is reproducible on their own.
*/
trait SerializableModule[T <: SerializableModuleParameter] { this: BaseModule =>
val parameter: T
}
object SerializableModuleGenerator {

/** serializer for SerializableModuleGenerator. */
implicit def rw[P <: SerializableModuleParameter, M <: SerializableModule[P]](
implicit rwP: ReadWriter[P],
pTypeTag: universe.TypeTag[P],
mTypeTag: universe.TypeTag[M]
): ReadWriter[SerializableModuleGenerator[M, P]] = readwriter[ujson.Value].bimap[SerializableModuleGenerator[M, P]](
{ (x: SerializableModuleGenerator[M, P]) =>
ujson
.Obj(
"parameter" -> upickle.default.writeJs[P](x.parameter),
"generator" -> x.generator.getName
)
},
{ (json: ujson.Value) =>
SerializableModuleGenerator[M, P](
Class.forName(json.obj("generator").str).asInstanceOf[Class[M]],
upickle.default.read[P](json.obj("parameter"))
)
}
)
}

/** the serializable module generator:
* @param generator a non-inner class of module, which should be a subclass of [[SerializableModule]]
* @param parameter the parameter of `generator`
*/
case class SerializableModuleGenerator[M <: SerializableModule[P], P <: SerializableModuleParameter](
generator: Class[M],
parameter: P
)(
implicit val pTag: universe.TypeTag[P],
implicit val mTag: universe.TypeTag[M]) {
private[chisel3] def construct: M = {
require(
generator.getConstructors.length == 1,
s"""only allow constructing SerializableModule from SerializableModuleParameter via class Module(val parameter: Parameter),
|you have ${generator.getConstructors.length} constructors
|""".stripMargin
)
require(
!generator.getConstructors.head.getParameterTypes.last.toString.contains("$"),
s"""You define your ${generator.getConstructors.head.getParameterTypes.last} inside other class.
|This is a forbidden behavior, since we cannot serialize the out classes,
|for debugging, these are full parameter types of constructor:
|${generator.getConstructors.head.getParameterTypes.mkString("\n")}
|""".stripMargin
)
require(
generator.getConstructors.head.getParameterCount == 1,
s"""You define multiple constructors:
|${generator.getConstructors.head.getParameterTypes.mkString("\n")}
|""".stripMargin
)
generator.getConstructors.head.newInstance(parameter).asInstanceOf[M]
}

/** elaborate a module from this generator. */
def module(): M = construct
}
2 changes: 2 additions & 0 deletions docs/src/appendix/chisel3-vs-chisel2.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ section: "chisel3"
import chisel3._
```

**Note** Chisel2 Compatibility Mode is entirely deprecated in 3.6, so this entire page is relevant only for 3.6 and earlier.

## Chisel2 Migration
For those moving from Chisel2, there were some backwards incompatible changes
and your RTL needs to be modified to work with Chisel3. The required
Expand Down
89 changes: 89 additions & 0 deletions docs/src/cookbooks/serialization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
layout: docs
title: "Serialization Cookbook"
section: "chisel3"
---

# Serialization Cookbook

* [Why do I need to serialize Modules](#why-do-i-need-to-serialize-modules)
* [How do I serialize Modules with SerializableModuleGenerator](#how-do-i-seerialize-modules-with-serializablemodulegenerator)

## Why do I need to serialize Modules
Chisel provides a very flexible hardware design experience. However, it sometimes becomes too flexible to design a relative big designs, since parameters of module might come from: 1. Global variables; 2. Outer class; 3. Entropies(time, random). It becomes really hard or impossible to describe "how to reproduce this single module?". This forbids doing unit-test for a module generator, and introduces issues in post-synthesis when doing ECO: a change to Module A might lead to change in Module B.
Thus `SerializableModuleGenerator`, `SerializableModule[T <: SerializableModuleParameter]` and `SerializableModuleParameter` are provided to solve these issues.
For any `SerializableModuleGenerator`, Chisel can automatically serialize and de-serialize it by adding these constraints:
1. the `SerializableModule` should not be inner class, since the outer class is a parameter to it;
1. the `SerializableModule` has and only has one parameter with `SerializableModuleParameter` as its type.
1. the Module neither depends on global variables nor uses non-reproducible functions(random, time, etc), and this should be guaranteed by user, since Scala cannot detect it.

It can provide these benefits:
1. user can use `SerializableModuleGenerator(module: class[SerializableModule], parameter: SerializableModuleParameter)` to auto serialize a Module and its parameter.
1. user can nest `SerializableModuleGenerator` in other serializable parameters to represent a relative large parameter.
1. user can elaborate any `SerializableModuleGenerator` into a single module for testing.


## How do I serialize Modules with `SerializableModuleGenerator`
It is pretty simple and illustrated by example below, the `GCD` Module with `width` as its parameter.

```scala mdoc:silent
import chisel3._
import chisel3.experimental.{SerializableModule, SerializableModuleGenerator, SerializableModuleParameter}
import upickle.default._

// provide serialization functions to GCDSerializableModuleParameter
object GCDSerializableModuleParameter {
implicit def rwP: ReadWriter[GCDSerializableModuleParameter] = macroRW
}

// Parameter
case class GCDSerializableModuleParameter(width: Int) extends SerializableModuleParameter

// Module
class GCDSerializableModule(val parameter: GCDSerializableModuleParameter)
extends Module
with SerializableModule[GCDSerializableModuleParameter] {
val io = IO(new Bundle {
val a = Input(UInt(parameter.width.W))
val b = Input(UInt(parameter.width.W))
val e = Input(Bool())
val z = Output(UInt(parameter.width.W))
})
val x = Reg(UInt(parameter.width.W))
val y = Reg(UInt(parameter.width.W))
val z = Reg(UInt(parameter.width.W))
val e = Reg(Bool())
when(e) {
x := io.a
y := io.b
z := 0.U
}
when(x =/= y) {
when(x > y) {
x := x - y
}.otherwise {
y := y - x
}
}.otherwise {
z := x
}
io.z := z
}
```
using `write` function in `upickle`, it should return a json string:
```scala mdoc
val j = upickle.default.write(
SerializableModuleGenerator(
classOf[GCDSerializableModule],
GCDSerializableModuleParameter(32)
)
)
```

You can then read from json string and elaborate the Module:
```scala mdoc:compile-only
chisel3.stage.ChiselStage.emitVerilog(
upickle.default.read[SerializableModuleGenerator[GCDSerializableModule, GCDSerializableModuleParameter]](
ujson.read(j)
).module()
)
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.5.8
sbt.version=1.8.0
8 changes: 4 additions & 4 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.15")

addSbtPlugin("com.thoughtworks.sbt-api-mappings" % "sbt-api-mappings" % "3.0.2")

addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.3.2")
addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.3.6")

addSbtPlugin("com.eed3si9n" % "sbt-sriracha" % "0.1.0")

addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.0")
addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.1")

addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.10")
addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.11")

addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.0")

// From FIRRTL for building from source
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.33")
Loading

0 comments on commit 67c7e12

Please sign in to comment.