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;
}

### ZYNQ中的PL PS AXI接口配置及使用方法 #### 一、AXI接口概述 在ZYNQ器件中,支持多种类型的AXI接口用于PS(处理系统)和PL(可编程逻辑)之间的通信。这些接口包括AXI-Lite, AXI4以及AXI-Stream[^3]。 #### 二、AXI Lite接口特点及其应用场合 对于简单的寄存器访问或者少量的数据交换场景下,通常会选择AXI-Lite接口来完成PS与PL间的交互操作。此接口具有较低带宽但是结构简单易于理解和实现的特点,在许多情况下足以满足需求[^1]。 #### 三、具体实例分析 当采用AXI-Lite作为两者间的主要通讯手段时,可以有两种不同的设计方案: 1. **方案A**: 将来自PS侧的数据先存储于位于PL内部的一个Block RAM (BRAM),之后再由其他部分负责取出并进一步处理; 2. **方案B**: 让PS能够直接向PL内的目标位置写入所需的信息而不必经过中间缓存环节;这两种方式各有优劣取决于实际应用场景的需求[^4]。 #### 四、高级功能扩展 除了基本的读/写之外,如果涉及到更大规模的数据流传输,则可能需要用到更复杂的协议比如AXI Stream或是借助FIFO组件构建起从内存至逻辑区域的有效桥梁,像AXI-FIFO-MM2S就是这样一个例子它实现了从PS端到PL端高效稳定的数据搬运过程[^2]。 ```python # Python伪代码展示如何初始化AXI Lite IP核参数设置 def configure_axi_lite(base_address): # 设置基础地址 axi_lite.set_base_addr(base_address) # 配置中断使能位 axi_lite.enable_interrupt(True) configure_axi_lite(0x43C00000) ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值