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.

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

`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)을 계속 유지한다.

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

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
'[Harman] 반도체 설계 > Quartus' 카테고리의 다른 글
Verilog Projects. (0) | 2023.11.12 |
---|---|
Quartus II project - Nios II (0) | 2023.09.14 |
Quartus II project - Verilog HDL Design pptx. (3) | 2023.08.27 |
Quartus II project - Uart Rx Segment [2]. (0) | 2023.08.19 |
Quartus II project - Uart Rx Segment [1]. (0) | 2023.08.17 |