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