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 8 using System; 9 using System.IO; 10 using System.Linq; 11 using Antmicro.Renode.Core; 12 using Antmicro.Renode.Core.Structure.Registers; 13 using Antmicro.Renode.Exceptions; 14 using Antmicro.Renode.Logging; 15 using Antmicro.Renode.Peripherals.Bus; 16 using Antmicro.Renode.Storage; 17 18 namespace Antmicro.Renode.Peripherals.MTD 19 { 20 public static class SamsungK9NANDFlashExtensions 21 { SamsungK9NANDFlashFromFile(this IMachine machine, string fileName, ulong busAddress, string name, bool nonPersistent = false, byte? partId = null, byte? manufacturerId = null)22 public static void SamsungK9NANDFlashFromFile(this IMachine machine, string fileName, ulong busAddress, string name, 23 bool nonPersistent = false, byte? partId = null, byte? manufacturerId = null) 24 { 25 SamsungK9NANDFlash flash; 26 try 27 { 28 flash = new SamsungK9NANDFlash(machine, fileName, nonPersistent, partId, manufacturerId); 29 } 30 catch(Exception e) 31 { 32 throw new ConstructionException($"Could not create {nameof(SamsungK9NANDFlash)}: {e.Message}", e); 33 } 34 machine.SystemBus.Register(flash, new BusRangeRegistration(busAddress, (ulong)flash.Size)); 35 machine.SetLocalName(flash, name); 36 } 37 } 38 39 public sealed class SamsungK9NANDFlash : BasicBytePeripheral, IKnownSize, IDisposable 40 { SamsungK9NANDFlash(IMachine machine, string fileName, bool nonPersistent = false, byte? partId = null, byte? manufacturerId = null)41 public SamsungK9NANDFlash(IMachine machine, string fileName, bool nonPersistent = false, 42 byte? partId = null, byte? manufacturerId = null) : base(machine) 43 { 44 this.partId = partId ?? DefaultPartId; 45 this.manufacturerId = manufacturerId ?? DefaultManufacturerId; 46 backingStream = DataStorage.Create(fileName, persistent: !nonPersistent, paddingByte: ErasedValue); 47 addressBytes = new byte[AddressCycles]; 48 DefineRegisters(); 49 Reset(); 50 } 51 Reset()52 public override void Reset() 53 { 54 base.Reset(); 55 state = State.ReadMemory; 56 idReadOffset = 0; 57 statusRegister = StatusFlags.WriteEnabled | StatusFlags.Ready; 58 } 59 Dispose()60 public void Dispose() 61 { 62 backingStream.Dispose(); 63 } 64 65 // Note that this is not the size of the flash memory, but the size of its register window. This 66 // flash is not memory-mapped. 67 public long Size => 0x10; 68 DefineRegisters()69 protected override void DefineRegisters() 70 { 71 Registers.Data.Define(this) 72 .WithValueField(0, 8, valueProviderCallback: _ => 73 { 74 switch(state) 75 { 76 case State.ReadMemory: 77 return HandleReadByte(); 78 case State.ReadStatus: 79 return (byte)statusRegister; 80 case State.ReadID: 81 return HandleReadID(idReadOffset++); 82 case State.ReadOob: 83 return ErasedValue; 84 default: 85 this.Log(LogLevel.Warning, "Data register read in unexpected state {0}", state); 86 return 0; 87 } 88 }, writeCallback: (_, value) => 89 { 90 switch(state) 91 { 92 case State.Programming: 93 HandleProgramByte((byte)value); 94 return; 95 default: 96 this.WarningLog("Data register written with 0x{0:x2} in unexpected state {1}", value, state); 97 return; 98 } 99 }); 100 101 Registers.Address.Define(this) 102 .WithValueField(0, 8, FieldMode.Write, writeCallback: (_, value) => 103 { 104 if(addressCycle < AddressCycles) 105 { 106 addressBytes[addressCycle++] = (byte)value; 107 } 108 else 109 { 110 this.WarningLog("Too long ({0} bytes) address write: 0x{1:x2}", ++addressCycle, value); 111 return; 112 } 113 114 if(state == State.Erasing && addressCycle == EraseAddressCycles) // 3-byte address for erase 115 { 116 var memoryOffset = addressBytes[0] | (uint)addressBytes[1] << 8 | (uint)addressBytes[2] << 16; 117 memoryOffset *= (uint)EraseBlockSize / 32; // The block number shifted into the device is multiplied by 32 118 SeekStream(memoryOffset); 119 } 120 else if(addressCycle == AddressCycles) // 5-byte address for read/write 121 { 122 var column = addressBytes[0] | (uint)addressBytes[1] << 8; 123 var row = addressBytes[2] | (uint)addressBytes[3] << 8 | (uint)addressBytes[4] << 16; 124 var memoryOffset = row * RowWidth + column; 125 SeekStream(memoryOffset); 126 } 127 }); 128 129 Registers.Command.Define(this) 130 .WithValueField(0, 8, FieldMode.Write, writeCallback: (_, value) => 131 { 132 // Get ready for a new address to be shifted in 133 addressCycle = 0; 134 135 switch((Command)value) 136 { 137 case Command.ReadMemory1: // some flash chips might only have 1 command cycle for reading, this supports both 138 case Command.ReadMemory2: 139 state = State.ReadMemory; 140 return; 141 case Command.BlockErase1: 142 state = State.Erasing; 143 return; 144 case Command.BlockErase2: 145 HandleErase(); 146 state = State.ReadStatus; 147 return; 148 case Command.PageProgram1: 149 state = State.Programming; 150 return; 151 case Command.ReadID: 152 state = State.ReadID; 153 return; 154 case Command.ReadOob: 155 state = State.ReadOob; 156 return; 157 case Command.PageProgram2: // do nothing, we already programmed on the #1 command 158 case Command.ReadStatus: 159 state = State.ReadStatus; 160 return; 161 case Command.Reset: 162 Reset(); 163 return; 164 default: 165 this.WarningLog("Unknown command written, value 0x{0:x2}", value); 166 return; 167 } 168 }); 169 } 170 HandleReadID(long offset)171 private byte HandleReadID(long offset) 172 { 173 switch(offset) 174 { 175 case 0: 176 return manufacturerId; 177 case 1: 178 return partId; 179 default: 180 this.Log(LogLevel.Warning, "ID Read at unsupported offset 0x{0:x} (tried to read extended ID?)", offset); 181 return 0; 182 } 183 } 184 SeekStream(long offset)185 private void SeekStream(long offset) 186 { 187 if(!IsValidOffset(offset)) 188 { 189 this.WarningLog("Tried to seek to invalid offset 0x{0:x}", offset); 190 return; 191 } 192 backingStream.Seek(offset, SeekOrigin.Begin); 193 } 194 HandleErase()195 private void HandleErase() 196 { 197 var offset = backingStream.Position; 198 if(!IsValidOffset(offset) || offset % EraseBlockSize != 0) 199 { 200 this.ErrorLog("Invalid (or not divisible by 0x{0:x}) block erase address given: 0x{1:x}", EraseBlockSize, offset); 201 statusRegister |= StatusFlags.ProgramEraseError; 202 return; 203 } 204 205 backingStream.Write(Enumerable.Repeat(ErasedValue, EraseBlockSize).ToArray(), 0, EraseBlockSize); 206 backingStream.Seek(offset, SeekOrigin.Begin); 207 this.NoisyLog("Erased block at offset 0x{0:x}, size 0x{1:x}", offset, EraseBlockSize); 208 } 209 HandleReadByte()210 private byte HandleReadByte() 211 { 212 if(!IsValidOffset(backingStream.Position)) 213 { 214 return ErasedValue; 215 } 216 return (byte)backingStream.ReadByte(); 217 } 218 HandleProgramByte(byte value)219 private void HandleProgramByte(byte value) 220 { 221 var offset = backingStream.Position; 222 if(!IsValidOffset(offset)) 223 { 224 return; 225 } 226 227 var flashCellState = (byte)(backingStream.ReadByte() & value); 228 backingStream.Seek(offset, SeekOrigin.Begin); 229 backingStream.WriteByte(flashCellState); 230 this.NoisyLog("Programmed byte at offset 0x{0:x} to 0x{1:x}{2}", offset, flashCellState, 231 value == flashCellState ? "" : " using flash behavior"); 232 } 233 IsValidOffset(long offset)234 private bool IsValidOffset(long offset) 235 { 236 return offset >= 0 && offset < backingStream.Length; 237 } 238 239 // The buffer size must be larger than the erase block size 240 private const int BufferSize = 100 * 1024; 241 private const int EraseBlockSize = 16 * 1024; 242 private const int DataBytesPerRow = 512; 243 private const int SpareBytesPerRow = 32; 244 private const int RowWidth = DataBytesPerRow + SpareBytesPerRow; 245 private const byte ErasedValue = 0xff; 246 private const byte DefaultManufacturerId = 0xec; 247 private const byte DefaultPartId = 0xd5; 248 // Address cycles for read and write 249 private const int AddressCycles = 5; 250 // Address cycles for block erase 251 private const int EraseAddressCycles = 3; 252 private readonly byte partId; 253 private readonly byte manufacturerId; 254 private readonly byte[] addressBytes; 255 private readonly Stream backingStream; 256 257 private StatusFlags statusRegister; 258 private State state; 259 private uint idReadOffset; 260 private int addressCycle; 261 262 private enum Command : byte 263 { 264 ReadID = 0x90, 265 ReadStatus = 0x70, 266 ReadOob = 0x50, 267 ReadMemory1 = 0x00, 268 ReadMemory2 = 0x30, 269 PageProgram1 = 0x80, 270 PageProgram2 = 0x10, 271 BlockErase1 = 0x60, 272 BlockErase2 = 0xD0, 273 Reset = 0xFF, 274 } 275 276 [Flags] 277 private enum StatusFlags : byte 278 { 279 ProgramEraseError = 0x01, 280 Ready = 0x40, 281 WriteEnabled = 0x80, 282 } 283 284 private enum State 285 { 286 ReadID, 287 ReadMemory, 288 ReadOob, 289 Erasing, 290 Programming, 291 ReadStatus 292 } 293 294 private enum Registers 295 { 296 Data = 0x00, 297 Address = 0x04, 298 Command = 0x08, 299 } 300 } 301 } 302