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.Collections.ObjectModel;
11 using System.IO;
12 using System.Linq;
13 using Antmicro.Renode.Core;
14 using Antmicro.Renode.Core.Structure.Registers;
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 PULP_I2S: BasicDoubleWordPeripheral, IDisposable, IKnownSize, INumberedGPIOOutput
23     {
PULP_I2S(IMachine machine)24         public PULP_I2S(IMachine machine) : base(machine)
25         {
26             CreateRegisters();
27             var irqs = new Dictionary<int, IGPIO>();
28             irqs[(int)Events.Rx] = new GPIO();
29             irqs[(int)Events.Tx] = new GPIO();
30             irqs[(int)Events.Extra] = new GPIO();
31             Connections = new ReadOnlyDictionary<int, IGPIO>(irqs);
32             Reset();
33         }
34 
Reset()35         public override void Reset()
36         {
37             base.Reset();
38             decoder?.Reset();
39             encoder?.FlushBuffer();
40 
41             rxThread = null;
42             txThread = null;
43             rxChannels = 1;
44             txChannels = 1;
45             rxSampleWidth = 16;
46             txSampleWidth = 16;
47 
48             RxSampleFrequency = 0;
49             TxSampleFrequency = 0;
50 
51             InputFile = "";
52             OutputFile = "";
53         }
54 
Dispose()55         public void Dispose()
56         {
57             encoder?.Dispose();
58         }
59 
60         public string InputFile { get; set; }
61         public string OutputFile { get; set; }
62         public uint RxSampleFrequency { get; set; }
63         public uint TxSampleFrequency { get; set; }
64 
65         public IReadOnlyDictionary<int, IGPIO> Connections { get; }
66 
67         public long Size => 0x80;
68 
StartRx()69         private void StartRx()
70         {
71             // All three flags must be set to start the acquisition, as they are stored in 3 separate registers this function will be called two times before we are ready to start
72             if(rxEnabled.Value == false || slaveEnabled.Value == false || slaveClockEnabled.Value == false)
73             {
74                 this.Log(LogLevel.Debug,
75                          @"Reception has not been started - it needs I2S_RX_CFG.ENABLE, I2S_SLV_SETUP.SLAVE_EN and I2S_CLKCFG_SETUP.SLAVE_CLK_EN to be set.
76                          Current state: I2S_RX_CFG.ENABLE             = {0},
77                                         I2S_SLV_SETUP.SLAVE_EN        = {1},
78                                         I2S_CLKCFG_SETUP.SLAVE_CLK_EN = {2}",
79                          rxEnabled.Value, slaveEnabled.Value, slaveClockEnabled.Value);
80                 return;
81             }
82 
83             if(InputFile == "")
84             {
85                 this.Log(LogLevel.Error, "Starting reception without an input file! Aborting");
86                 return;
87             }
88             decoder = new PCMDecoder(rxSampleWidth, RxSampleFrequency, rxChannels, false, this);
89             decoder.LoadFile(InputFile);
90 
91             this.Log(LogLevel.Debug, "Starting reception");
92             if(rxContinuous.Value)
93             {
94                 if(RxSampleFrequency == 0)
95                 {
96                     this.Log(LogLevel.Error, "Sampling frequency not set. Aborting continuous reception");
97                     return;
98                 }
99                 rxThread = machine.ObtainManagedThread(InputFrames, RxSampleFrequency);
100                 rxThread.Start();
101             }
102             else
103             {
104                 InputFrames();
105             }
106         }
107 
StartTx()108         private void StartTx()
109         {
110             // All three flags must be set to start the transmission, as they are stored in 3 separate registers this function will be called two times before we are ready to start
111             if(txEnabled.Value == false || masterEnabled.Value == false || masterClockEnabled.Value == false)
112             {
113                 this.Log(LogLevel.Debug,
114                          @"Transmission has not been started - it needs I2S_TX_CFG.ENABLE, I2S_MST_SETUP.MASTER_EN and I2S_CLKCFG_SETUP.MASTER_CLK_EN to be set.
115                          Current state: I2S_TX_CFG.ENABLE              = {0},
116                                         I2S_MST_SETUP.MASTER_EN        = {1},
117                                         I2S_CLKCFG_SETUP.MASTER_CLK_EN = {2}",
118                          txEnabled.Value, masterEnabled.Value, masterClockEnabled.Value);
119                 return;
120             }
121 
122             if(OutputFile == "")
123             {
124                 this.Log(LogLevel.Error, "Starting reception without an output file! Aborting");
125                 return;
126             }
127 
128             encoder = new PCMEncoder(txSampleWidth, TxSampleFrequency, txChannels, false);
129             // Write samples after reading whole buffer, rather than performing a write after receiving every single one
130             encoder.SetBufferingBySamplesCount((uint)txBufferSize.Value / (txSampleWidth / 8));
131             encoder.Output = OutputFile;
132 
133             this.Log(LogLevel.Debug, "Starting transmission");
134             if(txContinuous.Value)
135             {
136                 if(TxSampleFrequency == 0)
137                 {
138                     this.Log(LogLevel.Error, "Sampling frequency not set. Aborting continuous transmission");
139                     return;
140                 }
141                 txThread = machine.ObtainManagedThread(OutputFrames, TxSampleFrequency);
142                 txThread.Start();
143             }
144             else
145             {
146                 OutputFrames();
147             }
148         }
149 
StopThread(ref IManagedThread thread)150         private void StopThread(ref IManagedThread thread)
151         {
152             thread?.Stop();
153             thread = null;
154         }
155 
OutputFrames()156         private void OutputFrames()
157         {
158             var bufferPointerBackup = txBufferPointer.Value;
159             var bufferSizeBackup = txBufferSize.Value;
160 
161             var bufferStep = txSampleWidth / 8;
162             uint sample;
163             while(txBufferSize.Value > 0)
164             {
165                 if(txBufferSize.Value < bufferStep)
166                 {
167                     this.Log(LogLevel.Warning, "The I2S_TX_SIZE ({0} bytes) is misaligned to I2S_TX_CFG.DATASIZE ({1} bytes). This model will read bits form outside of the buffer, expect that your program will fail soon.",
168                              bufferSizeBackup, bufferStep);
169                 }
170 
171                 switch(txSampleWidth)
172                 {
173                     case 8:
174                         sample = sysbus.ReadByte(txBufferPointer.Value);
175                         break;
176                     case 16:
177                         sample = sysbus.ReadWord(txBufferPointer.Value);
178                         break;
179                     case 32:
180                         sample = sysbus.ReadDoubleWord(txBufferPointer.Value);
181                         break;
182                     default:
183                         throw new ArgumentException(String.Format("Invalid TX sample width: {}", txSampleWidth));
184                 }
185 
186                 encoder.AcceptSample(sample);
187                 // In case of some interrupt with higher priority we make sure we have buffer pointer and remaining buffer size updated
188                 txBufferPointer.Value += bufferStep;
189                 txBufferSize.Value -= bufferStep;
190             }
191 
192             Connections[(int)Events.Tx].Blink();
193             //  At the end of the buffer the uDMA reloads the address and size and starts a new transfer
194             if(txContinuous.Value)
195             {
196                 txBufferPointer.Value = bufferPointerBackup;
197                 txBufferSize.Value = bufferSizeBackup;
198             }
199         }
200 
InputFrames()201         private void InputFrames()
202         {
203             var bufferPointerBackup = rxBufferPointer.Value;
204             var bufferSizeBackup = rxBufferSize.Value;
205 
206             var samplesPerDoubleWord = 32 / rxSampleWidth;
207 
208             while(rxBufferSize.Value > 0)
209             {
210                 uint temp = 0;
211                 for(int i = 0; i < samplesPerDoubleWord; i++)
212                 {
213                     temp |= decoder.GetSingleSample() << (int)(rxSampleWidth * i);
214                 }
215 
216                 if(rxBufferSize.Value < 4)
217                 {
218                     // Handle buffer unaligned to double word
219                     var bitsLeft = (int)(8 * rxBufferSize.Value);
220                     BitHelper.ReplaceBits(ref temp, sysbus.ReadDoubleWord(rxBufferPointer.Value), 32 - bitsLeft, bitsLeft, bitsLeft);
221                 }
222                 sysbus.WriteDoubleWord(rxBufferPointer.Value, temp);
223                 // In case of some interrupt with higher priority we make sure we have buffer pointer and remaining buffer size updated
224                 rxBufferPointer.Value += 4;
225                 rxBufferSize.Value -= 4;
226              }
227 
228             Connections[(int)Events.Rx].Blink();
229             //  At the end of the buffer the uDMA reloads the address and size and starts a new transfer
230             if(rxContinuous.Value)
231             {
232                 rxBufferPointer.Value = bufferPointerBackup;
233                 rxBufferSize.Value = bufferSizeBackup;
234             }
235         }
236 
CreateRegisters()237         private void CreateRegisters()
238         {
239             Registers.RxBufferPointer.Define(this)
240                 .WithValueField(0, 16, out rxBufferPointer, name: "RX_SADDR")
241                 .WithReservedBits(16, 16);
242             Registers.RxBufferSize.Define(this)
243                 .WithValueField(0, 17, out rxBufferSize, name: "RX_SIZE")
244                 .WithReservedBits(17, 15);
245             Registers.RxConfig.Define(this, 0x4)
246                 .WithFlag(0, out rxContinuous, name: "CONTINOUS")
247                 .WithValueField(1, 2, out rxDataSize,
248                     writeCallback: (_, val) => {
249                         // b00 (8 bits)
250                         // b01 (16 bits)
251                         // b10 (32 bits)
252                         if(val > 2)
253                         {
254                             this.Log(LogLevel.Warning, "Trying to set forbidden RX DataSize. Setting to default");
255                             val = 0b10;
256                         }
257                         rxSampleWidth = (uint)(8 << (int)val);
258                     },
259                     name: "DATASIZE")
260                 .WithReservedBits(3, 1)
261                 .WithFlag(4, out rxEnabled,
262                     writeCallback: (_, val) => { if(val) StartRx(); },
263                     name: "EN")
264                 //The queue flag is not implemented as transfer is completed instantly
265                 .WithFlag(5,
266                     writeCallback: (_, val) => { if(val) StopThread(ref rxThread); },
267                     name: "CLR/PENDING")
268                 .WithReservedBits(6, 26);
269             Registers.RxInit.Define(this)
270                 // The documentation defines no fields in this register
271                 .WithReservedBits(0, 32);
272             Registers.TxBufferPointer.Define(this)
273                 .WithValueField(0, 16, out txBufferPointer, name: "TX_SADDR")
274                 .WithReservedBits(16, 16);
275             Registers.TxBufferSize.Define(this)
276                 .WithValueField(0, 17, out txBufferSize, name: "TX_SIZE")
277                 .WithReservedBits(17, 15);
278             Registers.TxConfig.Define(this, 0x4)
279                 .WithFlag(0, out txContinuous, name: "CONTINOUS")
280                 .WithValueField(1, 2, out txDataSize,
281                     writeCallback: (_, val) => {
282                         // b00 (8 bits)
283                         // b01 (16 bits)
284                         // b10 (32 bits)
285                         if(val > 2)
286                         {
287                             this.Log(LogLevel.Warning, "Trying to set forbidden TX DataSize. Setting to default");
288                             val = 0b10;
289                         }
290                     txSampleWidth = (uint)(8 << (int)val);
291                     }, name: "DATASIZE")
292                 .WithReservedBits(3, 1)
293                 .WithFlag(4, out txEnabled,
294                     writeCallback: (_, val) => { if(val) StartTx(); },
295                     name: "EN")
296                 .WithFlag(5,
297                     writeCallback: (_, val) => { if(val) StopThread(ref txThread); },
298                     name: "CLR/PENDING")
299                 .WithReservedBits(6, 26);
300             Registers.TxInit.Define(this)
301                 // The documentation defines no fields in this register
302                 .WithReservedBits(0, 32);
303             Registers.ClockConfiguration.Define(this)
304                 .WithTag("MASTER_CLK_DIV", 0, 8)
305                 .WithTag("SLAVE_CLK_DIV", 8, 8)
306                 .WithTag("COMMON_CLK_DIV", 16, 8)
307                 .WithFlag(24, out slaveClockEnabled, writeCallback: (_, val) => { if(val) StartRx(); }, name: "SLAVE_CLK_EN")
308                 .WithFlag(25, out masterClockEnabled, writeCallback: (_, val) => { if(val) StartTx(); }, name: "MASTER_CLK_EN")
309                 .WithFlag(26, out pdmClockEnabled, name: "PDM_CLK_EN")
310                 .WithTag("SLAVE_EXT", 28, 1)
311                 .WithTag("SLAVE_NUM", 29, 1)
312                 .WithTag("MASTER_EXT", 30, 1)
313                 .WithTag("MASTER_NUM", 31, 1);
314             Registers.SlaveSettings.Define(this)
315                 .WithTag("SLAVE_WORDS", 0, 3)
316                 .WithReservedBits(3, 5)
317                 .WithTag("SLAVE_BITS", 8, 5)
318                 .WithReservedBits(13, 3)
319                 .WithTag("SLAVE_LSB", 16, 1)
320                 .WithFlag(17, writeCallback: (_, val) => { rxChannels = val ? 2u : 1u; }, name: "SLAVE_2CH")
321                 .WithReservedBits(18, 13)
322                 .WithFlag(31, out slaveEnabled, writeCallback: (_, val) => { if(val) StartRx(); }, name: "SLAVE_EN");
323             Registers.MasterSettings.Define(this)
324                 .WithTag("MASTER_WORDS", 0, 3)
325                 .WithReservedBits(3, 5)
326                 .WithTag("MASTER_BITS", 8, 5)
327                 .WithReservedBits(13, 3)
328                 .WithTag("MASTER_LSB", 16, 1)
329                 .WithFlag(17, writeCallback: (_, val) => { txChannels = val ? 2u : 1u; }, name: "MASTER_2CH")
330                 .WithReservedBits(18, 13)
331                 .WithFlag(31, out masterEnabled, writeCallback: (_, val) => { if(val) StartTx(); }, name:"MASTER_EN");
332             Registers.PdmConfig.Define(this)
333                 .WithTag("PDM_SHIFT", 0, 3)
334                 .WithTag("PDM_DECIMATION", 3, 10)
335                 .WithTag("PDM_MODE", 13, 2)
336                 .WithReservedBits(15, 16)
337                 .WithTag("PDM_EN", 31, 1);
338         }
339 
340         private IFlagRegisterField masterClockEnabled;
341         private IFlagRegisterField masterEnabled;
342         private IFlagRegisterField pdmClockEnabled;
343         private IFlagRegisterField slaveClockEnabled;
344         private IFlagRegisterField slaveEnabled;
345         private IFlagRegisterField rxContinuous;
346         private IFlagRegisterField rxEnabled;
347         private IFlagRegisterField txContinuous;
348         private IFlagRegisterField txEnabled;
349 
350         private IValueRegisterField rxBufferPointer;
351         private IValueRegisterField rxBufferSize;
352         private IValueRegisterField rxDataSize;
353         private IValueRegisterField txBufferPointer;
354         private IValueRegisterField txBufferSize;
355         private IValueRegisterField txDataSize;
356 
357         private uint rxChannels;
358         private uint rxSampleWidth;
359         private uint txChannels;
360         private uint txSampleWidth;
361 
362         private IManagedThread rxThread;
363         private IManagedThread txThread;
364         private PCMDecoder decoder;
365         private PCMEncoder encoder;
366 
367         private enum Events
368         {
369             Rx = 0,
370             Tx = 1,
371             Extra = 2,
372         }
373 
374         private enum Registers :long
375         {
376             RxBufferPointer    = 0x0,  //    RX Channel 0 I2S uDMA transfer address of associated buffer
377             RxBufferSize       = 0x4,  //    RX Channel 0 I2S uDMA transfer size of buffer
378             RxConfig           = 0x8,  //    RX Channel 0 I2S uDMA transfer configuration
379             RxInit             = 0xC,  //
380             TxBufferPointer    = 0x10, //    TX Channel I2S uDMA transfer address of associated buffer
381             TxBufferSize       = 0x14, //    TX Channel I2S uDMA transfer size of buffer
382             TxConfig           = 0x18, //    TX Channel I2S uDMA transfer configuration
383             TxInit             = 0x1C, //
384             ClockConfiguration = 0x20, //    Clock configuration for both master, slave and pdm
385             SlaveSettings      = 0x24, //    Configuration of I2S slave
386             MasterSettings     = 0x28, //    Configuration of I2S master
387             PdmConfig          = 0x2C, //    Configuration of PDM module
388         }
389     }
390 }
391