URL
https://opencores.org/ocsvn/fpga-cf/fpga-cf/trunk
Subversion Repositories fpga-cf
[/] [fpga-cf/] [trunk/] [hdl/] [boardsupport/] [v5/] [rx_elastic_buffer.v] - Rev 8
Go to most recent revision | Compare with Previous | Blame | View Log
//---------------------------------------------------------------------- // File : rx_elastic_buffer.v // Author : Xilinx Inc. //---------------------------------------------------------------------- // Copyright (c) 2008 by Xilinx, Inc. All rights reserved. // This text/file contains proprietary, confidential // information of Xilinx, Inc., is distributed under license // from Xilinx, Inc., and may be used, copied and/or // disclosed only pursuant to the terms of a valid license // agreement with Xilinx, Inc. Xilinx hereby grants you // a license to use this text/file solely for design, simulation, // implementation and creation of design files limited // to Xilinx devices or technologies. Use with non-Xilinx // devices or technologies is expressly prohibited and // immediately terminates your license unless covered by // a separate agreement. // // Xilinx is providing this design, code, or information // "as is" solely for use in developing programs and // solutions for Xilinx devices. By providing this design, // code, or information as one possible implementation of // this feature, application or standard, Xilinx is making no // representation that this implementation is free from any // claims of infringement. You are responsible for // obtaining any rights you may require for your implementation. // Xilinx expressly disclaims any warranty whatsoever with // respect to the adequacy of the implementation, including // but not limited to any warranties or representations that this // implementation is free from claims of infringement, implied // warranties of merchantability or fitness for a particular // purpose. // // Xilinx products are not intended for use in life support // appliances, devices, or systems. Use in such applications are // expressly prohibited. // // This copyright and support notice must be retained as part // of this text at all times. (c) Copyright 2008 Xilinx, Inc. // All rights reserved. //---------------------------------------------------------------------- // Description: This is the Receiver Elastic Buffer for the design // example of the Virtex-5 Ethernet MAC Wrappers. // // The FIFO is created from Block Memory, is of data width // 32 (2 characters wide plus status) and is of depth 64 // words. This is twice the size of the elastic buffer in // the RocketIO which has been bypassed, // // When the write clock is a few parts per million faster // than the read clock, the occupancy of the FIFO will // increase and Idles should be removed. // // When the read clock is a few parts per million faster // than the write clock, the occupancy of the FIFO will // decrease and Idles should be inserted. The logic in // this example design will always insert as many idles as // necessary in every Inter-frame Gap period to restore the // FIFO occupancy. // // Note: the Idle /I2/ sequence is used as the clock // correction character. This is made up from a /K28.5/ // followed by a /D16.2/ character. `timescale 1 ns/1 ps module rx_elastic_buffer ( // Signals received from the RocketIO on RXRECCLK. rxrecclk, reset, rxchariscomma_rec, rxcharisk_rec, rxdisperr_rec, rxnotintable_rec, rxrundisp_rec, rxdata_rec, // Signals reclocked onto RXUSRCLK2. rxusrclk2, rxreset, rxchariscomma_usr, rxcharisk_usr, rxdisperr_usr, rxnotintable_usr, rxrundisp_usr, rxclkcorcnt_usr, rxbuferr, rxdata_usr ); // port declarations input rxrecclk; input reset; input rxchariscomma_rec; input rxcharisk_rec; input rxdisperr_rec; input rxnotintable_rec; input rxrundisp_rec; input [7:0] rxdata_rec; input rxusrclk2; input rxreset; output rxchariscomma_usr; output rxcharisk_usr; output rxdisperr_usr; output rxnotintable_usr; output rxrundisp_usr; output [2:0] rxclkcorcnt_usr; output rxbuferr; output [7:0] rxdata_usr; reg rxchariscomma_usr; reg rxcharisk_usr; reg rxdisperr_usr; reg rxnotintable_usr; reg rxrundisp_usr; reg rxbuferr; reg [7:0] rxdata_usr; //-------------------------------------------------------------------- // Constants to set FIFO thresholds //-------------------------------------------------------------------- // FIFO occupancy over this level: clock correct to remove Idles wire [6:0] upper_threshold; assign upper_threshold = 7'b1000001; // FIFO occupancy less than this level: clock correct to insert Idles wire [6:0] lower_threshold; assign lower_threshold = 7'b0111111; // FIFO occupancy less than this, we consider it to be an underflow wire [6:0] underflow_threshold; assign underflow_threshold = 7'b0000011; // FIFO occupancy greater than this, we consider it to be an overflow wire [6:0] overflow_threshold; assign overflow_threshold = 7'b1111100; //-------------------------------------------------------------------- // Signal Declarations //-------------------------------------------------------------------- // Write domain logic (RXRECCLK) reg [15:0] wr_data; // Formatted the data word from RocketIO signals. reg [15:0] wr_data_reg; // wr_data registered and formatting completed. reg [15:0] wr_data_reg_reg; // wr_data_reg registered : to be written to the BRAM. reg [6:0] next_wr_addr; // Next FIFO write address (to reduce latency in gray code logic). reg [6:0] wr_addr; // FIFO write address. wire wr_enable; // write enable for FIFO. reg [6:0] wr_addr_gray; // wr_addr is converted to a gray code. reg [6:0] wr_rd_addr_gray; // read address pointer (gray coded) reclocked onto the write clock domain). reg [6:0] wr_rd_addr_gray_reg; // read address pointer (gray coded) registered on write clock for the 2nd time. wire [6:0] wr_rd_addr; // wr_rd_addr_gray converted back to binary (on the write clock domain). reg [6:0] wr_occupancy; // The occupancy of the FIFO in write clock domain. wire filling; // FIFO is filling up: Idles should be removed. // synthesis attribute ASYNC_REG of wr_rd_addr_gray is "TRUE"; wire k28p5_wr; // /K28.5/ character is detected on data prior to FIFO. wire d16p2_wr; // /D16.2/ character is detected on data prior to FIFO. reg [2:0] d16p2_wr_pipe; // k28p5_wr registered. reg [2:0] k28p5_wr_pipe; // d16p2_wr registered. reg remove_idle; // An Idle is removed before writing it into the FIFO. reg remove_idle_reg; // remove_idle registered. // Read domain logic (RXUSRCLK2) wire [15:0] rd_data; // Date read out of the block RAM. reg [15:0] rd_data_reg; // rd_data is registered for logic pipeline. reg [6:0] next_rd_addr; // Next FIFO read address (to reduce latency in gray code logic). reg [6:0] rd_addr; // FIFO read address. wire rd_enable; // read enable for FIFO. reg [6:0] rd_addr_gray; // rd_addr is converted to a gray code. reg [6:0] rd_wr_addr_gray; // write address pointer (gray coded) reclocked onto the read clock domain). reg [6:0] rd_wr_addr_gray_reg; // write address pointer (gray coded) registered on read clock for the 2nd time. wire [6:0] rd_wr_addr; // rd_wr_addr_gray converted back to binary (on the read clock domain). reg [6:0] rd_occupancy; // The occupancy of the FIFO in read clock domain. wire emptying; // FIFO is emptying: Idles should be inserted. wire overflow; // FIFO has filled up to overflow. wire underflow; // FIFO has emptied to underflow // synthesis attribute ASYNC_REG of rd_wr_addr_gray is "TRUE"; reg even; // To control reading of data from upper or lower half of FIFO word. wire k28p5_rd; // /K28.5/ character is detected on data post FIFO. wire d16p2_rd; // /D16.2/ character is detected on data post FIFO. reg insert_idle; // An Idle is inserted whilst reading it out of the FIFO. reg insert_idle_reg; // insert_idle is registered. reg rd_enable_reg; // Read enable is registered. reg [2:0] rxclkcorcnt; // derive RXCLKCORCNT to mimic RocketIO behaviour. //-------------------------------------------------------------------- // FIFO write logic (Idles are removed as necessary). //-------------------------------------------------------------------- // Reclock the RocketIO data and format for storing in the BRAM. always @(posedge rxrecclk) begin : gen_wr_data if (reset === 1'b1) begin wr_data <= 16'b0; wr_data_reg <= 16'b0; wr_data_reg_reg <= 16'b0; end else begin wr_data_reg_reg <= wr_data_reg; wr_data_reg[15:14] <= wr_data[15:14]; wr_data_reg[13] <= remove_idle; wr_data_reg[12:0] <= wr_data[12:0]; // format the lower word wr_data[15:13] <= 3'b0; // unused wr_data[12] <= rxchariscomma_rec; wr_data[11] <= rxcharisk_rec; wr_data[10] <= rxdisperr_rec; wr_data[9] <= rxnotintable_rec; wr_data[8] <= rxrundisp_rec; wr_data[7:0] <= rxdata_rec[7:0]; end end // gen_wr_data // Detect /K28.5/ character in upper half of the word from RocketIO assign k28p5_wr = (wr_data[7:0] == 8'b10111100 && wr_data[11] == 1'b1) ? 1'b1 : 1'b0; // Detect /D16.2/ character in upper half of the word from RocketIO assign d16p2_wr = (wr_data[7:0] == 8'b01010000 && wr_data[11] == 1'b0) ? 1'b1 : 1'b0; always @(posedge rxrecclk) begin : gen_k_d_pipe if (reset === 1'b1) begin k28p5_wr_pipe <= 3'b000; d16p2_wr_pipe <= 3'b000; end else begin k28p5_wr_pipe[2:1] <= k28p5_wr_pipe[1:0]; d16p2_wr_pipe[2:1] <= d16p2_wr_pipe[1:0]; k28p5_wr_pipe[0] <= k28p5_wr; d16p2_wr_pipe[0] <= d16p2_wr; end end // gen_k_d_pipe // Create the FIFO write enable: Idles are removed by deasserting the // FIFO write_enable whilst an Idle is present on the data. always @(posedge rxrecclk) begin : gen_wr_enable if (reset === 1'b1) begin remove_idle <= 1'b0; remove_idle_reg <= 1'b0; end else begin remove_idle_reg <= remove_idle; // Idle removal (always leave the first /I2/ Idle, then every // alternate Idle can be removed. if (d16p2_wr == 1'b1 && k28p5_wr_pipe[0] == 1'b1 && d16p2_wr_pipe[1] == 1'b1 && k28p5_wr_pipe[2] == 1'b1 && filling == 1'b1 && remove_idle == 1'b0) begin remove_idle <= 1'b1; end // Else write new word on every clock edge. else begin remove_idle <= 1'b0; end end end // gen_wr_enable assign wr_enable = ~(remove_idle | remove_idle_reg); // Create the FIFO write address pointer. always @(posedge rxrecclk) begin : gen_wr_addr if (reset === 1'b1) begin next_wr_addr <= 7'b1000001; wr_addr <= 7'b1000000; end else if (wr_enable == 1'b1) begin next_wr_addr <= next_wr_addr + 7'b1; wr_addr <= next_wr_addr; end end // gen_wr_addr // Convert write address pointer into a gray code always @(posedge rxrecclk) begin : wr_addrgray_bits if (reset === 1'b1) wr_addr_gray <= 7'b1100001; else begin wr_addr_gray[6] <= next_wr_addr[6]; wr_addr_gray[5] <= next_wr_addr[6] ^ next_wr_addr[5]; wr_addr_gray[4] <= next_wr_addr[5] ^ next_wr_addr[4]; wr_addr_gray[3] <= next_wr_addr[4] ^ next_wr_addr[3]; wr_addr_gray[2] <= next_wr_addr[3] ^ next_wr_addr[2]; wr_addr_gray[1] <= next_wr_addr[2] ^ next_wr_addr[1]; wr_addr_gray[0] <= next_wr_addr[1] ^ next_wr_addr[0]; end end // wr_addrgray_bits; //-------------------------------------------------------------------- // Instantiate a dual port Block RAM //-------------------------------------------------------------------- RAMB16_S18_S18 dual_port_block_ram0 ( .ADDRA ({3'b0, wr_addr}), .DIA (wr_data_reg_reg[15:0]), .DIPA (2'b00), .DOA (), .DOPA (), .WEA (wr_enable), .ENA (1'b1), .SSRA (1'b0), .CLKA (rxrecclk), .ADDRB ({3'b0, rd_addr}), .DIB (16'b0), .DIPB (2'b00), .DOB (rd_data[15:0]), .DOPB (), .WEB (1'b0), .ENB (1'b1), .SSRB (rxreset), .CLKB (rxusrclk2) ); //-------------------------------------------------------------------- // FIFO read logic (Idles are insterted as necessary). //-------------------------------------------------------------------- // Register the BRAM data. always @(posedge rxusrclk2) begin : reg_rd_data if (rxreset == 1'b1) rd_data_reg <= 16'b0; else if (rd_enable_reg == 1'b1) rd_data_reg <= rd_data; end // reg_rd_data //-------------------------------------------------------------------- // FIFO read logic (Idles are insterted as necessary). //-------------------------------------------------------------------- // Detect /K28.5/ character in upper half of the word read from FIFO assign k28p5_rd = (rd_data_reg[7:0] == 8'b10111100 && rd_data_reg[11] == 1'b1) ? 1'b1 : 1'b0; // Detect /D16.2/ character in lower half of the word read from FIFO assign d16p2_rd = (rd_data[7:0] == 8'b01010000 && rd_data[11] == 1'b0) ? 1'b1 : 1'b0; // Create the FIFO read enable: Idles are inserted by pausing the // FIFO read_enable whilst an Idle is present on the data. always @(posedge rxusrclk2) begin : gen_rd_enable if (rxreset == 1'b1) begin even <= 1'b1; insert_idle <= 1'b0; insert_idle_reg <= 1'b0; rd_enable_reg <= 1'b1; end else begin insert_idle_reg <= insert_idle; rd_enable_reg <= rd_enable; // Repeat as many /I2/ code groups as required if nearly // empty by pausing rd_enable. if ((k28p5_rd == 1'b1 && d16p2_rd == 1'b1) && emptying == 1'b1 && insert_idle == 1'b0) begin insert_idle <= 1'b1; even <= 1'b0; end // Else read out a new word on every alternative clock edge. else begin insert_idle <= 1'b0; even <= ~(even); end end end // gen_rd_enable assign rd_enable = ~(insert_idle | insert_idle_reg); // Create the FIFO read address pointer. always @(posedge rxusrclk2) begin : gen_rd_addr if (rxreset == 1'b1) begin next_rd_addr <= 7'b0000001; rd_addr <= 7'b0000000; end else if (rd_enable == 1'b1) begin next_rd_addr <= next_rd_addr + 7'b1; rd_addr <= next_rd_addr; end end // gen_rd_addr // Convert read address pointer into a gray code always @(posedge rxusrclk2) begin : rd_addrgray_bits if (rxreset == 1'b1) rd_addr_gray <= 7'b0; else if (rd_enable == 1'b1) begin rd_addr_gray[6] <= next_rd_addr[6]; rd_addr_gray[5] <= next_rd_addr[6] ^ next_rd_addr[5]; rd_addr_gray[4] <= next_rd_addr[5] ^ next_rd_addr[4]; rd_addr_gray[3] <= next_rd_addr[4] ^ next_rd_addr[3]; rd_addr_gray[2] <= next_rd_addr[3] ^ next_rd_addr[2]; rd_addr_gray[1] <= next_rd_addr[2] ^ next_rd_addr[1]; rd_addr_gray[0] <= next_rd_addr[1] ^ next_rd_addr[0]; end end // rd_addrgray_bits // Create the output data signals. always @(posedge rxusrclk2) begin : gen_mux if (rxreset == 1'b1) begin rxchariscomma_usr <= 1'b0; rxcharisk_usr <= 1'b0; rxdisperr_usr <= 1'b0; rxnotintable_usr <= 1'b0; rxrundisp_usr <= 1'b0; rxdata_usr <= 8'b0; end else begin if (rd_enable_reg == 1'b0 && even == 1'b0) begin rxchariscomma_usr <= 1'b0; rxcharisk_usr <= 1'b0; rxdisperr_usr <= 1'b0; rxnotintable_usr <= 1'b0; rxrundisp_usr <= rd_data_reg[8]; rxdata_usr <= 8'b01010000; end else if (rd_enable_reg == 1'b0 && even == 1'b1) begin rxchariscomma_usr <= 1'b1; rxcharisk_usr <= 1'b1; rxdisperr_usr <= 1'b0; rxnotintable_usr <= 1'b0; rxrundisp_usr <= rd_data[8]; rxdata_usr <= 8'b10111100; end else begin rxchariscomma_usr <= rd_data_reg[12]; rxcharisk_usr <= rd_data_reg[11]; rxdisperr_usr <= rd_data_reg[10]; rxnotintable_usr <= rd_data_reg[9]; rxrundisp_usr <= rd_data_reg[8]; rxdata_usr <= rd_data_reg[7:0]; end end end // gen_mux // Create RocketIO style clock correction status when inserting / // removing Idles. always @(posedge rxusrclk2) begin : gen_rxclkcorcnt if (rxreset == 1'b1) rxclkcorcnt <= 3'b0; else begin if (rd_data_reg[13] == 1'b1 && rxclkcorcnt[0] == 1'b0) rxclkcorcnt <= 3'b001; else if (insert_idle_reg == 1'b1 && rxclkcorcnt != 3'b111) rxclkcorcnt <= 3'b111; else rxclkcorcnt <= 3'b000; end end // gen_rxclkcorcnt assign rxclkcorcnt_usr = rxclkcorcnt; //-------------------------------------------------------------------- // Create emptying/full thresholds in read clock domain. //-------------------------------------------------------------------- // Reclock the write address pointer (gray code) onto the read domain. // By reclocking the gray code, the worst case senario is that // the reclocked value is only in error by -1, since only 1 bit at a // time changes between gray code increments. always @(posedge rxusrclk2) begin : reclock_wr_addrgray if (rxreset === 1'b1) begin rd_wr_addr_gray <= 7'b1100001; rd_wr_addr_gray_reg <= 7'b1100000; end else begin rd_wr_addr_gray <= wr_addr_gray; rd_wr_addr_gray_reg <= rd_wr_addr_gray; end end // reclock_wr_addrgray // Convert the resync'd Write Address Pointer grey code back to binary assign rd_wr_addr[6] = rd_wr_addr_gray_reg[6]; assign rd_wr_addr[5] = rd_wr_addr_gray_reg[6] ^ rd_wr_addr_gray_reg[5]; assign rd_wr_addr[4] = rd_wr_addr_gray_reg[6] ^ rd_wr_addr_gray_reg[5] ^ rd_wr_addr_gray_reg[4]; assign rd_wr_addr[3] = rd_wr_addr_gray_reg[6] ^ rd_wr_addr_gray_reg[5] ^ rd_wr_addr_gray_reg[4] ^ rd_wr_addr_gray_reg[3]; assign rd_wr_addr[2] = rd_wr_addr_gray_reg[6] ^ rd_wr_addr_gray_reg[5] ^ rd_wr_addr_gray_reg[4] ^ rd_wr_addr_gray_reg[3] ^ rd_wr_addr_gray_reg[2]; assign rd_wr_addr[1] = rd_wr_addr_gray_reg[6] ^ rd_wr_addr_gray_reg[5] ^ rd_wr_addr_gray_reg[4] ^ rd_wr_addr_gray_reg[3] ^ rd_wr_addr_gray_reg[2] ^ rd_wr_addr_gray_reg[1]; assign rd_wr_addr[0] = rd_wr_addr_gray_reg[6] ^ rd_wr_addr_gray_reg[5] ^ rd_wr_addr_gray_reg[4] ^ rd_wr_addr_gray_reg[3] ^ rd_wr_addr_gray_reg[2] ^ rd_wr_addr_gray_reg[1] ^ rd_wr_addr_gray_reg[0]; // Determine the occupancy of the FIFO as observed in the read domain. always @(posedge rxusrclk2) begin : gen_rd_occupancy if (rxreset === 1'b1) rd_occupancy <= 7'b1000000; else rd_occupancy <= rd_wr_addr - rd_addr; end // gen_rd_occupancy // Set emptying flag if FIFO occupancy is less than LOWER_THRESHOLD. assign emptying = (rd_occupancy < lower_threshold) ? 1'b1 : 1'b0; // Set underflow if FIFO occupancy is less than UNDERFLOW_THRESHOLD. assign underflow = (rd_occupancy < underflow_threshold) ? 1'b1 : 1'b0; // Set overflow if FIFO occupancy is less than OVERFLOW_THRESHOLD. assign overflow = (rd_occupancy > overflow_threshold) ? 1'b1 : 1'b0; // If either an underflow or overflow, assert the buffer error signal. // Like the RocketIO, this will persist until a reset is issued. always @(posedge rxusrclk2) begin : gen_buffer_error if (rxreset === 1'b1) rxbuferr <= 1'b0; else if (overflow == 1'b1 || underflow == 1'b1) rxbuferr <= 1'b1; end // gen_buffer_error //-------------------------------------------------------------------- // Create emptying/full thresholds in write clock domain. //-------------------------------------------------------------------- // Reclock the read address pointer (gray code) onto the write domain. // By reclocking the gray code, the worst case senario is that // the reclocked value is only in error by -1, since only 1 bit at a // time changes between gray code increments. always @(posedge rxrecclk) begin : reclock_rd_addrgray if (reset === 1'b1) begin wr_rd_addr_gray <= 7'b0; wr_rd_addr_gray_reg <= 7'b0; end else begin wr_rd_addr_gray <= rd_addr_gray; wr_rd_addr_gray_reg <= wr_rd_addr_gray; end end // reclock_rd_addrgray // Convert the resync'd Read Address Pointer grey code back to binary assign wr_rd_addr[6] = wr_rd_addr_gray_reg[6]; assign wr_rd_addr[5] = wr_rd_addr_gray_reg[6] ^ wr_rd_addr_gray_reg[5]; assign wr_rd_addr[4] = wr_rd_addr_gray_reg[6] ^ wr_rd_addr_gray_reg[5] ^ wr_rd_addr_gray_reg[4]; assign wr_rd_addr[3] = wr_rd_addr_gray_reg[6] ^ wr_rd_addr_gray_reg[5] ^ wr_rd_addr_gray_reg[4] ^ wr_rd_addr_gray_reg[3]; assign wr_rd_addr[2] = wr_rd_addr_gray_reg[6] ^ wr_rd_addr_gray_reg[5] ^ wr_rd_addr_gray_reg[4] ^ wr_rd_addr_gray_reg[3] ^ wr_rd_addr_gray_reg[2]; assign wr_rd_addr[1] = wr_rd_addr_gray_reg[6] ^ wr_rd_addr_gray_reg[5] ^ wr_rd_addr_gray_reg[4] ^ wr_rd_addr_gray_reg[3] ^ wr_rd_addr_gray_reg[2] ^ wr_rd_addr_gray_reg[1]; assign wr_rd_addr[0] = wr_rd_addr_gray_reg[6] ^ wr_rd_addr_gray_reg[5] ^ wr_rd_addr_gray_reg[4] ^ wr_rd_addr_gray_reg[3] ^ wr_rd_addr_gray_reg[2] ^ wr_rd_addr_gray_reg[1] ^ wr_rd_addr_gray_reg[0]; // Determine the occupancy of the FIFO as observed in the write domain. always @(posedge rxrecclk) begin : gen_wr_occupancy if (reset === 1'b1) wr_occupancy <= 7'b1000000; else wr_occupancy <= wr_addr[6:0] - wr_rd_addr[6:0]; end // gen_wr_occupancy // Set filling flag if FIFO occupancy is greated than UPPER_THRESHOLD. assign filling = (wr_occupancy > upper_threshold) ? 1'b1 : 1'b0; endmodule
Go to most recent revision | Compare with Previous | Blame | View Log