Skip to content

Welcome

OpenCPI is an open-source data processing framework that facilitates the development and execution of component-based applications on heterogeneous embedded systems.

The framework is primarily used in Software Defined Radio (SDR) applications that leverage a combination of FPGAs and embedded processors.

Note

This documentation serves as a community-created alternative to the primary OpenCPI documentation. It is designed to provide training via practical examples, helping users to better understand the framework.

Installation ⚙

The latest stable version of OpenCPI is v2.4.6. OpenCPI fully supports CentOS 7, with partial support for Ubuntu 20.04. The process of adding support for newer versions of CentOS and Ubuntu is currently underway.

Tip

Using CentOS 7 is strongly recommended for new users of OpenCPI

The OpenCPI installer will request sudo privileges as it automatically installs a number of prerequisite packages.

git clone --branch v2.4.6 https://gitlab.com/opencpi/opencpi.git

cd opencpi

# Temporary patch due to moved prerequisites URL
sed -i 's/opencpi-repo.s3.us-east-2.amazonaws.com\/prerequisites/tukaani.org\/xz/g' build/prerequisites/lzma/install-lzma.sh

./scripts/install-opencpi.sh --minimal #(1)!

echo "source $PWD/cdk/opencpi-setup.sh -s" >> ~/.profile #(2)!
echo "export OCPI_AUTO_BUILD_WORKERS=1" >> ~/.profile #(3)!

reboot #(4)!
  1. A minimal install skips running unit tests after installation, and will not compile rarely used component libraries. This greatly reduces installation time.
  2. The opencpi-setup.sh script sets the environment variables needed to run OpenCPI. This command adds the script to the user's profile, so it is automatically run each time a new terminal is launched.
  3. The OCPI_AUTO_BUILD_WORKERS environment variable tells OpenCPI to automatically build un-built workers when they are needed. Setting this variable is recommended when using the --minimal option.
  4. Recommended to ensure .profile file is reloaded.
git clone --branch v2.4.6 https://gitlab.com/opencpi/opencpi.git && sed -i 's/opencpi-repo.s3.us-east-2.amazonaws.com\/prerequisites/tukaani.org\/xz/g' opencpi/build/prerequisites/lzma/install-lzma.sh && cd opencpi/ && ./scripts/install-opencpi.sh --minimal && echo "source $PWD/cdk/opencpi-setup.sh -s" >> ~/.profile && echo "export OCPI_AUTO_BUILD_WORKERS=1" >> ~/.profile

Target System Installation 📡

Once the main OpenCPI framework is installed, support for specific embedded systems and simulators can be optionally added using the ocpiadmin command.

ocpiadmin install platform <platform_name> --minimal

In most cases, before the platform can be installed, vendor tools for that platform need to be installed. Refer to the full install guide for instructions for each embedded platform.

Learn the Lingo! 📖

Components

Components serve as the fundamental building blocks in OpenCPI. Each component performs a specific function and contain ports that allow data to be sent and received. Additionally, components have properties that enable their configuration and control. To define a component in OpenCPI, developers use an OpenCPI Component Specification (OCS) file, which is written in XML. This file lists all the component's ports and properties and sets their respective attributes.

Example OCS [bias_s-spec.xml]
<componentspec>
    <property name="bias" type="short" writable="true" default="1" 
                description="Bias value added to input values."/>
    <port name="input" producer="false" protocol="short_timed_sample-prot"/>
    <port name="output" producer="true" protocol="short_timed_sample-prot"/>
</componentspec>

Protocols

OpenCPI protocols define the format of data that can be sent on a port. Protocols are defined by an OpenCPI Protocol Specification (OPS) XML file. A protocol can have a number of opcodes, where each opcode defines a fixed data structure that can be transmitted over a port. Data structures can be as simple as a single value, or as complex as a sequence of nested structs. Ports without protocols specified can transmit raw data.

Workers

OpenCPI Workers are implementations of a component specifications targeted towards a specific processing technology. OpenCPI uses the term "authoring model" to refer to a language model suited for the target technology. Currently two authoring models are supported:

  1. Resource-Constrained C Language (RCC) is used to generate code to execute on a general-purpose processor (GPP), and is written using the C++ language.
  2. Hardware Definition Language (HDL) is used to generate workers that run on FPGAs, and is usually written in VHDL.

Each worker is comprised of source files that are relevant to its authoring model. In addition, there is an OpenCPI Worker Description (OWD) XML file associated with each worker. The OWD file enables worker-specific attributes and properties to be configured and added.

bias_s.rcc/bias_s.xml
<rccworker language="c++" spec="bias_l-spec"> 
</rccworker> <!--(1)!-->
  1. No worker specific attributes or properties are needed for simple RCC workers
bias_s.rcc/bias_s.cc
#include "bias_s-worker.hh"
using namespace OCPI::RCC;
using namespace Bias_sWorkerTypes;

class Bias_sWorker : public Bias_sWorkerBase {
    RCCResult run(bool) {
        if (input.opCode() == Short_timed_sampleSample_OPERATION) {
            size_t length = input.sample().data().size();
            const int16_t *inData = input.sample().data().data();
            int16_t *outData = output.sample().data().data();

            output.setOpCode(Short_timed_sampleSample_OPERATION);
            output.sample().data().resize(length);

            for (size_t i = 0; i < length; i++) {
                outData[i] = inData[i] + properties().bias;
            }
            return RCC_ADVANCE;
        }
        return RCC_ERROR;
    }
};
BIAS_S_START_INFO
BIAS_S_END_INFO
bias_s.hdl/bias_s.xml
<hdlworker language="vhdl" spec="bias_s-spec" version="2">
    <streaminterface name="input" datawidth="16"/>  <!--(1)!-->
    <streaminterface name="output" datawidth="16"/>
</hdlworker>
  1. In HDL workers, ports are implemented as streaming interfaces. The width of this interface must be set for each port. A 16 bit wide interface can deliver up to 16 bits of data on every FPGA clock cycle. The width is typically set to match size of the main datatype transmitted over the interface, in this case a short int.
bias_s.hdl/bias_s.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library ocpi;
use ocpi.types.all;

architecture rtl of worker is
begin
    input_out.take <= output_in.ready;
    output_out.data <= std_logic_vector(signed(input_in.data) + props_in.bias)
                        when input_in.opcode = short_timed_sample_sample_op_e
                        else input_in.data;
    output_out.eom         <= input_in.eom;
    output_out.valid       <= input_in.valid;
    output_out.give        <= input_in.ready;
    output_out.opcode      <= input_in.opcode;
    output_out.byte_enable <= input_in.byte_enable;
end rtl;

Applications

An OpenCPI application is collection of components connected together and configured to perform a higher level function. Applications are described by an XML file called the OpenCPI Application Specification (OAS). An OAS can be run directly using the ocpirun command. Alternatively, an OAS can be constructed programmatically and an API, known as the Application Control Interface (ACI), can be used to run and interact with an application's ports and properties during runtime. ACI applications are written in C++.

bias_app.xml
<application done="file_write">
    <instance component="ocpi.core.file_read" connect="bias_s"> <!--(1)! -->
        <property name="filename" value="in_file.bin"/>
    </instance>
    <instance component="ocpi.comp.sdr.math.bias_s" connect="file_write">
        <property name="bias" value="5"/>
    </instance>
    <instance component="ocpi.core.file_write"> <!--(2)! -->
        <property name="filename" value="out_file.bin"/>
    </instance>
</application>
  1. ocpi.core.file_read is a built-in OpenCPI component that handles reading from a binary file and sending that data on an OpenCPI port.
  2. ocpi.core.file_write is a built-in OpenCPI component that handles reading from an OpenCPI port and writing it to a binary file.
classDiagram
`ocpi.comp.sdr.math.bias_s` <|-- `ocpi.core.file_read`
`ocpi.comp.sdr.math.bias_s` --|> `ocpi.core.file_write`
`ocpi.comp.sdr.math.bias_s` : bias="5"
`ocpi.core.file_read` : filename="in_file.bin"
`ocpi.core.file_write` :  filename="out_file.bin"
bias_app.cpp
#include <iostream>
#include <string>
#include "OcpiApi.hh"

namespace OA = OCPI::API;

int main() {
    try {
        char in_filename[2000];
        OA::Application app("bias_app.xml");
        app.initialize();

        OA::Property in_file(app, "file_read", "filename");
        OA::Property bytes(app, "file_write", "bytesWritten");
        OA::Property bias(app, "bias_s", "bias");

        bias.setULongValue(10);  // Set bias property to 10
        in_file.getStringValue(in_filename, sizeof(in_filename));
        std::cerr << "Input file: " << in_filename << std::endl;

        app.start();  // Start the application
        while(app.wait(100)) {  //Run every 100us until the app is done
            std::cerr << "Written " << bytes.getULongLongValue();
            std::cerr << " bytes" << std::endl;
        }
        app.stop();
    } catch (std::string &e) {
        std::cerr << "ERROR: " << e << std::endl;
        return 1;
    }
    return 0;
}

Hello, World! 👋

OpenCPI uses a tool called ocpidev to create the various assets needed to make OpenCPI applications.