• 简易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
  • 相关阅读:
    软件工程第四次作业
    软件工程第三次作业-------(李利思 岳庆)
    软件工程第二次作业
    《软件工程》第一次作业
    构建之法书评
    个人阅读作业三
    对MSF八个原则的思考
    个人阅读作业2 软工方法论无用?
    代码复审
    结对编程总结 1175 1176
  • 原文地址:https://www.cnblogs.com/yiwenbo/p/10047063.html
Copyright © 2020-2023  润新知