标签:
作者:MiS603开发团队
日期:20150911
公司:南京米联电子科技有限公司
论坛:www.osrc.cn
EAT博客:http://blog.chinaaet.com/whilebreak
博客园:http://www.cnblogs.com/milinker/
上一章节笔者讲解了FIFO的简单使用,那么FPGA的Block RAM(BRAM) 也是FPGA珍贵和重要的存储单元。本章将讲解BRAM的使用,和前面讲解FIFO使用类似,本章通过UART经过PC将数据发送给BRAM,BRAM缓存后再通过UART返回给电脑,让读者有一个全局的认识。当具体项目的时候,可以根据项目要求灵活运用。
Step1:新建工程BRAM_TEST
Step2:添加BRAM IP CORE
Step3:选择框选的接口类型,Native 主要是逻辑代码控制简单,AXI4稍微复杂一点。
Step4:选择简单双端口RAM
Step5:设置BRAM写地址空间为8bit X 32 depth
Step6:单击NEXT
Step8:单击NEXT
Step9:单击Generate产生IP CORE
Step10:添加IP CORE 并且添加 UART串口驱动
module BRAM_TEST(
input CLK_50MHZ_i,
input rst_n_i,
input uart_rx_i,
output uart_tx_o
);
wire IsRxdDone,UartIsFull;
wire [7:0] BRAM_din;
wire [7:0] BRAM_dout;
wire BRAM_wr_en;
reg UartIsSta;
parameter READ_BRAM0=0;
parameter READ_BRAM1=1;
parameter BRAM_BASE0 = 0;
parameter BRAM_BASE1 = 16;
reg BASE_FLAG;//基地址标准
//写地址累加
reg [4:0] bram_addra_r;
always @(posedge CLK_50MHZ_i)begin
if(~rst_n_i)begin
bram_addra_r <= 5‘d0;
BASE_FLAG <= 1‘b0;
end
else if(IsRxdDone)begin
bram_addra_r <= bram_addra_r + 1‘b1;//地址累加
end
else if(bram_addra_r==5‘d16)begin
bram_addra_r <= 5‘d0;
BASE_FLAG <= ~BASE_FLAG;
end
end
//读地址累加
reg [4:0] bram_addrb_r;
always @(posedge CLK_50MHZ_i)begin
if(~rst_n_i)begin
bram_addrb_r <= 5‘d0;
end
else if(UartIsSta)begin
bram_addrb_r <= bram_addrb_r + 1‘b1;//地址累加
end
else if(bram_addrb_r==5‘d16)begin
bram_addrb_r <= 5‘d0;
end
end
wire [4:0] bram_addra;
wire [4:0] bram_addrb;
//写BRAM地址+基地址
assign bram_addra = BASE_FLAG ? (bram_addra_r + BRAM_BASE1) : (bram_addra_r + BRAM_BASE0);//产生写地址
//读BRAM地址+基地址
assign bram_addrb = BASE_FLAG ? (bram_addrb_r + BRAM_BASE0) : (bram_addrb_r + BRAM_BASE1);//产生读写地址
reg f_s;
always @(posedge CLK_50MHZ_i)begin
if(~rst_n_i)begin
f_s <= 1‘b0;
end
else begin
case(f_s)
READ_BRAM0:begin //写完一次 准备读
if(bram_addra_r==5‘d16)
f_s <= READ_BRAM1;
end
READ_BRAM1:begin //读完一次 返回
if(bram_addrb_r==5‘d16)begin//读的速度需要大于或者等于写的速度,由于是串口读写速度一样,要求每次发送16bytes数据中间有一个时间停顿
f_s <= READ_BRAM0;
end
end
endcase
end
end
always @(posedge CLK_50MHZ_i)begin
if(~rst_n_i)begin
UartIsSta <= 1‘b0;
end
else begin
if(f_s==READ_BRAM1&&(~UartIsFull))
UartIsSta <= ~UartIsSta;
else UartIsSta <= 1‘b0;
end
end
assign BRAM_wr_en = IsRxdDone;//使能写
//BRAM 读写模块
BRAM BRAM_0 (
.clka(~CLK_50MHZ_i), // input clka
.wea(BRAM_wr_en), // input [0 : 0] wea
.addra(bram_addra), // input [4 : 0] addra
.dina(BRAM_din), // input [7 : 0] dina
.clkb(~CLK_50MHZ_i), // input clkb
.addrb(bram_addrb), // input [4 : 0] addrb
.doutb(BRAM_dout) // output [7 : 0] doutb
);
UartRxd U1 (.Clk_i(CLK_50MHZ_i),.RevD(BRAM_din),.IsDone(IsRxdDone),.Rxd_i(uart_rx_i));//recieve
UartTxd U2 (.Clk_i(CLK_50MHZ_i),.IsSta(UartIsSta),.IsFull(UartIsFull),.SendD(BRAM_dout),.Txd_o(uart_tx_o)); //send
endmodule
以上程序的关键就是如何实现BRAM地址空间的切换。
笔者把一个连续的BRAM 地址空间氛围两部分,第一部分是以BASE0为起始地址,一个是以BASE1为起始地址。为了实现缓存16 bytes 的数据,地址空间总计32 bytes深度,第一部分16 bytes,第二部分也是16 bytes。假设串口发送过来的数据首先保存到基地址为BASE0的内存部分。当这部分地址接收完成后,就可以从此空间读取数据。为了保障数据的流畅性,当从BASE0读取数据的时候,BASE1可以接收新的数据。这样就实现了一个二级缓存,实现了缓存一组数据,这个应用在视频处理方面非常实用。
程序上,写地址空间的增量bram_addra_r在每次串口接收到新数据收增加1,读地址空间增量bram_addrb_r在每次读取一个数据后,地址空间增加1。然后通过加上基地址的偏移量,实现计算出内存对应空间的读写地址。通过BASE_FLAG标志,实现读写地址空间的切换。实际上,笔者这里就是采用了一种叫做“乒乓”的处理方式,当对写地址空间写的时候,对读的地址空间读。址空间实现“乒乓”处理,这样在处理数据流方面,非常有用。
关于串口模块的收发,直接调用笔者写好的程序,不在详细介绍。
波特率设置到38400,每次发送16 bytes的数据,立刻返回16bytes的数据。
本章实现了简单双口RAM 实现数据流的“乒乓”缓存方式,通过串口实现了数据流输出,和输出的验证。
标签:
原文地址:http://www.cnblogs.com/milinker/p/4804845.html