CNN

[CNN]5. MNIST Post Conv Layer

낑낑이 2025. 1. 19. 21:14

오늘 해볼것은 Post_Conv_top의 Review입니다

드디어 하나의 Layer동작에 대해서 마무리 짓게 되네요

Post_Conv_top은 Activation과 Pooling Layer가 존재합니다

Yolo Series에 대해서 보게되면  Activation Layer와 Maxpooling Layer가 사용되게 됩니다

 

1.Activation Layer

이게 ReLu함수인데 음수의 값은 다 0으로 만들어 주게됩니다

이와같은 이유로 Activation Function을 사용하게됩니다

2. MaxPooling Layer

 

이와같은 이유로 사용하게 됩니다

코드는 비교적 간단합니다

    input clk,
    input nRst,
   
    input [DW*16-1:0] data_in,
   
    input post_enable,
    output wire [Out_ch-1:0] Pool_valid,
    output [DW*16-1:0] Layer0_poolout
    );
   
    genvar i, j;
    wire [Out_ch-1:0] Act_valid;
    wire [DW-1:0] Act_out[0:Out_ch-1];
    wire [DW-1:0] Pool_out[0:Out_ch-1];
    generate
    for(i=0;i<Out_ch;i=i+1) begin : Out_Ch
        Activation Active_Function(.clk(clk), .nRst(nRst), .act_en(post_enable), .Act_in(data_in[DW*(16-i)-1:DW*(15-i)]), .Act_out(Act_out[i]), .Act_valid(Act_valid[i]));
    end
    endgenerate
    generate
    for(j=0;j<Out_ch;j=j+1) begin : Out_CH
        Pooling Max_Pooling(.clk(clk), .nRst(nRst), .Pool_en(Act_valid[j]), .Pool_in(Act_out[j]), .valid(Pool_valid[j]), .Pool_out(Pool_out[j]));
    end
    endgenerate
   
    assign Layer0_poolout = {Pool_out[0], Pool_out[1], Pool_out[2], Pool_out[3],
                              Pool_out[4], Pool_out[5], Pool_out[6], Pool_out[7],
                              Pool_out[8], Pool_out[9], Pool_out[10], Pool_out[11],
                              Pool_out[12], Pool_out[13], Pool_out[14], Pool_out[15]};
 

이와같이 선언하면되는데 여기서 data in은 Conv0_Layer의 결과인 Cram_da_out_bundle

    assign Cram_da_out_bundle = {Cram_da_out[0], Cram_da_out[1], Cram_da_out[2], Cram_da_out[3],
                                  Cram_da_out[4], Cram_da_out[5], Cram_da_out[6], Cram_da_out[7],
                                  Cram_da_out[8], Cram_da_out[9], Cram_da_out[10], Cram_da_out[11],
                                  Cram_da_out[12], Cram_da_out[13], Cram_da_out[14], Cram_da_out[15]};

이 결과입니다

post_enable은 Cram_rd_en이 on 되면 on되어 데이터를 읽게됩니다 

이와같이 구성된다고 보면될거같습니다 

Conv_Control의 state가 Active라면 해당 data를 보내게 됩니다

    generate
    for(i=0;i<Out_ch;i=i+1) begin : Out_Ch
        Activation Active_Function(.clk(clk), .nRst(nRst), .act_en(post_enable), .Act_in(data_in[DW*(16-i)-1:DW*(15-i)]), .Act_out(Act_out[i]), .Act_valid(Act_valid[i]));
    end
    endgenerate

해당 문을 보면 채널마다 Act_in이 들어오게되는데

module Activation#(
    parameter DW = 16
    )(

    input clk,
    input nRst,
   
    input act_en,
    input [DW-1:0] Act_in,
   
    output reg [DW-1:0] Act_out,
    output reg Act_valid
   
    );
           
    always @ (posedge clk or negedge nRst)
    begin
        if(!nRst)
        begin
            Act_out <= 0;
            Act_valid <= 0;
        end
        else
        begin
            if(Act_in[DW-1]==0)
                Act_out <= Act_in;
            else
                Act_out <= 16'b0;
            Act_valid <= act_en;
        end
    end

endmodule

이 구문을 보게되면 최상위 비트가 0이라면 즉 양수라면 Relu함수이기떄문에 그대로 입력값이 출력되고

최상위비트가 그렇지않다면 0을 내보내 음수의값은 0이 출력되도록합니다

간단하죠? 

Maxpooling 시 Cram에서 읽는방법

cram에서 data를 읽을때 저장은 

이처럼 한 행씩 저장하게 됩니다

하지만 Maxpooling은 

이와같이 Maxpooling에 이루어져야하는데 어떻게 해야 그렇게 할수있을까요? 바로 읽는방법에서 차이가 있습니다

    reg [1:0] Cram_rd_cnt;
    reg [3:0] Cram_row_cnt;
    always @(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
        begin
            Cram_raddr <= 0;
            Cram_rd_cnt <= 0;
            Cram_row_cnt <= 0;
        end
        else
        begin
            if(Cram_rd_en)
            begin
                if(Cram_raddr == Conv0_slice**2 - 1)
                begin
                    Cram_raddr <= 0;
                end
                else
                begin
                    if(Cram_rd_cnt == 3)
                    begin
                        if(Cram_row_cnt == 13)
                        begin
                            Cram_row_cnt <= 0;
                            Cram_raddr <= Cram_raddr + 1;
                        end
                        else
                        begin
                            Cram_raddr <= Cram_raddr - Conv0_slice + 1;
                            Cram_row_cnt <= Cram_row_cnt + 1;
                        end
                       
                    end
                    else if(Cram_rd_cnt == 1)
                    begin
                        Cram_raddr <= Cram_raddr + Conv0_slice - 1;
                    end
                    else
                    begin
                        Cram_raddr <= Cram_raddr + 1;
                    end
                end
                Cram_rd_cnt <= Cram_rd_cnt + 1;
            end
            else
            begin
                Cram_raddr <= 0;
                Cram_rd_cnt <= 0;
                Cram_row_cnt <= 0;
            end
        end
    end
 

처음 이걸 보면 무슨말 하는지 모를수 있습니다 다시 정리해보면

이와같이 정리할수있습니다. 이걸봐도 모를수도 있습니다

그림으로 설명하면

이와같이 읽기동작이 나타나게됩니다 

rd_cnt는 열을 읽는동작이고 Row_cnt는 행을 읽는 동작입니다

그렇게되면 이와같이 동작한다는것을 볼 수 있습니다

 

    generate
    for(j=0;j<Out_ch;j=j+1) begin : Out_CH
        Pooling Max_Pooling(.clk(clk), .nRst(nRst), .Pool_en(Act_valid[j]), .Pool_in(Act_out[j]), .valid(Pool_valid[j]), .Pool_out(Pool_out[j]));
    end
    endgenerate
 

이 구문을 보게되면 

Maxpooling은 

이와같이 선언되어있습니다. 즉 2*2 Maxpooling이기때문에 4개의 값중 가장 큰값을 비교해 내보내도록됩니다 

4개의 pixel을 비교해 valid신호를 on시켜 4개의 pixel중 가장 큰값을 Pool_out으로 나가게 만드는것입니다

Poolout의 결과도 다음 Layer에서 효율적으로 이용하기위해 Bundle로 묶어 

    assign Layer0_poolout = {Pool_out[0], Pool_out[1], Pool_out[2], Pool_out[3],
                              Pool_out[4], Pool_out[5], Pool_out[6], Pool_out[7],
                              Pool_out[8], Pool_out[9], Pool_out[10], Pool_out[11],
                              Pool_out[12], Pool_out[13], Pool_out[14], Pool_out[15]};

이렇게 출력되도록 만들어줍니다 

'CNN' 카테고리의 다른 글

[CNN]4. MNIST Conv0_Layer  (1) 2025.01.19
[CNN]3. MINST Conv0_Input Buffer  (0) 2025.01.16
[CNN]2. MNIST Ctrl  (0) 2025.01.16
[CNN]1. MNIST  (2) 2025.01.16
[CNN]Convolution Control  (2) 2025.01.16