blob: 909092782ec3486d94a1474a15d3b66c3495f216 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Description: PWM Core Module
module pwm_core #(
parameter int NOutputs = 6,
parameter int PhaseCntDw = 16,
parameter int BeatCntDw = 27
) (
input clk_core_i,
input rst_core_ni,
input pwm_reg_pkg::pwm_reg2hw_t reg2hw,
output logic [NOutputs-1:0] pwm_o
);
// Reset internal counters whenever parameters change.
logic clr_phase_cntr;
logic [NOutputs-1:0] clr_blink_cntr;
assign clr_phase_cntr = reg2hw.cfg.clk_div.qe | reg2hw.cfg.dc_resn.qe | reg2hw.cfg.cntr_en.qe;
for (genvar ii = 0; ii < NOutputs; ii++) begin : gen_chan_clr
// Though it may be a bit overkill, we reset the internal blink counters whenever any channel
// specific parameters change.
assign clr_blink_cntr[ii] = reg2hw.pwm_en[ii].qe | reg2hw.invert[ii].qe |
reg2hw.pwm_param[ii].phase_delay.qe |
reg2hw.pwm_param[ii].htbt_en.qe |
reg2hw.pwm_param[ii].blink_en.qe |
reg2hw.duty_cycle[ii].a.qe |
reg2hw.duty_cycle[ii].b.qe |
reg2hw.blink_param[ii].x.qe |
reg2hw.blink_param[ii].y.qe;
end : gen_chan_clr
//
// Beat and phase counters (in core clock domain)
//
logic cntr_en;
logic [BeatCntDw-1:0] clk_div;
logic [3:0] dc_resn;
logic [3:0] lshift;
logic [BeatCntDw-1:0] beat_ctr_q;
logic [BeatCntDw-1:0] beat_ctr_d;
logic beat_ctr_en;
logic beat_end;
logic [PhaseCntDw-1:0] phase_ctr_q;
logic [PhaseCntDw-1:0] phase_ctr_d;
logic [PhaseCntDw-1:0] phase_ctr_incr;
logic [PhaseCntDw-1:0] phase_ctr_next;
logic phase_ctr_overflow;
logic phase_ctr_en;
logic cycle_end;
assign cntr_en = reg2hw.cfg.cntr_en.q;
assign dc_resn = reg2hw.cfg.dc_resn.q;
assign clk_div = reg2hw.cfg.clk_div.q;
assign beat_ctr_d = (clr_phase_cntr) ? '0 :
(beat_ctr_q == clk_div) ? '0 : (beat_ctr_q + 1'b1);
assign beat_ctr_en = clr_phase_cntr | cntr_en;
assign beat_end = (beat_ctr_q == clk_div);
always_ff @(posedge clk_core_i or negedge rst_core_ni) begin
if (!rst_core_ni) begin
beat_ctr_q <= '0;
end else begin
beat_ctr_q <= beat_ctr_en ? beat_ctr_d : beat_ctr_q;
end
end
// Only update phase_ctr at the end of each beat
// Exception: allow reset to zero whenever not enabled
assign lshift = 4'd15 - dc_resn;
assign phase_ctr_en = beat_end & (clr_phase_cntr | cntr_en);
assign phase_ctr_incr = (PhaseCntDw)'('h1) << lshift;
assign {phase_ctr_overflow, phase_ctr_next} = phase_ctr_q + phase_ctr_incr;
assign phase_ctr_d = clr_phase_cntr ? '0 : phase_ctr_next;
assign cycle_end = beat_end & phase_ctr_overflow;
always_ff @(posedge clk_core_i or negedge rst_core_ni) begin
if (!rst_core_ni) begin
phase_ctr_q <= '0;
end else begin
phase_ctr_q <= phase_ctr_en ? phase_ctr_d : phase_ctr_q;
end
end
for (genvar ii = 0; ii < NOutputs; ii++) begin : gen_chan_insts
//
// PWM Channel Instantiation
//
pwm_chan #(.CntDw(PhaseCntDw)) u_chan (
.clk_i (clk_core_i),
.rst_ni (rst_core_ni),
.pwm_en_i (reg2hw.pwm_en[ii].q),
.invert_i (reg2hw.invert[ii].q),
.phase_delay_i (reg2hw.pwm_param[ii].phase_delay.q),
.blink_en_i (reg2hw.pwm_param[ii].blink_en.q),
.htbt_en_i (reg2hw.pwm_param[ii].htbt_en.q),
.duty_cycle_a_i (reg2hw.duty_cycle[ii].a.q),
.duty_cycle_b_i (reg2hw.duty_cycle[ii].b.q),
.blink_param_x_i (reg2hw.blink_param[ii].x.q),
.blink_param_y_i (reg2hw.blink_param[ii].y.q),
.phase_ctr_i (phase_ctr_q),
.clr_blink_cntr_i (clr_blink_cntr[ii]),
.cycle_end_i (cycle_end),
.dc_resn_i (dc_resn),
.pwm_o (pwm_o[ii])
);
end : gen_chan_insts
// unused register configuration
logic unused_reg;
assign unused_reg = ^reg2hw.alert_test;
endmodule : pwm_core