1 //
2 // Copyright (c) 2010-2019 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 
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.Logging;
13 using Antmicro.Renode.Peripherals.Bus;
14 
15 namespace Antmicro.Renode.Peripherals.SPI
16 {
17     public class LiteX_SPI : NullRegistrationPointPeripheralContainer<ISPIPeripheral>, IDoubleWordPeripheral, IKnownSize
18     {
LiteX_SPI(IMachine machine)19         public LiteX_SPI(IMachine machine) : base(machine)
20         {
21             var registers = new Dictionary<long, DoubleWordRegister>
22             {
23                 {(long)Registers.Control, new DoubleWordRegister(this)
24                     .WithValueField(0, 8, out bitsPerWord, name: "bits per word", changeCallback: (_, __) => VerifyBitsPerWord())
25                     // the driver do not limit written value to a single byte, so without ignoring the following bits a warning would be generated
26                     .WithIgnoredBits(8, 8)
27                     .WithReservedBits(16, 16)
28                 },
29 
30                 // see comment above
31                 {(long)Registers.Control2, new DoubleWordRegister(this)
32                     .WithFlag(0, name: "start", writeCallback: (_, startBit) =>
33                     {
34                         if(!startBit)
35                         {
36                             return;
37                         }
38 
39                         if(loopbackMode.Value)
40                         {
41                             this.Log(LogLevel.Noisy, "Sending data in loopback mode");
42                             masterInBuffer.Value = masterOutBuffer.Value;
43                         }
44                         else
45                         {
46                             if(!chipSelect.Value)
47                             {
48                                 this.Log(LogLevel.Warning, "Tried to send some data over SPI, but CS is not set");
49                                 masterInBuffer.Value = NoResponse;
50                                 return;
51                             }
52 
53                             if(RegisteredPeripheral == null)
54                             {
55                                 this.Log(LogLevel.Warning, "Tried to send some data over SPI, but no device is currently connected");
56                                 masterInBuffer.Value = NoResponse;
57                                 return;
58                             }
59 
60                             if(bitsPerWord.Value == 0)
61                             {
62                                 this.Log(LogLevel.Warning, "Transfer length set to 0. Ignoring the transfer");
63                                 masterInBuffer.Value = NoResponse;
64                                 return;
65                             }
66 
67                             // see method's comment for details
68                             if(!VerifyBitsPerWord())
69                             {
70                                 masterInBuffer.Value = NoResponse;
71                                 return;
72                             }
73 
74                             var result = RegisteredPeripheral.Transmit((byte)masterOutBuffer.Value);
75                             masterInBuffer.Value = (byte)result;
76                         }
77                     })
78                     // the driver do not limit written value to a single byte, so without ignoring the following bits a warning would be generated
79                     .WithIgnoredBits(1, 15)
80                     .WithReservedBits(16, 16)
81                 },
82 
83                 {(long)Registers.Status, new DoubleWordRegister(this)
84                     .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => true, name: "done")
85                     .WithReservedBits(1, 31)
86                 },
87 
88                 {(long)Registers.MasterOutSlaveIn, new DoubleWordRegister(this)
89                     .WithValueField(0, 8, out masterOutBuffer)
90                     // the driver do not limit written value to a single byte, so without ignoring the following bits a warning would be generated
91                     .WithIgnoredBits(8, 24)
92                 },
93 
94                 {(long)Registers.MasterInSlaveOut, new DoubleWordRegister(this)
95                     .WithValueField(0, 8, out masterInBuffer)
96                     // the driver do not limit written value to a single byte, so without ignoring the following bits a warning would be generated
97                     .WithIgnoredBits(8, 24)
98                 },
99 
100                 {(long)Registers.Loopback, new DoubleWordRegister(this)
101                     .WithFlag(0, out loopbackMode)
102                     .WithReservedBits(1, 31)
103                 },
104 
105                 {(long)Registers.ChipSelect, new DoubleWordRegister(this)
106                     // for now we support only one device
107                     // this should be improved to support more devices
108                     .WithFlag(0, out chipSelect, name: "cs", writeCallback: (_, val) =>
109                     {
110                         if(val && RegisteredPeripheral == null)
111                         {
112                             this.Log(LogLevel.Warning, "CS set, but no device is currently connected");
113                         }
114                     })
115                     .WithReservedBits(1, 31)
116                 }
117             };
118 
119             registersCollection = new DoubleWordRegisterCollection(this, registers);
120             Reset();
121         }
122 
Reset()123         public override void Reset()
124         {
125             registersCollection.Reset();
126         }
127 
ReadDoubleWord(long offset)128         public uint ReadDoubleWord(long offset)
129         {
130             return registersCollection.Read(offset);
131         }
132 
WriteDoubleWord(long offset, uint value)133         public void WriteDoubleWord(long offset, uint value)
134         {
135             registersCollection.Write(offset, value);
136         }
137 
138         public long Size => 0x50;
139 
140         // This is due to limitations in Renode:
141         // proper handling of other values of BPR would require support for bit-by-bit transmission in SPI.
142         // Remove once the SPI interface is reworked.
VerifyBitsPerWord()143         private bool VerifyBitsPerWord()
144         {
145             if(bitsPerWord.Value != 8)
146             {
147                 this.Log(LogLevel.Warning, "Bits per word set to: {0}. This configuration is not supported by this model - ignoring the transfer", bitsPerWord.Value);
148                 return false;
149             }
150 
151             return true;
152         }
153 
154         private readonly DoubleWordRegisterCollection registersCollection;
155         private readonly IValueRegisterField masterOutBuffer;
156         private readonly IValueRegisterField masterInBuffer;
157         private readonly IFlagRegisterField loopbackMode;
158         private readonly IFlagRegisterField chipSelect;
159         private readonly IValueRegisterField bitsPerWord;
160 
161         private const byte NoResponse = 0xFF;
162 
163         private enum Registers
164         {
165             // in LiteX this is defined as one CSRStorage(16)
166             Control = 0x0,
167             Control2 = 0x4,
168 
169             Status = 0x8,
170             MasterOutSlaveIn = 0xC,
171             MasterInSlaveOut = 0x10,
172             ChipSelect = 0x14,
173             Loopback = 0x18
174         }
175     }
176 }
177