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_PDM: BasicDoubleWordPeripheral, IKnownSize 23 { NRF52840_PDM(IMachine machine)24 public NRF52840_PDM(IMachine machine) : base(machine) 25 { 26 CreateRegisters(); 27 IRQ = new GPIO(); 28 Reset(); 29 } 30 Reset()31 public override void Reset() 32 { 33 base.Reset(); 34 IRQ.Unset(); 35 decoderLeft?.Reset(); 36 decoderRight?.Reset(); 37 sampleThread?.Dispose(); 38 sampleThread = null; 39 inputFileLeft = ""; 40 inputFileRight = ""; 41 multiplierL = 1.0; 42 multiplierR = 1.0; 43 numberOfChannels = 2; 44 sampleRatio = 64; 45 clockFrequency = 32000000 / 31; 46 SetSampleFrequency(); 47 } 48 SetInputFile(string fileName, Channel channel = Channel.Left, int repeat = 1)49 public void SetInputFile(string fileName, Channel channel = Channel.Left, int repeat = 1) 50 { 51 switch(channel) 52 { 53 case Channel.Left: 54 { 55 if(decoderLeft == null) 56 { 57 decoderLeft = new PCMDecoder(16, sampleFrequency, 1, false, this); 58 } 59 60 for(var i = 0; i < repeat; i++) 61 { 62 decoderLeft.LoadFile(fileName); 63 } 64 inputFileLeft = fileName; 65 } 66 break; 67 68 case Channel.Right: 69 { 70 if(decoderRight == null) 71 { 72 decoderRight = new PCMDecoder(16, sampleFrequency, 1, false, this); 73 } 74 75 for(var i = 0; i < repeat; i++) 76 { 77 decoderRight.LoadFile(fileName); 78 } 79 inputFileRight = fileName; 80 } 81 break; 82 } 83 } 84 85 public GPIO IRQ { get; } 86 87 public long Size => 0x1000; 88 89 public enum Channel 90 { 91 Left = 0, 92 Right = 1, 93 } 94 UpdateInterrupts()95 private void UpdateInterrupts() 96 { 97 var stopped = eventStopped.Value && intenStopped.Value; 98 var started = eventStarted.Value && intenStarted.Value; 99 var end = eventEnd.Value && intenEnd.Value; 100 IRQ.Set(stopped || started || end); 101 } 102 Start()103 private void Start() 104 { 105 if(!enablePDM.Value) 106 { 107 this.Log(LogLevel.Warning, "Trying to start samples aquisition before enabling peripheral. Will not start"); 108 return; 109 } 110 111 if(inputFileLeft == "" || (numberOfChannels == 2 && inputFileRight == "")) 112 { 113 this.Log(LogLevel.Error, "Trying to start reception with not enough input files - please set input using `SetinputFile`. Aborting."); 114 return; 115 } 116 117 StartPDMThread(); 118 } 119 Stop()120 private void Stop() 121 { 122 eventStopped.Value = true; 123 UpdateInterrupts(); 124 this.Log(LogLevel.Debug, "Event Stopped"); 125 } 126 StartPDMThread()127 private bool StartPDMThread() 128 { 129 StopPDMThread(); 130 131 if(maxSamplesCount.Value == 0) 132 { 133 // Crate stub ManagedThread just to make clear that it was started, but just send 134 // eventStarted Interrupt - software might configure proper value in the IRQ handler 135 sampleThread = machine.ObtainManagedThread(InputSamples, 1); 136 eventStarted.Value = true; 137 UpdateInterrupts(); 138 return false; 139 } 140 // Since we handle all samples in one go we have to calculate how often should we do it 141 var eventFrequency = (sampleFrequency / (int)(maxSamplesCount.Value)) * numberOfChannels; 142 sampleThread = machine.ObtainManagedThread(InputSamples, (uint)eventFrequency); 143 sampleThread.Start(); 144 return true; 145 } 146 StopPDMThread()147 private bool StopPDMThread() 148 { 149 if(sampleThread == null) 150 { 151 return false; 152 } 153 sampleThread.Stop(); 154 sampleThread.Dispose(); 155 sampleThread = null; 156 return true; 157 } 158 InputSamples()159 private void InputSamples() 160 { 161 var currentPointer = samplePtr.Value; 162 eventStarted.Value = true; 163 UpdateInterrupts(); 164 165 var samplesCount = (uint)maxSamplesCount.Value; 166 var doubleWordsCount = samplesCount / 2; 167 var preparedDoubleWords = new uint[doubleWordsCount]; 168 169 switch(numberOfChannels) 170 { 171 case 1: 172 var samples = decoderLeft.GetSamplesByCount(samplesCount); 173 174 var index = 1u; 175 ushort prev = 0; 176 177 foreach(ushort sample in samples) 178 { 179 if(index % 2 != 0) 180 { 181 prev = sample; 182 } 183 else 184 { 185 // Assuming input file format of s16le 186 preparedDoubleWords[(index / 2) - 1] = (uint)((Misc.SwapBytesUShort(sample) << 16) | Misc.SwapBytesUShort(prev)); 187 } 188 index++; 189 } 190 191 if(index % 2 == 0) 192 { 193 // One sample left 194 preparedDoubleWords[(index / 2) - 1] = prev; 195 } 196 197 break; 198 case 2: 199 var samplesLeft = decoderLeft.GetSamplesByCount(samplesCount / 2).ToArray(); 200 var samplesRight = decoderRight.GetSamplesByCount(samplesCount / 2).ToArray(); 201 202 if(samplesLeft.Length != samplesRight.Length) 203 { 204 // Make sure arrays have equal size 205 var neededSize = Math.Max(samplesLeft.Length, samplesRight.Length); 206 Array.Resize(ref samplesLeft, neededSize); 207 Array.Resize(ref samplesRight, neededSize); 208 } 209 210 if(invertChannels.Value) 211 { 212 Misc.Swap(ref samplesLeft, ref samplesRight); 213 } 214 215 for(var i = 0; i < samplesLeft.Length; i++) 216 { 217 var right = (uint)Misc.SwapBytesUShort((ushort)samplesRight[i]); 218 var left = (uint)Misc.SwapBytesUShort((ushort)samplesLeft[i]); 219 220 preparedDoubleWords[i] = (right << 16) | left; 221 } 222 break; 223 } 224 225 foreach(uint i in preparedDoubleWords) 226 { 227 sysbus.WriteDoubleWord(currentPointer, i); 228 currentPointer += 4; 229 } 230 231 eventEnd.Value = true; 232 UpdateInterrupts(); 233 } 234 SetGain(ulong val, Channel channel)235 private void SetGain(ulong val, Channel channel) 236 { 237 if(val > 80) 238 { 239 this.Log(LogLevel.Error, "Trying to set GAIN.{0} value higher than 80. Setting gain to default value.", channel); 240 val = 40; 241 } 242 var gain = ((int)val - 40) / 2; 243 this.Log(LogLevel.Debug, "{0} channel gain set to {1}db", channel, gain); 244 // Convert dB of amplitude to multiplier 245 var multiplier = Math.Pow(10, gain / 20.0); 246 switch(channel) 247 { 248 case Channel.Left: 249 multiplierL = multiplier; 250 break; 251 case Channel.Right: 252 multiplierR = multiplier; 253 break; 254 } 255 } 256 SetClockFrequency(ClockFrequency frequency)257 private void SetClockFrequency(ClockFrequency frequency) 258 { 259 switch(frequency) 260 { 261 case ClockFrequency.f1000K: 262 clockFrequency = 32000000 / 32; 263 break; 264 case ClockFrequency.Default: 265 clockFrequency = 32000000 / 31; 266 break; 267 case ClockFrequency.f1067K: 268 clockFrequency = 32000000 / 30; 269 break; 270 case ClockFrequency.f1231K: 271 clockFrequency = 32000000 / 26; 272 break; 273 case ClockFrequency.f1280K: 274 clockFrequency = 32000000 / 25; 275 break; 276 case ClockFrequency.f1333K: 277 clockFrequency = 32000000 / 24; 278 break; 279 default: 280 this.Log(LogLevel.Error, "Wrong PDMCLKCTRL value, settting to default value"); 281 goto case ClockFrequency.Default; 282 } 283 SetSampleFrequency(); 284 } 285 SetSampleFrequency()286 private void SetSampleFrequency() 287 { 288 sampleFrequency = clockFrequency / sampleRatio; 289 this.Log(LogLevel.Debug, "Clock frequency = {0}kHz; Sample ratio = {1}; Sample frequency set to {2}kHz", clockFrequency / 1000.0, sampleRatio, sampleFrequency / 1000.0); 290 } 291 CreateRegisters()292 private void CreateRegisters() 293 { 294 Registers.TasksStart.Define(this, 0x0) 295 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) Start(); }, name: "TASKS_START") 296 .WithReservedBits(1, 31); 297 Registers.TasksStop.Define(this, 0x0) 298 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) Stop(); }, name: "TASKS_STOP") 299 .WithReservedBits(1, 31); 300 Registers.EventsStarted.Define(this, 0x0) 301 .WithFlag(0, out eventStarted, changeCallback: (_, __) => UpdateInterrupts(), name: "EVENTS_STARTED") 302 .WithReservedBits(1, 31); 303 Registers.EventsStopped.Define(this, 0x0) 304 .WithFlag(0, out eventStopped, changeCallback: (_, __) => UpdateInterrupts(), name: "EVENTS_STOPPED") 305 .WithReservedBits(1, 31); 306 Registers.EventsEnd.Define(this, 0x0) 307 .WithFlag(0, out eventEnd, changeCallback: (_, __) => UpdateInterrupts(), name: "EVENTS_END") 308 .WithReservedBits(1, 31); 309 Registers.InterruptEnable.Define(this, 0x0) 310 .WithFlag(0, out intenStarted, name: "STARTED") 311 .WithFlag(1, out intenStopped, name: "STOPPED") 312 .WithFlag(2, out intenEnd, name: "END") 313 .WithReservedBits(3, 29) 314 .WithWriteCallback((_, __) => UpdateInterrupts()); 315 Registers.InterruptEnableSet.Define(this, 0x0) 316 .WithFlag(0, 317 writeCallback: (_, val) => { intenStarted.Value |= val; }, 318 valueProviderCallback: (_) => { return intenStarted.Value; }, 319 name: "STARTED") 320 .WithFlag(1, 321 writeCallback: (_, val) => { intenStopped.Value |= val; }, 322 valueProviderCallback: (_) => { return intenStopped.Value; }, 323 name: "STOPPED") 324 .WithFlag(2, 325 writeCallback: (_, val) => { intenEnd.Value |= val; }, 326 valueProviderCallback: (_) => { return intenEnd.Value; }, 327 name: "END") 328 .WithReservedBits(3, 29) 329 .WithWriteCallback((_, __) => UpdateInterrupts()); 330 Registers.InterruptEnableClear.Define(this, 0x0) 331 .WithFlag(0, 332 writeCallback: (_, val) => { intenStarted.Value &= !val; }, 333 valueProviderCallback: (_) => { return intenStarted.Value; }, 334 name: "STARTED") 335 .WithFlag(1, 336 writeCallback: (_, val) => { intenStopped.Value &= !val; }, 337 valueProviderCallback: (_) => { return intenStopped.Value; }, 338 name: "STOPPED") 339 .WithFlag(2, 340 writeCallback: (_, val) => { intenEnd.Value &= !val; }, 341 valueProviderCallback: (_) => { return intenEnd.Value; }, 342 name: "END") 343 .WithReservedBits(3, 29) 344 .WithWriteCallback((_, __) => UpdateInterrupts()); 345 Registers.Enable.Define(this, 0x0) 346 .WithFlag(0, out enablePDM, name: "ENABLE") 347 .WithReservedBits(1, 31); 348 Registers.PdmClockControl.Define(this, 0x08400000) 349 .WithValueField(0, 32, 350 writeCallback: (_, val) => SetClockFrequency((ClockFrequency)val), name: "FREQ"); 351 Registers.Mode.Define(this, 0x0) 352 .WithValueField(0, 1, out operationMode, 353 writeCallback: (_, val) => 354 { 355 switch((OperationMode)val) 356 { 357 case OperationMode.Stereo: 358 numberOfChannels = 2; 359 break; 360 case OperationMode.Mono: 361 numberOfChannels = 1; 362 break; 363 } 364 this.Log(LogLevel.Debug, "MODE.OPERATION set to {0}", (OperationMode)val); 365 }, 366 name: "OPERATION") 367 .WithFlag(1, out invertChannels, name:"EDGE") 368 .WithReservedBits(2, 30); 369 Registers.Ratio.Define(this, 0x0) 370 .WithValueField(0, 1, 371 writeCallback: (_, val) => 372 { 373 switch((Ratio)val) 374 { 375 case Ratio.x64: 376 sampleRatio = 64; 377 break; 378 case Ratio.x80: 379 sampleRatio = 80; 380 break; 381 } 382 SetSampleFrequency(); 383 }, 384 name:"RATIO") 385 .WithReservedBits(1, 31); 386 Registers.GainLeft.Define(this, 0x28) 387 .WithValueField(0, 7, 388 writeCallback: (_, val) => SetGain(val, Channel.Left), name: "GAINL") 389 .WithReservedBits(8, 24); 390 Registers.GainRigth.Define(this, 0x28) 391 .WithValueField(0, 7, writeCallback: (_, val) => SetGain(val, Channel.Right), name: "GAINR") 392 .WithReservedBits(8, 24); 393 Registers.SamplePointer.Define(this, 0x0) 394 .WithValueField(0, 32, out samplePtr, name: "PTR"); 395 Registers.SampleBufferSize.Define(this, 0x0) 396 .WithValueField(0, 15, out maxSamplesCount, 397 writeCallback: (oldval, val) => 398 { 399 if(oldval != val && sampleThread != null) 400 { 401 // Need to restart thread to change how often it fires 402 this.Log(LogLevel.Debug, "Setting MaxSampleCount to {0}", val); 403 StartPDMThread(); 404 } 405 }, 406 name: "MAXCNT") 407 .WithReservedBits(15, 17); 408 Registers.PinSelectClk.Define(this, 0xFFFFFFFF) 409 .WithTaggedFlag("PIN", 0) 410 .WithTaggedFlag("PORT", 5) 411 .WithReservedBits(6, 25) 412 .WithTaggedFlag("CONNECT", 31); 413 Registers.PinSelectDin.Define(this, 0xFFFFFFFF) 414 .WithTaggedFlag("PIN", 0) 415 .WithTaggedFlag("PORT", 5) 416 .WithReservedBits(6, 25) 417 .WithTaggedFlag("CONNECT", 31); 418 } 419 420 private uint clockFrequency; 421 private uint numberOfChannels; 422 private uint sampleFrequency; 423 private uint sampleRatio; 424 private double multiplierL; 425 private double multiplierR; 426 private string inputFileLeft; 427 private string inputFileRight; 428 429 private PCMDecoder decoderLeft; 430 private PCMDecoder decoderRight; 431 private IManagedThread sampleThread; 432 433 private IFlagRegisterField enablePDM; 434 private IFlagRegisterField eventEnd; 435 private IFlagRegisterField eventStarted; 436 private IFlagRegisterField eventStopped; 437 private IFlagRegisterField intenEnd; 438 private IFlagRegisterField intenStarted; 439 private IFlagRegisterField intenStopped; 440 private IFlagRegisterField invertChannels; 441 private IValueRegisterField maxSamplesCount; 442 private IValueRegisterField operationMode; 443 private IValueRegisterField samplePtr; 444 445 private enum Registers : long 446 { 447 TasksStart = 0x000, // Starts continuous PDM transfer 448 TasksStop = 0x004, // Stops PDM transfer 449 EventsStarted = 0x100, // PDM transfer has started 450 EventsStopped = 0x104, // PDM transfer has finished 451 EventsEnd = 0x108, // The PDM has written the last sample specified by Sample_MAXCNT (or the last sample after aSTOP task has been received) to Data RAM. 452 InterruptEnable = 0x300, // Enable or disable interrupt 453 InterruptEnableSet = 0x304, // Enable interrupt 454 InterruptEnableClear = 0x308, // Disable interrupt 455 Enable = 0x500, // PDM module enable register 456 PdmClockControl = 0x504, // PDM clock generator control 457 Mode = 0x508, // Defines the routing of the connected PDM microphones' signals 458 GainLeft = 0x518, // Left output gain adjustment 459 GainRigth = 0x51C, // Right output gain adjustment 460 Ratio = 0x520, // Selects the ratio between PDM_CLK and output sample rate. Change PDMCLKCTRL accordingly. 461 PinSelectClk = 0x540, // Pin number configuration for PDM CLK signal 462 PinSelectDin = 0x544, // Pin number configuration for PDM DIN signal 463 SamplePointer = 0x560, // RAM address pointer to write samples to with EasyDMA 464 SampleBufferSize = 0x564, // Number of samples to allocate memory for in EasyDMA mode 465 } 466 467 private enum ClockFrequency 468 { 469 f1000K = 0x08000000, // PDM_CLK = 32 MHz / 32 = 1.000 MHz 470 Default = 0x08400000, // PDM_CLK = 32 MHz / 31 = 1.032 MHz. Nominal clock forRATIO=Ratio64. 471 f1067K = 0x08800000, // PDM_CLK = 32 MHz / 30 = 1.067 MHz 472 f1231K = 0x09800000, // PDM_CLK = 32 MHz / 26 = 1.231 MHz 473 f1280K = 0x0A000000, // PDM_CLK = 32 MHz / 25 = 1.280 MHz. Nominal clock forRATIO=Ratio80. 474 f1333K = 0x0A800000, // PDM_CLK = 32 MHz / 24 = 1.333 MHz 475 } 476 477 private enum OperationMode 478 { 479 Stereo = 0, 480 Mono = 1, 481 } 482 483 private enum Ratio 484 { 485 x64 = 0, 486 x80 = 1, 487 } 488 } 489 } 490