赞
踩
(1)选择芯片类型,封装,速度以及仿真器和语言等。
(2)创建一个新的source文件,并将该文件放到design目录下。
(3)创建IP核的配置,这里要与创建工程的一致。
(4)选择创建一个FIFO
(5)选择FIFO的类型
(6)选择FIFO的时钟和内存类型
(7)选择FIFO的位宽和深度
(8)选择FIFO的一些标志信号
(9)选择复位方式
这是配置完成后最终生成的报告。
(10)将生成的IP核工程添加到ISE工程中,注意:添加的IP核要选择xco文件。
(11)通过将instantation文件直接复制到design文件中,完成FIFO接口的例化。
`timescale 1ns / 1ps
module ex_ise_fifo(
input wire sclk,
input wire rst_n,
input wire [7:0] data_in,
input wire data_v, //数据有效
input wire r_flag, //读fifo标志
output wire [7:0] dout
);
wire w_full,r_emp;//写满信号,读空信号
wire wr_en;//写使能
wire rd_en;//读使能
wire almost_full,wr_ack,overflow,r_empty,almost_empty,valid,underflow;
assign wr_en = (~w_full) & data_v; //当写有效且没有写满时,写使能拉高
assign rd_en = (~r_empty) & r_flag; //当读有效且没有读空时,读使能拉高
sync_fifo_8x256 sync_fifo (
.clk(sclk), // input clk
.rst(rst_n), // input rst
.din(data_in), // input [7 : 0] din
.wr_en(wr_en), // input wr_en
.rd_en(rd_en), // input rd_en
.dout(dout), // output [7 : 0] dout
.full(w_full), // output full
.almost_full(almost_full), // output almost_full
.wr_ack(wr_ack), // output wr_ack
.overflow(overflow), // output overflow
.empty(r_empty), // output empty
.almost_empty(almost_empty), // output almost_empty
.valid(valid), // output valid
.underflow(underflow) // output underflow
);
endmodule
`timescale 1ns/1ns
module tb_ex_ise_fifo;
reg sclk,rst_n;
reg data_v;
reg [7:0] data;
reg r_flag;
wire [7:0] dout;
initial begin
sclk = 0;
rst_n = 0;
#100
rst_n = 1;
end
initial begin
data_v = 0;
data = 0;
r_flag = 0;
#200
send_data();
#60
read_data();
end
always #10 sclk =~ sclk;
ex_ise_fifo ex_ise_fifo_inst (
.sclk(sclk),
.rst_n(rst_n),
.data_in(data),
.data_v(data_v),
.r_flag(r_flag),
.dout(dout)
);
task send_data();
integer i;
begin
for(i=0;i<256;i=i+1)
begin
@(posedge sclk);
data_v = 1'b1;
data = i;
end
@(posedge sclk);
data_v = 1'b0;
data = 0;
end
endtask
task read_data();
integer i;
begin
for(i=0;i<256;i=i+1)
begin
@(posedge sclk);
r_flag = 1'b1;
end
r_flag = 1'b0;
end
endtask
endmodule
quit -sim
.main clear
vlib work
vlog ./tb_ex_ise_fifo.v
vlog ./../design/ex_ise_fifo.v
vlog ./../ise_prj/ipcore_dir/*.v
vlog ./ise_lib/*.v
vsim -voptargs=+acc work.tb_ex_ise_fifo work.glbl
add wave tb_ex_ise_fifo/ex_ise_fifo_inst/*
run 10us
(1)写FIFO
写应答信号wr_ack比写使能信号wr_en延迟一拍,数据在写使能为高的时候写入到FIFO(RAM)中,其实在下一个上升沿才真正将数据存入到RAM中去,所以ack是标志数据真正被写入RAM中去的标志。
写入数据从0到255后,写有效信号data_v拉低,wr_en信号拉低标志写完。对于几乎满信号almost_full,当还有一个数据的时候拉高。(这里几乎满信号可以设置成还有X个数据时拉高,在IP核中即可设置)
(2)读FIFO
当读空信号r_empty为低(标志着FIFO中还有内容),且r_flag拉高后,读使能信号rd_en拉高进行数据的读取,将数据读取到dout中。
这里几乎空信号almost_empty同样可以实现配置(此处设置为0),当读到最后一个数据拉高。当数据读取完成后,r_flag信号拉低标志读取完成。
(3)FIFO写溢出
写溢出表示写入的数据数量大于FIFO的深度。
这里多写了3个数据。当写溢出后,写溢出信号overflow被拉高了3个时钟周期,表示这三个时钟周期发送的数据是写溢出的数据。
(4)FIFO读溢出
读溢出表示读取的数据量大于FIFO中的内容。
这里多读了3个数据。当读溢出后,读溢出信号underflow被拉高了3个时钟周期,表示这三个时钟周期读取的数据是读溢出的数据。
写满和读空:
写指针赶上读指针,称为写满;读指针赶上写指针,称为读空。
//写满标志
assign w_full=(r_addr[2:0] == w_addr[2:0] && r_addr[3] == (~w_addr[3]));
//读空标志
assign r_empty=(r_addr[3:0] == w_addr[3:0]);
低三位相等,高位取反相等,相差是一个FIFO的深度,就是写满。
读地址和写地址相等时,则为读空。
格雷码:
编码方式:(十进制>>1) ^(十进制) 譬如:(2>>1) ^(2)=0011
特点:挨着的两个数发生跳变时,只需要改变一位。
FIFO逻辑图
整体包含了3个模块:双端口的FIFO存储模块、写FIFO模块、读FIFO模块。其中读写模块有各自的时钟,因此是异步的,这样就构成了2个时钟域。当读模块的信号进入到写模块中,相当于信号跨时钟域,所以需要打两拍处理。
格雷码控制器
写满读空逻辑控制器
这里与写满读空时的组合逻辑描述相同,在代码中需要实现。
此部分包含4个文件,3个模块(写FIFO控制器、读FIFO控制器和FIFO RAM),1个顶层TOP文件。
(1)写FIFO控制器:w_ctrl.v
module w_ctrl(
input wire w_clk,//写时钟
input wire rst_n,//复位
input wire w_en,//写使能
input wire [8:0] r_gaddr,//读时钟域过来的格雷码读地址指针
output reg w_full,//写满标志
output wire [8:0] w_addr,//256深度的FIFO写二进制地址
output wire [8:0] w_gaddr//写FIFO地址格雷码编码
);
reg [8:0] addr;
reg [8:0] gaddr;
wire [8:0] addr_wire;
wire [8:0] gaddr_wire;
reg [8:0] r_gaddr_d1,r_gaddr_d2;
wire w_full_wire;
//打两拍进行时钟同步
always @(posedge w_clk or negedge rst_n)
if(rst_n == 1'b0)
{r_gaddr_d2,r_gaddr_d1} <= 18'd0;
else
{r_gaddr_d2,r_gaddr_d1} <= {r_gaddr_d1,r_gaddr};
//产生写ram的地址指针二进制
assign w_addr = addr;
always @(posedge w_clk or negedge rst_n)
if(rst_n == 1'b0)
addr <= 9'd0;
else
addr <= addr_wire;
assign addr_wire = addr + ((~w_full)&w_en);
//转换格雷码编码地址
assign gaddr_wire=(addr_wire>>1)^addr_wire;
always @(posedge w_clk or negedge rst_n)
if(rst_n == 1'b0)
gaddr<=9'd0;
else gaddr <= gaddr_wire;
assign w_gaddr = gaddr;
//写满标志产生完成
always @(posedge w_clk or negedge rst_n)
if(rst_n == 1'b0)
w_full <= 1'b0;
else if({~gaddr_wire[8:7],gaddr_wire[6:0]}==r_gaddr_d2)//根据仿真验证一下打两拍对空满标志的影响???
w_full <=1'b1; //w_full_wire;
else w_full <=1'b0;
endmodule
从图中可以看出,写FIFO控制器的输入输出共7个端口,在接口区也已经有了定义。
(2)读FIFO控制器:r_ctrl.v
module r_ctrl (
input wire r_clk,//读时钟
input wire rst_n,
input wire r_en,//读使能
input wire [8:0] w_gaddr,//写时钟域中的写地址指针
output reg r_empty,//读空标志
output wire [8:0] r_addr,//读地址二进制
output wire [8:0] r_gaddr//读格雷码地址
);
reg [8:0] addr;
reg [8:0] gaddr;
wire [8:0] addr_wire;
wire [8:0] gaddr_wire;
reg [8:0] w_gaddr_d1,w_gaddr_d2;
wire r_empty_wire;
//打两拍进行时钟同步
always @(posedge r_clk or negedge rst_n)
if(rst_n == 1'b0)
{w_gaddr_d2,w_gaddr_d1} <= 18'd0;
else
{w_gaddr_d2,w_gaddr_d1} <= {w_gaddr_d1,w_gaddr};
//二进制的读地址
assign r_addr = addr;
always @(posedge r_clk or negedge rst_n)
if(rst_n == 1'b0)
addr <=9'd0;
else
addr <= addr_wire;
assign addr_wire = addr + ((~r_empty)&r_en);
//格雷码的读地址
assign r_gaddr = gaddr;
assign gaddr_wire = (addr_wire >>1 )^ addr_wire;
always @(posedge r_clk or negedge rst_n)
if(rst_n == 1'b0)
gaddr <= 9'd0;
else
gaddr <= gaddr_wire;
//读空标志的产生
assign r_empty_wire =(gaddr_wire == w_gaddr_d2);
always @(posedge r_clk or negedge rst_n)
if(rst_n == 1'b0)
r_empty<=1'b1;
else //if(gaddr_wire == w_gaddr_d2)//根据仿真验证一下打两拍对空满标志的影响???
r_empty <= r_empty_wire;
//else
//r_empty <= 1'b0;
endmodule
从图中可以看出,读FIFO控制器的输入输出共7个端口,在接口区也已经有了定义。
代码中的信号操作方式与写FIFO控制器中基本相同,这里不再赘述。唯一区别是,在进行读空判断时,直接判断打两拍后的w_gaddr_d2和gaddr_wire时候是否相等即可判断。
(3)FIFO双端口RAM:fifomem.v
module fifomem(
input wire w_clk,
input wire r_clk,
input wire w_en,//来自于FIFO的写控制模块
input wire w_full,//来自于FIFO的写控制模块
input wire [7:0] w_data,//来自于外部数据源
input wire [8:0] w_addr,//来自于FIFO的写控制模块
input wire r_empty,//来自于FIFO的读控制模块
input wire [8:0] r_addr,//来自于FIFO的读控制模块
output wire [7:0] r_data//读数据是从内部ram中读取
);
wire ram_w_en;
assign ram_w_en = w_en &(~w_full);
//ipcore 已经改为256深度的,但是名字没有改
dp_ram_512x8_swsr dp_ram_512x8_swsr_inst (
//写数据接口
.wrclock ( w_clk ),
.wren ( ram_w_en ),
.wraddress ( w_addr[7:0] ),
.data ( w_data ),
//读数据接口
.rdclock ( r_clk ),
.rdaddress ( r_addr[7:0] ),
.q ( r_data )
);
endmodule
此部分将读写FIFO控制器与RAM相连,如图中蓝色线。通过生成一个双端口的RAM的IP,其深度为512,位宽为8,用于FIFO的缓存。
(4)整理的顶层文件:ex_fifo.v
module ex_fifo(
input wire w_clk,
input wire r_clk,
input wire rst_n,
input wire w_en,
input wire [7:0] w_data,
output wire w_full,
input wire r_en,
output wire [7:0] r_data,
output wire r_empty
);
wire [8:0] r_gaddr;
wire [8:0] w_addr;
wire [8:0] w_gaddr;
wire [8:0] r_addr;
w_ctrl w_ctrl_inst(
.w_clk (w_clk),//写时钟
.rst_n (rst_n),//复位
.w_en (w_en),//写使能
.r_gaddr (r_gaddr),//读时钟域过来的格雷码读地址指针
.w_full (w_full),//写满标志
.w_addr (w_addr),//256深度的FIFO写二进制地址
.w_gaddr (w_gaddr)//写FIFO地址格雷码编码
);
fifomem fifomem_inst(
.w_clk (w_clk),
.r_clk (r_clk),
.w_en (w_en),//来自于FIFO的写控制模块
.w_full (w_full),//来自于FIFO的写控制模块
.w_data (w_data),//来自于外部数据源
.w_addr (w_addr),//来自于FIFO的写控制模块
.r_empty (r_empty),//来自于FIFO的读控制模块
.r_addr (r_addr),//来自于FIFO的读控制模块
.r_data (r_data)//读数据是从内部ram中读取
);
r_ctrl r_ctrl_inst(
.r_clk (r_clk),//读时钟
.rst_n (rst_n),
.r_en (r_en),//读使能
.w_gaddr (w_gaddr),//写时钟域中的写地址指针
.r_empty (r_empty),//读空标志
.r_addr (r_addr),//读地址二进制
.r_gaddr (r_gaddr)//读格雷码地址
);
endmodule
此部分功能是将三个模块的所有接口都引出来,并将3个模块进行连接。
`timescale 1ns/1ns
module tb_ex_fifo;
reg r_clk,w_clk,rst_n;
reg w_en;
reg [7:0] w_data;
reg r_en;
wire w_full;
wire r_empty;
wire [7:0] r_data;
parameter CLK_P=20;
initial begin
rst_n<=0;
r_clk<=0;
w_clk<=0;
#200
rst_n=1;
end
//写的初始化模块
initial begin
w_en=0;
w_data=0;
#300
write_data(256);
end
//读的初始化模块
initial begin
r_en =0;
@(posedge w_full);
#40;
read_data(256);
end
always #(CLK_P/2) r_clk =~r_clk;
always #(CLK_P/2) w_clk =~w_clk;
ex_fifo ex_fifo_inst(
.w_clk (w_clk),
.r_clk (r_clk),
.rst_n (rst_n),
.w_en (w_en),
.w_data (w_data),
.w_full (w_full),
.r_en (r_en),
.r_data (r_data),
.r_empty (r_empty)
);
task write_data(len);
integer i,len;
begin
for (i=0;i<len;i=i+1)
begin
@(posedge w_clk);
w_en=1'b1;
w_data=i;
end
@(posedge w_clk);
w_en = 1'b0;
w_data =0;
end
endtask
task read_data(len);
integer i,len;
begin
for (i=0;i<len;i=i+1)
begin
@(posedge r_clk);
r_en=1'b1;
end
@(posedge r_clk);
r_en = 1'b0;
end
endtask
endmodule
quit -sim
.main clear
vlib work
vlog ./tb_ex_fifo.v
vlog ./../design/*.v
vlog ./../quartus_prj/ipcore_dir/dp_ram_512x8_swsr.v
vlog ./altera_lib/*.v
vsim -voptargs=+acc work.tb_ex_fifo
add wave tb_ex_fifo/*
add wave tb_ex_fifo/ex_fifo_inst/*
add wave -divider {w}
add wave tb_ex_fifo/ex_fifo_inst/w_ctrl_inst/*
add wave -divider {R}
add wave tb_ex_fifo/ex_fifo_inst/r_ctrl_inst/*
run 25us
将256个数据写完之后,写使能信号w_en拉低,同时w_full信号拉高表示已写满。
将256个数据读完之后,读使能信号r_en拉低,同时r_empty信号拉高表示已读空。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。