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.Collections.Generic;
8 using Antmicro.Renode.Core;
9 using Antmicro.Renode.Core.Structure;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Peripherals.Bus;
13 
14 namespace Antmicro.Renode.Peripherals.SPI
15 {
16     public sealed class Quark_SPI : NullRegistrationPointPeripheralContainer<ISPIPeripheral>, IDoubleWordPeripheral, IKnownSize
17     {
Quark_SPI(IMachine machine)18         public Quark_SPI(IMachine machine) : base(machine)
19         {
20             IRQ = new GPIO();
21             CreateRegisters();
22             Reset();
23         }
24 
Reset()25         public override void Reset()
26         {
27             registers.Reset();
28             RefreshInterrupt();
29         }
30 
ReadDoubleWord(long offset)31         public uint ReadDoubleWord(long offset)
32         {
33             return registers.Read(offset);
34         }
35 
WriteDoubleWord(long offset, uint value)36         public void WriteDoubleWord(long offset, uint value)
37         {
38             registers.Write(offset, value);
39         }
40 
41         public long Size
42         {
43             get { return 0xFF; }
44         }
45 
46         public GPIO IRQ { get; set; }
47 
CreateRegisters()48         private void CreateRegisters()
49         {
50             var registersMap = new Dictionary<long, DoubleWordRegister>
51             {
52                 /*
53                  * Although some registers are locked if ssiEnabled is set, we do not have an abstraction to define it here.
54                  */
55                 {(long)Registers.Control0, new DoubleWordRegister(this, 0x70000)
56                     .WithTag("Frame Format FRF", 4, 2) //rw
57                     .WithTag("Serial Clock Phase SCPH", 6, 1) //rwl
58                     .WithTag("Serial Clock Polarity SCPOL", 7, 1) //rwl
59                     .WithEnumField(8, 2, out transferMode, FieldMode.Read | FieldMode.Write, name:"Transfer Mode TMOD") //rwl
60                     .WithTag("Slave Output Enable SLV_OE", 10, 1) //rwl
61                     .WithTag("Shift Register Loop SRL", 11, 1) //rwl
62                     .WithTag("Control Frame Size CFS", 12, 4) //rwl
63                     .WithTag("Data Frame Size in 32-bit mode DFS_32", 16, 7) //rwl
64 
65                 },
66                 {(long)Registers.Control1, new DoubleWordRegister(this, 0)
67                     .WithValueField(0, 16, name: "Number of Data Frames NDF")
68                 },
69                 {(long)Registers.SSIEnable, new DoubleWordRegister(this, 0)
70                     .WithFlag(0, out ssiEnabled, FieldMode.Read | FieldMode.Write, writeCallback: DisableSSI, name: "SSI Enable SSIENR")
71                 },
72                 {(long)Registers.SlaveEnable, new DoubleWordRegister(this, 0)
73                     .WithValueField(0, 4, changeCallback: (oldValue, newValue) => {
74                             if(newValue != 1 && newValue != 0)
75                             {
76                                 this.Log(LogLevel.Warning, "Unhandled write to slave enable.");
77                             }
78                         })
79                 },
80                 {(long)Registers.BaudRateSelect, new DoubleWordRegister(this, 0)
81                     .WithValueField(0, 15, name: "SSI Clock Divider SCKDV")
82                 },
83                 {(long)Registers.TransmitFIFOThresholdLevel, new DoubleWordRegister(this, 0)
84                     .WithValueField(0, 3, name: "Transmit FIFO Threshold TXFTLR") // it does not matter since our transmission ends immediately
85                 },
86                 {(long)Registers.ReceiveFIFOThresholdLevel, new DoubleWordRegister(this, 0)
87                     .WithValueField(0, 3, out receiveFifoInterruptThreshold, name: "Receive FIFO Threshold RFT")
88                     .WithValueField(3, 29, name: "Reserved") // reserved but written
89                 },
90                 {(long)Registers.TransmitFIFOLevel, new DoubleWordRegister(this, 0)
91                     .WithValueField(0, 4, FieldMode.Read, valueProviderCallback: (_) => 0, name: "Transmit FIFO Level TXTFL")
92                 },
93                 {(long)Registers.ReceiveFIFOLevel, new DoubleWordRegister(this, 0)
94                     .WithValueField(0, 4, FieldMode.Read, valueProviderCallback: (_) => (uint)receiveFifo.Count, name: "Receive FIFO Level RXFLR")
95                 },
96                 {(long)Registers.Status, new DoubleWordRegister(this, 6)
97                     .WithTag("SSI Busy BUSY", 0, 1)//ro, should probably always read 0
98                     .WithFlag(1, FieldMode.Read, name: "Transmit FIFO Not Full TFNF")
99                     .WithFlag(2, FieldMode.Read, name: "Transmit FIFO Empty TFE") // transmit fifo is always empty
100                     .WithFlag(3, FieldMode.Read, valueProviderCallback: x => receiveFifo.Count > 0, name: "Receive FIFO Not Empty RFNE")
101                     .WithTag("Receive FIFO Full RFF", 4, 1) //ro
102                     .WithTag("Transmission Error TXE", 5, 1) //ro
103                 },
104                 {(long)Registers.InterruptMask, new DoubleWordRegister(this, 3)
105                     .WithFlag(0, out transmitFifoEmptyInterruptEnabled, name: "Transmit FIFO Empty Interrupt Mask TXEIM")
106                     .WithFlag(1, name: "Transmit FIFO Overflow Interrupt Mask TXOIM")
107                     .WithFlag(2, name: "Receive FIFO Underflow Interrupt Mask RXUIM")
108                     .WithFlag(3, name: "Receive FIFO Overflow Interrupt Mask RXUIM")
109                     .WithFlag(4, out receiveFifoFullInterruptEnabled, name: "Receive FIFO Full Interrupt Mask RXFIM")
110                     .WithWriteCallback((_, __) => RefreshInterrupt())
111                 },
112                 {(long)Registers.InterruptStatus, new DoubleWordRegister(this, 0)
113                     .WithFlag(0, FieldMode.Read, valueProviderCallback: x => transmitFifoEmptyInterruptEnabled.Value, name: "Transmit FIFO Empty Interrupt Status TXEIS")
114                     .WithFlag(1, FieldMode.Read, name: "Transmit FIFO Overflow Interrupt Status TXOIS")
115                     .WithFlag(2, FieldMode.Read, name: "Receive FIFO Underflow Interrupt Status RXUIS")
116                     .WithFlag(3, FieldMode.Read, name: "Receive FIFO Overflow Interrupt Status RXUIS")
117                     .WithFlag(4, FieldMode.Read, valueProviderCallback: x => receiveFifo.Count > (int)receiveFifoInterruptThreshold.Value && receiveFifoFullInterruptEnabled.Value, name: "Receive FIFO Full Interrupt Status RXFIS")
118                 },
119                 {(long)Registers.RawInterruptStatus, new DoubleWordRegister(this, 0)
120                     .WithFlag(0, FieldMode.Read, valueProviderCallback: x => true, name: "Transmit FIFO Empty Raw Interrupt Status TXEIR")
121                     .WithTag("Transmit FIFO Overflow Raw Interrupt Status TXOIR", 1, 1) //ro
122                     .WithTag("Receive FIFO Underflow Raw Interrupt Status RXUIR", 2, 1) //ro
123                     .WithTag("Receive FIFO Overflow Raw Interrupt Status RXUIR", 3, 1) //ro
124                     .WithFlag(4, out receiveFifoFullRawInterrupt, FieldMode.Read, valueProviderCallback: x => receiveFifo.Count > (int)receiveFifoInterruptThreshold.Value, name: "Receive FIFO Full Raw Interrupt Status RXFIR")
125                 }
126             };
127             var dataRegister = new DoubleWordRegister(this, 0).WithValueField(0, 16, writeCallback: (_, val) => WriteData((uint)val), valueProviderCallback: _ => ReadData());
128             for(var i = 0; i < 36; i++)
129             {
130                 //the fifo elements are not addressable, so reading/writing to any of these has the same effect -> it can be a single register.
131                 registersMap.Add((long)Registers.Data + i, dataRegister);
132             }
133 
134             registers = new DoubleWordRegisterCollection(this, registersMap);
135         }
136 
ReadData()137         private uint ReadData()
138         {
139             return receiveFifo.Count > 0 ? receiveFifo.Dequeue() : (byte)0x00;
140         }
141 
WriteData(uint data)142         private void WriteData(uint data)
143         {
144             if(!ssiEnabled.Value)
145             {
146                 return;
147             }
148             if(transferMode.Value == TransferMode.ReceiveOnly || transferMode.Value == TransferMode.EEPROMRead)
149             {
150                 // note that number of data frames (NDF field in second control register) is important in this transfer mode
151                 // see datasheet for details
152                 this.Log(LogLevel.Error, "Unhandled transfer mode {0}.", transferMode);
153             }
154             RefreshInterrupt(true); // although we immediately transfer the byte, the interrupt line has to be turned off for the moment
155             var result = RegisteredPeripheral.Transmit((byte)data);
156             if(transferMode.Value == TransferMode.TransmitAndReceive)
157             {
158                 receiveFifo.Enqueue(result);
159             }
160 
161             RefreshInterrupt();
162         }
163 
DisableSSI(bool oldValue, bool newValue)164         private void DisableSSI(bool oldValue, bool newValue)
165         {
166             this.Log(LogLevel.Debug, "SSI {0}.", newValue ? "enabled" : "disabled");
167             receiveFifo.Clear();
168             RefreshInterrupt();
169             if(!newValue && oldValue)
170             {
171                 RegisteredPeripheral.FinishTransmission();
172             }
173         }
174 
RefreshInterrupt(bool transmitFifoEmptySuppressed = false)175         private void RefreshInterrupt(bool transmitFifoEmptySuppressed = false)
176         {
177             var value = ssiEnabled.Value && ((!transmitFifoEmptySuppressed && transmitFifoEmptyInterruptEnabled.Value) ||
178                                              (receiveFifoFullRawInterrupt.Value && receiveFifoFullInterruptEnabled.Value));
179             IRQ.Set(value);
180         }
181 
182         private DoubleWordRegisterCollection registers;
183 
184         private IFlagRegisterField ssiEnabled;
185         private IEnumRegisterField<TransferMode> transferMode;
186 
187         private IFlagRegisterField transmitFifoEmptyInterruptEnabled;
188         private IFlagRegisterField receiveFifoFullInterruptEnabled;
189         private IFlagRegisterField receiveFifoFullRawInterrupt;
190         private IValueRegisterField receiveFifoInterruptThreshold;
191 
192         private readonly Queue<byte> receiveFifo = new Queue<byte>();
193 
194         private enum TransferMode
195         {
196             TransmitAndReceive = 0x0,
197             TransmitOnly = 0x1,
198             ReceiveOnly = 0x2,
199             EEPROMRead = 0x3
200         }
201 
202         private enum Registers
203         {
204             Control0 = 0x0,
205             Control1 = 0x4,
206             SSIEnable = 0x8,
207             MicrowireControl = 0xC,
208             SlaveEnable = 0x10,
209             BaudRateSelect = 0x14,
210             TransmitFIFOThresholdLevel = 0x18,
211             ReceiveFIFOThresholdLevel = 0x1C,
212             TransmitFIFOLevel = 0x20,
213             ReceiveFIFOLevel = 0x24,
214             Status = 0x28,
215             InterruptMask = 0x2C,
216             InterruptStatus = 0x30,
217             RawInterruptStatus = 0x34,
218             TransmitFIFOOverflowInterruptClear = 0x38,
219             ReceiveFIFOOverflowInterruptClear = 0x3C,
220             ReceiveFIFOUnderflowInterruptClear = 0x40,
221             MultiMasterInterruptClear = 0x44, //only reserved fields?
222             InterruptClear = 0x48,
223             DMAControl = 0x4C,
224             DMATransmitDataLevel = 0x50,
225             DMAReceiveDataLevel = 0x54,
226             Identification = 0x58,
227             CoreKitVersionID = 0x5C,
228             Data = 0x60, //DR0 up to DR35 at 0xEC
229             RXSampleDelay = 0xF0
230         }
231     }
232 }
233