一起学习用Verilog在FPGA上实现CNN----(七)全连接层设计
一起学习用Verilog在FPGA上实现CNN----全连接层设计
1 全连接层设计
1.1 Layer
进行线性计算的单元layer,原理图如图所示:
1.2 processingElement
Layer中的线性计算单元processingElement,原理图如图所示:
processingElement模块展开原理图,如图所示,包含一个乘法器和一个加法器,对输入进行累乘和累加
1.3 weightMemory
全连接层的权重存储于weightMemory单元,原理图如图所示:
2 代码实现
2.1 weightMemory
2.1.1 设计输入
创建weightMemory文件,操作如图:
双击打开,输入代码:
module weightMemory(clk,address,weights);
parameter DATA_WIDTH = 32;
parameter INPUT_NODES = 100;
parameter OUTPUT_NODES = 32;
parameter file = "E:/FPGA_Learn/FPGA/Day1211/Weight/weightsdense_1_IEEE.txt";
localparam TOTAL_WEIGHT_SIZE = INPUT_NODES * OUTPUT_NODES;
input clk;
input [7:0] address;
output reg [DATA_WIDTH*OUTPUT_NODES-1:0] weights;
reg [DATA_WIDTH-1:0] memory [0:TOTAL_WEIGHT_SIZE-1];
integer i;
always @ (posedge clk) begin
if (address > INPUT_NODES-1 || address < 0) begin
weights = 0;
end else begin
for (i = 0; i < OUTPUT_NODES; i = i + 1) begin
weights[(OUTPUT_NODES-1-i)*DATA_WIDTH+:DATA_WIDTH] = memory[(address*OUTPUT_NODES)+i];
end
end
end
initial begin
$readmemh(file,memory);
end
endmodule
如图所示:
2.1.2 分析与综合
将weightMemory设置为顶层:
关闭上次分析文件:
对设计进行分析,操作如图:
分析后的设计,Vivado自动生成原理图,如图:
原理图如图:
对设计进行综合,操作如图:
综合完成,关闭即可
2.1.3 功能仿真
创建仿真激励文件,操作如图:
双击打开,输入激励代码:
module tb_weightMemroy();
reg clk;
reg [6:0] address;
wire [32*32-1:0] weights;
localparam PERIOD = 100;
always
#(PERIOD/2) clk = ~clk;
initial begin
#0
clk = 1'b0;
address = 0;
#PERIOD
address = 1;
#PERIOD
address = 2;
#PERIOD
address = 32;
#PERIOD
$stop;
end
weightMemory UUT
(
.clk(clk),
.address(address),
.weights(weights)
);
endmodule
如图所示:
将tb_weightMemory设置为顶层:
开始进行仿真,操作如下:
仿真波形,如图:
仿真结束,关闭仿真:
2.2 processingElement
2.2.1 设计输入
创建processingElement文件,操作如图:
双击打开,输入代码:
module processingElement(clk,reset,floatA,floatB,result);
parameter DATA_WIDTH = 32;
input clk, reset;
input [DATA_WIDTH-1:0] floatA, floatB;
output reg [DATA_WIDTH-1:0] result;
wire [DATA_WIDTH-1:0] multResult;
wire [DATA_WIDTH-1:0] addResult;
floatMult FM (floatA,floatB,multResult);
floatAdd FADD (multResult,result,addResult);
always @ (posedge clk or posedge reset) begin
if (reset == 1'b1) begin
result = 0;
end else begin
result = addResult;
end
end
endmodule
如图所示:
2.2.2 分析与综合
将processingElement设置为顶层:
对设计进行分析,操作如图:
分析后的设计,Vivado自动生成原理图,如图:
对设计进行综合,操作如图:
综合完成,关闭即可:
2.2.3 功能仿真
创建仿真激励文件,操作如图:
双击打开,输入激励代码:
module tb_processingElement();
reg clk,reset;
reg [31:0] floatA, floatB;
wire [31:0] result;
localparam PERIOD = 100;
always
#(PERIOD/2) clk = ~clk;
initial begin
#0
clk = 1'b0;
reset = 1;
// A = 2 , B = 3
floatA = 32'b01000000000000000000000000000000;
floatB = 32'b01000000010000000000000000000000;
#(PERIOD/4)
reset = 0;
// A = 1 , B = 5
#(3*PERIOD/4)
floatA = 32'b00111111100000000000000000000000;
floatB = 32'b01000000101000000000000000000000;
#(3*PERIOD/2)
reset = 1;
#(PERIOD)
$stop;
end
processingElement PE
(
.clk(clk),
.reset(reset),
.floatA(floatA),
.floatB(floatB),
.result(result)
);
endmodule
如图所示:
将tb_processingElement设置为顶层:
开始进行仿真,操作如下:
仿真波形,如图:
仿真结束,关闭仿真:
2.3 Layer
2.3.1 设计输入
创建Layer文件,操作如图:
输入文件名:
确定创建:
双击打开,输入代码:
module layer(clk,reset,input_fc,weights,output_fc);
parameter DATA_WIDTH = 32;
parameter INPUT_NODES = 100;
parameter OUTPUT_NODES = 32;
input clk, reset;
input [DATA_WIDTH*INPUT_NODES-1:0] input_fc;
input [DATA_WIDTH*OUTPUT_NODES-1:0] weights;
output [DATA_WIDTH*OUTPUT_NODES-1:0] output_fc;
reg [DATA_WIDTH-1:0] selectedInput;
integer j;
genvar i;
generate
for (i = 0; i < OUTPUT_NODES; i = i + 1) begin
processingElement PE
(
.clk(clk),
.reset(reset),
.floatA(selectedInput),
.floatB(weights[DATA_WIDTH*i+:DATA_WIDTH]),
.result(output_fc[DATA_WIDTH*i+:DATA_WIDTH])
);
end
endgenerate
always @ (posedge clk or posedge reset) begin
if (reset == 1'b1) begin
selectedInput = 0;
j = INPUT_NODES - 1;
end else if (j < 0) begin
selectedInput = 0;
end else begin
selectedInput = input_fc[DATA_WIDTH*j+:DATA_WIDTH];
j = j - 1;
end
end
endmodule
如图所示:
2.3.2 分析与综合
将Layer设置为顶层:
关闭上次的分析文件:
对设计进行分析,操作如图:
分析后的设计,Vivado自动生成原理图,如图:
原理图如图:
对设计进行综合,操作如图:
综合完成,关闭即可:
2.3.3 功能仿真
创建仿真激励文件,操作如图:
双击打开,输入激励代码:
module tb_layer();
reg clk, reset;
reg [32*100-1:0] input_fc;
wire [32*32-1:0] weights;
wire [32*32-1:0] output_fc;
reg [7:0] address;
localparam PERIOD = 100;
always
#(PERIOD/2) clk = ~clk;
always @ (posedge clk or posedge reset) begin
if (reset == 1'b1) begin
address = 0;
end else begin
address = address + 1;
end
end
weightMemory WM
(
.clk(clk),
.address(address),
.weights(weights)
);
initial begin
#0
clk = 1'b0;
reset = 1'b1;
input_fc = 3200'b00111110110101101010010110110100001111110011010001110101011100000011111100101101101110010110001000111110101011101000010101010100001111110110000001111111101110100011111011000011010010010111101000111111011111001000101111111100001111110111001000101111111000000011111101011010001000111010111000111111011101000000011111101110001111100010111111011001011101100011111100000010101100110000001000111110101010010111100101011010001111101100110010000101101000000011111101111011101001111111110000111110011010111101000111101110001111110110011010000011110100100011111101101111110100011101110000111110101011011010100101100100001111100001000110100001000100100011111100010001101100110010100000111111001011100010000101100100001111110111100011001101111010100011111010100100100001010011110000111111010011010100111110011100001111110010101001000011010101100011111101111100000000000000000000111110101010001011100101010110001111110100110001101111100111000011111000001100010010010011010000111111000110110111111100111000001111011001010011100001001010100011111100001100000001110001010000111111000000111101100100000010001111110110111100001011111000000011111100111000110101110110101000111111001011110010100101011110001111110110111101110111111000100011111101101001101000011100101000111111010110110100010110111110001111110011000111010101010111100011111101001000000100011001000000111110110001001110100110000000001111101111011010111101111101000011111011110100011110011111011000111101110110011001000110111100001111101110000100001101101111100011110110101010011000010101101000111111010100001000110110100000001111101011010011101101011100000011111101100000101000011011111000111110101110111001010110000000001111110100010100110011100011100011111010010001011000010001110000111101010111101000000110110000001111110101100010001101101100000011111100001101101010010010001000111111000000111001100100001010001111110110010011011011110011100011111101101111001100111101101000111111010111111011100110111110001111110001000111101011001000000011111010110001110010010111000000111111001100001010001101100010001111101010011010010001010000100011111101011110000100111100001000111101101000101100000101000110001111110110000011111011110010100011111101010010101000111010110000111111000001010101101100000010001111110010101100000101010100100011111100000001111101010000011000111111001000010011100101000010001111101100011100001101100001000011110110111001100100010111100000111110111101111001110111110100001110110111000000000001110110100011111010000110000011010000001000111111010010010011011110001110001111110000111010110101000111000011111101111101111010111111101000111111000110011001101100101100001111011000101110000001000101000011111001010010100010011001100000111110110100011001010110101010001111110011100101111011011101000011111000110011010110010111100000111111000110111101100100110100001111110000011101111011000001100011111101011111000000111100000000111111001110111111111101111010001111101100001101100101100010000011111100000100011111010000101000111100001100111000000100110110001111110110110010100011110101100011111001011000011110011100010000111110000100101010000100110000001111110101100001110101101101000011110111101101011100011111001000111101011101111110000111101110;
#PERIOD
reset = 1'b0;
#(102*PERIOD)
$stop;
end
layer UUT
(
.clk(clk),
.reset(reset),
.input_fc(input_fc),
.weights(weights),
.output_fc(output_fc)
);
endmodule
如图所示:
将tb_layer设置为顶层:
开始进行仿真,操作如下:
仿真波形,如图:
仿真结束,关闭仿真:
2.4 integrationFC
2.4.1 设计输入
打开integrationFC文件,输入代码:
module integrationFC(clk,reset,iFCinput,CNNoutput);
parameter DATA_WIDTH = 32;
parameter IntIn = 120;
parameter FC_1_out = 84;
parameter FC_2_out = 10;
input clk, reset;
input [IntIn*DATA_WIDTH-1:0] iFCinput;
output [FC_2_out*DATA_WIDTH-1:0] CNNoutput;
wire [FC_1_out*DATA_WIDTH-1:0] fc1Out;
wire [FC_1_out*DATA_WIDTH-1:0] fc1OutTanh;
wire [FC_2_out*DATA_WIDTH-1:0] fc2Out;
wire [FC_2_out*DATA_WIDTH-1:0] fc2OutSMax;
wire [DATA_WIDTH*FC_1_out-1:0] wFC1;
wire [DATA_WIDTH*FC_2_out-1:0] wFC2;
reg FC1reset;
reg FC2reset;
reg SMaxEnable;
wire DoneFlag;
reg [7:0] address1;
weightMemory
#(.INPUT_NODES(IntIn),
.OUTPUT_NODES(FC_1_out),
.file("E:/FPGA_Learn/FPGA/Day1211/Weight/weightsdense_1_IEEE.txt"))
W1(
.clk(clk),
.address(address1),
.weights(wFC1)
);
layer
#(.INPUT_NODES(IntIn),
.OUTPUT_NODES(FC_1_out))
FC1(
.clk(clk),
.reset(FC1reset),
.input_fc(iFCinput),
.weights(wFC1),
.output_fc(fc1Out)
);
layer
#(.INPUT_NODES(FC_1_out),
.OUTPUT_NODES(FC_2_out))
FC2(
.clk(clk),
.reset(FC2reset),
.input_fc(fc1OutTanh),
.weights(wFC2),
.output_fc(fc2Out)
);
softmax SMax(
.inputs(fc2Out),
.clk(clk),
.enable(SMaxEnable),
.outputs(CNNoutput),
.ackSoft(DoneFlag)
);
endmodule
如图所示:
2.4.2 分析与综合
将integrationFC设置为顶层:
关闭上次的分析文件:
对设计进行分析,操作如图:
分析后的设计,Vivado自动生成原理图,如图:
希望本文对大家有帮助,上文若有不妥之处,欢迎指正
分享决定高度,学习拉开差距
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)