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