프로젝트명 : PWM 생성기
기획 및 제작 : 박순창
분류 : 메인 프로젝트 (개인)
시작/제작 기간 : 23.10.00 ~ 23.10.00 (1일)
진행상태: 스펙 정의 - 기능 검증 - RTL 생성 - 시뮬레이션진행중
- 합성 및 배선
사용 툴:Vivado
,Xcelium
,Genus
,Innovus
사용 도구:FPGA
TIMER 구조
clk: 메인 클럭
50mhz
prescaler: 분주기
클럭을 나누는 역할
빠른 메인 클럭을 내려줄수 있음
50mhz / 1000 = 50khz
50khz마다 한번 카운트
counter mode: 카운터 상승/하강 선택
counter period: 카운터 주기 선택
주기를 1000으로 하면
50khz /1000 = 50hz
50hz마다 카운터가 초기화됨
상한선 같은 의미
카운터가 999가 넘어가면 다시 카운터가 0으로 돌아가고 이 상황을 반복
PWM: 일정 카운터에서 작동
주기를 100%로 잡았을 때
auto-reload preload : PWM 신호를 계속 반복해서 생성하고 싶으면 Enable, 아니면 Disable
내부 구조
memory map
Address : 0000_0100 ~ 32'h0000_0108
1) ADDR_DATA [0000_0100]
2) ADDR_DATA [0000_0101]
3) ADDR_DATA [0000_0102]
4) ADDR_DATA [0000_0103]
5) ADDR_DATA [0000_0104]
6) ADDR_DATA [0000_0105]
7) ADDR_DATA [0000_0106]
8) ADDR_DATA [0000_0107]
9) ADDR_DATA [0000_0108]
reserved (dummy register)
/
ADDR_DATA[32'h0000_0100] : x
ADDR_DATA[32'h0000_0101] : Prescaler register 16bit
ADDR_DATA[32'h0000_0102] : Counter_Period_register 32bit
ADDR_DATA[32'h0000_0103] : CH1~4 mode select
ADDR_DATA[32'h0000_0104] : CH1 Pulse
ADDR_DATA[32'h0000_0105] : CH2 Pulse
ADDR_DATA[32'h0000_0106] : CH3 Pulse
ADDR_DATA[32'h0000_0107] : CH4 Pulse
ADDR_DATA[32'h0000_0108] : x
/
`timescale 1ns/1ps
module TIM_TOP #(parameter BIT_NUM = 32, parameter CHANNEL_NUM = 4) (
input mclk,
input rst,
input [BIT_NUM-1:0] TIM_ADDR,
input [BIT_NUM-1:0] TIM_DATA,
input en,
output reg prescaler_clk,
output reg counter_period_clk,
output wire [3:0] pin_out);
reg [BIT_NUM-1 : 0] ADDR_DATA [32'h0000_0100 : 32'h0000_0108];
wire [15:0] prescaler_val;
wire [31:0] counter_period_val;
wire [15:0] TIM_CH_mode_val;
reg [15:0] prescaler_cnt;
reg [31:0] counter_period_cnt;
wire [31:0] pulse [0:3];
reg [3:0] CH_wire;
wire [3:0] CH_PWM;
wire [3:0] CH_OC;
TIM_CH TIM_CH1 (.cp_clk(counter_period_clk), .rst(rst), .pulse(pulse [0]), .cp_val(counter_period_val),
.pwm_out(CH_PWM[0]), .oc_out(CH_OC[0]));
TIM_CH TIM_CH2 (.cp_clk(counter_period_clk), .rst(rst), .pulse(pulse [1]), .cp_val(counter_period_val),
.pwm_out(CH_PWM[1]), .oc_out(CH_OC[1]));
TIM_CH TIM_CH3 (.cp_clk(counter_period_clk), .rst(rst), .pulse(pulse [2]), .cp_val(counter_period_val),
.pwm_out(CH_PWM[2]), .oc_out(CH_OC[2]));
TIM_CH TIM_CH4 (.cp_clk(counter_period_clk), .rst(rst), .pulse(pulse [3]), .cp_val(counter_period_val),
.pwm_out(CH_PWM[3]), .oc_out(CH_OC[3]));
assign prescaler_val = ADDR_DATA[32'h0000_0101] [15:0];
assign counter_period_val = ADDR_DATA[32'h0000_0102] [31:0];
assign TIM_CH_mode_val = ADDR_DATA[32'h0000_0103] [15:0];
assign pulse [0] [31:0] = ADDR_DATA[32'h0000_0104] [31:0];
assign pulse [1] [31:0] = ADDR_DATA[32'h0000_0105] [31:0];
assign pulse [2] [31:0] = ADDR_DATA[32'h0000_0106] [31:0];
assign pulse [3] [31:0] = ADDR_DATA[32'h0000_0107] [31:0];
assign pin_out = CH_wire;
always @(*) begin
case (TIM_CH_mode_val[3:0])
4'b0001: CH_wire[0] <= CH_PWM[0]; //pwm
4'b0010: CH_wire[0] <= CH_OC[0]; //output compare
default: CH_wire[0] <= 1'bz; //no output
endcase
case (TIM_CH_mode_val[7:4])
4'b0001: CH_wire[1] <= CH_PWM[1]; //pwm
4'b0010: CH_wire[1] <= CH_OC[1]; //output compare
default: CH_wire[1] <= 1'bz; //no output
endcase
case (TIM_CH_mode_val[11:8])
4'b0001: CH_wire[2] <= CH_PWM[2]; //pwm
4'b0010: CH_wire[2] <= CH_OC[2]; //output compare
default: CH_wire[2] <= 1'bz; //no output
endcase
case (TIM_CH_mode_val[15:12])
4'b0001: CH_wire[3] <= CH_PWM[3]; //pwm
4'b0010: CH_wire[3] <= CH_OC[3]; //output compare
default: CH_wire[3] <= 1'bz; //no output
endcase
end
always @(*) if(en) ADDR_DATA[TIM_ADDR] <= TIM_DATA;
always @(posedge mclk or posedge rst) begin
if(rst) begin
prescaler_cnt <= 16'd0;
prescaler_clk <= 1'b0;
end
else begin
if(prescaler_cnt == (prescaler_val/2)-1) begin
prescaler_cnt <= 16'd0;
prescaler_clk <= ~prescaler_clk;
end
else prescaler_cnt <= prescaler_cnt + 1'b1;
end
end
always @(negedge prescaler_clk or posedge rst) begin
if(rst) begin
counter_period_cnt <= 32'd0;
counter_period_clk <= 1'b0;
end
else begin
if(counter_period_cnt == (counter_period_val/2)-1) begin
counter_period_cnt <= 32'd0;
counter_period_clk <= ~counter_period_clk;
end
else begin
counter_period_cnt <= counter_period_cnt + 1'b1;
end
end
end
endmodule
`timescale 1ns/1ps
module TIM_CH (
input cp_clk,
input rst,
input [31:0] pulse,
input [31:0] cp_val,
output reg pwm_out,
output reg oc_out);
reg [31:0] cp_cnt;
always @(posedge cp_clk or posedge rst) begin
if(rst) begin
cp_cnt <= 1'b0;
pwm_out <= 1'b0;
oc_out <= 1'b0;
end
else begin
if(cp_cnt == cp_val-1) begin
cp_cnt <= 1'b0;
pwm_out <= 1'b0;
end
else begin
cp_cnt <= cp_cnt + 1'b1;
if(cp_cnt >= pulse-1) pwm_out <= 1'b1;
if (cp_cnt == pulse-1) oc_out <= 1'b1;
else oc_out <= 1'b0;
end
end
end
endmodule
`timescale 1ns/1ps
module TIM_TOP_tb;
parameter BIT_NUM = 32;
parameter CHANNEL_NUM = 4;
reg mclk, rst;
reg [BIT_NUM-1:0] TIM_ADDR, TIM_DATA;
reg en;
wire prescaler_clk, counter_period_clk;
wire [3:0] pin_out;
TIM_TOP #(.BIT_NUM (BIT_NUM), .CHANNEL_NUM(CHANNEL_NUM))
tim1 (mclk, rst, TIM_ADDR, TIM_DATA, en,
prescaler_clk, counter_period_clk, pin_out);
always #0.5 mclk <= ~mclk;
initial begin
$display("TEST start");
mclk <= 0; rst <= 0;
#10 TIM_ADDR <= 32'h0000_0100; TIM_DATA <= 32'h0000_0000; en <= 1;
#10 en <= 0;
#10 TIM_ADDR <= 32'h0000_0101; TIM_DATA <= 32'd20; en <= 1;
#10 en <= 0;
#10 TIM_ADDR <= 32'h0000_0102; TIM_DATA <= 32'd30; en <= 1;
#10 en <= 0;
#10 TIM_ADDR <= 32'h0000_0103; TIM_DATA <= 32'h0000_1111; en <= 1;
#10 en <= 0;
#10 TIM_ADDR <= 32'h0000_0104; TIM_DATA <= 32'd5; en <= 1;
#10 en <= 0;
#10 TIM_ADDR <= 32'h0000_0105; TIM_DATA <= 32'd10; en <= 1;
#10 en <= 0;
#10 TIM_ADDR <= 32'h0000_0106; TIM_DATA <= 32'd25; en <= 1;
#10 en <= 0;
#10 TIM_ADDR <= 32'h0000_0107; TIM_DATA <= 32'd29; en <= 1;
#10 en <= 0;
#10 TIM_ADDR <= 32'h0000_0108; TIM_DATA <= 32'h0000_0000; en <= 1;
#10 en <= 0; $display("check memory register");
#10 rst <= 1;
#10 rst <= 0;
#300000 $display("%0d, PWM fisnish", $time);
#10 TIM_ADDR <= 32'h0000_0103; TIM_DATA <= 32'h0000_2222; en <= 1;
#10 en <= 0;
#10 rst <= 1;
#10 rst <= 0;
#300000 $display("%0d, OC fisnish", $time);
#100 $display("TEST fisnish");
$finish;
end
endmodule