FPGA的学习:FIFO求和实验
Posted 石小舟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FPGA的学习:FIFO求和实验相关的知识,希望对你有一定的参考价值。
FIFO求和的系统框图如图所示:
其中蓝色的控制模块的实现如下:
时序图如下:
代码实现如下:
`timescale 1ns/1ns
module uart_rx
#(
parameter UART_BPS = 'd9600, //串口波特率
parameter CLK_FREQ = 'd50_000_000 //时钟频率
)
(
input wire sys_clk , //系统时钟50MHz
input wire sys_rst_n , //全局复位
input wire rx , //串口接收数据
output reg [7:0] po_data , //串转并后的8bit数据
output reg po_flag //串转并后的数据有效标志信号
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//localparam define
localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS ;
//reg define
reg rx_reg1 ;
reg rx_reg2 ;
reg rx_reg3 ;
reg start_nedge ;
reg work_en ;
reg [12:0] baud_cnt ;
reg bit_flag ;
reg [3:0] bit_cnt ;
reg [7:0] rx_data ;
reg rx_flag ;
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//插入两级寄存器进行数据同步,用来消除亚稳态
//rx_reg1:第一级寄存器,寄存器空闲状态复位为1
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_reg1 <= 1'b1;
else
rx_reg1 <= rx;
//rx_reg2:第二级寄存器,寄存器空闲状态复位为1
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_reg2 <= 1'b1;
else
rx_reg2 <= rx_reg1;
//rx_reg3:第三级寄存器和第二级寄存器共同构成下降沿检测
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_reg3 <= 1'b1;
else
rx_reg3 <= rx_reg2;
//start_nedge:检测到下降沿时start_nedge产生一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
start_nedge <= 1'b0;
else if((~rx_reg2) && (rx_reg3))
start_nedge <= 1'b1;
else
start_nedge <= 1'b0;
//work_en:接收数据工作使能信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
work_en <= 1'b0;
else if(start_nedge == 1'b1)
work_en <= 1'b1;
else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
work_en <= 1'b0;
//baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
baud_cnt <= 13'b0;
else if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))
baud_cnt <= 13'b0;
else if(work_en == 1'b1)
baud_cnt <= baud_cnt + 1'b1;
//bit_flag:当baud_cnt计数器计数到中间数时采样的数据最稳定,
//此时拉高一个标志信号表示数据可以被取走
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
bit_flag <= 1'b0;
else if(baud_cnt == BAUD_CNT_MAX/2 - 1)
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;
//bit_cnt:有效数据个数计数器,当8个有效数据(不含起始位和停止位)
//都接收完成后计数器清零
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
bit_cnt <= 4'b0;
else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
bit_cnt <= 4'b0;
else if(bit_flag ==1'b1)
bit_cnt <= bit_cnt + 1'b1;
//rx_data:输入数据进行移位
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_data <= 8'b0;
else if((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1))
rx_data <= {rx_reg3, rx_data[7:1]};
//rx_flag:输入数据移位完成时rx_flag拉高一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_flag <= 1'b0;
else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
rx_flag <= 1'b1;
else
rx_flag <= 1'b0;
//po_data:输出完整的8位有效数据
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
po_data <= 8'b0;
else if(rx_flag == 1'b1)
po_data <= rx_data;
//po_flag:输出数据有效标志(比rx_flag延后一个时钟周期,为了和po_data同步)
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
po_flag <= 1'b0;
else
po_flag <= rx_flag;
endmodule
`timescale 1ns/1ns
module uart_tx
#(
parameter UART_BPS = 'd9600, //串口波特率
parameter CLK_FREQ = 'd50_000_000 //时钟频率
)
(
input wire sys_clk , //系统时钟50MHz
input wire sys_rst_n , //全局复位
input wire [7:0] pi_data , //模块输入的8bit数据
input wire pi_flag , //并行数据有效标志信号
output reg tx //串转并后的1bit数据
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//localparam define
localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS ;
//reg define
reg [12:0] baud_cnt;
reg bit_flag;
reg [3:0] bit_cnt ;
reg work_en ;
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//work_en:接收数据工作使能信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
work_en <= 1'b0;
else if(pi_flag == 1'b1)
work_en <= 1'b1;
else if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
work_en <= 1'b0;
//baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
baud_cnt <= 13'b0;
else if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))
baud_cnt <= 13'b0;
else if(work_en == 1'b1)
baud_cnt <= baud_cnt + 1'b1;
//bit_flag:当baud_cnt计数器计数到1时让bit_flag拉高一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
bit_flag <= 1'b0;
else if(baud_cnt == 13'd1)
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;
//bit_cnt:数据位数个数计数,10个有效数据(含起始位和停止位)到来后计数器清零
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
bit_cnt <= 4'b0;
else if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
bit_cnt <= 4'b0;
else if((bit_flag == 1'b1) && (work_en == 1'b1))
bit_cnt <= bit_cnt + 1'b1;
//tx:输出数据在满足rs232协议(起始位为0,停止位为1)的情况下一位一位输出
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
tx <= 1'b1; //空闲状态时为高电平
else if(bit_flag == 1'b1)
case(bit_cnt)
0 : tx <= 1'b0;
1 : tx <= pi_data[0];
2 : tx <= pi_data[1];
3 : tx <= pi_data[2];
4 : tx <= pi_data[3];
5 : tx <= pi_data[4];
6 : tx <= pi_data[5];
7 : tx <= pi_data[6];
8 : tx <= pi_data[7];
9 : tx <= 1'b1;
default : tx <= 1'b1;
endcase
endmodule
`timescale 1ns/1ns
module fifo_sum_ctrl
(
input wire sys_clk , //频率为50MHz
input wire sys_rst_n , //复位信号,低有效
input wire [7:0] pi_data , //rx传入的数据信号
input wire pi_flag , //rx传入的标志信号
output reg [7:0] po_sum , //求和运算后的信号
output reg po_flag //输出数据标志信号
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter CNT_ROW_MAX = 7'd49 , //行计数最大值
CNT_COL_MAX = 7'd49 ; //列计数最大值
//wire define
wire [7:0] data_out1 ; //fifo1数据输出
wire [7:0] data_out2 ; //fifo2数据输出
//reg define
reg [6:0] cnt_row ; //行计数
reg [6:0] cnt_col ; //场计数
reg wr_en1 ; //fifo1写使能
reg wr_en2 ; //fifo2写使能
reg [7:0] data_in1 ; //fifo1写数据输入
reg [7:0] data_in2 ; //fifo2写数据输入
reg rd_en ; //fifo1、fifo2共用的读使能
reg dout_flag ; //控制fifo1,2-84行的写使能
reg po_flag_reg ; //输出标志位缓存,rd_en延后一拍得到,控制计算po_sum
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//cnt_row:行计数器,计数一行数据个数
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
cnt_row <= 7'd0;
else if((cnt_row == CNT_ROW_MAX) && (pi_flag == 1'b1))
cnt_row <= 7'd0;
else if(pi_flag == 1'b1)
cnt_row <= cnt_row + 1'b1;
end
//cnt_col:列计数器,计数数据行数
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
cnt_col <= 7'd0;
else if((cnt_col == CNT_COL_MAX) && (pi_flag == 1'b1) && (cnt_row == CNT_ROW_MAX))
cnt_col <= 7'd0;
else if((cnt_row == CNT_ROW_MAX) && (pi_flag == 1'b1))
cnt_col <= cnt_col + 1'b1;
end
//wr_en1:fifo1写使能信号,高电平有效
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
wr_en1 <= 1'b0;
else if((cnt_col == 7'd0) && (pi_flag == 1'b1))
wr_en1 <= 1'b1; //第0行写入fifo1
else
wr_en1 <= dout_flag; //2-84行写入fifo1
end
//wr_en2:fifo2写使能信号,高电平有效
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
wr_en2 <= 1'b0;
else if((cnt_col >= 7'd1) && (cnt_col <= CNT_COL_MAX - 1'b1) && (pi_flag == 1'b1))
wr_en2 <= 1'b1; //2-CNT_COL_MAX行写入fifo2
else
wr_en2 <= 1'b0;
end
//data_in1:fifo1数据输入
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
data_in1 <= 8'b0;
else if((pi_flag == 1'b1) && (cnt_col == 7'd0))
data_in1 <= pi_data; //第0行数据暂存fifo1中
else if(dout_flag == 1'b1)
data_in1 <= data_out2;//第2-CNT_COL_MAX-1行时,fifo2读出数据存入fifo1
else
data_in1 <= data_in1;
end
//data_in2:fifo2数据输入
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
data_in2 <= 8'b0;
else if((pi_flag == 1'b1)&&(cnt_col >= 7'd1)&&(cnt_col <= (CNT_COL_MAX - 1'b1)))
data_in2 <= pi_data;
else
data_in2 <= data_in2;
end
//rd_en:fifo1和fifo2的共用读使能信号
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
rd_en <= 1'b0;
else if((pi_flag == 1'b1)&&(cnt_col >= 7'd2)&&(cnt_col <= CNT_COL_MAX))
rd_en <= 1'b1;
else
rd_en <= 1'b0;
end
//dout_flag:控制2-CNT_COL_MAX-1行wr_en1信号
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
dout_flag <= 0;
else if((wr_en2 == 1'b1) && (rd_en == 1'b1))
dout_flag <= 1'b1;
else
dout_flag <= 1'b0;
end
//po_flag_reg:输出标志位缓存,延后rd_en一拍,控制po_sum信号
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
po_flag_reg <= 1'b0;
else if(rd_en == 1'b1)
po_flag_reg <= 1'b1;
else
po_flag_reg <= 1'b0;
end
//po_flag:输出标志信号,延后输出标志位缓存一拍,与po_sum同步输出
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
po_flag <= 1'b0;
else
po_flag <= po_flag_reg;
end
//po_sum:求和数据输出
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
po_sum <= 8'b0;
else if(po_flag_reg == 1'b1)
po_sum <= data_out1 + data_out2 + pi_data;
else
po_sum <= po_sum;
end
//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------- fifo_data_inst1 --------------
fifo_data fifo_data_inst1
(
.clock (sys_clk ), //input clock
.data (data_in1 ), //input [7:0] data
.wrreq (wr_en1 ), //input wrreq
.rdreq (rd_en ), //input rdreq
.q (data_out1 ) //output [7:0] q
);
//------------- fifo_data_inst2 --------------
fifo_data fifo_data_inst2
(
.clock (sys_clk ), //input clock
.data (data_in2 ), //input [7:0] data
.wrreq (wr_en2 ), //input wrreq
.rdreq (rd_en ), //input rdreq
.q (data_out2 ) //output [7:0] q
);
endmodule
`timescale 1ns/1ns
module fifo_sum
(
input wire sys_clk , //输入系统时钟,50MHz
input wire sys_rst_n , //复位信号,低电平有效
input wire rx , //串口数据接收
output wire tx //串口数据发送
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter UART_BPS = 14'd9600 , //比特率
CLK_FREQ = 26'd50_000_000 ; //时钟频率
//wire define
wire [7:0] pi_data ; //输入待求和数据
wire pi_flag ; //输入数据标志信号
wire [7:0] po_sum ; //输出求和后数据
wire po_flag ; //输出数据标志信号
//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------- uart_rx_inst --------------
uart_rx
#(
.UART_BPS (UART_BPS ), //串口波特率
.CLK_FREQ (CLK_FREQ ) //时钟频率
)
uart_rx_inst
(
.sys_clk (sys_clk ), //系统时钟50Mhz
.sys_rst_n (sys_rst_n ), //全局复位
.rx (rx ), //串口接收数据
.po_data (pi_data ), //串转并后的数据
.po_flag (pi_flag ) //串转并后的数据有效标志信号
);
//------------- fifo_sum_ctrl_inst --------------
fifo_sum_ctrl fifo_sum_ctrl_inst
(
.sys_clk (sys_clk ), //频率为50MHz
.sys_rst_n (sys_rst_n ), //复位信号,低有效
.pi_data (pi_data ), //rx传入的数据信号
.pi_flag (pi_flag ), //rx传入的标志信号
.po_sum (po_sum ), //求和运算后的信号
.po_flag (po_flag ) //输出数据标志信号
);
//------------- uart_tx_inst --------------
uart_tx
#(
.UART_BPS (UART_BPS ), //串口波特率
.CLK_FREQ (CLK_FREQ ) //时钟频率
)
uart_tx_inst
(
.sys_clk (sys_clk ), //系统时钟50Mhz
.sys_rst_n (sys_rst_n ), //全局复位
.pi_data (po_sum ), //并行数据
.pi_flag (po_flag ), //并行数据有效标志信号
.tx (tx ) //串口发送数据
);
endmodule
接着进行仿真:
`timescale 1ns/1ns
module tb_fifo_sum();
//wire define
wire tx ;
//reg define
reg clk ;
reg rst_n ;
reg rx ;
reg [7:0] data_men[2499:0] ;
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//读取数据
initial
$readmemh("E:/sources/fifo_sum/matlab/fifo_data.txt",data_men);
//生成时钟和复位信号
initial
begin
clk = 1'b1;
rst_n <= 1'b0;
#30
rst_n <= 1'b1;
end
always #10 clk = ~clk;
//rx赋初值,调用rx_byte
initial
begin
rx <= 1'b1;
#200
rx_byte();
end
//rx_byte
task rx_byte();
integer j;
for(j=0;j<2500;j=j+1)
rx_bit(data_men[j]);
endtask
//rx_bit
task rx_bit(input[7:0] data);//data是data_men[j]的值。
integer i;
for(i=0;i<10;i=i+1)
begin
case(i)
0: rx <= 1'b0; //起始位
1: rx <= data[0];
2: rx <= data[1];
3: rx <= data[2];
4: rx <= data[3];
5: rx <= data[4];
6: rx <= data[5];
7: rx <= data[6];
8: rx <= data[7]; //上面8个发送的是数据位
9: rx <= 1'b1; //停止位
endcase
#1040;
end
endtask
//重定义defparam,用于修改参数
defparam fifo_sum_inst.uart_rx_inst.CLK_FREQ = 500000 ;
defparam fifo_sum_inst.uart_tx_inst.CLK_FREQ = 500000 ;
//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------- fifo_sum_inst --------------
fifo_sum fifo_sum_inst
(
.sys_clk (clk ),
.sys_rst_n (rst_n ),
.rx (rx ),
.tx (tx )
);
endmodule
以上是关于FPGA的学习:FIFO求和实验的主要内容,如果未能解决你的问题,请参考以下文章
ZYNQ从入门到秃头08 FPGA片内异步FIFO读写测试实验