Amiya · 2022年03月28日

FPGA学习-基于FPGA的图像实时缩放

使用插值算法实现图像缩放是数字图像处理算法中经常遇到的问题。我们经常会将某种尺寸的图像转换为其他尺寸的图像,如放大或者缩小图像。由于在缩放的过程中会遇到浮点数,如何在FPGA中正确的处理浮点数运算是在FPGA中实现图像缩放的关键。

一、插值算法原理

在图像的缩放处理过程中,经常会用到插值算法,常见的插值算法包括最邻近插值,双线性插值,双三次线性插值,兰索斯插值等方法。其中,双线性插值由于折中的插值效果和实现复杂度,运用较为广泛。本文中仅介绍最临近插值,重点讨论如何在FPGA平台上使用双线性插值实现图像的缩放。

1.1 最临近插值---------最临近插值介绍

讲理论不如举例子来的快,所以为了更好更快的理解最临近插值,我们通过举个简单的例子来解释最临近插值是个什么神奇的东西。假如有一个3*3矩阵(一幅图像其实就是矩阵),如下,我们把这个图像叫做原图(source image):

66   28  128

25  88   200

36  68  120

在矩阵中,坐标(x,y)是这样确定的,矩阵的左上角的顶点为原点,从左到右为x轴,从上到下为y轴,如下所示:

image.png

图1 图像中坐标确定方式

 假设我们想把这个3*3的原图扩大成4*4(我们把这个4*4的图像叫做目的图像destination image)我们该如何做呢?首先当然是先把4*4的矩阵画出来,如下所示:

????

?  ???

????

????

矩阵画出来后,接下来就要像未知数里填充像素点了。要填入的值如何计算呢,通过如下公式进行计算:

0ec3ad46a09745e050d93daa4cb141c7.png

 首先我们先来填充目的图像 (0,0),套用上述公式可得到对应原图像的坐标点,srcX =0,srcY= 0;找到原图像中对应的坐标点的像素值,将该像素填充到目的图像中,如下

66  ???

????

????

接下来填充目的图像(1,0),仍然套用公式,srcX = 3/4,srcY = 0,结果发现得到的结果居然有小数,由于计算机中的像素点已经是最小单位,像素的坐标都是整数,没有小数。这是只需要按照四舍五入的思想将小数坐标转换为整数坐标即可.所以(3/4,0) ≈ (1,0),把原图像中(1,0)点的像素值填入目的图像(1,0)坐标处,得到如下结果:

66  28 ??

????

????

接下来重复上述过程,就可得到放大后的目的图像,如下:

66  28  128  128

25  88  200  200

36  68  120  120

36  68  120  120

这种放大图像的方法叫做最临近插值算法,这是一种最基本最简单的图像缩放算法,该方法缩放后的图像会出现失真现象。

1.2 双线性插值算法 

双线性插值算法是一种比较好的缩放算法,它充分利用源图中虚拟点四周的四个像素点来共同决定目标图形中的一个像素值,因此缩放效果要比最临近插值算法好的多。

双线性插值算法的描述如下:

对于目的图像中的某点坐标,通过乘以缩放倍数(srcwidth/dstwidth、srcheight/dstheight)得到一个浮点坐标(i+u,j+v)(其中i,j均为浮点坐标的整数部分;u,v为浮点坐标的小数部分),则这个浮点坐标(i+u,j+v)处的像素值f(i+u,j+v)可以由原图像中的坐标点(i,j)、(i+1,j)、(i,j+1)、(i+1,j+1)所对应的四个像素值来共同决定,即

549536b26defa5aa425a0db9134f2aab.png

如1.1计算的目的像素坐标(1,0)对应源图像的坐标为(3/4,0),即 i = 0,u = 0.75;j = 0,v = 0;即目的图像(1,0)处的像素值由源图像中(0,0)、(1,0)、(0,1)(1,1)四个坐标点对应的像素值来确定,代入上述公式即可计算出(3/4,0)处的像素值。

看了上述内容应该对最临近插值算法和双线性插值算法有一定的了解,其基本原理应该已经掌握。

在网上刷博客发现有好多大佬讲解了双线性插值算法的优化(双线性插值算法总结),发现优化以后缩放效果更好一些,优化的具体细节就不再讲解,具体优化过程就是使用

c1ca1ae0df1d0b48251c070e31593377.png

 写到这里双线性插值算法的基本原理及优化方式已经基本讲述完毕,那么,如何使用FPGA来实现双线性插值呢?接下来我们进行分析如何使用FPGA来实现双线性插值。

二、双线性插值的FPGA实现遇到的问题及解决方案

通过以上分析,我们会发现使用FPGA实现双线性插值有以下难点:

如何处理算法中出现的小数;

如何同时求出相邻的四个点的坐标以及系数;

如何使用这四个坐标同时输出相邻的四个像素值;

接下来我们依次解决上述问题

2.1 如何处理算法中出现的小数

FPGA处理浮点数一般都是将浮点数转化为定点数进行处理,我们在求srcX、srcY也按照此思想。由于FPGA中没法进行小数表示,因此我们将srcX、srcY放大一定倍数变成整数,这个整数的高n位表示坐标点的整数部分(i或j),低m位表示小数部分(u或者v)。如我们要将30*30的图像放大成64*64即srcwidth = osrcheight = 30,dstwidth = dstheight =64(接下来工程实现也按照此例子来实现)。

6f6d3cab05212f339641c073d5a2bc43.png

位宽为16位。由于放大了128倍,所以srcX[6:0]、srcY[6:0]的低7位表示小数部分,即u = osrcX[6:0],v=srcY[6:0];

其余高位部分表示整数部分,即i = osrcX[15:7],j = osrcY[15:7]。这样就可以解决算法中出现的小数问题。

2.2如何同时求出相邻的四个点的坐标以及系数

由1.1可知,我们可以求出第一个坐标点(i,j) =  (srcX[15:7],srcY[15:7]),那么如何求出相邻其它三个点的坐标呢?接下来需要求出其余三点(i+1,j)、(i,j+1)、(i+1,j+1)的坐标,

(i+1,j) = (srcX[15:7] + 'd1,srcY[15:7]);

(i,j+1) = (srcX[15:7] ,srcY[15:7] * osrcwidth);

(i+1,j+1) = (srcX[15:7] + 'd1 ,srcY[15:7] * osrcwidth); 

 通过以上方式便可以很容易的求出四个点的坐标。接下来就要考虑如何使用这四个坐标同时读取四个对应的像素值。

同时我们定义四个位宽为8位的系数变量coefficient1[7:0]、coefficient2[7:0]、coefficient3[7:0]、coefficient4[7:0],通过

coefficient1[7:0]  = ‘d128 - coefficient2;(由于系数放大了128倍,所以是128-系数2,对应1-u)

coefficient2[7:0] = {1'b0,u} = {1'b0,srcX[6:0]};

coefficient3[7:0] = ‘d128 - coefficient4;(由于系数放大了128倍,所以是128-系数4,对应1-v)

coefficient4[7:0] = {1'b0,v} = {1'b0,srcY[6:0]};

为什么定义的系数变量为8位而不是7位,这是因为系数变量的最大值为’d128,8位位宽才能表示‘d128。

 接下来使用上述求出的四个坐标求出对应的坐标的像素值,再套用公式

f30673f208e5b3b8c81324ac1f8bdd81.png

即可求出目的图像对应位置放大后的像素值。

通过以上分析,可以将公式变形为如下形式,求出对应点的像素值后直接使用以下公式即可:

eb405325272c662cdc78a660a4d7e7b5.png

 求得该像素值之后还需要将该像素值除以128*128才是所得的实际结果。

2.3  如何使用这四个坐标同时输出相邻的四个像素值

通过2.2分析可以同时求出四个像素点的坐标,但是如何通过这四个坐标同时求出对应的像素值呢?由于待缩放的数据是先缓存进RAM的,如果待缩放的图像数据仅仅缓存在一个RAM里,是不可能通过四个像素点的坐标同时访问这个RAM,即不可能同时求出对应的四个像素点的值。所以,可以通过牺牲面积换取速度的方式,即将RAM复制4次,每个RAM都缓存整个待缩放的图像数据,这样四个像素点的坐标就可以通过访问不同的RAM来同时访问对应的四个像素值了。虽然牺牲了FPGA内部的存储资源,但是提升了双线性插值算法的速度。

三 、双线性插值的FPGA实现

通过第二节内容的分析可知,使用FPGA实现双线性插值主要包含以下三个模块:生成目的图像对应原图像坐标和系数模块、待处理图像缓存模块、双线性插值运算单元模块。其数据流向图如下图所示:

d3731348c5dbcc036e4ee8df5e8cc14f.png

图2 FPGA实现双线性插值数据流向图

 图2中虚框部分是图像裁剪模块,这里不讨论图像裁剪功能的实现,仅仅讨论图像缩放功能的实现。

整个实现的过程描述如下:

上位机将待缩放的图像数据送进待处理图像缓存模块,该模块将待处理的数据复制四份

待1中数据缓存完毕,开始生成目的图像对应原图像坐标和系数;

将生成的坐标送给待处理图像缓存模块进行数据的访问,将读取的数据送给双线性插值运算单元;

将生成的系数送给双线性插值运算单元,与相应的像素值进行数学运算,实现双线性插值功能。

 实现双线性插值功能的代码如下所示:

 顶层模块:

module top(input clk,

  output \[7:0\]doutb,

  output de\_o,

  output v\_sync,

  output h\_sync

    );

parameter \[7:0\]src\_width = 'd30;

wire \[7:0\]coordinate\_x;

wire \[7:0\]coordinate\_y;

wire start;

wire en\_b;

//

wire \[7:0\]coefficient1;

wire \[7:0\]coefficient2;

wire \[7:0\]coefficient3;

wire \[7:0\]coefficient4;

wire \[7:0\]doutbx;

wire \[7:0\]doutbx1;

wire \[7:0\]doutby;

wire \[7:0\]doutby1;

// Instantiate the module

sourceimage\_virtualcoordinate coordinate (

    .clk(clk), 

    .src\_width(src\_width), 

    .start(start), 

    .coordinate\_x(coordinate\_x), 

    .coordinate\_y(coordinate\_y), 

    .coefficient1(coefficient1), 

    .coefficient2(coefficient2), 

    .coefficient3(coefficient3), 

    .coefficient4(coefficient4),

.en(en\_b)

    );

//

wire \[7:0\]dina;

wire valid\_zsc;

// Instantiate the module

self\_generate self\_generate (

    .clk(clk), 

    .data\_o(dina), 

    .valid\_zsc(valid\_zsc)

    );

// Instantiate the module

mem\_control mem\_control (

    .clk\_wr(clk), 

    .clk\_rd(clk), 

    .coordinate\_x(coordinate\_x), /

    .coordinate\_y(coordinate\_y), 

    .din\_a(dina), /

    .en\_a(valid\_zsc), ///

    .src\_width(src\_width), //

    .en\_b(en\_b), ///

    .doutbx(doutbx), 

    .doutbx1(doutbx1), 

    .doutby(doutby), 

    .doutby1(doutby1), 

    .start(start)

    );

///

wire \[7:0\] data\_o;

wire en\_o;

// Instantiate the module

arithmetic\_unit arithmetic\_unit (

    .clk(clk), 

    .coefficient1(coefficient1), 

    .coefficient2(coefficient2), 

    .coefficient3(coefficient3), 

    .coefficient4(coefficient4), 

    .en\_b(en\_b), 

    .doutbx(doutbx), 

    .doutbx1(doutbx1), 

    .doutby(doutby), 

    .doutby1(doutby1), 

    .data\_o(data\_o),

.en\_o(en\_o)

    );

wire de;

wire start\_en;

// Instantiate the module

mem\_64multi\_64 mem\_64multi\_64 (

    .clk(clk), 

    .dina(data\_o), 

    .ena(en\_o), 

    .enb(enb), 

.start\_en(start\_en),

    .doutb(doutb)

    );

// Instantiate the module

vesa\_out vesa\_out (

    .clk(clk), 

    .start\_en(start\_en), 

    .v\_sync(v\_sync), 

    .h\_sync(h\_sync), 

    .de(enb), 

    .de\_o(de\_o)

    );

endmodule

生成对应的坐标和系数模块:

//该模块是用来计算源图像的虚拟坐标,由于源图像和目的图像都是正方形,所以只考虑一个缩放倍数即可

//

module sourceimage\_virtualcoordinate(input clk,

input \[7:0\]src\_width,/src\_width = osrc\_height

input \[5:0\]dest\_width,/dest\_width = dest\_height = 'd64

input start,///数据缓存满了以后才可以进行计算

output \[7:0\]coordinate\_x,

output \[7:0\]coordinate\_y,

output \[7:0\]coefficient1,

output \[7:0\]coefficient2,

output \[7:0\]coefficient3,

output \[7:0\]coefficient4,

output reg en = 'd0

    );

/高电平有效rst

reg \[1:0\]cnt = 'd0;

always @(posedge clk)

if(cnt == 'd3)

cnt <= 'd3;

else

cnt <= cnt + 'd1;

reg rst = 'd1;

always @(posedge clk)

if(cnt == 'd3)

rst <= 'd0;

else

rst <= 'd1;

//

localparam \[1:0\]IDLE = 2'b01;

localparam \[1:0\]START = 2'b10;

/

reg\[1:0\]next\_state = 'd0;

reg\[1:0\]current\_state = 'd0;

always @(posedge clk)

if(rst)高电平复位

current\_state <= IDLE;

else

current\_state <= next\_state;

//

reg finish = 'd0;

always @(\*)

case(current\_state)

IDLE:begin

if(start)

next\_state = START;

else

next\_state = IDLE;

end

START:begin

if(finish)

next\_state = IDLE;

else

next\_state = START;

end

default:next\_state = IDLE;

endcase

//

//reg en = 'd0;//目的坐标计数器使能

always @(\*)

case(current\_state)

IDLE:begin

en = 'd0;

end

START:begin

en = 'd1;

end

default:en = 'd0;

endcase

///对目的图像坐标进行计数

reg\[5:0\] pos\_x = 'd0;/列计数

always@(posedge clk)

if(en)begin

if(pos\_x == 'd63)

pos\_x <= 'd0;

else

pos\_x <= pos\_x + 'd1;

end

else

pos\_x <= pos\_x;

reg\[5:0\] pos\_y = 'd0;行计数

always @(posedge clk)

if(pos\_x == 'd63)

pos\_y <= pos\_y + 'd1; 

else

pos\_y <= pos\_y;

//结束标志

always@(posedge clk)

if((pos\_x == 'd62)&&(pos\_y == 'd63))///是pos\_x==62而不是63

finish <= 'd1;

else

finish <= 'd0;

//通过pos\_x、pos\_y可以计算对应源图像位置的虚拟坐标

reg \[15:0\]src\_x = 'd0;///高8位表示整数,低8位表示小数

reg \[15:0\]src\_y = 'd0;///高8位表示整数,低8位表示小数

//assign osrc\_x = ((pos\_x<<1 + 'd1)\*src\_width - 'd64 > 'd0)?(pos\_x<<1 + 'd1)\*src\_width - 'd64:'d64-(pos\_x<<1 + 'd1)\*src\_width;

//assign osrc\_y = ((pos\_y<<1 + 'd1)\*src\_width - 'd64 > 'd0)?(pos\_y<<1 + 'd1)\*src\_width - 'd64:'d64-(pos\_y<<1 + 'd1)\*src\_width;

wire \[7:0\]pos\_xq;

wire \[7:0\]pos\_yq;

assign pos\_xq = pos\_x<<1;

assign pos\_yq = pos\_y<<1;

///

always @(posedge clk)

if(pos\_x == 'd0)begin

if(src\_width > 'd64)

src\_x <= osrc\_width - 'd64;

else

src\_x <= 'd64 - osrc\_width;

end

else begin

if((pos\_xq + 'd1)\*src\_width > 'd64)

src\_x <= (pos\_xq + 'd1)\*src\_width - 'd64;

else

src\_x <= 'd64 - (pos\_xq + 'd1)\*src\_width;

end

always @(posedge clk)

if(pos\_y == 'd0)begin

if(src\_width > 'd64)

src\_y <= osrc\_width - 'd64;

else

src\_y <= 'd64 - osrc\_width;

end

else begin

if((pos\_yq + 'd1)\*src\_width > 'd64)

src\_y <= (pos\_yq + 'd1)\*src\_width - 'd64;

else 

src\_y <= 'd64 - (pos\_yq + 'd1)\*src\_width;

end

//生成对应坐标

//wire \[6:0\]coordinate\_x;

//wire \[6:0\]coordinate\_y;

assign coordinate\_x = osrc\_x\[14:7\];

assign coordinate\_y = osrc\_y\[14:7\];

//生成对应系数 

//wire \[7:0\]coefficient1;

//wire \[7:0\]coefficient2;

//wire \[7:0\]coefficient3;

//wire \[7:0\]coefficient4;

assign coefficient2 = {1'b0,src\_x\[6:0\]};

assign coefficient1 = 'd128 - coefficient2;

assign coefficient4 = {1'b0,src\_y\[6:0\]};

assign coefficient3 = 'd128 - coefficient4;

endmodule

模拟上位机产生待缩放的数据模块:

//模拟的输入图像指标是30\*30@56Hz
//
module self\_generate(input clk,
output reg\[7:0\]data\_o = 'd0,
output reg valid\_zsc = 'd0
    );
reg \[10:0\] cnt\_h = 'd0;
reg \[10:0\] cnt\_v = 'd0;
always @(posedge clk)
if(cnt\_h == 'd799)
cnt\_h <= 'd0;
else
cnt\_h <= cnt\_h + 'd1;
always @(posedge clk)
if(cnt\_h == 'd799)begin
if(cnt\_v == 'd524)
cnt\_v <= 'd0;
else
cnt\_v <= cnt\_v + 'd1;
end
else
cnt\_v <= cnt\_v;
//reg dInValid = 'd0;
wire valid\_1;
assign valid\_1 = (((cnt\_h >='d144)&&(cnt\_h <='d173))&&((cnt\_v >='d35)&&(cnt\_v <='d64)))?'d1:'d0;/有效数据共30个
///
always @(posedge clk)
if(valid\_1)
//if((cnt\_h >='d144)&&(cnt\_h <='d176))
//data\_o <= 'hff;
//else if((cnt\_h >='d177)&&(cnt\_h <='d209))
//data\_o <= 'h0f;
//else if((cnt\_h >='d210)&&(cnt\_h <='d242))
//data\_o <= 'hff;
//else if((cnt\_h >='d243)&&(cnt\_h <='d275))
//data\_o <= 'h0f;
//else if((cnt\_h >='d276)&&(cnt\_h <='d308))
//data\_o <= 'hff;
//else if((cnt\_h >='d309)&&(cnt\_h <='d341))
//data\_o <= 'h0f;
//else if((cnt\_h >='d342)&&(cnt\_h <='d374))
//data\_o <= 'hff;
//else
//data\_o <= 'h0f;
data\_o <= data\_o + 'd1;
else
data\_o <= 'd0;
always @(posedge clk)
valid\_zsc <= valid\_1;
endmodule

待处理图像缓存模块:

module mem\_control(input clk\_wr,
input clk\_rd,
input \[7:0\]coordinate\_x,
input \[7:0\]coordinate\_y,
input \[7:0\]din\_a,
input en\_a,
input \[7:0\]src\_width,
input en\_b,
output \[7:0\]doutbx,
output \[7:0\]doutbx1,
output \[7:0\]doutby,
output \[7:0\]doutby1,
output reg start = 'd0
    );
wire \[15:0\]size;
assign size = osrc\_width\*src\_width - 'd1;
wire \[7:0\]width;
assign width = osrc\_width - 'd1;

//把数据复制四份,通过牺牲面积换取速度
reg \[15:0\]address\_a = 'd0;
always @(posedge clk\_wr)
if(en\_a)begin
if(address\_a == size)
address\_a <= 'd0;
else
address\_a <= address\_a + 'd1;
end
else
address\_a <= address\_a;

如果数据缓存完毕,产生start标志信号,表示可以进行数据处理

always @(posedge clk\_wr)
if(address\_a == size)
start <= 'd1;
else
start <= 'd0;
wire \[15:0\]address\_bx;
wire \[15:0\]address\_bx1;
wire \[15:0\]address\_by;
wire \[15:0\]address\_by1;
assign address\_bx = (coordinate\_x == width)?coordinate\_x + coordinate\_y\*src\_width - 'd1:coordinate\_x + coordinate\_y\*src\_width;

assign address\_bx1 = (coordinate\_x == width)?address\_bx:address\_bx + 'd1;

assign address\_by = (coordinate\_y==width)?address\_bx:coordinate\_x + (coordinate\_y+'d1)\*src\_width;

assign address\_by1 = (coordinate\_x == width)?address\_by:address\_by + 'd1;

// Instantiate the module

ram\_module ram\_1 (
    .clk\_wr(clk\_wr), 
    .address\_a(address\_a), 
    .en\_a(en\_a), 
    .din\_a(din\_a), 
    .clk\_rd(clk\_rd), 
    .address\_b(address\_bx), 
    .en\_b(en\_b), 
    .doutb(doutbx)
    );
// Instantiate the module

ram\_module ram\_2 (
    .clk\_wr(clk\_wr), 
    .address\_a(address\_a), 
    .en\_a(en\_a), 
    .din\_a(din\_a), 
    .clk\_rd(clk\_rd), 
    .address\_b(address\_bx1), 
    .en\_b(en\_b), 
    .doutb(doutbx1)
    );

// Instantiate the module

ram\_module ram\_3 (
    .clk\_wr(clk\_wr), 
    .address\_a(address\_a), 
    .en\_a(en\_a), 
    .din\_a(din\_a), 
    .clk\_rd(clk\_rd), 
    .address\_b(address\_by), 
    .en\_b(en\_b), 
    .doutb(doutby)
    );
// Instantiate the module

ram\_module ram\_4 (
    .clk\_wr(clk\_wr), 
    .address\_a(address\_a), 
    .en\_a(en\_a), 
    .din\_a(din\_a), 
    .clk\_rd(clk\_rd), 
    .address\_b(address\_by1), 
    .en\_b(en\_b), 
    .doutb(doutby1)
    );
endmodule

双线性插值运算单元模块

module arithmetic\_unit(input clk,
  input \[7:0\]coefficient1,
  input \[7:0\]coefficient2,
  input \[7:0\]coefficient3,
  input \[7:0\]coefficient4,
  input en\_b,
  input \[7:0\]doutbx,
  input \[7:0\]doutbx1,
  input \[7:0\]doutby,
  input \[7:0\]doutby1,
  output reg\[7:0\]data\_o = 'd0,
  output reg en\_o = 'd0
    );
wire \[23:0\]data\_1;
wire \[23:0\]data\_2;
wire \[23:0\]data\_3; 
wire \[23:0\]data\_4;
assign data\_1 = coefficient1\*coefficient3\*doutbx;

/
assign data\_2 = coefficient2\*coefficient3\*doutbx1;

/
assign data\_3 = coefficient1\*coefficient4\*doutby;
assign data\_4 = coefficient2\*coefficient4\*doutby1;
wire \[23:0\]data\_a;
assign data\_a = data\_1 + data\_2;
reg \[23:0\]data\_aq = 'd0;
always @(posedge clk)
data\_aq <= data\_a;
wire \[23:0\]data\_b;
assign data\_b = data\_3 + data\_4;
reg \[23:0\]data\_bq = 'd0;
always @(posedge clk)
data\_bq <= data\_b;
wire \[23:0\]data\_oq;
assign data\_oq = data\_aq + data\_bq;
always @(posedge clk)
data\_o <= data\_oq\[21:14\];

//en\_b
reg en\_b\_q;
reg en\_b\_q1;
always@(posedge clk)begin
en\_b\_q <= en\_b;
en\_b\_q1 <= en\_b\_q;
en\_o <= en\_b\_q1;
end
endmodule

 对运算后的数据进行缓存模块:

module mem\_64multi\_64(input clk,
input \[7:0\]dina,
input ena,
input enb,
output reg start\_en = 'd0,
output \[7:0\]doutb
    );
/ram write
reg ena\_q = 'd0;
always @(posedge clk)
ena\_q <= ena;
reg \[11:0\]addra = 'd0;
always @(posedge clk)
if(ena\_q)begin
if(addra == 'd4095)
addra <= 'd0;
else
addra <= addra + 'd1;
end
else

addra <= addra;

//ram read
reg \[11:0\]addrb = 'd0;
always @(posedge clk)
if(enb)begin
if(addrb == 'd4095)
addrb <= 'd0;
else
addrb <= addrb + 'd1;
end
else
addrb <= addrb;
//
always @(posedge clk)
if(addra == 'd4095)
start\_en <= 'd1;
else
start\_en <= start\_en;

//----------- Begin Cut here for INSTANTIATION Template ---// INST\_TAG
ram64multi64 ram64multi64 (
  .clka(clk), // input clka
  .ena(ena\_q), // input ena
  .wea(1'b1), // input \[0 : 0\] wea
  .addra(addra), // input \[11 : 0\] addra
  .dina(dina), // input \[7 : 0\] dina
  .clkb(clk), // input clkb
  .enb(enb), // input enb
  .addrb(addrb), // input \[11 : 0\] addrb
  .doutb(doutb) // output \[7 : 0\] doutb
);
endmodule

 输出显示模块:

module vesa\_out( input clk,
  input start\_en,
  output v\_sync,
  output h\_sync,
  output de,
  output reg de\_o = 'd0
    );
reg \[10:0\] cnt\_h = 'd0;
reg \[10:0\] cnt\_v = 'd0;
always @(posedge clk)
if(start\_en)begin
if(cnt\_h == 'd799) 
cnt\_h <= 'd0;
else
cnt\_h <= cnt\_h + 'd1;
end
else
cnt\_h  <= cnt\_h;
always @(posedge clk)
if(cnt\_h == 'd799)begin
if(cnt\_v == 'd524)
cnt\_v <= 'd0;
else
cnt\_v <= cnt\_v + 'd1;
end
else
cnt\_v <= cnt\_v;
assign de = (((cnt\_h >='d200)&&(cnt\_h <='d263))&&((cnt\_v >='d135)&&(cnt\_v <='d198)))?'d1:'d0;
always @(posedge clk)
de\_o <= de;
assign h\_sync = (cnt\_h >= 'd97)?'d1:'d0;
assign v\_sync = (cnt\_v >= 'd3)?'d1:'d0;
endmodule

 仿真模块:

module top\_tb;
// Inputs
reg clk;
// Outputs
wire \[7:0\] doutb;
wire de\_o;
wire v\_sync;
wire h\_sync;
// Instantiate the Unit Under Test (UUT)
top uut (
.clk(clk), 
.doutb(doutb), 
.de\_o(de\_o), 
.v\_sync(v\_sync), 
.h\_sync(h\_sync)
);
initial begin
// Initialize Inputs
clk = 0;
// Wait 100 ns for global reset to finish
#100;
// Add stimulus here
end
   always #5 clk = !clk;   
endmodule

仿真结果如下:该仿真结果处理的是30*30大小图像缩放成64*64图像,输入图像的每一行数据都是1,2,3···30,仿真结果如下所示:

0f96031122b4bf6ff4f21600083def40.png

经与matlab运算结果做对比,结果完全一致。

END

原文链接: https://mp.weixin.qq.com/s/o3kKDFNLxjmpDm9CrRI7LQ
微信公众号:
hack电子.jpg
推荐阅读
更多IC设计技术干货请关注IC设计技术专栏。
推荐阅读
关注数
20187
内容数
1307
主要交流IC以及SoC设计流程相关的技术和知识
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息