矩阵乘法设计
0 实验目的
以Verilog设计硬件4x4矩阵乘法器。可选用:
Cannon乘法、Fox乘法、Systolic乘法、DNS乘法等任何算法。
module Matrix4x4(input clk, input A[3:0][3:0][7:0], input B[3:0][3:0][7:0], output S[3:0][3:0][7:0]);
进一步讨论以硬件方式计算NxN矩阵乘法器设计。可先以软件模拟(以调用4x4矩阵计算函数,计算NxN矩阵)。也可认为相当于增加一条复杂指令。利用ISE进行仿真,并提交ISE工程。
1 实验原理
Cannon算法
verilog实现4x4乘法,采用4个运算单元并行计算,每个单元计算2x2的矩阵,移入操作采用状态机实现,因为对于4个运算单元,仅有1次初值赋值与一次逻辑移动,因此不必用循环移位器,可以直接修改第二次计算的输入值。
由于verilog不支持输入数组范围作为参数,因此将数组打包为1维(其他解决方法不清楚)
2 实验内容
代码如下:
2x2乘法器:
module Mat2x2(A,B,S);
input [31:0]A;
input [31:0]B;
output wire [31:0]S;
assign S[7:0]= A[7:0]* B[7:0] + A[15:8]* B[23:16];
assign S[15:8]=A[7:0]* B[15:8] + A[15:8]* B[31:24];
assign S[23:16]= A[23:16]* B[7:0] + A[31:24]* B[23:16];
assign S[31:24]= A[23:16]* B[15:8] + A[31:24]* B[31:24];
endmodule
4x4 cannon 乘法器
module Matrix4x4( clk, A, B, S);
input clk;
input [7:0] A[0:3][0:3];
input [7:0] B[0:3][0:3];
reg [127:0]tempA;
reg [127:0]tempB;
reg [127:0]tempS;
reg [127:0]result = 0;
reg [31:0]clkdiv = 0;
reg [3:0]State = 0;
output reg [7:0] S[0:3][0:3];
integer i = 0;
integer j = 0;
// 4 x 2x2 matrix multiplier
Mat2x2 m1(tempA[31:0] ,tempB[31:0] ,tempS[31:0] );
Mat2x2 m2(tempA[63:32], tempB[63:32] ,tempS[63:32]);
Mat2x2 m3(tempA[95:64], tempB[95:64] ,tempS[95:64]);
Mat2x2 m4(tempA[127:96],tempB[127:96],tempS[127:96]);
// clock divider
always@(posedge clk)begin
clkdiv <= 1'b1 + clkdiv;
end
always@(posedge clkdiv[2])begin
case(State)
0: begin
//initial
tempB[31:0] <= {B[1][1],B[1][0],B[0][1],B[0][0]} ;
tempB[63:32] <={B[1][3],B[1][2],B[0][3],B[0][2]} ;
tempB[95:64] <= {B[1][1],B[1][0],B[0][1],B[0][0]} ;
tempB[127:96] <={B[1][3],B[1][2],B[0][3],B[0][2]} ;
tempA[31:0] <= {A[1][1],A[1][0],A[0][1],A[0][0]};
tempA[63:32] <= {A[1][1],A[1][0],A[0][1],A[0][0]};
tempA[95:64] <= {A[3][1],A[3][0],A[2][1],A[2][0]};
tempA[127:96]<= {A[3][1],A[3][0],A[2][1],A[2][0]};
State <= State + 1;
end
// 第一项加入 因为单个元素只有8位,因此不能直接128位一起加,而要8位8位的加
1: begin
result[7:0] <= result [7:0] + tempS[7:0] ;
result[15:8] <= result [15:8] + tempS[15:8] ;
result[23:16] <= result [23:16] + tempS[23:16] ;
result[31:24] <= result [31:24] + tempS[31:24] ;
result[39:32] <= result [39:32] + tempS[39:32] ;
result[47:40] <=result [47:40] + tempS[47:40] ;
result[55:48] <= result [55:48] + tempS[55:48] ;
result[63:56] <= result [63:56] + tempS[63:56] ;
result[71:64] <= result [71:64] + tempS[71:64] ;
result[79:72] <=result [79:72] + tempS[79:72] ;
result[87:80] <= result [87:80] + tempS[87:80] ;
result[95:88] <= result [95:88] + tempS[95:88] ;
result[103:96] <=result [103:96] + tempS[103:96] ;
result[111:104] <= result [111:104] + tempS[111:104] ;
result[119:112] <= result [119:112] + tempS[119:112] ;
result[127:120] <= result [127:120] + tempS[127:120] ;
State <= State + 1;
end
// shift 1 block 2x2块移位通过直接写对应索引得到
2: begin
tempB[31:0] <= {B[3][1],B[3][0],B[2][1],B[2][0]};
tempB[63:32] <= {B[3][3],B[3][2],B[2][3],B[2][2]};
tempB[95:64] <= {B[3][1],B[3][0],B[2][1],B[2][0]};
tempB[127:96] <= {B[3][3],B[3][2],B[2][3],B[2][2]};
tempA[31:0] <= {A[1][3],A[1][2],A[0][3],A[0][2]};
tempA[63:32] <= {A[1][3],A[1][2],A[0][3],A[0][2]};
tempA[95:64] <={A[3][3],A[3][2],A[2][3],A[2][2]};
tempA[127:96]<= {A[3][3],A[3][2],A[2][3],A[2][2]};
State <= State +1;
end
3: begin
// 第二项加入
result[7:0] <= result [7:0] + tempS[7:0] ;
result[15:8] <= result [15:8] + tempS[15:8] ;
result[23:16] <= result [23:16] + tempS[23:16] ;
result[31:24] <= result [31:24] + tempS[31:24] ;
result[39:32] <= result [39:32] + tempS[39:32] ;
result[47:40] <=result [47:40] + tempS[47:40] ;
result[55:48] <= result [55:48] + tempS[55:48] ;
result[63:56] <= result [63:56] + tempS[63:56] ;
result[71:64] <= result [71:64] + tempS[71:64] ;
result[79:72] <=result [79:72] + tempS[79:72] ;
result[87:80] <= result [87:80] + tempS[87:80] ;
result[95:88] <= result [95:88] + tempS[95:88] ;
result[103:96] <=result [103:96] + tempS[103:96] ;
result[111:104] <= result [111:104] + tempS[111:104] ;
result[119:112] <= result [119:112] + tempS[119:112] ;
result[127:120] <= result [127:120] + tempS[127:120] ;
State <= State +1;
end
4: begin
//result 移位输入到S寄存器中
for(i = 0;i<2;i=i+1)begin
for(j=0;j<2;j=j+1)begin
#5
S[i*2+0][j*2+0] <= result[7:0];
S[i*2+0][j*2+1] <= result[15:8];
S[i*2+1][j*2+0] <= result[23:16];
S[i*2+1][j*2+1] <= result[31:24];
#5 result <= (result>>32);
end
end
State<=State+1;
end
endcase
end
endmodule
3 实验结果
4x4 cannon:
结果正确
4 总结与讨论
NxN乘法器?
NxN乘法器可以参照4x4用2x2的乘法器拓展得到,每次拓展一倍可采用相似构造。即8x8用4个4x4实现,其中4x4由4个2x2实现。而由于乘法分块的性质,对于非2的整数次幂尺寸的矩阵,可以在输入的多余位补零,结果取相应尺寸即可。
这样的优点是节约了硬件资源,去除了移位器的实现,不用存储输入到寄存器,同时也具有模块化的简便性。
但是对于实验中给出的接口,考虑到verilog对数组的实现不是很支持,system verilog也仅是支持输入参数,但不支持范围索引,因此采用之前打包为一维数组的方式来存储矩阵会使得接口通用性更高,便于拓展。
如果对于除了2x2的每一层都采用cannon算法,对于我的实现,每一层需要4个时钟周期(4个状态),因此对于nxn矩阵,时间复杂度大概为 O ( 4 log n ) O(4\log n) O(4logn)。而对软件实现的nxn方阵乘法,则需要 O ( n 3 ) O(n^3) O(n3)的复杂度,显然是巨大的飞跃。而同时cannon算法也减少了存储的花费,对于单个计算单元,不用额外存储其他计算单元的输入,只需每次传入不同的值即可,可以通过寄存器+状态机实现。
示例:对于
A =
1 2 9 1 2
3 4 0 3 3
5 6 7 2 2
7 8 1 2 4
2 2 9 8 1
B=
1 2 3 2 9
3 4 3 4 3
1 6 0 0 4
0 4 3 4 1
2 9 8 6 3
A*B =
拓展到8x8 cannon乘法器,包含4个4x4cannon乘法器,而4x4cannon乘法器由包含2x2传统乘法器:
A =
1 2 9 1 2 0 0 0
3 4 0 3 3 0 0 0
5 6 7 2 2 0 0 0
7 8 1 2 4 0 0 0
2 2 9 8 1 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
B=
1 2 3 2 9 0 0 0
3 4 3 4 3 0 0 0
1 6 0 0 4 0 0 0
0 4 3 4 1 0 0 0
2 9 8 6 3 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
A*B =