1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // Copyright (c) 2022 Pieter Agten 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 9 using System; 10 using System.Collections.Generic; 11 using Antmicro.Renode.Peripherals; 12 using Antmicro.Renode.Peripherals.Bus; 13 using Antmicro.Renode.Core.Structure.Registers; 14 using Antmicro.Renode.Exceptions; 15 using Antmicro.Renode.Logging; 16 using Antmicro.Renode.Utilities; 17 18 namespace Antmicro.Renode.Peripherals.CRC 19 { 20 public class STM32_CRC : IBytePeripheral, IWordPeripheral, IDoubleWordPeripheral, IKnownSize 21 { STM32_CRC(STM32Series series, bool configurablePoly=false)22 public STM32_CRC(STM32Series series, bool configurablePoly=false) 23 { 24 STM32Config conf; 25 if(!this.setupConfig.TryGetValue(series, out conf)) 26 { 27 throw new ConstructionException($"Unknown STM32 series value: {series}!"); 28 } 29 30 this.configurablePoly = configurablePoly; 31 this.configurableInitialValue = conf.configurableInitialValue; 32 33 var registersMap = new Dictionary<long, DoubleWordRegister> 34 { 35 {(long)Registers.Data, new DoubleWordRegister(this) 36 .WithValueField(0, 32, name: "CRC_DR", 37 writeCallback: (_, value) => 38 { 39 UpdateCRC((uint)value, 4); 40 // Equivalent for byte and word implemented directly in writeByte and writeWord methods 41 }, 42 valueProviderCallback: _ => CRC.Value 43 ) 44 }, 45 {(long)Registers.Control, new DoubleWordRegister(this) 46 .WithFlag(0, FieldMode.Read | FieldMode.WriteOneToClear, 47 valueProviderCallback: _ => false, 48 name: "RESET") 49 .WithReservedBits(1, 2) 50 .If(conf.hasPolySizeBits) 51 .Then(r => r.WithEnumField<DoubleWordRegister, PolySize>(3, 2, out polySize, name: "POLYSIZE")) 52 .Else(r => r.WithReservedBits(3, 2)) 53 .If(conf.reversibleIO) 54 .Then(r => { r.WithEnumField<DoubleWordRegister, BitReversal>(5, 2, out reverseInputData, name: "REV_IN"); 55 r.WithFlag(7, out reverseOutputData, name: "REV_OUT"); }) 56 .Else(r => r.WithReservedBits(5, 3)) 57 .WithReservedBits(8, 24) 58 .WithWriteCallback((_, __) => { crcConfigDirty = true; }) 59 }, 60 {(long)Registers.InitialValue, new DoubleWordRegister(this, DefaultInitialValue) 61 .WithValueField(0, 32, out initialValue, 62 FieldMode.Read | (configurableInitialValue ? FieldMode.Write : 0), 63 name: "CRC_INIT") 64 }, 65 {(long)Registers.Polynomial, new DoubleWordRegister(this, DefaultPolymonial) 66 .WithValueField(0, 32, out polynomial, 67 FieldMode.Read | (configurablePoly ? FieldMode.Write : 0), 68 name: "CRC_POL" 69 ) 70 .WithWriteCallback((_, __) => { crcConfigDirty = true; }) 71 }, 72 {(long)Registers.IndependentData, new DoubleWordRegister(this) 73 .WithTag("CRC_IDR", 0, (int)conf.independentDataWidth) 74 .If((int)conf.independentDataWidth != 32) 75 .Then(r => r.WithReservedBits((int)conf.independentDataWidth, 32 - (int)conf.independentDataWidth)) 76 .Else(_ => {}) 77 } 78 }; 79 80 registers = new DoubleWordRegisterCollection(this, registersMap); 81 } 82 ReadByte(long offset)83 public byte ReadByte(long offset) 84 { 85 // only properly aligned reads will be handled correctly here 86 return (byte)registers.Read(offset); 87 } 88 ReadWord(long offset)89 public ushort ReadWord(long offset) 90 { 91 // only properly aligned reads will be handled correctly here 92 return (ushort)registers.Read(offset); 93 } 94 ReadDoubleWord(long offset)95 public uint ReadDoubleWord(long offset) 96 { 97 return registers.Read(offset); 98 } 99 WriteByte(long offset, byte value)100 public void WriteByte(long offset, byte value) 101 { 102 if((Registers)offset == Registers.Data) 103 { 104 UpdateCRC(value, 1); 105 } 106 else 107 { 108 this.LogUnhandledWrite(offset, value); 109 } 110 } 111 WriteWord(long offset, ushort value)112 public void WriteWord(long offset, ushort value) 113 { 114 if((Registers)offset == Registers.Data) 115 { 116 UpdateCRC(value, 2); 117 } 118 else 119 { 120 this.LogUnhandledWrite(offset, value); 121 } 122 } 123 WriteDoubleWord(long offset, uint value)124 public void WriteDoubleWord(long offset, uint value) 125 { 126 registers.Write(offset, value); 127 } 128 Reset()129 public void Reset() 130 { 131 registers.Reset(); 132 ReloadCRCConfig(); 133 } 134 135 public long Size => 0x400; 136 137 public enum IndependentDataWidth 138 { 139 Bits8 = 8, 140 Bits32 = 32, 141 } 142 PolySizeToCRCWidth(PolySize poly)143 private static int PolySizeToCRCWidth(PolySize poly) 144 { 145 switch(poly) 146 { 147 case PolySize.CRC32: 148 return 32; 149 case PolySize.CRC16: 150 return 16; 151 case PolySize.CRC8: 152 return 8; 153 case PolySize.CRC7: 154 return 7; 155 default: 156 throw new ArgumentException($"Unknown PolySize value: {poly}!"); 157 } 158 } 159 UpdateCRC(uint value, int bytesCount)160 private void UpdateCRC(uint value, int bytesCount) 161 { 162 if(reverseInputData.Value == BitReversal.ByByte) 163 { 164 value = BitHelper.ReverseBitsByByte(value); 165 } 166 else if(reverseInputData.Value == BitReversal.ByWord) 167 { 168 switch(bytesCount) 169 { 170 case 1: 171 value = BitHelper.ReverseBitsByByte(value); 172 break; 173 case 2: 174 case 4: 175 value = BitHelper.ReverseBitsByWord(value); 176 break; 177 } 178 } 179 else if(reverseInputData.Value == BitReversal.ByDoubleWord) 180 { 181 switch(bytesCount) 182 { 183 case 1: 184 value = BitHelper.ReverseBitsByByte(value); 185 break; 186 case 2: 187 value = BitHelper.ReverseBitsByWord(value); 188 break; 189 case 4: 190 value = BitHelper.ReverseBits(value); 191 break; 192 } 193 } 194 CRC.Update(BitHelper.GetBytesFromValue(value, bytesCount)); 195 } 196 ReloadCRCConfig()197 private void ReloadCRCConfig() 198 { 199 var config = new CRCConfig( 200 (uint)polynomial.Value, 201 PolySizeToCRCWidth(polySize?.Value ?? PolySize.CRC32), 202 reflectInput: false, 203 reflectOutput: reverseOutputData?.Value ?? false, 204 init: (uint)initialValue.Value, 205 xorOutput: 0x0 206 ); 207 if(crc == null || !config.Equals(crc.Config)) 208 { 209 crc = new CRCEngine(config); 210 } 211 else 212 { 213 crc.Reset(); 214 } 215 crcConfigDirty = false; 216 } 217 218 private CRCEngine CRC 219 { 220 get 221 { 222 if(crc == null || crcConfigDirty) 223 { 224 ReloadCRCConfig(); 225 } 226 return crc; 227 } 228 } 229 230 private IFlagRegisterField reverseOutputData; 231 private IEnumRegisterField<BitReversal> reverseInputData; 232 private IEnumRegisterField<PolySize> polySize; 233 private IValueRegisterField initialValue; 234 private IValueRegisterField polynomial; 235 236 private bool crcConfigDirty; 237 private CRCEngine crc; 238 239 private readonly Dictionary<STM32Series, STM32Config> setupConfig = new Dictionary<STM32Series, STM32Config> () 240 { 241 { 242 STM32Series.F0, 243 new STM32Config() 244 { 245 configurablePoly = false, 246 configurableInitialValue = true, 247 hasPolySizeBits = true, 248 reversibleIO = true, 249 independentDataWidth = IndependentDataWidth.Bits8, 250 } 251 }, 252 { 253 STM32Series.F4, 254 new STM32Config() 255 { 256 configurablePoly = false, 257 configurableInitialValue = false, 258 hasPolySizeBits = false, 259 reversibleIO = false, 260 independentDataWidth = IndependentDataWidth.Bits8, 261 } 262 }, 263 { 264 STM32Series.WBA, 265 new STM32Config() 266 { 267 configurablePoly = true, 268 configurableInitialValue = true, 269 hasPolySizeBits = true, 270 reversibleIO = true, 271 independentDataWidth = IndependentDataWidth.Bits32, 272 } 273 } 274 }; 275 private readonly bool configurablePoly; 276 private readonly bool configurableInitialValue; 277 private readonly DoubleWordRegisterCollection registers; 278 279 private const uint DefaultInitialValue = 0xFFFFFFFF; 280 private const uint DefaultPolymonial = 0x04C11DB7; 281 282 private enum BitReversal 283 { 284 Disabled, 285 ByByte, 286 ByWord, 287 ByDoubleWord 288 } 289 290 private enum Registers : long 291 { 292 Data = 0x0, 293 IndependentData = 0x4, 294 Control = 0x8, 295 InitialValue = 0x10, 296 Polynomial = 0x14 297 } 298 299 private enum PolySize 300 { 301 CRC32, 302 CRC16, 303 CRC8, 304 CRC7 305 } 306 307 private struct STM32Config 308 { 309 public bool configurablePoly; 310 public bool configurableInitialValue; 311 public bool hasPolySizeBits; 312 public bool reversibleIO; 313 public IndependentDataWidth independentDataWidth; 314 } 315 } 316 } 317