728x90
반응형
[AXI Protocol]
📌 Block Design
이번엔 얘부터 추가해준다.
VIP : verification IP라고, 검증된 IP라는 뜻
📌 Simulation
현장가면 검증할 때, SystemVerilog를 대부분 사용하므로
미리 맛 좀 보고 가자.
가장 큰 차이점은
C++처럼 객체(Class)를 만들 수 있다는 장점이 있다.
Class의 장점은 다른 사람이 만들어 놓은 것을 가져다 쓰기 편하다는 것이다.
(Class의 Include가 가능)
그러다보니 가져다 쓸 만한 것들도 많이 만들어 놨음
가장 대표적인 것이바로
UVM
Verilog vs SystemVerilog | |
Verilog | SystemVerilog |
include | import |
reg | bit |
wire | logic |
import axi_vip_pkg::*; // C의 include와 같은 것
// 이 패키지에 있는 모든 것을 전부(*) 가져다 쓰겠다는 의미
import FND_IP_test_axi_vip_0_0_pkg::*;
import axi_vip_pkg::*; // AXI VIP 패키지를 모두 가져옴
// 이 패키지에 있는 모든 것을 전부(*) 가져다 쓰겠다는 의미
import FND_IP_test_axi_vip_0_0_pkg::*; // FND IP 테스트 AXI VIP 패키지를 모두 가져옴
module tb_FND_IP();
// 입력은 reg로, 출력은 wire로
bit aclk_0 = 0; // SystemVerilog에서는 bit가 reg
bit aresetn_0 = 0;
logic [3:0] com_0; // SystemVerilog에서는 wire가 logic
logic [7:0] seg_7_0;
xil_axi_ulong base_reg = 32'h0x44a00000; // AXI VIP의 베이스 레지스터 주소
xil_axi_prot_t prot = 0; // AXI VIP의 프로토콜
xil_axi_resp_t resp; // AXI VIP의 응답
bit [31:0] test_data1 = 32'h0000000f; // 테스트 데이터 1
bit [31:0] test_data2 = 32'h00000008; // 테스트 데이터 2
bit [31:0] test_data3 = 32'h00000037; // 테스트 데이터 3
//////////////////////// DUT Instance //////////////////////////
FND_IP_test_wrapper DUT(
.aclk_0,
.aresetn_0,
.com_0,
.seg_7_0
); // 디바이스 언더 테스트(DUT) 모듈 인스턴스화
////////////////////////////////////////////////////////////////
FND_IP_test_axi_vip_0_0_mst_t master_agent; // AXI VIP 마스터 에이전트 인스턴스
always begin #5 aclk_0 = ~aclk_0; end // 클럭 주기마다 클럭 반전
initial begin
master_agent = new("master vip agent", DUT.FND_IP_test_i.axi_vip_0.inst.IF); // AXI VIP 마스터 에이전트 생성
master_agent.start_master(); // 마스터 에이전트 시작
#100
aresetn_0 = 1; #50 // 리셋 해제 후 50 단위의 딜레이
master_agent.AXI4LITE_WRITE_BURST(base_reg, prot, test_data1, resp); #400; // AXI VIP에 대한 쓰기 버스트 동작 후 400 단위의 딜레이
$stop; // 시뮬레이션 종료
end
endmodule
그렇게 시뮬레이션을 진행하면,
X값만 출력된 이유
FND IP에서 링카운터 모듈을 보면, temp의 초기화가 안 돼 있다.
[Interrupt]
📌 Block Design
📌 Main Code
#include <stdio.h> // 표준 입력/출력 함수
#include "platform.h" // 플랫폼에 특화된 정의 및 초기화
#include "xil_printf.h" // Xilinx printf 함수
#include "xparameters.h" // 하드웨어 플랫폼의 매개변수 정의
#include "xgpio.h" // 하드웨어용 일반 I/O 함수
#include "xintc.h" // 인터럽트 컨트롤러 함수
#define BTN_ID XPAR_AXI_GPIO_BTN_DEVICE_ID // 버튼용 GPIO 컨트롤러의 ID
#define BTN_CHANNEL 1 // 버튼용 GPIO 채널
#define BTN_MASK 0b1111 // 버튼에 대한 마스크, 하위 4비트만 사용
XGpio btn_device; // 버튼용 XGpio 드라이버의 인스턴스
XIntc intc; // 인터럽트 컨트롤러의 인스턴스
int main()
{
init_platform(); // 플랫폼 초기화 (하드웨어 및 소프트웨어)
printf("Start!!\n\r"); // 프로그램 시작을 나타내는 메시지 출력
int btn_data = 0; // 버튼 데이터를 저장하는 변수
XGpio_Config *cfg_ptr_btn_intc; // 버튼 GPIO의 구성을 가리키는 포인터
cfg_ptr_btn_intc = XGpio_LookupConfig(BTN_ID); // 버튼 GPIO의 구성 조회
XGpio_CfgInitialize(&btn_device, cfg_ptr_btn_intc, cfg_ptr_btn_intc->BaseAddress); // 버튼 GPIO 초기화
XGpio_SetDataDirection(&btn_device, BTN_CHANNEL, BTN_MASK); // 버튼 GPIO의 데이터 방향 설정
while(1)
{
print("Hello World\n\r"); // 콘솔에 "Hello World" 출력
btn_data = XGpio_DiscreteRead(&btn_device, BTN_CHANNEL); // GPIO에서 버튼 데이터 읽기
xil_printf("btn data : %x \n\r", btn_data); // 버튼 데이터를 16진수 형식으로 출력
MB_Sleep(1000); // 1000밀리초 동안 대기
}
cleanup_platform(); // 종료 전에 리소스 정리 및 해제
return 0; // 성공적인 실행을 나타내기 위해 0 반환
}
cfg_ptr이란?
cfg_ptr는 일반적으로 "configuration pointer"의 줄임말로 사용된다.
이것은 프로그램이 특정 하드웨어 또는 소프트웨어 구성을 나타내는 구성(configuration) 정보에 대한 포인터이다..
구성 정보는 특정 기능 또는 하드웨어 장치의 동작을 제어하기 위한 설정과 관련된 데이터를 포함합니다.
sei()(set Interrupt), cli()(Clear Interrupt) 함수를 호출했던 것 처럼
#include <stdio.h> // 표준 입력/출력 함수
#include "platform.h" // 플랫폼에 특화된 정의 및 초기화
#include "xil_printf.h" // Xilinx printf 함수
#include "xparameters.h" // 하드웨어 플랫폼의 매개변수 정의
#include "xgpio.h" // 하드웨어용 일반 I/O 함수
#include "xintc.h" // 인터럽트 컨트롤러 함수
#include "xil_exception.h"
//////////////////////// User Define ////////////////////////
#define BTN_ID XPAR_AXI_GPIO_BTN_DEVICE_ID // 버튼용 GPIO 컨트롤러의 ID
#define BTN_CHANNEL 1 // 버튼용 GPIO 채널
#define BTN_MASK 0b1111 // 버튼에 대한 마스크, 하위 4비트만 사용
#define BTN_VEC_ID XPAR_INTC_0_GPIO_0_VEC_ID // VEC_ID는 이름을 바꿔도 처음 아이디 그대로 나온다.
#define INTC_ID XPAR_INTC_0_DEVICE_ID // Interrupt Controller의 디바이스 ID
////////////////////////////////////////////////////////////////////////////////
XGpio btn_device; // 버튼용 XGpio 드라이버의 인스턴스
XIntc intc; // 인터럽트 컨트롤러의 인스턴스
//////////// Function Prototype Declaration ////////////
void BTN_ISR(void *CallBackRef);
////////////////////////////////////////////////////////
int main()
{
init_platform(); // 플랫폼 초기화 (하드웨어 및 소프트웨어)
printf("Start!!\n\r"); // 프로그램 시작을 나타내는 메시지 출력
int btn_data = 0; // 버튼 데이터를 저장하는 변수
XGpio_Config *cfg_ptr_btn_intc; // 버튼 GPIO의 구성을 가리키는 포인터
cfg_ptr_btn_intc = XGpio_LookupConfig(BTN_ID); // 버튼 GPIO의 구성 조회
XGpio_CfgInitialize(&btn_device, cfg_ptr_btn_intc, cfg_ptr_btn_intc->BaseAddress); // 버튼 GPIO 초기화
XGpio_SetDataDirection(&btn_device, BTN_CHANNEL, BTN_MASK); // 버튼 GPIO의 데이터 방향 설정
//////////////////////// intc 초기화 ////////////////////////
XIntc_Initialize(&intc, INTC_ID);
XIntc_Connect(&intc, BTN_VEC_ID,
(XInterruptHandler)BTN_ISR, (void *)&btn_device);
// 인터럽트가 발생하면 이 아이디에 대해서 VEC_ID로 가고 함수의 주소를 찾아서 그 함수로 간다.
//----------------------- intc Enable -----------------------//
XIntc_Enable(&intc, BTN_VEC_ID); // Interrupt 활성화
XIntc_Start(&intc, XIN_REAL_MODE);
XGpio_InterruptEnable(&btn_device, BTN_CHANNEL); // 게별 인터럽트 활성화
XGpio_InterruptGlobalEnable(&btn_device); // Global 인터럽트 활성화
//-------------------- mblaze 인터럽트 할성화 --------------------//
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XIntc_InterruptHandler, &intc);
Xil_ExceptionEnable();
////////////////////////////////////////////////////////
while(1)
{
print("Hello World\n\r"); // 콘솔에 "Hello World" 출력
btn_data = XGpio_DiscreteRead(&btn_device, BTN_CHANNEL); // GPIO에서 버튼 데이터 읽기
xil_printf("btn data : %x \n\r", btn_data); // 버튼 데이터를 16진수 형식으로 출력
MB_Sleep(1000); // 1000밀리초 동안 대기
}
cleanup_platform(); // 종료 전에 리소스 정리 및 해제
return 0; // 성공적인 실행을 나타내기 위해 0 반환
}
void BTN_ISR(void *CallBackRef)
{
printf("btn Interrupt\n\r");
XGpio *GpioPtr = (XGpio *)CallBackRef;
// 이 CallBackRef가 누가 인터럽트를 발생시켰는지 알려준다.
// 그래서 이 주소를 받아서 클리어해주는 것이 좋다.
// if(XGpio_DiscreteRead(GpioPtr, BTN_CHANNEL)) // 버튼을 누를 때만 동작하게 하고 싶다면?
if(XGpio_DiscreteRead(GpioPtr, BTN_CHANNEL) & 0b0010) // 왼쪽 버튼만 인터럽트로 사용하고 싶다면?
{
printf("btn Left Pushed\n\r");
}
XGpio_InterruptClear(GpioPtr, BTN_CHANNEL);
// 여기서는 Interrupt가 발생해도 Flag가 0으로 초기화되지 않는다.
// 한 번 ISR이 동작하면, 계속 ISR이 동작해버림
// 그래서 한 번 동작하고 꺼주는 함수를 추가해줘야 한다.
return;
}
void BTN_ISR(void *CallBackRef)
{
printf("btn Interrupt\n\r");
XGpio *GpioPtr = (XGpio *)CallBackRef;
// 이 CallBackRef가 누가 인터럽트를 발생시켰는지 알려준다.
// 그래서 이 주소를 받아서 클리어해주는 것이 좋다.
// if(XGpio_DiscreteRead(GpioPtr, BTN_CHANNEL)) // 버튼을 누를 때만 동작하게 하고 싶다면?
if(XGpio_DiscreteRead(GpioPtr, BTN_CHANNEL) & 0b0010) // 왼쪽 버튼만 인터럽트로 사용하고 싶다면?
{
printf("btn Left Pushed\n\r");
}
else printf("btn Left released\n\r");
XGpio_InterruptClear(GpioPtr, BTN_CHANNEL);
// 여기서는 Interrupt가 발생해도 Flag가 0으로 초기화되지 않는다.
// 한 번 ISR이 동작하면, 계속 ISR이 동작해버림
// 그래서 한 번 동작하고 꺼주는 함수를 추가해줘야 한다.
return;
}
[Switch로 Interrupt 동작]
📌 Block Design
📌 Main Code
#include <stdio.h> // 표준 입력/출력 함수
#include "platform.h" // 플랫폼에 특화된 정의 및 초기화
#include "xil_printf.h" // Xilinx printf 함수
#include "xparameters.h" // 하드웨어 플랫폼의 매개변수 정의
#include "xgpio.h" // 하드웨어용 일반 I/O 함수
#include "xintc.h" // 인터럽트 컨트롤러 함수
#include "xil_exception.h"
//////////////////////// User Define ////////////////////////
#define SWITCH_ID XPAR_AXI_GPIO_SWITCH_DEVICE_ID // 스위치용 GPIO 컨트롤러의 ID
#define SWITCH_CHANNEL 1 // 스위치용 GPIO 채널
#define SWITCH_MASK 0b1111 // 스위치에 대한 마스크, 하위 4비트만 사용
#define SWITCH_VEC_ID XPAR_INTC_0_GPIO_0_VEC_ID // VEC_ID는 이름을 바꿔도 처음 아이디 그대로 나온다.
#define INTC_ID XPAR_INTC_0_DEVICE_ID // Interrupt Controller의 디바이스 ID
////////////////////////////////////////////////////////////////////////////////
XGpio switch_device; // 스위치용 XGpio 드라이버의 인스턴스
XIntc intc; // 인터럽트 컨트롤러의 인스턴스
//////////// 함수 프로토타입 선언 ////////////
void SWITCH_ISR(void *CallBackRef);
////////////////////////////////////////////////////////
int main()
{
init_platform(); // 플랫폼 초기화 (하드웨어 및 소프트웨어)
printf("Start!!\n\r"); // 프로그램 시작을 나타내는 메시지 출력
int switch_data = 0; // 스위치 데이터를 저장하는 변수
XGpio_Config *cfg_ptr_switch_intc; // 스위치 GPIO의 구성을 가리키는 포인터
cfg_ptr_switch_intc = XGpio_LookupConfig(SWITCH_ID); // 스위치 GPIO의 구성 조회
XGpio_CfgInitialize(&switch_device, cfg_ptr_switch_intc, cfg_ptr_switch_intc->BaseAddress);
// 스위치 GPIO 초기화
XGpio_SetDataDirection(&switch_device, SWITCH_CHANNEL, 1); // 스위치 GPIO의 데이터 방향 설정
//////////////////////// 인터럽트 컨트롤러 초기화 ////////////////////////
XIntc_Initialize(&intc, INTC_ID);
XIntc_Connect(&intc, SWITCH_VEC_ID, (XInterruptHandler)SWITCH_ISR, (void *)&switch_device);
// 인터럽트가 발생하면 이 아이디에 대해서 VEC_ID로 가고 함수의 주소를 찾아서 그 함수로 간다.
//----------------------- 인터럽트 활성화 -----------------------//
XIntc_Enable(&intc, SWITCH_VEC_ID); // Interrupt 활성화
XIntc_Start(&intc, XIN_REAL_MODE);
XGpio_InterruptEnable(&switch_device, SWITCH_CHANNEL); // 개별 인터럽트 활성화
XGpio_InterruptGlobalEnable(&switch_device); // Global 인터럽트 활성화
//-------------------- mblaze 인터럽트 활성화 --------------------//
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XIntc_InterruptHandler, &intc);
Xil_ExceptionEnable();
////////////////////////////////////////////////////////
while(1)
{
print("Hello World\n\r"); // 콘솔에 "Hello World" 출력
switch_data = XGpio_DiscreteRead(&switch_device, SWITCH_CHANNEL);
// GPIO에서 스위치 데이터 읽기
xil_printf("switch data : %x \n\r", switch_data);
// 스위치 데이터를 16진수 형식으로 출력
MB_Sleep(1000); // 1000밀리초 동안 대기
}
cleanup_platform(); // 종료 전에 리소스 정리 및 해제
return 0; // 성공적인 실행을 나타내기 위해 0 반환
}
void SWITCH_ISR(void *CallBackRef)
{
printf("switch Interrupt\n\r");
XGpio *GpioPtr = (XGpio *)CallBackRef;
// 이 CallBackRef가 누가 인터럽트를 발생시켰는지 알려준다.
// 그래서 이 주소를 받아서 클리어해주는 것이 좋다.
if(XGpio_DiscreteRead(GpioPtr, SWITCH_CHANNEL)) // 스위치을 누를 때만 동작하게 하고 싶다면?
// if(XGpio_DiscreteRead(GpioPtr, SWITCH_CHANNEL) & 0b0010) // 왼쪽 버튼만 인터럽트로 사용하고 싶다면?
{
printf("Switch On\n\r"); // 스위치가 켜졌다고 출력
}
else printf("Switch Off\n\r"); // 스위치가 꺼진 것을 출력
XGpio_InterruptClear(GpioPtr, SWITCH_CHANNEL);
// 여기서는 Interrupt가 발생해도 Flag가 0으로 초기화되지 않는다.
// 한 번 ISR이 동작하면, 계속 ISR이 동작해버림
// 그래서 한 번 동작하고 꺼주는 함수를 추가해줘야 한다.
return;
}
728x90
반응형