// This task is inspired by Sharada Yeluri // https://www.linkedin.com/posts/sharada-yeluri_chatgpt-agi-openai-activity-7286404473030094850-HnzB // // "Build a buffer manager for a 16K entry-deep buffer that is 128 bits wide, // shared dynamically between 256 queues. // The module should sustain one enqueue and one dequeue every cycle // without stalls... Use SRAMs for linked list structures, and yes, // the SRAMs have two-cycle read latencies..." // // Basic considerations: // - we must provide read/writa data word every clock, and the SRAM block // should provide at least one pipelined read port and one pipelined // write port, the memory implementation is beyond of this task scope // - memory delay is 2 clocks, and the queue should provide the buffer // at least for 2 data entries // - the queues are organized in the linked list, as next pointer is // supposed to be in SRAM block with 2 clocks delay at least 2 lists are // needed // - as next pointer belongs to the prev/next data entry the next pointer // SRAM block should be dedicated and detached from data entry memory // - the free memory container also can be organized as queue, with double // buffer and linked list //______________________________________________________________________________ // module memory_block # ( parameter data_width = 128, parameter data_depth = 16 * 1024, parameter addr_width = $clog2 (data_depth), parameter tag_width = 8 ) ( input clock, input reset, input write_ena, input [addr_width - 1 : 0] write_addr, input [data_width - 1 : 0] write_data, input read_ena, output read_ack, input [addr_width - 1 : 0] read_addr, output [data_width - 1 : 0] read_data, input [tag_width - 1 : 0] tag_in, output [tag_width - 1 : 0] tag_out ); reg [data_width - 1 : 0] data [0 : data_depth - 1]; reg [tag_width - 1 : 0] tag_reg [2]; reg [addr_width - 1 : 0] read_addr_reg [2]; reg [data_width - 1 : 0] read_data_reg [2]; reg [addr_width - 1 : 0] write_addr_reg; reg [data_width - 1 : 0] write_data_reg; reg [1 : 0] read_ena_reg; reg write_ena_reg; always_ff @(posedge clock or posedge reset) begin if (reset) begin /* Very simple reset, the address/data content does not matter */ read_ena_reg <= 2'b00; wrire_ena_reg <= 1'b0; end else begin /* Commit written data to the memory after 2 clocks */ write_addr_reg <= write_addr; write_data_reg <= write_data; write_ena_reg <= write_ena; if (write_ena_reg) data[write_addr_reg] <= write_data_reg; /* Read data instantly and propagate to the output in 2 clocks */ read_data_reg[0] <= data[read_addr]; read_data_reg[1] <= read_data_reg[0]; read_addr_reg[0] <= read_addr; read_addr_reg[1] <= read_addr_reg[0]; read_ena_reg[0] <= read_ena; read_ena_reg[1] <= read_ena_reg[0]; tag_reg[0] <= tag_in; tag_reg[1] <= tag_reg[0]; end end assign read_ack = read_ena_reg[1]; assign tag_out = tag_reg[1]; /* Bypass for output data */ assign read_data = ((read_addr_reg[1] == write_addr) & write_ena) ? write_data : (read_addr_reg[1] == write_addr_reg & write_ena_reg) ? write_data_reg : read_data_reg[1]; /* assert (!write_ena | (read_addr_reg[1] != write_addr)y == (a & b)) else $error("Error: y is not equal to a & b"); */ endmodule module free_entry # ( parameter data_width = 16 ) ( input clock, input reset, output [data_width - 1 : 0] entry_out, input entry_out_valid, output entry_out_ready, input [data_width - 1 : 0] entry_next, input entry_next_ready, output entry_next_valid, input [data_width - 1 : 0] entry_read, input entry_read_ready, input entry_read_grant_in, output entry_read_grant_out, output read_req, input read_req_grant_in, output read_req_grant_out, input [data_width - 1 : 0] entry_free, input entry_free_ready, input entry_free_grant_in, output entry_free_grant_out ); reg [data_width - 1 : 0] entry; reg ready; reg inreq; wire valid; assign entry_out_ready = ready; assign valid = entry_out_valid; assign entry_out = entry; always_ff @(posedge clock or posedge reset) begin if (reset) begin ready <= 1'b0; inreq <= 1'b0; end else begin /* No fetch, entry is not ready, try to fill. */ case ({valid, ready}) 2'b00: begin assert(~entry_next_ready) else $error ("Unexpected next entry ready"); if (inreq) begin /* We eat the read entry only if we issued the request. */ if (entry_read_ready & entry_read_grant_in) begin entry <= entry_read; ready <= 1'b1; inreq <= 1'b0; end end else begin /* Check if we can fill from free entry, otherwise raise the request. */ if (entry_free_ready & entry_free_grant_in) begin entry <= entry_free; ready <= 1'b1; end else inreq <= read_req_grant_in; end end /* No fetch, entry is ready, nothing to do. */ 2'b01: begin assert(~inreq) else $error("Active request for the ready entry"); end /* Fetch from the not ready entry, try to bypass. */ 2'b10: begin assert(~entry_next_read) else $error ("Unexpected next entry ready"); if (inreq) begin if (entry_read_ready & entry_read_grant_in) begin if (entry_free_ready & entry_free_grant_in) begin entry <= entry_free; ready <= 1'b1; inreq <= 1'b0; end else inreq <= read_req_grant_in; end end else inreq <= read_req_grant_in; end /* Fetch from the ready entry, try to pipeline. */ 2'b11: begin assert(~inreq) else $error("Active request for the ready entry"); if (entry_free_ready & entry_free_grant_in) entry <= entry_free; else if (entry_next_ready) entry <= entry_next; else begin ready <= 1'b0; inreq <= read_req_grant_in; end end endcase end end always_comb begin /* No fetch, entry is not ready, try to fill. */ case ({valid, ready}) 2'b00: begin if (inreq) begin entry_out = entry_read; entry_out_ready = entry_read_ready & entry_read_grant_in; entry_read_grant_out = 1'b0; read_req = 1'b0; read_req_grant_out = read_req_grant_in; entry_free_grant_out = entry_free_grant_in & (entry_read_ready & entry_read_grant_in); entry_next_valid = 1'b0; end else begin entry_out = entry_free; entry_out_ready = entry_free_ready & entry_free_grant_in; entry_read_grant_out = entry_read_grant_in; read_req = read_req_grant_in & ~(entry_free_ready & entry_free_grant_in); read_req_grant_out = read_req_grant_in & (entry_free_ready & entry_free_grant_in); entry_free_grant_out = 1'b0; entry_next_valid = 1'b0; end end /* No fetch, entry is ready, nothing to do. */ 2'b01: begin entry_out = entry; entry_out_ready = 1'b1; entry_read_grant_out = entry_read_grant_in; read_req = 1'b0; read_req_grant_out = read_req_grant_in; entry_free_grant_out = entry_free_grant_in; entry_next_valid = 1'b0; end /* Fetch from the not ready entry, try to bypass. */ 2'b10: begin if (inreq) begin entry_out = entry_read; entry_out_ready = entry_read_ready & entry_read_grant_in; entry_read_grant_out = 1'b0; read_req = 1'b0; read_req_grant_out = read_req_grant_in; entry_free_grant_out = entry_free_grant_in & (entry_read_ready & entry_read_grant_in); entry_next_valid = 1'b0; end else begin entry_out = entry_free; entry_out_ready = entry_free_ready & entry_free_grant_in; entry_read_grant_out = entry_read_grant_in; read_req = read_req_grant_in & ~(entry_free_ready & entry_free_grant_in); read_req_grant_out = 1'b0; entry_free_grant_out = 1'b0; entry_next_valid = 1'b0; end if (inreq) begin if (entry_read_ready & entry_read_grant_in) begin if (entry_free_ready & entry_free_grant_in) begin entry <= entry_free; ready <= 1'b1; inreq <= 1'b0; end else inreq <= read_req_grant_in; end end else inreq <= read_req_grant_in; end /* Fetch from the ready entry, try to pipeline. */ 2'b11: begin assert(~inreq) else $error("Active request for the ready entry"); if (entry_free_ready & entry_free_grant_in) entry <= entry_free; else if (entry_next_ready) entry <= entry_next; else begin ready <= 1'b0; inreq <= read_req_grant_in; end end endcase end input [data_width - 1 : 0] entry_read, input entry_read_ready, input entry_read_grant_in, output entry_read_grant_out, output read_req, input read_req_grant_in, output read_req_grant_out, input [data_width - 1 : 0] entry_free, entry_out, entry_out_ready, entry_next_valid, entry_read_grant_out, read_req, read_req_grant_out, entry_free_grant_out output [data_width - 1 : 0] entry_out, output entry_out_ready, output entry_next_valid, output entry_read_grant_out, output read_req, output read_req_grant_out, output entry_free_grant_out endmodule module free_list # ( parameter data_width = 16, parameter data_depth = 16 * 1024, ) ( input clock, input reset, output list_ready, input alloc_valid, output alloc_ready, output [data_width - 1 : 0] alloc_entry, input free_valid, output [data_width - 1 : 0] free_entry, ); localparam addr_width = $clog2 (data_depth); localparam count_one = {(addr_width - 2)1'b0, 2'b01}; localparam count_two = {(addr_width - 2)1'b0, 2'b10}; entry data_in_next data_in_read data_in_free clock reset reg [addr_width : 0] tail; reg [addr_width : 0] head; reg in_reset; reg [data_width - 1 : 0] entry[0:1]; wire write_req; wire read_req; wire empty; wire full; /* if (entry_valid[0]) if (alloc_req) if entry_valid[1] entry_valid[0] <= 1; else if (read_done) entry_valid[0] <= 1; else if (free_req) entry_valid[0] <= 1; else entry[0] entry[1] read_done free entry[1] entry[1] read_done free entry_valid request_sent ---- ---F --A- --AF --- */ memory_block # # ( .data_width = 128, .data_depth = 16 * 1024, ) parameterized_module #( .WIDTH(16), .DELAY(3) ) instance2 ( .clk(clk), .rst(rst), .data_in(data_in_wide), .data_out(data_out_instance2) ); ( input clock, input reset, input write_ena, input [addr_width - 1 : 0] write_addr, input [data_width - 1 : 0] write_data, input read_ena, output read_ack, input [addr_width - 1 : 0] read_addr, output [data_width - 1 : 0] read_data, input [tag_width - 1 : 0] tag_in, output [tag_width - 1 : 0] tag_out ); always_ff @(posedge clock or posedge reset) begin if (reset) begin in_reset <= 1'b1; head <= count_two; tail <= count_two; end else begin if (in_reset) begin if (tail[addr_width]) in_reset <= 1'b0; else tail <= tail + count_one; end else begin end end end ak endmodule /* module sharada # ( parameter buffer_width = 128, buffer_depth = 16 * 1024, num_queues = 256, queue_id_width = $clog2 (num_queues) ) ( input clock, input reset, input enqueue_valid, output enqueue_ready, input [buffer_width - 1:0] enqueue_data, input [queue_id_width - 1:0] enqueue_queue_id, input dequeue_request_valid, output dequeue_request_ready, output dequeue_request_empty, input [queue_id_width - 1:0] dequeue_request_queue_id, output dequeue_data_valid, input dequeue_data_ready, output [buffer_width - 1:0] dequeue_data, output [queue_id_width - 1:0] dequeue_data_queue_id ); localparam buffer_ptr_width = $clog2(buffer_depth); reg [buffer_width - 1:0] data [0:buffer_depth - 1]; reg [buffer_ptr_width - 1:0] next [0:buffer_depth - 1]; reg [buffer_ptr_width - 1:0] queue_head [0:num_queues - 1]; reg [buffer_ptr_width - 1:0] queue_tail [0:num_queues - 1]; reg [buffer_ptr_width - 1:0] free_head; reg [buffer_ptr_width - 1:0] free_tail; assign enqueue_ready = free_head != free_tail; assign dequeue_ready = dequeue_valid & (queue_head[dequeue_data_queue_id] != queue_tail[dequeue_data_queue_id]); always @(posedge clock) begin if (enqueue_valid & enqueue_ready) begin data[free_head] <= enqueue_data; free_head <= next[free_head]; if (queue_tail[enqueue_queue_id] == queue_head[enqueue_queue_id] begin next[free_head] = free_head; queue_tail[enqueue_queue_id] <= free_head; queue_head[enqueue_queue_id] <= free_head; end else begin end next[queue_tail[enqueue_queue_id] <= free_head; next[queue_tail[enqueue_queue_id] <= free_head; next[free_head] queue_tail[enqueue_queue_id] <= free_head; end end if (dequeue_valid) begin end end endmodule module testbench; // Don't forget to write a self-checking testbench! endmodule