1 //
2 // Copyright (c) 2010-2024 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.Exceptions;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Peripherals.Memory;
13 using Antmicro.Renode.Peripherals.SPI.NORFlash;
14 using Antmicro.Renode.Utilities;
15 
16 using Range = Antmicro.Renode.Core.Range;
17 
18 namespace Antmicro.Renode.Peripherals.SPI
19 {
20     public class GenericSpiFlash : ISPIPeripheral, IGPIOReceiver
21     {
GenericSpiFlash(MappedMemory underlyingMemory, byte manufacturerId, byte memoryType, bool writeStatusCanSetWriteEnable = true, byte extendedDeviceId = DefaultExtendedDeviceID, byte deviceConfiguration = DefaultDeviceConfiguration, byte remainingIdBytes = DefaultRemainingIDBytes, int sectorSizeKB = DefaultSectorSizeKB)22         public GenericSpiFlash(MappedMemory underlyingMemory, byte manufacturerId, byte memoryType,
23             bool writeStatusCanSetWriteEnable = true, byte extendedDeviceId = DefaultExtendedDeviceID,
24             byte deviceConfiguration = DefaultDeviceConfiguration, byte remainingIdBytes = DefaultRemainingIDBytes,
25             // "Sector" here is the largest erasable memory unit. It's also named "block" by many flash memory vendors.
26             int sectorSizeKB = DefaultSectorSizeKB)
27         {
28             if(!Misc.IsPowerOfTwo((ulong)underlyingMemory.Size))
29             {
30                 throw new ConstructionException("Size of the underlying memory must be a power of 2");
31             }
32 
33             volatileConfigurationRegister = new ByteRegister(this, 0xfb).WithFlag(3, name: "XIP");
34             nonVolatileConfigurationRegister = new WordRegister(this, 0xffff).WithEnumField<WordRegister, AddressingMode>(0, 1, out addressingMode, name: "addressWith3Bytes");
35             enhancedVolatileConfigurationRegister = new ByteRegister(this, 0xff)
36                 .WithValueField(0, 3, name: "Output driver strength")
37                 .WithReservedBits(3, 1)
38                 .WithTaggedFlag("Reset/hold", 4)
39                 //these flags are intentionally not implemented, as they described physical details
40                 .WithFlag(5, name: "Double transfer rate protocol")
41                 .WithFlag(6, name: "Dual I/O protocol")
42                 .WithFlag(7, name: "Quad I/O protocol");
43             statusRegister = new ByteRegister(this)
44                 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => false, name: "writeInProgress")
45                 .WithFlag(1, out enable, writeStatusCanSetWriteEnable ? FieldMode.Read | FieldMode.Write : FieldMode.Read, name: "writeEnableLatch");
46             configurationRegister = new WordRegister(this);
47             flagStatusRegister = new ByteRegister(this)
48                 .WithEnumField<ByteRegister, AddressingMode>(0, 1, FieldMode.Read, valueProviderCallback: _ => addressingMode.Value, name: "Addressing")
49                 //other bits indicate either protection errors (not implemented) or pending operations (they already finished)
50                 .WithReservedBits(3, 1)
51                 .WithFlag(7, FieldMode.Read, valueProviderCallback: _ => true, name: "ProgramOrErase");
52 
53             sectorSize = sectorSizeKB.KB();
54             this.underlyingMemory = underlyingMemory;
55             underlyingMemory.ResetByte = EmptySegment;
56 
57             this.manufacturerId = manufacturerId;
58             this.memoryType = memoryType;
59             this.capacityCode = GetCapacityCode();
60             this.remainingIdBytes = remainingIdBytes;
61             this.extendedDeviceId = extendedDeviceId;
62             this.deviceConfiguration = deviceConfiguration;
63 
64             deviceData = GetDeviceData();
65             SFDPSignature = GetSFDPSignature();
66         }
67 
OnGPIO(int number, bool value)68         public void OnGPIO(int number, bool value)
69         {
70             if(number == 0 && value)
71             {
72                 this.Log(LogLevel.Noisy, "Chip Select is deasserted.");
73                 FinishTransmission();
74             }
75         }
76 
FinishTransmission()77         public void FinishTransmission()
78         {
79             switch(currentOperation.State)
80             {
81                 case DecodedOperation.OperationState.RecognizeOperation:
82                 case DecodedOperation.OperationState.AccumulateCommandAddressBytes:
83                 case DecodedOperation.OperationState.AccumulateNoDataCommandAddressBytes:
84                     this.Log(LogLevel.Warning, "Transmission finished in the unexpected state: {0}", currentOperation.State);
85                     break;
86             }
87             // If an operation has at least 1 data byte or more than 0 address bytes,
88             // we can clear the write enable flag only when we are finishing a transmission.
89             switch(currentOperation.Operation)
90             {
91                 case DecodedOperation.OperationType.Program:
92                 case DecodedOperation.OperationType.Erase:
93                 case DecodedOperation.OperationType.WriteRegister:
94                     //although the docs are not clear, it seems that all register writes should clear the flag
95                     enable.Value = false;
96                     break;
97             }
98             currentOperation.State = DecodedOperation.OperationState.RecognizeOperation;
99             currentOperation = default(DecodedOperation);
100             temporaryConfiguration = 0;
101         }
102 
Reset()103         public void Reset()
104         {
105             statusRegister.Reset();
106             flagStatusRegister.Reset();
107             volatileConfigurationRegister.Reset();
108             nonVolatileConfigurationRegister.Reset();
109             enhancedVolatileConfigurationRegister.Reset();
110             currentOperation = default(DecodedOperation);
111             lockedRange = null;
112             FinishTransmission();
113         }
114 
Transmit(byte data)115         public byte Transmit(byte data)
116         {
117             this.Log(LogLevel.Noisy, "Transmitting data 0x{0:X}, current state: {1}", data, currentOperation.State);
118             switch(currentOperation.State)
119             {
120                 case DecodedOperation.OperationState.RecognizeOperation:
121                     // When the command is decoded, depending on the operation we will either start accumulating address bytes
122                     // or immediately handle the command bytes
123                     RecognizeOperation(data);
124                     break;
125                 case DecodedOperation.OperationState.AccumulateCommandAddressBytes:
126                     AccumulateAddressBytes(data, DecodedOperation.OperationState.HandleCommand);
127                     break;
128                 case DecodedOperation.OperationState.AccumulateNoDataCommandAddressBytes:
129                     AccumulateAddressBytes(data, DecodedOperation.OperationState.HandleNoDataCommand);
130                     break;
131                 case DecodedOperation.OperationState.HandleCommand:
132                     // Process the remaining command bytes
133                     return HandleCommand(data);
134             }
135 
136             // Warning: commands without data require immediate handling after the address was accumulated
137             if(currentOperation.State == DecodedOperation.OperationState.HandleNoDataCommand)
138             {
139                 HandleNoDataCommand();
140             }
141             return 0;
142         }
143 
144         public MappedMemory UnderlyingMemory => underlyingMemory;
145 
WriteToMemory(byte val)146         protected virtual void WriteToMemory(byte val)
147         {
148             if(!TryVerifyWriteToMemory(out var position))
149             {
150                 return;
151             }
152             underlyingMemory.WriteByte(position, val);
153         }
154 
TryVerifyWriteToMemory(out long position)155         protected bool TryVerifyWriteToMemory(out long position)
156         {
157             position = currentOperation.ExecutionAddress + currentOperation.CommandBytesHandled;
158             if(position > underlyingMemory.Size)
159             {
160                 this.Log(LogLevel.Error, "Cannot write to address 0x{0:X} because it is bigger than configured memory size.", currentOperation.ExecutionAddress);
161                 return false;
162             }
163             if(lockedRange.HasValue && lockedRange.Value.Contains((ulong)position))
164             {
165                 this.Log(LogLevel.Error, "Cannot write to address 0x{0:X} because it is in the locked range", position);
166                 return false;
167             }
168             return true;
169         }
170 
GetCapacityCode()171         protected virtual byte GetCapacityCode()
172         {
173             // capacity code:
174             // 0x10 -  64 KB
175             // 0x11 - 128 KB
176             // 0x12 - 256 KB
177             // ..
178             // 0x19 -  32 MB
179             //     NOTE: there is a gap between 0x19 and 0x20
180             // 0x20 -  64 MB
181             // 0x21 - 128 MB
182             // 0x22 - 256 MB
183             byte capacityCode = 0;
184 
185             if(underlyingMemory.Size <= 32.MB())
186             {
187                 capacityCode = (byte)BitHelper.GetMostSignificantSetBitIndex((ulong)underlyingMemory.Size);
188             }
189             else
190             {
191                 capacityCode = (byte)((BitHelper.GetMostSignificantSetBitIndex((ulong)underlyingMemory.Size) - 26) + 0x20);
192             }
193 
194             return capacityCode;
195         }
196 
GetSFDPSignature()197         protected virtual byte[] GetSFDPSignature()
198         {
199             return DefaultSFDPSignature;
200         }
201 
GetDummyBytes(Commands command)202         protected virtual int GetDummyBytes(Commands command)
203         {
204             switch(command)
205             {
206                 case Commands.FastRead:
207                     return 1;
208                 default:
209                     return 0;
210             }
211         }
212 
213         protected Range? lockedRange;
214 
215         protected readonly int sectorSize;
216         protected readonly ByteRegister statusRegister;
217         protected readonly WordRegister configurationRegister;
218         protected readonly MappedMemory underlyingMemory;
219 
AccumulateAddressBytes(byte addressByte, DecodedOperation.OperationState nextState)220         private void AccumulateAddressBytes(byte addressByte, DecodedOperation.OperationState nextState)
221         {
222             if(currentOperation.TryAccumulateAddress(addressByte))
223             {
224                 this.Log(LogLevel.Noisy, "Address accumulated: 0x{0:X}", currentOperation.ExecutionAddress);
225                 currentOperation.State = nextState;
226             }
227         }
228 
GetDeviceData()229         private byte[] GetDeviceData()
230         {
231             var data = new byte[20];
232             data[0] = manufacturerId;
233             data[1] = memoryType;
234             data[2] = capacityCode;
235             data[3] = remainingIdBytes;
236             data[4] = extendedDeviceId;
237             data[5] = deviceConfiguration;
238             // unique ID code (bytes 7:20)
239             return data;
240         }
241 
RecognizeOperation(byte firstByte)242         private void RecognizeOperation(byte firstByte)
243         {
244             currentOperation.Operation = DecodedOperation.OperationType.None;
245             currentOperation.State = DecodedOperation.OperationState.HandleCommand;
246             currentOperation.DummyBytesRemaining = GetDummyBytes((Commands)firstByte);
247             switch(firstByte)
248             {
249                 case (byte)Commands.ReadID:
250                 case (byte)Commands.MultipleIoReadID:
251                     currentOperation.Operation = DecodedOperation.OperationType.ReadID;
252                     break;
253                 case (byte)Commands.ReadSerialFlashDiscoveryParameter:
254                     currentOperation.Operation = DecodedOperation.OperationType.ReadSerialFlashDiscoveryParameter;
255                     currentOperation.State = DecodedOperation.OperationState.AccumulateCommandAddressBytes;
256                     currentOperation.AddressLength = 3;
257                     break;
258                 case (byte)Commands.FastRead:
259                     // fast read - 3 bytes of address + a dummy byte
260                     currentOperation.Operation = DecodedOperation.OperationType.ReadFast;
261                     currentOperation.AddressLength = 3;
262                     currentOperation.State = DecodedOperation.OperationState.AccumulateCommandAddressBytes;
263                     break;
264                 case (byte)Commands.Read:
265                 case (byte)Commands.DualOutputFastRead:
266                 case (byte)Commands.DualInputOutputFastRead:
267                 case (byte)Commands.QuadOutputFastRead:
268                 case (byte)Commands.QuadInputOutputFastRead:
269                 case (byte)Commands.DtrFastRead:
270                 case (byte)Commands.DtrDualOutputFastRead:
271                 case (byte)Commands.DtrDualInputOutputFastRead:
272                 case (byte)Commands.DtrQuadOutputFastRead:
273                 case (byte)Commands.DtrQuadInputOutputFastRead:
274                 case (byte)Commands.QuadInputOutputWordRead:
275                     currentOperation.Operation = DecodedOperation.OperationType.Read;
276                     currentOperation.AddressLength = NumberOfAddressBytes;
277                     currentOperation.State = DecodedOperation.OperationState.AccumulateCommandAddressBytes;
278                     break;
279                 case (byte)Commands.Read4byte:
280                 case (byte)Commands.FastRead4byte:
281                 case (byte)Commands.DualOutputFastRead4byte:
282                 case (byte)Commands.DualInputOutputFastRead4byte:
283                 case (byte)Commands.QuadOutputFastRead4byte:
284                 case (byte)Commands.QuadInputOutputFastRead4byte:
285                 case (byte)Commands.DtrFastRead4byte:
286                 case (byte)Commands.DtrDualInputOutputFastRead4byte:
287                 case (byte)Commands.DtrQuadInputOutputFastRead4byte:
288                     currentOperation.Operation = DecodedOperation.OperationType.Read;
289                     currentOperation.AddressLength = 4;
290                     currentOperation.State = DecodedOperation.OperationState.AccumulateCommandAddressBytes;
291                     break;
292                 case (byte)Commands.PageProgram:
293                 case (byte)Commands.DualInputFastProgram:
294                 case (byte)Commands.ExtendedDualInputFastProgram:
295                 case (byte)Commands.QuadInputFastProgram:
296                 case (byte)Commands.ExtendedQuadInputFastProgram:
297                     currentOperation.Operation = DecodedOperation.OperationType.Program;
298                     currentOperation.AddressLength = NumberOfAddressBytes;
299                     currentOperation.State = DecodedOperation.OperationState.AccumulateCommandAddressBytes;
300                     break;
301                 case (byte)Commands.PageProgram4byte:
302                 case (byte)Commands.QuadInputFastProgram4byte:
303                     currentOperation.Operation = DecodedOperation.OperationType.Program;
304                     currentOperation.AddressLength = 4;
305                     currentOperation.State = DecodedOperation.OperationState.AccumulateCommandAddressBytes;
306                     break;
307                 case (byte)Commands.WriteEnable:
308                     this.Log(LogLevel.Noisy, "Setting write enable latch");
309                     enable.Value = true;
310                     return; //return to prevent further logging
311                 case (byte)Commands.WriteDisable:
312                     this.Log(LogLevel.Noisy, "Unsetting write enable latch");
313                     enable.Value = false;
314                     return; //return to prevent further logging
315                 case (byte)Commands.SubsectorErase4kb:
316                     currentOperation.Operation = DecodedOperation.OperationType.Erase;
317                     currentOperation.EraseSize = DecodedOperation.OperationEraseSize.Subsector4K;
318                     currentOperation.AddressLength = NumberOfAddressBytes;
319                     currentOperation.State = DecodedOperation.OperationState.AccumulateNoDataCommandAddressBytes;
320                     break;
321                 case (byte)Commands.SubsectorErase32kb:
322                     currentOperation.Operation = DecodedOperation.OperationType.Erase;
323                     currentOperation.EraseSize = DecodedOperation.OperationEraseSize.Subsector32K;
324                     currentOperation.AddressLength = NumberOfAddressBytes;
325                     currentOperation.State = DecodedOperation.OperationState.AccumulateNoDataCommandAddressBytes;
326                     break;
327                 case (byte)Commands.SectorErase:
328                     currentOperation.Operation = DecodedOperation.OperationType.Erase;
329                     currentOperation.EraseSize = DecodedOperation.OperationEraseSize.Sector;
330                     currentOperation.AddressLength = NumberOfAddressBytes;
331                     currentOperation.State = DecodedOperation.OperationState.AccumulateNoDataCommandAddressBytes;
332                     break;
333                 case (byte)Commands.DieErase:
334                     currentOperation.Operation = DecodedOperation.OperationType.Erase;
335                     currentOperation.EraseSize = DecodedOperation.OperationEraseSize.Die;
336                     currentOperation.AddressLength = NumberOfAddressBytes;
337                     currentOperation.State = DecodedOperation.OperationState.AccumulateNoDataCommandAddressBytes;
338                     break;
339                 case (byte)Commands.BulkErase:
340                 case (byte)Commands.ChipErase:
341                     this.Log(LogLevel.Noisy, "Performing bulk/chip erase");
342                     EraseChip();
343                     break;
344                 case (byte)Commands.SubsectorErase4byte4kb:
345                     currentOperation.Operation = DecodedOperation.OperationType.Erase;
346                     currentOperation.EraseSize = DecodedOperation.OperationEraseSize.Subsector4K;
347                     currentOperation.AddressLength = 4;
348                     currentOperation.State = DecodedOperation.OperationState.AccumulateNoDataCommandAddressBytes;
349                     break;
350                 case (byte)Commands.SectorErase4byte:
351                     currentOperation.Operation = DecodedOperation.OperationType.Erase;
352                     currentOperation.EraseSize = DecodedOperation.OperationEraseSize.Sector;
353                     currentOperation.AddressLength = 4;
354                     currentOperation.State = DecodedOperation.OperationState.AccumulateNoDataCommandAddressBytes;
355                     break;
356                 case (byte)Commands.Enter4byteAddressMode:
357                     this.Log(LogLevel.Noisy, "Entering 4-byte address mode");
358                     addressingMode.Value = AddressingMode.FourByte;
359                     break;
360                 case (byte)Commands.Exit4byteAddressMode:
361                     this.Log(LogLevel.Noisy, "Exiting 4-byte address mode");
362                     addressingMode.Value = AddressingMode.ThreeByte;
363                     break;
364                 case (byte)Commands.ReadStatusRegister:
365                     currentOperation.Operation = DecodedOperation.OperationType.ReadRegister;
366                     currentOperation.Register = (uint)Register.Status;
367                     break;
368                 case (byte)Commands.ReadConfigurationRegister:
369                     currentOperation.Operation = DecodedOperation.OperationType.ReadRegister;
370                     currentOperation.Register = (uint)Register.Configuration;
371                     break;
372                 case (byte)Commands.WriteStatusRegister:
373                     currentOperation.Operation = DecodedOperation.OperationType.WriteRegister;
374                     currentOperation.Register = (uint)Register.Status;
375                     break;
376                 case (byte)Commands.ReadFlagStatusRegister:
377                     currentOperation.Operation = DecodedOperation.OperationType.ReadRegister;
378                     currentOperation.Register = (uint)Register.FlagStatus;
379                     break;
380                 case (byte)Commands.ReadVolatileConfigurationRegister:
381                     currentOperation.Operation = DecodedOperation.OperationType.ReadRegister;
382                     currentOperation.Register = (uint)Register.VolatileConfiguration;
383                     break;
384                 case (byte)Commands.WriteVolatileConfigurationRegister:
385                     currentOperation.Operation = DecodedOperation.OperationType.WriteRegister;
386                     currentOperation.Register = (uint)Register.VolatileConfiguration;
387                     break;
388                 case (byte)Commands.ReadNonVolatileConfigurationRegister:
389                     currentOperation.Operation = DecodedOperation.OperationType.ReadRegister;
390                     currentOperation.Register = (uint)Register.NonVolatileConfiguration;
391                     break;
392                 case (byte)Commands.WriteNonVolatileConfigurationRegister:
393                     currentOperation.Operation = DecodedOperation.OperationType.WriteRegister;
394                     currentOperation.Register = (uint)Register.NonVolatileConfiguration;
395                     break;
396                 case (byte)Commands.ReadEnhancedVolatileConfigurationRegister:
397                     currentOperation.Operation = DecodedOperation.OperationType.ReadRegister;
398                     currentOperation.Register = (uint)Register.EnhancedVolatileConfiguration;
399                     break;
400                 case (byte)Commands.WriteEnhancedVolatileConfigurationRegister:
401                     currentOperation.Operation = DecodedOperation.OperationType.WriteRegister;
402                     currentOperation.Register = (uint)Register.EnhancedVolatileConfiguration;
403                     break;
404                 case (byte)Commands.ResetEnable:
405                     // This command should allow ResetMemory to be executed
406                 case (byte)Commands.ResetMemory:
407                     // This command should reset volatile bits in configuration registers
408                 case (byte)Commands.EnterDeepPowerDown:
409                     // This command should enter deep power-down mode
410                 case (byte)Commands.ReleaseFromDeepPowerdown:
411                     // This command should leave deep power-down mode, but some chips use a different mechanism
412                     this.Log(LogLevel.Warning, "Unhandled parameterless command {0}", (Commands)firstByte);
413                     return;
414                 default:
415                     this.Log(LogLevel.Error, "Command decoding failed on byte: 0x{0:X} ({1}).", firstByte, (Commands)firstByte);
416                     return;
417             }
418             this.Log(LogLevel.Noisy, "Decoded operation: {0}, write enabled {1}", currentOperation, enable.Value);
419         }
420 
HandleCommand(byte data)421         private byte HandleCommand(byte data)
422         {
423             byte result = 0;
424             if(currentOperation.DummyBytesRemaining > 0)
425             {
426                 currentOperation.DummyBytesRemaining--;
427                 this.Log(LogLevel.Noisy, "Handling dummy byte in {0} operation, {1} remaining after this one",
428                     currentOperation.Operation, currentOperation.DummyBytesRemaining);
429                 return result;
430             }
431 
432             switch(currentOperation.Operation)
433             {
434                 case DecodedOperation.OperationType.ReadFast:
435                 case DecodedOperation.OperationType.Read:
436                     result = ReadFromMemory();
437                     break;
438                 case DecodedOperation.OperationType.ReadID:
439                     if(currentOperation.CommandBytesHandled < deviceData.Length)
440                     {
441                         result = deviceData[currentOperation.CommandBytesHandled];
442                     }
443                     else
444                     {
445                         this.Log(LogLevel.Error, "Trying to read beyond the length of the device ID table.");
446                         result = 0;
447                     }
448                     break;
449                 case DecodedOperation.OperationType.ReadSerialFlashDiscoveryParameter:
450                     result = GetSFDPByte();
451                     break;
452                 case DecodedOperation.OperationType.Program:
453                     if(enable.Value)
454                     {
455                         WriteToMemory(data);
456                         result = data;
457                     }
458                     else
459                     {
460                         this.Log(LogLevel.Error, "Memory write operations are disabled.");
461                     }
462                     break;
463                 case DecodedOperation.OperationType.ReadRegister:
464                     result = ReadRegister((Register)currentOperation.Register);
465                     break;
466                 case DecodedOperation.OperationType.WriteRegister:
467                     WriteRegister((Register)currentOperation.Register, data);
468                     break;
469                 default:
470                     this.Log(LogLevel.Warning, "Unhandled operation encountered while processing command bytes: {0}", currentOperation.Operation);
471                     break;
472             }
473             currentOperation.CommandBytesHandled++;
474             this.Log(LogLevel.Noisy, "Handled command: {0}, returning 0x{1:X}", currentOperation, result);
475             return result;
476         }
477 
GetSFDPByte()478         private byte GetSFDPByte()
479         {
480             if(currentOperation.ExecutionAddress >= SFDPSignature.Length)
481             {
482                 this.Log(LogLevel.Warning, "Tried to read SFDP signature byte out of range at position {0}", currentOperation.ExecutionAddress);
483                 return 0;
484             }
485 
486             var output = SFDPSignature[currentOperation.ExecutionAddress];
487             currentOperation.ExecutionAddress = (currentOperation.ExecutionAddress + 1) % (uint)SFDPSignature.Length;
488             return output;
489         }
490 
WriteRegister(Register register, byte data)491         private void WriteRegister(Register register, byte data)
492         {
493             if(!enable.Value)
494             {
495                 this.Log(LogLevel.Error, "Trying to write a register, but write enable latch is not set");
496                 return;
497             }
498             switch(register)
499             {
500                 case Register.VolatileConfiguration:
501                     volatileConfigurationRegister.Write(0, data);
502                     break;
503                 case Register.NonVolatileConfiguration:
504                 case Register.Configuration:
505                     if((currentOperation.CommandBytesHandled) >= 2)
506                     {
507                         this.Log(LogLevel.Error, "Trying to write to register {0} with more than expected 2 bytes.", register);
508                         break;
509                     }
510                     BitHelper.UpdateWithShifted(ref temporaryConfiguration, data, currentOperation.CommandBytesHandled * 8, 8);
511                     if(currentOperation.CommandBytesHandled == 1)
512                     {
513                         var targetReg = register == Register.Configuration ? configurationRegister : nonVolatileConfigurationRegister;
514                         targetReg.Write(0, (ushort)temporaryConfiguration);
515                     }
516                     break;
517                 //listing all cases as other registers are not writable at all
518                 case Register.EnhancedVolatileConfiguration:
519                     enhancedVolatileConfigurationRegister.Write(0, data);
520                     break;
521                 case Register.Status:
522                     statusRegister.Write(0, data);
523                     // Switch to the Configuration register and write from its start
524                     currentOperation.Register = (uint)Register.Configuration;
525                     currentOperation.CommandBytesHandled--;
526                     break;
527                 default:
528                     this.Log(LogLevel.Warning, "Trying to write 0x{0} to unsupported register \"{1}\"", data, register);
529                     break;
530             }
531         }
532 
ReadRegister(Register register)533         private byte ReadRegister(Register register)
534         {
535             switch(register)
536             {
537                 case Register.Status:
538                     // The documentation states that at least 1 byte will be read
539                     // If more than 1 byte is read, the same byte is returned
540                     return statusRegister.Read();
541                 case Register.FlagStatus:
542                     // The documentation states that at least 1 byte will be read
543                     // If more than 1 byte is read, the same byte is returned
544                     return flagStatusRegister.Read();
545                 case Register.VolatileConfiguration:
546                     // The documentation states that at least 1 byte will be read
547                     // If more than 1 byte is read, the same byte is returned
548                     return volatileConfigurationRegister.Read();
549                 case Register.NonVolatileConfiguration:
550                 case Register.Configuration:
551                     // The documentation states that at least 2 bytes will be read
552                     // After all 16 bits of the register have been read, 0 is returned
553                     if((currentOperation.CommandBytesHandled) < 2)
554                     {
555                         var sourceReg = register == Register.Configuration ? configurationRegister : nonVolatileConfigurationRegister;
556                         return (byte)BitHelper.GetValue(sourceReg.Read(), currentOperation.CommandBytesHandled * 8, 8);
557                     }
558                     return 0;
559                 case Register.EnhancedVolatileConfiguration:
560                     return enhancedVolatileConfigurationRegister.Read();
561                 case Register.ExtendedAddress:
562                 default:
563                     this.Log(LogLevel.Warning, "Trying to read from unsupported register \"{0}\"", register);
564                     return 0;
565             }
566         }
567 
HandleNoDataCommand()568         private void HandleNoDataCommand()
569         {
570             // The documentation describes more commands that don't have any data bytes (just code + address)
571             // but at the moment we have implemented just these ones
572             switch(currentOperation.Operation)
573             {
574                 case DecodedOperation.OperationType.Erase:
575                     if(enable.Value)
576                     {
577                         if(currentOperation.ExecutionAddress >= underlyingMemory.Size)
578                         {
579                             this.Log(LogLevel.Error, "Cannot erase memory because current address 0x{0:X} exceeds configured memory size.", currentOperation.ExecutionAddress);
580                             return;
581                         }
582                         switch(currentOperation.EraseSize)
583                         {
584                             case DecodedOperation.OperationEraseSize.Subsector4K:
585                                 EraseSegment(4.KB());
586                                 break;
587                             case DecodedOperation.OperationEraseSize.Subsector32K:
588                                 EraseSegment(32.KB());
589                                 break;
590                             case DecodedOperation.OperationEraseSize.Sector:
591                                 EraseSegment(sectorSize);
592                                 break;
593                             case DecodedOperation.OperationEraseSize.Die:
594                                 EraseDie();
595                                 break;
596                             default:
597                                 this.Log(LogLevel.Warning, "Unsupported erase type: {0}", currentOperation.EraseSize);
598                                 break;
599                         }
600                     }
601                     else
602                     {
603                         this.Log(LogLevel.Error, "Erase operations are disabled.");
604                     }
605                     break;
606                 default:
607                     this.Log(LogLevel.Warning, "Encountered unexpected command: {0}", currentOperation);
608                     break;
609             }
610         }
611 
EraseChip()612         private void EraseChip()
613         {
614             // Don't allow erasing the chip if range protection is enabled
615             if(lockedRange.HasValue)
616             {
617                 this.Log(LogLevel.Error, "Chip erase can only be performed when there is no locked range");
618                 return;
619             }
620             underlyingMemory.ZeroAll();
621         }
622 
EraseDie()623         private void EraseDie()
624         {
625             // Die erase is a separate operation because on multi-die chips it
626             // will erase only one die (part of the chip). This is not yet
627             // implemented, so we erase the whole chip.
628             EraseChip();
629         }
630 
EraseRangeUnchecked(Range range)631         private void EraseRangeUnchecked(Range range)
632         {
633             var segment = new byte[range.Size];
634             for(ulong i = 0; i < range.Size; i++)
635             {
636                 segment[i] = EmptySegment;
637             }
638             underlyingMemory.WriteBytes((long)range.StartAddress, segment);
639         }
640 
EraseSegment(int segmentSize)641         private void EraseSegment(int segmentSize)
642         {
643             // The documentations states that on erase the operation address is
644             // aligned to the segment size
645             var position = segmentSize * (currentOperation.ExecutionAddress / segmentSize);
646             var segmentToErase = new Range((ulong)position, (ulong)segmentSize);
647             this.Log(LogLevel.Noisy, "Full segment to erase: {0}", segmentToErase);
648             foreach(var subrange in lockedRange.HasValue ? segmentToErase.Subtract(lockedRange.Value) : new List<Range>() { segmentToErase })
649             {
650                 this.Log(LogLevel.Noisy, "Erasing subrange {0}", subrange);
651                 EraseRangeUnchecked(subrange);
652             }
653         }
654 
ReadFromMemory()655         private byte ReadFromMemory()
656         {
657             if(currentOperation.ExecutionAddress + currentOperation.CommandBytesHandled > underlyingMemory.Size)
658             {
659                 this.Log(LogLevel.Error, "Cannot read from address 0x{0:X} because it is bigger than configured memory size.", currentOperation.ExecutionAddress);
660                 return 0;
661             }
662 
663             var position = currentOperation.ExecutionAddress + currentOperation.CommandBytesHandled;
664             return  underlyingMemory.ReadByte(position);
665         }
666 
667         // The addressingMode field is 1-bit wide, so a conditional expression covers all possible cases
668         private int NumberOfAddressBytes => addressingMode.Value == AddressingMode.ThreeByte ? 3 : 4;
669 
670         private DecodedOperation currentOperation;
671         private uint temporaryConfiguration; //this should be an ushort, but due to C# type promotions it's easier to use uint
672 
673         private readonly byte[] deviceData;
674         private readonly byte[] SFDPSignature;
675         private readonly IFlagRegisterField enable;
676         private readonly ByteRegister flagStatusRegister;
677         private readonly IEnumRegisterField<AddressingMode> addressingMode;
678         private readonly ByteRegister volatileConfigurationRegister;
679         private readonly ByteRegister enhancedVolatileConfigurationRegister;
680         private readonly WordRegister nonVolatileConfigurationRegister;
681         private readonly byte manufacturerId;
682         private readonly byte memoryType;
683         private readonly byte capacityCode;
684         private readonly byte remainingIdBytes;
685         private readonly byte extendedDeviceId;
686         private readonly byte deviceConfiguration;
687         private const byte EmptySegment = 0xff;
688         private const byte DeviceGeneration = 0x1;      // 2nd generation
689         private const byte DefaultRemainingIDBytes = 0x10;
690         private const byte DefaultExtendedDeviceID = DeviceGeneration << 6;
691         private const byte DefaultDeviceConfiguration = 0x0;   // standard
692         private const int DefaultSectorSizeKB = 64;
693 
694         // Dummy SFDP header: 0 parameter tables, one empty required
695         private readonly byte[] DefaultSFDPSignature = new byte[]
696         {
697             0x53, 0x46, 0x44, 0x50, 0x06, 0x01, 0x00, 0xFF,
698             0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
699         };
700 
701         protected enum Commands : byte
702         {
703             // Software RESET Operations
704             ResetEnable = 0x66,
705             ResetMemory = 0x99,
706 
707             // READ ID Operations
708             ReadID = 0x9F,
709             MultipleIoReadID = 0xAF,
710             ReadSerialFlashDiscoveryParameter = 0x5A,
711 
712             // READ MEMORY Operations
713             Read = 0x03,
714             FastRead = 0x0B,
715             DualOutputFastRead = 0x3B,
716             DualInputOutputFastRead = 0xBB,
717             QuadOutputFastRead = 0x6B,
718             QuadInputOutputFastRead = 0xEB,
719             DtrFastRead = 0x0D,
720             DtrDualOutputFastRead = 0x3D,
721             DtrDualInputOutputFastRead = 0xBD,
722             DtrQuadOutputFastRead = 0x6D,
723             DtrQuadInputOutputFastRead = 0xED,
724             QuadInputOutputWordRead = 0xE7,
725 
726             // READ MEMORY Operations with 4-Byte Address
727             Read4byte = 0x13,
728             FastRead4byte = 0x0C,
729             DualOutputFastRead4byte = 0x3C,
730             DualInputOutputFastRead4byte = 0xBC,
731             QuadOutputFastRead4byte = 0x6C,
732             QuadInputOutputFastRead4byte = 0xEC,
733             DtrFastRead4byte = 0x0E,
734             DtrDualInputOutputFastRead4byte = 0xBE,
735             DtrQuadInputOutputFastRead4byte = 0xEE,
736 
737             // WRITE Operations
738             WriteEnable = 0x06,
739             WriteDisable = 0x04,
740 
741             // READ REGISTER Operations
742             ReadStatusRegister = 0x05,
743             ReadConfigurationRegister = 0x15,
744             ReadFlagStatusRegister = 0x70,
745             ReadNonVolatileConfigurationRegister = 0xB5,
746             ReadVolatileConfigurationRegister = 0x85,
747             ReadEnhancedVolatileConfigurationRegister = 0x65,
748             ReadExtendedAddressRegister = 0xC8,
749             ReadGeneralPurposeReadRegister = 0x96,
750 
751             // WRITE REGISTER Operations
752             WriteStatusRegister = 0x01,
753             WriteNonVolatileConfigurationRegister = 0xB1,
754             WriteVolatileConfigurationRegister = 0x81,
755             WriteEnhancedVolatileConfigurationRegister = 0x61,
756             WriteExtendedAddressRegister = 0xC5,
757 
758             // CLEAR FLAG STATUS REGISTER Operation
759             ClearFlagStatusRegister = 0x50,
760 
761             // PROGRAM Operations
762             PageProgram = 0x02,
763             DualInputFastProgram = 0xA2,
764             ExtendedDualInputFastProgram = 0xD2,
765             QuadInputFastProgram = 0x32,
766             ExtendedQuadInputFastProgram = 0x38,
767 
768             // PROGRAM Operations with 4-Byte Address
769             PageProgram4byte = 0x12,
770             QuadInputFastProgram4byte = 0x34,
771             QuadInputExtendedFastProgram4byte = 0x3E,
772 
773             // ERASE Operations
774             SubsectorErase32kb = 0x52,
775             SubsectorErase4kb = 0x20,
776             SectorErase = 0xD8,
777             BulkErase = 0x60,
778             ChipErase = 0xC7,
779             DieErase = 0xC4,
780 
781             // ERASE Operations with 4-Byte Address
782             SectorErase4byte = 0xDC,
783             SubsectorErase4byte4kb = 0x21,
784             SubsectorErase4byte32kb = 0x5C,
785 
786             // SUSPEND/RESUME Operations
787             ProgramEraseSuspend = 0x75,
788             ProgramEraseResume = 0x7A,
789 
790             // ONE-TIME PROGRAMMABLE (OTP) Operations
791             ReadOtpArray = 0x4B,
792             ProgramOtpArray = 0x42,
793 
794             // 4-BYTE ADDRESS MODE Operations
795             Enter4byteAddressMode = 0xB7,
796             Exit4byteAddressMode = 0xE9,
797 
798             // QUAD PROTOCOL Operations
799             EnterQuadInputOutputMode = 0x35,
800             ResetQuadInputOutputMode = 0xF5,
801 
802             // Deep Power-Down Operations
803             EnterDeepPowerDown = 0xB9,
804             ReleaseFromDeepPowerdown = 0xAB,
805 
806             // ADVANCED SECTOR PROTECTION Operations
807             ReadSectorProtection = 0x2D,
808             ProgramSectorProtection = 0x2C,
809             ReadVolatileLockBits = 0xE8,
810             WriteVolatileLockBits = 0xE5,
811             ReadNonvolatileLockBits = 0xE2,
812             WriteNonvolatileLockBits = 0xE3,
813             EraseNonvolatileLockBits = 0xE4,
814             ReadGlobalFreezeBit = 0xA7,
815             WriteGlobalFreezeBit = 0xA6,
816             ReadPassword = 0x27,
817             WritePassword = 0x28,
818             UnlockPassword = 0x29,
819 
820             // ADVANCED SECTOR PROTECTION Operations with 4-Byte Address
821             ReadVolatileLockBits4byte = 0xE0,
822             WriteVolatileLockBits4byte = 0xE1,
823 
824             // ADVANCED FUNCTION INTERFACE Operations
825             InterfaceActivation = 0x9B,
826             CyclicRedundancyCheck = 0x27
827         }
828 
829         private enum AddressingMode : byte
830         {
831             FourByte = 0x0,
832             ThreeByte = 0x1
833         }
834 
835         private enum Register : uint
836         {
837             Status = 1, //starting from 1 to leave 0 as an unused value
838             Configuration,
839             FlagStatus,
840             ExtendedAddress,
841             NonVolatileConfiguration,
842             VolatileConfiguration,
843             EnhancedVolatileConfiguration
844         }
845     }
846 }
847