簡易APB4 slave實踐


   一個簡易的(不完整的)APB4 slave的可以沒有PREADY和PSLVERR,這兩個信號都被賦予常數,以及沒有PPROT。

   兩種不同類型的寄存器:

       圖: 普通寄存器電路圖

           圖: 帶讀寫控制寄存器電路圖

                        圖:帶讀寫控制寄存器時序圖

 

 一般來講,一個模塊的interface到內部reg之間,需要的信號為地址信號addr,讀寫使能信號(分開),byte_strobe字節選通信號,讀寫數據信號(分開)。

注意:在傳輸結束后不要立即更改地址和寫信號,保持當前狀態知道開始下一個傳輸,這樣可以降低功耗。

 interface spec:

寄存器表: 

 

 代碼:apb4的頂層

 1 module apb4_slave #(
 2  parameter ADDRWIDTH = 12)
 3 (
 4     input     wire               PCLK,
 5     input     wire               PRESETn,
 6 
 7     input    wire                    PSEL,
 8     input    wire[ADDRWIDTH -1:0]    PADDR,
 9     input    wire                    PENABLE,
10     input    wire                    PWRITE,
11     input    wire[31:0]              PWDATA,
12     input    wire[3:0]               PSTRB,
13 
14     input    wire[3:0]                ECOREVNUM,
15     
16     output    wire[31:0]              PRDATA,
17     output    wire                    PREADY,
18     output    wire                    PSLVERR
19 );
20 21   wire  [ADDRWIDTH-1:0]  reg_addr;
22   wire             reg_read_en;
23   wire             reg_write_en;
24   wire  [3:0]          reg_byte_strobe;
25   wire  [31:0]        reg_wdata;
26   wire   [31:0]        reg_rdata;
27 28 apb4_slave #(.ADDRWIDTH  (ADDRWIDTH))
29   u_apb_slave_interface(
30 .pclk        (PCLK),
31 .presetn      (PRESETn),
32 
33 .psel        (PSEL),
34 .paddr        (PADDR),
35 .penable      (PENABLE),
36 .pwrite       (PWRITE),
37 .pwdata       (PWDATA),
38 .pstrb       (PSTRB),
39 
40 .prdata       (PRDATA),
41 .pready       (PREADY),
42 .pslverr      (PSLVERR),
43 
44 .addr        (reg_addr),
45 .read_en       (reg_read_en),
46 .write_en      (reg_write_en),
47 .byte_strobe    (reg_byte_strobe),
48 .wdata        (reg_wdata),
49 .tdata        (reg_rdata)
50 
51 );

 

 如何在interface這個模塊中確定APB的建立周期信號?

其實APB的建立周期信號可以理解為一個使能信號,在第一周期拉高這個使能信號,那么下個周期就可以判斷使能信號,並進入到傳輸周期了。這里使用組合邏輯去解決使能信號問題,一個是讀使能read_en,一個是寫使能write_en。read_en = psel &(~pwrite); write_en = psel &(~penable) & pwrite;  這里peanble是主機發過來的輸入信號,這里說的從機不用管。

代碼:apb4的邏輯接口

u_apb_slave_interfacef #(
    parameter    ADDRWIDTH = 12)
(
    // IO declaration
    input    wire                pclk,
    input    wire                presetn,
    // apb interface input
    input    wire                psel,
    input    wire[ADDRWIDTH-1:0]    paddr,
    input    wire                penable,
    input    wire                pwite,
    input    wire[31:0]            pwdata,
    input    wire[3:0]            pstrb,
    // apb interface output
    output     wire[31:0]            prdata,
    output     wire                pready,
    // Register interface
    output    wire[ADDRWIDTH-1:0]    addr,
    output    wire                read_en,
    output    wire                write_en,
    output    wire[3:0]            byte_pstrb,
    output    wire[31:0]            wdata,
    output    wire[31:0]            rdata
);

assign     pready = 1'b1;
assign     pslverr = 1'b0;

assign    addr = paddr;
assign    read_en = psel & (~pwrite);                // 當pwrite為0,psel為1的時候,才讀
assign    write_en = psel & (~penable) & pwrite;     // 在PCLK中,第一拍為psel有效,penable為低,第二拍才是psel和penable同時有效;

assign    byte_pstrb = pstrb;
assign    wdata = pwdata;
assign    prdata = rdata;

 如何設計有等待狀態的寫傳輸?

從模塊的pready輸出之前一直是高,現在想延遲一個時鍾周期,做成脈沖形式。

 可以使用組合邏輯如:

assgin pready_1 = psel & peanble & pwrite;

pready想延遲多個時鍾節拍,可以使用時序電路,建立周期完成后,等待多個clock之后,再講pready拉高。

 代碼:apb的寄存器讀寫

module apb4_slave_reg #(
    parameter    ADDRWIDTH = 12)
(
    input    wire                pclk,
    input    wire                presetn,

    input    wire[ADDRWIDTH-1:0]    addr,
    input    wire                read_en,
    input    wire                write_en,
    input    wire[3:0]            byte_pstrb,
    input    wire[31:0]            wdata,
    input    wire                ecorevnum,
    output    wire[31:0]            rdata

);

localparam ARM_CMSDK_APB4_EG_SLAVE_PID4    = 32'h00000004; //    12'hFD0;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID5    = 32'h00000000; //    12'hFD4;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID6    = 32'h00000000; //    12'hFD8;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID7    = 32'h00000000; //    12'hFDC;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID0    = 32'h00000019; //    12'hFE0;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID1    = 32'h000000B8; //    12'hFE4;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID2    = 32'h0000001B; //    12'hFE8;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID3    = 32'h00000000; //    12'hFEC;

wire        [3:0]        wr_sel;

// 取地址的高10位出來,地址是4K對其的,之后高12bits是不一樣的,從0xfff~0x000,其中的高10位就可以判斷出是要操作那個寄存器了
// 地址是32為對其的,末尾都是0(0000)、4(0100)、8(1000)、C(1100)循環的,低兩位都是一樣的,只有高10位不一樣
assign     wr_sel[0] = ((addr[(ADDRWIDTH-1):2]==10'b0000000000)&(write_en)) ? 1'b1: 1'b0;
assign     wr_sel[1] = ((addr[(ADDRWIDTH-1):2]==10'b0000000001)&(write_en)) ? 1'b1: 1'b0;
assign     wr_sel[2] = ((addr[(ADDRWIDTH-1):2]==10'b0000000010)&(write_en)) ? 1'b1: 1'b0;
assign     wr_sel[3] = ((addr[(ADDRWIDTH-1):2]==10'b0000000011)&(write_en)) ? 1'b1: 1'b0;
 
// write_en = psel & (~penable) & pwrite; 時序要求在penable為高這一拍把數據寫下去,所以要在其前一拍判斷是否要寫。

// 寄存器的寫操作
// Data register: data0
always @(posedge pclk or negedge presetn) begin
    if (~presetn) begin
        data0 <= {32{1'b0}};
    end 
    else if (wr_sel[0]) begin
        if (byte_strobe[0])
            data0[ 7:0] <= wdata[7:0];
        if (byte_strobe[1])
            data0[15:8] <= wdata[15:8];
        if (byte_strobe[2])
            data0[23:16] <= wdata[23:16];
        if (byte_strobe[3])
            data0[31:24] <= wdata[31:24];
    end
end
// Data register: data1
always @(posedge pclk or negedge presetn) begin
    if (~presetn) begin
        data1 <= {32{1'b0}};
    end 
    else if (wr_sel[1]) begin
        if (byte_strobe[0])
            data1[ 7:0] <= wdata[7:0];
        if (byte_strobe[1])
            data1[15:8] <= wdata[15:8];
        if (byte_strobe[2])
            data1[23:16] <= wdata[23:16];
        if (byte_strobe[3])
            data1[31:24] <= wdata[31:24];
    end
end
// Data register: data2
always @(posedge pclk or negedge presetn) begin
    if (~presetn) begin
        data2 <= {32{1'b0}};
    end 
    else if (wr_sel[2]) begin
        if (byte_strobe[0])
            data2[ 7:0] <= wdata[7:0];
        if (byte_strobe[1])
            data2[15:8] <= wdata[15:8];
        if (byte_strobe[2])
            data2[23:16] <= wdata[23:16];
        if (byte_strobe[3])
            data2[31:24] <= wdata[31:24];
    end
end
// Data register: data3
always @(posedge pclk or negedge presetn) begin
    if (~presetn) begin
        data3 <= {32{1'b0}};
    end 
    else if (wr_sel[1]) begin
        if (byte_strobe[0])
            data3[ 7:0] <= wdata[7:0];
        if (byte_strobe[1])
            data3[15:8] <= wdata[15:8];
        if (byte_strobe[2])
            data3[23:16] <= wdata[23:16];
        if (byte_strobe[3])
            data3[31:24] <= wdata[31:24];
    end
end

// 寄存器的讀操作
always @(read_en or    addr or    data0 or data1 or data2 or data3 or ecorevnum) begin
    case (read_en)
    1'b1: begin 
        if (addr[11:4] == 8'h00) begin        // 判斷為RW類型的寄存器
            case (addr[3:2])
                2'b00: rdata = data0;
                2'b01: rdata = data1;
                2'b10: rdata = data2;
                2'b11: rdata = data3;
                default: rdata = {32{1'bx}};
            endcase
        end
        else if (addr[11:6] == 6'h3F) begin     // 判斷為RO類型的寄存器
            case(addr[5:2])
            4'b0100:rdata = ARM_CMSDK_APB4_EG_SLAVE_PID4;
            4'b0101:rdata = ARM_CMSDK_APB4_EG_SLAVE_PID5;
            4'b0110:rdata = ARM_CMSDK_APB4_EG_SLAVE_PID6;
            4'b0111:rdata = ARM_CMSDK_APB4_EG_SLAVE_PID7;
            4'b1000:rdata = ARM_CMSDK_APB4_EG_SLAVE_PID0;
            4'b1001:rdata = ARM_CMSDK_APB4_EG_SLAVE_PID1;
            4'b1010:rdata = ARM_CMSDK_APB4_EG_SLAVE_PID2;
            4'b1011:rdata = {ARM_CMSDK_APB4_EG_SLAVE_PID3[31:0],ecorevnum[3:0],4'h0};
            default: rdata = {32{1'bx}};
            endcase
        end 
        else begin
            rdata = {32{1'b0}};
        end 
    end
    1'b0: begin
        rdata = {32{1'b0}};
    end 
    default: rdata = {32{1'bx}};
    endcase
end 

endmodule

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM