IC基础(六):3x3脉动阵列计算矩阵相乘

本文是在看了一个博客之后才知道怎么做的,就是这个博客FPGA脉动阵列的设计,可惜的是这个博客是转载的,原博客已经找不到了。但是其介绍的还是很详细的,在次基础上我完成了自己的的3*3脉动阵列的设计。本文后面会给出一个PPT文档,也是介绍脉动阵列的,FPGA脉动这列的设计这篇博客似乎也是这个PPT启发的。

一、引言

引言的大部分内容来自《VLSI数字性信号处理系统:设计与实现》一书。
脉动结构(也称脉动阵列)表示一种有节奏的计算并通过系统传输数据的处理单元(Processing elements,PE)网络。强调一下,这里说的是网络,不是单个计算单元!!!!!!!!!!!!!!!这些单元规则的泵入蹦出数据以维持规则的数据流。脉动系统的特征是模块化和规则化,这对于大规模集成电路设计是很重要的性质。脉动阵列可作为和主计算机结合的系处理器,从主计算机接数据样本送到处理单元,并将最终结果返回结算机。这个过程可以用下图说明。这个操作类似心脏血液的流动,因此命名为“脉动”。
典型情况下,脉动阵列的所有处理单元是相同的且全流水的,也就是处理部件的所有通信边沿都包含延时单元,且整个系统通常只包含局部互联。这句话怎么理解呢?我的理解是每个处理单元内部都需要有延时单元?为啥需要延时单元呢?因为处理单元会将输入的数据先拿来做运算,然后在下一个时钟把数据传递到下一个处理单元。因此必须需要延迟单元。那什么又是局部互联呢?局部互联是指每个处理单元只与自己相邻的处理单元有数据交流。但是有时候也会为了算法的需求,处理单元可以跨过相邻的处理单元进行互联。

IC基础(六):3x3脉动阵列计算矩阵相乘
二、脉动阵列的优势

脉动阵列可以是一维的网络,也可以是二维的,当然还可以有更高维度的。
脉动阵列这个概念其实从1978年就就已经有了,自从谷歌的TPU采用了脉动阵列单元来加速卷积计算后,这个概念又火了起来了。但是本文说的脉动阵列并不是谷歌所说的那种脉动阵列,本文介绍脉动阵列是用来做矩阵乘法的。
由于脉动阵列的输入数据是流过每一个处理单元的,所以就可以减少数据的访存,提高数据的吞吐量。另外一个就是脉动阵列规则,在专用集成电路布线简单,可以提高系统的工作频率。
为啥可以减少访存呢?举个例子,假设矩阵相乘:

IC基础(六):3x3脉动阵列计算矩阵相乘IC基础(六):3x3脉动阵列计算矩阵相乘IC基础(六):3x3脉动阵列计算矩阵相乘

IC基础(六):3x3脉动阵列计算矩阵相乘
要计算C[0][0]=23,需要访问A[0][0],A[0][1],A[0][2],B[0][0],B[1][0],B[2][0]。
C[0][0] = A[0][0]*B[0][0]+A[0][1]*B[1][0]+A[0][2]*B[2][0]
同理:
C[0][1] = A[0][0]*B[0][1]+A[0][1]*B[1][1]+A[0][2]*B[2][1]

计算C[0][0]和C[0][1]需要访问相同的元素:A[0][0],A[0][1],A[0][2]。如果不用脉动阵列,光是在计算C[0][0]和C[0][1]这两个元素上就需要访问A[0][0],A[0][1],A[0][2]两次,因此口带就限制了系统的速度。但是用脉动阵列买个数据就只用访问一次,大大提高了系统的运算速度,代价就是需要用更多的计算单元。

三、脉动阵列的计算过程

这部分的讲解需要画很多图来讲解,幸好有这个网页,这里的脉动阵列处理机讲的明白了。我就大概讲一下计算过程就好了。

首先先给出3x3脉动阵列处理网络,举的例子是在上面提到的矩阵A和矩阵B相乘。

脉动阵列网络结构图:

IC基础(六):3x3脉动阵列计算矩阵相乘
PE内部结构图:
IC基础(六):3x3脉动阵列计算矩阵相乘

网络结构图中*代表延迟一个周期的意思。所以两个 ** 代表延迟两个周期。
算了,计算过程不说了。如果你让认真看一定可以看懂,没看懂的一定是你没有认真看。

四、代码设计和功能仿真

这里使用有符号8位位宽的整数设计,输出位宽位16位。有一个问题值得注意就是计算过程中数据不能溢出,溢出就错了。另外还用到了xilinx的乘加器IP。

计算完一次要把计算单元中的寄存器清零**

代码:

1、PE模块代码

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2019/04/02 16:55:36
// Design Name: 
// Module Name: PE
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module PE #(parameter	N = 8)
	 (
	input		Clk,
	input		Rst_n,
	input		Sclr,
	input		[N-1:0]A,
	input		[N-1:0]B,
	//input		[N*2-1:0]C,
	output	reg	[N-1:0]Next_A,
	output	reg	[N-1:0]Next_B,
	output	    [N*2-1:0]P
    );
    
    reg		[N*2-1:0] P_reg=0;
    wire	[N*2-1:0] P_net;
    
    [email protected](posedge Clk or negedge Rst_n)begin
    	if(!Rst_n) begin
    		Next_A <=0;
    		Next_B <=0;
    	end
    	else if(Sclr==0)begin
  			Next_A <=0;
    		Next_B <=0;
    	end
    	else begin
    		Next_A <= A;
    		Next_B <= B;
    	end
    end
    
    xbip_multadd_0 MultAccumIP (
      .A(A),                // input wire [7 : 0] A
      .B(B),                // input wire [7 : 0] B
      .C(P_reg),                // input wire [15 : 0] C
      .SUBTRACT(1'b0),  // input wire SUBTRACT
      .P(P_net),                // output wire [15 : 0] P
      .PCOUT()        // output wire [47 : 0] PCOUT
    );
    [email protected](posedge Clk or posedge Rst_n)begin
 		if(!Rst_n)
 			P_reg <= 0;
 		else if(Sclr==1'b0)
 			P_reg <= 0;
 		else
 			P_reg <= P_net;	
    end
    
    assign  P = P_net;	
endmodule

2、顶层模块

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2019/04/02 20:14:46
// Design Name: 
// Module Name: pulsation_array
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module pulsation_array #
	(
	parameter N = 8
	)
	(
	input		Clk,
	input		Rst_n,
	input		In_Dv,
	input signed [N-1:0] A0,
	input signed [N-1:0] A1,
	input signed [N-1:0] A2,
	input signed [N-1:0] B0,
	input signed [N-1:0] B1,
	input signed [N-1:0] B2,
	output  reg signed [N*2-1:0] P11,
	output  reg signed [N*2-1:0] P12,
	output  reg signed [N*2-1:0] P13,
	output  reg signed [N*2-1:0] P21,
	output  reg signed [N*2-1:0] P22,
	output  reg signed [N*2-1:0] P23,
	output  reg signed [N*2-1:0] P31,
	output  reg signed [N*2-1:0] P32,
	output  reg signed [N*2-1:0] P33,
	output	reg				 Out_Dv
    );
    
    wire 		signed [N-1:0]	P11_A,P11_B;	
    wire		signed [N-1:0]	P12_A,P12_B;	
    wire		signed [N-1:0]	P21_A,P21_B;
    wire		signed [N-1:0]	P22_A,P22_B;
    wire		signed [N-1:0]	P31_A,P32_A;
    wire		signed [N-1:0]	P13_B,P23_B;	
    
    wire			signed [N*2-1:0] P11_reg,P12_reg,P13_reg;
    wire			signed [N*2-1:0] P21_reg,P22_reg,P23_reg;
    wire			signed [N*2-1:0] P31_reg,P32_reg,P33_reg;
    
    reg	[2:0] count;
    reg	[2:0] count_reg;
    reg		  Out_Dv_reg;		
    reg		  Sclr;
    [email protected](posedge Clk or negedge Rst_n) begin
    	if(!Rst_n) 
    		count <= 3'b000;
    	else if(In_Dv==1'b1)
    		if(count<6)
    			count <= count+1'b1;
    		else
    			count <=3'b000;
    	else
    		count <= 3'b000;
    end		
    
    [email protected](posedge Clk)begin
    	count_reg<=count;
    end
    [email protected](posedge Clk or negedge Rst_n) begin
    	if(!Rst_n)
    		Out_Dv_reg <= 1'b0;
    	else if(count_reg==5)
    		Out_Dv_reg <=1'b1;
    	else
    		Out_Dv_reg <=1'b0;
    		
    end
    
    [email protected](posedge Clk or Rst_n)begin
    	if(!Rst_n)
    		Sclr <= 1'b0;
    	else if(count_reg==5)
    		Sclr <= 1'b0;
    	else
    		Sclr <=1'b1;
    end
    
    [email protected](posedge Clk or negedge Rst_n)begin
    	if(!Rst_n)begin
    		P11 <=0;
    		P12 <=0;
    		P13 <=0;
    		P21 <=0;
    		P22 <=0;
    		P23 <=0;
    		P31 <=0;
    		P32 <=0;
    		P33 <=0;
    	end
    	else if(Out_Dv_reg==1'b1) begin
        	P11 <=P11_reg;
    		P12 <=P12_reg;
    		P13 <=P13_reg;
    		P21 <=P21_reg;
    		P22 <=P22_reg;
    		P23 <=P23_reg;
    		P31 <=P31_reg;
    		P32 <=P32_reg;
    		P33 <=P33_reg;
    	end
    	else begin
    		P11 <=0;
    		P12 <=0;
    		P13 <=0;
    		P21 <=0;
    		P22 <=0;
    		P23 <=0;
    		P31 <=0;
    		P32 <=0;
    		P33 <=0;
    	end
    end
[email protected](posedge Clk or negedge Rst_n)begin
	if(!Rst_n)
		Out_Dv <= 0;
	else
		Out_Dv <= Out_Dv_reg;
end
   ///////////////////////////First PE
    PE  #
    (
    .N(N)
    )
    PE_11
    (
    .Clk(Clk),
    .Rst_n(Rst_n),
    .Sclr(Sclr),
    .A(A0),
    .B(B0),
    .Next_A(P11_A),
    .Next_B(P11_B),
    .P(P11_reg)
    );
    /////////////////////////Second PE
    PE  #
    (
    .N(N)
    )
    PE_12
    (
    .Clk(Clk),
    .Rst_n(Rst_n),
    .Sclr(Sclr),
    .A(P11_A),
    .B(B1),
    .Next_A(P12_A),
    .Next_B(P12_B),
    .P(P12_reg)
    );
    
   //////////////////////////////
   PE  #
   (
   .N(N)
   )
   PE_13
   (
   .Clk(Clk),
   .Rst_n(Rst_n),
   .Sclr(Sclr),
   .A(P12_A),
   .B(B2),
   .Next_A(),
   .Next_B(P13_B),
   .P(P13_reg)
   );
  //
     PE  #
  (
  .N(N)
  )
  PE_21
  (
  .Clk(Clk),
  .Rst_n(Rst_n),
  .Sclr(Sclr),
  .A(A1),
  .B(P11_B),
  .Next_A(P21_A),
  .Next_B(P21_B),
  .P(P21_reg)
  );
  //////////////////////////////////
  
  PE  #
  (
  .N(N)
  )
  PE_22
  (
  .Clk(Clk),
  .Rst_n(Rst_n),
  .Sclr(Sclr),
  .A(P21_A),
  .B(P12_B),
  .Next_A(P22_A),
  .Next_B(P22_B),
  .P(P22_reg)
  );
  
  //////////////////////////////
     PE  #
  (
  .N(N)
  )
  PE_23
  (
  .Clk(Clk),
  .Rst_n(Rst_n),
  .Sclr(Sclr),
  .A(P22_A),
  .B(P13_B),
  .Next_A(),
  .Next_B(P23_B),
  .P(P23_reg)
  );
  /////////////////////////////////
     PE  #
  (
  .N(N)
  )
  PE_31
  (
  .Clk(Clk),
  .Rst_n(Rst_n),
  .Sclr(Sclr),
  .A(A2),
  .B(P21_B),
  .Next_A(P31_A),
  .Next_B(),
  .P(P31_reg)
  );
  ////////////////////////////////
     PE  #
  (
  .N(N)
  )
  PE_32
  (
  .Clk(Clk),
  .Rst_n(Rst_n),
  .Sclr(Sclr),
  .A(P31_A),
  .B(P22_B),
  .Next_A(P32_A),
  .Next_B(),
  .P(P32_reg)
  );
  //
     PE  #
  (
  .N(N)
  )
  PE_33
  (
  .Clk(Clk),
  .Rst_n(Rst_n),
  .Sclr(Sclr),
  .A(P32_A),
  .B(P23_B),
  .Next_A(),
  .Next_B(),
  .P(P33_reg)
  );
  
endmodule

3、测试文件

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2019/04/02 20:48:29
// Design Name: 
// Module Name: TB_pulsation_array
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module TB_pulsation_array;

parameter 	N = 8;
parameter	CLK_PERIOD = 20;
reg		[N-1:0] A0;
reg		[N-1:0] A1;
reg		[N-1:0] A2;
reg		[N-1:0] B0;
reg		[N-1:0] B1;
reg		[N-1:0] B2;
reg				In_Dv;

wire			Out_Dv;
wire	 [N*2-1:0] P11;
wire	 [N*2-1:0] P12;
wire	 [N*2-1:0] P13;
wire	 [N*2-1:0] P21;
wire	 [N*2-1:0] P22;
wire	 [N*2-1:0] P23;
wire	 [N*2-1:0] P31;
wire	 [N*2-1:0] P32;
wire	 [N*2-1:0] P33;


reg		Clk;
reg		Rst_n;

integer i=0;
initial
Clk=0;
always # (CLK_PERIOD/2) Clk=~Clk;

initial begin
Rst_n=1'b0;
In_Dv = 0;
A0=0;
A1=0;
A2=0;
B0=0;
B1=0;
B2=0;
#40;
Rst_n = 1'b1;
//1
#10;
for(i=0;i<25;i=i+1)begin
In_Dv =1'b1;
A0=3+i;
A1=0;
A2=0;
B0=3+i;
B1=0;
B2=0;
//2
#20;
A0=4+i;
A1=2+i;
A2=0;
B0=2+i;
B1=4+i;
B2=0;
//3
#20;
A0=2+i;
A1=5+i;
A2=3+i;
B0=3+i;
B1=5+i;
B2=2+i;
//4
#20;
A0=0;
A1=3+i;
A2=2+i;
B0=0;
B1=2+i;
B2=3+i;
//5
#20;
A0=0;
A1=0;
A2=5+i;
B0=0;
B1=0;
B2=5+i;


#60;
In_Dv = 0;
A0=0;
A1=0;
A2=0;
B0=0;
B1=0;
B2=0;
//Rst_n = 1'b0;
//#40;
//Rst_n = 1'b1;
end
end


pulsation_array #
	(
	.N(N)
	)
	uut
	(
	.Clk(Clk),
	.Rst_n(Rst_n),
	.In_Dv(In_Dv),
	.A0(A0),
	.A1(A1),
	.A2(A2),
	.B0(B0),
	.B1(B1),
	.B2(B2),
	.P11(P11),
	.P12(P12),
	.P13(P13),
	.P21(P21),
	.P22(P22),
	.P23(P23),
	.P31(P31),
	.P32(P32),
	.P33(P33),
	.Out_Dv(Out_Dv)
    );
endmodule

五、仿真波形

IC基础(六):3x3脉动阵列计算矩阵相乘

放大看看:

IC基础(六):3x3脉动阵列计算矩阵相乘