Verilog/C++实现排序算法

1、冒泡排序算法

冒泡排序是一种简单的交换类排序。

冒泡排序算法的原理如下:

  • 1、比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  • 2、对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  • 3、针对所有的元素重复以上的步骤,除了最后一个。
  • 4、持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

  

Verilog/C++实现排序算法_算法

 

        冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,是不会再交换的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。

1.1、C++实现代码如下:

//交换 a 和 b 的位置
void swap(int *a, int *b)
{
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}
//冒泡排序实现函数,从输出结果,可以看到冒泡的具体实现流程
void BubSort_test(int *a, int N) 
{
    for (int i = 0; i < N; i++) 
    {
        //对待排序序列进行冒泡排序
        for (int j = 0; j + 1 < N - i; j++) 
        {
            //相邻元素进行比较,当顺序不正确时,交换位置
            if (a[j] > a[j + 1]) 
            {
                swap(&a[j], &a[j + 1]);
            }
        }
        /*///-------输出本轮冒泡排序之后的序列----------
        printf("第%d轮冒泡排序:", i + 1);
        for (int i = 0; i < N; i++) 
        {
            printf("%d ", a[i]);
        }
        printf("\n");
        -------输出本轮冒泡排序之后的序列----------///*/
    }
}

1.2、Verilog实现代码如下:

1.2.1、冒泡排序代码:
`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/06/19 21:28:28
// Design Name: 
// Module Name: bubbling
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//

//冒泡排序
module bubbling	#( 	
        parameter         ROW_WIDTH = 8, //行
        parameter         COL_WIDTH = 16 //列
       )
    (
    input  wire           clk,
    input  wire           rst,
    	
    input  [15:0]         comp_data_i, //数据
    input                 valid_i      //数据有效
    );

//---------------------------------------------------
bubbling_sort  u_bubbling_sort(
    .clk               ( clk            ), 
    .rst               ( rst            ), 
			
    .comp_data_i       ( comp_data_i    ), //输入数据
    .valid_i           ( valid_i        ), //数据有效
    .sort_done         ( sort_done      )  //排序完成
);

endmodule
1.2.2、冒泡排序核心代码:
`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/06/19 21:32:23
// Design Name: 
// Module Name: bubbling_sort
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module bubbling_sort #(
      parameter           ROW_WIDTH = 8, //行
      parameter           COL_WIDTH = 16 //列
      )
    (
     input  wire               clk,
     input  wire               rst,
     input  wire  [15:0]       comp_data_i, //数据
     input  wire               valid_i,     //数据有效
     
     output  reg               sort_done    //排序完成
    );
    
//reg define
reg              comp_valid_f;
reg  [15:0]      comp_data      [COL_WIDTH:1];
reg  [7:0]       count;
reg  [7:0]       count_i;
reg  [7:0]       count_j;

//***********************************************
//**        读取数据
//***********************************************
always@(posedge	clk	or posedge	rst) begin
    if(rst) begin
        comp_valid_f <= 0;
        count <= 1;
    end
    else if(valid_i) begin
        comp_data[count] <= comp_data_i;
        count <= count + 1;
        if( count==COL_WIDTH )
            comp_valid_f <= 1;
        else
            comp_valid_f <= 0;
    end
    else begin
        comp_valid_f <= 0; 
        count <= 1;
    end
end

//-----------------------------------------------
always@(posedge	clk	or posedge	rst) begin
    if(rst) begin
        count_i <= COL_WIDTH + 1;
        count_j <= COL_WIDTH + 1;
    end
	else if(count_i < COL_WIDTH) begin
	    if(count_j < COL_WIDTH - count_i + 1) begin
	        count_j <= count_j + 1;
	        if(comp_data[count_j] < comp_data[count_j+1]) begin
	            comp_data[count_j] <= comp_data[count_j+1];
	            comp_data[count_j+1] <= comp_data[count_j];
	        end
	        else begin
	            ;
	        end
	    end
	    else begin
	        count_i <= count_i + 1;
	        count_j <= 1;
	    end
	end
	else if(comp_valid_f) begin
	    count_i <= 1;
	    count_j <= 1;
	end
	else begin
	    count_i <= count_i;
	    count_j <= count_j;
	end
end

//----------------------------------------------------
always@(posedge	clk	or posedge	rst) begin
    if(rst)
        sort_done <= 0;
    else if((count_i== (COL_WIDTH-1))&& (count_j== 2))
        sort_done <= 1;
    else
        sort_done <= 0;
end

endmodule
1.2.3、冒泡排序顶层代码:
`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/06/14 21:10:41
// Design Name: 
// Module Name: main
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//
//FPGA 排序算法

module main(
	input   clk,
	input   rst,
			    
	input   enable
    );

//parameter define
parameter  ROW_WIDTH = 8;  //行
parameter  COL_WIDTH = 16; //列

reg  [15:0]   comp_data_i; //数据
reg           valid_i;     //数据有效

reg  [15:0]   data  [COL_WIDTH:1];
reg  [7:0]    count;

//初始化数据---输入需要排序的数据
initial begin
	data[1]  = 16'd20;
	//data[2]  = 16'd723;
	data[2]   = 16'd20;
	data[3]   = 16'd12;
	data[4]   = 16'd456;	
	data[5]   = 16'd278;
	data[6]   = 16'd9756;
	data[7]   = 16'd433;
	data[8]   = 16'd10000;
	data[9]   = 16'd21;
	data[10]  = 16'd724;
	data[11]  = 16'd15;
	data[12]  = 16'd458;	
	data[13]  = 16'd279;
	data[14]  = 16'd9758;
	data[15]  = 16'd439;
	data[16]  = 16'd30;
end

//***********************************************
//**              读取数据
//***********************************************
always@(posedge	clk	or posedge rst) begin
	if(rst) begin
		count <= COL_WIDTH + 1;
		valid_i	<= 0;
		comp_data_i	<= 0;
	end
	else if(enable) begin
		count <= 1;
	end
	else if(count <= COL_WIDTH) begin
		comp_data_i <= data[count];
		count <= count + 1;
		valid_i <= 1;
	end
	else begin
		valid_i	<= 0;
		count <= count;
	end
end



//***********************************************
//**	        冒泡排序算法
//***********************************************
bubbling  u2_bubbling(
	.clk           (clk         ), 
	.rst           (rst         ), 
	.comp_data_i   (comp_data_i ), 
	.valid_i       (valid_i     )
);


endmodule
1.2.4、冒泡排序仿真代码:
`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/06/14 21:15:30
// Design Name: 
// Module Name: tb_main
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module tb_main();

// Inputs
reg clk;
reg rst;
reg enable;

//parameter CLK_PERIOD= 20; //50MHz系统时钟(一个周期是20ns:1/50MHz=0.02us=20ns)
//parameter CLK_PERIOD = 10;  //100MHz系统时钟(一个周期是10ns:1/100MHz=0.01us=10ns)
parameter CLK_PERIOD = 5;  //200MHz系统时钟(一个周期是5ns:1/200MHz=0.005us=5ns)

// Instantiate the Unit Under Test (UUT)
main u_main(
	.clk(clk), 
	.enable(enable),
	.rst(rst)
);

always #(CLK_PERIOD/2) clk = ~clk;

initial begin
   // Initialize Inputs
    clk = 0;
    rst = 1;
    enable = 0;
    
    // Wait 100 ns for global reset to finish
    #100;
    rst = 0;  
    // Add stimulus here
    enable = 1;
    #10;
    enable = 0;
    
    #5000;
    enable = 1;
    #10;
    enable = 0;
end

endmodule

仿真结果如下:

Verilog/C++实现排序算法_数据_02

从仿真波形图中可以看出,当 sort_done 为高电平时,表示排序完成。

2、选择排序算法

        选择排序法是一种不稳定的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到全部待排序的数据元素排完。

Verilog/C++实现排序算法_算法_03

       选择排序法是在要排序的一组数中,选出最小(或最大)的一个数与第一个位置的数交换;在剩下的数当中找最小的与第二个位置的数交换,即顺序放在已排好序的数列的最后,如此循环,直到全部数据元素排完为止。

Verilog/C++实现排序算法_数据结构_04

2.1、C++实现代码如下:

方式一:
//交换两个数据
//交换 a 和 b 的位置
void swap(int *a, int *b)
{
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}

//选择排序
void SelectSort(int* arr, int size)
{
	int i = 0;
    for (i = 0; i < size-1; i++)
    {
        int min = i;
        int j = 0;
        for (j = i+1; j < size; j++)
        {
            if (arr[j] < arr[min])
            {
                min = j;
            }
        }
        swap(&arr[i], &arr[min]);
    }
}
方式二:
//选择排序
void select_sort(int *p, int n)
{
    int i,j;
    int min = 0;
    for(i = 0; i<n-1; i++)//排序次数
    {
        min = i;
        for(j=i+1; j<n; j++)
        {
            if(p[j] < p[min])
            {
                min = j;//记录交换的元素下标值
            }
        }
        if(i != min)
        {
            int temp = p[i];
            p[i] = p[min];
            p[min] = temp;
        }  
    }
}

2.2、Verilog实现代码如下:

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

3、并行全比较排序法

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

       并行全比较算法就是一种以FPGA的资源换取排序时间的算法。

  • 1、第一个时钟周期:将其中一个数据和其他数据在一个周期中一一比较,比较器分三种情况:
  • 1.1、这个数据大于其他数据,则它的得分为0;
  • 1.2、这个数据等于其他数据,若它在这个序列中比和它相等的其他数据靠前,则它的得分为0,反之为1;
  • 1.3、这个数据小于其他数据,则它的得分为1;
  • 2、第二个时钟周期:将每个数据和其他数据比较后的得分数据累加;
  • 3、第三个时钟周期:将每个数据根据自己的得分高低分别赋值给新的数组(若得分为1的就赋值给数组中的第一个数,2就赋值给新的数组中第二个数);
  • 4、第四个时钟周期:将新数组输出;

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

3.1、C++实现代码如下:

3.2、Verilog实现代码如下:

1、第一个时钟周期:将其中一个数据和其他数据在一个周期中一一比较

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

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

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

  • 优点:并行比较排序方式在实时性上有明显的优势,只需要四个时钟周期就可以排序完成;
  • 缺点:
  • 1.由于是并行比较消耗了较多的资源,而且在第二个时钟周期(得分累加)需要大量的加法器级联,考虑到路径延迟、建立保持时间和时钟抖动,一个时钟周期许多个加法器级联会有问题;
  • 2.在代码可移植性方面也有欠缺,比如若序列大小改变,在第二个和第三个时钟周期的时候就需要人为修改多处代码;

 

4、串行全比较排序法

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

4.1、C++实现代码如下:

4.2、Verilog实现代码如下:

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

  • 优点:
  •         1、资源消耗的比较少;
  •         2、代码可移植性好,序列变化只需要改变几个参数,不需要大规模修改代码;
  • 缺点:串行全比较算法所消耗的时间比并行全比较算法长。

总结

  • 代码可移植性:传统串行排序算法>串行全比较排序法>并行全比较排序法
  • 资源使用:传统串行排序算法<串行全比较排序法<并行全比较排序法
  • l排序时间:并行全比较排序法<串行全比较排序法<传统串行排序算法