diff --git a/include/ccalix/ccalix.h b/include/ccalix/ccalix.h index 9a9d3131f77fd79ef84dda5fed9eafd7c9f0639d..7f8d061934d2d7e42c6f970d532946036888e406 100644 --- a/include/ccalix/ccalix.h +++ b/include/ccalix/ccalix.h @@ -1,4 +1,5 @@ #include "ccalix/genpybind.h" +#include "ccalix/hagen/multiplication.h" #include "ccalix/helpers.h" GENPYBIND_MANUAL({ diff --git a/include/ccalix/hagen/multiplication.h b/include/ccalix/hagen/multiplication.h new file mode 100644 index 0000000000000000000000000000000000000000..d1019a62ae8c607448e0dc00276a9a04e36c733f --- /dev/null +++ b/include/ccalix/hagen/multiplication.h @@ -0,0 +1,56 @@ +#pragma once + +#include "ccalix/genpybind.h" +#include "halco/hicann-dls/vx/v3/synapse_driver.h" +#include "halco/hicann-dls/vx/v3/synram.h" +#include "haldls/vx/v3/event.h" +#include "haldls/vx/v3/synapse.h" +#include "stadls/vx/v3/playback_program_builder.h" +#include <pybind11/numpy.h> + + +namespace ccalix GENPYBIND_TAG_CCALIX { +namespace hagen GENPYBIND_MODULE { +namespace multiplication { + +/** + * Generate events for the given vector in hagen mode. + * + * @param builder Builder to append writes to + * @param vector Array containing the input vector + * @param num_sends Number of repeats of the whole vector + * @param wait_period Wait time between two successive events + * @param synram_coord Coordinate of synapse array to target with the events + * @param synram_selection_bit Determines which bit in the event label selects the synram + */ +SYMBOL_VISIBLE GENPYBIND(visible) void send_vectors( + stadls::vx::v3::PlaybackProgramBuilder& builder, + const pybind11::array_t<uint_fast16_t>& vector, + const size_t num_sends, + const size_t wait_period, + const halco::hicann_dls::vx::v3::SynramOnDLS synram_coord, + const size_t synram_selection_bit); + +namespace detail { +/** + * Return a spike pack to chip, containing an event reaching the desired + * synapse driver on the desired synram. + * + * @param address Address that is sent to the driver. The MSB reaches the synapses, the lower 5 bit + * encode the desired activation. + * @param target_driver Coordinate of target synapse driver. + * @param synram_coord Coordinate of target synapse array. + * @param synram_selection_bit Bit position that selects synapse array. + * + * @return Spike packet to chip. + */ +haldls::vx::v3::SpikePack1ToChip prepare_event( + const haldls::vx::v3::SynapseQuad::Label address, + const halco::hicann_dls::vx::v3::SynapseDriverOnSynapseDriverBlock target_driver, + const halco::hicann_dls::vx::v3::SynramOnDLS synram_coord, + const size_t synram_selection_bit); +} // namespace detail + +} // namespace multiplication +} // namespace hagen +} // namespace ccalix diff --git a/src/cc/ccalix/hagen/multiplication.cpp b/src/cc/ccalix/hagen/multiplication.cpp new file mode 100644 index 0000000000000000000000000000000000000000..318d5f0d0b8397c351ef48d643caa27777953658 --- /dev/null +++ b/src/cc/ccalix/hagen/multiplication.cpp @@ -0,0 +1,98 @@ +#include "ccalix/hagen/multiplication.h" + +#include "halco/hicann-dls/vx/v3/timing.h" +#include "haldls/vx/v3/timer.h" +#include <sstream> +#include <stdexcept> +#include <pybind11/numpy.h> + + +namespace ccalix::hagen::multiplication { + +namespace detail { +haldls::vx::v3::SpikePack1ToChip prepare_event( + const haldls::vx::v3::SynapseQuad::Label address, + const halco::hicann_dls::vx::v3::SynapseDriverOnSynapseDriverBlock target_driver, + const halco::hicann_dls::vx::v3::SynramOnDLS synram_coord, + const size_t synram_selection_bit) +{ + halco::hicann_dls::vx::v3::SpikeLabel label; + + // select target PADI bus + label.set_spl1_address( + halco::hicann_dls::vx::SPL1Address(target_driver.toPADIBusOnPADIBusBlock())); + + // select target synram + label.set_neuron_label( + halco::hicann_dls::vx::NeuronLabel(synram_coord << synram_selection_bit)); + + // select target driver on the PADI bus + label.set_row_select_address( + halco::hicann_dls::vx::PADIRowSelectAddress(target_driver.toSynapseDriverOnPADIBus())); + + // set address sent to the driver (MSB + hagen activation) + label.set_synapse_label(address); + + haldls::vx::v3::SpikePack1ToChip::labels_type labels; + labels[0] = label; + return haldls::vx::v3::SpikePack1ToChip(labels); +} +} // namespace detail + +void send_vectors( + stadls::vx::v3::PlaybackProgramBuilder& builder, + const pybind11::array_t<uint_fast16_t>& vector, + const size_t num_sends, + const size_t wait_period, + const halco::hicann_dls::vx::v3::SynramOnDLS synram_coord, + const size_t synram_selection_bit) +{ + if (vector.size() != halco::hicann_dls::vx::v3::SynapseRowOnSynram::size) { + std::stringstream ss; + ss << "Length of vector (" << vector.size() + << ") does not match number of synapse rows in hemisphere (" + << halco::hicann_dls::vx::v3::SynapseRowOnSynram::size << ")."; + throw std::runtime_error(ss.str()); + } + + const size_t num_loops = (num_sends > 1 and wait_period <= 1) ? 1 : num_sends; + const size_t num_copies = (num_sends > 1 and wait_period <= 1) ? num_sends : 1; + + size_t entry_counter = 0; + builder.write(halco::hicann_dls::vx::v3::TimerOnDLS(), haldls::vx::v3::Timer()); + + stadls::vx::v3::PlaybackProgramBuilder vector_builder; + for (size_t i = 0; i < num_loops; ++i) { + for (size_t row = 0; row < halco::hicann_dls::vx::v3::SynapseRowOnSynram::size; ++row) { + uint_fast16_t entry = vector.at(row); + if (entry == 0) + continue; + + // send event on a different address in order to + // select one of the two rows connected to a driver + if ((row % halco::hicann_dls::vx::v3::SynapseRowOnSynapseDriver::size) == 0) + entry += 32; + + vector_builder.write( + halco::hicann_dls::vx::v3::SpikePack1ToChipOnDLS(), + detail::prepare_event( + halco::hicann_dls::vx::v3::SynapseLabel(entry), + halco::hicann_dls::vx::v3::SynapseDriverOnSynapseDriverBlock( + row / halco::hicann_dls::vx::v3::SynapseRowOnSynapseDriver::size), + synram_coord, synram_selection_bit)); + + // wait only if needed: + if (wait_period > 1) { + vector_builder.block_until( + halco::hicann_dls::vx::v3::TimerOnDLS(), + haldls::vx::v3::Timer::Value(wait_period * entry_counter)); + } + entry_counter += 1; + } + } + + for (size_t i = 0; i < num_copies; ++i) + builder.copy_back(vector_builder); +} + +} // namespace ccalix::hagen::multiplication diff --git a/src/py/calix/hagen/multiplication.py b/src/py/calix/hagen/multiplication.py index 722433ba064471577970ec5b2694454f5146913a..6d89db66657bcc08c6a6cb60faed76f7e9d032d9 100644 --- a/src/py/calix/hagen/multiplication.py +++ b/src/py/calix/hagen/multiplication.py @@ -10,6 +10,8 @@ import quantities as pq from dlens_vx_v3 import lola, halco, hal, sta, logger, hxcomm +import pyccalix + from calix.common import base, helpers from calix.hagen import neuron_helpers from calix import constants @@ -291,41 +293,6 @@ class Multiplication: return synapses - def prepare_event( - self, address: hal.SynapseQuad.Label, - target_driver: halco.SynapseDriverOnSynapseDriverBlock - ) -> hal.SpikePack1ToChip: - """ - Return a spike pack to chip, containing an event reaching - the desired synapse driver on the synram in use. - - :param address: Address that is sent to the driver. The - MSB reaches the synapses, the lower 5 bit encode the desired - activation. - :param target_driver: Coordinate of the targeted driver. - - :return: Spike packet to chip. - """ - - label = halco.SpikeLabel() - - # select target PADI bus - label.spl1_address = int( - target_driver.toPADIBusOnPADIBusBlock().toEnum()) - - # select target synram - label.neuron_label = (int( - self._synram_coord.toEnum()) << self.synram_selection_bit) - - # select target driver on the PADI bus - label.row_select_address = int( - target_driver.toSynapseDriverOnPADIBus().toEnum()) - - # set address sent to the driver (MSB + hagen activation) - label.synapse_label = address - - return hal.SpikePack1ToChip([label]) - def reset_synin(self, builder: sta.PlaybackProgramBuilder): """ @@ -402,42 +369,11 @@ class Multiplication: builder.copy_back(self.cached_reset_synin) helpers.wait(builder, 10 * pq.us) - # Optimize runtime if wait_period is 1 and multiple sends - # are requested by copying a vector_builder for each send - if self.num_sends > 1 and self.wait_period <= 1: # pylint: disable=chained-comparison - num_loops = 1 - num_copies = self.num_sends - else: - num_loops = self.num_sends - num_copies = 1 - - entry_counter = 0 - builder.write(halco.TimerOnDLS(), hal.Timer()) - - vector_builder = sta.PlaybackProgramBuilder() - for _ in range(num_loops): - for row, entry in enumerate(vector): - if entry == 0: - continue - - # send event on a different address in order to - # select one of the two rows connected to a driver - entry += 32 if row % 2 == 1 else 0 - vector_builder.write( - halco.SpikePack1ToChipOnDLS(), - self.prepare_event( - halco.SynapseLabel(entry), - halco.SynapseDriverOnSynapseDriverBlock(row // 2))) - - # wait only if needed: - if self.wait_period > 1: - vector_builder.block_until( - halco.TimerOnDLS(), hal.Timer.Value( - int(self.wait_period * entry_counter))) - entry_counter += 1 - - for _ in range(num_copies): - builder.copy_back(vector_builder) + pyccalix.hagen.send_vectors( + builder, vector, num_sends=self.num_sends, + wait_period=self.wait_period, + synram_coord=self.synram_coord, + synram_selection_bit=self.synram_selection_bit) # Read amplitudes coord = halco.CADCSampleRowOnDLS(