diff --git a/Jenkinsfile b/Jenkinsfile index f7075e8f..d8e58b9e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,6 +1,6 @@ // This file relates to internal XMOS infrastructure and should be ignored by external users -@Library('xmos_jenkins_shared_library@v0.49.0') _ +@Library('xmos_jenkins_shared_library@v0.52.0') _ getApproval() pipeline { diff --git a/lib_mic_array/api/mic_array/cpp/PdmRx.hpp b/lib_mic_array/api/mic_array/cpp/PdmRx.hpp index a95ffdce..be7e6f48 100644 --- a/lib_mic_array/api/mic_array/cpp/PdmRx.hpp +++ b/lib_mic_array/api/mic_array/cpp/PdmRx.hpp @@ -473,6 +473,34 @@ void mic_array::StandardPdmRxService::SetPort(port_t template void mic_array::StandardPdmRxService::ThreadEntry() { + + uint32_t good_frames = 0; + while(1){ + // During boot, the PDM port may read all 0s or all 1s. + // Output 0x55 (zero) to the buffer until we get a valid frame. + uint32_t data = port_in(this->p_pdm_mics); + this->blocks[0][--phase] = 0x55555555; + + if(!phase){ + this->phase = this->num_phases; + uint32_t* ready_block = this->blocks[0]; + this->blocks[0] = this->blocks[1]; + this->blocks[1] = ready_block; + + s_chan_out_word(this->c_pdm_blocks.end_a, reinterpret_cast(ready_block)); + } + if (data == 0x00000000 || data == 0xFFFFFFFF) { + good_frames = 0; + continue; + } + good_frames++; + if (good_frames > 1) { + // Pin can toggle between 0 and 1, so we need to wait for a few good + // frames before we start using the data. + break; + } + } + while(1){ this->blocks[0][--phase] = port_in(this->p_pdm_mics); diff --git a/python/mic_array/filters.py b/python/mic_array/filters.py index 20f1f3cc..0b1de82e 100644 --- a/python/mic_array/filters.py +++ b/python/mic_array/filters.py @@ -78,6 +78,45 @@ def _pad_input(self, sig_in): S[:,P:] = sig_in return S + def boot_logic(self, pdm_signal: np.ndarray) -> np.ndarray: + # Simulate the PdmRx thread boot loop (StandardPdmRxService::ThreadEntry, + # PdmRx.hpp): words are received one at a time; 0x55555555 is written to + # the buffer (discarding real data) until good_frames > 1, i.e. 2 + # consecutive words that are neither all-0 (0x00000000) nor all-1 + # (0xFFFFFFFF). Bit layout: bit (c + CHANS*s) of word w = + # to_binary[c, w*spw+s], where to_binary(+1)=0, to_binary(-1)=1. + + CHANS, SAMPS_IN = pdm_signal.shape + BOOT_WORD = np.uint32(0x55555555) + spw = 32 // CHANS # PDM samples per channel packed per interleaved word + total_words = SAMPS_IN // spw + binary = ((1 - pdm_signal) // 2).astype(np.uint32) + + words = np.zeros(total_words, dtype=np.uint32) + for s in range(spw): + for c in range(CHANS): + words |= binary[c, s::spw][:total_words] << np.uint32(c + CHANS * s) + + good_frames = 0 + boot_words = 0 + for word in words: + boot_words += 1 + if word == np.uint32(0x00000000) or word == np.uint32(0xFFFFFFFF): + good_frames = 0 + else: + good_frames += 1 + if good_frames > 1: + break + + pdm_signal = pdm_signal.copy() + for w in range(boot_words): + for s in range(spw): + for c in range(CHANS): + bit = int((BOOT_WORD >> np.uint32(c + CHANS * s)) & np.uint32(1)) + pdm_signal[c, w * spw + s] = np.int32(-1 if bit else 1) + + return pdm_signal + def FilterInt16(self, pdm_signal: np.ndarray) -> np.ndarray: if pdm_signal.ndim == 1: pdm_signal = pdm_signal[np.newaxis,:] @@ -85,6 +124,8 @@ def FilterInt16(self, pdm_signal: np.ndarray) -> np.ndarray: Q = self.DecimationFactor N_pcm = SAMPS_IN // self.DecimationFactor + pdm_signal = self.boot_logic(pdm_signal) + S = self._pad_input(pdm_signal) coefs = self.Coef.astype(np.int32)[:,np.newaxis] res = np.empty((CHANS, N_pcm), dtype=np.int32)