forked from SJTU-YONGFU-RESEARCH-GRP/core
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbidirectional_fifo.v
More file actions
executable file
·202 lines (182 loc) · 7.29 KB
/
bidirectional_fifo.v
File metadata and controls
executable file
·202 lines (182 loc) · 7.29 KB
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/**
* Bidirectional FIFO
*
* This module implements a bidirectional FIFO that allows data to flow in
* both directions simultaneously. It consists of two independent FIFOs:
* one for A→B direction and one for B→A direction.
*
* Key Features:
* - Bidirectional data flow: Data can flow A→B and B→A simultaneously
* - Independent FIFOs: Two separate FIFOs for each direction
* - Independent status: Each port has its own status flags
* - Per-direction counts: Separate counts for each direction
* - Full duplex: Can read and write on both ports simultaneously
*
* Operation:
* - A→B direction: Port A writes, Port B reads
* - B→A direction: Port B writes, Port A reads
* - Independent operation: Both directions operate independently
* - Separate memories: Each direction has its own memory array
*
* Status Flags:
* - Port A: Status based on B→A FIFO (A reads from B→A)
* - Port B: Status based on A→B FIFO (B reads from A→B)
* - Each port has: full, empty, almost_full, almost_empty
*
* Use Cases:
* - Full-duplex communication
* - Bidirectional data pipelines
* - Network interfaces
* - Communication channels
*
* @param DATA_WIDTH Width of data bus (default: 8 bits)
* @param ADDR_WIDTH Address width - FIFO depth = 2^ADDR_WIDTH (default: 4, depth=16)
* @param ALMOST_FULL_THRESHOLD Almost full threshold (default: 2)
* @param ALMOST_EMPTY_THRESHOLD Almost empty threshold (default: 2)
*
* @input clk Clock signal
* @input rst_n Active-low reset signal
*
* Port A Interface:
* @input a_wr_en Port A write enable (writes to A→B FIFO)
* @input a_wr_data[DATA_WIDTH-1:0] Port A write data
* @output a_full Port A full flag (based on B→A FIFO)
* @output a_almost_full Port A almost full flag
* @input a_rd_en Port A read enable (reads from B→A FIFO)
* @output a_rd_data[DATA_WIDTH-1:0] Port A read data
* @output a_empty Port A empty flag (based on B→A FIFO)
* @output a_almost_empty Port A almost empty flag
*
* Port B Interface:
* @input b_wr_en Port B write enable (writes to B→A FIFO)
* @input b_wr_data[DATA_WIDTH-1:0] Port B write data
* @output b_full Port B full flag (based on A→B FIFO)
* @output b_almost_full Port B almost full flag
* @input b_rd_en Port B read enable (reads from A→B FIFO)
* @output b_rd_data[DATA_WIDTH-1:0] Port B read data
* @output b_empty Port B empty flag (based on A→B FIFO)
* @output b_almost_empty Port B almost empty flag
*
* Status:
* @output a_to_b_count[ADDR_WIDTH:0] Data count from A to B
* @output b_to_a_count[ADDR_WIDTH:0] Data count from B to A
*/
module bidirectional_fifo #(
parameter DATA_WIDTH = 8,
parameter ADDR_WIDTH = 4, // FIFO depth = 2^ADDR_WIDTH
parameter ALMOST_FULL_THRESHOLD = 2,
parameter ALMOST_EMPTY_THRESHOLD = 2
) (
input wire clk,
input wire rst_n,
// Port A interface
input wire a_wr_en,
input wire [DATA_WIDTH-1:0] a_wr_data,
output wire a_full,
output wire a_almost_full,
input wire a_rd_en,
output wire [DATA_WIDTH-1:0] a_rd_data,
output wire a_empty,
output wire a_almost_empty,
// Port B interface
input wire b_wr_en,
input wire [DATA_WIDTH-1:0] b_wr_data,
output wire b_full,
output wire b_almost_full,
input wire b_rd_en,
output wire [DATA_WIDTH-1:0] b_rd_data,
output wire b_empty,
output wire b_almost_empty,
// Status
output wire [ADDR_WIDTH:0] a_to_b_count, // Data count from A to B
output wire [ADDR_WIDTH:0] b_to_a_count // Data count from B to A
);
// Memory arrays for bidirectional data flow
reg [DATA_WIDTH-1:0] a_to_b_mem [(1<<ADDR_WIDTH)-1:0]; // A to B direction
reg [DATA_WIDTH-1:0] b_to_a_mem [(1<<ADDR_WIDTH)-1:0]; // B to A direction
// Pointers for A to B direction
reg [ADDR_WIDTH:0] a_wr_ptr; // Write pointer for A
reg [ADDR_WIDTH:0] b_rd_ptr; // Read pointer for B
// Pointers for B to A direction
reg [ADDR_WIDTH:0] b_wr_ptr; // Write pointer for B
reg [ADDR_WIDTH:0] a_rd_ptr; // Read pointer for A
// Register outputs
reg [DATA_WIDTH-1:0] a_rd_data_reg;
reg [DATA_WIDTH-1:0] b_rd_data_reg;
// FIFO counts
assign a_to_b_count = a_wr_ptr - b_rd_ptr;
assign b_to_a_count = b_wr_ptr - a_rd_ptr;
// Status flags for A port
assign a_full = (b_to_a_count == (1<<ADDR_WIDTH));
assign a_empty = (b_to_a_count == 0);
assign a_almost_full = (b_to_a_count >= ((1<<ADDR_WIDTH) - ALMOST_FULL_THRESHOLD));
assign a_almost_empty = (b_to_a_count <= ALMOST_EMPTY_THRESHOLD) && !a_empty;
// Status flags for B port
assign b_full = (a_to_b_count == (1<<ADDR_WIDTH));
assign b_empty = (a_to_b_count == 0);
assign b_almost_full = (a_to_b_count >= ((1<<ADDR_WIDTH) - ALMOST_FULL_THRESHOLD));
assign b_almost_empty = (a_to_b_count <= ALMOST_EMPTY_THRESHOLD) && !b_empty;
// A port write pointer logic (for A to B direction)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
a_wr_ptr <= 0;
end else if (a_wr_en && !b_full) begin
a_wr_ptr <= a_wr_ptr + 1;
end
end
// B port read pointer logic (for A to B direction)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
b_rd_ptr <= 0;
end else if (b_rd_en && !b_empty) begin
b_rd_ptr <= b_rd_ptr + 1;
end
end
// B port write pointer logic (for B to A direction)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
b_wr_ptr <= 0;
end else if (b_wr_en && !a_full) begin
b_wr_ptr <= b_wr_ptr + 1;
end
end
// A port read pointer logic (for B to A direction)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
a_rd_ptr <= 0;
end else if (a_rd_en && !a_empty) begin
a_rd_ptr <= a_rd_ptr + 1;
end
end
// Memory write for A to B direction
always @(posedge clk) begin
if (a_wr_en && !b_full) begin
a_to_b_mem[a_wr_ptr[ADDR_WIDTH-1:0]] <= a_wr_data;
end
end
// Memory write for B to A direction
always @(posedge clk) begin
if (b_wr_en && !a_full) begin
b_to_a_mem[b_wr_ptr[ADDR_WIDTH-1:0]] <= b_wr_data;
end
end
// Memory read for A port (B to A direction)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
a_rd_data_reg <= {DATA_WIDTH{1'b0}};
end else if (a_rd_en && !a_empty) begin
a_rd_data_reg <= b_to_a_mem[a_rd_ptr[ADDR_WIDTH-1:0]];
end
end
// Memory read for B port (A to B direction)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
b_rd_data_reg <= {DATA_WIDTH{1'b0}};
end else if (b_rd_en && !b_empty) begin
b_rd_data_reg <= a_to_b_mem[b_rd_ptr[ADDR_WIDTH-1:0]];
end
end
// Output assignments
assign a_rd_data = a_rd_data_reg;
assign b_rd_data = b_rd_data_reg;
endmodule