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 
8 using System;
9 using System.Collections.Generic;
10 using Antmicro.Renode.Core;
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 using Antmicro.Renode.Utilities.Packets;
18 
19 namespace Antmicro.Renode.Peripherals.CRC
20 {
21     public class SAM4S_CRCCU : BasicDoubleWordPeripheral, IKnownSize
22     {
SAM4S_CRCCU(IMachine machine)23         public SAM4S_CRCCU(IMachine machine) : base(machine)
24         {
25             IRQ = new GPIO();
26             DefineRegisters();
27         }
28 
Reset()29         public override void Reset()
30         {
31             base.Reset();
32             UpdateInterrupts();
33         }
34 
35         public long Size => 0x100;
36         public GPIO IRQ { get; }
37 
DefineRegisters()38         private void DefineRegisters()
39         {
40            Registers.DescriptorBase.Define(this)
41                .WithReservedBits(0, 9)
42                .WithValueField(9, 23,
43                        out descriptorAddress,
44                        name: "DSCR");        // Descriptor Base Address
45            Registers.DMAEnable.Define(this)
46                .WithFlag(0, FieldMode.Write,
47                     writeCallback: (_, value) => { if (value) { ComputeCRC(); } },
48                     name: "DMAEN")           // DMA Enable bit
49                .WithReservedBits(1, 31);
50            Registers.DMADisable.Define(this)
51                .WithFlag(0, FieldMode.Write,
52                     name: "DMADIS")          // DMA Disable bit
53                .WithReservedBits(1, 31);
54            Registers.DMAStatus.Define(this)
55                .WithFlag(0, FieldMode.Read,
56                        valueProviderCallback: (_) => false,
57                        name: "DMASR")        // DMA Status bit
58                .WithReservedBits(1, 31);
59            Registers.DMAInterruptEnable.Define(this)
60                .WithFlag(0, FieldMode.Write,
61                     writeCallback: (_, value) => { if (value) { this.interruptEnableDMA = true; } },
62                     name: "DMAIER")          // DMA Interrupt Enable bit
63                .WithReservedBits(1, 31);
64            Registers.DMAInterruptDisable.Define(this)
65                .WithFlag(0, FieldMode.Write,
66                     writeCallback: (_, value) => { if (value) { this.interruptEnableDMA = false; } },
67                     name: "DMAIDR")          // DMA Interrupt Disable bit
68                .WithReservedBits(1, 31);
69            Registers.DMAInterruptMask.Define(this)
70                .WithFlag(0, FieldMode.Write,
71                     writeCallback: (_, value) => { this.interruptMaskDMA = value; },
72                     name: "DMAIMR")          // DMA Interrupt Mask bit
73                .WithReservedBits(1, 31);
74            Registers.DMAInterruptStatus.Define(this)
75                .WithFlag(0, FieldMode.Read,
76                     valueProviderCallback: _ => { var ret = transferDone; transferDone = false; return ret; },
77                     name: "DMAISR")          // DMA Interrupt Status bit. This flag is reset after read.
78                .WithReservedBits(1, 31);
79            Registers.Control.Define(this)
80                .WithFlag(0, FieldMode.Read | FieldMode.WriteOneToClear,
81                     valueProviderCallback: _ => false,
82                     name: "RESET")           // CRC Computation Reset
83                .WithReservedBits(1, 31)
84                .WithWriteCallback((_, __) => { crcConfigDirty = true; });
85            Registers.Mode.Define(this)
86                .WithFlag(0,
87                     valueProviderCallback: _ => this.globalEnable,
88                     writeCallback: (_, value) => { this.globalEnable = value; },
89                     name: "ENABLE")          // CRC Enable
90                .WithFlag(1,
91                     valueProviderCallback: _ => this.compareMode,
92                     writeCallback: (_, value) => { this.compareMode = value; },
93                     name: "COMPARE")         // CRC Compare
94                .WithEnumField(2, 2,
95                     out poly,
96                     name: "PTYPE")           // Primitive Polynomial 0 - CCITT8023, 1 - CASTAGNOLI, 2 - CCITT16
97                .WithTag("DIVIDER", 4, 4)     // Request Divider
98                .WithReservedBits(8, 24)
99                .WithWriteCallback((_, __) => { crcConfigDirty = true; });
100            Registers.Status.Define(this)
101                .WithValueField(0, 32, FieldMode.Read,
102                     valueProviderCallback: _ => CRC.Value,
103                     name: "CRC");            // Cyclic Redundancy Check Value
104            Registers.InterruptEnable.Define(this)
105                .WithFlag(0, FieldMode.Write,
106                     writeCallback: (_, value) => { if (value) { this.interruptEnableError = true; } },
107                     name: "ERRIER")          // Error Interrupt Enable bit
108                .WithReservedBits(1, 31);
109            Registers.InterruptDisable.Define(this)
110                .WithFlag(0, FieldMode.Write,
111                     writeCallback: (_, value) => { if (value) { this.interruptEnableError = false; } },
112                     name: "ERRIDR")          // Error Interrupt Disable bit
113                .WithReservedBits(1, 31);
114            Registers.InterruptMask.Define(this)
115                .WithFlag(0, FieldMode.Write,
116                     writeCallback: (_, value) => { this.interruptMaskError = value; },
117                     name: "ERRIMR")          // Error Interrupt Mask bit
118                .WithReservedBits(1, 31);
119            Registers.InterruptStatus.Define(this)
120                .WithFlag(0, FieldMode.Read,
121                     valueProviderCallback: _ => this.InterruptStatusError,
122                     name: "ERRISR")          // Error Interrupt Status bit
123                .WithReservedBits(1, 31);
124         }
125 
UpdateInterrupts()126         private void UpdateInterrupts()
127         {
128             var state = MaskedErrorInterruptStatus || MaskedDMAInterruptStatus;
129             this.DebugLog("Setting IRQ to {0}", state ? "set" : "unset");
130             IRQ.Set(state);
131         }
132 
ReloadCRCConfig()133         private void ReloadCRCConfig()
134         {
135             var config = new CRCConfig(
136                 polyMap[poly.Value],
137                 reflectInput: false,
138                 reflectOutput: false,
139                 init: 0xFFFFFFFF,
140                 xorOutput: 0x0
141             );
142             if(crc == null || !config.Equals(crc.Config))
143             {
144                 crc = new CRCEngine(config);
145             }
146             else
147             {
148                 crc.Reset();
149             }
150             crcConfigDirty = false;
151         }
152 
ComputeCRC()153         private void ComputeCRC()
154         {
155             if(globalEnable)
156             {
157                 tcRegisters = TransferControlPacket.ReadFrom(this.descriptorAddress.Value << 9, this.sysbus);
158                 var data = this.sysbus.ReadBytes(tcRegisters.transferAddress, (int)tcRegisters.transferSize * tcRegisters.readByteMultiplier);
159                 CRC.Calculate(data);
160                 transferDone = true;
161                 UpdateInterrupts();
162             }
163             else
164             {
165                 this.Log(LogLevel.Warning, "Trying to compute CRC without setting the ENABLE bit in CRCCU_MR");
166             }
167         }
168 
169         private CRCEngine CRC
170         {
171             get
172             {
173                 if(crc == null || crcConfigDirty)
174                 {
175                     ReloadCRCConfig();
176                 }
177                 return crc;
178             }
179         }
180 
181         private bool MaskedDMAInterruptStatus => transferDone && interruptEnableDMA && interruptMaskDMA && tcRegisters.contextDoneInterruptEnable;
182 
183         private bool InterruptStatusError => (CRC.Value != tcRegisters.referenceCRC) && compareMode;
184 
185         private bool MaskedErrorInterruptStatus => InterruptStatusError && interruptEnableError && interruptMaskError;
186 
187         private bool crcConfigDirty;
188         private bool compareMode;
189         private bool globalEnable;
190         private bool interruptEnableDMA;
191         private bool interruptEnableError;
192         private bool interruptMaskDMA;
193         private bool interruptMaskError;
194         private bool transferDone;
195         private CRCEngine crc;
196         private IValueRegisterField descriptorAddress;
197         private IEnumRegisterField<CRCPolyType> poly;
198         private TransferControlPacket tcRegisters;
199         private static readonly Dictionary<CRCPolyType, CRCPolynomial> polyMap = new Dictionary<CRCPolyType, CRCPolynomial> ()
200         {
201             {
202                 CRCPolyType.CRC32,
203                 CRCPolynomial.CRC32
204             },
205             {
206                 CRCPolyType.CRC32C,
207                 CRCPolynomial.CRC32C
208             },
209             {
210                 CRCPolyType.CRC16_CCITT,
211                 CRCPolynomial.CRC16_CCITT
212             },
213         };
214 
215         private enum CRCPolyType : byte
216         {
217             CRC32 = 0x0,
218             CRC32C = 0x1,
219             CRC16_CCITT = 0x2
220         }
221 
222         private enum Registers : long
223         {
224             DescriptorBase = 0x0,
225             DMAEnable = 0x8,
226             DMADisable = 0xC,
227             DMAStatus = 0x10,
228             DMAInterruptEnable = 0x14,
229             DMAInterruptDisable = 0x18,
230             DMAInterruptMask = 0x1C,
231             DMAInterruptStatus = 0x20,
232             Control = 0x34,
233             Mode = 0x38,
234             Status = 0x3C,
235             InterruptEnable = 0x40,
236             InterruptDisable = 0x44,
237             InterruptMask = 0x48,
238             InterruptStatus = 0x4C
239         }
240 
241         [LeastSignificantByteFirst]
242         private struct TransferControlPacket
243         {
244 #pragma warning disable 649
245             [PacketField, Offset(doubleWords: 0, bits: 0), Width(32)]
246             public uint transferAddress;
247             [PacketField, Offset(doubleWords: 1, bits: 0), Width(16)]
248             public uint transferSize;
249             [PacketField, Offset(doubleWords: 1, bits: 24), Width(2)]
250             public uint transferWidth;
251             [PacketField, Offset(doubleWords: 1, bits: 27), Width(1)]
252             public bool contextDoneInterruptEnable;
253             [PacketField, Offset(doubleWords: 4, bits: 0), Width(32)]
254             public uint referenceCRC;
255 #pragma warning restore 649
256 
257             public int readByteMultiplier;
258 
ReadFromAntmicro.Renode.Peripherals.CRC.SAM4S_CRCCU.TransferControlPacket259             public static TransferControlPacket ReadFrom(ulong address, IBusController sysbus)
260             {
261                 var tcBuffer = sysbus.ReadBytes(address, Packet.CalculateLength<TransferControlPacket>());
262                 var tcRegisters = Packet.Decode<TransferControlPacket>(tcBuffer);
263                 switch(tcRegisters.transferWidth)
264                 {
265                     case 1:
266                         // HALFWORD
267                         tcRegisters.readByteMultiplier = 2;
268                         break;
269                     case 2:
270                         // WORD
271                         tcRegisters.readByteMultiplier = 4;
272                         break;
273                     default:
274                         // BYTE
275                         tcRegisters.readByteMultiplier = 1;
276                         break;
277                 }
278                 return tcRegisters;
279             }
280         }
281     }
282 }
283