Diplomacyを使ってOCPのバスを作成するプロジェクト。 バスの定義があってもまずはウィジェットが無いと始まらない。 OCPウィジェットのPatternPusherを作成する。
PatternPusherはOCPのマスター(OCPClientNode
)として働く。TileLinkにもPatternPusherがあるので、これを流用して改造を行う。とりあえず用意するのはWritePattern
, ReadPattern
, ReadExpectPattern
だ。
WritePattern
: OCPバスに対して書き込みを行う。ReadPattern
: OCPバスに対して読み込みを行う。ReadExpectPattern
: OCPバスに対して読み込みを行い、想定するデータと比較する。
case class WritePattern(address: BigInt, size: Int, data: BigInt) extends Pattern { require (0 <= data && data < (BigInt(1) << (8 << size))) def bits(edge: OCPEdgeOut) = edge.Write(0.U, address.U, size.U, (data << (8*(address % edge.manager.beatBytes).toInt)).U) } case class ReadPattern(address: BigInt, size: Int) extends Pattern { def bits(edge: OCPEdgeOut) = edge.Read(0.U, address.U, size.U) } case class ReadExpectPattern(address: BigInt, size: Int, data: BigInt) extends Pattern { def bits(edge: OCPEdgeOut) = edge.Read(0.U, address.U, size.U) override def dataIn = Some(data) }
次にOCPPaternPusher
の定義を行う。これはTLPatternPusher
と同様に作成する。
class OCPPatternPusher(name: String, pattern: Seq[Pattern])(implicit p: Parameters) extends LazyModule { val node = OCPClientNode(Seq(OCPClientPortParameters(Seq(OCPClientParameters(name = name))))) lazy val module = new LazyModuleImp(this) { val io = IO(new Bundle { val run = Input(Bool()) val done = Output(Bool()) }) ...
まず、OCPClientNode
型のノードを作成する。これがDiplomacyのノードにあたるものだ。OCPClientNode
およびOCPManagerNode
の定義は以下のようになっている。
chisel-hw/src/main/scala/ocp/Nodes.scala
trait OCPFormatNode extends FormatNode[OCPEdgeIn, OCPEdgeOut] case class OCPClientNode(portParams: Seq[OCPClientPortParameters])(implicit valName: ValName) extends SourceNode(OCPImp)(portParams) with OCPFormatNode case class OCPManagerNode(portParams: Seq[OCPManagerPortParameters])(implicit valName: ValName) extends SinkNode(OCPImp)(portParams) with OCPFormatNode
長ったらしくて難しいのだが、
- 基本は
SourceNode
およびSinkNode
から拡張しているということ。OCPFormatNode
をトレイトとして使っている。
SourceNode
およびSinkNode
の引数となっているOCPImp
は以下のように定義しており、ノードの定義となっている。今回は面倒なのでモニターの定義は省略した。
object OCPImp extends NodeImp[OCPClientPortParameters, OCPManagerPortParameters, OCPEdgeOut, OCPEdgeIn, OCPBundle] { def edgeO(pd: OCPClientPortParameters, pu: OCPManagerPortParameters, p: Parameters, sourceInfo: SourceInfo) = new OCPEdgeOut(pd, pu, p, sourceInfo) def edgeI(pd: OCPClientPortParameters, pu: OCPManagerPortParameters, p: Parameters, sourceInfo: SourceInfo) = new OCPEdgeIn (pd, pu, p, sourceInfo) def bundleO(eo: OCPEdgeOut) = OCPBundle(eo.bundle) def bundleI(ei: OCPEdgeIn) = OCPBundle(ei.bundle) def render(ei: OCPEdgeIn) = RenderedEdge(colour = "#000000" /* black */, label = (ei.manager.beatBytes * 8).toString) // override def monitor(bundle: OCPBundle, edge: OCPEdgeIn) { // val monitor = Module(edge.params(OCPMonitorBuilder)(OCPMonitorArgs(edge))) // monitor.io.in := bundle // } override def mixO(pd: OCPClientPortParameters, node: OutwardNode[OCPClientPortParameters, OCPManagerPortParameters, OCPBundle]): OCPClientPortParameters = pd.copy(clients = pd.clients.map { c => c.copy (nodePath = node +: c.nodePath) }) override def mixI(pu: OCPManagerPortParameters, node: InwardNode[OCPClientPortParameters, OCPManagerPortParameters, OCPBundle]): OCPManagerPortParameters = pu.copy(managers = pu.managers.map { m => m.copy (nodePath = node +: m.nodePath) }) }
edgeO
: 新しいOCPEdgeOutノードを作成する。edgiI
: 新しいOCPEdgeInノードを作成する。
ぶっちゃけこれ以上は良く分からない。ひたすらTileLinkのノードを置き換えていっただけである...
次にOCPのノードの中で最も基本となるバッファを定義する。バッファの名前はOCPBuffer
である。
class OCPBuffer( a: BufferParams, c: BufferParams, d: BufferParams)(implicit p: Parameters) extends LazyModule { def this(ace: BufferParams)(implicit p: Parameters) = this(ace, ace, ace) def this()(implicit p: Parameters) = this(BufferParams.default) val node = new OCPBufferNode(a, c, d) lazy val module = new LazyModuleImp(this) { (node.in zip node.out) foreach { case ((in, edgeIn), (out, edgeOut)) => out.cmd <> a(in .cmd ) out.data <> c(in .data) in .resp <> d(out.resp) } } }
こちらもDiplomacyを使って定義している。中身は単純にノードの入力とノードの出力をすべてつなぎ合わせるだけである。複数ノードが接続されている場合はそれぞれペアにしてつないでいく。
次にオブジェクトの方を定義する。
object OCPBuffer { def apply() (implicit p: Parameters): OCPNode = apply(BufferParams.default) def apply(abcde: BufferParams) (implicit p: Parameters): OCPNode = apply(abcde, abcde, abcde) def apply( a: BufferParams, c: BufferParams, d: BufferParams)(implicit p: Parameters): OCPNode = { val buffer = LazyModule(new OCPBuffer(a, c, d)) buffer.node } ...
こちらも単純にapply()
を定義している。これでOCPバッファが接続定義できるようになった。