verilog实现卷积运算

本文转自本人csdn
本文地址
卷积是一种线性运算,是很多普通图像处理操作的基本算法之一。它提供了两个数组相乘的方式,两个数组拥有不同的大小,但是具有相同的维数,生成了一个用于相同维数的新数组。可以用来图像的执行操作,输入一组特定的像素值线性组合成为另一组像素值。在图像处理中常见的msk运算都是卷积,广泛应用于图像滤波。
1.1卷积出现的背景
卷积是在信号与线性系统的基础或背景中出现的,脱离这个背景单独谈卷积是没有任何意义的,除了那个所谓褶反公式上的数学意义和积分(或求和,离散情况下)。
信号与线性系统,讨论的就是一个信号经过一个线性系统以后发生的变化(就是输入、输出和所经过的所谓系统,这三者间的运算关系)。
因此,实际都是要根据我们需要处理的信号形式,来设计所谓的系统传递函数,那么这个系统的传递函数和输入信号,在数学上的形式就是所谓的卷积关系。
卷积关系最重要的一种情况,就是在信号与线性系统或数字信号处理中的卷积定理。利用该定理,可以将时间域或空间域中的卷积运算等价为频率域的相乘运算,从而利用FFT等快速算法,实现有效的计算,节省运算代价。卷积的本质是滑动平均思想,它就是一种微元相乘累加的极限形式。卷积本身不过是一种数学运算而已,就跟“蝶形运算”一样,在信号与系统中,y(t)=f(t)h(t)。时域的卷积等于频域的乘积,即有Y(s)=F(s)×H(s),拉氏变换后得到的函数其实就是信号的频域表达式)。
然而,在通信系统中,我们关心的以及要研究的是信号的频域,不是时域,原因是因为信号的频率是携带有信息的量。所以,我们需要的是这个表达式,但是实际上,我们往往不能很容易得到。
卷积其实就是通过两个函数 f
g生成第三个函数的一种数学算法,表征函数与经过翻转和平移的的重叠面积。如果将参加卷积的一个函数看作区间的指示函数,卷积也可以被看做是“移动平均”的推广。
图1.1中两个方形脉冲波做卷积。其中函数首先对反射,接着平移“t”,成为。那么重叠部分的面积就相当于“”处的卷积,其中横坐标代表待积变量以及新函数的自变量。

卷积示意图

构造一个3 * 3的卷积核,并利用该卷积核完成与6
6矩阵的卷积运算,数据位宽8bit补码数, 结果位宽20bit补码数。
卷积的基本过程如下:
对卷积核进行180度翻转(数据读写顺序的调度)将33卷积核的中心对准66矩阵的每个数进行对应数据乘累加得出结果,如此往复作业。输入数据补码8bit,实际有效7bit,输出数据补码20bit,实际有效19bit,卷积增加位数3*3=9,所以单个乘法最多增加19-7-9=3bit。所以卷积核采用3bit数,即4bit补码数。
内置电路图如图二所示:
芯片内置图.PNG

正如第二部分对本次设计的介绍,我们要做到的是对模拟信号的采样由A/D转换器来完成,而卷积过程由信号的移位来实现。为了设计卷积运算器,首先要设计RAM 和A/D转换器的VerilogHDL 模型。在电子工业发达的国家,可以通过商业渠道得到非常准确的外围器件的虚拟模型。如果没有外围器件的虚拟模型。因为RAM和A/D转换器不是我们设计的硬件对象,所以需要的只是他们的行为模型,精确的行为模型需要认真细致的编写,并不比综合模块容易编写。
运算过程简介
系统内置33的4bit补码数的卷积核

33卷积核.PNG

外部输入6
6的8比特补码数
66矩阵..PNG

工作过程:系统将反转后的卷积核与上图某一紫色圈圈数据和其周围的红色框框中的数据进行相乘并相加。直到所有红色矩形框中的部分均进行过卷积操作。
举例如下,对应第一个要进行卷积操作的数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_c
out_c个卷积核参与运算。例如,输入为[h:5,w:5,c:4],那么对应输出的每个通道,需要4个卷积核。输出为3个通道,所以总共需要3*4=12个卷积核。对于单个输出通道中的每个点,取值为对应的一组4个不同的卷积核经过卷积计算后的和。
python卷积结果.PNG

仿真说明
Modelsim可以支持命令行的方式,通过创建do文件,可以集成多个可执行的命令。那么对于前期一边编写代码,一边进行功能仿真,使用do文件是可以明显提高工作的效率。
编写wave文件对其进行波形仿真:在仿真前Transcript中命令:do wave.do加载预设波形;输出结果在Transcript中查看,输出结果如图:
波形仿真结果图.PNG

图片1.png

可以看到上图的红色框框中的输出结果与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])

小编还在成长,请大家多多指教!

你可能感兴趣的:(verilog实现卷积运算)