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 using System; 8 using System.Collections; 9 using System.Collections.Generic; 10 using System.Linq; 11 using Antmicro.Renode.Core; 12 using Antmicro.Renode.Time; 13 using Antmicro.Renode.Utilities; 14 15 namespace Antmicro.Renode.Peripherals.SENT 16 { 17 public class Transmitter 18 { Transmitter(IMachine machine, TimeInterval tickPeriod)19 public Transmitter(IMachine machine, TimeInterval tickPeriod) 20 { 21 this.machine = machine; 22 this.tickPeriod = tickPeriod; 23 } 24 25 public event Func<FastMessage> ProvideFastMessage; 26 public event Func<SlowMessage> ProvideSlowMessage; 27 public event Action<SENTEdge> Edge; 28 29 public bool Transmitting 30 { 31 get => sendThread != null; 32 set 33 { 34 if(value == Transmitting) 35 { 36 return; 37 } 38 39 sendThread?.Dispose(); 40 sendThread = null; 41 42 if(value) 43 { 44 var coroutine = TransmitterThread().GetEnumerator(); 45 sendThread = machine.ObtainManagedThread(delegate{}, tickPeriod, "SENT Transmitter", stopCondition: () => !coroutine.MoveNext()); 46 sendThread.Start(); 47 } 48 } 49 } 50 TransmitterThread()51 private IEnumerable TransmitterThread() 52 { 53 IEnumerator<bool> slowMessageBits = null; 54 55 while(true) 56 { 57 // Generating pulses based on the SENT frame 58 // 59 // Description: | Sync pulse | Each nibble | Pause pulse (optional) | Sync pulse ... 60 // Signal: | Low | High | Low | High | Currently not implemented | 61 // Ticks: | 1 | 55 | 5 | 7 + nibble value | | 62 // 63 // Each `yield return null` is waiting until the time of the next tick 64 65 var frame = ConstructFrame(ref slowMessageBits); 66 67 // Sync pulse 68 Edge?.Invoke(SENTEdge.Falling); 69 yield return null; 70 Edge?.Invoke(SENTEdge.Rising); 71 72 for(var i = 0; i < SynchPulseWidth - 1; i++) 73 { 74 yield return null; 75 } 76 77 // Pulse for each nibble to send 78 foreach(var nibble in frame) 79 { 80 Edge?.Invoke(SENTEdge.Falling); 81 for(var i = 0; i < NibbleLowPulseWidth; i++) 82 { 83 yield return null; 84 } 85 Edge?.Invoke(SENTEdge.Rising); 86 87 var pulseWidth = (nibble & NibbleByteMask) + NibblePulseOffset; 88 for(var i = 0; i < pulseWidth; i++) 89 { 90 yield return null; 91 } 92 // Next falling edge will be generated at the start of the inner or outer loop 93 } 94 } 95 } 96 ConstructFrame(ref IEnumerator<bool> slowMessageBits)97 private IEnumerable<byte> ConstructFrame(ref IEnumerator<bool> slowMessageBits) 98 { 99 // SENT frame structure (each segment is 4 bits long): 100 // | Status | | Data0 | ... | DataN | CRC | 101 // <- Max N = 6 -> 102 // Status: 103 // Bit 3 - Serial sync. Should be 1 when starting a new slow message otherwise 0 104 // Bit 2 - Serial data. One bit of the slow message 105 // Bits 1, 0 - Currently reserved 106 // Data: 107 // Data nibbles. Taken from the FastMessage class 108 // CRC: 109 // CRC of data nibbles. Calculated using the x4 + x3 + x2 + 1 polynomial with a starting value of 5 110 // Note that CRC is added already added by the `FastMessage` class 111 112 var newSlowMessage = false; 113 if(slowMessageBits == null || !slowMessageBits.MoveNext()) 114 { 115 slowMessageBits = ProvideSlowMessage().Bits.GetEnumerator(); 116 slowMessageBits.MoveNext(); 117 newSlowMessage = true; 118 } 119 120 byte statusNibble = 0x0; 121 BitHelper.SetBit(ref statusNibble, StatusSerialSyncBit, newSlowMessage); 122 BitHelper.SetBit(ref statusNibble, StatusSerialDataBit, slowMessageBits.Current); 123 124 return ProvideFastMessage().Nibbles.Prepend(statusNibble); 125 } 126 127 private IManagedThread sendThread; 128 129 private readonly IMachine machine; 130 private readonly TimeInterval tickPeriod; 131 132 private const int SynchPulseWidth = 56; 133 private const int NibbleLowPulseWidth = 5; 134 private const int NibblePulseOffset = 7; 135 private const byte NibbleByteMask = 0xF; 136 137 private const int StatusSerialSyncBit = 3; 138 private const int StatusSerialDataBit = 2; 139 } 140 } 141