FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

Diplomacyを使ってOCPバスを作成する(11. インタフェースのデバッグ)

Diplomacyを使ったOCPプロトコルの作成、Bufferのデバッグを行っているが、どうも良く分からないところがある。

例えばBufferのインタフェースにQueueを挿入せずにそのまま接続しようとした場合、以下のようなエラーが発生してしまう。

val ram0 = LazyModule(new OCPRAM(AddressSet(0x000, 0x3ff))) val xbar = LazyModule(new OCPBuffer(BufferCmdParams.none, BufferParams.none)) ​ ram0.node := xbar.node := pusher.node [error] Caused by: chisel3.internal.ChiselException: Connection between left (CmdReadyIO(Wire in OCPBuffer)) and source (CmdReadyIO(Wire in OCPBuffer)) failed @.accept: Locally unclear whether Left or Right (both internal) ううん、これは何なのかよくわからない。Data.scalaにおいて、Biconnect.connectが呼ばれているのは分かった。

private[chisel3] def bulkConnect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = { // scalastyle:ignore line.size.limit ... BiConnect.connect(sourceInfo, connectCompileOptions, this, that, Builder.referenceUserModule) // This function checks if element-level connection operation allowed. // Then it either issues it or throws the appropriate exception. def elemConnect(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, left: Element, right: Element, context_mod: RawModule): Unit = { // scalastyle:ignore line.size.limit cyclomatic.complexity method.length import BindingDirection.{Internal, Input, Output} // Using extensively so import these // If left or right have no location, assume in context module // This can occur if one of them is a literal, unbound will error previously val left_mod: BaseModule = left.topBinding.location.getOrElse(context_mod) val right_mod: BaseModule = right.topBinding.location.getOrElse(context_mod) ​ val left_direction = BindingDirection.from(left.topBinding, left.direction) val right_direction = BindingDirection.from(right.topBinding, right.direction) ​ // CASE: Context is same module as left node and right node is in a child module if( (left_mod == context_mod) && (right_mod.parent.map( == context_mod).getOrElse(false)) ) { // Thus, right node better be a port node and thus have a direction hint ((left_direction, right_direction): @unchecked) match { // CURRENT MOD CHILD MOD case (Input, Input) => issueConnectL2R(left, right) case (Internal, Input) => issueConnectL2R(left, right) ​ case (Output, Output) => issueConnectR2L(left, right) case (Internal, Output) => issueConnectR2L(left, right) ​ case (Input, Output) => throw BothDriversException case (Output, Input) => throw NeitherDriverException case (_, Internal) => throw UnknownRelationException } } ​ なぜかleft_directionとright_directionがどちらもInternalになっており、どうしても接続できない。

TileLinkで同様の試行をしても問題が発生しない。良く分からないのでTileLinkと全く同じバスを作って内部の接続状況を観察しよう。

いろいろ見ていると、どうやらBulkConnect内において以下の条件が違うらしい。

private[chisel3] def bulkConnect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = { // scalastyle:ignore line.size.limit if (connectCompileOptions.checkSynthesizable) { ... } else { this legacyConnect that } ​ 最終的に、compileOptionsの違いだった。

chiselFrontend/src/main/scala/chisel3/Data.scala

final def <> (that: Data)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Unit = this.bulkConnect(that)(sourceInfo, connectionCompileOptions) // scalastyle:ignore line.size.limit compileOptionsはimplicitなので探すのが大変なのだが、compatibilityに挿入されているらしい。やはりCompatibilityを一度介さないとだめなのか。

src/main/scala/chisel3/compatibility.scala

package object Chisel { // scalastyle:ignore package.object.name number.of.types number.of.methods ... implicit val defaultCompileOptions = chisel3.ExplicitCompileOptions.NotStrict そうしてchisel3パッケージではなくChiselパッケージを使うことで動作した。