verilog實現矩陣卷積運算
verilog實現卷積運算
卷積的運算原理
卷積是一種線性運算,是很多普通影像處理操作的基本演算法之一。它提供了兩個陣列相乘的方式,兩個陣列擁有不同的大小,但是具有相同的維數,生成了一個用於相同維數的新陣列。可以用來影像的執行操作,輸入一組特定的畫素值線性組合成為另一組畫素值。在影像處理中常見的msk運算都是卷積,廣泛應用於影像濾波。
1.1卷積出現的背景
卷積是在訊號與線性系統的基礎或背景中出現的,脫離這個背景單獨談卷積是沒有任何意義的,除了那個所謂褶反公式上的數學意義和積分(或求和,離散情況下)。
訊號與線性系統,討論的就是一個訊號經過一個線性系統以後發生的變化(就是輸入、輸出和所經過的所謂系統,這三者間的運算關係)。
因此,實際都是要根據我們需要處理的訊號形式,來設計所謂的系統傳遞函式,那麼這個系統的傳遞函式和輸入訊號,在數學上的形式就是所謂的卷積關係。
卷積關係最重要的一種情況,就是在訊號與線性系統或數字訊號處理中的卷積定理。利用該定理,可以將時間域或空間域中的卷積運算等價為頻率域的相乘運算,從而利用FFT等快速演算法,實現有效的計算,節省運算代價。卷積的本質是滑動平均思想,它就是一種微元相乘累加的極限形式。卷積本身不過是一種數學運算而已,就跟“蝶形運算”一樣,在訊號與系統中,y(t)=f(t)*h(t)。時域的卷積等於頻域的乘積,即有Y(s)=F(s)×H(s),拉氏變換後得到的函式其實就是訊號的頻域表示式)。
然而,在通訊系統中,我們關心的以及要研究的是訊號的頻域,不是時域,原因是因為訊號的頻率是攜帶有資訊的量。所以,我們需要的是這個表示式,但是實際上,我們往往不能很容易得到。
卷積其實就是通過兩個函式 f和g生成第三個函式的一種數學演算法,表徵函式與經過翻轉和平移的的重疊面積。如果將參加卷積的一個函式看作區間的指示函式,卷積也可以被看做是“移動平均”的推廣。
圖1.1中兩個方形脈衝波做卷積。其中函式首先對反射,接著平移“t”,成為。那麼重疊部分的面積就相當於“”處的卷積,其中橫座標代表待積變數以及新函式的自變數。
構造一個3 * 3的卷積核,並利用該卷積核完成與66矩陣的卷積運算,資料位寬8bit補碼數, 結果位寬20bit補碼數。
卷積的基本過程如下:
對卷積核進行180度翻轉(資料讀寫順序的排程)將33卷積核的中心對準66矩陣的每個數進行對應資料乘累加得出結果,如此往復作業。輸入資料補碼8bit,實際有效7bit,輸出資料補碼20bit,實際有效19bit,卷積增加位數33=9,所以單個乘法最多增加19-7-9=3bit。所以卷積核採用3bit數,即4bit補碼數。
內建電路圖如圖二所示:
正如第二部分對本次設計的介紹,我們要做到的是對模擬訊號的取樣由A/D轉換器來完成,而卷積過程由訊號的移位來實現。為了設計卷積運算器,首先要設計RAM 和A/D轉換器的VerilogHDL 模型。在電子工業發達的國家,可以通過商業渠道得到非常準確的外圍器件的虛擬模型。如果沒有外圍器件的虛擬模型。因為RAM和A/D轉換器不是我們設計的硬體物件,所以需要的只是他們的行為模型,精確的行為模型需要認真細緻的編寫,並不比綜合模組容易編寫。
運算過程簡介
系統內建33的4bit補碼數的卷積核
外部輸入66的8位元補碼數:
工作過程:系統將反轉後的卷積核與上圖某一紫色圈圈資料和其周圍的紅色框框中的資料進行相乘並相加。直到所有紅色矩形框中的部分均進行過卷積操作。
舉例如下,對應第一個要進行卷積操作的數D32
輸出資料為:O32= D21C33 + D22C32 + D23C31+ D31C23 + D32C22 + D33C21+ D11C13 + D42C12 + D43C11 ;
工作說明
每次啟動後TB讀取要卷積的資料,並將此資料傳輸給CONV,每次傳輸一個資料即8bit。
CONV接收完資料後開始卷積。卷積結束後把資料傳輸給TB,每次傳輸一個資料即20bit。
結果驗證
Python中的Scipy包致力於科學計算中常見問題的各個工具箱。它的不同子模組相應於不同的應用。例如:插值、積分、優化、影像處理、統計、特殊函式等等。通常用於計算numpy矩陣,有效便捷。
1)Python中卷積實現的原理:對於in_c個通道的輸入圖,如果需要經過卷積後輸出out_c個通道圖,那麼總共需要in_cout_c個卷積核參與運算。例如,輸入為[h:5,w:5,c:4],那麼對應輸出的每個通道,需要4個卷積核。輸出為3個通道,所以總共需要3*4=12個卷積核。對於單個輸出通道中的每個點,取值為對應的一組4個不同的卷積核經過卷積計算後的和。
模擬說明
Modelsim可以支援命令列的方式,通過建立do檔案,可以整合多個可執行的命令。那麼對於前期一邊編寫程式碼,一邊進行功能模擬,使用do檔案是可以明顯提高工作的效率。
編寫wave檔案對其進行波形模擬:在模擬前Transcript中命令:do wave.do載入預設波形;輸出結果在Transcript中檢視,輸出結果如圖:
可以看到上圖的紅色框框中的輸出結果與python的驗證結果相同,模擬結果圖中的輸入資料與input_data中的資料一致。
我們以輸入為6個通道的矩陣作為輸入、3*3的卷積核、1個通道寬高分別為4的輸出,作為結果驗證。
在modelsim,我們主要對testbench進行模擬。testbench程式碼如下:
//TESTBENCH
`timescale 1us/1us
module TESTBENCH();
reg signed [7:0] TiData[1:6][1:6]; // Test input Data
reg signed [19:0] ToData[1:4][1:4]; // Test output Data
reg signed [7:0] TiDataSingle; // for transmission
wire signed [19:0] ToDataSingle; // for transmission
reg clk;
reg reset;
reg CONV_start;
wire CONV_finish;
reg [7:0] i;
reg [7:0] j;
parameter period = 10;
parameter hperiod = 5;
CONV CONV_T(
.reset(reset),
.clk(clk),
.CONV_start(CONV_start),
.CONV_finish(CONV_finish),
.CONV_iData(TiDataSingle),
.CONV_oData(ToDataSingle));
initial
begin
$display("0.Load Data");
$readmemh("Data_input.txt", TiData);
for(i = 1; i < 7; i = i + 1)
$display("%d %d %d %d %d %d", TiData[i][1], TiData[i][2], TiData[i][3],
TiData[i][4], TiData[i][5], TiData[i][6]);
clk = 0;
CONV_start = 0;
reset = 1; // Reset Chip
#period
reset = 0; // Chip Working
#period
CONV_start = 1; // CONV start and writing data
// align test data to the negedge of clk
$display("1.Write Data");
for(i = 1; i < 7; i = i + 1)
for(j = 1; j < 7; j = j + 1)
begin
TiDataSingle = TiData[i][j];
#period;
end
CONV_start = 0; // finish writing data
$display("2.Convolution");
while(!CONV_finish) #period;
#period;
$display("3.Read Data");
for(i = 1; i < 5; i = i + 1)
for(j = 1; j < 5; j = j + 1)
begin
ToData[i][j] = ToDataSingle;
end
for(i = 1; i < 5; i = i + 1)
$display("%d %d %d %d", ToData[i][1], ToData[i][2], ToData[i][3], ToData[i][4]);
$display("End"):
end
always #hperiod clk = !clk;
endmodule
verlog原始碼
module CONV(
input wire reset,
input wire clk,
input wire CONV_start,
output reg CONV_finish,
input wire signed [7:0] CONV_iData,
output reg signed [19:0] CONV_oData
);
reg signed [3:0]CONV_core[1:9];
reg [3:0] ii_count;
reg [3:0] ij_count;
reg [3:0] ci_count;
reg [3:0] cj_count;
reg [3:0] oi_count;
reg [3:0] oj_count;
reg signed [7:0] CONV_iArrayData[1:6][1:6]; // input Data
reg signed [19:0] CONV_oArrayData[1:4][1:4]; // output Data
reg CONV_StartCal; // Start convolution
// For ReConstruct
wire signed [7:0] CONV_iReCon[1:9]; // input ReConstruct Temp
wire signed [19:0] CONV_mul[1:9];
wire signed [19:0] CONV_result;
// Calculating Convolution
assign CONV_iReCon[1] = CONV_iArrayData[ci_count+0][cj_count+0];
assign CONV_iReCon[2] = CONV_iArrayData[ci_count+0][cj_count+1];
assign CONV_iReCon[3] = CONV_iArrayData[ci_count+0][cj_count+2];
assign CONV_iReCon[4] = CONV_iArrayData[ci_count+1][cj_count+0];
assign CONV_iReCon[5] = CONV_iArrayData[ci_count+1][cj_count+1];
assign CONV_iReCon[6] = CONV_iArrayData[ci_count+1][cj_count+2];
assign CONV_iReCon[7] = CONV_iArrayData[ci_count+2][cj_count+0];
assign CONV_iReCon[8] = CONV_iArrayData[ci_count+2][cj_count+1];
assign CONV_iReCon[9] = CONV_iArrayData[ci_count+2][cj_count+2];
assign CONV_mul[1] = CONV_core[9]*CONV_iReCon[1];
assign CONV_mul[2] = CONV_core[8]*CONV_iReCon[2];
assign CONV_mul[3] = CONV_core[7]*CONV_iReCon[3];
assign CONV_mul[4] = CONV_core[6]*CONV_iReCon[4];
assign CONV_mul[5] = CONV_core[5]*CONV_iReCon[5];
assign CONV_mul[6] = CONV_core[4]*CONV_iReCon[6];
assign CONV_mul[7] = CONV_core[3]*CONV_iReCon[7];
assign CONV_mul[8] = CONV_core[2]*CONV_iReCon[8];
assign CONV_mul[9] = CONV_core[1]*CONV_iReCon[9];
assign CONV_result = CONV_mul[1] + CONV_mul[2] + CONV_mul[3] +
CONV_mul[4] + CONV_mul[5] + CONV_mul[6] +
CONV_mul[7] + CONV_mul[8] + CONV_mul[9];
// Init Core
always @(posedge reset)
begin
CONV_core[1] <= 4'h1;
CONV_core[2] <= 4'h2;
CONV_core[3] <= 4'hf;
CONV_core[4] <= 4'hd;
CONV_core[5] <= 4'h5;
CONV_core[6] <= 4'h3;
CONV_core[7] <= 4'he;
CONV_core[8] <= 4'h1;
CONV_core[9] <= 4'h2;
end
// Load input Data
always @(posedge clk or posedge reset or posedge CONV_finish)
begin
if(reset || CONV_finish)
begin
ii_count <= 1;
ij_count <= 1;
CONV_StartCal <= 0;
end
else if(CONV_start && (ii_count < 7))
begin
if(ij_count < 6) ij_count <= ij_count + 1;
else
begin
if(ii_count < 6)begin ii_count <= ii_count + 1; ij_count <= 1; end
else begin CONV_StartCal <= 1; end
end
CONV_iArrayData[ii_count][ij_count] <= CONV_iData; // Load Data
end
end
// Convolution
always @(posedge clk or posedge reset)
begin
if(reset)
begin
ci_count <= 1;
cj_count <= 1;
CONV_finish <= 0;
end
else if(CONV_StartCal && (ci_count < 5))
begin
if(cj_count < 4) cj_count <= cj_count + 1;
else
begin
if(ci_count < 4) begin ci_count <= ci_count + 1; cj_count <= 1; end
else begin CONV_finish <= 1; end
end
CONV_oArrayData[ci_count][cj_count] <= CONV_result; // Record the Result
end
end
// Output Data
always @(posedge clk or posedge reset or posedge CONV_start)
begin
if(reset || CONV_start)
begin
oi_count <= 1;
oj_count <= 1;
end
else if(CONV_finish && (oi_count < 5))
begin
if(oj_count < 4) oj_count <= oj_count + 1;
else
begin
if(oi_count < 4)begin oi_count <= oi_count + 1; oj_count <= 1; end
end
CONV_oData <= CONV_oArrayData[oi_count][oj_count]; // Output Data
end
end
endmodule
python驗證
import numpy as np
from scipy import signal
from scipy import misc
input_data=[
[1, 2, 3, 4, 5, 6],
[17, 18,19,20,21,22],
[33, 34,35,36,37,38],
[65, 66,67,68,69,70],
[-127,-126,-125, -124, -123, -122],
[-95,-94,-93, -92,-91, -90]
]
heigh,wid=input_data[:2]
weights_data=[
[1 ,2,-1],
[-3,5,3],
[-2 ,1,2]
]
heigh1,wid1 = weights_data[:2]
con_result = signal.convolve(input_data,weights_data,mode=
'full')
grad=signal.convolve2d(weights_data,input_data)
print(grad[2:6,2:6])
小編還在成長,請大家多多指教!
相關文章
- VIVADO vhdl verilog 實現矩陣運算矩陣
- 卷積運算元的矩陣向量乘積表示&一維離散降質模型卷積矩陣模型
- 脈動陣列在二維矩陣乘法及卷積運算中的應用陣列矩陣卷積
- OpenCV矩陣運算OpenCV矩陣
- 向量化實現矩陣運算最佳化(一)矩陣
- Bert結構手動矩陣運算實現(Transform)矩陣ORM
- 值得收藏 | 深度剖析 TensorCore 卷積運算元實現原理卷積
- Julia的矩陣運算矩陣
- Verilog實現加減乘除運算
- 【測繪程式設計試題集】 試題02 矩陣卷積計算程式設計矩陣卷積
- c語言中實現4行3列矩陣和3行4列矩陣的運算C語言矩陣
- Numpy中的矩陣運算矩陣
- 矩陣乘法的運算量計算(華為OJ)矩陣
- 計算兩個一維陣列的卷積陣列卷積
- pytorch基礎七(矩陣運算)PyTorch矩陣
- MATLAB(6)矩陣和向量運算Matlab矩陣
- 矩陣運算與相抵標準型矩陣
- 基礎入門:深度學習矩陣運算的概念和程式碼實現深度學習矩陣
- 矩陣計算矩陣
- 什麼是新媒體矩陣運營?運營矩陣其實很簡單矩陣
- 數字訊號處理:線性卷積、迴圈卷積、圓周卷積計算卷積
- C++實現蛇形矩陣C++矩陣
- OpenCL之矩陣乘法實現矩陣
- Eigen教程(3)之矩陣和向量的運算矩陣
- MKL稀疏矩陣運算示例及函式封裝矩陣函式封裝
- Matlab矩陣運算的硬體資源分析Matlab矩陣
- C++ 練氣期之二維陣列與矩陣運算C++陣列矩陣
- 454. 矩陣面積(入門)矩陣
- 矩陣:如何使用矩陣操作進行 PageRank 計算?矩陣
- 第四篇:使用 CUBLAS 庫給矩陣運算提速矩陣
- 計算矩陣的秩矩陣
- 高效能運算&CUDA | 使用numba對三維矩陣在gpu上進行運算矩陣GPU
- 【矩陣求導】關於點乘 (哈達瑪積)的矩陣求導矩陣求導點乘
- Hadoop 2.6 使用Map Reduce實現矩陣相乘1 矩陣轉置Hadoop矩陣
- 頭條號矩陣運營工具,運營矩陣進行多賬號管理矩陣
- 「技美之路 第04篇」圖形 1.2.3 MVP矩陣運算MVP矩陣
- Keras上實現卷積神經網路CNNKeras卷積神經網路CNN
- 利用 TensorFlow 實現卷積自編碼器卷積