草庐IT

FPGA开发(3)——fifo获得3×3数据矩阵

树叶~ 2023-04-19 原文

一、3×3矩阵的获取方式

查阅了一些FPGA图像处理的资料,总结出了获得3×3图像矩阵的方法主要有下面这几种。(1)用移位寄存器IP核;(2)用2个或者3个ram实现;(3)用2个或者3个fifo实现。我这边是使用vivado作为开发环境,quartus中有专门的IP核可以实现图像数据的缓存,但是vivado中的移位寄存器只可以缓存一行,而且最多缓存1088个,如下图所示。而且缓存数据很多时,会出现缓存数量不准确的现象,大家可以自己去试试。因此在vivado中推荐使用fifo或者ram来实现。

二、基于fifo提取3×3矩阵的时序图

利用时序图软件绘制了用fifo实现的移位寄存器的时序图,这边大致介绍下每个信号的作用。

clk:时钟信号
rst_n:复位信号,低有效
clken:数据有效信号,高有限
shiftin:数据输入信号
wren1:fifo1的写使能
data_count1:fifo1的数据计数
rden1:fifo1的读使能
dout:fifo1的数据输出
wren2:fifo2的写使能
data_count2:fifo2的数据计数
rden2:fifo2的读使能
taps1x:前前一行数据
taps0x:前一行数据
shift_in_dy2:第一行数据
clken_dy2:输出数据使能信号

然后时序图我假设每个fifo的深度是16,然后fifo每行缓存的数据是4个。当rst_n拉高后,clken是输入数据有效时刻,shift_in是输入数据。wren1和clken同步,把数据写到fifo1,data_count1开始计数。当计数满4个且clken为高电平时,rden1拉高开始从fifo1中读出数据,dout是从fifo1中读出的数据。wren2比rden1延迟一个时钟周期拉高,将fifo1中输出的数据写入到fifo2中。这是data_count2对fifo2中数据计数。当数据满足4个并且在clken_dy(clken延迟一个周期)为高时rden2有效,将fifo2中的数据读出。shift_in_dy2将输入信号延迟两个周期,clken_dy2将clken延迟两个周期以完成输出信号的同步。

三、基于fifo提取3×3矩阵的仿真

为了实现上述时序图的功能,首先需要例化一个fifo,例化过程如下图。选择同步fifo,然后设置位宽位深,打开fifo数据计数,其他默认即可。



根据时序图编写的verilog代码以及tb文件代码如下所示。

module line_shift_fifo_8bit(
    input clk,
    input rst_n,
    input clken,
    input [7:0] shiftin,
    output [7:0] taps0x,
    output [7:0] taps1x
);

reg clken_dy;
wire wren1;
wire rden1;
wire wren2;
reg wren2_dy;
wire rden2;
wire [7:0] dout;
reg [7:0] taps0x_dy;
wire [3:0] data_count1;
wire [3:0] data_count2;

//delay a clock, because read1 is quicker than write2
always @(posedge clk or negedge rst_n) begin
  if(~rst_n)begin
    wren2_dy<=1'b0;
  end
  else wren2_dy<=rden1;
end
//delay a clock, make write2 and read 2 at same time
always @(posedge clk or negedge rst_n) begin
  if(~rst_n)begin
    clken_dy<=1'b0;
  end
  else clken_dy<=clken;
end
//delay a clock, taps0x_dy
always @(posedge clk or negedge rst_n) begin
  if(~rst_n)begin
    taps0x_dy<=8'd0;
  end
  else taps0x_dy<=dout;
end

assign wren1= clken;
assign rden1= clken && (data_count1==4'd4);
assign wren2= wren2_dy;
assign rden2= clken_dy && (data_count2==4'd4);
assign taps0x=taps0x_dy;

fifo_generator_0 u_fifo_generator_0 (
  .clk              (clk),                // input wire clk
  .srst             (~rst_n),              // input wire srst
  .din              (shiftin),                // input wire [7 : 0] din
  .wr_en            (wren1),            // input wire wr_en
  .rd_en            (rden1),            // input wire rd_en
  .dout             (dout),              // output wire [7 : 0] dout
  .full             (),              // output wire full
  .empty            (),            // output wire empty
  .data_count       (data_count1)  // output wire [3 : 0] data_count
);

fifo_generator_0 u_fifo_generator_1 (
  .clk              (clk),                // input wire clk
  .srst             (~rst_n),              // input wire srst
  .din              (dout),                // input wire [7 : 0] din
  .wr_en            (wren2),            // input wire wr_en
  .rd_en            (rden2),            // input wire rd_en
  .dout             (taps1x),              // output wire [7 : 0] dout
  .full             (),              // output wire full
  .empty            (),            // output wire empty
  .data_count       (data_count2)  // output wire [3 : 0] data_count
);

endmodule 

`timescale 1ns/1ns
module tb_fifo();

reg clk;
reg rst_n;
reg clken;
reg [7:0] shiftin;
wire [7:0] taps0x;
wire [7:0] taps1x;
reg [7:0] shiftin_dy1;
reg [7:0] shiftin_dy2;
wire clken_dy2;
reg [1:0] clken_dy_two;

always #10 clk=~clk;

initial begin
  clk<=1'b0;
  clken<=1'b0;
  rst_n<=1'b0;
  #20;
  rst_n<=1'b1;
end

//these has a difference between zuse and fei zuse when simulation
initial begin
  #30;
  clken<=1'b1;
  #80;
  clken<=1'b0;
  #40;
  clken<=1'b1;
  #80;
  clken<=1'b0;
  #40;
  clken<=1'b1;
  #80;
  clken<=1'b0;
  #40;
  clken<=1'b1;
  #80;
  clken<=1'b0;
  #40;
  clken<=1'b1;
  #80;
  clken<=1'b0;
end

always @(posedge clk or negedge rst_n) begin
  if(~rst_n)begin
    shiftin<=8'd0;
  end
  else if(clken==1'b1)begin
    shiftin<=shiftin+1'b1;
  end
  else shiftin<=shiftin;
end

always @(posedge clk or negedge rst_n) begin
  if(~rst_n)begin
    clken_dy_two<=2'd0;
  end
  else begin
    clken_dy_two<={clken_dy_two[0],clken};
  end
end

always @(posedge clk or negedge rst_n) begin
  if(~rst_n)begin
    shiftin_dy1<=8'd0;
    shiftin_dy2<=8'd0;
  end
  else begin
    shiftin_dy1<=shiftin;
    shiftin_dy2<=shiftin_dy1;
  end
end

assign clken_dy2=clken_dy_two[1];


line_shift_fifo_8bit u_line_shift_fifo_8bit(
    .clk     ( clk     ),
    .rst_n   ( rst_n   ),
    .clken   ( clken   ),
    .shiftin ( shiftin ),
    .taps0x  ( taps0x  ),
    .taps1x  ( taps1x  )
);

endmodule 

最后给出vivado的仿真结果如下图所示。可以对比timgen的时序图和仿真的结果,是完全一致的。

四、利用fifo实现的3×3矩阵进行图像处理仿真

我这边是使用1024×768分辨率的屏幕,所以这边缓存数量选择1024个数据,改变一下fifo的深度为2048大于1024即可,然后代码中fifo技术改变位数,其他都不需要改动。
在之前的工程中用ram实现了矩阵生成的verilog代码,这边只需要将ram ip核改成fifo实现的就可以了。另外还需要注意的是,利用ram实现的缓存是只延迟了一拍,而用fifo实现的缓存则是消耗了两拍。所以最后给矩阵的数据是总共延迟了三拍,这点与之前不同。

module  VIP_matrix_generate
(
	input				clk,  		
	input				rst_n,				

	input				per_frame_vsync,
	input				per_frame_href,
	input				per_frame_clken,
	input        [7:0]  per_img_Y,

	output				matrix_frame_vsync,
	output				matrix_frame_href,
	output				matrix_frame_clken,
	output	reg  [7:0]  matrix_p11, 
	output	reg  [7:0]  matrix_p12,
	output	reg  [7:0]  matrix_p13,
	output	reg	 [7:0]  matrix_p21, 
	output	reg  [7:0]  matrix_p22, 
	output	reg  [7:0]  matrix_p23,
	output	reg	 [7:0]  matrix_p31, 
	output	reg  [7:0]  matrix_p32, 
	output	reg  [7:0]  matrix_p33
);

//wire define
wire  [7:0]  row1_data;
wire  [7:0]  row2_data;
wire	     read_frame_href ;
wire	     read_frame_clken;

//reg define
reg  [7:0]  row3_data;
reg  [7:0]  row3_data_dy;
reg  [2:0]  per_frame_vsync_r;
reg  [2:0]  per_frame_href_r;
reg  [2:0]  per_frame_clken_r;

assign	read_frame_href    = per_frame_href_r[1] ;
assign	read_frame_clken   = per_frame_clken_r[1];
assign	matrix_frame_vsync = per_frame_vsync_r[2];
assign	matrix_frame_href  = per_frame_href_r[2] ;
assign	matrix_frame_clken = per_frame_clken_r[2];

//present signal delay 2 clk 
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		row3_data_dy<= 0;
		row3_data <= 0;
	end
	else begin		
		if(per_frame_clken) begin
			row3_data_dy <= per_img_Y ;
			row3_data <= row3_data_dy;
		end
		else begin
			row3_data_dy<= row3_data_dy;
			row3_data <= row3_data;
		end
	end
end

line_shift_fifo_8bit u_line_shift_fifo_8bit(
    .clk     ( clk     			),
    .rst_n   ( rst_n   			),
    .clken   ( per_frame_clken  ),
    .shiftin ( per_img_Y 		),
    .taps0x  ( row2_data  		),
    .taps1x  ( row1_data  		)
);

//delay 3 tclk
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) begin		
		per_frame_vsync_r <= 0;
		per_frame_href_r  <= 0;
		per_frame_clken_r <= 0;
	end
	else begin		
		per_frame_vsync_r <= { per_frame_vsync_r[1:0], per_frame_vsync };
		per_frame_href_r  <= { per_frame_href_r[1:0],  per_frame_href  };
		per_frame_clken_r <= { per_frame_clken_r[1:0], per_frame_clken };
	end
end

//generate the 3X3 matrix 
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) begin		
		{matrix_p11, matrix_p12, matrix_p13} <= 24'h0;
		{matrix_p21, matrix_p22, matrix_p23} <= 24'h0;
		{matrix_p31, matrix_p32, matrix_p33} <= 24'h0;
	end
	else if(read_frame_href) begin
		if(read_frame_clken) begin			
			{matrix_p11, matrix_p12, matrix_p13} <= {matrix_p12, matrix_p13, row1_data};
			{matrix_p21, matrix_p22, matrix_p23} <= {matrix_p22, matrix_p23, row2_data};
			{matrix_p31, matrix_p32, matrix_p33} <= {matrix_p32, matrix_p33, row3_data};
		end
		else begin			
			{matrix_p11, matrix_p12, matrix_p13} <= {matrix_p11, matrix_p12, matrix_p13};
			{matrix_p21, matrix_p22, matrix_p23} <= {matrix_p21, matrix_p22, matrix_p23};
			{matrix_p31, matrix_p32, matrix_p33} <= {matrix_p31, matrix_p32, matrix_p33};
		end	
	end
	else begin		
		{matrix_p11, matrix_p12, matrix_p13} <= 24'h0;
		{matrix_p21, matrix_p22, matrix_p23} <= 24'h0;
		{matrix_p31, matrix_p32, matrix_p33} <= 24'h0;
	end
end

endmodule

给出最后的仿真结果,可以看到数据缓存到row1、row2和row3中,之后在送到矩阵matrix中,具体细节不在阐述。


放上最后的结果图,原图是加了噪声的风景图,然后经过了rgb转灰度,sobel边缘检测后输出图像。

有关FPGA开发(3)——fifo获得3×3数据矩阵的更多相关文章

  1. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  2. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  3. ruby - 无法在 60 秒内获得稳定的 Firefox 连接 (127.0.0.1 :7055) - 2

    我使用的是Firefox版本36.0.1和Selenium-Webdrivergem版本2.45.0。我能够创建Firefox实例,但无法使用脚本继续进行进一步的操作无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055)错误。有人能帮帮我吗? 最佳答案 我遇到了同样的问题。降级到firefoxv33后一切正常。您可以找到旧版本here 关于ruby-无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055),我们在StackOverflow上找到一个类

  4. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  5. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  6. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  7. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  8. ruby-on-rails - 在 Rails 开发环境中为 .ogv 文件设置 Mime 类型 - 2

    我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain

  9. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

  10. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

随机推荐