mor1kx-bemicrocv/ip/intercon/rtl/dma.vhd

241 lines
9.0 KiB
VHDL
Raw Permalink Normal View History

2016-08-04 19:22:38 +02:00
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.wishbone_package.all;
-- Assumption: wishbone_data_width >= wishbone_address_Width
entity dma is
generic(
-- Value 0 cannot stream
-- Value 1 only slaves with async ACK can stream
-- Value 2 only slaves with combined latency = 2 can stream
-- Value 3 only slaves with combined latency = 6 can stream
-- Value 4 only slaves with combined latency = 14 can stream
-- ....
logRingLen : integer := 4
);
port(
-- Common wishbone signals
clk : in std_logic;
rst : in std_logic;
-- Slave control port
slave_i : in wishbone_slave_in;
slave_o : out wishbone_slave_out;
-- Master reader port
r_master_i : in wishbone_master_in;
r_master_o : out wishbone_master_out;
-- Master writer port
w_master_i : in wishbone_master_in;
w_master_o : out wishbone_master_out;
-- Pulsed high completion signal
interrupt : out std_logic
);
end dma;
architecture rtl of dma is
constant ringLen : integer := 2**logRingLen;
type ring_t is array (ringLen-1 downto 0) of wishbone_data;
-- Ring buffer for shipping data from read master to write master
signal ring : ring_t;
-- State registers (pointer into the ring)
-- Invariant: read_issue_offset >= read_result_offset >= write_issue_offset >= write_result_offset
-- read_issue_offset - write_result_offset <= ringLen (*NOT* strict '<')
signal read_issue_offset : unsigned(logRingLen downto 0);
signal read_result_offset : unsigned(logRingLen downto 0);
signal write_issue_offset : unsigned(logRingLen downto 0);
signal write_result_offset : unsigned(logRingLen downto 0);
-- DMA control registers
signal read_issue_address : wishbone_address;
signal write_issue_address : wishbone_address;
signal read_stride : wishbone_address;
signal write_stride : wishbone_address;
signal transfer_count : wishbone_address;
-- result status: fail/ok ?
-- Registered wishbone control signals
signal r_master_o_CYC : std_logic;
signal w_master_o_CYC : std_logic;
signal r_master_o_STB : std_logic;
signal w_master_o_STB : std_logic;
signal slave_o_ACK : std_logic;
signal slave_o_DAT : wishbone_data;
function active_high(x : boolean)
return std_logic is
begin
if (x) then
return '1';
else
return '0';
end if;
end active_high;
function index(x : unsigned(logRingLen downto 0))
return integer is
begin
if logRingLen > 0 then
return to_integer(x(logRingLen-1 downto 0));
else
return 0;
end if;
end index;
procedure update(signal o : out wishbone_address) is
begin
for i in (wishbone_data_width/8)-1 downto 0 loop
if slave_i.SEL(i) = '1' then
o(i*8+7 downto i*8) <= slave_i.DAT(i*8+7 downto i*8);
end if;
end loop;
end update;
begin
-- Hard-wired slave pins
slave_o.ACK <= slave_o_ACK;
slave_o.ERR <= '0';
slave_o.RTY <= '0';
slave_o.STALL <= '0';
slave_o.DAT <= slave_o_DAT;
-- Hard-wired master pins
r_master_o.CYC <= r_master_o_CYC;
w_master_o.CYC <= w_master_o_CYC;
r_master_o.STB <= r_master_o_STB;
w_master_o.STB <= w_master_o_STB;
r_master_o.ADR <= read_issue_address;
w_master_o.ADR <= write_issue_address;
r_master_o.SEL <= (others => '1');
w_master_o.SEL <= (others => '1');
r_master_o.WE <= '0';
w_master_o.WE <= '1';
r_master_o.DAT <= (others => '0');
w_master_o.DAT <= ring(index(write_issue_offset));
main : process(clk)
variable read_issue_progress : boolean;
variable read_result_progress : boolean;
variable write_issue_progress : boolean;
variable write_result_progress : boolean;
variable new_read_issue_offset : unsigned(logRingLen downto 0);
variable new_read_result_offset : unsigned(logRingLen downto 0);
variable new_write_issue_offset : unsigned(logRingLen downto 0);
variable new_write_result_offset : unsigned(logRingLen downto 0);
variable new_transfer_count : wishbone_address;
variable ring_boundary : boolean;
variable ring_high : boolean;
variable ring_full : boolean;
variable ring_empty : boolean;
variable done_transfer : boolean;
begin
if (rising_edge(clk)) then
if (rst = '1') then
read_issue_offset <= (others => '0');
read_result_offset <= (others => '0');
write_issue_offset <= (others => '0');
write_result_offset <= (others => '0');
read_issue_address <= (others => '0');
write_issue_address <= (others => '0');
read_stride <= (others => '0');
write_stride <= (others => '0');
transfer_count <= (others => '0');
r_master_o_CYC <= '0';
w_master_o_CYC <= '0';
r_master_o_STB <= '0';
w_master_o_STB <= '0';
slave_o_ACK <= '0';
slave_o_DAT <= (others => '0');
interrupt <= '0';
else
-- Output any read the user requests
case to_integer(unsigned(slave_i.ADR(4 downto 2))) is
when 0 => slave_o_DAT <= read_issue_address;
when 1 => slave_o_DAT <= write_issue_address;
when 2 => slave_o_DAT <= read_stride;
when 3 => slave_o_DAT <= write_stride;
when 4 => slave_o_DAT <= transfer_count;
when others => slave_o_DAT <= (others => '0');
end case;
-- Detect bus progress
read_issue_progress := r_master_o_STB = '1' and r_master_i.STALL = '0';
write_issue_progress := w_master_o_STB = '1' and w_master_i.STALL = '0';
read_result_progress := r_master_o_CYC = '1' and (r_master_i.ACK = '1' or r_master_i.ERR = '1' or r_master_i.RTY = '1');
write_result_progress := w_master_o_CYC = '1' and (w_master_i.ACK = '1' or w_master_i.ERR = '1' or w_master_i.RTY = '1');
-- Advance read pointers
if read_issue_progress then
read_issue_address <= std_logic_vector(unsigned(read_issue_address) + unsigned(read_stride));
new_read_issue_offset := read_issue_offset + 1;
new_transfer_count := std_logic_vector(unsigned(transfer_count) - 1);
else
new_read_issue_offset := read_issue_offset;
new_transfer_count := transfer_count;
end if;
if read_result_progress then
ring(index(read_result_offset)) <= r_master_i.DAT;
new_read_result_offset := read_result_offset + 1;
else
new_read_result_offset := read_result_offset;
end if;
-- Advance write pointers
if write_issue_progress then
write_issue_address <= std_logic_vector(unsigned(write_issue_address) + unsigned(write_stride));
new_write_issue_offset := write_issue_offset + 1;
else
new_write_issue_offset := write_issue_offset;
end if;
if write_result_progress then
new_write_result_offset := write_result_offset + 1;
else
new_write_result_offset := write_result_offset;
end if;
ring_boundary := index(new_read_issue_offset) = index(new_write_result_offset);
ring_high := new_read_issue_offset(logRingLen) /= new_write_result_offset(logRingLen);
ring_full := ring_boundary and ring_high;
ring_empty := ring_boundary and not ring_high;
-- Shorten the critical path by comparing to the undecremented value
--done_transfer := unsigned(new_transfer_count) = 0;
done_transfer := unsigned(transfer_count(wishbone_address_width-1 downto 1)) = 0
and (read_issue_progress or transfer_count(0) = '0');
r_master_o_STB <= active_high (not ring_full and not done_transfer);
r_master_o_CYC <= active_high((not ring_full and not done_transfer) or
(new_read_result_offset /= new_read_issue_offset));
w_master_o_STB <= active_high (new_write_issue_offset /= new_read_result_offset);
w_master_o_CYC <= active_high (new_write_result_offset /= new_read_result_offset);
interrupt <= active_high (write_result_progress and done_transfer and ring_empty);
transfer_count <= new_transfer_count;
read_issue_offset <= new_read_issue_offset;
read_result_offset <= new_read_result_offset;
write_issue_offset <= new_write_issue_offset;
write_result_offset <= new_write_result_offset;
-- Control logic
if (slave_i.CYC = '1' and slave_i.STB = '1' and slave_i.WE = '1') then
case to_integer(unsigned(slave_i.ADR(4 downto 2))) is
when 0 => update(read_issue_address);
when 1 => update(write_issue_address);
when 2 => update(read_stride);
when 3 => update(write_stride);
when 4 => update(transfer_count);
when others => null;
end case;
end if;
slave_o_ACK <= slave_i.CYC and slave_i.STB;
end if;
end if;
end process;
end rtl;