blob: 8653ae63a4da1b246ed490af6df0b12207c46b71 [file] [log] [blame]
/*Author: Zhuxu
m99a1@yahoo.cn
Pulse Width Generators/timers with 16-bit main counter.
Period or timers target number is controlled by register [15:0]period.
Duty cycle is controlled by register [15:0]DC.
Clock used for PWM signal generation can be switched between Wishbone Bus clock and external clock. It is down clocked first.
o_pwm outputs PWM signal or timers interrupt.
control register [7:0]ctrl:
bit 0: When set, external clock is chosen for PWM/timer. When cleared, wb clock is used for PWM/timer.
bit 1: When set, PWM is enabled. When cleared, timer is enabled.
bit 2: When set, PWM/timer starts. When cleared, PWM/timer stops.
bit 3: When set, timer runs continuously. When cleared, timer runs one time.
bit 4: When set, o_pwm enabled.
bit 5: timer interrupt bit When it is written with 0, interrupt request is cleared.
bit 6: When set, a 16-bit external signal i_DC is used as duty cycle. When cleared, register DC is used.
bit 7: When set, counter reset for PWM/timer, it's output and bit 5 will also be cleared. When changing from PWM mode to timer mode reset is needed before timer starts.
*/
module PWM (
input i_wb_clk,
input i_wb_rst,
input i_wb_cyc,
input i_wb_stb,
input i_wb_we,
input [15:0]i_wb_adr,
input [15:0]i_wb_data,
output [15:0]o_wb_data,
output o_wb_ack,
input [15:0]i_DC,
input i_valid_DC,
output o_pwm
);
////////////////////control logic////////////////////////////
parameter adr_ctrl=0,
adr_divisor=2,
adr_period=4,
adr_DC=6;
reg [7:0]ctrl;
reg [15:0]period;
reg [15:0]DC;
reg [15:0]divisor;
integer i;
wire write;
wire pwm;
reg [1:0]state;
reg clrint;
wire ack_clrint;
wire int1;
assign write=i_wb_cyc&i_wb_stb&i_wb_we;
assign o_wb_ack=i_wb_stb;
always@(posedge i_wb_clk or posedge i_wb_rst)
if(i_wb_rst) begin
ctrl[4:0]<=0;
ctrl[7:6]<=0;
DC<=0;
period<=0;
divisor<=0;
end
else if(write) begin
case(i_wb_adr)
adr_ctrl:begin
ctrl[4:0]<=i_wb_data[4:0];
ctrl[7:6]<=i_wb_data[7:6];
end
adr_divisor: divisor<=i_wb_data;
adr_period: period<=i_wb_data;
adr_DC: DC<=i_wb_data;
endcase
end
assign pwm = ctrl[1];
always@(posedge i_wb_clk or posedge i_wb_rst)
if(i_wb_rst)begin
ctrl[5]<=0;
state<=0;
clrint<=0;
end
else begin
case(state)
1:begin
if(write)begin
if(i_wb_data[7])begin
ctrl[5]<=0;
state<=0;
end
else if(!i_wb_data[5])begin
ctrl[5]<=0;
if(!pwm)begin
clrint<=1;
state<=2;
end
else state<=0;
end
end
end
2:if(ack_clrint)begin
clrint<=0;
state<=0;
end
default:begin
if(!pwm)ctrl[5]<=int1;
if(int1)state<=1;
end
endcase
end
///////////////////////////////////////////////////////////
//////down clocking for pwm/timer///////////////////
wire clk_source;
wire eclk,oclk;
assign clk_source= i_wb_clk;
down_clocking_even down_clocking_even_0(
clk_source,
(!i_wb_rst),
{1'b0,divisor[15:1]},
eclk
);
down_clocking_odd down_clocking_odd_0(
clk_source,
(!i_wb_rst),
{1'b0,divisor[15:1]},
oclk
);
wire clk;
assign clk=divisor[0]?oclk:eclk;
///////////////////////////////////////////////////////
/////////////////main counter //////////////////////////
reg [15:0]ct;
reg pts; //PWM signal or timer interrupt signal
reg [15:0]extDC;
wire [15:0]DC_1;
assign DC_1=ctrl[6]? extDC:DC; //external or internal duty cycle toggle
wire [15:0]period_1;
assign period_1= (period==0) ? 0:(period-1);
reg switch_ack_clrint;
wire state_timer;
assign state_timer=ctrl[3];
wire rst_ct;
assign rst_ct=i_wb_rst|ctrl[7];
assign int1=pwm?0:pts;
assign ack_clrint=switch_ack_clrint?clrint:0;
always@(posedge clk or posedge rst_ct)
if(rst_ct)begin
pts<=0;
ct<=0;
switch_ack_clrint<=0;
extDC<=0;
end
else begin
if(i_valid_DC)
extDC<=i_DC;
if(switch_ack_clrint&&(!clrint))
switch_ack_clrint<=0;
if(ctrl[2])begin
case(pwm)
1:begin
if(ct>=period_1)
ct<=0;
else
ct<=ct+1;
if(ct<DC_1)
pts<=1;
else
pts<=0;
end
0:begin
case(state_timer)
0:begin
if(clrint)
switch_ack_clrint<=1;
if(ct>=period_1)begin
if(clrint)begin
pts<=0;
ct<=0;
end
else
pts<=1;
end
else
ct<=ct+1;
end
1:begin
if(ct>=period_1)begin
pts<=1;
ct<=0;
end
else begin
if(clrint)begin
switch_ack_clrint<=1;
pts<=0;
end
ct<=ct+1;
end
end
endcase
end
endcase
end
else if(clrint)begin
switch_ack_clrint<=1;
if(!pwm)begin
pts<=0;
ct<=0;
end
end
end
//////////////////////////////////////////////////////////
assign o_pwm=ctrl[4]?pts:0;
assign o_wb_data= i_wb_adr==adr_ctrl?{8'h0,ctrl}:
i_wb_adr==adr_divisor?divisor:
i_wb_adr==adr_period?period:
i_wb_adr==adr_DC?DC:0;
endmodule