【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的指令,则是由以太网上位机下发,指令格式如下:
指令对应含义如下:
这里用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。通过示波器抓取实际输出波形如下:
然后是读寄存器测试,这里可以通过指令55 A5 12 00 02 14 F0来读取CLK_OUT2的分频寄存器,VIO中得到的结果如下:
与写入的分频系数0x24一致,说明读写正常,AXI4-Lite接口代码设计无误。
【附录】
ad9238_udp_ddr3_PLL.rar
(2.13 MB, 下载次数: 34)
AXI4_Lite_Master.rar
(2.39 KB, 下载次数: 33)
|