本篇目的
上一篇中,简述中搭建PS工程的概貌。但其中AD9250_SPI_TOP如何设计和集成,一笔带过。思来想去,还是补充描述些AD9250_SPI_TOP的设计。
设计思路
AD9250_SPI_TOP功能简单来说,就是把AXI的配置转成SPI时序。
其中AXI4Lite到APB的转换是使用xilinx自带的AXI APB Bridge模块。为什么要先转APB呢,因为APB转寄存器读写时序最为简单。
SPI Master模块就是把APB地址读写操作转为为SPI MST时序。
对于写操作,
- 规定[31:24]为DevID,不同设备的映射关系如下:
localparam FMC27X_CPLD = 8'h00;
localparam FMC27X_AD9517 = 8'h84;
localparam FMC27X_AD9250_0 = 8'h80;
localparam FMC27X_AD9250_1 = 8'h81;
localparam FMC27X_AD9129_0 = 8'h82;
localparam FMC27X_AD9129_1 = 8'h83;
- 规定[19:8]为Addr,如对地址0x001进行写操作。
- 规定[7:0]为Wdata,即写数据。
例如:写值0x80000018为配置AD9250-0芯片的0x000地址,配置值为0x18.
对于读操作,需要先写0x008,再回读0x010,数据在回读的低8bit.
参考代码
AD9250_SPI_AXI.V
//--====================================================================================--//
// Copyright (C) 2024 funnydigitalworld. All rights reserved.
// This confidential and proprietary software may be used only as authorized
// by a licensing agreement from funnydigitalworld(funnydigitalworld@outlook.com).
// The entire notice above must be reproduced on all authorized copies.
//
// FILENAME : .\ad9250_spi_axi.v
// MODULE : AD9250_SPI_AXI
// AUTHOR : (Wechat:funnydigitalworld)/(QQ:2702272533)
// EMAIL : funnydigitalworld@outlook.com
// DATE: : 2024-04-15 00:15:06
// VERSION : v1.0
// DESCRIPTION :
//
// CHANGE LOG :
// @2024-04-15 00:15:06 : create this file (by funnydigitalworld)
//
//--====================================================================================--//
module AD9250_SPI_AXI
(
input s_axi_aclk , //(IW: 1)
input s_axi_aresetn , //(IW: 1)
input [7 :0] spi_rdata , //(IW: 8)
input spi_ready , //(IW: 1)
input [6 :0] s_axi_awaddr , //(IW: 7)
input s_axi_awvalid , //(IW: 1)
input [31:0] s_axi_wdata , //(IW: 32)
input [3 :0] s_axi_wstrb , //(IW: 4)
input s_axi_wvalid , //(IW: 1)
input s_axi_bready , //(IW: 1)
input [6 :0] s_axi_araddr , //(IW: 7)
input s_axi_arvalid , //(IW: 1)
input s_axi_rready , //(IW: 1)
output reg spi_tran_start , //(OR: 1)
output reg spi_rewn_flag , //(OR: 1)
output reg [7 :0] spi_demux_head , //(OR: 8)
output wire [12:0] spi_inst_addr , //(OW: 13)
output reg [7 :0] spi_write_data , //(OR: 8)
output wire s_axi_awready , //(OW: 1)
output wire s_axi_wready , //(OW: 1)
output wire [1 :0] s_axi_bresp , //(OW: 2)
output wire s_axi_bvalid , //(OW: 1)
output wire s_axi_arready , //(OW: 1)
output wire [31:0] s_axi_rdata , //(OW: 32)
output wire [1 :0] s_axi_rresp , //(OW: 2)
output wire s_axi_rvalid //(OW: 1)
);
//--=======================================================--//
// INTERNAL DECLARATION
//--=======================================================--//
//-=========(SI_BEG:PARA)==========-//
//-==========(SI_BEG:REG)==========-//
reg [31:0] axi_cfg_h00 ; //(R: 32)
reg [31:0] axi_cfg_h0c ; //(R: 32)
reg axi_rd_latch ; //(R: 1)
reg m_apb_pready ; //(R: 1)
reg cfg_wr ; //(R: 1)
reg cfg_rd ; //(R: 1)
reg [6 :0] cfg_addr ; //(R: 7)
reg [31:0] cfg_wdat ; //(R: 32)
reg [31:0] m_apb_prdata ; //(R: 32)
reg wait_spi_en ; //(R: 1)
reg cfg_ready ; //(R: 1)
reg [11:0] spi_offset_addr ; //(R: 12)
//-=========(SI_BEG:WIRE)==========-//
wire setup_phase ; //(W: 1)
wire apb_oper_done ; //(W: 1)
wire m_apb_pwrite ; //(W: 1)
wire m_apb_penable ; //(W: 1)
wire m_apb_psel ; //(W: 1)
wire [6 :0] m_apb_paddr ; //(W: 7)
wire [31:0] m_apb_pwdata ; //(W: 32)
//--=======================================================--//
// MAIN SOURCE CODE
//--=======================================================--//
axi4lite_apb_bridge U_AXI4LITE_APB
(
.s_axi_aclk (s_axi_aclk ), // input wire s_axi_aclk
.s_axi_aresetn (s_axi_aresetn ), // input wire s_axi_aresetn
.s_axi_awaddr (s_axi_awaddr ), // input wire [6 : 0] s_axi_awaddr
.s_axi_awvalid (s_axi_awvalid ), // input wire s_axi_awvalid
.s_axi_awready (s_axi_awready ), // output wire s_axi_awready
.s_axi_wdata (s_axi_wdata ), // input wire [31 : 0] s_axi_wdata
.s_axi_wvalid (s_axi_wvalid ), // input wire s_axi_wvalid
.s_axi_wready (s_axi_wready ), // output wire s_axi_wready
.s_axi_bresp (s_axi_bresp ), // output wire [1 : 0] s_axi_bresp
.s_axi_bvalid (s_axi_bvalid ), // output wire s_axi_bvalid
.s_axi_bready (s_axi_bready ), // input wire s_axi_bready
.s_axi_araddr (s_axi_araddr ), // input wire [6 : 0] s_axi_araddr
.s_axi_arvalid (s_axi_arvalid ), // input wire s_axi_arvalid
.s_axi_arready (s_axi_arready ), // output wire s_axi_arready
.s_axi_rdata (s_axi_rdata ), // output wire [31 : 0] s_axi_rdata
.s_axi_rresp (s_axi_rresp ), // output wire [1 : 0] s_axi_rresp
.s_axi_rvalid (s_axi_rvalid ), // output wire s_axi_rvalid
.s_axi_rready (s_axi_rready ), // input wire s_axi_rready
.m_apb_paddr (m_apb_paddr ), // output wire [6 : 0] m_apb_paddr
.m_apb_psel (m_apb_psel ), // output wire [0 : 0] m_apb_psel
.m_apb_penable (m_apb_penable ), // output wire m_apb_penable
.m_apb_pwrite (m_apb_pwrite ), // output wire m_apb_pwrite
.m_apb_pwdata (m_apb_pwdata ), // output wire [31 : 0] m_apb_pwdata
.m_apb_pready (m_apb_pready ), // input wire [0 : 0] m_apb_pready
.m_apb_prdata (m_apb_prdata ), // input wire [31 : 0] m_apb_prdata
.m_apb_pslverr (m_apb_pslverr ) // input wire [0 : 0] m_apb_pslverr
);
//--=========================================---
always@(posedge s_axi_aclk or negedge s_axi_aresetn)
begin
if(s_axi_aresetn == 1'b0)
m_apb_pready <= 1'b1 ;
else if(setup_phase)
m_apb_pready <= 1'b0 ;
else if(apb_oper_done)
m_apb_pready <= 1'b1 ;
end
assign setup_phase = m_apb_psel & (~m_apb_penable);
always@(posedge s_axi_aclk or negedge s_axi_aresetn)
begin
if(s_axi_aresetn == 1'b0)
begin
cfg_addr <= 7'b0 ;
cfg_wdat <= 32'b0 ;
cfg_wr <= 1'b0 ;
cfg_rd <= 1'b0 ;
end
else if(setup_phase)
begin
cfg_addr <= m_apb_paddr ;
cfg_wdat <= m_apb_pwdata ;
cfg_wr <= m_apb_pwrite ;
cfg_rd <=~m_apb_pwrite ;
end
else
begin
cfg_wr <= 1'b0 ;
cfg_rd <= 1'b0 ;
end
end
//--SPI Control--
always@(posedge s_axi_aclk or negedge s_axi_aresetn)
begin
if(s_axi_aresetn == 1'b0)
begin
spi_demux_head <= 8'h80 ;
spi_offset_addr <= 12'h00 ;
spi_write_data <= 8'h00 ;
end
else if((cfg_addr == 7'h04 || cfg_addr == 7'h08) & cfg_wr)
begin
spi_demux_head <= cfg_wdat[31:24] ;
spi_offset_addr <= cfg_wdat[19:8] ;
spi_write_data <= cfg_wdat[7:0] ;
end
end
assign spi_inst_addr = {1'b0,spi_offset_addr};
always@(posedge s_axi_aclk or negedge s_axi_aresetn)
begin
if(s_axi_aresetn == 1'b0)
spi_rewn_flag <= 1'b1 ;
else if((cfg_addr == 7'h04) & cfg_wr)
spi_rewn_flag <= 1'b0 ;
else if((cfg_addr == 7'h10) & cfg_rd)
spi_rewn_flag <= 1'b1 ;
end
always@(posedge s_axi_aclk or negedge s_axi_aresetn)
begin
if(s_axi_aresetn == 1'b0)
spi_tran_start <= 1'b0 ;
else if((cfg_addr == 7'h04) & cfg_wr)
spi_tran_start <= 1'b1 ;
else if((cfg_addr == 7'h10) & cfg_rd)
spi_tran_start <= 1'b1 ;
else
spi_tran_start <= 1'b0 ;
end
always@(posedge s_axi_aclk or negedge s_axi_aresetn)
begin
if(s_axi_aresetn == 1'b0)
begin
axi_cfg_h00 <= 32'haabbccdd ;
axi_cfg_h0c <= 32'h12345678 ;
end
else if(cfg_wr)
begin
case(cfg_addr)
7'h00: axi_cfg_h00 <= cfg_wdat ;
7'h0c: axi_cfg_h0c <= cfg_wdat ;
default: ;
endcase
end
end
always@(posedge s_axi_aclk or negedge s_axi_aresetn)
begin
if(s_axi_aresetn == 1'b0)
m_apb_prdata <= 32'b0 ;
else if(apb_oper_done)
begin
case(cfg_addr)
8'h00: m_apb_prdata <= axi_cfg_h00 ;
8'h04: m_apb_prdata <= {spi_demux_head[7:0],{3'b0,spi_rewn_flag,spi_offset_addr[11:0]},spi_write_data[7:0]} ;
8'h08: m_apb_prdata <= {spi_demux_head[7:0],{3'b0,spi_rewn_flag,spi_offset_addr[11:0]},8'b0} ;
8'h0c: m_apb_prdata <= axi_cfg_h0c ;
8'h10: m_apb_prdata <= {spi_demux_head[7:0],{3'b0,spi_rewn_flag,spi_offset_addr[11:0]},spi_rdata} ;
default: m_apb_prdata <= 32'b0;
endcase
end
end
always@(posedge s_axi_aclk or negedge s_axi_aresetn)
begin
if(s_axi_aresetn == 1'b0)
wait_spi_en <= 1'b0 ;
else if((cfg_addr == 7'h04) & cfg_wr)
wait_spi_en <= 1'b1 ;
else if((cfg_addr == 7'h10) & cfg_rd)
wait_spi_en <= 1'b1 ;
else if(cfg_wr | cfg_rd)
wait_spi_en <= 1'b0 ;
end
always@(posedge s_axi_aclk or negedge s_axi_aresetn)
begin
if(s_axi_aresetn == 1'b0)
cfg_ready <= 1'b0 ;
else
cfg_ready <= cfg_wr | cfg_rd ;
end
assign apb_oper_done = wait_spi_en ? spi_ready : cfg_ready ;
//--=======================================================--//
// MODULE INSTANTATION
//--=======================================================--//
//-====(INSTFILES)====-//
//-=========(SI_BEG:INST)==========-//
//--=======================================================--//
endmodule
AD9250_SPI_MST.v
//--====================================================================================--//
// Copyright (C) 2024 funnydigitalworld. All rights reserved.
// This confidential and proprietary software may be used only as authorized
// by a licensing agreement from funnydigitalworld(funnydigitalworld@outlook.com).
// The entire notice above must be reproduced on all authorized copies.
//
// FILENAME : .\ad9250_spi_mst.v
// MODULE : AD9250_SPI_MST
// AUTHOR : (Wechat:funnydigitalworld)/(QQ:2702272533)
// EMAIL : funnydigitalworld@outlook.com
// DATE: : 2024-04-14 22:01:22
// VERSION : v1.0
// DESCRIPTION :
//
// CHANGE LOG :
// @2024-04-14 22:01:22 : create this file (by funnydigitalworld)
//
//--====================================================================================--//
module AD9250_SPI_MST
(
input clk , //(IW: 1)
input rst_n , //(IW: 1)
input spi_tran_start , //(IW: 1)
input spi_rewn_flag , //(IW: 1)
input [7 :0] spi_demux_head , //(IW: 8)
input [12:0] spi_inst_addr , //(IW: 13)
input [7 :0] spi_write_data , //(IW: 8)
input spi_miso , //(IW: 1)
output reg spi_clk , //(OR: 1)
output reg spi_csn , //(OR: 1)
output wire spi_mosi , //(OW: 1)
output reg [7 :0] spi_rdata , //(OR: 8)
output reg spi_ready //(OR: 1)
);
//--=======================================================--//
// INTERNAL DECLARATION
//--=======================================================--//
//-=========(SI_BEG:PARA)==========-//
//-==========(SI_BEG:REG)==========-//
reg [3 :0] spi_div_cnt ; //(R: 4)
reg spi_tran_en ; //(R: 1)
reg [7 :0] spi_bit_cnt ; //(R: 8)
reg spi_rcv_en ; //(R: 1)
reg [32:0] spi_tran_stream ; //(R: 33)
//-=========(SI_BEG:WIRE)==========-//
wire [1 :0] spi_word_len ; //(W: 2)
wire spi_tran_done ; //(W: 1)
wire spi_rise_ps ; //(W: 1)
wire spi_fall_ps ; //(W: 1)
//--=======================================================--//
// MAIN SOURCE CODE
//--=======================================================--//
parameter SPI_RISE_CNT = 4'd7 ;
parameter SPI_FALL_CNT = 4'd3 ;
assign spi_word_len = 2'b00 ; //2'B00: 1 Byte Mode. 2'B01: 2 Byte Mode. ...
//--==================================--
// Gen SPI Clock
//--==================================--
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
spi_tran_en <= 1'b0 ;
else if(spi_tran_start)
spi_tran_en <= 1'b1 ;
else if(spi_tran_done)
spi_tran_en <= 1'b0 ;
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
spi_div_cnt <= 4'b0 ;
else if(~spi_tran_en)
spi_div_cnt <= 4'b0 ;
else if(spi_div_cnt >= SPI_RISE_CNT)
spi_div_cnt <= 4'b0 ;
else
spi_div_cnt <= spi_div_cnt + 4'b1 ;
end
assign spi_rise_ps = (spi_div_cnt == SPI_RISE_CNT);
assign spi_fall_ps = (spi_div_cnt == SPI_FALL_CNT);
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
spi_bit_cnt <= 8'b0 ;
else if(spi_tran_start)
spi_bit_cnt <= 8'b0 ;
else if(spi_fall_ps)
spi_bit_cnt <= spi_bit_cnt + 8'b1 ;
end
assign spi_tran_done = (spi_bit_cnt >= 8'd33) & spi_rise_ps;
//assign spi_tran_done = (spi_bit_cnt >= 8'd33) & spi_fall_ps;
//CSB Gen
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
spi_clk <= 1'b1 ;
else if(spi_tran_done)
spi_clk <= spi_clk ;
else if(spi_fall_ps)
spi_clk <= 1'b0 ;
else if(spi_rise_ps)
spi_clk <= 1'b1 ;
else if(~spi_tran_en)
spi_clk <= 1'b1 ;
end
//SCLK
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
spi_csn <= 1'b1 ;
else if(spi_tran_start)
spi_csn <= 1'b0 ;
else if(spi_tran_done)
spi_csn <= 1'b1 ;
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
spi_tran_stream <= {1'b1,8'b0,16'b0,8'b0} ;
else if(spi_tran_start & (spi_rewn_flag == 1'b0))
spi_tran_stream <= {1'b1,spi_demux_head[7:0],{1'b0,spi_word_len[1:0],spi_inst_addr[12:0]},spi_write_data[7:0]} ;
else if(spi_tran_start & (spi_rewn_flag == 1'b1))
spi_tran_stream <= {1'b1,spi_demux_head[7:0],{1'b1,spi_word_len[1:0],spi_inst_addr[12:0]},8'b0} ;
else if(spi_tran_en & spi_fall_ps)
spi_tran_stream <= {spi_tran_stream[31:0],1'b1};
end
assign spi_mosi = spi_tran_stream[32] ;
//Read Data
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
spi_rcv_en <= 1'b0 ;
else if(spi_tran_start & (spi_rewn_flag == 1'b1))
spi_rcv_en <= 1'b1 ;
else if(spi_tran_done)
spi_rcv_en <= 1'b0 ;
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
spi_rdata <= 8'b0 ;
else if(spi_tran_start)
spi_rdata <= 1'b0 ;
else if(spi_rcv_en & spi_rise_ps & (spi_bit_cnt >= 8'd25) & (spi_bit_cnt <= 8'd32))
spi_rdata <= {spi_rdata[6:0],spi_miso} ;
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
spi_ready <= 1'b0 ;
else if(spi_tran_start)
spi_ready <= 1'b0 ;
//else if(spi_rcv_en & spi_rise_ps & (spi_bit_cnt >= 8'd32))
else if(spi_rise_ps & (spi_bit_cnt == 8'd32))
spi_ready <= 1'b1 ;
else
spi_ready <= 1'b0 ;
end
//--=======================================================--//
// MODULE INSTANTATION
//--=======================================================--//
//-====(INSTFILES)====-//
//-=========(SI_BEG:INST)==========-//
//--=======================================================--//
endmodule
AXI APB Bridge IP配置