循环卷积和周期卷积的关系_基于单口RAM读写的卷积电路(下)
這是遲到很久的卷積電路verilog設計的下篇。。。你看我還有機會嗎。。。
上回我們給出系統的層次結構、卷積計算模塊以及用于數據緩存的fifo模塊,今天我們首先回顧一下上一次的關鍵內容。
系統結構回顧
RTL代碼文件可以分為結構如下所示?
~|--top_conv_tb.v|--top_conv.v| |--sram_input.v| |--sram_weight.v| |--sram_output.v |--conv.v |--weight_addr_gen.v |--pixel_addr_gen.v |--conv_calculation.v |--conv_fifo.v |--fifo_wr_control.v其中top_conv.v為設計的頂層模塊,其只有start,reset,clk三個輸入端口以及一個輸出端口finish,top_conv.v模塊負責例化各子模塊,完成conv.v, sram_input, sram_onput以及sram_weight之間的連接。?
sram_input, sram_onput以及sram_weight三個子模塊用于存儲輸入和輸出,直接例化所給代碼。
conv模塊是實驗中需要主要設計的模塊,共包含五個子模塊,各個子模塊的功能概述如下:
?weight_addr_gen模塊:用于在每個時鐘周期產生當前單元計算卷積所需要的權重在sram中的地址;?pixel_addr_gen模塊:用于在每個時鐘周期產生當前單元計算卷積所需要的像素值在sram中的地址;?conv_calculation模塊:利用得到的像素值和權重完成相乘、累加,并在當前窗口全部像素計算完成后,產生輸出請求信號,并輸出該窗口的卷積值;?conv_fifo模塊:輸入數據的sram和計算模塊之間的緩沖模塊,接受像素數據存儲器sram_input和權重數據存儲器及sram_weight的輸出數據,并向conv_calculation模塊輸出緩存數據;?fifo_wr_control模塊:這一模塊較為簡單,為一級寄存器,用于調整兩個地址產生模塊的讀請求、讀地址信號和寫fifo之間的時序。
整合后的系統框圖如下所示:
下面就進入到今天的新內容,我們將介紹系統中最后一個稍微麻煩的地址產生模塊以及給出最后的結果展示。
地址產生模塊
對于權重的地址產生模塊較為簡單,只需要依次從0到8進行循環即可:
//generate output weight_addralways @(*) beginif (start) begin case(weight_cnt) 4'd0: weight_addr = 0; 4'd1: weight_addr = 1; 4'd2: weight_addr = 2; 4'd3: weight_addr = 3; 4'd4: weight_addr = 4; 4'd5: weight_addr = 5; 4'd6: weight_addr = 6; 4'd7: weight_addr = 7; 4'd8: weight_addr = 8; default:weight_addr = `weight_addr_width'bx; endcaseendelseweight_addr = 0;end而對于像素的地址產生模塊,要相應的復雜一些,這里分兩步進行,首先產生每次計算時像素窗口第一個像素(左上角)的地址first_pixel,然后依次遍歷窗口內的各個元素:
//generate the first position(top-left) in the conv windowalways @(posedge clk or negedge reset) beginif (!reset) first_pixel <= `pixel_addr_width'b0;else if (start&&pixel_cnt==8) begin case(first_pixel) 29,61,93,125,157,189,221,253,285,317,349,381,413, 445,477,509,541,573,605,637,669,701,733,765,797, 829,861,893,925: first_pixel <= first_pixel+3; default:first_pixel <= first_pixel+1; endcaseendelse first_pixel <= first_pixel;end//generate output pixel_addralways @(*) beginif (start) begin case(pixel_cnt) 4'd0: pixel_addr = first_pixel + 7'd0; 4'd1: pixel_addr = first_pixel + 7'd1; 4'd2: pixel_addr = first_pixel + 7'd2; 4'd3: pixel_addr = first_pixel + 7'd32; 4'd4: pixel_addr = first_pixel + 7'd33; 4'd5: pixel_addr = first_pixel + 7'd34; 4'd6: pixel_addr = first_pixel + 7'd64; 4'd7: pixel_addr = first_pixel + 7'd65; 4'd8: pixel_addr = first_pixel + 7'd66; default:pixel_addr = `pixel_addr_width'bx; endcaseendelsepixel_addr = 0;end結果展示
1.波形仿真
我們首先進行波形仿真驗證。運行vivado對我們的設計進行仿真驗證,波形輸出如下圖所示:
仿真的關鍵信號大致可以分為讀sram,寫fifo,讀fifo,卷積計算以及輸出這五組,從圖中可以看出各個信號的時序滿足我們的設計需求,在開始信號start拉高后,conv_calculation模塊發出數據請求信號s_read_req,fifo即開始從sram中讀取數據。在fifo為非empty的后一個周期,fifo_read信號有效,conv_calculation開始從fifo中讀取數據,在開始計算后的第九個周期,計算模塊產生當前單元的正確卷積結果并發出完成信號sum_done用于輸出請求。
關于計算模塊的仿真波形區域放大圖,展示如下:
通過sram_output.v中的以下語句,我們將卷積輸出結果輸出到文本文件"Write_Out_File .txt中。
Write_Out_File =$fopen("Write_Out_File .txt");$fdisplay(Write_Out_File,"%h",s_write_data_b);Write_Out_File .txt文件共900行,對應于輸出圖片的900個像素點,安裝實驗要求,load_txt_to_pic.py將txt中的矩陣結果轉化為輸出圖片并顯示。作為參考,我們可以調用python中opencv庫中的cv.filter2D函數進行卷積運算,結果如下所示:
可以看出經過conv.v卷積邊緣提取后的結果仍能大致看出原有圖片輪廓,但是與直接運用python中cv.filter2D相比,有些細節仍然有所丟失。這與我們實驗中進行數據定點化處理以及cv.filter2D中卷積計算時的優化處理有關。另外,我們可以看出使用cv.filter2D后,圖片的大小仍然保持不變,這是由于在cv.filter2D中進行了一定的填充插值處理。
2.計算資源消耗
?計算延時:整個計算過程開始于start信號拉高的23ns,結束于finish信號拉高的89935ns,在一個時鐘周期為10ns的前提下,完成計算共需要8995個時鐘周期。?乘法器資源:實驗中一共進行了900次乘法運算,但每9個時鐘周期內只單獨進行一次乘法運算,所以總共需要一個乘法器。?訪存次數:訪存需要的次數與參考設計中相同,即每次計算一個3*3卷積窗,從input ram和weight ram取數3*3次,并存output到output ram一次。共訪問input ram 30*30*3*3次,訪問weight ram 30*30*3*3次,訪問output ram 900次。總共訪問次數為17100次。
vivado綜合后的資源使用情況如下:
優化方向
在前面的設計中,我們是按照卷積窗口的移動順序每次從sram中依次取出數據,對于這樣方法,計算一個卷積窗口需要9次訪存,總共900個窗口需要900*9次訪存,然而事實上,窗口中的9個weight的數據在計算始終保持不變,而計算相鄰窗口時pixel的部分數據也可以重復利用(數據復用),因此我們可以通過減少訪問模塊外部的sram的次數來提高系統的運行速度。?
對于weight來說,如果我們將sram中的數據讀取后存儲在計算模塊內的存儲器中,事實上總共只需要一次讀weight_sram的操作,從而可以大大減少訪問weight時的訪存次數。對于pixel來說,相鄰的窗口的部分數據讀到conv模塊后可以進行復用,如下圖所示,使用數據復用也可以減少pixel時的訪存次數。
另一種更加普遍的思路是將卷積計算轉化為矩陣乘法計算(im2col),對于矩陣乘法運算有大量的優化算法,并可以進一步利用脈動陣列來實現計算的并行以及數據的重用(谷歌TPU的基本架構),或者使用加法器和多個并行乘法器組成的加法樹完成計算的并行(寒武紀Diannao的基本架構)。這些優化方法由于時間原因,沒有進一步在現有的卷積電路上加以實現,但我們會在后續的推文中給出使用HSL搭建的卷積電路,從中可以明顯看出在使用了流水線以及循環展開unroll來獲得226倍的加速比。
總結
以上是生活随笔為你收集整理的循环卷积和周期卷积的关系_基于单口RAM读写的卷积电路(下)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 麟龙指标通达信指标公式源码_通达信指标公
- 下一篇: 原神手游噗噗雪人怎么获得