//======================================================================
//
// 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
//======================================================================