| // Copyright 2024 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // A FIFO that can enqueue and dequeue multiple elements at a time. |
| module MultiFifo#(type T=logic [7:0], |
| parameter N = 4, |
| parameter MAX_CAPACITY=16, |
| parameter INTERFACE_BITS=$clog2(N+1), |
| parameter CAPACITYBITS=$clog2(MAX_CAPACITY+1)) |
| ( |
| input clk, |
| input rstn, |
| |
| // Command input. |
| input logic [INTERFACE_BITS-1:0] valid_in, |
| input T [N-1:0] data_in, |
| |
| // fill_level is used to determine if elements can be enqueued and dequeued. |
| // valid_in must be <= MAX_CAPACITY - fill_level |
| // ready_out must be <= fill_level |
| output logic [CAPACITYBITS-1:0] fill_level, |
| |
| // Command output. |
| output T [N-1:0] data_out, |
| input logic [INTERFACE_BITS-1:0] ready_out |
| ); |
| typedef logic [CAPACITYBITS-1:0] buffer_ptr_t; |
| typedef logic [CAPACITYBITS-1:0] buffer_size_t; |
| |
| // Module state |
| buffer_ptr_t head; // Elements to enqueue |
| buffer_ptr_t tail; // Elements to dequeue |
| buffer_size_t m_fill_level; |
| T [MAX_CAPACITY-1:0] buffer; |
| |
| function automatic buffer_ptr_t WrapAroundSum(buffer_ptr_t ptr, |
| buffer_size_t sz); |
| logic [CAPACITYBITS:0] sum; |
| sum = ptr + sz; |
| return (sum >= MAX_CAPACITY) ? (sum - MAX_CAPACITY) : sum; |
| endfunction |
| |
| // Output fill_level |
| always_comb begin |
| fill_level = m_fill_level; |
| end |
| |
| always_ff @(posedge clk or negedge rstn) begin |
| if (!rstn) begin |
| head <= 0; |
| tail <= 0; |
| m_fill_level <= 0; |
| end else begin |
| head <= WrapAroundSum(head, valid_in); |
| tail <= WrapAroundSum(tail, ready_out); |
| |
| // Update buffer |
| for (int i = 0; i < N; i++) begin |
| if (i < valid_in) begin |
| buffer[WrapAroundSum(head, i)] <= data_in[i]; |
| end |
| end |
| m_fill_level <= m_fill_level + valid_in - ready_out; |
| end |
| end |
| |
| always_comb begin |
| for (int i = 0; i < N; i++) begin |
| data_out[i] = buffer[WrapAroundSum(tail, i)]; |
| end |
| end |
| |
| // Assertions |
| `ifndef SYNTHESIS |
| always @(posedge clk) begin |
| // Producer should enqueue less than what's empty |
| assert (valid_in <= (MAX_CAPACITY - m_fill_level)) else |
| $error("Trying to enqueue ", valid_in, " elements ", |
| (MAX_CAPACITY - m_fill_level), " free"); |
| |
| // Consumer should dequeue less than what's full |
| assert (ready_out <= m_fill_level) else |
| $error("Trying to dequeue ", ready_out, " elements ", m_fill_level, |
| " free"); |
| end |
| `endif // not def SYNTHESIS |
| |
| endmodule |