芯路恒电子技术论坛

 找回密码
 立即注册
热搜: 合集
查看: 244|回复: 0

【ACZ7015】基于verilog的AXI4-Lite主接口设计及案例实战(ACM9238_UDP_DDR)

[复制链接]

该用户从未签到

74

主题

104

帖子

1576

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
1576
发表于 2024-12-30 15:59:42 | 显示全部楼层 |阅读模式
【AXI4-lite主接口设计】
由于AXI4-Lite接口协议可以视为是AXI4接口协议的删减版,所以,这里AXI4-Lite主接口的verilog代码实现,可以直接通过修改我们的fifo_to_axi4和axi4_to_fifo模块实现。有关这两个模块的讲解可以参考我们B站的相关视频:

二者实现原理基本相同,且AXI4-Lite的实现更为简单。需要注意的是,在xilinx的AXI4-Lite接口逻辑中,主机的写地址和写数据通道应当同时工作,这样,从机的ready信号才会拉高。


【使用示例】
AXI4-Lite接口通常用来完成对寄存器的动态配置,通过使用该接口,再搭配上串口、以太网、USB等等通信接口,我们便可以在verilog设计中,在不需要重新编译的情况下完成对设计的实时修改。这里以acm9238_UDP_DDR3逻辑例程为例进行说明,该例程的原贴请参考下述内容:

在该例程中,ACM9238模块的工作时钟由PLL产生,而我们知道:

1,PLL支持AXI4-Lite接口的动态重配置,该接口可以配置PLL内部从VCO分倍频,到各路输出时钟从频率到占空比的数十个寄存器。

2.ACM9238的每条通道,其采样率在一定范围内,由提供给通道的时钟决定。

因此,这里在例程的基础上,使能了PLL的动态重配置,用于动态调整输出给ACM9238的时钟,至于原来的输出时钟测试,则保持不变。

PLL默认输出.png PLL动态重配置.png


至于配置PLL的指令,则是由以太网上位机下发,指令格式如下:

55 A5 05 02 24 00 32 F0

指令对应含义如下:

指令解析.png

这里用05来表示指令用于PLL配置,由于指令内容有限,所以这里单条指令只能实现对PLL其中一路时钟分频(仅整数)、相位、占空比的控制。当指令当中的读写标志位为读时,指令中整数分频、相位、占空比的值用来表示待读取的寄存器。指令被识别后,会交由cmd_pll_reconf模块进行解析和拆分。这里cmd_pll_reconf模块是基于AXI4-Lite接口模块写的上层模块,用来对指令进行解析拆分,然后产生一个或多个AXI突发,通过AXI4-Lite接口完成对PLL的配置和内部寄存器的读取。

代码如下:

[C#] 纯文本查看 复制代码
//指令格式:【31:28】读写控制 【27:24】输出时钟选择 
//          写寄存器:【23:16】频率 【15:8】相位 【7:0】占空比
//          读寄存器:【23:0】寄存器地址

module cmd_pll_reconf#(
    parameter write_num = 4,
    parameter SLAVE_BASE_ADDR = 32'h0,
    parameter AXI_DATA_WIDTH  = 32    ,
    parameter AXI_ADDR_WIDTH  = 32      
)
(
    input clk,
    input reset_n,
    input cmd_valid,
    input [31:0]data,
    output [31:0]RD_Data,
    output reg Done, 
    
    // Master Interface Write Address Ports
    output     [AXI_ADDR_WIDTH-1:0]    m_axi_awaddr     ,
    output     [2:0]                   m_axi_awprot     ,
    output                             m_axi_awvalid    ,
    input                              m_axi_awready    ,
    // Master Interface Write Data Ports
    output     [AXI_DATA_WIDTH-1:0]    m_axi_wdata      ,
    output     [AXI_DATA_WIDTH/8-1:0]  m_axi_wstrb      ,
    output                             m_axi_wvalid     ,
    input                              m_axi_wready     ,
    // Master Interface Write Response Ports
    input      [1:0]                   m_axi_bresp      ,
    input                              m_axi_bvalid     ,
    output                             m_axi_bready     ,
    // Master Interface Read Address Ports
    output     [AXI_ADDR_WIDTH-1:0]    m_axi_araddr     ,
    output     [2:0]                   m_axi_arprot     ,
    output                             m_axi_arvalid    ,
    input                              m_axi_arready    ,
    // Master Interface Read Data Ports
    input      [AXI_DATA_WIDTH-1:0]    m_axi_rdata      ,
    input      [1:0]                   m_axi_rresp      ,
    input                              m_axi_rvalid     ,
    output                             m_axi_rready     
);
    
    wire Write_done;
    wire Read_done;
    
    reg [31:0]data_r0;
    reg [31:0]data_r1;
    reg [31:0]Freq_Div;
    reg [31:0]Phase;
    reg [31:0]Duty;
    reg [AXI_DATA_WIDTH-1:0]WR_Data;
    reg [AXI_ADDR_WIDTH-1:0]Target_addr;
    wire Start;
    
    reg WR_Go;//单次写突发开始
    reg RD_Go;//单次读突发开始
    reg WR_RD_flag;//读写标志位
    reg [31:0]cmd_data;

    always@(posedge clk or negedge reset_n )
    if(!reset_n)begin 
        WR_RD_flag <= 0;
        cmd_data <= 32'd0;
    end
    else if(cmd_valid)begin
        WR_RD_flag <= data[28];
        cmd_data <= data;
    end
    
    data_to_lite
    #(
        .AXI_DATA_WIDTH(32),
        .AXI_ADDR_WIDTH(32)  
    )data_to_lite_inst
    (
      // clock reset
      .clk(clk)              ,
      .reset_n(reset_n)            ,
      .Go(WR_Go),
      .Target_addr(Target_addr),
      .Data(WR_Data),
      .trans_done(Write_done),
      // Master Interface Write Address Ports
      .m_axi_awaddr(m_axi_awaddr)     ,
      .m_axi_awprot(m_axi_awprot)     ,
      .m_axi_awvalid(m_axi_awvalid)    ,
      .m_axi_awready(m_axi_awready)    ,
      // Master Interface Write Data Ports
      .m_axi_wdata(m_axi_wdata)      ,
      .m_axi_wstrb(m_axi_wstrb)      ,
      .m_axi_wvalid(m_axi_wvalid)     ,
      .m_axi_wready(m_axi_wready)     ,
      // Master Interface Write Response Ports
      .m_axi_bresp(m_axi_bresp)      ,
      .m_axi_bvalid(m_axi_bvalid)     ,
      .m_axi_bready(m_axi_bready)     
    );

    
    reg [7:0]state;
    reg [2:0]burst_cnt;
    localparam 
        IDLE        = 8'b00000001,
        Write_Read  = 8'b00000010,//读写判断
        Write_Freq  = 8'b00000100,//时钟分频
        Write_Phase = 8'b00001000,//调相
        Write_Duty  = 8'b00010000,//调占空比
        Updata_Reg  = 8'b00100000,//加载进PLL内部寄存器
        Read_Reg    = 8'b01000000,//读寄存器
        Wait_Done   = 8'b10000000;//等待一次突发完成
    
    always@(posedge clk or negedge reset_n)
    if(!reset_n)begin
        state <= 6'b0;
        WR_Go <= 0;
        burst_cnt <= 3'd0;
        Done <= 1'd0;
        Freq_Div <= 1;
        Phase <= 0;
        Duty <= 32'd50000;
    end
    else 
    case(state)
        IDLE:
        begin
            Done <= 1'd0;
            burst_cnt <= 3'd0;
            if(cmd_valid)
                state <= Write_Read;
            else
                state <= IDLE;
        end
        
        Write_Read:
        begin
            if(!WR_RD_flag)begin
                Freq_Div <= cmd_data[23:16];
                Phase <= cmd_data[15:8]*1000;
                Duty <= cmd_data[7:0]*1000;     
                state <= Write_Freq;
            end
            else begin
                state <= Read_Reg;
            end
        end
        
        Write_Freq:
        begin
            Target_addr <= SLAVE_BASE_ADDR + 12*(cmd_data[27:24]-1)+ 12'd520;
            WR_Data <= Freq_Div;
            WR_Go <= 1'd1;
            burst_cnt <= burst_cnt + 1'b1;
            state <= Wait_Done;
        end
        
        Wait_Done:
        begin
            WR_Go <= 1'd0;
            RD_Go <= 1'd0;
            if(Write_done)begin
                if(burst_cnt == 3'd1)
                    state <= Write_Phase;
                else if(burst_cnt == 3'd2)
                    state <= Write_Duty;
                else if(burst_cnt == 3'd3)
                    state <= Updata_Reg;
                else if(burst_cnt == 3'd4)begin
                    state <= IDLE;
                    Done <= 1'd1;
                end
            end
            else if(Read_done)begin
                Done <= 1'd1;
                state <= IDLE;
            end
            else
                state <= Wait_Done;
        end
        
        Write_Phase:
        begin
            Target_addr <= SLAVE_BASE_ADDR + 12*(cmd_data[27:24]-1) + 4+ 12'd520;
            WR_Data <= Phase;
            WR_Go <= 1'd1;
            burst_cnt <= burst_cnt + 1'b1;
            state <= Wait_Done;
        end
        
        Write_Duty:
        begin
            Target_addr <= SLAVE_BASE_ADDR + 12*(cmd_data[27:24]-1) + 8+ 12'd520;
            WR_Data <= Duty;
            WR_Go <= 1'd1;
            burst_cnt <= burst_cnt + 1'b1;
            state <= Wait_Done;
        end
        
        Updata_Reg:
        begin
            Target_addr <= 12'd604;
            WR_Data <= 32'd3;
            WR_Go <= 1'd1;
            burst_cnt <= burst_cnt + 1'b1;
            state <= Wait_Done;
        end
        
        Read_Reg:
        begin
            Target_addr <= SLAVE_BASE_ADDR + cmd_data[23:0];
            RD_Go <= 1'd1;
            state <= Wait_Done;
        end
        
        default: state <= IDLE;  
    endcase
    
    lite_to_data
    #(
        .AXI_DATA_WIDTH(32) ,
        .AXI_ADDR_WIDTH(11)
    )lite_to_data
    (
    // clock reset
        .clk(clk)              ,
        .reset_n(reset_n)            ,
        .Go(RD_Go)               ,
        .Target_addr(Target_addr)      ,
        .Data(RD_Data)             ,
        .Read_done(Read_done)          ,
        // Master Interface Read Address Ports
        .m_axi_araddr(m_axi_araddr)     ,
        .m_axi_arprot(m_axi_arprot)     ,
        .m_axi_arvalid(m_axi_arvalid)    ,
        .m_axi_arready(m_axi_arready)    ,
        // Master Interface Read Data Ports
        .m_axi_rdata(m_axi_rdata)      ,
        .m_axi_rresp(m_axi_rresp)      ,
        .m_axi_rvalid(m_axi_rvalid)     ,
        .m_axi_rready(m_axi_rready)     
    );    
        
endmodule

至于从PLL寄存器中读取的数据,设计中通过VIO进行显示,用户在实际使用时可以根据自己的需求进行修改。

【上板测试】
原设计中,PLL的输入时钟为50M,VCO的倍频值为18,分频值为1。CLK_OUT2的分频值为18,输出50M,用于提供给ACM9238的两个通道。这里我们通过指令,分别测试一下对于PLL的写读寄存器控制。
首先是写寄存器测试,这里根据指令格式,我们可以通过以太网上位机下发指令55 A5 05 02 24 00 32 F0,将CLK_OUT2的输出频率降为25M。通过示波器抓取实际输出波形如下:
RigolDS8.png

然后是读寄存器测试,这里可以通过指令55 A5 12 00 02 14 F0来读取CLK_OUT2的分频寄存器,VIO中得到的结果如下:
VIO结果.png

与写入的分频系数0x24一致,说明读写正常,AXI4-Lite接口代码设计无误

【附录】
ad9238_udp_ddr3_PLL.rar (2.13 MB, 下载次数: 34)
AXI4_Lite_Master.rar (2.39 KB, 下载次数: 33)

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|芯路恒电子技术论坛 |鄂ICP备2021003648号

GMT+8, 2025-1-18 09:48 , Processed in 0.075742 second(s), 34 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc. Template By 【未来科技】【 www.wekei.cn 】

快速回复 返回顶部 返回列表