1. Title
AMBA BUS (APB Protocol)
2. Category
"RISC-V", "AMBA BUS", "APB Protocol"
3. Key Concepts
CPU Course Review - 17. AMBA BUS (APB Protocol - FND CONTROL)
1. TitleAMBA BUS (APB Protocol)2. Category"RISC-V", "AMBA BUS", "APB Protocol" 3. Key ConceptsAPB Protocol을 Verilog로 구현한다. 4. Setup CPU Course Review - 15. AMBA BUS (APB Protocol - GPIO)1. TitleAMBA BUS (APB Protocol)2. Category"RISC
salmon1113.tistory.com
APB Protocol을 Verilog로 구현한다.
4. Setup

이전 게시글에서 FND에 APB Interface를 추가한 것에 이어 APB Interface를 이용해 UART를 구현한다.

5. Code Review
module apb_slave_interface_uart (
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,
// Internal Signals
output logic control,
output logic [ 7:0] tx_data,
input logic [ 7:0] rx_data
);
localparam START_ADDR = 4'h0;
localparam TX_ADDR = 4'h4;
localparam RX_ADDR = 4'h8;
logic [31:0] start_reg, rx_reg, tx_reg;
assign control = start_reg[0];
assign tx_data = tx_reg[7:0];
assign rx_reg = {24'b0, rx_data};
always_ff @(posedge PCLK, posedge PRESET) begin
if (PRESET) begin
start_reg <= 0;
tx_reg <= 0;
end else begin
PREADY <= 1'b0;
if (PSEL && PENABLE && PWRITE) begin
PREADY <= 1'b1;
case (PADDR[3:0])
START_ADDR: start_reg <= PWDATA;
TX_ADDR: tx_reg <= PWDATA;
default: ;
endcase
end else if (PSEL && PENABLE && !PWRITE) begin
PREADY <= 1'b1;
case (PADDR[3:0])
START_ADDR: PRDATA <= start_reg;
RX_ADDR: PRDATA <= rx_reg;
default: PRDATA = 'x;
endcase
end
end
end
endmodule
module UART_FIFO_TOP (
input clk,
input reset,
input start,
input [7:0] tx_data,
output [7:0] rx_data,
input rx,
output tx
);
wire [7:0] w_tx_data, w_rx_data;
wire w_empty, w_full;
wire w_start;
wire [7:0] w_fifo_data;
wire w_tx_busy, w_rx_done;
/*
FIFO U_TX_FIFO(
.clk(clk), .reset(reset),
.wData(tx_data),
.wr_en(1),
.full(),
.rData(w_tx_data),
.rd_en(1),
.empty()
);
*/
UART_TOP U_UART (
.clk(clk),
.reset(reset),
.start(start),
.tx_data(tx_data),
.rx(rx),
.tx(tx),
.rx_data(rx_data),
.tx_busy(),
.tx_done(),
.rx_busy(),
.rx_done()
);
/*
FIFO U_RX_FIFO(
.clk(clk), .reset(reset),
.wData(rx_data),
.wr_en(1),
.full(),
.rData(rx_data),
.rd_en(1),
.empty()
);
*/
endmodule
CPU를 통해 0x1000_5000번지를 통해 UART를 ENABLE 시킨 후,
0x1000_5004번지에 값을 쓰게 되면, 해당 값을 UART로 송신한다.
0x1000_5008번지에서는 송신되는 값을 항상 받아 레지스터에 저장 후, LED를 동작시키는 신호로 빠져나간다.
module periph_uart (
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,
input logic start_button,
// Internal Signals
input logic rx,
output logic tx
);
logic [7:0] tx_data, rx_data;
logic control, clk_out;
apb_slave_interface_uart U_APB_SLAVE_INTERFACE (
.*,
.control(control),
.tx_data(tx_data),
.rx_data(rx_data)
);
clock_gate U_CLOCK_GATE(
.clk_in(PCLK),
.enable(control),
.clk_out(clk_out)
);
UART_FIFO_TOP U_UART (
.clk(clk_out),
.reset(PRESET),
.start(start_button),
.tx_data(tx_data),
.rx_data(rx_data),
.rx(rx),
.tx(tx)
);
endmodule

7. Testing and Debugging
* Testing Tools: VIVADO, BASYS-3, Modelsim
- 헤더 파일(하드웨어 레지스터 정의, 구조체, 함수 선언)
#include <stdint.h>
#define __IO volatile
typedef struct{
__IO uint32_t DDR;
__IO uint32_t ODR;
} GPO_TypeDef;
typedef struct{
__IO uint32_t DDR;
__IO uint32_t IDR;
} GPI_TypeDef;
typedef struct{
__IO uint32_t DDR;
__IO uint32_t IDR;
__IO uint32_t ODR;
} GPIO_TypeDef;
typedef struct{
__IO uint32_t START;
__IO uint32_t DATA;
} FND_TypeDef;
typedef struct{
__IO uint32_t START;
__IO uint32_t PRESCALER;
__IO uint32_t TX;
__IO uint32_t RX;
} UART_TypeDef;
#define APB_PERIPH_BASE 0X10000000
#define RAM_BASE (APB_PERIPH_BASE + 0X0000)
#define GPOA_BASE (APB_PERIPH_BASE + 0X1000)
#define GPIB_BASE (APB_PERIPH_BASE + 0X2000)
#define GPIOC_BASE (APB_PERIPH_BASE + 0X3000)
#define FND_BASE (APB_PERIPH_BASE + 0X4000)
#define UART_BASE (APB_PERIPH_BASE + 0X5000)
#define GPOA_DDR *(volatile uint32_t *)(GPOA_BASE + 0x00)
#define GPOA_ODR *(volatile uint32_t *)(GPOA_BASE + 0x04)
#define GPIB_DDR *(volatile uint32_t *)(GPIB_BASE + 0x00)
#define GPIB_IDR *(volatile uint32_t *)(GPIB_BASE + 0x04)
#define GPIOC_DDR *(volatile uint32_t *)(GPIOC_BASE + 0x00)
#define GPIOC_IDR *(volatile uint32_t *)(GPIOC_BASE + 0x04)
#define GPIOC_ODR *(volatile uint32_t *)(GPIOC_BASE + 0x08)
#define FND_START *(volatile uint32_t *)(FND_BASE + 0x00)
#define FND_DATA *(volatile uint32_t *)(FND_BASE + 0x04)
#define UART_START *(volatile uint32_t *)(UART_BASE + 0x00)
#define UART_PRESCALER *(volatile uint32_t *)(UART_BASE + 0x04)
#define UART_TX *(volatile uint32_t *)(UART_BASE + 0x08)
#define UART_RX *(volatile uint32_t *)(UART_BASE + 0x0c)
#define GPOA ((GPO_TypeDef *)GPOA_BASE)
#define GPIB ((GPI_TypeDef *)GPIB_BASE)
#define GPIOC ((GPIO_TypeDef *)GPIOC_BASE)
#define FND ((FND_TypeDef *)FND_BASE)
#define UART ((UART_TypeDef*)UART_BASE)
void UART_INIT(UART_TypeDef* UARTx, uint32_t prescale);
void UART_SEND(UART_TypeDef* UARTx, uint32_t number);
uint32_t UART_RECEIVE(UART_TypeDef* UARTx);
void LIGHT_INIT(GPO_TypeDef* GPOx);
void LIGHT_EN(GPO_TypeDef* GPOx, uint32_t number);
- 소스 파일(함수 구현)
void UART_INIT(UART_TypeDef* UARTx, uint32_t prescale)
{
UARTx->START = 0x01;
UARTx->PRESCALER = prescale;
}
void UART_SEND(UART_TypeDef* UARTx, uint32_t number)
{
UARTx->TX = number;
}
uint32_t UART_RECEIVE(UART_TypeDef* UARTx)
{
return UARTx->RX;
}
/////////////////////////////////////////////////////////////////////////////
void LIGHT_INIT(GPO_TypeDef* GPOx)
{
GPOx->DDR = 0xff;
}
void LIGHT_EN(GPO_TypeDef* GPOx, uint32_t number)
{
GPOx->ODR = number;
}
/////////////////////////////////////////////////////////////////////////////
void delay(int n)
{
uint32_t temp = 0;
for(int i = 0; i < n; i++)
{
for (int j = 0; j < 1000; j++)
{
temp ++;
}
}
}
- 응용 코드 (사용자 코드): 드라이버를 호출해서 하드웨어를 제어하는 부분.
int main()
{
uint32_t rx_data = 0;
uint32_t tx_data = 0x41; // 'A'
uint32_t prescale = 10416; // 9600 BAUD RATE;
UART_INIT(UART, prescale);
LIGHT_INIT(GPOA);
UART_SEND(UART, tx_data);
while(1)
{
rx_data = UART_RECEIVE(UART);
LIGHT_EN(GPOA, rx_data);
}
}
GPOA에 0x41('A') 값을 저장하고 있다가 User가 Start 신호를 보내면 TX로 'A'를 내보내고, RX는 값을 항상 받아 레지스터에 저장 후, LED를 동작시키는 신호로 동작하는 코드 작성.
'AI SOC COURSE > SYSTEM VERILOG (CPU 설계)' 카테고리의 다른 글
CPU Course Review - 20. AMBA BUS (APB Protocol - SENSORS) (0) | 2025.03.01 |
---|---|
CPU Course Review - 19. AMBA BUS (APB Protocol - PWM) (0) | 2025.02.28 |
CPU Course Review - 17. AMBA BUS (APB Protocol - FND CONTROL) (0) | 2025.02.27 |
CPU Course Review - 16. Linker Script, Startup Code (0) | 2025.02.27 |
CPU Course Review - 15. AMBA BUS (APB Protocol - GPIO) (0) | 2025.02.26 |