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