<== 第一章:五分钟学会主要步骤
在第一章,我们已经介绍过如何在超能云上面快速体验加速器的发布、部署,已经使用。在这一章,我们将介绍如何开发一个自己的加速器,可以部署到超能云上面。
为了方便读者,我们将以超能云FPGA创客空间提供的例子作为蓝本。一方面会讲解这个例子,另一方面将会介绍如何通过修改这个例子,来做一个自己的加速器。
登录SuperVessel超能云(http://www.ptopenlab.com),选择“Applicatoin Acceleration”服务。这时,需要再登录一次,进入应用加速服务。在服务页面的左下方,通过点击“Download”,可以看到“Sample Accelerator Design”,如下图。点击下载到本地。
在修改例子之前,有必要解释一下下载的这个例子里面所包含的文件。
一个可以被SuperVessel超能云 FPGA虚拟化平台识别的加速器设计是一个zip压缩包。里面用多个子目录分别存放了用户的加速器设计所需要的各种类型源文件,例如Verilog代 码,FPGA厂商的IP,用于参数定义的头文件等。另外为了通过SuperVessel FPGA虚拟化平台的仿真检查,加速器设计里应该提供一个用于仿真的输入激励的文件。这些文件都必须按照一定的规则存放到不同的目录里面,并最终打包成一 个zip压缩包。该zip压缩包用于上传到SuperVessel的云里面,并被进一步的自动化编译和部署。
下载的这个例子,是一个典型的加速器包的目录结构,如下示意图所示。add0-是设计包的顶层目录,用户可以为这个目录做任何命名。该目录包含三个子目录,分别为 data,inc和src。下面分别介绍这些目录的作用。
add ├── data │ ├── data_in.txt │ └── data_out.txt ├── inc └── src └── acc_top.v
该目录下的 data_in.txt 存放着用于接口检查所需要的激励数据。该文件里面的每一行的数据对应于加速器一个时钟周期的激励,每一行有三列用空格隔开的16进制表示的数据,分别对应 于加速器数据接口的三组变量:acc_data_eop_i,acc_data_de_i和acc_data_dat_i。
0 f 123456 0 f 987654321 1 3 010203040506
acc_data_eop_i (2bits):
End of Packet信号。0表示非最后一个周期,1表示最后一个周期。例如在这个例子中,第三行数据表示该周期是仿真激励的最后一个周期。
acc_data_de_i (4bits):
该信号暂时保留,没有使用。
acc_data_dat_i (128bits):
加速器输入数据总线(128位宽)
关于这三个信号更详细的说明,可参看链接:https://services.ptopenlab.com/mediawiki/index.php/FPGA-HW
该目录存放着用于Verilog或者Verilog需要包含的头文件或者VHDL包。在这个例子中,该目录为空。
该目录存放着加速设计的主要源文件。 用户的加速器的顶层模块名字必须是 acc_top,并且必须实现在一个独立的 RTL 代码文件中,并把该文件命名为 acc_top。对于 Verilog代码来说,这个文件的文件名必须是 acc_top.v 并在文件内用 Module acc_top 来声明模块。
本例子是最简单的内存回环拷贝。所以只通过一个acc_top.v就可以完成把输入数据直接输出的功能。该文件是对接SuperVessel FPGA虚拟化功能接口最重要的文件。module acc_top是定义了加速器模块最顶层的模块,以及该模块的接口。在编写你自己的加速器时,这一段不需要改变。
module acc_top ( clk, reset, busy_o, acc_data_ready_o, acc_data_ctl_i, acc_data_val_i, acc_data_dat_i, acc_data_eop_i, acc_data_de_i, acc_result_ready_i, acc_result_ctl_o, acc_result_val_o, acc_result_dat_o, acc_result_eop_o, acc_result_de_o );
后面这一段是用作定义模块acc_top各个接口信号的。在制作自己的加速器时,这一部分也不需要修改。
input clk; input reset; output busy_o; output acc_data_ready_o; input acc_data_ctl_i; input acc_data_val_i; input [127:0] acc_data_dat_i; input [1:0] acc_data_eop_i; input [3:0] acc_data_de_i; input acc_result_ready_i; output acc_result_ctl_o; output acc_result_val_o; output [127:0] acc_result_dat_o; output [1:0] acc_result_eop_o; output [3:0] acc_result_de_o; reg busy_o; reg acc_result_val_o; reg [127:0] acc_result_dat_o; reg [1:0] acc_result_eop_o; reg [3:0] acc_result_de_o;
各个信号定义的详细说明,可参看链接:https://services.ptopenlab.com/mediawiki/index.php/FPGA-HW
以下部分是对accelertor忙信号“busy_o”的定义。该信号有效时,外部电路不能向该Accelerator发布新的任务。一般简单的加速器,可以保留该部分,不需要修改。
always @ (posedge clk) begin if (reset) busy_o <= 0; else if (acc_data_ctl_i) busy_o <= 1; else if (acc_result_val_o & (acc_result_eop_o == 2'b01)) busy_o <= 0; end
acc_data_ready_o接收输入数据准备信号。当该信号有效时表示Accelerator可以接收至少2个cycle的数据。acc_result_ready_i是输出结果数据准备信号。当该信号有效时表示外部电路可以接收至少3个cycle的数据。
因为在本例子中,输入数据直接输出,所以我们把这两个信号“=”起来。但在更多的实际加速器中,acc_data_ready_o会依据加速器的实际逻辑来决定。
assign acc_data_ready_o = acc_result_ready_i; (有可能改变)
assign acc_result_ctl_o = 0; (不需改变)
acc_result_ctl_o目前没有使用该信号,所以置0即可。
always @ (posedge clk) begin acc_result_val_o <= acc_data_val_i; (有可能改变) acc_result_eop_o <= acc_data_eop_i; (有可能改变) acc_result_de_o <= acc_data_de_i; (不需要改变) end
acc_result_val_o表示该信号有效表示Accelerator输出当前周期的acc_result_dat_o,并且其中的数据为经过Accelerator处理的结果数据。由于本例子为输入输出回环,所以直接把"acc_result_val_o <= acc_data_val_i". 但在实际的加速器中,开发者有可能需要对这个赋值改变。信号acc_result_eop_o的情况与acc_result_val_o类似。
目前由于acc_result_de_o是没有被使用,所以可以保留“acc_result_de_o <=acc_data_de_i”.
always @ (posedge clk) begin acc_result_dat_o [7:0] <= acc_data_dat_i [7:0] + 8'h0; acc_result_dat_o [15:8] <= acc_data_dat_i [15:8] + 8'h1; acc_result_dat_o [23:16] <= acc_data_dat_i [23:16] + 8'h2; acc_result_dat_o [31:24] <= acc_data_dat_i [31:24] + 8'h3; acc_result_dat_o [39:32] <= acc_data_dat_i [39:32] + 8'h4; acc_result_dat_o [47:40] <= acc_data_dat_i [47:40] + 8'h5; acc_result_dat_o [55:48] <= acc_data_dat_i [55:48] + 8'h6; acc_result_dat_o [63:56] <= acc_data_dat_i [63:56] + 8'h7; acc_result_dat_o [71:64] <= acc_data_dat_i [71:64] + 8'he; acc_result_dat_o [79:72] <= acc_data_dat_i [79:72] + 8'he; acc_result_dat_o [87:80] <= acc_data_dat_i [87:80] + 8'he; acc_result_dat_o [95:88] <= acc_data_dat_i [95:88] + 8'he; acc_result_dat_o [103:96] <= acc_data_dat_i [103:96] + 8'he; acc_result_dat_o [111:104] <= acc_data_dat_i [111:104] + 8'he; acc_result_dat_o [119:112] <= acc_data_dat_i [119:112] + 8'he; acc_result_dat_o [127:120] <= acc_data_dat_i [127:120] + 8'he; end
以上这一段是真正实现输入数据直接环回到输出数据的语句。开发者需要用自己的逻辑功能替换掉这部分代码。
我们把acc_top.v稍加变化,例如,把128位中的某些字节置零。
always @ (posedge clk) begin acc_result_dat_o [7:0] <= acc_data_dat_i [7:0] + 8'h0; acc_result_dat_o [15:8] <= acc_data_dat_i [15:8] + 8'h1; acc_result_dat_o [23:16] <= 0; acc_result_dat_o [31:24] <= 0; acc_result_dat_o [39:32] <= acc_data_dat_i [39:32] + 8'h4; acc_result_dat_o [47:40] <= acc_data_dat_i [47:40] + 8'h5; acc_result_dat_o [55:48] <= 0; acc_result_dat_o [63:56] <= 0; acc_result_dat_o [71:64] <= acc_data_dat_i [71:64] + 8'he; acc_result_dat_o [79:72] <= acc_data_dat_i [79:72] + 8'he; acc_result_dat_o [87:80] <= acc_data_dat_i [87:80] + 8'he; acc_result_dat_o [95:88] <= acc_data_dat_i [95:88] + 8'he; acc_result_dat_o [103:96] <= acc_data_dat_i [103:96] + 8'he; acc_result_dat_o [111:104] <= acc_data_dat_i [111:104] + 8'he; acc_result_dat_o [119:112] <= acc_data_dat_i [119:112] + 8'he; acc_result_dat_o [127:120] <= acc_data_dat_i [127:120] + 8'he; end
同时,我们也修改一下输入数据文件data_in.txt
0 f 123456 0 f 987654321 0 f 112233445566 0 f 333333333333 0 f 444444444444 0 f 555555555555 1 f 010203040506
重新把整个加速器目录形成 set_0.zip。按照第一章的步骤形成上传和部署到SuperVessel上面。