Skip to content

Commit 4cd95ad

Browse files
committed
Added universal block RAM fifo
1 parent 1efbd7c commit 4cd95ad

File tree

2 files changed

+500
-0
lines changed

2 files changed

+500
-0
lines changed

fifo_single_clock_ram.sv

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
//------------------------------------------------------------------------------
2+
// fifo_single_clock_ram.sv
3+
// Konstantin Pavlov, pavlovconst@gmail.com
4+
//------------------------------------------------------------------------------
5+
6+
// INFO ------------------------------------------------------------------------
7+
// Single-clock FIFO buffer implementation, also known as "queue"
8+
//
9+
// This fifo variant should synthesize into block RAM seamlessly, both for
10+
// Altera and for Xilinx chips. Simulation is also consistent.
11+
// Use this fifo when you need cross-vendor and sim/synth compatibility.
12+
//
13+
// Features:
14+
// - single clock operation
15+
// - configurable depth and data width
16+
// - only "normal" mode is supported here, no FWFT mode
17+
// - protected against overflow and underflow
18+
//
19+
20+
21+
/* --- INSTANTIATION TEMPLATE BEGIN ---
22+
23+
fifo_single_clock_ram #(
24+
.DEPTH( 8 ),
25+
.DATA_W( 32 )
26+
) FF1 (
27+
.clk( clk ),
28+
.nrst( 1'b1 ),
29+
30+
.w_req( ),
31+
.w_data( ),
32+
33+
.r_req( ),
34+
.r_data( ),
35+
36+
.cnt( ),
37+
.empty( ),
38+
.full( )
39+
);
40+
41+
--- INSTANTIATION TEMPLATE END ---*/
42+
43+
module fifo_single_clock_ram #( parameter
44+
45+
//FWFT_MODE = "TRUE", // "TRUE" - first word fall-trrough" mode
46+
// "FALSE" - normal fifo mode
47+
48+
DEPTH = 8, // max elements count == DEPTH, DEPTH MUST be power of 2
49+
DEPTH_W = $clog2(DEPTH)+1, // elements counter width, extra bit to store
50+
// "fifo full" state, see cnt[] variable comments
51+
52+
DATA_W = 32 // data field width
53+
)(
54+
55+
input clk,
56+
input nrst, // inverted reset
57+
58+
// input port
59+
input w_req,
60+
input [DATA_W-1:0] w_data,
61+
62+
// output port
63+
input r_req,
64+
output [DATA_W-1:0] r_data,
65+
66+
// helper ports
67+
output logic [DEPTH_W-1:0] cnt = '0,
68+
output logic empty,
69+
output logic full,
70+
71+
output logic fail
72+
);
73+
74+
75+
// read and write pointers
76+
logic [DEPTH_W-1:0] w_ptr = '0;
77+
logic [DEPTH_W-1:0] r_ptr = '0;
78+
79+
// filtered requests
80+
logic w_req_f;
81+
assign w_req_f = w_req && ~full;
82+
83+
logic r_req_f;
84+
assign r_req_f = r_req && ~empty;
85+
86+
87+
true_dual_port_write_first_2_clock_ram #(
88+
.RAM_WIDTH( DATA_W ),
89+
.RAM_DEPTH( DEPTH ),
90+
.INIT_FILE( "" )
91+
) data_ram (
92+
.clka( clk ),
93+
.addra( w_ptr[DEPTH_W-1:0] ),
94+
.ena( w_req_f ),
95+
.wea( 1'b1 ),
96+
.dina( w_data[DATA_W-1:0] ),
97+
.douta( ),
98+
99+
.clkb( clk ),
100+
.addrb( r_ptr[DEPTH_W-1:0] ),
101+
.enb( r_req_f ),
102+
.web( 1'b0 ),
103+
.dinb( '0 ),
104+
.doutb( r_data[DATA_W-1:0] )
105+
);
106+
107+
108+
function [DEPTH_W-1:0] inc_ptr (
109+
input [DEPTH_W-1:0] ptr
110+
);
111+
112+
if( ptr[DEPTH_W-1:0] == DEPTH-1 ) begin
113+
inc_ptr[DEPTH_W-1:0] = '0;
114+
end else begin
115+
inc_ptr[DEPTH_W-1:0] = ptr[DEPTH_W-1:0] + 1'b1;
116+
end
117+
endfunction
118+
119+
120+
always_ff @(posedge clk) begin
121+
if ( ~nrst ) begin
122+
w_ptr[DEPTH_W-1:0] <= '0;
123+
r_ptr[DEPTH_W-1:0] <= '0;
124+
125+
cnt[DEPTH_W-1:0] <= '0;
126+
end else begin
127+
128+
if( w_req_f ) begin
129+
w_ptr[DEPTH_W-1:0] <= inc_ptr(w_ptr[DEPTH_W-1:0]);
130+
end
131+
132+
if( r_req_f ) begin
133+
r_ptr[DEPTH_W-1:0] <= inc_ptr(r_ptr[DEPTH_W-1:0]);
134+
end
135+
136+
if( w_req_f && ~r_req_f ) begin
137+
cnt[DEPTH_W-1:0] <= cnt[DEPTH_W-1:0] + 1'b1;
138+
end else if( ~w_req_f && r_req_f ) begin
139+
cnt[DEPTH_W-1:0] <= cnt[DEPTH_W-1:0] - 1'b1;
140+
end
141+
142+
end
143+
end
144+
145+
always_comb begin
146+
empty = ( cnt[DEPTH_W-1:0] == '0 );
147+
full = ( cnt[DEPTH_W-1:0] == DEPTH );
148+
149+
fail = ( empty && r_req ) ||
150+
( full && w_req );
151+
end
152+
153+
endmodule
154+
155+
156+
157+
module true_dual_port_write_first_2_clock_ram #( parameter
158+
RAM_WIDTH = 16,
159+
RAM_DEPTH = 8,
160+
INIT_FILE = ""
161+
)(
162+
input clka,
163+
input [clogb2(RAM_DEPTH-1)-1:0] addra,
164+
input ena,
165+
input wea,
166+
input [RAM_WIDTH-1:0] dina,
167+
output [RAM_WIDTH-1:0] douta,
168+
169+
input clkb,
170+
input [clogb2(RAM_DEPTH-1)-1:0] addrb,
171+
input enb,
172+
input web,
173+
input [RAM_WIDTH-1:0] dinb,
174+
output [RAM_WIDTH-1:0] doutb
175+
);
176+
177+
reg [RAM_WIDTH-1:0] BRAM [RAM_DEPTH-1:0];
178+
reg [RAM_WIDTH-1:0] ram_data_a = {RAM_WIDTH{1'b0}};
179+
reg [RAM_WIDTH-1:0] ram_data_b = {RAM_WIDTH{1'b0}};
180+
181+
// either initializes the memory values to a specified file or to all zeros
182+
// to match hardware
183+
generate
184+
if (INIT_FILE != "") begin: use_init_file
185+
initial
186+
$readmemh(INIT_FILE, BRAM, 0, RAM_DEPTH-1);
187+
end else begin: init_bram_to_zero
188+
integer ram_index;
189+
initial
190+
for (ram_index = 0; ram_index < RAM_DEPTH; ram_index = ram_index + 1)
191+
BRAM[ram_index] = {RAM_WIDTH{1'b0}};
192+
end
193+
endgenerate
194+
195+
always @(posedge clka)
196+
if (ena)
197+
if (wea) begin
198+
BRAM[addra] <= dina;
199+
ram_data_a <= dina;
200+
end else
201+
ram_data_a <= BRAM[addra];
202+
203+
always @(posedge clkb)
204+
if (enb)
205+
if (web) begin
206+
BRAM[addrb] <= dinb;
207+
ram_data_b <= dinb;
208+
end else
209+
ram_data_b <= BRAM[addrb];
210+
211+
// no output register
212+
assign douta = ram_data_a;
213+
assign doutb = ram_data_b;
214+
215+
// calculates the address width based on specified RAM depth
216+
function integer clogb2;
217+
input integer depth;
218+
for (clogb2=0; depth>0; clogb2=clogb2+1)
219+
depth = depth >> 1;
220+
endfunction
221+
222+
endmodule
223+

0 commit comments

Comments
 (0)