From 6a398408212b867133f0b4161f7fd0547249a73c Mon Sep 17 00:00:00 2001 From: Joris van Rantwijk Date: Tue, 8 Oct 2024 08:49:34 +0200 Subject: [PATCH] Add support for 4-input Red Pitaya --- fpga/.gitignore | 3 + fpga/12_build_bitfile_4ch.sh | 35 ++ fpga/constraints/red_pitaya_4ch.xdc | 240 ++++++++++ fpga/rtl/adc_capture_ddr.vhd | 118 +++++ fpga/rtl/ffpair.vhd | 51 ++ fpga/rtl/puzzlefw_pkg.vhd | 2 + fpga/rtl/puzzlefw_top_4ch.vhd | 709 ++++++++++++++++++++++++++++ 7 files changed, 1158 insertions(+) create mode 100755 fpga/12_build_bitfile_4ch.sh create mode 100644 fpga/constraints/red_pitaya_4ch.xdc create mode 100644 fpga/rtl/adc_capture_ddr.vhd create mode 100644 fpga/rtl/ffpair.vhd create mode 100644 fpga/rtl/puzzlefw_top_4ch.vhd diff --git a/fpga/.gitignore b/fpga/.gitignore index f0f473d..e99586d 100644 --- a/fpga/.gitignore +++ b/fpga/.gitignore @@ -4,11 +4,14 @@ redpitaya_puzzlefw.xsa vivado/redpitaya_puzzlefw.srcs/sources_1/bd/puzzlefw/ip vivado/redpitaya_puzzlefw.srcs/sources_1/bd/puzzlefw/ipshared vivado/redpitaya_puzzlefw.srcs/sources_1/bd/puzzlefw/puzzlefw.bda +vivado/redpitaya_puzzlefw.srcs/sources_1/bd/puzzlefw/ui vivado/redpitaya_puzzlefw.gen vivado/redpitaya_puzzlefw.cache vivado/redpitaya_puzzlefw.hw +vivado/redpitaya_puzzlefw.ip_user_files vivado/redpitaya_puzzlefw.runs vivado/output +vivado/output_4ch vivado/.Xil vivado/NONE vivado/*.html diff --git a/fpga/12_build_bitfile_4ch.sh b/fpga/12_build_bitfile_4ch.sh new file mode 100755 index 0000000..6789249 --- /dev/null +++ b/fpga/12_build_bitfile_4ch.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +set -e +set -o pipefail + +. script_env +setup_vivado + +rm -f puzzlefw_top_4ch.bit.bin redpitaya_puzzlefw_4ch.xsa + +rm -rf vivado/redpitaya_puzzlefw.srcs/sources_1/bd/puzzlefw/ip +rm -rf vivado/redpitaya_puzzlefw.srcs/sources_1/bd/puzzlefw/ipshared +rm -rf vivado/redpitaya_puzzlefw.gen +rm -rf vivado/output_4ch + +mkdir -p vivado/output_4ch + +( cd vivado + stdbuf -oL vivado -mode batch -source nonproject_4ch.tcl | tee output_4ch/build_log.txt + ) + +cat >vivado/output_4ch/bitstream.bif < "IDATAIN", + HIGH_PERFORMANCE_MODE => "FALSE", + IDELAY_TYPE => "FIXED", + IDELAY_VALUE => 30, + REFCLK_FREQUENCY => 200.0, + SIGNAL_PATTERN => "DATA" ) + port map ( + CNTVALUEOUT => open, + DATAOUT => s_data_delayed(i), + C => '0', + CE => '0', + CINVCTRL => '0', + CNTVALUEIN => (others => '0'), + DATAIN => '0', + IDATAIN => in_data(i), + INC => '0', + LD => '0', + LDPIPEEN => '0', + REGRST => '0' ); + + -- DDR input register. + inst_iddr: IDDR + generic map ( + DDR_CLK_EDGE => "SAME_EDGE_PIPELINED" ) + port map ( + Q1 => s_data_iddr(2*i), + Q2 => s_data_iddr(2*i+1), + C => clk_capture, + CE => '1', + D => s_data_delayed(i), + R => '0', + S => '0' ); + + end generate; + + -- + -- Re-capture samples on intermediate clock. + -- + gen_ffpair: for i in 0 to adc_data_bits - 1 generate + inst_ffpair: entity work.ffpair + port map ( + clk1 => clk_capture, + clk2 => clk_intermediate, + di => s_data_iddr(i), + do => s_data_staged(i) ); + end generate; + + -- + -- Re-capture samples on system clock. + -- + process (clk_handoff) is + begin + if rising_edge(clk_handoff) then + r_out_data <= s_data_staged; + end if; + end process; + + -- Drive output ports. + out_data <= r_out_data; + +end architecture; diff --git a/fpga/rtl/ffpair.vhd b/fpga/rtl/ffpair.vhd new file mode 100644 index 0000000..4bd8e41 --- /dev/null +++ b/fpga/rtl/ffpair.vhd @@ -0,0 +1,51 @@ +-- +-- Pair of flip-flops located in adjacent slices. +-- + +library ieee; +use ieee.std_logic_1164.all; + +entity ffpair is + + port ( + -- Clocks. + clk1: in std_logic; + clk2: in std_logic; + + -- Input data, synchronous to "clk1". + di: in std_logic; + + -- Output data, synchronous to "clk2"; + do: out std_logic + ); + +end entity; + +architecture rtl of ffpair is + + signal reg1: std_logic; + signal reg2: std_logic; + + attribute RLOC: string; + attribute RLOC of reg1: signal is "X0Y0"; + attribute RLOC of reg2: signal is "X1Y0"; + +begin + + process (clk1) is + begin + if rising_edge(clk1) then + reg1 <= di; + end if; + end process; + + process (clk2) is + begin + if rising_edge(clk2) then + reg2 <= reg1; + end if; + end process; + + do <= reg2; + +end architecture; diff --git a/fpga/rtl/puzzlefw_pkg.vhd b/fpga/rtl/puzzlefw_pkg.vhd index 00f366b..3bfcd4b 100644 --- a/fpga/rtl/puzzlefw_pkg.vhd +++ b/fpga/rtl/puzzlefw_pkg.vhd @@ -38,6 +38,8 @@ package puzzlefw_pkg is -- ADC input port type. type adc_data_input_type is array(0 to 1) of std_logic_vector(15 downto 0); + type adc_data_input_type_4ch is array(0 to 3) of std_logic_vector(6 downto 0); + type adc_clock_input_type_4ch is array(0 to 1) of std_logic_vector(1 downto 0); -- Register addresses. constant reg_addr_mask: std_logic_vector(31 downto 0) := x"0010fffc"; diff --git a/fpga/rtl/puzzlefw_top_4ch.vhd b/fpga/rtl/puzzlefw_top_4ch.vhd new file mode 100644 index 0000000..6e0eba5 --- /dev/null +++ b/fpga/rtl/puzzlefw_top_4ch.vhd @@ -0,0 +1,709 @@ +-- +-- Top-level FPGA design for Red Pitaya PuzzleFW firmware, +-- variant for 4 input channels. +-- +-- 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_4ch 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_4ch; -- ADC data + adc_clk_i: in adc_clock_input_type_4ch; -- ADC clock 1=pos, 0=neg + spi_csa_o: out std_logic; -- SPI interface to ADC + spi_csb_o: out std_logic; + spi_clk_o: out std_logic; + spi_mosi_o: out std_logic; + dac_pwm_o: out std_logic_vector(3 downto 0); -- PWM DAC + exp_p_io: inout std_logic_vector(10 downto 0); -- extension I/O pos + exp_n_io: inout std_logic_vector(10 downto 0); -- extension I/O neg + led_o: out std_logic_vector(7 downto 0) -- LEDs + ); + +end puzzlefw_top_4ch; + +architecture arch of puzzlefw_top_4ch is + + -- Main 125 MHz clock, derived from ADC A clock input port. + signal clk_adc: std_logic; + + -- Auxiliary clock from FCLK0. + signal clk_fclk200: std_logic; + + -- Reset signals. + signal s_ext_reset_n: std_logic; -- reset signal from GPIO, active low + signal s_pll_reset: std_logic; -- reset signal for PLL + signal s_pll_locked: std_logic; -- PLL locked status + signal s_reset: std_logic; -- main reset, synchronized to clk_adc + signal r_reset_done: std_logic; -- reset status report via GPIO + + -- Internal clock signals. + signal s_adc_clk_ibuf: std_logic_vector(1 downto 0); + signal clk_adc_capture: std_logic_vector(1 downto 0); + signal s_pll_clkfbout: std_logic; + signal s_pll_clkfbin: std_logic; + signal s_pll_clkout: std_logic; + + -- Blinking LED. + signal r_adcclk_cnt: unsigned(25 downto 0); + signal r_adcclk_led: std_logic; + + -- Internal GPIO and SPI signals from PS. + signal s_gpio_in: std_logic_vector(23 downto 0); + signal s_gpio_out: std_logic_vector(23 downto 0); + signal s_spi_sclk_o: std_logic; + signal s_spi_sclk_t: std_logic; + signal s_spi_mosi_o: std_logic; + signal s_spi_mosi_t: std_logic; + signal s_spi_ss_o: std_logic_vector(1 downto 0); + signal s_spi_ss_t: 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 3); + signal s_adc_sample: adc_data_array(0 to 3); + 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); + +begin + + -- Global FPGA reset. + -- GPIO(0) = '0' to reset. + s_ext_reset_n <= s_gpio_out(0); + + -- GPIO inputs to the PS. + -- GPIO(1) = '0' while in reset, '1' when reset released. + s_gpio_in(1) <= r_reset_done; + s_gpio_in(0) <= '0'; + s_gpio_in(23 downto 2) <= (others => '0'); + + -- 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); + + -- Drive safe levels to unused DAC pins. + dac_pwm_o <= (others => 'Z'); + + -- Use extension I/O pins as inputs only. + exp_p_io <= (others => 'Z'); + exp_n_io <= (others => 'Z'); + + -- Drive SPI bus. + inst_obuf_spi_clk: OBUFT + port map ( + I => s_spi_sclk_o, + T => s_spi_sclk_t, + O => spi_clk_o ); + + inst_obuf_spi_mosi: OBUFT + port map ( + I => s_spi_mosi_o, + T => s_spi_mosi_t, + O => spi_mosi_o ); + + inst_obuf_spi_csa: OBUFT + port map ( + I => s_spi_ss_o(0), + T => s_spi_ss_t, + O => spi_csa_o ); + + inst_obuf_spi_csb: OBUFT + port map ( + I => s_spi_ss_o(1), + T => s_spi_ss_t, + O => spi_csb_o ); + + -- Handle clock input and data input for each ADC. + gen_adcin: for i in 0 to 1 generate + + -- Differential clock input for ADC clock. + inst_ibuf_adc_clk: IBUFDS + port map ( + O => s_adc_clk_ibuf(i), + I => adc_clk_i(i)(1), + IB => adc_clk_i(i)(0) ); + + -- Clock buffer for ADC clock. + -- BUFR is faster (lower propagation delay) than BUFG. + inst_bufg_adc_clk: BUFR + port map ( + I => s_adc_clk_ibuf(i), + O => clk_adc_capture(i), + CE => '1', + CLR => '0' ); + + end generate; + + -- PLL for 125 MHz clock. + -- Input clock comes from ADC A. + -- Output clock drives most of the FPGA design. + inst_pll: PLLE2_BASE + generic map ( + BANDWIDTH => "OPTIMIZED", + CLKFBOUT_MULT => 8, + CLKFBOUT_PHASE => 0.0, + CLKIN1_PERIOD => 8.0, + CLKOUT0_DIVIDE => 8, + CLKOUT0_DUTY_CYCLE => 0.5, + CLKOUT0_PHASE => 0.0, + DIVCLK_DIVIDE => 1, + STARTUP_WAIT => "FALSE" ) + port map ( + CLKOUT0 => s_pll_clkout, + CLKFBOUT => s_pll_clkfbout, + LOCKED => s_pll_locked, + CLKIN1 => s_adc_clk_ibuf(0), + PWRDWN => '0', + RST => s_pll_reset, + CLKFBIN => s_pll_clkfbin ); + + -- Reset PLL when external reset is applied. + s_pll_reset <= not s_ext_reset_n; + + -- Clock buffers for PLL. + inst_bufg_pll_clkfb: BUFG + port map ( + I => s_pll_clkfbout, + O => s_pll_clkfbin ); + + inst_bufg_pll_clkout: BUFG + port map ( + I => s_pll_clkout, + O => clk_adc ); + + -- Since the design uses IDELAY, it must contain an IDELAYCTRL instance. + inst_idelayctrl: IDELAYCTRL + port map ( + RDY => open, + REFCLK => clk_fclk200, + RST => s_pll_reset ); + + -- ARM/PS block design. + inst_blockdesign: entity work.puzzlefw_wrapper + port map ( + sys_clk => clk_adc, + ps_fclk => clk_fclk200, + peripheral_reset_0(0) => s_reset, + ext_reset_in_0 => s_ext_reset_n, + dcm_locked_0 => s_pll_locked, + 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, + GPIO_I_0 => s_gpio_in, + GPIO_O_0 => s_gpio_out, + SPI0_MOSI_O_0 => s_spi_mosi_o, + SPI0_MOSI_T_0 => s_spi_mosi_t, + SPI0_SCLK_O_0 => s_spi_sclk_o, + SPI0_SCLK_T_0 => s_spi_sclk_t, + SPI0_SS1_O_0 => s_spi_ss_o(1), + SPI0_SS_O_0 => s_spi_ss_o(0), + SPI0_SS_T_0 => s_spi_ss_t, + IRQ_F2P => s_irq_f2p, + 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, + 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 + generic map ( + num_acq_channels => 4 ) + 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 => 14, + 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 => 12, + 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. + -- ADC A handles channels 0 and 1. + -- ADC B handles channels 2 and 3. + -- Each channel receives one 14-bit sample per clock cycle. + -- The 14 bits are transferred through 7 DDR signals. + gen_adc_capture: for i in 0 to 3 generate + inst_adc_capture: entity work.adc_capture_ddr + port map ( + clk_capture => clk_adc_capture(i / 2), + clk_intermediate => clk_adc_capture(0), + clk_handoff => clk_adc, + in_data => adc_dat_i(i), + out_data => s_adc_data(i) ); + end generate; + + -- Optionally generate simulated ADC samples. + gen_adc_sample_stream: for i in 0 to 1 generate + 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(2*i to 2*i+1), + out_data => s_adc_sample(2*i to 2*i+1) ); + end generate; + + -- Monitor range of ADC samples. + inst_monitor_gen: for i in 0 to 3 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 <= s_adc_sample; + + -- Analog acquisition data chain. + inst_acquisition_chain: entity work.acquisition_chain + generic map ( + num_channels => 4 ) + 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_ext_once => s_reg_control.trig_ext_once, + 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, + trig_detected => s_reg_status.trig_detected, + 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'); + + -- Report reset status via GPIO. + process (clk_adc, s_ext_reset_n) is + begin + if s_ext_reset_n = '0' then + r_reset_done <= '0'; + elsif rising_edge(clk_adc) then + r_reset_done <= not s_reset; + end if; + end process; + + -- Blinking LED, 1 Hz. + process (clk_adc) is + begin + if rising_edge(clk_adc) then + if s_reset = '1' then + r_adcclk_cnt <= (others => '0'); + r_adcclk_led <= '0'; + elsif 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;