我们之前在外国教材上已经实现了最简单的加法器电路,并且用HLS进行了仿真。今天我们来记一下笔记并且实现一个新的电路也就是led灯。

创建项目

image-20231025190541087

如图所示,输入项目名称后,这里可以添加现有的c语言文件。默认没有的话直接next,下面的testbench同理。

下面要确定我们的解决方案名称,解决方案在一个项目中可以有很多个,用来帮我们对比哪种更好。

image-20231025190812447

默认不用动即可。时钟频率默认也就是10ns。

成功创建工程之后,打开左边的include可以看到安装好的一些头文件和库。

image-20231025191536993

我们要生成的模块算法,要放到源文件source里面。testbench我们已经熟悉了,他是激励文件。

电路原理

电路原理图如下:

image-20231026101438076

然后开始创建源文件

编写项目文件

image-20231026103911361

在左边的source点击新源文件,可以在我们的项目目录中再创建一个src文件夹保存所有源文件,创建两个源文件,第一个是c语言实现算法,后缀为cpp文件,另一个是头文件,定义数据类型等,后缀.h。

然后编写c语言代码。

头文件代码如下:

1
2
3
4
5
6
7
8
9
#ifndef _SHIFT_LED_H
#define _SHIFT_LED_H

#define MAX_CNT 10000000/2 //定义半秒,因为默认单位都是1个时钟周期,也就是100ns,所以再补7个0除以2即可
#define SHIFT_FLAG MAX_CNT-2
typedef int led_t;
typedef int cnt32_t;//t就表示这是自定义类型,并非变量
void shift_led(led_t * led_origin, led_t led_i);
#endif

shift_led.cpp的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "shift_led.h"

void shift_led(led_t * led_o, led_t led_i)//这只是循环一次的函数!
{
led_t tmp_led;
//缓存变量
cnt32_t i;//for循环的变量
tmp_led = led_i;//也就是先初始化一下值,防止一直为0
for(i = 0; i < MAX_CNT; i++)
{
if(i == SHIFT_FLAG){
tmp_led = ((tmp_led >> 2) & 0x01) + ((tmp_led << 1) & 0x7);//确定取出一位最高位,再左移一位,加上移到最低位的最高位,取出最后的低三位。
//tmp_led <= {tmp_led[1:0], tmp_led[2]};这是verilog中应该存在的代码
*led_o = tmp_led;
}

}
}

然后在左边的testbench中定义testbench_shift_led文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "shift_led.h"
#include<stdio.h>
int main()
{
//定义激励
led_t led_o;
led_t led_i = 0x6;//输入,显然0x6 = 3'b110 -> 3'b101 -> 3'b011 -> 3'b110回来了!
const int SHIFT_TIME = 4;
int i;
for(i = 0; i < SHIFT_TIME; i++)
{
//定义了循环次数SHIFT_TIME
shift_led(&led_o, led_i);
led_i = led_o;//把输出赋值给输入!实现循环移位
fprintf(stdout, "output result = %x", led_o&0x7);//查看输出!只要最低三位即可
}
return 0;
}

启动仿真

注意,实际上在source文件中,我们的.h头文件是不允许存在的,所以此刻删掉.h文件。

image-20231026110256435

打开project栏看到c仿真

image-20231026110342368

我们看到报错很快,因为头文件的引用已经不成立了呀,刚刚被我们删除了头文件。

image-20231026110442030

我们打开project settings然后选择synthesis部分选择top_function

image-20231026111333116

注意这个并不是引起头文件错误的原因,这只是需要我们额外操作的部分。

头文件虽然在项目中被删除,但是文件还在,在src文件夹中并没有被删除,所以我们在testbench文件testbench_shift_led.cpp文件中第一行改为

1
#include "./../src/shift_led.h"//如果还是找不到请替换成绝对路径

即可找到!

当没有报错,完成仿真时,可以看到日志如下:

image-20231026112104966

我们可以在此日志上面看到生成结果

image-20231026113249427

符合,只不过没有输出成2进制的形式。

转换成verilog文件

image-20231026113350892

如图所示激活我们目前的solution,生成verilog文件!终于到了核心的部分。

当然,我们完全可以创建一个新的solution,并且和之前的solution之前来回切换。

生成后的结果如图所示

image-20231027005146305

可以看到左边一栏中多出了仿真结果,有VDHL语言,VERILOG语言和SYSTEMVERILOG语言,非常全面。并且包含了核心算法代码,也就是我打开的shift_led.v文件。

我们打开此文件,如图所示

image-20231027005354864

看到led_i也就是输入部分,竟然占到了32位,实在是太浪费了,因为比如我们刚才的输入只有3’b110也就是3位而已。

解决位宽问题

我们可以直接从头文件修改,修改shift_led.h如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef _SHIFT_LED_H
#define _SHIFT_LED_H

#include "ap_int.h"
#define MAX_CNT 10000000/2 //定义半秒,因为默认单位都是1个时钟周期,也就是100ns,所以再补7个0除以2即可
#define SHIFT_FLAG MAX_CNT-2
//typedef int led_t;
//typedef int cnt32_t;//t就表示这是自定义类型,并非变量

typedef ap_fixed<3, 3> led_t;//第一个3表示总共位宽为3,第二个3表示整数位宽为3,也就是这是3位整形数字!
typedef ap_fixed<32, 32> cnt32_t;//也就是所有位宽32位,其中整数占32位,表示32位整形。

void shift_led(led_t * led_origin, led_t led_i);
#endif

可以看到我们引入了一个vivado带的库,他非常方便可以定义任意位的整形或者浮点型。所以我们直接定义3位的整形即可。

然后重新运行c synthesis里面的active solution

运行结束之后再来查看位宽如下

image-20231027011242491

发现直接对应led输入输出变量部分变成了3位!

在输出报告里面可以看到只用了28个flip flop和135个LUT比较节省。

image-20231027011427853

接口配置

然后打开核心算法文件,看到右边出现directive,可以约束我们的输入和输出。

image-20231027011554400

比如我们先来配置一下输出接口led_o

image-20231027011738683

选择INTERFACE项。

模式我们选择OVLD,代表输出标志信号的意思,标志信号就是一个方波脉冲,作为信号的标志的意思。输入标志信号也是如此,只不过代号用VLD表示。

image-20231027012131975

注意,加入这些之后,在右边栏可以看到directive的一些声明。但是注意,添加在这里只是这一个solution下的directive。我们最好加在源文件中,就可以在所有solution中使用了。所以我们可以删除刚才添加的,从新添加到源文件中。添加之后如图所示

image-20231027012522684

然后重新运行c synthesis生成对应verilog代码。

结果如图

image-20231027012710076

可以看到我们定义的硬件接口。

然后进行联合仿真,如图所示,

image-20231027012842118

也就是c/rtl cosimulation

然后即可查看仿真运行结果,也就是一个信号输入,对应输出图。

第一次发现报错如图所示:

image-20231029004921513

实际上是输出类型出了问题,因为我们的led_o已经定义为ap_fixed类型,然而这种类型其实属于无符号整型 unsigned int所以在输出的地方应用强制类型转换。

输出行变成

1
fprintf(stdout, "output result = %x", (unsigned int)led_o&0x7);

重新运行联合仿真即可。