aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp2/fifo/fifo_2clock.v
blob: c6aaf34dc08f442e269fc6413409ccd2c24e7c06 (plain)
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//
// 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/>.
//


// FIXME ignores the AWIDTH (fifo size) parameter

module fifo_2clock
  #(parameter WIDTH=36, SIZE=6)
   (input wclk, input [WIDTH-1:0] datain, input src_rdy_i, output dst_rdy_o, output [15:0] space,
    input rclk, output [WIDTH-1:0] dataout, output src_rdy_o, input dst_rdy_i, output [15:0] occupied,
    input arst);
   
   wire [SIZE:0] level_rclk, level_wclk; // xilinx adds an extra bit if you ask for accurate levels
   wire 	 full, empty, write, read;

   assign dst_rdy_o  = ~full;
   assign src_rdy_o  = ~empty;
   assign write      = src_rdy_i & dst_rdy_o;
   assign read 	     = src_rdy_o & dst_rdy_i;
   wire 	 dummy;
   
   generate
      if(WIDTH==36)
	if(SIZE==9)
	  fifo_xlnx_512x36_2clk fifo_xlnx_512x36_2clk
	       (.rst(arst),
		.wr_clk(wclk),.din(datain),.full(full),.wr_en(write),.wr_data_count(level_wclk),
		.rd_clk(rclk),.dout(dataout),.empty(empty),.rd_en(read),.rd_data_count(level_rclk) );
	else if(SIZE==11)
	  fifo_xlnx_2Kx36_2clk fifo_xlnx_2Kx36_2clk 
		     (.rst(arst),
		      .wr_clk(wclk),.din(datain),.full(full),.wr_en(write),.wr_data_count(level_wclk),
		      .rd_clk(rclk),.dout(dataout),.empty(empty),.rd_en(read),.rd_data_count(level_rclk) );
	else if(SIZE==6)
	  fifo_xlnx_64x36_2clk fifo_xlnx_64x36_2clk 
		     (.rst(arst),
		      .wr_clk(wclk),.din(datain),.full(full),.wr_en(write),.wr_data_count(level_wclk),
		      .rd_clk(rclk),.dout(dataout),.empty(empty),.rd_en(read),.rd_data_count(level_rclk) );
	else
	  fifo_xlnx_512x36_2clk fifo_xlnx_512x36_2clk
	       (.rst(arst),
		.wr_clk(wclk),.din(datain),.full(full),.wr_en(write),.wr_data_count(level_wclk),
		.rd_clk(rclk),.dout(dataout),.empty(empty),.rd_en(read),.rd_data_count(level_rclk) );
      else if((WIDTH==19) & (SIZE==4))
	fifo_xlnx_16x19_2clk fifo_xlnx_16x19_2clk
	  (.rst(arst),
	   .wr_clk(wclk),.din(datain),.full(full),.wr_en(write),.wr_data_count(level_wclk),
	   .rd_clk(rclk),.dout(dataout),.empty(empty),.rd_en(read),.rd_data_count(level_rclk) );
      else if((WIDTH==18) & (SIZE==4))
	fifo_xlnx_16x19_2clk fifo_xlnx_16x19_2clk
	  (.rst(arst),
	   .wr_clk(wclk),.din({1'b0,datain}),.full(full),.wr_en(write),.wr_data_count(level_wclk),
	   .rd_clk(rclk),.dout({dummy,dataout}),.empty(empty),.rd_en(read),.rd_data_count(level_rclk) );
      else if ((WIDTH==18) & (SIZE==10))
	fifo_xlnx_1Kx18_2clk fifo_xlnx_1Kx18_2clk
	  (.rst(arst),
	   .wr_clk(wclk),.din(datain),.full(full),.wr_en(write),.wr_data_count(level_wclk),
	   .rd_clk(rclk),.dout(dataout),.empty(empty),.rd_en(read),.rd_data_count(level_rclk) );
   endgenerate
   
   assign occupied  = {{(16-SIZE-1){1'b0}},level_rclk};
   assign space     = ((1<<SIZE)+1)-level_wclk;
   
endmodule // fifo_2clock

/*
`else
   // ISE sucks, so the following doesn't work properly

   reg [AWIDTH-1:0] wr_addr, rd_addr;
   wire [AWIDTH-1:0] wr_addr_rclk, rd_addr_wclk;
   wire [AWIDTH-1:0] next_rd_addr;
   wire 	    enb_read;
   
   // Write side management
   wire [AWIDTH-1:0] next_wr_addr = wr_addr + 1;
   always @(posedge wclk or posedge arst)
     if(arst)
       wr_addr <= 0;
     else if(write)
       wr_addr <= next_wr_addr;
   assign 	    full = (next_wr_addr == rd_addr_wclk);

   //  RAM for data storage.  Data out is registered, complicating the
   //     read side logic
   ram_2port #(.DWIDTH(DWIDTH),.AWIDTH(AWIDTH)) mac_rx_ff_ram
     (.clka(wclk),.ena(1'b1),.wea(write),.addra(wr_addr),.dia(datain),.doa(),
      .clkb(rclk),.enb(enb_read),.web(1'b0),.addrb(next_rd_addr),.dib(0),.dob(dataout) );

   // Read side management
   reg 		    data_valid;
   assign 	    empty = ~data_valid;
   assign 	    next_rd_addr = rd_addr + data_valid;
   assign 	    enb_read = read | ~data_valid;

   always @(posedge rclk or posedge arst)
     if(arst)
       rd_addr <= 0;
     else if(read)
       rd_addr <= rd_addr + 1;

   always @(posedge rclk or posedge arst)
     if(arst)
       data_valid <= 0;
     else
       if(read & (next_rd_addr == wr_addr_rclk))
	 data_valid <= 0;
       else if(next_rd_addr != wr_addr_rclk)
	 data_valid <= 1;
	 
   // Send pointers across clock domains via gray code
   gray_send #(.WIDTH(AWIDTH)) send_wr_addr
     (.clk_in(wclk),.addr_in(wr_addr),
      .clk_out(rclk),.addr_out(wr_addr_rclk) );
   
   gray_send #(.WIDTH(AWIDTH)) send_rd_addr
     (.clk_in(rclk),.addr_in(rd_addr),
      .clk_out(wclk),.addr_out(rd_addr_wclk) );

   // Generate fullness info, these are approximate and may be delayed 
   // and are only for higher-level flow control.  
   // Only full and empty are guaranteed exact.
   always @(posedge wclk) 
     level_wclk <= wr_addr - rd_addr_wclk;
   always @(posedge rclk) 
     level_rclk <= wr_addr_rclk - rd_addr;
`endif
endmodule // fifo_2clock

*/