1 // 2 // Copyright (c) 2010-2023 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using System; 8 using System.Linq; 9 using System.Threading; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure; 12 using Antmicro.Renode.Core.Structure.Registers; 13 using Antmicro.Renode.Logging; 14 using Antmicro.Renode.Network; 15 using Antmicro.Renode.Peripherals.Bus; 16 using Antmicro.Renode.Peripherals.Memory; 17 using Antmicro.Renode.Utilities; 18 19 namespace Antmicro.Renode.Peripherals.Network 20 { 21 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord | AllowedTranslation.WordToDoubleWord)] 22 public class LiteX_Ethernet : NetworkWithPHY, IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IMACInterface, IKnownSize 23 { LiteX_Ethernet(IMachine machine, int numberOfWriteSlots = 2, int numberOfReadSlots = 2)24 public LiteX_Ethernet(IMachine machine, int numberOfWriteSlots = 2, int numberOfReadSlots = 2) : base(machine) 25 { 26 Interlocked.Add(ref NumberOfInstances, 1); 27 28 MAC = MACAddress.Parse("10:e2:d5:00:00:00").Next(NumberOfInstances - 1); 29 30 writeSlots = new Slot[numberOfWriteSlots]; 31 readSlots = new Slot[numberOfReadSlots]; 32 for(var i = 0; i < numberOfWriteSlots; i++) 33 { 34 writeSlots[i] = new Slot(); 35 } 36 for(var i = 0; i < numberOfReadSlots; i++) 37 { 38 readSlots[i] = new Slot(); 39 } 40 41 bbHelper = new BitBangHelper(width: 16, loggingParent: this); 42 43 RegistersCollection = new DoubleWordRegisterCollection(this); 44 DefineRegisters(); 45 } 46 Reset()47 public override void Reset() 48 { 49 RegistersCollection.Reset(); 50 bbHelper.Reset(); 51 52 latchedWriterSlot = -1; 53 lastPhyAddress = 0; 54 lastRegisterAddress = 0; 55 56 foreach(var slot in writeSlots.Union(readSlots)) 57 { 58 slot.Reset(); 59 } 60 61 RefreshIrq(); 62 } 63 ReadDoubleWord(long offset)64 public uint ReadDoubleWord(long offset) 65 { 66 return RegistersCollection.Read(offset); 67 } 68 WriteDoubleWord(long offset, uint value)69 public void WriteDoubleWord(long offset, uint value) 70 { 71 RegistersCollection.Write(offset, value); 72 } 73 ReceiveFrame(EthernetFrame frame)74 public void ReceiveFrame(EthernetFrame frame) 75 { 76 this.Log(LogLevel.Noisy, "Received frame of length: {0} bytes", frame.Bytes.Length); 77 78 var emptySlotId = -1; 79 for(var i = 0; i < writeSlots.Length; i++) 80 { 81 if(!writeSlots[i].IsBusy) 82 { 83 emptySlotId = i; 84 break; 85 } 86 } 87 88 if(emptySlotId == -1) 89 { 90 this.Log(LogLevel.Warning, "There are no empty write slots. Dropping the packet"); 91 return; 92 } 93 94 writerSlotNumber.Value = (uint)emptySlotId; 95 var slot = writeSlots[emptySlotId]; 96 if(!slot.TryWrite(frame.Bytes)) 97 { 98 this.Log(LogLevel.Warning, "Packet is too long. Dropping"); 99 return; 100 } 101 102 UpdateEvents(); 103 } 104 105 [ConnectionRegionAttribute("buffer")] ReadDoubleWordFromBuffer(long offset)106 public uint ReadDoubleWordFromBuffer(long offset) 107 { 108 var slot = FindSlot(offset, out var slotOffset); 109 if(slot == null) 110 { 111 this.Log(LogLevel.Warning, "Reading outside buffer memory"); 112 return 0; 113 } 114 115 return slot.ReadUInt32(slotOffset); 116 } 117 118 [ConnectionRegionAttribute("buffer")] WriteDoubleWordToBuffer(long offset, uint value)119 public void WriteDoubleWordToBuffer(long offset, uint value) 120 { 121 var slot = FindSlot(offset, out var slotOffset); 122 if(slot == null || !slot.TryWriteUInt32(slotOffset, value)) 123 { 124 this.Log(LogLevel.Warning, "Writing outside buffer memory"); 125 } 126 } 127 128 [ConnectionRegionAttribute("phy")] ReadDoubleWordOverMDIO(long offset)129 public uint ReadDoubleWordOverMDIO(long offset) 130 { 131 this.Log(LogLevel.Noisy, "Reading from PHY: offset 0x{0:X}", offset); 132 if(offset == (long)MDIORegisters.Read) 133 { 134 var result = bbHelper.EncodedInput 135 ? 1 136 : 0u; 137 138 this.Log(LogLevel.Noisy, "Returning value: 0x{0:X}", result); 139 return result; 140 } 141 142 this.Log(LogLevel.Warning, "Unhandled read from PHY register: 0x{0:X}", offset); 143 return 0; 144 } 145 146 [ConnectionRegionAttribute("phy")] WriteDoubleWordOverMDIO(long offset, uint value)147 public void WriteDoubleWordOverMDIO(long offset, uint value) 148 { 149 this.Log(LogLevel.Noisy, "Writing to PHY: offset 0x{0:X}, value 0x{1:X}", offset, value); 150 151 if(offset != (long)MDIORegisters.Write) 152 { 153 this.Log(LogLevel.Warning, "Unhandled write to PHY register: 0x{0:X}", offset); 154 return; 155 } 156 157 var dataDecoded = bbHelper.Update(value, dataBit: 2, clockBit: 0); 158 if(!dataDecoded) 159 { 160 return; 161 } 162 163 this.Log(LogLevel.Noisy, "Got a 16-bit packet in {0} state, the value is 0x{1:X}", phyState, bbHelper.DecodedOutput); 164 165 switch(phyState) 166 { 167 case PhyState.Idle: 168 { 169 if(bbHelper.DecodedOutput == 0xffff) 170 { 171 // sync, move on 172 phyState = PhyState.Syncing; 173 } 174 // if not, wait for sync pattern 175 break; 176 } 177 case PhyState.Syncing: 178 { 179 if(bbHelper.DecodedOutput == 0xffff) 180 { 181 phyState = PhyState.WaitingForCommand; 182 } 183 else 184 { 185 this.Log(LogLevel.Warning, "Unexpected bit pattern when syncing (0x{0:X}), returning to idle state", bbHelper.DecodedOutput); 186 phyState = PhyState.Idle; 187 } 188 break; 189 } 190 case PhyState.WaitingForCommand: 191 { 192 const int OpCodeRead = 0x1; 193 const int OpCodeWrite = 0x2; 194 195 var startField = bbHelper.DecodedOutput & 0x3; 196 var opCode = (bbHelper.DecodedOutput >> 2) & 0x3; 197 var phyAddress = (ushort)(BitHelper.ReverseBits((ushort)((bbHelper.DecodedOutput >> 4) & 0x1f)) >> 11); 198 var registerAddress = (ushort)(BitHelper.ReverseBits((ushort)((bbHelper.DecodedOutput >> 9) & 0x1f)) >> 11); 199 200 if(startField != 0x2 201 || (opCode != OpCodeRead && opCode != OpCodeWrite)) 202 { 203 this.Log(LogLevel.Warning, "Received an invalid PHY command: 0x{0:X}. Ignoring it", bbHelper.DecodedOutput); 204 phyState = PhyState.Idle; 205 break; 206 } 207 208 if(opCode == OpCodeWrite) 209 { 210 phyState = PhyState.WaitingForData; 211 this.Log(LogLevel.Noisy, "Write command to PHY 0x{0:X}, register 0x{1:X}. Waiting for data", phyAddress, registerAddress); 212 213 lastPhyAddress = phyAddress; 214 lastRegisterAddress = registerAddress; 215 } 216 else 217 { 218 ushort readValue = 0; 219 if(!TryGetPhy<ushort>(phyAddress, out var phy)) 220 { 221 this.Log(LogLevel.Warning, "Trying to read from non-existing PHY #{0}", phyAddress); 222 } 223 else 224 { 225 readValue = (ushort)phy.Read(registerAddress); 226 } 227 228 this.Log(LogLevel.Noisy, "Read value 0x{0:X} from PHY 0x{1:X}, register 0x{2:X}", readValue, phyAddress, registerAddress); 229 230 bbHelper.SetInputBuffer(readValue); 231 phyState = PhyState.Idle; 232 } 233 break; 234 } 235 case PhyState.WaitingForData: 236 { 237 if(!TryGetPhy<ushort>(lastPhyAddress, out var phy)) 238 { 239 this.Log(LogLevel.Warning, "Trying to write to non-existing PHY #{0}", lastPhyAddress); 240 } 241 else 242 { 243 this.Log(LogLevel.Noisy, "Writing value 0x{0:X} to PHY 0x{1:X}, register 0x{2:X}", bbHelper.DecodedOutput, lastPhyAddress, lastRegisterAddress); 244 phy.Write(lastRegisterAddress, (ushort)bbHelper.DecodedOutput); 245 } 246 247 phyState = PhyState.Idle; 248 break; 249 } 250 default: 251 throw new ArgumentOutOfRangeException("Unexpected PHY state: {0}".FormatWith(phyState)); 252 } 253 } 254 255 public MACAddress MAC { get; set; } 256 257 public event Action<EthernetFrame> FrameReady; 258 259 public GPIO IRQ { get; } = new GPIO(); 260 261 public DoubleWordRegisterCollection RegistersCollection { get; private set; } 262 263 public long Size => 0x100; 264 DefineRegisters()265 private void DefineRegisters() 266 { 267 Registers.ReaderEvPending.Define(this) 268 .WithFlag(0, out readerEventPending, FieldMode.Read | FieldMode.WriteOneToClear, writeCallback: (_, __) => RefreshIrq()) 269 ; 270 271 Registers.WriterLength0.DefineMany(this, NumberOfWriterLengthSubRegisters, (reg, idx) => 272 reg.WithValueField(0, DataWidth, FieldMode.Read, name: $"writer_length_{idx}", valueProviderCallback: _ => 273 { 274 return BitHelper.GetValue(writeSlots[writerSlotNumber.Value].DataLength, 275 offset: (NumberOfWriterLengthSubRegisters - idx - 1) * DataWidth, 276 size: DataWidth); 277 })) 278 ; 279 280 Registers.WriterEvPending.Define(this) 281 .WithFlag(0, out writerEventPending, FieldMode.Read | FieldMode.WriteOneToClear, writeCallback: (_, val) => 282 { 283 if(!val) 284 { 285 return; 286 } 287 288 if(latchedWriterSlot == -1) 289 { 290 // if no slot has been latched, release all (this might happen at startup when resetting the state) 291 foreach(var slot in writeSlots) 292 { 293 slot.Release(); 294 } 295 } 296 else 297 { 298 writeSlots[latchedWriterSlot].Release(); 299 } 300 301 latchedWriterSlot = -1; 302 303 // update writerSlotNumber 304 writerSlotNumber.Value = (uint)writeSlots 305 .Select((v, idx) => new { v, idx }) 306 .Where(x => x.v.IsBusy) 307 .Select(x => x.idx) 308 .FirstOrDefault(); 309 310 UpdateEvents(); 311 }) 312 ; 313 314 Registers.ReaderSlot.Define(this) 315 .WithValueField(0, 32, out readerSlotNumber, name: "reader_slot", writeCallback: (_, val) => 316 { 317 if((long)val >= readSlots.Length) 318 { 319 this.Log(LogLevel.Warning, "Trying to set reader slot number out of range ({0}). Forcing value of 0", val); 320 readerSlotNumber.Value = 0; 321 } 322 }) 323 ; 324 325 Registers.WriterSlot.Define(this) 326 .WithValueField(0, 32, out writerSlotNumber, FieldMode.Read, name: "writer_slot", readCallback: (_, val) => 327 { 328 // this is a bit hacky - here we remember the last returned writer slot number to release it later 329 latchedWriterSlot = (int)val; 330 }) 331 ; 332 333 Registers.ReaderLengthHi.DefineMany(this, NumberOfReaderLengthSubRegisters, (reg, idx) => 334 reg.WithValueField(0, DataWidth, name: $"reader_length_{idx}", 335 writeCallback: (_, val) => 336 { 337 readSlots[readerSlotNumber.Value].DataLength = 338 BitHelper.ReplaceBits(readSlots[readerSlotNumber.Value].DataLength, (uint)val, 339 width: DataWidth, 340 destinationPosition: (int)((NumberOfReaderLengthSubRegisters - idx - 1) * DataWidth)); 341 }, 342 valueProviderCallback: _ => 343 { 344 return BitHelper.GetValue(readSlots[readerSlotNumber.Value].DataLength, 345 offset: (NumberOfReaderLengthSubRegisters - idx - 1) * DataWidth, 346 size: DataWidth); 347 })) 348 ; 349 350 Registers.ReaderReady.Define(this) 351 .WithFlag(0, FieldMode.Read, name: "reader_ready", valueProviderCallback: _ => true) 352 ; 353 354 Registers.ReaderStart.Define(this) 355 .WithFlag(0, FieldMode.Write, name: "reader_start", writeCallback: (_, __) => SendPacket()) 356 ; 357 358 Registers.ReaderEvEnable.Define(this) 359 .WithFlag(0, out readerEventEnabled, name: "reader_event_enable", writeCallback: (_, __) => RefreshIrq()) 360 ; 361 362 Registers.WriterEvEnable.Define(this) 363 .WithFlag(0, out writerEventEnabled, name: "writer_event_enable", writeCallback: (_, __) => RefreshIrq()) 364 ; 365 } 366 UpdateEvents()367 private void UpdateEvents() 368 { 369 writerEventPending.Value = writeSlots.Any(s => s.IsBusy); 370 RefreshIrq(); 371 } 372 RefreshIrq()373 private void RefreshIrq() 374 { 375 var anyEventPending = (writerEventEnabled.Value && writerEventPending.Value) 376 || (readerEventEnabled.Value && readerEventPending.Value); 377 378 this.Log(LogLevel.Noisy, "Setting IRQ to: {0}", anyEventPending); 379 IRQ.Set(anyEventPending); 380 } 381 SendPacket()382 private void SendPacket() 383 { 384 var slot = readSlots[readerSlotNumber.Value]; 385 if(!Misc.TryCreateFrameOrLogWarning(this, slot.Read(), out var frame, addCrc: true)) 386 { 387 return; 388 } 389 390 this.Log(LogLevel.Noisy, "Sending packet of length {0} bytes.", frame.Length); 391 FrameReady?.Invoke(frame); 392 393 readerEventPending.Value = true; 394 RefreshIrq(); 395 } 396 FindSlot(long offset, out int slotOffset)397 private Slot FindSlot(long offset, out int slotOffset) 398 { 399 var slotId = offset / SlotSize; 400 slotOffset = (int)(offset % SlotSize); 401 402 if(slotId < writeSlots.Length) 403 { 404 return writeSlots[slotId]; 405 } 406 else if(slotId < (writeSlots.Length + readSlots.Length)) 407 { 408 return readSlots[slotId - writeSlots.Length]; 409 } 410 411 return null; 412 } 413 414 private static int NumberOfInstances; 415 416 private IFlagRegisterField readerEventEnabled; 417 private IFlagRegisterField writerEventEnabled; 418 private IFlagRegisterField readerEventPending; 419 private IFlagRegisterField writerEventPending; 420 private IValueRegisterField writerSlotNumber; 421 private IValueRegisterField readerSlotNumber; 422 private int latchedWriterSlot = -1; 423 private ushort lastPhyAddress; 424 private ushort lastRegisterAddress; 425 private PhyState phyState; 426 private BitBangHelper bbHelper; 427 428 // WARNING: 429 // read slots contains packets to be sent 430 // write slots contains received packets 431 private readonly Slot[] writeSlots; 432 private readonly Slot[] readSlots; 433 434 private const int SlotSize = 0x0800; 435 436 private const int DataWidth = 8; 437 // 'ReaderLength` is a 16-bit register, but because the data width is set to 8 by default, it is splitted into 2 subregisters 438 private const int ReaderLengthWidth = 16; 439 private const int NumberOfReaderLengthSubRegisters = ReaderLengthWidth / DataWidth; 440 // 'WriterLength` is a 32-bit register, but because the data width is set to 8 by default, it is splitted into 4 subregisters 441 private const int WriterLengthWidth = 32; 442 private const int NumberOfWriterLengthSubRegisters = WriterLengthWidth / DataWidth; 443 444 private class Slot 445 { Slot()446 public Slot() 447 { 448 buffer = new byte[SlotSize]; 449 } 450 TryWriteUInt32(int offset, uint value)451 public bool TryWriteUInt32(int offset, uint value) 452 { 453 if(offset > buffer.Length - 4) 454 { 455 return false; 456 } 457 458 buffer[offset + 3] = (byte)(value >> 24); 459 buffer[offset + 2] = (byte)(value >> 16); 460 buffer[offset + 1] = (byte)(value >> 8); 461 buffer[offset + 0] = (byte)(value); 462 return true; 463 } 464 TryWrite(byte[] data, int padding = 60)465 public bool TryWrite(byte[] data, int padding = 60) 466 { 467 if(data.Length > buffer.Length) 468 { 469 return false; 470 } 471 472 Array.Copy(data, buffer, data.Length); 473 474 var paddingBytesCount = data.Length % padding; 475 for(var i = 0; i < paddingBytesCount; i++) 476 { 477 buffer[data.Length + i] = 0; 478 } 479 480 DataLength = (uint)(data.Length + paddingBytesCount); 481 return true; 482 } 483 ReadUInt32(int offset)484 public uint ReadUInt32(int offset) 485 { 486 return (offset > buffer.Length - 4) 487 ? 0 488 : BitHelper.ToUInt32(buffer, offset, 4, true); 489 } 490 Read()491 public byte[] Read() 492 { 493 var result = new byte[DataLength]; 494 Array.Copy(buffer, result, DataLength); 495 return result; 496 } 497 Release()498 public void Release() 499 { 500 DataLength = 0; 501 } 502 Reset()503 public void Reset() 504 { 505 DataLength = 0; 506 Array.Clear(buffer, 0, buffer.Length); 507 } 508 509 public bool IsBusy => DataLength > 0; 510 511 public uint DataLength { get; set; } 512 513 private readonly byte[] buffer; 514 } 515 516 private enum Registers 517 { 518 WriterSlot = 0x0, 519 WriterLength0 = 0x04, 520 WriterLength1 = 0x08, 521 WriterLength2 = 0x0C, 522 WriterLength3 = 0x10, 523 WriterErrors = 0x14, 524 WriterEvStatus = 0x24, 525 WriterEvPending = 0x28, 526 WriterEvEnable = 0x2c, 527 ReaderStart = 0x30, 528 ReaderReady = 0x34, 529 ReaderLevel = 0x38, 530 ReaderSlot = 0x3c, 531 ReaderLengthHi = 0x40, 532 ReaderLengthLo = 0x44, 533 ReaderEvStatus = 0x48, 534 ReaderEvPending = 0x4c, 535 ReaderEvEnable = 0x50, 536 PreambleCRC = 0x54, 537 PreambleErrors = 0x58, 538 CrcErrors = 0x68 539 } 540 541 private enum MDIORegisters 542 { 543 Reset = 0x0, 544 Write = 0x4, 545 Read = 0x8 546 } 547 548 private enum PhyState 549 { 550 Idle, 551 Syncing, 552 WaitingForCommand, 553 WaitingForData 554 } 555 } 556 } 557