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_CSR32 : NetworkWithPHY, IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IMACInterface, IKnownSize 23 { LiteX_Ethernet_CSR32(IMachine machine, int numberOfWriteSlots = 2, int numberOfReadSlots = 2)24 public LiteX_Ethernet_CSR32(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.WriterLength.Define(this) 272 .WithValueField(0, 32, FieldMode.Read, name: "writer_length", valueProviderCallback: _ => writeSlots[writerSlotNumber.Value].DataLength) 273 ; 274 275 Registers.WriterEvPending.Define(this) 276 .WithFlag(0, out writerEventPending, FieldMode.Read | FieldMode.WriteOneToClear, writeCallback: (_, val) => 277 { 278 if(!val) 279 { 280 return; 281 } 282 283 if(latchedWriterSlot == -1) 284 { 285 // if no slot has been latched, release all (this might happen at startup when resetting the state) 286 foreach(var slot in writeSlots) 287 { 288 slot.Release(); 289 } 290 } 291 else 292 { 293 writeSlots[latchedWriterSlot].Release(); 294 } 295 296 latchedWriterSlot = -1; 297 298 // update writerSlotNumber 299 writerSlotNumber.Value = (uint)writeSlots 300 .Select((v, idx) => new { v, idx }) 301 .Where(x => x.v.IsBusy) 302 .Select(x => x.idx) 303 .FirstOrDefault(); 304 305 UpdateEvents(); 306 }) 307 ; 308 309 Registers.ReaderSlot.Define(this) 310 .WithValueField(0, 32, out readerSlotNumber, name: "reader_slot", writeCallback: (_, val) => 311 { 312 if((long)val >= readSlots.Length) 313 { 314 this.Log(LogLevel.Warning, "Trying to set reader slot number out of range ({0}). Forcing value of 0", val); 315 readerSlotNumber.Value = 0; 316 } 317 }) 318 ; 319 320 Registers.WriterSlot.Define(this) 321 .WithValueField(0, 32, out writerSlotNumber, FieldMode.Read, name: "writer_slot", readCallback: (_, val) => 322 { 323 // this is a bit hacky - here we remember the last returned writer slot number to release it later 324 latchedWriterSlot = (int)val; 325 }) 326 ; 327 328 Registers.ReaderLength.Define(this) 329 .WithValueField(0, 32, name: "reader_length", 330 writeCallback: (_, val) => 331 { 332 readSlots[readerSlotNumber.Value].DataLength = (uint)val; 333 }, 334 valueProviderCallback: _ => readSlots[readerSlotNumber.Value].DataLength) 335 ; 336 337 Registers.ReaderReady.Define(this) 338 .WithFlag(0, FieldMode.Read, name: "reader_ready", valueProviderCallback: _ => true) 339 ; 340 341 Registers.ReaderStart.Define(this) 342 .WithFlag(0, FieldMode.Write, name: "reader_start", writeCallback: (_, __) => SendPacket()) 343 ; 344 345 Registers.ReaderEvEnable.Define(this) 346 .WithFlag(0, out readerEventEnabled, name: "reader_event_enable", writeCallback: (_, __) => RefreshIrq()) 347 ; 348 349 Registers.WriterEvEnable.Define(this) 350 .WithFlag(0, out writerEventEnabled, name: "writer_event_enable", writeCallback: (_, __) => RefreshIrq()) 351 ; 352 } 353 UpdateEvents()354 private void UpdateEvents() 355 { 356 writerEventPending.Value = writeSlots.Any(s => s.IsBusy); 357 RefreshIrq(); 358 } 359 RefreshIrq()360 private void RefreshIrq() 361 { 362 var anyEventPending = (writerEventEnabled.Value && writerEventPending.Value) 363 || (readerEventEnabled.Value && readerEventPending.Value); 364 365 this.Log(LogLevel.Noisy, "Setting IRQ to: {0}", anyEventPending); 366 IRQ.Set(anyEventPending); 367 } 368 SendPacket()369 private void SendPacket() 370 { 371 var slot = readSlots[readerSlotNumber.Value]; 372 if(!Misc.TryCreateFrameOrLogWarning(this, slot.Read(), out var frame, addCrc: true)) 373 { 374 return; 375 } 376 377 this.Log(LogLevel.Noisy, "Sending packet of length {0} bytes.", frame.Length); 378 FrameReady?.Invoke(frame); 379 380 readerEventPending.Value = true; 381 RefreshIrq(); 382 } 383 FindSlot(long offset, out int slotOffset)384 private Slot FindSlot(long offset, out int slotOffset) 385 { 386 var slotId = offset / SlotSize; 387 slotOffset = (int)(offset % SlotSize); 388 389 if(slotId < writeSlots.Length) 390 { 391 return writeSlots[slotId]; 392 } 393 else if(slotId < (writeSlots.Length + readSlots.Length)) 394 { 395 return readSlots[slotId - writeSlots.Length]; 396 } 397 398 return null; 399 } 400 401 private static int NumberOfInstances; 402 403 private IFlagRegisterField readerEventEnabled; 404 private IFlagRegisterField writerEventEnabled; 405 private IFlagRegisterField readerEventPending; 406 private IFlagRegisterField writerEventPending; 407 private IValueRegisterField writerSlotNumber; 408 private IValueRegisterField readerSlotNumber; 409 private int latchedWriterSlot = -1; 410 private ushort lastPhyAddress; 411 private ushort lastRegisterAddress; 412 private PhyState phyState; 413 private BitBangHelper bbHelper; 414 415 // WARNING: 416 // read slots contains packets to be sent 417 // write slots contains received packets 418 private readonly Slot[] writeSlots; 419 private readonly Slot[] readSlots; 420 421 private const int SlotSize = 0x0800; 422 423 private class Slot 424 { Slot()425 public Slot() 426 { 427 buffer = new byte[SlotSize]; 428 } 429 TryWriteUInt32(int offset, uint value)430 public bool TryWriteUInt32(int offset, uint value) 431 { 432 if(offset > buffer.Length - 4) 433 { 434 return false; 435 } 436 437 buffer[offset + 3] = (byte)(value >> 24); 438 buffer[offset + 2] = (byte)(value >> 16); 439 buffer[offset + 1] = (byte)(value >> 8); 440 buffer[offset + 0] = (byte)(value); 441 return true; 442 } 443 TryWrite(byte[] data, int padding = 60)444 public bool TryWrite(byte[] data, int padding = 60) 445 { 446 if(data.Length > buffer.Length) 447 { 448 return false; 449 } 450 451 Array.Copy(data, buffer, data.Length); 452 453 var paddingBytesCount = data.Length % padding; 454 for(var i = 0; i < paddingBytesCount; i++) 455 { 456 buffer[data.Length + i] = 0; 457 } 458 459 DataLength = (uint)(data.Length + paddingBytesCount); 460 return true; 461 } 462 ReadUInt32(int offset)463 public uint ReadUInt32(int offset) 464 { 465 return (offset > buffer.Length - 4) 466 ? 0 467 : BitHelper.ToUInt32(buffer, offset, 4, true); 468 } 469 Read()470 public byte[] Read() 471 { 472 var result = new byte[DataLength]; 473 Array.Copy(buffer, result, DataLength); 474 return result; 475 } 476 Release()477 public void Release() 478 { 479 DataLength = 0; 480 } 481 Reset()482 public void Reset() 483 { 484 DataLength = 0; 485 Array.Clear(buffer, 0, buffer.Length); 486 } 487 488 public bool IsBusy => DataLength > 0; 489 490 public uint DataLength { get; set; } 491 492 private readonly byte[] buffer; 493 } 494 495 private enum Registers 496 { 497 WriterSlot = 0x0, 498 WriterLength = 0x04, 499 WriterErrors = 0x08, 500 WriterEvStatus = 0x0C, 501 WriterEvPending = 0x10, 502 WriterEvEnable = 0x14, 503 ReaderStart = 0x18, 504 ReaderReady = 0x1c, 505 ReaderLevel = 0x20, 506 ReaderSlot = 0x24, 507 ReaderLength = 0x28, 508 ReaderEvStatus = 0x2c, 509 ReaderEvPending = 0x30, 510 ReaderEvEnable = 0x34, 511 PreambleCRC = 0x38, 512 PreambleErrors = 0x3c, 513 CrcErrors = 0x40 514 } 515 516 private enum MDIORegisters 517 { 518 Reset = 0x0, 519 Write = 0x4, 520 Read = 0x8 521 } 522 523 private enum PhyState 524 { 525 Idle, 526 Syncing, 527 WaitingForCommand, 528 WaitingForData 529 } 530 } 531 } 532