FPGA/CPLD重要设计思想及工程应用
概述
“乒乓操作”是一个常常应用于数据流控制的处理技巧,典型的乒乓操作方法如下图所示。
乒乓操作的处理流程
输入数据流通过“输入数据选择单元”将数据流等时分配到两个数据缓冲区,数据缓冲模块可以为任何存储模块,比较常用的存储单元为双口RAM(DPRAM)、单口RAM(SPRAM)、FIFO等。
在第一个缓冲周期,将输入的数据流缓存到“数据缓冲模块1”。在第2个缓冲周期,通过“输入数据选择单元”的切换,将输入的数据流缓存到“数据缓冲模块2”,同时将“数据缓冲模块1”缓存的第1个周期数据通过“输出数据选择单元”的选择,送到“数据流运算处理模块”进行运算处理。
在第3个缓冲周期通过“输入数据选择单元”的再次切换,将输入的数据流缓存到“数据缓冲模块1”,同时将“数据缓冲模块2”缓存的第2个周期的数据通过“输出数据选择单元”切换,送到“数据流运算处理模块”进行运算处理。如此循环。
利用乒乓操作完成数据的无缝缓冲与处理
乒乓操作可以通过“输入数据选择单元”和“输出数据选择单元”按节拍、相互配合的切换,将经过缓冲的数据流没有停顿地送到“数据流运算处理模块”进行运算与处理。
把乒乓操作模块当做一个整体,站在这个模块的两端看数据,输入数据流和输出数据流都是连续不断的,没有任何停顿,因此非常适合对数据流进行流水线式处理。所以乒乓操作常常应用于流水线设计中,完成数据的无缝缓冲与处理。
串并转换
串并转换是FPGA设计的一个重要技巧,它是高速数据流处理的常用手段,串并转换的实现方法多种多样,根据数据的排序和数量的要求,可以选用寄存器、双口RAM(DPRAM)、单口RAM(SPRAM)、FIFO等实现。
若想数据的缓冲区开得很大,可以通过DPRAM实现了数据流的串并转换,对于数量比较小的设计可以采用寄存器完成串并转换。如无特殊需求,系统中应该用同步时序设计完成串并之间的转换。
那么在工程应用中,程序里怎么才能体现出串并转换设计的思想呢?怎么才能提高系统的处理速度呢?我们可以先来做一个串并转换的框架型设计。
这个章节老师没有给留下什么固定题目,所以自己构思起来也有点麻烦。想来想去就做个串入并出的设计吧。
设计的思想是这样的,有一组数据以50MHZ的速率从FPGA的一个I/O口传入,要实现在FPGA的另一端8个I/O口以50/8MHZ的速率把传入的速率吐出。也就是说每隔8个主时钟周期要从8个输出口输出从输入口输入的8个数据。
功能仿真的波形如下:
如图,从rst完成复位(拉低)并且输入使能信号en置位(拉高)后输入的数据(头8个时钟周期)为10101010,在第9个时钟周期,输出使能信号en_out拉高了,说明此时可以从8位并行数据输出口取数了,data_out的输出16进制aa正好就是输入的10101010,所以第一个数据的串并转换正确无误。往后输入11110000,输出是16进制f0也没错……
该程序实现了串并转换的要求,这样原来50MH速率传送的数据经过FPGA串并转换后只要用1/8的时钟频率就能完成数据流的传输,也可以说这是一个面积换速度的典型。
程序:
module sp_top(scl,rst,en,sda,data_out,en_out);
input scl;//50MHz主时钟信号
input rst;//复位信号,高有效
input en;//数据输入使能,高有效
input sda;//串行输入数据
output[7:0] data_out;//并行输出数据
output en_out;//输出使能信号,高有效
wire[7:0] data_reg;//并行输出数据寄存器
wire rdy;
series_in series_in(scl,rst,en,sda,data_reg,rdy);
parallel_out parallel_out(scl,rst,data_reg,rdy,data_out,en_out);
endmodule
module series_in(scl,rst,en,sda,data_reg,rdy);
input scl;
input rst;
input en;
input sda;
output[7:0] data_reg;
output rdy;
reg[7:0] data_reg;
reg[2:0] i;
reg rdy;
always @ ( posedge scl )
begin
if(rst) begin i <= 3'd0; data_reg <= 8'dz; rdy <= 0; end
else if(en)
begin
data_reg <= {data_reg[6:0],sda};
i <= i+1;
if(i==3'd7) rdy <= 1;
else rdy <= 0;
end
else begin i <= 3'd0; data_reg <= 8'dz; rdy <= 0; end
end
endmodule
module parallel_out(scl,rst,data_reg,rdy,data_out,en_out);
input scl;
input rst;
input[7:0] data_reg;
input rdy;
output[7:0] data_out;
output en_out;
reg[7:0] data_out;
reg en_out;
always @ ( posedge scl )
begin
if(rst) begin data_out <= 8'dz; en_out <= 0; end
else if(rdy) begin data_out <= data_reg; en_out <= 1; end
else begin data_out <= 8'dz; en_out <=0; end
end
endmodule
//-----------------------------------------------------
串并转换很简单,就是移位寄存器,后面最好跟一个锁存器,实现你所要求的功能需要四位移位寄存器和四位锁存器,锁存器的作用就是保持并行数据在移位时不发生变化:
module shift(nreset,clk,en,in,out);
input nreset,clk,en,in;
output [3:0] out;
reg [1:0] count;//移位计数,控制并行数据更新,这里是4bit并行数据
reg [3:0] data;
reg [3:0] out;
/* 移位计数,用于并行数据输出,也可以外加一个脉冲控制数据边界,这里移位4bit就并行输出一次*/
always@(posedge clk or negedge nreset)
begin
if(~nreset)
count <= 2'b00;
else if(en)
count <= count + 2'b01;
end
//移位
always@(posedge clk or negedge nreset)
begin
if(~nreset)
data <= 4'b0000;
eale if(en)
data <= {data[2:0],in};
end
//并行输出
always@(posedge clk or negedge nreset)
begin
if(~nreset)
out <= 4'b0000;
else if(en && (count==2'b11))
out <= data;
end
endmodule
我编译、仿真过了,没有问题,你原有的out<={out,in}应该写成像这样data <= {data[2:0],in};这就是一个移位寄存器!