一、项目概述

        通过Python,预先训练好的LeNet模型。选用VIVADO,使用Verilog硬件描述语言进行卷积层的硬件实现。

二、LeNet神经网络

        本文采用LeNet神经网络进行手写数字识别,这是一个在深度学习领域比较基础入门的神经网络,在理论结构上非常容易理解和实现,故此时就不再对其基本原理做详细的解释,感兴趣的friends可以在CSDN或者其他途径都可以找到许多相关资料学习

三、LeNet硬件实现

1.模块宏观结构

         采用LeNet实现对MNIST数据集的手写数字识别,MNIST数据集中的图片大小为28x28,在上述卷积层中所采用的卷积核大小为5x5x6,由于卷积核的第三维维数是6,故每一次卷积结果都会得到6个数。在原始图片中各像素值用8位宽储存,卷积核参数也用8位宽储存,所以卷积结构用16位宽进行储存。上图中,clk为输入时钟信号、rst_n为输入复位信号、data_in为数据输入信号、data_in_valid为数据输入有效信号、data_out为数据输出信号、data_out_valid为数据输出有效信号。

module conv1
(
    input                clk,
    input                rst_n,
    
    input     [8-1:0]    data_in,
    input                data_in_valid,

    output    [6-16-1:0] data_out,
    output               data_out_valid       
);

2.卷积核参数的读取与写入

reg    [4:0]    wr_addr;
reg    [4:0]    rd_addr;
wire   [4:0]    rd_addr_pre2 = wr_addr + 2;

        其中,卷积核有6层,每一层参数个数为5x5=25(十进制)=11001(二进制),由此可知需要用5位位宽来表示对应的地址空间。

        给定写入地址后,需在下一个时钟周期,数据才会写入到储存块中。

always@(posedge clk or negedge rst_n)begin
    if(~rst_n)begin
        wr_addr <= 0;
        rd_addr <= 0;
    end

    else if(data_in_valid == 1'b1)begin
        if(wr_addr == 'd27)
            wr_addr <= 5'd0;
        else
            wr_addr <= wr_addr + 1'd1;

        if(rd_addr_pre2 > 'd27)
            wr_addr <= wr_addr - 5'd28;
        else
            rd_addr <= rd_addr_pre2;
    end
end

            最后在仿真结果中呈现,在同一时钟周期下,rd_addr_pre2 = rd_addr + 1 = wr_addr + 2.

          对于数据的输入,用一个大小为5的数组进行储存,每个数据位宽为8位。

wire    [7:0]    window_in[0:5-1];
wire    [7:0]    window_out[0:5-1];
assign    window_in[0] = data_in;

genvar k;
generate
for(k=1;k<5;k=k+1)begin
    assign window_in[k] = window_out[k-1];
end
endgenerate

         该卷积层对应的卷积核大小为5x5x6,故而采用5个BRAM用于存储卷积核参数。

generate
for(k=0;k<5;k=k+1)begin
// BRAM
    gray_linebuffer gray_linebuffer_U(
        .clka(clk),
        .wea(data_in_valid),
        .addra(wr_addr),
        .dina(window_in[k]),    
    
        .clkb(clk),
        .enb(1'b1),
        .addrb(rd_addr),
        .doutb(window_out[k])
);
end
endgenerate

        上述代码所描述的,5个BRAM所构成的结构如下图所示,类似于移位寄存器的工作原理及结构。

         从上仿真波形图可以看出,在写入地址wr_addr=6时,输入数据data_in=72,对应的window_in[0]=72,所以此时在BRAM[0]的地址为6处存入数据72。当系统执行至读出地址rd_addr=6时,读出数据72,即window_out[0]=72,但是在上述波形图中对应的rd_addr=7,这是因为BRAM的读操作使用了输出寄存器,读操作输出需要一个额外的等待周期,即是当传入rd_addr=6后,在下一个时钟周期才会输出数据72,而在此时钟周期下,rd_addr以及加一变为7了。相应的,由于window_out[0]=72,那么就会使window_in[1]=72。其余数据均同理。

        接下来,定义计算窗口,即对应卷积核大小为5x5。

reg    signed [8:0]    window[5-1:0][5-1:0]
integer    i, j;
always@(posedge clk, negedge rst_n)begin
    if(~rst_n)begin
        for(i=0;i<5;i=i+1)begin
            for(j=0;j<5;j=j+1)begin
                window[i][j] <= 0;
            end
        end
    end

    else if(data_in_valid == 1'b1)begin
        for(i=0;i<5;i=i+1)begin
            window[i][0] <= window_in[i];
            for(j=1;j<5;j=j+1)begin
                window[i][j] <= window[i][j-1];
            end
        end
    end

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐