序章
FPGA は、固定小数点整数演算、つまり小数部分を含む乗算や加算演算を行うのに適しています。一般に、最初に数回拡張し、次に演算結果を数回削減して達成することを選択します。
応用事例、トゥルーカラー画像をグレースケール画像に変換するための心理計算式:
グレー = 0.299R + 0.587G + 0.114B
この記事では、具体的なデザインとシミュレーションのソース コード (Verilog HDL) を示します。MATLAB プラットフォームと組み合わせて、結果の精度を検証します。
Verilog コンパイル シミュレーション プラットフォーム: Vivado 2018.3
MATLAB バージョン: 2022a
デザイン
// ==============================================================================
// 功能描述:真彩图 转 灰度图
// 作 者:Xu Y. B.
// 计算公式:Gray = 0.299R + 0.587G + 0.114B
// 思 路:采用定点运算,先将系数扩大若干倍,最后的结果在缩小若干倍
// ==============================================================================
`timescale 1ns / 1ps
module RGB_2_GRAY_MDL #(
// ---- ---- ---- ---- 模块可重载参数 ---- ---- ---- ----
parameter P_PIXEL_DATA_WIDTH = 24, //像素数据位宽 ,RGB 分量等位宽
parameter P_SCALE_FACTOR = 1024, //系数缩放因子
parameter P_GRAY_DATA_WIDTH = 8 //输出灰度数据位宽
)(
// ---- ---- ---- ---- 模块端口 ---- ---- ---- ----
input I_OPR_CLK,
input I_OPR_RSTN,
input I_PIXEL_VAL,
input [P_PIXEL_DATA_WIDTH-1:0] I_PIXEL_DATA,// R:MSB B:LSB
output reg O_GRAY_VAL,
output [P_GRAY_DATA_WIDTH-1:0] O_GRAY_DATA
);
// ---- ---- ---- ---- 内部参数 ---- ---- ---- ----
localparam LP_RIGHT_SHIFT_VALUE = $clog2(P_SCALE_FACTOR);
integer INT_COEF_R = 0.299*P_SCALE_FACTOR;
integer INT_COEF_G = 0.587*P_SCALE_FACTOR;
integer INT_COEF_B = 0.114*P_SCALE_FACTOR;
localparam LP_GRAY_RES_WIDTH = FUNC_CAL_GRAY_RES_WIDTH(P_SCALE_FACTOR,P_PIXEL_DATA_WIDTH);
// ---- ---- ---- ---- 内部信号 ---- ---- ---- ----
reg [LP_GRAY_RES_WIDTH-1:0] R_GRAY_RES;
// ---- ---- ---- ---- 内部逻辑 ---- ---- ---- ----
always @ (posedge I_OPR_CLK)
begin
if(~I_OPR_RSTN)
begin
O_GRAY_VAL <= 1'b0;
R_GRAY_RES <= {LP_GRAY_RES_WIDTH{1'b0}};
end
else
begin
if(I_PIXEL_VAL)
begin
O_GRAY_VAL <= I_PIXEL_VAL;
R_GRAY_RES <= (I_PIXEL_DATA[P_PIXEL_DATA_WIDTH-1-:8] * INT_COEF_R
+I_PIXEL_DATA[P_PIXEL_DATA_WIDTH/3+:8] * INT_COEF_G
+I_PIXEL_DATA[0+:8] * INT_COEF_B)>>LP_RIGHT_SHIFT_VALUE;//此处组合逻辑延迟较大 , 待优化
end
else
begin
O_GRAY_VAL <= 1'b0;
R_GRAY_RES <= {LP_GRAY_RES_WIDTH{1'b0}};
end
end
end
assign O_GRAY_DATA = R_GRAY_RES[0+:P_GRAY_DATA_WIDTH];
// ---- ---- ---- ---- 函数/任务 ---- ---- ---- ----
// 计算最终结果的右移数值
function integer FUNC_CAL_GRAY_RES_WIDTH;
input integer SCALE_FACTOR;
input integer PIXEL_DATA_WIDTH;
begin
FUNC_CAL_GRAY_RES_WIDTH = PIXEL_DATA_WIDTH/3 + $clog2(SCALE_FACTOR) + 1 + 1;
end
endfunction
endmodule
設計ソースコードはパラメトリックプログラミングの仕様に従い、スケーリングファクターや出力ビット幅を設定できます。使用時には内部ロジックやパラメータを調整することなく直接呼び出すことができます。
3 つの乗算および加算演算のロジックには、例として、より高い周波数クロックでのセットアップ タイム/ホールド タイムが含まれる場合があることに注意してください。
ここでは、パイプライン構造を使用して計算するアイデアを示します。つまり、最初に 3 項目の乗算を計算し、次に 3 項目の加算を計算します。加算を計算する場合は、加数の数に注意が必要です。加算される は 2 の整数乗に等しくないため、2 つの加算値の合計を計算し、3 番目の加算値を 1 拍遅らせてから合計に加算して、最終結果を取得します。このようにして、タイミングの問題は軽減できますが、その結果、計算遅延が増加します。
シミュレーション
// ==============================================================================
// 功能描述:测试 RGB_2_GRAY_MDL 模块
// 作 者:Xu Y. B.
// 计算公式:像素数据激励
// 思 路:
// ==============================================================================
`timescale 1ns / 1ps
module TB_RGB_2_GRAY_MDL();
parameter P_PIXEL_DATA_WIDTH = 24; //像素数据位宽 ,RGB 分量等位宽
parameter P_SCALE_FACTOR = 1024; //系数缩放因子
parameter P_GRAY_DATA_WIDTH = 8; //输出灰度数据位宽
reg I_OPR_CLK;
reg I_OPR_RSTN;
reg I_PIXEL_VAL;
reg [P_PIXEL_DATA_WIDTH-1:0] I_PIXEL_DATA;// R:MSB B:LSB
wire O_GRAY_VAL;
wire [P_GRAY_DATA_WIDTH-1:0] O_GRAY_DATA;
reg [P_PIXEL_DATA_WIDTH-1:0] R_PIXEL_DATA[24366:1];
// 产生激励时钟
initial I_OPR_CLK = 1'b0;
always #5 I_OPR_CLK = ~I_OPR_CLK;
// 数据读取
initial
begin
$readmemb("D:/A_Vivado_WorkSpace/DSP_BASIC_STUDY/MAT_FILE/IMAGE_PIXEL_DATA.txt",R_PIXEL_DATA);
end
// 复位、数据控制
initial
begin
I_OPR_RSTN = 1'b0;
// I_PIXEL_VAL = 0;
// I_PIXEL_DATA = 0;
#109;
I_OPR_RSTN = 1'b1;
// @(posedge I_OPR_CLK)
// I_PIXEL_VAL <= 1;
// I_PIXEL_DATA <= {8'd121,8'd99,8'd230};
// @(posedge I_OPR_CLK)
// I_PIXEL_VAL <= 1;
// I_PIXEL_DATA <= {8'd101,8'd90,8'd210};
// @(posedge I_OPR_CLK)
// I_PIXEL_VAL <= 1;
// I_PIXEL_DATA <= {8'd151,8'd69,8'd240};
// @(posedge I_OPR_CLK)
// I_PIXEL_VAL <= 1;
// I_PIXEL_DATA <= {8'd221,8'd109,8'd20};
@(negedge O_GRAY_VAL)
I_PIXEL_VAL <= 0;
#290;
$finish;
end
integer K=1;
always @ (posedge I_OPR_CLK)
begin
if(~I_OPR_RSTN)
begin
I_PIXEL_VAL <= 0;
I_PIXEL_DATA <= 0;
end
else if(K<=24366)
begin
I_PIXEL_VAL <= 1;
I_PIXEL_DATA <= R_PIXEL_DATA[K];
K <= K+1;
end
else
begin
K = K;
I_PIXEL_VAL <= 0;
I_PIXEL_DATA <= 0;
end
end
integer FILE_ID ;
initial
begin
FILE_ID = $fopen("D:/A_Vivado_WorkSpace/DSP_BASIC_STUDY/MAT_FILE/GRAY_DATA.txt","w+");
while(~O_GRAY_VAL | ~I_OPR_RSTN) @(posedge I_OPR_CLK);
while(O_GRAY_VAL)
begin
$fdisplayb(FILE_ID,O_GRAY_DATA);
@(posedge I_OPR_CLK);
end
$fclose(FILE_ID);
end
RGB_2_GRAY_MDL #(
.P_PIXEL_DATA_WIDTH(P_PIXEL_DATA_WIDTH),
.P_SCALE_FACTOR (P_SCALE_FACTOR),
.P_GRAY_DATA_WIDTH (P_GRAY_DATA_WIDTH)
) INST_RGB_2_GRAY_MDL (
.I_OPR_CLK (I_OPR_CLK),
.I_OPR_RSTN (I_OPR_RSTN),
.I_PIXEL_VAL (I_PIXEL_VAL),
.I_PIXEL_DATA (I_PIXEL_DATA),
.O_GRAY_VAL (O_GRAY_VAL),
.O_GRAY_DATA (O_GRAY_DATA)
);
endmodule
この部分の検証には 2 つの方法があります。
1. 注釈が付けられた部分は、計算の精度を検証するためにいくつかのデータをモジュールに送信するだけです。
2. txt ファイル内のピクセル データを読み取り、計算のためにモジュールに送信し、計算結果を別の txt ファイルに保存して、MATLAB と FPGA によって計算された誤差を比較します。(この部分の具体的な実装については以下を参照してください)
比較して検証する
Matlab は、分析と比較のためのテスト データとソース コードを生成します。
%% ==================== 像素数据读取存储 ====================
% 作者:Xu Y. B.
% 说明:
% -1- 读取真彩图的RGB数据,拼接为24位二进制数,存入txt文件
% -2- 对真彩图作灰度处理,并与FPGA处理结果作对比
% =============================================================
%% CLEAR
clc;
clearvars;
close all;
%% 图片读取
FILE_PATH = "C:\Users\XYB\Pictures\高清壁纸Z\wallhaven-5758y8.jpg";
IMAGE_DATA = imread (FILE_PATH,"jpg");
figure;
imshow(IMAGE_DATA);
title("原真彩图")
% 图片截取
PIXEL_DATA = IMAGE_DATA(240:425,868:998,:);
figure;
subplot(121)
imshow(PIXEL_DATA);
title("截取的真彩图")
subplot(122)
IM2GRAY = im2gray(PIXEL_DATA);
imshow(IM2GRAY)
title("截取的真彩图转灰度图")
%% 数据转化存储
PIXEL_DATA_R = PIXEL_DATA(:,:,1);
PIXEL_DATA_G = PIXEL_DATA(:,:,2);
PIXEL_DATA_B = PIXEL_DATA(:,:,3);
PIXEL_DATA_R_BIN = dec2bin(reshape(PIXEL_DATA_R,[],1));
PIXEL_DATA_G_BIN = dec2bin(reshape(PIXEL_DATA_G,[],1));
PIXEL_DATA_B_BIN = dec2bin(reshape(PIXEL_DATA_B,[],1));
PIXEL_DATA_BIN = strcat(strcat(PIXEL_DATA_R_BIN,PIXEL_DATA_G_BIN),PIXEL_DATA_B_BIN);
WRITE_PATH = "D:\A_Vivado_WorkSpace\DSP_BASIC_STUDY\MAT_FILE\IMAGE_PIXEL_DATA.txt";
writematrix(PIXEL_DATA_BIN,WRITE_PATH,"WriteMode","overwrite","FileType","text")
%% FPGA处理结果读取
READ_PATH = "D:\A_Vivado_WorkSpace\DSP_BASIC_STUDY\MAT_FILE\GRAY_DATA.txt";
GRAY_DATA_FPGA = uint8(bin2dec(readmatrix(READ_PATH,"OutputType","string")));
GRAY_IMAG = reshape(GRAY_DATA_FPGA,186,[]);
figure;
subplot(121)
imshow(GRAY_IMAG)
title("FPGA处理结果")
subplot(122)
imshow(IM2GRAY)
title("MATLAB处理结果")
GRAY_PROC_ERR = uint8(abs(double(GRAY_IMAG)-double(IM2GRAY)));
figure;
mesh(GRAY_PROC_ERR);
title("FPGA MATLAB处理结果差异三维图")
figure;
imagesc(GRAY_PROC_ERR)
title("FPGA MATLAB处理结果差异平面")
元の画像:
MATLAB の画像インターセプトとグレースケール処理:
FPGA 処理結果と MATLAB 処理結果の比較:
エラー分析:
上記の分析と検証の結果に基づいて、Verilog HDL に基づいて設計されたモジュールはトゥルー カラー画像をグレースケール画像に正確に変換でき、MATLAB 計算結果との最大差は 1 であり、これはおそらく丸め精度の問題によるものであると考えられます。 。この問題は、スケーリング係数を 1024 から 2048 に変更するなど、スケーリング係数を増やすことによってのみ軽減できます。
説明する
上記のすべてのコード内の対応するファイル パスは個人用に設定する必要があり、直接コピーして貼り付けることはできません。そうしないと、パスに対応するファイルが見つからないというエラーが報告されます。
私の能力は限られているため、より良いアイデアがある場合、または使用中に問題が発生した場合は、コメント欄にメッセージを残して連絡してください~~~