Skip to content

Latest commit

 

History

History
995 lines (801 loc) · 39.7 KB

File metadata and controls

995 lines (801 loc) · 39.7 KB

APB (Advanced Peripheral Bus) RTL Implementation - Complete Guide

This repository contains a complete APB (Advanced Peripheral Bus) implementation following the ARM AMBA (Advanced Microcontroller Bus Architecture) specification. This document provides comprehensive, instructional information about the APB protocol, architecture, and implementation.

Table of Contents

  1. Introduction
  2. Files Overview
  3. APB Protocol Fundamentals
  4. APB Signal Reference
  5. Architecture and Block Diagrams
  6. Master-Slave Connection
  7. Why Three Modules?
  8. Module Descriptions
  9. State Machine Details
  10. Signal Flow Examples
  11. Usage Examples
  12. Simulation Guide
  13. Customization Guide

Introduction

APB (Advanced Peripheral Bus) is part of ARM's AMBA (Advanced Microcontroller Bus Architecture) protocol family. It is designed for low-bandwidth, low-power peripheral access. APB is a simple, non-pipelined bus protocol that provides an interface between a system bus (like AHB or AXI) and low-bandwidth peripherals.

Key Characteristics

  • Non-pipelined: Simple two-phase protocol
  • Low power: Minimal signal switching
  • Low bandwidth: Suitable for control registers and status registers
  • Synchronous: All signals are clocked
  • Simple: Easy to implement and verify

Files Overview

File Description
apb_master.v APB Master module - initiates transactions
apb_slave.v APB Slave module - responds to transactions
apb_decoder.v Address decoder - routes transactions to correct slave
apb_top.v Top-level module connecting all components
apb_tb.v Comprehensive testbench
APB_README.md This documentation file

APB Protocol Fundamentals

Protocol Phases

APB uses a simple two-phase protocol:

1. Setup Phase (PSEL=1, PENABLE=0)

  • Master asserts PSEL to select a peripheral
  • Address (PADDR) and control signals (PWRITE) are valid
  • Write data (PWDATA) is valid for write operations
  • PENABLE remains LOW

2. Access Phase (PSEL=1, PENABLE=1)

  • Master asserts PENABLE
  • Slave samples address and control signals
  • For writes: Slave captures PWDATA
  • For reads: Slave drives PRDATA
  • Slave asserts PREADY when transfer can complete
  • Transfer completes when PREADY=1

State Machine

The APB protocol follows a 3-state state machine:

    ┌─────┐
    │IDLE │ ←──────────┐
    └──┬──┘            │
       │ req           │ !req && PREADY
       ▼               │
   ┌───────┐           │
   │SETUP  │           │
   └───┬───┘           │
       │ (always)      │
       ▼               │
   ┌───────┐           │
   │ACCESS │───────────┘
   └───────┘
       │
       │ PREADY
       ▼
    Complete

States:

  • IDLE: No transfer in progress
  • SETUP: Setup phase - address and control valid
  • ACCESS: Access phase - data transfer occurs

APB Signal Reference

Clock and Reset (Shared)

Signal Direction Width Description
PCLK Input 1 bit APB clock - all APB signals are synchronous to this clock
PRESETn Input 1 bit Active LOW reset signal - asynchronous reset

Master to Slave Signals

Signal Direction Width Description
PSEL Master → Slave 1 bit Peripheral Select - Indicates that a transfer is starting. Must be HIGH for a valid transfer. Generated by decoder based on address.
PENABLE Master → Slave 1 bit Enable Signal - HIGH in ACCESS phase, LOW in SETUP phase. Indicates the access phase of the transfer.
PADDR Master → Slave ADDR_WIDTH Address Bus - Specifies which register/address to access. Word-aligned addresses.
PWRITE Master → Slave 1 bit Write/Read Control - 1 = Write operation, 0 = Read operation. Valid during both SETUP and ACCESS phases.
PWDATA Master → Slave DATA_WIDTH Write Data Bus - Valid during write operations. Must be stable during ACCESS phase when PENABLE=1.

Slave to Master Signals

Signal Direction Width Description
PRDATA Slave → Master DATA_WIDTH Read Data Bus - Valid when PREADY=1 during read operations. Must be driven to 0 when not selected.
PREADY Slave → Master 1 bit Ready Signal - Slave asserts HIGH when transfer can complete. Can extend ACCESS phase for slower peripherals. If LOW, master waits.
PSLVERR Slave → Master 1 bit Slave Error - Optional signal indicating transfer error. Can be tied LOW if not used. Does not prevent data transfer.

Signal Timing

Clock Cycle:    0     1     2     3     4
                ──────┴─────┴─────┴─────┴─────
PCLK            ─┐  ┌─┐  ┌─┐  ┌─┐  ┌─┐
                 └──┘ └──┘ └──┘ └──┘ └──┘
                 
State:         IDLE  SETUP ACCESS IDLE
PSEL            ──────┐     ┌─────┐
                     └─────┘     └─────
PENABLE        ──────────────────┐     ┌─────
                                 └─────┘
PADDR          ──────[Address]───────────
PWRITE         ──────[Write/Read]────────
PWDATA         ──────[Write Data]───────
PRDATA         ──────────────────[Read Data]
PREADY         ──────────────────┐
                                 └─────

Architecture and Block Diagrams

Complete System Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                         SYSTEM TOP LEVEL                            │
│                                                                      │
│  ┌──────────────────────────────────────────────────────────────┐ │
│  │                    CPU / Controller                           │ │
│  │  Inputs:  req, addr, write, wdata                            │ │
│  │  Outputs: rdata, ready, error                                │ │
│  └────────────────────────┬─────────────────────────────────────┘ │
│                             │                                        │
│                             ▼                                        │
│  ┌──────────────────────────────────────────────────────────────┐ │
│  │                    APB MASTER                                 │ │
│  │  ┌────────────────────────────────────────────────────────┐  │ │
│  │  │  State Machine: IDLE → SETUP → ACCESS                  │  │ │
│  │  │  - Controls protocol flow                              │  │ │
│  │  │  - Generates PSEL, PENABLE, PADDR, PWRITE, PWDATA      │  │ │
│  │  │  - Receives PRDATA, PREADY, PSLVERR                    │  │ │
│  │  └────────────────────────────────────────────────────────┘  │ │
│  └───────────┬───────────────────────────────────┬──────────────┘ │
│              │                                   │                 │
│              │ PADDR, PSEL                      │ PRDATA, PREADY,│
│              │                                   │ PSLVERR         │
│              ▼                                   │                 │
│  ┌───────────────────────────────────────────┐  │                 │
│  │           APB DECODER                     │  │                 │
│  │  ┌─────────────────────────────────────┐  │  │                 │
│  │  │  Address Decoding Logic            │  │  │                 │
│  │  │  PADDR[15:12] → slave_sel[3:0]      │  │  │                 │
│  │  │  0x0 → Slave 0 (0x0000_0000)        │  │  │                 │
│  │  │  0x1 → Slave 1 (0x0000_1000)        │  │  │                 │
│  │  │  0x2 → Slave 2 (0x0000_2000)        │  │  │                 │
│  │  │  0x3 → Slave 3 (0x0000_3000)        │  │  │                 │
│  │  └─────────────────────────────────────┘  │  │                 │
│  │                                            │  │                 │
│  │  Output: slave_sel[3:0] (one-hot)         │  │                 │
│  └───────────┬───────────────────────────────┘  │                 │
│              │                                   │                 │
│              │ slave_sel[0]                      │                 │
│              ▼                                   │                 │
│  ┌───────────────────────────────────────────┐  │                 │
│  │         APB SLAVE 0                        │  │                 │
│  │  Base: 0x0000_0000                        │  │                 │
│  │  ┌─────────────────────────────────────┐  │  │                 │
│  │  │  Registers: reg_data[0:15]           │  │  │                 │
│  │  │  - Responds to PSEL, PENABLE        │  │  │                 │
│  │  │  - Generates PRDATA, PREADY, PSLVERR│  │  │                 │
│  │  └─────────────────────────────────────┘  │  │                 │
│  │  Outputs: PRDATA, PREADY, PSLVERR ───────┼──┘                 │
│  └───────────────────────────────────────────┘                    │
│              │                                                      │
│              │ slave_sel[1]                                        │
│              ▼                                                      │
│  ┌───────────────────────────────────────────┐                    │
│  │         APB SLAVE 1                        │                    │
│  │  Base: 0x0000_1000                        │                    │
│  │  ┌─────────────────────────────────────┐  │                    │
│  │  │  Registers: reg_data[0:15]           │  │                    │
│  │  └─────────────────────────────────────┘  │                    │
│  │  Outputs: PRDATA, PREADY, PSLVERR ───────┼──┐                 │
│  └───────────────────────────────────────────┘  │                 │
│              │                                   │                 │
│              │ slave_sel[2]                      │                 │
│              ▼                                   │                 │
│  ┌───────────────────────────────────────────┐  │                 │
│  │         APB SLAVE 2                        │  │                 │
│  │  Base: 0x0000_2000                        │  │                 │
│  │  ┌─────────────────────────────────────┐  │  │                 │
│  │  │  Registers: reg_data[0:15]           │  │  │                 │
│  │  └─────────────────────────────────────┘  │  │                 │
│  │  Outputs: PRDATA, PREADY, PSLVERR ───────┼──┼──┐             │
│  └───────────────────────────────────────────┘  │  │             │
│              │                                   │  │             │
│              │ slave_sel[3]                      │  │             │
│              ▼                                   │  │             │
│  ┌───────────────────────────────────────────┐  │  │             │
│  │         APB SLAVE 3                        │  │  │             │
│  │  Base: 0x0000_3000                        │  │  │             │
│  │  ┌─────────────────────────────────────┐  │  │  │             │
│  │  │  Registers: reg_data[0:15]           │  │  │  │             │
│  │  └─────────────────────────────────────┘  │  │  │             │
│  │  Outputs: PRDATA, PREADY, PSLVERR ───────┼──┼──┼──┐         │
│  └───────────────────────────────────────────┘  │  │  │         │
│                                                  │  │  │         │
│  ┌───────────────────────────────────────────────┼──┼──┼──┐     │
│  │  Response Multiplexer                         │  │  │  │     │
│  │  master_PRDATA = slave_sel[0] ? PRDATA[0] :  │  │  │  │     │
│  │                slave_sel[1] ? PRDATA[1] : ... │  │  │  │     │
│  │  master_PREADY = PREADY[0] | PREADY[1] | ... │  │  │  │     │
│  │  master_PSLVERR = PSLVERR[0] | PSLVERR[1]|...│  │  │  │     │
│  └───────────────────────────────────────────────┘  │  │  │     │
│                                                      │  │  │     │
│  ┌───────────────────────────────────────────────────┼──┼──┼──┐ │
│  │  Common Bus Signals (shared by all slaves)       │  │  │  │ │
│  │  - PCLK, PRESETn                                 │  │  │  │ │
│  │  - PENABLE, PADDR, PWRITE, PWDATA (from master) │  │  │  │ │
│  └───────────────────────────────────────────────────┘  │  │  │  │ │
│                                                         │  │  │  │ │
└─────────────────────────────────────────────────────────┘  │  │  │  │
                                                              │  │  │
                                                              └──┴──┴──► Master

Master-Slave Direct Connection

┌─────────────────────────────────────────────────────────────┐
│                    APB MASTER                               │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  Internal Interface (from CPU/Controller)            │  │
│  │  - req, addr, write, wdata → rdata, ready, error    │  │
│  └──────────────────────────────────────────────────────┘  │
│                          │                                   │
│                          ▼                                   │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  APB Protocol State Machine                          │  │
│  │  (IDLE → SETUP → ACCESS)                             │  │
│  └──────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘
                          │
                          │ APB Bus Signals
                          │
        ┌─────────────────┼─────────────────┐
        │                 │                 │
        ▼                 ▼                 ▼
    ┌──────┐         ┌──────┐         ┌──────┐
    │ PCLK │         │PRESET│         │ PSEL │──┐
    └──────┘         └──────┘         └──────┘  │
                                                  │
        ┌─────────────────────────────────────────┘
        │
        ▼
┌─────────────────────────────────────────────────────────────┐
│                    APB SLAVE                               │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  APB Interface                                        │  │
│  │  - Receives: PSEL, PENABLE, PADDR, PWRITE, PWDATA   │  │
│  │  - Sends: PRDATA, PREADY, PSLVERR                    │  │
│  └──────────────────────────────────────────────────────┘  │
│                          │                                   │
│                          ▼                                   │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  Peripheral Logic (Registers, Memory, etc.)          │  │
│  │  - reg_data[0:15]                                    │  │
│  └──────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

Master-Slave Connection

Signal Connections

Master → Slave (Output from Master, Input to Slave)

Master Outputs                    Slave Inputs
─────────────────                ─────────────────
PSEL      ────────────────►      PSEL
PENABLE   ────────────────►      PENABLE
PADDR     ────────────────►      PADDR
PWRITE    ────────────────►      PWRITE
PWDATA    ────────────────►      PWDATA

Slave → Master (Output from Slave, Input to Master)

Slave Outputs                    Master Inputs
─────────────────               ─────────────────
PRDATA   ────────────────►      PRDATA
PREADY   ────────────────►      PREADY
PSLVERR  ────────────────►      PSLVERR

Shared Signals

PCLK     ────► Master ────► Slave
PRESETn  ────► Master ────► Slave

Connection Summary

In a multi-slave system:

  • Master → Decoder: PADDR, PSEL
  • Decoder → Slaves: slave_sel[3:0] (one-hot)
  • Master → All Slaves (shared bus): PCLK, PRESETn, PENABLE, PADDR, PWRITE, PWDATA
  • Slaves → Master (multiplexed): PRDATA[0:3] → master_PRDATA, PREADY[0:3] → master_PREADY, PSLVERR[0:3] → master_PSLVERR

Why Three Modules?

1. APB Master (apb_master.v)

Purpose: Initiates and controls all APB transactions.

Why needed:

  • The master is the controller that starts transactions
  • Implements the APB protocol state machine
  • Generates all control signals (PSEL, PENABLE, etc.)
  • Cannot be skipped - this is the core controller

Responsibilities:

  • State machine control (IDLE → SETUP → ACCESS)
  • Signal generation
  • Protocol compliance

2. APB Slave (apb_slave.v)

Purpose: Responds to transactions and stores/manages data.

Why needed:

  • The slave is the peripheral that actually has data
  • Stores registers, memory, or controls hardware
  • Responds to master's requests
  • Cannot be skipped - master needs something to talk to

Responsibilities:

  • Address decoding (within slave's address range)
  • Data storage (registers)
  • Response generation (PRDATA, PREADY, PSLVERR)

Real-world example:

  • Master: "Read from address 0x1000"
  • Slave: "Here's the data: 0xDEADBEEF"

3. APB Decoder (apb_decoder.v)

Purpose: Routes transactions to the correct slave.

Why needed:

  • When you have multiple slaves, the decoder determines which one should respond
  • Prevents multiple slaves from responding simultaneously
  • Can be skipped if you only have 1 slave

Responsibilities:

  • Address decoding (which slave owns this address?)
  • One-hot slave selection
  • Prevents bus conflicts

Example:

Master sends: Address = 0x0000_1000
Decoder: "This address belongs to Slave 1!"
Decoder activates: slave_sel[1] = 1

Comparison: 3-File vs Single-File

Aspect 3-File Modular Single-File
Organization Separated by role Everything together
Reusability High - reuse slaves/master Low - tied together
Scalability Easy to add slaves Must modify entire file
Industry Standard Yes (matches AMBA) Less common
Learning Clear separation of concerns Simpler to see all at once
Maintenance Easier - fix one module Harder - find code in large file

Note: Both approaches need an FSM. The difference is organization, not functionality.


Module Descriptions

APB Master (apb_master.v)

Implements the APB master interface with a 3-state state machine.

State Machine:

  • IDLE: No transfer in progress
  • SETUP: Setup phase (PSEL=1, PENABLE=0)
  • ACCESS: Access phase (PSEL=1, PENABLE=1)

Ports:

  • Internal Interface (from processor/controller):

    • req: Transfer request
    • addr[ADDR_WIDTH-1:0]: Address
    • write: 1=write, 0=read
    • wdata[DATA_WIDTH-1:0]: Write data
    • rdata[DATA_WIDTH-1:0]: Read data (output)
    • ready: Transfer complete (output)
    • error: Transfer error (output)
  • APB Interface:

    • PSEL: Peripheral select (output)
    • PENABLE: Enable signal (output)
    • PADDR[ADDR_WIDTH-1:0]: Address bus (output)
    • PWRITE: Write/Read control (output)
    • PWDATA[DATA_WIDTH-1:0]: Write data bus (output)
    • PRDATA[DATA_WIDTH-1:0]: Read data bus (input)
    • PREADY: Ready signal (input)
    • PSLVERR: Slave error (input)

APB Slave (apb_slave.v)

Implements the APB slave interface with address decoding and register storage.

Features:

  • Address decoding based on BASE_ADDR and ADDR_MASK
  • 16 internal registers (configurable)
  • Write and read operations
  • PREADY generation (can be modified for wait states)
  • PSLVERR generation for error conditions

Ports:

  • APB Interface:

    • PSEL: Peripheral select (input)
    • PENABLE: Enable signal (input)
    • PADDR[ADDR_WIDTH-1:0]: Address bus (input)
    • PWRITE: Write/Read control (input)
    • PWDATA[DATA_WIDTH-1:0]: Write data bus (input)
    • PRDATA[DATA_WIDTH-1:0]: Read data bus (output)
    • PREADY: Ready signal (output)
    • PSLVERR: Slave error (output)
  • Internal Interface:

    • reg_data[0:15][DATA_WIDTH-1:0]: Register array (output)
    • reg_write_en: Register write enable (output)
    • reg_addr[3:0]: Register address (output)

Address Mapping (default):

  • Slave 0: 0x0000_0000 - 0x0000_0FFF
  • Slave 1: 0x0000_1000 - 0x0000_1FFF
  • Slave 2: 0x0000_2000 - 0x0000_2FFF
  • Slave 3: 0x0000_3000 - 0x0000_3FFF

APB Decoder (apb_decoder.v)

Decodes the address and generates one-hot slave select signals.

Functionality:

  • Takes master's PADDR and PSEL
  • Decodes address to determine which slave should respond
  • Generates one-hot slave_sel signal

Ports:

  • PADDR[ADDR_WIDTH-1:0]: Address from master (input)
  • PSEL: Master select signal (input)
  • slave_sel[NUM_SLAVES-1:0]: One-hot slave select (output)

Decoding Logic:

case (PADDR[15:12])
    4'h0: slave_sel[0] = 1'b1;  // Slave 0
    4'h1: slave_sel[1] = 1'b1;  // Slave 1
    4'h2: slave_sel[2] = 1'b1;  // Slave 2
    4'h3: slave_sel[3] = 1'b1;  // Slave 3
    default: slave_sel = 0;      // No slave
endcase

APB Top (apb_top.v)

Top-level module that instantiates and connects all components.

Components:

  • 1 APB Master
  • 1 APB Decoder
  • 4 APB Slaves (configurable)

Functionality:

  • Connects master to decoder
  • Connects decoder to slaves
  • Multiplexes slave responses back to master
  • Provides unified interface to CPU/controller

State Machine Details

Master State Machine

The master implements a 3-state FSM that controls the APB protocol:

State: IDLE
- PSEL = 0
- PENABLE = 0
- Waiting for request

State: SETUP
- PSEL = 1
- PENABLE = 0
- PADDR, PWRITE, PWDATA valid
- One cycle duration

State: ACCESS
- PSEL = 1
- PENABLE = 1
- PADDR, PWRITE, PWDATA still valid
- Waiting for PREADY = 1
- Can extend if PREADY = 0

State Transitions

IDLE → SETUP:   When req = 1
SETUP → ACCESS: Always (automatic, one cycle)
ACCESS → IDLE:  When PREADY = 1 and req = 0
ACCESS → SETUP: When PREADY = 1 and req = 1 (back-to-back transfer)

Timing Diagram

Clock:     0     1     2     3     4     5
           ──────┴─────┴─────┴─────┴─────┴─────
PCLK       ─┐  ┌─┐  ┌─┐  ┌─┐  ┌─┐  ┌─┐
            └──┘ └──┘ └──┘ └──┘ └──┘ └──┘
            
State:     IDLE SETUP ACCESS IDLE SETUP ACCESS
req        ──────┐     ┌─────────────────┐
                 └─────┘                 └─────
                 
PSEL       ──────┐     ┌─────┐     ┌─────┐
                 └─────┘     └─────┘     └─────
                 
PENABLE    ──────────────────┐     ┌─────┐
                             └─────┘     └─────
                             
PADDR      ──────[Addr1]───────────[Addr2]───────
PWRITE     ──────[Write]───────────[Read]────────
PWDATA     ──────[Data1]───────────[X]───────────
PRDATA     ──────────────────[Data1]─────────────
PREADY     ──────────────────┐     ┌─────┐
                             └─────┘     └─────

Signal Flow Examples

Example 1: Write Transaction

Scenario: Write 0xDEADBEEF to address 0x0000_1000 (Slave 1, Register 0)

Cycle 0 (IDLE):
  Master:  PSEL=0, PENABLE=0
  Decoder: slave_sel = 0000
  Slaves:  All inactive

Cycle 1 (SETUP):
  Master:  PSEL=1, PENABLE=0, PADDR=0x1000, PWRITE=1, PWDATA=0xDEADBEEF
  Decoder: PADDR[15:12]=0x1 → slave_sel[1]=1, others=0
  Slave 1: Sees PSEL=1 (from decoder), prepares for write
  Slave 1: PREADY=0

Cycle 2 (ACCESS):
  Master:  PSEL=1, PENABLE=1, PADDR=0x1000, PWRITE=1, PWDATA=0xDEADBEEF
  Decoder: slave_sel[1]=1 (still active)
  Slave 1: Writes PWDATA (0xDEADBEEF) to reg_data[0]
  Slave 1: Asserts PREADY=1
  Master:  Sees PREADY=1, transaction complete, ready=1

Cycle 3 (IDLE):
  Master:  PSEL=0, PENABLE=0
  Decoder: slave_sel = 0000
  Slave 1: reg_data[0] = 0xDEADBEEF ✓

Example 2: Read Transaction

Scenario: Read from address 0x0000_2000 (Slave 2, Register 0) containing 0xCAFEBABE

Cycle 0 (IDLE):
  Master:  PSEL=0, PENABLE=0
  Decoder: slave_sel = 0000

Cycle 1 (SETUP):
  Master:  PSEL=1, PENABLE=0, PADDR=0x2000, PWRITE=0
  Decoder: PADDR[15:12]=0x2 → slave_sel[2]=1
  Slave 2: Sees PSEL=1, prepares for read
  Slave 2: PRDATA = reg_data[0] = 0xCAFEBABE (setup phase read)

Cycle 2 (ACCESS):
  Master:  PSEL=1, PENABLE=1, PADDR=0x2000, PWRITE=0
  Decoder: slave_sel[2]=1
  Slave 2: PRDATA = 0xCAFEBABE (valid)
  Slave 2: Asserts PREADY=1
  Master:  Captures PRDATA=0xCAFEBABE, rdata=0xCAFEBABE, ready=1

Cycle 3 (IDLE):
  Master:  PSEL=0, PENABLE=0
  Decoder: slave_sel = 0000

Example 3: Back-to-Back Transfers

Scenario: Write to Slave 0, then immediately read from Slave 1

Cycle 0: IDLE
Cycle 1: SETUP (Write to Slave 0)
Cycle 2: ACCESS (Write to Slave 0) - PREADY=1
Cycle 3: SETUP (Read from Slave 1) - req still high
Cycle 4: ACCESS (Read from Slave 1) - PREADY=1
Cycle 5: IDLE

Usage Examples

Complete System Instantiation

apb_top #(
    .ADDR_WIDTH(32),
    .DATA_WIDTH(32),
    .NUM_SLAVES(4)
) u_apb_system (
    .PCLK(clk),
    .PRESETn(rst_n),
    .req(req),
    .addr(addr),
    .write(write),
    .wdata(wdata),
    .rdata(rdata),
    .ready(ready),
    .error(error),
    .slave0_reg(slave0_reg),
    .slave1_reg(slave1_reg),
    .slave2_reg(slave2_reg),
    .slave3_reg(slave3_reg)
);

Using APB Master Directly

apb_master #(
    .ADDR_WIDTH(32),
    .DATA_WIDTH(32)
) u_master (
    .PCLK(clk),
    .PRESETn(rst_n),
    .req(req),
    .addr(addr),
    .write(write),
    .wdata(wdata),
    .rdata(rdata),
    .ready(ready),
    .error(error),
    .PSEL(PSEL),
    .PENABLE(PENABLE),
    .PADDR(PADDR),
    .PWRITE(PWRITE),
    .PWDATA(PWDATA),
    .PRDATA(PRDATA),
    .PREADY(PREADY),
    .PSLVERR(PSLVERR)
);

Using APB Slave Directly

apb_slave #(
    .ADDR_WIDTH(32),
    .DATA_WIDTH(32),
    .BASE_ADDR(32'h0000_0000),
    .ADDR_MASK(32'hFFFF_F000)
) u_slave (
    .PCLK(clk),
    .PRESETn(rst_n),
    .PSEL(PSEL),
    .PENABLE(PENABLE),
    .PADDR(PADDR),
    .PWRITE(PWRITE),
    .PWDATA(PWDATA),
    .PRDATA(PRDATA),
    .PREADY(PREADY),
    .PSLVERR(PSLVERR),
    .reg_data(reg_data),
    .reg_write_en(reg_write_en),
    .reg_addr(reg_addr)
);

Simple Usage Example

// Write operation
req = 1;
addr = 32'h0000_1000;
write = 1;
wdata = 32'hDEADBEEF;
wait(ready);
req = 0;

// Read operation
req = 1;
addr = 32'h0000_1000;
write = 0;
wait(ready);
read_data = rdata;
req = 0;

Simulation Guide

Using ModelSim/QuestaSim

# Compile all files
vlog apb_master.v apb_slave.v apb_decoder.v apb_top.v apb_tb.v

# Run simulation
vsim apb_tb

# Run for specified time
run -all

# Or run for specific time
run 1000ns

Using Icarus Verilog

# Compile and run
iverilog -o apb_tb apb_master.v apb_slave.v apb_decoder.v apb_top.v apb_tb.v
vvp apb_tb

# View waveforms (if using GTKWave)
vvp apb_tb
gtkwave dump.vcd

Using Verilator

# Compile to C++
verilator --cc --exe --build apb_master.v apb_slave.v apb_decoder.v apb_top.v apb_tb.v

# Run
./obj_dir/Vapb_tb

Expected Testbench Output

The testbench performs:

  • Write operations to all 4 slaves
  • Read operations from all 4 slaves
  • Multiple register writes/reads
  • Data verification
  • Error checking

You should see output like:

=== APB Testbench Started ===

Test 1: Writing to Slave 0
  Write: Address=0x00000000, Data=0xdeadbeef
  Write: Address=0x00000004, Data=0xcafebabe

Test 2: Reading from Slave 0
  Read: Address=0x00000000, Data=0xdeadbeef
  PASS: Data matches!
  Read: Address=0x00000004, Data=0xcafebabe
  PASS: Data matches!
...

Customization Guide

Adding Wait States

To add wait states (slave needs multiple cycles to respond), modify PREADY generation in apb_slave.v:

reg [2:0] wait_count;

always @(posedge PCLK or negedge PRESETn) begin
    if (!PRESETn) begin
        wait_count <= 0;
        PREADY <= 1'b0;
    end else begin
        if (slave_sel && PENABLE) begin
            if (wait_count < 3) begin  // Wait 3 cycles
                wait_count <= wait_count + 1;
                PREADY <= 1'b0;
            end else begin
                PREADY <= 1'b1;
                wait_count <= 0;
            end
        end else begin
            PREADY <= 1'b0;
            wait_count <= 0;
        end
    end
end

Custom Error Conditions

Modify PSLVERR logic in apb_slave.v:

always @(posedge PCLK or negedge PRESETn) begin
    if (!PRESETn) begin
        PSLVERR <= 1'b0;
    end else begin
        if (slave_sel && PENABLE) begin
            // Custom error conditions
            if (internal_addr > 15) begin
                PSLVERR <= 1'b1;  // Invalid register address
            end else if (PWRITE && (PWDATA == 32'hFFFFFFFF)) begin
                PSLVERR <= 1'b1;  // Reserved value
            end else begin
                PSLVERR <= 1'b0;
            end
        end else begin
            PSLVERR <= 1'b0;
        end
    end
end

Different Address Mapping

Option 1: Modify decoder logic in apb_decoder.v:

always @(*) begin
    slave_sel = 0;
    if (PSEL) begin
        case (PADDR[31:28])  // Use different address bits
            4'h0: slave_sel[0] = 1'b1;
            4'h1: slave_sel[1] = 1'b1;
            // ... etc
        endcase
    end
end

Option 2: Change BASE_ADDR and ADDR_MASK in slave instantiations:

apb_slave #(
    .BASE_ADDR(32'h1000_0000),  // Different base address
    .ADDR_MASK(32'hF000_0000)   // Different mask
) u_slave (...);

Adding More Slaves

  1. Update NUM_SLAVES parameter in apb_top.v
  2. Add more slave instantiations in the generate block
  3. Update decoder logic to handle more address ranges
  4. Update response multiplexer to include new slaves

Changing Data Width

Simply change the DATA_WIDTH parameter:

apb_master #(
    .DATA_WIDTH(64)  // 64-bit data bus
) u_master (...);

All signals will automatically adjust: PWDATA[63:0], PRDATA[63:0], etc.

Changing Address Width

Change the ADDR_WIDTH parameter:

apb_master #(
    .ADDR_WIDTH(16)  // 16-bit address bus
) u_master (...);

Important Notes

  1. APB is non-pipelined: Each transfer takes at least 2 clock cycles (SETUP + ACCESS)

  2. PREADY can extend transfers: If PREADY=0, the ACCESS phase extends until PREADY=1

  3. PSLVERR is optional: Can be tied LOW if error reporting is not needed

  4. Address alignment: APB addresses are typically word-aligned (lowest 2 bits ignored for 32-bit data)

  5. Only one slave active: The decoder ensures only one slave responds at a time

  6. Synchronous protocol: All signals are registered and synchronous to PCLK

  7. AMBA compliance: This implementation follows AMBA APB v2.0 specification


Troubleshooting

Common Issues

Issue: Slave not responding

  • Check PSEL is reaching the slave
  • Verify address decoding (BASE_ADDR and ADDR_MASK)
  • Check PREADY is being asserted

Issue: Wrong data read

  • Verify register address calculation (internal_addr)
  • Check register array indexing
  • Ensure PRDATA is valid when PREADY=1

Issue: Multiple slaves responding

  • Check decoder logic
  • Verify one-hot slave_sel signal
  • Ensure only one slave_sel bit is HIGH

Issue: Transfer never completes

  • Check PREADY generation
  • Verify slave_sel is correct
  • Check PENABLE timing

References

  • ARM AMBA APB Protocol Specification - Official ARM documentation
  • ARM AMBA 3 APB Protocol v1.0 - AMBA 3 specification
  • ARM AMBA 4 APB Protocol v2.0 - AMBA 4 specification

License

This implementation is provided for educational and reference purposes.


Last Updated: 2024 Version: 1.0