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 Antmicro.Renode.Core;
9 using Antmicro.Renode.Core.CAN;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Utilities;
13 using System;
14 using System.Collections.Generic;
15 using System.Linq;
16 
17 namespace Antmicro.Renode.Peripherals.CAN
18 {
19     public class UT32_CAN : BasicBytePeripheral, IKnownSize, ICAN
20     {
UT32_CAN(IMachine machine)21         public UT32_CAN(IMachine machine) : base(machine)
22         {
23             IRQ = new GPIO();
24             DefineRegisters();
25             Reset();
26         }
27 
Reset()28         public override void Reset()
29         {
30             base.Reset();
31             receiveFifo.Clear();
32             fifoUsedBytes = 0;
33             overrunPending = false;
34             UpdateInterrupt();
35         }
36 
OnFrameReceived(CANMessageFrame message)37         public void OnFrameReceived(CANMessageFrame message)
38         {
39             var accept = FilterFrame(message);
40             this.DebugLog("Received frame: {0}, filter {1}ed", message, accept ? "accept" : "reject");
41             if(!accept)
42             {
43                 return;
44             }
45 
46             var thisMessageBytes = MessageFifoByteCount(message);
47             if(fifoUsedBytes + thisMessageBytes <= FifoCapacity)
48             {
49                 receiveFifo.Enqueue(message);
50                 fifoUsedBytes += thisMessageBytes;
51             }
52             else
53             {
54                 // Functional difference from SJA1000: overrun IRQ and status not set until FIFO is read out
55                 overrunPending = true;
56             }
57             UpdateInterrupt();
58         }
59 
60         public long Size => 0x1000;
61 
62         public GPIO IRQ { get; }
63 
64         public event Action<CANMessageFrame> FrameSent;
65 
DefineRegisters()66         protected override void DefineRegisters()
67         {
68             Registers.Mode.Define(this, 1)
69                 .WithFlag(0, out resetMode, name: "RM")
70                 .WithConditionallyWritableFlag(1, out listenOnlyMode, () => resetMode.Value, this, name: "LOM")
71                 .WithConditionallyWritableFlag(2, out selfTestMode, () => resetMode.Value, this, name: "STM")
72                 .WithConditionallyWritableFlag(3, out singleAcceptanceFilter, () => resetMode.Value, this, name: "AFM")
73                 .WithTaggedFlag("SM", 4) // Sleep mode, SJA1000 only
74                 .WithReservedBits(5, 3);
75 
76             Registers.Command.Define(this)
77                 .WithFlag(0, FieldMode.Write, name: "TR", writeCallback: (_, value) =>
78                 {
79                     if(value)
80                     {
81                         SendFrame(TxFrame);
82                     }
83                 })
84                 .WithTaggedFlag("AT", 1)
85                 .WithFlag(2, FieldMode.Write, name: "RRB", writeCallback: (_, value) => // Release Receive Buffer
86                 {
87                     if(value)
88                     {
89                         if(receiveFifo.TryDequeue(out var message))
90                         {
91                             fifoUsedBytes -= MessageFifoByteCount(message);
92                         }
93                         dataOverrunStatus.Value = overrunPending;
94                         overrunPending = false;
95                         dataOverrunFlag.Value |= dataOverrunStatus.Value && dataOverrunInterruptEnable.Value;
96                         UpdateInterrupt();
97                     }
98                 })
99                 .WithFlag(3, FieldMode.Write, name: "CDO", writeCallback: (_, value) =>
100                 {
101                     if(value)
102                     {
103                         dataOverrunStatus.Value = false;
104                         UpdateInterrupt();
105                     }
106                 })
107                 .WithFlag(4, FieldMode.Write, name: "SRR", writeCallback: (_, value) =>
108                 {
109                     if(value && selfTestMode.Value)
110                     {
111                         var frame = TxFrame;
112                         SendFrame(frame);
113                         OnFrameReceived(frame); // Self-reception
114                     }
115                 })
116                 .WithReservedBits(5, 2);
117 
118             Registers.Status.Define(this)
119                 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => receiveFifo.Any(), name: "RBS") // Message available for reading
120                 .WithFlag(1, out dataOverrunStatus, FieldMode.Read, name: "DOS")
121                 .WithFlag(2, FieldMode.Read, valueProviderCallback: _ => true, name: "TBS") // Transmit buffer available for writing
122                 .WithFlag(3, FieldMode.Read, valueProviderCallback: _ => true, name: "TCS") // Last transmission completed successfully
123                 .WithTaggedFlag("RS", 4)
124                 .WithTaggedFlag("TS", 5)
125                 .WithTaggedFlag("ES", 6)
126                 .WithTaggedFlag("BS", 7);
127 
128             Registers.Interrupt.Define(this)
129                 .WithFlag(0, out receiveCompleteFlag, FieldMode.Read, name: "RI") // The only non-ReadToClear bit
130                 .WithFlag(1, out transmitCompleteFlag, FieldMode.ReadToClear, name: "TI")
131                 .WithTaggedFlag("EI", 2)
132                 .WithFlag(3, out dataOverrunFlag, FieldMode.ReadToClear, name: "DOI")
133                 .WithTaggedFlag("WUI", 4) // Sleep mode wakeup, SJA1000 only
134                 .WithTaggedFlag("EPI", 5)
135                 .WithTaggedFlag("ALI", 6)
136                 .WithTaggedFlag("BEI", 7)
137                 .WithReadCallback((_, __) => UpdateInterrupt()); // Read, not change, because of the RI bit
138 
139             Registers.InterruptEnable.Define(this)
140                 .WithFlag(0, out receiveCompleteInterruptEnable, name: "RIE")
141                 .WithFlag(1, out transmitCompleteInterruptEnable, name: "TIE")
142                 .WithTaggedFlag("EIE", 2)
143                 .WithFlag(3, out dataOverrunInterruptEnable, name: "DOIE")
144                 .WithTaggedFlag("WUIE", 4) // Sleep mode wakeup, SJA1000 only
145                 .WithTaggedFlag("EPIE", 5)
146                 .WithTaggedFlag("ALIE", 6)
147                 .WithTaggedFlag("BEIE", 7)
148                 .WithChangeCallback((_, __) => UpdateInterrupt());
149 
150             Registers.BusTiming0.Define(this)
151                 .WithTag("BRP", 0, 6)
152                 .WithTag("SJW", 6, 2);
153 
154             Registers.BusTiming1.Define(this)
155                 .WithTag("TSEG1", 0, 4)
156                 .WithTag("TSEG2", 4, 3)
157                 .WithTaggedFlag("SAM", 7);
158 
159             Registers.ArbLostCapture.Define(this)
160                 .WithTag("BITNO", 0, 5)
161                 .WithReservedBits(5, 3);
162 
163             Registers.ErrorCodeCapture.Define(this)
164                 .WithTag("SEG", 0, 5)
165                 .WithTaggedFlag("DIR", 5)
166                 .WithTag("ERRC", 6, 2);
167 
168             Registers.ErrorWarningLimit.Define(this, 96)
169                 .WithTag("ERROR_WARNING_LIMIT", 0, 8); // Writable in reset mode only
170 
171             Registers.RxErrorCounter.Define(this)
172                 .WithTag("RX_ERROR_COUNTER", 0, 8); // Writable in reset mode only
173 
174             Registers.TxErrorCounter.Define(this)
175                 .WithTag("TX_ERROR_COUNTER", 0, 8); // Writable in reset mode only
176 
177             // Acceptance mask registers are only available in reset mode
178             ResetModeRegisters.AcceptanceCode.DefineManyConditional(this, AcceptanceCodeLength, () => resetMode.Value, (reg, index) => reg
179                 .WithValueField(0, 8, out acceptanceCode[index], name: $"ACR{index}"), resetValue: 0x51); // The reset value for ACR0 is 0x51, the rest are undefined
180 
181             ResetModeRegisters.AcceptanceMask.DefineManyConditional(this, AcceptanceCodeLength, () => resetMode.Value, (reg, index) => reg
182                 .WithValueField(0, 8, out acceptanceMask[index], name: $"AMR{index}"), resetValue: 0xff); // Same as above, the reset value for AMR0 is 0xff
183 
184             // Frame info is only available in normal mode
185             Registers.FrameInfo.DefineConditional(this, () => !resetMode.Value)
186                 .WithValueField(0, 4, out dataLengthCode, name: "DLC",
187                     valueProviderCallback: _ => (ulong)(RxFrame?.Data.Length ?? 0))
188                 .WithReservedBits(4, 2)
189                 .WithFlag(6, out remoteTransmissionRequest, name: "RTR",
190                     valueProviderCallback: _ => RxFrame?.RemoteFrame ?? false)
191                 .WithFlag(7, out extendedFrameFormat, name: "FF", valueProviderCallback: _ =>
192                     {
193                         // If we're reading an EFF frame, adjust the register layout to match
194                         var receivingEff = RxFrame?.ExtendedFormat ?? false;
195                         extendedFrameFormat.Value = receivingEff;
196                         return receivingEff;
197                     });
198 
199             // Normal mode, ID1..2 are common for SFF and EFF but interpreted differently. The ID is left-aligned
200             // and the top 8 bits are always in ID1
201             Registers.Id1.DefineManyConditional(this, SffIdLength, () => !resetMode.Value, (reg, index) => reg
202                 .WithValueField(0, 8, out id[index], name: $"ID{index + 1}",
203                     valueProviderCallback: _ => GetRxIdByte(RxFrame, index, extendedFormat: extendedFrameFormat.Value)));
204 
205             // Normal mode, extended format extra ID bytes
206             ExtendedFrameFormatRegisters.Id3.DefineManyConditional(this, EffIdExtraLength, () => !resetMode.Value && extendedFrameFormat.Value, (reg, index) => reg
207                 .WithValueField(0, 8, out id[index + SffIdLength], name: $"ID{index + SffIdLength + 1}",
208                     valueProviderCallback: _ => GetRxIdByte(RxFrame, index + SffIdLength, extendedFormat: true)));
209 
210             // Normal mode, standard format data bytes
211             StandardFrameFormatRegisters.Data.DefineManyConditional(this, MaxDataLength, () => !resetMode.Value && !extendedFrameFormat.Value, (reg, index) => reg
212                 .WithValueField(0, 8, out data[index], name: $"DATA{index}", valueProviderCallback: _ => RxFrame?.Data[index] ?? 0));
213 
214             // Normal mode, extended format data bytes
215             // In the register object model, a field can be a part of only one register, but these are the same
216             // data fields as in the standard frame format. We use the standard fields as a backing and mirror
217             // values written here to them in the write callbacks.
218             ExtendedFrameFormatRegisters.Data.DefineManyConditional(this, MaxDataLength, () => !resetMode.Value && extendedFrameFormat.Value, (reg, index) => reg
219                 .WithValueField(0, 8, name: $"DATA{index}",
220                     valueProviderCallback: _ => RxFrame?.Data[index] ?? 0,
221                     writeCallback: (_, value) => data[index].Value = value));
222 
223             // Normal mode, standard format next frame info
224             StandardFrameFormatRegisters.NextFrameInfo.DefineConditional(this, () => !resetMode.Value && !extendedFrameFormat.Value)
225                 .WithValueField(0, 4, FieldMode.Read, name: "DLC", valueProviderCallback: _ => (ulong)(receiveFifo.ElementAtOrDefault(1)?.Data.Length ?? 0))
226                 .WithReservedBits(4, 2)
227                 .WithFlag(6, FieldMode.Read, name: "RTR", valueProviderCallback: _ => receiveFifo.ElementAtOrDefault(1)?.RemoteFrame ?? false)
228                 .WithFlag(7, FieldMode.Read, name: "FF", valueProviderCallback: _ => extendedFrameFormat.Value);
229 
230             // Normal mode, standard format next frame ID1
231             StandardFrameFormatRegisters.NextId1.DefineConditional(this, () => !resetMode.Value && !extendedFrameFormat.Value)
232                 .WithValueField(0, 8, FieldMode.Read, name: "ID1",
233                     valueProviderCallback: _ => GetRxIdByte(receiveFifo.ElementAtOrDefault(1), 0, extendedFormat: false));
234 
235             Registers.RxMessageCounter.Define(this)
236                 .WithValueField(0, 5, FieldMode.Read, name: "NM", valueProviderCallback: _ => (ulong)receiveFifo.Count)
237                 .WithReservedBits(5, 3);
238 
239             Registers.ClockDivider.Define(this)
240                 .WithReservedBits(0, 7)
241                 .WithFlag(7, FieldMode.Read, name: "PeliCAN", valueProviderCallback: _ => true);
242         }
243 
UpdateInterrupt()244         private void UpdateInterrupt()
245         {
246             var state = false;
247 
248             // The receive complete interrupt flag is not cleared by reading the interrupt register,
249             // so make make the interrupt corresponding to this flag edge-triggered
250             var newReceiveComplete = receiveCompleteInterruptEnable.Value && receiveFifo.Any();
251             state |= newReceiveComplete && !receiveCompleteFlag.Value;
252             receiveCompleteFlag.Value = newReceiveComplete;
253 
254             // These interrupt flags only get set if the interrupt is enabled when their trigger
255             // condition happens, so or them in directly now
256             state |= transmitCompleteFlag.Value || dataOverrunFlag.Value;
257             this.NoisyLog("Setting IRQ to {0}", state);
258             IRQ.Set(state);
259         }
260 
SendFrame(CANMessageFrame frame)261         private void SendFrame(CANMessageFrame frame)
262         {
263             if(listenOnlyMode.Value)
264             {
265                 this.WarningLog("Attempt to transmit frame when in listen-only mode");
266                 return;
267             }
268             var fs = FrameSent;
269             if(fs == null)
270             {
271                 this.ErrorLog("Attempt to transmit frame when not connected to medium");
272                 return;
273             }
274             this.DebugLog("Sending frame: {0}", frame);
275             fs(frame);
276 
277             transmitCompleteFlag.Value |= transmitCompleteInterruptEnable.Value;
278             UpdateInterrupt();
279         }
280 
281         // Convert the ID of a CAN message to values for use in the ID1..2 (or 1..4) registers according to the specified format
GetRxIdByte(CANMessageFrame frame, int pos, bool extendedFormat)282         private static byte GetRxIdByte(CANMessageFrame frame, int pos, bool extendedFormat)
283         {
284             if(frame == null)
285             {
286                 return 0;
287             }
288 
289             if(extendedFormat)
290             {
291                 switch(pos)
292                 {
293                     case 0:
294                         return (byte)(frame.Id >> 21);
295                     case 1:
296                         return (byte)(frame.Id >> 13);
297                     case 2:
298                         return (byte)(frame.Id >> 5);
299                     case 3:
300                         return (byte)((frame.Id << 3) | (frame.RemoteFrame ? 1u << 2 : 0));
301                 }
302             }
303 
304             switch(pos)
305             {
306                 case 0:
307                     return (byte)(frame.Id >> 3);
308                 case 1:
309                     return (byte)((frame.Id << 5) | (frame.RemoteFrame ? 1u << 4 : 0));
310             }
311 
312             return 0;
313         }
314 
MessageFifoByteCount(CANMessageFrame message)315         private static int MessageFifoByteCount(CANMessageFrame message)
316         {
317             // Frame information always takes 1 byte; ID takes 4 bytes in EFF, 2 bytes in SFF
318             int overhead = 1 + (message.ExtendedFormat ? EffIdLength : SffIdLength);
319             return message.Data.Length + overhead;
320         }
321 
FilterFrame(CANMessageFrame message)322         private bool FilterFrame(CANMessageFrame message)
323         {
324             // The ACR values are masked by AMR - 1 in AMR means the ACR bit is a don't care
325             var code = acceptanceCode.Select(r => (byte)r.Value).ToArray();
326             var mask = acceptanceMask.Select(r => (byte)r.Value).ToArray();
327             // For acceptance filtering, use the EFF flag in the incoming message, not our frame info register
328             var input = Enumerable.Range(0, AcceptanceCodeLength).Select(i => GetRxIdByte(message, i, message.ExtendedFormat)).ToArray();
329             if(singleAcceptanceFilter.Value)
330             {
331                 if(message.ExtendedFormat)
332                 {
333                     // ACR0              ACR1              ACR2              ACR3
334                     // 7 6 5 4 3 2 1 0   7 6 5 4 3 2 1 0   7 6 5 4 3 2 1 0   7 6 5 4 3 2 1 0
335 
336                     // I I I I I I I I   I I I I I I I I   I I I I I I I I   I I I I I R
337                     // D D D D D D D D   D D D D D D D D   D D D D D D D D   D D D D D T
338                     // 2 2 2 2 2 2 2 2   2 1 1 1 1 1 1 1   1 1 1 9 8 7 6 5   4 3 2 1 0 R
339                     // 8 7 6 5 4 3 2 1   0 9 8 7 6 5 4 3   2 1 0
340                     mask[3] |= 0b11; // Mask out unused bits
341                 }
342                 else
343                 {
344                     // ACR0              ACR1              ACR2              ACR3
345                     // 7 6 5 4 3 2 1 0   7 6 5 4 3 2 1 0   7 6 5 4 3 2 1 0   7 6 5 4 3 2 1 0
346 
347                     // I I I I I I I I   I I I R           B B B B B B B B   B B B B B B B B
348                     // D D D D D D D D   D D D T           1 1 1 1 1 1 1 1   2 2 2 2 2 2 2 2
349                     // 2 2 2 2 2 2 2 2   2 1 1 R           . . . . . . . .   . . . . . . . .
350                     // 8 7 6 5 4 3 2 1   0 9 8             7 6 5 4 3 2 1 0   7 6 5 4 3 2 1 0
351                     mask[1] |= 0b1111; // Mask out unused bits
352                     input[2] = message.Data.ElementAtOrDefault(0); // Compare the first 2 bytes of the frame data
353                     input[3] = message.Data.ElementAtOrDefault(1);
354                 }
355                 var matches = ~(BitConverter.ToUInt32(input, 0) ^ BitConverter.ToUInt32(code, 0));
356                 var ignores = BitConverter.ToUInt32(mask, 0);
357                 return (matches | ignores) == uint.MaxValue;
358             }
359             else
360             {
361                 if(message.ExtendedFormat)
362                 {
363                     // ACR0              ACR1              ACR2              ACR3
364                     // 7 6 5 4 3 2 1 0   7 6 5 4 3 2 1 0   7 6 5 4 3 2 1 0   7 6 5 4 3 2 1 0
365                     //
366                     // I I I I I I I I   I I I I I I I I   I I I I I I I I   I I I I I I I I
367                     // D D D D D D D D   D D D D D D D D   D D D D D D D D   D D D D D D D D
368                     // 2 2 2 2 2 2 2 2   2 1 1 1 1 1 1 1   2 2 2 2 2 2 2 2   2 1 1 1 1 1 1 1
369                     // 8 7 6 5 4 3 2 1   0 9 8 7 6 5 4 3   8 7 6 5 4 3 2 1   0 9 8 7 6 5 4 3
370                     // [ (filter #1) ]   [ (filter #1) ]   [ (filter #2) ]   [ (filter #2) ]
371                     input[2] = input[0]; // Copy the input for the second filter to use
372                     input[3] = input[1];
373                     var matches = ~(BitConverter.ToUInt32(input, 0) ^ BitConverter.ToUInt32(code, 0));
374                     var ignores = BitConverter.ToUInt32(mask, 0);
375                     var halves = matches | ignores; // Calculate both halves of the filter packed
376                     // The frame is accepted when filter #1 or filter #2 fully matches
377                     return (ushort)(halves >> 16) == ushort.MaxValue || (ushort)halves == ushort.MaxValue;
378                 }
379                 else
380                 {
381                     // ACR0              ACR1              ACR2              ACR3
382                     // 7 6 5 4 3 2 1 0   7 6 5 4 3 2 1 0   7 6 5 4 3 2 1 0   7 6 5 4 3 2 1 0
383                     //
384                     // I I I I I I I I   I I I R B B B B   I I I I I I I I   I I I R B B B B
385                     // D D D D D D D D   D D D T 1 1 1 1   D D D D D D D D   D D D T 1 1 1 1
386                     // 2 2 2 2 2 2 2 2   2 1 1 R . . . .   2 2 2 2 2 2 2 2   2 1 1 R . . . .
387                     // 8 7 6 5 4 3 2 1   0 9 8   7 6 5 4   8 7 6 5 4 3 2 1   0 9 8   3 2 1 0
388                     // [ (filter #1) ]   [ (filter #1) ]   [ (filter #2) ]   [(#2) ] [(#1) ]
389                     var firstByte = message.Data.ElementAtOrDefault(0);
390                     input[1] = input[1].ReplaceBits(firstByte, 4, sourcePosition: 4);
391                     input[3] = input[3].ReplaceBits(firstByte, 4);
392                     var matches = ~(BitConverter.ToUInt32(input, 0) ^ BitConverter.ToUInt32(code, 0));
393                     var ignores = BitConverter.ToUInt32(mask, 0);
394                     var result = BitConverter.GetBytes(matches | ignores);
395                     var filter1 = result[0] == byte.MaxValue && result[1] == byte.MaxValue && (result[3] & 0xf) == 0xf;
396                     var filter2 = result[2] == byte.MaxValue;
397                     return filter1 || filter2;
398                 }
399             }
400         }
401 
402         // ID of the frame to be transmitted
403         private uint TxId
404         {
405             get
406             {
407                 if(extendedFrameFormat.Value)
408                 {
409                     return (uint)(id[0].Value << 21) | (uint)(id[1].Value << 13) | (uint)(id[2].Value << 5) | (uint)(id[3].Value >> 3);
410                 }
411                 return (uint)(id[0].Value << 3) | (uint)(id[1].Value >> 5);
412             }
413         }
414 
415         // Data to be transmitted
416         private byte[] TxData => data.Take((int)dataLengthCode.Value).Select(r => (byte)r.Value).ToArray();
417 
418         // The frame to be transmitted
419         private CANMessageFrame TxFrame => new CANMessageFrame(TxId, TxData,
420             extendedFormat: extendedFrameFormat.Value, remoteFrame: remoteTransmissionRequest.Value);
421 
422         private CANMessageFrame RxFrame => receiveFifo.FirstOrDefault();
423 
424         private IFlagRegisterField resetMode;
425         private IFlagRegisterField listenOnlyMode;
426         private IFlagRegisterField selfTestMode;
427         private IFlagRegisterField singleAcceptanceFilter;
428         private IFlagRegisterField extendedFrameFormat;
429         private IValueRegisterField dataLengthCode;
430         private IValueRegisterField[] acceptanceCode = new IValueRegisterField[AcceptanceCodeLength];
431         private IValueRegisterField[] acceptanceMask = new IValueRegisterField[AcceptanceCodeLength];
432         private IValueRegisterField[] id = new IValueRegisterField[EffIdLength];
433         private IValueRegisterField[] data = new IValueRegisterField[MaxDataLength];
434         private IFlagRegisterField remoteTransmissionRequest;
435         private IFlagRegisterField dataOverrunStatus;
436         private IFlagRegisterField receiveCompleteFlag;
437         private IFlagRegisterField transmitCompleteFlag;
438         private IFlagRegisterField dataOverrunFlag;
439         private IFlagRegisterField receiveCompleteInterruptEnable;
440         private IFlagRegisterField transmitCompleteInterruptEnable;
441         private IFlagRegisterField dataOverrunInterruptEnable;
442 
443         private bool overrunPending;
444         private int fifoUsedBytes;
445 
446         private readonly Queue<CANMessageFrame> receiveFifo = new Queue<CANMessageFrame>();
447 
448         private const int SffIdLength = 2;
449         private const int EffIdExtraLength = 2;
450         private const int EffIdLength = SffIdLength + EffIdExtraLength;
451         private const int AcceptanceCodeLength = 4;
452         private const int MaxDataLength = 8;
453         // The number of messages that can be stored in the FIFO depends on the length of the individual messages
454         private const int FifoCapacity = 64; // bytes
455 
456         private enum Registers : long
457         {
458             Mode = 0x00,
459             Command = 0x01,
460             Status = 0x02,
461             Interrupt = 0x03,
462             InterruptEnable = 0x04,
463             // Gap
464             BusTiming0 = 0x06,
465             BusTiming1 = 0x07,
466             // Gap
467             ArbLostCapture = 0x0b,
468             ErrorCodeCapture = 0x0c,
469             ErrorWarningLimit = 0x0d,
470             RxErrorCounter = 0x0e,
471             TxErrorCounter = 0x0f,
472             FrameInfo = 0x10,
473             Id1 = 0x11,
474             Id2 = 0x12,
475             // 0x10..0x1c: SFF frame/EFF frame/Acceptance Code
476             RxMessageCounter = 0x1d,
477             // Gap
478             ClockDivider = 0x1f,
479         }
480 
481         private enum ResetModeRegisters : long
482         {
483             AcceptanceCode = 0x10, // 0x10..0x13
484             AcceptanceMask = 0x14, // 0x14..0x17
485         }
486 
487         private enum StandardFrameFormatRegisters : long
488         {
489             Data = 0x13, // 0x13..0x1a
490             NextFrameInfo = 0x1b,
491             NextId1 = 0x1c,
492         }
493 
494         private enum ExtendedFrameFormatRegisters : long
495         {
496             Id3 = 0x13,
497             Id4 = 0x14,
498             Data = 0x15, // 0x15..0x1c
499         }
500     }
501 
502     internal static class PeripheralRegisterExtensions
503     {
504         public static T WithConditionallyWritableFlag<T>(this T register, int position, out IFlagRegisterField flagField, Func<bool> writabilityCondition, IPeripheral parent, Action<bool, bool> readCallback = null,
505             Action<bool, bool> writeCallback = null, Action<bool, bool> changeCallback = null, Func<bool, bool> valueProviderCallback = null, bool softResettable = true, string name = null)
506             where T : PeripheralRegister
507         {
508             IFlagRegisterField ff = null;
509             ff = register.DefineFlagField(position, FieldMode.Read | FieldMode.Write, readCallback, writeCallback, changeCallback: (oldValue, value) =>
510             {
511                 if(!writabilityCondition())
512                 {
513                     parent.WarningLog("Flag {0} was changed while this was not allowed", name);
514                     ff.Value = oldValue;
515                 }
516                 else
517                 {
518                     changeCallback?.Invoke(oldValue, value);
519                 }
520             }, valueProviderCallback, softResettable, name);
521             flagField = ff;
522             return register;
523         }
524     }
525 }
526