pluto_hdl_adi/library/axi_dmac/axi_dmac_reset_manager.v

305 lines
8.7 KiB
Coq
Raw Normal View History

axi_dmac: Rework transfer shutdown The DMAC allows a transfer to be aborted. When a transfer is aborted the DMAC shuts down as fast as possible while still completing any pending transactions as required by the protocol specifications of the port. E.g. for AXI-MM this means to complete all outstanding bursts. Once the DMAC has entered an idle state a special synchronization signal is send to all modules. This synchronization signal instructs them to flush the pipeline and remove any stale data and metadata associated with the aborted transfer. Once all data has been flushed the DMAC enters the shutdown state and is ready for the next transfer. In addition each module has a reset that resets the modules state and is used at system startup to bring them into a consistent state. Re-work the shutdown process to instead of flushing the pipeline re-use the startup reset signal also for shutdown. To manage the reset signal generation introduce the reset manager module. It contains a state machine that will assert the reset signals in the correct order and for the appropriate duration in case of a transfer shutdown. The reset signal is asserted in all domains until it has been asserted for at least 4 clock cycles in the slowest domain. This ensures that the reset signal is not de-asserted in the faster domains before the slower domains have had a chance to process the reset signal. In addition the reset signal is de-asserted in the opposite direction of the data flow. This ensures that the data sink is ready to receive data before the data source can start sending data. This simplifies the internal handshaking. This approach has multiple advantages. * Issuing a reset and removing all state takes less time than explicitly flushing one sample per clock cycle at a time. * It simplifies the logic in the faster clock domains at the expense of more complicated logic in the slower control clock domain. This allows for higher fMax on the data paths. * Less signals to synchronize from the control domain to the data domains The implementation of the pause mode has also slightly changed. Pause is now a simple disable of the data domains. When the transfer is resumed after a pause the data domains are re-enabled and continue at their previous state. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2017-09-21 14:02:44 +00:00
// ***************************************************************************
// ***************************************************************************
// Copyright 2014 - 2018 (c) Analog Devices, Inc. All rights reserved.
//
// In this HDL repository, there are many different and unique modules, consisting
// of various HDL (Verilog or VHDL) components. The individual modules are
// developed independently, and may be accompanied by separate and unique license
// terms.
//
// The user should read each of these license terms, and understand the
// freedoms and responsibilities that he or she has by using this source/core.
//
// This core is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE.
//
// Redistribution and use of source or resulting binaries, with or without modification
// of this file, are permitted under one of the following two license terms:
//
// 1. The GNU General Public License version 2 as published by the
// Free Software Foundation, which can be found in the top level directory
// of this repository (LICENSE_GPL2), and also online at:
// <https://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
//
// OR
//
// 2. An ADI specific BSD license, which can be found in the top level directory
// of this repository (LICENSE_ADIBSD), and also on-line at:
// https://github.com/analogdevicesinc/hdl/blob/master/LICENSE_ADIBSD
// This will allow to generate bit files and not release the source code,
// as long as it attaches to an ADI device.
//
// ***************************************************************************
// ***************************************************************************
`timescale 1ns/100ps
axi_dmac: Rework transfer shutdown The DMAC allows a transfer to be aborted. When a transfer is aborted the DMAC shuts down as fast as possible while still completing any pending transactions as required by the protocol specifications of the port. E.g. for AXI-MM this means to complete all outstanding bursts. Once the DMAC has entered an idle state a special synchronization signal is send to all modules. This synchronization signal instructs them to flush the pipeline and remove any stale data and metadata associated with the aborted transfer. Once all data has been flushed the DMAC enters the shutdown state and is ready for the next transfer. In addition each module has a reset that resets the modules state and is used at system startup to bring them into a consistent state. Re-work the shutdown process to instead of flushing the pipeline re-use the startup reset signal also for shutdown. To manage the reset signal generation introduce the reset manager module. It contains a state machine that will assert the reset signals in the correct order and for the appropriate duration in case of a transfer shutdown. The reset signal is asserted in all domains until it has been asserted for at least 4 clock cycles in the slowest domain. This ensures that the reset signal is not de-asserted in the faster domains before the slower domains have had a chance to process the reset signal. In addition the reset signal is de-asserted in the opposite direction of the data flow. This ensures that the data sink is ready to receive data before the data source can start sending data. This simplifies the internal handshaking. This approach has multiple advantages. * Issuing a reset and removing all state takes less time than explicitly flushing one sample per clock cycle at a time. * It simplifies the logic in the faster clock domains at the expense of more complicated logic in the slower control clock domain. This allows for higher fMax on the data paths. * Less signals to synchronize from the control domain to the data domains The implementation of the pause mode has also slightly changed. Pause is now a simple disable of the data domains. When the transfer is resumed after a pause the data domains are re-enabled and continue at their previous state. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2017-09-21 14:02:44 +00:00
module axi_dmac_reset_manager #(
parameter ASYNC_CLK_REQ_SRC = 1,
parameter ASYNC_CLK_SRC_DEST = 1,
parameter ASYNC_CLK_DEST_REQ = 1
) (
input clk,
input resetn,
input ctrl_enable,
input ctrl_pause,
output req_resetn,
output req_enable,
input req_enabled,
input dest_clk,
input dest_ext_resetn,
output dest_resetn,
output dest_enable,
input dest_enabled,
input src_clk,
input src_ext_resetn,
output src_resetn,
output src_enable,
input src_enabled,
output [11:0] dbg_status
);
/*
* TODO:
* If an external reset is asserted for a domain that domain will go into reset
* immediately. If a transfer is currently active the transfer will be aborted
* and other domains will be shutdown gracefully. The reset manager will stay in
* the shutdown state until all external resets have been de-asserted.
*/
localparam STATE_DO_RESET = 3'h0;
localparam STATE_RESET = 3'h1;
localparam STATE_DISABLED = 3'h2;
localparam STATE_STARTUP = 3'h3;
localparam STATE_ENABLED = 3'h4;
localparam STATE_SHUTDOWN = 3'h5;
reg [2:0] state = 3'b000;
reg needs_reset = 1'b0;
reg do_reset = 1'b0;
reg do_enable = 1'b0;
wire enabled_dest;
wire enabled_src;
wire enabled_all;
wire disabled_all;
assign enabled_all = req_enabled & enabled_src & enabled_dest;
assign disabled_all = ~(req_enabled | enabled_src | enabled_dest);
assign req_enable = do_enable;
assign dbg_status = {needs_reset,req_resetn,src_resetn,dest_resetn,1'b0,req_enabled,enabled_src,enabled_dest,1'b0,state};
always @(posedge clk) begin
if (state == STATE_DO_RESET) begin
do_reset <= 1'b1;
end else begin
do_reset <= 1'b0;
end
end
always @(posedge clk) begin
if (state == STATE_STARTUP || state == STATE_ENABLED) begin
do_enable <= 1'b1;
end else begin
do_enable <= 1'b0;
end
end
/*
* If ctrl_enable goes from 1 to 0 a shutdown procedure is initiated. During the
* shutdown procedure all domains are signaled that a shutdown should occur. The
* domains will then complete any active transactions that are required to
* complete according to the interface semantics. Once a domain has completed
* its transactions it will indicate that it has been shutdown. Once all domains
* indicate that they have been disabled a reset pulse will be generated to all
* domains to clear all residual state. The reset pulse is long enough so that it
* is active in all domains for at least 4 clock cycles.
*
* Once the reset signal is de-asserted the DMA is in an idle state and can be
* enabled again. If the DMA receives a enable while it is performing a shutdown
* sequence it will only be re-enabled once the shutdown sequence has
* successfully completed.
*
* If ctrl_pause is asserted all domains will be disabled. But there will be no
* reset, so when the ctrl_pause signal is de-asserted again the DMA will resume
* with its previous state.
*
*/
/*
* If ctrl_enable goes low, even for a single clock cycle, we want to go through
* a full reset sequence. This might happen when the state machine is busy, e.g.
* going through a startup sequence. To avoid missing the event store it for
* later.
*/
always @(posedge clk) begin
if (state == STATE_RESET) begin
needs_reset <= 1'b0;
end else if (ctrl_enable == 1'b0) begin
needs_reset <= 1'b1;
end
end
always @(posedge clk) begin
if (resetn == 1'b0) begin
state <= STATE_DO_RESET;
end else begin
case (state)
STATE_DO_RESET: begin
state <= STATE_RESET;
end
STATE_RESET: begin
/*
* Wait for the reset sequence to complete. Stay in this state when
* ctrl_enable == 1'b0, otherwise we'd go through the reset sequence
* again and again.
*/
if (ctrl_enable == 1'b1 && req_resetn == 1'b1) begin
state <= STATE_DISABLED;
end
end
STATE_DISABLED: begin
if (needs_reset == 1'b1) begin
state <= STATE_DO_RESET;
end else if (ctrl_pause == 1'b0) begin
state <= STATE_STARTUP;
end
end
STATE_STARTUP: begin
/* Wait for all domains to be ready */
if (enabled_all == 1'b1) begin
state <= STATE_ENABLED;
end
end
STATE_ENABLED: begin
if (needs_reset == 1'b1 || ctrl_pause == 1'b1) begin
state <= STATE_SHUTDOWN;
end
end
STATE_SHUTDOWN: begin
/* Wait for all domains to complete outstanding transactions */
if (disabled_all == 1'b1) begin
state <= STATE_DISABLED;
end
end
endcase
end
end
/*
* Chain the reset through all clock domains. This makes sure that is asserted
* for at least 4 clock cycles of the slowest domain, no matter what. If
* successive domains have the same clock they'll share their reset signal.
*/
wire [3:0] reset_async_chain;
wire [3:0] reset_sync_chain;
wire [2:0] reset_chain_clks = {clk, src_clk, dest_clk};
localparam GEN_ASYNC_RESET = {
ASYNC_CLK_REQ_SRC ? 1'b1 : 1'b0,
ASYNC_CLK_SRC_DEST ? 1'b1 : 1'b0,
1'b1
};
assign reset_async_chain[0] = 1'b0;
assign reset_sync_chain[0] = reset_async_chain[3];
generate
genvar i;
for (i = 0; i < 3; i = i + 1) begin: reset_gen
if (GEN_ASYNC_RESET[i] == 1'b1) begin
reg [3:0] reset_async = 4'b1111;
reg [1:0] reset_sync = 2'b11;
reg reset_sync_in = 1'b1;
always @(posedge reset_chain_clks[i] or posedge reset_sync_chain[i]) begin
if (reset_sync_chain[i] == 1'b1) begin
reset_sync_in <= 1'b1;
end else begin
reset_sync_in <= reset_async[0];
end
end
always @(posedge reset_chain_clks[i] or posedge do_reset) begin
if (do_reset == 1'b1) begin
reset_async <= 4'b1111;
end else begin
reset_async <= {reset_async_chain[i], reset_async[3:1]};
end
end
always @(posedge reset_chain_clks[i]) begin
reset_sync <= {reset_sync_in,reset_sync[1]};
end
assign reset_async_chain[i+1] = reset_async[0];
assign reset_sync_chain[i+1] = reset_sync[0];
end else begin
assign reset_async_chain[i+1] = reset_async_chain[i];
assign reset_sync_chain[i+1] = reset_sync_chain[i];
end
end
endgenerate
/* De-assertions in the opposite direction of the data flow: dest, src, request */
assign dest_resetn = ~reset_sync_chain[1];
assign src_resetn = ~reset_sync_chain[2];
assign req_resetn = ~reset_sync_chain[3];
sync_bits #(
.NUM_OF_BITS (1),
.ASYNC_CLK (ASYNC_CLK_DEST_REQ)
) i_sync_control_dest (
.out_clk (dest_clk),
.out_resetn (1'b1),
.in_bits (do_enable),
.out_bits (dest_enable)
axi_dmac: Rework transfer shutdown The DMAC allows a transfer to be aborted. When a transfer is aborted the DMAC shuts down as fast as possible while still completing any pending transactions as required by the protocol specifications of the port. E.g. for AXI-MM this means to complete all outstanding bursts. Once the DMAC has entered an idle state a special synchronization signal is send to all modules. This synchronization signal instructs them to flush the pipeline and remove any stale data and metadata associated with the aborted transfer. Once all data has been flushed the DMAC enters the shutdown state and is ready for the next transfer. In addition each module has a reset that resets the modules state and is used at system startup to bring them into a consistent state. Re-work the shutdown process to instead of flushing the pipeline re-use the startup reset signal also for shutdown. To manage the reset signal generation introduce the reset manager module. It contains a state machine that will assert the reset signals in the correct order and for the appropriate duration in case of a transfer shutdown. The reset signal is asserted in all domains until it has been asserted for at least 4 clock cycles in the slowest domain. This ensures that the reset signal is not de-asserted in the faster domains before the slower domains have had a chance to process the reset signal. In addition the reset signal is de-asserted in the opposite direction of the data flow. This ensures that the data sink is ready to receive data before the data source can start sending data. This simplifies the internal handshaking. This approach has multiple advantages. * Issuing a reset and removing all state takes less time than explicitly flushing one sample per clock cycle at a time. * It simplifies the logic in the faster clock domains at the expense of more complicated logic in the slower control clock domain. This allows for higher fMax on the data paths. * Less signals to synchronize from the control domain to the data domains The implementation of the pause mode has also slightly changed. Pause is now a simple disable of the data domains. When the transfer is resumed after a pause the data domains are re-enabled and continue at their previous state. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2017-09-21 14:02:44 +00:00
);
sync_bits #(
.NUM_OF_BITS (1),
.ASYNC_CLK (ASYNC_CLK_DEST_REQ)
) i_sync_status_dest (
.out_clk (clk),
.out_resetn (1'b1),
.in_bits (dest_enabled),
.out_bits (enabled_dest)
axi_dmac: Rework transfer shutdown The DMAC allows a transfer to be aborted. When a transfer is aborted the DMAC shuts down as fast as possible while still completing any pending transactions as required by the protocol specifications of the port. E.g. for AXI-MM this means to complete all outstanding bursts. Once the DMAC has entered an idle state a special synchronization signal is send to all modules. This synchronization signal instructs them to flush the pipeline and remove any stale data and metadata associated with the aborted transfer. Once all data has been flushed the DMAC enters the shutdown state and is ready for the next transfer. In addition each module has a reset that resets the modules state and is used at system startup to bring them into a consistent state. Re-work the shutdown process to instead of flushing the pipeline re-use the startup reset signal also for shutdown. To manage the reset signal generation introduce the reset manager module. It contains a state machine that will assert the reset signals in the correct order and for the appropriate duration in case of a transfer shutdown. The reset signal is asserted in all domains until it has been asserted for at least 4 clock cycles in the slowest domain. This ensures that the reset signal is not de-asserted in the faster domains before the slower domains have had a chance to process the reset signal. In addition the reset signal is de-asserted in the opposite direction of the data flow. This ensures that the data sink is ready to receive data before the data source can start sending data. This simplifies the internal handshaking. This approach has multiple advantages. * Issuing a reset and removing all state takes less time than explicitly flushing one sample per clock cycle at a time. * It simplifies the logic in the faster clock domains at the expense of more complicated logic in the slower control clock domain. This allows for higher fMax on the data paths. * Less signals to synchronize from the control domain to the data domains The implementation of the pause mode has also slightly changed. Pause is now a simple disable of the data domains. When the transfer is resumed after a pause the data domains are re-enabled and continue at their previous state. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2017-09-21 14:02:44 +00:00
);
sync_bits #(
.NUM_OF_BITS (1),
.ASYNC_CLK (ASYNC_CLK_REQ_SRC)
) i_sync_control_src (
.out_clk (src_clk),
.out_resetn (1'b1),
.in_bits (do_enable),
.out_bits (src_enable)
axi_dmac: Rework transfer shutdown The DMAC allows a transfer to be aborted. When a transfer is aborted the DMAC shuts down as fast as possible while still completing any pending transactions as required by the protocol specifications of the port. E.g. for AXI-MM this means to complete all outstanding bursts. Once the DMAC has entered an idle state a special synchronization signal is send to all modules. This synchronization signal instructs them to flush the pipeline and remove any stale data and metadata associated with the aborted transfer. Once all data has been flushed the DMAC enters the shutdown state and is ready for the next transfer. In addition each module has a reset that resets the modules state and is used at system startup to bring them into a consistent state. Re-work the shutdown process to instead of flushing the pipeline re-use the startup reset signal also for shutdown. To manage the reset signal generation introduce the reset manager module. It contains a state machine that will assert the reset signals in the correct order and for the appropriate duration in case of a transfer shutdown. The reset signal is asserted in all domains until it has been asserted for at least 4 clock cycles in the slowest domain. This ensures that the reset signal is not de-asserted in the faster domains before the slower domains have had a chance to process the reset signal. In addition the reset signal is de-asserted in the opposite direction of the data flow. This ensures that the data sink is ready to receive data before the data source can start sending data. This simplifies the internal handshaking. This approach has multiple advantages. * Issuing a reset and removing all state takes less time than explicitly flushing one sample per clock cycle at a time. * It simplifies the logic in the faster clock domains at the expense of more complicated logic in the slower control clock domain. This allows for higher fMax on the data paths. * Less signals to synchronize from the control domain to the data domains The implementation of the pause mode has also slightly changed. Pause is now a simple disable of the data domains. When the transfer is resumed after a pause the data domains are re-enabled and continue at their previous state. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2017-09-21 14:02:44 +00:00
);
sync_bits #(
.NUM_OF_BITS (1),
.ASYNC_CLK (ASYNC_CLK_REQ_SRC)
) i_sync_status_src (
.out_clk (clk),
.out_resetn (1'b1),
.in_bits (src_enabled),
.out_bits (enabled_src)
axi_dmac: Rework transfer shutdown The DMAC allows a transfer to be aborted. When a transfer is aborted the DMAC shuts down as fast as possible while still completing any pending transactions as required by the protocol specifications of the port. E.g. for AXI-MM this means to complete all outstanding bursts. Once the DMAC has entered an idle state a special synchronization signal is send to all modules. This synchronization signal instructs them to flush the pipeline and remove any stale data and metadata associated with the aborted transfer. Once all data has been flushed the DMAC enters the shutdown state and is ready for the next transfer. In addition each module has a reset that resets the modules state and is used at system startup to bring them into a consistent state. Re-work the shutdown process to instead of flushing the pipeline re-use the startup reset signal also for shutdown. To manage the reset signal generation introduce the reset manager module. It contains a state machine that will assert the reset signals in the correct order and for the appropriate duration in case of a transfer shutdown. The reset signal is asserted in all domains until it has been asserted for at least 4 clock cycles in the slowest domain. This ensures that the reset signal is not de-asserted in the faster domains before the slower domains have had a chance to process the reset signal. In addition the reset signal is de-asserted in the opposite direction of the data flow. This ensures that the data sink is ready to receive data before the data source can start sending data. This simplifies the internal handshaking. This approach has multiple advantages. * Issuing a reset and removing all state takes less time than explicitly flushing one sample per clock cycle at a time. * It simplifies the logic in the faster clock domains at the expense of more complicated logic in the slower control clock domain. This allows for higher fMax on the data paths. * Less signals to synchronize from the control domain to the data domains The implementation of the pause mode has also slightly changed. Pause is now a simple disable of the data domains. When the transfer is resumed after a pause the data domains are re-enabled and continue at their previous state. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2017-09-21 14:02:44 +00:00
);
endmodule