UPGRADE YOUR BROWSER

We have detected your current browser version is not the latest one. Xilinx.com uses the latest web technologies to bring you the best online experience possible. Please upgrade to a Xilinx.com supported browser:Chrome, Firefox, Internet Explorer 11, Safari. Thank you!

cancel
Showing results for 
Search instead for 
Did you mean: 
Visitor lhochstetter
Visitor
6,579 Views
Registered: ‎08-18-2016

Accessing the ap_clock signal for a LED blinker

Jump to solution

Hi everyone,

 

I'm trying to build a simple LED blinker HLS IP Core.

 

Therefore I'd like to write a function which is called every clock cycle (100MHz). A counter is increased every clock cycle and if the counter equals a certain threshold the LED is either toggled on or toggled off and the counter is reset.

 

#include <stdbool.h>

#include "led_blinker.h"

unsigned char led_o = 0;
volatile bool clk;

void blink_led(unsigned char led, unsigned char blink_frequency)
{
	static unsigned int ticks = 0;
	static unsigned char switched_on = 0;
	
	if (clk) {
		if ((CLOCK_FREQUENCY / blink_frequency) == ticks) {
			if (switched_on) {
				led_o = 0;
				switched_on = 0;
			} else {
				led_o = 1 << led;
				switched_on = 1;
			}
			ticks = 0;
		}
		ticks++;
	}
}

As stated in the subject I want to access the clock signal of ap_clock using the clk global variable. It creates a one bit wide pin to the core. Everytime the clock goes high I'd like to increment the counter ticks++.

 

How do I access the clock signal in C? Is there any way to do so?


If I'm not able to do so: can I do this using C++ or SystemC?

0 Kudos
1 Solution

Accepted Solutions
Scholar u4223374
Scholar
12,046 Views
Registered: ‎04-26-2015

Re: Accessing the ap_clock signal for a LED blinker

Jump to solution

I can think of two ways to do this:

 

void blink_led(unsigned char led, unsigned char blink_frequency)
{
#pragma HLS PIPELINE II=1
    static unsigned int ticks = 0;
    static unsigned char switched_on = 0;
    
    if ((CLOCK_FREQUENCY / blink_frequency) == ticks) {
        if (switched_on) {
            led_o = 0;
            switched_on = 0;
        } else {
            led_o = 1 << led;
            switched_on = 1;
        }
        ticks = 0;
    }
    ticks++;
}

With this one, you'd just set the auto-restart bits and let it run. Assuming that HLS is able to meet the II=1 requirement, it'll start on every clock cycle, increment the counter on every clock cycle, and toggle the LED as required.

 

volatile unsigned char led_o = 0;
volatile bool clk;

void blink_led(unsigned char led, volatile unsigned char & blink_frequency)
{

	while (1) {
	#pragma HLS PIPELINE II=1
		unsigned int ticks = 0;
		unsigned char switched_on = 0;
		
		if ((CLOCK_FREQUENCY / blink_frequency) == ticks) {
			if (switched_on) {
				led_o = 0;
				switched_on = 0;
			} else {
				led_o = 1 << led;
				switched_on = 1;
			}
			ticks = 0;
		}
		ticks++;
		ap_wait();
	}
}

This one just starts once and then runs forever, but with the volatile input it should respond to changes while it's running. The ap_wait() forces HLS to put a one-cycle delay after incrementing the counter.

 

 

 

With that said, for this sort of thing, you're far better off with HDL code. In the same way that you wouldn't use Java for a timing-critical application on a normal PC (because the JVM might decide to do garbage collection and pause your program for an unknown length of time), HLS is not really the right tool when you want cycle-accurate timing on an FPGA. Yes, you can probably persuade it to do what you need, but when the equivalent HDL code is so trivial it's probably not worthwhile.

0 Kudos
8 Replies
Moderator
Moderator
6,530 Views
Registered: ‎04-17-2011

Re: Accessing the ap_clock signal for a LED blinker

Jump to solution
ap_clock is auto inferred by the Tool based on the RTL generated. SystemC has provisions which can match your requirement.
Regards,
Debraj
----------------------------------------------------------------------------------------------
Kindly note- Please mark the Answer as "Accept as solution" if information provided is helpful.

Give Kudos to a post which you think is helpful and reply oriented.
----------------------------------------------------------------------------------------------
0 Kudos
Visitor lhochstetter
Visitor
6,518 Views
Registered: ‎08-18-2016

Re: Accessing the ap_clock signal for a LED blinker

Jump to solution

Thanks for the quick reply ... just out of curiousity is there a way to do so in plain C,

maybe with a little bit more effort or is SystemC the only way (in terms of HLS while ignoring the HDL approach)?

0 Kudos
Scholar u4223374
Scholar
12,047 Views
Registered: ‎04-26-2015

Re: Accessing the ap_clock signal for a LED blinker

Jump to solution

I can think of two ways to do this:

 

void blink_led(unsigned char led, unsigned char blink_frequency)
{
#pragma HLS PIPELINE II=1
    static unsigned int ticks = 0;
    static unsigned char switched_on = 0;
    
    if ((CLOCK_FREQUENCY / blink_frequency) == ticks) {
        if (switched_on) {
            led_o = 0;
            switched_on = 0;
        } else {
            led_o = 1 << led;
            switched_on = 1;
        }
        ticks = 0;
    }
    ticks++;
}

With this one, you'd just set the auto-restart bits and let it run. Assuming that HLS is able to meet the II=1 requirement, it'll start on every clock cycle, increment the counter on every clock cycle, and toggle the LED as required.

 

volatile unsigned char led_o = 0;
volatile bool clk;

void blink_led(unsigned char led, volatile unsigned char & blink_frequency)
{

	while (1) {
	#pragma HLS PIPELINE II=1
		unsigned int ticks = 0;
		unsigned char switched_on = 0;
		
		if ((CLOCK_FREQUENCY / blink_frequency) == ticks) {
			if (switched_on) {
				led_o = 0;
				switched_on = 0;
			} else {
				led_o = 1 << led;
				switched_on = 1;
			}
			ticks = 0;
		}
		ticks++;
		ap_wait();
	}
}

This one just starts once and then runs forever, but with the volatile input it should respond to changes while it's running. The ap_wait() forces HLS to put a one-cycle delay after incrementing the counter.

 

 

 

With that said, for this sort of thing, you're far better off with HDL code. In the same way that you wouldn't use Java for a timing-critical application on a normal PC (because the JVM might decide to do garbage collection and pause your program for an unknown length of time), HLS is not really the right tool when you want cycle-accurate timing on an FPGA. Yes, you can probably persuade it to do what you need, but when the equivalent HDL code is so trivial it's probably not worthwhile.

0 Kudos
Visitor lhochstetter
Visitor
6,476 Views
Registered: ‎08-18-2016

Re: Accessing the ap_clock signal for a LED blinker

Jump to solution

Thanks for the input!

 

I understand that some things are simply done better and more efficient in HDL ... but I'm kinda "forced" to use HLS.

 

I got some questions regarding your second piece of code:

 

What is the purpose of the "&" infront of "blink_frequency"?

 

volatile unsigned char & blink_frequency

 

Why do I have to wait for one cycle using "ap_wait()"?

 

I suppose I have to connect the HLS clock pin to the same clock as ap_clk in Vivado, right?

0 Kudos
Scholar u4223374
Scholar
6,468 Views
Registered: ‎04-26-2015

Re: Accessing the ap_clock signal for a LED blinker

Jump to solution

The "&" means that this variable is passed by reference rather than passed by value. In normal C, without the & (or a * to indicate a pointer, or an array) a variable gets copied into the function that's being called. Because that function now has its own local copy of the variable, changes to the original variable while the function is running will not have any effect.

 

The "volatile" keyword means that something else might modify this variable while the function is running. With an input that's passed by value, this makes no sense at all - the local copy of the variable only exists inside that function, so nothing else can modify it.

 

Passing by reference means that no local copy is made; if the original is changed while the function is running, then the local variable inside the function changes too. This is the desired behaviour for a volatile variable.

 

I honestly have no idea what HLS would do with a volatile pass-by-value input, but it's better to be safe than sorry.

 

 

The waiting for one cycle is to explicitly put a break between one loop iteration and the next. Using the PIPELINE pragma tells HLS to make the loop run at one iteration per clock cycle, but it doesn't specify which parts happen in each clock cycle. HLS could, for example, wrap the loop around so that in any given clock cycle it's doing the last part of one iteration and the first part of the next - which may give unexpected results when you're after cycle-accurate timing. What ap_wait() does is guarantee that everything specified before ap_wait() happens before anything specified after ap_wait() happens.

 

Take this instruction sequence:

 

function0();
function1();
function2();
function3();

HLS will order these however it sees fit. It might run functions 0 and 3 first (simultaneously), then functions 1 and 2 (simultaneously) - if it can guarantee that the correct outputs are produced. Note that the order the outputs are produced in generally doesn't matter, as long as the correct outputs are available before they're used.

 

With ap_wait() inserted, you might do this:

function0();
ap_wait(); function1();
ap_wait(); function2();
ap_wait(); function3();

In this case, function0 will start, then finish. After it finishes, function1 will start, then finish. After it finishes, function2 will start, then finish. After it finishes, function3 will start, then finish. Now you have a guaranteed ordering in the system, which may well be what you desire. The downside is that it'll take much longer to run, because nothing can run in parallel any more.

 

With the clock: neither of my designs actually use the "clk" input; the only clock input to the module should be ap_clk (which you don't access directly from the C code, it's just implied).

 

 

Also, I totally failed to notice this one before, but you've got variables declared inside the loop that are expected to persist between loop iterations. This will not work, and it'll be a pain in the neck to debug because this code (and pretty much all HLS code with volatile variables) can't be simulated in C (cosim might work). I've copied the mistake into the second of my functions (the first is OK).

0 Kudos
Visitor lhochstetter
Visitor
6,426 Views
Registered: ‎08-18-2016

Re: Accessing the ap_clock signal for a LED blinker

Jump to solution

Thanks again and special thanks for the thorough explanation.

 

I used your first piece of code and it runs like a charm!

 

Some final questions:

 

Which code variant (reset bit / while loopt) is the "best" / "most natural" variant to do this (ruling out HDL as an option)?

 

Are there limits to one variant which the other one doesn't have (max. clock frequency etc.)?

0 Kudos
Scholar u4223374
Scholar
6,409 Views
Registered: ‎04-26-2015

Re: Accessing the ap_clock signal for a LED blinker

Jump to solution

Ah, now that's a difficult question.

 

I feel that the one you've used (ie auto-restart, no loop) is better. After all, you can actually stop it just by deasserting the auto-restart bit! Whereas the infinite loop will run until you reset the block. This also makes it a whole lot easier to test - doing a C simulation on a block that contains an infinite loop is obviously not going to work very well!

 

Not sure about limitations; I'd imagine that they're very similar in regards to resource usage and clock frequency.

0 Kudos
Visitor lhochstetter
Visitor
6,402 Views
Registered: ‎08-18-2016

Re: Accessing the ap_clock signal for a LED blinker

Jump to solution

Ok, I'll stick with the first variant. If I run into problems I'll open a new thread / try a different approach.

 

Thanks again!

0 Kudos