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