본문 바로가기
# Semiconductor/[Semicon Academy]

[Harman 세미콘 아카데미] 58일차 - Verilog(PWM 코드 및 시뮬레이션)

by Graffitio 2023. 9. 15.
[Harman 세미콘 아카데미] 58일차 - Verilog(PWM 코드 및 시뮬레이션)
728x90
반응형
[PWM]

 

📌 PWM 기초

 

클락의 펄스를 예로 들어 보자.

 

module clock_usec(
    input clk, rst,
    input enable,
    output clk_usec
    );
    
    // 125개 카운트하면 1us
    reg [6:0] cnt_8nsec;
    wire cp_usec; // cp : clock pulse
    
    always @(posedge clk, posedge rst) begin
        if(rst) cnt_8nsec = 0; // reset이 들어오면 cnt = 0
            else if(cnt_8nsec >= 124) cnt_8nsec = 0; // 0부터 124번까지 카운트하면 다시 0으로
            else cnt_8nsec = cnt_8nsec + 1;
     end
    
    assign cp_usec = cnt_8nsec < 63 ? 0 : 1; // 0~62 : 0, 63~124 : 1
    // 여기서 63 값을 조정하여 듀티사이클를 조정할 수 있다.
    // 예를 들어 90으로 바꾸면, 0~89까지는 0이고, 90~124까지는 1이므로 듀티사이클이 줄어든다.
    
    // 비정규 clock이므로 동기화가 필요하다.
    edge_detector_n edg(.clk(clk), .cp_in(cp_usec), .rst(rst), .n_edge(clk_usec)); /// 안 쓰는 출력은 빼버리면 된다.
    
endmodule

 

위 코드에서 cnt_8nsec 값을 비교하는 부분을 조절하면, 듀티사이클을 조절할 수 있다.

 


 

📌 부품에서의 PWM 사용

 

LED는 주파수가 1000Hz에서 10000Hz일 때 출력이 잘 나온다

최소 2ms 주기로 켜져있으면, 깜빡이는 것처럼 보이지 않는다.(다이내믹 구동)

깜빡이는 속도는 주파수로 조절, 밝기는 듀티사이클로 조절

모터는 100~1kHz 정도의 주파수로 조절해야 한다. 더 커지면 이잉 소리만 나면서 제대로 동작 안함.

 


 

📌 PWM 코드 및 설명

 

1초에 1000개의 클락을 만들 거면,

클락은 이 것의 100배인 100,000개가 나와야 하고

그 중에서 100개 셀 때마다 pwm이 하나씩 나와야 한다.

 - 이 때, 100개 중에서 1의 갯수에 따라 듀티 사이클이 정해진다.

 - 또한 여기서 100을 정밀도라고 한다.

따라서 1초에 1000개의 pwm이 나오게 되고

이 pwm의 주파수는 1000Hz

 

module PWM_100(
    input clk, rstp,
    input [6:0] duty, // 듀티비 설정 변수 
    input [13:0] pwm_freq, // parameter 대신 여기에 선언해도 된다. 10,000Hz 쓸 거니까 14bit
    output reg pwm_100pc // pc : % -> 하나당 센티(1/100)이라는 뜻
    );
    
    parameter sys_clk_freq = 125_000_000; // cora boadr의 시스템 클락
//    parameter pwm_freq = 1000; // 1000Hz짜리 pwm을 만들기 위한 define
    
    reg [26:0] cnt;
    reg clk_freqX100; // 100배된 클럭 주기 생성을 위한 변수 
    always @(posedge clk, posedge rstp) begin
        if(rstp) begin
            cnt = 0;
            clk_freqX100 = 0;
        end
        else begin
            // 125000까지 카운트하고 클럭 주기 생성
//            if(cnt >= 124_999) begin  // 1000Hz짜리를 만들 것이기 때문에 sys clk를 1000으로 나눔
//            if(cnt >= (sys_clk_freq / pwm_freq / 2)) begin // 토글시켜서 사용할 것이니까 2로 나눔
//            if(cnt >= (sys_clk_freq / pwm_freq / 2 / 100)) begin // 듀티비를 계산하기 위해 100 나눠주고 clk_freqX100을 카운트하여 0과 1을 부여한다.
            if(cnt >= (sys_clk_freq / pwm_freq / 200)) begin // always문 들어 올때마다 나누기 연산하게 되므로, Negative Slack 발생 가능성 높아짐. 간단하게 바꿔주자
                cnt = 0;
                clk_freqX100 = ~clk_freqX100; // pwm 한 주기를 만들기 위한 문장
            end
            else begin
                cnt = cnt + 1; // 클락을 세는 카운터
            end
//            // Duty cycle : 50%
//            if(cnt < 62500) begin
//                pwm_100pc = 0;
//            end
//            else begin
//                pwm_100pc = 1;
//            end
        end
    end 
    
    wire clk_freqX100_n;
    edge_detector_p edg(.clk(clk), .cp_in(clk_freqX100), .rst(rst), .n_edge(clk_freqX100_n)); // Edge Detector
    // 이전 always문에서 clk_freqX100 동작하고 4ns 후에나 edge 잡을 것이다.
    // Timing 문제로 인해 posedge clk를 사용하는 edge detector를 사용하는 것이 좋다.
    // 지금 상황에서는 nedge clk보다 pedge clk가 약 2배정도 더 여유가 있다.
    
    reg [6:0] cnt_duty; // 듀티사이클의 카운터 변수 
    always @(posedge clk, posedge rstp) begin
        if(rstp) begin
            cnt_duty = 0;
            pwm_100pc = 0;
        end
        else if(clk_freqX100_n) begin
            if(cnt_duty >= 99) begin
                cnt_duty = 0;    
            end
            else begin
                cnt_duty = cnt_duty + 1;
            end
            if(cnt_duty < duty) pwm_100pc = 1; // 예를 들어 50을 duty로 받으면, 50까지는 1, 나머지는 0
            else pwm_100pc = 0;
        end
    end 
endmodule

 


 

📌 PWM Simulation

 

clk : 8ns로 0 ~1 반복

rstp : 8ns동안 1줘서 초기화하고 0으로 전환

duty : Unsigned Decimal로 30 (30%)

pwm_freq : Unsigned Decimal로 1000 (주파수 1000Hz짜리 pwm)

 

clk 8ns, rstp 1주고 다시 0주고, duty 30, freq 1000

 

Add Marker 사용하여 커서를 추가할 수도 있다.

 


 

728x90
반응형