zynq_apb总线

发布于:2025-07-18 ⋅ 阅读:(19) ⋅ 点赞:(0)

apb_master.v

module apb_master #(
    parameter ADDR_WIDTH = 32,
    parameter DATA_WIDTH = 32
)(
    input  wire                     clk,
    input  wire                     rst_n,
    
    // APB Master Interface
    output reg  [ADDR_WIDTH-1:0]    paddr,      // APB address bus
    output reg                      pwrite,     // Write signal
    output reg  [DATA_WIDTH-1:0]    pwdata,     // Write data
    input  wire [DATA_WIDTH-1:0]    prdata,     // Read data
    output reg                      psel,       // Peripheral select
    output reg                      penable,    // Enable signal
    input  wire                     pready,     // Ready signal
    input  wire                     pslverr,    // Slave error signal
    
    // User Interface
    input  wire                     start,      // Start transfer
    input  wire                     write,      // Write/Read control
    input  wire [ADDR_WIDTH-1:0]    addr,       // Address
    input  wire [DATA_WIDTH-1:0]    wdata,      // Write data
    output reg  [DATA_WIDTH-1:0]    rdata,      // Read data
    output reg                      done,       // Transfer done
    output reg                      error       // Error indicator
);

    // APB State Machine States
    localparam IDLE     = 2'b00;
    localparam SETUP    = 2'b01;
    localparam ACCESS   = 2'b10;
    
    reg [1:0] state, next_state;
    
    // State Machine
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            state <= IDLE;
        else
            state <= next_state;
    end
    
    // Next State Logic
    always @(*) begin
        case (state)
            IDLE: begin
                if (start)
                    next_state = SETUP;
                else
                    next_state = IDLE;
            end
            
            SETUP: begin
                next_state = ACCESS;
            end
            
            ACCESS: begin
                if (pready)
                    next_state = IDLE;
                else
                    next_state = ACCESS;
            end
            
            default: next_state = IDLE;
        endcase
    end
    
    // Output Logic
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            paddr   <= {ADDR_WIDTH{1'b0}};
            pwrite  <= 1'b0;
            pwdata  <= {DATA_WIDTH{1'b0}};
            psel    <= 1'b0;
            penable <= 1'b0;
            rdata   <= {DATA_WIDTH{1'b0}};
            done    <= 1'b0;
            error   <= 1'b0;
        end
        else begin
            case (state)
                IDLE: begin
                    psel    <= 1'b0;
                    penable <= 1'b0;
                    done    <= 1'b0;
                    error   <= 1'b0;
                    
                    if (start) begin
                        paddr  <= addr;
                        pwrite <= write;
                        pwdata <= wdata;
                    end
                end
                
                SETUP: begin
                    psel    <= 1'b1;
                    penable <= 1'b0;
                end
                
                ACCESS: begin
                    penable <= 1'b1;
                    
                    if (pready) begin
                        psel    <= 1'b0;
                        penable <= 1'b0;
                        done    <= 1'b1;
                        error   <= pslverr;
                        
                        if (!pwrite)
                            rdata <= prdata;
                    end
                end
            endcase
        end
    end

endmodule 

apb_slave.v

module apb_slave #(
    parameter ADDR_WIDTH = 32,
    parameter DATA_WIDTH = 32,
    parameter MEM_SIZE   = 256    // Memory size in bytes
)(
    input  wire                     clk,
    input  wire                     rst_n,
    
    // APB Slave Interface
    input  wire [ADDR_WIDTH-1:0]    paddr,      // APB address bus
    input  wire                     pwrite,     // Write signal
    input  wire [DATA_WIDTH-1:0]    pwdata,     // Write data
    output reg  [DATA_WIDTH-1:0]    prdata,     // Read data
    input  wire                     psel,       // Peripheral select
    input  wire                     penable,    // Enable signal
    output reg                      pready,     // Ready signal
    output reg                      pslverr     // Slave error signal
);

    // Memory array
    reg [DATA_WIDTH-1:0] memory [0:(MEM_SIZE/DATA_WIDTH)-1];
    
    // Address decoding
    wire valid_addr;
    assign valid_addr = (paddr < MEM_SIZE);
    
    // Memory access
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            prdata  <= {DATA_WIDTH{1'b0}};
            pready  <= 1'b0;
            pslverr <= 1'b0;
        end
        else begin
            if (psel && penable) begin
                if (valid_addr) begin
                    if (pwrite) begin
                        memory[paddr[DATA_WIDTH/8-1:0]] <= pwdata;
                    end
                    else begin
                        prdata <= memory[paddr[DATA_WIDTH/8-1:0]];
                    end
                    pready  <= 1'b1;
                    pslverr <= 1'b0;
                end
                else begin
                    pready  <= 1'b1;
                    pslverr <= 1'b1;
                end
            end
            else begin
                pready  <= 1'b0;
                pslverr <= 1'b0;
            end
        end
    end

endmodule 

tb.v

`timescale 1ns/1ps

module tb;
    // Parameters
    parameter ADDR_WIDTH = 32;
    parameter DATA_WIDTH = 32;
    parameter MEM_SIZE   = 256;
    parameter CLK_PERIOD = 10;  // 100MHz clock

    // Clock and reset
    reg clk;
    reg rst_n;

    // APB signals
    wire [ADDR_WIDTH-1:0] paddr;
    wire                  pwrite;
    wire [DATA_WIDTH-1:0] pwdata;
    wire [DATA_WIDTH-1:0] prdata;
    wire                  psel;
    wire                  penable;
    wire                  pready;
    wire                  pslverr;

    // Master control signals
    reg                   start;
    reg                   write;
    reg  [ADDR_WIDTH-1:0] addr;
    reg  [DATA_WIDTH-1:0] wdata;
    wire [DATA_WIDTH-1:0] rdata;
    wire                  done;
    wire                  error;

    // Instantiate APB Master
    apb_master #(
        .ADDR_WIDTH(ADDR_WIDTH),
        .DATA_WIDTH(DATA_WIDTH)
    ) u_apb_master (
        .clk(clk),
        .rst_n(rst_n),
        .paddr(paddr),
        .pwrite(pwrite),
        .pwdata(pwdata),
        .prdata(prdata),
        .psel(psel),
        .penable(penable),
        .pready(pready),
        .pslverr(pslverr),
        .start(start),
        .write(write),
        .addr(addr),
        .wdata(wdata),
        .rdata(rdata),
        .done(done),
        .error(error)
    );

    // Instantiate APB Slave
    apb_slave #(
        .ADDR_WIDTH(ADDR_WIDTH),
        .DATA_WIDTH(DATA_WIDTH),
        .MEM_SIZE(MEM_SIZE)
    ) u_apb_slave (
        .clk(clk),
        .rst_n(rst_n),
        .paddr(paddr),
        .pwrite(pwrite),
        .pwdata(pwdata),
        .prdata(prdata),
        .psel(psel),
        .penable(penable),
        .pready(pready),
        .pslverr(pslverr)
    );

    // Clock generation
    initial begin
        clk = 0;
        forever #(CLK_PERIOD/2) clk = ~clk;
    end

    // Test stimulus
    initial begin
        // Initialize signals
        rst_n = 0;
        start = 0;
        write = 0;
        addr = 0;
        wdata = 0;

        // Reset
        #(CLK_PERIOD*2);
        rst_n = 1;
        #(CLK_PERIOD*2);

        // Test Case 1: Write operation
        $display("Test Case 1: Write operation");
        write = 1;
        addr = 32'h00000000;
        wdata = 32'hA5A5A5A5;
        start = 1;
        #(CLK_PERIOD);
        start = 0;
        wait(done);
        #(CLK_PERIOD*2);

        // Test Case 2: Read operation
        $display("Test Case 2: Read operation");
        write = 0;
        addr = 32'h00000000;
        start = 1;
        #(CLK_PERIOD);
        start = 0;
        wait(done);
        if (rdata === 32'hA5A5A5A5)
            $display("Read data matches: 0x%h", rdata);
        else
            $display("Read data mismatch: Expected 0xA5A5A5A5, Got 0x%h", rdata);
        #(CLK_PERIOD*2);

        // Test Case 3: Write to different address
        $display("Test Case 3: Write to different address");
        write = 1;
        addr = 32'h00000004;
        wdata = 32'h5A5A5A5A;
        start = 1;
        #(CLK_PERIOD);
        start = 0;
        wait(done);
        #(CLK_PERIOD*2);

        // Test Case 4: Read from different address
        $display("Test Case 4: Read from different address");
        write = 0;
        addr = 32'h00000004;
        start = 1;
        #(CLK_PERIOD);
        start = 0;
        wait(done);
        if (rdata === 32'h5A5A5A5A)
            $display("Read data matches: 0x%h", rdata);
        else
            $display("Read data mismatch: Expected 0x5A5A5A5A, Got 0x%h", rdata);
        #(CLK_PERIOD*2);

        // Test Case 5: Invalid address access
        $display("Test Case 5: Invalid address access");
        write = 0;
        addr = 32'h00000100;  // Outside memory range
        start = 1;
        #(CLK_PERIOD);
        start = 0;
        wait(done);
        if (error)
            $display("Error signal asserted as expected for invalid address");
        else
            $display("Error: Error signal not asserted for invalid address");
        #(CLK_PERIOD*2);

        // End simulation
        $display("Simulation completed");
        #(CLK_PERIOD*2);
        $finish;
    end

    // Monitor
    initial begin
        $monitor("Time=%0t rst_n=%b start=%b write=%b addr=%h wdata=%h rdata=%h done=%b error=%b",
                 $time, rst_n, start, write, addr, wdata, rdata, done, error);
    end

endmodule 

网站公告

今日签到

点亮在社区的每一天
去签到