Verilog_RTL 설계/Basys_3

[PWM]LED_128_Step

juniha 2025. 7. 17. 20:58

PWM_(Pulse Width Modulation)

  PWM이란 디지털 신호의 펄스 폭(ON 되는 시간)을 조절하여, 출력의 평균값이나 에너지를 조정하는 신호 처리 방식

 

  • 디지털 신호(0 또는 1, ON/OFF)로 구성된 사각파(펄스)를 일정 주기로 반복적으로 출력
  • 한 주기(Period) 동안 신호가 1(ON)인 구간의 비율(%)을 듀티 사이클(Duty Cycle)이라한다.
  • 이 듀티 사이클을 변화시키면, 출력의 평균 전압 또는 전달되는 에너지의 양이 변한다.

 

목적

  • Verilog를 이용하여 128단계의 PWM(Pulse Width Modulation) 신호를 생성하는 하드웨어 회로를 설계
  • PWM이란 디지털 신호의 펄스 폭(ON 되는 시간)을 조절하여, 출력의 평균값이나 에너지를 조정하는 신호 처리 방식

사용 코드_ 상위모듈

module led_pwm_top(
    input clk, reset_p,                    // 시스템 클록, 비동기 리셋(Active-high)
    output r, g, b,                        // RGB LED 출력
    output [15:0] debug_led,               // 디버그용 LED(16개)
    output [7:0] seg_7,                    // 7세그먼트 a~g, dp
    output [3:0] com                       // 7세그먼트 자릿수 선택
);

    // 카운터 선언: 다양한 주기/패턴 생성을 위한 카운터
    integer cnt;
    always @(posedge clk) cnt = cnt + 1;   // 매 클록 상승에지마다 cnt 1증가

    // 각 컬러별 PWM 신호 생성
    // cnt의 상위 비트들을 PWM duty로 사용해 색이 천천히 바뀜
    pwm_led_128step pwm_r(
        .clk(clk),
        .reset_p(reset_p),
        .duty(cnt[29:23]),                 // R: cnt[29:23] 사용(7비트)
        .pwm(r)
    );
        
    pwm_led_128step pwm_g(
        .clk(clk),
        .reset_p(reset_p),
        .duty(cnt[30:24]),                 // G: cnt[30:24] 사용(7비트)
        .pwm(g)
    );
        
    pwm_led_128step pwm_b(
        .clk(clk),
        .reset_p(reset_p),
        .duty(cnt[28:22]),                 // B: cnt[28:22] 사용(7비트)
        .pwm(b)
    );

    // 디버그용 LED 0번에도 PWM 출력 연결(Blue와 동일 duty)
    pwm_led_128step pwm_led(
        .clk(clk),
        .reset_p(reset_p),
        .duty(cnt[28:22]),
        .pwm(debug_led[0])
    );

    // cnt[28:22] 값(7비트)을 BCD로 변환하여 4자리 7세그먼트 표시
    wire [15:0] bcd_duty;                  // 변환된 BCD 값(4자리 x 4비트)
    bin_to_dec bcd_x(
        .bin(cnt[28:22]),                  // 입력: 7비트 이진값
        .bcd(bcd_duty)                     // 출력: 4자리 BCD
    );

    // 7세그먼트 드라이버: 변환된 BCD duty값 표시
    fnd_4digit_cntr fnd(
        .clk(clk),
        .reset_p(reset_p),
        .value(bcd_duty),                  // 표시할 값(BCD)
        .seg_7(seg_7),
        .com(com)
    );

endmodule

사용 코드_ 하위 모듈

module pwm_led_128step (
    input clk, reset_p,              // 시스템 클록, 비동기 리셋(Active-high)
    input [6:0] duty,                // 7비트 PWM 듀티비(0~127)
    output reg pwm                   // PWM 출력
);
    
    // 파라미터: 시스템 클록, PWM 목표 주파수, duty resolution(128단계)
    parameter sys_clk_freq = 100_000_000;  // 시스템 클록 100MHz (예시)
    parameter pwm_freq = 10_000;           // PWM 주파수 10kHz (예시)
    parameter duty_step = 128;             // PWM 분해능(128단계)
    parameter temp = sys_clk_freq / pwm_freq / duty_step / 2;
    // temp: 하나의 duty 구간에서 토글될 때까지의 클록 수 (1주기=2번 토글, 128단계*2번*temp=전체 PWM period)

    integer cnt;                   // sys_clk을 temp로 분주하는 카운터
    reg pwm_freqX128;              // 128*PWM 주기 신호(토글 신호)

    // [1] PWM 내부 분주기: cnt가 temp-1이 되면 토글, 128*2*temp=전체 PWM period
    always @(posedge clk, posedge reset_p) begin
        if (reset_p) begin
            cnt = 0;
        end else begin
            if (cnt >= temp-1) begin
                cnt = 0;
                pwm_freqX128 = ~pwm_freqX128; // 토글
            end else
                cnt = cnt + 1;
        end
    end

    // [2] 분주 신호의 네거티브 에지 검출기(1 PWM step마다 동작)
    wire pwm_freqX128_nedge;
    edge_detector_p ed_btn(
        .clk(clk),
        .reset_p(reset_p),
        .cp(pwm_freqX128),
        .n_edge(pwm_freqX128_nedge)
    );

    reg [6:0] cnt_duty;  // 0~127까지 카운터 (duty cycle)
    // [3] 네거티브 에지마다 duty 카운터 증가, 128되면 자동 0으로 wrap
    //     cnt_duty < duty 이면 PWM=1(ON), 아니면 PWM=0(OFF)
    always @(posedge clk or posedge reset_p) begin
        if (reset_p) begin
            cnt_duty = 0;
            pwm = 0;
        end
        else if (pwm_freqX128_nedge) begin
            cnt_duty = cnt_duty + 1;
            if (cnt_duty < duty)
                pwm = 1;   // 듀티 구간: ON
            else
                pwm = 0;   // OFF 구간
        end
    end
    
endmodule

사용 코드_ XDC

## Clock signal
set_property -dict { PACKAGE_PIN W5   IOSTANDARD LVCMOS33 } [get_ports clk]
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports clk]                                                                                   s clk]


## Switches
set_property PACKAGE_PIN V17 [get_ports {duty[0]}]
set_property PACKAGE_PIN W17 [get_ports {duty[1]}]
set_property PACKAGE_PIN W16 [get_ports {duty[2]}]
set_property PACKAGE_PIN V16 [get_ports {duty[3]}]
set_property PACKAGE_PIN W15 [get_ports {duty[4]}]
set_property PACKAGE_PIN V15 [get_ports {duty[5]}]
set_property PACKAGE_PIN W14 [get_ports {duty[6]}]

## LEDs
set_property -dict { PACKAGE_PIN U16   IOSTANDARD LVCMOS33 } [get_ports {debug_led[0]}]
set_property -dict { PACKAGE_PIN E19   IOSTANDARD LVCMOS33 } [get_ports {debug_led[1]}]
set_property -dict { PACKAGE_PIN U19   IOSTANDARD LVCMOS33 } [get_ports {debug_led[2]}]
set_property -dict { PACKAGE_PIN V19   IOSTANDARD LVCMOS33 } [get_ports {debug_led[3]}]
set_property -dict { PACKAGE_PIN W18   IOSTANDARD LVCMOS33 } [get_ports {debug_led[4]}]
set_property -dict { PACKAGE_PIN U15   IOSTANDARD LVCMOS33 } [get_ports {debug_led[5]}]
set_property -dict { PACKAGE_PIN U14   IOSTANDARD LVCMOS33 } [get_ports {debug_led[6]}]
set_property -dict { PACKAGE_PIN V14   IOSTANDARD LVCMOS33 } [get_ports {debug_led[7]}]
set_property -dict { PACKAGE_PIN V13   IOSTANDARD LVCMOS33 } [get_ports {debug_led[8]}]
set_property -dict { PACKAGE_PIN V3    IOSTANDARD LVCMOS33 } [get_ports {debug_led[9]}]
set_property -dict { PACKAGE_PIN W3    IOSTANDARD LVCMOS33 } [get_ports {debug_led[10]}]
set_property -dict { PACKAGE_PIN U3    IOSTANDARD LVCMOS33 } [get_ports {debug_led[11]}]
set_property -dict { PACKAGE_PIN P3    IOSTANDARD LVCMOS33 } [get_ports {debug_led[12]}]
set_property -dict { PACKAGE_PIN N3    IOSTANDARD LVCMOS33 } [get_ports {debug_led[13]}]
set_property -dict { PACKAGE_PIN P1    IOSTANDARD LVCMOS33 } [get_ports {debug_led[14]}]
set_property -dict { PACKAGE_PIN L1    IOSTANDARD LVCMOS33 } [get_ports {debug_led[15]}]


##7 Segment Display
set_property -dict { PACKAGE_PIN W7   IOSTANDARD LVCMOS33 } [get_ports {seg_7[7]}]
set_property -dict { PACKAGE_PIN W6   IOSTANDARD LVCMOS33 } [get_ports {seg_7[6]}]
set_property -dict { PACKAGE_PIN U8   IOSTANDARD LVCMOS33 } [get_ports {seg_7[5]}]
set_property -dict { PACKAGE_PIN V8   IOSTANDARD LVCMOS33 } [get_ports {seg_7[4]}]
set_property -dict { PACKAGE_PIN U5   IOSTANDARD LVCMOS33 } [get_ports {seg_7[3]}]
set_property -dict { PACKAGE_PIN V5   IOSTANDARD LVCMOS33 } [get_ports {seg_7[2]}]
set_property -dict { PACKAGE_PIN U7   IOSTANDARD LVCMOS33 } [get_ports {seg_7[1]}]

set_property -dict { PACKAGE_PIN V7   IOSTANDARD LVCMOS33 } [get_ports seg_7[0]]

set_property -dict { PACKAGE_PIN U2   IOSTANDARD LVCMOS33 } [get_ports {com[0]}]
set_property -dict { PACKAGE_PIN U4   IOSTANDARD LVCMOS33 } [get_ports {com[1]}]
set_property -dict { PACKAGE_PIN V4   IOSTANDARD LVCMOS33 } [get_ports {com[2]}]
set_property -dict { PACKAGE_PIN W4   IOSTANDARD LVCMOS33 } [get_ports {com[3]}]


##Buttons
set_property -dict { PACKAGE_PIN U18   IOSTANDARD LVCMOS33 } [get_ports reset_p]

##Pmod Header JA
set_property -dict { PACKAGE_PIN J1   IOSTANDARD LVCMOS33 } [get_ports {r}];#Sch name = JA1
set_property -dict { PACKAGE_PIN L2   IOSTANDARD LVCMOS33 } [get_ports {g}];#Sch name = JA2
set_property -dict { PACKAGE_PIN J2   IOSTANDARD LVCMOS33 } [get_ports {b}];#Sch name = JA3

##Pmod Header JXADC
set_property -dict { PACKAGE_PIN J3   IOSTANDARD LVCMOS33 } [get_ports {vauxp6}];#Sch name = XA1_P
set_property -dict { PACKAGE_PIN L3   IOSTANDARD LVCMOS33 } [get_ports {vauxp14}];#Sch name = XA2_P
set_property -dict { PACKAGE_PIN K3   IOSTANDARD LVCMOS33 } [get_ports {vauxn6}];#Sch name = XA1_N
set_property -dict { PACKAGE_PIN M3   IOSTANDARD LVCMOS33 } [get_ports {vauxn14}];#Sch name = XA2_N


## Configuration options, can be used for all designs
set_property CONFIG_VOLTAGE 3.3 [current_design]
set_property CFGBVS VCCO [current_design]

## SPI configuration mode options for QSPI boot, can be used for all designs
set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]
set_property BITSTREAM.CONFIG.CONFIGRATE 33 [current_design]
set_property CONFIG_MODE SPIx4 [current_design]

Schematic_ATL_Analysis

Schematic_ Synthesis

회로 구현

동작 영상

오실로스코프 측정

결과

 

  • 카운터 생성
    • 내부에서 cnt라는 정수형 카운터가 클럭마다 1씩 계속 증가한다.
    • 이 카운터의 상위 비트(cnt[28:22])는 0에서 127까지 변하며 계속 순환된다.
  • PWM 신호 생성
    • 각 RGB LED(R/G/B)와 디버그용 LED에 대해, 각각 cnt의 서로 다른 7비트 구간을 duty 입력으로 사용해
      PWM 신호를 만든다.
    • 이 PWM 신호는 duty 값에 따라 High(ON)와 Low(OFF) 구간이 달라지며, 실제 LED의 밝기를 제어하는 것을 확인 할 수 있다.
    • duty 값이 커질수록 ON 구간이 길어져서 LED가 더 밝아지는 것을 확인 할 수 있다..
  • 7세그먼트(FND) 표시
    • cnt[28:22] 값(0~127)을 이진수에서 BCD로 변환해서 4자리 7세그먼트에 나오는 것을 확인 할 수 있다.
    • 7세그먼트에 나타나는 숫자는 현재 PWM 듀티 값과 정확히 일치하는 것을 확인 할 수 있다.
  • LED 확인
    • FND=10 → PWM duty=10, LED 매우 어둡고, PWM ON구간 매우 짧음
    • FND=64 → PWM duty=64, LED 중간 밝기, PWM ON/OFF 50:50
    • FND=127 → PWM duty=127, LED 매우 밝음, PWM 거의 항상 ON
  • 오실로스코프 확인
    • FND에 표시되는 값이 1씩 증가하는 시점이 곧 PWM 파형의 듀티 사이클(duty cycle)이 갱신되어 한 주기가 종료되고 다음 주기가 시작되는 타이밍과 일치하는 것을 확인 할 수 있다.
    • 따라서, 7세그먼트 값이 변할 때마다 오실로스코프 상에서도 PWM 신호의 High/Low 패턴이 다음 duty로 즉시 전환되는 동기적인 변화를 확인 할 수 있다.

 

 

 

 

'Verilog_RTL 설계 > Basys_3' 카테고리의 다른 글

[FSM] I2C_LCD_Control  (3) 2025.07.22
[FSM] Ultrasonic_sensor_(HC_SR04)  (0) 2025.07.16
[FSM] DTH11_Sensor  (0) 2025.07.14
[Sequential_Circuit] edge_detector  (0) 2025.07.13
[Shift_Register] PIPO_(Parallel-In Parallel-Out)  (0) 2025.07.12