High Level Synthesis, low level I/O, and the RP

Just about everything about Red Pitaya
Post Reply
lhochstetter
Posts: 55
Joined: Tue Mar 01, 2016 1:43 pm

High Level Synthesis, low level I/O, and the RP

Post by lhochstetter » Sun May 21, 2017 2:09 pm

Hello everyone,

it's been a while. This time around I'd like to share my knowledge regarding low level I/O and High Level Synthesis (HLS) in conjunction with the Red Pitaya.

First I'll briefly introduce HLS (what it is / does, and why it was developed). Then I'll show you how to implement a simple binary counter and talk about some of the bigger issues I encountered during the implementation.

Note: This guide is for the Vivado Design Suite 2016.2 (WebPack) on Ubuntu 14.04.5 64 bit, and RP OS v0.95 - if you're using different versions YMMV.

High Level Synthesis

FPGAs offer besides high I/O performance a lot of computational power which you could use to e.g. accelerate an edge detection algorithm over an equivalent CPU implementation. When it comes to high level algorithms you want to focus on the algorithm itself and not the underlying hardware (be it a CPU, GPU, or an FPGA).

HLS enables you to implement a functionality in a high level programming language (C, C++, SystemC, OpenCL Kernel) and synthesize it into equivalent RTL code which can then be synthesized for and implemented on an FPGA.

There are however some limitations about language specific constructs e.g. you can not use system calls like malloc() or usleep().

Testbenches

Testbenches are used for the C/C++/SystemC/OpenCL kernel code and the RTL implementation to verify that for a given input the proper output is generated.

Note:

You can only validate that correct results are produced - if you have hard deadlines regarding code execution speed, you'll have to write a RTL testbench to validate that the synthesized RTL code executes within the maximal amount of time.

This is also true for any variable / value (button pressed / released) that could change "outside" of your code.

Binary counter

The idea for the binary counter is based on an [url=http://blog.redpitaya.com/examples-new/ ... led-diode/] example [url] by the Red Pitaya Team.

Two push buttons are connected as shown using the GPIO Header and are used to increment / decrement a value in steps of one unit. The value (8 bit, 0-255) is then shown in binary as LEDs being turned on / off.

I used C as the source language for HLS.

The first listing shows the header file. The function prototype declares the so called top function "push_button_counter" which has three parameters "inc_i", "dec_i", and "led_o". The booleans "inc_i" and "dec_i" are the inputs which are connected to the pins which again are connected to the GPIO pins which are finally connected to the push buttons. The "led_o" parameter is the output and will be connected to the LEDs.

Code: Select all

#ifndef push_button_counter_h
#define push_button_counter_h

#include <stdbool.h>

void push_button_counter(bool inc_i, bool dec_i, unsigned char *led_o);

#endif
The second listing shows the actual source code. The "inc_i" and "dec_i" parameters are declared as booleans since HLS will synthesize them into one bit wide input ports. The same goes for the "led_o" parameter which will be synthesized into an eight bit wide output port.

We have to detect when a button is pressed / released (i.e. positive / negative edge detection). Therefore two boolean arrays with two entries "pos_edge_inc" and "pos_edge_dec" are created. The "static" keyword allows them to keep their values between to clock signals.

The "value" variable stores the current counter value.

The function is implemented in such a way that it can be called every clock signal. This results in the button states being sampled at the frequency declared in the compiler script below (100MHz). The button state then is stored in the "pos_edge_inc" / "pos_edge_dec" arrays. The if - else if block increments / decrements the "value" variable.

The if - else if construct is necessary to allow for the function to be synthesized so it can be called each clock signal. If you used two if blocks it wouldn't be possible to decide in one clock cycle which if branch should be executed.

The if condition for the increment part checks if there is a positive edge (0->1) "pos_edge_inc[0] && !pos_edge_inc[1]" and that the decrement button isn't pressed to prevent data races and that the "value" is below 255 (if it reaches 256, all LEDs would turn off, as we use the binary representation of the "value" variable to toggle the LEDs). The decrement part works analogously.

The "value" is finally assigned to the "led_o" pointer so the LEDs light up accordingly.

Note: it seems that the GPIO pins are logical high by default, therefore I had to set the initial values of the "pos_edge_*" arrays to "1". If I had set them to "0" the code would have recognised it as a pressed button and turned the LEDs on / off accordingly.

Code: Select all

#include <stdbool.h>

#include "push_button_counter.h"

void push_button_counter(bool inc_i, bool dec_i, unsigned char *led_o) {

	static bool pos_edge_inc[2] = {1, 1};
	static bool pos_edge_dec[2] = {1, 1};

	static unsigned char value = 0;

	pos_edge_inc[1] = pos_edge_inc[0];
	pos_edge_inc[0] = inc_i;

	pos_edge_dec[1] = pos_edge_dec[0];
	pos_edge_dec[0] = dec_i;

	if (pos_edge_inc[0] && !pos_edge_inc[1] && dec_i && 255 > value) {
		value += 1;
		*led_o = value;
	} else if (pos_edge_dec[0] && !pos_edge_dec[1] && inc_i && 0 < value) {
		value -= 1;
		*led_o = value;
	}
}
The third listing shows the testbench. Since I can't simmulate button pushed properly I could only mimic the changes "1->0" button pressed, and "0->1" button released. As said above this is one of the drawbacks of - in this case - C based HLS. Depending on the source language YMMV regarding validation options.

Code: Select all

#include <stdbool.h>
#include <stdio.h>
#include "test.h"

#include "push_button_counter.h"

#define SAMPLE_SIZE 529

extern unsigned int err_cnt;

void test(void) {

	struct sample {
		bool inc;
		bool dec;
		unsigned char expected_result;
	};
	/*
	 * Note: the Red Pitaya exp_n_io pins are high by default,
	 * when a button is pressed the pin is set to 0
	 *
	 * Therefore we have to assume inc and dec high by default
	 */
	struct sample samples[SAMPLE_SIZE] = {
		/* idle */
		{ .inc = 1, .dec = 1, .expected_result = 0},
		{ .inc = 1, .dec = 1, .expected_result = 0},
		{ .inc = 1, .dec = 1, .expected_result = 0},
		/* underflow */
		{ .inc = 1, .dec = 0, .expected_result = 0},
		{ .inc = 1, .dec = 1, .expected_result = 0},
		/* inc button is pushed */
		{ .inc = 0, .dec = 1, .expected_result = 0},
		{ .inc = 1, .dec = 1, .expected_result = 1},
		/* inc button is pushed for a longer time */
		{ .inc = 0, .dec = 1, .expected_result = 1},
		{ .inc = 0, .dec = 1, .expected_result = 1},
		{ .inc = 1, .dec = 1, .expected_result = 2},
		/* dec button is pushed */
		{ .inc = 1, .dec = 0, .expected_result = 2},
		{ .inc = 1, .dec = 1, .expected_result = 1},
		{ .inc = 1, .dec = 1, .expected_result = 1},
		/* dec button is pushed for a longer time */
		{ .inc = 1, .dec = 0, .expected_result = 1},
		{ .inc = 1, .dec = 0, .expected_result = 1},
		{ .inc = 1, .dec = 0, .expected_result = 1},
		{ .inc = 1, .dec = 1, .expected_result = 0},
		/* inc to 255 */
		{ .inc = 0, .dec = 1, .expected_result = 0},
		{ .inc = 1, .dec = 1, .expected_result = 1},
		{ .inc = 0, .dec = 1, .expected_result = 1},
		{ .inc = 1, .dec = 1, .expected_result = 2},
		{ .inc = 0, .dec = 1, .expected_result = 2},
		{ .inc = 1, .dec = 1, .expected_result = 3},
		{ .inc = 0, .dec = 1, .expected_result = 3},
		{ .inc = 1, .dec = 1, .expected_result = 4},
		{ .inc = 0, .dec = 1, .expected_result = 4},
		{ .inc = 1, .dec = 1, .expected_result = 5},
		{ .inc = 0, .dec = 1, .expected_result = 5},
		{ .inc = 1, .dec = 1, .expected_result = 6},
		{ .inc = 0, .dec = 1, .expected_result = 6},
		{ .inc = 1, .dec = 1, .expected_result = 7},
		{ .inc = 0, .dec = 1, .expected_result = 7},
		{ .inc = 1, .dec = 1, .expected_result = 8},
		{ .inc = 0, .dec = 1, .expected_result = 8},
		{ .inc = 1, .dec = 1, .expected_result = 9},
		{ .inc = 0, .dec = 1, .expected_result = 9},
		{ .inc = 1, .dec = 1, .expected_result = 10},
		{ .inc = 0, .dec = 1, .expected_result = 10},
		{ .inc = 1, .dec = 1, .expected_result = 11},
		{ .inc = 0, .dec = 1, .expected_result = 11},
		{ .inc = 1, .dec = 1, .expected_result = 12},
		{ .inc = 0, .dec = 1, .expected_result = 12},
		{ .inc = 1, .dec = 1, .expected_result = 13},
		{ .inc = 0, .dec = 1, .expected_result = 13},
		{ .inc = 1, .dec = 1, .expected_result = 14},
		{ .inc = 0, .dec = 1, .expected_result = 14},
		{ .inc = 1, .dec = 1, .expected_result = 15},
		{ .inc = 0, .dec = 1, .expected_result = 15},
		{ .inc = 1, .dec = 1, .expected_result = 16},
		{ .inc = 0, .dec = 1, .expected_result = 16},
		{ .inc = 1, .dec = 1, .expected_result = 17},
		{ .inc = 0, .dec = 1, .expected_result = 17},
		{ .inc = 1, .dec = 1, .expected_result = 18},
		{ .inc = 0, .dec = 1, .expected_result = 18},
		{ .inc = 1, .dec = 1, .expected_result = 19},
		{ .inc = 0, .dec = 1, .expected_result = 19},
		{ .inc = 1, .dec = 1, .expected_result = 20},
		{ .inc = 0, .dec = 1, .expected_result = 20},
		{ .inc = 1, .dec = 1, .expected_result = 21},
		{ .inc = 0, .dec = 1, .expected_result = 21},
		{ .inc = 1, .dec = 1, .expected_result = 22},
		{ .inc = 0, .dec = 1, .expected_result = 22},
		{ .inc = 1, .dec = 1, .expected_result = 23},
		{ .inc = 0, .dec = 1, .expected_result = 23},
		{ .inc = 1, .dec = 1, .expected_result = 24},
		{ .inc = 0, .dec = 1, .expected_result = 24},
		{ .inc = 1, .dec = 1, .expected_result = 25},
		{ .inc = 0, .dec = 1, .expected_result = 25},
		{ .inc = 1, .dec = 1, .expected_result = 26},
		{ .inc = 0, .dec = 1, .expected_result = 26},
		{ .inc = 1, .dec = 1, .expected_result = 27},
		{ .inc = 0, .dec = 1, .expected_result = 27},
		{ .inc = 1, .dec = 1, .expected_result = 28},
		{ .inc = 0, .dec = 1, .expected_result = 28},
		{ .inc = 1, .dec = 1, .expected_result = 29},
		{ .inc = 0, .dec = 1, .expected_result = 29},
		{ .inc = 1, .dec = 1, .expected_result = 30},
		{ .inc = 0, .dec = 1, .expected_result = 30},
		{ .inc = 1, .dec = 1, .expected_result = 31},
		{ .inc = 0, .dec = 1, .expected_result = 31},
		{ .inc = 1, .dec = 1, .expected_result = 32},
		{ .inc = 0, .dec = 1, .expected_result = 32},
		{ .inc = 1, .dec = 1, .expected_result = 33},
		{ .inc = 0, .dec = 1, .expected_result = 33},
		{ .inc = 1, .dec = 1, .expected_result = 34},
		{ .inc = 0, .dec = 1, .expected_result = 34},
		{ .inc = 1, .dec = 1, .expected_result = 35},
		{ .inc = 0, .dec = 1, .expected_result = 35},
		{ .inc = 1, .dec = 1, .expected_result = 36},
		{ .inc = 0, .dec = 1, .expected_result = 36},
		{ .inc = 1, .dec = 1, .expected_result = 37},
		{ .inc = 0, .dec = 1, .expected_result = 37},
		{ .inc = 1, .dec = 1, .expected_result = 38},
		{ .inc = 0, .dec = 1, .expected_result = 38},
		{ .inc = 1, .dec = 1, .expected_result = 39},
		{ .inc = 0, .dec = 1, .expected_result = 39},
		{ .inc = 1, .dec = 1, .expected_result = 40},
		{ .inc = 0, .dec = 1, .expected_result = 40},
		{ .inc = 1, .dec = 1, .expected_result = 41},
		{ .inc = 0, .dec = 1, .expected_result = 41},
		{ .inc = 1, .dec = 1, .expected_result = 42},
		{ .inc = 0, .dec = 1, .expected_result = 42},
		{ .inc = 1, .dec = 1, .expected_result = 43},
		{ .inc = 0, .dec = 1, .expected_result = 43},
		{ .inc = 1, .dec = 1, .expected_result = 44},
		{ .inc = 0, .dec = 1, .expected_result = 44},
		{ .inc = 1, .dec = 1, .expected_result = 45},
		{ .inc = 0, .dec = 1, .expected_result = 45},
		{ .inc = 1, .dec = 1, .expected_result = 46},
		{ .inc = 0, .dec = 1, .expected_result = 46},
		{ .inc = 1, .dec = 1, .expected_result = 47},
		{ .inc = 0, .dec = 1, .expected_result = 47},
		{ .inc = 1, .dec = 1, .expected_result = 48},
		{ .inc = 0, .dec = 1, .expected_result = 48},
		{ .inc = 1, .dec = 1, .expected_result = 49},
		{ .inc = 0, .dec = 1, .expected_result = 49},
		{ .inc = 1, .dec = 1, .expected_result = 50},
		{ .inc = 0, .dec = 1, .expected_result = 50},
		{ .inc = 1, .dec = 1, .expected_result = 51},
		{ .inc = 0, .dec = 1, .expected_result = 51},
		{ .inc = 1, .dec = 1, .expected_result = 52},
		{ .inc = 0, .dec = 1, .expected_result = 52},
		{ .inc = 1, .dec = 1, .expected_result = 53},
		{ .inc = 0, .dec = 1, .expected_result = 53},
		{ .inc = 1, .dec = 1, .expected_result = 54},
		{ .inc = 0, .dec = 1, .expected_result = 54},
		{ .inc = 1, .dec = 1, .expected_result = 55},
		{ .inc = 0, .dec = 1, .expected_result = 55},
		{ .inc = 1, .dec = 1, .expected_result = 56},
		{ .inc = 0, .dec = 1, .expected_result = 56},
		{ .inc = 1, .dec = 1, .expected_result = 57},
		{ .inc = 0, .dec = 1, .expected_result = 57},
		{ .inc = 1, .dec = 1, .expected_result = 58},
		{ .inc = 0, .dec = 1, .expected_result = 58},
		{ .inc = 1, .dec = 1, .expected_result = 59},
		{ .inc = 0, .dec = 1, .expected_result = 59},
		{ .inc = 1, .dec = 1, .expected_result = 60},
		{ .inc = 0, .dec = 1, .expected_result = 60},
		{ .inc = 1, .dec = 1, .expected_result = 61},
		{ .inc = 0, .dec = 1, .expected_result = 61},
		{ .inc = 1, .dec = 1, .expected_result = 62},
		{ .inc = 0, .dec = 1, .expected_result = 62},
		{ .inc = 1, .dec = 1, .expected_result = 63},
		{ .inc = 0, .dec = 1, .expected_result = 63},
		{ .inc = 1, .dec = 1, .expected_result = 64},
		{ .inc = 0, .dec = 1, .expected_result = 64},
		{ .inc = 1, .dec = 1, .expected_result = 65},
		{ .inc = 0, .dec = 1, .expected_result = 65},
		{ .inc = 1, .dec = 1, .expected_result = 66},
		{ .inc = 0, .dec = 1, .expected_result = 66},
		{ .inc = 1, .dec = 1, .expected_result = 67},
		{ .inc = 0, .dec = 1, .expected_result = 67},
		{ .inc = 1, .dec = 1, .expected_result = 68},
		{ .inc = 0, .dec = 1, .expected_result = 68},
		{ .inc = 1, .dec = 1, .expected_result = 69},
		{ .inc = 0, .dec = 1, .expected_result = 69},
		{ .inc = 1, .dec = 1, .expected_result = 70},
		{ .inc = 0, .dec = 1, .expected_result = 70},
		{ .inc = 1, .dec = 1, .expected_result = 71},
		{ .inc = 0, .dec = 1, .expected_result = 71},
		{ .inc = 1, .dec = 1, .expected_result = 72},
		{ .inc = 0, .dec = 1, .expected_result = 72},
		{ .inc = 1, .dec = 1, .expected_result = 73},
		{ .inc = 0, .dec = 1, .expected_result = 73},
		{ .inc = 1, .dec = 1, .expected_result = 74},
		{ .inc = 0, .dec = 1, .expected_result = 74},
		{ .inc = 1, .dec = 1, .expected_result = 75},
		{ .inc = 0, .dec = 1, .expected_result = 75},
		{ .inc = 1, .dec = 1, .expected_result = 76},
		{ .inc = 0, .dec = 1, .expected_result = 76},
		{ .inc = 1, .dec = 1, .expected_result = 77},
		{ .inc = 0, .dec = 1, .expected_result = 77},
		{ .inc = 1, .dec = 1, .expected_result = 78},
		{ .inc = 0, .dec = 1, .expected_result = 78},
		{ .inc = 1, .dec = 1, .expected_result = 79},
		{ .inc = 0, .dec = 1, .expected_result = 79},
		{ .inc = 1, .dec = 1, .expected_result = 80},
		{ .inc = 0, .dec = 1, .expected_result = 80},
		{ .inc = 1, .dec = 1, .expected_result = 81},
		{ .inc = 0, .dec = 1, .expected_result = 81},
		{ .inc = 1, .dec = 1, .expected_result = 82},
		{ .inc = 0, .dec = 1, .expected_result = 82},
		{ .inc = 1, .dec = 1, .expected_result = 83},
		{ .inc = 0, .dec = 1, .expected_result = 83},
		{ .inc = 1, .dec = 1, .expected_result = 84},
		{ .inc = 0, .dec = 1, .expected_result = 84},
		{ .inc = 1, .dec = 1, .expected_result = 85},
		{ .inc = 0, .dec = 1, .expected_result = 85},
		{ .inc = 1, .dec = 1, .expected_result = 86},
		{ .inc = 0, .dec = 1, .expected_result = 86},
		{ .inc = 1, .dec = 1, .expected_result = 87},
		{ .inc = 0, .dec = 1, .expected_result = 87},
		{ .inc = 1, .dec = 1, .expected_result = 88},
		{ .inc = 0, .dec = 1, .expected_result = 88},
		{ .inc = 1, .dec = 1, .expected_result = 89},
		{ .inc = 0, .dec = 1, .expected_result = 89},
		{ .inc = 1, .dec = 1, .expected_result = 90},
		{ .inc = 0, .dec = 1, .expected_result = 90},
		{ .inc = 1, .dec = 1, .expected_result = 91},
		{ .inc = 0, .dec = 1, .expected_result = 91},
		{ .inc = 1, .dec = 1, .expected_result = 92},
		{ .inc = 0, .dec = 1, .expected_result = 92},
		{ .inc = 1, .dec = 1, .expected_result = 93},
		{ .inc = 0, .dec = 1, .expected_result = 93},
		{ .inc = 1, .dec = 1, .expected_result = 94},
		{ .inc = 0, .dec = 1, .expected_result = 94},
		{ .inc = 1, .dec = 1, .expected_result = 95},
		{ .inc = 0, .dec = 1, .expected_result = 95},
		{ .inc = 1, .dec = 1, .expected_result = 96},
		{ .inc = 0, .dec = 1, .expected_result = 96},
		{ .inc = 1, .dec = 1, .expected_result = 97},
		{ .inc = 0, .dec = 1, .expected_result = 97},
		{ .inc = 1, .dec = 1, .expected_result = 98},
		{ .inc = 0, .dec = 1, .expected_result = 98},
		{ .inc = 1, .dec = 1, .expected_result = 99},
		{ .inc = 0, .dec = 1, .expected_result = 99},
		{ .inc = 1, .dec = 1, .expected_result = 100},
		{ .inc = 0, .dec = 1, .expected_result = 100},
		{ .inc = 1, .dec = 1, .expected_result = 101},
		{ .inc = 0, .dec = 1, .expected_result = 101},
		{ .inc = 1, .dec = 1, .expected_result = 102},
		{ .inc = 0, .dec = 1, .expected_result = 102},
		{ .inc = 1, .dec = 1, .expected_result = 103},
		{ .inc = 0, .dec = 1, .expected_result = 103},
		{ .inc = 1, .dec = 1, .expected_result = 104},
		{ .inc = 0, .dec = 1, .expected_result = 104},
		{ .inc = 1, .dec = 1, .expected_result = 105},
		{ .inc = 0, .dec = 1, .expected_result = 105},
		{ .inc = 1, .dec = 1, .expected_result = 106},
		{ .inc = 0, .dec = 1, .expected_result = 106},
		{ .inc = 1, .dec = 1, .expected_result = 107},
		{ .inc = 0, .dec = 1, .expected_result = 107},
		{ .inc = 1, .dec = 1, .expected_result = 108},
		{ .inc = 0, .dec = 1, .expected_result = 108},
		{ .inc = 1, .dec = 1, .expected_result = 109},
		{ .inc = 0, .dec = 1, .expected_result = 109},
		{ .inc = 1, .dec = 1, .expected_result = 110},
		{ .inc = 0, .dec = 1, .expected_result = 110},
		{ .inc = 1, .dec = 1, .expected_result = 111},
		{ .inc = 0, .dec = 1, .expected_result = 111},
		{ .inc = 1, .dec = 1, .expected_result = 112},
		{ .inc = 0, .dec = 1, .expected_result = 112},
		{ .inc = 1, .dec = 1, .expected_result = 113},
		{ .inc = 0, .dec = 1, .expected_result = 113},
		{ .inc = 1, .dec = 1, .expected_result = 114},
		{ .inc = 0, .dec = 1, .expected_result = 114},
		{ .inc = 1, .dec = 1, .expected_result = 115},
		{ .inc = 0, .dec = 1, .expected_result = 115},
		{ .inc = 1, .dec = 1, .expected_result = 116},
		{ .inc = 0, .dec = 1, .expected_result = 116},
		{ .inc = 1, .dec = 1, .expected_result = 117},
		{ .inc = 0, .dec = 1, .expected_result = 117},
		{ .inc = 1, .dec = 1, .expected_result = 118},
		{ .inc = 0, .dec = 1, .expected_result = 118},
		{ .inc = 1, .dec = 1, .expected_result = 119},
		{ .inc = 0, .dec = 1, .expected_result = 119},
		{ .inc = 1, .dec = 1, .expected_result = 120},
		{ .inc = 0, .dec = 1, .expected_result = 120},
		{ .inc = 1, .dec = 1, .expected_result = 121},
		{ .inc = 0, .dec = 1, .expected_result = 121},
		{ .inc = 1, .dec = 1, .expected_result = 122},
		{ .inc = 0, .dec = 1, .expected_result = 122},
		{ .inc = 1, .dec = 1, .expected_result = 123},
		{ .inc = 0, .dec = 1, .expected_result = 123},
		{ .inc = 1, .dec = 1, .expected_result = 124},
		{ .inc = 0, .dec = 1, .expected_result = 124},
		{ .inc = 1, .dec = 1, .expected_result = 125},
		{ .inc = 0, .dec = 1, .expected_result = 125},
		{ .inc = 1, .dec = 1, .expected_result = 126},
		{ .inc = 0, .dec = 1, .expected_result = 126},
		{ .inc = 1, .dec = 1, .expected_result = 127},
		{ .inc = 0, .dec = 1, .expected_result = 127},
		{ .inc = 1, .dec = 1, .expected_result = 128},
		{ .inc = 0, .dec = 1, .expected_result = 128},
		{ .inc = 1, .dec = 1, .expected_result = 129},
		{ .inc = 0, .dec = 1, .expected_result = 129},
		{ .inc = 1, .dec = 1, .expected_result = 130},
		{ .inc = 0, .dec = 1, .expected_result = 130},
		{ .inc = 1, .dec = 1, .expected_result = 131},
		{ .inc = 0, .dec = 1, .expected_result = 131},
		{ .inc = 1, .dec = 1, .expected_result = 132},
		{ .inc = 0, .dec = 1, .expected_result = 132},
		{ .inc = 1, .dec = 1, .expected_result = 133},
		{ .inc = 0, .dec = 1, .expected_result = 133},
		{ .inc = 1, .dec = 1, .expected_result = 134},
		{ .inc = 0, .dec = 1, .expected_result = 134},
		{ .inc = 1, .dec = 1, .expected_result = 135},
		{ .inc = 0, .dec = 1, .expected_result = 135},
		{ .inc = 1, .dec = 1, .expected_result = 136},
		{ .inc = 0, .dec = 1, .expected_result = 136},
		{ .inc = 1, .dec = 1, .expected_result = 137},
		{ .inc = 0, .dec = 1, .expected_result = 137},
		{ .inc = 1, .dec = 1, .expected_result = 138},
		{ .inc = 0, .dec = 1, .expected_result = 138},
		{ .inc = 1, .dec = 1, .expected_result = 139},
		{ .inc = 0, .dec = 1, .expected_result = 139},
		{ .inc = 1, .dec = 1, .expected_result = 140},
		{ .inc = 0, .dec = 1, .expected_result = 140},
		{ .inc = 1, .dec = 1, .expected_result = 141},
		{ .inc = 0, .dec = 1, .expected_result = 141},
		{ .inc = 1, .dec = 1, .expected_result = 142},
		{ .inc = 0, .dec = 1, .expected_result = 142},
		{ .inc = 1, .dec = 1, .expected_result = 143},
		{ .inc = 0, .dec = 1, .expected_result = 143},
		{ .inc = 1, .dec = 1, .expected_result = 144},
		{ .inc = 0, .dec = 1, .expected_result = 144},
		{ .inc = 1, .dec = 1, .expected_result = 145},
		{ .inc = 0, .dec = 1, .expected_result = 145},
		{ .inc = 1, .dec = 1, .expected_result = 146},
		{ .inc = 0, .dec = 1, .expected_result = 146},
		{ .inc = 1, .dec = 1, .expected_result = 147},
		{ .inc = 0, .dec = 1, .expected_result = 147},
		{ .inc = 1, .dec = 1, .expected_result = 148},
		{ .inc = 0, .dec = 1, .expected_result = 148},
		{ .inc = 1, .dec = 1, .expected_result = 149},
		{ .inc = 0, .dec = 1, .expected_result = 149},
		{ .inc = 1, .dec = 1, .expected_result = 150},
		{ .inc = 0, .dec = 1, .expected_result = 150},
		{ .inc = 1, .dec = 1, .expected_result = 151},
		{ .inc = 0, .dec = 1, .expected_result = 151},
		{ .inc = 1, .dec = 1, .expected_result = 152},
		{ .inc = 0, .dec = 1, .expected_result = 152},
		{ .inc = 1, .dec = 1, .expected_result = 153},
		{ .inc = 0, .dec = 1, .expected_result = 153},
		{ .inc = 1, .dec = 1, .expected_result = 154},
		{ .inc = 0, .dec = 1, .expected_result = 154},
		{ .inc = 1, .dec = 1, .expected_result = 155},
		{ .inc = 0, .dec = 1, .expected_result = 155},
		{ .inc = 1, .dec = 1, .expected_result = 156},
		{ .inc = 0, .dec = 1, .expected_result = 156},
		{ .inc = 1, .dec = 1, .expected_result = 157},
		{ .inc = 0, .dec = 1, .expected_result = 157},
		{ .inc = 1, .dec = 1, .expected_result = 158},
		{ .inc = 0, .dec = 1, .expected_result = 158},
		{ .inc = 1, .dec = 1, .expected_result = 159},
		{ .inc = 0, .dec = 1, .expected_result = 159},
		{ .inc = 1, .dec = 1, .expected_result = 160},
		{ .inc = 0, .dec = 1, .expected_result = 160},
		{ .inc = 1, .dec = 1, .expected_result = 161},
		{ .inc = 0, .dec = 1, .expected_result = 161},
		{ .inc = 1, .dec = 1, .expected_result = 162},
		{ .inc = 0, .dec = 1, .expected_result = 162},
		{ .inc = 1, .dec = 1, .expected_result = 163},
		{ .inc = 0, .dec = 1, .expected_result = 163},
		{ .inc = 1, .dec = 1, .expected_result = 164},
		{ .inc = 0, .dec = 1, .expected_result = 164},
		{ .inc = 1, .dec = 1, .expected_result = 165},
		{ .inc = 0, .dec = 1, .expected_result = 165},
		{ .inc = 1, .dec = 1, .expected_result = 166},
		{ .inc = 0, .dec = 1, .expected_result = 166},
		{ .inc = 1, .dec = 1, .expected_result = 167},
		{ .inc = 0, .dec = 1, .expected_result = 167},
		{ .inc = 1, .dec = 1, .expected_result = 168},
		{ .inc = 0, .dec = 1, .expected_result = 168},
		{ .inc = 1, .dec = 1, .expected_result = 169},
		{ .inc = 0, .dec = 1, .expected_result = 169},
		{ .inc = 1, .dec = 1, .expected_result = 170},
		{ .inc = 0, .dec = 1, .expected_result = 170},
		{ .inc = 1, .dec = 1, .expected_result = 171},
		{ .inc = 0, .dec = 1, .expected_result = 171},
		{ .inc = 1, .dec = 1, .expected_result = 172},
		{ .inc = 0, .dec = 1, .expected_result = 172},
		{ .inc = 1, .dec = 1, .expected_result = 173},
		{ .inc = 0, .dec = 1, .expected_result = 173},
		{ .inc = 1, .dec = 1, .expected_result = 174},
		{ .inc = 0, .dec = 1, .expected_result = 174},
		{ .inc = 1, .dec = 1, .expected_result = 175},
		{ .inc = 0, .dec = 1, .expected_result = 175},
		{ .inc = 1, .dec = 1, .expected_result = 176},
		{ .inc = 0, .dec = 1, .expected_result = 176},
		{ .inc = 1, .dec = 1, .expected_result = 177},
		{ .inc = 0, .dec = 1, .expected_result = 177},
		{ .inc = 1, .dec = 1, .expected_result = 178},
		{ .inc = 0, .dec = 1, .expected_result = 178},
		{ .inc = 1, .dec = 1, .expected_result = 179},
		{ .inc = 0, .dec = 1, .expected_result = 179},
		{ .inc = 1, .dec = 1, .expected_result = 180},
		{ .inc = 0, .dec = 1, .expected_result = 180},
		{ .inc = 1, .dec = 1, .expected_result = 181},
		{ .inc = 0, .dec = 1, .expected_result = 181},
		{ .inc = 1, .dec = 1, .expected_result = 182},
		{ .inc = 0, .dec = 1, .expected_result = 182},
		{ .inc = 1, .dec = 1, .expected_result = 183},
		{ .inc = 0, .dec = 1, .expected_result = 183},
		{ .inc = 1, .dec = 1, .expected_result = 184},
		{ .inc = 0, .dec = 1, .expected_result = 184},
		{ .inc = 1, .dec = 1, .expected_result = 185},
		{ .inc = 0, .dec = 1, .expected_result = 185},
		{ .inc = 1, .dec = 1, .expected_result = 186},
		{ .inc = 0, .dec = 1, .expected_result = 186},
		{ .inc = 1, .dec = 1, .expected_result = 187},
		{ .inc = 0, .dec = 1, .expected_result = 187},
		{ .inc = 1, .dec = 1, .expected_result = 188},
		{ .inc = 0, .dec = 1, .expected_result = 188},
		{ .inc = 1, .dec = 1, .expected_result = 189},
		{ .inc = 0, .dec = 1, .expected_result = 189},
		{ .inc = 1, .dec = 1, .expected_result = 190},
		{ .inc = 0, .dec = 1, .expected_result = 190},
		{ .inc = 1, .dec = 1, .expected_result = 191},
		{ .inc = 0, .dec = 1, .expected_result = 191},
		{ .inc = 1, .dec = 1, .expected_result = 192},
		{ .inc = 0, .dec = 1, .expected_result = 192},
		{ .inc = 1, .dec = 1, .expected_result = 193},
		{ .inc = 0, .dec = 1, .expected_result = 193},
		{ .inc = 1, .dec = 1, .expected_result = 194},
		{ .inc = 0, .dec = 1, .expected_result = 194},
		{ .inc = 1, .dec = 1, .expected_result = 195},
		{ .inc = 0, .dec = 1, .expected_result = 195},
		{ .inc = 1, .dec = 1, .expected_result = 196},
		{ .inc = 0, .dec = 1, .expected_result = 196},
		{ .inc = 1, .dec = 1, .expected_result = 197},
		{ .inc = 0, .dec = 1, .expected_result = 197},
		{ .inc = 1, .dec = 1, .expected_result = 198},
		{ .inc = 0, .dec = 1, .expected_result = 198},
		{ .inc = 1, .dec = 1, .expected_result = 199},
		{ .inc = 0, .dec = 1, .expected_result = 199},
		{ .inc = 1, .dec = 1, .expected_result = 200},
		{ .inc = 0, .dec = 1, .expected_result = 200},
		{ .inc = 1, .dec = 1, .expected_result = 201},
		{ .inc = 0, .dec = 1, .expected_result = 201},
		{ .inc = 1, .dec = 1, .expected_result = 202},
		{ .inc = 0, .dec = 1, .expected_result = 202},
		{ .inc = 1, .dec = 1, .expected_result = 203},
		{ .inc = 0, .dec = 1, .expected_result = 203},
		{ .inc = 1, .dec = 1, .expected_result = 204},
		{ .inc = 0, .dec = 1, .expected_result = 204},
		{ .inc = 1, .dec = 1, .expected_result = 205},
		{ .inc = 0, .dec = 1, .expected_result = 205},
		{ .inc = 1, .dec = 1, .expected_result = 206},
		{ .inc = 0, .dec = 1, .expected_result = 206},
		{ .inc = 1, .dec = 1, .expected_result = 207},
		{ .inc = 0, .dec = 1, .expected_result = 207},
		{ .inc = 1, .dec = 1, .expected_result = 208},
		{ .inc = 0, .dec = 1, .expected_result = 208},
		{ .inc = 1, .dec = 1, .expected_result = 209},
		{ .inc = 0, .dec = 1, .expected_result = 209},
		{ .inc = 1, .dec = 1, .expected_result = 210},
		{ .inc = 0, .dec = 1, .expected_result = 210},
		{ .inc = 1, .dec = 1, .expected_result = 211},
		{ .inc = 0, .dec = 1, .expected_result = 211},
		{ .inc = 1, .dec = 1, .expected_result = 212},
		{ .inc = 0, .dec = 1, .expected_result = 212},
		{ .inc = 1, .dec = 1, .expected_result = 213},
		{ .inc = 0, .dec = 1, .expected_result = 213},
		{ .inc = 1, .dec = 1, .expected_result = 214},
		{ .inc = 0, .dec = 1, .expected_result = 214},
		{ .inc = 1, .dec = 1, .expected_result = 215},
		{ .inc = 0, .dec = 1, .expected_result = 215},
		{ .inc = 1, .dec = 1, .expected_result = 216},
		{ .inc = 0, .dec = 1, .expected_result = 216},
		{ .inc = 1, .dec = 1, .expected_result = 217},
		{ .inc = 0, .dec = 1, .expected_result = 217},
		{ .inc = 1, .dec = 1, .expected_result = 218},
		{ .inc = 0, .dec = 1, .expected_result = 218},
		{ .inc = 1, .dec = 1, .expected_result = 219},
		{ .inc = 0, .dec = 1, .expected_result = 219},
		{ .inc = 1, .dec = 1, .expected_result = 220},
		{ .inc = 0, .dec = 1, .expected_result = 220},
		{ .inc = 1, .dec = 1, .expected_result = 221},
		{ .inc = 0, .dec = 1, .expected_result = 221},
		{ .inc = 1, .dec = 1, .expected_result = 222},
		{ .inc = 0, .dec = 1, .expected_result = 222},
		{ .inc = 1, .dec = 1, .expected_result = 223},
		{ .inc = 0, .dec = 1, .expected_result = 223},
		{ .inc = 1, .dec = 1, .expected_result = 224},
		{ .inc = 0, .dec = 1, .expected_result = 224},
		{ .inc = 1, .dec = 1, .expected_result = 225},
		{ .inc = 0, .dec = 1, .expected_result = 225},
		{ .inc = 1, .dec = 1, .expected_result = 226},
		{ .inc = 0, .dec = 1, .expected_result = 226},
		{ .inc = 1, .dec = 1, .expected_result = 227},
		{ .inc = 0, .dec = 1, .expected_result = 227},
		{ .inc = 1, .dec = 1, .expected_result = 228},
		{ .inc = 0, .dec = 1, .expected_result = 228},
		{ .inc = 1, .dec = 1, .expected_result = 229},
		{ .inc = 0, .dec = 1, .expected_result = 229},
		{ .inc = 1, .dec = 1, .expected_result = 230},
		{ .inc = 0, .dec = 1, .expected_result = 230},
		{ .inc = 1, .dec = 1, .expected_result = 231},
		{ .inc = 0, .dec = 1, .expected_result = 231},
		{ .inc = 1, .dec = 1, .expected_result = 232},
		{ .inc = 0, .dec = 1, .expected_result = 232},
		{ .inc = 1, .dec = 1, .expected_result = 233},
		{ .inc = 0, .dec = 1, .expected_result = 233},
		{ .inc = 1, .dec = 1, .expected_result = 234},
		{ .inc = 0, .dec = 1, .expected_result = 234},
		{ .inc = 1, .dec = 1, .expected_result = 235},
		{ .inc = 0, .dec = 1, .expected_result = 235},
		{ .inc = 1, .dec = 1, .expected_result = 236},
		{ .inc = 0, .dec = 1, .expected_result = 236},
		{ .inc = 1, .dec = 1, .expected_result = 237},
		{ .inc = 0, .dec = 1, .expected_result = 237},
		{ .inc = 1, .dec = 1, .expected_result = 238},
		{ .inc = 0, .dec = 1, .expected_result = 238},
		{ .inc = 1, .dec = 1, .expected_result = 239},
		{ .inc = 0, .dec = 1, .expected_result = 239},
		{ .inc = 1, .dec = 1, .expected_result = 240},
		{ .inc = 0, .dec = 1, .expected_result = 240},
		{ .inc = 1, .dec = 1, .expected_result = 241},
		{ .inc = 0, .dec = 1, .expected_result = 241},
		{ .inc = 1, .dec = 1, .expected_result = 242},
		{ .inc = 0, .dec = 1, .expected_result = 242},
		{ .inc = 1, .dec = 1, .expected_result = 243},
		{ .inc = 0, .dec = 1, .expected_result = 243},
		{ .inc = 1, .dec = 1, .expected_result = 244},
		{ .inc = 0, .dec = 1, .expected_result = 244},
		{ .inc = 1, .dec = 1, .expected_result = 245},
		{ .inc = 0, .dec = 1, .expected_result = 245},
		{ .inc = 1, .dec = 1, .expected_result = 246},
		{ .inc = 0, .dec = 1, .expected_result = 246},
		{ .inc = 1, .dec = 1, .expected_result = 247},
		{ .inc = 0, .dec = 1, .expected_result = 247},
		{ .inc = 1, .dec = 1, .expected_result = 248},
		{ .inc = 0, .dec = 1, .expected_result = 248},
		{ .inc = 1, .dec = 1, .expected_result = 249},
		{ .inc = 0, .dec = 1, .expected_result = 249},
		{ .inc = 1, .dec = 1, .expected_result = 250},
		{ .inc = 0, .dec = 1, .expected_result = 250},
		{ .inc = 1, .dec = 1, .expected_result = 251},
		{ .inc = 0, .dec = 1, .expected_result = 251},
		{ .inc = 1, .dec = 1, .expected_result = 252},
		{ .inc = 0, .dec = 1, .expected_result = 252},
		{ .inc = 1, .dec = 1, .expected_result = 253},
		{ .inc = 0, .dec = 1, .expected_result = 253},
		{ .inc = 1, .dec = 1, .expected_result = 254},
		{ .inc = 0, .dec = 1, .expected_result = 254},
		{ .inc = 1, .dec = 1, .expected_result = 255},
		/* overflow */
		{ .inc = 0, .dec = 1, .expected_result = 255},
		{ .inc = 1, .dec = 1, .expected_result = 255}

	};

	unsigned char led_o = 0;

	for (int i = 0; i < SAMPLE_SIZE; i++) {

		push_button_counter(samples[i].inc, samples[i].dec, &led_o);

		if (samples[i].expected_result != led_o)
			err_cnt++;
	}
}

Compiler script

The following TCL script synthesizes the binary counter code into an IP Core. The script can be saved to a file and called with "vivado_hls -f FILENAME".

The first line creates a new project / resets the existing one named "push_button_counter"

The second line declares the top function in this case "push_button_counter". It usually defines the inputs / outputs of the IP core.

Note: there can be only one top function.

The lines with "add_files" add the source code files "push_button_counter.*" to the project. The "-tb" option adds files to the testbench.

Note: I implemented a small wrapper not included here which would simply call the "test()" function.

The line "open_solution" creates a new solution.

The line "set_part" defines the target FPGA.

The line "create_clock" defines the clock frequency (either ns or MHz).

Note: for C based HLS there can be only one clock signal.

The line "source" sources the compiler directives TCL script.

Note: you can write the compiler directives directly in the source code, however you will loose flexibility when you plan on synthesizing your code for e.g. multiple FPGAs.

Note: you can have multiple solutions for a project to experiment with different compiler directives, clock frequencies, and FPGAs.

The line "csim_design" runs the C code and the testbench to check for error in the implemented C code.

The line "csynth_design" performs the synthesis of the C source code into RTL code.

The line "cosim_design" uses the C testbench to validate that the RTL code works correctly i.e. produces the right results (see above for the limitations of C testbenches).

The line "export_design -format ip_catalog -evaluate verilog" exports the RTL code as an IP core to be used with Vivado. It also evaluates the RTL code (in this case the Verilog version, you can also evaluate the VHDL version). This step can be quite time consuming but I found it rather useful: Vivado HLS provides you with resource / time estimates for the IP core - I have discovered that they can be about two times off the actual used resources / time.

The "exit" line finally exits Vivado HLS.

Code: Select all

open_project -reset push_button_counter

set_top push_button_counter

add_files "push_button_counter.h push_button_counter.c"

add_files -tb "../../testbench/testbench.c ../../testbench/test.h"

add_files -tb test.c -cflags "-I../../testbench/"

open_solution -reset "solution1"
set_part {xc7z010clg400-1}
create_clock -period 100MHz -name default
source "./directives.tcl"

csim_design
csynth_design
cosim_design
export_design -format ip_catalog -evaluate verilog

exit
Compiler directives

The following TCL script declares the compiler directives used to synthesize the source code.

The "set_directive_interface" directive bundles the control signals ap_* created by HLS to an AXI Lite Slave port which than can be used to control the IP core from an ARM based application.

The "set_directive_pipeline" directive instructs the compiler to synthesize the source code in such a way that the function can be called each clock signal (-II 1, II = initiation interval). This part can be really tricky if you're dealing with code of higher complexity. The directive allows us to sample the GPIO pins at a 100MHz frequency, since the IP core is targeted for a frequency of 100MHz.

There are many different compiler directives to guide the compiler to the solution you want - it took me some trial and error to find a "proper" combination for other IP cores with higher complexity.

Code: Select all

set_directive_interface -mode s_axilite -bundle ctrl "push_button_counter"

set_directive_pipeline -II 1 "push_button_counter"
Integration and results

If you synthesize the above standing code with the given compiler directives you end up with an IP core, with an AXI Lite interface, two one bit wide inputs (inc_i, dec_i) and an eight bit wide output (led_o).

You can instantiate the IP core in the block diagram in Vivado (you might have to add the IP core to the IP catalogue). Connect the "inc_i" / "dec_i" ports to the GPIO pins to which you connect the push buttons to and the "led_o" port to the LED pins (look at the .xdc file to pick the right pins). The AXI Lite Slave port can be connected to the ZYNQ IP block using e.g. an AXI Interconnect - you then will have to assign addresses in the Address Editor for your IP core.

If you run the code / IP core and press the buttons the LEDs should light up accordingly ...

Note: the shown binary counter demonstrates two general tasks when dealing with low level I/O: input / output to pins and execution on a per clock cycle basis. The approach shown is rather general in its setup - you could adapt the code e.g. to interface with the ADC / DAC ...

Special thanks to
Nils Roos & pavel - they helped me quite a bit understanding the inner workings of the RP.

debrajr & u4223374 (Xilinx User Forum / HLS) - they helped me with questions in regards to HLS.

Further reading

Adam Taylor's ZYNQ Blogs - a lot useful information about the ZYNQ and its ins and outs

Xilinx User Guides 902 (HLS) and 998 (Introdution to HLS)

hunterakins
Posts: 6
Joined: Thu Apr 06, 2017 9:31 pm
Location: Berkeley, CA

Re: High Level Synthesis, low level I/O, and the RP

Post by hunterakins » Sat Aug 05, 2017 12:41 am

Wow that's super neat, so much faster than the 100kS/s that you can get with the given GPIO API.
I may experiment with this, since I'm running into problems with the GPIO sampling rate limitation while trying to code an I2C slave. In that case, I'll let you know how it goes
Cheers

lhochstetter
Posts: 55
Joined: Tue Mar 01, 2016 1:43 pm

Re: High Level Synthesis, low level I/O, and the RP

Post by lhochstetter » Mon Sep 18, 2017 4:07 pm

Hi hunterakins,

I'm glad to hear that you found my approach useful!

If you run into clocking issues i.e. you are not able to meet your clock target, try to remove the AXI Slave Interface - the timing improved by ~1 - 1.5ns for me.

However, you will lose the ability to directly connect your IP core to the ZYNQ IP core ... if I recall correctly pavel created an IP core to set registers inside the FPGA
using an AXI interface which could be adapted to control your now AXI-less IP core.

Best,

lhochstetter

Post Reply
jadalnie klasyczne ekskluzywne meble wypoczynkowe do salonu ekskluzywne meble tapicerowane ekskluzywne meble do sypialni ekskluzywne meble włoskie

Who is online

Users browsing this forum: No registered users and 29 guests