Commit 22d73e32 authored by Alexey Grebenkin's avatar Alexey Grebenkin
Browse files

Unfinished link layer

parent b7d39322
Loading
Loading
Loading
Loading

host/link.v

0 → 100644
+350 −0
Original line number Diff line number Diff line
/*******************************************************************************
 * Module: link
 * Date: 2015-07-11  
 * Author: Alexey     
 * Description: sata link layer implementation
 *
 * Copyright (c) 2015 Elphel, Inc.
 * link.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.
 *
 * link.v file 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/> .
 *******************************************************************************/
module link #(
    // 2 = word, 4 = dword or 8 = qword
    parameter DATA_BYTE_WIDTH = 4
)
(
    input   wire    rst,
    input   wire    clk,

    // inputs from transport layer
    // request for a new frame transition
    input   wire    frame_req,
    // input data stream (if any data during OOB setting => ignored)
    input   wire    [DATA_BYTE_WIDTH*8 - 1:0] data_in,
    // in case of strange data aligments and size (1st mentioned @ doc, p.310, odd number of words case)
    // Actually, only last data bundle shall be masked, others are always valid.
    // Mask could be encoded into 3 bits instead of 4 for qword, but encoding+decoding aren't worth the bit
    input   wire    [DATA_BYTE_WIDTH/2 - 1:0] data_mask_in,
    // buffer read strobe
    output  wire    data_strobe_out,
    // transaction's last data budle pulse
    input   wire    data_last_in,
    // read data is valid (if 0 while last pulse wasn't received => need to hold the line)
    input   wire    data_val_in,

    // outputs to transport layer
    // tell if the transaction is done and how it was done
    output  wire    frame_done_good,
    output  wire    frame_done_bad,
    // read data, same as related inputs
    output  wire    [DATA_BYTE_WIDTH*8 - 1:0] data_out,
    output  wire    [DATA_BYTE_WIDTH/2 - 1:0] data_mask_out,
    // count every data bundle read by transport layer, even if busy flag is set
    // let the transport layer handle oveflows by himself
    output  wire    data_val_out,
    output  wire    data_last_out,
    // transport layer tells if its inner buffer is almost full
    input   wire    data_busy_in,


    // inputs from phy
    // phy is ready - link is established
    input   wire    phy_ready,

);
wire    dec_err; // doc, p.311 TODO
wire    [:0]    rcvd_dword; // shows current processing primitive (or just data dword) TODO


wire    data_txing; // if there are still some data to transmit and the transaction wasn't cancelled
always @ (posedge clk)
    data_txing <= rst | (data_last_in & data_strobe_out | dword_val & rcvd_dword[CODE_DMATP]) ? 1'b0 : frame_req ? 1'b1 : data_txing;

// list of bits of rcvd_dword
localparam  CODE_DATA   = 0; 
localparam  CODE_HOLDP  = 1;
localparam  CODE_SYNCP  = 2;
localparam  CODE_DMATP  = 3;

// form a response to transport layer
wire    frame_done;
assign  frame_done      = frame_done_good | frame_done_bad;
assign  frame_done_good = dword_val & rcvd_dword[CODE_OKP];
assign  frame_done_bad  = dword_val & rcvd_dword[CODE_ERRP];

// fsm
// states and transitions are taken from the doc, "Link Layer State Machine" chapter
// power mode states are not implemented. TODO insert them as an additional branch of fsm

// !!!IMPORTANT!!! If add/remove any states, dont forget to change this parameter value
localparam STATES_COUNT = 23;
// idle state
wire    state_idle;
reg     state_sync_esc;     // SyncEscape
reg     state_nocommerr;    // NoComErr
reg     state_nocomm;       // NoComm
reg     state_align;        // SendAlign
reg     state_reset;        // RESET
// tranmitter branch
reg     state_send_rdy;     // SendChkRdy
reg     state_send_sof;     // SendSOF
reg     state_send_data;    // SendData
reg     state_send_rhold;   // RcvrHold - hold initiated by current data reciever
reg     state_send_shold;   // SendHold - hold initiated by current data sender
reg     state_send_crc;     // SendCVC
reg     state_send_eof;     // SendEOF
reg     state_wait;         // Wait
// receiver branch
reg     state_rcvr_wait;    // RcvWaitFifo
reg     state_rcvr_rdy;     // RcvChkRdy
reg     state_rcvr_data;    // RcvData
reg     state_rcvr_rhold;   // Hold     - hold initiated by current data reciever
reg     state_rcvr_shold;   // RcvHold  - hold initiated by current data sender
reg     state_rcvr_eof;     // RcvEOF
reg     state_rcvr_goodcrc; // GoodCRC
reg     state_rcvr_goodend; // GoodEnd
reg     state_rcvr_badend;  // BadEnd

wire    set_sync_esc;
wire    set_nocommerr;
wire    set_nocomm;
wire    set_align;
wire    set_reset;
wire    set_send_rdy;
wire    set_send_sof;
wire    set_send_data;
wire    set_send_rhold;
wire    set_send_shold;
wire    set_send_crc;
wire    set_send_eof;
wire    set_wait;
wire    set_rcvr_wait;
wire    set_rcvr_rdy;
wire    set_rcvr_data;
wire    set_rcvr_rhold;
wire    set_rcvr_shold;
wire    set_rcvr_eof;
wire    set_rcvr_goodcrc;
wire    set_rcvr_goodend;
wire    set_rcvr_badend;
                            
wire    clr_sync_esc;
wire    clr_nocommerr;
wire    clr_nocomm;
wire    clr_align;
wire    clr_reset;
wire    clr_send_rdy;
wire    clr_send_sof;
wire    clr_send_data;
wire    clr_send_rhold;
wire    clr_send_shold;
wire    clr_send_crc;
wire    clr_send_eof;
wire    clr_wait;
wire    clr_rcvr_wait;
wire    clr_rcvr_rdy;
wire    clr_rcvr_data;
wire    clr_rcvr_rhold;
wire    clr_rcvr_shold;
wire    clr_rcvr_eof;
wire    clr_rcvr_goodcrc;
wire    clr_rcvr_goodend;
wire    clr_rcvr_badend;

assign state_idle = ~state_sync_esc
                  | ~state_nocommerr
                  | ~state_nocomm
                  | ~state_align
                  | ~state_reset
                  | ~state_send_rdy
                  | ~state_send_sof
                  | ~state_send_data
                  | ~state_send_rhold
                  | ~state_send_shold
                  | ~state_send_crc
                  | ~state_send_eof
                  | ~state_wait
                  | ~state_rcvr_wait
                  | ~state_rcvr_rdy
                  | ~state_rcvr_data
                  | ~state_rcvr_rhold
                  | ~state_rcvr_shold
                  | ~state_rcvr_eof
                  | ~state_rcvr_goodcrc
                  | ~state_rcvr_goodend
                  | ~state_rcvr_badend;


assign  set_sync_esc        = 
assign  set_nocommerr       =
assign  set_nocomm          =
assign  set_align           =
assign  set_reset           =
assign  set_send_rdy        = state_idle        & frame_req;
assign  set_send_sof        = state_send_rdy    & phy_ready;
assign  set_send_data       = state_send_sof    & phy_ready 
                            | state_send_rhold  & data_txing & ~dec_err &                     dword_val & ~rcvd_dword[CODE_HOLDP] & ~rcvd_dword[CODE_SYNCP] & ~rcvd_dword[CODE_DMATP]
                            | state_send_shold  & data_txing &  data_val_in &                 dword_val & ~rcvd_dword[CODE_HOLDP] & ~rcvd_dword[CODE_SYNCP];
assign  set_send_rhold      = state_send_data   & data_txing &  data_val_in & ~data_last_in & dword_val &  rcvd_dword[CODE_HOLDP]
                            | state_send_shold  & data_txing &  data_val_in &                 dword_val &  rcvd_dword[CODE_HOLDP];
assign  set_send_shold      = state_send_data   & data_txing & ~data_val_in &                 dword_val & ~rcvd_dword[CODE_SYNCP];
assign  set_send_crc        = state_send_data   & data_txing &  data_val_in &  data_last_in & dword_val & ~rcvd_dword[CODE_SYNCP] 
                            | state_send_data   &                                             dword_val &  rcvd_dword[CODE_DMATP];
assign  set_send_eof        = state_send_crc    & phy_ready &                                 dword_val & ~rcvd_dword[CODE_SYNCP];
assign  set_wait            = state_send_eof    & phy_ready &                                 dword_val & ~rcvd_dword[CODE_SYNCP];
// receiver's branch
assign  set_rcvr_wait       = state_idle        & dword_val &  rcvd_dword[CODE_RDYP];
                            | state_send_rdy    & dword_val &  rcvd_dword[CODE_RDYP];
assign  set_rcvr_rdy        = state_rcv_wait    & dword_val &  rcvd_dword[CODE_RDYP]  & ~data_busy_in;
assign  set_rcvr_data       = state_rcvr_rdy    & dword_val &  rcvd_dword[CODE_SOFP];
                            | state_rcvr_rhold  & dword_val & ~rcvd_dword[CODE_HOLDP] & ~rcvd_dword[CODE_EOFP] & ~rcvd_dword[CODE_SYNCP] & ~data_busy_in;
                            | state_rcvr_shold  & dword_val & ~rcvd_dword[CODE_HOLDP] & ~rcvd_dword[CODE_EOFP] & ~rcvd_dword[CODE_SYNCP];
assign  set_rcvr_rhold      = state_rcvr_data   & dword_val &  rcvd_dword[CODE_DATA]  &  data_busy_in;
assign  set_rcvr_shold      = state_rcvr_data   & dword_val &  rcvd_dword[CODE_HOLDP];
                            | state_rcvr_rhold  & dword_val &  rcvd_dword[CODE_HOLDP] & ~data_busy_in;
assign  set_rcvr_eof        = state_rcvr_data   & dword_val &  rcvd_dword[CODE_EOFP];
                            | state_rcvr_rhold  & dword_val &  rcvd_dword[CODE_EOFP];
                            | state_rcvr_shold  & dword_val &  rcvd_dword[CODE_EOFP];
assign  set_rcvr_goodcrc    = state_rcvr_eof    & crc_good;
assign  set_rcvr_goodend    = state_rcvr_goodcrc& 
assign  set_rcvr_badend     = state_rcvr_data   & 
                            | state_rcvr_eof    & crc_bad;
                            | state_rcvr_goodcrc&

assign  clr_sync_esc        =
assign  clr_nocommerr       =
assign  clr_nocomm          =
assign  clr_align           =
assign  clr_reset           =
assign  clr_send_rdy        = 
assign  clr_send_sof        =
assign  clr_send_data       =
assign  clr_send_rhold      =
assign  clr_send_shold      =
assign  clr_send_crc        =
assign  clr_send_eof        =
assign  clr_wait            = frame_done; 
assign  clr_rcvr_wait       =
assign  clr_rcvr_rdy        =
assign  clr_rcvr_data       =
assign  clr_rcvr_rhold      =
assign  clr_rcvr_shold      =
assign  clr_rcvr_eof        =
assign  clr_rcvr_goodcrc    =
assign  clr_rcvr_goodend    =
assign  clr_rcvr_badend     =

always @ (posedge clk)
    state_sync_esc      <= (state_sync_esc     | set_sync_esc    ) & ~clr_sync_esc     & ~rst;
    state_nocommerr     <= (state_nocommerr    | set_nocommerr   ) & ~clr_nocommerr    & ~rst;
    state_nocomm        <= (state_nocomm       | set_nocomm      ) & ~clr_nocomm       & ~rst;
    state_align         <= (state_align        | set_align       ) & ~clr_align        & ~rst;
    state_reset         <= (state_reset        | set_reset       ) & ~clr_reset        & ~rst;
    state_send_rdy      <= (state_send_rdy     | set_send_rdy    ) & ~clr_send_rdy     & ~rst;
    state_send_sof      <= (state_send_sof     | set_send_sof    ) & ~clr_send_sof     & ~rst;
    state_send_data     <= (state_send_data    | set_send_data   ) & ~clr_send_data    & ~rst;
    state_send_rhold    <= (state_send_rhold   | set_send_rhold  ) & ~clr_send_rhold   & ~rst;
    state_send_shold    <= (state_send_shold   | set_send_shold  ) & ~clr_send_shold   & ~rst;
    state_send_crc      <= (state_send_crc     | set_send_crc    ) & ~clr_send_crc     & ~rst;
    state_send_eof      <= (state_send_eof     | set_send_eof    ) & ~clr_send_eof     & ~rst;
    state_wait          <= (state_wait         | set_wait        ) & ~clr_wait         & ~rst;
    state_rcvr_wait     <= (state_rcvr_wait    | set_rcvr_wait   ) & ~clr_rcvr_wait    & ~rst;
    state_rcvr_rdy      <= (state_rcvr_rdy     | set_rcvr_rdy    ) & ~clr_rcvr_rdy     & ~rst;
    state_rcvr_data     <= (state_rcvr_data    | set_rcvr_data   ) & ~clr_rcvr_data    & ~rst;
    state_rcvr_rhold    <= (state_rcvr_rhold   | set_rcvr_rhold  ) & ~clr_rcvr_rhold   & ~rst;
    state_rcvr_shold    <= (state_rcvr_shold   | set_rcvr_shold  ) & ~clr_rcvr_shold   & ~rst;
    state_rcvr_eof      <= (state_rcvr_eof     | set_rcvr_eof    ) & ~clr_rcvr_eof     & ~rst;
    state_rcvr_goodcrc  <= (state_rcvr_goodcrc | set_rcvr_goodcrc) & ~clr_rcvr_goodcrc & ~rst;
    state_rcvr_goodend  <= (state_rcvr_goodend | set_rcvr_goodend) & ~clr_rcvr_goodend & ~rst;
    state_rcvr_badend   <= (state_rcvr_badend  | set_rcvr_badend ) & ~clr_rcvr_badend  & ~rst;





















`ifdef CHECKERS_ENABLED
// States checker
reg  [STATES_COUNT - 1:0] sim_states_concat;
always @ (posedge clk)
    if (~rst)
    if ((state_idle
       + state_sync_esc
       + state_nocommerr
       + state_nocomm
       + state_align
       + state_reset
       + state_send_rdy
       + state_send_sof
       + state_send_data
       + state_send_rhold
       + state_send_shold
       + state_send_crc
       + state_send_eof
       + state_wait
       + state_rcvr_wait
       + state_rcvr_rdy
       + state_rcvr_data
       + state_rcvr_rhold
       + state_rcvr_shold
       + state_rcvr_eof
       + state_rcvr_goodcrc
       + state_rcvr_goodend
       + state_rcvr_badend
       ) != 1)
    begin
        sim_state_concat = {
                           state_idle
                         , state_sync_esc
                         , state_nocommerr
                         , state_nocomm
                         , state_align
                         , state_reset
                         , state_send_rdy
                         , state_send_sof
                         , state_send_data
                         , state_send_rhold
                         , state_send_shold
                         , state_send_crc
                         , state_send_eof
                         , state_wait
                         , state_rcvr_wait
                         , state_rcvr_rdy
                         , state_rcvr_data
                         , state_rcvr_rhold
                         , state_rcvr_shold
                         , state_rcvr_eof
                         , state_rcvr_goodcrc
                         , state_rcvr_goodend
                         , state_rcvr_badend
                         };
        $display("%m: invalid states: %b", sim_state_concat);
    end
`endif

endmodule