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