aboutsummaryrefslogtreecommitdiffstats
path: root/hdl/fifo_buffer.v
blob: dd56537422ba20ba9740a07642bd1397a088133e (plain) (blame)
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
`timescale 1ns / 1ps

module fifo_buffer (
    input wire wr_clk,
    input wire [7:0] wr_data,
    input wire wr_valid,
    output wire wr_ready,
    
    input wire rd_clk,
    output wire [7:0] rd_data,
    output wire rd_valid,
    input wire rd_ready,
    
    input wire rst_n,
    
    output wire fifo_empty,
    output wire fifo_full,
    output wire [7:0] fifo_level
);

    parameter DEPTH = 512;
    parameter ADDR_WIDTH = 9;
    
    reg [7:0] mem [0:DEPTH-1];
    
    reg [ADDR_WIDTH:0] wr_ptr;
    reg [ADDR_WIDTH:0] wr_ptr_gray;
    reg [ADDR_WIDTH:0] wr_ptr_gray_sync1;
    reg [ADDR_WIDTH:0] wr_ptr_gray_sync2;
    
    reg [ADDR_WIDTH:0] rd_ptr;
    reg [ADDR_WIDTH:0] rd_ptr_gray;
    reg [ADDR_WIDTH:0] rd_ptr_gray_sync1;
    reg [ADDR_WIDTH:0] rd_ptr_gray_sync2;
    
    wire [ADDR_WIDTH:0] wr_ptr_bin;
    wire [ADDR_WIDTH:0] rd_ptr_bin;
    wire [ADDR_WIDTH:0] fifo_count;
    
    reg wr_en;
    reg rd_en;
    
    reg [7:0] rd_data_reg;
    reg rd_valid_reg;
    
    assign wr_ready = ~fifo_full;
    
    always @(posedge wr_clk or negedge rst_n) begin
        if (!rst_n) begin
            wr_ptr <= 0;
            wr_ptr_gray <= 0;
            wr_en <= 1'b0;
        end else begin
            wr_en <= wr_valid && wr_ready;
            
            if (wr_en) begin
                mem[wr_ptr[ADDR_WIDTH-1:0]] <= wr_data;
                wr_ptr <= wr_ptr + 1;
            end
            
            wr_ptr_gray <= binary_to_gray(wr_ptr);
        end
    end
    
    always @(posedge wr_clk or negedge rst_n) begin
        if (!rst_n) begin
            rd_ptr_gray_sync1 <= 0;
            rd_ptr_gray_sync2 <= 0;
        end else begin
            rd_ptr_gray_sync1 <= rd_ptr_gray;
            rd_ptr_gray_sync2 <= rd_ptr_gray_sync1;
        end
    end
    
    assign rd_data = rd_data_reg;
    assign rd_valid = rd_valid_reg;
    
    always @(posedge rd_clk or negedge rst_n) begin
        if (!rst_n) begin
            rd_ptr <= 0;
            rd_ptr_gray <= 0;
            rd_en <= 1'b0;
            rd_data_reg <= 8'h00;
            rd_valid_reg <= 1'b0;
        end else begin
            rd_en <= rd_ready && ~fifo_empty;
            
            if (rd_en) begin
                rd_data_reg <= mem[rd_ptr[ADDR_WIDTH-1:0]];
                rd_valid_reg <= 1'b1;
                rd_ptr <= rd_ptr + 1;
            end else if (rd_ready) begin
                rd_valid_reg <= 1'b0;
            end
            
            rd_ptr_gray <= binary_to_gray(rd_ptr);
        end
    end
    
    always @(posedge rd_clk or negedge rst_n) begin
        if (!rst_n) begin
            wr_ptr_gray_sync1 <= 0;
            wr_ptr_gray_sync2 <= 0;
        end else begin
            wr_ptr_gray_sync1 <= wr_ptr_gray;
            wr_ptr_gray_sync2 <= wr_ptr_gray_sync1;
        end
    end
    
    assign wr_ptr_bin = gray_to_binary(wr_ptr_gray_sync2);
    assign rd_ptr_bin = gray_to_binary(rd_ptr_gray_sync2);
    
    wire [ADDR_WIDTH:0] fifo_count_wr;
    assign fifo_count_wr = wr_ptr - gray_to_binary(rd_ptr_gray_sync2);
    
    wire [ADDR_WIDTH:0] fifo_count_rd;
    assign fifo_count_rd = gray_to_binary(wr_ptr_gray_sync2) - rd_ptr;
    
    assign fifo_full = (fifo_count_wr >= DEPTH);
    assign fifo_empty = (fifo_count_rd == 0);
    
    reg [7:0] fifo_level_reg;
    always @(*) begin
        if (fifo_count_wr > DEPTH)
            fifo_level_reg = DEPTH;
        else
            fifo_level_reg = fifo_count_wr[7:0];
    end
    
    assign fifo_level = fifo_level_reg;
    
    function [ADDR_WIDTH:0] binary_to_gray;
        input [ADDR_WIDTH:0] binary;
    begin
        binary_to_gray = binary ^ (binary >> 1);
    end
    endfunction
    
    function [ADDR_WIDTH:0] gray_to_binary;
        input [ADDR_WIDTH:0] gray;
        reg [ADDR_WIDTH:0] binary;
        integer i;
    begin
        binary[ADDR_WIDTH] = gray[ADDR_WIDTH];
        for (i = ADDR_WIDTH-1; i >= 0; i = i - 1)
            binary[i] = binary[i+1] ^ gray[i];
        gray_to_binary = binary;
    end
    endfunction
    
    integer i;
    initial begin
        for (i = 0; i < DEPTH; i = i + 1)
            mem[i] = 8'h00;
    end

endmodule