diff --git a/Jenkinsfile b/Jenkinsfile index f7075e8f..bfc8e702 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 { @@ -169,6 +169,8 @@ pipeline { // This ensures a project for XS2 can be built and runs OK sh "xsim test_xs2_benign/bin/xs2.xe" + sh "xrun -l" + // Run this first to ensure the XTAG is up and running for subsequent tests timeout(time: 2, unit: 'MINUTES') { sh "xrun --xscope --id 0 unit/bin/tests-unit.xe" diff --git a/lib_mic_array/api/mic_array/cpp/PdmRx.hpp b/lib_mic_array/api/mic_array/cpp/PdmRx.hpp index a95ffdce..8408f2c7 100644 --- a/lib_mic_array/api/mic_array/cpp/PdmRx.hpp +++ b/lib_mic_array/api/mic_array/cpp/PdmRx.hpp @@ -473,6 +473,41 @@ void mic_array::StandardPdmRxService::SetPort(port_t template void mic_array::StandardPdmRxService::ThreadEntry() { + + // The boot pattern is interleaved silence on each channel, so 0101.. for 1 mic + // 00110011 for 2 mics etc. After deinterleaving, each channel ends up with the + // 0x55555555 (0101..) silence pattern. The pre-deinterleave pattern is + // CHANNELS_IN zeros followed by CHANNELS_IN ones, repeated. + uint32_t boot_pattern = (CHANNELS_IN == 1) ? 0x55555555 : + (CHANNELS_IN == 2) ? 0x33333333 : + (CHANNELS_IN == 4) ? 0x0F0F0F0F : + (CHANNELS_IN == 8) ? 0x00FF00FF : + 0x0000FFFF; // 16 mics + + 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] = boot_pattern; + + 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)); + } + // During boot the pin can toggle between all-0s and all-1s, so wait for a + // couple of consecutive valid frames before using the data + bool bad_frame = (data == 0x00000000) || (data == 0xFFFFFFFF); + good_frames = bad_frame ? 0 : (good_frames + 1); + if (good_frames > 1) { + 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..55f152c5 100644 --- a/python/mic_array/filters.py +++ b/python/mic_array/filters.py @@ -78,6 +78,30 @@ 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 nor all-1 + + good_frames = 0 + + for start in range(0, pdm_signal.shape[1], 32): + if np.all(pdm_signal[:,start:start+32] == 1) or np.all(pdm_signal[:,start:start+32] == -1): + good_frames = 0 + pdm_signal[:,start:start+32] = np.tile([-1, 1], 16).T # write 0x55555555 to buffer + continue + + # signal gets modified either way + pdm_signal[:,start:start+32] = np.tile([-1, 1], 16).T # write 0x55555555 to buffer + + good_frames += 1 + if good_frames > 1: + break + + 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 +109,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)