1. Title
AMBA BUS (APB Protocol)
2. Category
"RISC-V", "AMBA BUS", "APB Protocol"
3. Key Concepts
APB Protocol을 Verilog로 구현한다.
4. Setup
이전 게시글에서 FND에 APB Interface를 추가한 것에 이어 APB Interface를 이용해 UART를 구현한다.
5. Code Review
module PWM_TOP #(
parameter SYS_CLK = 100_000_000
) (
input logic clk,
input logic reset,
input logic [31:0] start,
input logic [31:0] prescaler,
input logic [31:0] duty,
output logic pwm_clk
);
logic [31:0] DUTY_CYCLE;
logic [31:0] pwm_clk_counter, prescaler_reg, duty_reg;
assign DUTY_CYCLE = (prescaler * duty) / 100;
always_ff @(posedge clk, posedge reset) begin
if (reset) begin
pwm_clk_counter <= 0;
pwm_clk <= 0;
end else begin
if(start == 0) begin
pwm_clk_counter <= 0;
pwm_clk <= 0;
end else begin
if (pwm_clk_counter == prescaler - 1) begin
pwm_clk_counter <= 0;
end else begin
pwm_clk_counter <= pwm_clk_counter + 1;
end
if (pwm_clk_counter < DUTY_CYCLE) begin
pwm_clk <= 1;
end else begin
pwm_clk <= 0;
end
end
end
end
endmodule
처음 작성한 코드는 PRESCLAER, DUTY 값을 CPU로부터 받아 DUTY CYCLE을 내부적으로 계산한 후, 이에 따른 PWM CLK을 제어하려 했었다. 하지만 조합 로직에서의 나눗셈으로 인해 타이밍 제약(timing constraints)을 만족하지 못하는 문제가 발생하였다.
assign DUTY_CYCLE = (prescaler * duty) / 100;
필자가 설계한 RISC-V의 경우 정수 계산 블록인 RV32I로만 구성되어 있고, 곱셈/나눗셈 연산이 불가능하기 때문에, FPGA 자체적으로 하드웨어로 합성하던 중 경로 지연(path delay)이 발생한 것이다. 이외에도 (prescaler * duty) / 100 연산이 32비트 정수 연산으로 구현되며 긴 Combinational Path 생성되었을 수도 있는데... 이를 해결하기 위해 DUTY CYCLE은 소프트웨어 연산한 값을 받기로 하였고, 아래와 같이 수정하였다.
module PWM_TOP (
input logic clk,
input logic reset,
input logic [31:0] prescaler,
input logic [31:0] duty_cycle,
output logic pwm_clk
);
logic [31:0] pwm_clk_counter;
always_ff @(posedge clk, posedge reset) begin
if (reset) begin
pwm_clk_counter <= 0;
pwm_clk <= 0;
end else begin
if (pwm_clk_counter == prescaler - 1) begin
pwm_clk_counter <= 0;
end else begin
pwm_clk_counter <= pwm_clk_counter + 1;
end
if (pwm_clk_counter < duty_cycle) begin
pwm_clk <= 1;
end else begin
pwm_clk <= 0;
end
end
end
endmodule
수정된 코드는 CPU를 통해 PRESCLAER, DUTY_CYCLE 값을 받게 되면 이를 이용하여 PWM 제어 신호인 PWM_CLK을 생성하게 된다.
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 TX;
__IO uint32_t RX;
} UART_TypeDef;
typedef struct{
__IO uint32_t START;
__IO uint32_t PRESCALER;
__IO uint32_t DUTY_CYCLE;
} PWM_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 PWM_BASE (APB_PERIPH_BASE + 0X6000)
#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_TX *(volatile uint32_t *)(UART_BASE + 0x04)
#define UART_RX *(volatile uint32_t *)(UART_BASE + 0x08)
#define PWM_START *(volatile uint32_t *)(PWM_BASE + 0x00)
#define PWM_PRESCALER *(volatile uint32_t *)(PWM_BASE + 0x04)
#define PWM_DUTY_CYCLE *(volatile uint32_t *)(PWM_BASE + 0x08)
#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)
#define PWM ((PWM_TypeDef*)PWM_BASE)
void FND_INIT(FND_TypeDef *FNDx);
void FND_WRITE(FND_TypeDef *FNDx, uint32_t number);
void FND_STOP(FND_TypeDef *FNDx);
void PWM_INIT(PWM_TypeDef* PWMx);
void PWM_STOP(PWM_TypeDef* PWMx);
void PWM_SET_PRESCALE(PWM_TypeDef* PWMx, uint32_t prescale);
void PWM_SET_DUTY_CYCLE(PWM_TypeDef* PWMx, uint32_t duty);
void delay(int n);
- 소스 파일(함수 구현)
void delay(int n)
{
uint32_t temp = 0;
for(int i = 0; i < n; i++)
{
for (int j = 0; j < 1000; j++)
{
temp ++;
}
}
}
//////////////////////////////////////////////////////////////////////
void FND_INIT(FND_TypeDef *FNDx){
FNDx->START = 0x01;
}
void FND_WRITE(FND_TypeDef* FNDx, uint32_t number)
{
FNDx->DATA = number;
}
void FND_STOP(FND_TypeDef *FNDx)
{
FNDx->START = 0x00;
}
//////////////////////////////////////////////////////////////////////
void PWM_INIT(PWM_TypeDef* PWMx)
{
PWMx->START = 0x01;
}
void PWM_STOP(PWM_TypeDef* PWMx)
{
PWMx->START = 0X00;
}
void PWM_SET_PRESCALE(PWM_TypeDef* PWMx, uint32_t prescale)
{
PWMx->PRESCALER = prescale;
}
void PWM_SET_DUTY_CYCLE(PWM_TypeDef* PWMx, uint32_t duty_cycle)
{
PWMx->DUTY_CYCLE = duty_cycle;
}
- 응용 코드 (사용자 코드): 드라이버를 호출해서 하드웨어를 제어하는 부분.
int main() {
uint32_t prescaler = 1000;
uint32_t DUTY_CYCLE = 10;
FND_INIT(FND);
PWM_INIT(PWM);
PWM_SET_PRESCALE(PWM, prescaler);
while(1) {
FND_WRITE(FND, DUTY_CYCLE);
if(DUTY_CYCLE == 1000) {
PWM_STOP(PWM);
FND_WRITE(FND, 0xEEEE);
delay(10000);
FND_STOP(FND);
return 0;
} else {
PWM_SET_DUTY_CYCLE(PWM, (uint32_t)DUTY_CYCLE);
DUTY_CYCLE++;
delay(100);
}
}
}
DUTY_CYCLE이 0.1초 간격으로 증가하면서 DC 모터 동작이 가능한 DUTY_CYCLE이 되면 모터가 동작, DUTY_CYCLE 이 최대치에 도달하면 FND에 "EEEE" 출력후 모터 동작 종료하는 프로그램.
'AI SOC COURSE > SYSTEM VERILOG (CPU 설계)' 카테고리의 다른 글
CPU Course Review - 20. AMBA BUS (APB Protocol - SENSORS) (0) | 2025.03.01 |
---|---|
CPU Course Review - 18. AMBA BUS (APB Protocol - UART) (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 |