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.Linq;
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Logging.Profiling;
13 using Antmicro.Renode.Peripherals.Bus;
14 using Antmicro.Renode.Peripherals.CPU;
15 using Antmicro.Renode.Peripherals.Memory;
16 
17 namespace Antmicro.Renode.Peripherals.MTD
18 {
19     [AllowedTranslations(AllowedTranslation.ByteToDoubleWord | AllowedTranslation.WordToDoubleWord)]
20     public class STM32L0_FlashController : STM32_FlashController, IKnownSize
21     {
STM32L0_FlashController(IMachine machine, MappedMemory flash, MappedMemory eeprom)22         public STM32L0_FlashController(IMachine machine, MappedMemory flash, MappedMemory eeprom) : base(machine)
23         {
24             this.underlyingFlash = flash;
25             this.underlyingEeprom = eeprom;
26 
27             powerDownLock = new LockRegister(this, nameof(powerDownLock), PowerDownKeys);
28             programEraseControlLock = new LockRegister(this, nameof(programEraseControlLock), ProgramEraseControlKeys);
29             programEraseLock = new LockRegister(this, nameof(programEraseLock), ProgramEraseKeys);
30             optionByteLock = new LockRegister(this, nameof(optionByteLock), OptionByteKeys);
31 
32             signatureRegisters = new DoubleWordRegisterCollection(this);
33 
34             DefineRegisters();
35             programEraseControlLock.Locked += delegate
36             {
37                 programEraseLock.Lock();
38                 optionByteLock.Lock();
39                 programEraseControl.Reset();
40             };
41 
42             Reset();
43         }
44 
WriteDoubleWord(long offset, uint value)45         public override void WriteDoubleWord(long offset, uint value)
46         {
47             var reg = (Registers)offset;
48             // The PECR lock is the only one that covers a whole register and not a single bit.
49             // Handle it here for simplicity.
50             if(reg == Registers.ProgramEraseControl && programEraseControlLock.IsLocked)
51             {
52                 this.Log(LogLevel.Warning, "Attempt to write {0:x8} to {1} while it is locked", value, reg);
53                 return;
54             }
55             base.WriteDoubleWord(offset, value);
56         }
57 
Reset()58         public override void Reset()
59         {
60             base.Reset();
61             powerDownLock.Reset();
62             programEraseControlLock.Reset();
63             programEraseLock.Reset();
64             optionByteLock.Reset();
65         }
66 
67         [ConnectionRegion("signature")]
ReadDoubleWordFromSignature(long offset)68         public uint ReadDoubleWordFromSignature(long offset)
69         {
70             return signatureRegisters.Read(offset);
71         }
72 
73         [ConnectionRegion("signature")]
WriteDoubleWordToSignature(long offset, uint value)74         public void WriteDoubleWordToSignature(long offset, uint value)
75         {
76             //intentionally left blank
77             this.Log(LogLevel.Error, "Attempt to write {0:x8} to {1} in the signature region", value, offset);
78         }
79 
80         public long Size => 0x400;
81 
DefineRegisters()82         private void DefineRegisters()
83         {
84             Registers.AccessControl.Define(this)
85                 .WithFlag(0, name: "LATENCY")
86                 .WithFlag(1, out prefetchEnabled, name: "PRFTEN", changeCallback: (oldValue, value) =>
87                     {
88                         if(value && disableBuffer.Value)
89                         {
90                             this.Log(LogLevel.Warning, "Attempt to set PRFTEN while DISAB_BUF is set, ignoring");
91                             prefetchEnabled.Value = oldValue;
92                         }
93                     })
94                 .WithReservedBits(2, 1)
95                 .WithTaggedFlag("SLEEP_PD", 3)
96                 .WithFlag(4, out runPowerDown, name: "RUN_PD", changeCallback: (oldValue, value) =>
97                     {
98                         if(powerDownLock.IsLocked)
99                         {
100                             this.Log(LogLevel.Warning, "Attempt to write RUN_PD while it is locked, ignoring");
101                             runPowerDown.Value = oldValue;
102                         }
103                         else if(!value)
104                         {
105                             powerDownLock.Lock(); // Resetting the RUN_PD flag re-locks the bit
106                         }
107                     })
108                 .WithFlag(5, out disableBuffer, name: "DISAB_BUF", changeCallback: (_, value) =>
109                     {
110                         if(value)
111                         {
112                             prereadEnabled.Value = false;
113                             prefetchEnabled.Value = false;
114                         }
115                     })
116                 .WithFlag(6, out prereadEnabled, name: "PRE_READ", changeCallback: (oldValue, value) =>
117                     {
118                         if(value && disableBuffer.Value)
119                         {
120                             this.Log(LogLevel.Warning, "Attempt to set PRE_READ while DISAB_BUF is set, ignoring");
121                             prereadEnabled.Value = oldValue;
122                         }
123                     });
124 
125             // PECR is protected by programEraseControlLock in WriteDoubleWord. If we get to any of
126             // the callbacks below, the PECR register is definitely not locked and they don't need to check.
127             programEraseControl = Registers.ProgramEraseControl.Define(this, 0x7)
128                 .WithFlag(0, name: "PE_LOCK", valueProviderCallback: _ => programEraseControlLock.IsLocked, changeCallback: (_, value) =>
129                     {
130                         if(value)
131                         {
132                             programEraseControlLock.Lock();
133                         }
134                         // We don't need to handle an attempt at a keyless unlock because PECR can only
135                         // be written if it is unlocked (see WriteDoubleWord)
136                     })
137                 .WithFlag(1, name: "PRG_LOCK", valueProviderCallback: _ => programEraseLock.IsLocked, changeCallback: (_, value) =>
138                     {
139                         if(value)
140                         {
141                             programEraseLock.Lock();
142                         }
143                         else
144                         {
145                             this.Log(LogLevel.Warning, "Attempt to unlock PRG_LOCK without key");
146                         }
147                     })
148                 .WithFlag(2, name: "OPT_LOCK", valueProviderCallback: _ => optionByteLock.IsLocked, changeCallback: (_, value) =>
149                     {
150                         if(value)
151                         {
152                             optionByteLock.Lock();
153                         }
154                         else
155                         {
156                             this.Log(LogLevel.Warning, "Attempt to unlock OPT_LOCK without key");
157                         }
158                     })
159                 .WithFlag(3, out programMemorySelect, name: "PROG")
160                 .WithTaggedFlag("DATA", 4)
161                 .WithReservedBits(5, 3)
162                 .WithTaggedFlag("FIX", 8)
163                 .WithFlag(9, name: "ERASE", changeCallback: (_, value) => { SetEraseMode(value); })
164                 .WithTaggedFlag("FPRG", 10)
165                 .WithReservedBits(11, 4)
166                 .WithTaggedFlag("PARALLELBANK", 15)
167                 .WithTaggedFlag("EOPIE", 16)
168                 .WithTaggedFlag("ERRIE", 17)
169                 .WithTaggedFlag("OBL_LAUNCH", 18)
170                 .WithReservedBits(19, 4)
171                 .WithTaggedFlag("NZDISABLE", 23)
172                 .WithReservedBits(24, 8);
173 
174             Registers.PowerDownKey.Define(this)
175                 .WithValueField(0, 32, mode: FieldMode.Write, name: "FLASH_PDKEYR", writeCallback: (_, value) =>
176                     {
177                         powerDownLock.ConsumeValue((uint)value);
178                     });
179 
180             Registers.ProgramEraseControlKey.Define(this)
181                 .WithValueField(0, 32, mode: FieldMode.Write, name: "FLASH_PEKEYR", writeCallback: (_, value) =>
182                     {
183                         programEraseControlLock.ConsumeValue((uint)value);
184                     });
185 
186             Registers.ProgramAndEraseKey.Define(this)
187                 .WithValueField(0, 32, mode: FieldMode.Write, name: "FLASH_PRGKEYR", writeCallback: (_, value) =>
188                     {
189                         programEraseLock.ConsumeValue((uint)value);
190                     });
191 
192             Registers.OptionBytesUnlockKey.Define(this)
193                 .WithValueField(0, 32, mode: FieldMode.Write, name: "FLASH_OPTKEYR", writeCallback: (_, value) =>
194                     {
195                         optionByteLock.ConsumeValue((uint)value);
196                         this.Log(LogLevel.Warning, "Option bytes unlock key register accessed, option bytes currently unimplemented");
197                     });
198 
199             Registers.Status.Define(this, 0xC)
200                 .WithFlag(0, mode: FieldMode.Read, valueProviderCallback: _ => false, name: "BSY")
201                 .WithFlag(1, mode: FieldMode.Read | FieldMode.WriteOneToClear, valueProviderCallback: _ => false, name: "EOP")
202                 .WithFlag(2, mode: FieldMode.Read, valueProviderCallback: _ => true, name: "ENDHV")
203                 .WithFlag(3, mode: FieldMode.Read, valueProviderCallback: _ => true, name: "READY")
204                 .WithReservedBits(4, 4)
205                 .WithFlag(8, mode: FieldMode.WriteOneToClear, name: "WRPERR")
206                 .WithFlag(9, mode: FieldMode.WriteOneToClear, name: "PGAERR")
207                 .WithFlag(10, mode: FieldMode.WriteOneToClear, name: "SIZERR")
208                 .WithFlag(11, mode: FieldMode.WriteOneToClear, name: "OPTVERR")
209                 .WithReservedBits(12, 1)
210                 .WithFlag(13, mode: FieldMode.WriteOneToClear, name: "RDERR")
211                 .WithReservedBits(14, 2)
212                 .WithFlag(16, mode: FieldMode.WriteOneToClear, name: "NOTZEROERR")
213                 .WithFlag(17, mode: FieldMode.WriteOneToClear, name: "FWWERR")
214                 .WithReservedBits(18, 14);
215 
216             Registers.OptionBytes.Define(this, 0x807000AA)
217                 .WithValueField(0, 8, mode: FieldMode.Read, name: "RDPROT")
218                 .WithFlag(8, mode: FieldMode.Read, name: "WPRMOD")
219                 .WithReservedBits(9, 7)
220                 .WithValueField(16, 4, mode: FieldMode.Read, name: "BOR_LEV")
221                 .WithFlag(20, mode: FieldMode.Read, name: "WDG_SW")
222                 .WithFlag(21, mode: FieldMode.Read, name: "nRTS_STOP")
223                 .WithFlag(22, mode: FieldMode.Read, name: "nRTS_STDBY")
224                 .WithFlag(23, mode: FieldMode.Read, name: "BFB2")
225                 .WithReservedBits(24, 5)
226                 .WithFlag(29, mode: FieldMode.Read, name: "nBOOT_SEL")
227                 .WithFlag(30, mode: FieldMode.Read, name: "nBOOT0")
228                 .WithFlag(31, mode: FieldMode.Read, name: "nBOOT1");
229 
230             Registers.WriteProtection1.Define(this)
231                 .WithValueField(0, 32, mode: FieldMode.Read, name: "WRPROT1");
232 
233             Registers.WriteProtection2.Define(this)
234                 .WithValueField(0, 16, mode: FieldMode.Read, name: "WRPROT2")
235                 .WithReservedBits(16, 16);
236 
237             // The fields containing the lot number are in ASCII (so the lot number is "000000")
238             SignatureRegisters.UniqueId1.Define(signatureRegisters)
239                 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => 0, name: "WAF_NUM")
240                 .WithValueField(8, 24, FieldMode.Read, valueProviderCallback: _ => 0x3030, name: "LOT_NUM[55:32]");
241 
242             SignatureRegisters.UniqueId2.Define(signatureRegisters)
243                 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => 0x30303030, name: "LOT_NUM[31:0]");
244 
245             SignatureRegisters.UniqueId3.Define(signatureRegisters)
246                 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => 0, name: "U_ID[95:64]");
247 
248             SignatureRegisters.FlashSize.Define(signatureRegisters)
249                 .WithValueField(0, 16, FieldMode.Read, valueProviderCallback: _ => (uint)(underlyingFlash.Size / 1024), name: "F_SIZE");
250         }
251 
EraseMemoryAccessHook(ulong pc, MemoryOperation operation, ulong virtualAddress, ulong physicalAddress, ulong value)252         private void EraseMemoryAccessHook(ulong pc, MemoryOperation operation, ulong virtualAddress, ulong physicalAddress, ulong value)
253         {
254             // Only write accesses can be used to erase
255             if(operation != MemoryOperation.MemoryWrite && operation != MemoryOperation.MemoryIOWrite)
256             {
257                 return;
258             }
259 
260             // Only accesses to our underlying flash or EEPROM peripheral can be used to erase
261             var registered = machine.SystemBus.WhatIsAt(physicalAddress);
262             if(registered == null)
263             {
264                 return;
265             }
266 
267             var offset = physicalAddress - registered.RegistrationPoint.Range.StartAddress;
268             if(registered.Peripheral == underlyingFlash)
269             {
270                 // Program memory must be selected
271                 if(!programMemorySelect.Value)
272                 {
273                     this.Log(LogLevel.Warning, "Erase attempted without program memory being selected");
274                     return;
275                 }
276 
277                 // Writing anything anywhere within a flash page erases the whole page
278                 var flashPageStart = offset & ~(ulong)(FlashPageSize - 1);
279                 underlyingFlash.WriteBytes((long)flashPageStart, FlashPageErasePattern);
280                 this.Log(LogLevel.Debug, "Erased flash page {0} (at 0x{1:x8})", flashPageStart / FlashPageSize, flashPageStart);
281             }
282             else if(registered.Peripheral == underlyingEeprom)
283             {
284                 // Program memory must not be selected
285                 if(programMemorySelect.Value)
286                 {
287                     this.Log(LogLevel.Warning, "EEPROM word erase attempted with program memory selected");
288                     return;
289                 }
290 
291                 // Writing anything anywhere within an EEPROM word erases the whole word
292                 var eepromWordStart = offset & ~(ulong)(EepromWordSize - 1);
293                 underlyingEeprom.WriteDoubleWord((long)eepromWordStart, EepromWordErasePattern);
294                 this.Log(LogLevel.Debug, "Erased EEPROM word {0} (at 0x{1:x8})", eepromWordStart / EepromWordSize, eepromWordStart);
295             }
296         }
297 
SetEraseMode(bool enabled)298         private void SetEraseMode(bool enabled)
299         {
300             if(!machine.SystemBus.TryGetCurrentCPU(out var icpu))
301             {
302                 this.Log(LogLevel.Error, "Failed to get CPU");
303                 return;
304             }
305 
306             var cpu = icpu as ICPUWithMemoryAccessHooks;
307             if(cpu == null)
308             {
309                 this.Log(LogLevel.Error, "CPU does not support memory access hooks, cannot trigger memory erase");
310                 return;
311             }
312 
313             Action<ulong, MemoryOperation, ulong, ulong, ulong> hook = EraseMemoryAccessHook;
314             cpu.SetHookAtMemoryAccess(enabled ? hook : null);
315         }
316 
317         private readonly MappedMemory underlyingFlash;
318         private readonly MappedMemory underlyingEeprom;
319 
320         private DoubleWordRegister programEraseControl;
321         private IFlagRegisterField prefetchEnabled;
322         private IFlagRegisterField runPowerDown;
323         private IFlagRegisterField prereadEnabled;
324         private IFlagRegisterField disableBuffer;
325         private IFlagRegisterField programMemorySelect;
326         private readonly LockRegister powerDownLock;
327         private readonly LockRegister programEraseControlLock;
328         private readonly LockRegister programEraseLock;
329         private readonly LockRegister optionByteLock;
330 
331         private readonly DoubleWordRegisterCollection signatureRegisters;
332 
333         private const int EepromWordSize = 4;
334         private const int FlashPageSize = 128;
335         private const uint EepromWordErasePattern = 0;
336         private static readonly byte[] FlashPageErasePattern = (byte[])Enumerable.Repeat((byte)0x00, FlashPageSize).ToArray();
337         private static readonly uint[] PowerDownKeys = {0x04152637, 0xFAFBFCFD};
338         private static readonly uint[] ProgramEraseControlKeys = {0x89ABCDEF, 0x02030405};
339         private static readonly uint[] ProgramEraseKeys = {0x8C9DAEBF, 0x13141516};
340         private static readonly uint[] OptionByteKeys = {0xFBEAD9C8, 0x24252627};
341 
342         private enum Registers : long
343         {
344             AccessControl = 0x00,          // ACR
345             ProgramEraseControl = 0x04,    // PECR
346             PowerDownKey = 0x08,           // PDKEYR
347             ProgramEraseControlKey = 0x0C, // PEKEYR
348             ProgramAndEraseKey = 0x10,     // PRGKEYR
349             OptionBytesUnlockKey = 0x14,   // OPTKEYR
350             Status = 0x18,                 // SR
351             OptionBytes = 0x1C,            // OPTR
352             WriteProtection1 = 0x20,       // WRPROT1
353             WriteProtection2 = 0x80,       // WRPROT2
354         }
355 
356         private enum SignatureRegisters : long
357         {
358             UniqueId1 = 0x50, // U_ID(31:0)
359             UniqueId2 = 0x54, // U_ID(63:32)
360             UniqueId3 = 0x64, // U_ID(95:64)
361             FlashSize = 0x7c, // F_SIZE
362         }
363     }
364 }
365