绪论
该项目的目标是展示 HLS 在设计数字系统方面的能力。为此,本文展示如何在 HLS 中描述数字时钟。如果有兴趣学习 HLS 编码技术,请参阅:
❝https://highlevel-synthesis.com/
❞
❝https://www.udemy.com/course/hls-combinational-circuits/?referralCode=8D449A491B9F4582DDEF
❞
时钟在 7 段数码管上显示小时、分钟和秒。
它有两种操作模式:时钟和设置。时钟模式是标准模式,在此模式下,当前时间显示在数码管上。在设置模式下,可以使用按钮设置时间。
下图显示开发板上的时钟配置。
如下图所示,该设计主要分为三个模块:秒时钟发生器、数字时钟引擎和显示驱动。
下面的流水线循环用于实现秒时钟发生器。
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);
}
数字时钟引擎主要是跟踪小时、分钟和秒,并在收到来自秒时钟发生器模块的时钟节拍时更新它们。以下代码完成上诉功能。
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;
}
最后一个 HLS 代码在 7 段数码管上显示当前时间。
#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;
}
综合这些代码后,使用 Vivado 工具将它们连接在一起并生成 FPGA 比特流。
对电路板编程后,可以看到下图:
代码
❝https://github.com/suisuisi/FPGATechnologyGroup/tree/main/DigitalClock_HLS
❞