基于神经网络的智能去雾芯片(卷积层的rtl代码实现)

        对于该芯片,我们使用的神经网络为AOD_net,输入特征图为640*480大小,位宽为8,是一个8位二进制数,范围为0-255,但在实际设计过程中,由于芯片面积的限制,我们精修了部分网络,并通过软件验证与原模型得出的结果误差小于百分之1,以下是我们设计时所使用的最终网络模型

        接下来我们介绍卷积层conv2的Verilog代码实现(已通过软件计算验证),第二层卷积层为3*3*3的kernel_size(3通道),对于整个卷积层,使用组内焦文博学长的卷积层设计思路和设计代码做一个详解,方便自己理解和学习,接下来是整个conv的模块连接思路(较为简洁)

下方为conv的状态机思路

        如上,状态由IDLE开始跳转,当输入的使能信号en为1是,IDLE跳转为C1状态,也就是channel1的计算状态,这个计算需要经过3个周期,每个channel都要3个周期计算,三通道计算完毕后停下,随后等待18个clk回到C1状态,至于为什么要等18个周期,这是因为我们最慢的一层计算需要27个周期(更改网络前,修改后实际上最慢需要18个周期,我日后会找时间精进修改),27-9=18,因此需要conv2闲置18个周期随后继续计算,

以下是状态机具体代码

    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            state<=0;
            cnt<=0;
        end
        else begin
            case (state)
                IDLE: begin
                    cnt<=cnt;
                    if (en) begin
                        state<=C1;
                    end
                    else begin
                        state<=IDLE;
                    end
                end
                C1:begin
                    if (cnt==2) begin
                        state<=C2;
                        cnt<=0;
                    end
                    else begin
                        state<=C1;
                        cnt<=cnt+1;
                    end
                end
                C2:begin
                    if (cnt==2) begin
                        state<=C3;
                        cnt<=0;
                    end
                    else begin
                        state<=C2;
                        cnt<=cnt+1;
                    end
                end
                C3:begin
                    if (cnt==2) begin
                        state<=STOP;
                        cnt<=0;
                    end
                    else begin
                        state<=C3;
                        cnt<=cnt+1;
                    end
                end
                STOP:begin
                    if (cnt==17) begin
                        state<=C1;
                        cnt<=0;
                    end
                    else begin
                        state<=STOP;
                        cnt<=cnt+1;
                    end
                end
                default: begin
                    cnt<=cnt;
                    state<=IDLE;
                end
            endcase
        end
    end

下方为为什么一个通道计算需要3个周期

        如上,对于三通道的输入图片,首先我们设定一个通道的卷积计算耗时一个周期,而第一个通道分别与三个卷积核的第一个通道分别卷积得到输出特征图的第一个通道的第一个像素点的值,而这个计算我们设定在三个周期内完成,而总共有三个通道,因此总共需要计算九个周期。

        而我们也设置了一个data2pe,来实现,在不同通道的计算状态下,存取不同通道的数据来实现计算,下方为部分代码,channel_RF为输入三通道的不同通道的数据,为寄存器组类型

    always @(*) begin
        case (state)
            IDLE: data2pe<=0;
            C1: data2pe<=channel_RF[0];
            C2: data2pe<=channel_RF[1];
            C3: data2pe<=channel_RF[2];
            STOP:data2pe<=0;
            default: data2pe<=0;
        endcase
    end

        我们定义一个子模块实现计算功能,为PE,同时在实现PE功能前,我们还需要放入我们所训练出来的权重数据来帮助计算,为weights模块,我们在权重模块定义寄存器组来存放weights数据,而weights模块方面的状态机如下图

        我们采用三段式来描述这个FSM,其优点为优点:A、FSM做到了同步寄存器输出;B、消除了组合逻辑输出的不稳定与毛刺的隐患;C、更利于时序路径分组;D、在FPGA/CPLD等可编程逻辑器件上的综合与布局布线效果更佳,但是资源相对于两段式状态机消耗更多代码如下

    //FSM1:状态转移时序逻辑
    always @(posedge clk or negedge rstn) begin
        if(!rstn)begin
            cstate <= IDLE;
        end
        else begin
            cstate <= nstate;
        end
    end

    //FSM2:产生下一个状态组合逻辑
    always @(*) begin
        if(!rstn)begin
            nstate = IDLE;
        end
        else begin
            case(cstate)
                IDLE:begin
                    if(weigth_en) nstate = SEND;
                    else nstate = IDLE;
                end
                SEND:begin
                    if(cnt_9_flag) nstate = STOP;
                    else nstate = SEND;
                end
                STOP:begin
                    if(cnt_26_flag) nstate = IDLE;
                    else nstate = STOP;
                end
                default: nstate = IDLE;
            endcase
        end
    end

    //FSM3:产生输出时序逻辑
    always @(posedge clk or negedge rstn) begin
        if(!rstn)begin
            kernel_weights <= IDLE;
        end
        else begin
            case(nstate)
                SEND:kernel_weights <= W_RF[cnt];
                default:kernel_weights <= 72'd0;
            endcase
        end
    end

         如上,利用cnt来分周期将权重数据赋值到kernel_weights中,接下来我们考虑PE模块的设计,即卷积计算的设计,对于这个计算我们使用三级流水,具体流程如下图

        完成上述传输计算后对于完整的卷积层计算还差添加偏置和激活函数(relu函数),因此我们随后给出bias模块和relu模块的设计思路和部分代码

        对于bias模块,我们考虑引入一个flag来控制剩余的conv计算,当flag有效时,计数器正常+1,从而控制三个周期完成计算,我们在bias模块中利用一个always块引入一个小型的一级流水线,加快计算速度,3个周期内完成计算,具体代码如下


    //cnt 控制信号
    always @(*) begin
        if (!rstn) begin
            flag=0;
        end
        else if(bais_en) begin
            flag=1;
        end
        else if(cnt==2) begin
            flag=0;
        end
        else flag=flag;
    end
    //cnt++
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            cnt<=0;
        end
        else if(cnt==2) begin
            cnt<=0;
        end
        else if(flag)begin
            cnt<=cnt+1;
        end
        else begin
            cnt<=cnt;
        end
    end
    //
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            data_m<=0;
            Bais<=0;
            bais2relu<=0;
        end
        else begin
            data_m<=pe2bias*M_RF[cnt];
            Bais<=B_RF[cnt];
            bais2relu<=(data_m>>>16)+Bais;
        end
    end
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            bais_valid<=0;
        end
        else if(cnt==1) begin
            bais_valid<=1;
        end
        else bais_valid<=0;
    end

        最后生成的valid有效信号是为了给下一个relu模块计算使用的使能信号 ,而我们编写的relu模块就很简单了,只需要实现小于-128的赋-128,大于127的赋值127,其余不变

module conv2_ReLU#(
    parameter WIDTH = 8
)(
    input clk,
    input rstn,
    input signed [20:0]qy,//输入数据
    output reg signed [WIDTH-1:0]q_relu //输出数据
    );

    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            q_relu <= 0;
        end
        else begin
            if (qy>127) begin
                q_relu<=127;
            end
            else if(qy<-128) begin
                q_relu<=-128;
            end
            else q_relu<=qy;
        end
    end


endmodule

接下来便是通过总conv2模块来将weights,pe,bias,relu连接起来,最终输出data_out,为一个3*3*3的输出特征图,以上不为完整代码,只为部分重要处进行讲解,详细代码请私信

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sonne_hfuter

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值