UH-OH

It looks like you don’t have access to that feature yet

Contact sales to get upgraded to the full DevStudio experience.

UH-OH

It looks like you don't have access to that feature yet.

to select
to navigate
escto close
Chimera Compute Library (CCL) API ReferenceData Access PatternsBroadcasting Data

Broadcasting Data

Quadric's Chimera architecture supports three different data access patterns from L2 Memory ↔︎ LRM (qVar) for data movement between different levels of memory hierarchy.

The Broadcast data access pattern or broadcasting for short, is a one-to-many data transfer in which each element within a region of interest (ROI) is written from L2 memory to the LRM of every PE in the array. Broadcasting data from L2 Memory to all PEs on the array is done using the dedicated Broadcast Bus which enables a user to send scalar data to all PEs with minimal clock cycles. The Broadcast Bus size is 64 bits and is only available on the west side of the Chimera GPNPU array.

BroadcastFlow Class

The class for broadcast, it provides capacity to stage a MinROI (required before consuming and a MinROI needs to be finished before reading the next can be started), read part or whole of MinROI in the multiple of 8 bytes, and maintain the offsets when moving MinROI inside the ROI.

File: /src/core/flows/broadcast_flow.hppLines 342–364
  /**
   * @brief The class for broadcast, it provides capacity to stage a MinROI (required before consuming and a MinROI
   * needs to be finished before reading the next can be started), read part or whole of MinROI in the multiple of 8
   * bytes, and maintain the offsets when moving MinROI inside the ROI
   *
   * @tparam Access the Accessor defining the MinRoi and RoiAxisGroup, other members will be ignored for broadcast
   * @tparam OCMShape The OCM tensor type.
   * @tparam RoiShape The ROI shape.
   * @tparam numberOfPartitions Number of VAP partitions to create. Use `1` if not needed.
   * @tparam autoStage Whether or not to call `stageMROI()` to stage the broadcast bus automatically. By default, it stages when: 1) the class is constructed 2) when a read is attempted and the previous MinROI is completely consumed. It comes with an overhead, this option should be disabled when lots of small read are performed to save cycles.
   * @note RuntimeTensor support:
   *   - For RuntimeTensors, PLS counts are auto-clamped to min(MinRoi, tensor dims) at each staging.
   * @tparam repetitions The number of times to repeat the flow.
   * @tparam repetitionAxis The axis to repeat.
   */
  template <typename Access,
            typename OCMShape,
            typename RoiShape               = OCMShape,
            std::uint8_t numberOfPartitions = 1,
            bool         autoStage          = true,
            std::int32_t repetitions        = 1,
            Direction    repetitionAxis = Direction::Unused>
  struct BroadcastFlow : detail::AutoStage<autoStage> {

Utility Methods

File: /src/core/flows/broadcast_flow.hppLines 414–425
    /**
     * @brief Read from a PE array via broadcast.
     *
     * The memory being read must be a multiple of 8 bytes and cannot be larger than what is
     * remaining in the current staged MinROI (or the whole MinROI if starting a new MinROI).
     *
     * @tparam T The data type to read. Default is `OCMShape::elemType`.
     * @tparam size The number elements to read.
     * @return container::NDArray<qVar_t<T>, size>
     */
    template <typename T = typename OCMShape::elemType, std::size_t size = numTiles<T>()>
    INLINE container::NDArray<qVar_t<T>, size> read() {

Examples

In this example, we setup a BroadcastFlow for an internal DMA transfer which stores the broadcast values in an NDArray named broadcastRead. We then iterate over the broadcast values and store the sum of those values in an output variable out.

File: /tests/static_data_tests/broadcast_stream_unit.cppLines 129–137
    BroadcastFlow<Access, OcmInShape> broadcastFlow(ocmInp);
    constexpr std::size_t             numTiles = broadcastFlow.numTiles();

    const container::NDArray<qVar_t<int>, 256UL> broadcastRead = broadcastFlow.read();

    qVar_t<std::int32_t> out = 0;
    for(std::size_t i = 0; i < numTiles; i++) {
      out += broadcastRead[i];
    }

Queue a Tensor to be broadcasted using the Broadcast bus.

File: /src/core/flows/broadcast_flow.hppLines 431–436
    /**
     * @brief Setup the broadcast bus for the next MinROI.
     * For RuntimeTensors, counts are auto-clamped to min(MinRoi, tensor) per dimension.
     * For static tensors, compile-time counts from DataAccessTraits are used.
     */
    void stageMROI() {

Examples

File: /tests/static_data_tests/broadcast_stream_unit.cppLines 107–117
    BroadcastFlow<Access16, OcmIn16Shape, OcmIn16Shape, 1, false> broadcastFlow(ocmInp16);
    broadcastFlow.stageMROI();

    qVar_t<std::int32_t> out = 0;

    for(std::int32_t i = 0; i < numBroadcasts16; i++) {
      container::NDArray<qVar_t<std::int16_t>, numRegs16> arr = broadcastFlow.read<std::int16_t, numRegs16>();
      for(std::size_t i = 0; i < numRegs16; i++) {
        out += arr[i];
      }
    }

Public Members

File: /src/core/flows/broadcast_flow.hppLines 402–409
    /**
     * @brief Returns the size of the MinROI in bytes.
     *
     * @tparam T The data type used in the Broadcast operation. Default is `OCMShape::elemType`.
     * @return std::int32_t The size of the MinROI in bytes.
     */
    template <typename T = typename OCMShape::elemType>
    static constexpr std::int32_t numTiles() {

Examples

File: /tests/static_data_tests/broadcast_stream_unit.cppLines 132–137
    const container::NDArray<qVar_t<int>, 256UL> broadcastRead = broadcastFlow.read();

    qVar_t<std::int32_t> out = 0;
    for(std::size_t i = 0; i < numTiles; i++) {
      out += broadcastRead[i];
    }

How to use broadcasting?

  1. First declare the
    File: /src/core/flows/broadcast_flow.hppSymbol: BroadcastFlow
    BroadcastFlow
  • BroadcastFlow<Access, OcmInOutShape> broadcastFlow(ocmInp);

  • Then consume the broadcasted values.

  1. container::NDArray<qVar_t<std::int32_t>, broadcastFlow.numTiles()> broadCastRead = broadcastFlow.read();

Broadcasting is a technique that is commonly used in conjunction with convolution-related operators. In particular, it is utilized to transfer convolution parameters, such as weights and biases, to the LRM.

To illustrate this process, consider the following code snippet:

File: /tests/static_data_tests/nn_conv3x3_i8_v2.cppLines 53–64
  for(std::uint32_t tileNum = 0; tileNum < inputFlow.numMROIs(); tileNum++) {
    auto                                                 qInput = inputFlow.read();
    BroadcastFlow<BroadcastAccess, OcmWeightTensorShape> broadcastFlow(weightsTensor);

    for(std::uint32_t channel = 0; channel < OcmOutShape::NUM_CHN; channel++) {
      qVar_t<std::int32_t> accumOutput  = nn::convTileBlockInt8<std::int32_t, OcmInputShape::NUM_CHN, 3>(qInput);
      auto                 scaledOutput = static_cast<std::int32_t>(((accumOutput * scale) + bias) >> shift);
      qOutput[channel]                  = static_cast<std::int8_t>(
        scaledOutput > int8High ? int8High : (scaledOutput < int8Low ? int8Low : scaledOutput));
    }
    outputFlow.write(qOutput);
  }

The provided code utilizes the

File: /src/core/flows/broadcast_flow.hppSymbol: BroadcastFlow
BroadcastFlow
class to broadcast the ocmWeights tensor to the PE. Subsequently, the convolution operation is performed for each channel in the output tensor using the
File: /src/neuralNetBlocks/conv.hppSymbol: convTileBlockInt8
convTileBlockInt8
function.

It should be noted that the

File: /src/neuralNetBlocks/conv.hppSymbol: convTileBlockInt8
convTileBlockInt8
function automatically consumes the corresponding weights for each output channel.

As a result, the user need not concern themselves with the low-level details of the broadcast process. Instead, they can simply utilize the

File: /src/neuralNetBlocks/conv.hppSymbol: convTileBlockInt8
convTileBlockInt8
function to perform the convolution operation with the correct weights for each output channel.

VAP (Virtual Array Partitioning)

The VAP (Virtual Array Partition) feature is an advanced technique that enables the efficient utilization and allocation of computational resources within the array. By partitioning the tile into smaller chunks, it allows broadcasting different weights for each partition, thereby optimizing the processing capabilities by increasing the core utilization.

Usage

To enable VAP, you can use our standard Broadcast API with the appropriate numberOfPartitions parameter:

File: /src/core/flows/broadcast_flow.hppLines 342–364
  /**
   * @brief The class for broadcast, it provides capacity to stage a MinROI (required before consuming and a MinROI
   * needs to be finished before reading the next can be started), read part or whole of MinROI in the multiple of 8
   * bytes, and maintain the offsets when moving MinROI inside the ROI
   *
   * @tparam Access the Accessor defining the MinRoi and RoiAxisGroup, other members will be ignored for broadcast
   * @tparam OCMShape The OCM tensor type.
   * @tparam RoiShape The ROI shape.
   * @tparam numberOfPartitions Number of VAP partitions to create. Use `1` if not needed.
   * @tparam autoStage Whether or not to call `stageMROI()` to stage the broadcast bus automatically. By default, it stages when: 1) the class is constructed 2) when a read is attempted and the previous MinROI is completely consumed. It comes with an overhead, this option should be disabled when lots of small read are performed to save cycles.
   * @note RuntimeTensor support:
   *   - For RuntimeTensors, PLS counts are auto-clamped to min(MinRoi, tensor dims) at each staging.
   * @tparam repetitions The number of times to repeat the flow.
   * @tparam repetitionAxis The axis to repeat.
   */
  template <typename Access,
            typename OCMShape,
            typename RoiShape               = OCMShape,
            std::uint8_t numberOfPartitions = 1,
            bool         autoStage          = true,
            std::int32_t repetitions        = 1,
            Direction    repetitionAxis = Direction::Unused>
  struct BroadcastFlow : detail::AutoStage<autoStage> {

To enable VAP, you can use our standard Broadcast API with the appropriate

File: /src/core/flows/broadcast_flow.hppSymbol: numberOfPartitions
numberOfPartitions
parameter:

Note: VAP is only supported for the following pairs of

File: /src/core/flows/broadcast_flow.hppSymbol: numberOfPartitions
numberOfPartitions
options and GPNPU hardware configurations. If you provide other values, it will act as if
File: /src/core/flows/broadcast_flow.hppSymbol: numberOfPartitions
numberOfPartitions
is set to 1.

Chimera GPNPUSupported Number of Partitions (
File: /src/core/flows/broadcast_flow.hppSymbol: numberOfPartitions
numberOfPartitions
)
QB11
QB41, 4
QB161, 4, 16

Examples

Assume we have the following weight tensor of shape (1,1,1,32) and type int8:

If we use 4 partitions, i.e.

File: /src/core/flows/broadcast_flow.hppSymbol: numberOfPartitions
numberOfPartitions
= 4, and broadcast data to LRM, the tiles will look like following:

Tile 1

Tile 2

Without VAP, the tiles will look like follows

Tile 1

Tile 2

Arrangement also depends on the number of partitions.

The diagram below illustrates how elements are arranged when the number of VAP partitions is set to 16. (This is only valid for QB16.) (Assume an int8 tensor)

Tile 1

The number of elements you can broadcast simultaneously depends on the data size, such as whether the element is an int8, int16, and so on. Consequently, the weight arrangement in the tile partitions also depends on the data type.

Please refer to the illustrations below to gain a better understanding of how the data is organized.

int8

Input tensor for broadcasting

If we rearrange this data in order we receive at LCM, it looks like follows.


The Quadric Broadcast Bus has a width of 64 bits, allowing it to transfer 8 units of int8 data in a single transfer. During the first broadcast iteration, it fetches the data up to the 31st value (32 values in total), which is equal to 8 (number of units of data) multiplied by 4 (number of partitions).
The remaining data will be fetched in the subsequent iterations. (if the input tensor size is greater than 32)

int16

Input tensor for broadcasting

If we rearrange this data in order we receive at LCM, it lookslike follows.


The Quadric Broadcast Bus has a 64-bit width, enabling it to transfer 4 units of int16 data in a single transfer. In the first broadcast iteration, the bus fetches data up to the 15th value (a total of 16 values), which is calculated as 4 (number of units of data) times 4 (number of partitions). The remaining data will be fetched in the subsequent iterations.

int32

Input tensor for broadcasting

If we rearrange this data in order we receive at LCM, it looks-like follows.

The Quadric Broadcast Bus features a 64-bit width, allowing it to transfer 2 units of int32 data in a single transfer.

During the first broadcast iteration, the bus fetches data up to the 7th value (a total of 8 values), which is calculated as 2 (number of units of data) times 4 (number of partitions).

The remaining data will be fetched in the subsequent iterations.

Table of Contents


© Copyright 2026 Quadric All Rights Reserved • Privacy Policy
This documentation is preliminary and confidential. It is subject to change. Quadric does not give any warranty express or implied that the contents will be complete or accurate or up to date. The company shall not be liable for any loss, actions, claims, proceedings, demands or costs or damages whatsoever or howsoever caused arising directly or indirectly in connection with or arising out of the use of this material.

Sign in to your account

Don't have an account? Create an Account
By signing in, you are agreeing to our Terms of Use and Privacy Policy.

Develop.

Simulate.

Profile.

Collaborate.

We use cookies to enhance your browsing experience, and analyze our traffic.
By clicking "Accept All", you consent to our use of cookies.