119 строки
4.6 KiB
Verilog
119 строки
4.6 KiB
Verilog
// 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
|