From dabfc869836f51cb3992ac501189750c6a0bda41 Mon Sep 17 00:00:00 2001 From: Ruige Lee Date: Wed, 9 Apr 2025 17:29:57 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=91=E9=80=81=E5=8D=95=E5=85=83=20?= =?UTF-8?q?=E6=8E=A5=E6=94=B6=E5=8D=95=E5=85=83=E4=BB=A3=E7=A0=81=E6=94=B6?= =?UTF-8?q?=E5=B0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/scala/backBoard/ebus/crc32.scala | 43 +++++++- .../scala/backBoard/shin/SlaveParser.scala | 9 +- .../scala/backBoard/shin/SlaveRecParser.scala | 98 ++++++++++++++----- .../scala/backBoard/shin/SlaveSendGen.scala | 88 +++++++++++------ 4 files changed, 179 insertions(+), 59 deletions(-) diff --git a/src/main/scala/backBoard/ebus/crc32.scala b/src/main/scala/backBoard/ebus/crc32.scala index 8f46cf7..7c51fea 100644 --- a/src/main/scala/backBoard/ebus/crc32.scala +++ b/src/main/scala/backBoard/ebus/crc32.scala @@ -98,9 +98,48 @@ class crc32_8() extends Module{ // val LFSR_POLY = "h4c11db7".U + val lfsr_c = for( i <- 0 until 32 ) yield Wire(Bool()) + val lfsr_q = for( i <- 0 until 32 ) yield RegEnable( lfsr_c(i), true.B, io.isEnable ) + io.crc := Cat( lfsr_q ) + + val dataIn = Wire(UInt(8.W)) + dataIn := Cat(io.dataIn.asBools) + + + lfsr_c( 0) := lfsr_q(24) ^ lfsr_q(30) ^ dataIn(0) ^ dataIn(6) + lfsr_c( 1) := lfsr_q(24) ^ lfsr_q(25) ^ lfsr_q(30) ^ lfsr_q(31) ^ dataIn(0) ^ dataIn(1) ^ dataIn(6) ^ dataIn(7) + lfsr_c( 2) := lfsr_q(24) ^ lfsr_q(25) ^ lfsr_q(26) ^ lfsr_q(30) ^ lfsr_q(31) ^ dataIn(0) ^ dataIn(1) ^ dataIn(2) ^ dataIn(6) ^ dataIn(7) + lfsr_c( 3) := lfsr_q(25) ^ lfsr_q(26) ^ lfsr_q(27) ^ lfsr_q(31) ^ dataIn(1) ^ dataIn(2) ^ dataIn(3) ^ dataIn(7) + lfsr_c( 4) := lfsr_q(24) ^ lfsr_q(26) ^ lfsr_q(27) ^ lfsr_q(28) ^ lfsr_q(30) ^ dataIn(0) ^ dataIn(2) ^ dataIn(3) ^ dataIn(4) ^ dataIn(6) + lfsr_c( 5) := lfsr_q(24) ^ lfsr_q(25) ^ lfsr_q(27) ^ lfsr_q(28) ^ lfsr_q(29) ^ lfsr_q(30) ^ lfsr_q(31) ^ dataIn(0) ^ dataIn(1) ^ dataIn(3) ^ dataIn(4) ^ dataIn(5) ^ dataIn(6) ^ dataIn(7) + lfsr_c( 6) := lfsr_q(25) ^ lfsr_q(26) ^ lfsr_q(28) ^ lfsr_q(29) ^ lfsr_q(30) ^ lfsr_q(31) ^ dataIn(1) ^ dataIn(2) ^ dataIn(4) ^ dataIn(5) ^ dataIn(6) ^ dataIn(7) + lfsr_c( 7) := lfsr_q(24) ^ lfsr_q(26) ^ lfsr_q(27) ^ lfsr_q(29) ^ lfsr_q(31) ^ dataIn(0) ^ dataIn(2) ^ dataIn(3) ^ dataIn(5) ^ dataIn(7) + lfsr_c( 8) := lfsr_q( 0) ^ lfsr_q(24) ^ lfsr_q(25) ^ lfsr_q(27) ^ lfsr_q(28) ^ dataIn(0) ^ dataIn(1) ^ dataIn(3) ^ dataIn(4) + lfsr_c( 9) := lfsr_q( 1) ^ lfsr_q(25) ^ lfsr_q(26) ^ lfsr_q(28) ^ lfsr_q(29) ^ dataIn(1) ^ dataIn(2) ^ dataIn(4) ^ dataIn(5) + lfsr_c(10) := lfsr_q( 2) ^ lfsr_q(24) ^ lfsr_q(26) ^ lfsr_q(27) ^ lfsr_q(29) ^ dataIn(0) ^ dataIn(2) ^ dataIn(3) ^ dataIn(5) + lfsr_c(11) := lfsr_q( 3) ^ lfsr_q(24) ^ lfsr_q(25) ^ lfsr_q(27) ^ lfsr_q(28) ^ dataIn(0) ^ dataIn(1) ^ dataIn(3) ^ dataIn(4) + lfsr_c(12) := lfsr_q( 4) ^ lfsr_q(24) ^ lfsr_q(25) ^ lfsr_q(26) ^ lfsr_q(28) ^ lfsr_q(29) ^ lfsr_q(30) ^ dataIn(0) ^ dataIn(1) ^ dataIn(2) ^ dataIn(4) ^ dataIn(5) ^ dataIn(6) + lfsr_c(13) := lfsr_q( 5) ^ lfsr_q(25) ^ lfsr_q(26) ^ lfsr_q(27) ^ lfsr_q(29) ^ lfsr_q(30) ^ lfsr_q(31) ^ dataIn(1) ^ dataIn(2) ^ dataIn(3) ^ dataIn(5) ^ dataIn(6) ^ dataIn(7) + lfsr_c(14) := lfsr_q( 6) ^ lfsr_q(26) ^ lfsr_q(27) ^ lfsr_q(28) ^ lfsr_q(30) ^ lfsr_q(31) ^ dataIn(2) ^ dataIn(3) ^ dataIn(4) ^ dataIn(6) ^ dataIn(7) + lfsr_c(15) := lfsr_q( 7) ^ lfsr_q(27) ^ lfsr_q(28) ^ lfsr_q(29) ^ lfsr_q(31) ^ dataIn(3) ^ dataIn(4) ^ dataIn(5) ^ dataIn(7) + lfsr_c(16) := lfsr_q( 8) ^ lfsr_q(24) ^ lfsr_q(28) ^ lfsr_q(29) ^ dataIn(0) ^ dataIn(4) ^ dataIn(5) + lfsr_c(17) := lfsr_q( 9) ^ lfsr_q(25) ^ lfsr_q(29) ^ lfsr_q(30) ^ dataIn(1) ^ dataIn(5) ^ dataIn(6) + lfsr_c(18) := lfsr_q(10) ^ lfsr_q(26) ^ lfsr_q(30) ^ lfsr_q(31) ^ dataIn(2) ^ dataIn(6) ^ dataIn(7) + lfsr_c(19) := lfsr_q(11) ^ lfsr_q(27) ^ lfsr_q(31) ^ dataIn(3) ^ dataIn(7) + lfsr_c(20) := lfsr_q(12) ^ lfsr_q(28) ^ dataIn(4) + lfsr_c(21) := lfsr_q(13) ^ lfsr_q(29) ^ dataIn(5) + lfsr_c(22) := lfsr_q(14) ^ lfsr_q(24) ^ dataIn(0) + lfsr_c(23) := lfsr_q(15) ^ lfsr_q(24) ^ lfsr_q(25) ^ lfsr_q(30) ^ dataIn(0) ^ dataIn(1) ^ dataIn(6) + lfsr_c(24) := lfsr_q(16) ^ lfsr_q(25) ^ lfsr_q(26) ^ lfsr_q(31) ^ dataIn(1) ^ dataIn(2) ^ dataIn(7) + lfsr_c(25) := lfsr_q(17) ^ lfsr_q(26) ^ lfsr_q(27) ^ dataIn(2) ^ dataIn(3) + lfsr_c(26) := lfsr_q(18) ^ lfsr_q(24) ^ lfsr_q(27) ^ lfsr_q(28) ^ lfsr_q(30) ^ dataIn(0) ^ dataIn(3) ^ dataIn(4) ^ dataIn(6) + lfsr_c(27) := lfsr_q(19) ^ lfsr_q(25) ^ lfsr_q(28) ^ lfsr_q(29) ^ lfsr_q(31) ^ dataIn(1) ^ dataIn(4) ^ dataIn(5) ^ dataIn(7) + lfsr_c(28) := lfsr_q(20) ^ lfsr_q(26) ^ lfsr_q(29) ^ lfsr_q(30) ^ dataIn(2) ^ dataIn(5) ^ dataIn(6) + lfsr_c(29) := lfsr_q(21) ^ lfsr_q(27) ^ lfsr_q(30) ^ lfsr_q(31) ^ dataIn(3) ^ dataIn(6) ^ dataIn(7) + lfsr_c(30) := lfsr_q(22) ^ lfsr_q(28) ^ lfsr_q(31) ^ dataIn(4) ^ dataIn(7) + lfsr_c(31) := lfsr_q(23) ^ lfsr_q(29) ^ dataIn(5) + - io.crc := "ha1b2c3d4".U - println("Warning, Empty Module Crc32_8!\n") } diff --git a/src/main/scala/backBoard/shin/SlaveParser.scala b/src/main/scala/backBoard/shin/SlaveParser.scala index 813f716..e70b4fc 100644 --- a/src/main/scala/backBoard/shin/SlaveParser.scala +++ b/src/main/scala/backBoard/shin/SlaveParser.scala @@ -46,21 +46,20 @@ abstract class SlaveParserBase extends Module{ - val devIndex = Reg(UInt(14.W)) //当前设备索引 val downRecParser = Module(new SlaveRecParser) val upRecParser = Module(new SlaveRecParser) - val downSendGen = Module(new SlaveSendGen) val upSendGen = Module(new SlaveSendGen) + val devIndex = Reg(UInt(14.W)) //当前设备索引 - - val downSinkSel = Reg(Bool()) - val upSinkSel = Reg(Bool()) + // 认为只有下行到下行,上行到上行模式,探针是独立发起操作 + // val downSinkSel = Reg(Bool()) + // val upSinkSel = Reg(Bool()) val downFrameInfo = Reg(new FrameHeader) val downSubMsgInfo = for( 0 until 2 ) yield { Reg(new SubMsgHeader) } diff --git a/src/main/scala/backBoard/shin/SlaveRecParser.scala b/src/main/scala/backBoard/shin/SlaveRecParser.scala index 0329f55..555cbf6 100644 --- a/src/main/scala/backBoard/shin/SlaveRecParser.scala +++ b/src/main/scala/backBoard/shin/SlaveRecParser.scala @@ -24,21 +24,41 @@ class SlaveRecParserIO extends Module{ class SlaveRecParser extends Module{ val io: SlaveRecParserIO = IO(new SlaveRecParserIO) + val crc = Module(new crc32_8) + + crc.io.isEnable := + stateCurr === STATE_HEADER | + stateCurr === STATE_PAYLOAD_INDEX_STYLE | + stateCurr === STATE_PAYLOAD_ADDR | + stateCurr === STATE_PAYLOAD_OP_LENGTH | + stateCurr === STATE_PAYLOAD_WORKCNT | + stateCurr === STATE_PAYLOAD_DATA + + crc.io.dataIn := io.rec.bits.tdata + + crc.reset := reset.asBool | stateCurr === STATE_IDLE + + val crcData = Reg(UInt(32.W)) + val isCrcPass = crc.io.crc === Cat( crcData << 8, io.rec.bits.tdata ) + + when( stateCurr === STATE_CRC ){ + crcData := Cat( crcData << 8, io.rec.bits.tdata ) + } val FRAME_STYLE_COMMON = 0.U val FRAME_STYLE_PROBE = 1.U - val STATE_IDLE = 0.U //空闲 - val STATE_HEADER = 1.U //长度/ /保留2bits //包类型 - val STATE_PAYLOAD_INDEX_STYLE = 2.U //子报文设备索引 //子报文类型 - val STATE_PAYLOAD_ADDR = 3.U //子报文地址 + val STATE_IDLE = 0.U //空闲 + val STATE_HEADER = 1.U //长度/ /保留2bits //包类型 + val STATE_PAYLOAD_INDEX_STYLE = 2.U //子报文设备索引 //子报文类型 + val STATE_PAYLOAD_ADDR = 3.U //子报文地址 val STATE_PAYLOAD_OP_LENGTH = 4.U //子报文命令类型 //子报文长度 - val STATE_PAYLOAD_WORKCNT = 5.U //子报文工作计数 - val STATE_PAYLOAD_DATA = 6.U //子报文数据 - val STATE_CRC = 7.U //CRC校验 - val STATE_ERROR = 8.U // - val STATE_WAIT = 9.U + val STATE_PAYLOAD_WORKCNT = 5.U //子报文工作计数 + val STATE_PAYLOAD_DATA = 6.U //子报文数据 + val STATE_CRC = 7.U //CRC校验 + val STATE_ERROR = 8.U // + val STATE_WAIT = 9.U val stateNext = Wire(UInt(4.W)); io.stateNext := stateNext @@ -46,18 +66,51 @@ class SlaveRecParser extends Module{ val lengthCnt = Reg(UInt(11.W)) //动态计数器 val frameLenCnt = Reg(UInt(12.W)) //帧长度计数 - val isCrcPass: Bool - val isShortRx: Bool //接收长度小于预期 - val frameLenAim = Reg(UInt(12.W)); io.frameLenAim := frameLenAim //帧长度目标 - val frameStyle = Reg(UInt(2.W)); io.frameStyle := frameStyle //包类型 - val devIndex = Reg(UInt(14.W)); io.subMsgReq.devIndex := devIndex // 子报文中设备索引 - val style = Reg(UInt(2.W)); io.subMsgReq.style := style // 子报文类型 - val address = Reg(UInt(16.W)); io.subMsgReq.address := address //子报文地址 - val operator = Reg(UInt(4.W)); io.subMsgReq.operator := operator //子报文命令类型 - val length = Reg(UInt(12.W)); io.subMsgReq.length := length //子报文负载长度 - val workCnt = Reg(UInt(16.W)); io.subMsgReq.workCnt := workCnt //子报文工作计数 + + val isShortRx = ~io.rec.valid //接收长度小于预期 + + + + + val frameLenAim = Reg(UInt(12.W)); io.frameReq.bits.frameLenAim := frameLenAim //帧长度目标 + val frameStyle = Reg(UInt(2.W)); io.frameReq.bits.frameStyle := frameStyle //包类型 + io.frameReq.valid := (stateCurr === STATE_HEADER) & (stateNext === STATE_PAYLOAD_INDEX_STYLE) + + val devIndex = Reg(UInt(14.W)); io.subMsgReq.bits.devIndex := devIndex // 子报文中设备索引 + val style = Reg(UInt(2.W)); io.subMsgReq.bits.style := style // 子报文类型 + val address = Reg(UInt(16.W)); io.subMsgReq.bits.address := address //子报文地址 + val operator = Reg(UInt(4.W)); io.subMsgReq.bits.operator := operator //子报文命令类型 + val length = Reg(UInt(12.W)); io.subMsgReq.bits.length := length //子报文负载长度 + val workCnt = Reg(UInt(16.W)); io.subMsgReq.bits.workCnt := workCnt //子报文工作计数 + io.subMsgReq.valid := (stateCurr === STATE_PAYLOAD_WORKCNT) & (stateNext === STATE_PAYLOAD_DATA) + + when( stateCurr === STATE_IDLE ){ + lengthCnt := 0.U + } .elsewhen( stateCurr =/= stateNext ){ + lengthCnt := 0.U + } .otherwise{ + lengthCnt := lengthCnt + 1.U + } + + when( stateCurr === STATE_IDLE || stateCurr === STATE_HEADER ){ + frameLenCnt := 0.U + } .elsewhen( + ( stateCurr === STATE_PAYLOAD_INDEX_STYLE ) || + ( stateCurr === STATE_PAYLOAD_ADDR ) || + ( stateCurr === STATE_PAYLOAD_OP_LENGTH ) || + ( stateCurr === STATE_PAYLOAD_WORKCNT ) || + ( stateCurr === STATE_PAYLOAD_DATA ) + ){ + frameLenCnt := frameLenCnt + 1.U + } + + io.data.valid := stateCurr === STATE_PAYLOAD_DATA + io.data.bits := io.rec.bits + assert( ~(io.data.valid & ~io.data.ready), "Assert Failed! Receive Data was recected by fifo!" ) + + when( stateCurr === STATE_HEADER & lengthCnt === 0.U ){ @@ -123,11 +176,10 @@ class SlaveRecParser extends Module{ Mux( frameLenCnt === frameLenAim, STATE_CRC, STATE_PAYLOAD_INDEX_STYLE ), STATE_PAYLOAD_DATA ) ), - ( stateCurr === STATE_CRC ) -> Mux( isShortRx, STATE_ERROR, - Mux( lengthCnt === (4 - 1).U, Mux( isCrcPass, STATE_IDLE, STATE_ERROR), STATE_CRC ) - ), + ( stateCurr === STATE_CRC ) -> + Mux( lengthCnt === (4 - 1).U, Mux( isCrcPass, STATE_IDLE, STATE_ERROR), Mux( isShortRx, STATE_ERROR, STATE_CRC ) ), ( stateCurr === STATE_ERROR ) -> Mux( io.resp, STATE_IDLE, STATE_ERROR ), - ( stateCurr === STATE_WAIT ) -> Mux( io.resp, STATE_IDLE, STATE_ERROR ) + ( stateCurr === STATE_WAIT ) -> Mux( io.resp, STATE_IDLE, STATE_WAIT ) )) diff --git a/src/main/scala/backBoard/shin/SlaveSendGen.scala b/src/main/scala/backBoard/shin/SlaveSendGen.scala index 560da95..b5a2ccb 100644 --- a/src/main/scala/backBoard/shin/SlaveSendGen.scala +++ b/src/main/scala/backBoard/shin/SlaveSendGen.scala @@ -12,24 +12,37 @@ class SubMsgTxInfo extends SubMsgHeader{ class SlaveSendGenIO extends Bundle{ val data = for( 0 until 2 ) yield { Flipped( Decoupled(UInt(8.W)) ) } - // val stateNext = Output(UInt(4.W)) - // val stateCurr = Output(UInt(4.W)) + val frameReq = Flipped(Decoupled( new FrameHeader )) val subMsgReq = Flipped(Decoupled( new SubMsgHeader )) val send = Decoupled(new AXIS_Bundle(8)) + val isError = Output(Bool()) } class SlaveSendGen extends Module{ val io: SlaveSendGenIO = IO(new SlaveSendGenIO) + val crc = Module(new crc32_8) + + crc.io.isEnable := + stateCurr === STATE_FRAME_HEADER | + stateCurr === STATE_SUBMSG_HEADER | + stateCurr === STATE_SUBMSG_DATA + + crc.io.dataIn := tdata_submsg_data + + crc.reset := reset.asBool | stateCurr === STATE_IDLE + + + val frameInfo = Reg(new FrameHeader) - val subMsgInfoPing = Reg(new SubMsgHeader) - val subMsgInfoPong = Reg(new SubMsgHeader) + val subMsgInfo = for( 0 until 2 ) yield { Reg(new SubMsgHeader) } + val isPingFill = RegInit(false.B) val isPingEmit = RegInit(false.B) @@ -40,55 +53,71 @@ class SlaveSendGen extends Module{ when( io.subMsgReq.fire ){ when( isPingFill ){ - subMsgInfoPing := io.subMsgReq.bits + subMsgInfo(0) := io.subMsgReq.bits } .otherwise{ - subMsgInfoPong := io.subMsgReq.bits + subMsgInfo(1) := io.subMsgReq.bits } } - val lengthCnt = Reg(UInt(12.W)) + val lengthCnt = Reg(UInt(12.W)) + val frameLenCnt = Reg(UInt(12.W)) + + when( stateCurr === STATE_IDLE || stateCurr === STATE_FRAME_HEADER ){ + frameLenCnt := 0.U + } .elsewhen( stateCurr === STATE_SUBMSG_HEADER || stateCurr === STATE_SUBMSG_DATA ){ + frameLenCnt := frameLenCnt + 1.U + } + + when( stateCurr === STATE_IDLE || stateCurr =/= stateNext ){ + lengthCnt := 0.U + } .otherwise{ + lengthCnt := lengthCnt + 1.U + } - - val subMsgInfoSel = Mux( isPingEmit, subMsgInfoPing, subMsgInfoPong ) + val subMsgInfoSel = Mux( isPingEmit, subMsgInfo(0), subMsgInfo(1) ) val STATE_IDLE = 0.U //空闲 - val STATE_FRAME_HEADER = 1.U //长度/ /保留2bits //包类型 - val STATE_SUBMSG_HEADER = 2.U - val STATE_SUBMSG_DATA = 3.U + val STATE_FRAME_HEADER = 1.U //长度/ /保留2bits //包类型 + val STATE_SUBMSG_HEADER = 2.U + val STATE_SUBMSG_DATA = 3.U val STATE_CRC = 4.U //CRC校验 val STATE_ERROR = 5.U - val STATE_WAIT = 6.U + // val STATE_WAIT = 6.U - val stateNext = Wire(UInt(4.W)) - val stateCurr = RegNext(stateNext, STATE_IDLE(4.W)) + val stateNext = Wire(UInt(3.W)) + val stateCurr = RegNext(stateNext, STATE_IDLE(3.W)) stateNext := Mux1H(Seq( (stateCurr === STATE_IDLE) -> Mux( io.frameReq.fire, STATE_FRAME_HEADER, STATE_IDLE ), (stateCurr === STATE_FRAME_HEADER) -> Mux( lengthCnt === (2-1).U, Mux( io.subMsgReq.valid, STATE_SUBMSG_HEADER, STATE_ERROR ), STATE_FRAME_HEADER), (stateCurr === STATE_SUBMSG_HEADER) -> Mux( lengthCnt === (64-1).U, STATE_SUBMSG_DATA, STATE_SUBMSG_HEADER), (stateCurr === STATE_SUBMSG_DATA) -> - Mux( lengthCnt === subMsgInfoSel.subMsg_length-1.U, //已经发送完成该子报文 + Mux( lengthCnt === subMsgInfoSel.length-1.U, //已经发送完成该子报文 Mux( frameLenCnt === frameInfo.lengthCnt, STATE_CRC, //主报文长度达到, 进CRC Mux( io.subMsgReq.valid, STATE_SUBMSG_HEADER, STATE_ERROR ) ), //主报文长度未到,进下一个子报文 STATE_SUBMSG_DATA ), (stateCurr === STATE_CRC) -> Mux( lengthCnt === (4-1).U, STATE_IDLE, STATE_CRC), - (stateCurr === STATE_ERROR) -> - (stateCurr === STATE_WAIT) -> + (stateCurr === STATE_ERROR) -> STATE_IDLE, + // (stateCurr === STATE_WAIT) -> )) + io.data(0).ready := (stateCurr === STATE_SUBMSG_DATA) & (subMsgInfoSel.source === 0.U) + io.data(1).ready := (stateCurr === STATE_SUBMSG_DATA) & (subMsgInfoSel.source === 1.U) + + io.send.valid := - (stateCurr === STATE_FRAME_HEADER) | + (stateCurr === STATE_FRAME_HEADER) | (stateCurr === STATE_SUBMSG_HEADER) | - (stateCurr === STATE_SUBMSG_DATA) | + (stateCurr === STATE_SUBMSG_DATA) | (stateCurr === STATE_CRC) @@ -109,17 +138,18 @@ class SlaveSendGen extends Module{ )) val tdata_submsg_data = Mux1H(Seq( - (subMsgInfoSel.source === 0.U) -> dataPing.bits - (subMsgInfoSel.source === 1.U) -> dataPong.bits - (subMsgInfoSel.source === 2.U) -> sram.rdata + (subMsgInfoSel.source === 0.U) -> io.data(0).bits, + (subMsgInfoSel.source === 1.U) -> io.data(1).bits, )) - dataPing.ready := - dataPong.ready := - sram.enr := + + val tdata_crc = Mux1H(Seq( - + (lengthCnt === 0.U) -> crc.io.crc(31,24), + (lengthCnt === 1.U) -> crc.io.crc(23,16), + (lengthCnt === 2.U) -> crc.io.crc(15, 8), + (lengthCnt === 3.U) -> crc.io.crc( 7, 0), )) io.send.bits.tdata := Mux1H(Seq( @@ -129,8 +159,8 @@ class SlaveSendGen extends Module{ (stateCurr === STATE_CRC) -> tdata_crc, )) - io.send.bits.tuser := - io.send.bits.tlast := + io.send.bits.tuser := (stateCurr === STATE_ERROR & stateNext === STATE_IDLE) + io.send.bits.tlast := (stateCurr === STATE_CRC & stateNext === STATE_IDLE) || (stateCurr === STATE_ERROR & stateNext === STATE_IDLE)