FPGA设计篇之冒泡排序
一、写在前面
在前面,我们分别学习了并行全排序算法和双调排序算法的原理及其RTL代码的编写,在本文中我们将继续学习经典的排序算法——冒泡排序算法,学习其原理及其Verilog实现。
二、冒泡排序原理
冒泡排序算法(Bubble Sort)是一种简单的排序算法,它会逐个元素地重复遍历输入列表,将当前元素与后面的元素进行比较,并根据升序排序或者降序排序交换它们的值。从前往后或者从后往前重复遍历这个列表,直到在一次遍历期间不必执行任何交换,这意味着列表已完全排序。一个长度为N的序列,对该列表的遍历次数为N-1次。那么,具体步骤可以分为以下几个步骤:
(1)对一个序列中的数值从前往后依次比较相邻的两个数值,如果前面的数值(数值一)大于后面的数值(数值二),则交换这两个数值;
(2)重复步骤(1)的操作(N-1)次,排序完成;
那么,对于一个长度N=5的序列,其排序过程如下图所示。
三、冒泡排序RTL实现
那么,根据冒泡排序的原理,我们可以编写对于的Verilog代码,如下。对于一个长度为N的序列,每轮排序总共需要进行(N-1)次比较,而总共要进行(N-1)轮的排序,所以在代码中我们分别设置了单论排序计数器cnt_1和排序轮数计数器cnt_2用于计数,如果单轮排序计数器cnt_1达到N-2,则表示单轮排序结束,开始下一轮的排序,如果排序轮数计数器cnt_2达到N-2,则表示对该序列排序结束,输出排序后的数值和标签。另外,我们可以修改参数值ASCEND,设置对序列进行升序排序还是降序排序。
module bubble_sort
#(
parameter DATA_WIDTH = 8,
parameter LABEL_WIDTH = 8,
parameter DATA_NUMBER = 8,
parameter ASCEND = 1
)
(
clk,
rst_n,
start,
data_unsort,
label_unsort,
data_sorted,
label_sorted,
data_valid
);
input clk;
input rst_n;
input start;
input [DATA_NUMBER*DATA_WIDTH-1:0] data_unsort; //未排序数值
input [DATA_NUMBER*LABEL_WIDTH-1:0] label_unsort; //未排序标签
output reg [DATA_NUMBER*DATA_WIDTH-1:0] data_sorted; //排序后数值
output reg [DATA_NUMBER*LABEL_WIDTH-1:0] label_sorted; //排序后标签
output reg data_valid; //数据输出有效标志
reg [DATA_NUMBER*DATA_WIDTH-1:0] data_temp;
reg [DATA_NUMBER*LABEL_WIDTH-1:0] label_temp;
reg [2:0] FSM_state; //排序状态机
reg [$clog2(DATA_NUMBER)-1:0] cnt_1; //单轮排序次数计数器
reg [$clog2(DATA_NUMBER)-1:0] cnt_2; //排序轮数计数器
localparam Initial = 3'b001; //初始化
localparam Sort = 3'b010; //排序
localparam Finish = 3'b100; //排序结束
// 状态机
always @(posedge clk or negedge rst_n)
if(!rst_n)
FSM_state <= Initial;
else begin
case(FSM_state)
Initial:
begin
if(start)
FSM_state <= Sort;
else
FSM_state <= Initial;
end
Sort:
begin
if(cnt_2 == DATA_NUMBER-2)
FSM_state <= Finish;
else
FSM_state <= Sort;
end
Finish:
begin
FSM_state <= Initial;
end
default:
FSM_state <= Initial;
endcase
end
// 单轮排序次数计数器
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt_1 <= 0;
else if(FSM_state == Sort && cnt_1 == DATA_NUMBER-2)
cnt_1 <= 0;
else if(FSM_state == Sort)
cnt_1 <= cnt_1 + 1'b1;
else
cnt_1 <= 0;
// 排序轮数计数器
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt_2 <= 0;
else if(FSM_state == Sort && cnt_1 == DATA_NUMBER-2)
cnt_2 <= cnt_2 + 1'b1;
else if(FSM_state == Sort)
cnt_2 <= cnt_2;
else
cnt_2 <= 0;
// 排序数值与标签
always @(posedge clk or negedge rst_n)
if(!rst_n) begin
data_temp <= 0;
label_temp <= 0;
end else if(FSM_state == Initial) begin
data_temp <= data_unsort;
label_temp <= label_unsort;
end else if(FSM_state == Sort) begin
if(ASCEND == 1'b1) //升序排序
if(data_temp[cnt_1*DATA_WIDTH+:DATA_WIDTH] > data_temp[(cnt_1+1)*DATA_WIDTH+:DATA_WIDTH]) begin
data_temp[cnt_1*DATA_WIDTH+:DATA_WIDTH] <= data_temp[(cnt_1+1)*DATA_WIDTH+:DATA_WIDTH];
data_temp[(cnt_1+1)*DATA_WIDTH+:DATA_WIDTH] <= data_temp[cnt_1*DATA_WIDTH+:DATA_WIDTH];
label_temp[cnt_1*LABEL_WIDTH+:LABEL_WIDTH] <= label_temp[(cnt_1+1)*LABEL_WIDTH+:LABEL_WIDTH];
label_temp[(cnt_1+1)*LABEL_WIDTH+:LABEL_WIDTH] <= label_temp[cnt_1*LABEL_WIDTH+:LABEL_WIDTH];
end else begin
data_temp[cnt_1*DATA_WIDTH+:DATA_WIDTH] <= data_temp[cnt_1*DATA_WIDTH+:DATA_WIDTH];
data_temp[(cnt_1+1)*DATA_WIDTH+:DATA_WIDTH] <= data_temp[(cnt_1+1)*DATA_WIDTH+:DATA_WIDTH];
label_temp[cnt_1*LABEL_WIDTH+:LABEL_WIDTH] <= label_temp[cnt_1*LABEL_WIDTH+:LABEL_WIDTH];
label_temp[(cnt_1+1)*LABEL_WIDTH+:LABEL_WIDTH] <= label_temp[(cnt_1+1)*LABEL_WIDTH+:LABEL_WIDTH];
end
else if(ASCEND == 1'b0) //降序排序
if(data_temp[cnt_1*DATA_WIDTH+:DATA_WIDTH] < data_temp[(cnt_1+1)*DATA_WIDTH+:DATA_WIDTH]) begin
data_temp[cnt_1*DATA_WIDTH+:DATA_WIDTH] <= data_temp[(cnt_1+1)*DATA_WIDTH+:DATA_WIDTH];
data_temp[(cnt_1+1)*DATA_WIDTH+:DATA_WIDTH] <= data_temp[cnt_1*DATA_WIDTH+:DATA_WIDTH];
label_temp[cnt_1*LABEL_WIDTH+:LABEL_WIDTH] <= label_temp[(cnt_1+1)*LABEL_WIDTH+:LABEL_WIDTH];
label_temp[(cnt_1+1)*LABEL_WIDTH+:LABEL_WIDTH] <= label_temp[cnt_1*LABEL_WIDTH+:LABEL_WIDTH];
end else begin
data_temp[cnt_1*DATA_WIDTH+:DATA_WIDTH] <= data_temp[cnt_1*DATA_WIDTH+:DATA_WIDTH];
data_temp[(cnt_1+1)*DATA_WIDTH+:DATA_WIDTH] <= data_temp[(cnt_1+1)*DATA_WIDTH+:DATA_WIDTH];
label_temp[cnt_1*LABEL_WIDTH+:LABEL_WIDTH] <= label_temp[cnt_1*LABEL_WIDTH+:LABEL_WIDTH];
label_temp[(cnt_1+1)*LABEL_WIDTH+:LABEL_WIDTH] <= label_temp[(cnt_1+1)*LABEL_WIDTH+:LABEL_WIDTH];
end
end else begin
data_temp <= data_temp;
label_temp <= label_temp;
end
// 数据输出有效标志
always @(posedge clk or negedge rst_n)
if(!rst_n)
data_valid <= 1'b0;
else if(FSM_state == Finish)
data_valid <= 1'b1;
else
data_valid <= 1'b0;
// 排序后数据和标签
always @(posedge clk or negedge rst_n)
if(!rst_n) begin
data_sorted <= 0;
label_sorted <= 0;
end else if(data_valid)begin
data_sorted <= data_temp;
label_sorted <= label_temp;
end else begin
data_sorted <= data_sorted;
label_sorted <= label_sorted;
end
endmodule
四、TestBench
在这里,我们以序列长度N=8为例,编写一个简单的测试文件,如下。输入序列为:9,3,15,10,11,8,4,5,其对应的标签为:0,1,2,3,4,5,6,7。那么,经过排序后的数值顺序应当是:3,4,5,8,9,10,11,15,排序后的标签应当是:1,6,7,5,0,3,4,2。
`timescale 1ns/1ns
module tb_bubble_sort();
parameter DATA_WIDTH = 8;
parameter DATA_NUMBER = 8;
parameter LABEL_WIDTH = $clog2(DATA_NUMBER);
parameter ASCEND = 1'b1;
reg clk,rst_n,start;
reg [DATA_WIDTH-1:0] data_unsort [DATA_NUMBER-1:0];
reg [LABEL_WIDTH-1:0] label_unsort [DATA_NUMBER-1:0];
wire [DATA_WIDTH-1:0] data_sorted [DATA_NUMBER-1:0];
wire [LABEL_WIDTH-1:0] label_sorted [DATA_NUMBER-1:0];
wire data_valid;
initial begin
clk = 1'b1;
rst_n = 1'b0;
start <= 0;
#20
rst_n = 1'b1;
data_unsort[0] <= 9;
data_unsort[1] <= 3;
data_unsort[2] <= 15;
data_unsort[3] <= 10;
data_unsort[4] <= 11;
data_unsort[5] <= 8;
data_unsort[6] <= 4;
data_unsort[7] <= 5;
label_unsort[0] <= 0;
label_unsort[1] <= 1;
label_unsort[2] <= 2;
label_unsort[3] <= 3;
label_unsort[4] <= 4;
label_unsort[5] <= 5;
label_unsort[6] <= 6;
label_unsort[7] <= 7;
#40 start <= 1'b1;
#20 start <= 1'b0;
end
always #10 clk = ~clk;
bubble_sort
#(
.DATA_WIDTH(DATA_WIDTH),
.LABEL_WIDTH(LABEL_WIDTH),
.DATA_NUMBER(DATA_NUMBER),
.ASCEND(ASCEND)
)
bubble_sort
(
.clk(clk),
.rst_n(rst_n),
.start(start),
.data_unsort({data_unsort[7],data_unsort[6],data_unsort[5],data_unsort[4],data_unsort[3],data_unsort[2],data_unsort[1],data_unsort[0]}),
.label_unsort({label_unsort[7],label_unsort[6],label_unsort[5],label_unsort[4],label_unsort[3],label_unsort[2],label_unsort[1],label_unsort[0]}),
.data_sorted({data_sorted[7],data_sorted[6],data_sorted[5],data_sorted[4],data_sorted[3],data_sorted[2],data_sorted[1],data_sorted[0]}),
.label_sorted({label_sorted[7],label_sorted[6],label_sorted[5],label_sorted[4],label_sorted[3],label_sorted[2],label_sorted[1],label_sorted[0]}),
.data_valid(data_valid)
);
endmodule
五、仿真结果
运行仿真程序,得到仿真结果如下。可见排序后的结果与我们理论上的结果一致,仿真通过。
六、写在后面
在本文中,我们学习了经典的排序算法——冒泡排序算法的原理及其Verilog代码的实现,冒泡排序算法相比与并行全排序算法和双调排序算法,其时间复杂度要高,性能不佳,所以在实际的应用中很少用到,多用于实验中。
好了,以上就是关于FPGA中实现冒泡排序的一些学习笔记,如果有疑义的地方欢迎评论区友好探讨学习!!!!!
代码:(FPGA设计篇之冒泡排序Verilog代码)