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

/**
* backPressureMaxBufferSize: バッファ使用状況通知用のバッファの最大エントリ数.通知しない場合はNone,通知する場合はSome(n)を指定
*/
class UdpStreamWriter(config: EthernetServiceConfig = EthernetServiceConfig.default(), streamWidth: Int = 1, backPressureMaxBufferSize: Option[Int] = None) extends Module {
val io = IO(new Bundle {
val port = new UdpServicePort(streamWidth)
val dataReceived = Irrevocable(MultiByteSymbol(streamWidth))
val backPressure = backPressureMaxBufferSize match {
case Some(maxBufferSize) => Some(Flipped(Irrevocable(UInt(log2Ceil(maxBufferSize + 1).W))))
case None => None
}
})

object State extends ChiselEnum {
val Idle, SendBackPressurePayload = Value
}

val state = RegInit(State.Idle)

val udpReceiveContextReady = RegInit(false.B)
udpReceiveContextReady := true.B
io.port.udpReceiveContext.ready := udpReceiveContextReady

// 受信データ出力ポートはそのままUDP Serviceのデータ受信ストリームをつなぐだけ
io.dataReceived <> io.port.udpReceiveData

backPressureMaxBufferSize match {
case Some(maxBufferSize) => { // バッファ使用状況通知あり
val backPressurePort = io.backPressure.get
val backPressure = RegInit(0.U(log2Ceil(maxBufferSize + 1).W))
val backPressureDataSizeBits = log2Ceil(maxBufferSize + 1)
val backPressureDataSizeBytes = (backPressureDataSizeBits + 7) / 8 // Number of bytes required to store back pressure data.
val backPressurePayloadSize = 1 + backPressureDataSizeBytes * 2 // Number of bytes of back pressure payload.length(1byte) + current buffer usage(n bytes) + max buffer size(n bytes)
val sendPortValid = RegInit(false.B)

// 最後にデータを受信したときのコンテキストから,バッファ使用状況返信用のコンテキストを作る
val sendContext = RegInit(UdpContext.default())
val sendContextValid = RegInit(false.B)
when( io.port.udpReceiveContext.fire && state === State.Idle ) {
sendContext.dataLength := (8 + backPressurePayloadSize).U
sendContext.sourceAddress := io.port.udpReceiveContext.bits.destinationAddress
sendContext.sourcePort := io.port.udpReceiveContext.bits.destinationPort
sendContext.sourceMacAddress := io.port.udpReceiveContext.bits.destinationMacAddress
sendContext.destinationAddress := io.port.udpReceiveContext.bits.sourceAddress
sendContext.destinationPort := io.port.udpReceiveContext.bits.sourcePort
sendContext.destinationMacAddress := io.port.udpReceiveContext.bits.sourceMacAddress
sendContextValid := true.B
}

when(io.port.udpSendContext.fire) {
sendPortValid := false.B
}

// 通知用ペイロードの構造
// 0 : payload size
// 1 - backPressureDataSizeBytes : current buffer usage from io.backPressure
// backPressureDataSizeBytes + 1 - backPressureDataSizeBytes * 2 : max buffer size
val payloadIndex = RegInit(0.U(log2Ceil(backPressurePayloadSize + 1).W))
val backPressureValue = Cat(0.U((8 - backPressureDataSizeBits%8).W), backPressure)
val backPressureMaxValue = maxBufferSize.U((backPressureDataSizeBytes * 8).W)

io.port.udpSendData.valid := state === State.SendBackPressurePayload
io.port.udpSendData.bits.keep := 1.U
val sendData = WireDefault(0.U(8.W))
io.port.udpSendData.bits.data := sendData
// 現在の送信オクテット位置 (payloadIndex) から,送信するオクテットを決定
when( payloadIndex === 0.U ) {
sendData := backPressurePayloadSize.U
}
for(i <- 0 to backPressureDataSizeBytes - 1) {
when( payloadIndex === (i + 1).U ) {
sendData := backPressureValue(8 * (backPressureDataSizeBytes - i) - 1, 8 * (backPressureDataSizeBytes - i - 1))
}
when( payloadIndex === (i + 1 + backPressureDataSizeBytes).U ) {
sendData := backPressureMaxValue(8 * (backPressureDataSizeBytes - i) - 1, 8 * (backPressureDataSizeBytes - i - 1))
}
}
// バッファ使用状況通知データの最終オクテットの場合はlastをアサート
io.port.udpSendData.bits.last := payloadIndex === (backPressurePayloadSize - 1).U
backPressurePort.ready := state === State.Idle && sendContextValid && !sendPortValid
switch(state) {
is(State.Idle) {
// バッファ使用状況データがある場合,送信を開始
when( backPressurePort.fire ) {
backPressure := backPressurePort.bits
state := State.SendBackPressurePayload
payloadIndex := 0.U
sendPortValid := true.B // Send context
}
}
is(State.SendBackPressurePayload) { // 送信中
when( io.port.udpSendData.fire ) {
payloadIndex := payloadIndex + 1.U
when( payloadIndex === (backPressurePayloadSize - 1).U ) {
state := State.Idle
}
}
}
}
io.port.udpSendContext.valid := sendPortValid
io.port.udpSendContext.bits := sendContext
}
case None => {
// バッファ使用状況通知なし
io.port.udpSendContext.valid := false.B
io.port.udpSendContext.bits := UdpContext.default()
io.port.udpSendData.valid := false.B
io.port.udpSendData.bits.data := 0.U
io.port.udpSendData.bits.keep := 0.U
io.port.udpSendData.bits.last := false.B
}
}
}