侧边栏壁纸
    • 累计撰写 302 篇文章
    • 累计收到 527 条评论
    AMBA--APB总线协议及Verilog实现与仿真
    我的学记|刘航宇的博客

    AMBA--APB总线协议及Verilog实现与仿真

    刘航宇
    2023-02-28 / 0 评论 / 1,398 阅读 / 正在检测是否收录...

    1、APB总线简介

    APB:Advanced Peripheral Bus,高级外设总线,具备以下特性:

    (1)低功耗;
    (2)接口协议简单;
    (3)总线传输使用时钟上升沿进行,便于时序分析;
    (4)应用广泛,支持多种外设。

    所有的APB模块均是APB从机。

    2、APB信号列表

    所有的APB总线信号都以字母P作为前缀,下表列出了APB信号的名称以及对信号的描述:

    NameWidthI/ODescription
    PCLK1bitInAPB总线时钟,所有传输只能发生在PCLK的上升沿
    PRESETn1bitInAPB总线复位信号,低电平有效
    PADDR32bitInAPB地址总线
    PSEL1bitInAPB从机片选信号
    PENABLE1bitInAPB选通信号,高电平表示APB传输的第二个周期
    PWRITE1bitInAPB读写控制信号,高表示写,低表示读
    PRDATA32bitOutAPB读数据信号,最高为32位
    PWDATA32bitInAPB写数据信号,最高为32位

    3、APB总线时序

    (1)写时序

    写传输开始于T2时刻,在改时钟上升沿时刻,地址、写信号、PSEL、写数据信号同时发生变化,T2时钟,即传输的第一个时钟被称为SETUP周期。在下个时钟上升沿T3,PENABLE信号拉高,表示ENABLE周期,在该周期内,数据、地址以及控制信号都必须保持有效。整个写传输在这个周期结束时完成。
    (2)读时序

    读传输开始于T2时刻,在改时钟上升沿时刻,地址、写信号、PSEL信号同时发生变化,在下个时钟上升沿T3,PENABLE信号拉高,从机必须在ENABLE周期内提供读数据,读数据信号将在T4上升沿时刻被采样。
    经历2个cycle就读数据,不需要握手
    4、Verilog实现
    下面编写一个简单的基于APB接口的memory读写控制程序供读数据,读数据信号将在T4上升沿时刻被采样。

    `timescale 1ns / 1ps
    
    module apb_sram #(
        parameter                           SIZE_IN_BYTES = 1024
    )
    (
        //----------------------------------
        // IO Declarations
        //----------------------------------
        input                               PRESETn,
        input                               PCLK,
        input                               PSEL,
        input [31:0]                        PADDR,
        input                               PENABLE,
        input                               PWRITE,
        input [31:0]                        PWDATA,
        output reg [31:0]                   PRDATA
    );
    
        //----------------------------------
        // Local Parameter Declarations
        //----------------------------------
        localparam                          A_WIDTH = clogb2(SIZE_IN_BYTES);
    
        //----------------------------------
        // Variable Declarations
        //----------------------------------
        reg [31:0]                          mem[0:SIZE_IN_BYTES/4-1];
        wire                                wren;
        wire                                rden;
        wire [A_WIDTH-1:2]                  addr; 
     
        //----------------------------------
        // Function Declarations
        //----------------------------------
        function integer clogb2;
            input [31:0]                    value; 
            reg [31:0]                      tmp; 
            reg [31:0]                      rt;
        begin
            tmp = value - 1;
            for (rt = 0; tmp > 0; rt = rt + 1) 
                tmp = tmp >> 1;
            clogb2 = rt;
        end
        endfunction
    
        //----------------------------------
        // Start of Main Code
        //----------------------------------
        // Create read and write enable signals using APB control signals
        assign wren = PWRITE && PENABLE && PSEL; // Enable Period
        assign rden = ~PWRITE && ~PENABLE && PSEL; // Setup Period
    
        assign addr = PADDR[A_WIDTH-1:2];
        
        // Write mem
        always @(posedge PCLK)
        begin
            if (wren)
                mem[addr] <= PWDATA;
        end
    
        // Read mem
        always @(posedge PCLK)
        begin
            if (rden)
                PRDATA <= mem[addr];
            else
                PRDATA <= 'h0;
        end
    
    endmodule

    测试代码:

    `timescale 1ns / 1ps
    
    `ifndef CLK_FREQ
    `define CLK_FREQ 50000000
    `endif
    
    module top_tb();
    
        //----------------------------------
        // Local Parameter Declarations
        //----------------------------------
        parameter                           SIZE_IN_BYTES = 1024;
    
        localparam                          CLK_FREQ = `CLK_FREQ;
        localparam                          CLK_PERIOD_HALF = 1000000000/(CLK_FREQ*2);
    
        //----------------------------------
        // Variable Declarations
        //----------------------------------
        reg                                 PRESETn = 1'b0;
        reg                                 PCLK = 1'b0;
        reg                                 PSEL;
        reg [31:0]                          PADDR;
        reg                                 PENABLE;
        reg                                 PWRITE;
        reg [31:0]                          PWDATA;   
        wire [31:0]                         PRDATA;
    
        reg [31:0]                          reposit[0:1023];
    
        //----------------------------------
        // Start of Main Code
        //----------------------------------
        apb_sram #(
            .SIZE_IN_BYTES                  (SIZE_IN_BYTES)
        )
        u_apb_sram (
            .PRESETn                        (PRESETn),
            .PCLK                           (PCLK),
            .PSEL                           (PSEL),
            .PADDR                          (PADDR),
            .PENABLE                        (PENABLE),
            .PWRITE                         (PWRITE),
            .PWDATA                         (PWDATA),
            .PRDATA                         (PRDATA)
        );
        
        // generate PCLK
        always #CLK_PERIOD_HALF 
        begin
            PCLK <= ~PCLK;
        end 
    
        // generate PRESETn
        initial begin
            PRESETn <= 1'b0;
            repeat(5) @(posedge PCLK);
            PRESETn <= 1'b1;
        end
    
        // test memory
        initial begin
            PSEL = 1'b0;
            PADDR = ~32'h0;
            PENABLE = 1'b0;
            PWRITE = 1'b0;
            PWDATA = 32'hffff_ffff;
            wait(PRESETn == 1'b0);
            wait(PRESETn == 1'b1);
            repeat(3) @(posedge PCLK);
            memory_test(0, SIZE_IN_BYTES/4-1);
            repeat(5) @(posedge PCLK);
            $finish(2);
        end
    
        // memory test task
        task memory_test;
            // starting address
            input [31:0]                    start;
            // ending address, inclusive
            input [31:0]                    finish; 
            reg [31:0]                      dataW;
            reg [31:0]                      dataR;
            integer                         a; 
            integer                         b; 
            integer                         err;
        begin
            err = 0;
            // read-after-write test
            for (a = start; a <= finish; a = a + 1) begin
                dataW = $random;
                apb_write(4*a, dataW);
                apb_read (4*a, dataR);
                if (dataR !== dataW) begin
                    err = err + 1;
                    $display($time,,"%m Read after Write error at A:0x%08x D:0x%x, but 0x%x expected", a, dataR, dataW);
                end
            end
            if (err == 0) 
                $display($time,,"%m Read after Write 0x%x-%x test OK", start, finish);
            err = 0;
            // read_all-after-write_all test
            for (a = start; a <= finish; a = a + 1) begin
                b = a - start;
                reposit[b] = $random;
                apb_write(4*a, reposit[b]);
            end
            for (a = start; a <= finish; a = a + 1) begin
                b = a - start;
                apb_read(4*a, dataR);
                if (dataR !== reposit[b]) begin
                    err = err + 1;
                    $display($time,,"%m Read all after Write all error at A:0x%08x D:0x%x, but 0x%x expected", a, dataR, reposit[b]);
                end
            end
            if (err == 0) 
                $display($time,,"%m Read all after Write all 0x%x-%x test OK", start, finish);
        end
        endtask
    
        // APB write task
        task apb_write;
            input [31:0]                    addr;
            input [31:0]                    data;
        begin
            @(posedge PCLK);
            PADDR <= #1 addr;
            PWRITE <= #1 1'b1;
            PSEL <= #1 1'b1;
            PWDATA <= #1 data;
            @(posedge PCLK);
            PENABLE <= #1 1'b1;
            @(posedge PCLK);
            PSEL <= #1 1'b0;
            PENABLE <= #1 1'b0;
        end
        endtask
    
        // APB read task
        task apb_read;
            input [31:0]                     addr;
            output [31:0]                    data;
        begin
            @(posedge PCLK);
            PADDR <= #1 addr;
            PWRITE <= #1 1'b0;
            PSEL <= #1 1'b1;
            @(posedge PCLK);
            PENABLE <= #1 1'b1;
            @(posedge PCLK);
            PSEL <= #1 1'b0;
            PENABLE <= #1 1'b0;
            data = PRDATA; // it should be blocking
        end
        endtask
    
    `ifdef VCS
        initial begin
            $fsdbDumpfile("top_tb.fsdb");
            $fsdbDumpvars;
        end
    
        initial begin
        `ifdef DUMP_VPD
            $vcdpluson();
        `endif
        end
    `endif
    
    endmodule

    该测试用例,主要实现了APB读和写的task,用于产生APB读写时序,对memory的测试分成连个部分,一个是每进行一次写传输后,紧接着进行同地址的读传输,让后对比读写结果一致性;另一个测试是在连续写一段地址后,再全部读出改地址段的数据,完成读操作后进行数据比对。

    下面是仿真打印信息

    APB写传输时序的仿真波形如下:

    APB写传输时序的仿真波形如下:

    2
    天线设计1-基本参数
    « 上一篇 2023-03-03
    VLSI设计基础11-运算模块之加法器
    下一篇 » 2023-02-27

    评论 (0)

    取消