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