14FPGA综设之图像边沿检测的sobel算法
连续学习FPGA基础课程接近一个月了,迎来第一个有难度的综合设计,图像的边沿检测算法sobel,用verilog代码实现算法功能。
一设计功能
(一设计要求)
(二系统框图)
根据上面的系统,Verilog代码如下:注意的是,VGA模块的时钟输入有两个,一是50M,二是25M。PLL的IP核的输入时钟连接顶层时钟,产生的输出时钟连接各个功能模块,有两个一是50M,二是25M。50M连接串口接收,sobel_ctrl控制模块。25M连接VGA_ram的vga显示部分和RAM的读地址的时钟,50M连接VGA_ram的RAM的写地址时钟。
module top_sobel( output wire hsync, wire clk_25out; wire uart_flag; wire sobel_flag; uart_rx uart_rx_m0( sobel_ctrl inst_sobel_ctrl ( vga_ram inst_vga_ram ( endmodule |
二设计思路
在原有基础上,自己动手设计,仿真验证,调试直到成功。
(一)我自己觉得这次的功能和上次的双FIFO流水线很紧密,所以这次,应该是先把双FIFO的逻辑弄懂,即把他们的时序图自己大致画一下,然后我觉得关键是怎么采集三行三列的9个数,我的想法是,比如第零行用FIFO1存储,然后再加三个寄存器,打拍操作。每来一个串口接收标志信号就送FIFO1的数据到如寄存器1中,然后是寄存器2,再是寄存器3。这样不断循环,就可以采集九个数据,最后再按照步骤进行相应的加法和乘法,绝对值操作。
刚开始要循序渐进,可以弄20X20的数据,而且只是实现到DX这一步,等完成了再往下继续加功能。
(二)设计知识点
1.打拍操作:同步复位,没有复位信号
第二点,我的FIFO1和FIFO2数据是在rd_en读使能信号控制下进行寄存器的打拍操作,而对这就个数进行运算则是在add_flag控制下,赋值输出(原因是rd_en 提前了add_flag一个时钟周期)
尤老师,讲为啥有wr_en_pre1和wr_en_pre12.主要是让FIFO的写使能信号和读使能信号同步。
sobel算法实现过程介绍
第三点,仔细看了几遍sobel算法的介绍,明白只需要采集到9个点,然后再按照步骤实现功能即可。
三所遇问题及解决办法
由于这次编写的综合设计模块,众多,而且代码量有上千行,我先暂时不详解每个模块的设计代码,而是我在亲自动手设计图像边沿检测的收获。
问题一:我直接在v3的源码中修改,想直接仿真运行,却发现modelsim报错:显示路径错误
* Error: (vopt-1933) Unable to create temporary directory D:/netclass/firstlevel/net19_double_fifo/double_fifo/work/_tempmsg # No such file or directory. (errno = ENOENT) # Error loading design |
原因:一是可能路径太长,二是原工程的路径和当前不符合。所以要么把工程直接放在更目录或者自己重新建立一个。
我的解决办法,自己重新建立工程,如IP核等,或者路径更改,可以移除工程在添加进去。
问题二:仿真时找不到文本,提示如下
** Warning: (vsim-7) Failed to open readmem file "./data.txt" in read mode. # No such file or directory. (errno = ENOENT) : sim/tb_top_uart.v(45) # Time: 0 ps Iteration: 0 Instance: /tb_top_uart 下面是错误目录下 |
解决办法:我是问了尤老师才知道应该放在modelsim的目录下即和ISE工程同一目录下
问题三:怎么对86X4的数据进行仿真
答案是并转串。即本来是86X4的矩阵,但我可以先在TXT文档中用344X1的数据替代,直接在仿真中把86改成344即可。
`timescale 1ns / 1ps module tb_top_uart; // Inputs reg sclk; reg rst_n; reg rx; reg [7:0] mem[343:0]; // Outputs wire tx; // Instantiate the Unit Under Test (UUT) top_dfifo uut ( .clk(sclk), .rst_n(rst_n), .rx(rx), .tx(tx) ); initial begin // Initialize Inputs sclk = 0; rst_n = 0; rx = 1; // Wait 100 ns for global reset to finish #100; rst_n =1; // Add stimulus here end initial begin $readmemb("./data.txt",mem); end always #10 sclk = ~sclk; initial begin #200; rx_byte(); end task rx_byte(); integer i; integer j; begin for(j=0;j<344;j=j+1)begin for (i=0;i<344;i=i+1)begin rx_bit(mem[i]); end end end endtask task rx_bit(input [7:0] data); integer i; begin for(i=0;i<10;i=i+1) begin case (i) 0:rx =0; 1:rx =data[i-1]; 2:rx =data[i-1]; 3:rx =data[i-1]; 4:rx =data[i-1]; 5:rx =data[i-1]; 6:rx =data[i-1]; 7:rx =data[i-1]; 8:rx =data[i-1]; 9:rx =1; endcase #104160; end end endtask endmodule |
问题三,怎么进行绝对值的计算
方法:先自己网上搜了下,大概是利用原码反码,补码的关系。即最高位为符号位,1表示负数,绝对值为取反加一。0表示正数,绝对值等于本身。
//abs_dx reg [7:0]abs_dx; reg [7:0]abs_dy; always@(posedge clk or negedge rst_n) if(!rst_n) abs_dx<=0; else if(flag_abs & dx[7]==1) abs_dx<=~dx+1; else if(flag_abs & dx[7]==0) abs_dx<=dx; |
问题四,怎么写VGA控制程序,在里面调用一个RAM。用来存储198X198个数据,VGA模块负责RAM的读写,让RAM里写入sobel_ctrl模块处理好数据,读出来的数据需要给rgb进行显示。
我的想法是:要做一个新东西,就首先学会联系已学过的东西(基础),既然用RAM读写这198X198数据,那么首先得搞明白RAM。再自己适当修改一下读写逻辑,如数据的读写地址等等就欧克。 我选择的RAM类型为:Simple Dual Port RAM,该ram包含两个地址总线,一个写地址和一个读地址,分别控制两个地址总线可以控制该ram的读和写。还有一个关键信号:wr_en,控制读写逻辑。本RAM位宽为8深度为256的ram. 下面的代码是根据上面的RAM的读写时序和设定的位宽深度设计的:
而这次的写使能信号是由 sobel_ctrl的pi_flag控制的,还有一个关键点是写地址是50M的系统时钟,而读地址是25M的时钟。所以需要在顶层加一个PLL输出两个时钟,一个是50M,一个是25M,PLL的输入时钟接顶层时钟50M,然后把PLL输出的时钟分别连在串口模块,sobel模块,VGA模块。 |
关键点:在VGA里调用一个40K的ram存储198X198的数据,怎么设计写使能信号和读写地址值得注意。先将它的代码展示如下
module vga_ram( reg [15:0] addrb,addra; parameter MAX_value = 16'd39203; reg [8:0]x; //行移动计数器最大439 reg dec_x;//行计数器减一切换标志信号 reg [9:0]cnt_h; parameter h_max =10'd799; //行计数器 //vsync场同步信号 parameter T100MS = 23'd2_599_999; //the flag of one_s_flag always@(posedge clk or negedge rst_n) always@(posedge clk or negedge rst_n) always @(posedge clk or negedge rst_n) begin always @(posedge clks or negedge rst_n) begin RAM40K ram_inst( |
问题五,怎么产生200X200的数据,即把一个200X200像素的图片转换为200X200的数据阵列?
以前的经验是,直接弄一个txt文本储存200X200的数据,不过是并转串,而且是用在仿真中。 尤老师的经验是,用MATLAB处理,即给一个200X200的像素图片,用MATLAB的相应语句转换产生一个200X200的数据阵列,再复制到友善串口助手发送,之后发现显示器还是黑色的,他推测可能是阈值过大。 我觉得遇到这种完全新的,还是先记录问题,再看哈视频,然后自己动手做。 在图像边沿检测的视频二50分钟,我看到了建完所有的模块。 下面是MATLAB的图片转阵列的代码,第一行是读取图片(直接把图片 粘贴在当前文件夹下,再v3edu修改成图片相应的名字)第二行是转换的图片的阵列大小,这个是根据MATLAB相应的工作路径下显示可以转换的范围,而不是胡乱搞的。
操作示意图如上 解决办法:即出现多沿触发时,要同一个信号如CLK都是上升沿,或rst_n都为下降沿有效。 |
问题五。怎么debug?
首先在顶层,看看各个模块的连接有没有错。第二步,检查控制模块sobel_ctrl的各个模块和信号的逻辑有没有错。第三步,逻辑分析仪:在ise14.7中建立了ICON和ILA这两个IP核(弄懂这)
问题六:分成两部分一是没有图像显示,原来时没有管脚约束文件。。
第二个问题是,RAM的数据没有读出来,即移动的方框一直显示黑色,即RAM输出的po_data一直为零。(rgb信号全1为白,全零为黑)
故解决办法,明天好好改哈RAM的读写逻辑。
通过综合器的警告,我发现,在顶层模块中,每个功能模块没有和顶层的时钟信号连接,即只连接的自己模块的,这没有时钟驱动源。我用pll模块产生了两个时钟输出:一个50M和25M,然后对应连接各个模块就欧克勒。 |
最终结论确实是RAM模块的读写地址时钟不一样,还有就是顶层模块,除了PLL的输入时钟连接系统时钟,其他模块的时钟信号都是连接的PLL的输出时钟,50M 或25M。
最终显示效果如下,在看到图像那一刻很开心,比较自己亲自调了一周程序,还好没放弃:
皮卡丘原图:
经过sobel算法处理后的图片:
知识点:一是怎么采集3X3的9个数,直接用2个FIFO,在每个FIFO用三个寄存器,在标志信号的控制下进行打拍 操作。二是怎么把一个图片转换为一个如200X200的矩阵数列,直接用MATLAB转换即可。
我的收获是:一是做一个新东西,在原有基础上想办法。二是,要拆分设计验证,不能一把搞完所有模块,直接去上板验证。
14FPGA综设之图像边沿检测的sobel算法的更多相关文章
- 边沿检测电路设计verilog
Abstract 边沿检测电路(edge detection circuit)是个常用的基本电路. Introduction 所谓边沿检测就是对前一个clock状态和目前clock状态的比较,如果是由 ...
- Atitit 图像清晰度 模糊度 检测 识别 评价算法 源码实现attilax总结
Atitit 图像清晰度 模糊度 检测 识别 评价算法 源码实现attilax总结 1.1. 原理,主要使用像素模糊后的差别会变小1 1.2. 具体流程1 1.3. 提升性能 可以使用采样法即可..1 ...
- Atitit 图像清晰度 模糊度 检测 识别 评价算法 原理
Atitit 图像清晰度 模糊度 检测 识别 评价算法 原理 1.1. 图像边缘一般都是通过对图像进行梯度运算来实现的1 1.2. Remark: 1 1.3. 1.失焦检测. 衡量画面模糊的主要方 ...
- FPGA学习笔记之格雷码、边沿检测、门控时钟
一.格雷码 格雷码的优点主要是进位时只有一位跳变,误码率低. 1.二进制转格雷码 我们观察下表: 二进制码 格雷码 00 00 01 01 10 11 11 10 二进制码表示为B[],格雷码表示为G ...
- YOLT:将YOLO用于卫星图像目标检测
之前作者用滑动窗口和HOG来进行船体监测,在开放水域和港湾取得了不错的成绩,但是对于不一致的复杂背景,这个方法的性能会下降.为了解决这个缺点,作者使用YOLO作为物体检测的流水线,这个方法相比于HOG ...
- 边沿检测方法-FPGA入门教程
本节实验主要讲解FPGA开发中边沿检测方法,我们在设计中会经常用到.这个地方大家一定要理解. 1.1.1.原理介绍 学习HDL语言设计与其他语言不一样,HDL语言设计需要考虑更多的信号的电气特性,时序 ...
- verilog 之数字电路 边沿检测电路
由代码可知:此边沿检测电路是由两个触发器级联而成,sign_c_r 输出是sign_c_r2的输入.并且有异步复位端没有使能端.最后输出:由触发器的输出取反和直接输出相与.如下的RTL图.
- 使用Caffe完成图像目标检测 和 caffe 全卷积网络
一.[用Python学习Caffe]2. 使用Caffe完成图像目标检测 标签: pythoncaffe深度学习目标检测ssd 2017-06-22 22:08 207人阅读 评论(0) 收藏 举报 ...
- FPGA编程技巧系列之按键边沿检测
抖动的产生: 通常的按键所用开关为机械弹性开关,当机械触点断开.闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开.因而在闭合及断开的瞬间均伴随有一连串的 ...
随机推荐
- Large Sacle Distributed Deep Networks
本文是谷歌发表在NeurIPS 2012上的一篇论文,主要讨论了在几万个CPU节点上训练大规模深度网络的问题,并提出了一个名为DistBelief的软件框架.在该框架下实现了两种大规模分布式训练算法: ...
- java-poi 批量导入excel数据
1,首先,前端发送MultipartFile类型文件,后端接收 2,分别创建多个ImportParams对象(easypoi),对应工作蒲 注意:pom中 要有相对应的配置 <!-- easyp ...
- jsp页面获取请求参数问题记录
同一个请求可以从请求路径中获取参数,使用param.参数名 window.location.href = "admin/page.html?pageNum="+pageNum+&q ...
- WEB服务蜜罐部署实验
实验目的 了解WEB蜜罐的基本原理,掌握Trap Server的使用. 实验原理 Trap Server是一款WEB服务器蜜罐软件,它可以模拟很多不同的服务器,例如Apache. HTTP Serve ...
- 2022李宏毅作业hw1—新冠阳性人员数量预测。
事前 : kaggle地址:ML2021Spring-hw1 | Kaggle 我的git地址: https://github.com/xiaolilaoli/lihongyi2022homew ...
- python运算符优先级及部分运算
在python里面,有很多运算符,比如:算术运算符.赋值运算符.比较运算符.逻辑运算符.成员运算符.身份运算符和位运算符等.这里主要来看看这些运算符的优先级:从上到下优先级依次递减. 优先顺序 运算符 ...
- 微服务从代码到k8s部署应有尽有系列(九、事务精讲)
我们用一个系列来讲解从需求到上线.从代码到k8s部署.从日志到监控等各个方面的微服务完整实践. 整个项目使用了go-zero开发的微服务,基本包含了go-zero以及相关go-zero作者开发的一些中 ...
- 进程&线程(三):外部子进程subprocess、异步IO、协程、分布式进程
1.外部子进程subprocess python之subprocess模块详解--小白博客 - 夜风2019 - 博客园 python subprocess模块 - lincappu - 博客园 之前 ...
- Vue之路由的使用
零.传统路由与SPA的区别 传统开发方式下,URL改变后,就会立刻发生请求去请求整个页面,这样可能请求加载的资源过多,可能会让页面出现白屏. 在SPA(Single Page Application) ...
- 六、Java方法
Java方法 何为方法 System.out.println(),那么它是什么呢? System是一个类,out是一个对象,println()是一个方法 Java方法是语句的集合,它们在一起执行的 ...