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 using System;
8 using System.Linq;
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Peripherals.Bus;
13 using Antmicro.Renode.Utilities;
14 
15 namespace Antmicro.Renode.Peripherals.Sound
16 {
17     public class EOSS3_Voice : BasicDoubleWordPeripheral, IKnownSize
18     {
EOSS3_Voice(IMachine machine)19         public EOSS3_Voice(IMachine machine) : base(machine)
20         {
21             CreateRegisters();
22             IRQ = new GPIO();
23             Reset();
24         }
25 
Reset()26         public override void Reset()
27         {
28             base.Reset();
29             decoderLeft?.Reset();
30             decoderRight?.Reset();
31             sampleThread?.Dispose();
32             sampleThread = null;
33             inputFileLeft = null;
34             inputFileRight = null;
35             numberOfChannels = 1;
36             IRQ.Unset();
37         }
38 
SetInputFile(string fileName, Channel channel = Channel.Left, int repeat = 1)39         public void SetInputFile(string fileName, Channel channel = Channel.Left, int repeat = 1)
40         {
41             switch(channel)
42             {
43                 case Channel.Left:
44                     {
45                         if(decoderLeft == null)
46                         {
47                             decoderLeft = new PCMDecoder(16, 16000, 1, false, this);
48                         }
49 
50                         for(var i = 0; i < repeat; i++)
51                         {
52                             decoderLeft.LoadFile(fileName);
53                         }
54                         inputFileLeft = fileName;
55                     }
56                     break;
57 
58                 case Channel.Right:
59                     {
60                         if(decoderRight == null)
61                         {
62                             decoderRight = new PCMDecoder(16, 16000, 1, false, this);
63                         }
64 
65                         for(var i = 0; i < repeat; i++)
66                         {
67                             decoderRight.LoadFile(fileName);
68                         }
69                         inputFileRight = fileName;
70                     }
71                     break;
72             }
73         }
74 
75         public GPIO IRQ { get; }
76 
77         public long Size => 0x1000;
78 
79         public enum Channel
80         {
81             Left = 0,
82             Right = 1,
83         }
84 
Start()85         private void Start()
86         {
87             if(!enable.Value)
88             {
89                 this.Log(LogLevel.Warning, "Trying to start samples aquisition before enabling peripheral. Will not start");
90                 return;
91             }
92 
93             if(inputFileLeft == null || (numberOfChannels == 2 && inputFileRight == null))
94             {
95                 this.Log(LogLevel.Error, "Trying to start reception with not enough input files - please set input using `SetinputFile`. Aborting.");
96                 return;
97             }
98 
99             StartPDMThread();
100         }
101 
Stop()102         private void Stop()
103         {
104             StopPDMThread();
105             this.Log(LogLevel.Debug, "Event Stopped");
106         }
107 
StartPDMThread()108         private void StartPDMThread()
109         {
110             StopPDMThread();
111             sampleThread = machine.ObtainManagedThread(InputSamples, 1);
112             sampleThread.Start();
113         }
114 
StopPDMThread()115         private void StopPDMThread()
116         {
117             if(sampleThread == null)
118             {
119                 return;
120             }
121             sampleThread.Stop();
122             sampleThread.Dispose();
123             sampleThread = null;
124         }
125 
InputSamples()126         private void InputSamples()
127         {
128             var samplesCount = (uint)bufferTransferLength.Value * 2; // samples are 16bit and the register indicates the amount of 32bit words
129             var preparedDoubleWords = new uint[samplesCount / 2];
130 
131             switch(numberOfChannels)
132             {
133                 case 1:
134                     var samples = decoderLeft.GetSamplesByCount(samplesCount);
135 
136                     var index = 1u;
137                     ushort prev = 0;
138 
139                     foreach(ushort sample in samples)
140                     {
141                         if(index % 2 != 0)
142                         {
143                             prev = sample;
144                         }
145                         else
146                         {
147                             // Assuming input file format of s16le
148                             preparedDoubleWords[(index / 2) - 1] = (uint)((Misc.SwapBytesUShort(sample) << 16) | Misc.SwapBytesUShort(prev));
149                         }
150                         index++;
151                     }
152 
153                     if(index % 2 == 0)
154                     {
155                         // One sample left
156                         preparedDoubleWords[(index / 2) - 1] = prev;
157                     }
158 
159                     break;
160                 case 2:
161                     var samplesLeft = decoderLeft.GetSamplesByCount(samplesCount / 2).ToArray();
162                     var samplesRight = decoderRight.GetSamplesByCount(samplesCount / 2).ToArray();
163 
164                     if(samplesLeft.Length != samplesRight.Length)
165                     {
166                         // Make sure arrays have equal size
167                         var neededSize = Math.Max(samplesLeft.Length, samplesRight.Length);
168                         Array.Resize(ref samplesLeft, neededSize);
169                         Array.Resize(ref samplesRight, neededSize);
170                     }
171 
172                     for(var i = 0; i < samplesLeft.Length; i++)
173                     {
174                         var right = (uint)Misc.SwapBytesUShort((ushort)samplesRight[i]);
175                         var left = (uint)Misc.SwapBytesUShort((ushort)samplesLeft[i]);
176 
177                         preparedDoubleWords[i] = (right << 16) | left;
178                     }
179                     break;
180             }
181 
182             var data = new byte[preparedDoubleWords.Length * 4];
183             System.Buffer.BlockCopy(preparedDoubleWords, 0, data, 0, preparedDoubleWords.Length * 4);
184             sysbus.WriteBytes(data, dmac0DestAddr.Value);
185 
186             IRQ.Blink();
187         }
188 
CreateRegisters()189         private void CreateRegisters()
190         {
191             Registers.VoiceConfig.Define(this)
192                 .WithTag("DMIC_SEL", 0, 1)
193                 .WithTag("LPSD_SEL", 1, 1)
194                 .WithTag("MODE_SEL", 2, 1)
195                 .WithTag("MONO_CHN_SEL", 3, 1)
196                 .WithTag("I2S_DS_SEL", 4, 1)
197                 .WithTag("PDM_VOICE_SCENARIO", 5, 3)
198                 .WithTag("PDM_MIC_SWITCH_TO_AP", 8, 1)
199                 .WithTag("LPSD_USE_DC_BLOCK", 9, 1)
200                 .WithTag("LPSD_MUX", 10, 1)
201                 .WithTag("LPSD_NO", 11, 1)
202                 .WithTag("I2S_PGA_EN", 12, 1)
203                 .WithReservedBits(13, 2)
204                 .WithTag("DIV_AP", 15, 3)
205                 .WithTag("DIV_WD", 18, 6)
206                 .WithTag("FIFO_0_CLEAR", 24, 1)
207                 .WithTag("FIFO_1_CLEAR", 25, 1)
208                 .WithTag("LPSD_VOICE_DETECTED_MASK", 26, 1)
209                 .WithTag("DMIC_VOICE_DETECTED_MASK", 27, 1)
210                 .WithTag("DMAC_BLK_DONE_MASK", 28, 1)
211                 .WithTag("DMAC_BUF_DONE_MASK", 29, 1)
212                 .WithTag("AP_PDM_CLK_ON_MASK", 30, 1)
213                 .WithTag("AP_PDM_CLK_OFF_MASK", 31, 1);
214 
215             Registers.LPSDConfig.Define(this)
216                 .WithTag("LPSD_THD", 0, 16)
217                 .WithTag("LPSD_RATIO_STOP", 16, 8)
218                 .WithTag("LPSD_RATIO_RUN", 24, 8);
219 
220             Registers.VoiceDMACConfig.Define(this)
221                 .WithFlag(0, out enable, name: "DMAC_EN")
222                 .WithFlag(1, FieldMode.Write, writeCallback: (_, val) =>
223                     {
224                         if(!val)
225                         {
226                             return;
227                         }
228                         Start();
229                     }, name: "DMAC_START")
230                 .WithFlag(2, writeCallback: (_, val) =>
231                     {
232                         if(!val)
233                         {
234                             return;
235                         }
236                         Stop();
237                     }, name: "DMAC_STOP")
238                 .WithTag("AHB_RDY", 3, 1)
239                 .WithTag("AHB_BURST_LENGTH", 4, 2)
240                 .WithTag("PINGPONG_MODE", 6, 1)
241                 .WithTag("STEREO_DUAL_BUF_MODE", 7, 1)
242                 .WithTag("VOICE_DMAC_BURST_SPD", 8, 8)
243                 .WithReservedBits(16, 16);
244 
245             Registers.VoiceDMACLength.Define(this)
246                 .WithValueField(0, 16, out blockTransferLength, name: "DMAC_BLK_LEN")
247                 .WithValueField(16, 16, out bufferTransferLength, name: "DMAC_BUF_LEN");
248 
249             Registers.VoiceDMACFifo.Define(this)
250                 .WithReservedBits(0, 16)
251                 .WithTag("DMAC_BUF_OFFSET", 16, 16);
252 
253             Registers.VoiceDMACDestinationAddress0.Define(this)
254                 .WithValueField(0, 32, out dmac0DestAddr, name: "VOICE_DMAC_DST_ADDR0");
255 
256             Registers.VoiceDMACDestinationAddress1.Define(this)
257                 .WithTag("VOICE_DMAC_DST_ADDR1", 0, 32);
258 
259             Registers.PDMCoreConfig.Define(this)
260                 .WithTag("PDM_CORE_EN", 0, 1)
261                 .WithTag("SOFT_MUTE", 1, 1)
262                 .WithTag("DIV_MODE", 2, 1)
263                 .WithTag("S_CYCLES", 3, 3)
264                 .WithTag("HP_GAIN", 6, 4)
265                 .WithTag("ADCHPD", 10, 1)
266                 .WithTag("M_CLK_DIV", 11, 2)
267                 .WithTag("SINC_RATE", 13, 7)
268                 .WithTag("PGA_L", 20, 5)
269                 .WithTag("PGA_R", 25, 5)
270                 .WithTag("DMICK_DLY", 30, 1)
271                 .WithTag("DIV_WD_MODE", 31, 1);
272 
273             Registers.VoiceStatus.Define(this)
274                 .WithTag("FIFO_0A_EMPTY", 0, 1)
275                 .WithTag("FIFO_0A_FULL", 1, 1)
276                 .WithTag("FIFO_0A_OVERFLOW", 2, 1)
277                 .WithReservedBits(3, 1)
278                 .WithTag("FIFO_0B_EMPTY", 4, 1)
279                 .WithTag("FIFO_0B_FULL", 5, 1)
280                 .WithTag("FIFO_0B_OVERFLOW", 6, 1)
281                 .WithReservedBits(7, 1)
282                 .WithTag("FIFO_1A_EMPTY", 8, 1)
283                 .WithTag("FIFO_1A_FULL", 9, 1)
284                 .WithTag("FIFO_1A_OVERFLOW", 10, 1)
285                 .WithReservedBits(11, 1)
286                 .WithTag("FIFO_1B_EMPTY", 12, 1)
287                 .WithTag("FIFO_1B_FULL", 13, 1)
288                 .WithTag("FIFO_1B_OVERFLOW", 14, 1)
289                 .WithReservedBits(15, 1)
290                 .WithTag("DMIC_VOICE_DETECTED_REG", 16, 1)
291                 .WithTag("LPSD_VOICE_DETECTED_REG", 17, 1)
292                 .WithTag("AP_PDM_CLK_OFF_REG", 18, 1)
293                 .WithTag("AP_PDM_CLK_ON_REG", 19, 1)
294                 .WithTag("DMAC1_BUF_DONE_REG", 20, 1)
295                 .WithTag("DMAC1_BLK_DONE_REG", 21, 1)
296                 .WithTag("DMAC0_BUF_DONE_REG", 22, 1)
297                 .WithTag("DMAC0_BLK_DONE_REG", 23, 1)
298                 .WithReservedBits(24, 8);
299 
300             Registers.I2SConfig.Define(this)
301                 .WithTag("I2S_LRCDIV", 0, 12)
302                 .WithTag("I2S_BCLKDIV", 12, 6)
303                 .WithTag("I2S_CLK_INV", 18, 1)
304                 .WithTag("I2S_IWL", 19, 2)
305                 .WithReservedBits(21, 11);
306 
307             Registers.FifoSRAMConfig.Define(this)
308                 .WithTag("SRAM_0A_TEST1", 0, 1)
309                 .WithTag("SRAM_0A_RME", 1, 1)
310                 .WithTag("SRAM_0A_RM", 2, 4)
311                 .WithTag("SRAM_0B_TEST1", 6, 1)
312                 .WithTag("SRAM_0B_RME", 7, 1)
313                 .WithTag("SRAM_0B_RM", 8, 4)
314                 .WithTag("SRAM_1A_TEST1", 12, 1)
315                 .WithTag("SRAM_1A_RME", 13, 1)
316                 .WithTag("SRAM_1A_RM", 14, 4)
317                 .WithTag("SRAM_1B_TEST1", 18, 1)
318                 .WithTag("SRAM_1B_RME", 19, 1)
319                 .WithTag("SRAM_1B_RM", 20, 4)
320                 .WithReservedBits(24, 8);
321 
322             Registers.PDMSRAMConfig.Define(this)
323                 .WithTag("PDM_SRAM_L_TEST1", 0, 1)
324                 .WithTag("PDM_SRAM_L_RME", 1, 1)
325                 .WithTag("PDM_SRAM_L_RM", 2, 4)
326                 .WithTag("PDM_SRAM_R_TEST1", 6, 1)
327                 .WithTag("PDM_SRAM_R_RME", 7, 1)
328                 .WithTag("PDM_SRAM_R_RM", 8, 4)
329                 .WithReservedBits(12, 20);
330 
331             Registers.DebugMUXConfig.Define(this)
332                 .WithTag("DBG_MUX_CFG", 0, 32);
333         }
334 
335         private uint numberOfChannels;
336         private string inputFileLeft;
337         private string inputFileRight;
338 
339         private PCMDecoder decoderLeft;
340         private PCMDecoder decoderRight;
341         private IManagedThread sampleThread;
342 
343         private IFlagRegisterField enable;
344         private IValueRegisterField blockTransferLength;
345         private IValueRegisterField bufferTransferLength;
346         private IValueRegisterField dmac0DestAddr;
347 
348         private enum Registers : long
349         {
350             VoiceConfig = 0x0,
351             LPSDConfig = 0x4,
352             VoiceDMACConfig = 0x8,
353             VoiceDMACLength = 0xC,
354             VoiceDMACFifo = 0x10,
355             VoiceDMACDestinationAddress0 = 0x14,
356             VoiceDMACDestinationAddress1 = 0x18,
357             PDMCoreConfig = 0x1C,
358             VoiceStatus = 0x20,
359             I2SConfig = 0x24,
360             FifoSRAMConfig = 0x28,
361             PDMSRAMConfig = 0x2C,
362             DebugMUXConfig = 0x30
363         }
364     }
365 }
366