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 System.IO;
11 using System.Linq;
12 using Antmicro.Renode.Core;
13 using Antmicro.Renode.Core.Structure.Registers;
14 using Antmicro.Renode.Exceptions;
15 using Antmicro.Renode.Logging;
16 using Antmicro.Renode.Peripherals.Bus;
17 using Antmicro.Renode.Sound;
18 using Antmicro.Renode.Utilities;
19 
20 namespace Antmicro.Renode.Peripherals.Sound
21 {
22     public class NRF52840_PDM: BasicDoubleWordPeripheral, IKnownSize
23     {
NRF52840_PDM(IMachine machine)24         public NRF52840_PDM(IMachine machine) : base(machine)
25         {
26             CreateRegisters();
27             IRQ = new GPIO();
28             Reset();
29         }
30 
Reset()31         public override void Reset()
32         {
33             base.Reset();
34             IRQ.Unset();
35             decoderLeft?.Reset();
36             decoderRight?.Reset();
37             sampleThread?.Dispose();
38             sampleThread = null;
39             inputFileLeft = "";
40             inputFileRight = "";
41             multiplierL = 1.0;
42             multiplierR = 1.0;
43             numberOfChannels = 2;
44             sampleRatio = 64;
45             clockFrequency = 32000000 / 31;
46             SetSampleFrequency();
47         }
48 
SetInputFile(string fileName, Channel channel = Channel.Left, int repeat = 1)49         public void SetInputFile(string fileName, Channel channel = Channel.Left, int repeat = 1)
50         {
51             switch(channel)
52             {
53                 case Channel.Left:
54                 {
55                     if(decoderLeft == null)
56                     {
57                         decoderLeft = new PCMDecoder(16, sampleFrequency, 1, false, this);
58                     }
59 
60                     for(var i = 0; i < repeat; i++)
61                     {
62                         decoderLeft.LoadFile(fileName);
63                     }
64                     inputFileLeft = fileName;
65                 }
66                 break;
67 
68                 case Channel.Right:
69                 {
70                     if(decoderRight == null)
71                     {
72                         decoderRight = new PCMDecoder(16, sampleFrequency, 1, false, this);
73                     }
74 
75                     for(var i = 0; i < repeat; i++)
76                     {
77                         decoderRight.LoadFile(fileName);
78                     }
79                     inputFileRight = fileName;
80                 }
81                 break;
82             }
83         }
84 
85         public GPIO IRQ { get; }
86 
87         public long Size => 0x1000;
88 
89         public enum Channel
90         {
91             Left = 0,
92             Right = 1,
93         }
94 
UpdateInterrupts()95         private void UpdateInterrupts()
96         {
97             var stopped = eventStopped.Value && intenStopped.Value;
98             var started = eventStarted.Value && intenStarted.Value;
99             var end = eventEnd.Value && intenEnd.Value;
100             IRQ.Set(stopped || started || end);
101         }
102 
Start()103         private void Start()
104         {
105             if(!enablePDM.Value)
106             {
107                 this.Log(LogLevel.Warning, "Trying to start samples aquisition before enabling peripheral. Will not start");
108                 return;
109             }
110 
111             if(inputFileLeft == "" || (numberOfChannels == 2 && inputFileRight == ""))
112             {
113                 this.Log(LogLevel.Error, "Trying to start reception with not enough input files - please set input using `SetinputFile`. Aborting.");
114                 return;
115             }
116 
117             StartPDMThread();
118         }
119 
Stop()120         private void Stop()
121         {
122             eventStopped.Value = true;
123             UpdateInterrupts();
124             this.Log(LogLevel.Debug, "Event Stopped");
125         }
126 
StartPDMThread()127         private bool StartPDMThread()
128         {
129             StopPDMThread();
130 
131             if(maxSamplesCount.Value == 0)
132             {
133                 // Crate stub ManagedThread just to make clear that it was started, but just send
134                 // eventStarted Interrupt - software might configure proper value in the IRQ handler
135                 sampleThread = machine.ObtainManagedThread(InputSamples, 1);
136                 eventStarted.Value = true;
137                 UpdateInterrupts();
138                 return false;
139             }
140             // Since we handle all samples in one go we have to calculate how often should we do it
141             var eventFrequency = (sampleFrequency / (int)(maxSamplesCount.Value)) * numberOfChannels;
142             sampleThread = machine.ObtainManagedThread(InputSamples, (uint)eventFrequency);
143             sampleThread.Start();
144             return true;
145         }
146 
StopPDMThread()147         private bool StopPDMThread()
148         {
149            if(sampleThread == null)
150            {
151                return false;
152            }
153            sampleThread.Stop();
154            sampleThread.Dispose();
155            sampleThread = null;
156            return true;
157         }
158 
InputSamples()159         private void InputSamples()
160         {
161             var currentPointer = samplePtr.Value;
162             eventStarted.Value = true;
163             UpdateInterrupts();
164 
165             var samplesCount = (uint)maxSamplesCount.Value;
166             var doubleWordsCount = samplesCount / 2;
167             var preparedDoubleWords = new uint[doubleWordsCount];
168 
169             switch(numberOfChannels)
170             {
171                 case 1:
172                     var samples = decoderLeft.GetSamplesByCount(samplesCount);
173 
174                     var index = 1u;
175                     ushort prev = 0;
176 
177                     foreach(ushort sample in samples)
178                     {
179                         if(index % 2 != 0)
180                         {
181                             prev = sample;
182                         }
183                         else
184                         {
185                             // Assuming input file format of s16le
186                             preparedDoubleWords[(index / 2) - 1] = (uint)((Misc.SwapBytesUShort(sample) << 16) | Misc.SwapBytesUShort(prev));
187                         }
188                         index++;
189                     }
190 
191                     if(index % 2 == 0)
192                     {
193                         // One sample left
194                         preparedDoubleWords[(index / 2) - 1] = prev;
195                     }
196 
197                     break;
198                 case 2:
199                     var samplesLeft  = decoderLeft.GetSamplesByCount(samplesCount / 2).ToArray();
200                     var samplesRight = decoderRight.GetSamplesByCount(samplesCount / 2).ToArray();
201 
202                     if(samplesLeft.Length != samplesRight.Length)
203                     {
204                         // Make sure arrays have equal size
205                         var neededSize = Math.Max(samplesLeft.Length, samplesRight.Length);
206                         Array.Resize(ref samplesLeft, neededSize);
207                         Array.Resize(ref samplesRight, neededSize);
208                     }
209 
210                     if(invertChannels.Value)
211                     {
212                         Misc.Swap(ref samplesLeft, ref samplesRight);
213                     }
214 
215                     for(var i = 0; i < samplesLeft.Length; i++)
216                     {
217                         var right = (uint)Misc.SwapBytesUShort((ushort)samplesRight[i]);
218                         var left  = (uint)Misc.SwapBytesUShort((ushort)samplesLeft[i]);
219 
220                         preparedDoubleWords[i] = (right << 16) | left;
221                     }
222                     break;
223             }
224 
225             foreach(uint i in preparedDoubleWords)
226             {
227                 sysbus.WriteDoubleWord(currentPointer, i);
228                 currentPointer += 4;
229             }
230 
231             eventEnd.Value = true;
232             UpdateInterrupts();
233         }
234 
SetGain(ulong val, Channel channel)235         private void SetGain(ulong val, Channel channel)
236         {
237             if(val > 80)
238             {
239                 this.Log(LogLevel.Error, "Trying to set GAIN.{0} value higher than 80. Setting gain to default value.", channel);
240                 val = 40;
241             }
242             var gain = ((int)val - 40) / 2;
243             this.Log(LogLevel.Debug, "{0} channel gain set to {1}db", channel,  gain);
244             // Convert dB of amplitude to multiplier
245             var multiplier = Math.Pow(10, gain / 20.0);
246             switch(channel)
247             {
248                 case Channel.Left:
249                     multiplierL = multiplier;
250                     break;
251                 case Channel.Right:
252                     multiplierR = multiplier;
253                     break;
254             }
255         }
256 
SetClockFrequency(ClockFrequency frequency)257         private void SetClockFrequency(ClockFrequency frequency)
258         {
259             switch(frequency)
260             {
261                 case ClockFrequency.f1000K:
262                     clockFrequency = 32000000 / 32;
263                     break;
264                 case ClockFrequency.Default:
265                     clockFrequency = 32000000 / 31;
266                     break;
267                 case ClockFrequency.f1067K:
268                     clockFrequency = 32000000 / 30;
269                     break;
270                 case ClockFrequency.f1231K:
271                     clockFrequency = 32000000 / 26;
272                     break;
273                 case ClockFrequency.f1280K:
274                     clockFrequency = 32000000 / 25;
275                     break;
276                 case ClockFrequency.f1333K:
277                     clockFrequency = 32000000 / 24;
278                     break;
279                 default:
280                     this.Log(LogLevel.Error, "Wrong PDMCLKCTRL value, settting to default value");
281                     goto case ClockFrequency.Default;
282             }
283             SetSampleFrequency();
284         }
285 
SetSampleFrequency()286         private void SetSampleFrequency()
287         {
288             sampleFrequency = clockFrequency / sampleRatio;
289             this.Log(LogLevel.Debug, "Clock frequency = {0}kHz; Sample ratio = {1}; Sample frequency set to {2}kHz", clockFrequency / 1000.0, sampleRatio, sampleFrequency / 1000.0);
290         }
291 
CreateRegisters()292         private void CreateRegisters()
293         {
294             Registers.TasksStart.Define(this, 0x0)
295                 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) Start(); }, name: "TASKS_START")
296                 .WithReservedBits(1, 31);
297             Registers.TasksStop.Define(this, 0x0)
298                 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) Stop(); }, name: "TASKS_STOP")
299                 .WithReservedBits(1, 31);
300             Registers.EventsStarted.Define(this, 0x0)
301                 .WithFlag(0, out eventStarted, changeCallback: (_, __) => UpdateInterrupts(), name: "EVENTS_STARTED")
302                 .WithReservedBits(1, 31);
303             Registers.EventsStopped.Define(this, 0x0)
304                 .WithFlag(0, out eventStopped, changeCallback: (_, __) => UpdateInterrupts(), name: "EVENTS_STOPPED")
305                 .WithReservedBits(1, 31);
306             Registers.EventsEnd.Define(this, 0x0)
307                 .WithFlag(0, out eventEnd, changeCallback: (_, __) => UpdateInterrupts(), name: "EVENTS_END")
308                 .WithReservedBits(1, 31);
309             Registers.InterruptEnable.Define(this, 0x0)
310                 .WithFlag(0, out intenStarted, name: "STARTED")
311                 .WithFlag(1, out intenStopped, name: "STOPPED")
312                 .WithFlag(2, out intenEnd, name: "END")
313                 .WithReservedBits(3, 29)
314                 .WithWriteCallback((_, __) => UpdateInterrupts());
315             Registers.InterruptEnableSet.Define(this, 0x0)
316                 .WithFlag(0,
317                     writeCallback: (_, val) => { intenStarted.Value |= val; },
318                     valueProviderCallback: (_) => { return intenStarted.Value; },
319                     name: "STARTED")
320                 .WithFlag(1,
321                     writeCallback: (_, val) => { intenStopped.Value |= val; },
322                     valueProviderCallback: (_) => { return intenStopped.Value; },
323                     name: "STOPPED")
324                 .WithFlag(2,
325                     writeCallback: (_, val) => { intenEnd.Value |= val; },
326                     valueProviderCallback: (_) => { return intenEnd.Value; },
327                     name: "END")
328                 .WithReservedBits(3, 29)
329                 .WithWriteCallback((_, __) => UpdateInterrupts());
330             Registers.InterruptEnableClear.Define(this, 0x0)
331                 .WithFlag(0,
332                     writeCallback: (_, val) => { intenStarted.Value &= !val; },
333                     valueProviderCallback: (_) => { return intenStarted.Value; },
334                     name: "STARTED")
335                 .WithFlag(1,
336                     writeCallback: (_, val) => { intenStopped.Value &= !val; },
337                     valueProviderCallback: (_) => { return intenStopped.Value; },
338                     name: "STOPPED")
339                 .WithFlag(2,
340                     writeCallback: (_, val) => { intenEnd.Value &= !val; },
341                     valueProviderCallback: (_) => { return intenEnd.Value; },
342                     name: "END")
343                 .WithReservedBits(3, 29)
344                 .WithWriteCallback((_, __) => UpdateInterrupts());
345             Registers.Enable.Define(this, 0x0)
346                 .WithFlag(0, out enablePDM, name: "ENABLE")
347                 .WithReservedBits(1, 31);
348             Registers.PdmClockControl.Define(this, 0x08400000)
349                 .WithValueField(0, 32,
350                     writeCallback: (_, val) => SetClockFrequency((ClockFrequency)val), name: "FREQ");
351             Registers.Mode.Define(this, 0x0)
352                 .WithValueField(0, 1, out operationMode,
353                     writeCallback: (_, val) =>
354                         {
355                             switch((OperationMode)val)
356                             {
357                                 case OperationMode.Stereo:
358                                     numberOfChannels = 2;
359                                     break;
360                                 case OperationMode.Mono:
361                                     numberOfChannels = 1;
362                                     break;
363                             }
364                             this.Log(LogLevel.Debug, "MODE.OPERATION set to {0}", (OperationMode)val);
365                         },
366                     name: "OPERATION")
367                 .WithFlag(1, out invertChannels, name:"EDGE")
368                 .WithReservedBits(2, 30);
369             Registers.Ratio.Define(this, 0x0)
370                 .WithValueField(0, 1,
371                      writeCallback: (_, val) =>
372                      {
373                          switch((Ratio)val)
374                          {
375                              case Ratio.x64:
376                                  sampleRatio = 64;
377                                  break;
378                              case Ratio.x80:
379                                  sampleRatio = 80;
380                                  break;
381                          }
382                          SetSampleFrequency();
383                      },
384                      name:"RATIO")
385                 .WithReservedBits(1, 31);
386             Registers.GainLeft.Define(this, 0x28)
387                 .WithValueField(0, 7,
388                     writeCallback: (_, val) => SetGain(val, Channel.Left), name: "GAINL")
389                 .WithReservedBits(8, 24);
390             Registers.GainRigth.Define(this, 0x28)
391                 .WithValueField(0, 7, writeCallback: (_, val) => SetGain(val, Channel.Right), name: "GAINR")
392                 .WithReservedBits(8, 24);
393             Registers.SamplePointer.Define(this, 0x0)
394                 .WithValueField(0, 32, out samplePtr, name: "PTR");
395             Registers.SampleBufferSize.Define(this, 0x0)
396                 .WithValueField(0, 15, out maxSamplesCount,
397                     writeCallback: (oldval, val) =>
398                     {
399                         if(oldval != val && sampleThread != null)
400                         {
401                             // Need to restart thread to change how often it fires
402                             this.Log(LogLevel.Debug, "Setting MaxSampleCount to {0}", val);
403                             StartPDMThread();
404                         }
405                     },
406                     name: "MAXCNT")
407                 .WithReservedBits(15, 17);
408             Registers.PinSelectClk.Define(this, 0xFFFFFFFF)
409                 .WithTaggedFlag("PIN", 0)
410                 .WithTaggedFlag("PORT", 5)
411                 .WithReservedBits(6, 25)
412                 .WithTaggedFlag("CONNECT", 31);
413             Registers.PinSelectDin.Define(this, 0xFFFFFFFF)
414                 .WithTaggedFlag("PIN", 0)
415                 .WithTaggedFlag("PORT", 5)
416                 .WithReservedBits(6, 25)
417                 .WithTaggedFlag("CONNECT", 31);
418         }
419 
420         private uint clockFrequency;
421         private uint numberOfChannels;
422         private uint sampleFrequency;
423         private uint sampleRatio;
424         private double multiplierL;
425         private double multiplierR;
426         private string inputFileLeft;
427         private string inputFileRight;
428 
429         private PCMDecoder decoderLeft;
430         private PCMDecoder decoderRight;
431         private IManagedThread sampleThread;
432 
433         private IFlagRegisterField enablePDM;
434         private IFlagRegisterField eventEnd;
435         private IFlagRegisterField eventStarted;
436         private IFlagRegisterField eventStopped;
437         private IFlagRegisterField intenEnd;
438         private IFlagRegisterField intenStarted;
439         private IFlagRegisterField intenStopped;
440         private IFlagRegisterField invertChannels;
441         private IValueRegisterField maxSamplesCount;
442         private IValueRegisterField operationMode;
443         private IValueRegisterField samplePtr;
444 
445         private enum Registers : long
446         {
447             TasksStart           = 0x000, // Starts continuous PDM transfer
448             TasksStop            = 0x004, // Stops PDM transfer
449             EventsStarted        = 0x100, // PDM transfer has started
450             EventsStopped        = 0x104, // PDM transfer has finished
451             EventsEnd            = 0x108, // The PDM has written the last sample specified by Sample_MAXCNT (or the last sample after aSTOP task has been received) to Data RAM.
452             InterruptEnable      = 0x300, // Enable or disable interrupt
453             InterruptEnableSet   = 0x304, // Enable interrupt
454             InterruptEnableClear = 0x308, // Disable interrupt
455             Enable               = 0x500, // PDM module enable register
456             PdmClockControl      = 0x504, // PDM clock generator control
457             Mode                 = 0x508, // Defines the routing of the connected PDM microphones' signals
458             GainLeft             = 0x518, // Left output gain adjustment
459             GainRigth            = 0x51C, // Right output gain adjustment
460             Ratio                = 0x520, // Selects the ratio between PDM_CLK and output sample rate. Change PDMCLKCTRL accordingly.
461             PinSelectClk         = 0x540, // Pin number configuration for PDM CLK signal
462             PinSelectDin         = 0x544, // Pin number configuration for PDM DIN signal
463             SamplePointer        = 0x560, // RAM address pointer to write samples to with EasyDMA
464             SampleBufferSize     = 0x564, // Number of samples to allocate memory for in EasyDMA mode
465         }
466 
467         private enum ClockFrequency
468         {
469             f1000K  = 0x08000000, // PDM_CLK = 32 MHz / 32 = 1.000 MHz
470             Default = 0x08400000, // PDM_CLK = 32 MHz / 31 = 1.032 MHz. Nominal clock forRATIO=Ratio64.
471             f1067K  = 0x08800000, // PDM_CLK = 32 MHz / 30 = 1.067 MHz
472             f1231K  = 0x09800000, // PDM_CLK = 32 MHz / 26 = 1.231 MHz
473             f1280K  = 0x0A000000, // PDM_CLK = 32 MHz / 25 = 1.280 MHz. Nominal clock forRATIO=Ratio80.
474             f1333K  = 0x0A800000, // PDM_CLK = 32 MHz / 24 = 1.333 MHz
475         }
476 
477         private enum OperationMode
478         {
479             Stereo = 0,
480             Mono   = 1,
481         }
482 
483         private enum Ratio
484         {
485             x64 = 0,
486             x80 = 1,
487         }
488     }
489 }
490