FPGA实现排序

来源:互联网 发布:cisco端口看环路 编辑:程序博客网 时间:2024/06/06 17:36

github位置
https://github.com/C-L-G/sort
本文所用的算法很简单,以升序来说,有一数列[A0…..An],分别比较相邻的两两个元素,如果A(n) > A(n+1),则交换两个数的位置

举个例子:排序 序列
[ 4 3 2 1]
第一次比较分组
{A0–A1};{A2–A3}: {4 > 3};{2>1}
第一次交换位置后
[ 3 4 1 2]
第二次比较分组
{A0};{A1-A2};{A3}:{3};{4>1};{2}
当然不能和第一次一样分组,那样就是原地踏步
第二次交换位置后
[ 3 1 4 2 ]
第三次比较分组
{A0–A1};{A2–A3}:{3 > 1};{4 > 2}
其实是和第一次一样,这个很重要,意味着可以用迭代的方法
第三次交换位置后
[1 3 2 4]
第四次比较分组
{A0};{A1-A2};{A3}:{1};{3>2};{4}
和第二次一样
第四次交换位置后
[ 1 2 3 4 ]
排序完成
算法需要4个clock,对4个,不是8个
此算法处理N长度的序列就需要N个clock,序列元素可以同值。
上面所有的“两两“交换也是http://opencores.org/的实现方式。
我要用更激进的方法~~”四四“分组,组内一次排4个数
序列
[A0,A1,A2,A3…….A(n-2),A(n-1),A(n)]
第一次分组
{A1,A2,A3,A4};{A5->A8}…..{A(n-3),A(n-2),A(n-1),A(n)};
第二次分组
{A1-A2};{A3,A4,A5,A6}…..{A(n-5)->A(n-2};{A(n-1),A(n)};
循环第一第二次。

此方法有个缺点,会有冗余的比较,例如第二次分组的时候A3,A4顺序是已经排过的,但是没关系,并不影响结果。
实现思路:

4个数分组排序是核心,如何实现4个数分组排序呢,且要在尽可能少的clock内完成,最好只用一个clock,如果用4个clock,就和上面的方式一样了。
假设排序前的序列是[A0,A1,A2,A3] ,排好序后的序列是[B0,B1,B2,B3]
A序列两两比较( >= :大于或等于),共有6种结果,我们把结果用一个的向量wire[5:0] C表示便是 6‘bXXX_XXX(本人使用的是verilog)

C[5] = A0>=A1;
C[4] = A0>=A2;
C[3] = A0>=A3;
C[2] = A1>=A2;
C[1] = A1>=A3;
C[0] = A2>=A3;

那么,B的排序应该就是由C决定的 B=F(C);
B的值又是怎么决定的呢?
1、B[0]

如果A种的某个元素Ax不小于其他3个,那么我们就可以把它存入B[0]中。如果A0>{A1,A2,A3},那么就把A0赋予B0,
verilog实现:

always@(posedge clock)begin
casex(C)
6’b111_xxx: B[0]<= A[0];
6’b0xx_11x: B[0]<= A[1];
6’bx0x_0x1: B[0]<= A[2];
6’bxx0_x00: B[0]<= A[3];
default: B[0]<= A[0];
endcase
end

为什么case 有些状态有0?
对于A[0]来说 它比其他三个数大即 6‘b111_xxxx:
但是对于A[1] ,他要是最大必须是 6’b0xx_11x; C[5] 对于的是A[0] >= A[1], “0” 表示此式子不成立!
2、B[3]

和B[0] 类似,就不细说了
3、B[1],B[2]

B[1]:Ax对其他3个数中,比一个小,比两个大
B[2]:Ax对其他3个数中,比两个小,比一个大
情况比上两种复杂

always@(posedge clock)begin
casex(C)
6’b011_xxx,6’b101_xxx,6’b110_xxx: B[1]<= A[0]; //D0-01-d1 D0-01-d2 D0-01-d3
6’b1xx_11x,6’b0xx_01x,6’b0xx_10x: B[1]<= A[1]; //d0-01-D1 D1-01-d2 D1-01-d3
6’bx1x_0x1,6’bx0x_1x1,6’bx0x_0x0: B[1]<= A[2]; //d0-01-D2 d1-01-D2 D2-01-d3
6’bxx1_x00,6’bxx0_x10,6’bxx0_x01: B[1]<= A[3]; //d0-01-D3 d1-01-D3 d2-01-D3
default: B[1]<= A[0];
endcase

end

always@(posedge clock)begin
casex(C)
6’b001_xxx,6’b100_xxx,6’b010_xxx: B[2]<= A[0]; //D0-01-d1 D0-01-d2 D0-01-d3
6’b1xx_01x,6’b0xx_00x,6’b1xx_10x: B[2]<= A[1]; //d0-01-D1 D1-01-d2 D1-01-d3
6’bx1x_1x1,6’bx0x_1x0,6’bx1x_0x0: B[2]<= A[2]; //d0-01-D2 d1-01-D2 D2-01-d3
6’bxx1_x10,6’bxx0_x11,6’bxx1_x01: B[2]<= A[3]; //d0-01-D3 d1-01-D3 d2-01-D3
default: B[2]<= A[0];
endcase

end

OK,”四四说完了“

组装N个序列了

组装一个25的看看,为什么是25,25不仅不是偶数,更不是4的倍数。haha~,25是5x5,排序25,就是直接做了一个5x5的中值滤波器!
25的分组,用6个“四”的排序,余1就不比较了,github上我是有用“三”的排序。

奇数次分组
{A0->A3} {A4->A7} {A8->A11} {A12-A15} {A16->A19} {A20->A23} {A24}
偶数次分组
{A0} {A1} {A2->A5} {A6->A9} {A10->A13} {A14->A17} {A18->A21} {A22-A24}

只要迭代6次(github上的可不是迭代,纯流水实现),共排序13次(因为不够4除)就能实现25的排序,比传统方法快一倍,流水资源自然少一半。

FINISH:

既然多个数分组排序能提高速度和资源,为什么选4个而不是8个,16个呢,这样不是更快吗?
是的,组内数越多,迭代次数越少
如果把[A0…An] 只做成一个组,那么只要一个clock就行了。
时序,数越多,路径延时越长

排4的时候是
case((3_+2+1)’bxxxxxx)

排8的时候就是
case((7+6+5+4+2+1)’dx)=(case(sum(#8))= case(25’dx)
2^6 = 64种情况

排25的时候
2^25 = 33554432 放弃吧

0 0