Commit 52428736 authored by Dimitris Lampridis's avatar Dimitris Lampridis

hdl: fix L2P DMA flow control

This commit fixes an issue that would cause the L2P DMA to drop some data when the WB slave would
stall at the same cycle as the dual-clock FIFO would raise the 'full' flag.

The WB state machine has been redesigned in order to solve this issue and make sure that data is
properly retained when this condition appears.
Signed-off-by: Dimitris Lampridis's avatarDimitris Lampridis <dimitris.lampridis@cern.ch>
parent c3776541
......@@ -96,7 +96,7 @@ architecture arch of l2p_dma_master is
L2P_NEXT, L2P_ERROR);
signal l2p_dma_current_state : l2p_dma_state_type := L2P_IDLE;
type wb_dma_state_type is (WB_IDLE, WB_SETUP, WB_DATA1,WB_DATA2, WB_HOLD);
type wb_dma_state_type is (WB_IDLE, WB_SETUP, WB_DATA, WB_WAIT_ACK);
signal wb_dma_current_state : wb_dma_state_type := WB_IDLE;
signal dma_target_addr : unsigned(29 downto 0) := (others => '0');
......@@ -408,67 +408,68 @@ begin
case wb_dma_current_state is
when WB_IDLE =>
-- Start when the new DMA parameters (address, size) have been received
if dma_param_wr = '1' then
wb_dma_addr := unsigned(dma_param_sync(59 downto 30));
wb_dma_cnt_stb := unsigned(dma_param_sync(29 downto 0));
wb_dma_cnt_ack := unsigned(dma_param_sync(29 downto 0));
wb_dma_current_state <= WB_SETUP;
end if;
when WB_SETUP =>
wb_dma_addr := unsigned(dma_param_sync(59 downto 30));
wb_dma_cnt_stb := unsigned(dma_param_sync(29 downto 0));
wb_dma_cnt_ack := unsigned(dma_param_sync(29 downto 0));
-- Start/maintain the WB cycle
wb_dma_cyc <= '1';
-- Always keep track of pending ACKs
if wb_dma_i.ack = '1' then
wb_dma_cnt_ack := wb_dma_cnt_ack - 1;
end if;
-- If there is space in the FIFO, send the first/next read request
if data_fifo_full = '0' then
wb_dma_current_state <= WB_DATA1;
wb_dma_o.adr <= "00" & std_logic_vector(wb_dma_addr);
wb_dma_stb <= '1';
wb_dma_current_state <= WB_DATA;
end if;
when WB_DATA1 =>
when WB_DATA =>
-- Maintain the WB cycle
wb_dma_cyc <= '1';
-- Always keep track of pending ACKs
if wb_dma_i.ack = '1' then
wb_dma_cnt_ack := wb_dma_cnt_ack - 1;
end if;
if data_fifo_full = '1' then
wb_dma_stb <= '0';
wb_dma_current_state <= WB_HOLD;
else
if wb_dma_i.stall = '0' then
-- If the slave was not stalling on the previous cycle,
-- update address and counters
if wb_dma_i.stall = '0' then
wb_dma_addr := wb_dma_addr + 1;
wb_dma_cnt_stb := wb_dma_cnt_stb - 1;
-- If all read requests have been issued, move to next state
if wb_dma_cnt_stb = 0 then
wb_dma_stb <= '0';
wb_dma_current_state <= WB_WAIT_ACK;
-- If there is no room in the FIFO, drop strobe and wait
elsif data_fifo_full = '1' then
wb_dma_stb <= '0';
wb_dma_current_state <= WB_SETUP;
-- Otherwise send the next request
else
wb_dma_o.adr <= "00" & std_logic_vector(wb_dma_addr);
wb_dma_addr := wb_dma_addr + 1;
if wb_dma_cnt_stb = 0 then
wb_dma_stb <= '0';
wb_dma_current_state <= WB_DATA2;
else
wb_dma_cnt_stb := wb_dma_cnt_stb - 1;
wb_dma_stb <= '1';
end if;
wb_dma_stb <= '1';
end if;
end if;
when WB_DATA2 =>
when WB_WAIT_ACK =>
-- Maintain the WB cycle
wb_dma_cyc <= '1';
-- Always keep track of pending ACKs
if wb_dma_i.ack = '1' then
wb_dma_cnt_ack := wb_dma_cnt_ack - 1;
end if;
if data_fifo_full = '1' then
wb_dma_stb <= '0';
wb_dma_current_state <= WB_HOLD;
elsif wb_dma_cnt_ack = 0 then
-- If all ACKs have been received, we are done
if wb_dma_cnt_ack = 0 then
wb_dma_cyc <= '0';
wb_dma_current_state <= WB_IDLE;
end if;
when WB_HOLD =>
wb_dma_cyc <= '1';
wb_dma_stb <= '0';
if wb_dma_i.ack = '1' then
wb_dma_cnt_ack := wb_dma_cnt_ack - 1;
end if;
if data_fifo_full = '0' then
if wb_dma_cnt_stb = 0 then
wb_dma_current_state <= WB_DATA2;
else
wb_dma_current_state <= WB_DATA1;
end if;
end if;
when others =>
wb_dma_current_state <= WB_IDLE;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment