-- -- Top-level FPGA design for Red Pitaya PuzzleFW firmware. -- -- Joris van Rantwijk 2024 -- library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_misc.all; use ieee.numeric_std.all; library unisim; use unisim.vcomponents.all; library xpm; use xpm.vcomponents.all; use work.puzzlefw_pkg.all; entity puzzlefw_top is port ( -- Ports directly connected to ARM/PS. DDR_0_addr: inout std_logic_vector(14 downto 0); DDR_0_ba: inout std_logic_vector(2 downto 0); DDR_0_cas_n: inout std_logic; DDR_0_ck_n: inout std_logic; DDR_0_ck_p: inout std_logic; DDR_0_cke: inout std_logic; DDR_0_cs_n: inout std_logic; DDR_0_dm: inout std_logic_vector(3 downto 0); DDR_0_dq: inout std_logic_vector(31 downto 0); DDR_0_dqs_n: inout std_logic_vector(3 downto 0); DDR_0_dqs_p: inout std_logic_vector(3 downto 0); DDR_0_odt: inout std_logic; DDR_0_ras_n: inout std_logic; DDR_0_reset_n: inout std_logic; DDR_0_we_n: inout std_logic; FIXED_IO_0_ddr_vrn: inout std_logic; FIXED_IO_0_ddr_vrp: inout std_logic; FIXED_IO_0_mio: inout std_logic_vector(53 downto 0); FIXED_IO_0_ps_clk: inout std_logic; FIXED_IO_0_ps_porb: inout std_logic; FIXED_IO_0_ps_srstb: inout std_logic; -- Ports controlled by FPGA. adc_dat_i: in adc_data_input_type; -- ADC data adc_clk_i: in std_logic_vector(1 downto 0); -- ADC clock 1=pos, 0=neg adc_clk_o: out std_logic_vector(1 downto 0); -- optional clock output for ADC adc_cdcs_o: out std_logic; -- ADC clock duty cycle stabilizer dac_dat_o: out std_logic_vector(13 downto 0); -- DAC data dac_wrt_o: out std_logic; -- DAC write control dac_sel_o: out std_logic; -- DAC channel select dac_clk_o: out std_logic; -- DAC clock dac_rst_o: out std_logic; -- DAC reset dac_pwm_o: out std_logic_vector(3 downto 0); -- PWM DAC exp_p_io: inout std_logic_vector(7 downto 0); -- extension I/O pos exp_n_io: inout std_logic_vector(7 downto 0); -- extension I/O neg led_o: out std_logic_vector(7 downto 0) -- LEDs ); end puzzlefw_top; architecture arch of puzzlefw_top is -- Main 125 MHz clock, derived from ADC clock input port. signal clk_adc: std_logic; -- Auxiliary clock from FCLK0. signal clk_fclk: std_logic; -- Main reset signal, derived from FCLK_RESET0, active high, synchronous to clk_adc. signal s_reset: std_logic; -- Internal clock signal. signal s_adc_clk_ibuf: std_logic; -- Blinking LED. signal r_adcclk_cnt: unsigned(25 downto 0); signal r_adcclk_led: std_logic; -- APB bus for register access. signal s_apb_paddr: std_logic_vector(31 downto 0); signal s_apb_penable: std_logic; signal s_apb_prdata: std_logic_vector(31 downto 0); signal s_apb_pready: std_logic; signal s_apb_psel: std_logic; signal s_apb_pslverr: std_logic; signal s_apb_pwdata: std_logic_vector(31 downto 0); signal s_apb_pwrite: std_logic; -- AXI bus for DMA. signal s_axi_awid: std_logic_vector(5 downto 0); signal s_axi_awaddr: std_logic_vector(31 downto 0); signal s_axi_awlen: std_logic_vector(3 downto 0); signal s_axi_awsize: std_logic_vector(2 downto 0); signal s_axi_awburst: std_logic_vector(1 downto 0); signal s_axi_awlock: std_logic_vector(1 downto 0); signal s_axi_awcache: std_logic_vector(3 downto 0); signal s_axi_awprot: std_logic_vector(2 downto 0); signal s_axi_awqos: std_logic_vector(3 downto 0); signal s_axi_awvalid: std_logic; signal s_axi_awready: std_logic; signal s_axi_wid: std_logic_vector(5 downto 0); signal s_axi_wdata: std_logic_vector(63 downto 0); signal s_axi_wstrb: std_logic_vector(7 downto 0); signal s_axi_wlast: std_logic; signal s_axi_wvalid: std_logic; signal s_axi_wready: std_logic; signal s_axi_bid: std_logic_vector(5 downto 0); signal s_axi_bresp: std_logic_vector(1 downto 0); signal s_axi_bvalid: std_logic; signal s_axi_bready: std_logic; signal s_axi_arid: std_logic_vector(5 downto 0); signal s_axi_araddr: std_logic_vector(31 downto 0); signal s_axi_arlen: std_logic_vector(3 downto 0); signal s_axi_arsize: std_logic_vector(2 downto 0); signal s_axi_arburst: std_logic_vector(1 downto 0); signal s_axi_arlock: std_logic_vector(1 downto 0); signal s_axi_arcache: std_logic_vector(3 downto 0); signal s_axi_arprot: std_logic_vector(2 downto 0); signal s_axi_arqos: std_logic_vector(3 downto 0); signal s_axi_arvalid: std_logic; signal s_axi_arready: std_logic; signal s_axi_rid: std_logic_vector(5 downto 0); signal s_axi_rdata: std_logic_vector(63 downto 0); signal s_axi_rresp: std_logic_vector(1 downto 0); signal s_axi_rlast: std_logic; signal s_axi_rvalid: std_logic; signal s_axi_rready: std_logic; -- Interrupts. signal s_irq_pending: std_logic_vector(1 downto 0); signal s_irq_f2p: std_logic_vector(7 downto 0); -- Registers. signal s_reg_control: registers_control; signal s_reg_status: registers_status; -- DMA write channel control. signal s_dma_write_cmd_addr: dma_address_array(0 to 1); signal s_dma_write_cmd_length: dma_burst_length_array(0 to 1); signal s_dma_write_cmd_valid: std_logic_vector(1 downto 0); signal s_dma_write_cmd_ready: std_logic_vector(1 downto 0); signal s_dma_write_data: dma_data_array(0 to 1); signal s_dma_write_data_ready: std_logic_vector(1 downto 0); signal s_dma_write_finished: std_logic_vector(1 downto 0); signal s_acq_dma_valid: std_logic; signal s_acq_dma_ready: std_logic; signal s_acq_dma_empty: std_logic; signal s_acq_dma_data: dma_data_type; signal s_tt_dma_valid: std_logic; signal s_tt_dma_ready: std_logic; signal s_tt_dma_empty: std_logic; signal s_tt_dma_data: dma_data_type; signal s_timestamp: std_logic_vector(timestamp_bits - 1 downto 0); signal s_adc_data: adc_data_array(0 to 1); signal s_adc_sample: adc_data_array(0 to 1); signal s_dig_in: std_logic_vector(3 downto 0); signal s_dig_sync: std_logic_vector(3 downto 0); signal s_dig_deglitch: std_logic_vector(3 downto 0); signal s_dig_sample: std_logic_vector(3 downto 0); -- TODO signal r_mhz_cnt: unsigned(7 downto 0); signal r_khz_cnt: unsigned(9 downto 0); signal r_blink_mhz: std_logic; signal r_blink_mhz_d: std_logic; signal r_blink_khz: std_logic; begin -- Drive LEDs. led_o(0) <= r_adcclk_led; -- blinking LED, 1 Hz led_o(1) <= s_reg_control.acquisition_en; -- acquisition enabled led_o(2) <= s_reg_status.trig_waiting; -- waiting for trigger led_o(3) <= or_reduce(s_reg_control.timetagger_en); -- timetagger enabled led_o(7 downto 4) <= s_reg_control.led_state(7 downto 4); -- Enable ADC clock duty cycle stabilizer. adc_cdcs_o <= '1'; -- ADC clock outputs are not connected on vanilla Red Pitaya 125-14. adc_clk_o <= (others => 'Z'); -- Drive safe levels to unused DAC pins. dac_dat_o <= (others => '0'); dac_wrt_o <= '0'; dac_sel_o <= '0'; dac_clk_o <= '0'; dac_rst_o <= '0'; dac_pwm_o <= (others => 'Z'); -- Use extension I/O pins as inputs only. -- TODO -- temporary test pulse generator exp_p_io <= (2 => r_blink_khz, 3 => r_blink_khz, 4 => r_blink_mhz, 5 => r_blink_mhz, 6 => r_blink_mhz_d, 7 => r_blink_mhz_d, others => 'Z'); exp_n_io <= (others => 'Z'); -- TODO process (clk_adc) is begin if rising_edge(clk_adc) then if r_mhz_cnt < 124 then r_mhz_cnt <= r_mhz_cnt + 1; else r_mhz_cnt <= (others => '0'); if r_khz_cnt < 999 then r_khz_cnt <= r_khz_cnt + 1; else r_khz_cnt <= (others => '0'); end if; end if; if r_mhz_cnt < 62 then r_blink_mhz <= '0'; else r_blink_mhz <= '1'; end if; r_blink_mhz_d <= r_blink_mhz; if r_khz_cnt < 500 then r_blink_khz <= '0'; else r_blink_khz <= '1'; end if; end if; end process; -- Differential clock input for ADC clock. inst_ibuf_adc_clk: IBUFDS port map ( O => s_adc_clk_ibuf, I => adc_clk_i(1), IB => adc_clk_i(0) ); -- Clock buffer for ADC clock. inst_bufg_adc_clk: BUFG port map ( I => s_adc_clk_ibuf, O => clk_adc ); -- ARM/PS block design. inst_blockdesign: entity work.puzzlefw_wrapper port map ( sys_clk => clk_adc, ps_fclk => clk_fclk, peripheral_reset_0(0) => s_reset, DDR_0_addr => DDR_0_addr, DDR_0_ba => DDR_0_ba, DDR_0_cas_n => DDR_0_cas_n, DDR_0_ck_n => DDR_0_ck_n, DDR_0_ck_p => DDR_0_ck_p, DDR_0_cke => DDR_0_cke, DDR_0_cs_n => DDR_0_cs_n, DDR_0_dm => DDR_0_dm, DDR_0_dq => DDR_0_dq, DDR_0_dqs_n => DDR_0_dqs_n, DDR_0_dqs_p => DDR_0_dqs_p, DDR_0_odt => DDR_0_odt, DDR_0_ras_n => DDR_0_ras_n, DDR_0_reset_n => DDR_0_reset_n, DDR_0_we_n => DDR_0_we_n, FIXED_IO_0_ddr_vrn => FIXED_IO_0_ddr_vrn, FIXED_IO_0_ddr_vrp => FIXED_IO_0_ddr_vrp, FIXED_IO_0_mio => FIXED_IO_0_mio, FIXED_IO_0_ps_clk => FIXED_IO_0_ps_clk, FIXED_IO_0_ps_porb => FIXED_IO_0_ps_porb, FIXED_IO_0_ps_srstb => FIXED_IO_0_ps_srstb, APB_M_0_paddr => s_apb_paddr, APB_M_0_penable => s_apb_penable, APB_M_0_prdata => s_apb_prdata, APB_M_0_pready(0) => s_apb_pready, APB_M_0_psel(0) => s_apb_psel, APB_M_0_pslverr(0) => s_apb_pslverr, APB_M_0_pwdata => s_apb_pwdata, APB_M_0_pwrite => s_apb_pwrite, IRQ_F2P => s_irq_f2p, S_AXI_HP0_0_araddr => s_axi_araddr, S_AXI_HP0_0_arburst => s_axi_arburst, S_AXI_HP0_0_arcache => s_axi_arcache, S_AXI_HP0_0_arid => s_axi_arid, S_AXI_HP0_0_arlen => s_axi_arlen, S_AXI_HP0_0_arlock => s_axi_arlock, S_AXI_HP0_0_arprot => s_axi_arprot, S_AXI_HP0_0_arqos => s_axi_arqos, S_AXI_HP0_0_arready => s_axi_arready, S_AXI_HP0_0_arsize => s_axi_arsize, S_AXI_HP0_0_arvalid => s_axi_arvalid, S_AXI_HP0_0_awaddr => s_axi_awaddr, S_AXI_HP0_0_awburst => s_axi_awburst, S_AXI_HP0_0_awcache => s_axi_awcache, S_AXI_HP0_0_awid => s_axi_awid, S_AXI_HP0_0_awlen => s_axi_awlen, S_AXI_HP0_0_awlock => s_axi_awlock, S_AXI_HP0_0_awprot => s_axi_awprot, S_AXI_HP0_0_awqos => s_axi_awqos, S_AXI_HP0_0_awready => s_axi_awready, S_AXI_HP0_0_awsize => s_axi_awsize, S_AXI_HP0_0_awvalid => s_axi_awvalid, S_AXI_HP0_0_bid => s_axi_bid, S_AXI_HP0_0_bready => s_axi_bready, S_AXI_HP0_0_bresp => s_axi_bresp, S_AXI_HP0_0_bvalid => s_axi_bvalid, S_AXI_HP0_0_rdata => s_axi_rdata, S_AXI_HP0_0_rid => s_axi_rid, S_AXI_HP0_0_rlast => s_axi_rlast, S_AXI_HP0_0_rready => s_axi_rready, S_AXI_HP0_0_rresp => s_axi_rresp, S_AXI_HP0_0_rvalid => s_axi_rvalid, S_AXI_HP0_0_wdata => s_axi_wdata, S_AXI_HP0_0_wid => s_axi_wid, S_AXI_HP0_0_wlast => s_axi_wlast, S_AXI_HP0_0_wready => s_axi_wready, S_AXI_HP0_0_wstrb => s_axi_wstrb, S_AXI_HP0_0_wvalid => s_axi_wvalid ); -- Memory-mapped registers. inst_registers: entity work.registers port map ( clk => clk_adc, reset => s_reset, apb_psel => s_apb_psel, apb_penable => s_apb_penable, apb_pwrite => s_apb_pwrite, apb_paddr => s_apb_paddr, apb_pwdata => s_apb_pwdata, apb_pready => s_apb_pready, apb_pslverr => s_apb_pslverr, apb_prdata => s_apb_prdata, reg_control => s_reg_control, reg_status => s_reg_status ); -- AXI master. inst_axi_master: entity work.dma_axi_master generic map ( num_read_channels => 0, num_write_channels => 2 ) port map ( clk => clk_adc, reset => s_reset, dma_en => s_reg_control.dma_en, dma_busy => s_reg_status.dma_busy, window_base_addr => s_reg_control.dma_buf_addr, window_size => s_reg_control.dma_buf_size, err_read => s_reg_status.dma_err_read, err_write => s_reg_status.dma_err_write, err_address => s_reg_status.dma_err_address, err_any => s_reg_status.dma_err_any, clear_errors => s_reg_control.dma_clear, read_cmd_addr => (others => (others => '0')), read_cmd_length => (others => (others => '0')), read_cmd_valid => (others => '0'), read_cmd_ready => open, read_data => open, read_data_valid => open, write_cmd_addr => s_dma_write_cmd_addr, write_cmd_length => s_dma_write_cmd_length, write_cmd_valid => s_dma_write_cmd_valid, write_cmd_ready => s_dma_write_cmd_ready, write_data => s_dma_write_data, write_data_ready => s_dma_write_data_ready, write_finished => s_dma_write_finished, m_axi_awid => s_axi_awid, m_axi_awaddr => s_axi_awaddr, m_axi_awlen => s_axi_awlen, m_axi_awsize => s_axi_awsize, m_axi_awburst => s_axi_awburst, m_axi_awlock => s_axi_awlock, m_axi_awcache => s_axi_awcache, m_axi_awprot => s_axi_awprot, m_axi_awqos => s_axi_awqos, m_axi_awvalid => s_axi_awvalid, m_axi_awready => s_axi_awready, m_axi_wid => s_axi_wid, m_axi_wdata => s_axi_wdata, m_axi_wstrb => s_axi_wstrb, m_axi_wlast => s_axi_wlast, m_axi_wvalid => s_axi_wvalid, m_axi_wready => s_axi_wready, m_axi_bid => s_axi_bid, m_axi_bresp => s_axi_bresp, m_axi_bvalid => s_axi_bvalid, m_axi_bready => s_axi_bready, m_axi_arid => s_axi_arid, m_axi_araddr => s_axi_araddr, m_axi_arlen => s_axi_arlen, m_axi_arsize => s_axi_arsize, m_axi_arburst => s_axi_arburst, m_axi_arlock => s_axi_arlock, m_axi_arcache => s_axi_arcache, m_axi_arprot => s_axi_arprot, m_axi_arqos => s_axi_arqos, m_axi_arvalid => s_axi_arvalid, m_axi_arready => s_axi_arready, m_axi_rid => s_axi_rid, m_axi_rdata => s_axi_rdata, m_axi_rresp => s_axi_rresp, m_axi_rlast => s_axi_rlast, m_axi_rvalid => s_axi_rvalid, m_axi_rready => s_axi_rready ); -- DMA write channel for analog acquisition inst_acq_dma: entity work.dma_write_channel generic map ( transfer_size_bits => 4, queue_size_bits => 10, idle_timeout => 256 ) port map ( clk => clk_adc, reset => s_reset, channel_en => s_reg_control.acq_dma_en, channel_busy => s_reg_status.acq_dma_busy, channel_init => s_reg_control.acq_dma_init, addr_start => s_reg_control.acq_addr_start, addr_end => s_reg_control.acq_addr_end, addr_limit => s_reg_control.acq_addr_limit, addr_interrupt => s_reg_control.acq_addr_intr, addr_pointer => s_reg_status.acq_addr_ptr, intr_en => s_reg_control.acq_intr_en, intr_clear => s_reg_control.acq_intr_clear, intr_out => s_irq_pending(0), in_valid => s_acq_dma_valid, in_ready => s_acq_dma_ready, in_empty => s_acq_dma_empty, in_data => s_acq_dma_data, write_cmd_addr => s_dma_write_cmd_addr(0), write_cmd_length => s_dma_write_cmd_length(0), write_cmd_valid => s_dma_write_cmd_valid(0), write_cmd_ready => s_dma_write_cmd_ready(0), write_data => s_dma_write_data(0), write_data_ready => s_dma_write_data_ready(0), write_finished => s_dma_write_finished(0) ); -- DMA write channel for time tagger inst_tt_dma: entity work.dma_write_channel generic map ( transfer_size_bits => 4, queue_size_bits => 10, idle_timeout => 256 ) port map ( clk => clk_adc, reset => s_reset, channel_en => s_reg_control.tt_dma_en, channel_busy => s_reg_status.tt_dma_busy, channel_init => s_reg_control.tt_dma_init, addr_start => s_reg_control.tt_addr_start, addr_end => s_reg_control.tt_addr_end, addr_limit => s_reg_control.tt_addr_limit, addr_interrupt => s_reg_control.tt_addr_intr, addr_pointer => s_reg_status.tt_addr_ptr, intr_en => s_reg_control.tt_intr_en, intr_clear => s_reg_control.tt_intr_clear, intr_out => s_irq_pending(1), in_valid => s_tt_dma_valid, in_ready => s_tt_dma_ready, in_empty => s_tt_dma_empty, in_data => s_tt_dma_data, write_cmd_addr => s_dma_write_cmd_addr(1), write_cmd_length => s_dma_write_cmd_length(1), write_cmd_valid => s_dma_write_cmd_valid(1), write_cmd_ready => s_dma_write_cmd_ready(1), write_data => s_dma_write_data(1), write_data_ready => s_dma_write_data_ready(1), write_finished => s_dma_write_finished(1) ); -- Timestamp generator. inst_timestamp_gen: entity work.timestamp_gen port map ( clk => clk_adc, reset => s_reset, clear => s_reg_control.timestamp_clear, timestamp => s_timestamp ); s_reg_status.timestamp <= s_timestamp; -- Capture ADC data. -- Ignore the 2 LSB bits which are not-connected on the ADC side. inst_capture_gen: for i in 0 to 1 generate inst_adc_capture: entity work.adc_capture port map ( clk => clk_adc, in_data => adc_dat_i(i)(15 downto 2), out_data => s_adc_data(i) ); end generate; -- Optionally generate simulated ADC samples. inst_adc_sample_stream: entity work.adc_sample_stream port map ( clk => clk_adc, reset => s_reset, simulate => s_reg_control.simulate_adc, in_data => s_adc_data(0 to 1), out_data => s_adc_sample(0 to 1) ); -- Monitor range of ADC samples. inst_monitor_gen: for i in 0 to 1 generate inst_range_monitor: entity work.adc_range_monitor generic map ( signed_data => false ) port map ( clk => clk_adc, reset => s_reset, clear => s_reg_control.adc_range_clear, in_data => s_adc_sample(i), min_value => s_reg_status.adc_min_value(i), max_value => s_reg_status.adc_max_value(i) ); end generate; -- Monitor current ADC sample value. s_reg_status.adc_sample(0 to 1) <= s_adc_sample(0 to 1); -- Drive dummy values to not-implemented channels 2, 3. s_reg_status.adc_sample(2 to 3) <= (others => (others => '0')); s_reg_status.adc_min_value(2 to 3) <= (others => (others => '0')); s_reg_status.adc_max_value(2 to 3) <= (others => (others => '0')); -- Analog acquisition data chain. inst_acquisition_chain: entity work.acquisition_chain generic map ( num_channels => 2 ) port map ( clk => clk_adc, reset => s_reset, acquisition_en => s_reg_control.acquisition_en, trigger_delay => s_reg_control.trigger_delay, record_length => s_reg_control.record_length, decimation_factor => s_reg_control.decimation_factor, averaging => s_reg_control.averaging_en, shift_steps => s_reg_control.shift_steps, ch4_mode => s_reg_control.ch4_mode, trig_auto_en => s_reg_control.trig_auto_en, trig_ext_en => s_reg_control.trig_ext_en, trig_force => s_reg_control.trig_force, trig_ext_select => s_reg_control.trig_ext_select, trig_ext_falling => s_reg_control.trig_ext_falling, timestamp_in => s_timestamp, adc_data_in => s_adc_sample, trig_ext_in => s_dig_sample, trig_waiting => s_reg_status.trig_waiting, out_valid => s_acq_dma_valid, out_ready => s_acq_dma_ready, out_empty => s_acq_dma_empty, out_data => s_acq_dma_data ); -- Capture digital inputs. s_dig_in(0) <= exp_p_io(0); s_dig_in(1) <= exp_n_io(0); s_dig_in(2) <= exp_p_io(1); s_dig_in(3) <= exp_n_io(1); inst_dig_capture_gen: for i in 0 to 3 generate -- Use a 2-flipflop synchronizer to avoid metastability. inst_dig_sync: entity work.syncdff port map ( clk => clk_adc, di => s_dig_in(i), do => s_dig_sync(i) ); -- Deglitch filter. inst_dig_deglitch: entity work.deglitch generic map ( deglitch_cycles => 4 ) port map ( clk => clk_adc, din => s_dig_sync(i), dout => s_dig_deglitch(i) ); end generate; -- Optionally generate simulated digital signals. process (clk_adc) is begin if rising_edge(clk_adc) then if s_reg_control.dig_simulate = '1' then s_dig_sample <= s_reg_control.dig_sim_state; else s_dig_sample <= s_dig_deglitch; end if; end if; end process; -- Monitor digital signal state. s_reg_status.dig_sample <= s_dig_sample; -- Time tagger. inst_timetagger: entity work.timetagger port map ( clk => clk_adc, reset => s_reset, channel_en => s_reg_control.timetagger_en, marker => s_reg_control.timetagger_mark, timestamp_in => s_timestamp, dig_sample => s_dig_sample, out_valid => s_tt_dma_valid, out_ready => s_tt_dma_ready, out_empty => s_tt_dma_empty, out_data => s_tt_dma_data ); -- Collect interrupt signals from peripherals and generate interrupt to PS. s_reg_status.irq_pending <= s_irq_pending; s_irq_f2p(0) <= s_reg_control.irq_enable and or_reduce(s_irq_pending); s_irq_f2p(7 downto 1) <= (others => '0'); -- Blinking LED, 1 Hz. process (clk_adc) is begin if rising_edge(clk_adc) then if r_adcclk_cnt = 62499999 then r_adcclk_cnt <= (others => '0'); r_adcclk_led <= not r_adcclk_led; else r_adcclk_cnt <= r_adcclk_cnt + 1; end if; end if; end process; end architecture;