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