-- -- Memory-mapped registers for Red Pitaya PuzzleFW firmware. -- -- Joris van Rantwijk 2024 -- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use work.puzzlefw_pkg.all; entity registers is port ( -- Main clock, active on rising edge. clk: in std_logic; -- Reset, active high, synchronous to main clock. reset: in std_logic; -- APB bus interface. apb_psel: in std_logic; apb_penable: in std_logic; apb_pwrite: in std_logic; apb_paddr: in std_logic_vector(31 downto 0); apb_pwdata: in std_logic_vector(31 downto 0); apb_pready: out std_logic; apb_pslverr: out std_logic; apb_prdata: out std_logic_vector(31 downto 0); -- FPGA interface. reg_control: out registers_control; reg_status: in registers_status ); end entity; architecture arch of registers is type regs_type is record pready: std_logic; prdata: std_logic_vector(31 downto 0); reg_control: registers_control; end record; constant regs_init: regs_type := ( pready => '0', prdata => (others => '0'), reg_control => registers_control_init ); signal r: regs_type := regs_init; signal rnext: regs_type; begin -- Drive output signals. apb_pready <= r.pready; apb_pslverr <= '0'; -- never signal errors apb_prdata <= r.prdata; reg_control <= r.reg_control; -- Combinatorial process. process (all) is variable v: regs_type; begin -- Load current register values. v := r; -- Clear single-cycle trigger pulses. v.reg_control.dma_clear := '0'; v.reg_control.timestamp_clear := '0'; v.reg_control.acq_dma_init := '0'; v.reg_control.acq_intr_clear := '0'; v.reg_control.trig_force := '0'; v.reg_control.adc_range_clear := '0'; v.reg_control.tt_dma_init := '0'; v.reg_control.tt_intr_clear := '0'; v.reg_control.timetagger_mark := '0'; -- Respond to each APB access on the next clock cycle (no wait states). v.pready := apb_psel and (not apb_penable); -- Handle APB read transfer. if apb_psel = '1' and apb_penable = '0' and apb_pwrite = '0' then -- Initialize result to all-zero bits. v.prdata := (others => '0'); -- Determine read result as function of address. case to_integer(unsigned(apb_paddr and reg_addr_mask)) is when reg_info => v.prdata := fw_info_word; when reg_irq_enable => v.prdata(0) := r.reg_control.irq_enable; when reg_irq_pending => v.prdata(1 downto 0) := reg_status.irq_pending; when reg_dma_en => v.prdata(0) := r.reg_control.dma_en; when reg_dma_status => v.prdata(0) := reg_status.dma_busy; v.prdata(1) := reg_status.dma_err_read; v.prdata(2) := reg_status.dma_err_write; v.prdata(3) := reg_status.dma_err_address; v.prdata(4) := reg_status.dma_err_any; when reg_timestamp_lo => v.prdata := reg_status.timestamp(31 downto 0); when reg_timestamp_hi => v.prdata(15 downto 0) := reg_status.timestamp(47 downto 32); when reg_acq_addr_start => v.prdata(31 downto 7) := r.reg_control.acq_addr_start; when reg_acq_addr_end => v.prdata(31 downto 7) := r.reg_control.acq_addr_end; when reg_acq_addr_limit => v.prdata(31 downto 7) := r.reg_control.acq_addr_limit; when reg_acq_addr_intr => v.prdata(31 downto 3) := r.reg_control.acq_addr_intr; when reg_acq_addr_ptr => v.prdata(31 downto 3) := reg_status.acq_addr_ptr; when reg_acq_dma_ctrl => v.prdata(0) := r.reg_control.acq_dma_en; when reg_acq_intr_ctrl => v.prdata(0) := r.reg_control.acq_intr_en; when reg_acq_dma_status => v.prdata(0) := reg_status.acq_dma_busy; when reg_acquisition_en => v.prdata(0) := r.reg_control.acquisition_en; when reg_record_length => v.prdata(15 downto 0) := r.reg_control.record_length; when reg_decimation_factor => v.prdata(17 downto 0) := r.reg_control.decimation_factor; when reg_shift_steps => v.prdata(3 downto 0) := r.reg_control.shift_steps; when reg_averaging_en => v.prdata(0) := r.reg_control.averaging_en; when reg_ch4_mode => v.prdata(0) := r.reg_control.ch4_mode; when reg_simulate_adc => v.prdata(0) := r.reg_control.simulate_adc; when reg_trigger_mode => v.prdata(0) := r.reg_control.trig_auto_en; v.prdata(1) := r.reg_control.trig_ext_en; v.prdata(5 downto 4) := r.reg_control.trig_ext_select; v.prdata(7) := r.reg_control.trig_ext_falling; when reg_trigger_delay => v.prdata(15 downto 0) := r.reg_control.trigger_delay; when reg_trigger_status => v.prdata(0) := reg_status.trig_waiting; when reg_adc_sample => v.prdata(adc_data_bits - 1 downto 0) := reg_status.adc_sample(0); v.prdata(adc_data_bits + 15 downto 16) := reg_status.adc_sample(1); when reg_adc23_sample => v.prdata(adc_data_bits - 1 downto 0) := reg_status.adc_sample(2); v.prdata(adc_data_bits + 15 downto 16) := reg_status.adc_sample(3); when reg_adc0_minmax => v.prdata(adc_data_bits - 1 downto 0) := reg_status.adc_min_value(0); v.prdata(adc_data_bits + 15 downto 16) := reg_status.adc_max_value(0); when reg_adc1_minmax => v.prdata(adc_data_bits - 1 downto 0) := reg_status.adc_min_value(1); v.prdata(adc_data_bits + 15 downto 16) := reg_status.adc_max_value(1); when reg_adc2_minmax => v.prdata(adc_data_bits - 1 downto 0) := reg_status.adc_min_value(2); v.prdata(adc_data_bits + 15 downto 16) := reg_status.adc_max_value(2); 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_tt_addr_start => v.prdata(31 downto 7) := r.reg_control.tt_addr_start; when reg_tt_addr_end => v.prdata(31 downto 7) := r.reg_control.tt_addr_end; when reg_tt_addr_limit => v.prdata(31 downto 7) := r.reg_control.tt_addr_limit; when reg_tt_addr_intr => v.prdata(31 downto 3) := r.reg_control.tt_addr_intr; when reg_tt_addr_ptr => v.prdata(31 downto 3) := reg_status.tt_addr_ptr; when reg_tt_dma_ctrl => v.prdata(0) := r.reg_control.tt_dma_en; when reg_tt_intr_ctrl => v.prdata(0) := r.reg_control.tt_intr_en; when reg_tt_dma_status => v.prdata(0) := reg_status.tt_dma_busy; when reg_timetagger_en => v.prdata(7 downto 0) := r.reg_control.timetagger_en; 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; end case; end if; -- Handle APB write transfer. if apb_psel = '1' and apb_penable = '0' and apb_pwrite = '1' then case to_integer(unsigned(apb_paddr and reg_addr_mask)) is when reg_irq_enable => v.reg_control.irq_enable := apb_pwdata(0); when reg_dma_en => v.reg_control.dma_en := apb_pwdata(0); when reg_dma_clear => v.reg_control.dma_clear := apb_pwdata(0); when reg_timestamp_clear => v.reg_control.timestamp_clear := apb_pwdata(0); when reg_acq_addr_start => v.reg_control.acq_addr_start := apb_pwdata(31 downto 7); when reg_acq_addr_end => v.reg_control.acq_addr_end := apb_pwdata(31 downto 7); when reg_acq_addr_limit => v.reg_control.acq_addr_limit := apb_pwdata(31 downto 7); when reg_acq_addr_intr => v.reg_control.acq_addr_intr := apb_pwdata(31 downto 3); when reg_acq_dma_ctrl => v.reg_control.acq_dma_en := apb_pwdata(0); v.reg_control.acq_dma_init := apb_pwdata(1); when reg_acq_intr_ctrl => v.reg_control.acq_intr_en := apb_pwdata(0); v.reg_control.acq_intr_clear := apb_pwdata(1); when reg_acquisition_en => v.reg_control.acquisition_en := apb_pwdata(0); when reg_record_length => v.reg_control.record_length := apb_pwdata(15 downto 0); when reg_decimation_factor => v.reg_control.decimation_factor := apb_pwdata(17 downto 0); when reg_shift_steps => v.reg_control.shift_steps := apb_pwdata(3 downto 0); when reg_averaging_en => v.reg_control.averaging_en := apb_pwdata(0); when reg_ch4_mode => v.reg_control.ch4_mode := apb_pwdata(0); when reg_simulate_adc => v.reg_control.simulate_adc := apb_pwdata(0); when reg_trigger_mode => v.reg_control.trig_auto_en := apb_pwdata(0); v.reg_control.trig_ext_en := apb_pwdata(1); v.reg_control.trig_ext_select := apb_pwdata(5 downto 4); v.reg_control.trig_ext_falling := apb_pwdata(7); 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_tt_addr_start => v.reg_control.tt_addr_start := apb_pwdata(31 downto 7); when reg_tt_addr_end => v.reg_control.tt_addr_end := apb_pwdata(31 downto 7); when reg_tt_addr_limit => v.reg_control.tt_addr_limit := apb_pwdata(31 downto 7); when reg_tt_addr_intr => v.reg_control.tt_addr_intr := apb_pwdata(31 downto 3); when reg_tt_dma_ctrl => v.reg_control.tt_dma_en := apb_pwdata(0); v.reg_control.tt_dma_init := apb_pwdata(1); when reg_tt_intr_ctrl => v.reg_control.tt_intr_en := apb_pwdata(0); v.reg_control.tt_intr_clear := apb_pwdata(1); when reg_timetagger_en => v.reg_control.timetagger_en := apb_pwdata(7 downto 0); when reg_timetagger_mark => v.reg_control.timetagger_mark := apb_pwdata(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; end case; end if; -- Synchronous reset. if reset = '1' then v := regs_init; 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;