1 //
2 // Copyright (c) 2010-2024 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.Collections.Generic;
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Exceptions;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Peripherals.Bus;
15 using Antmicro.Renode.Utilities;
16 
17 namespace Antmicro.Renode.Peripherals.SPI
18 {
19     public class RenesasDA_SPI : SimpleContainer<ISPIPeripheral>, IDoubleWordPeripheral, IKnownSize, IProvidesRegisterCollection<DoubleWordRegisterCollection>
20     {
RenesasDA_SPI(IMachine machine, int fifoDepth = DefaultFifoDepth)21         public RenesasDA_SPI(IMachine machine, int fifoDepth = DefaultFifoDepth) : base(machine)
22         {
23             IRQ = new GPIO();
24             receiveQueue = new Queue<byte>();
25             if(fifoDepth < 1)
26             {
27                 throw new ConstructionException($"{nameof(fifoDepth)} value cannot be less than 1, got {fifoDepth}");
28             }
29             this.fifoDepth = fifoDepth;
30 
31             RegistersCollection = new DoubleWordRegisterCollection(this, BuildRegisterMap());
32             Reset();
33         }
34 
Reset()35         public override void Reset()
36         {
37             receiveQueue.Clear();
38             IRQ.Unset();
39             RegistersCollection.Reset();
40         }
41 
ReadDoubleWord(long offset)42         public uint ReadDoubleWord(long offset)
43         {
44             return RegistersCollection.Read(offset);
45         }
46 
WriteDoubleWord(long offset, uint value)47         public void WriteDoubleWord(long offset, uint value)
48         {
49             RegistersCollection.Write(offset, value);
50         }
51 
52         public DoubleWordRegisterCollection RegistersCollection { get; }
53         public GPIO IRQ { get; }
54         public long Size => 0x100;
55         public bool ChipSelectEnableValue { get; set; }
56 
BuildRegisterMap()57         private Dictionary<long, DoubleWordRegister> BuildRegisterMap()
58         {
59             var registerMap = new Dictionary<long, DoubleWordRegister>
60             {
61                 {(long)Registers.Control, new DoubleWordRegister(this)
62                     .WithFlag(0, out spiEnabled, name: "SPI_EN")
63                     .WithFlag(1, out transmitEnabled, name: "SPI_TX_EN")
64                     .WithFlag(2, out receiveEnabled, name: "SPI_RX_EN")
65                     .WithTaggedFlag("SPI_DMA_TX_EN", 3)
66                     .WithTaggedFlag("SPI_DMA_RX_EN", 4)
67                     .WithFlag(5, FieldMode.Read | FieldMode.WriteOneToClear, name: "SPI_FIFO_RESET",
68                         changeCallback: (_, value) =>
69                         {
70                             if(!value)
71                             {
72                                 return;
73                             }
74 
75                             this.DebugLog("Resetting FIFO");
76                             receiveQueue.Clear();
77                         })
78                     .WithTaggedFlag("SPI_CAPTURE_AT_NEXT_EDGE", 6)
79                     .WithTaggedFlag("SPI_SWAP_BYTES", 7)
80                     .WithReservedBits(8, 24)
81                 },
82 
83                 {(long)Registers.Clock, new DoubleWordRegister(this)
84                     .WithTag("SPI_CLK_DIV", 0, 7)
85                     .WithReservedBits(7, 25)
86                 },
87 
88                 {(long)Registers.Configuration, new DoubleWordRegister(this)
89                     .WithTag("SPI_MODE", 0, 2)
90                     .WithValueField(2, 5, out wordBits, name: "SPI_WORD_LENGTH",
91                         changeCallback: (_, value) =>
92                         {
93                             var length = value + WordBitsOffset;
94                             if(length != 8)
95                             {
96                                 this.ErrorLog("Word length of {0} bits is currently not supported. Reverting to word length of 8 bits");
97                                 wordBits.Value = length - WordBitsOffset;
98                             }
99                         })
100                     .WithTaggedFlag("SPI_SLAVE_EN", 7)
101                     .WithReservedBits(8, 24)
102                 },
103 
104                 {(long)Registers.FifoConfiguration, new DoubleWordRegister(this)
105                     .WithTag("SPI_TX_TL", 0, 4)
106                     .WithValueField(4, 4, out receiveFIFOThresholdLevel, name: "SPI_RX_TL", changeCallback: (_, __) => UpdateInterrupts())
107                     .WithReservedBits(8, 24)
108                 },
109 
110                 {(long)Registers.InterruptMask, new DoubleWordRegister(this)
111                     .WithFlag(0, out fifoTransmitEmptyInterruptEnabled, name: "SPI_IRQ_MASK_TX_EMPTY")
112                     .WithFlag(1, out fifoReceiveFullInterruptEnabled, name: "SPI_IRQ_MASK_RX_FULL")
113                     .WithReservedBits(2, 30)
114                     .WithChangeCallback((_, __) => UpdateInterrupts())
115                 },
116 
117                 {(long)Registers.Status, new DoubleWordRegister(this)
118                     .WithFlag(0, FieldMode.Read, name: "SPI_STATUS_TX_EMPTY", valueProviderCallback: _ => true)
119                     .WithFlag(1, FieldMode.Read, name: "SPI_STATUS_RX_FULL", valueProviderCallback: _ => IsReceiveFIFOFull)
120                     .WithReservedBits(2, 30)
121                 },
122 
123                 {(long)Registers.FifoStatus, new DoubleWordRegister(this)
124                     .WithValueField(0, 6, FieldMode.Read, name: "SPI_RX_FIFO_LEVEL", valueProviderCallback: _ => (ulong)Math.Min(fifoDepth, receiveQueue.Count))
125                     .WithValueField(6, 6, FieldMode.Read, name: "SPI_TX_FIFO_LEVEL", valueProviderCallback: _ => 0)
126                     .WithFlag(12, FieldMode.Read, name: "SPI_STATUS_RX_EMPTY", valueProviderCallback: _ => receiveQueue.Count == 0)
127                     .WithFlag(13, FieldMode.Read, name: "SPI_STATUS_TX_FULL", valueProviderCallback: _ => false)
128                     .WithFlag(14, FieldMode.Read, name: "SPI_RX_FIFO_OVFL", valueProviderCallback: _ => false)
129                     .WithFlag(15, FieldMode.Read, name: "SPI_TRANSACTION_ACTIVE", valueProviderCallback: _ => false)
130                     .WithReservedBits(16, 16)
131                 },
132 
133                 {(long)Registers.FifoRead, new DoubleWordRegister(this)
134                     .WithValueField(0, 32, FieldMode.Read, name: "SPI_FIFO_READ",
135                         valueProviderCallback: _ =>
136                         {
137                             if(!spiEnabled.Value)
138                             {
139                                 this.WarningLog("SPI Module is disabled, returning 0");
140                                 return 0;
141                             }
142 
143                             if(!receiveEnabled.Value)
144                             {
145                                 this.WarningLog("Reception is disabled, returning 0");
146                                 return 0;
147                             }
148 
149                             if(!receiveQueue.TryDequeue(out byte result))
150                             {
151                                 this.WarningLog("Reading from a empty receive FIFO, returning 0");
152                                 return 0x0;
153                             }
154                             UpdateInterrupts();
155                             return result;
156                         })
157                 },
158 
159                 {(long)Registers.FifoWrite, new DoubleWordRegister(this)
160                     .WithValueField(0, 32, FieldMode.Write, name: "SPI_FIFO_WRITE",
161                         writeCallback: (_, value) =>
162                         {
163                             if(!spiEnabled.Value)
164                             {
165                                 this.WarningLog("SPI Module is disabled, returning");
166                                 return;
167                             }
168 
169                             if(!transmitEnabled.Value)
170                             {
171                                 this.WarningLog("Transmission is disabled, returning");
172                                 return;
173                             }
174 
175                             if(selectedPeripheral == null)
176                             {
177                                 this.WarningLog("No peripheral is selected, returning");
178                                 return;
179                             }
180 
181                             receiveQueue.Enqueue(selectedPeripheral.Transmit((byte)value));
182                             UpdateInterrupts();
183                         })
184                 },
185 
186                 {(long)Registers.CsConfiguration, new DoubleWordRegister(this)
187                     .WithEnumField<DoubleWordRegister, ChipSelect>(0, 3, name: "SPI_CS_SELECT",
188                         changeCallback: (_, value) => SwitchActivePeripheral(value))
189                     .WithReservedBits(3, 29)
190                 },
191 
192                 {(long)Registers.TXBufferForce, new DoubleWordRegister(this)
193                     .WithTag("SPI_TXBUFFER_FORCE", 0, 32)
194                 }
195             };
196 
197             return registerMap;
198         }
199 
UpdateInterrupts()200         private void UpdateInterrupts()
201         {
202             var state = fifoTransmitEmptyInterruptEnabled.Value || (fifoReceiveFullInterruptEnabled.Value && IsReceiveFIFOFull);
203             this.DebugLog("IRQ {0}", state ? "set" : "unset");
204             IRQ.Set(state);
205         }
206 
SwitchActivePeripheral(ChipSelect value)207         private void SwitchActivePeripheral(ChipSelect value)
208         {
209             switch(value)
210             {
211                 case ChipSelect.None:
212                 {
213                     if(selectedPeripheral is IGPIOReceiver receiver)
214                     {
215                         receiver.OnGPIO(0, !ChipSelectEnableValue);
216                     }
217                     else
218                     {
219                         selectedPeripheral?.FinishTransmission();
220                     }
221                     selectedPeripheral = null;
222                     return;
223                 }
224                 case ChipSelect.SPIEnable:
225                 case ChipSelect.SPIEnable2:
226                 case ChipSelect.GPIO:
227                 {
228                     if(selectedPeripheral is IGPIOReceiver oldReceiver)
229                     {
230                         oldReceiver.OnGPIO(0, !ChipSelectEnableValue);
231                     }
232                     else
233                     {
234                         selectedPeripheral?.FinishTransmission();
235                     }
236 
237                     var address = ChipSelectToAddress(value);
238                     if(!TryGetByAddress(address, out var peripheral))
239                     {
240                         this.WarningLog("No peripheral with address {0} exists (Chip select: {1})", address, value);
241                         selectedPeripheral = null;
242                         return;
243                     }
244 
245                     if(peripheral is IGPIOReceiver newReceiver)
246                     {
247                         newReceiver.OnGPIO(0, ChipSelectEnableValue);
248                     }
249 
250                     selectedPeripheral = peripheral;
251                     return;
252                 }
253                 default:
254                     this.ErrorLog("Invalid chip select value 0x{0:X}", value);
255                     return;
256             }
257         }
258 
ChipSelectToAddress(ChipSelect chipSelect)259         private int ChipSelectToAddress(ChipSelect chipSelect)
260         {
261             switch(chipSelect)
262             {
263                 case ChipSelect.SPIEnable:
264                     return 1;
265                 case ChipSelect.SPIEnable2:
266                     return 2;
267                 case ChipSelect.GPIO:
268                     return 3;
269                 default:
270                     return -1;
271             }
272         }
273 
274         private bool IsReceiveFIFOFull => (ulong)receiveQueue.Count >= (receiveFIFOThresholdLevel.Value + 1);
275 
276         private readonly Queue<byte> receiveQueue;
277         private readonly int fifoDepth;
278 
279         private ISPIPeripheral selectedPeripheral;
280 
281         private IFlagRegisterField spiEnabled;
282         private IFlagRegisterField transmitEnabled;
283         private IFlagRegisterField receiveEnabled;
284         private IFlagRegisterField fifoTransmitEmptyInterruptEnabled;
285         private IFlagRegisterField fifoReceiveFullInterruptEnabled;
286         private IValueRegisterField receiveFIFOThresholdLevel;
287         private IValueRegisterField wordBits;
288 
289         private const int DefaultFifoDepth = 4;
290         private const int WordBitsOffset = 1;
291 
292         public enum Registers
293         {
294             Control           = 0x0,  // SPI_CTRL_REG
295             Configuration     = 0x4,  // SPI_CONFIG_REG
296             Clock             = 0x8,  // SPI_CLOCK_REG
297             FifoConfiguration = 0xC,  // SPI_FIFO_CONFIG_REG
298             InterruptMask     = 0x10, // SPI_IRQ_MASK_REG
299             Status            = 0x14, // SPI_STATUS_REG
300             FifoStatus        = 0x18, // SPI_FIFO_STATUS_REG
301             FifoRead          = 0x1C, // SPI_FIFO_READ_REG
302             FifoWrite         = 0x20, // SPI_FIFO_WRITE_REG
303             CsConfiguration   = 0x24, // SPI_CS_CONFIG_REG
304             TXBufferForce     = 0x2C, // SPI_TXBUFFER_FORCE_REG
305         }
306 
307         private enum ChipSelect
308         {
309             None        = 0,
310             SPIEnable   = 1,
311             SPIEnable2  = 2,
312             GPIO        = 4,
313         }
314     }
315 }
316