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每次只能传输一个数据。
在这里插入图片描述

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

2.1 Block RAM 设置

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

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如下:
在这里插入图片描述
  地址映射如下:
在这里插入图片描述

3 SDK下验证

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

#include <stdio.h>
#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;
}

  • 4
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Zynq-7000是Xilinx推出的一款全新的可编程系统芯片(SoC),其核心特点是将ARM Cortex-A9处理器与可编程逻辑器件(FPGA)集成在一起。ZED是由Digilent和Avnet合作生产的一款基于Zynq-7000 SoC的开发板。 关于Zynq-7000-ZED的资料,您可以从以下几个方面进行了解和获取: 1. 官方文档:Xilinx官方网站提供了针对Zynq-7000 SoC的详细技术资料和应用指南,包括技术手册、用户指南、应用笔记等。这些资料涵盖了硬件设计、软件开发、调试和测试等方面的内容,非常全面详细。 2. 社区论坛:在Xilinx官方网站上,您可以加入或搜索Xilinx社区论坛。这个论坛是一个交流平台,可以与其他用户分享经验和解决问题。在论坛上,您可以找到很多关于使用Zynq-7000-ZED进行开发的实际案例和技术讨论。 3. 参考设计和开发板说明:Digilent和Avnet官方网站上提供了基于Zynq-7000-ZED开发板的一些参考设计和示例代码。这些参考设计涵盖了不同的应用领域,包括图像处理、通信、机器学习等。通过研究这些设计,您可以更好地了解如何使用Zynq-7000-ZED进行应用开发。 4. 在线教程和视频:在互联网上,有许多第三方的在线教程和视频可以帮助您快速入门Zynq-7000-ZED的开发。这些教程和视频通常以实际的项目为例,展示了如何进行硬件设计、软件开发和调试等过程。 总之,Zynq-7000-ZED的资料非常丰富,包括官方文档、社区论坛、参考设计、在线教程等多种渠道。只要您愿意花时间去研究和学习,就能够轻松掌握Zynq-7000-ZED的开发技术和应用方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值