관리 메뉴

거북이처럼 천천히

Verilog RTL 설계(8월 12일 - 3, ADC Converter - 3) 본문

RTL Design/Verilog RTL 설계

Verilog RTL 설계(8월 12일 - 3, ADC Converter - 3)

유로 청년 2024. 8. 24. 21:06

1. 조이스틱의 x축, y축 아날로그 값을 xadc 모듈을 통해 디지털로 변환

  • 이번에는 조이스틱의 x축, y축 아날로그 값을 xadc 모듈을 통해 디지털로 변환한 뒤, FND로 출력해보도록 하겠다.
  • 이번에는 변환해야하는 아날로그 값이 2개이기 때문에 xadc 모듈의 설정 값을 다음과 같이 설정하였다.

 

 

2. ADC Converter Module 설정

  • 2개의 아날로그 값을 변환하기 위해 JXADC 모듈의 6핀과 15핀을 사용하도록 하겠다.

Basic 상태 창
ADC Set up 창
Alarms 창

 

 

 

 

 

3. ADC Converter 모듈 설계

  • ADC Converter 모듈에 대한 설명은 코드와 함께 설명하도록 하겠다.

 

< Source, Input / Output 선언 >

module adc_converter_1_ch14_15(
    input clk, reset_p,
    input vauxp6, vauxn6,
    input vauxp15, vauxn15,
    input sw, 
    output reg [11:0] red_duty, green_duty, blue_duty,
    output reg red_blue);
    
endmodule
  • xadc 모듈의 6번 핀과 15핀을 사용할 것이기 때문에 이에 대한 6번과15핀의 input 설정한다.
  • RGB 색상의 duty을 컨트롤하기 위해 red_duty, green_duty, blue_duty 을 이용하도록 하겠다.
  • red_blue는 조이스틱의 x축을 통해 Red의 duty, Blue의 duty 값을 조절하기 위해 선언한 Register이다.

 

 

< Source, xadc_wiz 인스턴스 선언 >

// Declare variables.
    wire eoc_out; // End of convert, 변환 종료 신호
    wire [4:0] channel_out; // 현재 선택된 채널
    wire [15:0] do_out;
    
    // Instance of XADC Converter 
    xadc_wiz_0 adc_ch14_ch15 (
          .daddr_in({2'b0, channel_out}),   // XADC 레지스터에 접근하기 위한 주소를 지정
          .dclk_in(clk),                    // xadc 모듈의 clk 설정
          .den_in(eoc_out),                 // den_in는 eoc_out로 부터 디지털 변환 신호를 끝났다는 신호를 
                                            // 받으면 데이터 전송을 시작하게 된다.
   
          .reset_in(reset_p),               // 시스템 제어를 위한 리셋 신호
          .vauxp6(vauxp6),                  // Auxiliary channel 14
          .vauxn6(vauxn6),
          .vauxp15(vauxp15),                  // Auxiliary channel 15
          .vauxn15(vauxn15),
          .channel_out(channel_out),        // 채널 선택 출력, 현재 선택된 채널을 나타낸다.
          .do_out(do_out),                  // 아날로그 신호를 디지털 값으로 변환된 결과 값
          .eoc_out(eoc_out));               // 변환 종료 신호 );

 

 

 

 

< Source, eoc_out의 Positive edge 얻기 >

// Get positive edge of eoc_out.
    wire eoc_out_pedge;
    edge_detector edge_detector_eoc_out (.clk(clk), .reset_p(reset_p), .cp(eoc_out), .p_edge(eoc_out_pedge));
  • eoc_out 변수는 아날로그에서 디지털 변환이 끝났는지 여부를 나타내는 Flag이다.
  • 디지털로 변환이 끝나면 eoc_out 값이 0 → 1로 변화하기 때문에 eoc_out 변수의 positive edge를 통해 변화가 끝났는지 여부를 확인할 수 있다.

 

 

 

< Source, 조이스틱의 스위치의 Positive edge 얻기 >

// Get positive edge of switch
    wire sw_pedge;
    btn_cntr switch (.clk(clk), .reset_p(reset_p), .btn(sw), .btn_pedge(sw_pedge));

 

 

 

 

< Source, 변환이 끝난 뒤, channel_out을 통해 변환된 디지털 값을 output으로 전달한다. >

// 조이스틱의 스위치가 눌리면 mode 변환하여 blue 값 조절
    // red duty : x축, green duty : y 축, blue : x 축
    
    // 변환이 끝나면 channel_out을 통해 몇 번 채널의 변환이 끝났는지 확인 후, 출력
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) begin
            red_duty = 0;
            green_duty = 0;
            blue_duty = 0;
            red_blue = 1;
        end
        else if(eoc_out_pedge) begin
            case(channel_out[3:0])
                6 : begin
                    if(red_blue) red_duty = do_out[15:4];
                    else blue_duty = do_out[15:4];
                end
                15 : green_duty = do_out[15:4];
            endcase
        end
        else if(sw_pedge) red_blue = ~red_blue;
    end
endmodule
  • eoc_out_pedge 를 통해 아날로그에서 디지털로 변환이 끝났는지 여부를 확인할 수 있기 때문에 eoc_out_pedge가 활성화되면 channel_out 변수를 통해 몇 번 채널의 변환이 끝났는지 여부를 확인하여 해당 채널의 디지털 값을 Output 으로 전달한다.
  • Basys3는 6, 7, 14 ,15 채널을 지원하기 때문에 channel_out 변수에 4bit을 통해 몇 번 채널이 끝났는지 여부를 확인할 수 있다.
  • sw_pedge 가 활성화 되면 red_blue 값을 Toggle 시킨도록 설계하였다.
  • red_blue 값에 따라 변환된 디지털 값을 red duty에 전달할 지, blue duty에 전달할 지가 결정된다.  

 

 

 

4. 전체 소스 코드

// Top module of xadc
module top_module_of_xadc (
    input clk, reset_p,
    input vauxp6, vauxn6,
    input vauxp15, vauxn15,
    input sw, 
    output [3:0] com,
    output [7:0] seg_7 );
    
    // Instance of adc converter
    wire [11:0] red_duty, green_duty, blue_duty;
    wire red_blue;
    adc_converter_1_ch14_15 adc_convert (.clk(clk), .reset_p(reset_p),
                       .vauxp6(vauxp6), .vauxn6(vauxn6), .vauxp15(vauxp15), .vauxn15(vauxn15),
                       .sw(sw), .red_duty(red_duty), .green_duty(green_duty), .blue_duty(blue_duty), .red_blue(red_blue));
    
    // Convert from binary to BCD
    wire [15:0] red_duty_bcd, green_duty_bcd, blue_duty_bcd;
    bin_to_dec bcd_red(.bin({6'b0, red_duty[11:6]}),  .bcd(red_duty_bcd));
    bin_to_dec bcd_green(.bin({6'b0, green_duty[11:6]}),  .bcd(green_duty_bcd));
    bin_to_dec bcd_blue(.bin({6'b0, blue_duty[11:6]}),  .bcd(blue_duty_bcd));
    
    // Select FND
    wire [15:0] red_green, blue_green, fnd;
    
    assign red_green = {red_duty_bcd[7:0], green_duty_bcd[7:0]};
    assign blue_green = {blue_duty_bcd[7:0], green_duty_bcd[7:0]};
    
    assign fnd = (red_blue) ? red_green : blue_green;
    
   // FND Control
    fnd_cntr control_fnd (.clk(clk), .reset_p(reset_p), .hex_value(fnd), .com(com), .seg_7(seg_7));
    
endmodule

// adc converter module
module adc_converter_1_ch14_15(
    input clk, reset_p,
    input vauxp6, vauxn6,
    input vauxp15, vauxn15,
    input sw, 
    output reg [11:0] red_duty, green_duty, blue_duty,
    output reg red_blue);
    
    // Declare variables.
    wire eoc_out; // End of convert, 변환 종료 신호
    wire [4:0] channel_out; // 현재 선택된 채널
    wire [15:0] do_out;
    
    // Instance of XADC Converter 
    xadc_wiz_0 adc_ch14_ch15 (
          .daddr_in({2'b0, channel_out}),   // XADC 레지스터에 접근하기 위한 주소를 지정
          .dclk_in(clk),                    // xadc 모듈의 clk 설정
          .den_in(eoc_out),                 // den_in는 eoc_out로 부터 디지털 변환 신호를 끝났다는 신호를 
                                            // 받으면 데이터 전송을 시작하게 된다.
   
          .reset_in(reset_p),               // 시스템 제어를 위한 리셋 신호
          .vauxp6(vauxp6),                  // Auxiliary channel 14
          .vauxn6(vauxn6),
          .vauxp15(vauxp15),                  // Auxiliary channel 15
          .vauxn15(vauxn15),
          .channel_out(channel_out),        // 채널 선택 출력, 현재 선택된 채널을 나타낸다.
          .do_out(do_out),                  // 아날로그 신호를 디지털 값으로 변환된 결과 값
          .eoc_out(eoc_out));               // 변환 종료 신호 );
    
    // Get positive edge of eoc_out.
    wire eoc_out_pedge;
    edge_detector edge_detector_eoc_out (.clk(clk), .reset_p(reset_p), .cp(eoc_out), .p_edge(eoc_out_pedge));
    
    // Get positive edge of switch
    wire sw_pedge;
    btn_cntr switch (.clk(clk), .reset_p(reset_p), .btn(sw), .btn_pedge(sw_pedge));
    
    // 조이스틱의 스위치가 눌리면 mode 변환하여 blue 값 조절
    // red duty : x축, green duty : y 축, blue : x 축
    
    // 변환이 끝나면 channel_out을 통해 몇 번 채널의 변환이 끝났는지 확인 후, 출력
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) begin
            red_duty = 0;
            green_duty = 0;
            blue_duty = 0;
            red_blue = 1;
        end
        else if(eoc_out_pedge) begin
            case(channel_out[3:0])
                6 : begin
                    if(red_blue) red_duty = do_out[15:4];
                    else blue_duty = do_out[15:4];
                end
                15 : green_duty = do_out[15:4];
            endcase
        end
        else if(sw_pedge) red_blue = ~red_blue;
    end
endmodule

// Button Control Module
module btn_cntr (
    input clk, reset_p,
    input btn,
    output btn_pedge );
  
    // temp register of button
    reg reg_btn;
    
    // 1ms one cycle pulse
    wire clk_1usec, clk_1msec;
    clk_div_100 clk_div_1usec (.clk(clk), .reset_p(reset_p), .clk_div_100_nedge(clk_1usec));
    clk_div_1000 clk_div_1umsec (.clk(clk), .reset_p(reset_p), .clk_source(clk_1usec), .clk_div_1000_nedge(clk_1msec));
  
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) reg_btn = 0;
        else reg_btn = btn;
    end
    
    edge_detector ed_button (.clk(clk), .reset_p(reset_p), .cp(reg_btn), .p_edge(btn_pedge));
endmodule

// Clock divider 100
module clk_div_100 (
    input clk, reset_p,
    output clk_div_100,
    output clk_div_100_nedge, clk_div_100_pedge );
    
    reg [6:0] counter;
    
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) counter = 0;
        else begin
            if(counter >= 99) counter = 0;
            else counter = counter + 1;
        end
    end
    
    assign clk_div_100 = (counter < 50) ? 0 : 1;
    
    edge_detector edge_detector_clk_div_100 (.clk(clk), .reset_p(reset_p), .cp(clk_div_100), 
                .n_edge(clk_div_100_nedge), .p_edge(clk_div_100_pedge));
    
endmodule

// Clock divider 1000
module clk_div_1000 (
    input clk, reset_p,
    input clk_source,
    output clk_div_1000,
    output clk_div_1000_nedge, clk_div_1000_pedge );
    
    wire clk_source_nedge;
    edge_detector edge_detector_0 (.clk(clk), .reset_p(reset_p), .cp(clk_source), 
                .n_edge(clk_source_nedge));
    
    reg [9:0] counter;
    
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) counter = 0;
        else if(clk_source_nedge) begin
            if(counter >= 999) counter = 0;
            else counter = counter + 1;
        end
    end
    
    assign clk_div_1000 = (counter < 500) ? 0 : 1;
    
    edge_detector edge_detector_1 (.clk(clk), .reset_p(reset_p), .cp(clk_div_1000), 
                .n_edge(clk_div_1000_nedge), .p_edge(clk_div_1000_pedge));
    
endmodule

// Edge detector 
module edge_detector (
    input clk, reset_p,
    input cp,
    output p_edge, n_edge );
    
    reg flip_flop_current, flip_flop_old;
    
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) begin
            flip_flop_current <= 0;
            flip_flop_old <= 0;
        end
        else begin
            flip_flop_current <= cp;
            flip_flop_old <= flip_flop_current;
        end
    end
    
    assign p_edge = ({flip_flop_current, flip_flop_old} == 2'b10) ? 1 : 0;
    assign n_edge = ({flip_flop_current, flip_flop_old} == 2'b01) ? 1 : 0;
    
endmodule

// Convert from binary to BCD
module bin_to_dec(
        input [11:0] bin,
        output reg [15:0] bcd
    );

    reg [3:0] i;

    always @(bin) begin
        bcd = 0;
        for (i=0;i<12;i=i+1)begin
            bcd = {bcd[14:0], bin[11-i]};
            if(i < 11 && bcd[3:0] > 4) bcd[3:0] = bcd[3:0] + 3;
            if(i < 11 && bcd[7:4] > 4) bcd[7:4] = bcd[7:4] + 3;
            if(i < 11 && bcd[11:8] > 4) bcd[11:8] = bcd[11:8] + 3;
            if(i < 11 && bcd[15:12] > 4) bcd[15:12] = bcd[15:12] + 3;
        end
    end
endmodule

 

< Basys3의 xdc 파일 내에서 JXADC 포트 설정 >

 

 

 

5. 하드웨어 구현