FIFO是英文First In First Out 的缩写,是一种先进先出的数据缓存器,他与普通存储器的区别是没有外部读写地址线,这样使用起来非常简单,但缺点就是只能顺序写入数据,顺序的读出数据,其数据地址由内部读写指针自动加1完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。
FIFO多用于:1、不同时钟之间的数据传输;2、不同跨度的接口;
//程序实现的功能:串口从FIFO中读出数据,然后发送给上位机(串口调试助手) module fifo_uart ( clk, rst_n, rs232_tx ); input clk; input rst_n; output rs232_tx; wire[7:0]wrf_data; //数据写入缓存FIFO输入数据总线; wire wrf_wrreq; //数据写入缓存FIFO输入数据请求信号,高电平有效; wire fifo232_rdreq; //FIFO读请求信号,高电平有效; wire fifo_empty; //FIFO空请求信号,高电平有效; wire[7:0] tx_data; //串口要发送的数据; wire tx_start ; //串口发送标志位; assign tx_start=~fifo_empty; datagene datagene //数据产生模块; ( .clk(clk) , .rst_n(rst_n), .wrf_data(wrf_data), .wrf_wrreq(wrf_wrreq) ); fifo_232 fifo_232_inst //例化FIFO模块 ( .clock ( clk ), .data ( wrf_data ), .rdreq ( fifo232_rdreq ), .wrreq ( wrf_wrreq ), .empty ( fifo_empty ), .q ( tx_data ) ); uart uart //串口通信模块 ( .clk(clk), .rst_n(rst_n), .tx_start(tx_start), .tx_data(tx_data), .fifo232_rdreq(fifo232_rdreq), .rs232_tx(rs232_tx) ); endmodule module datagene //数据产生模块; ( clk, rst_n, wrf_wrreq, wrf_data ); input clk; input rst_n; output wrf_wrreq; //数据写入缓存FIFO输入数据请求信号,高电平有效; output[7:0] wrf_data; reg[24:0] cntwr; always @ (posedge clk or negedge rst_n) begin if(!rst_n) cntwr<=25'd0; else cntwr<=cntwr+1'b1; end assign wrf_wrreq=(cntwr>=25'h1fffff0)&&(cntwr<=25'h1ffffff); reg[7:0] wrf_data_r; always @ (posedge clk or negedge rst_n) begin if(!rst_n) wrf_data_r<=8'd0; else if((cntwr>=25'h1fffff0)&&(cntwr<=25'h1ffffff)) wrf_data_r<=wrf_data_r+1'b1; end assign wrf_data=wrf_data_r; endmodule module uart //串口通信顶层模块; ( clk, rst_n, tx_start, tx_data, fifo232_rdreq, rs232_tx ); input clk; input rst_n; input tx_start; //串口发送标志位; input[7:0] tx_data; //串口待发送数据; output fifo232_rdreq; //FIFO读请求信号,高电平有效; output rs232_tx; //rs232发送给PC机的数据; wire bps_start; //波特率计数器启动信号; wire clk_bps; //发送数据标志位; speed_select speed_select //模特率产生模块; ( .clk(clk), .rst_n(rst_n), .bps_start(bps_start), .clk_bps(clk_bps) ); uart_tx uart_tx //串口发送模块; ( .clk(clk), .rst_n(rst_n), .clk_bps(clk_bps), .tx_start(tx_start), .tx_data(tx_data), .bps_start(bps_start), .fifo232_rdreq(fifo232_rdreq), .rs232_tx(rs232_tx) ); endmodule module speed_select ( clk, rst_n, bps_start, clk_bps ); input clk; input rst_n; input bps_start; // output clk_bps; parameter BPS=5207; //波特率为9600bps,传一位数据所需要计的数; parameter BPS_2=2603; //波特率为9600bps,一位数据传需到中间位置时,所需记得数; reg [12:0] cnt; always @ (posedge clk or negedge rst_n) begin if(!rst_n) cnt<=13'd0; else if(!bps_start||cnt==BPS) cnt<=13'd0; else cnt<=cnt+1'b1; end reg clk_bps_r; always @ (posedge clk or negedge rst_n) begin if(!rst_n) clk_bps_r<=1'b0; else if(cnt==BPS_2) //一位数据传需到中间位置是,最稳定,所以可以开始采样; clk_bps_r<=1'b1; else clk_bps_r<=1'b0; end assign clk_bps=clk_bps_r; endmodule module uart_tx ( clk, rst_n, clk_bps, tx_start, tx_data, bps_start, fifo232_rdreq, rs232_tx ); input clk; //系统时钟; input rst_n; //复位信号,低电平有效; input tx_start; //串口发送标志位; input clk_bps; //发送数据标志位; input[7:0] tx_data; //串口待发送数据 output bps_start; //波特率计数器启动信号; output fifo232_rdreq; output rs232_tx; reg tx_en; //数据发送能信号; reg[3:0] num; always @ (posedge clk or negedge rst_n) begin if(!rst_n) tx_en<=1'b0; else if(num==4'd11) //数据发送完成; tx_en<=1'b0; else if(tx_start) //进入发送数据状态 tx_en<=1'b1; end assign bps_start=tx_en; reg tx_enr0; always @ (posedge clk or negedge rst_n) begin if(!rst_n) tx_enr0<=1'b0; else tx_enr0<=tx_en; end reg tx_enr1; always @ (posedge clk or negedge rst_n) begin if(!rst_n) tx_enr1<=1'b0; else tx_enr1<=tx_enr0; end assign fifo232_rdreq=(~tx_enr1)&tx_enr0; //上升沿检测; 当tx_en由0变为1时,fifo232_rdreq置高一个周期; reg rs232_tx_r; always @ (posedge clk or negedge rst_n) begin if(!rst_n) begin num<=4'd0; rs232_tx_r<=1'b1; end else if(tx_en) begin if(clk_bps) begin num<=num+1'b1; case(num) 4'd0 : rs232_tx_r<=1'b0; //发送起始位 4'd1 : rs232_tx_r<=tx_data[0]; 4'd2 : rs232_tx_r<=tx_data[1]; 4'd3 : rs232_tx_r<=tx_data[2]; 4'd4 : rs232_tx_r<=tx_data[3]; 4'd5 : rs232_tx_r<=tx_data[4]; 4'd6 : rs232_tx_r<=tx_data[5]; 4'd7 : rs232_tx_r<=tx_data[6]; 4'd8 : rs232_tx_r<=tx_data[7]; 4'd9 : rs232_tx_r<=1'b1; //结束位; default : rs232_tx_r<=1'b1; endcase end else if(num==4'd11) num<=4'd0; end end assign rs232_tx=rs232_tx_r; endmodule
程序RTL视图
程序现象:
只要FIFO中有数据,串口接口就会将数据发送给上位机(串口调试助手)。