1. Title
AMBA BUS (APB Protocol)
2. Category
"RISC-V", "AMBA BUS", "APB Protocol"
3. Key Concepts
APB Protocol을 Verilog로 구현한다.
4. Setup
AMBA BUS - 00. What is AMBA BUS?
AMBA(Advanced Microcontroller Bus Architecture) 버스는 ARM에서 개발한 버스 프로토콜 및 아키텍처로, 시스템 온 칩(SoC) 및 임베디드 시스템에서 다양한 컴포넌트(프로세서, 메모리, 주변 장치 등) 간 데이터
salmon1113.tistory.com
APB Protocol은 적은 전력 소모와 간소화된 인터페이스를 갖고 있다. APB는 파이프라인 구조를 사용하지 않으며, 단순하고 동기적인 방식의 프로토콜이며, 모든 전송은 완료하는 데 최소 두 사이클이 필요하다.
APB 주변 장치는 일반적으로 APB 브리지를 통해 메인 메모리 시스템에 연결되기 때문에, AXI에서 APB로의 브리지를 사용하면 여러 APB 주변 장치를 AXI 메모리 시스템에 연결할 수 있다.

APB 브리지는 'Master 혹은 Requester'라고 불리며, APB 주변 장치는 'Slave 혹은 Completer'라고 불린다.
* APB Protocol의 동작 시퀀스 (Write / Read)
State Diagram과 Timing Diagram 분석을 통해 APB Protocol의 동작 시퀀스에 대해 자세히 알아보자


APB는 IDLE, SETUP, ACCESS의 세 가지 상태를 가진 FSM으로 동작한다.
PREADY 신호가 들어올 때까지 ACCESS 상태를 유지하며, 이후 IDLE 상태로 전환된다.
이처럼 세 가지 상태를 가지므로, 앞서 언급한 대로 모든 전송은 완료되는 데 최소 두 사이클이 필요하게 된다.

-> PREADY 신호가 들어오기 전까지 대기하기 때문에 두 사이클 이상의 시간이 필요한 경우


Read도 Write와 마찬가지로 IDLE, SETUP, ACCESS의 세 가지 상태를 가진 FSM을 기반으로 동작한다.
5. BLOCK DIAGRAM


6. Code Review
module APB_Master_Interface (
input logic PCLK, // APB CLK
input logic PRESET, // APB asynchronous RESET
// APB Interface Signals
output logic [31:0] PADDR,
output logic PWRITE,
output logic PSEL
output logic PENABLE,
output logic [31:0] PWDATA,
input logic [31:0] PRDATA
input logic PREADY
// Internal Interface Signals
input logic write,
input logic [31:0] addr,
input logic [31:0] wdata,
output logic [31:0] rdata,
input logic req,
output logic ready
);
Input:
PCLK: APB 버스의 클럭 신호. 슬레이브의 모든 동작은 일반적으로 이 클럭에 동기화된다.
PRESET: 비동기 리셋 신호로, 슬레이브의 내부 상태를 초기화하는 데 사용된다.
PRDATA: 각 슬레이브(RAM, GPO, GPI, GPIO)에서 오는 32비트 읽기 데이터.
PREADY: 각 슬레이브에서 오는 준비 신호로, 트랜잭션 완료를 알림.
Output:
PADDR [31:0]: 32비트 주소 버스로, 슬레이브에 전달.
PWRITE: 쓰기(1) 또는 읽기(0)를 나타내는 제어 신호.
PSEL: 각 슬레이브에 대한 선택 신호로, 특정 슬레이브를 선택.
PENABLE: 접근 단계에서 활성화되는 신호.
PWDATA [31:0]: 32비트 쓰기 데이터 버스로, 슬레이브에 전달.
내부 인터페이스 신호:
CPU(또는 다른 상위 컨트롤러)와 APB 마스터 인터페이스 간의 상호작용을 위해 사용되는 신호. 이 신호들은 APB 마스터가 CPU 같은 외부 모듈로부터 명령을 받아 동작하고, 그 결과를 다시 돌려주는 역할을 한다.
module apb_slave_interface (
input logic PCLK, // APB CLK
input logic PRESET, // APB asynchronous RESET
// APB Interface Signals
input logic [31:0] PADDR,
input logic PWRITE,
input logic PSEL,
input logic PENABLE,
input logic [31:0] PWDATA,
output logic [31:0] PRDATA,
output logic PREADY,
// User Define Signal Start
// User Define Signal End
);
Input:
PCLK: APB 버스의 클럭 신호. 슬레이브의 모든 동작은 일반적으로 이 클럭에 동기화된다.
PRESET: 비동기 리셋 신호로, 슬레이브의 내부 상태를 초기화하는 데 사용된다.
PADDR [31:0]: 32비트 주소 버스로, 마스터가 접근하려는 메모리 매핑된 레지스터나 위치를 나타낸다.
PWRITE: 제어 신호로, 하이(1)이면 쓰기 동작, 로우(0)이면 읽기 동작을 의미한다.
PSEL: 선택 신호로, 하이일 때 이 슬레이브가 마스터에 의해 선택되었음을 나타낸다.
PENABLE: 활성화 신호로, 접근 단계에서 하이일 때 트랜잭션이 진행 중임을 나타낸다.
PWDATA [31:0]: 32비트 쓰기 데이터 버스로, 쓰기 동작 시 마스터에서 슬레이브로 데이터를 전달한다.
Output:
PRDATA [31:0]: 32비트 읽기 데이터 버스로, 읽기 동작 시 슬레이브에서 마스터로 데이터를 전달한다.
PREADY: 준비 신호로, 하이일 때 슬레이브가 현재 트랜잭션을 완료했음을 나타낸다 (예: 읽기 데이터 준비 완료 또는 쓰기 완료).
사용자 정의 신호:
애플리케이션에 따라 내부 레지스터나 외부 하드웨어와 연결하기 위한 추가 입력/출력을 넣을 수 있다.
module APB_Decoder (
input logic enable,
input logic [31:0] sel,
output logic [ 3:0] y
);
always_comb begin
y = 4'b0000;
if (enable) begin
casex (sel)
32'h1000_0xxx: y = 4'b0001; // RAM
32'h1000_1xxx: y = 4'b0010; // GP0
32'h1000_2xxx: y = 4'b0100; // GPI
32'h1000_3xxx: y = 4'b1000; // GPIO
endcase
end
end
endmodule
메모리 매핑(Memory Mapping)을 위한 부분으로, 특정 메모리 주소 범위가 어떤 하드웨어 자원(예: ROM, RAM, GPO 등)에 매핑되어 있는지를 나타낸다. 프로그램이 실행될 때, 프로세서가 특정 주소에 접근하면 해당 주소에 매핑된 하드웨어 자원과 상호작용하게 된다.
module APB_Master_Interface (
input logic PCLK, // APB CLK
input logic PRESET, // APB asynchronous RESET
// APB Interface Signals
output logic [31:0] PADDR,
output logic PWRITE,
output logic PSEL_RAM,
output logic PSEL_GPO,
output logic PSEL_GPI,
output logic PSEL_GPIO,
output logic PENABLE,
output logic [31:0] PWDATA,
input logic [31:0] PRDATA_RAM,
input logic [31:0] PRDATA_GPO,
input logic [31:0] PRDATA_GPI,
input logic [31:0] PRDATA_GPIO,
input logic PREADY_RAM,
input logic PREADY_GPO,
input logic PREADY_GPI,
input logic PREADY_GPIO,
// Internal Interface Signals
input logic write,
input logic [31:0] addr,
input logic [31:0] wdata,
output logic [31:0] rdata,
input logic req,
output logic ready
);
logic [3:0] selx;
logic decoder_en;
logic [31:0] temp_addr, temp_wdata, temp_addr_next, temp_wdata_next;
logic temp_write, temp_write_next;
typedef enum logic [1:0] {
IDLE,
SETUP,
ACCESS
} apb_state_e;
apb_state_e state, state_next;
assign PSEL_RAM = selx[0];
assign PSEL_GPO = selx[1];
assign PSEL_GPI = selx[2];
assign PSEL_GPIO = selx[3];
always_ff @(posedge PCLK, posedge PRESET) begin
if (PRESET) begin
state <= IDLE;
temp_addr <= 0;
temp_wdata <= 0;
temp_write <= 0;
end else begin
state <= state_next;
temp_addr <= temp_addr_next;
temp_wdata <= temp_wdata_next;
temp_write <= temp_write_next;
end
end
always_comb begin
state_next = state;
PADDR = 0;
PWDATA = 0;
PWRITE = 0;
PENABLE = 0;
decoder_en = 0;
temp_addr_next = temp_addr;
temp_wdata_next = temp_wdata;
temp_write_next = temp_write;
case (state)
IDLE: begin
decoder_en = 1'b0; // PSELx
if (req) begin
state_next = SETUP;
temp_addr_next = addr; // latching
temp_wdata_next = wdata;
temp_write_next = write;
end
end
SETUP: begin
decoder_en = 1'b1;
PENABLE = 1'b0;
PADDR = temp_addr;
if (temp_write == 1'b1) begin // for write
PWDATA = temp_wdata;
PWRITE = 1'b1;
end else begin // for read
PWRITE = 0;
end
state_next = ACCESS;
end
ACCESS: begin
decoder_en = 1'b1;
PENABLE = 1'b1;
PADDR = temp_addr;
if (temp_write == 1'b1) begin // for write
PWDATA = temp_wdata;
PWRITE = 1'b1;
end else begin // for read
PWRITE = 0;
end
if ((ready == 1'b1) && (req == 1'b0)) begin
state_next = IDLE;
end
//else if((PREADY == 1'b1) && (req == 1'b1)) state_next = SETUP;
end
endcase
end
APB_Decoder U_APB_Decoder (
.enable(decoder_en),
.sel(addr),
.y(selx)
);
APB_Mux U_APB_Mux (
.sel(addr),
.x0 (PRDATA_RAM),
.x1 (PRDATA_GPO),
.x2 (PRDATA_GPI),
.x3 (PRDATA_GPIO),
.y (rdata),
.r0 (PREADY_RAM),
.r1 (PREADY_GPO),
.r2 (PREADY_GPI),
.r3 (PREADY_GPIO),
.r (ready)
);
endmodule
상위 모듈(CPU)의 요청을 받아 APB 프로토콜을 통해 여러 슬레이브(RAM, GPO, GPI, GPIO)와 통신하는 APB MASTER 구조이다.
상태 전이 및 출력 제어:
IDLE: req가 들어오면 SETUP으로 전환하며 데이터를 래치.
SETUP: decoder_en = 1로 디코더 활성화 → PSEL_* 신호 생성. PADDR, PWRITE, PWDATA(쓰기 시) 설정.
ACCESS: PENABLE = 1로 접근 단계 진입. ready가 1이고 req가 0이면 IDLE로 복귀.
module periph_ram (
input logic PCLK, // APB CLK
input logic PRESET, // APB asynchronous RESET
// APB Interface Signals
input logic [31:0] PADDR,
input logic PWRITE,
input logic PSEL,
input logic PENABLE,
input logic [31:0] PWDATA,
output logic [31:0] PRDATA,
output logic PREADY
);
logic we;
always_comb begin
if (PSEL && PENABLE) begin
we = PWRITE;
PREADY = 1'b1;
end else begin
we = 1'b0;
PREADY = 0;
end
end
RAM U_RAM (
.clk(PCLK),
.we(we),
.addr(PADDR[11:0]),
.wData(PWDATA),
.rData(PRDATA)
);
endmodule
사용자가 설계한 맞춤형 모듈에 APB Slave Interface를 연결하면, 해당 모듈이 CPU와 소통할 수 있게 된다.

7. Testing and Debugging
* Testing Tools: VIVADO, BASYS-3, Modelsim
#include <stdint.h>
int main()
{
*(uint32_t *)(0x10000000) = 0x11111111;
*(uint32_t *)(0x10000004) = 0x22222222;
*(uint32_t *)(0x10000008) = 0x33333333;
*(uint32_t *)(0x1000000c) = 0x44444444;
}
특정 메모리 주소(0x10000000부터 0x1000000c까지 4바이트 간격으로)에 32비트 unsigned integer 값을 저장하는 코드를 기계어로 번역하여, 내가 설계한 RISC-V의 Instruction Memory에 다운로드함으로서 APB Interface가 올바르게 동작하는지 확인해 보자.
li sp, 0x10001000
main:
addi sp,sp,-16
sw ra,12(sp)
sw s0,8(sp)
addi s0,sp,16
li a5,268435456
li a4,286330880
addi a4,a4,273
sw a4,0(a5)
li a5,268435456
addi a5,a5,4
...
...
기계어 번역시 주의 사항으로는, CPU, 프로그램이 스택 포인터를 자동으로 초기화하는 기능을 갖추고 있지 않기 때문에 "li sp, 0x10001000" 명령어로 스택 포인터를 명시적으로 초기화해줘야 한다.


프로그램을 실행하게 되면, 우선 return 주소를 저장하기 위해 RAM에 접근하게 되고, 이로 인해 APB Interface의 Signal이 변하는 모습을 확인할 수 있다.



이후 RAM에 값을 저장할 때마다 APB Interface의 Signal이 변하는 모습을 확인할 수 있다.
'AI SOC COURSE > SYSTEM VERILOG (CPU 설계)' 카테고리의 다른 글
CPU Course Review - 13. AMBA BUS (APB Protocol - GPI) (0) | 2025.02.26 |
---|---|
CPU Course Review - 12. AMBA BUS (APB Protocol - GPO) (0) | 2025.02.25 |
CPU Course Review - 10. Analysis of Assembly Language (Single Cycle RISC-V) (0) | 2025.02.21 |
CPU Course Review - 09. RISC-V CPU 개요 (0) | 2025.02.12 |
CPU Course Review - 08. RAM (0) | 2025.02.12 |