1 // 2 // Copyright (c) 2010-2023 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 using Antmicro.Renode.Core.Structure.Registers; 10 using Antmicro.Renode.Logging; 11 using Antmicro.Renode.Peripherals.Bus.Wrappers; 12 using Antmicro.Renode.Peripherals.Memory; 13 using Antmicro.Renode.Utilities; 14 15 namespace Antmicro.Renode.Peripherals.SPI 16 { 17 public class NANDFlash : ISPIPeripheral, IProvidesRegisterCollection<ByteRegisterCollection> 18 { NANDFlash(MappedMemory dataMemory, MappedMemory spareMemory, uint pageSize = 2048, uint spareSize = 64, uint pagesPerBlock = 64, uint blocksPerLun = 2048, uint lunsPerChip = 1)19 public NANDFlash(MappedMemory dataMemory, MappedMemory spareMemory, 20 uint pageSize = 2048, uint spareSize = 64, 21 uint pagesPerBlock = 64, uint blocksPerLun = 2048, 22 uint lunsPerChip = 1) 23 { 24 receiveStack = new Stack<byte>(); 25 sendQueue = new Queue<byte>(); 26 27 RegistersCollection = new ByteRegisterCollection(this); 28 29 // Use separate data & spare areas so we can easily program 30 // the data memory from a bin/hex file 31 this.dataMemory = dataMemory; 32 this.spareMemory = spareMemory; 33 dataMemory.ResetByte = EmptySegment; 34 spareMemory.ResetByte = EmptySegment; 35 36 this.pageSize = pageSize; 37 this.spareSize = spareSize; 38 this.pagesPerBlock = pagesPerBlock; 39 this.blocksPerLun = blocksPerLun; 40 this.lunsPerChip = lunsPerChip; 41 42 this.pageCache = new byte[pageSize + spareSize]; 43 44 DefineRegisters(); 45 Reset(); 46 } 47 Reset()48 public void Reset() 49 { 50 RegistersCollection.Reset(); 51 state = State.Idle; 52 53 receiveStack.Clear(); 54 sendQueue.Clear(); 55 } 56 Transmit(byte data)57 public byte Transmit(byte data) 58 { 59 this.Log(LogLevel.Noisy, "Received byte: 0x{0:X} in state {1}", data, state); 60 byte result = 0; 61 62 switch(state) 63 { 64 case State.Idle: 65 HandleCommand((Command)data); 66 break; 67 case State.Unsupported: 68 break; 69 case State.GetFeatureAddress: 70 { 71 var r = RegistersCollection.Read(data); 72 sendQueue.Enqueue(r); 73 this.Log(LogLevel.Noisy, "Reading register {0} = {1}", data, r); 74 state = State.TransmitQueue; 75 break; 76 } 77 case State.GetReadColumn: 78 receiveStack.Push(data); 79 result = sendQueue.Dequeue(); 80 if(sendQueue.Count == 0) 81 { 82 column = RxDequeueAsUInt(); 83 this.Log(LogLevel.Noisy, "Column set to {0}", column); 84 state = State.TransmitCache; 85 sendQueue.Enqueue(0); 86 } 87 break; 88 case State.TransmitCache: 89 sendQueue.Enqueue(pageCache[column++]); 90 result = sendQueue.Dequeue(); 91 break; 92 case State.SetFeatureAddress: 93 receiveStack.Push(data); 94 state = State.SetFeatureData; 95 break; 96 case State.SetFeatureData: 97 if(receiveStack.Count == 0) 98 { 99 this.Log(LogLevel.Error, "Trailing bytes on SetFeature command"); 100 } 101 else 102 { 103 var r = (long)receiveStack.Pop(); 104 RegistersCollection.Write(r, data); 105 this.Log(LogLevel.Noisy, "Setting register {0} = {1}", (long)r, data); 106 } 107 break; 108 case State.TransmitQueue: 109 receiveStack.Push(data); 110 if(sendQueue.Count == 0) 111 { 112 this.Log(LogLevel.Warning, "Nothing left in send queue"); 113 } 114 else 115 { 116 result = sendQueue.Dequeue(); 117 } 118 break; 119 default: 120 this.Log(LogLevel.Error, "Received byte 0x{0:X} in an unexpected state: {1}. Ignoring it...", data, state); 121 break; 122 } 123 124 this.Log(LogLevel.Noisy, "Returning byte: 0x{0:X}", result); 125 return result; 126 } 127 FinishTransmission()128 public void FinishTransmission() 129 { 130 this.Log(LogLevel.Noisy, "Transmission finished"); 131 132 if(lastCommand == Command.PageRead) 133 { 134 if(receiveStack.Count != 3) 135 { 136 this.Log(LogLevel.Error, "Incorrect byte count for PageRead {0}", receiveStack.Count); 137 } 138 else 139 { 140 uint page = RxDequeueAsUInt(); 141 this.Log(LogLevel.Noisy, "Loading page {0} from cache", page); 142 uint offset = page * pageSize; 143 dataMemory.ReadBytes(offset, (int)pageSize, pageCache, 0); 144 // TODO: Copy spareMemory to pageCache 145 } 146 } 147 148 receiveStack.Clear(); 149 sendQueue.Clear(); 150 state = State.Idle; 151 } 152 153 public ByteRegisterCollection RegistersCollection { get; } 154 DefineRegisters()155 private void DefineRegisters() 156 { 157 RegisterType.BlockLock.Define(this) 158 .WithValueField(0, 8, name: "data") 159 ; 160 RegisterType.Config.Define(this) 161 .WithValueField(0, 8, name: "config") 162 ; 163 RegisterType.Status.Define(this) 164 .WithValueField(0, 8, name: "status") 165 ; 166 RegisterType.DieSelect.Define(this) 167 .WithValueField(0, 8, name: "die_select") 168 ; 169 } 170 RxDequeueAsUInt()171 private uint RxDequeueAsUInt() 172 { 173 uint output = 0; 174 var count = Math.Min(sizeof(uint), receiveStack.Count); 175 for(int i = 0; i < count; i++) 176 { 177 output |= (uint)(receiveStack.Pop() << (i * 8)); 178 } 179 return output; 180 } 181 HandleCommand(Command command)182 private void HandleCommand(Command command) 183 { 184 lastCommand = command; 185 byte[] bytesToLoad = null; 186 187 switch(command) 188 { 189 case Command.Reset: 190 Reset(); 191 break; 192 case Command.ReadId: 193 bytesToLoad = ReadIdBytes; 194 state = State.TransmitQueue; 195 break; 196 case Command.GetFeature: 197 state = State.GetFeatureAddress; 198 break; 199 case Command.SetFeature: 200 state = State.SetFeatureAddress; 201 break; 202 case Command.PageRead: 203 bytesToLoad = PageReadBytes; 204 state = State.TransmitQueue; 205 break; 206 case Command.ReadFromCache: 207 case Command.FastReadFromCache: 208 bytesToLoad = ReadFromCacheBytes; 209 state = State.GetReadColumn; 210 break; 211 default: 212 this.Log(LogLevel.Error, "Unsupported command 0x{0:X}", (byte)command); 213 state = State.Unsupported; 214 break; 215 } 216 217 if(bytesToLoad != null) 218 { 219 sendQueue.EnqueueRange(bytesToLoad); 220 } 221 } 222 223 private readonly Queue<byte> sendQueue; 224 private readonly Stack<byte> receiveStack; 225 private readonly MappedMemory dataMemory; 226 private readonly MappedMemory spareMemory; 227 228 private readonly byte[] pageCache; 229 private readonly uint pageSize; 230 private readonly uint spareSize; 231 private readonly uint pagesPerBlock; 232 private readonly uint blocksPerLun; 233 private readonly uint lunsPerChip; 234 235 private State state; 236 private Command lastCommand; 237 238 private uint column; 239 240 private static readonly byte[] ReadIdBytes = new byte[] { 0x0, 0xef, 0xbc }; 241 private static readonly byte[] PageReadBytes = new byte[] { 0x0, 0x0, 0x0 }; 242 private static readonly byte[] ReadFromCacheBytes = new byte[] { 0x0, 0x0 }; 243 244 private const byte EmptySegment = 0xff; 245 246 private enum Command 247 { 248 Reset = 0xff, 249 ReadId = 0x9f, 250 GetFeature = 0x0f, 251 SetFeature = 0x1f, 252 PageRead = 0x13, 253 ReadFromCache = 0x03, 254 FastReadFromCache = 0x0b, 255 } 256 257 private enum State 258 { 259 Idle, 260 TransmitQueue, 261 TransmitCache, 262 GetFeatureAddress, 263 SetFeatureAddress, 264 SetFeatureData, 265 GetReadColumn, 266 Unsupported 267 } 268 269 [RegisterMapper.RegistersDescription] 270 private enum RegisterType 271 { 272 BlockLock = 0xa0, 273 Config = 0xb0, 274 Status = 0xc0, 275 DieSelect = 0xd0 276 } 277 } 278 } 279