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 
16 namespace Antmicro.Renode.Peripherals.SPI
17 {
18     public class HiFive_SPI : SimpleContainer<ISPIPeripheral>, IDoubleWordPeripheral, IKnownSize
19     {
HiFive_SPI(IMachine machine, bool isFlashEnabled = false, int numberOfSupportedSlaves = 1)20         public HiFive_SPI(IMachine machine, bool isFlashEnabled = false, int numberOfSupportedSlaves = 1) : base(machine)
21         {
22             if(numberOfSupportedSlaves < 1 || numberOfSupportedSlaves > 32)
23             {
24                 throw new ConstructionException($"Wrong number of supported slaves: {numberOfSupportedSlaves}. Provide a value in range from 1 to 32.");
25             }
26 
27             this.csWidth = numberOfSupportedSlaves;
28             receiveQueue = new Queue<byte>();
29             IRQ = new GPIO();
30 
31             var registersMap = new Dictionary<long, DoubleWordRegister>()
32             {
33                 {(long)Registers.SerialClockDivisor, new DoubleWordRegister(this, 0x3)
34                     .WithValueField(0, 12, name: "div")
35                     .WithReservedBits(12, 20)
36                 },
37                 {(long)Registers.SerialClockMode, new DoubleWordRegister(this, 0x0)
38                     .WithFlag(0, name: "pha")
39                     .WithFlag(1, name: "pol")
40                     .WithReservedBits(2, 30)
41                 },
42                 {(long)Registers.ChipSelectId, new DoubleWordRegister(this)
43                     .WithValueField(0, 32, out selectedSlave, name: "csid", writeCallback: (previousValue, value) =>
44                     {
45                         if(!ChildCollection.ContainsKey(checked((int)value)))
46                         {
47                             this.Log(LogLevel.Warning, "Selected pin {0}, but there is no device connected to it.", value);
48                         }
49 
50                         if(previousValue != value)
51                         {
52                             FinishTransmission();
53                         }
54                         else
55                         {
56                             ClearReceiveQueue();
57                         }
58                     })
59                 },
60 
61                 {(long)Registers.ChipSelectDefault, new DoubleWordRegister(this, (1u << numberOfSupportedSlaves) - 1)
62                     // this field's width and reset value depend on a constructor parameter `numberOfSupportedSlaves`
63                     .WithValueField(0, numberOfSupportedSlaves, name: "csdef")
64                 },
65 
66                 {(long)Registers.ChipSelectMode, new DoubleWordRegister(this)
67                     .WithEnumField<DoubleWordRegister, ChipSelectMode>(0, 2, name: "csmode",
68                         writeCallback: (_, val) =>
69                         {
70                             // means off
71                             if(val == ChipSelectMode.Auto)
72                             {
73                                 FinishTransmission();
74                             }
75                         })
76                 },
77 
78                 {(long)Registers.FrameFormat, new DoubleWordRegister(this, 0x80000)
79                     .WithValueField(0, 2, out spiProtocol, name: "proto", writeCallback: (oldValue, value) =>
80                     {
81                         if(value != 0x0)
82                         {
83                             this.WarningLog("Only single SPI protocol (value 0x0) is supported");
84                             spiProtocol.Value = oldValue;
85                         }
86                     })
87                     .WithFlag(2, out spiEndianess, name: "endian", writeCallback: (oldValue, value) =>
88                     {
89                         if(value)
90                         {
91                             this.WarningLog("Only transmitting MSB first (value 0x0) is supported");
92                             spiEndianess.Value = oldValue;
93                         }
94                     })
95                     .WithFlag(3, out spiIODirection, name: "dir", writeCallback: (oldValue, value) =>
96                     {
97                         if(value)
98                         {
99                             this.WarningLog("Only Rx direction (value 0x0) is supported");
100                             spiIODirection.Value = oldValue;
101                         }
102                     })
103                     .WithReservedBits(4, 12)
104                     .WithValueField(16, 4, out numberOfBitsPerFrame, name: "len", writeCallback: (oldValue, value) =>
105                     {
106                         if(value != 0x8)
107                         {
108                             this.WarningLog("Only 8 bits per frame (value 0x8) are supported");
109                             numberOfBitsPerFrame.Value = oldValue;
110                         }
111                     })
112                     .WithReservedBits(20, 12)
113                 },
114 
115                 {(long)Registers.TxFifoData, new DoubleWordRegister(this, 0x0)
116                     .WithValueField(0, 8, FieldMode.Write, writeCallback: (_, value) => HandleFifoWrite((byte)value), name: "data")
117                     .WithReservedBits(8, 23)
118                     .WithFlag(31, FieldMode.Read, valueProviderCallback: _ => false, name: "full")
119                 },
120 
121                 {(long)Registers.RxFifoData, new DoubleWordRegister(this, 0x0)
122                     // According to the documentation this registers is divided into two fields and a reserved block.
123                     // I decided not to split it because the value of both fields must be evaluated IN PROPER ORDER
124                     // (flag before dequeuing) and currently the API does not guarantee that.
125                     .WithValueField(0, 32, FieldMode.Read, name: "data+empty", valueProviderCallback: _ =>
126                     {
127                         var result = (receiveQueue.Count == 0)
128                             ? FifoEmptyMarker
129                             : receiveQueue.Dequeue();
130 
131                         UpdateInterrupts();
132                         return result;
133                     })
134                 },
135 
136                 {(long)Registers.TxFifoWatermark, new DoubleWordRegister(this, isFlashEnabled ? 0x1 : 0x0u)
137                     .WithValueField(0, 3, out transmitWatermark, writeCallback: (_, __) => UpdateInterrupts(), name: "txmark")
138                     .WithReservedBits(3, 29)
139                 },
140 
141                 {(long)Registers.RxFifoWatermark, new DoubleWordRegister(this, 0x0)
142                     .WithValueField(0, 3, out receiveWatermark, writeCallback: (_, __) => UpdateInterrupts(), name: "rxmark")
143                     .WithReservedBits(3, 29)
144                 },
145 
146                 {(long)Registers.InterruptEnable, new DoubleWordRegister(this, 0x0)
147                     .WithFlag(0, out transmitWatermarkInterruptEnable, writeCallback: (_, __) => UpdateInterrupts(), name: "txwm")
148                     .WithFlag(1, out receiveWatermarkInterruptEnable, writeCallback: (_, __) => UpdateInterrupts(), name: "rxwm")
149                     .WithReservedBits(2, 30)
150                 },
151 
152                 {(long)Registers.InterruptPending, new DoubleWordRegister(this, 0x0)
153                     .WithFlag(0, out transmitWatermarkPending, FieldMode.Read, name: "txwm")
154                     .WithFlag(1, out receiveWatermarkPending, FieldMode.Read, name: "rxwm")
155                     .WithReservedBits(2, 30)
156                 }
157             };
158 
159             registers = new DoubleWordRegisterCollection(this, registersMap);
160         }
161 
Register(ISPIPeripheral peripheral, NumberRegistrationPoint<int> registrationPoint)162         public override void Register(ISPIPeripheral peripheral, NumberRegistrationPoint<int> registrationPoint)
163         {
164             if(registrationPoint.Address < 0 || registrationPoint.Address > csWidth - 1)
165             {
166                 throw new RecoverableException($"Wrong registration point: {registrationPoint}. Provide address in range from 0 to {csWidth - 1}.");
167             }
168 
169             base.Register(peripheral, registrationPoint);
170         }
171 
Reset()172         public override void Reset()
173         {
174             registers.Reset();
175             UpdateInterrupts();
176         }
177 
ReadDoubleWord(long offset)178         public uint ReadDoubleWord(long offset)
179         {
180             return registers.Read(offset);
181         }
182 
WriteDoubleWord(long offset, uint value)183         public void WriteDoubleWord(long offset, uint value)
184         {
185             registers.Write(offset, value);
186         }
187 
188         public long Size => 0x78;
189 
190         public GPIO IRQ { get; private set; }
191 
HandleFifoWrite(byte value)192         private void HandleFifoWrite(byte value)
193         {
194             receiveQueue.Enqueue(!TryGetByAddress((int)selectedSlave.Value, out var slavePeripheral)
195                 ? (byte)0
196                 : slavePeripheral.Transmit(value));
197 
198             UpdateInterrupts();
199         }
200 
UpdateInterrupts()201         private void UpdateInterrupts()
202         {
203             transmitWatermarkPending.Value = (transmitWatermark.Value > 0);
204             receiveWatermarkPending.Value = (receiveQueue.Count > (int)receiveWatermark.Value);
205 
206             IRQ.Set((transmitWatermarkInterruptEnable.Value && transmitWatermarkPending.Value)
207                 || (receiveWatermarkInterruptEnable.Value && receiveWatermarkPending.Value));
208         }
209 
ClearReceiveQueue()210         private void ClearReceiveQueue()
211         {
212             if(receiveQueue.Count > 0)
213             {
214                 this.Log(LogLevel.Warning, "Clearing receive queue, but there are still {0} bytes there that will be lost...", receiveQueue.Count);
215             }
216 
217             receiveQueue.Clear();
218             UpdateInterrupts();
219         }
220 
FinishTransmission()221         private void FinishTransmission()
222         {
223             if(TryGetByAddress((int)selectedSlave.Value, out var slavePeripheral))
224             {
225                 slavePeripheral.FinishTransmission();
226             }
227 
228             ClearReceiveQueue();
229         }
230 
231         private readonly int csWidth;
232         private readonly Queue<byte> receiveQueue;
233         private readonly IFlagRegisterField transmitWatermarkInterruptEnable;
234         private readonly IFlagRegisterField receiveWatermarkInterruptEnable;
235         private readonly IFlagRegisterField transmitWatermarkPending;
236         private readonly IFlagRegisterField receiveWatermarkPending;
237         private readonly IValueRegisterField transmitWatermark;
238         private readonly IValueRegisterField receiveWatermark;
239         private readonly IValueRegisterField selectedSlave;
240         private readonly IValueRegisterField spiProtocol;
241         private readonly IFlagRegisterField spiEndianess;
242         private readonly IFlagRegisterField spiIODirection;
243         private readonly IValueRegisterField numberOfBitsPerFrame;
244         private readonly DoubleWordRegisterCollection registers;
245 
246         private const uint FifoEmptyMarker = 0x80000000;
247 
248         private enum Registers
249         {
250             SerialClockDivisor = 0x0,
251             SerialClockMode = 0x4,
252             // 0x8, 0xC - reserved
253             ChipSelectId = 0x10,
254             ChipSelectDefault = 0x14,
255             ChipSelectMode = 0x18,
256             // 0x1C, 0x20, 0x24 - reserved
257             DelayControl0 = 0x28,
258             DelayControl1 = 0x2C,
259             // 0x30, 0x34, 0x38, 0x3C - reserved
260             FrameFormat = 0x40,
261             // 0x44 - reserved
262             TxFifoData = 0x48,
263             RxFifoData = 0x4C,
264             TxFifoWatermark = 0x50,
265             RxFifoWatermark = 0x54,
266             // 0x58, 0x5C - reserved
267             FlashInterfaceControl = 0x60,
268             FlashInstructionFormat = 0x64,
269             // 0x68, 0x6C - reserved
270             InterruptEnable = 0x70,
271             InterruptPending = 0x74
272         }
273 
274         private enum ChipSelectMode
275         {
276             Auto = 0x0, // Assert/deassert CS at the beginning/end of each frame
277             // 0x1 is undefined in the documentation
278             Hold = 0x2, // Keep CS continously asserted after the initial frame
279             Off = 0x3 // Disable hardware control of the CS pin
280         }
281     }
282 }
283