diff --git a/include/fisch/vx/extoll.h b/include/fisch/vx/extoll.h index f59f41bdab9d2f0d3552a3c2b9c1d6c3878f110e..5bd528ea5c492632f714199791fef229bfbf6c4b 100644 --- a/include/fisch/vx/extoll.h +++ b/include/fisch/vx/extoll.h @@ -12,12 +12,14 @@ class access; namespace halco::hicann_dls::vx { struct ExtollAddress; +struct ExtollAddressOnExtollNetwork; } // namespace halco::hicann_dls::vx namespace fisch::vx GENPYBIND_TAG_FISCH_VX { /** - * Container for reading and writing an Extoll (quad-)word, i.e., 64-bit. + * Container for reading and writing an Extoll (quad-)word, + * i.e., 64-bit to the odfi-rf on the own FPGA node. */ class GENPYBIND(visible) Extoll { @@ -73,4 +75,63 @@ private: void serialize(Archive& ar, std::uint32_t const version); }; + +/** + * Container for reading and writing an Extoll (quad-)word, + * i.e. 64-bit to an odfi-rf on an arbitrary network node. + */ +class GENPYBIND(visible) ExtollOnNwNode +{ +public: + typedef halco::hicann_dls::vx::ExtollAddressOnExtollNetwork coordinate_type; + + typedef word_access_type::ExtollOnNwNode Value GENPYBIND(visible); + + /** + * Default constructor. + */ + explicit ExtollOnNwNode(); + + /** + * Construct an instance with a value. + * @param value Value to construct instance with + */ + explicit ExtollOnNwNode(Value const& value); + + /** + * Get value. + * @return Word value + */ + Value const& get() const; + + /** + * Set value. + * @param value Word value to set + */ + void set(Value const& value); + + GENPYBIND(stringstream) + friend std::ostream& operator<<(std::ostream& os, ExtollOnNwNode const& word); + + bool operator==(ExtollOnNwNode const& other) const; + bool operator!=(ExtollOnNwNode const& other) const; + + constexpr static size_t GENPYBIND(hidden) encode_read_ut_message_count = 6; + constexpr static size_t GENPYBIND(hidden) encode_write_ut_message_count = 8; + constexpr static size_t GENPYBIND(hidden) decode_ut_message_count = 2; + + static std::array<hxcomm::vx::UTMessageToFPGAVariant, encode_read_ut_message_count> encode_read( + coordinate_type const& coord) GENPYBIND(hidden); + std::array<hxcomm::vx::UTMessageToFPGAVariant, encode_write_ut_message_count> encode_write( + coordinate_type const& coord) const GENPYBIND(hidden); + void decode(UTMessageFromFPGARangeOmnibus const& messages) GENPYBIND(hidden); + +private: + Value m_data; + + friend class cereal::access; + template <class Archive> + void serialize(Archive& ar, std::uint32_t const version); +}; + } // namespace fisch::vx diff --git a/include/fisch/vx/omnibus_constants.h b/include/fisch/vx/omnibus_constants.h index d2bc2b793bba1356831081207f6aa1521354fccb..36622f32be65ce501a7a3bc097fe358e45a93d29 100644 --- a/include/fisch/vx/omnibus_constants.h +++ b/include/fisch/vx/omnibus_constants.h @@ -23,6 +23,17 @@ constexpr halco::hicann_dls::vx::OmnibusAddress event_switch_mask{ constexpr halco::hicann_dls::vx::OmnibusAddress external_omnibus_bit_switch{0x0400'0000}; constexpr halco::hicann_dls::vx::OmnibusAddress external_omnibus_base_address{ external_omnibus_bit_switch | executor_omnibus_mask}; +constexpr halco::hicann_dls::vx::OmnibusAddress odfi_rf_omnibus_base_address{ + 0x0000'0000 | external_omnibus_base_address}; +constexpr halco::hicann_dls::vx::OmnibusAddress odfi_rf_omnibus_bit_switch_n{0x0100'0000}; +constexpr halco::hicann_dls::vx::OmnibusAddress pulse_comm_config_omnibus_base_address{ + odfi_rf_omnibus_bit_switch_n | external_omnibus_base_address}; + +constexpr halco::hicann_dls::vx::OmnibusAddress odfi_external_access_config_base_address{ + pulse_comm_config_omnibus_base_address}; + +constexpr halco::hicann_dls::vx::OmnibusAddress odfi_external_access_config_reg{ + odfi_external_access_config_base_address + 0x0}; constexpr halco::hicann_dls::vx::OmnibusAddress i2c_prescaler_base_address{i2c_over_omnibus_mask + 0x200}; diff --git a/include/fisch/vx/word_access/type/extoll.h b/include/fisch/vx/word_access/type/extoll.h index 54794a0882906c4c93bfa919896bc3735ee8148c..5a77966b8f2718e4fd5b72315b54e5ea0cf5ae56 100644 --- a/include/fisch/vx/word_access/type/extoll.h +++ b/include/fisch/vx/word_access/type/extoll.h @@ -12,6 +12,14 @@ struct GENPYBIND(inline_base("*")) Extoll : public halco::common::detail::BaseTy {} }; +struct GENPYBIND(inline_base("*")) ExtollOnNwNode + : public halco::common::detail::BaseType<ExtollOnNwNode, uint64_t> +{ + constexpr explicit ExtollOnNwNode(value_type const value = 0) GENPYBIND(implicit_conversion) : + base_t(value) + {} +}; + } // namespace word_access_type } // namespace fisch::vx diff --git a/src/fisch/vx/extoll.cpp b/src/fisch/vx/extoll.cpp index f3e013786ab7701fc3547a1fe016ff50d9dd82a1..e1504bddb83ecb7385dee95f5ca939bfc170fdf7 100644 --- a/src/fisch/vx/extoll.cpp +++ b/src/fisch/vx/extoll.cpp @@ -16,7 +16,7 @@ std::array<halco::hicann_dls::vx::OmnibusAddress, 2> ExtollAddressToOmnibusAddre { auto const to_omnibus = [eaddr](size_t const offset) { auto omniAddr = halco::hicann_dls::vx::OmnibusAddress((eaddr.value() >> 2) + offset); - if (omniAddr >= (external_omnibus_bit_switch - 0x1 + offset)) { + if (omniAddr >= (odfi_rf_omnibus_bit_switch_n - 0x1 + offset)) { throw std::runtime_error( "The given Extoll Registerfile Address (" + std::to_string(eaddr.value()) + ") is too large for conversion into the current Omnibus Tree!"); @@ -26,7 +26,7 @@ std::array<halco::hicann_dls::vx::OmnibusAddress, 2> ExtollAddressToOmnibusAddre return {to_omnibus(0x0), to_omnibus(0x1)}; } -} +} // namespace Extoll::Extoll() : m_data() {} Extoll::Extoll(Value const& value) : m_data(value) {} @@ -113,6 +113,119 @@ void Extoll::serialize(Archive& ar, std::uint32_t const) EXPLICIT_INSTANTIATE_CEREAL_SERIALIZE(Extoll) + +ExtollOnNwNode::ExtollOnNwNode() : m_data() {} +ExtollOnNwNode::ExtollOnNwNode(Value const& value) : m_data(value) {} + +ExtollOnNwNode::Value const& ExtollOnNwNode::get() const +{ + return m_data; +} + +void ExtollOnNwNode::set(Value const& value) +{ + m_data = value; +} + +std::ostream& operator<<(std::ostream& os, ExtollOnNwNode const& word) +{ + std::stringstream ss_d; + ss_d << "0d" << std::dec << word.m_data.value(); + std::stringstream ss_x; + ss_x << "0x" << std::hex << word.m_data.value(); + hate::bitset<sizeof(typename ExtollOnNwNode::Value::value_type) * CHAR_BIT> bits( + word.m_data.value()); + os << "ExtollOnNwNode(" << ss_d.str() << " " << ss_x.str() << " 0b" << bits << ")"; + return os; +} + +bool ExtollOnNwNode::operator==(ExtollOnNwNode const& other) const +{ + return (m_data == other.m_data); +} + +bool ExtollOnNwNode::operator!=(ExtollOnNwNode const& other) const +{ + return !(*this == other); +} + +std::array<hxcomm::vx::UTMessageToFPGAVariant, ExtollOnNwNode::encode_read_ut_message_count> +ExtollOnNwNode::encode_read(coordinate_type const& coord) +{ + using address = hxcomm::vx::instruction::omnibus_to_fpga::Address; + using data = hxcomm::vx::instruction::omnibus_to_fpga::Data; + + auto omni_addresses = ExtollAddressToOmnibusAddress(coord.toExtollAddress()); + auto coord_ndid_on_nw = coord.toExtollNodeIdOnExtollNetwork(); + auto coord_ndid = coord_ndid_on_nw.toExtollNodeId(); + uint32_t ndid = coord_ndid.value() & 0xffff; + bool is_self = (coord_ndid == halco::hicann_dls::vx::ExtollNodeIdOnExtollNetwork::self); + + // encode valid-bit together with ndid if it's not a local access: + uint32_t conf = is_self ? 0x0 : static_cast<uint32_t>(ndid | (0x1 << 16)); + + std::array<hxcomm::vx::UTMessageToFPGAVariant, encode_read_ut_message_count> ret; + + ret[0] = hxcomm::vx::UTMessageToFPGA<address>( + address::Payload(odfi_external_access_config_reg, false)); + ret[1] = hxcomm::vx::UTMessageToFPGA<data>(data::Payload(conf)); + ret[2] = + hxcomm::vx::UTMessageToFPGA<address>(address::Payload(omni_addresses[0].value(), true)); + ret[3] = + hxcomm::vx::UTMessageToFPGA<address>(address::Payload(omni_addresses[1].value(), true)); + ret[4] = hxcomm::vx::UTMessageToFPGA<address>( + address::Payload(odfi_external_access_config_reg, false)); + ret[5] = hxcomm::vx::UTMessageToFPGA<data>(data::Payload(0x0)); + return ret; +} + +std::array<hxcomm::vx::UTMessageToFPGAVariant, ExtollOnNwNode::encode_write_ut_message_count> +ExtollOnNwNode::encode_write(coordinate_type const& coord) const +{ + using address = hxcomm::vx::instruction::omnibus_to_fpga::Address; + using data = hxcomm::vx::instruction::omnibus_to_fpga::Data; + + auto omni_addresses = ExtollAddressToOmnibusAddress(coord.toExtollAddress()); + auto coord_ndid = coord.toExtollNodeIdOnExtollNetwork(); + uint16_t ndid = coord_ndid.value() & 0xffff; + bool is_self = (coord_ndid == halco::hicann_dls::vx::ExtollNodeIdOnExtollNetwork::self); + + uint32_t conf = is_self ? 0x0 : static_cast<uint32_t>(ndid | (0x1 << 16)); + + std::array<hxcomm::vx::UTMessageToFPGAVariant, encode_write_ut_message_count> ret; + + ret[0] = hxcomm::vx::UTMessageToFPGA<address>( + address::Payload(odfi_external_access_config_reg, false)); + ret[1] = hxcomm::vx::UTMessageToFPGA<data>(data::Payload(conf)); + ret[2] = + hxcomm::vx::UTMessageToFPGA<address>(address::Payload(omni_addresses[0].value(), false)); + ret[3] = hxcomm::vx::UTMessageToFPGA<data>(data::Payload(m_data.value() & 0xffff'ffff)); + ret[4] = + hxcomm::vx::UTMessageToFPGA<address>(address::Payload(omni_addresses[1].value(), false)); + ret[5] = hxcomm::vx::UTMessageToFPGA<data>(data::Payload((m_data.value() >> 32) & 0xffff'ffff)); + ret[6] = hxcomm::vx::UTMessageToFPGA<address>( + address::Payload(odfi_external_access_config_reg, false)); + ret[7] = hxcomm::vx::UTMessageToFPGA<data>(data::Payload(0x0)); + + return ret; +} + +void ExtollOnNwNode::decode(UTMessageFromFPGARangeOmnibus const& messages) +{ + m_data = Value( + (static_cast<uint64_t>(messages[1].decode()) << 32) | + static_cast<uint64_t>(messages[0].decode())); +} + +template <class Archive> +void ExtollOnNwNode::serialize(Archive& ar, std::uint32_t const) +{ + ar(CEREAL_NVP(m_data)); +} + +EXPLICIT_INSTANTIATE_CEREAL_SERIALIZE(ExtollOnNwNode) + } // namespace fisch::vx CEREAL_CLASS_VERSION(fisch::vx::Extoll, 0) +CEREAL_CLASS_VERSION(fisch::vx::ExtollOnNwNode, 0) diff --git a/tests/sw/fisch/vx/test-extoll.cpp b/tests/sw/fisch/vx/test-extoll.cpp index a2e59a685eee41c535fae434d57f46d34061e2ad..fda66275a7a4d24b1894f0a4b05ef2e0bd9e22b4 100644 --- a/tests/sw/fisch/vx/test-extoll.cpp +++ b/tests/sw/fisch/vx/test-extoll.cpp @@ -156,3 +156,181 @@ TEST(Extoll, CerealizeCoverage) } ASSERT_EQ(obj1, obj2); } + + +TEST(ExtollOnNwNode, General) +{ + using namespace fisch::vx; + EXPECT_NO_THROW(ExtollOnNwNode()); + + ExtollOnNwNode default_config; + EXPECT_EQ(default_config.get(), ExtollOnNwNode::Value()); + + ExtollOnNwNode::Value value(0xbeef'cafe'babe'abba); + ExtollOnNwNode value_config(value); + EXPECT_EQ(value_config.get(), value); + + ExtollOnNwNode::Value other_value(0xbeef'beef'abba'abba); + value_config.set(other_value); + EXPECT_EQ(value_config.get(), other_value); + + ExtollOnNwNode other_config = value_config; + + EXPECT_EQ(other_config, value_config); + EXPECT_NE(default_config, value_config); +} + +TEST(ExtollOnNwNode, EncodeRead) +{ + using namespace fisch::vx; + using namespace hxcomm::vx; + using namespace halco::hicann_dls::vx; + + ExtollOnNwNode obj; + + ExtollOnNwNode::coordinate_type coord( + ExtollAddress(3), + ExtollNodeIdOnExtollNetwork(ExtollNodeId(5), ExtollChipType(ExtollChipType::fpga))); + auto messages = obj.encode_read(coord); + auto omni_addresses = ExtollAddressToOmnibusAddress(coord.toExtollAddress()); + + EXPECT_EQ(messages.size(), 6); + auto message_addr = + std::get<UTMessageToFPGA<instruction::omnibus_to_fpga::Address>>(messages.at(2)); + EXPECT_EQ( + message_addr, UTMessageToFPGA<instruction::omnibus_to_fpga::Address>( + instruction::omnibus_to_fpga::Address::Payload(omni_addresses[0], true))); + message_addr = std::get<UTMessageToFPGA<instruction::omnibus_to_fpga::Address>>(messages.at(3)); + EXPECT_EQ( + message_addr, UTMessageToFPGA<instruction::omnibus_to_fpga::Address>( + instruction::omnibus_to_fpga::Address::Payload(omni_addresses[1], true))); + + auto ndid_conf = std::get<UTMessageToFPGA<instruction::omnibus_to_fpga::Data>>(messages.at(1)); + EXPECT_EQ( + ndid_conf, UTMessageToFPGA<instruction::omnibus_to_fpga::Data>( + instruction::omnibus_to_fpga::Data::Payload(0x1'0005))); + ndid_conf = std::get<UTMessageToFPGA<instruction::omnibus_to_fpga::Data>>(messages.at(5)); + EXPECT_EQ( + ndid_conf, UTMessageToFPGA<instruction::omnibus_to_fpga::Data>( + instruction::omnibus_to_fpga::Data::Payload(0x0'0000))); + + auto conf_addr = + std::get<UTMessageToFPGA<instruction::omnibus_to_fpga::Address>>(messages.at(0)); + EXPECT_EQ( + conf_addr, UTMessageToFPGA<instruction::omnibus_to_fpga::Address>( + instruction::omnibus_to_fpga::Address::Payload(0x8d00'0000, false))); + conf_addr = std::get<UTMessageToFPGA<instruction::omnibus_to_fpga::Address>>(messages.at(4)); + EXPECT_EQ( + conf_addr, UTMessageToFPGA<instruction::omnibus_to_fpga::Address>( + instruction::omnibus_to_fpga::Address::Payload(0x8d00'0000, false))); +} + +TEST(ExtollOnNwNode, EncodeWrite) +{ + using namespace fisch::vx; + using namespace hxcomm::vx; + using namespace halco::hicann_dls::vx; + + ExtollOnNwNode obj; + obj.set(ExtollOnNwNode::Value(0xcafe'0000'babe)); + + ExtollOnNwNode::coordinate_type coord( + ExtollAddress(3), + ExtollNodeIdOnExtollNetwork(ExtollNodeId(5), ExtollChipType(ExtollChipType::fpga))); + auto messages = obj.encode_write(coord); + auto omni_addresses = ExtollAddressToOmnibusAddress(coord.toExtollAddress()); + + EXPECT_EQ(messages.size(), 8); + auto message_addr = + std::get<UTMessageToFPGA<instruction::omnibus_to_fpga::Address>>(messages.at(2)); + EXPECT_EQ( + message_addr, + UTMessageToFPGA<instruction::omnibus_to_fpga::Address>( + instruction::omnibus_to_fpga::Address::Payload(omni_addresses[0], false))); + message_addr = std::get<UTMessageToFPGA<instruction::omnibus_to_fpga::Address>>(messages.at(4)); + EXPECT_EQ( + message_addr, + UTMessageToFPGA<instruction::omnibus_to_fpga::Address>( + instruction::omnibus_to_fpga::Address::Payload(omni_addresses[1], false))); + + auto ndid_conf = std::get<UTMessageToFPGA<instruction::omnibus_to_fpga::Data>>(messages.at(1)); + EXPECT_EQ( + ndid_conf, UTMessageToFPGA<instruction::omnibus_to_fpga::Data>( + instruction::omnibus_to_fpga::Data::Payload(0x1'0005))); + ndid_conf = std::get<UTMessageToFPGA<instruction::omnibus_to_fpga::Data>>(messages.at(7)); + EXPECT_EQ( + ndid_conf, UTMessageToFPGA<instruction::omnibus_to_fpga::Data>( + instruction::omnibus_to_fpga::Data::Payload(0x0'0000))); + + auto conf_addr = + std::get<UTMessageToFPGA<instruction::omnibus_to_fpga::Address>>(messages.at(0)); + EXPECT_EQ( + conf_addr, UTMessageToFPGA<instruction::omnibus_to_fpga::Address>( + instruction::omnibus_to_fpga::Address::Payload(0x8d00'0000, false))); + conf_addr = std::get<UTMessageToFPGA<instruction::omnibus_to_fpga::Address>>(messages.at(6)); + EXPECT_EQ( + conf_addr, UTMessageToFPGA<instruction::omnibus_to_fpga::Address>( + instruction::omnibus_to_fpga::Address::Payload(0x8d00'0000, false))); + + auto message_data = + std::get<UTMessageToFPGA<instruction::omnibus_to_fpga::Data>>(messages.at(3)); + EXPECT_EQ( + message_data, + UTMessageToFPGA<instruction::omnibus_to_fpga::Data>( + instruction::omnibus_to_fpga::Data::Payload(obj.get().value() & 0xffff'ffff))); + message_data = std::get<UTMessageToFPGA<instruction::omnibus_to_fpga::Data>>(messages.at(5)); + EXPECT_EQ( + message_data, + UTMessageToFPGA<instruction::omnibus_to_fpga::Data>( + instruction::omnibus_to_fpga::Data::Payload((obj.get().value() >> 32) & 0xffff'ffff))); +} + +TEST(ExtollOnNwNode, Decode) +{ + using namespace fisch::vx; + using namespace hxcomm::vx; + + ExtollOnNwNode obj; + + UTMessageFromFPGA<instruction::omnibus_from_fpga::Data> message_one( + instruction::omnibus_from_fpga::Data::Payload(0xcafe)); + UTMessageFromFPGA<instruction::omnibus_from_fpga::Data> message_two( + instruction::omnibus_from_fpga::Data::Payload(0xbeef)); + + obj.decode({&message_one, &message_two}); + EXPECT_EQ(obj.get(), ExtollOnNwNode::Value(0xbeef'0000'cafe)); +} + +TEST(ExtollOnNwNode, Ostream) +{ + using namespace fisch::vx; + + ExtollOnNwNode obj(ExtollOnNwNode::Value(13)); + + std::stringstream stream; + stream << obj; + + EXPECT_EQ( + stream.str(), "ExtollOnNwNode(0d13 0xd " + "0b0000000000000000000000000000000000000000000000000000000000001101)"); +} + +TEST(ExtollOnNwNode, CerealizeCoverage) +{ + using namespace fisch::vx; + ExtollOnNwNode obj1, obj2; + obj1.set(ExtollOnNwNode::Value(0x12345678)); + + std::ostringstream ostream; + { + cereal::JSONOutputArchive oa(ostream); + oa(obj1); + } + + std::istringstream istream(ostream.str()); + { + cereal::JSONInputArchive ia(istream); + ia(obj2); + } + ASSERT_EQ(obj1, obj2); +} \ No newline at end of file