module control #( parameter int unsigned DAC_DATA_WIDTH = 12 ) ( input logic ctrl_clk, input logic dac_clk_in, input logic adc_clk_in, input logic rst_n, input logic rst_soft, // adc_clk_in domain input logic finish, input logic [127:0] cfg_bus, input logic start, // dac_clk_in domain outputs output logic [31:0] dac_pulse_width, output logic [31:0] dac_pulse_period, output logic [DAC_DATA_WIDTH-1:0] dac_pulse_height, output logic [15:0] dac_pulse_num, // adc_clk_in domain outputs output logic [31:0] adc_pulse_period, output logic [15:0] adc_pulse_num, // pulse outputs output logic dac_start, output logic adc_start, output logic dac_rst, output logic adc_rst ); // static checks initial begin if (DAC_DATA_WIDTH > 16) begin $error("DAC_DATA_WIDTH must be <= 16"); end if (DAC_DATA_WIDTH == 0) begin $error("DAC_DATA_WIDTH must be > 0"); end end // reset synchronizers: async assert, sync deassert in each domain logic rst_ff1, rst_ff2; logic dac_rst_ff1, dac_rst_ff2; logic adc_rst_ff1, adc_rst_ff2; logic ctrl_rst; logic dac_rst_int; logic adc_rst_int; always_ff @(posedge ctrl_clk or negedge rst_n) begin if (!rst_n) begin rst_ff1 <= 1'b1; rst_ff2 <= 1'b1; end else begin rst_ff1 <= 1'b0; rst_ff2 <= rst_ff1; end end always_ff @(posedge dac_clk_in or negedge rst_n) begin if (!rst_n) begin dac_rst_ff1 <= 1'b1; dac_rst_ff2 <= 1'b1; end else begin dac_rst_ff1 <= 1'b0; dac_rst_ff2 <= dac_rst_ff1; end end always_ff @(posedge adc_clk_in or negedge rst_n) begin if (!rst_n) begin adc_rst_ff1 <= 1'b1; adc_rst_ff2 <= 1'b1; end else begin adc_rst_ff1 <= 1'b0; adc_rst_ff2 <= adc_rst_ff1; end end assign ctrl_rst = eth_rst_ff2; assign dac_rst_int = dac_rst_ff2; assign adc_rst_int = adc_rst_ff2; // Event toggles cntrl -> DAC/ADC logic start_toggle; logic rst_toggle; // Config request toggles cntrl -> DAC/ADC logic cfg_req_toggle_dac; logic cfg_req_toggle_adc; // ACK toggles DAC/ADC -> contrl logic cfg_ack_toggle_dac; logic cfg_ack_toggle_adc; (* ASYNC_REG = "TRUE" *) logic cfg_ack_toggle_dac_meta, cfg_ack_toggle_dac_sync, cfg_ack_toggle_dac_sync_d; (* ASYNC_REG = "TRUE" *) logic cfg_ack_toggle_adc_meta, cfg_ack_toggle_adc_sync, cfg_ack_toggle_adc_sync_d; wire cfg_ack_pulse_dac_eth = cfg_ack_toggle_dac_sync ^ cfg_ack_toggle_dac_sync_d; wire cfg_ack_pulse_adc_eth = cfg_ack_toggle_adc_sync ^ cfg_ack_toggle_adc_sync_d; always_ff @(posedge ctrl_clk or posedge ctrl_rst) begin if (ctrl_rst) begin cfg_ack_toggle_dac_meta <= 1'b0; cfg_ack_toggle_dac_sync <= 1'b0; cfg_ack_toggle_dac_sync_d <= 1'b0; cfg_ack_toggle_adc_meta <= 1'b0; cfg_ack_toggle_adc_sync <= 1'b0; cfg_ack_toggle_adc_sync_d <= 1'b0; end else begin cfg_ack_toggle_dac_meta <= cfg_ack_toggle_dac; cfg_ack_toggle_dac_sync <= cfg_ack_toggle_dac_meta; cfg_ack_toggle_dac_sync_d <= cfg_ack_toggle_dac_sync; cfg_ack_toggle_adc_meta <= cfg_ack_toggle_adc; cfg_ack_toggle_adc_sync <= cfg_ack_toggle_adc_meta; cfg_ack_toggle_adc_sync_d <= cfg_ack_toggle_adc_sync; end end // finish event: ADC -> ETH via toggle CDC logic finish_toggle_adc; logic finish_meta, finish_sync, finish_sync_d; wire finish_pulse = finish_sync ^ finish_sync_d; always_ff @(posedge adc_clk_in or posedge adc_rst_int) begin if (adc_rst_int) begin finish_toggle_adc <= 1'b0; end else if (finish) begin finish_toggle_adc <= ~finish_toggle_adc; end end always_ff @(posedge ctrl_clk or posedge ctrl_rst) begin if (ctrl_rst) begin finish_meta <= 1'b0; finish_sync <= 1'b0; finish_sync_d <= 1'b0; end else begin finish_meta <= finish_toggle_adc; finish_sync <= finish_meta_eth; finish_sync_d <= finish_sync_eth; end end // ETH -> DAC: start/reset event sync (* ASYNC_REG = "TRUE" *) logic start_meta_dac, start_sync_dac; logic start_sync_dac_d; (* ASYNC_REG = "TRUE" *) logic rst_meta_dac, rst_sync_dac; logic rst_sync_dac_d; wire dac_start_pulse = start_sync_dac ^ start_sync_dac_d; wire dac_rst_pulse = rst_sync_dac ^ rst_sync_dac_d; always_ff @(posedge dac_clk_in or posedge dac_rst_int) begin if (dac_rst_int) begin start_meta_dac <= 1'b0; start_sync_dac <= 1'b0; start_sync_dac_d <= 1'b0; rst_meta_dac <= 1'b0; rst_sync_dac <= 1'b0; rst_sync_dac_d <= 1'b0; dac_start <= 1'b0; dac_rst <= 1'b0; end else begin start_meta_dac <= start_toggle; start_sync_dac <= start_meta_dac; start_sync_dac_d <= start_sync_dac; rst_meta_dac <= rst_toggle; rst_sync_dac <= rst_meta_dac; rst_sync_dac_d <= rst_sync_dac; dac_start <= dac_start_pulse; dac_rst <= dac_rst_pulse; end end // ETH -> ADC: start/reset event sync (* ASYNC_REG = "TRUE" *) logic start_meta_adc, start_sync_adc; logic start_sync_adc_d; (* ASYNC_REG = "TRUE" *) logic rst_meta_adc, rst_sync_adc; logic rst_sync_adc_d; wire adc_start_pulse = start_sync_adc ^ start_sync_adc_d; wire adc_rst_pulse = rst_sync_adc ^ rst_sync_adc_d; always_ff @(posedge adc_clk_in or posedge adc_rst_int) begin if (adc_rst_int) begin start_meta_adc <= 1'b0; start_sync_adc <= 1'b0; start_sync_adc_d <= 1'b0; rst_meta_adc <= 1'b0; rst_sync_adc <= 1'b0; rst_sync_adc_d <= 1'b0; adc_start <= 1'b0; adc_rst <= 1'b0; end else begin start_meta_adc <= start_toggle; start_sync_adc <= start_meta_adc; start_sync_adc_d <= start_sync_adc; rst_meta_adc <= rst_toggle; rst_sync_adc <= rst_meta_adc; rst_sync_adc_d <= rst_sync_adc; adc_start <= adc_start_pulse; adc_rst <= adc_rst_pulse; end end // contrl -> DAC config CDC // cfg_bus is kept stable in contrl domain until DAC and ADC both ACK. (* ASYNC_REG = "TRUE" *) logic cfg_req_meta_dac, cfg_req_sync_dac; logic cfg_req_sync_dac_d; wire cfg_req_pulse_dac = cfg_req_sync_dac ^ cfg_req_sync_dac_d; always_ff @(posedge dac_clk_in or posedge dac_rst_int) begin if (dac_rst_int) begin cfg_req_meta_dac <= 1'b0; cfg_req_sync_dac <= 1'b0; cfg_req_sync_dac_d<= 1'b0; cfg_ack_toggle_dac<= 1'b0; dac_pulse_width <= '0; dac_pulse_period <= '0; dac_pulse_num <= '0; dac_pulse_height <= '0; end else begin cfg_req_meta_dac <= cfg_req_toggle_dac_eth; cfg_req_sync_dac <= cfg_req_meta_dac; cfg_req_sync_dac_d <= cfg_req_sync_dac; if (cfg_req_pulse_dac) begin dac_pulse_width <= cfg_bus[31:0]; dac_pulse_period <= cfg_bus[63:32]; dac_pulse_num <= cfg_bus[79:64]; dac_pulse_height <= cfg_bus[80 +: DAC_DATA_WIDTH]; cfg_ack_toggle_dac <= ~cfg_ack_toggle_dac; end end end // ETH -> ADC config CDC logic cfg_req_meta_adc, cfg_req_sync_adc, cfg_req_sync_adc_d; wire cfg_req_pulse_adc = cfg_req_sync_adc ^ cfg_req_sync_adc_d; always_ff @(posedge adc_clk_in or posedge adc_rst_int) begin if (adc_rst_int) begin cfg_req_meta_adc <= 1'b0; cfg_req_sync_adc <= 1'b0; cfg_req_sync_adc_d <= 1'b0; cfg_ack_toggle_adc <= 1'b0; adc_pulse_period <= '0; adc_pulse_num <= '0; end else begin cfg_req_meta_adc <= cfg_req_toggle_adc_eth; cfg_req_sync_adc <= cfg_req_meta_adc; cfg_req_sync_adc_d <= cfg_req_sync_adc; if (cfg_req_pulse_adc) begin adc_pulse_period <= cfg_bus[127:96]; adc_pulse_num <= cfg_bus[79:64]; cfg_ack_toggle_adc <= ~cfg_ack_toggle_adc; end end end endmodule