|  | // Copyright lowRISC contributors. | 
|  | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | module pwm_chan #( | 
|  | parameter int CntDw = 16 | 
|  | ) ( | 
|  | input        clk_i, | 
|  | input        rst_ni, | 
|  |  | 
|  | input        pwm_en_i, | 
|  | input        invert_i, | 
|  | input        blink_en_i, | 
|  | input        htbt_en_i, | 
|  | input [15:0] phase_delay_i, | 
|  | input [15:0] duty_cycle_a_i, | 
|  | input [15:0] duty_cycle_b_i, | 
|  | input [15:0] blink_param_x_i, | 
|  | input [15:0] blink_param_y_i, | 
|  |  | 
|  | input [15:0] phase_ctr_i, | 
|  | input        cycle_end_i, | 
|  | input        clr_blink_cntr_i, | 
|  | input [3:0]  dc_resn_i, | 
|  |  | 
|  | output logic pwm_o | 
|  | ); | 
|  |  | 
|  | logic [15:0] duty_cycle_actual; | 
|  | logic [15:0] on_phase; | 
|  | logic [15:0] off_phase; | 
|  | logic        phase_wrap; | 
|  | logic        pwm_int; | 
|  |  | 
|  | // Standard blink mode | 
|  | logic [CntDw-1:0] blink_ctr_q; | 
|  | logic [CntDw-1:0] blink_ctr_d; | 
|  | logic [CntDw-1:0] duty_cycle_blink; | 
|  |  | 
|  | logic unused_sum; | 
|  | logic [CntDw-1:0] blink_sum; | 
|  | assign {unused_sum, blink_sum} = blink_param_x_i + blink_param_y_i + 1'b1; | 
|  | assign blink_ctr_d = (!(blink_en_i && !htbt_en_i) || clr_blink_cntr_i) ? '0 : | 
|  | ((blink_ctr_q == blink_sum[CntDw-1:0]) && cycle_end_i) | 
|  | ? '0 : (cycle_end_i) ? blink_ctr_q + 1'b1 : blink_ctr_q; | 
|  |  | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | blink_ctr_q <= '0; | 
|  | end else begin | 
|  | if (clr_blink_cntr_i) begin | 
|  | blink_ctr_q <= '0; | 
|  | end else begin | 
|  | blink_ctr_q <= (blink_en_i && !htbt_en_i) ? blink_ctr_d : blink_ctr_q; | 
|  | end | 
|  | end | 
|  | end | 
|  |  | 
|  | assign duty_cycle_blink = (blink_en_i && !htbt_en_i && (blink_ctr_q > blink_param_x_i)) ? | 
|  | duty_cycle_b_i : duty_cycle_a_i; | 
|  |  | 
|  | // Heartbeat mode | 
|  | logic [CntDw-1:0] htbt_ctr_q; | 
|  | logic [CntDw-1:0] htbt_ctr_d; | 
|  | logic [CntDw-1:0] duty_cycle_htbt; | 
|  | logic [CntDw-1:0] dc_htbt_d; | 
|  | logic [CntDw-1:0] dc_htbt_q; | 
|  | logic dc_htbt_end; | 
|  |  | 
|  | assign htbt_ctr_d = (!(blink_en_i && htbt_en_i) || clr_blink_cntr_i) ? '0 : | 
|  | ((htbt_ctr_q == blink_param_x_i) && cycle_end_i) ? '0 : | 
|  | (cycle_end_i) ? (htbt_ctr_q + 1'b1) : htbt_ctr_q; | 
|  |  | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | htbt_ctr_q <= '0; | 
|  | end else begin | 
|  | if (clr_blink_cntr_i) begin | 
|  | htbt_ctr_q <= '0; | 
|  | end else begin | 
|  | htbt_ctr_q <= (blink_en_i && htbt_en_i) ? htbt_ctr_d : htbt_ctr_q; | 
|  | end | 
|  | end | 
|  | end | 
|  | assign dc_htbt_end = cycle_end_i & (htbt_ctr_q == blink_param_x_i); | 
|  |  | 
|  | logic htbt_direction; | 
|  | logic dc_wrap; | 
|  | logic pos_htbt; | 
|  | logic neg_htbt; | 
|  | assign pos_htbt = (duty_cycle_a_i < duty_cycle_b_i); | 
|  | assign neg_htbt = (duty_cycle_a_i > duty_cycle_b_i); | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | htbt_direction <= '0; | 
|  | end else if (pos_htbt && ((dc_htbt_q >= duty_cycle_b_i) || (dc_wrap && dc_htbt_end))) begin | 
|  | htbt_direction <= 1'b1; // duty cycle counts down | 
|  | end else if (pos_htbt && (dc_htbt_q == duty_cycle_a_i) && dc_htbt_end) begin | 
|  | htbt_direction <= 1'b0; // duty cycle counts up | 
|  | end else if (neg_htbt && ((dc_htbt_q <= duty_cycle_b_i) || (dc_wrap && dc_htbt_end))) begin | 
|  | htbt_direction <= 1'b0; // duty cycle counts up | 
|  | end else if (neg_htbt && (dc_htbt_q == duty_cycle_a_i) && dc_htbt_end) begin | 
|  | htbt_direction <= 1'b1; // duty cycle counts down | 
|  | end else begin | 
|  | htbt_direction <= htbt_direction; | 
|  | end | 
|  | end | 
|  |  | 
|  | logic pattern_repeat; | 
|  | assign pattern_repeat = (pos_htbt & htbt_direction) | (neg_htbt & ~htbt_direction) | | 
|  | (~pos_htbt & ~neg_htbt); | 
|  | localparam int CntExtDw = CntDw + 1; | 
|  | assign {dc_wrap, dc_htbt_d} = !(htbt_ctr_q == blink_param_x_i) ? (CntExtDw)'(dc_htbt_q) : | 
|  | ((dc_htbt_q == duty_cycle_a_i) && pattern_repeat) ? | 
|  | (CntExtDw)'(duty_cycle_a_i) : (htbt_direction) ? | 
|  | (CntExtDw)'(dc_htbt_q) - (CntExtDw)'(blink_param_y_i) - 1'b1 : | 
|  | (CntExtDw)'(dc_htbt_q) + (CntExtDw)'(blink_param_y_i) + 1'b1; | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | dc_htbt_q <= '0; | 
|  | end else if (!htbt_en_i && dc_htbt_q != duty_cycle_a_i) begin | 
|  | // the heart beat duty cycle is only changed when the heartbeat is not currently | 
|  | // ticking. | 
|  | dc_htbt_q <= duty_cycle_a_i; | 
|  | end else begin | 
|  | dc_htbt_q <= ((htbt_ctr_q == blink_param_x_i) && cycle_end_i) ? dc_htbt_d : dc_htbt_q; | 
|  | end | 
|  | end | 
|  | assign duty_cycle_htbt = dc_htbt_q; | 
|  |  | 
|  | assign duty_cycle_actual = (blink_en_i && !htbt_en_i) ? duty_cycle_blink : | 
|  | (blink_en_i && htbt_en_i) ? duty_cycle_htbt : duty_cycle_a_i; | 
|  |  | 
|  | logic [30:0] phase_delay_scaled; | 
|  | logic [30:0] duty_cycle_scaled; | 
|  | logic [3:0] lshift; | 
|  | logic unused_shift; | 
|  |  | 
|  | assign lshift = 4'd15 - dc_resn_i; | 
|  | assign phase_delay_scaled = phase_delay_i << lshift; | 
|  | assign duty_cycle_scaled = duty_cycle_actual << lshift; | 
|  | assign unused_shift = ^phase_delay_scaled | ^duty_cycle_scaled; | 
|  |  | 
|  | assign on_phase = phase_delay_scaled[15:0]; | 
|  | assign {phase_wrap, off_phase} = {1'b0, phase_delay_scaled[15:0]} + | 
|  | {1'b0, duty_cycle_scaled[15:0]}; | 
|  |  | 
|  | logic on_phase_exceeded; | 
|  | logic off_phase_exceeded; | 
|  |  | 
|  | assign on_phase_exceeded  = (phase_ctr_i >= on_phase); | 
|  | assign off_phase_exceeded = (phase_ctr_i >= off_phase); | 
|  |  | 
|  | assign pwm_int = !pwm_en_i ? 1'b0 : | 
|  | phase_wrap ? on_phase_exceeded | ~off_phase_exceeded : | 
|  | on_phase_exceeded & ~off_phase_exceeded; | 
|  |  | 
|  | assign pwm_o = invert_i ? ~pwm_int : pwm_int; | 
|  |  | 
|  | endmodule : pwm_chan |