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

class UdpGpio(config: EthernetServiceConfig = EthernetServiceConfig.default(), streamWidth: Int = 1, numInputBits: Int = 8, numOutputBits: Int = 8) extends Module {
val io = IO(new Bundle {
val port = new UdpServicePort(streamWidth)
val gpioIn = Input(UInt(numInputBits.W))
val gpioOut = Output(UInt(numOutputBits.W))
})

object State extends ChiselEnum {
val Idle, Receiving, Sending = Value
}

val state = RegInit(State.Idle)
val udpContext = Reg(new UdpContext)

val udpReceiveContextReady = WireDefault(false.B)
io.port.udpReceiveContext.ready := udpReceiveContextReady
val udpReceiveDataReady = WireDefault(false.B)
io.port.udpReceiveData.ready := udpReceiveDataReady

val udpSendContextValid = RegInit(false.B)
io.port.udpSendContext.valid := udpSendContextValid
io.port.udpSendContext.bits := udpContext
val udpSendDataValid = RegInit(false.B)
val udpSendData = RegInit(0.U(8.W))
val udpSendDataLast = RegInit(false.B)
io.port.udpSendData.valid := udpSendDataValid
io.port.udpSendData.bits.data := udpSendData
io.port.udpSendData.bits.keep := 1.U
io.port.udpSendData.bits.last := udpSendDataLast

when(udpSendContextValid && io.port.udpSendContext.ready) {
udpSendContextValid := false.B
}
when(udpSendDataValid && io.port.udpSendData.ready) {
udpSendDataValid := false.B
}
// ここでの `Input` `Output` はGPIOの入出力の意味 (UDPの入出力ではない) ので注意
val numInputBytes = (numInputBits + 7) / 8 // GPIO入力バイト数 (=UDP送信バイト数)
val numOutputBytes = (numOutputBits + 7) / 8 // GPIO出力バイト数 (=UDP受信バイト数)
val gpioIn = RegInit(VecInit(Seq.fill(numInputBytes)(0.U(8.W)))) // GPIO入力ビット列
val gpioOut = RegInit(VecInit(Seq.fill(numOutputBytes)(0.U(8.W)))) // GPIO出力ビット列
val bytesInput = RegInit(0.U(log2Ceil(numInputBytes + 1).W)) // 送信済みバイト数
val bytesOutput = RegInit(0.U(log2Ceil(numOutputBytes + 1).W)) // 受信済みバイト数

io.gpioOut := Cat(gpioOut.reverse)

switch(state) {
is(State.Idle) {
udpReceiveContextReady := !udpSendContextValid // 送信完了するまで次の受信データは受け取らない
when( !udpSendContextValid && io.port.udpReceiveContext.valid ) {
// UDPのコンテキストを送信元・送信先を入れ替えて保持する
udpContext.dataLength := (8 + numInputBytes).U
udpContext.sourceAddress := io.port.udpReceiveContext.bits.destinationAddress
udpContext.sourcePort := io.port.udpReceiveContext.bits.destinationPort
udpContext.sourceMacAddress := io.port.udpReceiveContext.bits.destinationMacAddress
udpContext.destinationAddress := io.port.udpReceiveContext.bits.sourceAddress
udpContext.destinationPort := io.port.udpReceiveContext.bits.sourcePort
udpContext.destinationMacAddress := io.port.udpReceiveContext.bits.sourceMacAddress
udpSendContextValid := true.B
bytesInput := 0.U
bytesOutput := 0.U
gpioIn := ((0 to numInputBytes - 1).map(i => io.gpioIn(((i+1)*8).min(numInputBits) - 1, i*8)))
state := State.Receiving
}
}
is(State.Receiving) { // 受信中
udpReceiveDataReady := true.B
when( io.port.udpReceiveData.valid ) {
when(bytesOutput < numOutputBytes.U) { // GPIO出力バイト数まではデータを受信して詰める
gpioOut(bytesOutput) := io.port.udpReceiveData.bits.data
bytesOutput := bytesOutput + 1.U
}
when(io.port.udpReceiveData.bits.last) { // ペイロードの終端なので送信処理に遷移
state := State.Sending
}
}
}
is(State.Sending) { // 送信中
when( !udpSendDataValid || io.port.udpSendData.ready ) {
udpSendData := gpioIn(bytesInput)
bytesInput := bytesInput + 1.U
udpSendDataValid := true.B
udpSendDataLast := false.B
when( bytesInput === (numInputBytes - 1).U) { // 終端まで送信したので待機状態に戻る
udpSendDataLast := true.B
state := State.Idle
}
}
}
}
}