1
0
mirror of https://github.com/aolofsson/oh.git synced 2025-01-21 22:12:54 +08:00
oh/erx/hdl/erx_io.v
aolofsson 4f51cc342d Adding new verilog modules for receiver and transmitter
-moving away from Vivado block editor
-creating a "clean" split between RX and TX
2014-12-14 17:18:53 -05:00

347 lines
12 KiB
Verilog

/*
File: e_rx_io.v
This file is part of the Parallella Project .
Copyright (C) 2014 Adapteva, Inc.
Contributed by Fred Huettig <fred@adapteva.com>
This program 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.
This program 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 (see the file COPYING). If not, see
<http://www.gnu.org/licenses/>.
*/
module erx_io (/*AUTOARG*/
// Outputs
rx_wr_wait_p, rx_wr_wait_n, rx_rd_wait_p, rx_rd_wait_n, rxlclk_p,
rxframe_p, rxdata_p, ecfg_datain,
// Inputs
rx_lclk_p, rx_lclk_n, reset, ioreset, rx_frame_p, rx_frame_n,
rx_data_p, rx_data_n, rx_wr_wait, rx_rd_wait, ecfg_rx_enable,
ecfg_rx_gpio_mode, ecfg_dataout
);
parameter IOSTD_ELINK = "LVDS_25";
//###########
//# eLink pins
//###########
input rx_lclk_p, rx_lclk_n; // Differential clock from IOB
input reset;
input ioreset;
input rx_frame_p, rx_frame_n; // Inputs from eLink
input [7:0] rx_data_p, rx_data_n;
output rx_wr_wait_p, rx_wr_wait_n;
output rx_rd_wait_p, rx_rd_wait_n;
//#############
//# Fabric interface, 1/8 bit rate of eLink
//#############
output rxlclk_p; // Parallel clock output (slow)
output [7:0] rxframe_p;
output [63:0] rxdata_p;
input rx_wr_wait;
input rx_rd_wait;
//#############
//# Configuration bits
//#############
input ecfg_rx_enable; //enable signal for rx
input ecfg_rx_gpio_mode; //forces rx wait pins to constants
input [10:0] ecfg_dataout; // rd_wait, wr_wait for GPIO mode
output [8:0] ecfg_datain; //gpio data in (data in and frame)
//############
//# REGS
//############
reg [63:0] rxdata_p; // output registers
reg [7:0] rxframe_p;
//############
//# WIRES
//############
wire [7:0] rx_data; // High-speed serial data
wire rx_frame; // serial frame
wire serdes_reset;
//################################
//# Input Buffers Instantiation
//################################
IBUFDS
#(.DIFF_TERM ("TRUE"), // Differential termination
.IOSTANDARD (IOSTD_ELINK))
ibufds_rxdata[0:7]
(.I (rx_data_p),
.IB (rx_data_n),
.O (rx_data));
IBUFDS
#(.DIFF_TERM ("TRUE"), // Differential termination
.IOSTANDARD (IOSTD_ELINK))
ibufds_rxframe
(.I (rx_frame_p),
.IB (rx_frame_n),
.O (rx_frame));
//#####################
//# Clock Buffers
//#####################
wire rx_lclk; // Single-ended clock
wire rx_lclk_s; // Serial clock after BUFIO
IBUFGDS
#(.DIFF_TERM ("TRUE"), // Differential termination
.IOSTANDARD (IOSTD_ELINK))
ibufds_rxlclk
(.I (rx_lclk_p),
.IB (rx_lclk_n),
.O (rx_lclk));
BUFIO bufio_rxlclk
(.I (rx_lclk),
.O (rx_lclk_s));
// BUFR generates the slow clock
BUFR
#(.SIM_DEVICE("7SERIES"),
.BUFR_DIVIDE("4"))
clkout_bufr
(.O (rxlclk_p),
.CE(1'b1),
.CLR(1'b0),
.I (rx_lclk));
//#############################
//# Deserializer instantiations
//#############################
wire [63:0] rxdata_des;
wire [7:0] rxframe_des;
wire rx_lclk_sn = ~rx_lclk_s;
genvar i;
generate for(i=0; i<8; i=i+1)
begin : gen_serdes
ISERDESE2
#(
.DATA_RATE("DDR"), // DDR, SDR
.DATA_WIDTH(8), // Parallel data width (2-8,10,14)
.DYN_CLKDIV_INV_EN("FALSE"), // Enable DYNCLKDIVINVSEL inversion (FALSE, TRUE)
.DYN_CLK_INV_EN("FALSE"), // Enable DYNCLKINVSEL inversion (FALSE, TRUE)
// INIT_Q1 - INIT_Q4: Initial value on the Q outputs (0/1)
.INIT_Q1(1'b0),
.INIT_Q2(1'b0),
.INIT_Q3(1'b0),
.INIT_Q4(1'b0),
.INTERFACE_TYPE("NETWORKING"),
// MEMORY, MEMORY_DDR3, MEMORY_QDR, NETWORKING, OVERSAMPLE
.IOBDELAY("NONE"), // NONE, BOTH, IBUF, IFD
.NUM_CE(2), // Number of clock enables (1,2)
.OFB_USED("FALSE"), // Select OFB path (FALSE, TRUE)
.SERDES_MODE("MASTER"), // MASTER, SLAVE
// SRVAL_Q1 - SRVAL_Q4: Q output values when SR is used (0/1)
.SRVAL_Q1(1'b0),
.SRVAL_Q2(1'b0),
.SRVAL_Q3(1'b0),
.SRVAL_Q4(1'b0)
)
ISERDESE2_rxdata
(
.O(), // 1-bit output: Combinatorial output
// Q1 - Q8: 1-bit (each) output: Registered data outputs
.Q1(rxdata_des[i]), // Last data in?
.Q2(rxdata_des[i+8]),
.Q3(rxdata_des[i+16]),
.Q4(rxdata_des[i+24]),
.Q5(rxdata_des[i+32]),
.Q6(rxdata_des[i+40]),
.Q7(rxdata_des[i+48]),
.Q8(rxdata_des[i+56]), // First data in?
// SHIFTOUT1, SHIFTOUT2: 1-bit (each) output: Data width expansion output ports
.SHIFTOUT1(),
.SHIFTOUT2(),
.BITSLIP(1'b0), // 1-bit input: The BITSLIP pin performs a Bitslip operation
// synchronous to CLKDIV when asserted (active High). Subsequently, the data
// seen on the Q1 to Q8 output ports will shift, as in a barrel-shifter
// operation, one position every time Bitslip is invoked. DDR operation is
// different from SDR.
// CE1, CE2: 1-bit (each) input: Data register clock enable inputs
.CE1(1'b1),
.CE2(1'b1),
.CLKDIVP(1'b0), // 1-bit input: TBD
// Clocks: 1-bit (each) input: ISERDESE2 clock input ports
.CLK(rx_lclk_s), // 1-bit input: High-speed clock
.CLKB(rx_lclk_sn), // 1-bit input: High-speed secondary clock
.CLKDIV(rxlclk_p), // 1-bit input: Divided clock
.OCLK(1'b0), // 1-bit input: High speed output clock used when INTERFACE_TYPE="MEMORY"
// Dynamic Clock Inversions: 1-bit (each) input: Dynamic clock inversion pins to switch clock polarity
.DYNCLKDIVSEL(1'b0), // 1-bit input: Dynamic CLKDIV inversion
.DYNCLKSEL(1'b0), // 1-bit input: Dynamic CLK/CLKB inversion
// Input Data: 1-bit (each) input: ISERDESE2 data input ports
.D(rx_data[i]), // 1-bit input: Data input
.DDLY(1'b0), // 1-bit input: Serial data from IDELAYE2
.OFB(1'b0), // 1-bit input: Data feedback from OSERDESE2
.OCLKB(1'b0), // 1-bit input: High speed negative edge output clock
.RST(serdes_reset), // 1-bit input: Active high asynchronous reset
// SHIFTIN1, SHIFTIN2: 1-bit (each) input: Data width expansion input ports
.SHIFTIN1(1'b0),
.SHIFTIN2(1'b0)
);
end // block: gen_serdes
endgenerate
ISERDESE2
#(
.DATA_RATE("DDR"), // DDR, SDR
.DATA_WIDTH(8), // Parallel data width (2-8,10,14)
.DYN_CLKDIV_INV_EN("FALSE"), // Enable DYNCLKDIVINVSEL inversion (FALSE, TRUE)
.DYN_CLK_INV_EN("FALSE"), // Enable DYNCLKINVSEL inversion (FALSE, TRUE)
// INIT_Q1 - INIT_Q4: Initial value on the Q outputs (0/1)
.INIT_Q1(1'b0),
.INIT_Q2(1'b0),
.INIT_Q3(1'b0),
.INIT_Q4(1'b0),
.INTERFACE_TYPE("NETWORKING"),
// MEMORY, MEMORY_DDR3, MEMORY_QDR, NETWORKING, OVERSAMPLE
.IOBDELAY("NONE"), // NONE, BOTH, IBUF, IFD
.NUM_CE(2), // Number of clock enables (1,2)
.OFB_USED("FALSE"), // Select OFB path (FALSE, TRUE)
.SERDES_MODE("MASTER"), // MASTER, SLAVE
// SRVAL_Q1 - SRVAL_Q4: Q output values when SR is used (0/1)
.SRVAL_Q1(1'b0),
.SRVAL_Q2(1'b0),
.SRVAL_Q3(1'b0),
.SRVAL_Q4(1'b0)
)
ISERDESE2_rxframe
(
.O(), // 1-bit output: Combinatorial output
// Q1 - Q8: 1-bit (each) output: Registered data outputs
.Q1(rxframe_des[0]),
.Q2(rxframe_des[1]),
.Q3(rxframe_des[2]),
.Q4(rxframe_des[3]),
.Q5(rxframe_des[4]),
.Q6(rxframe_des[5]),
.Q7(rxframe_des[6]),
.Q8(rxframe_des[7]),
// SHIFTOUT1, SHIFTOUT2: 1-bit (each) output: Data width expansion output ports
.SHIFTOUT1(),
.SHIFTOUT2(),
.BITSLIP(1'b0), // 1-bit input: The BITSLIP pin performs a Bitslip operation
// synchronous to CLKDIV when asserted (active High). Subsequently, the data
// seen on the Q1 to Q8 output ports will shift, as in a barrel-shifter
// operation, one position every time Bitslip is invoked. DDR operation is
// different from SDR.
// CE1, CE2: 1-bit (each) input: Data register clock enable inputs
.CE1(1'b1),
.CE2(1'b1),
.CLKDIVP(1'b0), // 1-bit input: TBD
// Clocks: 1-bit (each) input: ISERDESE2 clock input ports
.CLK(rx_lclk_s), // 1-bit input: High-speed clock
.CLKB(rx_lclk_sn), // 1-bit input: High-speed secondary clock
.CLKDIV(rxlclk_p), // 1-bit input: Divided clock
.OCLK(1'b0), // 1-bit input: High speed output clock used when INTERFACE_TYPE="MEMORY"
// Dynamic Clock Inversions: 1-bit (each) input: Dynamic clock inversion pins to switch clock polarity
.DYNCLKDIVSEL(1'b0), // 1-bit input: Dynamic CLKDIV inversion
.DYNCLKSEL(1'b0), // 1-bit input: Dynamic CLK/CLKB inversion
// Input Data: 1-bit (each) input: ISERDESE2 data input ports
.D(rx_frame), // 1-bit input: Data input
.DDLY(1'b0), // 1-bit input: Serial data from IDELAYE2
.OFB(1'b0), // 1-bit input: Data feedback from OSERDESE2
.OCLKB(1'b0), // 1-bit input: High speed negative edge output clock
.RST(serdes_reset), // 1-bit input: Active high asynchronous reset
// SHIFTIN1, SHIFTIN2: 1-bit (each) input: Data width expansion input ports
.SHIFTIN1(1'b0),
.SHIFTIN2(1'b0)
);
// Sync control signals into our RX clock domain
reg [1:0] rxenb_sync;
wire rxenb = rxenb_sync[0];
assign serdes_reset = ~rxenb;
reg [1:0] rxgpio_sync;
wire rxgpio = rxgpio_sync[0];
// Register outputs once for good measure, then mux in loopback data if enabled
reg [63:0] rxdata_reg;
reg [7:0] rxframe_reg;
wire rxreset = reset | ~ecfg_rx_enable;
always @ (posedge rxlclk_p or posedge rxreset) begin
if(rxreset)
rxenb_sync <= 'd0;
else
rxenb_sync <= {1'b1, rxenb_sync[1]};
end
always @ (posedge rxlclk_p) begin
rxgpio_sync <= {ecfg_rx_gpio_mode, rxgpio_sync[1]};
rxdata_reg <= rxdata_des;
rxframe_reg <= rxframe_des & {8{rxenb}} & {8{~rxgpio}};
rxdata_p <= rxdata_reg;
rxframe_p <= rxframe_reg;
end
end // always @ (posedge rxlclk_p)
//#############
//# GPIO mode inputs
//#############
reg [8:0] datain_reg;
reg [8:0] ecfg_datain;
always @ (posedge rxlclk_p)
begin
datain_reg[8] <= rxframe_p[0];
datain_reg[7:0] <= rxdata_p[7:0];
ecfg_datain[8:0] <= datain_reg[8:0];
end
//#############
//# Wait signals (asynchronous)
//#############
wire wr_wait = rxgpio ? ecfg_dataout[9] : rx_wr_wait;
wire rd_wait = rxgpio ? ecfg_dataout[10] : rx_rd_wait;
OBUFDS
#(
.IOSTANDARD(IOSTD_ELINK),
.SLEW("SLOW")
) OBUFDS_RXWRWAIT
(
.O(rx_wr_wait_p),
.OB(rx_wr_wait_n),
.I(wr_wait)
);
OBUFDS
#(
.IOSTANDARD(IOSTD_ELINK),
.SLEW("SLOW")
) OBUFDS_RXRDWAIT
(
.O(rx_rd_wait_p),
.OB(rx_rd_wait_n),
.I(rd_wait)
);
endmodule // e_rx_io