diff --git a/fpga/rtl/dma_axi_master.vhd b/fpga/rtl/dma_axi_master.vhd index 22ebab1..4188f22 100644 --- a/fpga/rtl/dma_axi_master.vhd +++ b/fpga/rtl/dma_axi_master.vhd @@ -1,6 +1,13 @@ -- -- AXI3 master for multi-channel DMA controller. -- +-- Note: DMA transfers may not cross a 4 kB address boundary. +-- +-- It is recommended to use only so-called regular transactions. +-- A transaction is regular if its length is 1, 2, 4, 8 or 16 beats, +-- and its address is aligned to the burst length. +-- Regular transactions never cross a 4 kB boundary. +-- -- Joris van Rantwijk 2024 -- @@ -14,9 +21,6 @@ use work.puzzlefw_pkg.all; entity dma_axi_master is generic ( - -- Number of beats per transfer. - transfer_size: integer range 1 to 16 := 16; - -- Number of read channels. num_read_channels: integer range 0 to 16; @@ -57,10 +61,11 @@ entity dma_axi_master is -- Read channels. -- The client passes a read command for a specified address via valid/ready handshake. - -- Some time later, the controller pushes (transfer_size) data words out to the channel. + -- Some time later, the controller pushes (read_cmd_length + 1) data words out to the channel. -- The client must be ready to accept all data words. -- Multiple transfers can be in flight for the channel. read_cmd_addr: in dma_address_array(0 to num_read_channels-1); + read_cmd_length: in dma_burst_length_array(0 to num_read_channels-1); read_cmd_valid: in std_logic_vector(num_read_channels-1 downto 0); read_cmd_ready: out std_logic_vector(num_read_channels-1 downto 0); read_data: out dma_data_array(0 to num_read_channels-1); @@ -68,11 +73,12 @@ entity dma_axi_master is -- Write channels. -- The client passes a write command for a specified address via valid/ready handshake. - -- Some time later, the controller pulls (transfer_size) data words from the channel. + -- Some time later, the controller pulls (write_cmd_length + 1) data words from the channel. -- The client must supply these data words promptly. -- Some more time later, the controller pulses write_finished to indicate that the write has finished. -- Multiple transfers can be in flight for the channel. write_cmd_addr: in dma_address_array(0 to num_write_channels-1); + write_cmd_length: in dma_burst_length_array(0 to num_write_channels-1); write_cmd_valid: in std_logic_vector(num_write_channels-1 downto 0); write_cmd_ready: out std_logic_vector(num_write_channels-1 downto 0); write_data: in dma_data_array(0 to num_write_channels-1); @@ -131,11 +137,13 @@ architecture arch of dma_axi_master is -- Registered output signals to AXI bus. awaddr: std_logic_vector(31 downto 3); + awlen: std_logic_vector(3 downto 0); awvalid: std_logic; wdata: std_logic_vector(63 downto 0); wlast: std_logic; wvalid: std_logic; araddr: std_logic_vector(31 downto 3); + arlen: std_logic_vector(3 downto 0); arvalid: std_logic; -- Registered output signals to read channels. @@ -171,11 +179,13 @@ architecture arch of dma_axi_master is constant regs_init: regs_type := ( awaddr => (others => '0'), + awlen => (others => '0'), awvalid => '0', wdata => (others => '0'), wlast => '0', wvalid => '0', araddr => (others => '0'), + arlen => (others => '0'), arvalid => '0', read_cmd_ready => (others => '0'), read_data => (others => '0'), @@ -204,11 +214,12 @@ architecture arch of dma_axi_master is -- Check that the DMA transfer fits inside the address window if it starts at the specified address offset function is_valid_dma_address(addr: std_logic_vector(31 downto 3); + len: std_logic_vector(3 downto 0); limit: std_logic_vector(31 downto 12)) return boolean is begin return (unsigned(limit) /= 0) - and (unsigned(addr) <= shift_left(resize(unsigned(limit), 29), 9) - transfer_size); + and (unsigned(addr) < shift_left(resize(unsigned(limit), 29), 9) - unsigned(len)); end function; -- Calculate tha AXI address for a DMA transfer by adding the address offset from the client @@ -224,8 +235,6 @@ architecture arch of dma_axi_master is begin -- Drive fixed output signals to AXI bus. - m_axi_awlen <= std_logic_vector(to_unsigned(transfer_size - 1, 4)); -- use fixed burst length - m_axi_arlen <= std_logic_vector(to_unsigned(transfer_size - 1, 4)); -- use fixed burst length m_axi_awsize <= "011"; -- always use 64-bit transfers m_axi_arsize <= "011"; -- always use 64-bit transfers m_axi_awburst <= "01"; -- always use incrementing burst @@ -245,6 +254,7 @@ begin -- Drive variable output signals to AXI bus. m_axi_awid <= std_logic_vector(to_unsigned(r.write_channel, 6)); m_axi_awaddr <= r.awaddr & "000"; -- addresses are 8-byte aligned + m_axi_awlen <= r.awlen; m_axi_awvalid <= r.awvalid; m_axi_wid <= std_logic_vector(to_unsigned(r.write_channel, 6)); m_axi_wdata <= r.wdata; @@ -252,6 +262,7 @@ begin m_axi_wvalid <= r.wvalid; m_axi_arid <= std_logic_vector(to_unsigned(r.read_channel, 6)); m_axi_araddr <= r.araddr & "000"; -- addresses are 8-byte aligned + m_axi_arlen <= r.arlen; m_axi_arvalid <= r.arvalid; -- Drive output signals to read channels. @@ -340,23 +351,26 @@ begin when WRITE_STATE_START => -- Calculate the AXI address by adding the client address offset to the window base address. v.awaddr := calc_dma_address(write_cmd_addr(r.write_channel), window_base_addr); + v.awlen := write_cmd_length(r.write_channel); -- Setup first data word. v.wdata := write_data(r.write_channel); - v.cnt_write_beat := (others => '0'); - if transfer_size = 1 then + v.cnt_write_beat := unsigned(write_cmd_length(r.write_channel)); + if unsigned(write_cmd_length(r.write_channel)) = 0 then v.wlast := '1'; else v.wlast := '0'; end if; -- Check address. - if is_valid_dma_address(write_cmd_addr(r.write_channel), window_size) then + if is_valid_dma_address(write_cmd_addr(r.write_channel), + write_cmd_length(r.write_channel), + window_size) then -- Setup AXI write burst. v.awvalid := '1'; v.wvalid := '1'; v.cnt_write_start := r.cnt_write_start + 1; - if transfer_size = 1 then + if unsigned(write_cmd_length(r.write_channel)) = 0 then v.write_state := WRITE_STATE_WAIT; else v.write_state := WRITE_STATE_FLOW; @@ -385,14 +399,14 @@ begin -- Push data words to interconnect. if m_axi_wready = '1' then v.wdata := write_data(r.write_channel); - if r.cnt_write_beat = transfer_size - 2 then + if r.cnt_write_beat = 1 then -- This will be the last beat of the transfer. v.wlast := '1'; v.write_state := WRITE_STATE_WAIT; else v.wlast := '0'; end if; - v.cnt_write_beat := r.cnt_write_beat + 1; + v.cnt_write_beat := r.cnt_write_beat - 1; end if; when WRITE_STATE_WAIT => @@ -470,9 +484,12 @@ begin -- Calculate the AXI address by adding the client address offset to the window base address. v.araddr := calc_dma_address(read_cmd_addr(r.read_channel), window_base_addr); + v.arlen := read_cmd_length(r.read_channel); -- Check address. - if is_valid_dma_address(read_cmd_addr(r.read_channel), window_size) then + if is_valid_dma_address(read_cmd_addr(r.read_channel), + read_cmd_length(r.read_channel), + window_size) then -- Setup AXI read burst. v.arvalid := '1'; v.read_state := READ_STATE_WAIT; diff --git a/fpga/rtl/dma_write_channel.vhd b/fpga/rtl/dma_write_channel.vhd index 7a80db0..08cdd6f 100644 --- a/fpga/rtl/dma_write_channel.vhd +++ b/fpga/rtl/dma_write_channel.vhd @@ -1,8 +1,6 @@ -- -- Management of DMA transfers from FPGA to memory. -- --- The AXI master MUST be configured for 16 beats per transfer. --- -- Joris van Rantwijk 2024 -- @@ -16,8 +14,16 @@ use work.puzzlefw_pkg.all; entity dma_write_channel is generic ( + -- Preferred DMA burst length as the 2-log of the number of beats. + -- Transactions will use the preferred burst length unless there + -- are fewer words available in the buffer. + transfer_size_bits: integer range 0 to 4 := 4; + -- Size of the input data queue as 2-log of the number of words. - queue_size_bits: integer range 5 to 16 := 10 + queue_size_bits: integer range 5 to 16 := 10; + + -- Number of clock cycles to wait before starting a small DMA burst. + idle_timeout: natural := 256 ); port ( @@ -32,6 +38,10 @@ entity dma_write_channel is -- no new transfer will be started. channel_en: in std_logic; + -- High if there uncompleted DMA transfers are in progress. + -- If a DMA error occurs, the channel may get stuck in a busy state. + channel_busy: out std_logic; + -- High to initialize the channel. -- -- This resets the write pointer to the start of the buffer and deletes @@ -39,7 +49,7 @@ entity dma_write_channel is -- -- This function must be used whenever the buffer address range is changed. -- This function must not be used while uncompleted DMA transfers - -- are in progress. + -- are in progress, except to recover from an error. channel_init: in std_logic; -- Start and end address of the DMA buffer. @@ -55,10 +65,10 @@ entity dma_write_channel is -- An interrupt is triggered when the write pointer equals the -- interrupt address. - addr_interrupt: in std_logic_vector(31 downto 7); + addr_interrupt: in std_logic_vector(31 downto 3); -- Write pointer. - addr_pointer: out std_logic_vector(31 downto 7); + addr_pointer: out std_logic_vector(31 downto 3); -- High to enable interrupt on reaching a configured address. intr_en: in std_logic; @@ -77,6 +87,7 @@ entity dma_write_channel is -- Signals to AXI master. write_cmd_addr: out dma_address_type; + write_cmd_length: out dma_burst_length_type; write_cmd_valid: out std_logic; write_cmd_ready: in std_logic; write_data: out dma_data_type; @@ -88,37 +99,56 @@ end entity; architecture arch of dma_write_channel is - -- Number of beats per DMA transfer. - -- The AXI master must use the same transfer size. - -- Address alignments must match the transfer size. - constant transfer_size: integer := 16; + constant transfer_size: integer range 1 to 16 := 2**transfer_size_bits; - type state_type is (STATE_IDLE, STATE_START, STATE_FLOW); + type state_type is (STATE_SINGLE_IDLE, STATE_SINGLE_START, STATE_SINGLE_DATA, + STATE_BURST_PREPARE, + STATE_BURST_IDLE, STATE_BURST_START, STATE_BURST_DATA); type regs_type is record cmd_valid: std_logic; - cmd_addr: std_logic_vector(31 downto 7); - addr_pointer: std_logic_vector(31 downto 7); + cmd_addr: std_logic_vector(31 downto 3); + cmd_full_burst: std_logic; + addr_pointer: std_logic_vector(31 downto 3); intr_out: std_logic; + channel_busy: std_logic; state: state_type; - pending_beats: unsigned(4 downto 0); + pending_beats: unsigned(3 downto 0); + pending_transfers: unsigned(3 downto 0); + idle_timer: integer range 0 to idle_timeout - 1; end record; constant regs_init: regs_type := ( - cmd_valid => '0', - cmd_addr => (others => '0'), - addr_pointer => (others => '0'), - intr_out => '0', - state => STATE_IDLE, - pending_beats => (others => '0') + cmd_valid => '0', + cmd_addr => (others => '0'), + cmd_full_burst => '0', + addr_pointer => (others => '0'), + intr_out => '0', + channel_busy => '0', + state => STATE_SINGLE_IDLE, + pending_beats => (others => '0'), + pending_transfers => (others => '0'), + idle_timer => 0 ); signal r: regs_type := regs_init; signal rnext: regs_type; signal s_fifo_reset: std_logic; + signal s_fifo_valid: std_logic; signal s_fifo_length: unsigned(queue_size_bits - 1 downto 0); + -- Return true if address is aligned to a full-size burst. + function is_address_aligned(addr: std_logic_vector(31 downto 3)) + return boolean + is begin + if transfer_size = 1 then + return true; + else + return (unsigned(addr(transfer_size_bits + 2 downto 3)) = 0); + end if; + end function; + begin -- @@ -134,19 +164,22 @@ begin in_valid => in_valid, in_ready => in_ready, in_data => in_data, - out_valid => open, + out_valid => s_fifo_valid, out_ready => write_data_ready, out_data => write_data, queue_length => s_fifo_length ); -- Drive output ports. - addr_pointer <= r.addr_pointer; - intr_out <= r.intr_out; - write_cmd_addr <= r.cmd_addr & "0000"; - write_cmd_valid <= r.cmd_valid; + channel_busy <= r.channel_busy; + addr_pointer <= r.addr_pointer; + intr_out <= r.intr_out; + write_cmd_addr <= r.cmd_addr; + write_cmd_length <= "0000" when (r.cmd_full_burst = '0') else + std_logic_vector(to_unsigned(transfer_size - 1, 4)); + write_cmd_valid <= r.cmd_valid; -- Drive reset signal to FIFO. - s_fifo_reset <= reset or channel_init; + s_fifo_reset <= reset or channel_init; -- -- Combinatorial process. @@ -160,36 +193,137 @@ begin -- State machine. case r.state is - when STATE_IDLE => - -- Issue a new transfer when possible. - -- Data for the whole transfer must be ready in the FIFO. - if (channel_en = '1') - and (s_fifo_length >= transfer_size) - and (r.cmd_addr /= addr_limit) then - v.cmd_valid := '1'; - v.state := STATE_START; - v.pending_beats := to_unsigned(transfer_size, v.pending_beats'length); + when STATE_SINGLE_IDLE => + + v.cmd_full_burst := '0'; + + -- Clear busy flag. + if r.pending_transfers = 0 then + v.channel_busy := '0'; end if; - when STATE_START => + -- Update idle timer. + if (s_fifo_valid = '1') and (r.idle_timer /= idle_timeout - 1) then + v.idle_timer := r.idle_timer + 1; + end if; + + -- Switch to burst mode if the FIFO has enough data, + -- and the address is aligned to a full burst. + if (transfer_size > 1) + and (s_fifo_length >= transfer_size) + and is_address_aligned(r.cmd_addr) then + + v.state := STATE_BURST_PREPARE; + + -- Otherwise, start a single-beat transfer when possible. + elsif (channel_en = '1') + and (s_fifo_valid = '1') + and (r.cmd_addr(31 downto 7) /= addr_limit) + and (r.pending_transfers /= 15) + and ((transfer_size = 1) + or (not is_address_aligned(r.cmd_addr)) + or (r.idle_timer = idle_timeout - 1)) then + + v.cmd_valid := '1'; + v.channel_busy := '1'; + v.pending_beats := to_unsigned(0, v.pending_beats'length); + v.state := STATE_SINGLE_START; + + end if; + + when STATE_SINGLE_START => + -- Wait until start of transfer. + + if write_cmd_ready = '1' then + v.cmd_valid := '0'; + + -- Update pending transfers. + v.pending_transfers := r.pending_transfers + 1; + + -- Update command address. + v.cmd_addr := std_logic_vector(unsigned(r.cmd_addr) + 1); + if v.cmd_addr(31 downto 7) = addr_end then + -- Wrap at end of buffer. + v.cmd_addr := addr_start & "0000"; + end if; + + if write_data_ready = '1' then + v.state := STATE_SINGLE_IDLE; + else + v.state := STATE_SINGLE_DATA; + end if; + end if; + + -- Reset idle counter. + v.idle_timer := 0; + + when STATE_SINGLE_DATA => + + -- Wait until data accepted. + if (write_data_ready = '1') and (r.pending_beats = 0) then + v.state := STATE_SINGLE_IDLE; + end if; + + when STATE_BURST_PREPARE => + + -- Wait until pending transfers are completed. + if r.pending_transfers = 0 then + v.state := STATE_BURST_IDLE; + end if; + + -- Reset idle counter. + v.idle_timer := 0; + + when STATE_BURST_IDLE => + + v.cmd_full_burst := '1'; + + -- Clear busy flag. + if r.pending_transfers = 0 then + v.channel_busy := '0'; + end if; + + -- Switch to single transfer mode if FIFO has insufficient data. + if (s_fifo_length < transfer_size) + and (r.pending_transfers = 0) then + v.state := STATE_SINGLE_IDLE; + end if; + + -- Start a burst transfer if possible. + if (channel_en = '1') + and (s_fifo_length >= transfer_size) + and (r.cmd_addr(31 downto 7) /= addr_limit) + and (r.pending_transfers /= 15) then + v.cmd_valid := '1'; + v.channel_busy := '1'; + v.pending_beats := to_unsigned(transfer_size - 1, v.pending_beats'length); + v.state := STATE_BURST_START; + end if; + + when STATE_BURST_START => + -- Wait until start of transfer. if write_cmd_ready = '1' then v.cmd_valid := '0'; + -- Update pending transfers. + v.pending_transfers := r.pending_transfers + 1; + -- Update command address. - v.cmd_addr := std_logic_vector(unsigned(r.cmd_addr) + 1); - if v.cmd_addr = addr_end then + v.cmd_addr := std_logic_vector(unsigned(r.cmd_addr) + transfer_size); + if v.cmd_addr(31 downto 7) = addr_end then -- Wrap at end of buffer. - v.cmd_addr := addr_start; + v.cmd_addr := addr_start & "0000"; end if; - v.state := STATE_FLOW; + v.state := STATE_BURST_DATA; end if; - when STATE_FLOW => + when STATE_BURST_DATA => + -- Wait until last beat. - if (write_data_ready = '1') and (r.pending_beats = 1) then - v.state := STATE_IDLE; + if (write_data_ready = '1') and (r.pending_beats = 0) then + v.state := STATE_BURST_IDLE; end if; end case; @@ -200,17 +334,29 @@ begin end if; -- Update pointer on write completion. - if write_finished = '1' then - v.addr_pointer := std_logic_vector(unsigned(r.addr_pointer) + 1); - if v.addr_pointer = addr_end then - -- Wrap at end of buffer. - v.addr_pointer := addr_start; - end if; - end if; + if (write_finished = '1') and (r.pending_transfers /= 0) then + v.pending_transfers := v.pending_transfers - 1; + + if r.cmd_full_burst = '1' then + -- Completion of a full burst. + v.addr_pointer := std_logic_vector(unsigned(r.addr_pointer) + transfer_size); + + -- Raise interrupt when pointer passes threshold. + if (intr_en = '1') + and (r.addr_pointer(31 downto 3 + transfer_size_bits) + = addr_interrupt(31 downto 3 + transfer_size_bits)) then + v.intr_out := '1'; + end if; + else + -- Completion of single transfer. + v.addr_pointer := std_logic_vector(unsigned(r.addr_pointer) + 1); + end if; + + -- Wrap at end of buffer. + if v.addr_pointer(31 downto 7) = addr_end then + v.addr_pointer := addr_start & "0000"; + end if; - -- Clear interrupt. - if intr_clear = '1' then - v.intr_out := '0'; end if; -- Raise interrupt when pointer equals threshold. @@ -218,13 +364,18 @@ begin v.intr_out := '1'; end if; - -- Initialize channel. - if channel_init = '1' then - v.cmd_valid := '0'; - v.cmd_addr := addr_start; - v.addr_pointer := addr_start; + -- Clear interrupt. + if intr_clear = '1' then v.intr_out := '0'; - v.state := STATE_IDLE; + end if; + + -- Initialize pointers. + if channel_init = '1' then + v.cmd_addr := addr_start & "0000"; + v.addr_pointer := addr_start & "0000"; + v.state := STATE_SINGLE_IDLE; + v.pending_transfers := (others => '0'); + v.idle_timer := 0; end if; -- Synchronous reset. diff --git a/fpga/rtl/puzzlefw_pkg.vhd b/fpga/rtl/puzzlefw_pkg.vhd index 28c88b8..d574368 100644 --- a/fpga/rtl/puzzlefw_pkg.vhd +++ b/fpga/rtl/puzzlefw_pkg.vhd @@ -15,10 +15,27 @@ package puzzlefw_pkg is subtype dma_address_type is std_logic_vector(31 downto 3); type dma_address_array is array(natural range <>) of dma_address_type; + -- Burst length for DMA on AXI bus as number of beats minus 1. + subtype dma_burst_length_type is std_logic_vector(3 downto 0); + type dma_burst_length_array is array (natural range <>) of dma_burst_length_type; + -- 64-bit data for DMA on AXI bus. subtype dma_data_type is std_logic_vector(63 downto 0); type dma_data_array is array(natural range <>) of dma_data_type; + -- 14-bit ADC data. + constant adc_data_bits: integer := 14; + subtype adc_data_type is std_logic_vector(adc_data_bits - 1 downto 0); + type adc_data_array is array(natural range <>) of adc_data_type; + + -- 24-bit averaged data. + constant sample_data_bits: integer := 24; + subtype sample_data_type is std_logic_vector(sample_data_bits - 1 downto 0); + type sample_data_array is array(natural range <>) of sample_data_type; + + -- 48-bit timestamp. + constant timestamp_bits: integer := 48; + -- Register addresses. constant reg_addr_mask: std_logic_vector(31 downto 0) := x"0010fffc"; constant reg_info: natural := 16#000000#; @@ -35,6 +52,7 @@ package puzzlefw_pkg is constant reg_acq_channel_ctrl: natural := 16#000214#; constant reg_acq_intr_ctrl: natural := 16#000218#; constant reg_test_led: natural := 16#000404#; + constant reg_test_divider: natural := 16#000408#; constant reg_dma_buf_addr: natural := 16#100000#; constant reg_dma_buf_size: natural := 16#100004#; @@ -48,6 +66,11 @@ package puzzlefw_pkg is & std_logic_vector(to_unsigned(fw_version_major, 8)) & std_logic_vector(to_unsigned(fw_version_minor, 8)); + -- Data stream. + constant msg_adc_data: std_logic_vector(7 downto 0) := x"01"; + constant msg_trigger: std_logic_vector(7 downto 0) := x"02"; + constant msg_overflow: std_logic_vector(7 downto 0) := x"10"; + -- ADC input port type. type adc_data_input_type is array(0 to 1) of std_logic_vector(15 downto 0); @@ -55,11 +78,12 @@ package puzzlefw_pkg is 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; acq_addr_start: std_logic_vector(31 downto 7); acq_addr_end: std_logic_vector(31 downto 7); acq_addr_limit: std_logic_vector(31 downto 7); - acq_addr_intr: std_logic_vector(31 downto 7); + acq_addr_intr: std_logic_vector(31 downto 3); acq_channel_en: std_logic; acq_intr_en: std_logic; dma_buf_addr: std_logic_vector(31 downto 12); @@ -74,7 +98,8 @@ package puzzlefw_pkg is dma_err_write: std_logic; dma_err_address: std_logic; dma_err_any: std_logic; - acq_addr_ptr: std_logic_vector(31 downto 7); + acq_addr_ptr: std_logic_vector(31 downto 3); + acq_channel_busy: std_logic; end record; -- Trigger registers: write-only access by processor, single-cycle pulse signals to FPGA. @@ -87,6 +112,7 @@ package puzzlefw_pkg is constant registers_control_init: registers_control := ( irq_enable => '0', test_led => (others => '0'), + test_divider => (others => '0'), dma_en => '0', acq_addr_start => (others => '0'), acq_addr_end => (others => '0'), diff --git a/fpga/rtl/puzzlefw_top.vhd b/fpga/rtl/puzzlefw_top.vhd index 7050045..b4290e0 100644 --- a/fpga/rtl/puzzlefw_top.vhd +++ b/fpga/rtl/puzzlefw_top.vhd @@ -137,6 +137,7 @@ architecture arch of puzzlefw_top is signal s_reg_trigger: registers_trigger; 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); signal s_dma_write_cmd_ready: std_logic_vector(0 downto 0); signal s_dma_write_data: dma_data_array(0 downto 0); @@ -150,6 +151,7 @@ architecture arch of puzzlefw_top is 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; @@ -281,7 +283,6 @@ begin -- AXI master. inst_axi_master: entity work.dma_axi_master generic map ( - transfer_size => 16, num_read_channels => 0, num_write_channels => 1 ) port map ( @@ -297,11 +298,13 @@ begin err_any => s_reg_status.dma_err_any, clear_errors => s_reg_trigger.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, @@ -350,11 +353,14 @@ begin -- DMA Write Channel inst_write_channel: entity work.dma_write_channel generic map ( - queue_size_bits => 10 ) + 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_channel_en, + channel_busy => s_reg_status.acq_channel_busy, channel_init => s_reg_trigger.acq_channel_init, addr_start => s_reg_control.acq_addr_start, addr_end => s_reg_control.acq_addr_end, @@ -368,6 +374,7 @@ begin in_ready => s_dma_in_ready, in_data => s_dma_in_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), @@ -384,10 +391,24 @@ begin process (clk_adc) is begin if rising_edge(clk_adc) then - s_dma_in_valid <= '1'; - if s_dma_in_ready = '1' then - s_dma_in_data(15 downto 0) <= std_logic_vector(unsigned(s_dma_in_data(15 downto 0)) + 1); - s_dma_in_data(63 downto 16) <= std_logic_vector(unsigned(s_dma_in_data(63 downto 16)) - 1); + if s_reg_control.test_divider = x"ffff" then + s_dma_in_valid <= '1'; + if s_dma_in_ready = '1' then + s_dma_in_data(47 downto 0) <= std_logic_vector(unsigned(s_dma_in_data(47 downto 0)) + 1); + s_dma_in_data(63 downto 48) <= (others => '0'); + end if; + else + if s_dma_in_ready = '1' then + s_dma_in_valid <= '0'; + end if; + if r_test_cnt >= unsigned(s_reg_control.test_divider) then + r_test_cnt <= (others => '0'); + s_dma_in_valid <= '1'; + s_dma_in_data(47 downto 0) <= std_logic_vector(unsigned(s_dma_in_data(47 downto 0)) + 1); + s_dma_in_data(63 downto 48) <= (others => '0'); + else + r_test_cnt <= r_test_cnt + 1; + end if; end if; end if; end process; diff --git a/fpga/rtl/registers.vhd b/fpga/rtl/registers.vhd index 862083b..b510876 100644 --- a/fpga/rtl/registers.vhd +++ b/fpga/rtl/registers.vhd @@ -101,11 +101,14 @@ begin 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 7) := r.reg_control.acq_addr_intr; - when reg_acq_addr_ptr => v.prdata(31 downto 7) := reg_status.acq_addr_ptr; - when reg_acq_channel_ctrl => v.prdata(0) := r.reg_control.acq_channel_en; + 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_channel_ctrl => + v.prdata(0) := r.reg_control.acq_channel_en; + v.prdata(8) := reg_status.acq_channel_busy; when reg_acq_intr_ctrl => v.prdata(0) := r.reg_control.acq_intr_en; 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_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; @@ -122,7 +125,7 @@ begin 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 7); + when reg_acq_addr_intr => v.reg_control.acq_addr_intr := apb_pwdata(31 downto 3); when reg_acq_channel_ctrl => v.reg_control.acq_channel_en := apb_pwdata(0); v.reg_trigger.acq_channel_init := apb_pwdata(1); @@ -130,6 +133,7 @@ begin v.reg_control.acq_intr_en := apb_pwdata(0); v.reg_trigger.acq_intr_clear := apb_pwdata(1); 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_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;