blob: 44078a06e68fb47553ee3886f39699b68803cdef [file] [log] [blame]
/*
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(
//tlul interface
input clk_i,
input rst_ni,
input re_i,
input we_i,
input [7:0] addr_i,
input [31:0] wdata_i,
input [3:0] be_i,
output [31:0] rdata_o,
//output error_o,
input i_extclk,
input [15:0] i_DC,
input i_valid_DC,
output o_pwm,
output o_pwm_2,
output reg oe_pwm1,
output reg oe_pwm2
);
////////////////////control logic////////////////////////////
parameter adr_ctrl_1 = 0,
adr_divisor_1= 4,
adr_period_1 = 8,
adr_DC_1 = 12;
parameter adr_ctrl_2 = 16,
adr_divisor_2= 20,
adr_period_2 = 24,
adr_DC_2 = 28;
reg [7:0] ctrl;
reg [15:0] period;
reg [15:0] DC;
reg [15:0] divisor;
reg [7:0] ctrl_2;
reg [15:0] period_2;
reg [15:0] DC_2;
reg [15:0] divisor_2;
wire write;
assign write = we_i & ~re_i;
always@(posedge clk_i)
if(~rst_ni)begin
ctrl[4:2] <= 0;
ctrl[0] <= 0;
ctrl[7:6] <= 0;
DC <= 0;
period <= 0;
divisor <= 0;
ctrl_2[4:2] <= 0;
ctrl_2[0] <= 0;
ctrl_2[7:6] <= 0;
DC_2 <= 0;
period_2 <= 0;
divisor_2 <= 0;
end
else if(write)begin
case(addr_i)
adr_ctrl_1:begin
ctrl[0] <= wdata_i[0];
ctrl[4:2] <= wdata_i[4:2];
ctrl[7:6] <= wdata_i[7:6];
end
adr_ctrl_2:begin
ctrl_2[0] <= wdata_i[0];
ctrl_2[4:2] <= wdata_i[4:2];
ctrl_2[7:6] <= wdata_i[7:6];
end
adr_divisor_1 : divisor <= wdata_i[15:0];
adr_period_1 : period <= wdata_i[15:0];
adr_DC_1 : DC <= wdata_i[15:0];
adr_divisor_2 : divisor_2 <= wdata_i[15:0];
adr_period_2 : period_2 <= wdata_i[15:0];
adr_DC_2 : DC_2 <= wdata_i[15:0];
endcase
end
wire pwm;
always @(posedge clk_i) begin
ctrl[1] <= 1'b1;
end
assign pwm = ctrl[1];
wire pwm_1;
always @(posedge clk_i) begin
ctrl_2[1] <= 1'b1;
end
assign pwm_1 = ctrl_2[1];
wire eclk_2,oclk_2;
///////////////////////////////////////////////////////////
//////down clocking for pwm///////////////////
wire clk_source;
wire eclk,oclk;
assign clk_source = clk_i;
down_clocking_even clock_div_ev(
.clk_i (clk_source) ,
.rst_ni (rst_ni),
.i_divisor ({1'b0,divisor[15:1]}),
.o_clk (eclk)
);
down_clocking_odd clock_div_od(
.clk_i (clk_source),
.rst_ni (rst_ni),
.i_divisor ({1'b0,divisor[15:1]}),
.o_clk (oclk)
);
wire clk;
assign clk = divisor[0]? oclk: eclk;
down_clocking_even clock_div_ev_2(
.clk_i (clk_source) ,
.rst_ni (rst_ni),
.i_divisor ({1'b0,divisor_2[15:1]}),
.o_clk (eclk_2)
);
down_clocking_odd clock_div_od_2(
.clk_i (clk_source),
.rst_ni (rst_ni),
.i_divisor ({1'b0,divisor_2[15:1]}),
.o_clk (oclk_2)
);
wire clk_2;
assign clk_2 = divisor_2[0]? oclk_2: eclk_2;
///////////////////////////////////////////////////////
/////////////////main counter //////////////////////////
reg [15:0] ct;
reg pts; //PWM 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);
wire rst_ct;
assign rst_ct = ~rst_ni|ctrl[7];
reg [15:0] ct_2;
reg pts_2; //PWM signal
reg [15:0] extDC_2;
wire [15:0] DCw_2;
assign DCw_2 = ctrl_2[6]? extDC_2: DC_2; //external or internal duty cycle toggle
wire [15:0] period_P2;
assign period_P2 = (period_2==0)? 0: (period_2-1);
wire rst_ct_2;
assign rst_ct_2 = ~rst_ni|ctrl_2[7];
always@(posedge clk )
if(rst_ct)begin
pts <= 0;
ct <= 0;
extDC <= 0;
end
else begin
if(i_valid_DC) extDC <= i_DC;
if(ctrl[2])begin
if(pwm) begin
oe_pwm1 <= 1'b1;
if(ct >= period_1) ct <= 0;
else ct <= ct+1;
if(ct < DC_1) pts <= 1'b1;
else pts <= 1'b0;
end
end
else begin
pts <= 1'b0;
ct <= 0;
oe_pwm1 <= 0;
end
end
always@(posedge clk_2 )
if(rst_ct_2)begin
pts_2 <= 0;
ct_2 <= 0;
extDC_2 <= 0;
end
else begin
if(i_valid_DC) extDC_2 <= i_DC;
if(ctrl_2[2])begin
if(pwm_1) begin
oe_pwm2 <= 1'b1;
if(ct_2 >= period_P2) ct_2 <= 0;
else ct_2 <= ct_2+1;
if(ct_2 < DCw_2) pts_2 <= 1'b1;
else pts_2 <= 1'b0;
end
end
else begin
pts_2 <= 1'b0;
ct_2 <= 0;
oe_pwm2 <= 1'b0;
end
end
//////////////////////////////////////////////////////////
assign o_pwm = ctrl[4]? pts: 0;
assign o_pwm_2 = ctrl_2[4]? pts_2: 0;
assign rdata_o = (addr_i == adr_ctrl_1) ? {8'h0,ctrl} :
(addr_i == adr_divisor_1)? divisor :
(addr_i == adr_period_1) ? period :
(addr_i == adr_DC_1) ? DC :
(addr_i == adr_DC_2) ? DC_2 :
(addr_i == adr_period_2) ? period_2 :
(addr_i == adr_divisor_2)? divisor_2 :
(addr_i == adr_ctrl_2) ? {8'h0,ctrl_2}:0;
endmodule