1 //
2 // Copyright (c) 2010-2019 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 Antmicro.Renode.Logging;
9 
10 namespace Antmicro.Renode.Utilities
11 {
12     public class BitBangHelper
13     {
BitBangHelper(int width, bool outputMsbFirst = false, IEmulationElement loggingParent = null)14         public BitBangHelper(int width, bool outputMsbFirst = false, IEmulationElement loggingParent = null)
15         {
16             outputDecoder = new Decoder(width, outputMsbFirst, loggingParent);
17             inputEncoder = new Encoder(width, loggingParent);
18 
19             this.loggingParent = loggingParent;
20             Reset();
21         }
22 
ResetOutput()23         public void ResetOutput()
24         {
25             outputDecoder.Reset();
26         }
27 
Update(uint encodedSignals, int dataBit, int clockBit, bool dataEnabled = true)28         public bool Update(uint encodedSignals, int dataBit, int clockBit, bool dataEnabled = true)
29         {
30             var clockSignal = (encodedSignals & (1 << clockBit)) != 0;
31             var dataSignal = (encodedSignals & (1 << dataBit)) != 0;
32 
33             return Update(clockSignal, dataSignal, dataEnabled);
34         }
35 
Update(bool clockSignal, bool dataSignal, bool dataEnabled = true)36         public bool Update(bool clockSignal, bool dataSignal, bool dataEnabled = true)
37         {
38             var result = false;
39             // clock rising
40             var tickDetected = (clockSignal && !previousClockSignal);
41             if(tickDetected)
42             {
43                 loggingParent?.Log(LogLevel.Noisy, "Tick detected");
44 
45                 inputEncoder.Tick();
46                 if(dataEnabled)
47                 {
48                     result = outputDecoder.Tick(dataSignal);
49                 }
50             }
51 
52             previousClockSignal = clockSignal;
53             return result;
54         }
55 
SetInputBuffer(uint data)56         public void SetInputBuffer(uint data)
57         {
58             inputEncoder.Encode(data);
59         }
60 
Reset()61         public void Reset()
62         {
63             inputEncoder.Reset();
64             outputDecoder.Reset();
65 
66             previousClockSignal = false;
67         }
68 
69         public uint DecodedOutput => outputDecoder.DecodedData;
70 
71         public bool EncodedInput => inputEncoder.CurrentBit;
72 
73         private bool previousClockSignal;
74 
75         private readonly Decoder outputDecoder;
76         private readonly Encoder inputEncoder;
77         private readonly IEmulationElement loggingParent;
78 
79         public class Encoder
80         {
Encoder(int width, IEmulationElement loggingParent = null)81             public Encoder(int width, IEmulationElement loggingParent = null)
82             {
83                 this.loggingParent = loggingParent;
84                 buffer = new bool[width];
85                 Reset();
86             }
87 
Tick()88             public void Tick()
89             {
90                 if(bufferPosition >= 0)
91                 {
92                     bufferPosition--;
93                 }
94             }
95 
Encode(uint data)96             public void Encode(uint data)
97             {
98                 var dataBits = BitHelper.GetBits(data);
99                 Array.Copy(dataBits, 0, buffer, 0, buffer.Length);
100                 bufferPosition = buffer.Length;
101             }
102 
Reset()103             public void Reset()
104             {
105                 bufferPosition = -1;
106             }
107 
108             public bool CurrentBit
109             {
110                 get
111                 {
112                     if(bufferPosition < 0 || bufferPosition >= buffer.Length)
113                     {
114                         loggingParent?.Log(LogLevel.Warning, "Trying to read bit, but the buffer is empty");
115                         return false;
116                     }
117 
118                     return buffer[bufferPosition];
119                 }
120             }
121 
122             private int bufferPosition;
123 
124             private readonly bool[] buffer;
125             private readonly IEmulationElement loggingParent;
126         }
127 
128         public class Decoder
129         {
Decoder(int width, bool msbFirst = false, IEmulationElement loggingParent = null)130             public Decoder(int width, bool msbFirst = false, IEmulationElement loggingParent = null)
131             {
132                 this.loggingParent = loggingParent;
133                 this.msbFirst = msbFirst;
134                 buffer = new bool[width];
135 
136                 Reset();
137             }
138 
Tick(bool dataSignal)139             public bool Tick(bool dataSignal)
140             {
141                 loggingParent?.Log(LogLevel.Noisy, "Latching bit #{0}, value: {1}", bufferPosition, dataSignal);
142 
143                 buffer[bufferPosition] = dataSignal;
144                 bufferPosition += msbFirst
145                     ? -1
146                     : 1;
147 
148                 if(bufferPosition == buffer.Length || bufferPosition == -1)
149                 {
150                     ResetBuffer();
151                     DecodedData = BitHelper.GetValueFromBitsArray(buffer);
152                     return true;
153                 }
154 
155                 return false;
156             }
157 
Reset()158             public void Reset()
159             {
160                 ResetBuffer();
161                 DecodedData = 0;
162             }
163 
164             public uint DecodedData { get; private set; }
165 
ResetBuffer()166             private void ResetBuffer()
167             {
168                 bufferPosition = msbFirst
169                     ? buffer.Length - 1
170                     : 0;
171             }
172 
173             private int bufferPosition;
174 
175             private readonly bool[] buffer;
176             private readonly bool msbFirst;
177             private readonly IEmulationElement loggingParent;
178         }
179     }
180 }
181