blob: 3c01e37cd15cd00f1d89783fe944e8e61fe3df50 [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: CDC for PWM
module pwm_cdc #(
parameter int NOutputs = 6
) (
input clk_core_i,
input rst_core_ni,
input pwm_reg_pkg::pwm_reg2hw_t reg2hw,
output pwm_reg_pkg::pwm_reg2hw_t reg2hw_sync,
output logic clr_phase_cntr,
output logic [NOutputs-1:0] clr_blink_cntr
);
wire [31:0] common_sync_in = {reg2hw.cfg.clk_div.q,
reg2hw.cfg.dc_resn.q,
reg2hw.cfg.cntr_en.q};
wire [31:0] common_sync_out;
assign {reg2hw_sync.cfg.clk_div.q,
reg2hw_sync.cfg.dc_resn.q,
reg2hw_sync.cfg.cntr_en.q} = common_sync_out;
// Regen field does not need syncing, but assign it a value for completeness.
assign reg2hw_sync.regen.q = 1'b0;
reg [31:0] common_sync_q;
prim_flop_2sync #(
.Width(32),
.ResetValue(32'h0)
) u_common_sync1 (
.clk_i (clk_core_i),
.rst_ni (rst_core_ni),
.d_i (common_sync_in),
.q_o (common_sync_out)
);
always_ff @(posedge clk_core_i or negedge rst_core_ni) begin
if (!rst_core_ni) begin
common_sync_q <= 32'h0;
end else begin
common_sync_q <= common_sync_out;
end
end
// Reset internal counters whenever parameters change. Though this may cause a single clock
// nondeterministic delay as the buses wind though the CDC, this does not matter, particularly
// since all channels will experience the same delay.
assign clr_phase_cntr = (common_sync_q != common_sync_out);
for (genvar ii = 0; ii < NOutputs; ii++) begin : gen_chan_cdc
wire [83:0] chan_sync_in = {reg2hw.pwm_en[ii].q,
reg2hw.invert[ii].q,
reg2hw.pwm_param[ii].phase_delay.q,
reg2hw.pwm_param[ii].htbt_en.q,
reg2hw.pwm_param[ii].blink_en.q,
reg2hw.duty_cycle[ii].a.q,
reg2hw.duty_cycle[ii].b.q,
reg2hw.blink_param[ii].x.q,
reg2hw.blink_param[ii].y.q};
wire [83:0] chan_sync_out;
assign {reg2hw_sync.pwm_en[ii].q,
reg2hw_sync.invert[ii].q,
reg2hw_sync.pwm_param[ii].phase_delay.q,
reg2hw_sync.pwm_param[ii].htbt_en.q,
reg2hw_sync.pwm_param[ii].blink_en.q,
reg2hw_sync.duty_cycle[ii].a.q,
reg2hw_sync.duty_cycle[ii].b.q,
reg2hw_sync.blink_param[ii].x.q,
reg2hw_sync.blink_param[ii].y.q} = chan_sync_out;
reg [83:0] chan_sync_q;
prim_flop_2sync #(
.Width(84),
.ResetValue(84'h0)
) u_common_sync2 (
.clk_i (clk_core_i),
.rst_ni (rst_core_ni),
.d_i (chan_sync_in),
.q_o (chan_sync_out)
);
always_ff @(posedge clk_core_i or negedge rst_core_ni) begin
if (!rst_core_ni) begin
chan_sync_q <= 84'h0;
end else begin
chan_sync_q <= chan_sync_out;
end
end
// Though it may be a bit overkill, we reset the internal blink counters whenever any channel
// specific parameters change.
assign clr_blink_cntr[ii] = (chan_sync_q != chan_sync_out);
end : gen_chan_cdc
endmodule : pwm_cdc