1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
|
//
// Copyright 2011 Ettus Research LLC
//
// 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. If not, see <http://www.gnu.org/licenses/>.
//
// Source-synchronous receiver
// Assumes both clocks are at the same rate
// Relative clock phase is
// unknown
// variable
// bounded
// The output will come several cycles later than the input
// This should synthesize efficiently in Xilinx distributed ram cells,
// which is why we use a buffer depth of 16
// FIXME Async reset on rxclk side?
module ss_rcvr
#(parameter WIDTH=16)
(input rxclk,
input sysclk,
input rst,
input [WIDTH-1:0] data_in,
output [WIDTH-1:0] data_out,
output reg clock_present);
wire [3:0] rd_addr, wr_addr;
// Distributed RAM
reg [WIDTH-1:0] buffer [0:15];
always @(posedge rxclk)
buffer[wr_addr] <= data_in;
assign data_out = buffer[rd_addr];
// Write address generation
reg [3:0] wr_counter;
always @(posedge rxclk or posedge rst)
if (rst)
wr_counter <= 0;
else
wr_counter <= wr_counter + 1;
assign wr_addr = {wr_counter[3], ^wr_counter[3:2], ^wr_counter[2:1], ^wr_counter[1:0]};
// Read Address generation
wire [3:0] wr_ctr_sys, diff, abs_diff;
reg [3:0] wr_addr_sys_d1, wr_addr_sys_d2;
reg [3:0] rd_counter;
assign rd_addr = {rd_counter[3], ^rd_counter[3:2], ^rd_counter[2:1], ^rd_counter[1:0]};
always @(posedge sysclk)
wr_addr_sys_d1 <= wr_addr;
always @(posedge sysclk)
wr_addr_sys_d2 <= wr_addr_sys_d1;
assign wr_ctr_sys = {wr_addr_sys_d2[3],^wr_addr_sys_d2[3:2],^wr_addr_sys_d2[3:1],^wr_addr_sys_d2[3:0]};
assign diff = wr_ctr_sys - rd_counter;
assign abs_diff = diff[3] ? (~diff+1) : diff;
always @(posedge sysclk)
if(rst)
begin
clock_present <= 0;
rd_counter <= 0;
end
else
if(~clock_present)
if(abs_diff > 5)
clock_present <= 1;
else
;
else
if(abs_diff<3)
clock_present <= 0;
else
rd_counter <= rd_counter + 1;
endmodule // ss_rcvr
|