1 //
2 // Copyright (c) 2010-2025 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.Linq;
8 using System.Collections.Generic;
9 
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Extensions;
12 using Antmicro.Renode.Core.Structure.Registers;
13 using Antmicro.Renode.Core.Structure;
14 using Antmicro.Renode.Logging;
15 using Antmicro.Renode.Peripherals.Bus;
16 
17 namespace Antmicro.Renode.Peripherals.I2C
18 {
19     public class SAMD21_I2C : SimpleContainer<II2CPeripheral>,
20         IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>,
21         IWordPeripheral, IProvidesRegisterCollection<WordRegisterCollection>,
22         IBytePeripheral, IProvidesRegisterCollection<ByteRegisterCollection>,
23         IKnownSize
24     {
SAMD21_I2C(IMachine machine)25         public SAMD21_I2C(IMachine machine) : base(machine)
26         {
27             // NOTE: This class is manually managing access translation for respective
28             //       register collections. As such, this comes with some caveats; please refer
29             //       to the implementation of Read/Write methods for more information.
30             doubleWordRegistersCollection = new DoubleWordRegisterCollection(this);
31             wordRegistersCollection = new WordRegisterCollection(this);
32             byteRegistersCollection = new ByteRegisterCollection(this);
33 
34             IRQ = new GPIO();
35 
36             DefineRegisters();
37             Reset();
38         }
39 
ReadDoubleWord(long offset)40         public uint ReadDoubleWord(long offset)
41         {
42             return doubleWordRegistersCollection.Read(offset);
43         }
44 
WriteDoubleWord(long offset, uint value)45         public void WriteDoubleWord(long offset, uint value)
46         {
47             doubleWordRegistersCollection.Write(offset, value);
48         }
49 
ReadWord(long offset)50         public ushort ReadWord(long offset)
51         {
52             if(!wordRegistersCollection.HasRegisterAtOffset(offset))
53             {
54                 // NOTE: Fallback to DoubleWord registers if we don't have
55                 //       an explicit Word register for given offset
56                 return this.ReadWordUsingDoubleWord(offset);
57             }
58             return wordRegistersCollection.Read(offset);
59         }
60 
WriteWord(long offset, ushort value)61         public void WriteWord(long offset, ushort value)
62         {
63             if(!wordRegistersCollection.HasRegisterAtOffset(offset))
64             {
65                 // NOTE: Fallback to DoubleWord registers if we don't have
66                 //       an explicit Word register for given offset
67                 // WARN: THIS SHOULD BE USED CAREFULLY IF THE valueProviderCallback
68                 //       FOR GIVEN REGISTER HAS SIDE-EFFECTS!
69                 this.WriteWordUsingDoubleWord(offset, value);
70                 return;
71             }
72             wordRegistersCollection.Write(offset, value);
73         }
74 
ReadByte(long offset)75         public byte ReadByte(long offset)
76         {
77             if(!byteRegistersCollection.HasRegisterAtOffset(offset))
78             {
79                 // NOTE: Fallback to Word registers if we don't have
80                 //       an explicit Byte register for given offset
81                 return this.ReadByteUsingWord(offset);
82             }
83             return byteRegistersCollection.Read(offset);
84         }
85 
WriteByte(long offset, byte value)86         public void WriteByte(long offset, byte value)
87         {
88             if(!byteRegistersCollection.HasRegisterAtOffset(offset))
89             {
90                 // NOTE: Fallback to Word registers if we don't have
91                 //       an explicit Byte register for given offset
92                 // WARN: THIS SHOULD BE USED CAREFULLY IF THE valueProviderCallback
93                 //       FOR GIVEN REGISTER HAS SIDE-EFFECTS!
94                 this.WriteByteUsingWord(offset, value);
95                 return;
96             }
97             byteRegistersCollection.Write(offset, value);
98         }
99 
Reset()100         public override void Reset()
101         {
102             doubleWordRegistersCollection.Reset();
103             wordRegistersCollection.Reset();
104             byteRegistersCollection.Reset();
105         }
106 
107         public GPIO IRQ { get; }
108 
109         DoubleWordRegisterCollection IProvidesRegisterCollection<DoubleWordRegisterCollection>.RegistersCollection => doubleWordRegistersCollection;
110         WordRegisterCollection IProvidesRegisterCollection<WordRegisterCollection>.RegistersCollection => wordRegistersCollection;
111         ByteRegisterCollection IProvidesRegisterCollection<ByteRegisterCollection>.RegistersCollection => byteRegistersCollection;
112 
113         public long Size => 0x100;
114 
TryFlush()115         private void TryFlush()
116         {
117             if(activePeripheral == null || txQueue.Count == 0)
118             {
119                 return;
120             }
121 
122             activePeripheral.Write(txQueue.ToArray());
123             txQueue.Clear();
124         }
125 
StartTransaction(int address, bool reading)126         private void StartTransaction(int address, bool reading)
127         {
128             if(!TryGetByAddress(address, out var peripheral))
129             {
130                 // NOTE: Peripheral with given address is not connected
131                 hostOnBusInterruptPending.Value = true;
132                 acknowledgeMissing.Value = true;
133                 UpdateInterrupts();
134                 return;
135             }
136 
137             activePeripheral = peripheral;
138             state = reading ? State.Reading : State.Writing;
139             RestartTransaction();
140         }
141 
RestartTransaction()142         private void RestartTransaction()
143         {
144             if(state != State.Reading && state != State.Writing)
145             {
146                 this.Log(LogLevel.Warning, "Tried to RESTART without an ongoing transaction");
147                 return;
148             }
149 
150 
151             txQueue.Clear();
152             hostOnBusInterruptPending.Value |= state != State.Reading;
153             clientOnBusInterruptPending.Value |= state == State.Reading;
154             acknowledgeMissing.Value = false;
155             UpdateInterrupts();
156         }
157 
DefineRegisters()158         private void DefineRegisters()
159         {
160             var thisProvidesDoubleWordRegisterCollection = this as IProvidesRegisterCollection<DoubleWordRegisterCollection>;
161             var thisProvidesWordRegisterCollection = this as IProvidesRegisterCollection<WordRegisterCollection>;
162             var thisProvidesByteRegisterCollection = this as IProvidesRegisterCollection<ByteRegisterCollection>;
163 
164             Registers.ControlA.Define(thisProvidesDoubleWordRegisterCollection)
165                 .WithFlag(0, FieldMode.Write, name: "SWRST",
166                     writeCallback: (_, value) => { if (value) { Reset(); } })
167                 .WithFlag(1, out enabled, name: "ENABLE")
168                 .WithEnumField(2, 3, out mode, name: "MODE",
169                     changeCallback: (previousValue, value) =>
170                     {
171                         switch(value)
172                         {
173                             case Mode.I2CHost:
174                                 break;
175 
176                             default:
177                                 this.Log(LogLevel.Warning, "{0} mode is currently not supported, reverting to {1}", value, previousValue);
178                                 mode.Value = previousValue;
179                                 break;
180                         }
181 
182                     })
183                 .WithReservedBits(5, 2)
184                 .WithTaggedFlag("RUNSTDBY", 7)
185                 .WithReservedBits(8, 8)
186                 .WithTaggedFlag("PINOUT", 16)
187                 .WithReservedBits(17, 3)
188                 .WithTag("SDAHOLD", 20, 2)
189                 .WithTaggedFlag("MEXTTOEN", 22)
190                 .WithTaggedFlag("SEXTTOEN", 23)
191                 .WithEnumField(24, 2, out speedMode, name: "SPEED",
192                     changeCallback: (previousValue, value) =>
193                     {
194                         switch(value)
195                         {
196                             case SpeedMode.Standard:
197                             case SpeedMode.Fast:
198                             case SpeedMode.HighSpeed:
199                                 break;
200                             default:
201                                 this.WarningLog("Attempted write with reserved value to SPEED (0x{0:X}), ignoring", value);
202                                 speedMode.Value = previousValue;
203                             break;
204                         }
205                     }
206                 )
207                 .WithReservedBits(26, 1)
208                 .WithTaggedFlag("SCLSM", 27)
209                 .WithTag("INACTOUT", 28, 2)
210                 .WithTaggedFlag("LOWTOUTEN", 30)
211                 .WithReservedBits(31, 1)
212             ;
213 
214             Registers.ControlB.Define(thisProvidesDoubleWordRegisterCollection)
215                 .WithReservedBits(0, 8)
216                 .WithFlag(8, out smartMode, name: "SMEN")
217                 .WithTaggedFlag("GCMD / QCEN", 9)
218                 .WithTaggedFlag("AACKEN", 10)
219                 .WithReservedBits(11, 3)
220                 .WithTag("AMODE", 14, 2)
221                 .WithValueField(16, 2, name: "CMD",
222                     valueProviderCallback: _ => 0,
223                     writeCallback: (_, value) =>
224                     {
225                         switch(value)
226                         {
227                             case 0: // NOTE: No-op
228                                 break;
229                             case 1:
230                                 RestartTransaction();
231                                 break;
232                             case 2:
233                                 // NOTE: "Execute acknowledge, then read byte"
234                                 //       As we execute actual byte read on reading the `DATA` register,
235                                 //       we can omit that here.
236                                 break;
237                             case 3:
238                                 state = State.Idle;
239                                 if(shouldFinishTransmission.Value && !smartMode.Value)
240                                 {
241                                     activePeripheral?.FinishTransmission();
242                                 }
243                                 break;
244                         }
245                     })
246                 .WithFlag(18, out shouldFinishTransmission, name: "ACKACT")
247                 .WithReservedBits(19, 5)
248                 .WithReservedBits(24, 8)
249             ;
250 
251             Registers.BaudRate.Define(thisProvidesDoubleWordRegisterCollection)
252                 .WithTag("BAUD", 0, 8)
253                 .WithTag("BAUDLOW", 8, 8)
254                 .WithTag("HSBAUD", 16, 8)
255                 .WithTag("HSBAUDLOW", 24, 8)
256             ;
257 
258             Registers.InterruptEnableClear.Define(thisProvidesByteRegisterCollection)
259                 .WithFlag(0, name: "AMATCH / MB",
260                     valueProviderCallback: _ => hostOnBusInterruptEnabled,
261                     writeCallback: (_, value) => { if(value) hostOnBusInterruptEnabled = false; })
262                 .WithFlag(1, name: "PREC / SB",
263                     valueProviderCallback: _ => clientOnBusInterruptEnabled,
264                     writeCallback: (_, value) => { if(value) clientOnBusInterruptEnabled = false; })
265                 .WithReservedBits(2, 5)
266                 .WithFlag(7, name: "ERROR",
267                     valueProviderCallback: _ => errorInterruptEnabled,
268                     writeCallback: (_, value) => { if(value) errorInterruptEnabled = false; })
269                 .WithChangeCallback((_, __) => UpdateInterrupts())
270             ;
271 
272             Registers.InterruptEnableSet.Define(thisProvidesByteRegisterCollection)
273                 .WithFlag(0, name: "AMATCH / MB",
274                     valueProviderCallback: _ => hostOnBusInterruptEnabled,
275                     writeCallback: (_, value) => { if(value) hostOnBusInterruptEnabled = true; })
276                 .WithFlag(1, name: "PREC / SB",
277                     valueProviderCallback: _ => clientOnBusInterruptEnabled,
278                     writeCallback: (_, value) => { if(value) clientOnBusInterruptEnabled = true; })
279                 .WithReservedBits(2, 5)
280                 .WithFlag(7, name: "ERROR",
281                     valueProviderCallback: _ => errorInterruptEnabled,
282                     writeCallback: (_, value) => { if(value) errorInterruptEnabled = true; })
283                 .WithChangeCallback((_, __) => UpdateInterrupts())
284             ;
285 
286             Registers.InterruptFlagStatusAndClear.Define(thisProvidesByteRegisterCollection)
287                 .WithFlag(0, out hostOnBusInterruptPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "AMATCH / MB")
288                 .WithFlag(1, out clientOnBusInterruptPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "PREC / SB")
289                 .WithReservedBits(2, 5)
290                 .WithFlag(7, out errorInterruptPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "ERROR")
291                 .WithChangeCallback((_, __) => UpdateInterrupts())
292             ;
293 
294             Registers.Status.Define(thisProvidesWordRegisterCollection)
295                 .WithTaggedFlag("BUSERR", 0)
296                 .WithTaggedFlag("COLL / ARBLOST", 1)
297                 .WithFlag(2, out acknowledgeMissing, name: "RXNACK")
298                 .WithTaggedFlag("DIR", 3)
299                 .WithTag("SR / BUSSTATE", 4, 2)
300                 .WithTaggedFlag("LOWTOUT", 6)
301                 .WithTaggedFlag("CLKHOLD", 7)
302                 .WithTaggedFlag("MEXTTOUT", 8)
303                 .WithTaggedFlag("SEXTTOUT", 9)
304                 .WithTaggedFlag("HS / LENERR", 10)
305                 .WithReservedBits(11, 5)
306             ;
307 
308             Registers.SynchronizationBusy.Define(thisProvidesDoubleWordRegisterCollection)
309                 .WithTaggedFlag("SWRST", 0)
310                 .WithTaggedFlag("ENABLE", 1)
311                 .WithTaggedFlag("SYSOP", 2)
312                 .WithReservedBits(3, 29)
313             ;
314 
315             Registers.AddressRegister.Define(thisProvidesDoubleWordRegisterCollection)
316                 .WithValueField(0, 11, name: "ADDR",
317                     writeCallback: (_, value) =>
318                     {
319                         TryFlush();
320                         var reading = (value & 0x1) > 0;
321                         var address = (int)(value >> 1);
322                         StartTransaction(address, reading);
323                     })
324                 .WithReservedBits(11, 2)
325                 .WithTaggedFlag("LENEN", 13)
326                 .WithTaggedFlag("HS", 14)
327                 .WithTaggedFlag("TENBITEN", 15)
328                 .WithTag("LEN", 16, 8)
329                 .WithReservedBits(24, 8)
330             ;
331 
332             Registers.DataRegister0.Define(thisProvidesByteRegisterCollection)
333                 .WithValueField(0, 8, name: "DATA",
334                     valueProviderCallback: _ =>
335                     {
336                         if(state == State.Reading)
337                         {
338                             clientOnBusInterruptPending.Value = true;
339                             UpdateInterrupts();
340                         }
341 
342                         var returnValue = activePeripheral?.Read(1)?.FirstOrDefault() ?? (byte)0x00;
343                         if(smartMode.Value && shouldFinishTransmission.Value)
344                         {
345                             activePeripheral?.FinishTransmission();
346                         }
347 
348                         return returnValue;
349                     },
350                     writeCallback: (_, value) =>
351                     {
352                         if(state != State.Writing)
353                         {
354                             this.Log(LogLevel.Warning, "Tried to write the DATA register while not in writing state");
355                             return;
356                         }
357 
358                         txQueue.Enqueue((byte)value);
359                         hostOnBusInterruptPending.Value = true;
360                         UpdateInterrupts();
361                     })
362             ;
363 
364             Registers.DataRegister1.Define(thisProvidesByteRegisterCollection)
365                 .WithReservedBits(0, 8)
366             ;
367 
368             Registers.DebugControl.Define(thisProvidesByteRegisterCollection)
369                 .WithTaggedFlag("DBGSTOP", 0)
370                 .WithReservedBits(1, 7)
371             ;
372         }
373 
UpdateInterrupts()374         private void UpdateInterrupts()
375         {
376             var interrupt = false;
377 
378             interrupt |= hostOnBusInterruptEnabled && hostOnBusInterruptPending.Value;
379             interrupt |= clientOnBusInterruptEnabled && clientOnBusInterruptPending.Value;
380             interrupt |= errorInterruptEnabled && errorInterruptPending.Value;
381 
382             this.Log(LogLevel.Debug, "IRQ set to: {0}", interrupt);
383             IRQ.Set(interrupt);
384         }
385 
386         private readonly DoubleWordRegisterCollection doubleWordRegistersCollection;
387         private readonly WordRegisterCollection wordRegistersCollection;
388         private readonly ByteRegisterCollection byteRegistersCollection;
389         private readonly Queue<byte> txQueue = new Queue<byte>();
390 
391         private State state;
392         private II2CPeripheral activePeripheral;
393 
394         private IFlagRegisterField enabled;
395         private IEnumRegisterField<Mode> mode;
396         private IFlagRegisterField smartMode;
397         private IEnumRegisterField<SpeedMode> speedMode;
398         private IFlagRegisterField acknowledgeMissing;
399         private IFlagRegisterField shouldFinishTransmission;
400 
401         private IFlagRegisterField hostOnBusInterruptPending;
402         private IFlagRegisterField clientOnBusInterruptPending;
403         private IFlagRegisterField errorInterruptPending;
404 
405         private bool hostOnBusInterruptEnabled;
406         private bool clientOnBusInterruptEnabled;
407         private bool errorInterruptEnabled;
408 
409         private enum State
410         {
411             Unknown,
412             Idle,
413             Reading,
414             Writing,
415         }
416 
417         private enum Mode : ulong
418         {
419             USARTExternalClock,
420             USARTInternalClock,
421             SPIClient,
422             SPIHost,
423             I2CClient,
424             I2CHost,
425             Undefined0,
426             Undefined1,
427         }
428 
429         private enum SpeedMode : ulong
430         {
431             Standard,
432             Fast,
433             HighSpeed,
434         }
435 
436         private enum Registers : long
437         {
438             ControlA = 0x0, // CTRLA
439             ControlB = 0x4, // CTRLB
440             // Reserved 0x08 - 0x0B,
441             BaudRate = 0xC, // BAUD
442             // Reserved 0x10 - 0x13,
443             InterruptEnableClear = 0x14, // INTENCLR
444             // Reserved 0x15,
445             InterruptEnableSet = 0x16, // INTENSET
446             // Reserved 0x17,
447             InterruptFlagStatusAndClear = 0x18, // INTFLAG
448             // Reserved 0x19,
449             Status = 0x1A, // STATUS
450             SynchronizationBusy = 0x1C, // SYNCBUSY
451             // Reserved 0x20 - 0x23,
452             AddressRegister = 0x24, // ADDR
453             DataRegister0 = 0x28, // DATA [ 7:0]
454             DataRegister1 = 0x29, // DATA [15:8]
455             DebugControl = 0x30 // DBGCTRL
456         }
457     }
458 }
459