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.
- Introduction
- Files Overview
- APB Protocol Fundamentals
- APB Signal Reference
- Architecture and Block Diagrams
- Master-Slave Connection
- Why Three Modules?
- Module Descriptions
- State Machine Details
- Signal Flow Examples
- Usage Examples
- Simulation Guide
- Customization Guide
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.
- 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
| 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 uses a simple two-phase protocol:
- 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
- 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
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
| 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 |
| 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. |
| 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. |
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 ──────────────────┐
└─────
┌─────────────────────────────────────────────────────────────────────┐
│ 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
┌─────────────────────────────────────────────────────────────┐
│ 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 Outputs Slave Inputs
───────────────── ─────────────────
PSEL ────────────────► PSEL
PENABLE ────────────────► PENABLE
PADDR ────────────────► PADDR
PWRITE ────────────────► PWRITE
PWDATA ────────────────► PWDATA
Slave Outputs Master Inputs
───────────────── ─────────────────
PRDATA ────────────────► PRDATA
PREADY ────────────────► PREADY
PSLVERR ────────────────► PSLVERR
PCLK ────► Master ────► Slave
PRESETn ────► Master ────► Slave
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
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
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"
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
| 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.
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 requestaddr[ADDR_WIDTH-1:0]: Addresswrite: 1=write, 0=readwdata[DATA_WIDTH-1:0]: Write datardata[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)
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
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
endcaseTop-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
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
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)
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 ──────────────────┐ ┌─────┐
└─────┘ └─────
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 ✓
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
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
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)
);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)
);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)
);// 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;# 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# 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# 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_tbThe 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!
...
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
endModify 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
endOption 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
endOption 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 (...);- Update
NUM_SLAVESparameter inapb_top.v - Add more slave instantiations in the generate block
- Update decoder logic to handle more address ranges
- Update response multiplexer to include new slaves
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.
Change the ADDR_WIDTH parameter:
apb_master #(
.ADDR_WIDTH(16) // 16-bit address bus
) u_master (...);-
APB is non-pipelined: Each transfer takes at least 2 clock cycles (SETUP + ACCESS)
-
PREADY can extend transfers: If PREADY=0, the ACCESS phase extends until PREADY=1
-
PSLVERR is optional: Can be tied LOW if error reporting is not needed
-
Address alignment: APB addresses are typically word-aligned (lowest 2 bits ignored for 32-bit data)
-
Only one slave active: The decoder ensures only one slave responds at a time
-
Synchronous protocol: All signals are registered and synchronous to PCLK
-
AMBA compliance: This implementation follows AMBA APB v2.0 specification
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
- 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
This implementation is provided for educational and reference purposes.
Last Updated: 2024 Version: 1.0