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 [159:0] cfg_bus_input, input logic cfg_bus_valid, input logic start, // status signals output logic busy, // 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, output logic [31:0] adc_window_size, // 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 = rst_ff2; assign dac_rst_int = dac_rst_ff2; assign adc_rst_int = adc_rst_ff2; // Pending ACKs for config delivery logic cfg_wait_dac_ack; logic cfg_wait_adc_ack; // 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 -> cntrl 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 = cfg_ack_toggle_dac_sync ^ cfg_ack_toggle_dac_sync_d; wire cfg_ack_pulse_adc = 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 -> cntrl 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; finish_sync_d <= finish_sync; end end logic [159:0] cfg_bus; always_ff @(posedge ctrl_clk or posedge ctrl_rst) begin if (ctrl_rst) begin cfg_bus <= '0; busy <= 0; start_toggle <= 0; rst_toggle <= 0; cfg_req_toggle_dac <= 0; cfg_req_toggle_adc <= 0; cfg_wait_dac_ack <= 0; cfg_wait_adc_ack <= 0; end else begin if (finish_pulse) begin busy <= 0; end if (cfg_ack_pulse_dac) begin cfg_wait_dac_ack <= 1'b0; end if (cfg_ack_pulse_adc) begin cfg_wait_adc_ack <= 1'b0; end if (cfg_bus_valid && !busy && !cfg_wait_dac_ack && !cfg_wait_adc_ack) begin cfg_bus <= cfg_bus_input; cfg_req_toggle_dac <= ~cfg_req_toggle_dac; cfg_req_toggle_adc <= ~cfg_req_toggle_adc; cfg_wait_dac_ack <= 1; cfg_wait_adc_ack <= 1; end if (start && !busy && !cfg_wait_dac_ack && !cfg_wait_adc_ack) begin start_toggle <= ~start_toggle; busy <= 1; end if (rst_soft) begin rst_toggle <= ~rst_toggle; end end end // cntrl -> 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 // cntrl -> 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; 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 // cntrl -> 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; adc_window_size <= '0; end else begin cfg_req_meta_adc <= cfg_req_toggle_adc; 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]; adc_window_size <= cfg_bus[159:128]; cfg_ack_toggle_adc <= ~cfg_ack_toggle_adc; end end end endmodule