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