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
Introduction to the Chimera SDK
Chimera SDK Quick Start Guide
Chimera SDK Command Line Interface (CLI)
Tutorial: Using SDK as a Library
Chimera Compute Library (CCL) API
Chimera LLVM C++ Compiler
Chimera SDK Licensing Policy Documentation
Glossary
Chimera Software User GuideChimera Compute Library (CCL) APITutorial: Creating a Simple Kernel

Tutorial: Creating a Simple Kernel

Introduction

This tutorial shows you how to create a simple kernel which flows in a tensor from DDR into the Array via the L2 memory, then performs operations on the tensor, and finally flows the tensor back out to DDR memory.

Refer to the Chimera Software API Reference guide for more information on the APIs described in this section.

Requirements

You need to first install the Quadric SDK.

Getting Started

Follow the steps below to create a simple kernel:

  • Add the appropriate Quadric C++ includes to the top of your .cpp file:
File: /tests/static_data_tests/simple_flow.cppLines 19–23
//! [Adding Quadric C++ Headers]
// Include quadric Intermediate Language Header.
#include <quadric/host.h>
#include <quadric/qil.h>
using namespace chimera;
  • Define the shapes of the tensors you wish to flow into the Chimera processor:
File: tests/static_data_tests/simple_flow.cppLines 26–42
constexpr std::uint32_t height    = core_array::coreDim;
constexpr std::uint32_t width     = core_array::coreDim;
constexpr std::uint32_t depth     = 1;
constexpr std::uint32_t batchSize = 1;

//! [Adding Tensor Defs]
/*
 * Define shapes of Tensors, Note there's a DDR Tensor
 * and also an OcmTensor shape. The types DdrInOutShape and  OcmInOutShape
 * contain the attributes of the data in DDR and OCM respectively.
 */
typedef DdrTensor<std::int32_t, batchSize, depth, height, width> DdrInOutShape;
typedef OcmTensor<std::int32_t, batchSize, depth, height, width> OcmInOutShape;
//! [Adding Tensor Defs]

//! [OcmRoi]
typedef OcmTensor<std::int32_t, batchSize, depth, height, width> minRoiInOutShape;
  • Define the CPU version of the algorithm you want to implement. You can use this as a reference to measure the accuracy of the quadric model.
File: /tests/static_data_tests/simple_flow.cppLines 111–125
/*
Implement CPU version of the algorithm
This can be used as a reference to validate the quadric generated output.
*/
#ifndef __epu__
void generateOutput(DdrInOutShape& ddrInputImage, DdrInOutShape& ddrOutputImage) {
  auto ddrInp = DdrInOutShape::cast(ddrInputImage);
  auto ddrOut = DdrInOutShape::cast(ddrOutputImage);
  for(std::int32_t y = 0; y < height; ++y) {
    for(std::int32_t x = 0; x < width; ++x) {
      (*ddrOut)[0][0][y][x] = (*ddrInp)[0][0][y][x] + 1;
    }
  }
}
#endif
  • Define a kernel function with a return type of void and DDR tensors are passed as pointers arguments:
File: /tests/static_data_tests/simple_flow.cppLine 48
EPU_ENTRY void myKernel(DdrInOutShape::ptrType ddrInpPtr, DdrInOutShape::ptrType ddrOutPtr) {
  • DDR tensors are passed in as pointers, so recreate the tensor object as shown below:
File: /tests/static_data_tests/simple_flow.cppLines 53–55
  //! [Recreate Tensor Obj]
  DdrInOutShape ddrInp(ddrInpPtr);
  DdrInOutShape ddrOut(ddrOutPtr);
  • Create L2 memory tensors and allocate them:
File: /tests/static_data_tests/simple_flow.cppLines 58–64
  //! [Create OcmTensors]
  OcmInOutShape ocmInp;
  OcmInOutShape ocmOut;
  // Create an instance of the On Chip Memory (OCM) Memory Allocator
  MemAllocator ocmMem;
  ocmMem.allocate(ocmInp);
  ocmMem.allocate(ocmOut);
  • Copy memory data from DDR to OCM using memCpy in the memCpy API:
File: /tests/static_data_tests/simple_flow.cppLines 67–68
  //! [Do inbound Memcpy]
  memCpy(ddrInp, ocmInp);
  • Define TensorAccessors for reading and writing:
File: /tests/static_data_tests/simple_flow.cppLines 71–89
  • In this special case, the shapes of both our input and output tensors are the same (minRoiInOutShape), so we can define a single 'TensorAccessor' for reading and writing (input and output).
  /*The first parameter of TensorAccessor is MinRoiDescriptor.
    This object specifies;
      1) Shape of the MinROI
      2) AxisGroup, how the data is arranged within the MinROI,(Order of the Axis priority)
      3) Granularity shape: Square or Row
      4) Border: Whether we want data in border cores.
    If the input and output shapes are different, you need to define two MinRoiDescriptor.
  */
  using MroiDescp =
    MinRoiDescriptor<minRoiInOutShape, AxisGroup<Direction::Height, Direction::Width>, Granularity::Square, true>;

  /*
  A TensorAccessor object expects two parameters.
    1) MinRoiDescriptor: The MinRoiDescriptor.(See the above definition)
    2) RoiAxisGroup: The RoiAxisGroup indicates the moving pattern for the MinROI inside the ROI.

  If the input and output shapes are different, you need to define two TensorAccessors.
  */
  using InOutAccessor = TensorAccessor<MroiDescp, AxisGroup<Direction::Width>>;
  • Define flow objects for reading and writing data:
File: /tests/static_data_tests/simple_flow.cppLines 91–95
  /*
      Define flow objects for reading and writing
  */
  ReadFlow<InOutAccessor, OcmInOutShape>  readFlow(ocmInp);
  WriteFlow<InOutAccessor, OcmInOutShape> writeFlow(ocmOut);
  • Create NDArray Objects to store tiles, and flow in data from/to L2 memory to/from the LRM (Local Register Memory) Array:
File: /tests/static_data_tests/simple_flow.cppLines 98–99
    auto                                                    qIn = readFlow.read();
    WriteFlow<InOutAccessor, minRoiInOutShape>::MinRoiArray qOut;
  • Perform an operation on each tile of data. In the example below, a 1 is added to each tile of data:
File: /tests/static_data_tests/simple_flow.cppLines 101–103
    for(std::size_t tileIdx = 0; tileIdx < qIn.size(); ++tileIdx) {
      qOut[tileIdx] = qIn[tileIdx] + 1;
    }
  • Flow out data from the array to the L2 memory:
File: /tests/static_data_tests/simple_flow.cppLine 104
    writeFlow.write(qOut);
  • Finally, write the data to DDR:
File: /tests/static_data_tests/simple_flow.cppLines 106–107
  //! [Write to DDR]
  memCpy(ocmOut, ddrOut);
  • Perform host-side set up to launch the kernel with data:
File: /tests/static_data_tests/simple_flow.cppLines 129–174
HOST_MAIN(
  // Create DDR Tensors on the host computer, one for input, one for output,
  // one for expected output(from the CPU model)
  DdrInOutShape ddrInPtr; DdrInOutShape ddrOutPtr; DdrInOutShape ddrOutExp;

  // Allocate tensors on the host computer.
  DdrInOutShape::allocate(ddrInPtr);
  DdrInOutShape::allocate(ddrOutPtr);
  DdrInOutShape::allocate(ddrOutExp);

  // Populate the Tensor sequentially.
  populateTensorSequential(ddrInPtr);

  generateOutput(ddrInPtr, ddrOutExp);

  // Wrap the Tensor in a TensorArg meant to pass to the kernel.
  TensorArg<DdrInOutShape> inputArg{&ddrInPtr};
  TensorArg<DdrInOutShape> outputArg{&ddrOutPtr};

  //! [Call Kernel]
  packageKernel(OUTPUT_PREFIX, FUNC_ARG(myKernel), inputArg, outputArg);

  DeviceBufferRef in, out;

  auto device = DeviceManager().getDevice(callKernelGlobals.deviceConfig);
  device.loadKernel();
  device.allocateAndCopyToDevice(ddrInPtr, in);
  device.allocate<DdrInOutShape>(out);

  device.runKernel(ENTRYPOINT(myKernel), in, out);

  device.copyBufferFromDevice(out, ddrOutPtr);
  //! [Call Kernel]

  // Print execution cycle count breakdown
  device.printProfile();

  // Compare results
  auto nativeCompareVisitor =
    [](chimera-software-user-guide/chimera-compute-library-ccl-api/const auto& t1, const auto& t2, const DimensionContext& context) {
      return nativeCompare<DdrInOutShape>(t1, t2, context, 1);
    };

  runtime_assert(compareTensors(ddrOutPtr, ddrOutExp, nativeCompareVisitor), "tensor mismatch");

);
  • Launch the kernel using the SDK:

$ quadric sdk source simple_flow.cpp

The following example shows the complete code for a kernel that includes all of the steps described above: (sdk) tests/static_data_tests/simple_flow.cpp

Table of Contents
Introduction to the Chimera SDK
Chimera SDK Quick Start Guide
Chimera SDK Command Line Interface (CLI)
Tutorial: Using SDK as a Library
Chimera Compute Library (CCL) API
Chimera LLVM C++ Compiler
Chimera SDK Licensing Policy Documentation
Glossary


© 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.