diff --git a/src/main/scala/mac/MacTx.scala b/src/main/scala/mac/MacTx.scala index d975840..9560f5d 100644 --- a/src/main/scala/mac/MacTx.scala +++ b/src/main/scala/mac/MacTx.scala @@ -1,7 +1,477 @@ package MAC import chisel3._ -import chisel3.util +import chisel3.util._ + + +class MacTxIO extends Bundle{ + + val TxStartFrm = Input(Bool()) // Transmit packet start frame + val TxEndFrm = Input(Bool()) // Transmit packet end frame + val TxUnderRun = Input(Bool()) // Transmit packet under-run + val TxData = Input(UInt(8.W)) // Transmit packet data byte + val CarrierSense = Input(Bool()) // Carrier sense (synchronized) + val Collision = Input(Bool()) // Collision (synchronized) + val Pad = Input(Bool()) // Pad enable (from register) + val CrcEn = Input(Bool()) // Crc enable (from register) + val FullD = Input(Bool()) // Full duplex (from register) + val HugEn = Input(Bool()) // Huge packets enable (from register) + val DlyCrcEn = Input(Bool()) // Delayed Crc enabled (from register) + val MinFL = Input(UInt(16.W)) // Minimum frame length (from register) + val MaxFL = Input(UInt(16.W)) // Maximum frame length (from register) + val IPGT = Input(UInt(7.W)) // Back to back transmit inter packet gap parameter (from register) + val IPGR1 = Input(UInt(7.W)) // Non back to back transmit inter packet gap parameter IPGR1 (from register) + val IPGR2 = Input(UInt(7.W)) // Non back to back transmit inter packet gap parameter IPGR2 (from register) + val CollValid = Input(UInt(6.W)) // Valid collision window (from register) + val MaxRet = Input(UInt(4.W)) // Maximum retry number (from register) + val NoBckof = Input(Bool()) // No backoff (from register) + val ExDfrEn = Input(Bool()) // Excessive defferal enable (from register) + + val MTxD = Output(UInt(4.W)) // Transmit nibble (to PHY) + val MTxEn = Output(Bool()) // Transmit enable (to PHY) + val MTxErr = Output(Bool()) // Transmit error (to PHY) + val TxDone = Output(Bool()) // Transmit packet done (to RISC) + val TxRetry = Output(Bool()) // Transmit packet retry (to RISC) + val TxAbort = Output(Bool()) // Transmit packet abort (to RISC) + val TxUsedData = Output(Bool()) // Transmit packet used data (to RISC) + val WillTransmit = Output(Bool()) // Will transmit (to RxEthMAC) + val ResetCollision = Output(Bool()) // Reset Collision (for synchronizing collision) + val RetryCnt = Output(UInt(4.W)) // Latched Retry Counter for tx status purposes + val StartTxDone = Output(Bool()) + val StartTxAbort = Output(Bool()) + val MaxCollisionOccured= Output(Bool()) + val LateCollision = Output(Bool()) + val DeferIndication = Output(Bool()) + val StatePreamble = Output(Bool()) + val StateData = Output(UInt(2.W)) +} + +abstract class MacTxBase extends Module{ + val io: MacTxIO = IO(new MacTxIO) + + + + + val StartIPG = Wire(Bool()) + val StartPreamble = Wire(Bool()) + val StartData = Wire( Vec(2,Bool())) + val StartFCS = Wire(Bool()) + val StartJam = Wire(Bool()) + val StartDefer = Wire(Bool()) + val StartBackoff = Wire(Bool()) + + + + + val StateSFD = Wire(Bool()) + + val UnderRun = Wire(Bool()) + val TooBig = Wire(Bool()) + val Crc = Wire(UInt(32.W)) + val CrcError = Wire(Bool()) + + + val NibbleMinFl = Wire(Bool()) + val ExcessiveDefer = Wire(Bool()) + + val MaxFrame = Wire(Bool()) + val RetryMax = Wire(Bool()) + val RandomEq0 = Wire(Bool()) + val RandomEqByteCnt = Wire(Bool()) + + val StateIPG = RegInit(false.B) + val StateIdle = RegInit(false.B) + val StatePreamble = RegInit(false.B); io.StatePreamble := StatePreamble + val StateData = RegNext( Cat(StartData(1), StartData(0)), 0.U(2.W)); io.StateData := StateData + val StatePAD = RegInit(false.B) + val StateFCS = RegInit(false.B) + val StateJam = RegInit(false.B) + val StateJam_q = RegNext(StateJam, false.B) + val StateBackOff = RegInit(false.B) + val StateDefer = RegInit(true.B) + val Rule1 = RegInit(false.B) + + val ColWindow = RegInit(true.B) + val DlyCrcCnt = RegInit(0.U(3.W)) // Delayed CRC counter + val PacketFinished_q = RegInit(false.B) + val ByteCnt = RegInit(0.U(16.W)) // Transmit Byte Counter + val NibCnt = RegInit(0.U(16.W)) // Nibble Counter + val NibCntEq7 = NibCnt === 7.U + val NibCntEq15 = NibCnt === 15.U + + val RetryCnt = RegInit(0.U(4.W)) +} + + + +trait MacTxFSM { this: MacTxBase => + + // Defining the next state + io.StartIPG := StateDefer & ~ExcessiveDefer & ~io.CarrierSense + + val StartIdle = StateIPG & (Rule1 & NibCnt(6,0) >= io.IPGT | ~Rule1 & NibCnt(6,0) >= io.IPGR2) + + StartPreamble := StateIdle & io.TxStartFrm & ~io.CarrierSense + + StartData(0) := ~io.Collision & (StatePreamble & NibCntEq15 | StateData(1) & ~io.TxEndFrm) + StartData(1) := ~io.Collision & StateData(0) & ~io.TxUnderRun & ~MaxFrame + + val StartPAD = ~io.Collision & StateData(1) & io.TxEndFrm & io.Pad & ~NibbleMinFl + + val StartFCS = ~io.Collision & StateData(1) & io.TxEndFrm & (~io.Pad | io.Pad & NibbleMinFl) & io.CrcEn + | ~io.Collision & StatePAD & NibbleMinFl & io.CrcEn + + StartJam := (io.Collision | UnderRun) & ((StatePreamble & NibCntEq15) | (StateData(1) | StateData(0)) | StatePAD | StateFCS) + + StartBackoff := StateJam & ~RandomEq0 & ColWindow & ~RetryMax & NibCntEq7 & ~io.NoBckof + + StartDefer := StateIPG & ~Rule1 & io.CarrierSense & NibCnt(6,0) <= io.IPGR1 & NibCnt(6,0) =/= io.IPGR2 + | StateIdle & io.CarrierSense + | StateJam & NibCntEq7 & (io.NoBckof | RandomEq0 | ~ColWindow | RetryMax) + | StateBackOff & (io.TxUnderRun | RandomEqByteCnt) + | io.StartTxDone | TooBig; + + io.DeferIndication := StateIdle & io.CarrierSense; + + + + when(StartDefer | StartIdle){ + StateIPG := false.B + }.elsewhen(io.StartIPG){ + StateIPG := true.B + } + + when(StartDefer | StartPreamble){ + StateIdle := false.B + }.elsewhen(StartIdle){ + StateIdle := true.B + } + + when(StartData(0) | StartJam){ + StatePreamble := false.B + } .elsewhen(StartPreamble){ + StatePreamble := true.B + } + + when(StartFCS | StartJam){ + StatePAD := false.B + }.elsewhen(StartPAD){ + StatePAD := true.B + } + + when(StartJam | StartDefer){ + StateFCS := false.B + } .elsewhen(StartFCS){ + StateFCS := true.B + } + + when(StartBackoff | StartDefer){ + StateJam := false.B + } .elsewhen(StartJam){ + StateJam := true.B + } + + when(StartDefer){ + StateBackOff := false.B + } .elsewhen(StartBackoff){ + StateBackOff := true.B + } + + when(io.StartIPG){ + StateDefer := false.B + } .elsewhen(StartDefer){ + StateDefer := true.B + } + + // This sections defines which interpack gap rule to use + when(StateIdle | StateBackOff){ + Rule1 := false.B + } .elsewhen(StatePreamble | io.FullD){ + Rule1 := true.B + } + + +} + +trait MacTxCounter { this: MacTxBase => + + val ByteCntMax = Wire(Bool()) + + val IncrementNibCnt = + StateIPG | StatePreamble | + (StateData(1) | StateData(0)) | + StatePAD | StateFCS | StateJam | StateBackOff | + (StateDefer & ~ExcessiveDefer & io.TxStartFrm) + + + val ResetNibCnt = + (StateDefer & ExcessiveDefer & ~io.TxStartFrm) | + (StatePreamble & NibCntEq15) | + (StateJam & NibCntEq7) | + StateIdle | StartDefer | io.StartIPG | StartFCS | StartJam + + + when(ResetNibCnt){ + NibCnt := 0.U + } .elsewhen(IncrementNibCnt){ + NibCnt := NibCnt + 1.U + } + + NibbleMinFl := NibCnt >= (((MinFL-4.U)<<1) - 1.U) // FCS should not be included in NibbleMinFl + + ExcessiveDefer := NibCnt(13,0) === "h17b7".U & ~io.ExDfrEn; // 6071 nibbles + + val IncrementByteCnt = + (StateData(1) & ~ByteCntMax) | + (StateBackOff & (NibCnt(6,0) === 127.U)) | + ((StatePAD | StateFCS) & NibCnt.extract(0) & ~ByteCntMax) + + val ResetByteCnt = + StartBackoff | + (StateIdle & io.TxStartFrm) | + PacketFinished_q + + + ByteCntMax := ByteCnt.andR + when(ResetByteCnt){ + ByteCnt := 0.U + } .elsewhen(IncrementByteCnt){ + ByteCnt := ByteCnt + 1.U + } + + MaxFrame := (ByteCnt === io.MaxFL) & ~io.HugEn; + + when((StateData(1) & DlyCrcCnt === 4.U) | StartJam | PacketFinished_q){ + DlyCrcCnt := 0.U + }.elsewhen(io.DlyCrcEn & (StateSFD | StateData(1) & (DlyCrcCnt.orR))){ + DlyCrcCnt := DlyCrcCnt + 1.U + } + +} + + + +trait MacTxCRC{ this: MacTxBase => + + val Initialize_Crc = StateIdle | StatePreamble | (DlyCrcCnt.orR) + val Enable_Crc = ~StateFCS + + val Data_Crc = + Mux1H(Seq( + StateData(0) -> Cat( TxData(3), TxData(2), TxData(1), TxData(0) ), + StateData(1) -> Cat( TxData(7), TxData(6), TxData(5), TxData(4) ), + )) + + when( Initialize_Crc ){ + Crc := "hFFFFFFFF".U + } .otherwise{ + Crc := Cat( + Crc.extract(27), + Crc.extract(26), + (Enable_Crc & (Data_Crc.extract(3) ^ Crc.extract(31))) ^ Crc.extract(25), + (Enable_Crc & (Data_Crc.extract(2) ^ Crc.extract(30))) ^ Crc.extract(24), + (Enable_Crc & (Data_Crc.extract(1) ^ Crc.extract(29))) ^ Crc.extract(23), + (Enable_Crc & (Data_Crc.extract(3) ^ Data_Crc.extract(0) ^ Crc.extract(31) ^ Crc.extract(28))) ^ Crc.extract(22), + (Enable_Crc & (Data_Crc.extract(3) ^ Data_Crc.extract(2) ^ Crc.extract(31) ^ Crc.extract(30))) ^ Crc.extract(21), + (Enable_Crc & (Data_Crc.extract(2) ^ Data_Crc.extract(1) ^ Crc.extract(30) ^ Crc.extract(29))) ^ Crc.extract(20), + (Enable_Crc & (Data_Crc.extract(1) ^ Data_Crc.extract(0) ^ Crc.extract(29) ^ Crc.extract(28))) ^ Crc.extract(19), + (Enable_Crc & (Data_Crc.extract(0) ^ Crc.extract(28))) ^ Crc.extract(18), + Crc.extract(17), + Crc.extract(16), + (Enable_Crc & (Data_Crc.extract(3) ^ Crc.extract(31))) ^ Crc.extract(15), + (Enable_Crc & (Data_Crc.extract(2) ^ Crc.extract(30))) ^ Crc.extract(14), + (Enable_Crc & (Data_Crc.extract(1) ^ Crc.extract(29))) ^ Crc.extract(13), + (Enable_Crc & (Data_Crc.extract(0) ^ Crc.extract(28))) ^ Crc.extract(12), + (Enable_Crc & (Data_Crc.extract(3) ^ Crc.extract(31))) ^ Crc.extract(11), + (Enable_Crc & (Data_Crc.extract(3) ^ Data_Crc.extract(2) ^ Crc.extract(30) ^ Crc.extract(31))) ^ Crc.extract(10), + (Enable_Crc & (Data_Crc.extract(3) ^ Data_Crc.extract(2) ^ Data_Crc.extract(1) ^ Crc.extract(29) ^ Crc.extract(30) ^ Crc.extract(31))) ^ Crc.extract(9), + (Enable_Crc & (Data_Crc.extract(2) ^ Data_Crc.extract(1) ^ Data_Crc.extract(0) ^ Crc.extract(28) ^ Crc.extract(29) ^ Crc.extract(30))) ^ Crc.extract(8), + (Enable_Crc & (Data_Crc.extract(3) ^ Data_Crc.extract(1) ^ Data_Crc.extract(0) ^ Crc.extract(28) ^ Crc.extract(29) ^ Crc.extract(31))) ^ Crc.extract(7), + (Enable_Crc & (Data_Crc.extract(3) ^ Data_Crc.extract(2) ^ Data_Crc.extract(0) ^ Crc.extract(28) ^ Crc.extract(30) ^ Crc.extract(31))) ^ Crc.extract(6), + (Enable_Crc & (Data_Crc.extract(2) ^ Data_Crc.extract(1) ^ Crc.extract(29) ^ Crc.extract(30))) ^ Crc.extract(5), + (Enable_Crc & (Data_Crc.extract(3) ^ Data_Crc.extract(1) ^ Data_Crc.extract(0) ^ Crc.extract(28) ^ Crc.extract(29) ^ Crc.extract(31))) ^ Crc.extract(4), + (Enable_Crc & (Data_Crc.extract(3) ^ Data_Crc.extract(2) ^ Data_Crc.extract(0) ^ Crc.extract(28) ^ Crc.extract(30) ^ Crc.extract(31))) ^ Crc.extract(3), + (Enable_Crc & (Data_Crc.extract(2) ^ Data_Crc.extract(1) ^ Crc.extract(29) ^ Crc.extract(30))) ^ Crc.extract( 2), + (Enable_Crc & (Data_Crc.extract(3) ^ Data_Crc.extract(1) ^ Data_Crc.extract(0) ^ Crc.extract(28) ^ Crc.extract(29) ^ Crc.extract(31))) ^ Crc.extract(1), + (Enable_Crc & (Data_Crc.extract(3) ^ Data_Crc.extract(2) ^ Data_Crc.extract(0) ^ Crc.extract(28) ^ Crc.extract(30) ^ Crc.extract(31))) ^ Crc.extract(0), + Enable_Crc & (Data_Crc.extract(3) ^ Data_Crc.extract(2) ^ Data_Crc.extract(1) ^ Crc.extract(29) ^ Crc.extract(30) ^ Crc.extract(31)), + Enable_Crc & (Data_Crc.extract(2) ^ Data_Crc.extract(1) ^ Data_Crc.extract(0) ^ Crc.extract(28) ^ Crc.extract(29) ^ Crc.extract(30)), + Enable_Crc & (Data_Crc.extract(1) ^ Data_Crc.extract(0) ^ Crc.extract(28) ^ Crc.extract(29)), + Enable_Crc & (Data_Crc.extract(0) ^ Crc.extract(28)) + ) + } + + CrcError := Crc =/= "hc704dd7b".U // CRC not equal to magic number + + +} + +trait MacTxRandom{ this: MacTxBase => + + val x = RegInit(0.U(10.W)) + when(true.B){ + x := Cat( x(8,0), ~(x.extract(2) ^ x.extract(9)) ) + } + + val RandomLatched = RegEnable( + Cat( + Mux( RetryCnt > 9.U, x.extract(9), 0.U(1.W)), + Mux( RetryCnt > 8.U, x.extract(8), 0.U(1.W)), + Mux( RetryCnt > 7.U, x.extract(7), 0.U(1.W)), + Mux( RetryCnt > 6.U, x.extract(6), 0.U(1.W)), + Mux( RetryCnt > 5.U, x.extract(5), 0.U(1.W)), + Mux( RetryCnt > 4.U, x.extract(4), 0.U(1.W)), + Mux( RetryCnt > 3.U, x.extract(3), 0.U(1.W)), + Mux( RetryCnt > 2.U, x.extract(2), 0.U(1.W)), + Mux( RetryCnt > 1.U, x.extract(1), 0.U(1.W)), + x.extract(0) + ), + 0.U(10.W), + StateJam & StateJam_q + ) + + // Random Number == 0 IEEE 802.3 page 68. If 0 we go to defer and not to backoff. + RandomEq0 := RandomLatched === 0.U + RandomEqByteCnt := ByteCnt(9,0) === RandomLatched & (NibCnt(6,0).andR) + +} + +class MacTx extends MacTxBase with MacTxFSM with MacTxCounter with MacTxCRC with MacTxRandom{ + val MTxD = RegInit(UInt(4.W)); io.MTxD := MTxD + val StopExcessiveDeferOccured = RegInit(false.B) + + val StatusLatch = RegInit(false.B) + val TxUsedData = RegNext(StartData.orR, false.B) + val TxDone = RegInit(false.B) + val TxRetry = RegInit(false.B) + val TxAbort = RegInit(false.B) + + val MTxEn = RegNext(StatePreamble | (StateData(0) | StateData(1)) | StatePAD | StateFCS | StateJam, false.B); io.MTxEn := MTxEn + val MTxErr = RegNext( TooBig | UnderRun, false.B) // Transmit error + + val WillTransmit = RegNext(StartPreamble | StatePreamble | (StateData(0) | StateData(1)) | StatePAD | StateFCS | StateJam, false.B) // WillTransmit + + + + io.ResetCollision := ~(StatePreamble | (StateData(0) | StateData(1)) | StatePAD | StateFCS) + val ExcessiveDeferOccured = io.TxStartFrm & StateDefer & ExcessiveDefer & ~StopExcessiveDeferOccured + io.StartTxDone = ~io.Collision & (StateFCS & NibCntEq7 | StateData(1) & io.TxEndFrm & (~io.Pad | io.Pad & NibbleMinFl) & ~io.CrcEn) + UnderRun := StateData(0) & io.TxUnderRun & ~io.Collision + TooBig := ~io.Collision & MaxFrame & (StateData(0) & ~io.TxUnderRun | StateFCS); + val StartTxRetry = StartJam & (ColWindow & ~RetryMax) & ~UnderRun; + io.LateCollision := StartJam & ~ColWindow & ~UnderRun; + io.MaxCollisionOccured := StartJam & ColWindow & RetryMax; + StateSFD := StatePreamble & NibCntEq15; + io.StartTxAbort := TooBig | UnderRun | ExcessiveDeferOccured | io.LateCollision | io.MaxCollisionOccured + + when(~io.TxStartFrm){ + StopExcessiveDeferOccured := false.B + }.elsewhen(ExcessiveDeferOccured){ + StopExcessiveDeferOccured := true.B + } + + + // Collision Window + when(~io.Collision & ByteCnt(5,0) === io.CollValid & (StateData(1) | StatePAD & NibCnt.extract(0) | StateFCS & NibCnt.extract(0))){ + ColWindow := false.B + } .elsewhen(StateIdle | StateIPG){ + ColWindow := true.B + } + + + + + // Start Window + when(~io.TxStartFrm){ + StatusLatch := false.B + } .elsewhen(ExcessiveDeferOccured | StateIdle){ + StatusLatch := true.B + } + + + // Transmit packet done + when(io.TxStartFrm & ~StatusLatch){ + TxDone := false.B + }.elsewhen(io.StartTxDone){ + TxDone := true.B + } + + // Transmit packet retry + when(io.TxStartFrm & ~StatusLatch){ + TxRetry := false.B + } .elsewhen(StartTxRetry){ + TxRetry := true.B + } + + + // Transmit packet abort + when(io.TxStartFrm & ~StatusLatch & ~ExcessiveDeferOccured){ + TxAbort := false.B + } .elsewhen(io.StartTxAbort){ + TxAbort := true.B + } + + // Retry counter + when(ExcessiveDeferOccured | UnderRun | TooBig | io.StartTxDone | io.TxUnderRun + | StateJam & NibCntEq7 & (~ColWindow | RetryMax)){ + RetryCnt := 0.U + }.elsewhen(StateJam & NibCntEq7 & ColWindow & (RandomEq0 | io.NoBckof) | StateBackOff & RandomEqByteCnt){ + RetryCnt := RetryCnt + 1.U + } + + RetryMax := RetryCnt === io.MaxRet + + when( true.B ){ + MTxD := // Transmit nibble + Mux( + StateData(0), TxData(3,0), // Lower nibble + Mux(StateData(1), TxData(7,4), // Higher nibble + Mux( StateFCS, Cat(~Crc.extract(28), ~Crc.extract(29), ~Crc.extract(30), ~Crc.extract(31)), // Crc + Mux(StateJam, 9.U, // Jam pattern + Mux( + StatePreamble, Mux(NibCntEq15, "hd".U, 5.U),// SFD,Preamble + 0.U + ) + ) + ) + ) + ) + } + + val PacketFinished_d = io.StartTxDone | TooBig | UnderRun | io.LateCollision | io.MaxCollisionOccured | ExcessiveDeferOccured; + val PacketFinished = RegNext(PacketFinished_d, false.B) + when(true.B){ + PacketFinished_q := PacketFinished + } + +} + + + + + + + + + + + + + + + + + + + + + + + + + + +