본문 바로가기

[Harman] 반도체 설계/Quartus

Quartus II project - Avalon pwm.

PWM : Pulse Width Modulation.

Required Register : 8 bit duty, 8 bit div(period)

 

Why 8 Bit?

 : Computer Architecture 에서 8bit CPU라고 하면 ALU, 레지스터, 데이터 버스 등의 처리 단위가 8비트로 된 CPU를 말한다. Imbedded System 과 같이 특수한 기능만을 수행하는 Computing System 에서는 아직도 단가가 싸고 프로그래밍이 편리한 8bit Micro-Controller 가 많이 사용된다.

 

 

Avalon Bus Read & Write.

 

Avalon PWM.

Block Diagram.

 

하드웨어의 올바른 동작을 확인하기 위해 Modelsim으로 먼저 avalon_pwm module의 시뮬레이션을 진행한다. Test - Bench의 A.V. Bus(Avalon Bus Data) 값들에 대해 하드 코딩으로 시뮬레이션을 진행한다. 

입력 신호는 Reg, 출력 신호는 Wire

 

`timescale 1 ns / 1 ns

module avalon_pwm_tb();

    reg         clk     ;
    reg  [31:0] wr_data ;
    reg         wr_n    ;
    reg         addr    ;
    reg         clr_n   ;
    wire [31:0] rd_data ;
    wire [7:0]  pwm_out ;
    
    avalon_pwm U_avalon_pwm_0(
        .clk(clk),
        .wr_data(wr_data),
        .wr_n(wr_n),
        .addr(addr),
        .clr_n(clr_n),
        .rd_data(rd_data),
        .pwm_out(pwm_out)
    );
    
    initial begin
    ~

 

initial begin 구문 아래로 reg 변수들에 대해서 하드 코딩을 하는데, avalon_pwm의 동작에 대한 이해가 선행되어야 한다.

 

module avalon_pwm (

    input         clk     ,
    input  [31:0] wr_data ,
    input         wr_n    ,
    input         addr    ,
    input         clr_n   ,
    output [31:0] rd_data ,
    output [7:0]  pwm_out  
	
);

    reg    [7:0]  div3,  div2,  div1,  div0  ;
    reg    [7:0]  duty3, duty2, duty1, duty0 ;
    reg    [31:0] counter;
    reg           off;
    reg    [31:0] rd_data;
    
    wire          div_en3 , div_en2 , div_en1 , div_en0  ;
    wire          duty_en3, duty_en2, duty_en1, duty_en0 ;

 

CPU 8 Bit  →  reg [7:0] div, reg [7:0] duty

wr_data 32 Bit  →  div3 ~ 0 & duty3 ~ 0

 

div로 period를 결정하고, duty로 duty ratio를 결정한다.

 

    always @(posedge clk or negedge clr_n)
    begin
        if (clr_n == 0)
        begin
            div3  <= 8'h 00;
            div2  <= 8'h 00;
            div1  <= 8'h 00;
            div0  <= 8'h 00;
            duty3 <= 8'h 00;
            duty2 <= 8'h 00;
            duty1 <= 8'h 00;
            duty0 <= 8'h 00;
        end
        else
        begin
            if (div_en3)
                div3 <= wr_data[31:24];
            else
                div3 <= div3;
            
            if (div_en2)
                div2 <= wr_data[23:16];
            else
                div2 <= div2;
            
            if (div_en1)
                div1 <= wr_data[15:8];
            else
                div1 <= div1;
            
            if (div_en0)
                div0 <= wr_data[7:0];
            else
                div0 <= div0;
            
            if (duty_en3)
                duty3 <= wr_data[31:24];
            else
                duty3 <= duty3;
            
            if (duty_en2)
                duty2 <= wr_data[23:16];
            else
                duty2 <= duty2;
            
            if (duty_en1)
                duty1 <= wr_data[15:8];
            else
                duty1 <= duty1;
            
            if (duty_en0)
                duty0 <= wr_data[7:0];
            else
                duty0 <= duty0;
        end		
    end
    
    assign div_en3  = !wr_n & !addr ;
    assign div_en2  = !wr_n & !addr ;
    assign div_en1  = !wr_n & !addr ;
    assign div_en0  = !wr_n & !addr ;
    assign duty_en3 = !wr_n & addr  ;
    assign duty_en2 = !wr_n & addr  ;
    assign duty_en1 = !wr_n & addr  ;
    assign duty_en0 = !wr_n & addr  ;

 

wr_n 와 addr 신호의 값에 따라 div, duty 변수에 wr_data 값의 할당할지 아니면 변수 값을 유지할지 결정한다.

 

Table 1. [1] [2] [3] [4]
wr_n 0 0 1 1
addr 0 1 0 1
div_ena 1 0 0 0
duty_ena 0 1 0 0
Action div ← wr_data
duty ← duty
div ← div
duty ← wr_data
div ← div
duty ← duty
div ← div
duty ← duty

 

[1] 임의의 wr_data(1)를 div에 할당하여 period를 결정한다. 

[2] period는 이전의 wr_data(1) 값을 유지하고 새로운 wr_data(2) 값을 duty ratio로 할당한다. 

[3], [4] period는 wr_data(1), duty ratio는 wr_data(2) 를 유지한다. 

 

    always @(posedge clk or negedge clr_n)
    begin
        if (clr_n == 0)
            counter <= 0;
        else
            if (counter >= {div3, div2, div1, div0})
                counter <= 0;
            else	
                counter <= counter + 1;
    end
	
    always @(posedge clk or negedge clr_n)
    begin
        if (clr_n == 0)
            off <= 0;
        else
            if (counter > {duty3, duty2, duty1, duty0})
                off <= 1;
            else
                if (counter == 0)
                    off <= 0;
                else
                    off <= off;
    end

 

period(div) : wrdata(1) 만큼의 Count가 한 주기가 되므로 그 이상으로 count가 올라가면 0으로 초기화한다.

duty ratio(duty) : wrdata(2)가 wrdata(1) 보다 작으면 1을, 크면 0을 출력하여 한 주기 내 1과 0신호를 확인한다. 

 

   initial begin 
       clr_n = 0;
   #60 clr_n = 1;
   end 

   initial begin
       wr_n = 1; 
   #60 wr_n = 0;
   #60 wr_n = 1;
   end

   initial begin
       addr = 1;
   #30 addr = 0;
   #50 addr = 1;
   end

   initial begin
      clk = 1;
      forever #10 clk = ~clk;
   end
	
   initial begin
       wr_data = 32'hf;
   #75 wr_data = 32'h3;
   end

wr_data = 32'hf  →  div0, duty0

 

60ns : Table.1의 [3], [4]에 해당한다. 하지만 그게 아니더라도 clr_n = 0이기 때문에 div0 = 0, duty = 0

 

80ns : Table.1의 [1]에 해당한다. div0에 32'hf(15)를 할당하고 duty0은 이전 값(0)을 유지한다. 

→ Period : 0 ~ 15

 

120ns : Table.1의 [2]에 해당한다. div0는 이전 값(15)를 유지하고 duty0에 32'h3(3)을 할당한다. 

→ Period : 0 ~ 15, Duty ratio : 4

   if (counter >= {duty3,duty2, duty1, duty0}) // 3 count after 1 cycle
// if (counter > {duty3, duty2, duty1, duty0}) // 4 count 
// different simulation waveform : overlaps with line 103, counter == 0
      off <= 1;
   else
      if (counter == 0)
         off <= 0;
      else
         off <= off;

120ns ~ 2000ns : Table.1의 [4]에 해당한다. div0, duty0 모두 이전 값(15, 3)을 계속 유지한다. 

15,3

16 Count를 한 주기라고 할 때, 3 Count 동안만 1 로 동작하는 waveform이 pwm_out으로 출력되었고, Simulation 결과를 통해 PWM 제어가 제대로 이뤄졌음을 확인할 수 있다.

15,8

 

vlib work
vlog avalon_pwm.v avalon_pwm_tb.v
vsim work.avalon_pwm_tb
view wave 
add wave -radix unsigned /clk 
add wave -radix unsigned /clr_n
add wave -radix unsigned /wr_data
add wave -radix bin /wr_n
add wave -radix bin /addr
add wave -radix unsigned /U_avalon_pwm_0/div0
add wave -radix unsigned /U_avalon_pwm_0/duty0
add wave -radix unsigned /U_avalon_pwm_0/counter
add wave -radix unsigned /rd_data
add wave -radix unsigned /pwm_out
run 2000ns