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 using Antmicro.Renode.Utilities.Collections;
14 
15 namespace Antmicro.Renode.Peripherals.SPI
16 {
17     public class CC2538_SSI : NullRegistrationPointPeripheralContainer<ISPIPeripheral>, IDoubleWordPeripheral, IKnownSize
18     {
CC2538_SSI(IMachine machine)19         public CC2538_SSI(IMachine machine) : base(machine)
20         {
21             IRQ = new GPIO();
22 
23             rxFifo = new CircularBuffer<byte>(FifoCapacity);
24             txFifo = new CircularBuffer<byte>(FifoCapacity);
25 
26             var regs = new Dictionary<long, DoubleWordRegister>
27             {
28                 { (long)Registers.Control0, new DoubleWordRegister(this)
29                     .WithValueField(0, 4, writeCallback: (_, value) => {
30                         if(value <= 2)
31                         {
32                             this.Log(LogLevel.Warning, "Trying to set as reserved value of DSS: {0}", value);
33                         }
34                         else if(value != 7) //8-bit data
35                         {
36                             this.Log(LogLevel.Warning, "An unsupported data size {0} set, only 7 (8 bits) is legal.", value);
37                         }
38                     }, name: "DSS")
39                     .WithTag("FRF", 4, 2)
40                     .WithTaggedFlag("SPO", 6)
41                     .WithTaggedFlag("SPH", 7)
42                     .WithTag("SCR", 8, 8)
43                     .WithReservedBits(16, 16)
44                 },
45                 { (long)Registers.Control1, new DoubleWordRegister(this)
46                     .WithTaggedFlag("LBM", 0)
47                     .WithFlag(1, out enabled, writeCallback: EnableTransmitter, name: "SSE")
48                     .WithTaggedFlag("MS", 2)
49                     .WithTaggedFlag("SOD", 3)
50                     .WithReservedBits(4, 28)
51                 },
52                 { (long)Registers.Data, new DoubleWordRegister(this)
53                     .WithValueField(0, 16, valueProviderCallback: _ => rxFifo.TryDequeue(out var val) ? val : 0u, writeCallback: (_, value) => SendData((uint)value), name: "DATA")
54                     .WithReservedBits(16, 16)
55                     .WithReadCallback((_, __) => Update())
56                 },
57                 { (long)Registers.Status, new DoubleWordRegister(this)
58                     .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => txFifo.Count == 0, name: "TFE")
59                     .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => txFifo.Count != txFifo.Capacity, name: "TNF")
60                     .WithFlag(2, FieldMode.Read, valueProviderCallback: _ => rxFifo.Count != 0, name: "RNE")
61                     .WithFlag(3, FieldMode.Read, valueProviderCallback: _ => rxFifo.Count == rxFifo.Capacity, name: "RFF")
62                     .WithFlag(4, FieldMode.Read, valueProviderCallback: _ => txFifo.Count != 0, name: "BSY") //SSI is currently transmitting and/or
63                         //receiving a frame or the transmit fifo is not empty. Only the last part is applicable
64                     .WithReservedBits(5, 27)
65                 },
66                 { (long)Registers.InterruptMask, new DoubleWordRegister(this)
67                     .WithFlag(0, out rxFifoOverrunInterruptMask, name: "RORIM")
68                     .WithTaggedFlag("RTIM", 1)
69                     .WithFlag(2, out rxFifoInterruptMask, name: "RXIM")
70                     .WithFlag(3, out txFifoInterruptMask, name: "TXIM")
71                     .WithReservedBits(4, 28)
72                     .WithWriteCallback((_, __) => Update())
73                 },
74                 { (long)Registers.RawInterruptStatus, new DoubleWordRegister(this)
75                     .WithFlag(0, out rxFifoOverrunInterrupt, FieldMode.Read, name: "RORRIS")
76                     .WithTaggedFlag("RTRIS", 1)
77                     .WithFlag(2, out rxFifoInterrupt, FieldMode.Read, name: "RXRIS")
78                     .WithFlag(3, out txFifoInterrupt, FieldMode.Read, name: "TXRIS")
79                     .WithReservedBits(4, 28)
80                 },
81                 { (long)Registers.MaskedInterruptStatus, new DoubleWordRegister(this)
82                     .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => rxFifoOverrunInterrupt.Value && rxFifoOverrunInterruptMask.Value, name: "RORMIS")
83                     .WithTaggedFlag("RTMIS", 1)
84                     .WithFlag(2, FieldMode.Read, valueProviderCallback: _ => rxFifoInterrupt.Value && rxFifoInterruptMask.Value, name: "RXMIS")
85                     .WithFlag(3, FieldMode.Read, valueProviderCallback: _ => txFifoInterrupt.Value && txFifoInterruptMask.Value, name: "TXMIS")
86                     .WithReservedBits(4, 28)
87                 },
88                 { (long)Registers.InterruptClear, new DoubleWordRegister(this)
89                     .WithFlag(0, FieldMode.Read | FieldMode.WriteOneToClear, writeCallback: (_, value) => {
90                                 if(value)
91                                 {
92                                     rxFifoOverrunInterrupt.Value = false;
93                                 }
94                             })
95                     .WithTaggedFlag("RTIC", 1)
96                     .WithReservedBits(2, 30)
97                     .WithWriteCallback((_, __) => Update())
98                 },
99             };
100             registers = new DoubleWordRegisterCollection(this, regs);
101         }
102 
ReadDoubleWord(long offset)103         public uint ReadDoubleWord(long offset)
104         {
105             return registers.Read(offset);
106         }
107 
WriteDoubleWord(long offset, uint value)108         public void WriteDoubleWord(long offset, uint value)
109         {
110             registers.Write(offset, value);
111         }
112 
Reset()113         public override void Reset()
114         {
115             registers.Reset();
116             rxFifo.Clear();
117             txFifo.Clear();
118             IRQ.Unset();
119         }
120 
121         public GPIO IRQ { get; private set; }
122 
123         public long Size => 0x1000;
124 
EnableTransmitter(bool _, bool enabled)125         private void EnableTransmitter(bool _, bool enabled)
126         {
127             if(enabled && txFifo.Count > 0)
128             {
129                 foreach(var value in txFifo.DequeueAll())
130                 {
131                     SendByte(value);
132                 }
133                 Update();
134             }
135         }
136 
SendData(uint value)137         private void SendData(uint value)
138         {
139             var byteValue = (byte)value;
140             if(value != byteValue)
141             {
142                 this.Log(LogLevel.Warning, "Trying to send 0x{0:X}, but it doesn't fit in a byte. Will send 0x{1:X} instead", value, byteValue);
143             }
144             if(enabled.Value)
145             {
146                 SendByte(byteValue);
147             }
148             else
149             {
150                 this.Log(LogLevel.Noisy, "Deferring the transfer of 0x{0:X} as the transmitter is not enabled", byteValue);
151                 txFifo.Enqueue(byteValue);
152             }
153             Update();
154         }
155 
SendByte(byte value)156         private void SendByte(byte value)
157         {
158             if(RegisteredPeripheral == null)
159             {
160                 this.Log(LogLevel.Warning, "Trying to write 0x{0:X} to a slave peripheral, but nothing is connected");
161                 return;
162             }
163             var response = RegisteredPeripheral.Transmit(value);
164             this.Log(LogLevel.Noisy, "Transmitted deferred data 0x{0:X}, received 0x{1:X}", value, response);
165             if(!rxFifo.Enqueue(response))
166             {
167                 this.Log(LogLevel.Warning, "Receive FIFO overrun");
168                 rxFifoOverrunInterrupt.Value = true;
169             }
170         }
171 
Update()172         private void Update()
173         {
174             txFifoInterrupt.Value = txFifo.Count <= txFifo.Capacity / 2;
175             rxFifoInterrupt.Value = rxFifo.Count >= rxFifo.Capacity / 2;
176             //rxFifoOverrunInterrupt is set in `SendData`
177             IRQ.Set((txFifoInterrupt.Value && txFifoInterruptMask.Value)
178                         || (rxFifoInterrupt.Value && rxFifoInterruptMask.Value)
179                         || (rxFifoOverrunInterrupt.Value && rxFifoOverrunInterruptMask.Value));
180         }
181 
182         private DoubleWordRegisterCollection registers;
183         private CircularBuffer<byte> rxFifo;
184         private CircularBuffer<byte> txFifo;
185 
186         private IFlagRegisterField enabled;
187 
188         private IFlagRegisterField rxFifoOverrunInterruptMask;
189         private IFlagRegisterField rxFifoInterruptMask;
190         private IFlagRegisterField txFifoInterruptMask;
191 
192         private IFlagRegisterField rxFifoOverrunInterrupt;
193         private IFlagRegisterField rxFifoInterrupt;
194         private IFlagRegisterField txFifoInterrupt;
195 
196         private const int FifoCapacity = 16;
197 
198         public enum Registers
199         {
200             Control0 = 0x0,
201             Control1 = 0x4,
202             Data = 0x8,
203             Status = 0xC,
204             ClockPrescaler = 0x10,
205             InterruptMask = 0x14,
206             RawInterruptStatus = 0x18,
207             MaskedInterruptStatus = 0x1C,
208             InterruptClear = 0x20,
209             DMAControl = 0x24,
210             ClockConfiguration = 0xFC8,
211         }
212     }
213 }
214