Commit f81326a9 authored by Andrey Filippov's avatar Andrey Filippov
Browse files

Continue on AHCI implementation - started FIS transmit

parent ce85dcb3
Loading
Loading
Loading
Loading
+35 −20
Original line number Original line Diff line number Diff line
@@ -43,14 +43,21 @@ module ahci_fis_receive#(
    input                         get_sdbfis,
    input                         get_sdbfis,
    input                         get_ufis,
    input                         get_ufis,
    input                         get_data_fis,
    input                         get_data_fis,
    input                         get_ignore,    // ignore whatever FIS data in the
    input                         get_ignore,    // ignore whatever FIS (use for DMA activate too?)
    output reg                    get_fis_busy,  // busy processing FIS 
    output reg                    get_fis_busy,  // busy processing FIS 
    output reg                    fis_first_vld, // fis_first contains valid FIS header, reset by get_*
    output reg                    fis_first_vld, // fis_first contains valid FIS header, reset by get_*
    output reg                    fis_ok,        // FIS done,  checksum OK reset by starting a new get FIS
    output reg                    fis_ok,        // FIS done,  checksum OK reset by starting a new get FIS
    output reg                    fis_err,       // FIS done, checksum ERROR reset by starting a new get FIS
    output reg                    fis_err,       // FIS done, checksum ERROR reset by starting a new get FIS
    output                        fis_ferr,      // FIS done, fatal error - FIS too long
    output                        fis_ferr,      // FIS done, fatal error - FIS too long
    input                         update_err_sts,// update PxTFD.STS and PxTFD.ERR from the last received regs d2h
    input                         update_err_sts,// update PxTFD.STS and PxTFD.ERR from the last received regs d2h
    input                         clear_bsy_drq, // clear PxTFD.STS.BSY and PxTFD.STS.DRQ, update
    input                         set_bsy,       // set PxTFD.STS.BSY, update
    input                         set_sts_7f,    // set PxTFD.STS = 0x7f, update
    input                         set_sts_80,    // set PxTFD.STS = 0x80 (may be combined with set_sts_7f), update
    // TODO: Add writing PRDBC here?
    
    output                  [7:0] tfd_sts,       // Current PxTFD status field (updated after regFIS and SDB - certain fields)
    output                  [7:0] tfd_sts,       // Current PxTFD status field (updated after regFIS and SDB - certain fields)
                                                 // tfd_sts[7] - BSY, tfd_sts[4] - DRQ, tfd_sts[0] - ERR
    output                  [7:0] tfd_err,       // Current PxTFD error field (updated after regFIS and SDB)
    output                  [7:0] tfd_err,       // Current PxTFD error field (updated after regFIS and SDB)
    output reg                    fis_i,         // value of "I" field in received regsD2H or SDB FIS
    output reg                    fis_i,         // value of "I" field in received regsD2H or SDB FIS
    output reg                    sdb_n,         // value of "N" field in received SDB FIS 
    output reg                    sdb_n,         // value of "N" field in received SDB FIS 
@@ -86,6 +93,8 @@ CLB_OFFS = 0x800 # In the second half of the register space (0x800..0xbff - 1KB)
FB_OFFS =  0xc00 # Needs 0x100 bytes 
FB_OFFS =  0xc00 # Needs 0x100 bytes 
#HBA_PORT0 = 0x100 Not needed, always HBA_OFFS + 0x100
#HBA_PORT0 = 0x100 Not needed, always HBA_OFFS + 0x100




*/
*/
localparam HBA_OFFS32 =         0;
localparam HBA_OFFS32 =         0;
localparam HBA_PORT0_OFFS32  = 'h40;
localparam HBA_PORT0_OFFS32  = 'h40;
@@ -147,6 +156,7 @@ localparam DATA_TYPE_ERR = 3;
    
    


    reg          [15:0] tf_err_sts;
    reg          [15:0] tf_err_sts;
    reg                 update_err_sts_r;
    
    
    // Forward data to DMA (dev->mem) engine
    // Forward data to DMA (dev->mem) engine
    assign              dma_in_valid = dma_in_ready && (hda_data_in_type == DATA_TYPE_DMA) && data_in_ready && !too_long_err;
    assign              dma_in_valid = dma_in_ready && (hda_data_in_type == DATA_TYPE_DMA) && data_in_ready && !too_long_err;
@@ -163,8 +173,6 @@ localparam DATA_TYPE_ERR = 3;
    assign tfd_sts = tf_err_sts[ 7:0];
    assign tfd_sts = tf_err_sts[ 7:0];
    assign tfd_err = tf_err_sts[15:8];
    assign tfd_err = tf_err_sts[15:8];
     
     
    
     
    always @ (posedge mclk) begin
    always @ (posedge mclk) begin
        if (hba_rst || dma_in_stop) dma_in <= 0;
        if (hba_rst || dma_in_stop) dma_in <= 0;
        else if (dma_in_start)      dma_in <= 1;
        else if (dma_in_start)      dma_in <= 1;
@@ -239,28 +247,35 @@ localparam DATA_TYPE_ERR = 3;
        
        
        if (reg_we_w)                         reg_data[31:8] <= hda_data_in[31:8];
        if (reg_we_w)                         reg_data[31:8] <= hda_data_in[31:8];
        else if (update_sig[1])               reg_data[31:8] <= hda_data_in[23:0];
        else if (update_sig[1])               reg_data[31:8] <= hda_data_in[23:0];
        else if (update_err_sts) reg_data[31:8] <= {16'b0,tf_err_sts[15:8]};
        else if (update_err_sts_r)            reg_data[31:8] <= {16'b0,tf_err_sts[15:8]};


        if (reg_we_w)                         reg_data[ 7:0] <=  hda_data_in[ 7:0];
        if (reg_we_w)                         reg_data[ 7:0] <=  hda_data_in[ 7:0];
        else if (update_sig[3])               reg_data[ 7:0] <=  hda_data_in[ 7:0];
        else if (update_sig[3])               reg_data[ 7:0] <=  hda_data_in[ 7:0];
        else if (update_err_sts) reg_data[ 7:0] <=  tf_err_sts [ 7:0];
        else if (update_err_sts_r)            reg_data[ 7:0] <=  tf_err_sts [ 7:0];
        
        
        if (reg_d2h || update_sig[0])         tf_err_sts  <= hda_data_in[15:0];
        if (reg_d2h || update_sig[0])         tf_err_sts  <= hda_data_in[15:0];
        else if (reg_sdb)                     tf_err_sts  <= {hda_data_in[15:8], tf_err_sts[7], hda_data_in[6:4], tf_err_sts[3],hda_data_in[2:0]};
        else if (reg_sdb)                     tf_err_sts  <= {hda_data_in[15:8], tf_err_sts[7], hda_data_in[6:4], tf_err_sts[3],hda_data_in[2:0]};
        else if (clear_bsy_drq || set_bsy)    tf_err_sts  <= tf_err_sts & {8'hff,clear_bsy_drq,3'h7,clear_bsy_drq,3'h7} | {8'h0,set_bsy,7'h0};
        else if (set_sts_7f || set_sts_80)    tf_err_sts  <= {tf_err_sts[15:8],set_sts_80,{7{set_sts_7f}}} ;
        
        reg_we <= reg_we_w || update_sig[3] || update_err_sts_r;
        
        
        reg_we <= reg_we_w || update_sig[3] || update_err_sts;
        if (reg_we_w || update_sig[3])        reg_addr <=  reg_addr_r;
        if (reg_we_w || update_sig[3])        reg_addr <=  reg_addr_r;
        else if (update_err_sts)     reg_addr <=  PXTFD_OFFS32;
        else if (update_err_sts_r)            reg_addr <=  PXTFD_OFFS32;


        if (reg_d2h || reg_sdb || reg_ds[0])  fis_i <=           hda_data_in[14];
        if (reg_d2h || reg_sdb || reg_ds[0])  fis_i <=           hda_data_in[14];
        if (reg_sdb)                          sdb_n <=           hda_data_in[15];
        if (reg_sdb)                          sdb_n <=           hda_data_in[15];
        if (reg_ds[0])                        {dma_a,dma_d}  <=  {hda_data_in[15],hda_data_in[13]};
        if (reg_ds[0])                        {dma_a,dma_d}  <=  {hda_data_in[15],hda_data_in[13]};


        if (reg_ps[0])                        {pio_i,pio_d}  <=  {hda_data_in[14],hda_data_in[13]};
        if (reg_ps[0])                        {pio_i,pio_d}  <=  {hda_data_in[14],hda_data_in[13]};
        if (reg_ps[3])                       pio_es  <=         hda_data_in[31:24];
        
        if (hba_rst)                          pio_es  <=         0;
        else if (reg_ps[3])                   pio_es  <=         hda_data_in[31:24];
        
        
        if (reg_ps[4] || reg_ds[5])           xfer_cntr[31:1] <= {reg_ds[5]?hda_data_in[31:16]:16'b0,hda_data_in[15:1]};
        if (reg_ps[4] || reg_ds[5])           xfer_cntr[31:1] <= {reg_ds[5]?hda_data_in[31:16]:16'b0,hda_data_in[15:1]};
        
        
        update_err_sts_r <= update_err_sts || clear_bsy_drq || set_bsy || set_sts_7f || set_sts_80;
        
    end
    end


endmodule
endmodule
+171 −0
Original line number Original line Diff line number Diff line
/*******************************************************************************
 * Module: ahci_fis_transmit
 * Date:2016-01-07  
 * Author: andrey     
 * Description: Fetches commands, command tables, creates/sends FIS
 *
 * Copyright (c) 2016 Elphel, Inc .
 * ahci_fis_transmit.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.
 *
 *  ahci_fis_transmit.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  ahci_fis_transmit #(
    parameter READ_REG_LATENCY =  2 // 0 if  reg_rdata is available with reg_re/reg_addr

)(
    input                         hba_rst, // @posedge mclk - sync reset
    input                         mclk, // for command/status
    
    
    input                         fetch_chead, // fetch command header (from the register memory
    output                 [15:0] ch_prdtl,    // Physical region descriptor table length (in entries, 0 is 0)
    output                        ch_c,        // Clear busy upon R_OK for this FIS
    output                        ch_b,        // Built-in self test command
    output                        ch_r,        // reset - may need to send SYNC escape before this command
    output                        ch_p,        // prefetchable - only used with non-zero PRDTL or ATAPI bit set
    output                        ch_w,        // Write: system memory -> device
    output                        ch_a,        // ATAPI: 1 means device should send PIO setup FIS for ATAPI command
    output                  [4:0] ch_cfl,      // length of the command FIS in DW, 0 means none. 0 and 1 - illegal,
                                               // maximal is 16 (0x10)
    output                 [31:7] ch_ctba,     // command table base address                                       
    

    // register memory interface
    output reg [ADDRESS_BITS-1:0] reg_addr,      
    output                        reg_re,
    output reg             [31:0] reg_rdata,
    
    // DMA (memory -> device) interface
    input                  [31:0] dma_out,      // 32-bit data from the DMA module, HBA -> device port
    input                         dma_dav,      // at least one dword is ready to be read from DMA module
    input                         dma_re,       // read dword from DMA module to the outpu register
    
    // Data System memory or FIS -> device
    output reg             [31:0] todev_data,     // 32-bit data from the system memory to HBA (dma data)
    output reg             [ 1:0] todev_type,     // 0 - data, 1 - FIS head, 2 - FIS END (make FIS_Last?)
    output                        todev_valid,    // output register full
    input                         todev_ready     // send FIFO has room for data (>= ? dwords)
    
    // Add a possiblity to flush any data to FIFO if error was detected after data went there?
);
    localparam CLB_OFFS32 = 'h200; //  # In the second half of the register space (0x800..0xbff - 1KB)
 
    reg                 todev_full_r;
    reg                 dma_en_r;
    wire                fis_data_valid;
    wire          [1:0] fis_data_type;
    wire         [31:0] fis_data_out;
    
    wire                write_or_w = (dma_en_r?dma_dav:fis_data_valid) && todev_ready; // do not fill the buffer if FIFO is not ready
    wire                fis_out_w =  !dma_en_r && fis_data_valid && todev_ready;
    wire                dma_re_w =    dma_en_r && dma_dav && todev_ready;

    reg               [15:0] ch_prdtl_r;
    reg                      ch_c_r;
    reg                      ch_b_r;
    reg                      ch_r_r;
    reg                      ch_p_r;
    reg                      ch_w_r;
    reg                      ch_a_r;
    reg                [4:0] ch_cfl_r;
    reg               [31:7] ch_ctba_r;                                       
    reg [READ_REG_LATENCY:0] reg_re_r;
    wire                     reg_re_w; // combined conditions to read register memory
    wire                     reg_stb =     reg_re_r[READ_REG_LATENCY];
    wire                     pre_reg_stb = reg_re_r[READ_REG_LATENCY-1];
    reg                [3:0] fetch_chead_r;
    reg                [2:0] fetch_chead_stb_r;
    reg                      chead_bsy;    // busy reading command header
    reg                      chead_bsy_re; // busy sending read command header
    
    assign todev_valid = todev_full_r;
    assign dma_re =   dma_re_w;
    assign reg_re =   reg_re_r[0]; 
    
    assign ch_prdtl = ch_prdtl_r;
    assign ch_c =     ch_c_r;
    assign ch_b =     ch_b_r;
    assign ch_r =     ch_r_r;
    assign ch_p =     ch_p_r;
    assign ch_w =     ch_w_r;
    assign ch_a =     ch_a_r;
    assign ch_cfl =   ch_cfl_r;
    assign ch_ctba =  ch_ctba_r[31:7];

    assign reg_re_w = fetch_chead || chead_bsy_re;

    
    always @ (posedge mclk) begin
        // Mutliplex between DMA and FIS output to the output routed to transmit FIFO
        // Count bypassing DMA dwords to generate FIS_last condition?
        if      (hba_rst)     todev_full_r <= 0;
        else if (write_or_w)  todev_full_r <= 1; // do not fill the buffer if FIFO is not ready
        else if (todev_ready) todev_full_r <= 0;
        
        if (write_or_w)       todev_data <= dma_en_r? dma_out: fis_data_out;
        
        if      (hba_rst)     todev_type <= 3; // invalid?
        else if (write_or_w)  todev_type <= dma_en_r? 2'h0 : fis_data_type;
        
        if (hba_rst)          fetch_chead_r <= 0;
        else if (fetch_chead) fetch_chead_r <= 1;
        else                  fetch_chead_r <= fetch_chead_r << 1;
        
        if      (hba_rst)                  fetch_chead_stb_r <= 0;
        else if (pre_reg_stb && chead_bsy) fetch_chead_stb_r <= 1;
        else                               fetch_chead_stb_r <= fetch_chead_stb_r << 1;
        
        if      (hba_rst)              chead_bsy <= 0;
        else if (fetch_chead)          chead_bsy <= 1;
        else if (fetch_chead_stb_r[2]) chead_bsy <= 0;

        if      (hba_rst)              chead_bsy_re <= 0;
        else if (fetch_chead)          chead_bsy_re <= 1;
        else if (fetch_chead_r[1])     chead_bsy_re <= 0; // read 3 dwords
        
        if      (hba_rst)              reg_re_r <= 0;
        else if (reg_re_w)             reg_re_r <= 1;
        else                           reg_re_r <= reg_re_r << 1;
        
        if      (fetch_chead)          reg_addr <= CLB_OFFS32;   // there will be more conditions
        else if (reg_re_r[0])          reg_addr <= reg_addr + 1;
        
        // save command header data to registers
        if (fetch_chead_stb_r[0]) begin
            ch_prdtl_r <= reg_rdata[31:16];
            ch_c_r <=     reg_rdata[   10];
            ch_b_r <=     reg_rdata[    9];
            ch_r_r <=     reg_rdata[    8];
            ch_p_r <=     reg_rdata[    7];
            ch_w_r <=     reg_rdata[    6];
            ch_a_r <=     reg_rdata[    5];
            ch_cfl_r <=   reg_rdata[ 4: 0];
        end

        if (fetch_chead_stb_r[2]) ch_ctba_r[31:7] <= reg_rdata[31:7];
        
        
/*
    reg                [3:0] fetch_chead_r;
    reg                [3:0] fetch_chead_stb_r;
    reg                      chead_bsy; // busy reading command header

*/        
        
    end 
    


endmodule