LOCALBUS总线转AXI-LITE总线逻辑实现

Scroll Down

前言

AXI(Advanced eXtensible Interface)是一种总线协议,该协议是ARM公司提出的AMBA(Advanced Microcontroller Bus Architecture)3.0协议中最重要的部分,是一种面向高性能、高带宽、低延迟的片内总线。它的地址/控制和数据相位是分离的,支持不对齐的数据传输,同时在突发传输中,只需要首地址,同时分离的读写数据通道、并支持Outstanding传输访问和乱序访问,并更加容易进行时序收敛。AXI 是AMBA 中一个新的高性能协议。AXI 技术丰富了现有的AMBA 标准内容,满足超高性能和复杂的片上系统(SoC)设计的需求。

AXI4.0总线的分类

AMBA4.0 包括AXI4.0、AXI4.0-lite、ACE4.0、AXI4.0-stream。
AXI4.0-LITE是AXI的简化版本,AXI4.0-stream是ARM公司和Xilinx公司一起提出,主要用在FPGA进行以数据为主导的大量数据的传输应用。在工作中常常会遇到localbus与axi总线对接的情况,这里以简单的AXI4.0-LITE为例梳理一下逻辑时序的实现。

LOCALBUS总线的时序说明

localbus总线的读写时序图如下所示。在一个lbe_cs_n片选周期内只允许产生一次lbe_wr_en或lbe_rd_en。在片选信号下降沿到来之前以及片选信号有效期间内,lbe_addr和lbe_wr_dat(lbe_rd_dat)要保持稳定。
image.png
image.png

LOCALBUS转读写REQUEST信号时序说明

下图是LOCALBUS转读写REQUEST信号时序说明。lbe_addr[7]和lbe_addr[0]作为控制。localbus数据可为16位或8位,当为16位时需要lbe_addr[0]由0跳到1才能读出32位axi数据,且lbe_addr[0]为1时读出低16位数据。
lbe_addr[6:1]作为axi接口地址使用。
image.png
image.png

`timescale 1ns/1ns

module lbe_to_req #(
    parameter   U_DLY               = 1'b1          
)(
/*================================================================
    Clock & Reset 
================================================================*/
    input                           clk_sys                     , // (input )
    input                           rst_n                       , // (input )
/*================================================================
    LBE Signals 
================================================================*/
    input                           lbe_cs_n                    , // (input )
    input                           lbe_wr_en                   , // (input )
    input                           lbe_rd_en                   , // (input )
    input                    [ 7:0] lbe_addr                    , // (input )
    input                    [15:0] lbe_wr_dat                  , // (input ) 
    output reg               [15:0] lbe_rd_dat                  , // (output)
/*================================================================
    Control Signals 
================================================================*/
    output reg                      wr_req                      , // (output) 
    output reg                [7:0] wr_addr                     , // (output)
    output reg               [31:0] wr_data                     , // (output)
    output                    [3:0] wr_wbe                      , // (output)
    input                           wr_busy                     , // (input )
    input                     [1:0] wr_status                   , // (input )

    output reg                      rd_req                      , // (output)
    output reg                [7:0] rd_addr                     , // (output)
    input                    [31:0] rd_data                     , // (input )
    input                           rd_busy                     , // (input )
    input                     [1:0] rd_status                     // (input )
);
//Register Define
reg                          [15:0] test_reg                    ; 

//Wire Define

/*================================================================
\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
       Write Request Generate
       Two 16bit Write & High Addr Trigger wr_req
/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
================================================================*/
always @(posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        wr_req <= #U_DLY 1'b0;
    else if((lbe_cs_n == 1'b0) && (lbe_wr_en == 1'b1) && (lbe_addr[7] == 1'b0) && (lbe_addr[0] == 1'b1)) // high addr trigger wr_req
        wr_req <= #U_DLY 1'b1;
    else
        wr_req <= #U_DLY 1'b0;
end

always @(posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        wr_addr <= #U_DLY 8'h0;
    else
        wr_addr <= #U_DLY {lbe_addr[6:1],2'h0};
end

/*==============Load Write Data==============*/
always @(posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        wr_data <= #U_DLY 32'h0;
    else 
        begin
            if((lbe_cs_n == 1'b0) && (lbe_wr_en == 1'b1) && (lbe_addr[7] == 1'b0))
                begin
                    if (lbe_addr[0] == 1'b0)
                        wr_data <= #U_DLY {lbe_wr_dat,16'h0000};
                    else
                        wr_data <= #U_DLY {wr_data[31:16],lbe_wr_dat};
                end
            else
                ;
        end
end

assign wr_wbe = 4'hf;

/*================================================================
\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
        Read Request Generate       
        Two 16bit Read & Low Addr Trigger rd_req
/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
================================================================*/
always @(posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        rd_req <= #U_DLY 1'b0;
    else if((lbe_cs_n == 1'b0) && (lbe_rd_en == 1'b1) && (lbe_addr[7] == 1'b0) && (lbe_addr[0] == 1'b0)) //low addr trigger rd_req
        rd_req <= #U_DLY 1'b1;
    else
        rd_req <= #U_DLY 1'b0;
end

always @(posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        rd_addr <= #U_DLY 8'h0;
    else
        rd_addr <= #U_DLY {lbe_addr[6:1],2'h0};
end

/*==============Load Test Register==============*/
always @(posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        test_reg <= #U_DLY 16'h55aa;
    else if((lbe_cs_n == 1'b0) && (lbe_wr_en == 1'b1) && (lbe_addr[7:0] == 8'h80))
        test_reg <= #U_DLY ~lbe_wr_dat;
    else;
end
/*==============Load Read Data==============*/
always @(posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        lbe_rd_dat <= #U_DLY 16'h0;
    else if((lbe_cs_n == 1'b0) && (lbe_rd_en == 1'b1) && (lbe_addr[7] == 1'b1))
        case(lbe_addr[6:0])
            7'h00   : lbe_rd_dat <= #U_DLY test_reg;
            7'h01   : lbe_rd_dat <= #U_DLY {13'h0,wr_busy,wr_status};
            7'h02   : lbe_rd_dat <= #U_DLY {13'h0,rd_busy,rd_status};
            default : lbe_rd_dat <= #U_DLY 16'h0;
        endcase
    else if((lbe_cs_n == 1'b0) && (lbe_addr[7] == 1'b0))
        case(lbe_addr[0])
            1'b0    : lbe_rd_dat <= #U_DLY rd_data[31:16];
            1'b1    : lbe_rd_dat <= #U_DLY rd_data[15:0];
            default : lbe_rd_dat <= #U_DLY 16'h0;
        endcase   
    else
        ;
end

endmodule

读写REQUEST转AXI-LITE时序说明

下图为读写REQUEST转AXI-LITE时序说明。由于localbus速率不高,所以axi-lite以1组32bit数据非连续传输,主机端的数据与有效信号同时保持在数据线上,当主机收到ready信号时代表数据已经完成一次传输。
image.png
image.png

`timescale 1ns/1ns


module req_to_axi #(
    parameter   U_DLY               = 1'b1          
)(
/*================================================================
    Clock & Reset 
================================================================*/
    input                           clk_sys                     , // (input )
    input                           rst_n                       , // (input )
/*================================================================
    Control Signals 
================================================================*/
    input                           wr_req                      , // (input )
    input                     [7:0] wr_addr                     , // (input )
    input                    [31:0] wr_data                     , // (input )
    input                     [3:0] wr_wbe                      , // (input )
    output reg                      wr_busy                     , // (output)
    output reg                [1:0] wr_status                   , // (output)

    input                           rd_req                      , // (input )
    input                     [7:0] rd_addr                     , // (input )
    output reg               [31:0] rd_data                     , // (output)
    output reg                      rd_busy                     , // (output)
    output reg                [1:0] rd_status                   , // (output)
/*================================================================
    AXI_LITE Port 
================================================================*/
    input                           s_axi_awready               , // (input ) Write Port Command Ready   
    output reg                [7:0] s_axi_awaddr                , // (output) Write Port Addr 
    output reg                      s_axi_awvalid               , // (output) Write Port Command Valid
    input                           s_axi_wready                , // (input ) Write Port Data Ready 
    output reg               [31:0] s_axi_wdata                 , // (output) Write Port Data  
    output reg                [3:0] s_axi_wstrb                 , // (output) Write Port Byte Enable 
    output reg                      s_axi_wvalid                , // (output) Write Port Data Valid 
    input                     [1:0] s_axi_bresp                 , // (input ) Write Response Status 
    input                           s_axi_bvalid                , // (input ) Write Response Valid 
    output reg                      s_axi_bready                , // (output) Write Response Ready

    input                           s_axi_arready               , // (input ) Read Port Command Ready 
    output reg                [7:0] s_axi_araddr                , // (output) Read Port Addr 
    output reg                      s_axi_arvalid               , // (output) Read Port Command Valid 
    input                    [31:0] s_axi_rdata                 , // (input ) Read Port Data 
    input                     [1:0] s_axi_rresp                 , // (input ) Read Port Status 
    input                           s_axi_rvalid                , // (input ) Read Port Data Valid 
    output reg                      s_axi_rready                  // (output) Read Port Data Ready 

);
//Register Define

//Wire Define


/*================================================================
    Write Addr 
================================================================*/
/*==============Write Address Valid Generate==============*/
always @(posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        s_axi_awvalid <= #U_DLY 1'b0;
    else if(wr_req == 1'b1)
        s_axi_awvalid <= #U_DLY 1'b1;
    else if(s_axi_awready == 1'b1)
        s_axi_awvalid <= #U_DLY 1'b0;
end
/*==============Load Address==============*/
always @(posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        s_axi_awaddr <= #U_DLY 8'h0;
    else if(wr_req == 1'b1)
        s_axi_awaddr <= #U_DLY wr_addr;
    else if(s_axi_awready == 1'b1)
        s_axi_awaddr <= #U_DLY 8'h0;
end



/*================================================================
    Write Data 
================================================================*/
/*==============Write Data Valid Generate==============*/
always @(posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        s_axi_wvalid <= #U_DLY 1'b0;
    else if(wr_req == 1'b1)
        s_axi_wvalid <= #U_DLY 1'b1;
    else if(s_axi_wready == 1'b1)
        s_axi_wvalid <= #U_DLY 1'b0;
end
/*==============Load Write Data==============*/
always @(posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        s_axi_wdata <= #U_DLY 32'h0;
    else if(wr_req == 1'b1)
        s_axi_wdata <= #U_DLY wr_data;
    else if(s_axi_wready == 1'b1)
        s_axi_wdata <= #U_DLY 32'h0;
end
/*==============Load WSTRB==============*/
always @(posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        s_axi_wstrb <= #U_DLY 4'h0;
    else if(wr_req == 1'b1)
        s_axi_wstrb <= #U_DLY wr_wbe;//wr_wbe=4'hf 1bit for 1byte wdata
    else if(s_axi_wready == 1'b1)
        s_axi_wstrb <= #U_DLY 4'h0;
end

/*================================================================
    Write Response Generate 
================================================================*/
/*==============Write Respone Ready==============*/
always @(posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        s_axi_bready <= #U_DLY 1'b0;
    else if(s_axi_bvalid == 1'b1)
        s_axi_bready <= #U_DLY 1'b0;
    else
        s_axi_bready <= #U_DLY 1'b1;
end

/*================================================================
    WRITE STATUS 
================================================================*/
/*==============Write Busy Generate==============*/
always @(posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        wr_busy <= #U_DLY 1'b0;
    else if(wr_req == 1'b1)
        wr_busy <= #U_DLY 1'b1;
    else if(s_axi_bvalid == 1'b1)
        wr_busy <= #U_DLY 1'b0;
    else;
end

always @(posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        wr_status <= #U_DLY 2'h0;
    else if(wr_req == 1'b1)
        wr_status <= #U_DLY 2'h0;
    else if(s_axi_bvalid == 1'b1)
        wr_status <= #U_DLY s_axi_bresp;
    else; 
end
/*================================================================
    READ ADDR 
================================================================*/
always @(posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        s_axi_araddr <= #U_DLY 8'h0;
    else if(rd_req == 1'b1)
        s_axi_araddr <= #U_DLY rd_addr;
    else if(s_axi_arready == 1'b1)
        s_axi_araddr <= #U_DLY 8'h0;
    else;
end

always @(posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        s_axi_arvalid <= #U_DLY 1'b0;
    else if(rd_req == 1'b1)
        s_axi_arvalid <= #U_DLY 1'b1;
    else if(s_axi_arready == 1'b1)
        s_axi_arvalid <= #U_DLY 1'b0;
end

/*================================================================
    READ DATA 
================================================================*/
always @(posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        s_axi_rready <= #U_DLY 1'b0;
    else if(s_axi_rvalid == 1'b1)
        s_axi_rready <= #U_DLY 1'b0;
    else
        s_axi_rready <= #U_DLY 1'b1;
end

always @(posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        rd_data <= #U_DLY 32'h0;
    else if(s_axi_rvalid == 1'b1)
        rd_data <= #U_DLY s_axi_rdata;
    else;
end

/*================================================================
    READ STATUS 
================================================================*/
always @(posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        rd_busy <= #U_DLY 1'b0;
    else if(rd_req == 1'b1)
        rd_busy <= #U_DLY 1'b1;
    else if(s_axi_arvalid == 1'b1)
        rd_busy <= #U_DLY 1'b0;
    else;
end

always @(posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        rd_status <= #U_DLY 2'h0;
    else if(rd_req == 1'b1)
        rd_status <= #U_DLY 2'h0;
    else if(s_axi_arvalid == 1'b1)
        rd_status <= #U_DLY s_axi_rresp;
    else;
end

endmodule