거북이처럼 천천히

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

RTL Design/Verilog RTL 설계

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

유로 청년 2024. 8. 1. 21:40

1. Pulse wave의 duty ratio를 128단계로 나누어 컨트롤 하는 모듈을 설계한 뒤, LED를 통한 출력

 

< Source, Pulse wave의 duty ratio를 128단계로 나누어 컨트롤 하는 모듈 >

// Duty ratio 128 step control
module PWM_prescaling_128_Control_LED(
    input clk, reset_p,
    input [6:0] duty,
   output pwm );
   
   parameter sys_clk = 100_000_000;
   parameter step_duty = 128;
   parameter pwm_freq = 10_000;
   
   parameter temp = sys_clk / step_duty / pwm_freq;
   parameter temp_half = temp / 2;
   
  integer 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 clk_div_temp;
   assign clk_div_temp = (cnt_sysclk < temp_half) ? 0 : 1;
   
   wire clk_div_temp_n_edge;
   edge_detector edge_detector_0(.clk(clk), .reset_p(reset_p), .cp(clk_div_temp), .n_edge(clk_div_temp_n_edge));
   
   // 128 Prescaling
   reg [6:0] clk_pwm_duty;
   always @(posedge clk or posedge reset_p) begin
        if(reset_p) clk_pwm_duty = 0;
        else if(clk_div_temp_n_edge) clk_pwm_duty = clk_pwm_duty + 1;
   end
   
   assign pwm = (clk_pwm_duty < duty) ? 1 : 0;
endmodule

// Edge detector 
module edge_detector (
    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
  • Clock Pulse Wave를 duty ratio를 128단계로 나누어 컨트롤하기 위해 128분주화 하면 새로운 Pulse wave를 얻을 수 있으며, 이 새로운 Pulse wave는 100000000Hz / 128 = 781250Hz를 갖는다.
  • 하지만, LED를 연속적으로 불이 켜지도록 만들기 위해서는 최소한 10kHz Pulse여야 한다.
  • 따라서 781250Hz Pulse를 몇 분주해야 10kHz Pulse wave가 나올 수 있는지에 대해서 생각해볼 필요가 있다.
  • 781250Hz Pulse를 781250Hz / 10kHz = 78.125 = 78 분주를 하게 되면 10kHz Pulse wave를 얻을 수 있다.
  • 따라서 CP를 128분주 + 78분주를 통해 10kHz Pulse를 얻을 수 있으며, 해당 Pulse의 duty ratio를 128단계를 나누 었기 때문에 128단계로 LED의 밝기를 조절할 수 있다.

  • duty 변수와 Basys3의 switch를 연결하여 switch를 통해 duty 값을 전달하면 해당 duty 값 만큼 LED의 밝기가 나와 Basys3의 switch를 통해 LED의 밝기를 조절할 수 있다.

 

 

< Source, Top Module >

module PWM_LED_0(
    input clk, reset_p,
    input [6:0] switch,
    output pwm_led);
    
    PWM_prescaling_128_Control_LED PWM_LED(.clk(clk), .reset_p(reset_p), .duty(switch), .pwm(pwm_led) );
    
endmodule
  • Basys3의 7개 Switch를 통해 10kHz, Pulse Wave의 Duty ratio를 컨트롤 하게 된다.

 

 

< LED 밝기 동작 확인 >

 

 

 

 

 

2. Pulse wave의 duty ratio를 128단계로 나누어 컨트롤 하는 모듈을 설계한 뒤, LED를 통한 출력
   하는데, 추가적으로 시간에 지남에 따라 LED 밝기를 점차 증가되도록 설계

 

< Source, Top Module >

module PWM_LED_0(
    input clk, reset_p,
    output pwm_led);
    
    reg [31:0] cnt_sysclk;
    always @(posedge clk) cnt_sysclk = cnt_sysclk + 1;
    
    PWM_prescaling_128_Control_LED PWM_LED(.clk(clk), .reset_p(reset_p), .duty(cnt_sysclk[30:25]), .pwm(pwm_led) );
    
endmodule
  • 10ns Clock Pulse마다 Counting되는 Counter를 이용하여 32bit 중에서 7bit를 묶어 duty 값으로 전달한다.
  • cnt_sysclk은 10ns 마다 1씩 카운트 되기 때문에 LSB 영역은 빠르게 값이 변화할 것이다.
  • 하지만, MSB 영역쪽은 상대적으로 천천히 변화할 것이기 때문에 이를 활용하여 LSB 쪽에 가까운 7bit를 묶어 전달한다면 LED의 밝기는 빠르게 변화할 것이다.
  • 그러나, MSB 영역쪽에서 7bit를 묶어 전달한다면 값이 천천히 변화하기 때문에 상대적으로 LED의 밝기는 천천히 변화할 것이다.


  • 위 경우에는 30번째 bit ~ 25번째 bit를 duty 값으로 주었기 때문에 LED의 밝기는 2^31 * 10ns = 21.474초 동안 점차 밝아 지다가 다시 어두워 질 것이다.
  • LSB에서 MSB쪽으로 1bit씩 갈수록 LED의 밝기가 점차 밝아지는 속도는 2배 느려지며, 밝아지는 시간은 2배 늘어난다.

 

 

 

< LED 밝기 동작 확인 >