aboutsummaryrefslogblamecommitdiff
path: root/src/rtl/fmc_arbiter.v
blob: 376e85b729a56ab751aba058968343b6fc1a7617 (plain) (tree)
1
2
3
4
5
6
7
8
9








                                                                        
                                                                  





























                                                                           




















































































                                                                                   
 
      
                   
      


                                                                                 
 


                                                                                    


                                                               
                                                               


                                                               
                                                               


                                                              
































                                                                                
 







                                                 
 










































                                                                                                                              



                        
                                      
                    
                                      
    
                                             
















                                                 








                                                                    

                         






                                                                         
 


                        





                                                                    


      
                          
      














                                                                  




                       






                                                          




                          
                            







                                                                        
//======================================================================
//
// fmc_arbiter.v
// -------------
// Port arbiter for the FMC interface for the Cryptech
// Novena FPGA + STM32 Bridge Board framework.
//
//
// Author: Pavel Shatov
// Copyright (c) 2015, 2018-2019 NORDUnet A/S All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// - Redistributions of source code must retain the above copyright
//   notice, this list of conditions and the following disclaimer.
//
// - Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in the
//   documentation and/or other materials provided with the distribution.
//
// - Neither the name of the NORDUnet nor the names of its contributors may
//   be used to endorse or promote products derived from this software
//   without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//======================================================================

module fmc_arbiter
(
    // fmc bus
    fmc_a, fmc_d,
    fmc_ne1, fmc_nl, fmc_nwe, fmc_noe, fmc_nwait,

    // system clock, i/o clock
    sys_clk,
    io_clk,

    // user bus
    sys_addr,
    sys_wr_en,
    sys_data_out,
    sys_rd_en,
    sys_data_in
);


    //
    // Parameters
    //
    parameter NUM_ADDR_BITS = 24;


    //
    // Ports
    //
    input        wire [NUM_ADDR_BITS-1:0] fmc_a;
    inout        wire [             31:0] fmc_d;
    input        wire                     fmc_ne1;
    input        wire                     fmc_nl;
    input        wire                     fmc_nwe;
    input        wire                     fmc_noe;
    output       wire                     fmc_nwait;

    input        wire                     sys_clk;
    input        wire                     io_clk;

    output       wire [NUM_ADDR_BITS-1:0] sys_addr;
    output       wire                     sys_wr_en;
    output       wire [             31:0] sys_data_out;
    output       wire                     sys_rd_en;
    input        wire [             31:0] sys_data_in;


    //
    // Data Bus PHY
    //

    /* PHY is needed to control bi-directional data bus. */

    wire [31: 0] fmc_d_ro; // value read from pins (receiver output)
    wire [31: 0] fmc_d_di; // value drives onto pins (driver input)

    fmc_d_phy #
    (
        .BUS_WIDTH(32)
    )
    d_phy
    (
        //.buf_io (fmc_d_swapped), // <-- connect directly to top-level bi-dir port
        
        // we just swap the order of wires in the bi-directional data bus
        .buf_io({fmc_d[7:0], fmc_d[15:8], fmc_d[23:16], fmc_d[31:24]}),

        .buf_di (fmc_d_di),      // driver input (from FPGA to STM32)
        .buf_ro (fmc_d_ro),      // receiver output (from STM32 to FPGA)
        .buf_t  (fmc_noe)        // <-- bus direction is controlled by STM32
    );


    //
    // CDC Helper Signals
    //
    reg cdc_slow_ff = 1'b0;
    reg cdc_fast_ff = 1'b0;

    always @(posedge io_clk)  cdc_slow_ff <= ~cdc_slow_ff;
    always @(posedge sys_clk) cdc_fast_ff <= cdc_slow_ff;

    reg cdc_same_edges;

    always @(posedge sys_clk)
        cdc_same_edges <= cdc_slow_ff ^ cdc_fast_ff;


    //
    // Synchronizer
    //
    (* SHREG_EXTRACT="NO" *) (* IOB="FALSE" *) reg [NUM_ADDR_BITS-1:0] fmc_a_ff1;
    (* SHREG_EXTRACT="NO" *)                   reg [NUM_ADDR_BITS-1:0] fmc_a_ff2;
    (* SHREG_EXTRACT="NO" *)                   reg [NUM_ADDR_BITS-1:0] fmc_a_ff3;

    (* SHREG_EXTRACT="NO" *) (* IOB="TRUE" *)  reg [           32-1:0] fmc_d_ro_ff1;
    (* SHREG_EXTRACT="NO" *)                   reg [           32-1:0] fmc_d_ro_ff2;
    (* SHREG_EXTRACT="NO" *)                   reg [           32-1:0] fmc_d_ro_ff3;

    (* SHREG_EXTRACT="NO" *) (* IOB="FALSE" *) reg fmc_ne1_ff1;
    (* SHREG_EXTRACT="NO" *)                   reg fmc_ne1_ff2;
    (* SHREG_EXTRACT="NO" *)                   reg fmc_ne1_ff3;

    (* SHREG_EXTRACT="NO" *) (* IOB="FALSE" *) reg fmc_nwe_ff1;
    (* SHREG_EXTRACT="NO" *)                   reg fmc_nwe_ff2;
    (* SHREG_EXTRACT="NO" *)                   reg fmc_nwe_ff3;

    (* SHREG_EXTRACT="NO" *) (* IOB="FALSE" *) reg fmc_nl_ff1;
    (* SHREG_EXTRACT="NO" *)                   reg fmc_nl_ff2;
    (* SHREG_EXTRACT="NO" *)                   reg fmc_nl_ff3;

    wire [NUM_ADDR_BITS-1:0] fmc_a_sync_sys    = fmc_a_ff3;
    wire [           32-1:0] fmc_d_ro_sync_sys = fmc_d_ro_ff3;
    wire                     fmc_ne1_sync_sys  = fmc_ne1_ff3;
    wire                     fmc_nwe_sync_sys  = fmc_nwe_ff3;
    wire                     fmc_nl_sync_sys   = fmc_nl_ff3;

    always @(posedge io_clk) begin
        //
        {fmc_a_ff2,    fmc_a_ff1}    <= {fmc_a_ff1,    fmc_a};
        {fmc_d_ro_ff2, fmc_d_ro_ff1} <= {fmc_d_ro_ff1, fmc_d_ro};		
        {fmc_ne1_ff2,  fmc_ne1_ff1}  <= {fmc_ne1_ff1,  fmc_ne1};
        {fmc_nwe_ff2,  fmc_nwe_ff1}  <= {fmc_nwe_ff1,  fmc_nwe};
        {fmc_nl_ff2,   fmc_nl_ff1}   <= {fmc_nl_ff1,   fmc_nl};
        //
    end

    always @(posedge sys_clk)
        //
        if (cdc_same_edges) begin
            fmc_a_ff3    <= fmc_a_ff2;
            fmc_d_ro_ff3 <= fmc_d_ro_ff2;
            fmc_ne1_ff3  <= fmc_ne1_ff2;
            fmc_nwe_ff3  <= fmc_nwe_ff2;
            fmc_nl_ff3   <= fmc_nl_ff2;
        end


    //
    // FSM
    //
    localparam FSM_STATE_IDLE            = 4'd00;

    localparam FSM_STATE_WRITE_START     = 4'd01;
    localparam FSM_STATE_WRITE_LATENCY_1 = 4'd02;
    localparam FSM_STATE_WRITE_LATENCY_2 = 4'd03;
    localparam FSM_STATE_WRITE_LATENCY_3 = 4'd04;
    localparam FSM_STATE_WRITE_LATENCY_4 = 4'd05;
    localparam FSM_STATE_WRITE_LATENCY_5 = 4'd06;
    localparam FSM_STATE_WRITE_LATENCY_6 = 4'd07;
    localparam FSM_STATE_WRITE_STOP      = 4'd08;

    localparam FSM_STATE_READ_START      = 4'd09;
    localparam FSM_STATE_READ_LATENCY_1  = 4'd10;
    localparam FSM_STATE_READ_LATENCY_2  = 4'd11;
    localparam FSM_STATE_READ_LATENCY_3  = 4'd12;
    localparam FSM_STATE_READ_STOP       = 4'd13;

    reg	[ 3: 0]	fsm_state = FSM_STATE_IDLE;
    reg	[ 3: 0]	fsm_state_next;

    always @(posedge sys_clk)
        //
        fsm_state <= fsm_state_next;


    //
    // FSM Transition Logic
    //
    always @*
        //
        if (!cdc_same_edges)                fsm_state_next = fsm_state;
        else begin
            //
            if (fmc_ne1_sync_sys)           fsm_state_next = FSM_STATE_IDLE;
            else case (fsm_state)
                FSM_STATE_IDLE:             fsm_state_next = !fmc_nwe_sync_sys ? FSM_STATE_WRITE_START : FSM_STATE_READ_START;
                FSM_STATE_WRITE_START:      fsm_state_next = FSM_STATE_WRITE_LATENCY_1;
                FSM_STATE_WRITE_LATENCY_1:  fsm_state_next = FSM_STATE_WRITE_LATENCY_2;
                FSM_STATE_WRITE_LATENCY_2:  fsm_state_next = FSM_STATE_WRITE_LATENCY_3;
                FSM_STATE_WRITE_LATENCY_3:  fsm_state_next = FSM_STATE_WRITE_LATENCY_4;
                FSM_STATE_WRITE_LATENCY_4:  fsm_state_next = FSM_STATE_WRITE_LATENCY_5;
                FSM_STATE_WRITE_LATENCY_5:  fsm_state_next = FSM_STATE_WRITE_LATENCY_6;
                FSM_STATE_WRITE_LATENCY_6,
                FSM_STATE_WRITE_STOP:       fsm_state_next = FSM_STATE_WRITE_STOP;
                FSM_STATE_READ_START:       fsm_state_next = FSM_STATE_READ_LATENCY_1;
                FSM_STATE_READ_LATENCY_1:   fsm_state_next = FSM_STATE_READ_LATENCY_2;
                FSM_STATE_READ_LATENCY_2:   fsm_state_next = FSM_STATE_READ_LATENCY_3;
                FSM_STATE_READ_LATENCY_3,
                FSM_STATE_READ_STOP:        fsm_state_next = FSM_STATE_READ_STOP;
                default:                    fsm_state_next = FSM_STATE_IDLE;
            endcase
            //
        end
        

    //
    // Output Data Latch
    //
    reg	[31:0] sys_data_in_latch_fast;
    (* IOB="TRUE" *)
    reg	[31:0] sys_data_in_latch_slow;
    
    assign fmc_d_di = sys_data_in_latch_slow;


    //
    // System Interface
    //
    reg [NUM_ADDR_BITS-1:0] sys_addr_reg;
    reg                     sys_wr_en_reg = 1'b0;
    reg [             31:0] sys_data_out_reg;
    reg                     sys_rd_en_reg = 1'b0;

    assign sys_addr     = sys_addr_reg;
    assign sys_wr_en    = sys_wr_en_reg;
    assign sys_data_out = sys_data_out_reg;
    assign sys_rd_en    = sys_rd_en_reg;


    //
    // Address Latch
    //
    always @(posedge sys_clk)
        //
        if (!fmc_ne1_sync_sys && !fmc_nl_sync_sys && cdc_same_edges)
            sys_addr_reg <= fmc_a_sync_sys;


    //
    // Write Enable Logic
    //
    always @(posedge sys_clk)
        //
        case (fsm_state)
            FSM_STATE_WRITE_LATENCY_6:  sys_wr_en_reg <= ~cdc_same_edges;
            default:                    sys_wr_en_reg <= 1'b0;
        endcase


    //
    // Read Enable Logic
    //
    always @(posedge sys_clk)
        //
        case (fsm_state_next)
            FSM_STATE_READ_START:   sys_rd_en_reg <= cdc_same_edges;
            default:                sys_rd_en_reg <= 1'b0;
        endcase


    //
    // Output Data Latches
    //
    always @(posedge sys_clk)
        //
        case (fsm_state)
            FSM_STATE_READ_LATENCY_2:
                if (cdc_same_edges)
                    sys_data_in_latch_fast <= sys_data_in; 
        endcase
        
    always @(negedge io_clk)
        //
        case (fsm_state)
            FSM_STATE_READ_LATENCY_3:
                sys_data_in_latch_slow <= sys_data_in_latch_fast; 
        endcase
        


    //
    // Input Data Latch
    //
    always @(posedge sys_clk)
        //
        case (fsm_state)
            FSM_STATE_WRITE_LATENCY_6:
                if (!cdc_same_edges)
                    sys_data_out_reg <= fmc_d_ro_sync_sys;
        endcase


    //
    // Unused NWAIT tieoff
    //
    assign fmc_nwait = 1'b0;


endmodule


//======================================================================
// EOF fmc_arbiter.v
//======================================================================