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_I2S : BasicDoubleWordPeripheral, IDisposable, IKnownSize
23     {
NRF52840_I2S(IMachine machine)24         public NRF52840_I2S(IMachine machine) : base(machine)
25         {
26             CreateRegisters();
27             IRQ = new GPIO();
28             Reset();
29         }
30 
Dispose()31         public void Dispose()
32         {
33             encoder?.Dispose();
34         }
35 
Reset()36         public override void Reset()
37         {
38             base.Reset();
39             IRQ.Unset();
40             decoder?.Reset();
41             encoder?.FlushBuffer();
42 
43             sampleRatio = 256;
44             sampleWidth = 8;
45             numberOfChannels = 2;
46             masterFrequency  = 4000000;
47             samplesPerDoubleWord = 4;
48         }
49 
50         public GPIO IRQ { get; }
51         public string InputFile  { get; set; }
52         public string OutputFile  { get; set; }
53         public long Size => 0x1000;
54 
UpdateInterrupts()55         private void UpdateInterrupts()
56         {
57             var stopped = eventStopped.Value && interruptEnableStopped.Value;
58             var rxPointerUpdated = eventRxPointerUpdated.Value && interruptEnableRxPointerUpdated.Value;
59             var txPointerUpdated = eventTxPointerUpdated.Value && interruptEnableTxPointerUpdated.Value;
60             IRQ.Set(stopped || rxPointerUpdated || txPointerUpdated);
61         }
62 
Start()63         private void Start()
64         {
65             if(enableTx.Value)
66             {
67                 if(OutputFile == "")
68                 {
69                     this.Log(LogLevel.Error, "Starting transmission without an output file!");
70                     return;
71                 }
72                 encoder = new PCMEncoder(sampleWidth, sampleFrequency, numberOfChannels, false);
73                 encoder.SetBufferingBySamplesCount((uint)maxSamplesCount.Value);
74                 encoder.Output = OutputFile;
75             }
76 
77             if(enableRx.Value)
78             {
79                 if(InputFile == "")
80                 {
81                     this.Log(LogLevel.Error, "Starting reception without an input file!");
82                     return;
83                 }
84                 decoder = new PCMDecoder(sampleWidth, sampleFrequency, numberOfChannels, false, this);
85                 decoder.LoadFile(InputFile);
86             }
87             StartRxTxThread();
88         }
89 
Stop()90         private void Stop()
91         {
92             encoder?.FlushBuffer();
93             StopRxTxThread();
94             eventStopped.Value = true;
95             UpdateInterrupts();
96         }
97 
StartRxTxThread()98         private void StartRxTxThread()
99         {
100             if(!enableI2S.Value)
101             {
102                 this.Log(LogLevel.Error, "Trying to start aquisition, when peripheral is disabled (ENABLE==0). Will not start");
103                 return;
104             }
105             rxTxThread = machine.ObtainManagedThread(ProcessFrames, sampleFrequency / ((uint)maxSamplesCount.Value * samplesPerDoubleWord));
106             rxTxThread.Start();
107         }
108 
StopRxTxThread()109         private void StopRxTxThread()
110         {
111             if(rxTxThread == null)
112             {
113                 this.Log(LogLevel.Debug, "Trying to stop sampling when it is not active");
114                 return;
115             }
116             rxTxThread.Stop();
117             rxTxThread = null;
118         }
119 
ProcessFrames()120         private void ProcessFrames()
121         {
122             if(enableRx.Value)
123             {
124                 InputFrames();
125             }
126             if(enableTx.Value)
127             {
128                 OutputFrames();
129             }
130         }
131 
OutputFrames()132         private void OutputFrames()
133         {
134             var currentPointer = txdPointer.Value;
135             // The TXD.PTR register has been copied to internal double-buffers
136             eventTxPointerUpdated.Value = true;
137             UpdateInterrupts();
138 
139             // RxTxMaxCnt denotes number of DoubleWords, we need to calculate samples number
140             for(var samples = 0u; samples < maxSamplesCount.Value * samplesPerDoubleWord; samples++)
141             {
142                 var thisSample = sysbus.ReadDoubleWord(currentPointer + samples * sampleWidth / 8);
143                 BitHelper.ClearBits(ref thisSample, (int)sampleWidth, (int)(32 - sampleWidth));
144                 encoder.AcceptSample(thisSample);
145             }
146         }
147 
InputFrames()148         private void InputFrames()
149         {
150             var currentPointer = rxdPointer.Value;
151             // The RXD.PTR register has been copied to internal double-buffers
152             eventRxPointerUpdated.Value = true;
153             UpdateInterrupts();
154 
155             for(var doubleWords = 0u; doubleWords < maxSamplesCount.Value; doubleWords++)
156             {
157                 // Double word may consist on many samples when sampleWidth is not equal 32bit
158                 uint valueToStore = 0;
159                 for(var sampleOffset = samplesPerDoubleWord; sampleOffset > 0; sampleOffset--)
160                 {
161                     valueToStore |= decoder.GetSingleSample() << (int)(sampleWidth * (sampleOffset - 1));
162                 }
163                 sysbus.WriteDoubleWord(currentPointer + doubleWords * 4, valueToStore);
164             }
165         }
166 
SetMasterClockLrckRatio(MasterLrClockRatio value)167         private void SetMasterClockLrckRatio(MasterLrClockRatio value)
168         {
169             switch(value)
170             {
171                 case MasterLrClockRatio.X32:
172                     sampleRatio = 32;
173                     break;
174                 case MasterLrClockRatio.X48:
175                     sampleRatio = 48;
176                     break;
177                 case MasterLrClockRatio.X64:
178                     sampleRatio = 64;
179                     break;
180                 case MasterLrClockRatio.X96:
181                     sampleRatio = 96;
182                     break;
183                 case MasterLrClockRatio.X128:
184                     sampleRatio = 128;
185                     break;
186                 case MasterLrClockRatio.X192:
187                     sampleRatio = 192;
188                     break;
189                 case MasterLrClockRatio.X256:
190                     sampleRatio = 256;
191                     break;
192                 case MasterLrClockRatio.X384:
193                     sampleRatio = 384;
194                     break;
195                 case MasterLrClockRatio.X512:
196                     sampleRatio = 512;
197                     break;
198                 default:
199                     this.Log(LogLevel.Error, "Wrong CONFIG.RATIO value");
200                     break;
201             }
202             SetSampleFrequency();
203         }
204 
SetMasterClockFrequency(MasterClockFrequency val)205         private void SetMasterClockFrequency(MasterClockFrequency val)
206         {
207             switch(val)
208             {
209                 case MasterClockFrequency.Mhz32Div8:
210                     masterFrequency = 32000000 / 8;
211                     break;
212                 case MasterClockFrequency.Mhz32Div10:
213                     masterFrequency = 32000000 / 10;
214                     break;
215                 case MasterClockFrequency.Mhz32Div11:
216                     masterFrequency = 32000000 / 11;
217                     break;
218                 case MasterClockFrequency.Mhz32Div15:
219                     masterFrequency = 32000000 / 15;
220                     break;
221                 case MasterClockFrequency.Mhz32Div16:
222                     masterFrequency = 32000000 / 16;
223                     break;
224                 case MasterClockFrequency.Mhz32Div21:
225                     masterFrequency = 32000000 / 21;
226                     break;
227                 case MasterClockFrequency.Mhz32Div23:
228                     masterFrequency = 32000000 / 23;
229                     break;
230                 case MasterClockFrequency.Mhz32Div30:
231                     masterFrequency = 32000000 / 30;
232                     break;
233                 case MasterClockFrequency.Mhz32Div31:
234                     masterFrequency = 32000000 / 31;
235                     break;
236                 case MasterClockFrequency.Mhz32Div32:
237                     masterFrequency = 32000000 / 32;
238                     break;
239                 case MasterClockFrequency.Mhz32Div42:
240                     masterFrequency = 32000000 / 42;
241                     break;
242                 case MasterClockFrequency.Mhz32Div63:
243                     masterFrequency = 32000000 / 63;
244                     break;
245                 case MasterClockFrequency.Mhz32Div125:
246                     masterFrequency = 32000000 / 125;
247                     break;
248                 default:
249                     this.Log(LogLevel.Error, "Wrong CONFIG.MCK value");
250                     break;
251             }
252             SetSampleFrequency();
253         }
254 
SetSampleWidth(uint value)255         private void SetSampleWidth(uint value)
256         {
257             // Only 3 values possible:
258             //  0  -  8  Bit
259             //  1  -  16 Bit (Default)
260             //  2  -  32 Bit
261             if(value > 2)
262             {
263                 this.Log(LogLevel.Warning, "Sample width set to invalid value : 0x{0:X}. Setting default value.", value);
264                 value = 1;
265             }
266             sampleWidth = (uint)(8 * (1 << (int)value));
267             samplesPerDoubleWord = 32 / sampleWidth;
268             SetSampleFrequency();
269         }
270 
SetSampleFrequency()271         private void SetSampleFrequency()
272         {
273             if(sampleRatio < 2 * sampleWidth)
274             {
275                 this.Log(LogLevel.Error, "Invalid CONFIG.RATIO value, it cannot exceed `2* CONFIG.SWIDTH`");
276             }
277             sampleFrequency = GetClosestValue(masterFrequency / sampleRatio, possibleSamplingRates);
278             this.Log(LogLevel.Debug, "Set sample frequency to {0}Hz, {1}Bit", sampleFrequency, sampleWidth);
279         }
280 
GetClosestValue(uint freq, uint[] possibleVals)281         private uint GetClosestValue(uint freq, uint[] possibleVals)
282         {
283             var closest = possibleVals.OrderBy(x => Math.Abs((long) x - freq)).First();
284             return closest;
285         }
286 
CreateRegisters()287         private void CreateRegisters()
288         {
289             Registers.TasksStart.Define(this)
290                     .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) Start(); }, name: "TASKS_START")
291                     .WithReservedBits(1,31);
292             Registers.TasksStop.Define(this)
293                     .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) Stop(); }, name: "TASKS_STOP")
294                     .WithReservedBits(1,31);
295             Registers.EventsRxptrUpdated .Define(this)
296                     .WithFlag(0, out eventRxPointerUpdated, changeCallback: (_, __) => UpdateInterrupts(), name: "EVENTS_RXPTRUPD")
297                     .WithReservedBits(1,31);
298             Registers.EventsStopped.Define(this)
299                     .WithFlag(0, out eventStopped, changeCallback: (_, __) => UpdateInterrupts(), name: "EVENTS_STOPPED")
300                     .WithReservedBits(1,31);
301             Registers.EventsTxptrUpdated.Define(this)
302                     .WithFlag(0, out eventTxPointerUpdated, changeCallback: (_, __) => UpdateInterrupts(), name: "EVENTS_TXPTRUPD")
303                     .WithReservedBits(1,31);
304             Registers.InterruptEnable.Define(this)
305                     .WithReservedBits(0,1)
306                     .WithFlag(1, out interruptEnableRxPointerUpdated, name: "RXPTRUPD")
307                     .WithFlag(2, out interruptEnableStopped, name: "STOPPED")
308                     .WithReservedBits(3,2)
309                     .WithFlag(5, out interruptEnableTxPointerUpdated, name: "TXPTRUPD")
310                     .WithReservedBits(6,25);
311             Registers.InterruptEnableSet.Define(this)
312                     .WithReservedBits(0,1)
313                     .WithFlag(1,
314                         writeCallback: (_, val) => { interruptEnableRxPointerUpdated.Value |= val; },
315                         valueProviderCallback: (_) => { return interruptEnableRxPointerUpdated.Value; },
316                         name: "RXPTRUPD")
317                     .WithFlag(2,
318                         writeCallback: (_, val) => { interruptEnableStopped.Value |= val; },
319                         valueProviderCallback: (_) => { return interruptEnableStopped.Value; },
320                         name: "STOPPED")
321                     .WithReservedBits(3,2)
322                     .WithFlag(5,
323                         writeCallback: (_, val) => { interruptEnableTxPointerUpdated.Value |= val; },
324                         valueProviderCallback: (_) => { return interruptEnableTxPointerUpdated.Value; },
325                         name: "TXPTRUPD")
326                     .WithReservedBits(6,25);
327             Registers.InterruptEnableClear.Define(this)
328                     .WithReservedBits(0,1)
329                     .WithFlag(1,
330                         writeCallback: (_, val) => { interruptEnableRxPointerUpdated.Value &= !val; },
331                         valueProviderCallback: (_) => { return interruptEnableRxPointerUpdated.Value; },
332                         name: "RXPTRUPD")
333                     .WithFlag(2,
334                         writeCallback: (_, val) => { interruptEnableStopped.Value &= !val; },
335                         valueProviderCallback: (_) => { return interruptEnableStopped.Value; },
336                         name: "STOPPED")
337                     .WithReservedBits(3,2)
338                     .WithFlag(5,
339                         writeCallback: (_, val) => { interruptEnableTxPointerUpdated.Value &= !val; },
340                         valueProviderCallback: (_) => { return interruptEnableTxPointerUpdated.Value; },
341                         name: "TXPTRUPD")
342                     .WithReservedBits(6,25);
343             Registers.Enable.Define(this)
344                     .WithFlag(0, out enableI2S, name: "ENABLE")
345                     .WithReservedBits(1,31);
346             Registers.ConfigMode.Define(this)
347                     .WithValueField(0, 1,
348                         writeCallback: (_, val) =>
349                             {
350                                 if((Mode)val == Mode.Slave)
351                                 {
352                                     //This requires ability to use master device clock configuration and handling alignment / format properly
353                                     this.Log(LogLevel.Error, "Slave mode unimplemented");
354                                 }
355                             },
356                         name: "MODE")
357                     .WithReservedBits(1,31);
358             Registers.ConfigRxEnable.Define(this)
359                     .WithFlag(0, out enableRx, name: "RXEN")
360                     .WithReservedBits(1,31);
361             Registers.ConfigTxEnable.Define(this, 0x1)
362                     .WithFlag(0, out enableTx, name: "TXEN")
363                     .WithReservedBits(1,31);
364             Registers.ConfigMasterClockEnable.Define(this, 0x1)
365                     .WithFlag(0, name: "MCKEN")
366                     .WithReservedBits(1,31);
367             Registers.ConfigMasterClockFrequency.Define(this, 0x20000000)
368                     .WithValueField(0, 32, writeCallback: (_, val) => SetMasterClockFrequency((MasterClockFrequency)val), name: "MCKFREQ");
369             Registers.ConfigRatio.Define(this, 0x6)
370                     .WithValueField(0, 4, writeCallback: (_, val) => SetMasterClockLrckRatio((MasterLrClockRatio)val), name: "RATIO")
371                     .WithReservedBits(4,28);
372             Registers.ConfigSwidth.Define(this, 0x1)
373                     .WithValueField(0, 2, writeCallback: (_, val) => SetSampleWidth((uint)val), name: "SWIDTH")
374                     .WithReservedBits(2,30);
375             Registers.ConfigAlign.Define(this)
376                     .WithTaggedFlag("ALIGN", 0)
377                     .WithReservedBits(1, 31);
378             Registers.ConfigFormat.Define(this)
379                     .WithTaggedFlag("FORMAT",0)
380                     .WithReservedBits(1, 31);
381             Registers.ConfigChannels.Define(this)
382                     .WithValueField(0, 2,
383                         writeCallback: (_, val) => { numberOfChannels = (Channels)val == Channels.Stereo ? 2u : 1u;},
384                         name: "CHANNELS")
385                     .WithReservedBits(2, 30);
386             Registers.RxdPointer.Define(this)
387                     .WithValueField(0, 32, out rxdPointer, name: "PTR");
388             Registers.TxdPointer.Define(this)
389                     .WithValueField(0, 32, out txdPointer, name: "PTR");
390             Registers.RxTxBufferSize.Define(this)
391                     .WithValueField(0, 14, out maxSamplesCount, name: "MAXCNT")
392                     .WithReservedBits(14, 18);
393             Registers.PinSelectMasterClock.Define(this, 0xFFFFFFFF)
394                     .WithTag("PIN", 0, 5)
395                     .WithTag("PORT", 5, 1)
396                     .WithReservedBits(6, 25)
397                     .WithTaggedFlag("CONNECT", 31);
398             Registers.PinSelectSCK.Define(this, 0xFFFFFFFF)
399                     .WithTag("PIN", 0, 5)
400                     .WithTag("PORT", 5, 1)
401                     .WithReservedBits(6, 25)
402                     .WithTaggedFlag("CONNECT", 31);
403             Registers.PinSelectLRCK.Define(this, 0xFFFFFFFF)
404                     .WithTag("PIN", 0, 5)
405                     .WithTag("PORT", 5, 1)
406                     .WithReservedBits(6, 25)
407                     .WithTaggedFlag("CONNECT", 31);
408             Registers.PinSelectSDIN.Define(this, 0xFFFFFFFF)
409                     .WithTag("PIN", 0, 5)
410                     .WithTag("PORT", 5, 1)
411                     .WithReservedBits(6, 25)
412                     .WithTag("CONNECT", 31, 1);
413             Registers.PinSelectSDOUT.Define(this, 0xFFFFFFFF)
414                     .WithValueField(0, 5, name: "PIN")
415                     .WithValueField(5, 1, name: "PORT")
416                     .WithReservedBits(6, 25)
417                     .WithTaggedFlag("CONNECT", 31);
418         }
419 
420         private IFlagRegisterField enableI2S;
421         private IFlagRegisterField enableRx;
422         private IFlagRegisterField enableTx;
423         private IFlagRegisterField eventRxPointerUpdated;
424         private IFlagRegisterField eventStopped;
425         private IFlagRegisterField eventTxPointerUpdated;
426         private IFlagRegisterField interruptEnableRxPointerUpdated;
427         private IFlagRegisterField interruptEnableStopped;
428         private IFlagRegisterField interruptEnableTxPointerUpdated;
429         private IValueRegisterField maxSamplesCount;
430         private IValueRegisterField rxdPointer;
431         private IValueRegisterField txdPointer;
432 
433         private uint masterFrequency;
434         private uint numberOfChannels;
435         private uint sampleFrequency;
436         private uint sampleRatio;
437         private uint samplesPerDoubleWord;
438         private uint sampleWidth;
439 
440         private IManagedThread rxTxThread;
441         private PCMDecoder decoder;
442         private PCMEncoder encoder;
443         private readonly uint[] possibleSamplingRates = {1000, 2000, 4000, 8000, 10000, 11025, 12000, 16000, 20000, 22050, 24000, 30000, 32000, 44100, 48000};
444 
445         private enum Registers :long
446         {
447             TasksStart           = 0x000, //Starts continuous I2S transfer. Also starts MCK generator when this is enabled.
448             TasksStop            = 0x004, //Stops I2S transfer. Also stops MCK generator. Triggering this task will cause the STOPPED event to be generated.
449             EventsRxptrUpdated   = 0x104, //The RXD.PTR register has been copied to internal double-buffers. When the I2S module is started and RX is enabled, this event will be generated for every RXTXD.MAXCNT words that are received on the SDIN pin.
450             EventsTxptrUpdated   = 0x114, //The TDX.PTR register has been copied to internal double-buffers. When the I2S module is started and TX is enabled, this event will be generated for every RXTXD.MAXCNT words that are sent on the SDOUT pin.
451             EventsStopped        = 0x108, //I2S transfer stopped.
452             InterruptEnable      = 0x300, //Enable or disable interrupt
453             InterruptEnableSet   = 0x304, //Enable interrupt
454             InterruptEnableClear = 0x308, //Disable interrupt
455             Enable               = 0x500, //Enable I2S module.
456             ConfigMode           = 0x504, //I2S mode.
457             ConfigRxEnable       = 0x508, //Reception (RX) enable.
458             ConfigTxEnable       = 0x50C, //Transmission (TX) enable.
459             ConfigMasterClockEnable      = 0x510, //Master clock generator enable.
460             ConfigMasterClockFrequency   = 0x514, //Master clock generator frequency.
461             ConfigRatio          = 0x518, //MCK / LRCK ratio.
462             ConfigSwidth         = 0x51C, //Sample width.
463             ConfigAlign          = 0x520, //Alignment of sample within a frame.
464             ConfigFormat         = 0x524, //Frame format.
465             ConfigChannels       = 0x528, //Enable channels.
466             RxdPointer           = 0x538, //Receive buffer RAM start address.
467             TxdPointer           = 0x540, //Transmit buffer RAM start address.
468             RxTxBufferSize       = 0x550, //Size of RXD and TXD buffers.
469             PinSelectMasterClock = 0x560, //Pin select for MCK signal.
470             PinSelectSCK         = 0x564, //Pin select for SCK signal.
471             PinSelectLRCK        = 0x568, //Pin select for LRCK signal.
472             PinSelectSDIN        = 0x56C, //Pin select for SDIN signal.
473             PinSelectSDOUT       = 0x570, //Pin select for SDOUT signal.
474         }
475 
476         private enum MasterLrClockRatio
477         {
478             X32  = 0,
479             X48  = 1,
480             X64  = 2,
481             X96  = 3,
482             X128 = 4,
483             X192 = 5,
484             X256 = 6,
485             X384 = 7,
486             X512 = 8,
487         }
488 
489         private enum SampleWidth
490         {
491             Sample8Bit  = 0,
492             Sample16Bit = 1,
493             Sample24Bit = 2,
494         }
495 
496         private enum Mode
497         {
498             Master = 0,
499             Slave  = 1,
500         }
501 
502         private enum Alignment
503         {
504             Left  = 0,
505             Right = 1,
506         }
507 
508         private enum Channels
509         {
510             Stereo = 0,
511             Left   = 1,
512             Right  = 2,
513         }
514 
515         private enum DataFormat
516         {
517             Standard      = 0,
518             LeftJustified = 1,
519         }
520 
521         private enum MasterClockFrequency
522         {
523             Mhz32Div8   = 0x20000000,
524             Mhz32Div10  = 0x18000000,
525             Mhz32Div11  = 0x16000000,
526             Mhz32Div15  = 0x11000000,
527             Mhz32Div16  = 0x10000000,
528             Mhz32Div21  = 0x0C000000,
529             Mhz32Div23  = 0x0B000000,
530             Mhz32Div30  = 0x08800000,
531             Mhz32Div31  = 0x08400000,
532             Mhz32Div32  = 0x08000000,
533             Mhz32Div42  = 0x06000000,
534             Mhz32Div63  = 0x04100000,
535             Mhz32Div125 = 0x020C0000,
536         }
537     }
538 }
539