米联客FDMA3.2源码分析以及控制BRAM、DDR3读写验证

news2024/9/23 19:27:42

文章目录

  • 一、FDMA简介
  • 二、读写操作时序
    • 2.1 写时序
    • 2.2 读时序
  • 三、FDMA源码分析
  • 四、源码仿真验证
    • 4.1 FDMA控制代码
    • 4.2 系统框图
    • 4.3 仿真结果
      • 4.3.1 写通道
      • 4.3.2 读通道
  • 五、使用FDMA控制BRAM读写测试
    • 5.1 系统框图
    • 5.2 读写数据控制模块
    • 5.3 仿真结果
    • 5.4 下板验证
  • 六、使用FDMA控制DDR3读写测试
    • 6.1 BD系统框图
    • 6.2 ddr读写数据控制程序
    • 6.3 顶层框图
    • 6.4 仿真结果
    • 6.5 下板验证
  • 参考


一、FDMA简介

  FDMA (Fast Direct Memory Access)是米联客的基于 AXI4 总线协议定制的一个 DMA 控制器,对 AXI4-FULL 总线接口进行了封装,同时定义了简单的 APP 接口提供用户调用 AXI4 总线实现数据交互。有了这个 IP 我们可以统一实现用 FPGA 代码直接读写 PL DDR 或者 ZYNQ/ZYNQMP SOC PS 的 DDR 或者 BRAM。

二、读写操作时序

2.1 写时序

  以下是FDMA的写操作时序图:
在这里插入图片描述
  fdma_wready是用户给的,设置为1表示让FDMA准备好。当 fdma_wbusy=0 的时候代表 FDMA 的总线非忙,可以进行一次新的 FDMA 传输;这时候拉高 fdma_wreq=1,同时给出 本次fdma 突发传输的起始地址和本次需要传输的数据大小fdma_wsize(以 bytes 为单位)。当 fdma_wvalid=1 的时候需要给出有效的数据,写入 AXI 总线。当最后一个数写完后,fdma_wvalid 和fdma_wbusy 变为 0。
  AXI4 总线最大的 burst lenth 是 256,而经过封装后,用户接口的 fdma_size 可以任意大小的,fdma ip 内部代码控制每次 重新计算AXI4 总线的 Burst 长度,

2.2 读时序

  以下是FDMA的读操作时序图:
在这里插入图片描述
  fdma_wready是用户给的,设置为1表示让FDMA准备好。当 fdma_rbusy=0 的时候代表 FDMA 的总线非忙,可以进行一次新的 FDMA 传输;这时候拉高 fdma_rreq=1,同时给出 本次fdma 突发传输的起始地址和本次需要传输的数据大小fdma_rsize(以 bytes 为单位)。当 fdma_rvalid=1 的时候表示读出来的数据有效。当最后一个数读完后,fdma_rvalid 和fdma_rbusy 变为 0。

三、FDMA源码分析

  以下是FDMA源码,添加了个人理解的部分信号的注释;如果注释不对,还请指出:


/*******************************MILIANKE*******************************
*Company : MiLianKe Electronic Technology Co., Ltd.
*WebSite:https://www.milianke.com
*TechWeb:https://www.uisrc.com
*tmall-shop:https://milianke.tmall.com
*jd-shop:https://milianke.jd.com
*taobao-shop1: https://milianke.taobao.com
*Create Date: 2023/03/23
*Module Name:
*File Name:
*Description: 
*The reference demo provided by Milianke is only used for learning. 
*We cannot ensure that the demo itself is free of bugs, so users 
*should be responsible for the technical problems and consequences
*caused by the use of their own products.
*Copyright: Copyright (c) MiLianKe
*All rights reserved.
*Revision: 3.2
*Signal description
*1) I_ input
*2) O_ output
*3) IO_ input output
*4) S_ system internal signal
*5) _n activ low
*6) _dg debug signal 
*7) _r delay or register
*8) _s state mechine
*********************************************************************/

/*********uiFDMA(AXI-FAST DMA Controller)基于AXI总线的自定义内存控制器***********
--1.代码简洁,占用极少逻辑资源,代码结构清晰,逻辑设计严谨,读写对称
--2.fdma控制信号,简化了AXI总线的控制,根据I_fdma_wsize和I_fdma_rsize可以自动完成AXI总线的控制,完成数据的搬运
--3版本号说明
--1.0 初次发布
--2.0 修改型号定义,解决1.0版本中,last信号必须连续前一个valid的bug
--3.0 修改,AXI-burst最大burst 256
--3.1 修改可以设置AXI burst长度
--3.2 解决3.1版本中,当总的burst长度是奇数的时候出现错误,修改端口命名规则,设置I代表了输入信号,O代表了输出信号
*********************************************************************/
`timescale 1ns / 1ns
module uiFDMA#
(
parameter  integer         M_AXI_ID_WIDTH			= 3		    , //ID,demo中没用到
parameter  integer         M_AXI_ID			        = 0		    , //ID,demo中没用到
parameter  integer         M_AXI_ADDR_WIDTH			= 32		, //内存地址位宽
parameter  integer         M_AXI_DATA_WIDTH			= 128		, //AXI总线的数据位宽
parameter  integer		   M_AXI_MAX_BURST_LEN      = 64          //AXI总线的burst 大小,对于AXI4,支持任意长度(ps:AXI4协议最大支持256,如果这数据大于256,则分多次突发),对于AXI3以下最大16
)
(
input   wire [M_AXI_ADDR_WIDTH-1 : 0]      I_fdma_waddr          ,//FDMA写通道地址
input                                      I_fdma_wareq          ,//FDMA写通道写请求
input   wire [15 : 0]                      I_fdma_wsize          ,//FDMA写通道一次突发的传输数据个数                                            
output                                     O_fdma_wbusy          ,//FDMA处于BUSY状态,AXI总线正在写操作  
				
input   wire [M_AXI_DATA_WIDTH-1 :0]       I_fdma_wdata		     ,//FDMA写数据
output  wire                               O_fdma_wvalid         ,//FDMA写数据有效
input	wire                               I_fdma_wready		 ,//让FDMA写准备好,用户可以写数据

input   wire [M_AXI_ADDR_WIDTH-1 : 0]      I_fdma_raddr          ,// FDMA读通道地址
input                                      I_fdma_rareq          ,// FDMA读通道请求
input   wire [15 : 0]                      I_fdma_rsize          ,// FDMA读通道一次突发的传输数据个数                                   
output                                     O_fdma_rbusy          ,// FDMA处于BUSY状态,AXI总线正在读操作 
				
output  wire [M_AXI_DATA_WIDTH-1 :0]       O_fdma_rdata		     ,// FDMA读数据
output  wire                               O_fdma_rvalid         ,// FDMA读数据有效
input	wire                               I_fdma_rready		 ,// 让FDMA读准备好,用户可以读数据

//以下为AXI总线信号		
input 	wire  								M_AXI_ACLK			,
input 	wire  								M_AXI_ARESETN		,
output 	wire [M_AXI_ID_WIDTH-1 : 0]		    M_AXI_AWID			,
output 	wire [M_AXI_ADDR_WIDTH-1 : 0] 	    M_AXI_AWADDR		,
output 	wire [7 : 0]						M_AXI_AWLEN			,
output 	wire [2 : 0] 						M_AXI_AWSIZE		,
output 	wire [1 : 0] 						M_AXI_AWBURST		,
output 	wire  								M_AXI_AWLOCK		,
output 	wire [3 : 0] 						M_AXI_AWCACHE		,
output 	wire [2 : 0] 						M_AXI_AWPROT		,  
output 	wire [3 : 0] 						M_AXI_AWQOS			,
output 	wire  								M_AXI_AWVALID		,
input	wire  								M_AXI_AWREADY		,
output  wire [M_AXI_ID_WIDTH-1 : 0] 		M_AXI_WID			,
output  wire [M_AXI_DATA_WIDTH-1 : 0] 	    M_AXI_WDATA			,
output  wire [M_AXI_DATA_WIDTH/8-1 : 0] 	M_AXI_WSTRB			,
output  wire  								M_AXI_WLAST			, 			
output  wire  								M_AXI_WVALID		,
input   wire  								M_AXI_WREADY		,
input   wire [M_AXI_ID_WIDTH-1 : 0] 		M_AXI_BID			,
input   wire [1 : 0] 						M_AXI_BRESP			,
input   wire  								M_AXI_BVALID		,
output  wire  								M_AXI_BREADY		, 
output  wire [M_AXI_ID_WIDTH-1 : 0] 		M_AXI_ARID			,	 

output  wire [M_AXI_ADDR_WIDTH-1 : 0] 	    M_AXI_ARADDR		,	 	
output  wire [7 : 0] 						M_AXI_ARLEN			,	 
output  wire [2 : 0] 						M_AXI_ARSIZE		,	 
output  wire [1 : 0] 						M_AXI_ARBURST		,	 
output  wire  								M_AXI_ARLOCK		,	 
output  wire [3 : 0] 						M_AXI_ARCACHE		,	 
output  wire [2 : 0] 						M_AXI_ARPROT		,	 
output  wire [3 : 0] 						M_AXI_ARQOS			,	 	   
output  wire  								M_AXI_ARVALID		,	 
input   wire  								M_AXI_ARREADY		,	 
input   wire [M_AXI_ID_WIDTH-1 : 0] 		M_AXI_RID			,	 
input   wire [M_AXI_DATA_WIDTH-1 : 0] 	    M_AXI_RDATA			,	 
input   wire [1 : 0] 						M_AXI_RRESP			,	 
input   wire  								M_AXI_RLAST			,	 
input   wire  								M_AXI_RVALID		,    
output  wire  								M_AXI_RREADY				
	);

//调用函数:计算数据位宽
function integer clogb2 (input integer bit_depth);              
begin                                                        
	    for(clogb2=0; bit_depth>0; clogb2=clogb2+1)                   
	        bit_depth = bit_depth >> 1;                               
end                                                           
endfunction 


localparam AXI_BYTES =  M_AXI_DATA_WIDTH/8;                                                         //AXI传输一个数据的字节大小
localparam [3:0] MAX_BURST_LEN_SIZE = clogb2(M_AXI_MAX_BURST_LEN)  -1;                              //突发大小的位宽
                                                    
//fdma axi write----------------------------------------------
reg             [M_AXI_ADDR_WIDTH-1 : 0]            axi_awaddr  =0;                                 //AXI4 写地址
reg                                                 axi_awvalid = 1'b0;                             //AXI4 写地有效
wire            [M_AXI_DATA_WIDTH-1 : 0]            axi_wdata   ;                                   //AXI4 写数据
wire                                                axi_wlast   ;                                   //AXI4 写数据LAST信号
reg                                                 axi_wvalid  = 1'b0;                             //AXI4 写数据有效
wire                                                w_next= (M_AXI_WVALID & M_AXI_WREADY);          //AXI4 写数据握手信号
reg             [8 :0]                              wburst_len  = 1  ;                              //本次写传输的axi burst长度,代码会自动计算每次axi传输的burst 长度
reg             [8 :0]                              wburst_cnt  = 0  ;                              //一次突发写中,写数据个数计数器(因为AXI4一次突发最多传输256)
reg             [15:0]                              wfdma_cnt   = 0  ;                              //总共本次传输中FDMA写数据个数计数器
reg                                                 axi_wstart_locked   =0;                         //axi 传输进行中,lock住,用于时序控制
wire            [15:0]                              axi_wburst_size =    wburst_len * AXI_BYTES;    //AXI4 计算一次突发传输,需要累加的地址长度

assign M_AXI_AWID           = M_AXI_ID;                                                                 //写地址ID,用来标志一组写信号, M_AXI_ID是通过参数接口定义
assign M_AXI_AWADDR         = axi_awaddr;
assign M_AXI_AWLEN          = wburst_len - 1;                                                           //AXI4 burst的长度,等于实际突发长度-1
assign M_AXI_AWSIZE         = clogb2(AXI_BYTES-1);                                                      //突发大小
assign M_AXI_AWBURST        = 2'b01;                                                                    //AXI4的busr类型INCR模式,地址递增
assign M_AXI_AWLOCK         = 1'b0;
assign M_AXI_AWCACHE        = 4'b0010;                                                                  //不使用cache,不使用buffer
assign M_AXI_AWPROT         = 3'h0;
assign M_AXI_AWQOS          = 4'h0;
assign M_AXI_AWVALID        = axi_awvalid;
assign M_AXI_WDATA          = axi_wdata;
assign M_AXI_WSTRB          = {(AXI_BYTES){1'b1}};                                                      //设置所有的WSTRB为1代表传输的所有数据有效
assign M_AXI_WLAST          = axi_wlast;
assign M_AXI_WVALID         = axi_wvalid & I_fdma_wready;                                               //写数据有效,这里必须设置I_fdma_wready有效
assign M_AXI_BREADY         = 1'b1;
//----------------------------------------------------------------------------  
//AXI4 FULL Write
assign  axi_wdata           = I_fdma_wdata;
assign  O_fdma_wvalid       = w_next;
reg     fdma_wstart_locked  = 1'b0;
wire    fdma_wend;                                                                                      //fdma写结束信号
wire    fdma_wstart;                                                                                    //fdma写开始信号
assign   O_fdma_wbusy = fdma_wstart_locked ;                                                            //FDAM忙信号
//在整个写过程中fdma_wstart_locked将保持有效,直到本次FDMA写结束
always @(posedge M_AXI_ACLK)
    if(M_AXI_ARESETN == 1'b0 || fdma_wend == 1'b1 )
        fdma_wstart_locked <= 1'b0;                                                                     //如果FDMA写完成,则拉低锁信号
    else if(fdma_wstart)
        fdma_wstart_locked <= 1'b1;                                                                     //如果FDMA开始写,则拉高锁信号
//产生fdma_wstart信号,整个信号保持1个  M_AXI_ACLK时钟周期
assign fdma_wstart = (fdma_wstart_locked == 1'b0 && I_fdma_wareq == 1'b1);                              //当前FDMA非忙时,请求写时,拉高一个周期的写开始信号,然后锁住
        
//AXI4 write burst lenth busrt addr ------------------------------
//当fdma_wstart信号有效,代表一次新的FDMA传输,首先把地址本次fdma的burst地址寄存到axi_awaddr作为第一次axi burst的地址。如果fdma的数据长度大于256,那么当axi_wlast有效的时候,自动计算下次axi的burst地址
always @(posedge M_AXI_ACLK)
    if(fdma_wstart)    
        axi_awaddr <= I_fdma_waddr;
    else if(axi_wlast == 1'b1)
        axi_awaddr <= axi_awaddr + axi_wburst_size ;                    
//AXI4 write cycle -----------------------------------------------
//axi_wstart_locked_r1, axi_wstart_locked_r2信号是用于时序同步,也方便产生上升沿
reg axi_wstart_locked_r1 = 1'b0;
reg axi_wstart_locked_r2 = 1'b0;

always @(posedge M_AXI_ACLK)begin
    axi_wstart_locked_r1 <= axi_wstart_locked;
    axi_wstart_locked_r2 <= axi_wstart_locked_r1;
end
// axi_wstart_locked的作用代表一次axi写burst操作正在进行中。
always @(posedge M_AXI_ACLK)
    if((fdma_wstart_locked == 1'b1) &&  axi_wstart_locked == 1'b0)
        axi_wstart_locked <= 1'b1; 
    else if(axi_wlast == 1'b1 || fdma_wstart == 1'b1)
        axi_wstart_locked <= 1'b0;
        
//AXI4 addr valid and write addr----------------------------------- 
always @(posedge M_AXI_ACLK)
     if((axi_wstart_locked_r1 == 1'b1) &&  axi_wstart_locked_r2 == 1'b0)                        //拉高awvalid信号
         axi_awvalid <= 1'b1;
     else if((axi_wstart_locked == 1'b1 && M_AXI_AWREADY == 1'b1)|| axi_wstart_locked == 1'b0)  //当前没锁住或者握手成功了 就拉低
         axi_awvalid <= 1'b0;       
//AXI4 write data---------------------------------------------------        
always @(posedge M_AXI_ACLK)
    if((axi_wstart_locked_r1 == 1'b1) &&  axi_wstart_locked_r2 == 1'b0)                         //锁住后就一直拉高wvalid信号
        axi_wvalid <= 1'b1;
    else if(axi_wlast == 1'b1 || axi_wstart_locked == 1'b0)                                     //直到失锁或者wlast拉高
        axi_wvalid <= 1'b0;//   
//AXI4 write data burst len counter----------------------------------
always @(posedge M_AXI_ACLK)
    if(axi_wstart_locked == 1'b0)
        wburst_cnt <= 'd0;
    else if(w_next)                                                                             //写数据握手成功一次,计数器加一
        wburst_cnt <= wburst_cnt + 1'b1;    
            
assign axi_wlast = (w_next == 1'b1) && (wburst_cnt == M_AXI_AWLEN);                             //写到最后一个数据后,拉高last信号
//fdma write data burst len counter----------------------------------
reg wburst_len_req = 1'b0;
reg [15:0] fdma_wleft_cnt =16'd0;

// wburst_len_req信号是自动管理每次axi需要burst的长度
always @(posedge M_AXI_ACLK)
        wburst_len_req <= fdma_wstart|axi_wlast;

// fdma_wleft_cnt用于记录一次FDMA剩余需要传输的数据数量  
always @(posedge M_AXI_ACLK)
    if( fdma_wstart )begin
        wfdma_cnt <= 1'd0;
        fdma_wleft_cnt <= I_fdma_wsize;
    end
    else if(w_next)begin
        wfdma_cnt <= wfdma_cnt + 1'b1;  
        fdma_wleft_cnt <= (I_fdma_wsize - 1'b1) - wfdma_cnt;
    end
//当最后一个数据的时候,产生fdma_wend信号代表本次fdma传输结束
assign  fdma_wend = w_next && (fdma_wleft_cnt == 1 );
//一次axi最大传输的长度是256因此当大于256,自动拆分多次传输
always @(posedge M_AXI_ACLK)begin
    if(M_AXI_ARESETN == 1'b0)begin
        wburst_len <= 1;
    end
    else if(wburst_len_req)begin
        if(fdma_wleft_cnt[15:MAX_BURST_LEN_SIZE] >0)  
            wburst_len <= M_AXI_MAX_BURST_LEN;
        else 
            wburst_len <= fdma_wleft_cnt[MAX_BURST_LEN_SIZE-1:0];
    end
    else wburst_len <= wburst_len;
end



//fdma axi read----------------------------------------------
reg             [M_AXI_ADDR_WIDTH-1 : 0]            axi_araddr  =0   ;                          //AXI4读地址
reg                                                 axi_arvalid =1'b0;                          //AXI4读地有效
wire                                                axi_rlast   ;                               //AXI4 读LAST信号
reg                                                 axi_rready  = 1'b0;                         //AXI4读准备好
wire                                                r_next  = (M_AXI_RVALID && M_AXI_RREADY);   // 当读数据握手信号
reg             [8 :0]                              rburst_len  = 1  ;                          //读传输的axi burst长度,代码会自动计算每次axi传输的burst 长度
reg             [8 :0]                              rburst_cnt  = 0  ;                          //每次突发传输中的的读数据计数器
reg             [15:0]                              rfdma_cnt   = 0  ;                          //一次fdma传输中的的读数据计数器
reg                                                 axi_rstart_locked   =0;                     //axi 传输进行中,lock住,用于时序控制
wire            [15:0]                              axi_rburst_size =   rburst_len * AXI_BYTES; //axi 传输的地址长度计算  

assign M_AXI_ARID       = M_AXI_ID;                                                             //读地址ID,用来标志一组写信号, M_AXI_ID是通过参数接口定义
assign M_AXI_ARADDR     = axi_araddr;
assign M_AXI_ARLEN      = rburst_len - 1;                                                       //AXI4 burst的长度
assign M_AXI_ARSIZE     = clogb2((AXI_BYTES)-1);
assign M_AXI_ARBURST    = 2'b01;                                                                //AXI4的busr类型INCR模式,地址递增
assign M_AXI_ARLOCK     = 1'b0;                                                                 //不使用cache,不使用buffer
assign M_AXI_ARCACHE    = 4'b0010;
assign M_AXI_ARPROT     = 3'h0;
assign M_AXI_ARQOS      = 4'h0;
assign M_AXI_ARVALID    = axi_arvalid;
assign M_AXI_RREADY     = axi_rready&&I_fdma_rready;                                            //让FDMA读数据准备好,这里必须设置I_fdma_rready有效
assign O_fdma_rdata     = M_AXI_RDATA;    
assign O_fdma_rvalid    = r_next;    

//AXI4 FULL Read-----------------------------------------   

reg      fdma_rstart_locked = 1'b0;
wire     fdma_rend;
wire     fdma_rstart;
assign   O_fdma_rbusy = fdma_rstart_locked ;
//在整个读过程中fdma_rstart_locked将保持有效,直到本次FDMA写结束
always @(posedge M_AXI_ACLK)
    if(M_AXI_ARESETN == 1'b0 || fdma_rend == 1'b1)
        fdma_rstart_locked <= 1'b0;
    else if(fdma_rstart)
        fdma_rstart_locked <= 1'b1;                                
//产生fdma_rstart信号,整个信号保持1个  M_AXI_ACLK时钟周期
assign fdma_rstart = (fdma_rstart_locked == 1'b0 && I_fdma_rareq == 1'b1);    
//AXI4 read burst lenth busrt addr ------------------------------
//当fdma_rstart信号有效,代表一次新的FDMA传输,首先把地址本次fdma的burst地址寄存到axi_araddr作为第一次axi burst的地址。如果fdma的数据长度大于256,那么当axi_rlast有效的时候,自动计算下次axi的burst地址
always @(posedge M_AXI_ACLK)
    if(fdma_rstart == 1'b1)    
        axi_araddr <= I_fdma_raddr;
    else if(axi_rlast == 1'b1)
        axi_araddr <= axi_araddr + axi_rburst_size ;                                                
//AXI4 r_cycle_flag-------------------------------------    
//axi_rstart_locked_r1, axi_rstart_locked_r2信号是用于时序同步
reg axi_rstart_locked_r1 = 1'b0, axi_rstart_locked_r2 = 1'b0;
always @(posedge M_AXI_ACLK)begin
    axi_rstart_locked_r1 <= axi_rstart_locked;
    axi_rstart_locked_r2 <= axi_rstart_locked_r1;
end
// axi_rstart_locked的作用代表一次axi读burst操作正在进行中。
always @(posedge M_AXI_ACLK)
    if((fdma_rstart_locked == 1'b1) &&  axi_rstart_locked == 1'b0)
        axi_rstart_locked <= 1'b1; 
    else if(axi_rlast == 1'b1 || fdma_rstart == 1'b1)
        axi_rstart_locked <= 1'b0;
//AXI4 addr valid and read addr-----------------------------------  
always @(posedge M_AXI_ACLK)
     if((axi_rstart_locked_r1 == 1'b1) &&  axi_rstart_locked_r2 == 1'b0)
         axi_arvalid <= 1'b1;
     else if((axi_rstart_locked == 1'b1 && M_AXI_ARREADY == 1'b1)|| axi_rstart_locked == 1'b0)
         axi_arvalid <= 1'b0;       
//AXI4 read data---------------------------------------------------     
always @(posedge M_AXI_ACLK)
    if((axi_rstart_locked_r1 == 1'b1) &&  axi_rstart_locked_r2 == 1'b0)
        axi_rready <= 1'b1;
    else if(axi_rlast == 1'b1 || axi_rstart_locked == 1'b0)
        axi_rready <= 1'b0;//   
//AXI4 read data burst len counter----------------------------------
always @(posedge M_AXI_ACLK)
    if(axi_rstart_locked == 1'b0)
        rburst_cnt <= 'd0;
    else if(r_next)
        rburst_cnt <= rburst_cnt + 1'b1;            
assign axi_rlast = (r_next == 1'b1) && (rburst_cnt == M_AXI_ARLEN);
//fdma read data burst len counter----------------------------------
reg rburst_len_req = 1'b0;
reg [15:0] fdma_rleft_cnt =16'd0;
// rburst_len_req信号是自动管理每次axi需要burst的长度  
always @(posedge M_AXI_ACLK)
        rburst_len_req <= fdma_rstart | axi_rlast;  
// fdma_rleft_cnt用于记录一次FDMA剩余需要传输的数据数量          
always @(posedge M_AXI_ACLK)
    if(fdma_rstart )begin
        rfdma_cnt <= 1'd0;
        fdma_rleft_cnt <= I_fdma_rsize;
    end
    else if(r_next)begin
        rfdma_cnt <= rfdma_cnt + 1'b1;  
        fdma_rleft_cnt <= (I_fdma_rsize - 1'b1) - rfdma_cnt;
    end
//当最后一个数据的时候,产生fdma_rend信号代表本次fdma传输结束
assign  fdma_rend = r_next && (fdma_rleft_cnt == 1 );
//axi auto burst len caculate-----------------------------------------
//一次axi最大传输的长度是256因此当大于256,自动拆分多次传输
always @(posedge M_AXI_ACLK)begin
     if(M_AXI_ARESETN == 1'b0)begin
        rburst_len <= 1;
     end
     else if(rburst_len_req)begin
        if(fdma_rleft_cnt[15:MAX_BURST_LEN_SIZE] >0)  
            rburst_len <= M_AXI_MAX_BURST_LEN;
        else 
            rburst_len <= fdma_rleft_cnt[MAX_BURST_LEN_SIZE-1:0];
     end
     else rburst_len <= rburst_len;
end
		              		   
endmodule

四、源码仿真验证

  打开测试的BD文件,外部输入50M晶振时钟,通过锁相环产生100M时钟给AXI接口,外部通过普通用户接口FDMA_S_0来控制FDMA的读写,FDMA通过AXI4 控制从机进行读写。
在这里插入图片描述

4.1 FDMA控制代码

  连线完成后,添加外部的控制程序,来产生读写地址,以及读写命令,控制程序代码如下:

`timescale 1ns / 1ns
module fdma_ctrl(
    input                                               clk ,
    input                                               rst_n   ,
    //write
    input                                               fdma_wbusy  ,    
    output          [31:0]                              fdma_waddr  ,
    output                                              fdma_wareq  ,
    output          [15:0]                              fdma_wsize  ,
    input                                               fdma_wvalid ,
    output          [31:0]                              fdma_wdata  ,
    //read
    input                                               fdma_rbusy  ,    
    output          [31:0]                              fdma_raddr  ,
    output                                              fdma_rareq  ,
    output          [15:0]                              fdma_rsize  ,
    input                                               fdma_rvalid ,
    input           [31:0]                              fdma_rdata  
);

/***************function**************/

/***************parameter*************/
localparam  IDEL        = 4'b0001,
            WRITE       = 4'b0010,
            WAITE_READ  = 4'b0100,
            READ        = 4'b1000;

localparam  BURST_LEN   = 300;
localparam  ADDR_INR    = BURST_LEN * 4;

/***************port******************/             

/***************mechine***************/
reg             [3:0]                               cur_state   ;

/***************reg*******************/
reg             [31:0]                              r_fdma_waddr    ;
reg                                                 r_fdma_wareq    ;
reg             [31:0]                              r_fdma_raddr    ;
reg                                                 r_fdma_rareq    ;
reg             [31:0]                              r_fdma_wdata    ;

/***************wire******************/

/***************component*************/

/***************assign****************/
assign  fdma_waddr  = r_fdma_waddr;   
assign  fdma_wareq  = r_fdma_wareq;   
assign  fdma_wsize  = BURST_LEN;   
assign  fdma_raddr  = r_fdma_raddr;   
assign  fdma_rareq  = r_fdma_rareq;   
assign  fdma_rsize  = BURST_LEN;   
assign  fdma_wdata  = r_fdma_wdata;

/***************always****************/

always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        cur_state <= IDEL;
        r_fdma_wareq <= 1'b0;
        r_fdma_waddr <= 'd0;
        r_fdma_wdata <= 32'd66;
        r_fdma_raddr <= 'd0;
        r_fdma_rareq <= 1'b0;
    end
    else
        case (cur_state)
            IDEL: begin
                if(fdma_wbusy == 1'b0)
                    r_fdma_wareq <= 1'b1;
                else
                    r_fdma_wareq <= 1'b0;
                
                if((fdma_wbusy == 1'b1) &&(r_fdma_wareq == 1'b1))
                    cur_state <= WRITE;
                else
                    cur_state <= IDEL;
            end
            WRITE:begin
                if(fdma_wbusy == 1'b0)begin
                    r_fdma_wdata <= 32'd66;
                    r_fdma_waddr <= r_fdma_waddr + ADDR_INR;
                    cur_state <= WAITE_READ;
                end
                else if(fdma_wvalid)begin
                    r_fdma_wdata <= r_fdma_wdata + 1'b1;
                    cur_state <= WRITE;
                end
                else begin
                    r_fdma_wdata <= r_fdma_wdata;
                    cur_state <= WRITE;
                end
            end
            WAITE_READ:begin
                if(fdma_rbusy == 1'b0)
                    r_fdma_rareq <= 1'b1;
                else
                    r_fdma_rareq <= 1'b0;
                
                if((fdma_rbusy == 1'b1) &&(r_fdma_rareq == 1'b1))
                    cur_state <= READ;
                else
                    cur_state <= WAITE_READ;
            end
            READ:begin
                if(fdma_rbusy == 1'b0)begin
                    cur_state <= IDEL;
                    r_fdma_raddr <= r_fdma_raddr + ADDR_INR;
                end
                else begin
                    r_fdma_raddr <= r_fdma_raddr;
                    cur_state <= READ;
                end
            end
            default: cur_state <= IDEL;
        endcase
end


endmodule

4.2 系统框图

   创建一个顶层文件,把FDMA控制程序以及封装好的BD程序连接起来,入下图所示:

在这里插入图片描述

4.3 仿真结果

4.3.1 写通道

   创建TB文件,只需要给时钟和复位的激励即可,然后开始仿真,先观察写通道:

在这里插入图片描述
   从仿真可以看出,本次fdma从地址0开始写,数据从66开始一直累加,直到写满300个。

在这里插入图片描述
   写入最后一个数据365后,wbusy就拉低了,表示300个数已经成功写入。

4.3.2 读通道

在这里插入图片描述
  在rbusy非忙时写入读地址,以及读突发长度为300。

在这里插入图片描述
  过了一段时间,读数据从66开始累加的读出来

在这里插入图片描述
  最后一个读数据是365,和之前写入的最后一个数据一致,因此本次fdma验证完成,接下来我们用fdma来控制BRAM和DDR。

五、使用FDMA控制BRAM读写测试

5.1 系统框图

  新建一个工程,创建一个BD命名为:fdma_ctrl_bram。然后添加FDMA、AXI_BRAM_Controller、BMG、以及Processor_System_Reset然后按照下图所示连线:

在这里插入图片描述

5.2 读写数据控制模块

  再创建一个bram_rw_ctrl模块,代码如下:

`timescale 1ns / 1ns
module bram_rw_ctrl(
    input                                               clk ,
    input                                               rst_n   ,
    //write
    input                                               fdma_wbusy  ,    
    output          [31:0]                              fdma_waddr  ,
    output                                              fdma_wareq  ,
    output          [15:0]                              fdma_wsize  ,
    input                                               fdma_wvalid ,
    output          [127:0]                             fdma_wdata  ,
    //read
    input                                               fdma_rbusy  ,    
    output          [31:0]                              fdma_raddr  ,
    output                                              fdma_rareq  ,
    output          [15:0]                              fdma_rsize  ,
    input                                               fdma_rvalid ,
    input           [127:0]                             fdma_rdata  
);

/***************function**************/

/***************parameter*************/
localparam  IDEL        = 4'b0001,
            WRITE       = 4'b0010,
            WAITE_READ  = 4'b0100,
            READ        = 4'b1000;

localparam  BURST_LEN   = 512;
localparam  ADDR_INR    = BURST_LEN * 16;

/***************port******************/             

/***************mechine***************/
reg             [3:0]                               cur_state   ;

/***************reg*******************/
reg             [31:0]                              r_fdma_waddr    ;
reg                                                 r_fdma_wareq    ;
reg             [31:0]                              r_fdma_raddr    ;
reg                                                 r_fdma_rareq    ;
reg             [127:0]                             r_fdma_wdata    ;

/***************wire******************/

/***************component*************/

/***************assign****************/
assign  fdma_waddr  = r_fdma_waddr;   
assign  fdma_wareq  = r_fdma_wareq;   
assign  fdma_wsize  = BURST_LEN;   
assign  fdma_raddr  = r_fdma_raddr;   
assign  fdma_rareq  = r_fdma_rareq;   
assign  fdma_rsize  = BURST_LEN;   
assign  fdma_wdata  = r_fdma_wdata;

/***************always****************/

always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        cur_state <= IDEL;
        r_fdma_wareq <= 1'b0;
        r_fdma_waddr <= 'd0;
        r_fdma_wdata <= 'd66;
        r_fdma_raddr <= 'd0;
        r_fdma_rareq <= 1'b0;
    end
    else
        case (cur_state)
            IDEL: begin
                if(fdma_wbusy == 1'b0)
                    r_fdma_wareq <= 1'b1;
                else
                    r_fdma_wareq <= 1'b0;
                
                if((fdma_wbusy == 1'b1) &&(r_fdma_wareq == 1'b1))
                    cur_state <= WRITE;
                else
                    cur_state <= IDEL;
            end
            WRITE:begin
                if(fdma_wbusy == 1'b0)begin
                    r_fdma_wdata <= 'd66;
                    r_fdma_waddr <= r_fdma_waddr + ADDR_INR;
                    cur_state <= WAITE_READ;
                end
                else if(fdma_wvalid)begin
                    r_fdma_wdata <= r_fdma_wdata + 1'b1;
                    cur_state <= WRITE;
                end
                else begin
                    r_fdma_wdata <= r_fdma_wdata;
                    cur_state <= WRITE;
                end
            end
            WAITE_READ:begin
                if(fdma_rbusy == 1'b0)
                    r_fdma_rareq <= 1'b1;
                else
                    r_fdma_rareq <= 1'b0;
                
                if((fdma_rbusy == 1'b1) &&(r_fdma_rareq == 1'b1))
                    cur_state <= READ;
                else
                    cur_state <= WAITE_READ;
            end
            READ:begin
                if(fdma_rbusy == 1'b0)begin
                    cur_state <= IDEL;
                    r_fdma_raddr <= r_fdma_raddr + ADDR_INR;
                end
                else begin
                    r_fdma_raddr <= r_fdma_raddr;
                    cur_state <= READ;
                end
            end
            default: cur_state <= IDEL;
        endcase
end

endmodule

  我们控制fdma的读写地址以及写数据,每次传输突发512个数据,写数据从66开始累加,写完一次突发后就启动读,然后打开仿真如下:

5.3 仿真结果

在这里插入图片描述
  结果如上所示,当fdma检测到写使能时,拉高w_busy信号,然后从地址0开始写数据66,一直累加。

在这里插入图片描述

  写最后一个数据是577符合预期(66+511=577),然后启动读,然后从地址0开始读数据,读数据从66开始累加的出来。

在这里插入图片描述
  最后一个读出的数据是577然后又启动写,如此反复,因此使用fdma读写控制bram验证成功。

5.4 下板验证

  我们分配好管脚后,添加ila核抓一下rw_ctrl模块的各个信号,然后下板观察:

在这里插入图片描述
  下板后观察,读数据从66开始累加,最后一个读数据是577,下板也没问题。

六、使用FDMA控制DDR3读写测试

  前面分析了FDMA的源码以及使用FDMA控制AXI接口的BRAM的读写,由于FDMA的读写操作都是基于AXI4总线的,因此同样可以使用FDMA控制AXI接口的MIG核来实现DDR3的读写。

6.1 BD系统框图

  整体系统框图与fdma_ctrl_bram相同;新建一个工程,创建一个BD命名为:fdma_ctrl_ddr。然后添加FDMA、MIG、以及Processor_System_Reset;其中MIG核的配置在《详解DDR3原理以及使用Xilinx MIG IP核(app 接口)实现DDR3读写测试》中有详细的介绍,这里不全部展开,只配置重要的部分,配置部分如下:

在这里插入图片描述

在这里插入图片描述
   把以上的IP都添加后联上线,系统框图如下所示:

在这里插入图片描述

6.2 ddr读写数据控制程序

   我们需要一个控制程序来控制整个BD系统的读写,控制程序和上面BRAM控制代码一模一样,这也体现出米联客的FDMA模块的方便性,代码如下:

`timescale 1ns / 1ns
module ddr_rw_ctrl(
    input                                               clk ,
    input                                               rst_n   ,
    //write
    input                                               fdma_wbusy  ,    
    output          [31:0]                              fdma_waddr  ,
    output                                              fdma_wareq  ,
    output          [15:0]                              fdma_wsize  ,
    input                                               fdma_wvalid ,
    output          [127:0]                             fdma_wdata  ,
    //read
    input                                               fdma_rbusy  ,    
    output          [31:0]                              fdma_raddr  ,
    output                                              fdma_rareq  ,
    output          [15:0]                              fdma_rsize  ,
    input                                               fdma_rvalid ,
    input           [127:0]                             fdma_rdata  
);

/***************function**************/

/***************parameter*************/
localparam  IDEL        = 4'b0001,
            WRITE       = 4'b0010,
            WAITE_READ  = 4'b0100,
            READ        = 4'b1000;

localparam  BURST_LEN   = 512;
localparam  ADDR_INR    = BURST_LEN * 16;

/***************port******************/             

/***************mechine***************/
reg             [3:0]                               cur_state   ;

/***************reg*******************/
reg             [31:0]                              r_fdma_waddr    ;
reg                                                 r_fdma_wareq    ;
reg             [31:0]                              r_fdma_raddr    ;
reg                                                 r_fdma_rareq    ;
reg             [127:0]                             r_fdma_wdata    ;

/***************wire******************/

/***************component*************/

/***************assign****************/
assign  fdma_waddr  = r_fdma_waddr;   
assign  fdma_wareq  = r_fdma_wareq;   
assign  fdma_wsize  = BURST_LEN;   
assign  fdma_raddr  = r_fdma_raddr;   
assign  fdma_rareq  = r_fdma_rareq;   
assign  fdma_rsize  = BURST_LEN;   
assign  fdma_wdata  = r_fdma_wdata;

/***************always****************/

always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        cur_state <= IDEL;
        r_fdma_wareq <= 1'b0;
        r_fdma_waddr <= 'd0;
        r_fdma_wdata <= 'd66;
        r_fdma_raddr <= 'd0;
        r_fdma_rareq <= 1'b0;
    end
    else
        case (cur_state)
            IDEL: begin
                if(fdma_wbusy == 1'b0)
                    r_fdma_wareq <= 1'b1;
                else
                    r_fdma_wareq <= 1'b0;
                
                if((fdma_wbusy == 1'b1) &&(r_fdma_wareq == 1'b1))
                    cur_state <= WRITE;
                else
                    cur_state <= IDEL;
            end
            WRITE:begin
                if(fdma_wbusy == 1'b0)begin
                    r_fdma_wdata <= 'd66;
                    r_fdma_waddr <= r_fdma_waddr + ADDR_INR;
                    cur_state <= WAITE_READ;
                end
                else if(fdma_wvalid)begin
                    r_fdma_wdata <= r_fdma_wdata + 1'b1;
                    cur_state <= WRITE;
                end
                else begin
                    r_fdma_wdata <= r_fdma_wdata;
                    cur_state <= WRITE;
                end
            end
            WAITE_READ:begin
                if(fdma_rbusy == 1'b0)
                    r_fdma_rareq <= 1'b1;
                else
                    r_fdma_rareq <= 1'b0;
                
                if((fdma_rbusy == 1'b1) &&(r_fdma_rareq == 1'b1))
                    cur_state <= READ;
                else
                    cur_state <= WAITE_READ;
            end
            READ:begin
                if(fdma_rbusy == 1'b0)begin
                    cur_state <= IDEL;
                    r_fdma_raddr <= r_fdma_raddr + ADDR_INR;
                end
                else begin
                    r_fdma_raddr <= r_fdma_raddr;
                    cur_state <= READ;
                end
            end
            default: cur_state <= IDEL;
        endcase
end


endmodule

6.3 顶层框图

   新建一个顶层模块,把上面的BD和ddr_rw_ctrl模块连起来,代码如下:

module fdma_ctrl_ddr_top (
    input                                               clk,   
    //ddr_interface
    output          [13:0]                              DDR3_interface_addr ,
    output          [2 :0]                              DDR3_interface_ba   ,
    output                                              DDR3_interface_cas_n    ,
    output          [0 :0]                              DDR3_interface_ck_n ,
    output          [0 :0]                              DDR3_interface_ck_p ,
    output          [0 :0]                              DDR3_interface_cke  ,
    output          [0 :0]                              DDR3_interface_cs_n ,
    output          [3 :0]                              DDR3_interface_dm   ,
    inout           [31:0]                              DDR3_interface_dq   ,
    inout           [3 :0]                              DDR3_interface_dqs_n    ,
    inout           [3 :0]                              DDR3_interface_dqs_p    ,
    output          [0 :0]                              DDR3_interface_odt  ,
    output                                              DDR3_interface_ras_n    ,
    output                                              DDR3_interface_reset_n  ,
    output                                              DDR3_interface_we_n ,
    output                                              init_calib_complete 

);



    wire            [31:0]                              FDMA_S_i_fdma_raddr ;
    wire                                                FDMA_S_i_fdma_rareq ;
    wire            [15:0]                              FDMA_S_i_fdma_rsize ;
    wire            [31:0]                              FDMA_S_i_fdma_waddr ;
    wire                                                FDMA_S_i_fdma_wareq ;
    wire            [127:0]                             FDMA_S_i_fdma_wdata ;
    wire            [15:0]                              FDMA_S_i_fdma_wsize ;
    wire                                                FDMA_S_o_fdma_rbusy ;
    wire            [127:0]                             FDMA_S_o_fdma_rdata ;
    wire                                                FDMA_S_o_fdma_rvalid    ;
    wire                                                FDMA_S_o_fdma_wbusy ;
    wire                                                FDMA_S_o_fdma_wvalid    ;
    wire                                                ui_clk  ;


fdma_ctrl_ddr fdma_ctrl_ddr_i(
    .DDR3_interface_addr        (DDR3_interface_addr    ),
    .DDR3_interface_ba          (DDR3_interface_ba      ),
    .DDR3_interface_cas_n       (DDR3_interface_cas_n   ),
    .DDR3_interface_ck_n        (DDR3_interface_ck_n    ),
    .DDR3_interface_ck_p        (DDR3_interface_ck_p    ),
    .DDR3_interface_cke         (DDR3_interface_cke     ),
    .DDR3_interface_cs_n        (DDR3_interface_cs_n    ),
    .DDR3_interface_dm          (DDR3_interface_dm      ),
    .DDR3_interface_dq          (DDR3_interface_dq      ),
    .DDR3_interface_dqs_n       (DDR3_interface_dqs_n   ),
    .DDR3_interface_dqs_p       (DDR3_interface_dqs_p   ),
    .DDR3_interface_odt         (DDR3_interface_odt     ),
    .DDR3_interface_ras_n       (DDR3_interface_ras_n   ),
    .DDR3_interface_reset_n     (DDR3_interface_reset_n ),
    .DDR3_interface_we_n        (DDR3_interface_we_n    ),
    .FDMA_S_i_fdma_raddr        (FDMA_S_i_fdma_raddr    ),
    .FDMA_S_i_fdma_rareq        (FDMA_S_i_fdma_rareq    ),
    .FDMA_S_i_fdma_rready       (1'b1                   ),
    .FDMA_S_i_fdma_rsize        (FDMA_S_i_fdma_rsize    ),
    .FDMA_S_i_fdma_waddr        (FDMA_S_i_fdma_waddr    ),
    .FDMA_S_i_fdma_wareq        (FDMA_S_i_fdma_wareq    ),
    .FDMA_S_i_fdma_wdata        (FDMA_S_i_fdma_wdata    ),
    .FDMA_S_i_fdma_wready       (1'b1                   ),
    .FDMA_S_i_fdma_wsize        (FDMA_S_i_fdma_wsize    ),
    .FDMA_S_o_fdma_rbusy        (FDMA_S_o_fdma_rbusy    ),
    .FDMA_S_o_fdma_rdata        (FDMA_S_o_fdma_rdata    ),
    .FDMA_S_o_fdma_rvalid       (FDMA_S_o_fdma_rvalid   ),
    .FDMA_S_o_fdma_wbusy        (FDMA_S_o_fdma_wbusy    ),
    .FDMA_S_o_fdma_wvalid       (FDMA_S_o_fdma_wvalid   ),
    .init_calib_complete_0      (init_calib_complete    ),
    .sys_clk                    (clk                	),
    .ui_clk                     (ui_clk                 )
    );

    ddr_rw_ctrl u_ddr_rw_ctrl(
    .clk          ( ui_clk                  ),
    .rst_n        ( init_calib_complete     ),
    .fdma_wbusy   ( FDMA_S_o_fdma_wbusy     ),
    .fdma_waddr   ( FDMA_S_i_fdma_waddr     ),
    .fdma_wareq   ( FDMA_S_i_fdma_wareq     ),
    .fdma_wsize   ( FDMA_S_i_fdma_wsize     ),
    .fdma_wvalid  ( FDMA_S_o_fdma_wvalid    ),
    .fdma_wdata   ( FDMA_S_i_fdma_wdata     ),
    .fdma_rbusy   ( FDMA_S_o_fdma_rbusy     ),
    .fdma_raddr   ( FDMA_S_i_fdma_raddr     ),
    .fdma_rareq   ( FDMA_S_i_fdma_rareq     ),
    .fdma_rsize   ( FDMA_S_i_fdma_rsize     ),
    .fdma_rvalid  ( FDMA_S_o_fdma_rvalid    ),
    .fdma_rdata   ( FDMA_S_o_fdma_rdata     )
);

endmodule

6.4 仿真结果

   DDR的仿真文件不需要自己编写,只需要打开设计例程,拿出DDR3的仿真模型和一些参数文件,具体操作参考《详解DDR3原理以及使用Xilinx MIG IP核(app 接口)实现DDR3读写测试》,然后打开仿真,如下所示:

在这里插入图片描述
  DDR的仿真需要一定时间,大概再218us的时候,DDR初始化完成信号拉高,然后就启动ddr_rw_ctrl模块,我们先来看写部分:

在这里插入图片描述
  我们一次写突发512个数据,当ddr初始化完成后,我们拉高写使能,以及写地址从0开始,然后数据从66开始写直到66+511=577为止,

在这里插入图片描述
  写完后,拉高读使能以及读地址,过一段时间后,读数据从66开始出来,最后一个读数据是577。

在这里插入图片描述
  所以我们可以使用同样的fdma来控制bram和ddr的读写,具有极强的通用性。

6.5 下板验证

  我们分配好管脚后,添加ila核抓一下ddr_rw_ctrl模块的各个信号,然后下板观察:
在这里插入图片描述

在这里插入图片描述

  最后一个读ddr数据是577,验证完成

参考

《米联客2024版PL-DDR缓存方案》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2074376.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

快讯 | 美军500天AI计划启动,“破解AI“与“反AI“策略亮相

在数字化浪潮的推动下&#xff0c;人工智能&#xff08;AI&#xff09;正成为塑造未来的关键力量。硅纪元视角栏目紧跟AI科技的最新发展&#xff0c;捕捉行业动态&#xff1b;提供深入的新闻解读&#xff0c;助您洞悉技术背后的逻辑&#xff1b;汇聚行业专家的见解&#xff0c;…

VBA之正则表达式(46)-- 解析业务逻辑公式

实例需求&#xff1a;某业务系统的逻辑公式如下所示&#xff08;单行文本&#xff09;&#xff0c;保存在活动工作表的A1单元格中。 "DSO_90Day"->"FA_NoFunc"->"FCCS_No Intercompany"->"FCCS_Data Input"->"FCCS_…

<数据集>非洲动物识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;1504张 标注数量(xml文件个数)&#xff1a;1504 标注数量(txt文件个数)&#xff1a;1504 标注类别数&#xff1a;4 标注类别名称&#xff1a;[buffalo, elephant, rhino, zebra] 序号类别名称图片数框数1buffalo3…

Java生成一个5位的随机验证码(大小写字母和数字)

生成验证码 内容&#xff1a;可以是小写字母&#xff0c;也可以是大写字母&#xff0c;还可以是数字 规则&#xff1a;长度为5 内容中四位字母&#xff0c;一位数字 其中数字只有一位&#xff0c;但是可以出现在任意位置。 package test;impo…

arm-Pwn环境搭建+简单题目

前言 起因是看到一篇IOT CVE的分析文章。 正好也在学pwn&#xff0c;arm架构的也是IOT这些固件最常用的&#xff0c;所以先安一个arm-pwn的环境。 环境搭建/调试 1. 安装 gdb-multiarch sudo apt-get install gdb-multiarch2. 安装qemu ctf的arm_pwn只需要安装qemu-user就…

结构体内存的对齐

结构体的对齐规则 第一个成员在结构体变量偏移量为0的地址处。 其他成员变量要对齐到某个数字&#xff08;对齐数&#xff09;的整数倍的地址处 1&#xff09; 对齐数 min( 编译器默认的一个对齐数, 该成员大小&#xff09;。 2&#xff09;默认的对齐数&#xff0c;可以通过宏…

kafka的12个重要概念

kafka的12个重要概念 1、服务器broker1.1、Broker 的主要功能1.2、Kafka Broker 的架构1.3、配置和管理1.4、高可用性和负载均衡1.5、总结 2、主题topic2.1、主要特点 3、事件Event4、生产者producer4.1、主要功能4.2、Producer 的配置选项4.3、Producer 的工作流程4.4、总结 5…

(javaweb)maven高级

目录 ​编辑 1.分模块设计与开发 2.继承与聚合--继承关系实现 3.继承与聚合--版本锁定 4.继承与聚合--聚合版本 5.私服 资源的上传与下载 1.分模块设计与开发 分模块&#xff1a;拆分成多个模块进行开发 不分模块&#xff1a;业务代码堆积成一个 不利于项目管理和维护并…

考研数学|零基础9月开始100天备考攻略

马上就要9月了&#xff0c;很多同学相比快要结束强化了&#xff0c;零基础的同学&#xff0c;进度可能会慢一些&#xff0c;但是别担心&#xff0c;考研数学的学习&#xff0c;进度不是最要紧的&#xff0c;学习效果才是&#xff01;千万不要比进度&#xff0c;也不要赶进度&am…

Linux中的PCI配置空间

在计算机系统中&#xff0c;PCI&#xff08;Peripheral Component Interconnect&#xff09;总线是一种用于连接硬件设备的标准接口。PCI总线提供了一个通用的、高性能的数据传输通道&#xff0c;广泛应用于PC系统和服务器中。在Linux操作系统中&#xff0c;PCI设备的配置空间是…

Modern C++——不准确“类型声明”引发的非必要性能损耗

大纲 案例代码地址 C是一种强类型语言。我们在编码时就需要明确指出每个变量的类型&#xff0c;进而让编译器可以正确的编译。看似C编译器比其他弱类型语言的编译器要死板&#xff0c;实则它也做了很多“隐藏”的操作。它会在尝试针对一些非预期类型进行相应转换&#xff0c;以…

JS脚本实现RPA模拟人工操作网页获取数据

一、首先我们可以根据查询条件去预置一个Excel&#xff0c;比如我们以公司名称为例。 二、然后我们用JS读取Excel内容&#xff0c;进行页面打开与条件记录 <!DOCTYPE html> <html> <div style"text-align: center;margin-top: 300px;"><input …

一款人性化的终端用户界面工具

A collection of human friendly terminal user interface. 截图 历史文件预览 注意: find file 依赖 fzf. file browser依赖 ranger / lf / … 安装 git clone https://github.com/StubbornVegeta/StartUp ~/.config/ cd ~/.config/StartUp ./install.sh用法 . $HOME/.…

【binder】【android12】【2.servicemanager启动——全源码分析】

系列文章目录 可跳转到下面链接查看下表所有内容https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5501文章浏览阅读2次。系列文章大全https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5501 目录 …

浅谈【数据结构】树与二叉树二

目录 1、二叉排序树 1.1二叉树排序树插入 1.1.1两种插入方法 1.1.2循环法 1.1.3递归法 1.2二叉树的打印 1.3二叉树的结点删除 1.4销毁二叉树 1.5层次打印 谢谢帅气美丽且优秀的你看完我的文章还要点赞、收藏加关注 没错&#xff0c;说的就是你&#xff0c;不用再怀疑&…

前端实现 http请求中 表单请求体和json请求体的互相转换,外加转为 冒号换行格式,用于ApiFox批量导入

在线体验&#xff1a;https://ikaros-521.github.io/dev_tool/http/param_json_converter.html 直接上源码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Query String to JSON Converter</titl…

慢SQL定位及优化

1.如何定位慢查询 方案1&#xff1a;开源工具 调式工具&#xff1a;Arthas 运维工具&#xff1a;Prometheus、Skywalking 方案2&#xff1a;MySQL自带慢日志 慢查询日志记录了所有执行时间超过指定参数&#xff08;long_query_time&#xff0c;单位&#xff1a;秒&#xff0c…

QT error: undefined reference to `vtable for Net‘

报错 C:\Users\Administrator\Desktop\VideoHill\GikISearch\net.cpp:4: error: undefined reference to vtable for Net 以下是两个可能错误原因 1&#xff0c;未定义Q_OBJECT 宏 在头文件中加上 加上#include <QObject>&#xff0c; 改写继承QObject 和定义宏 …

【HarmonyOS】鸿蒙应用蓝牙功能实现 (三)

【HarmonyOS】鸿蒙应用蓝牙功能实现 &#xff08;三&#xff09; 前言 今天整理蓝牙Demo代码&#xff0c;查看官网时发现自己帐号没有登录&#xff0c;竟然也可以访问最新的API文档&#xff0c;真是喜大奔普。看来华为已经开始对外开放最新的API文档&#xff0c;不再有白名单…

Leetcode每日刷题之面试题01.06字符串压缩(C++)

1.题目解析 本题的目的是遍历一个字符串&#xff0c;将重复的字符串以该字符出现次数进行压缩&#xff0c;最终返回压缩后的字符串即可 题目来源&#xff1a;面试题01.06.字符串压缩 2.算法原理 我们从左往右遍历字符串&#xff0c;用 ch 记录当前要压缩的字符&#xff0c;cnt …