/* | |
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 |