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 System.Collections.Generic;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Logging.Profiling;
14 using Antmicro.Renode.Peripherals.Bus;
15 using Antmicro.Renode.Peripherals.CPU;
16 using Antmicro.Renode.Peripherals.Memory;
17 using Antmicro.Renode.Utilities;
18 
19 namespace Antmicro.Renode.Peripherals.MTD
20 {
21     [AllowedTranslations(AllowedTranslation.ByteToDoubleWord | AllowedTranslation.WordToDoubleWord)]
22     public class STM32F4_FlashController : STM32_FlashController, IKnownSize
23     {
STM32F4_FlashController(IMachine machine, MappedMemory flash)24         public STM32F4_FlashController(IMachine machine, MappedMemory flash) : base(machine)
25         {
26             this.flash = flash;
27 
28             controlLock = new LockRegister(this, nameof(controlLock), ControlLockKey);
29             optionControlLock = new LockRegister(this, nameof(optionControlLock), OptionLockKey);
30 
31             optionBytesRegisters = new DoubleWordRegisterCollection(this);
32 
33             DefineRegisters();
34             Reset();
35         }
36 
Reset()37         public override void Reset()
38         {
39             base.Reset();
40             controlLock.Reset();
41             optionControlLock.Reset();
42         }
43 
44         [ConnectionRegion("optionBytes")]
ReadDoubleWordFromOptionBytes(long offset)45         public uint ReadDoubleWordFromOptionBytes(long offset)
46         {
47             uint value = optionBytesRegisters.Read(offset);
48             this.Log(LogLevel.Debug, "Reading from option bytes (offset: 0x{0:X} value: 0x{1:X8})", offset, value);
49             return value;
50         }
51 
52         [ConnectionRegion("optionBytes")]
WriteDoubleWordToOptionBytes(long offset, uint value)53         public void WriteDoubleWordToOptionBytes(long offset, uint value)
54         {
55             // This region is modified by using the OptionControl register. Direct modification is not allowed
56             this.Log(LogLevel.Error, "Attempt to write 0x{0:X8} to {1} in the option bytes region", value, offset);
57         }
58 
WriteDoubleWord(long offset, uint value)59         public override void WriteDoubleWord(long offset, uint value)
60         {
61             if((Registers)offset == Registers.Control && controlLock.IsLocked)
62             {
63                 this.Log(LogLevel.Warning, "Attempted to write 0x{0:X8} to a locked Control register. Ignoring...", value);
64                 return;
65             }
66 
67             if((Registers)offset == Registers.OptionControl && optionControlLock.IsLocked)
68             {
69                 this.Log(LogLevel.Warning, "Attempted to write 0x{0:X8} to a locked OptionControl register. Ignoring...", value);
70                 return;
71             }
72 
73             base.WriteDoubleWord(offset, value);
74         }
75 
76         public long Size => 0x400;
77 
DefineRegisters()78         private void DefineRegisters()
79         {
80             Registers.AccessControl.Define(this)
81                 //This field is written and read by software and we need to keep it's value.
82                 .WithValueField(0, 4, name: "LATENCY")
83                 .WithReservedBits(4, 4)
84                 .WithTaggedFlag("PRFTEN", 8)
85                 .WithTaggedFlag("ICEN", 9)
86                 .WithTaggedFlag("DCEN", 10)
87                 .WithTaggedFlag("ICRST", 11)
88                 .WithTaggedFlag("DCRST", 12)
89                 .WithReservedBits(13, 19);
90 
91             Registers.Key.Define(this)
92                 .WithValueField(0, 32, FieldMode.Write, name: "FLASH_KEYR",
93                     writeCallback: (_, value) => controlLock.ConsumeValue((uint)value));
94 
95             Registers.OptionKey.Define(this)
96                 .WithValueField(0, 32, FieldMode.Write, name: "FLASH_OPTKEYR",
97                     writeCallback: (_, value) => optionControlLock.ConsumeValue((uint)value));
98 
99             Registers.Status.Define(this)
100                 .WithTaggedFlag("EOP", 0)
101                 .WithTaggedFlag("OPERR", 1)
102                 .WithReservedBits(2, 2)
103                 .WithTaggedFlag("WRPERR", 4)
104                 .WithTaggedFlag("PGAERR", 5)
105                 .WithTaggedFlag("PGPERR", 6)
106                 .WithTaggedFlag("PGSERR", 7)
107                 .WithTaggedFlag("RDERR", 8)
108                 .WithReservedBits(9, 7)
109                 .WithTaggedFlag("BSY", 16)
110                 .WithReservedBits(17, 15);
111 
112             Registers.Control.Define(this)
113                 .WithTaggedFlag("PG", 0)
114                 .WithFlag(1, out var sectorErase, name: "SER")
115                 .WithFlag(2, out var massErase, name: "MER")
116                 .WithValueField(3, 4, out var sectorNumber, name: "SNB")
117                 .WithReservedBits(7, 1)
118                 .WithTag("PSIZE", 8, 2)
119                 .WithReservedBits(10, 6)
120                 .WithFlag(16, out var startErase, name: "STRT", mode: FieldMode.Read | FieldMode.Set, valueProviderCallback: _ => false)
121                 .WithReservedBits(17, 7)
122                 .WithTaggedFlag("EOPIE", 24)
123                 .WithTaggedFlag("ERRIE", 25)
124                 .WithReservedBits(26, 5)
125                 .WithFlag(31, FieldMode.Read | FieldMode.Set, name: "LOCK", valueProviderCallback: _ => controlLock.IsLocked,
126                     changeCallback: (_, value) =>
127                     {
128                         if(value)
129                         {
130                             controlLock.Lock();
131                         }
132                     })
133                 .WithChangeCallback((_, __) =>
134                 {
135                     if(startErase.Value)
136                     {
137                         Erase(massErase.Value, sectorErase.Value, (uint)sectorNumber.Value);
138                     }
139                 });
140 
141             Registers.OptionControl.Define(this, 0xFFFAAED)
142                 .WithFlag(0, FieldMode.Read | FieldMode.Set, name: "OPTLOCK", valueProviderCallback: _ => optionControlLock.IsLocked,
143                     changeCallback: (_, value) =>
144                     {
145                         if (value)
146                         {
147                             optionControlLock.Lock();
148                         }
149                     })
150                 .WithFlag(1, FieldMode.Read | FieldMode.Set, name: "OPTSTRT", changeCallback: (_, value) =>
151                     {
152                         if(value)
153                         {
154                             readProtectionOptionBytes.Value = readProtectionRegister.Value;
155                             writeProtectionOptionBytes.Value = writeProtectionRegister.Value;
156                         }
157                     })
158                 .WithTag("BOR_LEV", 2, 2)
159                 .WithReservedBits(4, 1)
160                 .WithTag("USER", 5, 2)
161                 // According to the documentation those fields should be reloaded from flash memory at reset
162                 // but it doesn't specify where they should be stored and how should they be saved
163                 .WithValueField(8, 8, out readProtectionRegister, name: "RDP", softResettable: false)
164                 .WithValueField(16, 12, out writeProtectionRegister, name: "nWRP", softResettable: false)
165                 .WithReservedBits(28, 3)
166                 .WithTaggedFlag("SPRMOD", 31);
167 
168             OptionBytesRegisters.ReadProtectionAndUser.Define(optionBytesRegisters, 0xAAEC)
169                 .WithReservedBits(0, 2)
170                 .WithTag("BOR_LEV", 2, 2)
171                 .WithReservedBits(4, 1)
172                 .WithTaggedFlag("WDG_SW", 5)
173                 .WithTaggedFlag("nRST_STOP", 6)
174                 .WithTaggedFlag("nRST_STDBY", 7)
175                 .WithValueField(8, 8, out readProtectionOptionBytes, name: "RDP")
176                 .WithReservedBits(16, 16);
177 
178             OptionBytesRegisters.WriteProtection.Define(optionBytesRegisters, 0xFFF)
179                 .WithValueField(0, 12, out writeProtectionOptionBytes, name: "nWRPi")
180                 .WithReservedBits(12, 3)
181                 .WithTaggedFlag("SPRMOD", 15)
182                 .WithReservedBits(16, 16);
183         }
184 
Erase(bool massErase, bool sectorErase, uint sectorNumber)185         private void Erase(bool massErase, bool sectorErase, uint sectorNumber)
186         {
187             if(!massErase && !sectorErase)
188             {
189                 this.Log(LogLevel.Warning, "Tried to erase flash, but MER and SER are reset. This should be forbidden, ignoring...");
190                 return;
191             }
192 
193             if(massErase)
194             {
195                 PerformMassErase();
196             }
197             else
198             {
199                 PerformSectorErase(sectorNumber);
200             }
201         }
202 
PerformSectorErase(uint sectorNumber)203         private void PerformSectorErase(uint sectorNumber)
204         {
205             if(!Sectors.ContainsKey(sectorNumber))
206             {
207                 this.Log(LogLevel.Warning, "Tried to erase sector {0}, which doesn't exist. Ignoring...", sectorNumber);
208                 return;
209             }
210 
211             this.Log(LogLevel.Noisy, "Erasing sector {0}, offset 0x{1:X}, size 0x{2:X}", sectorNumber, Sectors[sectorNumber].Offset, Sectors[sectorNumber].Size);
212             flash.WriteBytes(Sectors[sectorNumber].Offset, ErasePattern, Sectors[sectorNumber].Size);
213         }
214 
PerformMassErase()215         private void PerformMassErase()
216         {
217             this.Log(LogLevel.Noisy, "Performing flash mass erase");
218             foreach(var sectorNumber in Sectors.Keys)
219             {
220                 PerformSectorErase(sectorNumber);
221             }
222         }
223 
224         private readonly MappedMemory flash;
225         private readonly LockRegister controlLock;
226         private readonly LockRegister optionControlLock;
227         private readonly DoubleWordRegisterCollection optionBytesRegisters;
228 
229         private IValueRegisterField readProtectionRegister;
230         private IValueRegisterField writeProtectionRegister;
231 
232         private IValueRegisterField readProtectionOptionBytes;
233         private IValueRegisterField writeProtectionOptionBytes;
234 
235         private static readonly uint[] ControlLockKey = {0x45670123, 0xCDEF89AB};
236         private static readonly uint[] OptionLockKey = {0x8192A3B, 0x4C5D6E7F};
237         private static readonly byte[] ErasePattern = Enumerable.Repeat((byte)0xFF, MaxSectorSize).ToArray();
238         private static readonly Dictionary<uint, Sector> Sectors = new Dictionary<uint, Sector>()
239         {
240             { 0, new Sector { Offset = 0x00000000, Size = 0x4000 } },
241             { 1, new Sector { Offset = 0x00004000, Size = 0x4000 } },
242             { 2, new Sector { Offset = 0x00008000, Size = 0x4000 } },
243             { 3, new Sector { Offset = 0x0000C000, Size = 0x4000 } },
244             { 4, new Sector { Offset = 0x00010000, Size = 0x4000 } },
245             { 5, new Sector { Offset = 0x00020000, Size = 0x10000 } },
246             { 6, new Sector { Offset = 0x00040000, Size = 0x20000 } },
247             { 7, new Sector { Offset = 0x00060000, Size = 0x20000 } },
248             { 8, new Sector { Offset = 0x00080000, Size = 0x20000 } },
249             { 9, new Sector { Offset = 0x000A0000, Size = 0x20000 } },
250             { 10, new Sector { Offset = 0x000C0000, Size = 0x20000 } },
251             { 11, new Sector { Offset = 0x000E0000, Size = 0x20000 } },
252         };
253 
254         private const int MaxSectorSize = 0x20000;
255 
256         private class Sector
257         {
258             public uint Offset { get; set; }
259             public int Size { get; set; }
260         }
261 
262         private enum Registers
263         {
264             AccessControl = 0x00,   // FLASH_ACR
265             Key = 0x04,             // FLASH_KEYR
266             OptionKey = 0x08,       // FLASH_OPTKEYR
267             Status = 0x0C,          // FLASH_SR
268             Control = 0x10,         // FLASH_CR
269             OptionControl = 0x14,   // FLASH_OPTCR
270         }
271 
272         private enum OptionBytesRegisters
273         {
274             ReadProtectionAndUser = 0x0,
275             WriteProtection = 0x8,
276         }
277     }
278 }
279