使用Verilog实现矩阵-向量乘法,其目的是将一个M阶矩阵与一个列向量相乘,并将结果输出到指定的输出端口。本设计中,矩阵的阶数、输入输出数据的位宽均被参数化,可自行调整,从而实现任意矩阵阶数、任意数据位宽的矩阵-向量乘法。
本算法中输入是一个矩阵和一个向量,输出为一个向量。矩阵相当于二维数组,向量为一维数组,在verilog中,数组(Memory型)不能作为输入输出的端口,因此要将输入数据转化为数组进行操作(pack),计算后又要将输出的数组还原为reg型变量(unpack)。
具体过程见代码中的注释.
3阶矩阵-向量乘法实现
代码如下:
`timescale 1ns / 1ps
module matrix_vector_multiplier
#(parameter M = 3, //order of matrix and vector
DW_in = 4, //input data width
DW_out = 10 //output data width(bit number of M*(2^DW_in-1)^2)at least)
)
(input [DW_in * M * M - 1 : 0] matrix_input,
input [DW_in * M - 1 : 0] vector_input,
output reg [DW_out * M - 1 : 0] vector_output);
reg [DW_in-1:0] A [0:M*M-1]; //input matrix in the form of 1-d array
reg [DW_in-1:0] B [0:M-1][0:M-1]; //input matrix in the form of 2-d array
reg [DW_in-1:0] V [0:M-1]; //input vector array
reg [DW_out-1:0] Res [0:M-1]; //output vector array
integer i,j,k,p,q,r;
always@(*)
begin
//pack input matrix into 1-d array
for(i=0;i<M*M;i=i+1)
begin
A[i] = matrix_input[(DW_in*(M*M-i) - 1) -: DW_in];
end
//pack 1-d array input matrix into 2-d array
for(j=0;j<M;j=j+1)
begin
V[j] = vector_input[(DW_in*(M-j) - 1) -: DW_in];
Res[j]=0;
for(k=0;k<M;k=k+1)
begin
B[j][k]=A[j*M+k];
end
end
//matrix-vector multiplication
for(p=0;p<M;p=p+1)
begin
for(q=0;q<M;q=q+1)
begin
Res[p]=Res[p]+ (B[p][q]*V[q]);
end
end
//unpack output array
for(r=0;r<M;r=r+1)
begin
vector_output[(DW_out*(M-r)-1) -: DW_out] = Res[r];
end
end
endmodule
testbench:
`timescale 1ns/1ps
module mm_tb ;
reg [35:0] A; //word length is M*M*DW_in
reg [11:0] V; //word length is M*DW_in
wire [29:0] result; //word length is M*DW_out
matrix_vector_multiplier umatrix_vector_multiplier(.matrix_input(A), .vector_input(V), .vector_output(result));
initial begin
//Initial inputs
A={4'd1,4'd2,4'd3,
4'd4,4'd5,4'd6,
4'd7,4'd8,4'd9}; //input matrix
V={4'd5,4'd3,4'd5}; //input vector
//Verdi operation,不使用verdi可略过
`ifdef fsdbdump
$display("\n*** fsdb file dump is turned on ***\n");
$fsdbDumpfile("mvm.fsdb");
$fsdbDumpMDA;
$fsdbDumpvars(0);
#1000
$fsdbDumpoff;
`endif
end
endmodule
仿真结果:
它实现了矩阵乘法
结果为{26,65,104}。在输出result中以二进制数表示,其中每个元素位宽为10位。
128阶矩阵-向量乘法实现
本设计的一大优点在于矩阵的阶数、输入输出数据的位宽均被参数化,可自行调整,即是矩阵阶数很大,依然可以计算。
首先更改module matrix_vector_multiplier中的参数为:
#(parameter M = 128,
DW_in = 4,
DW_out = 16)
128阶矩阵较大,直接在测试代码中描述矩阵不太方便 ,可将矩阵存放在txt文档中,再通过$readmemh读取至memory型变量。
但这样做会testbench中表示输入矩阵和向量的A和V不能直接获得数据,因此还需要将memory中的数据unpack至A和V中。
具体代码如下
`timescale 1ns/1ps
module mm_tb ;
reg [65535:0] A; //word length is M*M*DW_in
reg [511:0] V; //word length is M*DW_in
wire [2047:0] result; //word length is M*DW_out
reg [3:0] mem_matrix [0:16383]; //to store the input matrix
reg [3:0] mem_vector [0:127];
integer i , j;
matrix_vector_multiplier umatrix_vector_multiplier(.matrix_input(A), .vector_input(V), .vector_output(result));
initial begin
$readmemh("unit_matrix.txt",mem_matrix);
$readmemh("vector.txt",mem_vector);
for(i=0; i<16384; i=i+1)
A [65535-4*i -: 4] = mem_matrix[i]; //unpack mem_matrix to A
for(j=0; j<128; j=j+1)
V [(511-4*j) -: 4] = mem_vector[j]; //unpack mem_vector to V
//Verdi operation
`ifdef fsdbdump
$display("\n*** fsdb file dump is turned on ***\n");
$fsdbDumpfile("mvm.fsdb");
$fsdbDumpMDA;
$fsdbDumpvars(0);
#1000
$fsdbDumpoff;
`endif
end
endmodule
例如,下面做一个最简单的测试。用C语言生成一个128阶的单位阵和128阶的全为1的向量,分别写入 unix_matrix.txt和vector.txt.
生成128阶单位矩阵:
#include <stdio.h>
int main() {
int matrix_size = 128;
FILE* file;
file = fopen("unit_matrix.txt", "w");
if (file == NULL) {
printf("无法打开文件\n");
return 1;
}
// 生成单位矩阵并写入文件
for (int i = 0; i < matrix_size; i++) {
for (int j = 0; j < matrix_size; j++) {
if (i == j) {
fprintf(file, "1 ");
}
else {
fprintf(file, "0 ");
}
}
fprintf(file, "\n");
}
fclose(file);
printf("已生成并写入单位矩阵到 unit_matrix.txt\n");
return 0;
}
生成128维全1的向量
#include <stdio.h>
int main() {
int matrix_size = 128;
FILE* file;
file = fopen("vector.txt", "w");
if (file == NULL) {
printf("无法打开文件\n");
return 1;
}
//
for (int i = 0; i < matrix_size; i++) {
fprintf(file, "1\n");
}
fclose(file);
printf("已生成并写入单位矩阵到 vector.txt\n");
return 0;
}
仿真结果:
得到的result也为全1的列向量,仿真结果正确