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