lab4:VGG16 及 如何把c的for转成verliog状态机
准备工作:
(1)维度:
卷积维度 粉红色最后的卷积结果矩阵维度=绿色矩阵维数-橙色矩阵维数+1
池化的最终的结论是要把原来的维度减少到1/n
padding:周围维一圈0 => 粉=绿+2-3+1=绿 维数可以不减(在卷积核维数是3时)
(2)vgg 16结构
附件的vgg16.txt
Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) :3通道,每通道64个(64*3的矩阵),步长1,补01圈
从矩阵可以看出并行和分块的思路。
nn.ReLU()
这个表示使用ReLU激活函数,里面有一个参数inplace,默认设置为False,表示新创建一个对象对其修改,也可以设置为True,表示直接对这个对象进行修改
MaxPool2d (size=(2, 2), stride=(2, 2), dilation=(1, 1)) :pooling 相邻2*2矩阵选最小值,步长2
(3)总结构:2+2+3+3+3+3=16
数据
(下面省略了bias和RU,每次卷积核全连接都要激活)
state1
(1)64*3(3*3)@ 3*1(224*224)= 64*1*(224*224=3211264)
(2)64*64(3*3=36864)@ 64*1(224*224)= 64*1(224*224)
(3)=64*1(112*112)
state2
(4)128*64(3*3)@ 64*1(112*112)=128*1(112*112=1605632)
(5)128*128(3*3=147456)@ 128*1(112*112)=128*1(112*112)
(6)=128*1(56*56)
state3
(7)256*128(3*3)@ 128*1(56*56)=256*1(56*56)
(8)256*256(3*3=589824)@ 256*1(56*56)=256*1(56*56)
(9)256*256(3*3)@ 256*1(56*56)=256*1(56*56)
(10)=256*1(28*28)
state4
(11)512*256(3*3)@ 256*1(28*28)=512*1(28*28=401408)
(12)512*512(3*3=2359296)@ 512*1(28*28)=512*1(28*28)
(13)512*512(3*3)@ 512*1(28*28)=512*1(28*28)
(14)=512*1(14*14)
state5
(15)512*512(3*3)@512*1(14*14)=512*1(14*14)
(16)512*512(3*3)@512*1(14*14)=512*1(14*14)
(17)512*512(3*3)@512*1(14*14)=512*1(14*14)
(18)=512*1(7*7)= 25088*1
state6
(19)(4096*25088=102760448) @ (25088*1)= (4096*1)
state7
(20)(4096*4096=16777216)@(4096*1)=(4096*1)
state8
(21)(1000*4096)@(4096*1)=(1000*1)
经验和错误
1.状态机不好拆分地话可以先写成嵌套for循环再拆:
(1)写c并验证
下面是最基本的写法,要改粒度的话改一下自加的步长,换循环方式交换ij都可以实现并行计算。
#include "stdio.h" /* son_num_list={0,2,2,3,3,3} 命名规则 cov_state_ cov状态机需要的参数 cov_xxx_i cov状态机的xxx循环变量 cov_state cov的状态 cov_state_start 状态机开始信号 cov_state_finish 状态机完成信号 full 前连接层 */ FILE *out; int cov_state_son_num,cov_state_data_dimension, cov_state_row,cov_state_col, weightAddr,dataRAddr,dataWAddr; int axi_start,axi_finish; int full_state_row,full_state_col; void axi_read(){ axi_finish=1; } void loadWeight(){ } void loadData(){ } void cov(){ } void rectMul(){ } void addToAns(){ } int flag=0,exit=0; int main(){ out=fopen("out.txt","w"); for(int global_state=1;global_state<=9;global_state++){ //卷积 if(global_state==1,2,3,4,5){ cov_state_son_num=global_state<=2?2:3; cov_state_data_dimension=224/global_state;//data的维数 for(int cov_son_i=0;cov_son_i<cov_state_son_num;cov_son_i++){ //初始化数据 axi_start=1; axi_read(); while(axi_finish==0);//等待数据初始化完成,初始化完成后跳出 axi_finish=0; axi_start=0; //data维度 cov_state_data_dimension=224/global_state; //weight行数 //weight列数 switch(global_state){ case 1:cov_state_row=64;cov_state_col=cov_son_i==0?3:64;break; case 2:cov_state_row=128;cov_state_col=cov_son_i==0?64:128;break; case 3:cov_state_row=256;cov_state_col=cov_son_i==0?128:256;break; case 4:cov_state_row=512;cov_state_col=cov_son_i==0?256:512;break; case 5:cov_state_row=512;cov_state_col=512;break; default:; } //开始cov状态机 for(int cov_weight_i=0;cov_weight_i<cov_state_row;cov_weight_i++){ //取bias int bias=0; for(int cov_weight_j=0;cov_weight_j<cov_state_col;cov_weight_j++){ //weight的起始地址,连续load9个 weightAddr=cov_weight_i*cov_state_col+cov_weight_j; loadWeight(); //data 行cov_data_i 列cov_data_j for(int cov_data_i=0;cov_data_i<cov_state_data_dimension;cov_data_i++){ for(int cov_data_j=0;cov_data_j<cov_state_data_dimension;cov_data_j++){ //data的左上角地址,load上下3*3 dataRAddr=cov_data_i*cov_state_data_dimension+cov_data_j; loadData(); //注意补0,先判断addr //卷积计算 cov(); //累加 位置:cov行的 int ans_base=cov_weight_i*cov_state_data_dimension*cov_state_data_dimension; int ans_i=cov_data_i; int ans_j=cov_data_j; dataWAddr=ans_base+ans_i*cov_state_data_dimension+ans_j; addToAns(); } } } } } //pool,pool的结果加bias int t=0; for(int pool_num=0;pool_num<cov_state_row;pool_num++){ int pool_r_base=pool_num*cov_state_data_dimension*cov_state_data_dimension; int pool_w_base=pool_num*(cov_state_data_dimension/2)*(cov_state_data_dimension/2); for(int pool_i=0;pool_i<cov_state_data_dimension;pool_i+=2){ for(int pool_j=0;pool_j<cov_state_data_dimension;pool_j+=2){ dataRAddr=pool_i*cov_state_data_dimension+pool_j+pool_r_base; //dataWVal=loadPool()+bias;//取相邻2*2 选最大值 dataWAddr=(pool_i/2)*(cov_state_data_dimension/2)+pool_j/2+pool_w_base; } } } } //全连接 if(global_state==6,7,8){ full_state_row=global_state==8?1000:4096; full_state_col=global_state==6?25088:4096; axi_start=1; axi_read(); while(axi_finish==0);//等待数据初始化完成,初始化完成后跳出 axi_finish=0; axi_start=0; rectMul(); } if(global_state==9) exit=1; } fclose(out); }
(2)拆状态机,有for的都能拆出来,写上标记
(3)起名字
将所有标记改为xxx_finish=0;xxx_start=1;while(xxx_finish==0);
改写for循环:
进之前初始化,叶子自加
如两层ij循环
#include "stdio.h" int name_state=0; int name_start,name_finish;//控制信号 int name_i,name_j;//循环变量 //循环体变量 int n=5; int main(){ for(int i=0;i<n;i++){ for(int j=0;j<n*n;j++){ printf("%d %d\n",i,j); } } //改为 name_state=0; name_finish=0; name_start=1; while(name_finish==0){ switch(name_state){ case 0: if(name_start==1){ name_i=0; name_state=1; } break; case 1: if(name_i>=n) { name_finish=1; name_start=0; name_state=0; } else{ name_j=0; name_state=2; } break; case 2: if(name_j>=n*n){ name_i++; name_state=1; }else{ printf("%d %d\n",name_i,name_j); name_j++; } break; } } }
(4)用查找替换,把int换成integer,把{换成begin,}换成end,case删除,switch换成case,break删去
粘到verilog里,再改改endcase,i++展开写就能用了。