Gowin Vol.3 第2部第4章 リスト1

/**
* numberOfBitsPerChannel: チャネル当たりのビット数.ステレオ16ビットなら16を指定する
* numberOfCyclesPerChannel: 1チャネルあたりの1ワード転送に要するBCKサイクル数.モジュールの動作周波数 / サンプリング・レートを指定する
* defaultValue: 入力データが無い場合に出力する値. 通常は0で良い
*/
class I2sMaster(val numberOfBitsPerChannel: Int, val numberOfCyclesPerChannel: Int, val defaultValue: BigInt) extends Module {
if( numberOfBitsPerChannel > numberOfCyclesPerChannel) {
throw new IllegalArgumentException("numberOfBitsPerChannel must be less than or equal to numberOfCyclesPerChannel")
}

val io = IO(new Bundle {
val clockEnable = Input(Bool()) // 動作の間引きが必要ならclockEnableを下げる.通常は常にtrueで良い.
val dataIn = Flipped(Irrevocable(UInt((numberOfBitsPerChannel * 2).W))) // データの入力.numberOfBitsPerChannel * 2 の幅のストリーム
val wordSelect = Output(Bool()) // WS信号出力
val dataOut = Output(Bool()) // DIN信号出力
})

val data = RegInit(((defaultValue << numberOfBitsPerChannel) | defaultValue).U((numberOfBitsPerChannel*2).W))
val wordSelect = RegInit(true.B)
val dataOut = RegInit(false.B)
val counter = RegInit(0.U(log2Ceil(numberOfCyclesPerChannel).W)) // WSの切り換えまでのサイクル数を数えるカウンタ

val nextDataIn = Mux(io.dataIn.valid, io.dataIn.bits, Fill(2, defaultValue.U(numberOfBitsPerChannel.W))) // 次の入力データが無い場合はdefaultValueになる
val nextDataOut = data(numberOfBitsPerChannel * 2 - 1)

io.dataIn.ready := counter === 1.U && wordSelect && io.clockEnable // WSが1でcounterが1のときに次のデータを入力する
io.wordSelect := wordSelect
io.dataOut := dataOut

when( io.clockEnable ) {
when( io.dataIn.ready ) {
printf(p"counter: ${counter} set: ${Hexadecimal(nextDataIn)} out: ${nextDataOut}\n")
data := nextDataIn
} .elsewhen( (numberOfBitsPerChannel == numberOfCyclesPerChannel).B || (0.U < counter && counter <= numberOfBitsPerChannel.U) ) {
// WSの切り替わりからチャネルのビット数サイクル以内であれば,データをMSBから順に出力するように更新する
printf(p"counter: ${counter} shift: ${Hexadecimal(data << 1)} out: ${nextDataOut}\n")
data := data << 1
}

dataOut := data(numberOfBitsPerChannel * 2 - 1) // dataOutレジスタに次のデータをセット

when( counter === 0.U ) {
wordSelect := !wordSelect
counter := (numberOfCyclesPerChannel - 1).U
} .otherwise {
counter := counter - 1.U
}
}
}