1 //
2 // Copyright (c) 2010-2022 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.Registers;
10 using Antmicro.Renode.Logging;
11 using Antmicro.Renode.Peripherals.Memory;
12 using Antmicro.Renode.Peripherals.SPI.NORFlash;
13 
14 namespace Antmicro.Renode.Peripherals.SPI
15 {
16     public class GigaDevice_GD25LQ : ISPIPeripheral, IGPIOReceiver
17     {
GigaDevice_GD25LQ(MappedMemory underlyingMemory)18         public GigaDevice_GD25LQ(MappedMemory underlyingMemory)
19         {
20             var registerMap = new Dictionary<long, ByteRegister>
21             {
22                 {(long)Register.StatusLow, new ByteRegister(this)
23                     .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => writeInProgress, name: "Write in progress")
24                     .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => writeEnableLatch, name: "Write enable latch")
25                     .WithTag("Block protect 0", 2, 1)
26                     .WithTag("Block protect 1", 3, 1)
27                     .WithTag("Block protect 2", 4, 1)
28                     .WithTag("Block protect 3", 5, 1)
29                     .WithTag("Block protect 4", 6, 1)
30                     .WithTag("Status register protect 0", 7, 1)
31                 },
32                 {(long)Register.StatusHigh, new ByteRegister(this)
33                     .WithTag("Status register protect 1", 0, 1)
34                     .WithFlag(1, out quadEnable, name: "Quad enable")
35                     .WithTag("SUS2", 2, 1)
36                     .WithTag("LB1", 3, 1)
37                     .WithTag("LB2", 4, 1)
38                     .WithTag("LB3", 5, 1)
39                     .WithTag("CMP", 6, 1)
40                     .WithTag("SUS1", 7, 1)
41                 }
42             };
43             registers = new ByteRegisterCollection(this, registerMap);
44             this.underlyingMemory = underlyingMemory;
45         }
46 
OnGPIO(int number, bool value)47         public void OnGPIO(int number, bool value)
48         {
49             if(number == 0 && value)
50             {
51                 this.Log(LogLevel.Noisy, "Chip Select is deasserted.");
52                 FinishTransmission();
53             }
54         }
55 
FinishTransmission()56         public void FinishTransmission()
57         {
58             switch(currentOperation.State)
59             {
60                 case DecodedOperation.OperationState.RecognizeOperation:
61                 case DecodedOperation.OperationState.AccumulateCommandAddressBytes:
62                 case DecodedOperation.OperationState.AccumulateNoDataCommandAddressBytes:
63                 case DecodedOperation.OperationState.HandleImmediateCommand:
64                     this.Log(LogLevel.Warning, "Transmission finished in the unexpected state: {0}", currentOperation.State);
65                     break;
66                 default:
67                     this.Log(LogLevel.Noisy, "Transmission finished in state: {0}", currentOperation.State);
68                     break;
69             }
70             currentOperation.State = DecodedOperation.OperationState.RecognizeOperation;
71             currentOperation = default(DecodedOperation);
72             writeInProgress = false;
73         }
74 
Reset()75         public void Reset()
76         {
77             writeEnableLatch = false;
78             writeInProgress = false;
79             currentOperation = default(DecodedOperation);
80             registers.Reset();
81             FinishTransmission();
82         }
83 
Transmit(byte data)84         public byte Transmit(byte data)
85         {
86             this.Log(LogLevel.Noisy, "Transmitting data 0x{0:X}, current state: {1}", data, currentOperation.State);
87             switch(currentOperation.State)
88             {
89                 case DecodedOperation.OperationState.RecognizeOperation:
90                     // When the command is decoded, depending on the operation, we will either start accumulating address bytes
91                     // or immediately handle the command bytes
92                     RecognizeOperation(data);
93                     break;
94                 case DecodedOperation.OperationState.HandleCommand:
95                     // Process the remaining command bytes
96                     return HandleCommand(data);
97             }
98             return 0;
99         }
100 
RecognizeOperation(byte firstByte)101         private void RecognizeOperation(byte firstByte)
102         {
103             currentOperation.Operation = DecodedOperation.OperationType.None;
104             currentOperation.AddressLength = 0;
105             currentOperation.State = DecodedOperation.OperationState.HandleCommand;
106             switch((Commands)firstByte)
107             {
108                 case Commands.WriteStatusRegister:
109                     currentOperation.Operation = DecodedOperation.OperationType.WriteRegister;
110                     currentOperation.Register = (uint)Register.StatusLow;
111                     currentOperation.State = DecodedOperation.OperationState.HandleCommand;
112                     break;
113                 case Commands.ReadStatusRegisterLow:
114                     currentOperation.Operation = DecodedOperation.OperationType.ReadRegister;
115                     currentOperation.Register = (uint)Register.StatusLow;
116                     currentOperation.State = DecodedOperation.OperationState.HandleCommand;
117                     break;
118                 case Commands.WriteEnable:
119                     currentOperation.Operation = DecodedOperation.OperationType.WriteEnable;
120                     currentOperation.State = DecodedOperation.OperationState.HandleImmediateCommand;
121                     break;
122                 case Commands.ReadStatusRegisterHigh:
123                     currentOperation.Operation = DecodedOperation.OperationType.ReadRegister;
124                     currentOperation.Register = (uint)Register.StatusHigh;
125                     currentOperation.State = DecodedOperation.OperationState.HandleCommand;
126                     break;
127                 case Commands.ReadID:
128                     currentOperation.Operation = DecodedOperation.OperationType.ReadID;
129                     currentOperation.State = DecodedOperation.OperationState.HandleImmediateCommand;
130                     break;
131                 default:
132                     this.Log(LogLevel.Error, "Command decoding failed on byte: 0x{0:X} ({1}).", firstByte, (Commands)firstByte);
133                     return;
134             }
135             if(currentOperation.State == DecodedOperation.OperationState.HandleImmediateCommand)
136             {
137                 switch(currentOperation.Operation)
138                 {
139                     case DecodedOperation.OperationType.WriteEnable:
140                         writeEnableLatch = true;
141                         break;
142                     case DecodedOperation.OperationType.ReadID:
143                         currentOperation.State = DecodedOperation.OperationState.HandleCommand;
144                         break;
145                     default:
146                         this.Log(LogLevel.Error, "Encountered invalid immediate command: {0}", currentOperation.Operation);
147                         break;
148                 }
149             }
150             this.Log(LogLevel.Noisy, "Decoded operation: {0}", currentOperation);
151         }
152 
HandleCommand(byte data)153         private byte HandleCommand(byte data)
154         {
155             byte result = 0;
156             switch(currentOperation.Operation)
157             {
158                 case DecodedOperation.OperationType.ReadID:
159                     if(currentOperation.CommandBytesHandled < deviceID.Length)
160                     {
161                         result = deviceID[currentOperation.CommandBytesHandled];
162                     }
163                     else
164                     {
165                         this.Log(LogLevel.Error, "Trying to read beyond the length of the device ID table.");
166                         result = 0;
167                     }
168                     break;
169                 case DecodedOperation.OperationType.ReadRegister:
170                     result = ReadRegister(currentOperation.Register);
171                     break;
172                 case DecodedOperation.OperationType.WriteRegister:
173                     if(currentOperation.Register == (uint)Register.StatusLow)
174                     {
175                         WriteRegister(currentOperation.Register, data);
176                         currentOperation.Register = (uint)Register.StatusHigh;
177                         writeInProgress = true;
178                     }
179                     else
180                     {
181                         WriteRegister(currentOperation.Register, data);
182                         writeEnableLatch = false;
183                     }
184                     break;
185                 default:
186                     this.Log(LogLevel.Warning, "Unhandled operation encountered while processing command bytes: {0}", currentOperation.Operation);
187                     break;
188             }
189             currentOperation.CommandBytesHandled++;
190             this.Log(LogLevel.Noisy, "Handled command: {0}, returning 0x{1:X}", currentOperation, result);
191             return result;
192         }
193 
ReadRegister(uint offset)194         private byte ReadRegister(uint offset)
195         {
196             return registers.Read(offset);
197         }
198 
WriteRegister(uint offset, byte data)199         private void WriteRegister(uint offset, byte data)
200         {
201             if(!writeEnableLatch)
202             {
203                 this.Log(LogLevel.Warning, "Trying to write 0x{0:X} to {1} register while operation is not enabled.", data, (Register)offset);
204                 return;
205             }
206             this.Log(LogLevel.Noisy, "Writing value: 0x{0:X} to {1}", data, (Register)offset);
207             registers.Write(offset, data);
208         }
209 
210         private DecodedOperation currentOperation;
211         private ByteRegisterCollection registers;
212 
213         private readonly MappedMemory underlyingMemory;
214         private readonly IFlagRegisterField quadEnable;
215 
216         private bool writeInProgress;
217         private bool writeEnableLatch;
218 
219         private static byte[] deviceID = { 0xC8, 0x60, 0x18 };
220 
221         private enum Commands : byte
222         {
223             // There are multiple gaps in command coding
224             WriteStatusRegister = 0x1,
225             PageProgram = 0x2,
226             ReadData = 0x3,
227             WriteDisable = 0x4,
228             ReadStatusRegisterLow = 0x5,
229             WriteEnable = 0x6,
230             FastRead = 0xB,
231             SectorErase = 0x20,
232             QuadPageProgram = 0x32,
233             ReadStatusRegisterHigh = 0x35,
234             EnableQPI = 0x38,
235             DualOutputFastRead = 0x3B,
236             ProgramSecurityRegisters = 0x42,
237             EraseSecurityRegisters = 0x44,
238             ReadSecurityRegisters = 0x48,
239             VolatileStatusRegisterWriteEnable = 0x50,
240             BlockErase32K = 0x52,
241             ReadSerialFlash = 0x5A,
242             EnableReset = 0x66,
243             QuadOutputFastRead = 0x6B,
244             ProgramOrEraseSuspend = 0x75,
245             SetBurstWithWrap = 0x77,
246             ProgramOrEraseResume = 0x7A,
247             DeviceID = 0x90,
248             DeviceIDByDualIO = 0x92,
249             DeviceIDByQuadIO = 0x94,
250             Reset = 0x99,
251             ReadID = 0x9F,
252             ReleaseFromDeepPowerDown = 0xAB,
253             DeepPowerDown = 0xB9,
254             DualIOFastRead = 0xBB,
255             ChipErase = 0xC7,
256             BlockErase64K = 0xD8,
257             QuadIOWordFastRead = 0xE7,
258             QuadIOFastRead = 0xEB
259         }
260 
261         private enum Register : uint
262         {
263             StatusLow = 0,
264             StatusHigh
265         }
266     }
267 }
268