// Buffer used to add intermediate data buffering in channels // // Data within the read/write window is directly accessible via rd_addr/wr_addr. // The read_advance/write_advance signals update the read/write data pointers by adding RD_WINDOW/WR_WINDOW. // The status_counter indicate how many items are currently in the buffer (only registered after an advance signal is asserted). // The ready/valid signals are used to implement a handshake protocol. // // Usage: create and pass instance to additional arguments of $tvm_session. module tvm_buffer #( parameter DATA_WIDTH = 256, parameter DEPTH = 1024, parameter CNTR_WIDTH = 10, // log base 2 of BUFF_DEPTH parameter RD_WINDOW = 8, // set to 1 for FIFO behavior, or DEPTH for SRAM behavior parameter RD_ADVANCE = 2, // window advance (set to 1 for FIFO behavior) parameter RD_ADDR_WIDTH = 3, // log base 2 of RD_WINDOW parameter WR_WINDOW = 8, // set to 1 for FIFO behavior, or DEPTH for SRAM behavior parameter WR_ADVANCE = 2, // window advance (set to 1 for FIFO behavior) parameter WR_ADDR_WIDTH = 3 // log base 2 of WR_WINDOW ) ( input clk, input rst, // Read ports input read_advance, // Window advance (read pointer) input [RD_ADDR_WIDTH-1:0] read_addr, // Read address offset input read_ready, // Read ready (dequeue) output read_valid, // Read valid (not empty) output [DATA_WIDTH-1:0] read_data, // Read data port // Write ports input write_advance, // Window advance (write pointer) input [WR_ADDR_WIDTH-1:0] write_addr, // Write address offset output write_ready, // Write ready (not full) input write_valid, // Write valid (enqueue) input [DATA_WIDTH-1:0] write_data, // Write data port // Other outputs output [CNTR_WIDTH-1:0] status_counter // Number of elements currently in FIFO ); // Outputs that need to be latched reg read_data; reg status_counter; // Internal registers (read pointer, write pointer) reg[CNTR_WIDTH-1:0] read_ptr; reg[CNTR_WIDTH-1:0] write_ptr; // RAM instance reg [DATA_WIDTH-1:0] ram[DEPTH-1:0]; // Empty and full logic assign read_valid = (status_counter>=RD_WINDOW) ? 1'b1 : 1'b0; assign write_ready = (status_counter<(DEPTH-WR_WINDOW)) ? 1'b1 : 1'b0; // Counter logic (only affected by enq and deq) always @(posedge clk) begin // Case 1: system reset if (rst==1'b1) begin status_counter <= 0; // Case 2: simultaneous write advance and read advance and deq end else if ((write_advance && write_ready) && (read_advance && read_valid)) begin status_counter <= status_counter + (WR_ADVANCE - RD_ADVANCE); // Case 3: write advance end else if (write_advance && write_ready) begin status_counter <= status_counter + WR_ADVANCE; // Case 4: deq end else if (read_advance && read_valid) begin status_counter <= status_counter - RD_ADVANCE; // Default end else begin status_counter <= status_counter; end end // Output logic always @(posedge clk) begin if (rst==1'b1) begin read_data <= 0; end else begin if(read_ready) begin read_data <= ram[(read_ptr+read_addr)%DEPTH]; end else begin read_data <= read_data; end end end // RAM writing logic always @(posedge clk) begin if(write_valid) begin ram[((write_ptr+write_addr)%DEPTH)] <= write_data; end end // Read and write pointer logic always@(posedge clk) begin if (rst==1'b1) begin write_ptr <= 0; read_ptr <= 0; end else begin // Increment write pointer by WR_ADVANCE when asserting write_advance // When performing a write, no need to update the write pointer if (write_advance && write_ready) begin write_ptr <= (write_ptr + WR_ADVANCE) % DEPTH; end else begin write_ptr <= write_ptr; end // Increment read pointer by RD_ADVANCE when asserting read_advance // When performing a read, no need to update the read pointer if(read_advance && read_valid) begin read_ptr <= (read_ptr + RD_ADVANCE) % DEPTH; end else begin read_ptr <= read_ptr; end end end endmodule // tvm_buffer