Newer
Older
--------------------------------------------------------------------------------

Dimitris Lampridis
committed
-- CERN BE-CO-HT
-- GN4124 core for PCIe FMC carrier
-- http://www.ohwr.org/projects/gn4124-core
--------------------------------------------------------------------------------
--

Dimitris Lampridis
committed
-- unit name: dma_controller
--
-- description: Manages the DMA transfers.
--
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

Dimitris Lampridis
committed
-- Copyright and related rights are licensed under the Solderpad Hardware
-- License, Version 2.0 (the "License"); you may not use this file except
-- in compliance with the License. You may obtain a copy of the License at
-- http://solderpad.org/licenses/SHL-2.0.
-- Unless required by applicable law or agreed to in writing, software,
-- hardware and materials distributed under this License is distributed on an
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
-- or implied. See the License for the specific language governing permissions
-- and limitations under the License.
--------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.NUMERIC_STD.all;
entity dma_controller is
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
port (
---------------------------------------------------------
-- GN4124 core clock and reset
clk_i : in std_logic;
rst_n_i : in std_logic;
---------------------------------------------------------
-- Interrupt request
dma_ctrl_irq_o : out std_logic;
---------------------------------------------------------
-- To the L2P DMA master and P2L DMA master
dma_ctrl_carrier_addr_o : out std_logic_vector(31 downto 0);
dma_ctrl_host_addr_h_o : out std_logic_vector(31 downto 0);
dma_ctrl_host_addr_l_o : out std_logic_vector(31 downto 0);
dma_ctrl_len_o : out std_logic_vector(31 downto 0);
dma_ctrl_start_l2p_o : out std_logic; -- To the L2P DMA master
dma_ctrl_start_p2l_o : out std_logic; -- To the P2L DMA master
dma_ctrl_start_next_o : out std_logic; -- To the P2L DMA master
dma_ctrl_byte_swap_o : out std_logic_vector(1 downto 0);
dma_ctrl_abort_o : out std_logic;
dma_ctrl_done_i : in std_logic;
dma_ctrl_error_i : in std_logic;
---------------------------------------------------------
-- From P2L DMA master
next_item_carrier_addr_i : in std_logic_vector(31 downto 0);
next_item_host_addr_h_i : in std_logic_vector(31 downto 0);
next_item_host_addr_l_i : in std_logic_vector(31 downto 0);
next_item_len_i : in std_logic_vector(31 downto 0);
next_item_next_l_i : in std_logic_vector(31 downto 0);
next_item_next_h_i : in std_logic_vector(31 downto 0);
next_item_attrib_i : in std_logic_vector(31 downto 0);
next_item_valid_i : in std_logic;
---------------------------------------------------------
-- Wishbone slave interface
wb_rst_n_i : in std_logic;
wb_clk_i : in std_logic; -- Bus clock
wb_adr_i : in std_logic_vector(3 downto 0); -- Adress
wb_dat_o : out std_logic_vector(31 downto 0); -- Data in
wb_dat_i : in std_logic_vector(31 downto 0); -- Data out
wb_sel_i : in std_logic_vector(3 downto 0); -- Byte select
wb_cyc_i : in std_logic; -- Read or write cycle
wb_stb_i : in std_logic; -- Read or write strobe
wb_we_i : in std_logic; -- Write
wb_ack_o : out std_logic -- Acknowledge
);
architecture arch of dma_controller is
-- Values for the STAT register

Dimitris Lampridis
committed
constant c_DMA_STAT_IDLE : std_logic_vector(1 downto 0) := "00";
constant c_DMA_STAT_BUSY : std_logic_vector(1 downto 0) := "01";
constant c_DMA_STAT_ERROR : std_logic_vector(1 downto 0) := "10";
constant c_DMA_STAT_ABORT : std_logic_vector(1 downto 0) := "11";
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
signal dma_ctrl_start_wb : std_logic;
signal dma_ctrl_abort_wb : std_logic;
signal dma_ctrl_wr_wb : std_logic;
signal dma_ctrl_wack_wb : std_logic;
signal dma_ctrl_byte_swap_wb : std_logic_vector(1 downto 0);
signal dma_ctrl_byte_swap : std_logic_vector(1 downto 0);
signal dma_ctrl_start : std_logic;
signal dma_ctrl_abort : std_logic;
signal dma_ctrl_wr : std_logic;
signal dma_cstart : std_logic_vector(31 downto 0);
signal dma_hstartl : std_logic_vector(31 downto 0);
signal dma_hstarth : std_logic_vector(31 downto 0);
signal dma_len : std_logic_vector(31 downto 0);
signal dma_nextl : std_logic_vector(31 downto 0);
signal dma_nexth : std_logic_vector(31 downto 0);
signal dma_attrib_chain : std_logic;
signal dma_attrib_dir : std_logic;
signal dma_stat_irq_i_wb : std_logic;
signal dma_stat_irq_o_wb : std_logic;
signal dma_stat_status_wb : std_logic_vector(1 downto 0);
signal dma_stat_wr_wb : std_logic;
signal dma_stat_wack_wb : std_logic;
signal dma_stat_rd_wb : std_logic;
signal dma_stat_rack_wb : std_logic;
signal dma_stat_irq_wr_wb : std_logic;
signal dma_stat_irq_wr : std_logic;
signal dma_stat_wr : std_logic;
signal dma_cstart_reg : std_logic_vector(31 downto 0);
signal dma_hstartl_reg : std_logic_vector(31 downto 0);
signal dma_hstarth_reg : std_logic_vector(31 downto 0);
signal dma_len_reg : std_logic_vector(31 downto 0);
signal dma_nextl_reg : std_logic_vector(31 downto 0);
signal dma_nexth_reg : std_logic_vector(31 downto 0);
signal dma_attrib_chain_reg : std_logic;
signal dma_attrib_dir_reg : std_logic;
signal dma_ctrl_byte_swap_reg : std_logic_vector(1 downto 0);
-- DMA controller FSM
type dma_ctrl_state_type is (DMA_IDLE, DMA_START_TRANSFER, DMA_TRANSFER,
DMA_START_CHAIN, DMA_CHAIN);
signal dma_ctrl_current_state : dma_ctrl_state_type;
-- status signals

Dimitris Lampridis
committed
signal dma_stat_reg : std_logic_vector(1 downto 0);
signal dma_irq_reg : std_logic;
attribute keep of dma_cstart, dma_hstartl, dma_hstarth, dma_len,
dma_nextl, dma_nexth, dma_attrib_chain, dma_attrib_dir : signal is "true";
begin
------------------------------------------------------------------------------
-- Wishbone slave instantiation
------------------------------------------------------------------------------
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
inst_dma_controller_regs : entity work.dma_controller_regs
port map (
rst_n_i => wb_rst_n_i,
clk_i => wb_clk_i,
wb_adr_i => wb_adr_i,
wb_dat_i => wb_dat_i,
wb_dat_o => wb_dat_o,
wb_cyc_i => wb_cyc_i,
wb_sel_i => wb_sel_i,
wb_stb_i => wb_stb_i,
wb_we_i => wb_we_i,
wb_ack_o => wb_ack_o,
ctrl_start_o => dma_ctrl_start_wb,
ctrl_start_i => '0',
ctrl_abort_o => dma_ctrl_abort_wb,
ctrl_abort_i => '0',
ctrl_byteswap_o => dma_ctrl_byte_swap_wb,
ctrl_wr_o => dma_ctrl_wr_wb,
ctrl_wack_i => dma_ctrl_wack_wb,
stat_status_o => open,
stat_status_i => dma_stat_status_wb,
stat_irq_i => dma_stat_irq_i_wb,
stat_irq_o => dma_stat_irq_o_wb,
stat_wr_o => dma_stat_wr_wb,
stat_wack_i => dma_stat_wack_wb,
stat_rd_o => dma_stat_rd_wb,
stat_rack_i => dma_stat_rack_wb,
cstart_o => dma_cstart,
hstartl_o => dma_hstartl,
hstarth_o => dma_hstarth,
len_o => dma_len,
nextl_o => dma_nextl,
nexth_o => dma_nexth,
attrib_chain_o => dma_attrib_chain,
attrib_dir_o => dma_attrib_dir,
cur_cstart_i => dma_cstart_reg,
cur_hstartl_i => dma_hstartl_reg,
cur_hstarth_i => dma_hstarth_reg,
cur_len_i => dma_len_reg);
-- Synchronizers for control.
inst_sync_ctrl : entity work.gc_sync_word_wr
generic map (
g_WIDTH => 4)
port map (
clk_in_i => wb_clk_i,
rst_in_n_i => wb_rst_n_i,
clk_out_i => clk_i,
rst_out_n_i => rst_n_i,
data_i(0) => dma_ctrl_start_wb,
data_i(1) => dma_ctrl_abort_wb,
data_i(3 downto 2) => dma_ctrl_byte_swap_wb,
wr_i => dma_ctrl_wr_wb,
ack_o => dma_ctrl_wack_wb,
data_o(0) => dma_ctrl_start,
data_o(1) => dma_ctrl_abort,
data_o(3 downto 2) => dma_ctrl_byte_swap,
wr_o => dma_ctrl_wr);
dma_stat_irq_wr_wb <= dma_stat_wr_wb and dma_stat_irq_o_wb;
-- Pulse is generated when a '1' is written to the irq bit of the stat reg.
inst_sync_stat_wr : entity work.gc_sync_word_wr
generic map (
g_auto_wr => FALSE,
g_width => 1)
port map (
clk_in_i => wb_clk_i,
rst_in_n_i => wb_rst_n_i,
clk_out_i => clk_i,
rst_out_n_i => rst_n_i,
data_i(0) => dma_stat_irq_wr_wb,
wr_i => dma_stat_wr_wb,
ack_o => dma_stat_wack_wb,
data_o(0) => dma_stat_irq_wr,
wr_o => dma_stat_wr);
-- Sync stat
inst_sync_stat_rd : entity work.gc_sync_word_rd
generic map (
port map (
clk_out_i => wb_clk_i,
rst_out_n_i => wb_rst_n_i,
clk_in_i => clk_i,
rst_in_n_i => rst_n_i,
data_in_i(1 downto 0) => dma_stat_reg,
data_in_i(2) => dma_irq_reg,
rd_out_i => dma_stat_rd_wb,
ack_out_o => dma_stat_rack_wb,
data_out_o(1 downto 0) => dma_stat_status_wb,
data_out_o(2) => dma_stat_irq_i_wb,
rd_in_o => open);
------------------------------------------------------------------------------
-- DMA controller FSM
------------------------------------------------------------------------------
p_fsm : process (clk_i)
if rising_edge(clk_i) then
dma_ctrl_current_state <= DMA_IDLE;
dma_ctrl_start_l2p_o <= '0';
dma_ctrl_start_p2l_o <= '0';
dma_ctrl_start_next_o <= '0';
dma_ctrl_abort_o <= '0';
dma_stat_reg <= c_DMA_STAT_IDLE;
dma_irq_reg <= '0';
dma_cstart_reg <= (others => '0');
dma_hstartl_reg <= (others => '0');
dma_hstarth_reg <= (others => '0');
dma_len_reg <= (others => '0');
dma_nextl_reg <= (others => '0');
dma_nexth_reg <= (others => '0');
dma_ctrl_byte_swap_reg <= "00";
dma_attrib_chain_reg <= '0';
dma_attrib_dir_reg <= '0';
else
case dma_ctrl_current_state is
when DMA_IDLE =>
-- Clear interrupt when idle status is read.

Dimitris Lampridis
committed
if dma_stat_irq_wr = '1' and dma_stat_wr = '1' then
if (dma_ctrl_wr and dma_ctrl_start) = '1' then
-- Capture parameters
-- All these inputs registers are synchronized on the start pulse.
dma_cstart_reg <= dma_cstart;
dma_hstartl_reg <= dma_hstartl;
dma_hstarth_reg <= dma_hstarth;
dma_len_reg <= dma_len;
dma_nextl_reg <= dma_nextl;
dma_nexth_reg <= dma_nexth;
dma_attrib_chain_reg <= dma_attrib_chain;
dma_attrib_dir_reg <= dma_attrib_dir;
dma_ctrl_byte_swap_reg <= dma_ctrl_byte_swap;
-- Starts a new transfer
dma_ctrl_current_state <= DMA_START_TRANSFER;
when DMA_START_TRANSFER =>
-- Clear abort signal
dma_ctrl_abort_o <= '0';
-- Requesting a DMA of 0 word length gives a error
dma_irq_reg <= '1';
dma_stat_reg <= c_DMA_STAT_ERROR;
dma_ctrl_current_state <= DMA_IDLE;
-- Start the DMA if the length is not 0
if dma_attrib_dir_reg = '0' then
-- L2P transfer (from target to PCIe)
dma_ctrl_start_l2p_o <= '1';
-- P2L transfer (from PCIe to target)
dma_ctrl_start_p2l_o <= '1';
end if;
dma_ctrl_current_state <= DMA_TRANSFER;

Dimitris Lampridis
committed
dma_stat_reg <= c_DMA_STAT_BUSY;
end if;
when DMA_TRANSFER =>
-- Clear start signals, to make them 1 tick pulses
dma_ctrl_start_l2p_o <= '0';
dma_ctrl_start_p2l_o <= '0';
if (dma_ctrl_abort and dma_ctrl_wr) = '1' then
-- Transfer aborted
dma_stat_reg <= c_DMA_STAT_ABORT;
dma_ctrl_abort_o <= '1';
dma_ctrl_current_state <= DMA_IDLE;
elsif dma_ctrl_error_i = '1' then
-- An error occured
dma_irq_reg <= '1';
dma_stat_reg <= c_DMA_STAT_ERROR;
dma_ctrl_current_state <= DMA_IDLE;
elsif dma_ctrl_done_i = '1' then
-- End of DMA transfer
if dma_attrib_chain_reg = '1' then
-- More transfers in chained DMA
dma_ctrl_current_state <= DMA_START_CHAIN;
else
-- Was the last transfer

Dimitris Lampridis
committed
dma_stat_reg <= c_DMA_STAT_IDLE;
dma_irq_reg <= '1';
dma_ctrl_current_state <= DMA_IDLE;
end if;
end if;
when DMA_START_CHAIN =>
-- Catch the next item in host memory
dma_ctrl_current_state <= DMA_CHAIN;
dma_hstarth_reg <= dma_nexth_reg;
dma_hstartl_reg <= dma_nextl_reg;
dma_len_reg <= X"0000001C";
dma_ctrl_start_next_o <= '1';
when DMA_CHAIN =>
-- Clear start next signal, to make it 1 tick pulse
dma_ctrl_start_next_o <= '0';
if (dma_ctrl_abort and dma_ctrl_wr) = '1' then
-- Transfer aborted
dma_stat_reg <= c_DMA_STAT_ABORT;
dma_ctrl_abort_o <= '1';
dma_ctrl_current_state <= DMA_IDLE;
elsif dma_ctrl_error_i = '1' then
-- An error occured
dma_irq_reg <= '1';
dma_stat_reg <= c_DMA_STAT_ERROR;
dma_ctrl_current_state <= DMA_IDLE;
elsif next_item_valid_i = '1' then
-- Capture parameters
dma_cstart_reg <= next_item_carrier_addr_i;
dma_hstartl_reg <= next_item_host_addr_l_i;
dma_hstarth_reg <= next_item_host_addr_h_i;
dma_len_reg <= next_item_len_i;
dma_nextl_reg <= next_item_next_l_i;
dma_nexth_reg <= next_item_next_h_i;
dma_attrib_chain_reg <= next_item_attrib_i(1);
dma_attrib_dir_reg <= next_item_attrib_i(0);
dma_ctrl_current_state <= DMA_START_TRANSFER;
end case;
end if;
dma_ctrl_carrier_addr_o <= dma_cstart_reg;
dma_ctrl_host_addr_h_o <= dma_hstarth_reg;
dma_ctrl_host_addr_l_o <= dma_hstartl_reg;
dma_ctrl_len_o <= dma_len_reg;
dma_ctrl_irq_o <= dma_irq_reg;
dma_ctrl_byte_swap_o <= dma_ctrl_byte_swap_reg;