AMBA APB总线

前面分析了AHB总线协议。接下来分析APB总线协议。

  (一) APB总线接口:

  PCLK APB总线时钟。

  PRESETn APB总线复位。低有效。

  PADDR 地址总线。

  PSELx 从设备选择。

  PENABLE APB传输选通。

  PWRITE 高为写传输,低为读。

  PRDATA 读数据总线。

  PWDATA 写数据总线。

  接口信号定义如下:

  interface   apb_slv_intf #(
    parameter   AW  = 32,
                DW  = 32
) (
    input   logic       PCLK,
    input   logic       PRESETn
);
    logic               PSEL;
    logic               PENABLE;
    logic   [AW-1:0]    PADDR;
    logic               PWRITE;
    logic   [DW-1:0]    PWDATA;

    logic   [DW-1:0]    PRDATA;


modport m (
    input   PRDATA,
    output  PSEL, PENABLE, PADDR, PWRITE, PWDATA
);

modport s (
    input   PSEL, PENABLE, PADDR, PWRITE, PWDATA,
    output  PRDATA
);

endinterface: apb_slv_intf

(二) APB总线时序图:

写传输

读传输

  注意在PENABLE信号有效后从设备需要给出有效数据/读取有效数据。

  (三) AHB总线到APB总线转换桥


  module ahb2apb_bridge #(
    parameter   AHB_AW  = 32,
               AHB_DW  = 32,
               APB_AW  = 32,
               APB_DW  = 32,
               NSLV    = 16
) (
    input   logic   HCLK,
    input   logic   HRESETn,
    input   logic   PCLK,
    input   logic   PRESETn,
    ahb_slv_intf.s  ahb,
    apb_slv_intf.m  apbv[NSLV]
);

logic   ahb_work;
logic   apb_work;

genvar  i;

typedef enum logic [1:0] {
    AHB_IDLE    = 2'b00,
    AHB_WRITE   = 2'b01,
    AHB_READ    = 2'b10,
    AHB_WAIT    = 2'b11
} ahb_state_e;

// Signal of AHB Domain
struct {
    logic                   work;
    logic   [AHB_AW-1:0]    addr;
    logic   [AHB_DW-1:0]    data;
    logic                   write;
    ahb_state_e             cstate, nstate;
} ahbd;

typedef enum logic [1:0] {
    APB_IDLE    = 2'b00,
    APB_WRITE   = 2'b01,
    APB_READ    = 2'b10
} apb_state_e;

// Signal of APB Domain
struct {
    logic                   work;
    logic   [APB_DW-1:0]    data[NSLV];
    logic                   PSEL[NSLV];
    logic                   PENABLE[NSLV];
    apb_state_e             cstate, nstate;
} apbd;


// AHB Control Logic
always_comb begin
    case (ahbd.cstate)
        AHB_IDLE: begin
            if (ahb.HSEL && ahb.HTRANS == HTRANS_NONSEQ) begin
                if (ahb.HWRITE)
                    ahbd.nstate = AHB_WRITE;
                else
                    ahbd.nstate = AHB_READ;
            end
            else
                ahbd.nstate = AHB_IDLE;
        end
        AHB_WRITE: begin
            if (apbd.work)
                ahbd.nstate = AHB_WAIT;
            else
                ahbd.nstate = AHB_WRITE;
        end
        AHB_READ: begin
            if (apbd.work)
                ahbd.nstate = AHB_WAIT;
            else
                ahbd.nstate = AHB_READ;
        end
        AHB_WAIT: begin
            if (!apbd.work)
                ahbd.nstate = AHB_IDLE;
            else
                ahbd.nstate = AHB_WAIT;
        end
        default: ahbd.nstate = AHB_IDLE;
    endcase
end

always_ff @(posedge HCLK or negedge HRESETn) begin
    if (!HRESETn)
        ahbd.cstate <= AHB_IDLE;
    else
        ahbd.cstate <= ahbd.nstate;
end

always_ff @(posedge HCLK or negedge HRESETn) begin
    if (!HRESETn) begin
        ahbd.work   <= 1'b0;
        ahbd.addr   <= '0;
        ahbd.data   <= '0;
        ahbd.write  <= 1'b0;
        ahb.HREADY  <= 1'b1;
        ahb.HRDATA[APB_DW-1:0]  <= '0;
    end
    else begin
        case (ahbd.cstate)
            AHB_IDLE: begin
                if (ahb.HSEL && ahb.HTRANS == HTRANS_NONSEQ) begin
                    ahbd.addr   <= ahb.HADDR;
                    ahbd.write  <= ahb.HWRITE;
                    ahb.HREADY  <= 1'b0;
                end
                else begin
                    ahbd.addr   <= '0;
                    ahbd.write  <= 1'b0;
                    ahb.HREADY  <= 1'b1;
                end
                ahbd.work   <= 1'b0;
                ahbd.data   <= '0;
                ahb.HRDATA[APB_DW-1:0]  <= apbd.data[ahbd.addr[AHB_AW-5:AHB_AW-8]];
            end
            AHB_WRITE: begin
                ahb.HREADY  <= 1'b0;
                ahbd.work   <= 1'b1;
                ahbd.data   <= ahb.HWDATA;
                ahb.HRDATA[APB_DW-1:0]  <= '0;
            end
            AHB_READ: begin
                ahbd.work   <= 1'b1;
                ahbd.data   <= '0;
                ahb.HREADY  <= 1'b0;
                ahb.HRDATA[APB_DW-1:0]  <= '0;
            end
            AHB_WAIT: begin
                ahbd.work   <= 1'b0;
                ahb.HREADY  <= 1'b0;
                ahb.HRDATA[APB_DW-1:0]  <= '0;
            end
        endcase
    end
end

assign  ahb.HRESP = HRESP_OKAY;
// assign  ahb.HRDATA[AHB_DW-1:APB_DW] = '0;


// APB Control Logic
always_comb begin
    case (apbd.cstate)
        APB_IDLE: begin
            if (ahbd.work) begin
                if (ahbd.write)
                    apbd.nstate = APB_WRITE;
                else
                    apbd.nstate = APB_READ;
            end
            else
                apbd.nstate = APB_IDLE;
        end
        APB_WRITE:  apbd.nstate = APB_IDLE;
        APB_READ:   apbd.nstate = APB_IDLE;
        default:    apbd.nstate = APB_IDLE;
    endcase
end

always_ff @(posedge PCLK or negedge PRESETn) begin
    if (!PRESETn)
        apbd.cstate <= APB_IDLE;
    else
        apbd.cstate <= apbd.nstate;
end

always_ff @(posedge PCLK or negedge PRESETn) begin
    if (!PRESETn) begin
        apbd.work   <= 1'b0;
        for (int j = 0; j < NSLV; j++) begin
            apbd.PSEL[j]    <= 1'b0;
            apbd.PENABLE[j] <= 1'b0;
        end
    end
    else begin
        case (apbd.cstate)
            APB_IDLE: begin
                if (ahbd.work) begin
                    apbd.work <= 1'b1;
                    for (int j = 0; j < NSLV; j++)
                        apbd.PSEL[j] <= (ahbd.addr[AHB_AW-5:AHB_AW-8] == j) ? 1'b1 : 1'b0;
                end
                else begin
                    apbd.work <= 1'b0;
                    for (int j = 0; j < NSLV; j++)
                        apbd.PSEL[j] <= 1'b0;
                end
                for (int j = 0; j < NSLV; j++)
                    apbd.PENABLE[j] <= 1'b0;
            end
            APB_WRITE: begin
                apbd.work <= 1'b1;
                for (int j = 0; j < NSLV; j++)
                    apbd.PENABLE[j] <= (ahbd.addr[AHB_AW-5:AHB_AW-8] == j) ? 1'b1 : 1'b0;
            end
            APB_READ: begin
                apbd.work <= 1'b1;
                for (int j = 0; j < NSLV; j++)
                    apbd.PENABLE[j] <= (ahbd.addr[AHB_AW-5:AHB_AW-8] == j) ? 1'b1 : 1'b0;
            end
        endcase
    end
end

generate
    for (i = 0; i < NSLV; i++) begin: apbv_loop
        assign apbv[i].PADDR    = {4'h0, ahbd.addr[APB_AW-4-1:0]};
        assign apbv[i].PWRITE   = ahbd.write;
        assign apbv[i].PWDATA   = ahbd.data[APB_DW-1:0];
        assign apbd.data[i]     = apbv[i].PRDATA;
        assign apbv[i].PSEL     = apbd.PSEL[i];
        assign apbv[i].PENABLE  = apbd.PENABLE[i];
    end
endgenerate

endmodule: ahb2apb_bridge  

作者:绿杨
首发博客:https://www.cnblogs.com/lyuyangly/p/4853921.html

发表评论

邮箱地址不会被公开。 必填项已用*标注