-
Notifications
You must be signed in to change notification settings - Fork 615
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into 3.6-release
- Loading branch information
Showing
16 changed files
with
478 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
83 changes: 83 additions & 0 deletions
83
core/src/main/scala/chisel3/experimental/SerializableModuleGenerator.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
sbt.version=1.5.8 | ||
sbt.version=1.8.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.