基于FPGA的数字图像处理【1.9】

3.2 模块化设计

        和软件编程一样,在硬件系统设计中,模块化的思维也是十分重要的。特别是对于图像处理这样的大型系统,模块化不仅可大幅度增加代码的可读性(尤其是硬件描述语言这样“晦涩”的语言),还可以提高代码的可重用性、可维护性和可移植性。此外,模块化测试是在测试工作中必不可少的一环,把大的工作任务划分为小的模块进行测试,可大大减轻测试和调试bug的工作量。值得注意的是,在软件中,封装和模块化会带来一定程度的效率问题,而在硬件模块化的过程中,将涉及少量因布局布线而带来的硬件成本问题。

        在模块化的过程中需要注意的有以下几点:
        (1)子模块的划分要十分合理。模块设计的首要考虑因素是功能,需实现真正的高内聚低耦合。模块仅留出对外接口,同时内部调用其他的模块。硬件描述语言描述的一个模块就是一个拥有输入/输出接口的“IC”,很难想象这个IC出现一个莫名其妙的与本模块无关的信号,或是需要一个很复杂的非标准机制才能正常工作。
        (2)顶层逻辑设计中,建议只出现布局布线资源,尽量避免不必要的逻辑处理。就像画电路图一样,普遍的做法是直接把现成的封装好的器件拖进来,使得在电路图中很少能看到芯片内部的结构设计。这样不仅会使整个图纸看起来很“不雅”,而且顶层文件都是相对比较复杂的系统连接,模块化连接会使系统的可读性和调试工作变得更加简单。
        (3)在顶层文件中,除非是比较简单的系统,尽量使用硬件描述语言文件来代替电路图文件。电路图文件在处理比较大的系统时极易出错,不够灵活,维护性也比较差。
        (4)避免太多层次的模块调用,一般情况下建议子层深度不超过5,不然会使代码可读性变得极差。

3.3 可移植性

        模块化设计带来的显著优势就是代码的可移植性和可重用性。在FPGA领域的可移植性和嵌入式软件领域的可移植性还是有所不同。硬件的可移植性要考虑的问题非常之多。嵌入式软件的可移植性主要是通过屏蔽硬件平台的差异性,常用的方法包括将硬件操作抽象成标准的文件流操作等。对于FPGA来说,可移植性就没有那么简单了。虽然说VHDL和Verilog都已经有了IEEE标准,但是FPGA本身的硬件差异性就非常之大。单不说有数十家的FPGA生产厂商,就是在两大主流厂商
Altera和Xilinx中,不同型号的FPGA差异性也都比较大。在一个图像处理系统中,不可避免地要用到很多硬件IP核,包括锁相环、内部Fifo、内部Ram、内部Rom、DSP单元及滤波器等。而这些IP核是系统对于不同厂家的FPGA来说是肯定不可移植的,对相同厂家的不同型号,可移植性问题也得十分小心地处理。

        对于外部硬件设备和用户自定义逻辑,可移植性不是什么难题。在FPGA的图像处理中,典型的可移植的对象有数据位宽、图像宽度、图像高度、缓存深度、处理窗口(size)及视频流处理阈值等。以下是两个典型的可移植性实例。
示例1 局部总线控制vga控制器

module vga_ctrl(
clk,
rst_n,
rgb_din,
din_valid,
vsync,
hsync,
rgb_out,
blank
);parameter DW = 8; //像素流数据位宽
parameter IW = 640; //图像宽度
parameter IH = 512; //图像高度

示例2 符合Altera Avalon总线标准的vga控制器

module vga_avalon_slave (
clk,
disp_clk,
reset_n,
vga_reset_n,
vga_vsync,
vga_hsync,
vga_balnk,
vga_rgb_out,
monitor_w,
monitor_h,
image_w,
image_h,
scan_freq,
sys_err,
master_address,
master_burstcount,
master_byteenable,
master_chipselect,
master_write_n,
master_writedata,
master_write_waitrequest
);parameter DW = 8; //像素流数据位宽
parameter FIFO_DEPTH = 4096; //显存FIFO的深度
parameter FIFO_DEPTH_LOG2 = 12; //显存FIFO位宽
parameter BURST_WIDTH = 10; //突发传输位宽

        读者可能已经注意到以上两个示例的些许差别:在示例1中视频的宽度和高度是通道宏参数传入的,而在示例2中是作为输入接口传入模块。通常来说,从代码的规范性和完善性角度考虑,示例2无疑是比较好的选择,但是在实际应用中,可能示例1用的概率更大一点。这是基于两个方面的考虑:一方面在实际用途中,我们已经知道了这个视频处理的分辨率,再做输入端口意义不大,还会增加不必要的逻辑资源。另一方面,示例2无疑会带来更多的输入接口,耗用更多的布线资源,这往往是我们所不希望看到的。当然,如果想做一个通用的跨平台的控制器,示例2的方法无疑是首选。
        但是,示例2定义的模块是否可以移植到不同的硬件平台上,答案显然是不确定的。这是因为这个显示控制器往往会设置一片显示缓存。如果这个缓存控制是用户自定义的逻辑,那么这个模块将会拥有非常完美的可移植性。而事实上我们经常会“懒得”写出自己的逻辑,往往会调用更为安全可靠且能节省资源的IP核。在调用IP核的时候,利用GUI界面进行配置往往比较直观和安全,但是不如直接例化来得方便一点,特别是在我们需要对某些参数进行自定义例化的时候(IP核生成器这一块往往做得很差劲)。

        直接用HDL代码对其进行例化是一种很好的解决办法,另外一个比较实用的方法是在GUI例化后手动修改。修改后的例化参数必须满足IP核的要求。
        以下的参考代码是一个同步FIFO作为行缓存的设计实例(基于Altera器件)。

// synopsys translate_off'timescale 1 ps / 1 ps
// synopsys translate_on
module line_buffer_fifo (
aclr,
clock,
data,
rdreq,
wrreq,
empty,
full,
q,
usedw);
parameter DW = 8;//can not be modified here
parameter DEPTH = 1024;
parameter DW_DEPTH = 10;
input aclr;
input clock;
input [DW-1:0]data;
input rdreq;
input wrreq;
output empty;
output full;
output [DW-1:0]q;
output [DW_DEPTH-1:0]usedw;
wire [DW_DEPTH-1:0]sub_wire0;
wire sub_wire1;
wire sub_wire2;wire [DW-1:0]sub_wire3;
wire [DW_DEPTH-1:0]usedw = sub_wire0[DW_DEPTH-1:0];
wire empty = sub_wire1;
wire full = sub_wire2;
wire [DW-1:0]q = sub_wire3[DW-1:0];
scfifo scfifo_component (
.clock (clock),
.wrreq (wrreq),
.aclr (aclr),
.data (data),
.rdreq (rdreq),
.usedw (sub_wire0),
.empty (sub_wire1),
.full (sub_wire2),
.q (sub_wire3),
.almost_empty (),
.almost_full (),
.sclr ()
);
defparam
scfifo_component.add_ram_output_register = "OFF",
scfifo_component.intended_device_family = "Stratix
III",
scfifo_component.lpm_numwords = DEPTH,
scfifo_component.lpm_showahead = "OFF",
scfifo_component.lpm_type = "scfifo",
scfifo_component.lpm_width = DW,scfifo_component.lpm_widthu = DW_DEPTH,
scfifo_component.overflow_checking = "ON",
scfifo_component.underflow_checking = "ON",
scfifo_component.use_eab = "ON";
endmodule

        可以看到,系统也是通过实例化altera_mf库里面的scfifo模块来实现fifo模块的,和普通的用户逻辑并没有什么实质性的区别。一般情况下,位宽、深度和深度的位宽这几个参数也常常作为缓存模块的参数。细心的读者可能已经注意到了,在dcfifo实例化的过程中,有一个intended_device_family的参数可以选择,通过这个字符串参数可以选择不同的器件来屏蔽底层的器件差异性。

3.4 不可移植性

        读到这里,读者可能已经非常欣喜:是否意味着大部分的代码都可以进行自由的移植和重用了?然而在图像处理的FPGA算法领域,现实是非常“骨感的”。通常情况下,在某个实际应用中,算法是确定的,而为了节省逻辑资源和减小计算复杂度,通常会首先对算法进行变形,例如,将除法操作转换为移位运算,去开方转化等。下面举一个例子说明这个问题。假设现在要实现这样一个算法,该算法要计算一个15×15窗口内像素均值。计算公式如式(3-1)所示:

        其中,r代表窗口的尺寸,x,y代表窗口的中心像素。
        这个算法非常简单,尤其是对于软件来讲,用两个for循环来实现窗口内像素相加,再将结果除以像素总数,也就是15×15=225。然而在硬件计算中,通常不会这么做。除法操作在硬件计算是非常“吓人”的,为了追求计算效率和尽量节省资源,FPGA通常会将算法转换为移位和加法操作来对除法进行近似。具体计算过程如下:

        可以想象,不同的算法的转换方法千变万化,不同的窗口尺寸所带来的转换方法是有很大差别的,而在软件中,这样的算法移植性是不费吹灰之力的一件事情。
        读者需注意,在FPGA算法处理领域,上述处理是非常常见和实用的,在本书后面章节的算法介绍中,也会采用相同的处理方法。而把握好逻辑资源的使用和计算误差的平衡是在设计时所需要考虑的问题之一。
        不可移植性不仅仅是前面所考虑的问题,更多的问题是由于硬件依赖所带来的不可移植性。在FPGA设计中,盲目地追求可移植性和代码重用的想法是大错特错的。这是由于FPGA是一个硬件设计平台,对硬件的依赖性非常强。用户应该尽可能熟悉FPGA内部的硬件资源并做到充分利用。一个典型的例子是Altera器件片内存储器的应用,用片内的寄存器资源实现深度较大的缓存模块虽然可以带来移植方便的好处,但无疑是非常不合理的。此外,一些FPGA内部的特有硬件,例如移位寄存器、DSP块及除法器等,在实际应用时可以例化这些硬件模块来代替用其他资源生成的描述架构(部分综合器在进行编译时可以自动例化片内的硬件资源,例如乘法器和除法器等)。

3.5 测试逻辑

        通常,可调试性不是我们在设计代码的过程中主要的考虑因素,因为FPGA厂商所提供的开发工具都提供了非常完善的调试工具来实现在线调试。然而,在实际中一些冗余的测试逻辑是十分有用的。特别是在整个工程比较大的时候,虽然仿真工作可以发现大部分的bug,但是由于部分仿真模型很难建立(例如一个复杂的外部接口),只能通过在线调试进行验证。然而每次的编译综合和下载过程十分耗时(一个比较大的工程可能会消耗半个小时以上),适当的测试逻辑可以对系统功能进行自我测试。
        这 个 自 我 测 试 也 称 为 內 建 自 测 试 ( Bulit-In Self-Test ,BIST)。在这种方法中,可以通过特定的输入信号或者输入信号的组合激活芯片内部的一些测试电路。在图像处理领域,我们有时候希望得到整个算法某个中间结果的输出进行分析(例如输出到显示器),特备是算法效果不太理想的时候,在分析问题的时候我们可能想知道中间结果是否正常,这个时候算法输出一路debug信号也是十分有用的。如下所示:

module Morph_Open_2D(
clk,
rst_n,
din,
din_valid,
din_vsync,
dout,//输出逻辑
dout_valid,
dout_vsync,
dbg_sel,//debug输出选择 可以包含多个输出结果
dbg_out,//测试逻辑输出
dbg_valid,
dbg_vsync
);

        特别地,显示模块也可以接收多路的输入信号进行切换显示,切换可以通过板上的硬件(如按键或者拨码开关)来进行配合。

3.6 冗余逻辑

        冗余逻辑最常应用于在那些需要连续运行而不能出现任何问题的系统中(例如军事系统和银行系统),在这类系统中,逻辑是有备份的,在冗余硬件的后面有一个设备专门比较冗余硬件的输出。通常,这些系统中会有3个冗余模块,如果其中的一个出现了问题,而其他两个还在工作,那么出现问题的模块将会被忽略。用来比较的硬件被称为“投票逻辑”,因为它能比较这3个冗余模块的输出信号,并判断输出一致且占多数的信号具有正确的输出值,如图3-2所示。

        除非是系统对稳定性有足够高的要求,冗余逻辑不是必需的,因为它会带来额外的资源开销。部分综合器会在综合的过程中将冗余逻辑给优化掉,这是在冗余逻辑设计的过程中需要考虑的问题。

  • 28
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BinaryStarXin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值