axi_hp_abort.v 6.04 KB
Newer Older
1 2 3
/*******************************************************************************
 * Module: axi_hp_abort
 * Date:2016-02-07  
4
 * Author: Andrey Filippov     
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 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 78 79 80 81 82 83 84 85 86 87 88 89 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 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
 * Description: Trying to gracefully reset AXI HP after aborted transmission
 * For read channel - just keep afi_rready on until RD FIFO is empty (afi_rcount ==0)
 * For write - keep track aof all what was sent so far, assuming aw is always ahead of w
 * Reset only by global reset (system POR) - probably it is not possible to just
 * reset PL or relaod bitfile, 
 *
 * Copyright (c) 2016 Elphel, Inc .
 * axi_hp_abort.v is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 *  axi_hp_abort.v 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.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/> .
 *******************************************************************************/
`timescale 1ns/1ps

module  axi_hp_abort(
    input             hclk,
    input             hrst,  // just disables processing inputs
    input             abort,
    output            busy, // should disable control of afi_wvalid, afi_awid
    output reg        done,
    input             afi_awvalid, // afi_awready is supposed to be always on when afi_awvalid (caller uses fifo counetrs) ?
    input             afi_awready, //
    input      [ 5:0] afi_awid, 
    input       [3:0] afi_awlen, 
    input             afi_wvalid_in,
    input             afi_wready,
    output            afi_wvalid,
    output reg [ 5:0] afi_wid,
    input             afi_arvalid,
    input             afi_arready,
    input      [ 3:0] afi_arlen,
    input             afi_rready_in,
    input             afi_rvalid,
    output            afi_rready,
    output            afi_wlast,
// TODO:  Try to resolve problems when afi_racount, afi_wacount afi_wcount do not match expected
    input      [ 2:0] afi_racount,
    input      [ 7:0] afi_rcount,
    input      [ 5:0] afi_wacount,
    input      [ 7:0] afi_wcount,
    output reg        dirty,     // single bit to be sampled in different clock domain to see if flushing is needed
    output reg        axi_mismatch,   // calculated as 'dirty' but axi hp counters are 0
    output     [21:0] debug
);
    reg               busy_r;
    wire              done_w = busy_r && !dirty ;
    reg         [3:0] aw_lengths_ram[0:31];
    reg         [4:0] aw_lengths_waddr = 0;
    reg         [4:0] aw_lengths_raddr = 0;
    reg         [5:0] aw_count = 0;
    reg         [7:0] w_count = 0;
    reg         [7:0] r_count = 0;
    reg               adav = 0;
    wire              arwr = !hrst && afi_arvalid && afi_arready;
    wire              drd =  !hrst && afi_rvalid && afi_rready_in;
    wire              awr = !hrst && afi_awvalid && afi_awready;
    reg               ard_r = 0; // additional length read if not much data
    wire              ard = adav && ((|w_count[7:4]) || ard_r);
    wire              wwr = !hrst && afi_wready && afi_wvalid_in;
    reg               afi_rready_r;
    reg               afi_wlast_r; // wait one cycle after last in each burst (just to ease timing)
    reg               busy_aborting; // actually aborting
    wire              reset_counters = busy_r && !busy_aborting;
    assign busy = busy_r;
    
    assign afi_rready = busy_aborting && (|r_count) && ((|afi_rcount[7:1]) || (!afi_rready_r &&  afi_rcount[0]));
    assign afi_wlast =  busy_aborting && adav && (w_count[3:0] == aw_lengths_ram[aw_lengths_raddr]);
    assign afi_wvalid = busy_aborting && adav && !afi_wlast_r;
    assign debug = {aw_count[5:0], w_count[7:0], r_count[7:0]};
    
    // Watch for transactios performed by others (and this one too)
    always @ (posedge hclk) begin
        // read channel
        if (reset_counters) r_count <= 0;
        else if (drd)
             if (arwr)      r_count <= r_count + {4'b0, afi_arlen};
             else           r_count <= r_count - 1;
        else
             if (arwr)      r_count <= w_count  + {4'b0, afi_arlen} + 1;
    
        // write channel
    
        if (awr) afi_wid <= afi_awid; // one command is supposed to use just one awid/wid

        if (awr) aw_lengths_ram [aw_lengths_waddr] <= afi_awlen;

        if (reset_counters) aw_lengths_waddr <= 0;
        else if (awr)       aw_lengths_waddr <= aw_lengths_waddr + 1;

        if (reset_counters) aw_lengths_raddr <= 0;
        else if (ard)       aw_lengths_raddr <= aw_lengths_raddr + 1;
        
        if (reset_counters)    aw_count <= 0;
        else if ( awr && !ard) aw_count <= aw_count + 1;
        else if (!awr &&  ard) aw_count <= aw_count - 1;
        
        adav <= !reset_counters && (|aw_count[5:1]) || ((awr || aw_count[0]) && !ard) || (awr && aw_count[0]);
        
        ard_r <= !ard && adav && (w_count[3:0] > aw_lengths_ram[aw_lengths_raddr]);
        
        if (reset_counters) w_count <= 0;
        else if (wwr)
             if (ard)       w_count <= w_count - {4'b0, aw_lengths_ram[aw_lengths_raddr]};
             else           w_count <= w_count + 1;
        else
             if (ard)       w_count <= w_count - {4'b0, aw_lengths_ram[aw_lengths_raddr]} - 1;
        
        dirty <= (|r_count) || (|aw_count); // assuming w_count can never be non-zero? - no
    end
    
    // flushing part
    always @ (posedge hclk) begin
    
        if      (abort)  busy_r <= 1;
        else if (done_w) busy_r <= 0;

        if      (abort && ((|afi_racount) || (|afi_rcount) || (|afi_wacount) || (|afi_wcount)))  busy_aborting <= 1;
        else if (done_w) busy_aborting <= 0;

        
        done <=  done_w;
        afi_rready_r <= afi_rready;
        afi_wlast_r <=  afi_wlast;
        
        axi_mismatch <= busy && !busy_aborting && dirty;  //  
    end 
    

endmodule