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