diff --git a/fpga/rtl/acquisition_manager.vhd b/fpga/rtl/acquisition_manager.vhd index efb9b2e..c1ac6ef 100644 --- a/fpga/rtl/acquisition_manager.vhd +++ b/fpga/rtl/acquisition_manager.vhd @@ -125,6 +125,7 @@ begin v.decimation_cnt := unsigned(decimation_factor); v.trig_waiting := acquisition_en; if acquisition_en = '1' and trig_in = '1' then + v.trig_waiting := '0'; if unsigned(trigger_delay) = 0 then v.trig_ack := '1'; v.sample_start := '1'; diff --git a/fpga/rtl/deglitch.vhd b/fpga/rtl/deglitch.vhd new file mode 100644 index 0000000..10f8cc7 --- /dev/null +++ b/fpga/rtl/deglitch.vhd @@ -0,0 +1,93 @@ +-- +-- Remove glitches from a digital signal. +-- +-- Joris van Rantwijk 2024 +-- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +entity deglitch is + + generic ( + -- The output follows the input when the input is stable + -- for "deglitch_cycles" clock cycles. + -- + -- A glitch-free transition on the input, propagates to the output + -- after a delay of "deglitch_cycles" clock cycles. + -- + deglitch_cycles: integer range 2 to 16 := 4 + ); + + port ( + -- Main clock, active on rising edge. + clk: in std_logic; + + -- Input signal. + din: in std_logic; + + -- Deglitched output signal. + dout: out std_logic + ); + +end entity; + +architecture arch of deglitch is + + type regs_type is record + cnt: integer range 0 to deglitch_cycles - 1; + dout: std_logic; + end record; + + constant regs_init: regs_type := ( + cnt => 0, + dout => '0' + ); + + signal r: regs_type := regs_init; + signal rnext: regs_type; + +begin + + -- Drive output. + dout <= r.dout; + + -- + -- Combinatorial process. + -- + process (all) is + variable v: regs_type; + variable v_trig: std_logic; + begin + -- Load current register values. + v := r; + + if (din xor r.dout) = '1' then + if r.cnt = deglitch_cycles - 1 then + v.dout := din; + v.cnt := 0; + else + v.cnt := r.cnt + 1; + end if; + else + v.cnt := 0; + end if; + + -- Drive new register values to synchronous process. + rnext <= v; + + end process; + + -- + -- Synchronous process. + -- + process (clk) is + begin + if rising_edge(clk) then + r <= rnext; + end if; + end process; + +end architecture; diff --git a/fpga/rtl/puzzlefw_pkg.vhd b/fpga/rtl/puzzlefw_pkg.vhd index 5e3217c..718a237 100644 --- a/fpga/rtl/puzzlefw_pkg.vhd +++ b/fpga/rtl/puzzlefw_pkg.vhd @@ -75,8 +75,9 @@ package puzzlefw_pkg is constant reg_adc1_minmax: natural := 16#000294#; constant reg_adc2_minmax: natural := 16#000298#; constant reg_adc3_minmax: natural := 16#00029c#; - constant reg_test_led: natural := 16#000404#; - constant reg_test_divider: natural := 16#000408#; + constant reg_dig_simulate: natural := 16#000330#; + constant reg_dig_sample: natural := 16#000338#; + constant reg_led_state: natural := 16#000404#; constant reg_dma_buf_addr: natural := 16#100000#; constant reg_dma_buf_size: natural := 16#100004#; @@ -98,8 +99,6 @@ package puzzlefw_pkg is -- Control registers: read/write access by processor, output signals to FPGA. type registers_control is record irq_enable: std_logic; - test_led: std_logic_vector(7 downto 0); - test_divider: std_logic_vector(15 downto 0); dma_en: std_logic; dma_clear: std_logic; -- single cycle timestamp_clear: std_logic; -- single cycle @@ -125,6 +124,9 @@ package puzzlefw_pkg is trig_ext_falling: std_logic; trigger_delay: std_logic_vector(15 downto 0); adc_range_clear: std_logic; + dig_simulate: std_logic; + dig_sim_state: std_logic_vector(3 downto 0); + led_state: std_logic_vector(7 downto 0); dma_buf_addr: std_logic_vector(31 downto 12); dma_buf_size: std_logic_vector(31 downto 12); end record; @@ -144,12 +146,11 @@ package puzzlefw_pkg is adc_sample: adc_data_array(0 to 3); adc_min_value: adc_data_array(0 to 3); adc_max_value: adc_data_array(0 to 3); + dig_sample: std_logic_vector(3 downto 0); end record; constant registers_control_init: registers_control := ( irq_enable => '0', - test_led => (others => '0'), - test_divider => (others => '0'), dma_en => '0', dma_clear => '0', timestamp_clear => '0', @@ -175,6 +176,9 @@ package puzzlefw_pkg is trig_ext_falling => '0', trigger_delay => (others => '0'), adc_range_clear => '0', + dig_simulate => '0', + dig_sim_state => (others => '0'), + led_state => (others => '0'), dma_buf_addr => (others => '0'), dma_buf_size => (others => '0') ); diff --git a/fpga/rtl/puzzlefw_top.vhd b/fpga/rtl/puzzlefw_top.vhd index 67ed76c..8b2d105 100644 --- a/fpga/rtl/puzzlefw_top.vhd +++ b/fpga/rtl/puzzlefw_top.vhd @@ -77,11 +77,11 @@ architecture arch of puzzlefw_top is -- Internal clock signal. signal s_adc_clk_ibuf: std_logic; - signal r_fclk_cnt: unsigned(26 downto 0); - signal r_fclk_led: std_logic; - signal r_adcclk_cnt: unsigned(26 downto 0); + -- 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); @@ -91,6 +91,7 @@ architecture arch of puzzlefw_top is 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); @@ -130,12 +131,15 @@ architecture arch of puzzlefw_top is signal s_axi_rvalid: std_logic; signal s_axi_rready: std_logic; + -- Interrupts. signal s_irq_pending: std_logic_vector(0 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 downto 0); signal s_dma_write_cmd_length: dma_burst_length_array(0 to 0); signal s_dma_write_cmd_valid: std_logic_vector(0 downto 0); @@ -144,25 +148,26 @@ architecture arch of puzzlefw_top is signal s_dma_write_data_ready: std_logic_vector(0 downto 0); signal s_dma_write_finished: std_logic_vector(0 downto 0); - 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_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 r_test_prefetch: std_logic; - signal r_test_raddr: std_logic_vector(9 downto 0); - signal r_test_waddr: std_logic_vector(9 downto 0); - signal r_test_cnt: unsigned(15 downto 0); - signal s_test_dout: std_logic_vector(63 downto 0); - signal s_test_ren: std_logic; + 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); begin - led_o(7 downto 2) <= s_reg_control.test_led(7 downto 2); + 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) <= timetagger_en + led_o(7 downto 4) <= s_reg_control.led_state(7 downto 4); -- Differential clock input for ADC clock. inst_ibuf_adc_clk: IBUFDS @@ -179,18 +184,6 @@ begin O => clk_adc ); - inst_obuf_led0: OBUF - port map ( - I => r_fclk_led, - O => led_o(0) - ); - - inst_obuf_led1: OBUF - port map ( - I => r_adcclk_led, - O => led_o(1) - ); - -- ARM/PS block design. inst_blockdesign: entity work.puzzlefw_wrapper port map ( @@ -465,24 +458,64 @@ begin 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; + + -- TODO : time tagger + -- 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'); - process (clk_fclk) is - begin - if rising_edge(clk_fclk) then - r_fclk_cnt <= r_fclk_cnt + 1; - r_fclk_led <= r_fclk_cnt(r_fclk_cnt'high); - end if; - end process; - + -- Blinking LED, 1 Hz. process (clk_adc) is begin if rising_edge(clk_adc) then - r_adcclk_cnt <= r_adcclk_cnt + 1; - r_adcclk_led <= r_adcclk_cnt(r_adcclk_cnt'high); + 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; diff --git a/fpga/rtl/registers.vhd b/fpga/rtl/registers.vhd index a8d3c70..e2a3919 100644 --- a/fpga/rtl/registers.vhd +++ b/fpga/rtl/registers.vhd @@ -141,8 +141,11 @@ begin when reg_adc3_minmax => v.prdata(adc_data_bits - 1 downto 0) := reg_status.adc_min_value(3); v.prdata(adc_data_bits + 15 downto 16) := reg_status.adc_max_value(3); - when reg_test_led => v.prdata(7 downto 0) := r.reg_control.test_led; - when reg_test_divider => v.prdata(15 downto 0) := r.reg_control.test_divider; + when reg_dig_simulate => + v.prdata(8) := r.reg_control.dig_simulate; + v.prdata(3 downto 0) := r.reg_control.dig_sim_state; + when reg_dig_sample => v.prdata(3 downto 0) := reg_status.dig_sample; + when reg_led_state => v.prdata(7 downto 0) := r.reg_control.led_state; when reg_dma_buf_addr => v.prdata(31 downto 12) := r.reg_control.dma_buf_addr; when reg_dma_buf_size => v.prdata(31 downto 12) := r.reg_control.dma_buf_size; when others => null; @@ -182,8 +185,10 @@ begin v.reg_control.trig_force := apb_pwdata(8); when reg_trigger_delay => v.reg_control.trigger_delay := apb_pwdata(15 downto 0); when reg_adc_range_clear => v.reg_control.adc_range_clear := apb_pwdata(0); - when reg_test_led => v.reg_control.test_led := apb_pwdata(7 downto 0); - when reg_test_divider => v.reg_control.test_divider := apb_pwdata(15 downto 0); + when reg_dig_simulate => + v.reg_control.dig_simulate := apb_pwdata(8); + v.reg_control.dig_sim_state := apb_pwdata(3 downto 0); + when reg_led_state => v.reg_control.led_state := apb_pwdata(7 downto 0); when reg_dma_buf_addr => v.reg_control.dma_buf_addr := apb_pwdata(31 downto 12); when reg_dma_buf_size => v.reg_control.dma_buf_size := apb_pwdata(31 downto 12); when others => null; diff --git a/fpga/rtl/syncdff.vhd b/fpga/rtl/syncdff.vhd new file mode 100644 index 0000000..2c1495a --- /dev/null +++ b/fpga/rtl/syncdff.vhd @@ -0,0 +1,39 @@ +-- +-- Double flip-flop synchronizer. +-- + +library ieee; +use ieee.std_logic_1164.all; +library xpm; +use xpm.vcomponents.all; + +entity syncdff is + + port ( + -- Clock (destination domain). + clk: in std_logic; + + -- Input data (asynchronous). + di: in std_logic; + + -- Output data, synchronous to "clk". + do: out std_logic + ); + +end entity; + +architecture rtl of syncdff is + +begin + + inst: xpm_cdc_single + generic map ( + DEST_SYNC_FF => 2, + SRC_INPUT_REG => 0 ) + port map ( + dest_clk => clk, + dest_out => do, + src_clk => '0', + src_in => di ); + +end architecture; diff --git a/fpga/vivado/nonproject.tcl b/fpga/vivado/nonproject.tcl index 54e3be7..b7b75fb 100644 --- a/fpga/vivado/nonproject.tcl +++ b/fpga/vivado/nonproject.tcl @@ -28,11 +28,13 @@ read_vhdl -vhdl2008 ../rtl/acquisition_stream.vhd read_vhdl -vhdl2008 ../rtl/adc_capture.vhd read_vhdl -vhdl2008 ../rtl/adc_range_monitor.vhd read_vhdl -vhdl2008 ../rtl/adc_sample_stream.vhd +read_vhdl -vhdl2008 ../rtl/deglitch.vhd read_vhdl -vhdl2008 ../rtl/dma_axi_master.vhd read_vhdl -vhdl2008 ../rtl/dma_write_channel.vhd read_vhdl -vhdl2008 ../rtl/registers.vhd read_vhdl -vhdl2008 ../rtl/sample_decimation.vhd read_vhdl -vhdl2008 ../rtl/shift_engine.vhd +read_vhdl -vhdl2008 ../rtl/syncdff.vhd read_vhdl -vhdl2008 ../rtl/simple_fifo.vhd read_vhdl -vhdl2008 ../rtl/timestamp_gen.vhd read_vhdl -vhdl2008 ../rtl/trigger_detector.vhd diff --git a/os/src/userspace/testje.c b/os/src/userspace/testje.c index f686c0a..2955071 100644 --- a/os/src/userspace/testje.c +++ b/os/src/userspace/testje.c @@ -59,6 +59,9 @@ #define REG_ADC1_MINMAX 0x0294 #define REG_ADC2_MINMAX 0x0298 #define REG_ADC3_MINMAX 0x029c +#define REG_DIG_SIMULATE 0x0330 +#define REG_DIG_SAMPLE 0x0338 +#define REG_LED_STATE 0x0404 struct puzzlefw_context { @@ -628,6 +631,10 @@ static void show_status(struct puzzlefw_context *ctx) (v >> 16) & 0xffff, adc_range & 0xffff, (adc_range >> 16) & 0xffff); + + v = puzzlefw_read_reg(ctx, REG_DIG_SAMPLE); + printf(" digital input = %d %d %d %d\n", + (v >> 3) & 1, (v >> 2) & 1, (v >> 1) & 1, v & 1); } @@ -1065,6 +1072,7 @@ int main(int argc, char **argv) int avgon = 0, avgoff = 0; int simon = 0, simoff = 0; int rangeclear = 0; + int set_digsim = 0, digsim = 0; if (argc == 2 && strcmp(argv[1], "show") == 0) { show = 1; @@ -1114,6 +1122,13 @@ int main(int argc, char **argv) simoff = 1; } else if (argc == 2 && strcmp(argv[1], "rangeclear") == 0) { rangeclear = 1; + } else if (argc == 3 && strcmp(argv[1], "digsim") == 0) { + set_digsim = 1; + if (strcmp(argv[2], "off") == 0) { + digsim = -1; + } else { + digsim = atoi(argv[2]); + } } else if (argc == 2 && strcmp(argv[1], "server") == 0) { server = 1; } else { @@ -1185,6 +1200,9 @@ int main(int argc, char **argv) " testje rangeclear\n" " Clear min/max ADC sample monitor.\n" "\n" + " testje digsim N/'off'\n" + " Set simulated digital input.\n" + "\n" " testje server\n" " Open TCP port 5001 to stream DMA data.\n" "\n"); @@ -1288,6 +1306,14 @@ int main(int argc, char **argv) puzzlefw_write_reg(&ctx, REG_ADC_RANGE_CLEAR, 1); } + if (set_digsim) { + if (digsim < 0) { + puzzlefw_write_reg(&ctx, REG_DIG_SIMULATE, 0); + } else { + puzzlefw_write_reg(&ctx, REG_DIG_SIMULATE, 0x100 + digsim); + } + } + if (server) { run_server(&ctx); }