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 System.Linq; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Exceptions; 13 using Antmicro.Renode.Logging; 14 using Antmicro.Renode.Peripherals.Bus; 15 using Antmicro.Renode.Peripherals.Memory; 16 using Antmicro.Renode.Utilities; 17 18 namespace Antmicro.Renode.Peripherals.MTD 19 { 20 public class EFR32xg13FlashController : IDoubleWordPeripheral, IKnownSize 21 { EFR32xg13FlashController(IMachine machine, MappedMemory flash)22 public EFR32xg13FlashController(IMachine machine, MappedMemory flash) 23 { 24 this.flash = flash; 25 if(flash.Size < PageNumber * PageSize) 26 { 27 throw new ConstructionException($"Provided flash size is too small, expected 0x{PageNumber * PageSize:X} bytes, got 0x{flash.Size:X} bytes"); 28 } 29 interruptsManager = new InterruptManager<Interrupt>(this); 30 31 var registersMap = new Dictionary<long, DoubleWordRegister> 32 { 33 {(long)Registers.Control, new DoubleWordRegister(this, 0x1) 34 .WithTaggedFlag("ADDRFAULTEN", 0) 35 .WithTaggedFlag("CLKDISFAULTEN", 1) 36 .WithTaggedFlag("PWRUPONDEMAND", 2) 37 .WithTaggedFlag("IFCREADCLEAR", 3) 38 .WithTaggedFlag("TIMEOUTFAULTEN", 4) 39 .WithReservedBits(5, 3) 40 .WithIgnoredBits(8, 1) // this is written by emlib as an errata 41 .WithReservedBits(9, 23) 42 }, 43 {(long)Registers.ReadControl, new DoubleWordRegister(this, 0x1000100) 44 .WithReservedBits(0, 3) 45 .WithTaggedFlag("IFCDIS", 3) 46 .WithTaggedFlag("AIDIS", 4) 47 .WithTaggedFlag("ICCDIS", 5) 48 .WithReservedBits(6, 2) 49 .WithTaggedFlag("PREFETCH", 8) 50 .WithTaggedFlag("USEHPROT", 9) 51 .WithReservedBits(10, 14) 52 .WithTag("MODE", 24, 2) 53 .WithReservedBits(26, 2) 54 .WithTaggedFlag("SCBTP", 28) 55 .WithReservedBits(29, 3) 56 }, 57 {(long)Registers.WriteControl, new DoubleWordRegister(this) 58 .WithFlag(0, out isWriteEnabled, name: "WREN") 59 .WithTaggedFlag("IRQERASEABORT", 1) 60 .WithReservedBits(2, 30) 61 }, 62 {(long)Registers.WriteCommand, new DoubleWordRegister(this) 63 .WithFlag(0, FieldMode.Toggle, changeCallback: (_, __) => UpdateWriteAddress(), name: "LADDRIM") 64 .WithFlag(1, FieldMode.Toggle, changeCallback: (_, __) => ErasePage(), name: "ERASEPAGE") 65 .WithTaggedFlag("WRITEEND", 2) 66 .WithFlag(3, FieldMode.Toggle, changeCallback: (_, __) => WriteWordToFlash(), name: "WRITEONCE") 67 .WithTaggedFlag("WRITETRIG", 4) 68 .WithTaggedFlag("ERASEABORT", 5) 69 .WithReservedBits(6, 2) 70 .WithTaggedFlag("ERASEMAIN0", 8) 71 .WithReservedBits(9, 3) 72 .WithTaggedFlag("CLEARWDATA", 12) 73 .WithReservedBits(13, 19) 74 }, 75 {(long)Registers.AddressBuffer, new DoubleWordRegister(this) 76 .WithValueField(0, 32, out writeAddress, name: "ADDRB") 77 }, 78 {(long)Registers.WriteData, new DoubleWordRegister(this) 79 .WithValueField(0, 32, out writeData, writeCallback: (_, __) => writeDataReady.Value = false, name: "WDATA") 80 }, 81 {(long)Registers.Status, new DoubleWordRegister(this, 0x8) 82 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => false, name: "BUSY") 83 .WithTaggedFlag("LOCKED", 1) 84 .WithFlag(2, out invalidAddress, FieldMode.Read, name: "INVADDR") 85 // we assume a single-word buffer 86 .WithFlag(3, out writeDataReady, FieldMode.Read, name: "WDATAREADY") 87 .WithTaggedFlag("WORDTIMEOUT", 4) 88 .WithTaggedFlag("ERASEABORT", 5) 89 .WithTaggedFlag("PCRUNNING", 6) 90 .WithReservedBits(7, 17) 91 .WithTag("WDATAVALID", 24, 4) 92 .WithTag("CLEARWDATA", 28, 4) 93 }, 94 {(long)Registers.InterruptFlag, interruptsManager.GetMaskedInterruptFlagRegister<DoubleWordRegister>()}, 95 {(long)Registers.InterruptFlagSet, interruptsManager.GetInterruptSetRegister<DoubleWordRegister>()}, 96 {(long)Registers.InterruptFlagClear, interruptsManager.GetInterruptClearRegister<DoubleWordRegister>()}, 97 {(long)Registers.InterruptEnable, interruptsManager.GetInterruptEnableRegister<DoubleWordRegister>()}, 98 {(long)Registers.ConfigurationLock, new DoubleWordRegister(this) 99 .WithValueField(0, 16, 100 writeCallback: (_, value) => { isLocked = value != UnlockPattern; }, 101 valueProviderCallback: _ => isLocked ? 1u : 0u, 102 name: "LOCKKEY") 103 .WithReservedBits(16, 16) 104 }, 105 {(long)Registers.Command, new DoubleWordRegister(this) 106 .WithTaggedFlag("PWRUP", 0) 107 .WithReservedBits(1, 31) 108 }, 109 }; 110 111 registers = new DoubleWordRegisterCollection(this, registersMap); 112 113 //calling Reset to fill the memory with 0xFFs 114 Reset(); 115 } 116 ReadDoubleWord(long offset)117 public uint ReadDoubleWord(long offset) 118 { 119 return registers.Read(offset); 120 } 121 WriteDoubleWord(long offset, uint value)122 public void WriteDoubleWord(long offset, uint value) 123 { 124 if(isLocked && lockableRegisters.Contains((Registers)offset)) 125 { 126 this.Log(LogLevel.Warning, "Trying to write 0x{0:X} to {1} register but the configuration is locked", value, (Registers)offset); 127 return; 128 } 129 registers.Write(offset, value); 130 } 131 Reset()132 public void Reset() 133 { 134 registers.Reset(); 135 isLocked = false; 136 internalAddressRegister = 0; 137 138 // Clear the whole flash memory 139 for(var i = 0; i < PageNumber; ++i) 140 { 141 flash.WriteBytes(PageSize * i, ErasePattern, 0, PageSize); 142 } 143 } 144 145 [IrqProvider] 146 public GPIO IRQ => new GPIO(); 147 public long Size => 0x800; 148 WriteWordToFlash()149 private void WriteWordToFlash() 150 { 151 const uint highWord = 0xFFFF0000; 152 const uint lowWord = 0xFFFF; 153 if(isWriteEnabled.Value && !invalidAddress.Value) 154 { 155 var effectiveValue = 0u; 156 var oldValue = flash.ReadDoubleWord(internalAddressRegister); 157 var writeValue = (uint)writeData.Value; 158 var newValue = oldValue & writeValue; 159 160 // This code is here to reflect half-word (16bits) writes. It is legal to unset bits once for each half-word. 161 // The driver can write 0xFFFF1111 followed by 0x2222FFFF, resulting in 0x22221111. 162 // It is also ok to write 0xFFFF1111 followed by 0x22221111. 163 // Technicaly, it should also be ok to write 0xFFFF1111 followed by 0x22223333 (the lower half-word is not zeroing any more bits), 164 // which would result in 0x22221111, but we treat this situation as a possible bug and we issue a warning. 165 // The same result would be achieved when writing 0xFFFF1111 followed 0x22220000 -> 0x22221111 (the lower part is not effective). 166 var writeHigh = writeValue & highWord; 167 var oldHigh = oldValue & highWord; 168 var newHigh = newValue & highWord; 169 if(writeHigh != highWord && oldHigh != highWord && oldHigh != writeHigh) 170 { 171 this.Log(LogLevel.Warning, "Writing upper word at dirty address 0x{0:X} - currently holding 0x{2:X}, trying to write 0x{1:X}", internalAddressRegister, writeHigh, oldHigh); 172 effectiveValue |= oldHigh; 173 } 174 else 175 { 176 effectiveValue |= newHigh; 177 } 178 179 var writeLow = writeValue & lowWord; 180 var oldLow = oldValue & lowWord; 181 var newLow = newValue & lowWord; 182 if(writeLow != lowWord && oldLow != lowWord && oldLow != writeLow) 183 { 184 this.Log(LogLevel.Warning, "Writing lower word at dirty address 0x{0:X} - currently holding 0x{2:X}, trying to write 0x{1:X}", internalAddressRegister, writeLow, oldLow); 185 effectiveValue |= oldLow; 186 } 187 else 188 { 189 effectiveValue |= newLow; 190 } 191 192 this.Log(LogLevel.Noisy, "Writing 0x{0:X} to 0x{1:X}", effectiveValue, internalAddressRegister); 193 flash.WriteDoubleWord(internalAddressRegister, effectiveValue); 194 internalAddressRegister += 4; 195 writeDataReady.Value = true; 196 interruptsManager.SetInterrupt(Interrupt.WriteDone); 197 } 198 else 199 { 200 this.Log(LogLevel.Warning, "Trying to write to flash, but it didn't work {0} {1}", isWriteEnabled.Value, invalidAddress.Value); 201 } 202 } 203 ErasePage()204 private void ErasePage() 205 { 206 //while the MSC_WRITECMD.ERASEPAGE suggests using MSC_ADDRB directly, MSC_STATUS.INVADDR is described 207 //as "Invalid Write Address or Erase Page", suggesting using the internal address register 208 if(isWriteEnabled.Value && !invalidAddress.Value) 209 { 210 flash.WriteBytes((long)(internalAddressRegister), ErasePattern, 0, PageSize); 211 this.Log(LogLevel.Noisy, "Erasing page on address 0x{0:X}", internalAddressRegister); 212 interruptsManager.SetInterrupt(Interrupt.EraseDone); 213 } 214 else 215 { 216 this.Log(LogLevel.Warning, "Trying to erase page at 0x{0:X}, but writing is disabled", internalAddressRegister); 217 } 218 } 219 UpdateWriteAddress()220 private void UpdateWriteAddress() 221 { 222 if(isWriteEnabled.Value) 223 { 224 if((long)writeAddress.Value < flash.Size) 225 { 226 internalAddressRegister = (uint)writeAddress.Value; 227 invalidAddress.Value = false; 228 } 229 else 230 { 231 this.Log(LogLevel.Error, "Trying to write outside the flash memory at 0x{0:X}", writeAddress.Value); 232 invalidAddress.Value = true; 233 } 234 } 235 } 236 237 private bool isLocked; 238 private uint internalAddressRegister; 239 240 private readonly IFlagRegisterField isWriteEnabled; 241 private readonly IValueRegisterField writeAddress; 242 private readonly IValueRegisterField writeData; 243 private readonly IFlagRegisterField invalidAddress; 244 private readonly IFlagRegisterField writeDataReady; 245 private readonly DoubleWordRegisterCollection registers; 246 private readonly MappedMemory flash; 247 private readonly byte[] ErasePattern = (byte[])Enumerable.Repeat((byte)0xFF, PageSize).ToArray(); 248 private readonly Registers[] lockableRegisters = new Registers[] { Registers.Control, Registers.ReadControl, 249 Registers.WriteCommand, Registers.StartupControl, Registers.SoftwareUnlockAPPCommand }; 250 private readonly InterruptManager<Interrupt> interruptsManager; 251 252 private const uint UnlockPattern = 0x1B71; 253 private const int PageSize = 2048; 254 private const int PageNumber = 256; 255 256 private enum Interrupt 257 { 258 EraseDone, 259 WriteDone, 260 CacheHitsOverflow, 261 CacheMissesOverflow, 262 FlashPowerUpSequenceComplete, 263 ICacheRAMParityError, 264 FlashControllerWriteBufferOverflow, 265 [NotSettable] 266 Reserved, 267 FlashLVEWriteError 268 } 269 270 private enum Registers : long 271 { 272 Control = 0x00, 273 ReadControl = 0x04, 274 WriteControl = 0x08, 275 WriteCommand = 0x0C, 276 AddressBuffer = 0x10, 277 WriteData = 0x18, 278 Status = 0x1C, 279 InterruptFlag = 0x30, 280 InterruptFlagSet = 0x34, 281 InterruptFlagClear = 0x38, 282 InterruptEnable = 0x3C, 283 ConfigurationLock = 0x40, 284 FlashCacheCommand = 0x44, 285 CacheHitsCounter = 0x48, 286 CacheMissesCounter = 0x4C, 287 MassEraseLock = 0x54, 288 StartupControl = 0x5C, 289 Command = 0x74, 290 BootloaderReadAndWriteEnable = 0x90, 291 SoftwareUnlockAPPCommand = 0x94, 292 CacheConfiguration0 = 0x98, 293 } 294 } 295 } 296