XCORE SDK
XCORE Software Development Kit
PdmRx.hpp
1 // Copyright 2022 XMOS LIMITED.
2 // This Software is subject to the terms of the XMOS Public Licence: Version 1.
3 
4 #pragma once
5 
6 #include <cstdint>
7 #include <string>
8 #include <cassert>
9 
10 #include <xcore/interrupt.h>
11 #include <xcore/channel_streaming.h>
12 #include <xcore/port.h>
13 
14 #include "mic_array.h"
15 #include "Util.hpp"
16 
17 // This has caused problems previously, so just catch the problems here.
18 #if defined(BLOCK_SIZE) || defined(CHANNELS_IN) || defined(CHANNELS_OUT) || defined(SUBBLOCKS)
19 # error Application must not define the following as precompiler macros: MIC_COUNT, CHANNELS_IN, CHANNELS_OUT, SUBBLOCKS.
20 #endif
21 
22 using namespace std;
23 
32 extern "C" {
33  extern struct {
34  port_t p_pdm_mics;
35  uint32_t* pdm_buffer[2];
36  unsigned phase;
37  unsigned phase_reset;
38  chanend_t c_pdm_data;
39  } pdm_rx_isr_context;
40 
51  static inline
52  void enable_pdm_rx_isr(
53  const port_t p_pdm_mics)
54  {
55  asm volatile(
56  "setc res[%0], %1 \n"
57  "ldap r11, pdm_rx_isr \n"
58  "setv res[%0], r11 \n"
59  "eeu res[%0] "
60  :
61  : "r"(p_pdm_mics), "r"(XS1_SETC_IE_MODE_INTERRUPT)
62  : "r11" );
63  }
64 
65 }
66 
67 
68 namespace mic_array {
69 
70 
71 
100  template <unsigned BLOCK_SIZE, class SubType>
102  {
103  // @todo: Use a static assertion to check that SubType is in fact a sub-type
104  // of `PdmRxService`.
105 
106  public:
107 
111  static constexpr unsigned BlockSize = BLOCK_SIZE;
112 
113  protected:
117  port_t p_pdm_mics;
118 
122  unsigned phase = BLOCK_SIZE;
123 
127  uint32_t block_data[2][BLOCK_SIZE];
128 
136  uint32_t* blocks[2] = {&block_data[0][0], &block_data[1][0]};
137 
138  public:
139 
143  void SetPort(port_t p_pdm_mics);
144 
148  void ProcessNext();
149 
156  void ThreadEntry();
157 
158  };
159 
160 
248  template <unsigned CHANNELS_IN, unsigned CHANNELS_OUT, unsigned SUBBLOCKS>
249  class StandardPdmRxService : public PdmRxService<CHANNELS_IN * SUBBLOCKS,
250  StandardPdmRxService<CHANNELS_IN, CHANNELS_OUT, SUBBLOCKS>>
251  {
255  using Super = PdmRxService<CHANNELS_IN * SUBBLOCKS,
257 
258  private:
262  streaming_channel_t c_pdm_blocks;
263 
273  unsigned channel_map[CHANNELS_OUT];
274 
283  uint32_t out_block[CHANNELS_OUT][SUBBLOCKS];
284 
285  public:
286 
293  uint32_t ReadPort();
294 
300  void SendBlock(uint32_t block[CHANNELS_IN * SUBBLOCKS]);
301 
307  void Init(port_t p_pdm_mics);
308 
323  void MapChannels(unsigned map[CHANNELS_OUT]);
324 
340  void MapChannel(unsigned out_channel, unsigned in_channel);
341 
347  void InstallISR();
348 
352  void UnmaskISR();
353 
365  uint32_t* GetPdmBlock();
366  };
367 
368 }
369 
371 // Template function implementations below. //
373 
374 
376 // PdmRxService //
378 
379 template <unsigned BLOCK_SIZE, class SubType>
381 {
382  this->p_pdm_mics = p_pdm_mics;
383 }
384 
385 
386 template <unsigned BLOCK_SIZE, class SubType>
388 {
389  this->blocks[0][--phase] = static_cast<SubType*>(this)->ReadPort();
390 
391  if(!phase){
392  this->phase = BLOCK_SIZE;
393  uint32_t* ready_block = this->blocks[0];
394  this->blocks[0] = this->blocks[1];
395  this->blocks[1] = ready_block;
396 
397  static_cast<SubType*>(this)->SendBlock(ready_block);
398  }
399 }
400 
401 
402 template <unsigned BLOCK_SIZE, class SubType>
404 {
405  while(1){
406  this->ProcessNext();
407  }
408 }
409 
410 
411 
413 // StandardPdmRxService //
415 
416 
417 template <unsigned CHANNELS_IN, unsigned CHANNELS_OUT, unsigned SUBBLOCKS>
420 {
421  return port_in(this->p_pdm_mics);
422 }
423 
424 
425 template <unsigned CHANNELS_IN, unsigned CHANNELS_OUT, unsigned SUBBLOCKS>
427  ::SendBlock(uint32_t block[CHANNELS_IN*SUBBLOCKS])
428 {
429  s_chan_out_word(this->c_pdm_blocks.end_a,
430  reinterpret_cast<uint32_t>( &block[0] ));
431 }
432 
433 
434 template <unsigned CHANNELS_IN, unsigned CHANNELS_OUT, unsigned SUBBLOCKS>
436  ::Init(port_t p_pdm_mics)
437 {
438  for(int k = 0; k < CHANNELS_OUT; k++)
439  this->channel_map[k] = k;
440 
441  this->c_pdm_blocks = s_chan_alloc();
442 
443  this->SetPort(p_pdm_mics);
444 }
445 
446 
447 template <unsigned CHANNELS_IN, unsigned CHANNELS_OUT, unsigned SUBBLOCKS>
449  ::MapChannels(unsigned map[CHANNELS_OUT])
450 {
451  for(int k = 0; k < CHANNELS_OUT; k++)
452  this->channel_map[k] = map[k];
453 }
454 
455 
456 template <unsigned CHANNELS_IN, unsigned CHANNELS_OUT, unsigned SUBBLOCKS>
458  ::MapChannel(unsigned out_channel, unsigned in_channel)
459 {
460  this->channel_map[out_channel] = in_channel;
461 }
462 
463 
464 template <unsigned CHANNELS_IN, unsigned CHANNELS_OUT, unsigned SUBBLOCKS>
467 {
468  pdm_rx_isr_context.p_pdm_mics = this->p_pdm_mics;
469  pdm_rx_isr_context.c_pdm_data = this->c_pdm_blocks.end_a;
470  pdm_rx_isr_context.pdm_buffer[0] = &this->block_data[0][0];
471  pdm_rx_isr_context.pdm_buffer[1] = &this->block_data[1][0];
472  pdm_rx_isr_context.phase_reset = CHANNELS_IN*SUBBLOCKS-1;
473  pdm_rx_isr_context.phase = CHANNELS_IN*SUBBLOCKS-1;
474 
475  enable_pdm_rx_isr(this->p_pdm_mics);
476 }
477 
478 
479 template <unsigned CHANNELS_IN, unsigned CHANNELS_OUT, unsigned SUBBLOCKS>
482 {
483  interrupt_unmask_all();
484 }
485 
486 
487 template <unsigned CHANNELS_IN, unsigned CHANNELS_OUT, unsigned SUBBLOCKS>
490 {
491  uint32_t* full_block = (uint32_t*) s_chan_in_word(this->c_pdm_blocks.end_b);
492  mic_array::deinterleave_pdm_samples<CHANNELS_IN>(full_block, SUBBLOCKS);
493 
494  uint32_t (*block)[CHANNELS_IN] = (uint32_t (*)[CHANNELS_IN]) full_block;
495 
496  for(int ch = 0; ch < CHANNELS_OUT; ch++) {
497  for(int sb = 0; sb < SUBBLOCKS; sb++) {
498  unsigned d = this->channel_map[ch];
499  this->out_block[ch][sb] = block[SUBBLOCKS-1-sb][d];
500  }
501  }
502  return &this->out_block[0][0];
503 }
Used to collect PDM sample data from a port.
Definition: PdmRx.hpp:102
void ThreadEntry()
Entry point for PDM processing thread.
Definition: PdmRx.hpp:403
port_t p_pdm_mics
Port from which to collect PDM data.
Definition: PdmRx.hpp:117
void SetPort(port_t p_pdm_mics)
Set the port from which to collect PDM samples.
Definition: PdmRx.hpp:380
void ProcessNext()
Perform a port read and if a new block has completed, signal.
Definition: PdmRx.hpp:387
PDM rx service which uses a streaming channel to send a block of data by pointer.
Definition: PdmRx.hpp:251
uint32_t * GetPdmBlock()
Get a block of PDM data.
Definition: PdmRx.hpp:489
void SendBlock(uint32_t block[CHANNELS_IN *SUBBLOCKS])
Send a block of PDM data to a listener.
Definition: PdmRx.hpp:427
uint32_t ReadPort()
Read a word of PDM data from the port.
Definition: PdmRx.hpp:419
void InstallISR()
Install ISR for PDM reception on the current core.
Definition: PdmRx.hpp:466
void Init(port_t p_pdm_mics)
Initialize this object with a channel and port.
Definition: PdmRx.hpp:436
void MapChannels(unsigned map[CHANNELS_OUT])
Set the input-output mapping for all output channels.
Definition: PdmRx.hpp:449
void MapChannel(unsigned out_channel, unsigned in_channel)
Set the input-output mapping for a single output channel.
Definition: PdmRx.hpp:458
void UnmaskISR()
Unmask interrupts on the current core.
Definition: PdmRx.hpp:481