Rework DMA to support single-beat transfers
This commit is contained in:
parent
c50dd84011
commit
4abc2ee165
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue