1 //
2 // Copyright (c) 2010-2023 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.Core.Structure.Registers;
12 using Antmicro.Renode.Exceptions;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Utilities;
15 
16 namespace Antmicro.Renode.Peripherals.Analog
17 {
18     public class Xilinx_XADC : BasicDoubleWordPeripheral, IKnownSize
19     {
Xilinx_XADC(IMachine machine)20         public Xilinx_XADC(IMachine machine) : base(machine)
21         {
22             DefineRegisters();
23 
24             IRQ = new GPIO();
25             dataFifo = new Queue<uint>();
26             channelValues = new ushort[NUMBER_OF_CHANNELS];
27 
28             Reset();
29         }
30 
Reset()31         public override void Reset()
32         {
33             base.Reset();
34 
35             dataFifo.Clear();
36             Array.Clear(channelValues, 0, NUMBER_OF_CHANNELS);
37             IRQ.Unset();
38             readDataValue = 0;
39         }
40 
SetChannelValue(ushort channel, ushort value)41         public void SetChannelValue(ushort channel, ushort value)
42         {
43             var channelIndex = ValidateChannelNumber(channel);
44             channelValues[channelIndex] = value;
45         }
46 
GetChannelValue(ushort channel)47         public ushort GetChannelValue(ushort channel)
48         {
49             var channelIndex = ValidateChannelNumber(channel);
50             return channelValues[channelIndex];
51         }
52 
53         public long Size => 0x1C;
54         public GPIO IRQ { get; }
55 
DefineRegisters()56         private void DefineRegisters()
57         {
58             Register.Configuration.Define(this, resetValue: 0x00001114)
59                 .WithTag("IGAP", 0, 5)
60                 .WithReservedBits(5, 3)
61                 .WithTag("TCKRATE", 8, 2)
62                 .WithReservedBits(10, 2)
63                 .WithTag("REDGE", 12, 1)
64                 .WithTag("WEDGE", 13, 1)
65                 .WithReservedBits(14, 2)
66                 .WithValueField(16, 4, out dataFifoThreshold, name: "DFIFOTH")
67                 .WithTag("CFIFOTH", 20, 4)
68                 .WithReservedBits(24, 7)
69                 .WithTag("ENABLE", 31, 1);
70 
71             interruptStatus = Register.InterruptStatus.Define(this, resetValue: 0x00000200)
72                 .WithTag("ALM", 0, 7)
73                 .WithTag("OT", 7, 1)
74                 .WithFlag(8, out intStatusDfifoGth, FieldMode.Read | FieldMode.WriteOneToClear, name: "DFIFO_GTH")
75                 .WithTag("CFIFO_LTH", 9, 1)
76                 .WithReservedBits(10, 22)
77                 .WithChangeCallback((oldValue, newValue) => UpdateInterrupts());
78 
79             interruptMask = Register.InterruptMask.Define(this, resetValue: 0xFFFFFFFF)
80                 .WithTag("M_ALM", 0, 7)
81                 .WithTag("M_OT", 7, 1)
82                 .WithFlag(8, name: "M_DFIFO_GTH")
83                 .WithTag("M_CFIFO_LTH", 9, 1)
84                 .WithReservedBits(10, 22)
85                 .WithWriteCallback((oldValue, newValue) => UpdateInterrupts());
86 
87             Register.MiscStatus.Define(this, resetValue: 0x00000500)
88                 .WithTag("ALM", 0, 7)
89                 .WithTag("OT", 7, 1)
90                 .WithFlag(8, FieldMode.Read, valueProviderCallback: (_) => dataFifo.Count == 0, name: "DFIFOE")
91                 .WithFlag(9, FieldMode.Read, valueProviderCallback: (_) => dataFifo.Count == DATA_FIFO_CAPACITY, name: "DFIFOF")
92                 // Command FIFO empty - always true because we execute commands immediately after they're written to the FIFO
93                 .WithFlag(10, FieldMode.Read, valueProviderCallback: (_) => true, name: "CFIFOE")
94                 // Command FIFO full - always false, see above
95                 .WithFlag(11, FieldMode.Read, valueProviderCallback: (_) => false, name: "CFIFOF")
96                 .WithValueField(12, 4, FieldMode.Read, valueProviderCallback: (_) => (uint)dataFifo.Count, name: "DFIFO_LVL")
97                 .WithTag("CFIFO_LVL", 16, 4)
98                 .WithReservedBits(20, 12);
99 
100             Register.CommmandFifo.Define(this)
101                 .WithValueField(0, 32, FieldMode.Write, writeCallback: (oldValue, newValue) =>
102                 {
103                     this.DebugLog($"Command FIFO write: 0x{newValue:x}");
104                     HandleCommand((uint)newValue);
105                 });
106 
107             Register.DataFifo.Define(this)
108                 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: (oldValue) =>
109                 {
110                     if(!dataFifo.TryDequeue(out var value))
111                     {
112                         this.Log(LogLevel.Warning, "Read from empty data FIFO");
113                     }
114 
115                     this.DebugLog($"Data FIFO read returning 0x{value:x}");
116                     return value;
117                 });
118 
119             Register.MiscControl.Define(this)
120                 .WithReservedBits(0, 1, 0)
121                 .WithReservedBits(1, 3)
122                 .WithTag("RESET", 4, 1)
123                 .WithReservedBits(5, 27);
124         }
125 
HandleCommand(uint command)126         private void HandleCommand(uint command)
127         {
128             var cmd = (command >> 26) & 0xF;
129             var drpAddress = (ushort)((command >> 16) & 0x3FF);
130             var drpData = (ushort)((command >> 0) & 0x7FFF);
131 
132             switch((PsXadcCommand)cmd)
133             {
134                 case PsXadcCommand.NoOperation:
135                     DataFifoSend(readDataValue);
136                     readDataValue = 0;
137                     break;
138                 case PsXadcCommand.DrpRead:
139                     DataFifoSend(0);
140                     readDataValue = HandleDrpRead(drpAddress);
141                     break;
142                 case PsXadcCommand.DrpWrite:
143                     DataFifoSend(HandleDrpWrite(drpAddress, drpData));
144                     break;
145                 default:
146                     this.Log(LogLevel.Warning, $"Unknown DRP command 0x{cmd:x}");
147                     break;
148             }
149         }
150 
AdcChannelNumberToIndex(ushort address)151         private uint? AdcChannelNumberToIndex(ushort address)
152         {
153             if(address >= (ushort)DrpRegister.Temperature && address <= (ushort)DrpRegister.VccBram)
154             {
155                 return (uint)(address - (ushort)DrpRegister.Temperature);
156             }
157 
158             if(address >= (ushort)DrpRegister.VccPint && address <= (ushort)DrpRegister.VauxpVauxn15)
159             {
160                 return (uint)(address - (ushort)DrpRegister.VccPint)
161                     + ((ushort)DrpRegister.VccBram - (ushort)DrpRegister.Temperature + 1);
162             }
163 
164             return null;
165         }
166 
ValidateChannelNumber(ushort channel)167         private uint ValidateChannelNumber(ushort channel)
168         {
169             if(!(AdcChannelNumberToIndex(channel) is uint channelIndex))
170             {
171                 throw new RecoverableException($"Invalid channel number {channel}");
172             }
173             return channelIndex;
174         }
175 
HandleDrpRead(ushort address)176         private ushort HandleDrpRead(ushort address)
177         {
178             // The DRP register addresses match the ADC channel numbers used in the datasheet.
179             if(AdcChannelNumberToIndex(address) is uint channelIndex)
180             {
181                 var rawValue = channelValues[channelIndex];
182                 this.DebugLog($"Read ADC channel {address} with value {rawValue}");
183                 return (ushort)(rawValue << 4); // MSB-justify 12-bit value in 16-bit register
184             }
185             else
186             {
187                 this.Log(LogLevel.Warning, $"Unhandled DRP register read: 0x{address:x}");
188                 return 0;
189             }
190         }
191 
HandleDrpWrite(ushort address, ushort value)192         private ushort HandleDrpWrite(ushort address, ushort value)
193         {
194             this.Log(LogLevel.Warning, $"Unhandled DRP register write: 0x{address:x}, value: 0x{value:x}");
195             return 0;
196         }
197 
DataFifoSend(uint value)198         private void DataFifoSend(uint value)
199         {
200             if(dataFifo.Count == DATA_FIFO_CAPACITY)
201             {
202                 this.Log(LogLevel.Warning, $"Tried to enqueue onto a full data FIFO, value: 0x{value:x}");
203                 return;
204             }
205 
206             dataFifo.Enqueue(value);
207             UpdateInterrupts();
208         }
209 
UpdateInterrupts()210         private void UpdateInterrupts()
211         {
212             intStatusDfifoGth.Value |= dataFifo.Count > (int)dataFifoThreshold.Value;
213 
214             var interruptFlag = (interruptStatus.Value & ~interruptMask.Value) != 0;
215             IRQ.Set(interruptFlag);
216         }
217 
218         private uint readDataValue;
219 
220         private IValueRegisterField dataFifoThreshold;
221 
222         private DoubleWordRegister interruptStatus;
223         private IFlagRegisterField intStatusDfifoGth;
224 
225         private DoubleWordRegister interruptMask;
226 
227         private readonly Queue<uint> dataFifo;
228         private readonly ushort[] channelValues;
229 
230         private const int NUMBER_OF_CHANNELS = 1 +
231             (int)DrpRegister.VccBram - (int)DrpRegister.Temperature +
232             (int)DrpRegister.VauxpVauxn15 - (int)DrpRegister.VccPint;
233         private const int DATA_FIFO_CAPACITY = 15;
234 
235         private enum PsXadcCommand
236         {
237             NoOperation = 0x0,
238             DrpRead     = 0x1,
239             DrpWrite    = 0x2,
240         }
241 
242         private enum Register: long
243         {
244             Configuration   = 0x00, // XADCIF_CFG
245             InterruptStatus = 0x04, // XADCIF_INT_STS
246             InterruptMask   = 0x08, // XADCIF_INT_MASK
247             MiscStatus      = 0x0C, // XADCIF_MSTS
248             CommmandFifo    = 0x10, // XADCIF_CMDFIFO
249             DataFifo        = 0x14, // XADCIF_RDFIFO
250             MiscControl     = 0x18, // XADCIF_MCTL
251         }
252 
253         private enum DrpRegister: ushort
254         {
255             /* ADC conversion result registers. Each of these is a 12-bit value MSB-justified to 16 bits. */
256             Temperature           = 0x00,
257             VccInt                = 0x01,
258             VccAux                = 0x02,
259             VpVn                  = 0x03,
260             Vrefp                 = 0x04,
261             Vrefn                 = 0x05,
262             VccBram               = 0x06,
263             VccPint               = 0x0D,
264             VccPaux               = 0x0E,
265             VccoDdr               = 0x0F,
266             VauxpVauxn0           = 0x10,
267             VauxpVauxn15          = 0x1F,
268             /* End of ADC conversion result registers. */
269         }
270     }
271 }
272