Commit 0fd78dc6 authored by Andrey Filippov's avatar Andrey Filippov
Browse files

Working on AHCI DMA module

parent 270a7673
Loading
Loading
Loading
Loading
+218 −7
Original line number Diff line number Diff line
@@ -31,10 +31,17 @@ module ahci_dma (
    input                  [31:7] ctba,         // command table base address
    input                         ctba_ld,      // load command table base address
    input                  [15:0] prdtl,        // number of entries in PRD table (valid at cmd_start)
    input                         dev_wr,       // write to device (valid at start)
    input                         cmd_start,     // start processing command table, reset prdbc
    input                         cmd_abort,     // try to abort a command
    // Some data from the command table will be used internally, data will be available on the general
    // sys_out[31:0] port and should be consumed
    output                        ct_busy,      // cleared after 0x20 DWORDs are read out
    // reading out command table data
    input                  [ 4:0] ct_addr,     // DWORD address
    input                         ct_re,       //  
    output reg             [31:0] ct_data,     // 
    
    // After the first 0x80 bytes of the Command Table are read out, this module will read/process PRDs,
    // not forwarding them to the output 
    output                        prd_done,     // prd done (regardless of the interrupt)
@@ -43,12 +50,12 @@ module ahci_dma (
    output                        cmd_done,
    
    // Data System memory -> HBA interface @ mclk
    output                 [31:0] sys_out,      // 32-bit data from the system memory to HBA - command table, dma data
    output                 [31:0] sys_out,      // 32-bit data from the system memory to HBA (dma data)
    output                        sys_dav,      // at least one dword is ready to be read
    output                        sys_dav_many, // several DWORDs are in the FIFO (TODO: decide how many)
//    output                        sys_dav_many, // several DWORDs are in the FIFO (TODO: decide how many)
    input                         sys_re,       // sys_out data read, advance internal FIFO
    // Data HBA -> System memory  interface @ mclk
    output                 [31:0] sys_out,      // 32-bit data from the system memory to HBA - command table, dma data
    input                  [31:0] sys_in,       // HBA -> system memory
    output                        sys_nfull,    // internal FIFO has room for more data (will decide - how big reserved space to keep)
    input                         sys_we,    
    
@@ -83,14 +90,14 @@ module ahci_dma (
    output         afi_wrissuecap1en,
    // AXI_HP signals - read channel
    // read address
    output  [31:0] afi_araddr,
    output reg [31:0] afi_araddr,
    output            afi_arvalid,
    input             afi_arready,  // @SuppressThisWarning VEditor unused - used FIF0 level
    output  [ 5:0] afi_arid,
    output  [ 1:0] afi_arlock,
    output  [ 3:0] afi_arcache,
    output  [ 2:0] afi_arprot,
    output  [ 3:0] afi_arlen,
    output reg  [ 3:0] afi_arlen,
    output  [ 1:0] afi_arsize,
    output  [ 1:0] afi_arburst,
    output  [ 3:0] afi_arqos,
@@ -108,5 +115,209 @@ module ahci_dma (
);


// Read command table
//    localparam AFI_FIFO_LAT = 2; // >=2
   localparam SAFE_RD_BITS =   3; //2; // 3;

    reg     [31:0] ct_data_ram [0:31];
    reg      [3:0] int_data_addr;    // internal (ct,prd) data address
    reg     [31:7] ctba_r;
    reg     [15:0] prdtl_mclk;
    wire           cmd_start_hclk;
    wire           cmd_abort_hclk;
    reg     [31:4] ct_maddr; // granularity matches PRDT entry - 4xDWORD, 2xQWORD
    reg            ct_done_r;
    reg            prd_done_r;
    wire           axi_set_raddr_ready = !(|afi_racount[2:1]); // What is the size of ra fifo?
    wire           axi_set_raddr_w;
    wire           axi_set_raddr_ct_w;   // next will be setting address/len/... to read command table
    reg            axi_set_raddr_prd;  // next will be setting address/len/... to read PRD entry
    wire           axi_set_raddr_data_w; // next will be setting address/len/... to read DATA  
    reg            axi_set_raddr_r; // [0] - actual write address to fifo
    reg            was_ct_addr; // AXI RD channel was set to read command table 
    reg            was_prd_addr;// AXI RD channel was set to read prd table
    
    reg     [31:1] data_addr; // 2 lower addresses will be used in in/out fifo modules
    reg      [3:0] data_len; //
    reg            data_irq; // interrupt at the end of this PRD
    reg     [21:1] wcount;  // Word count
    reg            wcount_set; 
    reg     [21:1] qwcount; // only [21:3] are used
    reg            next_data16; // next data r/w address incremented by 16 QWORDS
    wire           data_afi_re;
    
    reg     [15:0] prds_left;
    reg            last_prd;
    
    reg     [1:0]  afi_rd_ctl; // read non-data (CT or PRD)
    reg     [1:0]  ct_busy_r;
    reg            prd_rd_busy; // reading PRD
    
    wire           prd_done; // data transfer of one PRD is finished (any direction)
    reg            dev_wr_mclk;
    reg            dev_wr_hclk;
    reg            prd_wr;    // write PRD data to memory
    reg            prd_rd;    // read  PRD data from memory
    wire     [3:0] afi_wstb4;
//    reg      [1:0] afi_rready_ctl_r; 
    
    assign afi_arvalid = axi_set_raddr_r[0];
    assign axi_set_raddr_w = (axi_set_raddr_ct_w || axi_set_raddr_prd || axi_set_raddr_data_w) && axi_set_raddr_ready ;
    assign afi_rready = afi_rd_ctl[0] || data_afi_re;
    assign ct_busy = ct_busy_r[0];
    
    assign         afi_wstrb = {{2{afi_wstb4[3]}},{2{afi_wstb4[2]}},{2{afi_wstb4[1]}},{2{afi_wstb4[0]}}};
    
//    assign afi_rready = data_afi_re || ((was_ct_addr || was_prd_addr) &&
//                                        ((|afi_rcount[6:SAFE_RD_BITS]) || (afi_rvalid && !afi_rready &&!(|afi_rready_r))));
    
    always @ (posedge mclk) begin
        if (ct_re) ct_data <=         ct_data_ram[ct_addr];
        if (ctba_ld) ctba_r <=        ctba[31:7];
        if (cmd_start) prdtl_mclk <=  prdtl;
        if (cmd_start) dev_wr_mclk <= dev_wr;
    end
       
        
        
    always @ (posedge hclk) begin
        if (cmd_start_hclk)  ct_maddr[31:4] <= {ctba_r[31:7],3'b0};
        else if (ct_done_r)  ct_maddr[31:4] <= ct_maddr[31:4] + 16;
        else if (prd_done_r) ct_maddr[31:4] <= ct_maddr[31:4] + 1;
        
        if (hrst) axi_set_raddr_r <= 0;
        else      axi_set_raddr_r <= axi_set_raddr_w;
        
        if (axi_set_raddr_w) begin
            was_ct_addr <= axi_set_raddr_ct_w;
            was_prd_addr <= axi_set_raddr_prd;
        end

        if      (cmd_start_hclk)                  prds_left  <= prdtl_mclk;
        else if (axi_set_raddr_r && was_prd_addr) prds_left  <= prds_left  - 1;

        if (axi_set_raddr_r && was_prd_addr) last_prd  <= prds_left == 1;
        
        if (axi_set_raddr_r) begin
            if (was_ct_addr || was_prd_addr) afi_araddr <= {ct_maddr[31:4],4'b0};
            else                             afi_araddr <= {data_addr[31:3],3'b0};
            
            if      (was_ct_addr)            afi_arlen  <= 4'hf; // 16 QWORDS
            else if (was_prd_addr)           afi_arlen  <= 4'h1; //  2 QWORDS
            else                             afi_arlen  <= data_len; // TBD - all but last are 4'hf
        end
        
        if      (axi_set_raddr_r)                                int_data_addr <= 0; //  && (was_ct_addr || was_prd_addr))
        else if (afi_rd_ctl[0] && (was_ct_addr || was_prd_addr)) int_data_addr <= int_data_addr + 1;
        
        if (afi_rd_ctl[0] && was_ct_addr) {ct_data_ram[{int_data_addr,1'b1}],ct_data_ram[{int_data_addr,1'b0}]} <= afi_rdata; // make sure it is synthesized correctly
        
        if      (hrst)                                      ct_busy_r[0] <= 0;
        else if (cmd_start_hclk)                            ct_busy_r[0] <= 1;
        else if (afi_rd_ctl[0] && was_ct_addr && (&int_data_addr)) ct_busy_r[0] <= 0;
        ct_busy_r[1] <= ct_busy_r[0]; // delayed version to detect end of command
        
        if      (hrst)        prd_rd_busy <= 0;
        else if (prd_rd_busy) prd_rd_busy <= 1;
        else if (wcount_set)  prd_rd_busy <= 0;
        
        
        
        // start PRD read
        if (hrst) axi_set_raddr_prd <= 0;
        else      axi_set_raddr_prd <= ((|prds_left) && ((ct_busy_r==2'b10) || prd_done));
        
        // store data address from PRD
        if (afi_rd_ctl[0] && was_prd_addr && (!int_data_addr[0])) data_addr[31:1] <= afi_rdata[31:1];
        else if (next_data16)                              data_addr[31:7] <= data_addr[31:7] + 1; // add 64 bytes to address, keep low bits
        
        if (afi_rd_ctl[0] && was_prd_addr && (int_data_addr[0])) data_irq <=     afi_rdata[63];

        if (afi_rd_ctl[0] && was_prd_addr && (int_data_addr[0])) wcount[21:1] <= afi_rdata[37:17];

        wcount_set <= afi_rd_ctl[0] && was_prd_addr && (int_data_addr[0]);

        if (wcount_set) qwcount[21:1] <= wcount[21:1] + data_addr[2:1];
        
        if (cmd_start_hclk) dev_wr_hclk <= dev_wr_mclk; // 1: memory -> device, 0: device -> memory
        
        prd_wr <= wcount_set && !dev_wr_hclk;
        prd_rd <= wcount_set &&  dev_wr_hclk;
        
        afi_rd_ctl <= { afi_rd_ctl[0],(ct_busy_r[0] || prd_rd_busy) && ((|afi_rcount[7:SAFE_RD_BITS]) || (afi_rvalid && !(|afi_rd_ctl)))};
        
    end
    
    ahci_dma_rd_fifo #( // memory to device
        .WCNT_BITS    (21),
        .ADDRESS_BITS (3)
    ) ahci_dma_rd_fifo_i (
        .mrst         (mrst),                        // input
        .hrst         (hrst),                        // input
        .mclk         (mclk),                        // input
        .hclk         (hclk),                        // input
        .wcnt         (wcount[21:1]),                // input[20:0] 
        .woffs        (data_addr[2:1]),              // input[1:0] 
        .start        (prd_rd),                      // input
        .din          (afi_rdata),                   // input[63:0] 
        .din_av       (afi_rvalid),                  // input
        .din_av_many  (|afi_rcount[7:SAFE_RD_BITS]), // input
        .last_prd     (last_prd),                    // input
        .din_re       (data_afi_re),                 // output
        .done         (), // output reg 
        .done_flush   (), // output
        .dout         (sys_out),                     // output[31:0] 
        .dout_vld     (sys_dav),                     // output
        .dout_re      (sys_re)                       // input
    );
    
    ahci_dma_wr_fifo #( // device to memory
        .WCNT_BITS    (21),
        .ADDRESS_BITS (3)
    ) ahci_dma_wr_fifo_i (
        .mrst         (mrst),           // input
        .hrst         (hrst),           // input
        .mclk         (mclk),           // input
        .hclk         (hclk),           // input
        .wcnt         (wcount[21:1]),   // input[20:0] 
        .woffs        (data_addr[2:1]), // input[1:0] 
        .init         (cmd_start_hclk), // input
        .start        (prd_wr),         // input
        .dout         (afi_wdata),      // output[63:0] reg 
        .dout_av      (), // input
        .dout_av_many (), // input
        .last_prd     (last_prd),       // input
        .dout_we      (afi_wvalid),     // output
        .dout_wstb    (afi_wstb4),      // output[3:0] reg 
        .done         (), // output reg 
        .busy         (), // output
        .din          (sys_in),         // input[31:0] 
        .din_rdy      (sys_nfull),      // output
        .din_avail    (sys_we)          // input
    );
    
    // mclk -> hclk cross-clock synchronization
    pulse_cross_clock #(
        .EXTRA_DLY(0)
    ) cmd_start_hclk_i (
        .rst       (mrst),            // input
        .src_clk   (mclk),            // input
        .dst_clk   (hclk),            // input
        .in_pulse  (cmd_start),       // input
        .out_pulse (cmd_start_hclk),    // output
        .busy()                       // output
    );
    pulse_cross_clock #(
        .EXTRA_DLY(0)
    ) cmd_abort_hclk_i (
        .rst       (mrst),            // input
        .src_clk   (mclk),            // input
        .dst_clk   (hclk),            // input
        .in_pulse  (cmd_abort),       // input
        .out_pulse (cmd_abort_hclk),    // output
        .busy()                       // output
    );


endmodule