The goal of this project is to demonstrate the capability of HLS in designing digital systems. For this purpose, I explain how to describe a digital clock in HLS. The NEXYS-4 evaluation board would be our target FPGA platform. If you are interested in learning HLS coding techniques please refer here or here.
The clock shows hours, minutes, and seconds on 7-segment displays.
It has two modes of operation: clock and setting. The clock mode is the standard mode during which the current time is shown on display. In the setting mode, you can use push-buttons to set the time.
The following figure shows the clock configuration on the board.
As shown in the following figure, the design has three main modules: one-second clock generator, digital clock engine and display driver.
The following pipelined loop is used to implement the one-second clock generator.
bool delay(long long int n) {
#pragma HLS INLINE off
static bool dummy = 0;
for (long long int j = 0; j < n; j++) {
#pragma HLS pipeline
dummy = !dummy;
}
return dummy;
}
void one_second_clock_generator(bool &second) {
#pragma HLS INTERFACE ap_none port=second
#pragma HLS INTERFACE ap_ctrl_none port=return
static bool s = 0;
s=!s;
second = s;
delay(50000000L);
}
The digital clock engine keeps track of hours, minutes and seconds and updates them when it receives a clock tick from the one-second clock generator module. The following code does this task.
void debounce(bool pulse, bool &out) {
#pragma HLS INLINE off
static bool out0 = 0;
static bool out1 = 0;
static bool out2 = 0;
static bool state = 0;
if (state == 0) {
out2 = out1;
out1 = out0;
out0 = pulse;
state = 1;
} else {
delay(2500000);
state = 0;
}
out = out0 & out1 & out2;
}
void set_time(
ap_uint<6> &seconds,
ap_uint<6> &minutes,
ap_uint<5> &hours,
bool set_second,
bool set_minute,
bool set_hour)
{
//--------------------------------------------------
static bool second_state = 0;
if (second_state == 0 && set_second == 1 ) {
seconds++;
if (seconds == 60) {
seconds = 0;
}
second_state = 1;
}
if (second_state == 1 && set_second == 0 ) {
second_state = 0;
}
//---------------------------------------------------
static bool minute_state = 0;
if (minute_state == 0 && set_minute == 1 ) {
minutes++;
if (minutes == 60) {
minutes = 0;
}
minute_state = 1;
}
if (minute_state == 1 && set_minute == 0 ) {
minute_state = 0;
}
//----------------------------------------------------
static bool hour_state = 0;
if (hour_state == 0 && set_hour == 1) {
hours++;
if (hours == 24) {
hours = 0;
}
hour_state = 1;
}
if (hour_state == 1 && set_hour == 0) {
hour_state = 0;
}
//----------------------------------------------------
}
void clock_ticking(
ap_uint<5> &hours,
ap_uint<6> &minutes,
ap_uint<6> &seconds)
{
seconds++;
if (seconds == 60) {
seconds = 0;
minutes++;
if (minutes == 60) {
minutes = 0;
hours++;
if (hours == 24)
hours = 0;
}
}
}
void digital_clock(
bool set_time_sw,
bool &set_time_led,
bool set_second,
bool set_minute,
bool set_hour,
bool one_second_delay,
ap_uint<6> &seconds_out,
ap_uint<6> &minutes_out,
ap_uint<5> &hours_out
)
{
#pragma HLS INTERFACE ap_none port=set_time_sw
#pragma HLS INTERFACE ap_none port=set_time_led
#pragma HLS INTERFACE ap_none port=set_minute
#pragma HLS INTERFACE ap_none port=set_hour
#pragma HLS INTERFACE ap_none port=seconds_out
#pragma HLS INTERFACE ap_none port=minutes_out
#pragma HLS INTERFACE ap_none port=hours_out
#pragma HLS INTERFACE ap_ctrl_none port=return
static ap_uint<6> seconds = 0;
static ap_uint<6> minutes = 0;
static ap_uint<5> hours = 0;
ap_uint<8> segment_data;
ap_uint<8> segment_enable;
static bool state_clock = 0;
bool one_second = one_second_delay;
bool set_time_flag = set_time_sw;
if (one_second==1&&set_time_flag==0&&state_clock==0) {
clock_ticking(hours, minutes, seconds);
state_clock = 1;
}
if (one_second==0&&set_time_flag==0&&state_clock==1) {
state_clock = 0;
}
if (set_time_flag == 1) {
bool set_minute_debounce;
bool set_hour_debounce;
bool set_second_debounce;
debounce (set_minute, set_minute_debounce);
debounce (set_hour, set_hour_debounce);
debounce (set_second, set_second_debounce);
set_time(seconds, minutes, hours, set_second_debounce, set_minute_debounce, set_hour_debounce);
}
seconds_out = seconds;
minutes_out = minutes;
hours_out = hours;
set_time_led = set_time_sw;
}
And the last HLS code shows the current time on the 7-segment display.
#include <ap_int.h>
const ap_uint<8> seven_segment_code[10] = {
0b11000000,
0b11111001,
0b10100100,
0b10110000,
0b10011001,
0b10010010,
0b10000010,
0b11111000,
0b10000000,
0b10010000
};
bool delay(long long int n) {
#pragma HLS INLINE off
static bool dummy = 0;
for (long long int j = 0; j < n; j++) {
#pragma HLS pipeline
dummy = !dummy;
}
return dummy;
}
void seven_segment_display(
ap_uint<5> hours,
ap_uint<6> minutes,
ap_uint<6> seconds,
ap_uint<8> &seven_segment_data,
ap_uint<8> &seven_segment_enable)
{
#pragma HLS INTERFACE ap_none port=hours
#pragma HLS INTERFACE ap_none port=minutes
#pragma HLS INTERFACE ap_none port=seconds
#pragma HLS INTERFACE ap_none port=seven_segment_data
#pragma HLS INTERFACE ap_none port=seven_segment_enable
#pragma HLS INTERFACE ap_ctrl_none port=return
ap_uint<4> second_digit_1 = seconds%10;
ap_uint<4> second_digit_2 = seconds/10;
ap_uint<4> minute_digit_1 = minutes%10;
ap_uint<4> minute_digit_2 = minutes/10;
ap_uint<4> hours_digit_1 = hours%10;
ap_uint<4> hours_digit_2 = hours/10;
ap_uint<8> segment_data;
ap_uint<8> segment_enable;
static ap_uint<3> state = 0;
switch (state) {
// second
case 0:
segment_data = seven_segment_code[second_digit_1];
segment_enable = 0b11111110;
delay(250000L);
state = 1;
break;
case 1:
segment_data = seven_segment_code[second_digit_2];
segment_enable = 0b11111101;
state = 2;
delay(250000L);
break;
// minutes
case 2:
segment_data = seven_segment_code[minute_digit_1];
segment_enable = 0b11110111;
state = 3;
delay(250000L);
break;
case 3:
segment_data = seven_segment_code[minute_digit_2];
segment_enable = 0b11101111;
state = 4;
delay(250000L);
break;
// hours
case 4:
segment_data = seven_segment_code[hours_digit_1];
segment_enable = 0b10111111;
state = 5;
delay(250000L);
break;
case 5:
segment_data = seven_segment_code[hours_digit_2];
segment_enable = 0b01111111;
state = 0;
delay(250000L);
break;
default:
segment_data = seven_segment_code[0];
segment_enable = 0b11111111;
state = 0;
delay(250000L);
break;
}
seven_segment_data = segment_data;
seven_segment_enable = segment_enable;
}
After synthesising these codes, we should use the Xilinx Vivado toolset to connect them together and generate the FPGA bitstream.
After programming the board, you can test the design.
Comments