Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions Jenkinsfile
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -169,11 +169,15 @@ 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"
}

dir("signal/profile") {
sh "pytest -v"
}
// note no xdist for HW tests as only 1 hw instance
// Each test has it's own conftest.py so we need to run these seprarately
dir("signal/pdmrx_isr") {
Expand Down Expand Up @@ -202,9 +206,7 @@ pipeline {
dir("signal/FilterDesign") {
runPytest('-v')
}
dir("signal/profile") {
sh "pytest -v"
}

}
}
archiveArtifacts artifacts: "**/*.pkl", allowEmptyArchive: true
Expand Down
36 changes: 36 additions & 0 deletions lib_mic_array/api/mic_array/cpp/Decimator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#pragma once

#include <cstdint>
#include <cstring>
#include <string>
#include <cassert>

Expand Down Expand Up @@ -69,6 +70,14 @@ class Decimator
* Per-mic, 32-bit PDM output words from the PDM RX stage.
*/
unsigned pdm_out_words_per_mic;

/**
* Per-mic flag indicating whether the most recently processed PDM word
* was flagged as bad (excessive leading run of 0s or 1s). Used so that
* the word immediately following a bad word is also replaced, since it
* may itself be corrupted.
*/
bool pdm_word_was_bad[MIC_COUNT];
} stage1;

public:
Expand Down Expand Up @@ -197,8 +206,11 @@ void mic_array::Decimator<MIC_COUNT>
this->stage1.pdm_out_words_per_mic = pdm_out_words_per_mic;

memset(this->stage1.pdm_history_ptr, 0x55, sizeof(int32_t) * MIC_COUNT * this->stage1.pdm_history_sz);
memset(this->stage1.pdm_word_was_bad, 0, sizeof(this->stage1.pdm_word_was_bad));

if(decimator_conf.num_filter_stages >= 2) {
memset(decimator_conf.filter_conf[1].state, 0, sizeof(int32_t) * MIC_COUNT * decimator_conf.filter_conf[1].state_words_per_channel);

for(int k = 0; k < MIC_COUNT; k++){
filter_fir_s32_init(&this->stage2.filters[k], decimator_conf.filter_conf[1].state + (k * decimator_conf.filter_conf[1].state_words_per_channel),
decimator_conf.filter_conf[1].num_taps, decimator_conf.filter_conf[1].coef, decimator_conf.filter_conf[1].shr);
Expand All @@ -207,6 +219,8 @@ void mic_array::Decimator<MIC_COUNT>
}

if(decimator_conf.num_filter_stages == 3) {
memset(decimator_conf.filter_conf[2].state, 0, sizeof(int32_t) * MIC_COUNT * decimator_conf.filter_conf[2].state_words_per_channel);

for(int k = 0; k < MIC_COUNT; k++){
filter_fir_s32_init(&this->stage3.filters[k], decimator_conf.filter_conf[2].state + (k * decimator_conf.filter_conf[2].state_words_per_channel),
decimator_conf.filter_conf[2].num_taps, decimator_conf.filter_conf[2].coef, decimator_conf.filter_conf[2].shr);
Expand All @@ -224,9 +238,29 @@ void mic_array::Decimator<MIC_COUNT>
{
for(unsigned mic = 0; mic < MIC_COUNT; mic++){
uint32_t* hist = this->stage1.pdm_history_ptr + (mic * this->stage1.pdm_history_sz);
bool prev_word_bad = this->stage1.pdm_word_was_bad[mic];

for(unsigned k = 0; k < this->stage2.decimation_factor; k++){
hist[0] = *(pdm_block + (mic*this->stage2.decimation_factor + k));

constexpr unsigned max_leading_run = 3;
// leading_zeros and leading_ones are mutually exclusive (only one can
// be non-zero for a given word), so XOR the word with its own sign
// mask (0x00000000 or 0xFFFFFFFF) to fold whichever run it has down
// to a leading-zero run, then use a single clz (a single-cycle
// instruction on XS3A) instead of two clz calls plus a max().
uint32_t sign_mask = (uint32_t)((int32_t)hist[0] >> 31);
unsigned max_leading_run_len = __builtin_clz(hist[0] ^ sign_mask);

bool this_word_bad = max_leading_run_len > max_leading_run;


if(this_word_bad || prev_word_bad) {
hist[0] = 0x55555555; // write 0x55555555 to buffer
}

prev_word_bad = this_word_bad;

int32_t streamA_sample = fir_1x16_bit(hist, this->stage1.filter_coef);
shift_buffer(hist);

Expand All @@ -236,6 +270,8 @@ void mic_array::Decimator<MIC_COUNT>
sample_out[mic] = filter_fir_s32(&this->stage2.filters[mic], streamA_sample);
}
}

this->stage1.pdm_word_was_bad[mic] = prev_word_bad;
}
}

Expand Down
35 changes: 35 additions & 0 deletions lib_mic_array/api/mic_array/cpp/PdmRx.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,41 @@ void mic_array::StandardPdmRxService<CHANNELS_IN, CHANNELS_OUT>::SetPort(port_t
template <unsigned CHANNELS_IN, unsigned CHANNELS_OUT>
void mic_array::StandardPdmRxService<CHANNELS_IN, CHANNELS_OUT>::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<uint32_t>(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);

Expand Down
26 changes: 26 additions & 0 deletions python/mic_array/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,39 @@ 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,:]
CHANS, SAMPS_IN = pdm_signal.shape
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)
Expand Down