Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >FPGA上如何求32个输入的最大值和次大值:分治

FPGA上如何求32个输入的最大值和次大值:分治

作者头像
sea-wind
发布于 2019-07-31 02:57:59
发布于 2019-07-31 02:57:59
3.4K00
代码可运行
举报
文章被收录于专栏:海风海风
运行总次数:0
代码可运行

上午在论坛看到个热帖,里头的题目挺有意思的,简单的记录了一下。

0. 题目 

FPGA上实现一个模块,求32个输入中的最大值和次大值,32个输入由一个时钟周期给出。(题目来自论坛,面试题,如果觉得不合适请留言删除)

从我个人的观点来看,这是一道很好的面试题目:

  • 其一是这大概是某些机器学习算法实现过程中遇到的问题的简化,是很有意义的一道题目;
  • 其二是这道题目不仅要求FPGA代码能力,还有很多可以在算法上优化的可能;

当然,输入的位宽可能会影响最终的解题思路和最终的实现可能性。但位宽在一定范围内,譬如8或者32,解题的方案应该都是一致的,只是会影响最终的频率。后文针对这一题目做具体分析。(题目没有说明重复元素如何处理,这里认为最大值和次大值可以是一样的,即计算重复元素)

1. 解法

从算法本身来看,找最大值和次大值的过程很简单;通过两次遍历:第一次求最大值,第二次求次大值; 算法复杂度是O(2n)。FPGA显然不可能在一个周期内完成如此复杂的操作,一般需要流水设计。这一方法下,整个结构是这样的

  1. 通过比较,求最大值,通过流水线实现两两之间的比较,32-16-8-4-2-1通过5个clk的延迟可以求得最大值;
  2. 由于需要求取次大值,因此需要确定最大值的位置,在求最大值的过程中需要维持最大值的坐标;
  3. 最大值坐标处取值清零(置为最小)
  4. 通过流水线实现两两之间的比较,32-16-8-4-2-1,再经过5个clk的延迟可以求得次大值;

这种解法有若干个缺点,包括:延迟求最大值和次大值分别需要5clk延时,总延迟会超过10个cycles;资源占用较高,维持最大值坐标和清零操作耗费了较多资源,同时为了计算次大值,需要将输入寄存若干个周期,寄存器消耗较多。

另一个种思路考虑同时求最大值和次大值,由于这一逻辑较为复杂,可以将其流水化,如下图。(以8输入为例,32输入需要增加两级)

其中sort模块完成对4输入进行排序,得到最大值和次大值输出的功能。4个数的排序较为复杂,这一过程大概需要2-3个cycles完成。对于32输入而言,输入数据经过32-16-8-4-2输出得到结果,延迟大概也有10个周期。

2. 分治

如果需要在FPGA上实现一个特定的算法,那么去找一个合适的方法去实现就好了;但如果是要实现一个特定的功能,那么需要找一个优秀的且适合FPGA实现的方法

求最大值和次大值是一个很不完全的排序,通过简单的查找复杂度为O(2n),且不利于硬件实现。对于排序而言,无论快速排序或者归并排序都用了分治的思想,如果我们试图用分治的思想来解决这一问题。考虑当只有2个输入时,通过一个比较就可以得到输出,此时得到的是一个长度为2的有序数组。如果两个有序数组,那么通过两次比较就可以得到最大值和次大值。采用归并排序的思想,查找最大值和次大值的复杂度为O(1.5n)(即为n/2+n/2+n/4… ,不知道有没有算错)。采用归并排序的思想,从算法时间复杂度上看更为高效了。

那么这一方案是否适合FPGA实现呢,答案是肯定的。分治的局部性适合FPGA的流水实现,框图如下。(以8输入为例,32输入需要增加两级)

其中meg模块内部有两级的比较器,一般而言1clk就可以完成,输入数据经过32-32-16-8-4-2得到结果,延迟为5个时钟周期。实现代码如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
module test#(
parameter DW = 8
)
(
input clk,
input [32*DW-1 :0] din,
output [DW-1:0] max1,
output [DW-1:0] max2
);

wire[DW-1:0] d[31:0];
generate
    genvar i;
    for(i=0;i<32;i=i+1)
    begin:loop_assign
        assign d[i] = din[DW*i+DW-1:DW*i];
    end
endgenerate

// stage 1,comp
reg[DW-1:0] s1_max[15:0];
reg[DW-1:0] s1_min[15:0];
generate
    for(i=0;i<16;i=i+1)
    begin:loop_comp
        always@(posedge clk)
            if(d[2*i]>d[2*i+1])begin
                s1_max[i] <= d[2*i];
                s1_min[i] <= d[2*i+1];
            end
            else begin
                s1_max[i] <= d[2*i+1];
                s1_min[i] <= d[2*i];        
            end
    end
endgenerate

// stage 2,
wire[DW-1:0] s2_max[7:0];
wire[DW-1:0] s2_min[7:0];
generate
    for(i=0;i<8;i=i+1)
    begin:loop_megs2
        meg u_s2meg(
            .clk(clk),
            .g1_max(s1_max[2*i]),
            .g1_min(s1_min[2*i]),
            .g2_max(s1_max[2*i+1]),
            .g2_min(s1_min[2*i+1]),            
            .max1(s2_max[i]),
            .max2(s2_min[i])
        );
    end
endgenerate
// stage 3,
wire[DW-1:0] s3_max[3:0];
wire[DW-1:0] s3_min[3:0];
generate
    for(i=0;i<4;i=i+1)
    begin:loop_megs3
        meg u_s3meg(
            .clk(clk),
            .g1_max(s2_max[2*i]),
            .g1_min(s2_min[2*i]),
            .g2_max(s2_max[2*i+1]),
            .g2_min(s2_min[2*i+1]),            
            .max1(s3_max[i]),
            .max2(s3_min[i])
        );
    end
endgenerate

// stage 4,
wire[DW-1:0] s4_max[1:0];
wire[DW-1:0] s4_min[1:0];
generate
    for(i=0;i<2;i=i+1)
    begin:loop_megs4
        meg u_s4meg(
            .clk(clk),
            .g1_max(s3_max[2*i]),
            .g1_min(s3_min[2*i]),
            .g2_max(s3_max[2*i+1]),
            .g2_min(s3_min[2*i+1]),            
            .max1(s4_max[i]),
            .max2(s4_min[i])
        );
    end
endgenerate

// stage 5,
meg u_s5meg(
    .clk(clk),
    .g1_max(s4_max[0]),
    .g1_min(s4_min[0]),
    .g2_max(s4_max[1]),
    .g2_min(s4_min[1]),            
    .max1(max1),
    .max2(max2)
);
endmodule

module meg#(
parameter DW = 8
)
(
input clk,
input [DW-1 :0] g1_max,
input [DW-1 :0] g1_min,
input [DW-1 :0] g2_max,
input [DW-1 :0] g2_min,
output reg [DW-1:0] max1,
output reg [DW-1:0] max2
);
always@(posedge clk)
begin
    if(g1_max>g2_max) begin
        max1 <= g1_max;
        if(g2_max>g1_min)
            max2 <= g2_max;
        else
            max2 <= g1_min;
    end
    else begin
        max1 <= g2_max;
        if(g1_max>g2_min)
            max2 <= g1_max;
        else
            max2 <= g2_min;
    end
end
endmodule

3. 其他

简单测试了上面的代码,在上一代器件上(20nm FPGA),8bit数据输入模块能综合到很高的频率,逻辑级数大概是5级左右,对于整个工程而言瓶颈基本不会出现在这一部分。32bit数据输入由于数据位宽太大,频率不会太高,但是通过将meg模块做一级流水,也几乎不会成为整个系统的瓶颈。

32bit32输入情况下,数据输入位宽为1024(不是IO输入,是内部信号)。之前在通信/数字信号处理方面可能不会用到这么大位宽的数据,但对于AI领域FPGA的应用,数千比特的输入应该是很平常的,这的确会影响最终FPGA上实现的效果。要想让机器学习算法在FPGA上跑得更好,还需要算法和FPGA共同努力才是。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-01-30 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
呜呜祖啦滤波器FPGA实现
大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分。大侠可以关注FPGA技术江湖,在“闯荡江湖”、"行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢。
FPGA技术江湖
2020/12/29
7690
呜呜祖啦滤波器FPGA实现
基于FPGA的均值滤波(三)
基于FPGA的均值滤波(三) 之二维求和模块 在实现了窗口内一维行方向上的求和操作,现在要得到整个窗口内的像素之和,还必须将每一行的计算结果再叠加起来。但是每一行的计算结果就不可以使用上面的增量更新的
瓜大三哥
2018/02/26
9620
基于FPGA的均值滤波(三)
荐读:FPGA设计经验之图像处理
大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分。大侠可以关注FPGA技术江湖,在“闯荡江湖”、"行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢。
FPGA技术江湖
2020/12/29
1.4K0
荐读:FPGA设计经验之图像处理
有趣的算法(十一) ——分治法:快速​求最值
有趣的算法(十一)——分治法:快速求最值 (原创内容,转载请注明来源,谢谢) 一、需求 一个数组,里面有若干的数字,现需要得到这一组数字的最大值和最小值。 二、简单分析 最基本的做法,是两两比对,可以区分出临时的最大值和最小值,再拿临时的最大值和最小值往后比较,有新的最值则更新。总的需要的比较次数是2n-2。 三、优化 使用分治法快速求最值。即把数组分到最小的1-2个数,两两比较后,仅将最大值和最小值回传,再两两比较最值,回传新的最值,最终得出最大值和最小值。 分析需要比较的次数。当数组只有1个数时,
用户1327360
2018/03/07
1.7K0
leetcode-747-Largest Number At Least Twice of Others(求vector的最大值和次大值)
题目描述: In a given integer array nums, there is always exactly one largest element. Find whether the largest element in the array is at least twice as much as every other number in the array. If it is, return the index of the largest element, otherwise retur
chenjx85
2018/05/21
6980
基于FPGA灰度图像的形态学腐蚀
数学形态学是一门建立在集论基础上的学科,是几何形态学分析和描述的有力工具。数学形态学的蓬勃发展,其并行快速,易于硬件实现,目前已经在计算机视觉、信号处理与图像分析、模式识别等方面得到了极为广泛的应用。
FPGA开源工作室
2019/10/29
9190
基于FPGA灰度图像的形态学腐蚀
形态学滤波(五)
形态学滤波(五) 之一维形态学腐蚀/膨胀子模块设计 对于图像处理而言,是纵向和横向两个维度的处理。我们知道,对于任何二维的操作,都可以分解为一维方向的操作来简化来设计。在图像处理中,习惯是首先横向处理,然后纵向处理。所谓横向处理就是对每一行进行处理。对于尺寸nxn的处理窗口可以采用一个1xn的窗体从图像第一行第一列开始,自左向右滑动,依次取出窗口内的n个限售股灰度值,比较得到灰度最小值或者最大值并按顺序存储。 以处理窗口尺寸为5说明,要完成5个数据的比较,可以在一个时钟完成两对数据的比较,第二个时钟完成上述
瓜大三哥
2018/02/26
6140
形态学滤波(五)
基于FPGA的均值滤波(二)
基于FPGA的均值滤波(二) 之一维求和模块 均值滤波按照整体设计可以分为以下几个子模块: (1)一维求和模块,这里记为sum_1D; (2)二维求和模块,这里记为sum_2D; (3)除法转换模块,此模块比较简单,一般情况下不进行模块封装。 (4)行缓存电路实现行列间像素对齐。 整个顶层模块调用sum_2D模块和除法转换电路求取平均值,记为mean_2D。 用FPGA来求和是最简单的事情,所要注意的是求和结果不要溢出。一般情况下,2个位宽为DW的数据想家,至少得用一个DW+1位宽的数据来存放。 假设窗口尺
瓜大三哥
2018/02/26
1.5K0
基于FPGA的均值滤波(二)
基于FPGA的自动白平衡算法的实现(附代码)
对于白平衡基本概念的详细介绍请查看文章《白平衡初探》,白平衡算法主要的作用是将偏暖或者偏冷的色调自动恢复到正常色调,是图像看起来更加色彩饱满正常。
FPGA技术江湖
2021/04/07
2K0
基于FPGA的自动白平衡算法的实现(附代码)
图像分割(五)
图像分割(五) 之基于FPGA的局部自适应分割 子模块设计 数据累加模块add_tree 数据累加模块负责将窗口内所有元素与均值之差的平方相加,这里还是采用以前的加法思路:每个加法器限制两个输入,这样
瓜大三哥
2018/02/26
6620
图像分割(五)
基于FPGA的非线性滤波器(三)
基于FPGA的非线性滤波器(三) 之并行全比较排序模块设计 由于排序运算在图像的行列方向上是同性的,因此,同时考虑首先进行一维图像方向上的排序,再对列方向上的行排序结果进行排序,即可得到一个窗口内的排序结果。 一维方向的排序运算模块,记为sort_1d。同样地,对于最终的二维排序运算模块,记为sort_2d。 1.sort_1d模块设计 计算步骤如下: (1)首先得到待排序的n个数据:这可以通过将数据流打n-1拍实现。 (2)进行全比较:当前数据与其他所有一次进行比较,并记录比较结果,比较的过程需先考虑输入
瓜大三哥
2018/02/26
7450
基于FPGA的非线性滤波器(三)
基于FPGA的直方图拉伸
在视频处理中,为了能够实时调节图像的对比对,通常需要对直方图进行拉伸处理。直方图拉伸是指将图像灰度直方图较窄的灰度级区间向两端拉伸,增强整幅图像像素的灰度级对比度,达到增强图像的效果。
FPGA开源工作室
2019/12/18
1.2K0
形态学滤波(六)
形态学滤波(六) 之二维形态学腐蚀/膨胀子模块设计 按照二维扩展的思路,将每一行的一维算子的计算结果对齐在列方向上再进行一维运算,得到的结果即是二维运算结果。 上面的结构图中涉及到: (1)minma
瓜大三哥
2018/02/26
7300
形态学滤波(六)
题解 | Verilog刷题解析及对应笔试面试注意点【6-9】(涉及==和===、for展开问题等)
目的:不仅仅是解题,更多的是想从真实的FPGA和数字IC实习秋招和实际工程应用角度,解读一些【笔试面试】所注意的知识点,做了一些扩展。
FPGA探索者
2022/05/26
1.3K0
题解 | Verilog刷题解析及对应笔试面试注意点【6-9】(涉及==和===、for展开问题等)
谁能想到,求最值的算法还能优化?
今天主要来聊两个问题:给一个数组,如何同时求出最大值和最小值,如何同时求出最大值和第二大值?
labuladong
2021/09/23
8650
LeetCode 第 18 场双周赛(188/587,前32%)
题目链接 给你一个整数数组 arr ,请你将数组中的每个元素替换为它们排序后的序号。
Michael阿明
2020/07/13
7590
LeetCode 第 18 场双周赛(188/587,前32%)
求最大最小值,最少要进行多少次比较? | 经典面试题
(3)两个子数组的最大值里再取最大值,两个子数组的最小值里再取最小值,就是最终解;
架构师之路
2021/11/10
9580
图像分割(四)
图像分割(四) 之基于FPGA的局部自适应分割 子模块设计 窗口缓存模块win_buf 本模块不做任何算法上的处理,只是负责将当前输入像素的二维窗口元素缓存并组成一个一维的向量输出。 模块的构建非常简
瓜大三哥
2018/02/26
8660
图像分割(四)
Greedy & Violent
1 2 3//坐标缩小后就可以更方便的选择 double pos = (double)i / n * (n + m);//原来雕像的位置 ans += fabs(pos - floor(pos + 0.5))/(n + m);//*n+m后就选四舍五入最近的
radaren
2018/08/28
5580
影响FPGA时序的进位链(Carry Chain), 你用对了么??
在FPGA中我们写的最多的逻辑是什么?相信对大部分朋友来说应该都是计数器,从最初板卡的测试时我们会闪烁LED,到复杂的AXI总线中产生地址或者last等信号,都会用到计数器,使用计数器那必然会用到进位链。
猫叔Rex
2020/06/29
2.1K0
相关推荐
呜呜祖啦滤波器FPGA实现
更多 >
领券
💥开发者 MCP广场重磅上线!
精选全网热门MCP server,让你的AI更好用 🚀
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验