Commit 4336698a authored by Andrey Filippov's avatar Andrey Filippov
Browse files

finished initial code for ahci_fis_transmit module

parent 892ec17c
Loading
Loading
Loading
Loading
+101 −102
Original line number Original line Diff line number Diff line
@@ -21,32 +21,32 @@
`timescale 1ns/1ps
`timescale 1ns/1ps


module  ahci_fis_transmit #(
module  ahci_fis_transmit #(
    parameter PREFETCH_ALWAYS =   0,
    parameter READ_REG_LATENCY =  2, // 0 if  reg_rdata is available with reg_re/reg_addr
    parameter READ_REG_LATENCY =  2, // 0 if  reg_rdata is available with reg_re/reg_addr
    parameter READ_CT_LATENCY =   2, // 0 if  reg_rdata is available with reg_re/reg_addr
    parameter READ_CT_LATENCY =   2, // 0 if  reg_rdata is available with reg_re/reg_addr
    parameter ADDRESS_BITS =     10 // number of memory address bits - now fixed. Low half - RO/RW/RWC,RW1 (2-cycle write), 2-nd just RW (single-cycle)
    parameter ADDRESS_BITS =     10 // number of memory address bits - now fixed. Low half - RO/RW/RWC,RW1 (2-cycle write), 2-nd just RW (single-cycle)


)(
)(
    input                         hba_rst, // @posedge mclk - sync reset
    input                         hba_rst, // @posedge mclk - when port is reset (even COMINIT)?
    input                         mclk, // for command/status
    input                         mclk, // for command/status
    
    // Command pulses to execute states
    
    input                         fetch_cmd,   // Enter p:FetchCmd, fetch command header (from the register memory, prefetch command FIS)
    input                         fetch_cmd,   // Enter p:FetchCmd, fetch command header (from the register memory, prefetch command FIS)
                                               // wait for either fetch_cmd_busy == 0 or pCmdToIssue ==1 after fetch_cmd
                                               // wait for either fetch_cmd_busy == 0 or pCmdToIssue ==1 after fetch_cmd
    output                        pCmdToIssue, // AHCI port variable
    output                        dmaCntrZero, // DmA counter is zero (first command)
    output reg                    fetch_cmd_busy, // does not include prefetching CT
//    input                         fetch_ct,    // fetch command table (ch_ctba[31:7] should be valid by now)
    input                         cfis_xmit,    // transmit command (wait for dma_ct_busy == 0)
    input                         cfis_xmit,    // transmit command (wait for dma_ct_busy == 0)
    input                         dx_transmit,  // send FIS header DWORD, (just 0x46), then forward DMA data
    input                         dx_transmit,  // send FIS header DWORD, (just 0x46), then forward DMA data
                                                // transmit until error, 2048DWords or pDmaXferCnt 
                                                // transmit until error, 2048DWords or pDmaXferCnt 
    input                         atapi_xmit,   // tarsmit ATAPI command FIS
    output reg                    done,
    output reg                    busy,

    input                         clearCmdToIssue, // From CFIS:SUCCESS 
    output                        pCmdToIssue, // AHCI port variable
//    output                        dmaCntrZero, // DMA counter is zero - would be a duplicate to the one in receive module and dwords_sent output
    output reg                    fetch_cmd_busy, // does not include prefetching CT
    input                         syncesc_recv, // These two inputs interrupt transmit
    input                         syncesc_recv, // These two inputs interrupt transmit
    input                         xmit_err,     // 
    input                         xmit_err,     // 
    output                        dx_busy,
    output                        dx_done,      // single-clock dx_transmit is finished, check dx_err
    output                 [ 1:0] dx_err,       // bit 0 - syncesc_recv, 1 - xmit_err  (valid @ xmit_err and later, reset by new command)
    output                 [ 1:0] dx_err,       // bit 0 - syncesc_recv, 1 - xmit_err  (valid @ xmit_err and later, reset by new command)
    
    
    input                         atapi_xmit,   // tarsmit ATAPI command FIS
    
    output                 [15:0] ch_prdtl,    // Physical region descriptor table length (in entries, 0 is 0)
    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_c,        // Clear busy upon R_OK for this FIS
    output                        ch_b,        // Built-in self test command
    output                        ch_b,        // Built-in self test command
@@ -56,39 +56,30 @@ module ahci_fis_transmit #(
    output                        ch_a,        // ATAPI: 1 means device should send PIO setup FIS for ATAPI command
    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,
    output                  [4:0] ch_cfl,      // length of the command FIS in DW, 0 means none. 0 and 1 - illegal,
                                               // maximal is 16 (0x10)
                                               // maximal is 16 (0x10)
//    output                 [31:7] ch_ctba,     // command table base address - use   reg_rdata[31:7] - outside
    output reg             [11:2] dwords_sent, // number of DWORDs transmitted (up to 2048)                                 
    output reg             [11:2] dwords_sent, // number of DWORDs transmitted (up to 2048)                                 
    
    

    // register memory interface
    // register memory interface
    output reg [ADDRESS_BITS-1:0] reg_addr,      
    output reg [ADDRESS_BITS-1:0] reg_addr,      
    output                        reg_re,
    output                        reg_re,
    input                  [31:0] reg_rdata,
    input                  [31:0] reg_rdata,



    // ahci_fis_receive interface
    // ahci_fis_receive interface
    input                  [31:2] xfer_cntr,     // transfer counter in words for both DMA (31 bit) and PIO (lower 15 bits), updated after decr_dwc
    input                  [31:2] xfer_cntr,     // transfer counter in words for both DMA (31 bit) and PIO (lower 15 bits), updated after decr_dwc




    output                        dma_ctba_ld,   // load command table base address
    output                        dma_ctba_ld,   // load command table address from 
    output                        dma_start,     // start processing command table, reset prdbc (next cycle after dma_ctba_ld, bits prdtl valid)
    output                        dma_start,     // start processing command table, reset prdbc (next cycle after dma_ctba_ld, bits prdtl valid)
    output                        dma_dev_wr,    // write to device (valid at start)
    output                        dma_dev_wr,    // write to device (valid at start)
    input                         dma_ct_busy,   // dma module is busy reading command table from the system memory
    input                         dma_ct_busy,   // dma module is busy reading command table from the system memory
    // issue dma_prd_start same time as dma_start if prefetch enabled, otherwise with cfis_xmit
    // issue dma_prd_start same time as dma_start if prefetch enabled, otherwise with cfis_xmit
    output                        dma_prd_start, // at or after cmd_start - enable reading PRD/data (if any) ch_prdtl should be valid
    output reg                    dma_prd_start, // at or after cmd_start - enable reading PRD/data (if any) ch_prdtl should be valid, twice - OK
    
    output reg                    cmd_abort,   // try to abort a command TODO: Implement
//    output                        cmd_abort,   // try to abort a command TODO: Implement
    
    
    
    // reading out command table data from DMA module
    // reading out command table data from DMA module
    output reg             [ 4:0] ct_addr,     // DWORD address
    output reg             [ 4:0] ct_addr,     // DWORD address
    output                        ct_re,       //  
    output                        ct_re,       //  
    input                  [31:0] ct_data,     // 
    input                  [31:0] ct_data,     // 
    
    
    
    
    
    
    // DMA (memory -> device) interface
    // DMA (memory -> device) interface
    input                  [31:0] dma_out,      // 32-bit data from the DMA module, HBA -> device port
    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_dav,      // at least one dword is ready to be read from DMA module
@@ -122,8 +113,12 @@ module ahci_fis_transmit #(
    reg                      ch_p_r;
    reg                      ch_p_r;
    reg                      ch_w_r;
    reg                      ch_w_r;
    reg                      ch_a_r;
    reg                      ch_a_r;
    reg                [4:0] ch_cfl_r;
    reg                [4:0] ch_cmd_len_r;
    reg                [4:0] ch_cfl_out_r;
    reg                [4:0] cfis_acmd_left_r; // number of DWORDS in CFIS or ACMD area of the command table left to be fetched from ahci_dma module BRAM
                                               // For CFIS this register is set from ch_cmd_len_r, for ACMD - from the xfer_cntr input
                                               // (stored in the ahci_fis_receive module)
    reg                [4:0] cfis_acmd_left_out_r; // Same, just with latency of the data available from the ahci_dma module
    
//    reg               [31:7] ch_ctba_r;                                       
//    reg               [31:7] ch_ctba_r;                                       
    reg [READ_REG_LATENCY:0] reg_re_r;
    reg [READ_REG_LATENCY:0] reg_re_r;
    wire                     reg_re_w; // combined conditions to read register memory
    wire                     reg_re_w; // combined conditions to read register memory
@@ -135,17 +130,14 @@ module ahci_fis_transmit #(
    reg                      chead_bsy;    // busy reading command header
    reg                      chead_bsy;    // busy reading command header
    reg                      chead_bsy_re; // busy sending read command header
    reg                      chead_bsy_re; // busy sending read command header
    reg                      pCmdToIssue_r;
    reg                      pCmdToIssue_r;
    wire                     clearCmdToIssue; // TODO: assign - clear pCmdToIssue
//    reg                      fetch_ct_r;
//    reg                      fetch_ct_r;
    reg                      cfis_xmit_pend_r; //
    reg                      acfis_xmit_pend_r; //
    reg                      cfis_xmit_start_r; 
    reg                      acfis_xmit_start_r; 
    reg                      cfis_xmit_busy_r; //
    reg                      acfis_xmit_busy_r; //
    reg                      dmaCntrZero_r;  // first command 
//    reg                      anc_fis_r; // This is ATAPI FIS, not Command FIS
//    wire                     start_sync_escape_w = cfis_xmit && ch_r_r; - no, it should be instead of a ct_fetch
// TODO: Start FIS transmit when all FIS is in FIFO or data and >half(too slow, need minimum to be able to send wait primitive) or less?


    wire                     cfis_xmit_start_w = (dx_transmit || cfis_xmit_pend_r) && !dma_ct_busy && !fetch_cmd_busy; // dma_ct_busy no gaps with fetch_cmd_busy
    wire                     acfis_xmit_start_w = (cfis_xmit || atapi_xmit || acfis_xmit_pend_r) && !dma_ct_busy && !fetch_cmd_busy; // dma_ct_busy no gaps with fetch_cmd_busy
    wire                     cfis_xmit_end;
    wire                     acfis_xmit_end = ct_stb && fis_dw_last;
    
    
    wire                     ct_re_w; // next cycle will be ct_re;
    wire                     ct_re_w; // next cycle will be ct_re;
    reg  [READ_CT_LATENCY:0] ct_re_r;
    reg  [READ_CT_LATENCY:0] ct_re_r;
@@ -159,8 +151,9 @@ module ahci_fis_transmit #(
    wire                     dx_dma_last_w; // sending last adat word
    wire                     dx_dma_last_w; // sending last adat word
    reg                      dx_busy_r;
    reg                      dx_busy_r;
    reg               [ 1:0] dx_err_r;
    reg               [ 1:0] dx_err_r;
    reg                      dx_done_r;
    wire                     any_cmd_start = fetch_cmd || cfis_xmit || dx_transmit || atapi_xmit;
    wire                     any_cmd_start = fetch_cmd || cfis_xmit || dx_transmit || atapi_xmit;
    wire                     done_w = dx_dma_last_w || ((|dx_err_r) && dx_busy_r) || chead_done_w || acfis_xmit_end; // done on last transmit or error
    
    
    
    assign todev_valid = todev_full_r;
    assign todev_valid = todev_full_r;
    assign dma_re =   dma_re_w;
    assign dma_re =   dma_re_w;
@@ -173,27 +166,23 @@ module ahci_fis_transmit #(
    assign ch_p =     ch_p_r;
    assign ch_p =     ch_p_r;
    assign ch_w =     ch_w_r;
    assign ch_w =     ch_w_r;
    assign ch_a =     ch_a_r;
    assign ch_a =     ch_a_r;
    assign ch_cfl =   ch_cfl_r;
    assign ch_cfl =   cfis_acmd_left_r;
//    assign ch_ctba =  ch_ctba_r[31:7];

    assign reg_re_w = fetch_cmd || chead_bsy_re;
    assign reg_re_w = fetch_cmd || chead_bsy_re;
    assign dma_ctba_ld = fetch_chead_stb_r[2];
    assign dma_ctba_ld = fetch_chead_stb_r[2];
    assign dma_start =   fetch_chead_stb_r[3]; // next cycle after dma_ctba_ld 
    assign dma_start =   fetch_chead_stb_r[3]; // next cycle after dma_ctba_ld 
    assign pCmdToIssue = pCmdToIssue_r;
    assign pCmdToIssue = pCmdToIssue_r;
    assign dmaCntrZero = dmaCntrZero_r;
//    assign dmaCntrZero = dmaCntrZero_r;
    assign ct_re =       ct_re_r[0];
    assign ct_re =       ct_re_r[0];
    assign fis_data_valid = ct_stb; // no wait write to output register 'todev_data', ct_re_r[0] is throttled according to FIFO room availability
    assign fis_data_valid = ct_stb; // no wait write to output register 'todev_data', ct_re_r[0] is throttled according to FIFO room availability
    assign ct_re_w = todev_ready && ((ch_cfl_r[4:1] != 0) || (ch_cfl_r[0] && !ct_re_r[0]));  // Later add more sources
    assign ct_re_w = todev_ready && ((cfis_acmd_left_r[4:1] != 0) || (cfis_acmd_left_r[0] && !ct_re_r[0]));  // Later add more sources
    assign fis_dw_last = (ch_cfl_out_r == 1);
    assign fis_dw_last = (cfis_acmd_left_out_r == 1);
    assign fis_data_type = {fis_dw_last, (write_or_w && dx_fis_pend_r) | (fis_dw_first && ct_stb)};
    assign fis_data_type = {fis_dw_last, (write_or_w && dx_fis_pend_r) | (fis_dw_first && ct_stb)};
    
    
    assign fis_data_out = ({32{dx_fis_pend_r}} & DATA_FIS) | ({32{ct_stb}} & ct_data) ;
    assign fis_data_out = ({32{dx_fis_pend_r}} & DATA_FIS) | ({32{ct_stb}} & ct_data) ;
    assign dx_dma_last_w = dma_en_r && dma_re_w && (dx_dwords_left[11:2] == 1);
    assign dx_dma_last_w = dma_en_r && dma_re_w && (dx_dwords_left[11:2] == 1);


    assign dx_busy = dx_busy_r;
    assign dx_done = dx_done_r;
    assign dx_err = dx_err_r;
    assign dx_err = dx_err_r;
    
    assign dma_dev_wr = ch_w_r;
    
    
    always @ (posedge mclk) begin
    always @ (posedge mclk) begin
        // Mutliplex between DMA and FIS output to the output routed to transmit FIFO
        // Mutliplex between DMA and FIS output to the output routed to transmit FIFO
@@ -207,7 +196,9 @@ module ahci_fis_transmit #(
        if      (hba_rst)                  todev_type <= 3; // invalid? - no, now first and last word in command FIS (impossible?)
        if      (hba_rst)                  todev_type <= 3; // invalid? - no, now first and last word in command FIS (impossible?)
        else if (write_or_w)               todev_type <= dma_en_r? {dx_dma_last_w, 1'b0} : fis_data_type;
        else if (write_or_w)               todev_type <= dma_en_r? {dx_dma_last_w, 1'b0} : fis_data_type;
        
        
        if (hba_rst)          fetch_chead_r <= 0;
        // Read 3 DWORDs from the command header
        
        if (hba_rst)                       fetch_chead_r <= 0; // running 1 
        else if (fetch_cmd)                fetch_chead_r <= 1;
        else if (fetch_cmd)                fetch_chead_r <= 1;
        else                               fetch_chead_r <= fetch_chead_r << 1;
        else                               fetch_chead_r <= fetch_chead_r << 1;
        
        
@@ -223,7 +214,7 @@ module ahci_fis_transmit #(
        else if (fetch_cmd)                chead_bsy_re <= 1;
        else if (fetch_cmd)                chead_bsy_re <= 1;
        else if (fetch_chead_r[1])         chead_bsy_re <= 0; // read 3 dwords
        else if (fetch_chead_r[1])         chead_bsy_re <= 0; // read 3 dwords
        
        
        if      (hba_rst)              reg_re_r <= 0;
        if      (hba_rst)                  reg_re_r <= 0; // [0] -> reg_re output
        else if (reg_re_w)                 reg_re_r <= 1;
        else if (reg_re_w)                 reg_re_r <= 1;
        else                               reg_re_r <= reg_re_r << 1;
        else                               reg_re_r <= reg_re_r << 1;
        
        
@@ -239,7 +230,7 @@ module ahci_fis_transmit #(
            ch_p_r <=       reg_rdata[    7];
            ch_p_r <=       reg_rdata[    7];
            ch_w_r <=       reg_rdata[    6];
            ch_w_r <=       reg_rdata[    6];
            ch_a_r <=       reg_rdata[    5];
            ch_a_r <=       reg_rdata[    5];
            ch_cfl_r <=   reg_rdata[ 4: 0];
            ch_cmd_len_r<=  reg_rdata[ 4: 0];
        end
        end


       if      (hba_rst)         pCmdToIssue_r <= 0;
       if      (hba_rst)         pCmdToIssue_r <= 0;
@@ -250,39 +241,41 @@ module ahci_fis_transmit #(
       else if (fetch_cmd)       fetch_cmd_busy <= 1;
       else if (fetch_cmd)       fetch_cmd_busy <= 1;
       else if (dma_start)       fetch_cmd_busy <= 0;
       else if (dma_start)       fetch_cmd_busy <= 0;
       
       
       // fetch and send command fis
       //CFIS/ATAPI common
       if (hba_rst || cfis_xmit_start_w) cfis_xmit_pend_r <= 0;
       else if (cfis_xmit)               cfis_xmit_pend_r <= 1;
       
       
       cfis_xmit_start_r <= !hba_rst && cfis_xmit_start_w;
//       if (hba_rst || cfis_xmit)  anc_fis_r <= 0;
//       else if (atapi_xmit)       anc_fis_r <= 1;


       if      (hba_rst)           cfis_xmit_busy_r <= 0;
       // fetch and send command/atapi FIS
       else if (cfis_xmit_start_r) cfis_xmit_busy_r <= 1;
       if (hba_rst || acfis_xmit_start_w) acfis_xmit_pend_r <= 0;
       else if (cfis_xmit_end)     cfis_xmit_busy_r <= 0;
       else if (cfis_xmit || atapi_xmit)  acfis_xmit_pend_r <= 1;
        
        
       if      (fetch_chead_stb_r[0])            ch_cfl_r <=   reg_rdata[ 4: 0];  // Will assume that there is room for ... 
       acfis_xmit_start_r <= !hba_rst && acfis_xmit_start_w;
       else if (cfis_xmit_busy_r && ct_re_r[0])  ch_cfl_r <=   ch_cfl_r - 1;


       // Counting CFIS dwords sent to TL
       if      (hba_rst)            acfis_xmit_busy_r <= 0;
       if (cfis_xmit_start_w) ch_cfl_out_r <= ch_cfl_r;
       else if (acfis_xmit_start_r) acfis_xmit_busy_r <= 1;
       else if (ct_stb)       ch_cfl_out_r <=  ch_cfl_out_r - 1;
       else if (acfis_xmit_end)     acfis_xmit_busy_r <= 0;
       
       if      (cfis_xmit)                        cfis_acmd_left_r <=   ch_cmd_len_r[ 4: 0];   // Will assume that there is room for ...
       else if (atapi_xmit)                       cfis_acmd_left_r <=  (|xfer_cntr[31:4]) ? 5'h4 : {3'b0,xfer_cntr[3:2]};
       else if (acfis_xmit_busy_r && ct_re_r[0])  cfis_acmd_left_r <=   cfis_acmd_left_r - 1;
       
       // Counting CFIS/ATAPI FIS dwords sent to TL
       if      (acfis_xmit_start_r) cfis_acmd_left_out_r <= cfis_acmd_left_r;
       else if (ct_stb)             cfis_acmd_left_out_r <= cfis_acmd_left_out_r - 1;
       
       
       ct_re_r <= {ct_re_r[READ_CT_LATENCY-1:0],ct_re_w};
       ct_re_r <= {ct_re_r[READ_CT_LATENCY-1:0],ct_re_w};
       
       
       if      (cfis_xmit)   ct_addr <= 0;
       if      (cfis_xmit)   ct_addr <= 0;
       else if (ch_cfl_r[0]) ct_addr <= ct_addr + 1;
       else if (atapi_xmit)  ct_addr <= 'h10; // start of ATAPI area
       else if (cfis_acmd_left_r[0]) ct_addr <= ct_addr + 1;


       // first/last dword in FIS
       // first/last dword in FIS
       if (!cfis_xmit_busy_r) fis_dw_first <= 1;
       if (!acfis_xmit_busy_r) fis_dw_first <= 1;
       else if (ct_stb)        fis_dw_first <= 0;
       else if (ct_stb)        fis_dw_first <= 0;
       
       
       // TODO: Implement ATAPI command, other FIS to send?
       //TODO: update xfer length, prdtl (only after R_OK) - yes, do it outside
       
       
       // Send Data FIS TODO: abort on errors, and busy (or done) output
       //    input                         syncesc_recv, // These two inputs interrupt transmit
       // input                         xmit_err,     //
       
       //TODO: update xfer length, prdtl (only after R_OK)?
       if   (dx_transmit) dx_dwords_left[11:2] <= (|xfer_cntr[31:11])?10'h200:{1'b0,xfer_cntr[10:2]};
       if   (dx_transmit) dx_dwords_left[11:2] <= (|xfer_cntr[31:11])?10'h200:{1'b0,xfer_cntr[10:2]};
       else if (dma_re_w) dx_dwords_left[11:2] <= dx_dwords_left[11:2] - 1;
       else if (dma_re_w) dx_dwords_left[11:2] <= dx_dwords_left[11:2] - 1;
       
       
@@ -306,15 +299,21 @@ module ahci_fis_transmit #(
       if      (hba_rst)                      dx_busy_r <= 0;
       if      (hba_rst)                      dx_busy_r <= 0;
       else if (dx_transmit)                  dx_busy_r <= 1;
       else if (dx_transmit)                  dx_busy_r <= 1;
       else if (dx_dma_last_w || (|dx_err_r)) dx_busy_r <= 0;
       else if (dx_dma_last_w || (|dx_err_r)) dx_busy_r <= 0;
       // done on last transmit or error
       if      (hba_rst)                      dx_done_r <= 0;
       else                                   dx_done_r <= dx_dma_last_w || ((|dx_err_r) && dx_busy_r);
       // TODO: Make commmon done for all commands?


       dma_prd_start <= (dma_start && (PREFETCH_ALWAYS || ch_p_r || !ch_w_r)) ||  // device read may prefetch just prd addresses
                        (dx_fis_pend_r &&  write_or_w); // enable PRD read now (if it was not already done)
        
        
    end 
       if      (hba_rst)                      done <= 0;
       else                                   done <= done_w;
       
       
       if      (hba_rst)                      busy <= 0;
       else if (any_cmd_start)                busy <= 1;
       else if (done_w)                       busy <= 0;


       cmd_abort <= done_w && (|dx_err_r);


    end 


endmodule
endmodule