본문 바로가기
# Semiconductor/- Semicon Academy

[Harman 세미콘 아카데미] 100일차 - SoC Design(StopWatch)

by Graffitio 2023. 11. 27.
[Harman 세미콘 아카데미] 100일차 - SoC Design(StopWatch)
728x90
반응형
[StopWatch]

 

📌 Block Design

이전에 RTL로 코딩했던 StopWatch 모듈을 IP로 만들고 HLS로 코딩하여 동작시켜보자.

 

// [IP top] //

// Users to add ports here
output [3:0] com, 
output [7:0] seg_7,
// User ports ends

) myip_stopwatch_v1_0_S00_AXI_inst (
	    .com(com), .seg_7(seg_7),
		.S_AXI_ACLK(s00_axi_aclk),
		.S_AXI_ARESETN(s00_axi_aresetn),

 

 

// [IP main] // 

// Users to add ports here
output [3:0] com, 
output [7:0] seg_7,
// User ports ends

// Add user logic here
    Stop_Watch_top stopwatch(
    .clk(S_AXI_ACLK),
    .reset_p(~S_AXI_ARESETN),
    .btn(slv_reg0), // 버튼 2개 쓸 것이므로
    .com(com), 
    .seg_7(seg_7)
    //    output [7:0] LED_bar
);
// User logic ends

 

Tip)

 

우리는 AXI의 slv_reg에 쓰는 작업을 할 수 없다.

이미 Peripheral과 AXI Interconnect 사이에 회로가 만들어져 있기 때문에

slv_reg에 쓰게 되면, Multiple Driven(Racing)이 발생한다.

따라서 slv_reg를 다른 변수로 대체하는 건 가능하지만 쓰는 작업은 불가능하다.

 

그래서 DHT11이나 초음파센서 모듈을 쓸 때,

wire distance_cm 같은 변수를 선언해서 3'h0   : reg_data_out <= distance_cm; 이렇게 썼던 것.

slv_reg에 값을 쓸 수 없으니까, distance_cm를 읽어오는 방식으로 코딩한 것이다.

 

본론으로 돌아와서, stopwatch IP를 만들 때

왜 wire [1:0] btn 으로 해주면 안 되냐?

따로 Address도 선언해줘야 한다.

근데 그건 AXI가 하는 거야.

따라서 그냥 slv_reg의 값을 읽어서 사용하는 방식으로 코딩하면 된다.

 


 

📌 Main Code

 

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "xgpio.h"

#define BTN_ID		XPAR_AXI_GPIO_BTN_DEVICE_ID
#define BTN_CHANNEL	1
#define BTN_MASK	0b1111 // 입/출력 결정하는 MASK, 여기서는 1111을 줘서 전부 입력으로 설정


int main()
{
    init_platform();
    print("Start!\n\r");

    XGpio_Config *cfg_ptr_btn;
    XGpio btn_device;

    /////// BTN ///////
    cfg_ptr_btn = XGpio_LookupConfig(BTN_ID);
    XGpio_CfgInitialize(&btn_device, cfg_ptr_btn, cfg_ptr_btn->BaseAddress);
    XGpio_SetDataDirection(&btn_device, BTN_CHANNEL, BTN_MASK); // 1을 줘서 입력 설정

    u8 btn_data = 0;
    volatile unsigned int *StopWatch = (volatile unsigned int *) 0x44A00000;


    while(1)
    {
    	btn_data = XGpio_DiscreteRead(&btn_device, BTN_CHANNEL);
//    	print("Hello world!\n\r");
    	StopWatch[0] = btn_data;
    	MB_Sleep(10);
    }

    cleanup_platform();
    return 0;
}

 

 


 

📌 Improve

 

근데, 버튼을 굳이 IP 내에서 TFF로 만들어줄 필요없이,

HLS 코딩에서 셋팅해줘도 되지 않을까?

그리고 굳이 FND로만 출력할 필요는 없지 않을까?

레지스터로 직접 접근해서 동작하도록 수정해보자.

 

버튼 입력 기반 모듈을 레지스터 기반 모듈로 수정

module Stop_Watch_top(
    input clk,
    input [3:0] sw_control_reg, 
    output [3:0] com, 
    output [7:0] seg_7,
    output [7:0] LED_bar,
    output [15:0] value
    );
    
    wire start_stop, lap, lap_btn, clear;
    assign start_stop = sw_control_reg[0];
    assign lap = sw_control_reg[1];
    assign clear = sw_control_reg[2];
    
    // Lap 기능 구현
    edge_detector_n edg_lap(.clk(clk), .cp_in(sw_control_reg[1]), .rst(clear), .n_edge(lap_btn)); // Edga Detector

    // 분주기 인스턴스
    wire clk_usec, clk_msec, clk_sec;
    clock_usec_en usec_clk(.clk(clk), .reset_p(clear), .enable(start_stop), .clk_usec(clk_usec));
    clock_div_1000 msec_clk(.clk(clk), .clk_source(clk_usec), .reset_p(clear), .clk_div_1000(clk_msec));
    clock_div_1000 sec_clk(.clk(clk), .clk_source(clk_msec), .reset_p(clear), .clk_div_1000(clk_sec));
    clock_min min_clk(.clk(clk), .clk_sec(clk_sec), .reset_p(clear), .clk_min(clk_min)); 
    
    // 카운터 인스턴스
    wire [3:0] msec1, msec10, msec100;
    counter_dec_100_stopwatch cnt_100_msec(.clk(clk), .reset_p(clear), .clk_time(clk_msec), .dec1(msec1), .dec10(msec10), .dec100(msec100));
    wire [3:0] sec1, sec10; // sec1 : 1의자리, sec10 : 10의 자리
    counter_dec_60 cnt_60_sec(.clk(clk), .reset_p(clear), .clk_time(clk_sec), .dec1(sec1), .dec10(sec10)); // sec count
    wire [3:0] min1, min10; // min1 : 1의자리, min10 : 10의 자리
    counter_dec_60 cnt_60_min(.clk(clk), .reset_p(clear), .clk_time(clk_min), .dec1(min1), .dec10(min10)); // min count
    
    // Lap Value / value Select
    reg [15:0] lap_value;
    always @(posedge clk, posedge clear) begin
        if(clear) lap_value = 0;
        else if(lap_btn) // lap_btn 나올 때 저장
            lap_value = {min1, sec10, sec1, msec100};
    end
    
    // lap 버튼에 따른 FND 출력
    assign value = lap ? lap_value : {min1, sec10, sec1, msec100};
    
    FND_4digit_cntr fnd_cntr(.clk(clk), .rst(clear), .value(value), .com(com), .seg_7(seg_7));  /// 컨트롤러가 분주기 가지고 있으니 그냥 clk준다.
    
endmodule

 

추가로 전부 다 리셋해버리는 기능 대신, clear만 해주는 것으로 바꿔주자.

 

// Add user logic here
    Stop_Watch_top stopwatch(
    .clk(S_AXI_ACLK),
    .sw_control_reg(slv_reg0), // 버튼 2개 쓸 것이므로
    .com(com), 
    .seg_7(seg_7)
);
// User logic ends

 

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "xgpio.h"

#define BTN_ID	XPAR_AXI_GPIO_BTN_DEVICE_ID
#define BTN_CHANNEL	1
#define BTN_MASK	0b1111 // 입/출력 결정하는 MASK, 여기서는 1111을 줘서 전부 입력으로 설정


int main()
{
    init_platform();
    print("Start!\n\r");

    XGpio_Config *cfg_ptr_btn;
    XGpio btn_device;

    /////// BTN ///////
    cfg_ptr_btn = XGpio_LookupConfig(BTN_ID);
    XGpio_CfgInitialize(&btn_device, cfg_ptr_btn, cfg_ptr_btn->BaseAddress);
    XGpio_SetDataDirection(&btn_device, BTN_CHANNEL, BTN_MASK); // 1을 줘서 입력 설정

    char btn_flag = 1;

    u8 btn_data = 0;
    volatile unsigned int *StopWatch_ctrl_reg = (volatile unsigned int *) 0x44A00000;


    while(1)
    {
    	btn_data = XGpio_DiscreteRead(&btn_device, BTN_CHANNEL);
    	if(btn_flag && btn_data == 0b1) // Start/Stop
    	{
    		btn_flag = 0; // 버튼을 계속 누르고 있을 때, 연속적인 값이 들어가는 것을 방지
    		StopWatch_ctrl_reg[0] ^= 0b1; // start/stop
    		xil_printf("%x\n\r", btn_data);
    	}
    	else if(btn_flag && btn_data == 0b10) // Lap
    	{
    		btn_flag = 0;
    		StopWatch_ctrl_reg[0] ^= 0b10; // ^= : 토글
    		xil_printf("%x\n\r", btn_data);
    	}
    	else if(btn_flag && btn_data == 0b100) // Clear
    	{
    		btn_flag = 0;
    		StopWatch_ctrl_reg[0] = 0b100; // 눌르고 있을 동안만 clear해줘야 한다. / Set
    		xil_printf("%x\n\r", btn_data);
    	}
    	else if(btn_data == 0) // 뗐을 때, btn_data = 0이므로
    	{
    		btn_flag = 1; // 눌렀다 뗐을 때  btn_flag = 1이 되도록
    		StopWatch_ctrl_reg[0] &= ~0b100; // Clear
//    		StopWatch_ctrl_reg[0] = 0b0;
    		xil_printf("%x\n\r", btn_data);
    	}
    	MB_Sleep(3);
    }

    cleanup_platform();
    return 0;
}

 

Bit 3 2 1 0
Control Reg - clear lap start/stop

 

이런 식으로 만들어주면, 우리도 데이터시트를 만들어줄 수 있다.

우리가 이번에 만든 것을 Control Register라고 한다.

 

RTL 그대로 올려서 쓸 수도 있지만,

이렇게 레지스터 기반으로 만들어주면

사용자들이 데이터시트보고 쓰기 쉽겠지?

 


 

728x90
반응형