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;
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 PULP_uDMA_SPI : NullRegistrationPointPeripheralContainer<ISPIPeripheral>, IDoubleWordPeripheral, IKnownSize
18     {
PULP_uDMA_SPI(IMachine machine)19         public PULP_uDMA_SPI(IMachine machine) : base(machine)
20         {
21             sysbus = machine.GetSystemBus(this);
22             RxIRQ = new GPIO();
23             TxIRQ = new GPIO();
24             CmdIRQ = new GPIO();
25 
26             var registers = new Dictionary<long, DoubleWordRegister>
27             {
28                 {(long)Registers.RxTransferAddress, new DoubleWordRegister(this)
29                     .WithValueField(0, 32, out rxTransferAddress, name: "SPIM_RX_SADDR")
30                 },
31 
32                 {(long)Registers.RxTransferBufferSize, new DoubleWordRegister(this)
33                     .WithValueField(0, 20, out rxTransferBufferSize, name: "SPIM_RX_SIZE")
34                     .WithReservedBits(20, 12)
35                 },
36 
37                 {(long)Registers.RxTransferConfiguration, new DoubleWordRegister(this)
38                     .WithTag("CONTINOUS", 0, 1)
39                     .WithTag("DATASIZE", 1, 2)
40                     .WithReservedBits(3, 1)
41                     .WithFlag(4, out rxEnable, name: "EN")
42                     .WithTag("CLR/PENDING", 5, 1)
43                     .WithReservedBits(6, 26)
44                     .WithWriteCallback((_, __) => TryStartReception())
45                 },
46 
47                 {(long)Registers.TxTransferAddress, new DoubleWordRegister(this)
48                     .WithValueField(0, 32, out txTransferAddress, name: "SPIM_TX_SADDR")
49                 },
50 
51                 {(long)Registers.TxTransferBufferSize, new DoubleWordRegister(this)
52                     .WithValueField(0, 20, out txTransferBufferSize, name: "SPIM_TX_SIZE")
53                     .WithReservedBits(20, 12)
54                 },
55 
56                 {(long)Registers.TxTransferConfiguration, new DoubleWordRegister(this)
57                     .WithTag("CONTINOUS", 0, 1)
58                     .WithTag("DATASIZE", 1, 2)
59                     .WithReservedBits(3, 1)
60                     .WithFlag(4, out txEnable, name: "EN")
61                     .WithTag("CLR/PENDING", 5, 1)
62                     .WithReservedBits(6, 26)
63                     .WithWriteCallback((_, __) => TryStartTransmission())
64                 },
65 
66                 {(long)Registers.CommandTransferAddress, new DoubleWordRegister(this)
67                     .WithValueField(0, 32, out commandTransferAddress, name: "SPIM_CMD_SADDR")
68                 },
69 
70                 {(long)Registers.CommandTransferBufferSize, new DoubleWordRegister(this)
71                     .WithTag("CMD_SIZE", 0, 20)
72                     .WithReservedBits(20, 12)
73                 },
74 
75                 {(long)Registers.CommandTransferConfiguration, new DoubleWordRegister(this)
76                     .WithTag("CONTINOUS", 0, 1)
77                     .WithTag("DATASIZE", 1, 2)
78                     .WithReservedBits(3, 1)
79                     .WithFlag(4, out commandEnable, name: "EN")
80                     .WithTag("CLR/PENDING", 5, 1)
81                     .WithReservedBits(6, 26)
82                     .WithWriteCallback((_, __) => TryExecuteTransaction())
83                 },
84             };
85 
86             registersCollection = new DoubleWordRegisterCollection(this, registers);
87             Reset();
88         }
89 
ReadDoubleWord(long offset)90         public uint ReadDoubleWord(long offset)
91         {
92             return registersCollection.Read(offset);
93         }
94 
WriteDoubleWord(long offset, uint value)95         public void WriteDoubleWord(long offset, uint value)
96         {
97             registersCollection.Write(offset, value);
98         }
99 
Reset()100         public override void Reset()
101         {
102             registersCollection.Reset();
103             command = Commands.None;
104             // Updating interrupts here is not necessary as we blink with them
105         }
106 
107         public long Size => 0x80;
108 
109         public GPIO RxIRQ { get; }
110         public GPIO TxIRQ { get; }
111         public GPIO CmdIRQ { get; }
112 
TryExecuteTransaction()113         private void TryExecuteTransaction()
114         {
115             if(command != Commands.None)
116             {
117                 this.Log(LogLevel.Debug, "Tried to process a new command before finishing the previous one.");
118                 return;
119             }
120             if(!commandEnable.Value)
121             {
122                 this.Log(LogLevel.Debug, "Tried to issue a transaction without full configuration.");
123                 return;
124             }
125             command = ReadCommand();
126             switch(command)
127             {
128                 case Commands.ReceiveData:
129                     TryStartReception();
130                     break;
131                 case Commands.TransferData:
132                     TryStartTransmission();
133                     break;
134                 default:
135                     this.Log(LogLevel.Error, "Encountered unsupported command: 0x{0:X}", command);
136                     command = Commands.None;
137                     break;
138             }
139             CmdIRQ.Blink();
140         }
141 
TryStartReception()142         private void TryStartReception()
143         {
144             if(!rxEnable.Value || command != Commands.ReceiveData)
145             {
146                 this.Log(LogLevel.Debug, "Tried to issue a transaction without full configuration.");
147                 return;
148             }
149             if(RegisteredPeripheral == null)
150             {
151                 this.Log(LogLevel.Warning, "Trying to issue a transaction to a slave peripheral, but nothing is connected");
152                 return;
153             }
154             var receiveQueue = new Queue<byte>();
155             for(int i = 0; i < (int)rxTransferBufferSize.Value; i++)
156             {
157                 receiveQueue.Enqueue(RegisteredPeripheral.Transmit(0));
158             }
159             RegisteredPeripheral.FinishTransmission();
160             sysbus.WriteBytes(receiveQueue.ToArray(), rxTransferAddress.Value);
161             receiveQueue.Clear();
162             command = Commands.None;
163             RxIRQ.Blink();
164         }
165 
TryStartTransmission()166         private void TryStartTransmission()
167         {
168             if(!txEnable.Value || command != Commands.TransferData)
169             {
170                 this.Log(LogLevel.Debug, "Tried to issue a transaction without full configuration.");
171                 return;
172             }
173             if(RegisteredPeripheral == null)
174             {
175                 this.Log(LogLevel.Warning, "Trying to issue a transaction to a slave peripheral, but nothing is connected");
176                 return;
177             }
178             foreach(var b in sysbus.ReadBytes(txTransferAddress.Value, (int)txTransferBufferSize.Value))
179             {
180                 RegisteredPeripheral.Transmit(b);
181             }
182             RegisteredPeripheral.FinishTransmission();
183             command = Commands.None;
184             TxIRQ.Blink();
185         }
186 
ReadCommand()187         private Commands ReadCommand()
188         {
189             // The command is the third element of an array stored in memory, hence the addition
190             var cmd = (Commands)(sysbus.ReadDoubleWord(commandTransferAddress.Value + 8) >> 28);
191             if(!Enum.IsDefined(typeof(Commands), cmd))
192             {
193                 this.Log(LogLevel.Warning, "Invalid command has been issued: {0}", cmd);
194             }
195             return cmd;
196         }
197 
198         private Commands command;
199 
200         private readonly IBusController sysbus;
201         private readonly DoubleWordRegisterCollection registersCollection;
202         private readonly IFlagRegisterField commandEnable;
203         private readonly IFlagRegisterField rxEnable;
204         private readonly IFlagRegisterField txEnable;
205         private readonly IValueRegisterField rxTransferAddress;
206         private readonly IValueRegisterField rxTransferBufferSize;
207         private readonly IValueRegisterField txTransferAddress;
208         private readonly IValueRegisterField txTransferBufferSize;
209         private readonly IValueRegisterField commandTransferAddress;
210 
211         private enum Commands : byte
212         {
213             Config = 0x0,
214             SetChipSelect = 0x1,
215             SendCommand = 0x2,
216             // no 0x3 command
217             ReceiveDummyBits = 0x4,
218             Wait = 0x5,
219             TransferData = 0x6,
220             ReceiveData = 0x7,
221             RepeatCommand = 0x8,
222             ClearChipSelect = 0x9,
223             RepeatCommandEnd = 0xA,
224             CheckAgainstExpectedValue = 0xB,
225             FullDuplex = 0xC,
226             SetAddressForDMA = 0xD,
227             SetSizeAndStartDMA = 0xE,
228             // not a standard command
229             None = 0xF
230         }
231 
232         private enum Registers
233         {
234             RxTransferAddress = 0x0,
235             RxTransferBufferSize = 0x4,
236             RxTransferConfiguration = 0x8,
237             RxInitConfig = 0xC,
238             TxTransferAddress = 0x10,
239             TxTransferBufferSize = 0x14,
240             TxTransferConfiguration = 0x18,
241             TxInitConfig = 0x1C,
242             CommandTransferAddress = 0x20,
243             CommandTransferBufferSize = 0x24,
244             CommandTransferConfiguration = 0x28,
245             CommandInitConfig = 0x2C
246         }
247     }
248 }
249