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