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;
8 using System.Collections.Generic;
9 
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Peripherals.Memory;
14 using Antmicro.Renode.Utilities;
15 
16 using Range = Antmicro.Renode.Core.Range;
17 
18 namespace Antmicro.Renode.Peripherals.SPI
19 {
20     public class NPCX_Flash : ISPIPeripheral, IGPIOReceiver, IProvidesRegisterCollection<ByteRegisterCollection>
21     {
NPCX_Flash(MappedMemory memory)22         public NPCX_Flash(MappedMemory memory)
23         {
24             this.memory = memory;
25 
26             addressBuffer = new List<byte>();
27             RegistersCollection = new ByteRegisterCollection(this);
28             DefineRegisters();
29             Reset();
30         }
31 
Reset()32         public void Reset()
33         {
34             writeEnabled = false;
35             resetWriteEnable = false;
36             WriteProtect.Set();
37             FinishTransmission();
38         }
39 
Transmit(byte data)40         public byte Transmit(byte data)
41         {
42             byte returnValue = 0x0;
43             if(!currentCommand.HasValue)
44             {
45                 currentCommand = (Commands)data;
46                 // The following commands will only send 1 byte so they are
47                 // explicitly handled with the command latching logic
48                 switch(currentCommand.Value)
49                 {
50                     case Commands.WriteEnable:
51                         writeEnabled = true;
52                         break;
53                     case Commands.WriteDisable:
54                         writeEnabled = false;
55                         break;
56                 }
57                 return returnValue;
58             }
59 
60             switch(currentCommand.Value)
61             {
62                 case Commands.ReadStatusRegister1:
63                     returnValue = RegistersCollection.Read((long)Registers.StatusRegister1);
64                     break;
65                 case Commands.ReadStatusRegister2:
66                     returnValue = RegistersCollection.Read((long)Registers.StatusRegister2);
67                     break;
68                 case Commands.BlockErase64:
69                 {
70                     if(addressBuffer.Count < AddressByteCount)
71                     {
72                         addressBuffer.Add(data);
73                     }
74 
75                     if(addressBuffer.Count != AddressByteCount)
76                     {
77                         break;
78                     }
79 
80                     if(!writeEnabled)
81                     {
82                         this.ErrorLog("Attempted to perform a Block Erase operation while flash is in write-disabled state. Operation will be ignored");
83                         break;
84                     }
85 
86                     var address = BitHelper.ToUInt32(addressBuffer.ToArray(), 0, AddressByteCount, true);
87                     var protectedRange = ProtectedRange;
88                     if(protectedRange.HasValue && protectedRange.Value.Contains(address))
89                     {
90                         this.ErrorLog("Attempted to perform a Block Erase operation on a protected block. Operation will be ignored");
91                         break;
92                     }
93 
94                     memory.SetRange(address, 64.KB(), 0xFF);
95                     writeEnabled = false;
96                     break;
97                 }
98                 case Commands.PageProgram:
99                 {
100                     if(addressBuffer.Count < AddressByteCount)
101                     {
102                         addressBuffer.Add(data);
103                         if(addressBuffer.Count == AddressByteCount)
104                         {
105                             temporaryAddress = BitHelper.ToUInt32(addressBuffer.ToArray(), 0, AddressByteCount, true);
106                         }
107                         break;
108                     }
109 
110                     if(!writeEnabled)
111                     {
112                         this.ErrorLog("Attempted to perform a Page Program operation while flash is in write-disabled state. Operation will be ignored");
113                         break;
114                     }
115 
116                     var protectedRange = ProtectedRange;
117                     if(protectedRange.HasValue && protectedRange.Value.Contains(temporaryAddress))
118                     {
119                         this.ErrorLog("Attempted to perform a Page Program operation on a protected block. Operation will be ignored");
120                         break;
121                     }
122 
123                     memory.WriteByte(temporaryAddress, data);
124                     var currentPage = temporaryAddress / PageProgramSize;
125                     var nextPage = (temporaryAddress + 1) / PageProgramSize;
126                     if(nextPage == currentPage)
127                     {
128                         temporaryAddress++;
129                     }
130                     else
131                     {
132                         // Address should wrap around to the start of the page
133                         // when attempting to cross the page boundary
134                         temporaryAddress = currentPage * PageProgramSize;
135                     }
136                     resetWriteEnable = true;
137                     break;
138                 }
139                 case Commands.WriteStatusRegister:
140                     if(statusRegisterProtect.Value && !WriteProtect.IsSet)
141                     {
142                         this.Log(LogLevel.Warning, "Trying to write status register while SRP is enabled and WP signal is low; ignoring");
143                         break;
144                     }
145 
146                     if(temporaryAddress > (uint)Registers.StatusRegister2)
147                     {
148                         writeEnabled = false;
149                     }
150 
151                     if(!writeEnabled)
152                     {
153                         this.ErrorLog("Attempted to perform a Write Status operation while flash is in write-disabled state. Operation will be ignored");
154                         break;
155                     }
156 
157                     RegistersCollection.Write(temporaryAddress, data);
158                     temporaryAddress++;
159                     break;
160                 default:
161                     if(Enum.IsDefined(typeof(Commands), currentCommand.Value))
162                     {
163                         this.WarningLog("Unsupported command: {0}", currentCommand.Value);
164                     }
165                     else
166                     {
167                         this.ErrorLog("Invalid command: {0}", currentCommand.Value);
168                     }
169                     break;
170             }
171             return returnValue;
172         }
173 
FinishTransmission()174         public void FinishTransmission()
175         {
176             currentCommand = null;
177             addressBuffer.Clear();
178             temporaryAddress = 0x0;
179             if(resetWriteEnable)
180             {
181                 writeEnabled = false;
182             }
183             resetWriteEnable = false;
184         }
185 
OnGPIO(int number, bool value)186         public void OnGPIO(int number, bool value)
187         {
188             if(number != 0)
189             {
190                 this.Log(LogLevel.Error, "This peripheral supports gpio input only on index 0, but {0} was called.", number);
191                 return;
192             }
193             WriteProtect.Set(value);
194         }
195 
196         public ByteRegisterCollection RegistersCollection { get; }
197         public GPIO WriteProtect { get; } = new GPIO();
198 
199         public BlockProtect BlockProtectBits
200         {
201             get => blockProtectBits.Value;
202             set
203             {
204                 blockProtectBitsMSB.Value = value != BlockProtect.None;
205                 blockProtectBits.Value = value;
206             }
207         }
208 
209         public Range? ProtectedRange
210         {
211             get
212             {
213                 switch(BlockProtectBits)
214                 {
215                     case BlockProtect._64KB:
216                         return new Range(0x0, (ulong)64.KB());
217                     case BlockProtect._128KB:
218                         return new Range(0x0, (ulong)128.KB());
219                     case BlockProtect._256KB:
220                         return new Range(0x0, (ulong)256.KB());
221                     case BlockProtect._512KB:
222                         return new Range(0x0, (ulong)512.KB());
223                     case BlockProtect.Everything:
224                         return new Range(0x0, (ulong)memory.Size);
225                     case BlockProtect.None:
226                     case BlockProtect.Reserved1:
227                     case BlockProtect.Reserved2:
228                         return null;
229                     default:
230                         throw new Exception("unreachable");
231                 }
232             }
233         }
234 
DefineRegisters()235         private void DefineRegisters()
236         {
237             Registers.StatusRegister1.Define(this)
238                 .WithTaggedFlag("BUSY", 0)
239                 .WithFlag(1, name: "WEL (Write Enable Latch)",
240                     valueProviderCallback: _ => writeEnabled,
241                     writeCallback: (_, value) => writeEnabled = value)
242                 .WithEnumField(2, 3, out blockProtectBits, name: "BP0-BP2 (Block Protect Bits)")
243                 .WithFlag(5, out blockProtectBitsMSB, name: "BP3 (Block Protect Bits)")
244                 .WithReservedBits(6, 1)
245                 .WithFlag(7, out statusRegisterProtect, name: "SRP (Status Register Protect)");
246 
247             Registers.StatusRegister2.Define(this)
248                 .WithReservedBits(0, 1)
249                 .WithTaggedFlag("QE (Quad Enable)", 1)
250                 .WithTaggedFlag("LB0 (Security Register Lock)", 2)
251                 .WithTaggedFlag("LB1 (Security Register Lock)", 3)
252                 .WithTaggedFlag("LB2 (Security Register Lock)", 4)
253                 .WithTaggedFlag("LB3 (Security Register Lock)", 5)
254                 .WithReservedBits(6, 2);
255         }
256 
257         private Commands? currentCommand;
258 
259         private bool writeEnabled;
260         private uint temporaryAddress;
261         private bool resetWriteEnable;
262 
263         private readonly List<byte> addressBuffer;
264         private readonly MappedMemory memory;
265 
266         private const int PageProgramSize = 256;
267         private const int AddressByteCount = 3;
268 
269         private IEnumRegisterField<BlockProtect> blockProtectBits;
270         private IFlagRegisterField blockProtectBitsMSB;
271         private IFlagRegisterField statusRegisterProtect;
272 
273         public enum BlockProtect
274         {
275             None,
276             _64KB,
277             _128KB,
278             _256KB,
279             _512KB,
280             Reserved1,
281             Reserved2,
282             Everything,
283         }
284 
285         private enum Registers
286         {
287             StatusRegister1,
288             StatusRegister2,
289         }
290 
291         private enum Commands : byte
292         {
293             WriteEnable                             = 0x06,
294             WriteEnableForVolatileStatusRegister    = 0x50,
295             WriteDisable                            = 0x04,
296             ReadStatusRegister1                     = 0x05,
297             ReadStatusRegister2                     = 0x35,
298             WriteStatusRegister                     = 0x01,
299             ReadSFDPRegister                        = 0x5A,
300             ReadData                                = 0x03,
301             FastRead                                = 0x0B,
302             FastReadDualIO                          = 0xBB,
303             FastReadQuadIO                          = 0xEB,
304             PageProgram                             = 0x02,
305             SectorErase                             = 0x20, // Erases 4KB
306             BlockErase32                            = 0x51, // Erases 32KB
307             BlockErase64                            = 0xD8, // Erases 64KB
308             ChipErase1                              = 0xC7,
309             ChipErase2                              = 0x60,
310             PowerDown                               = 0xB9,
311             ReleasePowerDown                        = 0xAB,
312             ManufacturerDeviceID                    = 0x90,
313             JEDECID                                 = 0x9F,
314         }
315     }
316 }
317