ZYNQ基础----使用AXI-LITE接口访问Block RAM

1. 前言

  在之前的博客中有介绍到AXI接口和AXI-stream接口,AXI-lite接口也经常使用。最近恰好在做一个小的项目,需要对采集到的ADC数据进行缓存。由于采集到的数据是经过数字下变频DDC的,因此其速率不会很快,根据速率变换的不同,可以在1M到50K之间。因此也不需要使用外部的存储器,片上的RAM就足够了。
  由于使用到的ZYNQ,希望使用ZYNQ的网口来进行数据的传输,因此就免不了使用AXI接口。这个就相较于使用存粹的FPGA逻辑的开发要稍微复杂一点。但是在进行数据传输的时候,使用lwip以太网就可以很简单的进行传输了,有失必有得,有得必有失。

2. AXI-Lite 接口时序

  AXI-Lite的接口时序和AXI_MM也就是AXI4的时序基本上是一致的,AXI4是可以突发的,但AXI-Lite每次只能传输一个数据。
ZYNQ基础----使用AXI-LITE接口访问Block RAM_第1张图片

ZYNQ基础----使用AXI-LITE接口访问Block RAM_第2张图片
  最近调试这个AXI-lite接口,通过AXI4-lite接口与BRAM进行通信,由于BRAM采用的是传统的接口,没有使用BRAM controller这个IP,因此需要自己手动来写IP完成和BRAM的数据交互。但是在使用AXI-Lite接口的时候发现实际上的AXI4-Lite接口时序,在Slave IP中好像与常规的不一样。下面是通过ILA抓取到的AXI4-Lite接口的数据。
需要说明的,这个是Slave器件的时序图:
ZYNQ基础----使用AXI-LITE接口访问Block RAM_第3张图片
  把写时序这个过程绘制下来如下图所示:红色的信号是该IP输出的信号。可以看到,这个AXI-Lite接口和常规的AXI时序有点不太。一般情况下的AXI时序,都是首先给出地址,地址响应后才进行数据的传输。
ZYNQ基础----使用AXI-LITE接口访问Block RAM_第4张图片
  AXI4-Lite的读时序也和写时序相同,相较于常规的时序有一点不同。
ZYNQ基础----使用AXI-LITE接口访问Block RAM_第5张图片
ZYNQ基础----使用AXI-LITE接口访问Block RAM_第6张图片

2.1 Block RAM 设置

  弄清了上面的AXI-Lite时序滞后,就需要对BRAM接口进行简单的设置的,使用纯逻辑进行开发的时候,RAM这个IP那是使用了相当多的次数,Block Design中使用的时候,还是有一点区别。
  这里的模式选择Stand Alone模式,因为是自己来实现这个AXI接口,将DDC后的数据写入到RAM中。使用真双口RAM,这个真双口RAM在纯逻辑开发的时候,用的不多,在Block Design的设计中用得还是挺多得。
ZYNQ基础----使用AXI-LITE接口访问Block RAM_第7张图片
  接下来就是位宽和深度得设置,这里设置位宽位32,深度为2048.因此采样得IQ对,IQ数据都是32位,在向上位机传递数据的时候,都是以IQ对来传递的,上位机接收数据的时候需要最小的IQ对位512。
ZYNQ基础----使用AXI-LITE接口访问Block RAM_第8张图片

2.2 封装AXI-lite接口IP

  封装两个IP,一个用来对BRAM进行写, 一个用来读。

// 该模块只向block ram进行数据的写入
module axi_bram_write#
(
    parameter integer AXI_DATA_WIDTH = 32,
    parameter integer AXI_ADDR_WIDTH = 16,
    parameter integer BRAM_DATA_WIDTH = 32,
    parameter integer BRAM_ADDR_WIDTH = 10
)(
    // System signals
    input  wire                         aclk,
    input  wire                         aresetn,
  
    // Slave side
    input  wire [AXI_ADDR_WIDTH-1:0]    s_axi_awaddr,  // AXI4-Lite slave: Write address
    input  wire                         s_axi_awvalid, // AXI4-Lite slave: Write address valid
    output wire                         s_axi_awready, // AXI4-Lite slave: Write address ready
    input  wire [AXI_DATA_WIDTH-1:0]    s_axi_wdata,   // AXI4-Lite slave: Write data
    input  wire [AXI_DATA_WIDTH/8-1:0]  s_axi_wstrb,   // AXI4-Lite slave: Write strobe
    input  wire                         s_axi_wvalid,  // AXI4-Lite slave: Write data valid
    output wire                         s_axi_wready,  // AXI4-Lite slave: Write data ready
    output wire [1:0]                   s_axi_bresp,   // AXI4-Lite slave: Write response
    output wire                         s_axi_bvalid,  // AXI4-Lite slave: Write response valid
    input  wire                         s_axi_bready,  // AXI4-Lite slave: Write response ready
    input  wire [AXI_ADDR_WIDTH-1:0]    s_axi_araddr,  // AXI4-Lite slave: Read address
    input  wire                         s_axi_arvalid, // AXI4-Lite slave: Read address valid
    output wire                         s_axi_arready, // AXI4-Lite slave: Read address ready
    output wire [AXI_DATA_WIDTH-1:0]    s_axi_rdata,   // AXI4-Lite slave: Read data
    output wire [1:0]                   s_axi_rresp,   // AXI4-Lite slave: Read data response
    output wire                         s_axi_rvalid,  // AXI4-Lite slave: Read data valid
    input  wire                         s_axi_rready,  // AXI4-Lite slave: Read data ready
  
    // BRAM port
    output wire                         bram_porta_clk,
    output wire                         bram_porta_rst,
    output wire [BRAM_ADDR_WIDTH-1:0]   bram_porta_addr,
    output wire [BRAM_DATA_WIDTH-1:0]   bram_porta_wrdata,
    output wire [BRAM_DATA_WIDTH/8-1:0] bram_porta_we
);


//====================================================
//axi_lite port
//====================================================
reg     awready;
reg     wready  ;
reg     bvalid ;

assign s_axi_bresp = 2'd0;
assign s_axi_wready = wready;
assign s_axi_awready = awready;
assign s_axi_bvalid = bvalid;

always @(posedge aclk) begin
    if (aresetn == 1'b0) begin
        awready <= 1'b0;
    end
    else if (s_axi_awvalid == 1'b1 && s_axi_wvalid == 1'b1) begin
        awready <= 1'b1;
    end
    else begin
        awready <= 1'b0;
    end
end

always @(posedge aclk) begin
    if (aresetn == 1'b0) begin
        wready <= 1'b1;
    end
    else if (s_axi_wvalid == 1'b1) begin
        wready <= 1'b0;
    end
    else begin
        wready <= 1'b1;
    end
end

always @(posedge aclk) begin
    if (aresetn == 1'b0) begin
        bvalid <= 1'b0;
    end
    else if (s_axi_awvalid == 1'b1 && s_axi_wvalid == 1'b0) begin
        bvalid <= 1'b1;
    end
    else begin
        bvalid <= 1'b0;
    end
end

//====================================================
//bram port
//====================================================

function integer clogb2 (input integer value);
    for(clogb2 = 0; value > 0; clogb2 = clogb2 + 1) value = value >> 1;
endfunction

localparam integer ADDR_LSB = clogb2(AXI_DATA_WIDTH/8 - 1);

assign bram_porta_clk = aclk;
assign bram_porta_rst = ~aresetn;
// 字节地址到Block RAM的存储地址的转换
assign bram_porta_addr = s_axi_awaddr[ADDR_LSB+BRAM_ADDR_WIDTH-1:ADDR_LSB];
assign bram_porta_wrdata = s_axi_wdata;
// 写入数据的字节有效信号
assign bram_porta_we = s_axi_awvalid & s_axi_wvalid ? s_axi_wstrb : {(BRAM_DATA_WIDTH/8){1'b0}};

endmodule
module axi_bram_reader #
(
  	parameter integer AXI_DATA_WIDTH = 32,
  	parameter integer AXI_ADDR_WIDTH = 16,
  	parameter integer BRAM_DATA_WIDTH = 32,
  	parameter integer BRAM_ADDR_WIDTH = 10
)	
(	
  	// System signals
  	input  wire                       aclk,
  	input  wire                       aresetn,
	
  	// Slave side
  	input  wire [AXI_ADDR_WIDTH-1:0]  s_axi_awaddr,  // AXI4-Lite slave: Write address
  	input  wire                       s_axi_awvalid, // AXI4-Lite slave: Write address valid
  	output wire                       s_axi_awready, // AXI4-Lite slave: Write address ready
  	input  wire [AXI_DATA_WIDTH-1:0]  s_axi_wdata,   // AXI4-Lite slave: Write data
  	input  wire                       s_axi_wvalid,  // AXI4-Lite slave: Write data valid
  	output wire                       s_axi_wready,  // AXI4-Lite slave: Write data ready
  	output wire [1:0]                 s_axi_bresp,   // AXI4-Lite slave: Write response
  	output wire                       s_axi_bvalid,  // AXI4-Lite slave: Write response valid
  	input  wire                       s_axi_bready,  // AXI4-Lite slave: Write response ready
  	input  wire [AXI_ADDR_WIDTH-1:0]  s_axi_araddr,  // AXI4-Lite slave: Read address
  	input  wire                       s_axi_arvalid, // AXI4-Lite slave: Read address valid
  	output wire                       s_axi_arready, // AXI4-Lite slave: Read address ready
  	output wire [AXI_DATA_WIDTH-1:0]  s_axi_rdata,   // AXI4-Lite slave: Read data
  	output wire 					  s_axi_rlast,
  	output wire [1:0]                 s_axi_rresp,   // AXI4-Lite slave: Read data response
  	output wire                       s_axi_rvalid,  // AXI4-Lite slave: Read data valid
  	input  wire                       s_axi_rready,  // AXI4-Lite slave: Read data ready
	
  	// BRAM port
  	output wire                       bram_porta_clk,
  	output wire                       bram_porta_rst,
  	output wire [BRAM_ADDR_WIDTH-1:0] bram_porta_addr,
  	input  wire [BRAM_DATA_WIDTH-1:0] bram_porta_rddata
);

reg 		arready;
reg 		rvalid ;
reg 		rlast	;

assign s_axi_arready = arready;
assign s_axi_rdata = bram_porta_rddata;
assign s_axi_rresp = 2'd0;
assign s_axi_rvalid = rvalid;
assign s_axi_rlast = rlast;

always @(posedge aclk) begin
	if (aresetn == 1'b0) begin
		arready <= 1'b0;	
	end
	else if (s_axi_arvalid == 1'b1 && s_axi_arready == 1'b0) begin
		arready <= 1'b1;
	end
	else begin
		arready <= 1'b0;
	end
end

always @(posedge aclk) begin
	if (aresetn == 1'b0)  begin
		rvalid <= 1'b0;
		rlast <= 1'b0;
	end
	else if (arready == 1'b1) begin
		rvalid <= 1'b1;	
		rlast <= 1'b1;
	end
	else  begin
		rvalid <= 1'b0;
		rlast <= 1'b0;
	end
end


//====================================================
// bram port
//====================================================

function integer clogb2 (input integer value);
	for(clogb2 = 0; value > 0; clogb2 = clogb2 + 1) value = value >> 1;
endfunction

localparam integer ADDR_LSB = clogb2(AXI_DATA_WIDTH/8 - 1);

assign bram_porta_clk = aclk;
assign bram_porta_rst = ~aresetn;
assign bram_porta_addr = s_axi_araddr[ADDR_LSB+BRAM_ADDR_WIDTH-1:ADDR_LSB];

endmodule

  最终搭建好的Block Design如下:
ZYNQ基础----使用AXI-LITE接口访问Block RAM_第9张图片
  地址映射如下:
ZYNQ基础----使用AXI-LITE接口访问Block RAM_第10张图片

3 SDK下验证

  搭建完毕后,就可以简单验证以下,写一个循环读写的简单的demo.
  只需要正确的掌握映射的地址就可以

#include 
#include "platform.h"
#include "xil_printf.h"

#define 	READ_ADDR   0x43C00000
#define 	WRITE_ADDR	0x43C02000

char * wrBuf = (char *)WRITE_ADDR;
char * rdBuf = (char *)READ_ADDR;
int main()
{
    init_platform();
    for	(u8 i = 0; i < 100; i++)
    {
    	*(wrBuf + i) = i;
    }
    for(int i = 0; i < 100; i++)
    {
    	xil_printf("rdBuf[%d] = %d \n", i, rdBuf[i]);
    }

    print("Hello World\n\r");

    cleanup_platform();
    return 0;
}

你可能感兴趣的:(ZYNQ,Block,RAM)