blob: 922c3695ac06f5cd05556d6b72b5e0015d31995f [file] [log] [blame]
//-------------------------------------------------------------------
// PWM waveform period: 1000/((cfg_pwm_high+1) + (cfg_pwm_low+1))
// For 1 Second with Duty cycle 50 = 1000/((499+1) + (499+1))
// For 1 Second with 1ms On and 999ms Off = 1000/((0+1) + (998+1))
// Timing Run's with 1 Milisecond pulse
//-------------------------------------------------------------------
module pwm(
input logic h_reset_n ,
input logic mclk ,
output logic pwm_wfm_o ,
output logic pwm_os_done ,
output logic pwm_ovflow_pe ,
output logic gpio_tgr ,
input logic [7:0] pad_gpio ,
input logic cfg_pwm_enb , // pwm operation enable
input logic cfg_pwm_run , // pwm operation enable
input logic [3:0] cfg_pwm_scale , // pwm clock scaling
input logic cfg_pwm_oneshot , // pwm OneShot mode
input logic cfg_pwm_frun , // pwm is free running
input logic cfg_pwm_gpio_enb , // pwm gpio based trigger
input logic cfg_pwm_gpio_edge , // pwm gpio based trigger edge
input logic [2:0] cfg_pwm_gpio_sel , // gpio Selection
input logic cfg_pwm_hold , // Hold data pwm data During pwm Disable
input logic cfg_pwm_inv , // invert output
input logic cfg_pwm_zeropd , // Reset on pmw_cnt match to period
input logic [1:0] cfg_pwm_mode , // pwm Pulse Generation mode
input logic cfg_comp0_center , // Compare cnt at comp0 center
input logic cfg_comp1_center , // Compare cnt at comp1 center
input logic cfg_comp2_center , // Compare cnt at comp2 center
input logic cfg_comp3_center , // Compare cnt at comp3 center
input logic [15:0] cfg_pwm_period , // pwm period
input logic [15:0] cfg_pwm_comp0 , // compare0
input logic [15:0] cfg_pwm_comp1 , // compare1
input logic [15:0] cfg_pwm_comp2 , // compare2
input logic [15:0] cfg_pwm_comp3 // compare3
);
logic [14:0] pwm_scnt ; // PWM Scaling counter
logic [15:0] pwm_cnt ; // PWM counter
logic cnt_trg ;
logic pwm_wfm_i ;
logic pwm_wfm_hold;
logic comp0_match ;
logic comp1_match ;
logic comp2_match ;
logic comp3_match ;
//--------------------------------
// Counter Scaling
// In GPIO mode, wait for first GPIO transition
//--------------------------------
always @(posedge mclk or negedge h_reset_n)
begin
if ( ~h_reset_n ) begin
pwm_scnt <= 15'h0;
end else begin
//-------------------------------------------------------
// Added additional case to handle when new gpio trigger
// generated before completing the current Run
//-------------------------------------------------------
if(cfg_pwm_enb && cfg_pwm_run && !gpio_tgr) begin
pwm_scnt <= pwm_scnt + 1;
end else begin
pwm_scnt <= 15'h0;
end
end
end
//-----------------------------------------------------------------------------
// pwm_scaling used to decide on the trigger event for the pwm_cnt
// 0 ==> pwm_cnt increment every system cycle
// 1 ==> 2^0 => pmw_cnt increase once in two system cycle
// 15 ==>a 2^15 => pwm_cnt increase once in 32768 system cycle
//-----------------------------------------------------------------------------
always_comb
begin
cnt_trg = 0;
case(cfg_pwm_scale)
4'b0000: cnt_trg = 1;
4'b0001: cnt_trg = pwm_scnt[0];
4'b0010: cnt_trg = &pwm_scnt[1:0];
4'b0011: cnt_trg = &pwm_scnt[2:0];
4'b0100: cnt_trg = &pwm_scnt[3:0];
4'b0101: cnt_trg = &pwm_scnt[4:0];
4'b0110: cnt_trg = &pwm_scnt[5:0];
4'b0111: cnt_trg = &pwm_scnt[6:0];
4'b1000: cnt_trg = &pwm_scnt[7:0];
4'b1001: cnt_trg = &pwm_scnt[8:0];
4'b1010: cnt_trg = &pwm_scnt[9:0];
4'b1011: cnt_trg = &pwm_scnt[10:0];
4'b1100: cnt_trg = &pwm_scnt[11:0];
4'b1101: cnt_trg = &pwm_scnt[12:0];
4'b1110: cnt_trg = &pwm_scnt[13:0];
4'b1111: cnt_trg = &pwm_scnt[14:0];
default: cnt_trg = 0;
endcase
end
//----------------------------------------------------------
//Counter Overflow condition
// 1. At Roll Over
// 2. If compare on period enable, then at period
//----------------------------------------------------------
logic pwm_ovflow_l;
wire pwm_ovflow = ((&pwm_cnt) | (cfg_pwm_zeropd && (pwm_cnt == cfg_pwm_period))) & cfg_pwm_enb;
// overflow single cycle pos edge pulse
assign pwm_ovflow_pe = (!pwm_ovflow_l & pwm_ovflow);
// Don't generate PWM done at exact clash at gpio trigger, higer priority to gpio trigger
assign pwm_os_done = (cfg_pwm_oneshot && !gpio_tgr) ? pwm_ovflow_pe : 1'b0;
always @(posedge mclk or negedge h_reset_n)
begin
if ( ~h_reset_n ) begin
pwm_cnt <= 16'h0;
pwm_ovflow_l <= 1'b0;
end else begin
pwm_ovflow_l <= pwm_ovflow;
//-------------------------------------------------------
// Added additional case to handle when new gpio trigger
// generated before completing the current Run
//-------------------------------------------------------
if(cfg_pwm_enb && cfg_pwm_run && !gpio_tgr) begin
if(cnt_trg) begin
if(pwm_ovflow) begin
pwm_cnt <= 'h0;
end else begin
pwm_cnt <= pwm_cnt + 1;
end
end
end else begin
pwm_cnt <= 16'h0;
end
end
end
//-----------------------------
// compare-0 match logic generation
//------------------------------
always_comb begin
comp0_match = 0;
if(cfg_comp0_center)begin
comp0_match = (({16{pwm_cnt[15]}} ^ pwm_cnt) >= cfg_pwm_comp0);
end else begin
comp0_match = (pwm_cnt >= cfg_pwm_comp0);
end
end
//-----------------------------
// compare-1 match logic generation
//------------------------------
always_comb begin
comp1_match = 0;
if(cfg_comp1_center)begin
comp1_match = (({16{pwm_cnt[15]}}^ pwm_cnt) >= cfg_pwm_comp1);
end else begin
comp1_match = (pwm_cnt >= cfg_pwm_comp1);
end
end
//-----------------------------
// compare-2 match logic generation
//------------------------------
always_comb begin
comp2_match = 0;
if(cfg_comp2_center)begin
comp2_match = (({16{pwm_cnt[15]}} ^ pwm_cnt) >= cfg_pwm_comp2);
end else begin
comp2_match = (pwm_cnt >= cfg_pwm_comp2);
end
end
//-----------------------------
// compare-3 match logic generation
//------------------------------
always_comb begin
comp3_match = 0;
if(cfg_comp3_center) begin
comp3_match = (({16{pwm_cnt[15]}} ^ pwm_cnt) >= cfg_pwm_comp3);
end else begin
comp3_match = (pwm_cnt >= cfg_pwm_comp3);
end
end
//---------------------------------------------
// Consolidated pwm waform generation
// based on pwm mode
//---------------------------------------------
always_comb begin
pwm_wfm_i = 0;
case(cfg_pwm_mode)
2'b00: pwm_wfm_i = comp0_match;
2'b01: pwm_wfm_i = comp0_match ^ comp1_match;
2'b10: pwm_wfm_i = comp0_match ^ comp1_match ^ comp2_match;
2'b11: pwm_wfm_i = comp0_match ^ comp1_match ^ comp2_match ^ comp3_match;
default: pwm_wfm_i=0;
endcase
end
//-----------------------------------------------
// Holding the pwm waveform in active region
//------------------------------------------------
always @(posedge mclk or negedge h_reset_n)
begin
if ( ~h_reset_n ) begin
pwm_wfm_hold <= 1'b0;
end else if(cfg_pwm_enb) begin
pwm_wfm_hold <= pwm_wfm_i;
end
end
//--------------------------------------------
// Final Waveform output generation based
// on pwm_hold and pwm_inv combination
//--------------------------------------------
always_comb begin
pwm_wfm_o = 0;
if(!cfg_pwm_enb && cfg_pwm_hold) begin
if(cfg_pwm_inv) pwm_wfm_o = !pwm_wfm_hold;
else pwm_wfm_o = pwm_wfm_hold;
end else begin
if(cfg_pwm_inv) pwm_wfm_o = !pwm_wfm_i;
else pwm_wfm_o = pwm_wfm_i;
end
end
//----------------------------------------
// GPIO Trigger Generation
//----------------------------------------
logic gpio_l;
wire gpio = pad_gpio[cfg_pwm_gpio_sel];
// GPIO Pos and Neg Edge Selection
wire gpio_pe = (gpio & !gpio_l);
wire gpio_ne = (!gpio & gpio_l);
always @(posedge mclk or negedge h_reset_n)
begin
if ( ~h_reset_n ) begin
gpio_l <= 1'b0;
gpio_tgr <= 1'b0;
end else begin
gpio_l <= gpio;
if(cfg_pwm_enb && cfg_pwm_gpio_enb) begin
gpio_l <= gpio;
if(cfg_pwm_gpio_edge) begin
gpio_tgr <= gpio_ne;
end else begin
gpio_tgr <= gpio_pe;
end
end else begin
gpio_l <= 1'b0;
gpio_tgr <= 1'b0;
end
end
end
endmodule