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