分享

基于FPGA的几种排序算法

 追逐四叶 2018-06-19

        最近笔者在项目中正好遇到需要排序的情况,以前刚接触C语言的时候排序的方法主要有冒泡排序、选择排序等方法;于是就用Verilog实现了冒泡法,但是发现此方法和选择排序法需要的时间周期太长,比如16个数据差不多需要136个周期才能完成排序,于是笔者在网上找到了并行全比较排序法和改进的串行全比较排序法;以下将一一介绍。

1      冒泡法和比较排序法


1.1        算法原理

这两种方法比较容易理解,下面给出两种排序方法的百科连接,大家可以自行百度。

冒泡排序:

https://baike.baidu.com/item/%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F


选择排序:

https://baike.baidu.com/item/%E9%80%89%E6%8B%A9%E6%8E%92%E5%BA%8F%E6%B3%95/2304587?fr=aladdin

1.2        仿真结果

        下面给出冒泡法的Verilog仿真结果,蓝色箭头是输入的原始数据,红色箭头所指的是排完序的数据,黄色箭头指的是排序所用的时间,仿真用的是100MHz的始终,所以冒泡总共需要136个周期。


1.3        算法优缺点

    选择排序法和冒泡法属于传统的两两比较的算法,但是消耗的周期比较长,在一些对实时性要求较高的情况下无法满足要求。

2      并行全比较排序法

2.1        算法原理及Verilog实现

        传统的排序方式是以两两之间顺序比较为基础,而并行全比较实时排序算法是基于序列中任意两个数并行比较实现。由于从原来的串行比较变成了并行比较,所以需要消耗比以前多的比较器,诠释了FPGA中“用面积换速度”的思想。以序列长度为m的数据为例:

A:    第一个时钟周期:将其中一个数据和其他数据在一个周期中一一比较,比较器分三种情况:

    1.        这个数据大于其他数据,则它的得分为0;

    2.        这个数据等于其他数据,若它在这个序列中比和它相等的其他数据靠前,则它的得分为0,反之为1;

    3.        这个数据小于其他数据,则它的得分为1;

  1. //--------------------------            计算得分        ----------------------------------  
  2.     generate  
  3.         genvar i;  
  4.             for(i=1;i<=COL_WIDTH;i=i 1)  
  5.                 begin:unfold_i  
  6.                     always@(posedge clk or  posedge rst)  
  7.                         begin  
  8.                             if(rst)  
  9.                                 begin     
  10.                                     comp_valid_i[i] <= 0;  
  11.                                     score_i[i] <= 0;   
  12.                                 end  
  13.                             else    if(comp_valid_f)  
  14.                                 begin  
  15.                                     if(comp_data[col] > comp_data[i])  
  16.                                         score_i[i] <= 0;  
  17.                                     else    if(comp_data[col] == comp_data[i])  
  18.                                         begin  
  19.                                             if(col <= i) //如果有两个相同的数据,其中若输入顺序在前面则排序就靠前  
  20.                                                 score_i[i] <= 0;  
  21.                                             else  
  22.                                                 score_i[i] <= 1;  
  23.                                         end  
  24.                                     else      
  25.                                         score_i[i] <= 1;  
  26.                                     comp_valid_i[i] <= comp_valid_f;  
  27.                                 end  
  28.                             else  
  29.                                 begin  
  30.                                     score_i[i] <= 0;  
  31.                                     comp_valid_i[i] <= 0;  
  32.                                 end  
  33.                         end  
  34.                 end  
  35.     endgenerate  
//-------------------------- 计算得分 ---------------------------------- generate genvar i; for(i=1;i<=COL_WIDTH;i=i 1) begin:unfold_i always@(posedge clk or posedge rst) begin if(rst) begin comp_valid_i[i] <= 0; score_i[i] <= 0; end else if(comp_valid_f) begin if(comp_data[col] > comp_data[i]) score_i[i] <= 0; else if(comp_data[col] == comp_data[i]) begin if(col <= i) //如果有两个相同的数据,其中若输入顺序在前面则排序就靠前 score_i[i] <= 0; else score_i[i] <= 1; end else score_i[i] <= 1; comp_valid_i[i] <= comp_valid_f; end else begin score_i[i] <= 0; comp_valid_i[i] <= 0; end end end endgenerate


B:第二个时钟周期:将每个数据和其他数据比较后的数据累加;

  1.     always@(posedge clk or posedge  rst)  
  2.         begin  
  3.             if(rst)  
  4.                 begin  
  5.                     score <= 0;  
  6. //                  score_t[1]<=0;  
  7. //                  score_t[2]<=0;  
  8.                     location <= 0;  
  9.                     add_cnt <= 0;  
  10.                     data_o <= 0;  
  11.                     valid_o <= 0;  
  12.                 end  
  13.             else    if(comp_valid_j[1] == 1)  
  14.                 begin  
  15.                     add_cnt <= 0;  
  16. //                  add_cnt <= 1;  
  17. //                  score_t[1] <= ( score_j[1] score_j[2] )   ( score_j[3] score_j[4] );  
  18. //                  score_t[2] <= ( score_j[5] score_j[6] )   ( score_j[7] score_j[8] );  
  19.                     score    <= ( ( score_j[1] score_j[2] )   ( score_j[3] score_j[4] ) )   
  20.                                  ( ( score_j[5] score_j[6] )   ( score_j[7] score_j[8] ) )   
  21.                                  ( ( score_j[9] score_j[10] )   ( score_j[11] score_j[12] ) )   
  22.                                  ( ( score_j[13] score_j[14] )   ( score_j[15] score_j[16] ) )  1;  
  23.                     location <= {row[7:0],col[7:0]};//行,列  
  24.                     data_o <= comp_data[col];//数据  
  25.                     valid_o <= 1;  
  26.                 end  
  27. end  
always@(posedge clk or posedge rst) begin if(rst) begin score <= 0; // score_t[1]<=0; // score_t[2]<=0; location <= 0; add_cnt <= 0; data_o <= 0; valid_o <= 0; end else if(comp_valid_j[1] == 1) begin add_cnt <= 0; // add_cnt <= 1; // score_t[1] <= ( score_j[1] score_j[2] ) ( score_j[3] score_j[4] ); // score_t[2] <= ( score_j[5] score_j[6] ) ( score_j[7] score_j[8] ); score <= ( ( score_j[1] score_j[2] ) ( score_j[3] score_j[4] ) ) ( ( score_j[5] score_j[6] ) ( score_j[7] score_j[8] ) ) ( ( score_j[9] score_j[10] ) ( score_j[11] score_j[12] ) ) ( ( score_j[13] score_j[14] ) ( score_j[15] score_j[16] ) ) 1; location <= {row[7:0],col[7:0]};//行,列 data_o <= comp_data[col];//数据 valid_o <= 1; end end

C: 第三个时钟周期:将每个数据根据自己的得分赋值给新的数组(若得分为1的就赋值给数组中的第一个数,2就赋值给新的数组中第二个数);

  1. //--------------------------------------------------------------------------------  
  2.     always@(posedge clk or posedge  rst)  
  3.         begin  
  4.             if(rst)  
  5.                 begin  
  6.                     sort_done <= 0; //数据的坐标    
  7.                 end  
  8.             else    if(valid_o[1])  
  9.                 begin  
  10.                     chn[score[1]] <= data_o[1]; //重新排列的数据      
  11.                     chn[score[2]] <= data_o[2]; //重新排列的数据  
  12.                     chn[score[3]] <= data_o[3]; //重新排列的数据  
  13.                     chn[score[4]] <= data_o[4]; //重新排列的数据  
  14.                     chn[score[5]] <= data_o[5]; //重新排列的数据  
  15.                     chn[score[6]] <= data_o[6]; //重新排列的数据  
  16.                     chn[score[7]] <= data_o[7]; //重新排列的数据  
  17.                     chn[score[8]] <= data_o[8]; //重新排列的数据  
  18.                     chn[score[9]] <= data_o[9]; //重新排列的数据  
  19.                     chn[score[10]] <= data_o[10]; //重新排列的数据  
  20.                     chn[score[11]] <= data_o[11]; //重新排列的数据  
  21.                     chn[score[12]] <= data_o[12]; //重新排列的数据  
  22.                     chn[score[13]] <= data_o[13]; //重新排列的数据  
  23.                     chn[score[14]] <= data_o[14]; //重新排列的数据  
  24.                     chn[score[15]] <= data_o[15]; //重新排列的数据  
  25.                     chn[score[16]] <= data_o[16]; //重新排列的数据  
  26.                     sort_done <= 1;  
  27.                 end  
  28.             else  
  29.                 begin  
  30.                     sort_done <= 0;  
  31.                 end  
  32.         end   
//-------------------------------------------------------------------------------- always@(posedge clk or posedge rst) begin if(rst) begin sort_done <= 0; //数据的坐标 end else if(valid_o[1]) begin chn[score[1]] <= data_o[1]; //重新排列的数据 chn[score[2]] <= data_o[2]; //重新排列的数据 chn[score[3]] <= data_o[3]; //重新排列的数据 chn[score[4]] <= data_o[4]; //重新排列的数据 chn[score[5]] <= data_o[5]; //重新排列的数据 chn[score[6]] <= data_o[6]; //重新排列的数据 chn[score[7]] <= data_o[7]; //重新排列的数据 chn[score[8]] <= data_o[8]; //重新排列的数据 chn[score[9]] <= data_o[9]; //重新排列的数据 chn[score[10]] <= data_o[10]; //重新排列的数据 chn[score[11]] <= data_o[11]; //重新排列的数据 chn[score[12]] <= data_o[12]; //重新排列的数据 chn[score[13]] <= data_o[13]; //重新排列的数据 chn[score[14]] <= data_o[14]; //重新排列的数据 chn[score[15]] <= data_o[15]; //重新排列的数据 chn[score[16]] <= data_o[16]; //重新排列的数据 sort_done <= 1; end else begin sort_done <= 0; end end

D:  第四个时钟周期:将新数组输出;

经过以上四个步骤,即可将算法完成。

2.2        仿真结果

        如上图所示,红色箭头代表着输入数据,其中红色的圈标记出输入相同的两个数据;黄色箭头为每个数据与别的数据比较后的得分,得分越小则代表这个数据越大,其中黄色部分是第一个数据和第二个数据的得分,这两个数据的大小是一样的,但是第一个数据输入数组中比第二个数据靠前,所以它的得分低一分,这也验证了我们设计逻辑上在是正确的;蓝色箭头则代表的是排完序的输出序列,经观察发现结果正确。

2.3    算法优缺点

l  优点:并行比较排序方式在实时性上有明显的优势,只需要四个时钟周期就可以排序完成;

l  缺点:

        1.        由于是并行比较消耗了较多的资源,而且在第二个时钟周期(得分累加)需要大量的加法器级联,考虑到路径延迟、建立保持时间和时钟抖动,一个时钟周期许多个加法器级联会有问题;

        2.        在代码可移植性方面也有欠缺,比如若序列大小改变,在第二个和第三个时钟周期的时候就需要人为修改多处代码;

3      串行全比较排序法

3.1        算法原理及Verilog实现

        串行全比较排序法在并行全比较排序法做了一些改进,将原来并行全比较排序法的前三个周期由并行转变为串行,但是可以在比较的同时将得分累加,所以串行全比较排序法排序需要的周期是2*m(m个序列)个周期。

  1. //--------------------------------------------------------------------------------  
  2.     always@(posedge clk or posedge  rst)  
  3.         begin  
  4.             if(rst)  
  5.                 begin  
  6.                     comp_cnt <= COL_WIDTH   1;  
  7.                     score <= 1;  
  8.                     valid_o <=   0;  
  9.                 end  
  10.             else    if(comp_cnt <= COL_WIDTH)  
  11.                 begin  
  12.                     comp_cnt <= comp_cnt   1;  
  13.                       
  14.                     if(comp_data[col] > comp_data[comp_cnt])  
  15.                         score <= score;  
  16.                     else    if(comp_data[col] == comp_data[comp_cnt])  
  17.                         begin  
  18.                             if(col <= comp_cnt)  
  19.                                 score <= score;  
  20.                             else  
  21.                                 score <= score   1;  
  22.                         end  
  23.                     else          
  24.                         score <= score   1;  
  25.                       
  26.                     if(comp_cnt == COL_WIDTH)   valid_o <=   1;  
  27.                     else        valid_o <=   0;  
  28.                 end  
  29.             else    if(valid_r)  
  30.                 begin  
  31.                     comp_cnt <= 1;  
  32.                     score <= 1;  
  33.                     valid_o <=   0;  
  34.                 end  
  35.             else  
  36.                 begin  
  37.                     comp_cnt <= comp_cnt;  
  38.                     score <= score;  
  39.                     valid_o <=   0;  
  40.                 end  
  41.         end  
  42. //--------------------------------------------------------------------------------  
//-------------------------------------------------------------------------------- always@(posedge clk or posedge rst) begin if(rst) begin comp_cnt <= COL_WIDTH 1; score <= 1; valid_o <= 0; end else if(comp_cnt <= COL_WIDTH) begin comp_cnt <= comp_cnt 1; if(comp_data[col] > comp_data[comp_cnt]) score <= score; else if(comp_data[col] == comp_data[comp_cnt]) begin if(col <= comp_cnt) score <= score; else score <= score 1; end else score <= score 1; if(comp_cnt == COL_WIDTH) valid_o <= 1; else valid_o <= 0; end else if(valid_r) begin comp_cnt <= 1; score <= 1; valid_o <= 0; end else begin comp_cnt <= comp_cnt; score <= score; valid_o <= 0; end end //--------------------------------------------------------------------------------
  1. //--------------------------------------------------------------------------------  
  2.     always@(posedge clk or posedge  rst)  
  3.         begin  
  4.             if(rst)  
  5.                 begin  
  6.                     sort_count <= COL_WIDTH   1;  
  7.                     sort_done <= 0;  
  8.                 end  
  9.             else    if(sort_count <= COL_WIDTH)  
  10.                 begin  
  11.                     sort_count <= sort_count   1;  
  12.                     comp_data_index[score[sort_count]] <= comp_data[sort_count]; //这边改成DPR_RAM  
  13.                     if(sort_count == COL_WIDTH)  
  14.                         sort_done <= 1;  
  15.                     else          
  16.                         sort_done <= 0;  
  17.                 end  
  18.             else    if(valid_o[1])  
  19.                 begin  
  20.                     sort_count <= 1;  
  21.                     sort_done <= 0;  
  22.                 end  
  23.             else  
  24.                 begin  
  25.                     sort_count <= sort_count;  
  26.                     sort_done <= 0;  
  27.                 end  
  28.         end  
  29. //--------------------------------------------------------------------------------  
//-------------------------------------------------------------------------------- always@(posedge clk or posedge rst) begin if(rst) begin sort_count <= COL_WIDTH 1; sort_done <= 0; end else if(sort_count <= COL_WIDTH) begin sort_count <= sort_count 1; comp_data_index[score[sort_count]] <= comp_data[sort_count]; //这边改成DPR_RAM if(sort_count == COL_WIDTH) sort_done <= 1; else sort_done <= 0; end else if(valid_o[1]) begin sort_count <= 1; sort_done <= 0; end else begin sort_count <= sort_count; sort_done <= 0; end end //--------------------------------------------------------------------------------

3.2       仿真结果



        如上图所示,红色箭头代表着输入数据;黄色箭头为每个数据与别的数据比较后的得分;蓝色箭头则代表的是排完序的输出序列,经观察发现结果正确。

        从图中可以读出排序需要的时间是330ns,由于仿真采用的时钟是100MHz,所以串行全比较算法需要33个时钟周期(大约2*n个周期)。

3.3       算法优缺点

串行全比较算法和并行全比较算法比较:

l  优点:

        1.        资源消耗的比较少;

        2.        代码可移植性好,序列变化只需要改变几个参数,不需要大规模修改代码;

l  缺点:

        串行全比较算法所消耗的时间比并行全比较算法长。

2      总结

l  代码可移植性:传统串行排序算法>串行全比较排序法>并行全比较排序法

l  资源使用:传统串行排序算法<串行全比较排序法<并行全比较排序法

l  排序时间:并行全比较排序法<串行全比较排序法<传统串行排序算法

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多