Minimal instrument with Koheron SDK

LED blinker for the Red Pitaya has the following files:

├── block_design.tcl
├── config.yml
├── constraints.xdc
├── led_blinker.hpp
├── led_blinker.py
├── test.py
└── web
    ├── app.ts
    ├── control.ts
    ├── index.html
    ├── led_blinker.ts
    └── main.css

Memory

Memory registers are specified in the instrument configuration file.

memory:
  - name: control
    offset: '0x60000000'
    range: 4K
  - name: status
    offset: '0x50000000'
    range: 4K

control_registers:
  - led # defines a control register

status_registers:
  - forty_two # defines a status register

Registers are accessible by their names from Tcl and C++.

FPGA

Block Design

The Vivado block design is generated by the file block_design.tcl:

# Add PS and AXI Interconnect
set board_preset $board_path/config/board_preset.tcl

source $sdk_path/fpga/lib/starting_point.tcl
#source $sdk_path/fpga/lib/starting_point.tcl

# Add config and status registers
source $sdk_path/fpga/lib/ctl_sts.tcl
add_ctl_sts

# Connect LEDs to config register
create_bd_port -dir O -from 7 -to 0 led_o
connect_port_pin led_o [get_slice_pin [ctl_pin led] 7 0]

# Connect 42 to status register
connect_pins [get_constant_pin 42 32] [sts_pin forty_two]

LED Blinker Vivado Block Design

The control and status IPs (ctl and sts) are connected to the port M_AXI_GP0 of the Zynq Processing System (ps_0) via the AXI Interconnect (axi_mem_intercon_0). This connection is clocked with ps_0/FCLK_0 which has been defined to 50 MHz via the parameter fclk0 in the configuration file.

Cores

Cores must be specified in the instrument configuration file.

cores:
  - fpga/cores/axi_ctl_register_v1_0
  - fpga/cores/axi_sts_register_v1_0
  - fpga/cores/dna_reader_v1_0

axi_ctl_register and axi_sts_register are used for communication between the Programmable Logic (PL) and the Processing System (PS) running Linux. Both cores are accessible from Linux as memory-mapped regions.

C++ drivers

C++ Drivers must be specified in the instrument configuration file.

Below is the C++ driver led_blinker.hpp:

#include <context.hpp>

class LedBlinker
{
  public:
    LedBlinker(Context& ctx)
    : ctl(ctx.mm.get<mem::control>())
    , sts(ctx.mm.get<mem::status>())
    {}

    void set_leds(uint32_t led_value) {
        ctl.write<reg::led>(led_value);
    }

    uint32_t get_leds() {
        return ctl.read<reg::led>();
    }

    void set_led(uint32_t index, bool status) {
        ctl.write_bit_reg(reg::led, index, status);
    }

    uint32_t get_forty_two() {
        return sts.read<reg::forty_two>();
    }

  private:
    Memory<mem::control>& ctl;
    Memory<mem::status>& sts;
};

Python

Python scripts are written in line with the Python API.

Driver

from koheron import command

class LedBlinker(object):
    def __init__(self, client):
        self.client = client

    @command()
    def set_leds(self, led_value):
        pass

    @command()
    def get_leds(self, led_value):
        return self.client.recv_uint32()

    @command()
    def set_led(self, index, status):
        pass

    @command()
    def get_forty_two(self):
        return self.client.recv_uint32()

Application

from koheron import connect
import os
import time
from led_blinker import LedBlinker

host = os.getenv('HOST','192.168.1.100')
client = connect(host, name='led-blinker')
driver = LedBlinker(client)

print(driver.get_forty_two())

print('Start blinking...')
for i in range(255):
    driver.set_leds(i)
    time.sleep(0.01)

TypeScript

TypeScript files are written in line with the TypeScript API.

Driver

class LedBlinker {
    private driver: Driver;
    private id: number;
    private cmds: HashTable<ICommand>;

    constructor (private client: Client) {
        this.driver = this.client.getDriver('LedBlinker');
        this.id = this.driver.id;
        this.cmds = this.driver.getCmds();
    }

    getLeds(cb: (value: number) => void): void {
        this.client.readUint32(Command(this.id, this.cmds['get_leds']),
                                 (value) => {cb(value)});
    }

    setLed(index:number, status: boolean): void {
        this.client.send(Command(this.id, this.cmds['set_led'], index, status));
    }

}

Application

class App {
    private driver: LedBlinker;
    public control: Control;

    constructor(window: Window, document: Document, ip: string) {
        let client = new Client(ip, 5);

        window.addEventListener('load', () => {
            client.init( () => {
                this.driver = new LedBlinker(client);
                this.control = new Control(document, this.driver);
            });
        }, false);

        window.onbeforeunload = () => { client.exit(); };
    }
}

let app = new App(window, document, location.hostname);

See also