거북이처럼 천천히

Verilog RTL 설계(7월 31일 - 3, PWM - 3) 본문

RTL Design/Verilog RTL 설계

Verilog RTL 설계(7월 31일 - 3, PWM - 3)

유로 청년 2024. 8. 1. 13:56

1.  LED의 밝기를 128단계로 나누어 컨트롤하기.

  • 이번에는 LED의 밝기를 128단계로 나누어 컨트롤 할 수 있도록 모듈 설계해보도록 하겠다.
  • 이를 통해 "왜 2단계로 나누어 Prescaling을 진행하는가?"를 정확하게 이해 할 수 있다.

 

< Source, LED의 밝기를 128단계로 나누기 위해 Duty ratio를 128단계로 나누어 컨트롤하는 모듈 >

// Duty ratio 128 step Control
module pwm_led_128_step(
    input clk, reset_p,
    input [6:0] duty,
    output pwm);
    
    // Declare base parameter
    parameter sys_clk = 100_000_000;
    parameter pwm_freq = 10_000;
    parameter duty_step = 128;
    
    // 최종적으로 10kHz Pulse를 만들기 위한 prescaling 만들기.
    parameter temp = sys_clk / pwm_freq / duty_step;
    parameter temp_half = temp / 2;
    
    
    // 10kHz Pulse wave를 만들기 위한 Prscaling 
    reg [6:0] cnt_sysclk;
    
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) cnt_sysclk = 0;
        else begin
            if(cnt_sysclk >= temp - 1) cnt_sysclk = 0;
            else cnt_sysclk = cnt_sysclk + 1;
        end
    end
    
    wire pwm_freqX128;
    assign pwm_freqX128 = (cnt_sysclk < temp_half)? 0 : 1;
    
    wire pwm_freqX128_nedge;
    edge_detector_p edge_detector_0 (.clk(clk), .reset_p(reset_p), .cp(pwm_freqX128), .n_edge(pwm_freqX128_nedge));
    
    // PWM의 Duty ratio을 128단계로 나누어 컨트롤 하기 위해서
    // 따라서 128로 분주화하는 것이다.
    reg [6:0] cnt_pwm;
    
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) cnt_pwm = 0;
        else if(pwm_freqX128_nedge) cnt_pwm = cnt_pwm + 1;
    end
    
    assign pwm = (cnt_pwm < duty)? 1 : 0;
    
endmodule

// Edge detector
module edge_detector_p (
    input clk, reset_p,
    input cp,
    output n_edge, p_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
  • 선언한 3개의 parameter는 다음과 같은 의미를 같는다.
    - sys_clk : 10ns인 Clock Pulse의 주파수
    - pwm_freq : LED가 연속적으로 나오기 위한 주파수 , 10kHz
    - duty_step : duty ratio를 128단계로 나눈다는 정보를 담고 있는 parameter


    ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
  • Q) 아래 코드에서 왜 아래와 같은 공식 나왔는가?
parameter temp = sys_clk / pwm_freq / duty_step;
  • A) 이는 prescaling을 두 번으로 나누어 시행한 이유와도 연관 있다. 먼저, PWM의 duty ratio을 몇 단계로 나눌지 여부를 결정한다. 그리고, 난 뒤, Clock Pulse를 duty_step만큼 prescaling한다. 이렇게 prescaling을 통해 생성된 새로운 Pulse wave는 100000000Hz / duty_step 만큼의 주파수를 갖는다.

    하지만, 우리가 원하는 Pulse wave는 10000Hz를 가져야 하기 때문에 100000000Hz / duty_step 만큼의 주파수를 갖는 펄스파를 몇 분주해야 10000Hz Pulse wave로 만들 수 있는가?

    바로, 100000000Hz / duty_step / 10000 만큼  Prescaling을 해야 분주화를 통해 10000Hz Pulse wave를 얻을 수 있다. 따라서 위 식은 이러한 과정을 통해 얻었으며, 이는 "왜 2번의 Prescaling을 해야하는가?"에 대한 질문의 해답이라고 할 수 있다.


  • 첫 번째, Prescaling은 10000Hz pulse wave를 만들기 위함의 Prescaling이며,
    두 번째, Prescaling은 Pulse wave의 duty ratio를 단계별로 나누어 컨트롤하기 위한 Prescaling이다.



 

< Source, Period = 100usec, Duty ratio의 step = 128인 PWM을 control하는 Top Module >

// Duty ratio 128 step Control top module
module pwm_led_128_step_top_module (
    input clk, reset_p,
    input [6:0] duty,
    output pwm);
    
     pwm_led_128_step pwm_module(.clk(clk), .reset_p(reset_p), .duty(duty), .pwm(pwm));
    
endmodule

 




< Simulation, Duty ratio = 100 >

 

 

< Simulation, Duty ratio = 50 >

 

 

< Simulation, Duty ratio = 120 >