diff options
| -rw-r--r-- | host/include/uhd/rfnoc/fir_filter_block_control.hpp | 7 | ||||
| -rw-r--r-- | host/lib/rfnoc/fir_filter_block_control.cpp | 171 | ||||
| -rw-r--r-- | host/tests/rfnoc_block_tests/fir_filter_block_test.cpp | 173 | 
3 files changed, 218 insertions, 133 deletions
| diff --git a/host/include/uhd/rfnoc/fir_filter_block_control.hpp b/host/include/uhd/rfnoc/fir_filter_block_control.hpp index 99526e5ee..8f660c14e 100644 --- a/host/include/uhd/rfnoc/fir_filter_block_control.hpp +++ b/host/include/uhd/rfnoc/fir_filter_block_control.hpp @@ -28,6 +28,7 @@ public:      RFNOC_DECLARE_BLOCK(fir_filter_block_control)      // Block registers +    static const uint32_t REG_FIR_BLOCK_SIZE;      static const uint32_t REG_FIR_MAX_NUM_COEFFS_ADDR;      static const uint32_t REG_FIR_LOAD_COEFF_ADDR;      static const uint32_t REG_FIR_LOAD_COEFF_LAST_ADDR; @@ -39,7 +40,7 @@ public:       *       * \returns The maximum number of filter coefficients supported by this block       */ -    virtual size_t get_max_num_coefficients() const = 0; +    virtual size_t get_max_num_coefficients(const size_t chan = 0) const = 0;      /*! Set the filter coefficients       * @@ -54,7 +55,7 @@ public:       *       * \param coeffs A vector of integer coefficients for the FIR filter       */ -    virtual void set_coefficients(const std::vector<int16_t>& coeffs) = 0; +    virtual void set_coefficients(const std::vector<int16_t>& coeffs, const size_t chan = 0) = 0;      /*! Get the filter coefficients       * @@ -62,7 +63,7 @@ public:       *       * \returns The vector of current filter coefficients       */ -    virtual std::vector<int16_t> get_coefficients() const = 0; +    virtual std::vector<int16_t> get_coefficients(const size_t chan = 0) const = 0;  };  }} // namespace uhd::rfnoc diff --git a/host/lib/rfnoc/fir_filter_block_control.cpp b/host/lib/rfnoc/fir_filter_block_control.cpp index 1de15e237..e5f713dec 100644 --- a/host/lib/rfnoc/fir_filter_block_control.cpp +++ b/host/lib/rfnoc/fir_filter_block_control.cpp @@ -6,97 +6,160 @@  #include <uhd/exception.hpp>  #include <uhd/rfnoc/defaults.hpp> -#include <uhd/rfnoc/fir_filter_block_control.hpp> +#include <uhd/rfnoc/multichan_register_iface.hpp>  #include <uhd/rfnoc/property.hpp>  #include <uhd/rfnoc/registry.hpp> +#include <uhd/rfnoc/fir_filter_block_control.hpp>  using namespace uhd::rfnoc; -const uint32_t fir_filter_block_control::REG_FIR_MAX_NUM_COEFFS_ADDR  = 0; -const uint32_t fir_filter_block_control::REG_FIR_LOAD_COEFF_ADDR      = 4; -const uint32_t fir_filter_block_control::REG_FIR_LOAD_COEFF_LAST_ADDR = 8; +const uint32_t fir_filter_block_control::REG_FIR_BLOCK_SIZE           = 1 << 4; +const uint32_t fir_filter_block_control::REG_FIR_MAX_NUM_COEFFS_ADDR  = 0x00; +const uint32_t fir_filter_block_control::REG_FIR_LOAD_COEFF_ADDR      = 0x04; +const uint32_t fir_filter_block_control::REG_FIR_LOAD_COEFF_LAST_ADDR = 0x08; + +// User property names +const char* const PROP_KEY_MAX_NUM_COEFFS = "max_num_coeffs";  class fir_filter_block_control_impl : public fir_filter_block_control  {  public: -    RFNOC_BLOCK_CONSTRUCTOR(fir_filter_block_control) -    , _max_num_coeffs(this->regs().peek32(REG_FIR_MAX_NUM_COEFFS_ADDR)), -        _coeffs(_max_num_coeffs, int16_t(0)) +    RFNOC_BLOCK_CONSTRUCTOR(fir_filter_block_control), +        _fir_filter_reg_iface(*this, 0, REG_FIR_BLOCK_SIZE)      { -        // register edge properties -        register_property(&_prop_type_in); -        register_property(&_prop_type_out); - -        // add resolvers for type (keeps it constant) -        add_property_resolver({&_prop_type_in}, {&_prop_type_in}, [this]() { -            _prop_type_in.set(IO_TYPE_SC16); -        }); -        add_property_resolver({&_prop_type_out}, {&_prop_type_out}, [this]() { -            _prop_type_out.set(IO_TYPE_SC16); -        }); - -        // initialize hardware with an impulse response -        _coeffs[0] = std::numeric_limits<int16_t>::max(); -        _program_coefficients(); +        UHD_ASSERT_THROW(get_num_input_ports() == get_num_output_ports()); +        _register_props();      } -    size_t get_max_num_coefficients() const override +    size_t get_max_num_coefficients(const size_t chan = 0) const override      { -        return _max_num_coeffs; +        if (chan >= get_num_input_ports()) { +            std::string error_msg = +                "Cannot get max number of coefficients for FIR Filter channel " +                + std::to_string(chan) + ", channel value must be less than " +                "or equal to " + std::to_string(get_num_input_ports()-1); +            throw uhd::value_error(error_msg); +        } +        return _max_num_coeffs.at(chan);      } -    void set_coefficients(const std::vector<int16_t>& coeffs) override +    void set_coefficients(const std::vector<int16_t>& coeffs, const size_t chan = 0) override      { -        if (coeffs.size() > _max_num_coeffs) { +        if (chan >= get_num_input_ports()) {              std::string error_msg = -                "Too many filter coefficients specified (max " + -                std::to_string(_max_num_coeffs) + ")"; +                "Cannot set coefficients for FIR Filter channel " +                + std::to_string(chan) + ", channel value must be less than " +                "or equal to " + std::to_string(get_num_input_ports()-1); +            throw uhd::value_error(error_msg); +        } +        if (coeffs.size() > _max_num_coeffs.at(chan)) { +            std::string error_msg = "Too many filter coefficients specified (max " +                                    + std::to_string(_max_num_coeffs.at(chan)) + ")";              throw uhd::value_error(error_msg);          } -        // save the new coefficients... -        _coeffs = coeffs; -        // ...and expand it to the number supported by the hardware, -        // padding with zeroes -        _coeffs.resize(_max_num_coeffs, 0); -        _program_coefficients(); +        _coeffs[chan] = coeffs; +        // Expand coefficients to the number supported by the hardware, +        // by padding with zeroes +        _coeffs[chan].resize(_max_num_coeffs.at(chan), 0); +        _program_coefficients(chan);      } -    std::vector<int16_t> get_coefficients() const override +    std::vector<int16_t> get_coefficients(const size_t chan = 0) const override      { -        return _coeffs; +        if (chan >= get_num_input_ports()) { +            std::string error_msg = +                "Cannot get coefficients for FIR Filter channel " +                + std::to_string(chan) + ", channel value must be less than " +                "or equal to " + std::to_string(get_num_input_ports()-1); +            throw uhd::value_error(error_msg); +        } +        return _coeffs.at(chan);      }  private: -    void _program_coefficients() +    void _register_props()      { -        // Write coefficients [0..num_coeffs-2].. -        std::vector<uint32_t> coeffs_addr(_max_num_coeffs - 1, REG_FIR_LOAD_COEFF_ADDR); -        std::vector<uint32_t> coeffs_minus_last(_max_num_coeffs - 1); -        std::transform(_coeffs.begin(), -            _coeffs.end() - 1, +        const size_t num_chans = get_num_input_ports(); +        _max_num_coeffs.reserve(num_chans); +        _coeffs.reserve(num_chans); +        _prop_max_num_coeffs.reserve(num_chans); +        _prop_type_in.reserve(num_chans); +        _prop_type_out.reserve(num_chans); + +        for (size_t chan = 0; chan < num_chans; chan++) { +            const uint32_t max_num_coeffs = +                _fir_filter_reg_iface.peek32(REG_FIR_MAX_NUM_COEFFS_ADDR, chan); +            _max_num_coeffs.emplace_back(max_num_coeffs); + +            // set impulse as default filter for each channel +            std::vector<int16_t> impulse_coeffs(max_num_coeffs, 0); +            impulse_coeffs.front() = std::numeric_limits<int16_t>::max(); +            _coeffs.emplace_back(impulse_coeffs); +            _program_coefficients(chan); + +            // register user properties +            _prop_max_num_coeffs.emplace_back(property_t<int>{PROP_KEY_MAX_NUM_COEFFS, +                static_cast<int>(max_num_coeffs), +                {res_source_info::USER, chan}}); +            register_property(&_prop_max_num_coeffs.back()); +            add_property_resolver({&ALWAYS_DIRTY}, +                {&_prop_max_num_coeffs.back()}, +                [this, chan, max_num_coeffs]() { _prop_max_num_coeffs.at(chan).set(max_num_coeffs); }); + +            // register edge properties +            _prop_type_in.emplace_back(property_t<std::string>{ +                PROP_KEY_TYPE, IO_TYPE_SC16, {res_source_info::INPUT_EDGE, chan}}); +            _prop_type_out.emplace_back(property_t<std::string>{ +                PROP_KEY_TYPE, IO_TYPE_SC16, {res_source_info::OUTPUT_EDGE, chan}}); +            register_property(&_prop_type_in.back()); +            register_property(&_prop_type_out.back()); + +            // add resolvers for type +            add_property_resolver({&_prop_type_in.back()}, +                {&_prop_type_in.back()}, +                [this, chan]() { _prop_type_in.at(chan).set(IO_TYPE_SC16); }); +            add_property_resolver({&_prop_type_out.back()}, +                {&_prop_type_out.back()}, +                [this, chan]() { _prop_type_out.at(chan).set(IO_TYPE_SC16); }); +        } +    } + +    void _program_coefficients(const size_t chan = 0) +    { +        // Write coefficients [0..num_coeffs-2]... +        const size_t num_coeffs = _coeffs.at(chan).size(); +        std::vector<uint32_t> coeffs_addr(num_coeffs - 1, REG_FIR_LOAD_COEFF_ADDR); +        std::vector<uint32_t> coeffs_minus_last(num_coeffs - 1); +        std::transform(_coeffs.at(chan).begin(), +            _coeffs.at(chan).end() - 1,              coeffs_minus_last.begin(),              [](int16_t value) -> uint32_t { return static_cast<uint32_t>(value); }); -        this->regs().multi_poke32(coeffs_addr, coeffs_minus_last); -        // ...and the final coefficients (num_coeffs-1) -        this->regs().poke32( -            REG_FIR_LOAD_COEFF_LAST_ADDR, _coeffs.at(_max_num_coeffs - 1)); +        _fir_filter_reg_iface.multi_poke32(coeffs_addr, coeffs_minus_last, chan); +        // ...and the final coefficient (num_coeffs-1) +        _fir_filter_reg_iface.poke32(REG_FIR_LOAD_COEFF_LAST_ADDR, +            static_cast<uint32_t>(_coeffs.at(chan).at(num_coeffs - 1)), +            chan);      } -    //! Number of coefficients supported by the FIR filter -    const size_t _max_num_coeffs; +    //! Maximum number of coefficients supported by the FIR filter +    std::vector<size_t> _max_num_coeffs; -    //! Current FIR filter coefficients -    std::vector<int16_t> _coeffs; +    //! Current fir filter coefficients +    std::vector<std::vector<int16_t>> _coeffs;      /**************************************************************************       * Attributes       *************************************************************************/ -    property_t<std::string> _prop_type_in = property_t<std::string>{ -        PROP_KEY_TYPE, IO_TYPE_SC16, {res_source_info::INPUT_EDGE}}; -    property_t<std::string> _prop_type_out = property_t<std::string>{ -        PROP_KEY_TYPE, IO_TYPE_SC16, {res_source_info::OUTPUT_EDGE}}; +    std::vector<property_t<std::string>> _prop_type_in; +    std::vector<property_t<std::string>> _prop_type_out; +    std::vector<property_t<int>> _prop_max_num_coeffs; + +    /************************************************************************** +     * Register interface +     *************************************************************************/ +    multichan_register_iface _fir_filter_reg_iface;  };  UHD_RFNOC_BLOCK_REGISTER_DIRECT( diff --git a/host/tests/rfnoc_block_tests/fir_filter_block_test.cpp b/host/tests/rfnoc_block_tests/fir_filter_block_test.cpp index fe660b626..3eeb73179 100644 --- a/host/tests/rfnoc_block_tests/fir_filter_block_test.cpp +++ b/host/tests/rfnoc_block_tests/fir_filter_block_test.cpp @@ -7,8 +7,8 @@  #include "../rfnoc_graph_mock_nodes.hpp"  #include <uhd/rfnoc/actions.hpp>  #include <uhd/rfnoc/defaults.hpp> -#include <uhd/rfnoc/fir_filter_block_control.hpp>  #include <uhd/rfnoc/mock_block.hpp> +#include <uhd/rfnoc/fir_filter_block_control.hpp>  #include <uhdlib/rfnoc/graph.hpp>  #include <uhdlib/rfnoc/node_accessor.hpp>  #include <uhdlib/utils/narrow.hpp> @@ -33,20 +33,31 @@ noc_block_base::make_args_t::~make_args_t() = default;  class fir_filter_mock_reg_iface_t : public mock_reg_iface_t  {  public: -    fir_filter_mock_reg_iface_t(size_t max_num_coeffs) : _max_num_coeffs(max_num_coeffs) +    fir_filter_mock_reg_iface_t(size_t num_chans, std::vector<size_t> max_num_coeffs) +        : last_coeff_write_pos(num_chans, 0) +        , coeffs(num_chans) +        , _num_chans(num_chans) +        , _max_num_coeffs(max_num_coeffs)      { +        reset();      }      void _poke_cb(          uint32_t addr, uint32_t data, uhd::time_spec_t /*time*/, bool /*ack*/) override      { -        if (addr == fir_filter_block_control::REG_FIR_MAX_NUM_COEFFS_ADDR) { +        size_t chan   = addr / fir_filter_block_control::REG_FIR_BLOCK_SIZE; +        size_t offset = addr % fir_filter_block_control::REG_FIR_BLOCK_SIZE; +        if (chan >= _num_chans) { +            throw uhd::assertion_error("Invalid channel index"); +        } + +        if (offset == fir_filter_block_control::REG_FIR_MAX_NUM_COEFFS_ADDR) {              throw uhd::assertion_error("Invalid write to read-only register"); -        } else if (addr == fir_filter_block_control::REG_FIR_LOAD_COEFF_ADDR) { -            coeffs.push_back(uhd::narrow_cast<int16_t>(data)); -        } else if (addr == fir_filter_block_control::REG_FIR_LOAD_COEFF_LAST_ADDR) { -            last_coeff_write_pos = coeffs.size(); -            coeffs.push_back(uhd::narrow_cast<int16_t>(data)); +        } else if (offset == fir_filter_block_control::REG_FIR_LOAD_COEFF_ADDR) { +            coeffs.at(chan).push_back(uhd::narrow_cast<int16_t>(data)); +        } else if (offset == fir_filter_block_control::REG_FIR_LOAD_COEFF_LAST_ADDR) { +            last_coeff_write_pos[chan] = coeffs.at(chan).size(); +            coeffs.at(chan).push_back(uhd::narrow_cast<int16_t>(data));          } else {              throw uhd::assertion_error("Invalid write to out of bounds address");          } @@ -54,8 +65,14 @@ public:      void _peek_cb(uint32_t addr, uhd::time_spec_t /*time*/) override      { -        if (addr == fir_filter_block_control::REG_FIR_MAX_NUM_COEFFS_ADDR) { -            read_memory[addr] = uhd::narrow_cast<int32_t>(_max_num_coeffs); +        size_t chan   = addr / fir_filter_block_control::REG_FIR_BLOCK_SIZE; +        size_t offset = addr % fir_filter_block_control::REG_FIR_BLOCK_SIZE; +        if (chan >= _num_chans) { +            throw uhd::assertion_error("Invalid channel index"); +        } + +        if (offset == fir_filter_block_control::REG_FIR_MAX_NUM_COEFFS_ADDR) { +            read_memory[addr] = uhd::narrow_cast<int32_t>(_max_num_coeffs.at(chan));          } else {              throw uhd::assertion_error("Invalid read from out of bounds address");          } @@ -63,15 +80,18 @@ public:      void reset()      { -        last_coeff_write_pos = 0; -        coeffs.clear(); +        for (size_t chan = 0; chan < _num_chans; chan++) { +            last_coeff_write_pos[chan] = 0; +            coeffs.at(chan).clear(); +        }      } -    size_t last_coeff_write_pos = 0; -    std::vector<int16_t> coeffs{}; +    std::vector<size_t> last_coeff_write_pos; +    std::vector<std::vector<int16_t>> coeffs;  private: -    const size_t _max_num_coeffs; +    const size_t _num_chans; +    const std::vector<size_t> _max_num_coeffs;  }; @@ -81,16 +101,17 @@ private:   * case. The instance of the object is destroyed at the end of each test   * case.   */ -constexpr size_t MAX_NUM_COEFFS = 3000; -constexpr size_t DEFAULT_MTU    = 8000; +constexpr size_t DEFAULT_MTU = 8000; +constexpr size_t NUM_CHANS   = 4; +static const std::vector<size_t> MAX_NUM_COEFFS{1, 2, 1337, 65537};  struct fir_filter_block_fixture  {      fir_filter_block_fixture() -        : reg_iface(std::make_shared<fir_filter_mock_reg_iface_t>(MAX_NUM_COEFFS)) +        : reg_iface(std::make_shared<fir_filter_mock_reg_iface_t>(NUM_CHANS, MAX_NUM_COEFFS))          , block_container(get_mock_block(FIR_FILTER_BLOCK, -              1, -              2, +              NUM_CHANS, +              NUM_CHANS,                uhd::device_addr_t(),                DEFAULT_MTU,                ANY_DEVICE, @@ -112,17 +133,21 @@ struct fir_filter_block_fixture   */  BOOST_FIXTURE_TEST_CASE(fir_filter_test_construction, fir_filter_block_fixture)  { -    // Check that the number of coefficients is expected -    BOOST_CHECK_EQUAL(reg_iface->coeffs.size(), MAX_NUM_COEFFS); -    // Check that the first coefficient is the only non-zero value -    // (impulse response) -    BOOST_CHECK_NE(reg_iface->coeffs.at(0), 0); -    for (size_t i = 1; i < reg_iface->coeffs.size(); i++) { -        BOOST_CHECK_EQUAL(reg_iface->coeffs.at(i), 0); +    for (size_t chan = 0; chan < NUM_CHANS; chan++) { +        // Check that the number of coefficients is expected +        BOOST_CHECK_EQUAL(reg_iface->coeffs.at(chan).size(), MAX_NUM_COEFFS.at(chan)); +        // Check that the first coefficient is the maximum positive int16_t +        // value and then all other values are 0 (i.e. impulse) +        BOOST_CHECK_EQUAL( +            reg_iface->coeffs.at(chan).at(0), std::numeric_limits<int16_t>::max()); +        for (size_t i = 1; i < reg_iface->coeffs.at(chan).size(); i++) { +            BOOST_CHECK_EQUAL(reg_iface->coeffs.at(chan).at(i), 0); +        } +        // Check that the LOAD_COEFF_LAST register was written at the right +        // time (i.e. with the last value) +        BOOST_CHECK_EQUAL( +            reg_iface->last_coeff_write_pos.at(chan), MAX_NUM_COEFFS.at(chan) - 1);      } -    // Check that the LOAD_COEFF_LAST register was written at the right -    // time (i.e. with the last value) -    BOOST_CHECK_EQUAL(reg_iface->last_coeff_write_pos, MAX_NUM_COEFFS - 1);  }  /* @@ -130,7 +155,9 @@ BOOST_FIXTURE_TEST_CASE(fir_filter_test_construction, fir_filter_block_fixture)   */  BOOST_FIXTURE_TEST_CASE(fir_filter_test_max_num_coeffs, fir_filter_block_fixture)  { -    BOOST_CHECK_EQUAL(test_fir_filter->get_max_num_coefficients(), MAX_NUM_COEFFS); +    for (size_t chan = 0; chan < NUM_CHANS; chan++) { +        BOOST_CHECK_EQUAL(test_fir_filter->get_max_num_coefficients(chan), MAX_NUM_COEFFS.at(chan)); +    }  }  /* @@ -143,58 +170,50 @@ BOOST_FIXTURE_TEST_CASE(fir_filter_test_set_get_coefficients, fir_filter_block_f      // Reset state of mock FIR filter register interface      reg_iface->reset(); -    // First test: 10 coefficients -    std::vector<int16_t> coeffs1{1, 2, 3, 4, 5, -1, -2, -3, -4, -5}; -    test_fir_filter->set_coefficients(coeffs1); +    for (size_t chan = 0; chan < NUM_CHANS; chan++) { +        // Generate some dummy coefficients +        const size_t num_coeffs = test_fir_filter->get_max_num_coefficients(chan); +        const std::vector<int16_t> coeffs(num_coeffs, chan); +        test_fir_filter->set_coefficients(coeffs, chan); -    // Check that all coefficients were written -    BOOST_CHECK_EQUAL(reg_iface->coeffs.size(), MAX_NUM_COEFFS); +        // Check that all coefficients were written +        BOOST_CHECK_EQUAL(reg_iface->coeffs.at(chan).size(), num_coeffs); -    // Check correctness of coefficients -    for (size_t i = 0; i < coeffs1.size(); i++) { -        BOOST_CHECK_EQUAL(reg_iface->coeffs.at(i), coeffs1.at(i)); -    } -    for (size_t i = coeffs1.size(); i < MAX_NUM_COEFFS; i++) { -        BOOST_CHECK_EQUAL(reg_iface->coeffs.at(i), 0); -    } -    // Check that the LOAD_COEFF_LAST register was written at the right -    // time (i.e. with the last value) -    BOOST_CHECK_EQUAL(reg_iface->last_coeff_write_pos, MAX_NUM_COEFFS - 1); +        // Check correctness of coefficients +        for (size_t i = 0; i < coeffs.size(); i++) { +            BOOST_CHECK_EQUAL(reg_iface->coeffs.at(chan).at(i), chan); +        } +        // Check that the LOAD_COEFF_LAST register was written at the right +        // time (i.e. with the last value) +        BOOST_CHECK_EQUAL(reg_iface->last_coeff_write_pos.at(chan), num_coeffs - 1); -    // Verify that get_coefficients() returns what we expect. Note that -    // get_coefficients() returns the padded set of coefficients. -    std::vector<int16_t> received_coeffs = test_fir_filter->get_coefficients(); +        // Verify that get_coefficients() returns what we expect +        const std::vector<int16_t> received_coeffs = test_fir_filter->get_coefficients(chan); -    BOOST_CHECK_EQUAL(received_coeffs.size(), MAX_NUM_COEFFS); +        BOOST_CHECK_EQUAL(received_coeffs.size(), num_coeffs); -    // Check correctness of returned coefficients -    for (size_t i = 0; i < coeffs1.size(); i++) { -        BOOST_CHECK_EQUAL(received_coeffs.at(i), coeffs1.at(i)); -    } -    for (size_t i = coeffs1.size(); i < MAX_NUM_COEFFS; i++) { -        BOOST_CHECK_EQUAL(received_coeffs.at(i), 0); -    } +        // Check correctness of returned coefficients +        for (size_t i = 0; i < coeffs.size(); i++) { +            BOOST_CHECK_EQUAL(received_coeffs.at(i), coeffs.at(i)); +        } -    reg_iface->reset(); +        // Now send only one coefficent and ensure the rest are zero padded +        const std::vector<int16_t> coeffs2{std::numeric_limits<int16_t>::max()}; -    // Now update the coefficients with a smaller set, and ensure that -    // the hardware gets the correct coefficients -    std::vector<int16_t> coeffs2{1, 3, 5, 7}; -    test_fir_filter->set_coefficients(coeffs2); +        // Reset recorded coefficients in mock reg iface +        reg_iface->reset(); -    // Check that all coefficients were written -    BOOST_CHECK_EQUAL(reg_iface->coeffs.size(), MAX_NUM_COEFFS); +        test_fir_filter->set_coefficients(coeffs2, chan); -    // Check correctness of coefficients -    for (size_t i = 0; i < coeffs2.size(); i++) { -        BOOST_CHECK_EQUAL(reg_iface->coeffs.at(i), coeffs2.at(i)); -    } -    for (size_t i = coeffs2.size(); i < MAX_NUM_COEFFS; i++) { -        BOOST_CHECK_EQUAL(reg_iface->coeffs.at(i), 0); +        // Verify that get_coefficients() returns what we expect +        const std::vector<int16_t> received_coeffs2 = test_fir_filter->get_coefficients(chan); +        BOOST_CHECK_EQUAL(received_coeffs2.at(0), coeffs2.at(0)); +        for (size_t i = 1; i < num_coeffs; i++) { +            BOOST_CHECK_EQUAL(received_coeffs2.at(i), 0); +        } +        // Check that the LOAD_COEFF_LAST register was written at the right time +        BOOST_CHECK_EQUAL(reg_iface->last_coeff_write_pos.at(chan), num_coeffs - 1);      } -    // Check that the LOAD_COEFF_LAST register was written at the right -    // time (i.e. with the last value) -    BOOST_CHECK_EQUAL(reg_iface->last_coeff_write_pos, MAX_NUM_COEFFS - 1);  }  /* @@ -203,9 +222,11 @@ BOOST_FIXTURE_TEST_CASE(fir_filter_test_set_get_coefficients, fir_filter_block_f   */  BOOST_FIXTURE_TEST_CASE(fir_filter_test_length_error, fir_filter_block_fixture)  { -    size_t num_coeffs = test_fir_filter->get_max_num_coefficients(); -    std::vector<int16_t> coeffs(num_coeffs * 2); -    BOOST_CHECK_THROW(test_fir_filter->set_coefficients(coeffs), uhd::value_error); +    for (size_t chan = 0; chan < NUM_CHANS; chan++) { +        const size_t num_coeffs = test_fir_filter->get_max_num_coefficients(chan); +        const std::vector<int16_t> coeffs(num_coeffs * 2); +        BOOST_CHECK_THROW(test_fir_filter->set_coefficients(coeffs, chan), uhd::value_error); +    }  }  /* | 
