├── buffet.f ├── Makefile ├── README.md ├── dut ├── utils │ ├── reverse.v │ ├── priorityEncoder.v │ ├── dpram_bb.v │ ├── dpram.v │ └── leadingZero.v ├── fifo.v ├── buffet.v └── buffet_control.v ├── buffet_defines.v └── testbench ├── tb_leadingzero.v ├── tb_fifo.v ├── tb_buffet_credit.v ├── tb_buffet_readwrite.v ├── tb_buffet_update.v └── tb_buffet_stall.v /buffet.f: -------------------------------------------------------------------------------- 1 | ./dut/fifo.v 2 | ./dut/buffet.v 3 | ./dut/buffet_control.v 4 | ./dut/utils/dpram.v 5 | ./dut/utils/leadingZero.v 6 | ./dut/utils/priorityEncoder.v 7 | ./dut/utils/reverse.v 8 | ./buffet_defines.v 9 | ./testbench/tb_buffet_update.v 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL = /bin/bash 2 | 3 | all: 4 | vcs -full64 -debug_all -f buffet.f 5 | ./simv +verbose=1 > sim.rpt 6 | tail -n 100 sim.rpt 7 | 8 | all_gui: 9 | vcs -full64 -debug_all -f buffet.f 10 | ./simv -gui +verbose=1 11 | 12 | clean: 13 | rm -rf *.rpt *.key simv* csrc simv.daidir DVEfiles inter.vpd .restartSimSession* .synopsys_dve_re* *.lib .cds* 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # buffets 2 | 3 | This is a Verilog implementation of Buffets, a storage idiom for explicit decoupled data orchestration. Here is the [Paper](https://ysshao.github.io/assets/papers/Buffet_ASPLOS19_Final.pdf) from ASPLOS 2019. 4 | 5 | Usage: 6 | 7 | **Simulate using VCS :** 8 | - `make all` (You may use `make all_gui` for GUI) 9 | 10 | **To run different tests:** 11 | - Open `buffet.f` 12 | - Add the desired test from `./testbench/` 13 | - `make all` 14 | 15 | 16 | Note: This implementation focuses on depicting the interfaces and working principles rather than optimized implementation. We recommend replacing the register file with a SRAM from a RAM generator. 17 | 18 | If this was useful in your research, please cite: 19 | ``` 20 | @inproceedings{pellauer2019buffets, 21 | title={Buffets: An Efficient and Composable Storage Idiom for Explicit Decoupled Data Orchestration}, 22 | author={Pellauer, Michael and Shao, Yakun Sophia and Clemons, Jason and Crago, Neal and Hegde, Kartik and Venkatesan, Rangharajan and Keckler, Stephen W and Fletcher, Christopher W and Emer, Joel}, 23 | booktitle={Proceedings of the Twenty-Fourth International Conference on Architectural Support for Programming Languages and Operating Systems}, 24 | pages={137--151}, 25 | year={2019}, 26 | organization={ACM} 27 | } 28 | ``` 29 | -------------------------------------------------------------------------------- /dut/utils/reverse.v: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Kartik Hegde (kartikhegde.net) 3 | // 4 | // Copyright (c) 2019 Authors of "Buffets: An Efficient and Composable Storage Idiom for Explicit Decoupled Data 5 | // Orchestration". 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 8 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 13 | // Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 16 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | 21 | 22 | /* 23 | * Module to reverse a bus 24 | */ 25 | 26 | module reverse(bus_i, bus_o); 27 | 28 | parameter WIDTH = 32; 29 | 30 | input [WIDTH-1:0] bus_i; 31 | output [WIDTH-1:0] bus_o; 32 | 33 | genvar i; 34 | generate 35 | for(i=0;i 100) begin 98 | $display("TEST DRAIN FAILED %d", count); 99 | $finish; 100 | end 101 | end 102 | 103 | count = 0; 104 | while(data_i_ready == 1'b1) begin 105 | #10 106 | data_i = 12; 107 | data_i_valid = 1; 108 | data_o_ready = 0; 109 | if(count > 100) begin 110 | $display("TEST FILL FAILED %d", count); 111 | $finish; 112 | end 113 | count = count + 1; 114 | end 115 | data_i_valid = 0; 116 | $display("TEST FILL PASSED"); 117 | end 118 | endtask 119 | 120 | task TASK_draintest; 121 | begin 122 | // Assert ready high and wait till valid data comes 123 | data_o_ready = 1'b1; 124 | count = 0; 125 | while(data_o_valid == 1'b0) begin 126 | #10 127 | count = count + 1; 128 | if(count > 100) begin 129 | $display("TEST DRAIN FAILED %d", count); 130 | $finish; 131 | end 132 | end 133 | 134 | count = 0; 135 | while(data_o_valid == 1'b1) begin 136 | #10 137 | data_i_valid = 0; 138 | data_o_ready = 1; 139 | if(count > 100) begin 140 | $display("TEST DRAIN FAILED %d", count); 141 | $finish; 142 | end 143 | count = count + 1; 144 | end 145 | data_o_ready = 0; 146 | $display("TEST DRAIN PASSED"); 147 | end 148 | endtask 149 | 150 | // Main Initial Block 151 | 152 | initial begin 153 | 154 | TASK_reset; 155 | TASK_init; 156 | 157 | #10 158 | TASK_filltest; 159 | #10 160 | TASK_draintest; 161 | #10 162 | TASK_filltest; 163 | #10 164 | TASK_drain; 165 | TASK_drain; 166 | TASK_filltest; 167 | #10 168 | TASK_draintest; 169 | 170 | // Test till the FIFO fills and back pressure is applied 171 | $display("ALL TESTS PASSED OK"); 172 | $finish; 173 | end 174 | 175 | // Clock Generator 176 | always #5 clk = ~clk; 177 | 178 | endmodule 179 | -------------------------------------------------------------------------------- /testbench/tb_buffet_credit.v: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Kartik Hegde (kartikhegde.net) 3 | // 4 | // Copyright (c) 2019 Authors of "Buffets: An Efficient and Composable Storage Idiom for Explicit Decoupled Data 5 | // Orchestration". 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 8 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 13 | // Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 16 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | 21 | 22 | `include "buffet_defines.v" 23 | 24 | module tb_buffet_credit; 25 | 26 | parameter IDX_WIDTH = `IDX_WIDTH; // Index width 27 | parameter DATA_WIDTH = `DATA_WIDTH; // Data width 28 | 29 | reg clk, nreset_i; 30 | 31 | // Send credits to producer 32 | // Matches FIFO fills. 33 | wire [IDX_WIDTH-1:0] credit_out; 34 | wire credit_valid; 35 | reg credit_ready; 36 | 37 | // Operation: Fill(Data) -> void; 38 | // Matches FIFO fills. 39 | reg [DATA_WIDTH-1:0] push_data; 40 | reg push_data_valid; 41 | wire push_data_ready; 42 | // Asserted to 1 as producer will not send w/o credit. 43 | 44 | // Operation: Read(Index, bool) -> Data 45 | reg [IDX_WIDTH-1:0] read_idx; 46 | reg read_idx_valid; 47 | reg read_will_update; 48 | wire [DATA_WIDTH-1:0] read_data; 49 | wire read_data_valid; 50 | reg read_data_ready; 51 | wire read_idx_ready; 52 | 53 | // Operation: Shrink(Size) -> void 54 | // Shrinks share the same port as read in order to maintain ordering. 55 | // read_idx will be considered as shrink size. 56 | reg is_shrink; 57 | reg [IDX_WIDTH-1:0] update_idx; 58 | reg update_idx_valid; 59 | reg [DATA_WIDTH-1:0] update_data; 60 | reg update_data_valid; 61 | wire update_ready; 62 | wire update_receive_ack; 63 | 64 | reg [IDX_WIDTH-1:0] credit_received; 65 | integer count=0, i; 66 | 67 | buffet u_buffet( 68 | clk, 69 | nreset_i, 70 | // Read Port 71 | read_data, 72 | read_data_ready, 73 | read_data_valid, 74 | read_idx, 75 | read_idx_valid, 76 | read_idx_ready, 77 | read_will_update, 78 | // Write Port 79 | push_data, 80 | push_data_valid, 81 | push_data_ready, 82 | //Update Port 83 | update_data, 84 | update_data_valid, 85 | update_idx, 86 | update_idx_valid, 87 | update_ready, 88 | update_receive_ack, 89 | // Shrink Port 90 | is_shrink, 91 | // Credits 92 | credit_ready, 93 | credit_out, 94 | credit_valid 95 | 96 | ); 97 | 98 | 99 | // Reset 100 | task TASK_reset; 101 | begin 102 | clk = 0; 103 | nreset_i = 0; 104 | #2 105 | nreset_i = 1; 106 | end 107 | endtask 108 | 109 | // Initialize regs 110 | task TASK_init; 111 | begin 112 | read_idx_valid = 0; 113 | update_idx_valid = 0; 114 | update_data_valid = 0; 115 | push_data_valid = 0; 116 | credit_ready = 0; 117 | end 118 | endtask 119 | 120 | task TASK_fill; 121 | begin 122 | push_data = 1234; 123 | push_data_valid = 1; 124 | #10 125 | push_data_valid = 0; 126 | end 127 | endtask 128 | 129 | task TASK_shrink; 130 | input [`IDX_WIDTH-1:0] idx; 131 | begin 132 | read_idx = idx; 133 | read_idx_valid = 1; 134 | is_shrink = 1; 135 | read_will_update = 0; 136 | #10 137 | read_idx_valid = 0; 138 | is_shrink = 0; 139 | end 140 | endtask 141 | 142 | task TASK_getcredit; 143 | output [`IDX_WIDTH-1:0] credit; 144 | begin 145 | credit_ready = 1; 146 | //Wait till the credit high comes out 147 | while(credit_valid == 0) begin 148 | #10 149 | count = count + 1; 150 | if(count > 1000) begin 151 | $display("TIMED OUT WAITING FOR RESP"); 152 | $finish; 153 | end 154 | end 155 | 156 | credit = credit_out; 157 | end 158 | endtask 159 | 160 | task TASK_nop; 161 | input [7:0] iterations; 162 | integer cnt; 163 | begin 164 | cnt =0; 165 | while(cnt < iterations) begin 166 | #10 167 | cnt = cnt + 1; 168 | end 169 | end 170 | endtask 171 | 172 | 173 | initial begin 174 | 175 | TASK_reset; 176 | TASK_init; 177 | 178 | // FIRST TEST: Empty Buffet 179 | TASK_getcredit(credit_received); 180 | if(credit_received == `SIZE) 181 | $display("TEST1 PASSED"); 182 | else begin 183 | $display("TEST1 FAILED, Credit: %d", credit_received); 184 | $finish; 185 | end 186 | 187 | // Second test: Test after pushing some data (tests head) 188 | for(i=0; i<5;i=i+1) 189 | TASK_fill; 190 | TASK_nop(5); 191 | TASK_getcredit(credit_received); 192 | if(credit_received == `SIZE-5) 193 | $display("TEST2 PASSED"); 194 | else begin 195 | $display("TEST2 FAILED, Credit: %d", credit_received); 196 | $finish; 197 | end 198 | 199 | // Third test: Test after shrinking (tests tail) 200 | TASK_shrink(5); 201 | TASK_nop(5); 202 | TASK_getcredit(credit_received); 203 | if(credit_received == `SIZE) 204 | $display("TEST3 PASSED"); 205 | else begin 206 | $display("TEST3 FAILED, Credit: %d", credit_received); 207 | $finish; 208 | end 209 | 210 | $display("\n\n\t\t *** ALL CREDIT TESTS PASSED ***\n\n"); 211 | $finish; 212 | end 213 | 214 | 215 | // Clock Generator 216 | always #5 clk = ~clk; 217 | 218 | endmodule 219 | 220 | -------------------------------------------------------------------------------- /testbench/tb_buffet_readwrite.v: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Kartik Hegde (kartikhegde.net) 3 | // 4 | // Copyright (c) 2019 Authors of "Buffets: An Efficient and Composable Storage Idiom for Explicit Decoupled Data 5 | // Orchestration". 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 8 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 13 | // Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 16 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | 21 | 22 | `include "buffet_defines.v" 23 | 24 | module tb_buffet_readwrite; 25 | 26 | parameter IDX_WIDTH = `IDX_WIDTH; // Index width 27 | parameter DATA_WIDTH = `DATA_WIDTH; // Data width 28 | 29 | reg clk, nreset_i; 30 | 31 | // Send credits to producer 32 | // Matches FIFO fills. 33 | wire [IDX_WIDTH-1:0] credit_out; 34 | wire credit_valid; 35 | reg credit_ready; 36 | 37 | // Operation: Fill(Data) -> void; 38 | // Matches FIFO fills. 39 | reg [DATA_WIDTH-1:0] push_data; 40 | reg push_data_valid; 41 | wire push_data_ready; 42 | // Asserted to 1 as producer will not send w/o credit. 43 | 44 | // Operation: Read(Index, bool) -> Data 45 | reg [IDX_WIDTH-1:0] read_idx; 46 | reg read_idx_valid; 47 | reg read_will_update; 48 | wire [DATA_WIDTH-1:0] read_data; 49 | wire read_data_valid; 50 | reg read_data_ready; 51 | wire read_idx_ready; 52 | 53 | // Operation: Shrink(Size) -> void 54 | // Shrinks share the same port as read in order to maintain ordering. 55 | // read_idx will be considered as shrink size. 56 | reg is_shrink; 57 | reg [IDX_WIDTH-1:0] update_idx; 58 | reg update_idx_valid; 59 | reg [DATA_WIDTH-1:0] update_data; 60 | reg update_data_valid; 61 | wire update_ready; 62 | wire update_receive_ack; 63 | 64 | reg [DATA_WIDTH-1:0] data_received; 65 | 66 | reg [DATA_WIDTH-1:0] ref_data [4:0]; 67 | integer count=0, i; 68 | 69 | buffet u_buffet( 70 | clk, 71 | nreset_i, 72 | // Read Port 73 | read_data, 74 | read_data_ready, 75 | read_data_valid, 76 | read_idx, 77 | read_idx_valid, 78 | read_idx_ready, 79 | read_will_update, 80 | // Write Port 81 | push_data, 82 | push_data_valid, 83 | push_data_ready, 84 | //Update Port 85 | update_data, 86 | update_data_valid, 87 | update_idx, 88 | update_idx_valid, 89 | update_ready, 90 | update_receive_ack, 91 | // Shrink Port 92 | is_shrink, 93 | // Credits 94 | credit_ready, 95 | credit_out, 96 | credit_valid 97 | 98 | ); 99 | 100 | 101 | // Reset 102 | task TASK_reset; 103 | begin 104 | clk = 0; 105 | nreset_i = 0; 106 | #2 107 | nreset_i = 1; 108 | end 109 | endtask 110 | 111 | // Initialize regs 112 | task TASK_init; 113 | begin 114 | read_idx_valid = 0; 115 | update_idx_valid = 0; 116 | update_data_valid = 0; 117 | push_data_valid = 0; 118 | credit_ready = 0; 119 | end 120 | endtask 121 | 122 | task TASK_fill; 123 | input [DATA_WIDTH-1:0] data; 124 | begin 125 | push_data = data; 126 | push_data_valid = 1; 127 | #10 128 | push_data_valid = 0; 129 | end 130 | endtask 131 | 132 | task TASK_shrink; 133 | input [`IDX_WIDTH-1:0] idx; 134 | begin 135 | read_idx = idx; 136 | read_idx_valid = 1; 137 | is_shrink = 1; 138 | read_will_update = 0; 139 | #10 140 | read_idx_valid = 0; 141 | is_shrink = 0; 142 | end 143 | endtask 144 | 145 | task TASK_read; 146 | input [`IDX_WIDTH-1:0] idx; 147 | output [DATA_WIDTH-1:0] data; 148 | begin 149 | read_idx = idx; 150 | read_idx_valid = 1; 151 | is_shrink = 0; 152 | read_will_update = 0; 153 | read_data_ready = 1; 154 | #10 155 | read_idx_valid = 0; 156 | while(read_data_valid == 0) begin 157 | #10 158 | count = count + 1; 159 | if(count > 1000) begin 160 | $display("TIMED OUT WAITING FOR READ"); 161 | $finish; 162 | end 163 | end 164 | read_data_ready = 0; 165 | data = read_data; 166 | end 167 | endtask 168 | 169 | task TASK_getcredit; 170 | output [`IDX_WIDTH-1:0] credit; 171 | begin 172 | credit_ready = 1; 173 | //Wait till the credit high comes out 174 | while(credit_valid == 0) begin 175 | #10 176 | count = count + 1; 177 | if(count > 1000) begin 178 | $display("TIMED OUT WAITING FOR RESP"); 179 | $finish; 180 | end 181 | end 182 | 183 | credit = credit_out; 184 | end 185 | endtask 186 | 187 | task TASK_nop; 188 | input [7:0] iterations; 189 | integer cnt; 190 | begin 191 | cnt =0; 192 | while(cnt < iterations) begin 193 | #10 194 | cnt = cnt + 1; 195 | end 196 | end 197 | endtask 198 | 199 | 200 | initial begin 201 | 202 | TASK_reset; 203 | TASK_init; 204 | 205 | // fill some random data 206 | for(i=0; i<5;i=i+1) 207 | ref_data[i] = $random; 208 | // Write some data and read the exact same data 209 | for(i=0; i<5;i=i+1) 210 | TASK_fill(ref_data[i]); 211 | TASK_nop(5); 212 | for(i=0; i<5;i=i+1) begin 213 | TASK_read(i, data_received); 214 | if(data_received != ref_data[i]) begin 215 | $display("TEST FAILED, data: %d", data_received); 216 | $finish; 217 | end 218 | end 219 | 220 | $display("\n\n\t\t *** READ WRITE TEST PASSED ***\n\n"); 221 | $finish; 222 | end 223 | 224 | 225 | // Clock Generator 226 | always #5 clk = ~clk; 227 | 228 | endmodule 229 | 230 | -------------------------------------------------------------------------------- /dut/fifo.v: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Kartik Hegde (kartikhegde.net) 3 | // 4 | // Copyright (c) 2019 Authors of "Buffets: An Efficient and Composable Storage Idiom for Explicit Decoupled Data 5 | // Orchestration". 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 8 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 13 | // Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 16 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | 21 | 22 | module fifo( 23 | clk, 24 | nreset_i, 25 | data_i, 26 | data_i_valid, 27 | data_i_ready, 28 | data_o, 29 | data_o_valid, 30 | data_o_ready 31 | ); 32 | 33 | //------------------------------------------------------------------ 34 | // PARAMETERS 35 | //------------------------------------------------------------------ 36 | 37 | parameter DATA_WIDTH = 32; 38 | parameter FIFO_DEPTH = 8; //Assumed to be power of 2 39 | 40 | localparam IDX_WIDTH = $clog2(FIFO_DEPTH); 41 | localparam IDLE = 2'b00, READ_ONLY = 2'b01, WRITE_ONLY = 2'b10, READ_WRITE = 2'b11; 42 | localparam READY = 2'b00, ALMOST_FULL = 2'b01, FULL = 2'b10; 43 | 44 | //------------------------------------------------------------------ 45 | // INPUT/OUTPUT PORTS 46 | //------------------------------------------------------------------ 47 | 48 | input clk, nreset_i; 49 | 50 | // Data In 51 | input [DATA_WIDTH-1:0] data_i; 52 | input data_i_valid; 53 | output data_i_ready; 54 | //Data Out 55 | output [DATA_WIDTH-1:0] data_o; 56 | output data_o_valid; 57 | input data_o_ready; 58 | 59 | //------------------------------------------------------------------ 60 | // REGISTERS 61 | //------------------------------------------------------------------ 62 | 63 | reg [DATA_WIDTH-1:0] regfile [FIFO_DEPTH-1:0]; 64 | reg [IDX_WIDTH-1:0] head, tail; 65 | reg [1:0] state; 66 | 67 | reg [DATA_WIDTH-1:0] data_o_r; 68 | reg data_o_valid_r, data_i_ready_r; 69 | //------------------------------------------------------------------ 70 | // WIRES 71 | //------------------------------------------------------------------ 72 | 73 | // Head Tail chase has two cases: (1) one where head is greater than tail and (2) vice versa 74 | wire head_greater_than_tail = (head < tail)? 1'b0:1'b1; 75 | 76 | // Distance between the tail and the end of the FIFO 77 | wire [IDX_WIDTH-1:0] tail_offset = FIFO_DEPTH - tail; 78 | 79 | // Distance between head and tail (applicable in case 1) 80 | wire [IDX_WIDTH-1:0] head_tail_distance = head - tail; 81 | 82 | // In case 1, head_tail_distance directly gives occupancy, in case (2) offset needs to be added to head. 83 | wire [IDX_WIDTH-1:0] occupancy = (head_greater_than_tail)? head_tail_distance : 84 | (head + tail_offset); 85 | 86 | // Empty FIFO 87 | wire empty = ((occupancy == 1'b0)&&(state != FULL))? 1'b1:1'b0; 88 | 89 | // Four events: Only read, only write, both or none 90 | wire read_event = ~empty & data_o_ready; 91 | wire write_event = data_i_valid & (state != FULL); 92 | wire [1:0] event_cur = {write_event, read_event}; 93 | 94 | // Almost full refers to FIFO full if another data is written 95 | // Case 1, when head greater than tail (hgtt) - Distance is 2 96 | wire almost_full_hgtt = (head_tail_distance == FIFO_DEPTH-2)? 1'b1 : 1'b0; 97 | // Case 2, when ~hgtt. Head is 0 and tail offset is 1, or head is 1 and tail offset is 0 98 | wire almost_full_hgtt_n = ((head + tail_offset) == FIFO_DEPTH-2)? 1'b1: 1'b0; 99 | // Mux the above two to get the almost full 100 | wire almost_full = (head_greater_than_tail) ? almost_full_hgtt : almost_full_hgtt_n; 101 | 102 | // When will the FIFO be full: when it is almost full and there is only write (no read) 103 | wire fifo_will_be_full = almost_full & data_i_valid & ~data_o_ready; 104 | 105 | //------------------------------------------------------------------ 106 | // SEQUENTIAL LOGIC 107 | //------------------------------------------------------------------ 108 | 109 | // State Machine for FIFO status 110 | always @(posedge clk or negedge nreset_i) begin 111 | if(~nreset_i) begin 112 | state <= READY; 113 | end 114 | else begin 115 | case(state) 116 | 117 | READY: 118 | state <= (almost_full)? ALMOST_FULL : READY; 119 | ALMOST_FULL: 120 | state <= (event_cur == WRITE_ONLY)? FULL : 121 | ((event_cur == READ_ONLY)? READY : ALMOST_FULL); 122 | FULL: 123 | state <= (event_cur == READ_ONLY)? ALMOST_FULL : FULL; 124 | 125 | endcase 126 | end 127 | end 128 | 129 | // ready is pulled low if - (1) Almost full and valid input, or already full 130 | always @(posedge clk or negedge nreset_i) begin 131 | if(~nreset_i) begin 132 | data_i_ready_r <= 1'b1; 133 | end 134 | else begin 135 | if((state == ALMOST_FULL) && (event_cur == WRITE_ONLY)) 136 | data_i_ready_r <= 1'b0; 137 | else if((state == FULL) & (event_cur == READ_ONLY)) 138 | data_i_ready_r <= 1'b1; 139 | end 140 | end 141 | 142 | // If the data out ready is asserted high, we send the data 143 | always @(posedge clk) 144 | if(read_event) 145 | data_o_r <= regfile[tail]; 146 | 147 | // Data out valid is asserted high whenever we can read out 148 | always @(posedge clk or negedge nreset_i) begin 149 | if(~nreset_i) begin 150 | data_o_valid_r <= 1'b0; 151 | end 152 | else begin 153 | data_o_valid_r <= read_event; 154 | end 155 | end 156 | 157 | // Tail is updated as the data is read 158 | always @(posedge clk or negedge nreset_i) begin 159 | if(~nreset_i) begin 160 | tail <= {IDX_WIDTH{1'b0}}; 161 | end 162 | else begin 163 | // Wrapping counter 164 | if(read_event) 165 | tail <= tail + 1'b1; 166 | end 167 | end 168 | 169 | // Data is always accepted (assuming the source has checked ready before dispatch) 170 | // Head is updated on every write event 171 | always @(posedge clk or negedge nreset_i) begin 172 | if(~nreset_i) begin 173 | head <= {IDX_WIDTH{1'b0}}; 174 | end 175 | else begin 176 | // Wrapping counter 177 | if(write_event) 178 | head <= head + 1'b1; 179 | end 180 | end 181 | 182 | // Write the data 183 | always @(posedge clk) begin 184 | if(write_event) 185 | regfile[head] <= data_i; 186 | end 187 | 188 | 189 | //------------------------------------------------------------------ 190 | // Assign Outputs 191 | //------------------------------------------------------------------ 192 | 193 | assign data_o = data_o_r; 194 | assign data_o_valid = data_o_valid_r; 195 | assign data_i_ready = data_i_ready_r; 196 | 197 | 198 | endmodule 199 | -------------------------------------------------------------------------------- /testbench/tb_buffet_update.v: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Kartik Hegde (kartikhegde.net) 3 | // 4 | // Copyright (c) 2019 Authors of "Buffets: An Efficient and Composable Storage Idiom for Explicit Decoupled Data 5 | // Orchestration". 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 8 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 13 | // Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 16 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | 21 | 22 | `include "buffet_defines.v" 23 | 24 | module tb_buffet_update; 25 | 26 | parameter IDX_WIDTH = `IDX_WIDTH; // Index width 27 | parameter DATA_WIDTH = `DATA_WIDTH; // Data width 28 | 29 | reg clk, nreset_i; 30 | 31 | // Send credits to producer 32 | // Matches FIFO fills. 33 | wire [IDX_WIDTH-1:0] credit_out; 34 | wire credit_valid; 35 | reg credit_ready; 36 | 37 | // Operation: Fill(Data) -> void; 38 | // Matches FIFO fills. 39 | reg [DATA_WIDTH-1:0] push_data; 40 | reg push_data_valid; 41 | wire push_data_ready; 42 | // Asserted to 1 as producer will not send w/o credit. 43 | 44 | // Operation: Read(Index, bool) -> Data 45 | reg [IDX_WIDTH-1:0] read_idx; 46 | reg read_idx_valid; 47 | reg read_will_update; 48 | wire [DATA_WIDTH-1:0] read_data; 49 | wire read_data_valid; 50 | reg read_data_ready; 51 | wire read_idx_ready; 52 | 53 | // Operation: Shrink(Size) -> void 54 | // Shrinks share the same port as read in order to maintain ordering. 55 | // read_idx will be considered as shrink size. 56 | reg is_shrink; 57 | reg [IDX_WIDTH-1:0] update_idx; 58 | reg update_idx_valid; 59 | reg [DATA_WIDTH-1:0] update_data; 60 | reg update_data_valid; 61 | wire update_ready; 62 | wire update_receive_ack; 63 | 64 | reg [DATA_WIDTH-1:0] data_received; 65 | 66 | reg [DATA_WIDTH-1:0] ref_data [4:0]; 67 | integer count=0, i; 68 | 69 | buffet u_buffet( 70 | clk, 71 | nreset_i, 72 | // Read Port 73 | read_data, 74 | read_data_ready, 75 | read_data_valid, 76 | read_idx, 77 | read_idx_valid, 78 | read_idx_ready, 79 | read_will_update, 80 | // Write Port 81 | push_data, 82 | push_data_valid, 83 | push_data_ready, 84 | //Update Port 85 | update_data, 86 | update_data_valid, 87 | update_idx, 88 | update_idx_valid, 89 | update_ready, 90 | update_receive_ack, 91 | // Shrink Port 92 | is_shrink, 93 | // Credits 94 | credit_ready, 95 | credit_out, 96 | credit_valid 97 | 98 | ); 99 | 100 | 101 | // Reset 102 | task TASK_reset; 103 | begin 104 | clk = 0; 105 | nreset_i = 0; 106 | #2 107 | nreset_i = 1; 108 | end 109 | endtask 110 | 111 | // Initialize regs 112 | task TASK_init; 113 | begin 114 | read_idx_valid = 0; 115 | update_idx_valid = 0; 116 | update_data_valid = 0; 117 | push_data_valid = 0; 118 | credit_ready = 0; 119 | end 120 | endtask 121 | 122 | task TASK_fill; 123 | input [DATA_WIDTH-1:0] data; 124 | begin 125 | push_data = data; 126 | push_data_valid = 1; 127 | #10 128 | push_data_valid = 0; 129 | end 130 | endtask 131 | 132 | task TASK_shrink; 133 | input [`IDX_WIDTH-1:0] idx; 134 | begin 135 | read_idx = idx; 136 | read_idx_valid = 1; 137 | is_shrink = 1; 138 | read_will_update = 0; 139 | #10 140 | read_idx_valid = 0; 141 | is_shrink = 0; 142 | end 143 | endtask 144 | 145 | task TASK_read; 146 | input [`IDX_WIDTH-1:0] idx; 147 | output [DATA_WIDTH-1:0] data; 148 | begin 149 | read_idx = idx; 150 | read_idx_valid = 1; 151 | is_shrink = 0; 152 | read_will_update = 0; 153 | read_data_ready = 1; 154 | #10 155 | read_idx_valid = 0; 156 | count = 0; 157 | while(read_data_valid == 0) begin 158 | #10 159 | count = count + 1; 160 | if(count > 1000) begin 161 | $display("TIMED OUT WAITING FOR READ"); 162 | $finish; 163 | end 164 | end 165 | read_data_ready = 0; 166 | data = read_data; 167 | end 168 | endtask 169 | 170 | task TASK_readupdate; 171 | input [`IDX_WIDTH-1:0] idx; 172 | output [DATA_WIDTH-1:0] data; 173 | begin 174 | read_idx = idx; 175 | read_idx_valid = 1; 176 | is_shrink = 0; 177 | read_will_update = 1; 178 | read_data_ready = 1; 179 | #10 180 | read_idx_valid = 0; 181 | count = 0; 182 | while(read_data_valid == 0) begin 183 | #10 184 | count = count + 1; 185 | if(count > 1000) begin 186 | $display("TIMED OUT WAITING FOR READ"); 187 | $finish; 188 | end 189 | end 190 | read_data_ready = 0; 191 | read_will_update = 0; 192 | data = read_data; 193 | end 194 | endtask 195 | 196 | task TASK_readtimeout; 197 | input [`IDX_WIDTH-1:0] idx; 198 | output [DATA_WIDTH-1:0] data; 199 | begin 200 | read_idx = idx; 201 | read_idx_valid = 1; 202 | is_shrink = 0; 203 | read_will_update = 0; 204 | read_data_ready = 1; 205 | #10 206 | count = 0; 207 | read_idx_valid = 0; 208 | while(count <100) begin 209 | #10 210 | count = count + 1; 211 | if(read_data_valid == 1) begin 212 | $display("EXPECTED TIME OUT, TEST FAILED"); 213 | $finish; 214 | end 215 | end 216 | read_data_ready = 0; 217 | data = read_data; 218 | end 219 | endtask 220 | task TASK_update; 221 | input [`IDX_WIDTH-1:0] idx; 222 | input [DATA_WIDTH-1:0] data; 223 | begin 224 | update_idx = idx; 225 | update_data = data; 226 | update_idx_valid = 1; 227 | update_data_valid = 1; 228 | is_shrink = 0; 229 | read_will_update = 0; 230 | #10 231 | update_idx_valid = 0; 232 | update_data_valid = 0; 233 | count = 0; 234 | while(update_receive_ack == 0) begin 235 | #10 236 | count = count + 1; 237 | if(count > 1000) begin 238 | $display("TIMED OUT WAITING FOR update ack"); 239 | $finish; 240 | end 241 | end 242 | end 243 | endtask 244 | 245 | task TASK_getcredit; 246 | output [`IDX_WIDTH-1:0] credit; 247 | begin 248 | credit_ready = 1; 249 | //Wait till the credit high comes out 250 | while(credit_valid == 0) begin 251 | #10 252 | count = count + 1; 253 | if(count > 1000) begin 254 | $display("TIMED OUT WAITING FOR RESP"); 255 | $finish; 256 | end 257 | end 258 | 259 | credit = credit_out; 260 | end 261 | endtask 262 | 263 | task TASK_nop; 264 | input [7:0] iterations; 265 | integer cnt; 266 | begin 267 | cnt =0; 268 | while(cnt < iterations) begin 269 | #10 270 | cnt = cnt + 1; 271 | end 272 | end 273 | endtask 274 | 275 | 276 | initial begin 277 | 278 | TASK_reset; 279 | TASK_init; 280 | 281 | // fill some random data 282 | for(i=0; i<5;i=i+1) 283 | ref_data[i] = $random; 284 | 285 | // Write some data 286 | for(i=0; i<5;i=i+1) 287 | TASK_fill(ref_data[i]); 288 | TASK_nop(5); 289 | 290 | // Read that data with marking them for future update 291 | for(i=0; i<5;i=i+1) begin 292 | TASK_readupdate(i, data_received); 293 | if(data_received != ref_data[i]) begin 294 | $display("TEST FAILED, data: %d", data_received); 295 | $finish; 296 | end 297 | end 298 | 299 | // Try reading the same data without updating it, it should time-out. 300 | TASK_readtimeout(1, data_received); 301 | 302 | // Update the data 303 | TASK_update(1, $random); 304 | 305 | // Now the read should go through 306 | TASK_read(1, data_received); 307 | 308 | $display("\n\n\t\t *** UPDATE TEST PASSED ***\n\n"); 309 | $finish; 310 | end 311 | 312 | 313 | // Clock Generator 314 | always #5 clk = ~clk; 315 | 316 | endmodule 317 | 318 | -------------------------------------------------------------------------------- /testbench/tb_buffet_stall.v: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Kartik Hegde (kartikhegde.net) 3 | // 4 | // Copyright (c) 2019 Authors of "Buffets: An Efficient and Composable Storage Idiom for Explicit Decoupled Data 5 | // Orchestration". 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 8 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 13 | // Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 16 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | 21 | 22 | `include "buffet_defines.v" 23 | 24 | module tb_buffet_stall; 25 | 26 | parameter IDX_WIDTH = `IDX_WIDTH; // Index width 27 | parameter DATA_WIDTH = `DATA_WIDTH; // Data width 28 | 29 | reg clk, nreset_i; 30 | 31 | // Send credits to producer 32 | // Matches FIFO fills. 33 | wire [IDX_WIDTH-1:0] credit_out; 34 | wire credit_valid; 35 | reg credit_ready; 36 | 37 | // Operation: Fill(Data) -> void; 38 | // Matches FIFO fills. 39 | reg [DATA_WIDTH-1:0] push_data; 40 | reg push_data_valid; 41 | wire push_data_ready; 42 | // Asserted to 1 as producer will not send w/o credit. 43 | 44 | // Operation: Read(Index, bool) -> Data 45 | reg [IDX_WIDTH-1:0] read_idx; 46 | reg read_idx_valid; 47 | reg read_will_update; 48 | wire [DATA_WIDTH-1:0] read_data; 49 | wire read_data_valid; 50 | reg read_data_ready; 51 | wire read_idx_ready; 52 | 53 | // Operation: Shrink(Size) -> void 54 | // Shrinks share the same port as read in order to maintain ordering. 55 | // read_idx will be considered as shrink size. 56 | reg is_shrink; 57 | reg [IDX_WIDTH-1:0] update_idx; 58 | reg update_idx_valid; 59 | reg [DATA_WIDTH-1:0] update_data; 60 | reg update_data_valid; 61 | wire update_ready; 62 | wire update_receive_ack; 63 | 64 | reg [DATA_WIDTH-1:0] data_received; 65 | 66 | reg [DATA_WIDTH-1:0] ref_data [4:0]; 67 | integer count=0, i; 68 | 69 | buffet u_buffet( 70 | clk, 71 | nreset_i, 72 | // Read Port 73 | read_data, 74 | read_data_ready, 75 | read_data_valid, 76 | read_idx, 77 | read_idx_valid, 78 | read_idx_ready, 79 | read_will_update, 80 | // Write Port 81 | push_data, 82 | push_data_valid, 83 | push_data_ready, 84 | //Update Port 85 | update_data, 86 | update_data_valid, 87 | update_idx, 88 | update_idx_valid, 89 | update_ready, 90 | update_receive_ack, 91 | // Shrink Port 92 | is_shrink, 93 | // Credits 94 | credit_ready, 95 | credit_out, 96 | credit_valid 97 | 98 | ); 99 | 100 | 101 | // Reset 102 | task TASK_reset; 103 | begin 104 | clk = 0; 105 | nreset_i = 0; 106 | #2 107 | nreset_i = 1; 108 | end 109 | endtask 110 | 111 | // Initialize regs 112 | task TASK_init; 113 | begin 114 | read_idx_valid = 0; 115 | update_idx_valid = 0; 116 | update_data_valid = 0; 117 | push_data_valid = 0; 118 | credit_ready = 0; 119 | end 120 | endtask 121 | 122 | task TASK_fill; 123 | input [DATA_WIDTH-1:0] data; 124 | begin 125 | push_data = data; 126 | push_data_valid = 1; 127 | #10 128 | push_data_valid = 0; 129 | end 130 | endtask 131 | 132 | task TASK_shrink; 133 | input [`IDX_WIDTH-1:0] idx; 134 | begin 135 | read_idx = idx; 136 | read_idx_valid = 1; 137 | is_shrink = 1; 138 | read_will_update = 0; 139 | #10 140 | read_idx_valid = 0; 141 | is_shrink = 0; 142 | end 143 | endtask 144 | 145 | task TASK_read; 146 | input [`IDX_WIDTH-1:0] idx; 147 | output [DATA_WIDTH-1:0] data; 148 | begin 149 | read_idx = idx; 150 | read_idx_valid = 1; 151 | is_shrink = 0; 152 | read_will_update = 0; 153 | read_data_ready = 1; 154 | #10 155 | read_idx_valid = 0; 156 | count = 0; 157 | while(read_data_valid == 0) begin 158 | #10 159 | count = count + 1; 160 | if(count > 1000) begin 161 | $display("TIMED OUT WAITING FOR READ"); 162 | $finish; 163 | end 164 | end 165 | read_data_ready = 0; 166 | data = read_data; 167 | end 168 | endtask 169 | 170 | task TASK_readupdate; 171 | input [`IDX_WIDTH-1:0] idx; 172 | output [DATA_WIDTH-1:0] data; 173 | begin 174 | read_idx = idx; 175 | read_idx_valid = 1; 176 | is_shrink = 0; 177 | read_will_update = 1; 178 | read_data_ready = 1; 179 | #10 180 | read_idx_valid = 0; 181 | count = 0; 182 | while(read_data_valid == 0) begin 183 | #10 184 | count = count + 1; 185 | if(count > 1000) begin 186 | $display("TIMED OUT WAITING FOR READ"); 187 | $finish; 188 | end 189 | end 190 | read_data_ready = 0; 191 | read_will_update = 0; 192 | data = read_data; 193 | end 194 | endtask 195 | 196 | task TASK_readtimeout; 197 | input [`IDX_WIDTH-1:0] idx; 198 | output [DATA_WIDTH-1:0] data; 199 | begin 200 | read_idx = idx; 201 | read_idx_valid = 1; 202 | is_shrink = 0; 203 | read_will_update = 0; 204 | read_data_ready = 1; 205 | #10 206 | count = 0; 207 | read_idx_valid = 0; 208 | while(count <100) begin 209 | #10 210 | count = count + 1; 211 | if(read_data_valid == 1) begin 212 | $display("EXPECTED TIME OUT, TEST FAILED"); 213 | $finish; 214 | end 215 | end 216 | read_data_ready = 0; 217 | data = read_data; 218 | end 219 | endtask 220 | 221 | task TASK_wait4read; 222 | input [DATA_WIDTH-1:0] expected_data; 223 | begin 224 | is_shrink = 0; 225 | read_will_update = 0; 226 | read_data_ready = 1; 227 | #10 228 | count = 0; 229 | read_idx_valid = 0; 230 | while(read_data_valid != 1) begin 231 | #10 232 | count = count + 1; 233 | if(count > 100) begin 234 | $display("EXPECTED TIME OUT, TEST FAILED"); 235 | $finish; 236 | end 237 | end 238 | 239 | if(read_data != expected_data) begin 240 | $display("RECEIVED UNEXPECTED DATA %d", read_data); 241 | $finish; 242 | end 243 | read_data_ready = 0; 244 | end 245 | endtask 246 | 247 | task TASK_update; 248 | input [`IDX_WIDTH-1:0] idx; 249 | input [DATA_WIDTH-1:0] data; 250 | begin 251 | update_idx = idx; 252 | update_data = data; 253 | update_idx_valid = 1; 254 | update_data_valid = 1; 255 | is_shrink = 0; 256 | read_will_update = 0; 257 | #10 258 | update_idx_valid = 0; 259 | update_data_valid = 0; 260 | count = 0; 261 | while(update_receive_ack == 0) begin 262 | #10 263 | count = count + 1; 264 | if(count > 1000) begin 265 | $display("TIMED OUT WAITING FOR update ack"); 266 | $finish; 267 | end 268 | end 269 | end 270 | endtask 271 | 272 | task TASK_getcredit; 273 | output [`IDX_WIDTH-1:0] credit; 274 | begin 275 | credit_ready = 1; 276 | //Wait till the credit high comes out 277 | while(credit_valid == 0) begin 278 | #10 279 | count = count + 1; 280 | if(count > 1000) begin 281 | $display("TIMED OUT WAITING FOR RESP"); 282 | $finish; 283 | end 284 | end 285 | 286 | credit = credit_out; 287 | end 288 | endtask 289 | 290 | task TASK_nop; 291 | input [7:0] iterations; 292 | integer cnt; 293 | begin 294 | cnt =0; 295 | while(cnt < iterations) begin 296 | #10 297 | cnt = cnt + 1; 298 | end 299 | end 300 | endtask 301 | 302 | 303 | initial begin 304 | 305 | TASK_reset; 306 | TASK_init; 307 | 308 | // fill some random data 309 | for(i=0; i<5;i=i+1) 310 | ref_data[i] = $random; 311 | 312 | // Write some data 313 | for(i=0; i<5;i=i+1) 314 | TASK_fill(ref_data[i]); 315 | TASK_nop(5); 316 | 317 | // Read that data with marking them for future update 318 | for(i=0; i<3;i=i+1) begin 319 | TASK_readupdate(i, data_received); 320 | if(data_received != ref_data[i]) begin 321 | $display("TEST FAILED, data: %d", data_received); 322 | $finish; 323 | end 324 | end 325 | 326 | $display("Testing whether read will time out"); 327 | // Try reading the same data without updating it, it should time-out. 328 | TASK_readtimeout(1, data_received); 329 | $display("Read 1 timed out"); 330 | // Send another read while the read is stalled, this read should be blocked. 331 | // Note that this was not marked for update 332 | TASK_readtimeout(4, data_received); 333 | $display("Read 2 timed out"); 334 | 335 | // Update the data 336 | TASK_update(1, $random); 337 | $display("Update finished, now the reads should go through"); 338 | 339 | // Now we need to see both blocked reads complete. 340 | TASK_wait4read(ref_data[1]); 341 | $display("Read 1 went through"); 342 | TASK_wait4read(ref_data[4]); 343 | $display("Read 2 went through"); 344 | 345 | $display("\n\n\t\t *** STALL TEST PASSED ***\n\n"); 346 | $finish; 347 | end 348 | 349 | 350 | // Clock Generator 351 | always #5 clk = ~clk; 352 | 353 | endmodule 354 | 355 | -------------------------------------------------------------------------------- /dut/buffet.v: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Kartik Hegde (kartikhegde.net) 3 | // 4 | // Copyright (c) 2019 Authors of "Buffets: An Efficient and Composable Storage Idiom for Explicit Decoupled Data 5 | // Orchestration". 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 8 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 13 | // Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 16 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | 21 | 22 | `include "buffet_defines.v" 23 | 24 | module buffet( 25 | clk, 26 | nreset_i, 27 | // Read Port 28 | read_data, 29 | read_data_ready, 30 | read_data_valid, 31 | read_idx, 32 | read_idx_valid, 33 | read_idx_ready, 34 | read_will_update, 35 | // Write Port 36 | push_data, 37 | push_data_valid, 38 | push_data_ready, 39 | //Update Port 40 | update_data, 41 | update_data_valid, 42 | update_idx, 43 | update_idx_valid, 44 | update_ready, 45 | update_receive_ack, 46 | // Shrink Port 47 | is_shrink, 48 | // Credits 49 | credit_ready, 50 | credit_out, 51 | credit_valid 52 | 53 | ); 54 | 55 | parameter IDX_WIDTH = `IDX_WIDTH; // Index width 56 | parameter DATA_WIDTH = `DATA_WIDTH; // Data width 57 | 58 | // If this is 0, the in-place update path will be 59 | // statically removed (only Fill writes the RAM) 60 | // This would reduce hardware cost. 61 | parameter SUPPORTS_UPDATE = `SUPPORTS_UPDATE; 62 | 63 | // If this is 0, Fill and Update data will 64 | // share a single arbitrated RAM write port. 65 | // This could reduce hardware cost, but also 66 | // can degrade performace. 67 | // Because of buffet semantics this will never introduce deadlocks. 68 | // TODO: control over arbitration fairness. 69 | parameter SEPARATE_WRITE_PORTS = `SEPARATE_WRITE_PORTS; 70 | 71 | input clk, nreset_i; 72 | 73 | // Send credits to producer 74 | // Matches FIFO fills. 75 | output [IDX_WIDTH-1:0] credit_out; 76 | output credit_valid; 77 | input credit_ready; 78 | 79 | // Operation: Fill(Data) -> void; 80 | // Matches FIFO fills. 81 | input [DATA_WIDTH-1:0] push_data; 82 | input push_data_valid; 83 | output push_data_ready; 84 | // Asserted to 1 as producer will not send w/o credit. 85 | 86 | // Operation: Read(Index, bool) -> Data 87 | // Read closely matches scratchpads, but with index relative to the oldest element instead of absolute address. Additionally the "read_will_update" flag is used to indicate if the data will be modified in place. Subsequent reads to the same index will block until the RAW hazard is cleared by the update completing. 88 | // Read response matches scratchpad. However read responses may be 89 | // additionally delayed until fill and update hazards have cleared. 90 | // No response for shrinks. 91 | input [IDX_WIDTH-1:0] read_idx; 92 | input read_idx_valid; 93 | output read_idx_ready; 94 | input read_will_update; 95 | output [DATA_WIDTH-1:0] read_data; 96 | output read_data_valid; 97 | input read_data_ready; 98 | 99 | // Operation: Shrink(Size) -> void 100 | // Shrinks share the same port as read in order to maintain ordering. 101 | // read_idx will be considered as shrink size. 102 | input is_shrink; 103 | 104 | // Operation: Update(Index, Data) -> void; 105 | // Closely resembles scratchpad write but with relative index, and 106 | // separate valid for idx/data allows for potentially distinct producers 107 | input [IDX_WIDTH-1:0] update_idx; 108 | input update_idx_valid; 109 | input [DATA_WIDTH-1:0] update_data; 110 | input update_data_valid; 111 | output update_ready; 112 | output update_receive_ack; 113 | // Update is always ready since there was a proceeding Read() to same index. 114 | // Request is accepted as soon as both are valid. 115 | 116 | //------------------------------------------------------------------ 117 | // WIRES/REGS 118 | //------------------------------------------------------------------ 119 | 120 | wire [IDX_WIDTH-1:0] read_idx_fifo, update_idx_fifo; 121 | wire [DATA_WIDTH-1:0] push_data_fifo, update_data_fifo; 122 | wire read_idx_fifo_valid, read_idx_buffet_ready, read_will_update_fifo, is_shrink_fifo; 123 | wire push_data_fifo_valid, update_idx_fifo_valid, update_buffet_ready, read_data_fifo_ready; 124 | 125 | wire [IDX_WIDTH-1:0] araddr_buffet, waddr0_buffet, waddr1_buffet; 126 | wire [DATA_WIDTH-1:0] read_data_buffet, wdata0_buffet, wdata1_buffet; 127 | wire arvalid_buffet, wvalid0_buffet, wvalid1_buffet, read_data_buffet_valid; 128 | 129 | reg update_receive_ack_r; 130 | 131 | //------------------------------------------------------------------ 132 | // INSTANTIATIONS 133 | //------------------------------------------------------------------ 134 | 135 | 136 | // We will adda FIFO to every Request/Responnse port for better throughput 137 | 138 | fifo 139 | #( 140 | .DATA_WIDTH(IDX_WIDTH+2), 141 | .FIFO_DEPTH(`READREQ_FIFO_DEPTH) 142 | ) 143 | u_channel_readreq 144 | ( 145 | .clk(clk), 146 | .nreset_i(nreset_i), 147 | .data_i({read_idx, read_will_update, is_shrink}), 148 | .data_i_valid(read_idx_valid), 149 | .data_i_ready(read_idx_ready), 150 | .data_o({read_idx_fifo, read_will_update_fifo, is_shrink_fifo}), 151 | .data_o_valid(read_idx_fifo_valid), 152 | .data_o_ready(read_idx_buffet_ready) 153 | ); 154 | 155 | 156 | fifo 157 | #( 158 | .DATA_WIDTH(DATA_WIDTH), 159 | .FIFO_DEPTH(`READRESP_FIFO_DEPTH) 160 | ) 161 | u_channel_readresp 162 | ( 163 | .clk(clk), 164 | .nreset_i(nreset_i), 165 | .data_i(read_data_buffet), 166 | .data_i_valid(read_data_buffet_valid), 167 | .data_i_ready(read_data_fifo_ready), 168 | .data_o(read_data), 169 | .data_o_valid(read_data_valid), 170 | .data_o_ready(read_data_ready) 171 | ); 172 | 173 | fifo 174 | #( 175 | .DATA_WIDTH(IDX_WIDTH+DATA_WIDTH), 176 | .FIFO_DEPTH(`UPDATE_FIFO_DEPTH) 177 | ) 178 | u_channel_update 179 | ( 180 | .clk(clk), 181 | .nreset_i(nreset_i), 182 | .data_i({update_data, update_idx}), 183 | .data_i_valid(update_idx_valid & update_data_valid), 184 | .data_i_ready(), 185 | .data_o({update_data_fifo, update_idx_fifo}), 186 | .data_o_valid(update_idx_fifo_valid), 187 | .data_o_ready(update_buffet_ready) 188 | ); 189 | 190 | fifo 191 | #( 192 | .DATA_WIDTH(DATA_WIDTH), 193 | .FIFO_DEPTH(`PUSH_FIFO_DEPTH) 194 | ) 195 | u_channel_push 196 | ( 197 | .clk(clk), 198 | .nreset_i(nreset_i), 199 | .data_i(push_data), 200 | .data_i_valid(push_data_valid), 201 | .data_i_ready(push_data_ready), 202 | .data_o(push_data_fifo), 203 | .data_o_valid(push_data_fifo_valid), 204 | .data_o_ready(1'b1) 205 | ); 206 | 207 | //TODO Add a backpressure from the consumer for the read 208 | 209 | // The Buffet Controller 210 | buffet_control 211 | #( 212 | .DATA_WIDTH(DATA_WIDTH), 213 | .ADDR_WIDTH(IDX_WIDTH), 214 | .SIZE(`SIZE) 215 | ) 216 | u_control 217 | ( 218 | .clk(clk), 219 | .nreset_i(nreset_i), 220 | // read index input and output 221 | .read_idx_i(read_idx_fifo), 222 | .read_idx_valid_i(read_idx_fifo_valid), 223 | .read_data_ready_i(read_data_fifo_ready), 224 | .read_idx_o(araddr_buffet), 225 | .read_idx_valid_o(arvalid_buffet), 226 | .read_idx_ready_o(read_idx_buffet_ready), 227 | // read controls 228 | .read_will_update(read_will_update_fifo), 229 | .read_is_shrink(is_shrink_fifo), 230 | // Data pushes 231 | .push_data_i(push_data_fifo), 232 | .push_data_valid_i(push_data_fifo_valid), 233 | .push_data_ready(), 234 | .push_data_o(wdata0_buffet), 235 | .push_idx_o(waddr0_buffet), 236 | .push_data_valid_o(wvalid0_buffet), 237 | // Updates 238 | .update_data_i(update_data_fifo), 239 | .update_idx_i(update_idx_fifo), 240 | .update_valid_i(update_idx_fifo_valid), 241 | .update_data_o(wdata1_buffet), 242 | .update_idx_o(waddr1_buffet), 243 | .update_valid_o(wvalid1_buffet), 244 | .update_ready_o(update_buffet_ready), 245 | //Credits 246 | .credit_ready(credit_ready), 247 | .credit_out(credit_out), 248 | .credit_valid(credit_valid) 249 | 250 | ); 251 | 252 | // RAM 253 | dpram 254 | #( 255 | .ADDR_WIDTH(IDX_WIDTH), 256 | .DATA_WIDTH(DATA_WIDTH), 257 | .SEPARATE_WRITE_PORTS(SEPARATE_WRITE_PORTS) 258 | ) 259 | u_dpram 260 | ( 261 | .CLK(clk), 262 | .RESET(nreset_i), 263 | .ARADDR(araddr_buffet), 264 | .ARVALID(arvalid_buffet), 265 | .WADDR0(waddr0_buffet), 266 | .WVALID0(wvalid0_buffet), 267 | .WADDR1(waddr1_buffet), 268 | .WVALID1(wvalid1_buffet), 269 | .WDATA0(wdata0_buffet), 270 | .WDATA1(wdata1_buffet), 271 | .RDATA(read_data_buffet), 272 | .RVALID(read_data_buffet_valid) 273 | ); 274 | 275 | 276 | //------------------------------------------------------------------ 277 | // SEQUENTIAL LOGIC 278 | //------------------------------------------------------------------ 279 | 280 | always @(posedge clk or negedge nreset_i) begin 281 | if(~nreset_i) begin 282 | update_receive_ack_r <= 1'b0; 283 | end 284 | else begin 285 | update_receive_ack_r <= update_idx_valid & update_data_valid; 286 | end 287 | end 288 | 289 | //------------------------------------------------------------------ 290 | // ASSIGN OUTPUTS 291 | //------------------------------------------------------------------ 292 | assign update_receive_ack = update_receive_ack_r; 293 | 294 | endmodule 295 | -------------------------------------------------------------------------------- /dut/buffet_control.v: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Kartik Hegde (kartikhegde.net) 3 | // 4 | // Copyright (c) 2019 Authors of "Buffets: An Efficient and Composable Storage Idiom for Explicit Decoupled Data 5 | // Orchestration". 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 8 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 13 | // Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 16 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | 21 | 22 | `include "buffet_defines.v" 23 | 24 | module buffet_control 25 | ( 26 | clk, 27 | nreset_i, 28 | //Read in and out 29 | read_idx_i, 30 | read_idx_valid_i, 31 | read_data_ready_i, 32 | read_idx_o, 33 | read_idx_valid_o, 34 | read_idx_ready_o, 35 | read_will_update, 36 | read_is_shrink, 37 | // Push data 38 | push_data_i, 39 | push_data_valid_i, 40 | push_data_ready, 41 | push_data_o, 42 | push_idx_o, 43 | push_data_valid_o, 44 | // Updates 45 | update_data_i, 46 | update_idx_i, 47 | update_valid_i, 48 | update_data_o, 49 | update_idx_o, 50 | update_valid_o, 51 | update_ready_o, 52 | // Credits 53 | credit_ready, 54 | credit_out, 55 | credit_valid 56 | ); 57 | 58 | 59 | //------------------------------------------------------------------ 60 | // PARAMETERS 61 | //------------------------------------------------------------------ 62 | 63 | parameter DATA_WIDTH = 32; 64 | parameter ADDR_WIDTH = 8; 65 | parameter SIZE = 2 ** ADDR_WIDTH; 66 | 67 | localparam READY=2'b00, WAIT=2'b01, DISPATCH=2'b10; 68 | //------------------------------------------------------------------ 69 | // INPUT/OUTPUT PORTS 70 | //------------------------------------------------------------------ 71 | 72 | input clk, nreset_i; 73 | 74 | // Read Ports (In and Out) 75 | input [ADDR_WIDTH-1:0] read_idx_i; 76 | output [ADDR_WIDTH-1:0] read_idx_o; 77 | input read_idx_valid_i; 78 | input read_data_ready_i; 79 | output read_idx_valid_o; 80 | output read_idx_ready_o; 81 | input read_will_update; 82 | input read_is_shrink; 83 | 84 | // Push Port 85 | input [DATA_WIDTH-1:0] push_data_i; 86 | output [DATA_WIDTH-1:0] push_data_o; 87 | input push_data_valid_i; 88 | output push_data_valid_o; 89 | output [ADDR_WIDTH-1:0] push_idx_o; 90 | output push_data_ready; 91 | 92 | // Updates 93 | input [DATA_WIDTH-1:0] update_data_i; 94 | output [DATA_WIDTH-1:0] update_data_o; 95 | input [ADDR_WIDTH-1:0] update_idx_i; 96 | output [ADDR_WIDTH-1:0] update_idx_o; 97 | input update_valid_i; 98 | output update_valid_o; 99 | output update_ready_o; 100 | 101 | // Credits 102 | input credit_ready; 103 | output [ADDR_WIDTH-1:0] credit_out; 104 | output credit_valid; 105 | 106 | //------------------------------------------------------------------ 107 | // REGISTERS 108 | //------------------------------------------------------------------ 109 | 110 | reg [ADDR_WIDTH-1:0] tail, head; 111 | reg [1:0] read_state; 112 | 113 | // Output registers 114 | reg [ADDR_WIDTH-1:0] read_idx_o_r, push_idx_o_r, update_idx_o_r, credit_out_r; 115 | reg [DATA_WIDTH-1:0] push_data_o_r, update_data_o_r; 116 | reg read_idx_valid_o_r, push_data_valid_o_r, update_valid_o_r, credit_valid_r; 117 | reg read_idx_ready_o_r, update_ready_o_r; 118 | reg read_will_update_r, read_will_update_stage_r; 119 | reg [ADDR_WIDTH-1:0] read_idx_i_r, update_idx_i_r, read_idx_stage_r; 120 | reg read_idx_valid_i_r, read_idx_valid_stage_r, update_valid_i_r; 121 | reg stall_r; 122 | 123 | reg [ADDR_WIDTH-1:0] scoreboard [`SCOREBOARD_SIZE-1:0]; 124 | reg [`SCOREBOARD_SIZE-1:0] scoreboard_valid; 125 | 126 | //------------------------------------------------------------------ 127 | // WIRES 128 | //------------------------------------------------------------------ 129 | 130 | // Head Tail chase has two cases: (1) one where tail is greater than head and (2) vice versa 131 | wire tail_greater_than_head = (tail < head)? 1'b0:1'b1; 132 | 133 | // Distance between the head and the end of the FIFO 134 | wire [ADDR_WIDTH-1:0] head_offset = SIZE - head; 135 | 136 | // Distance between tail and head (applicable in case 1) 137 | wire [ADDR_WIDTH-1:0] tail_head_distance = tail - head; 138 | 139 | // In case 1, tail_head_distance directly gives occupancy, in case (2) offset needs to be added to tail. 140 | wire [ADDR_WIDTH-1:0] occupancy = (tail_greater_than_head)? tail_head_distance : 141 | (tail + head_offset); 142 | 143 | // Available space in the bbuffer 144 | wire [ADDR_WIDTH-1:0] space_avail = SIZE - occupancy; 145 | 146 | // Empty FIFO 147 | wire empty = (occupancy == 1'b0)? 1'b1:1'b0; 148 | 149 | // All the possible events 150 | wire read_event = ~empty & read_idx_valid_i & ~read_is_shrink; 151 | wire shrink_event = ~empty & read_idx_valid_i & read_is_shrink; 152 | wire write_event = push_data_valid_i; 153 | wire update_event = update_valid_i; 154 | wire [1:0] event_cur = {read_event, shrink_event, write_event, update_event}; 155 | 156 | // check if the read is between the tail and head 157 | // This changes based on tail_greater_than_head value (first check if the read is valid) 158 | 159 | wire read_valid_hgtt = ((read_idx_i_r < tail) && (read_idx_i_r >= head))? 1'b1 :1'b0; 160 | wire read_valid_hgtt_n = ~read_valid_hgtt; 161 | wire read_valid = (tail_greater_than_head)? read_valid_hgtt : read_valid_hgtt_n; 162 | // WAR hazard is when you are trying to read something that is not present in the buffet yet - wait till you receive it. 163 | // Caution: this might lead to a lock -- TODO a way to retire waiting reads. 164 | wire war_hazard = ~read_valid; 165 | 166 | // RAW hazard detection is simply checking the outstanding updates 167 | 168 | wire [`SCOREBOARD_SIZE-1:0] match_read, match_update; 169 | 170 | genvar i; 171 | generate 172 | for (i = 0; i < `SCOREBOARD_SIZE; i = i + 1) begin : SCOREBOARD_COMP 173 | assign match_read[i] = (scoreboard_valid[i])? ((scoreboard[i] == read_idx_i_r) ? 1'b1:1'b0) : 1'b0; 174 | assign match_update[i] = (scoreboard_valid[i])? ((scoreboard[i] == update_idx_i_r) ? 1'b1:1'b0) : 1'b0; 175 | end 176 | endgenerate 177 | 178 | //Any match? 179 | wire raw_hazard = |match_read; 180 | 181 | // Pipeline should stall on hazards 182 | wire stall = raw_hazard | war_hazard | ~read_data_ready_i; 183 | 184 | // Next scoreboard entry - TODO use generic LZ 185 | wire [$clog2(`SCOREBOARD_SIZE)-1:0] next_entry; 186 | priorityEncoder #(`SCOREBOARD_SIZE) u_nextslot(.in(scoreboard_valid), .out(next_entry)); 187 | 188 | // Match address (reverse the match and then find the leading zeros 189 | wire [`SCOREBOARD_SIZE-1:0] match_reversed; 190 | wire [$clog2(`SCOREBOARD_SIZE):0] match_addr; 191 | reverse #(`SCOREBOARD_SIZE) u_reverse(.bus_i(match_update), .bus_o(match_reversed)); 192 | leadingZero8 u_LZE8(.sequence(match_reversed), .index(match_addr)); 193 | 194 | //------------------------------------------------------------------ 195 | // SEQUENTIAL LOGIC 196 | //------------------------------------------------------------------ 197 | 198 | 199 | 200 | //----------------------------------------------------------------------------------// 201 | 202 | //**********************// 203 | // *** Credit logic *** // 204 | //**********************// 205 | 206 | // Reflects credit ready 207 | always @(posedge clk or negedge nreset_i) begin 208 | if(~nreset_i) begin 209 | credit_valid_r <= 1'b0; 210 | end 211 | else begin 212 | credit_valid_r <= credit_ready; 213 | end 214 | end 215 | 216 | // No Reset, reply with the available space in the buffer 217 | always @(posedge clk) begin 218 | if(credit_ready) 219 | credit_out_r <= space_avail; 220 | end 221 | 222 | //----------------------------------------------------------------------------------// 223 | 224 | //**********************// 225 | // *** Push Logic *** // 226 | //**********************// 227 | 228 | // Producer will never send more data than there is space --> due to credit req/rsp.. 229 | // Therefore, there is nothing tricky here - as you get the data, update the tail 230 | // and push the data to the buffer. 231 | 232 | //push data output valid reflects the input 233 | always @(posedge clk or negedge nreset_i) begin 234 | if(~nreset_i) begin 235 | push_data_valid_o_r <= 1'b0; 236 | end 237 | else begin 238 | push_data_valid_o_r <= push_data_valid_i; 239 | end 240 | end 241 | 242 | // Update the tail (Counter wraps around) 243 | always @(posedge clk or negedge nreset_i) begin 244 | if(~nreset_i) begin 245 | tail <= {ADDR_WIDTH{1'b0}}; 246 | end 247 | else begin 248 | if(push_data_valid_i) 249 | tail <= tail + 1'b1; 250 | end 251 | end 252 | 253 | // Send the data off to the buffer 254 | always @(posedge clk) begin 255 | if(push_data_valid_i) 256 | push_data_o_r <= push_data_i; 257 | push_idx_o_r <= tail; 258 | end 259 | 260 | //----------------------------------------------------------------------------------// 261 | 262 | //**********************// 263 | // *** Update Logic ***// 264 | //**********************// 265 | 266 | // If there is a separate write port for updates, this is straightforward too. 267 | // First, clear the entry in the scoreboard. Send the update with the address to the 268 | // buffer. We need not wait for the ack, as it need not wait. 269 | // However, if writes and updates share a write path, then we need to wait for the 270 | // acknowledgement of update. TODO: Arbitration 271 | // NOTE: This problem does not exist if the update gets static priority over the writes. 272 | 273 | // We will first register the update request. 274 | always @(posedge clk or negedge nreset_i) begin 275 | if(~nreset_i) begin 276 | update_valid_i_r <= 1'b0; 277 | update_idx_i_r <= {ADDR_WIDTH{1'b0}}; 278 | end 279 | else begin 280 | if(update_valid_i) begin 281 | update_idx_i_r <= update_idx_i; 282 | end 283 | update_valid_i_r <= update_valid_i; 284 | end 285 | end 286 | 287 | // Send off update 288 | // Set the update valid 289 | always @(posedge clk or negedge nreset_i) begin 290 | if(~nreset_i) begin 291 | update_valid_o_r <= 1'b0; 292 | end 293 | else begin 294 | update_valid_o_r <= update_valid_i_r; 295 | end 296 | end 297 | 298 | always @(posedge clk) begin 299 | if(update_valid_i_r) begin 300 | update_idx_o_r <= update_idx_i_r; 301 | update_data_o_r <= update_data_i; 302 | end 303 | end 304 | 305 | 306 | // We will update the scoreboard separately 307 | 308 | //----------------------------------------------------------------------------------// 309 | 310 | //**********************// 311 | // *** Read Logic ***// 312 | //**********************// 313 | 314 | // Read gets stalled if (1) tries to get something that is slated for an update (2) beyond the window. 315 | // Otherwise, simply return the data. 316 | 317 | // State Machine 318 | always @(posedge clk or negedge nreset_i) begin 319 | if(~nreset_i) begin 320 | read_state <= READY; 321 | end 322 | else begin 323 | case(read_state) 324 | 325 | // Check if there are hazards 326 | READY: 327 | read_state <= (read_idx_valid_i_r)? ((stall) ? WAIT : DISPATCH): READY; 328 | 329 | // Wait till hazards are cleared 330 | WAIT: 331 | read_state <= (stall)? WAIT:DISPATCH; 332 | 333 | // Dispatch and go back 334 | DISPATCH: 335 | read_state <= READY; 336 | 337 | endcase 338 | end 339 | end 340 | 341 | // this always block registers new read events 342 | always @(posedge clk or negedge nreset_i) begin 343 | if(~nreset_i) begin 344 | read_idx_valid_i_r <= 1'b0; 345 | read_will_update_r <= 1'b0; 346 | read_idx_i_r <= {ADDR_WIDTH{1'b0}}; 347 | end 348 | else begin 349 | if(read_event & (read_state != WAIT)) begin 350 | read_idx_valid_i_r <= read_idx_valid_i; 351 | read_will_update_r <= read_will_update; 352 | read_idx_i_r <= read_idx_i + head; 353 | end 354 | else if((read_idx_valid_stage_r) & (read_state == DISPATCH)) begin 355 | read_idx_valid_i_r <= 1'b1; 356 | read_will_update_r <= read_will_update_stage_r; 357 | read_idx_i_r <= read_idx_stage_r; 358 | end 359 | else begin 360 | read_idx_valid_i_r <= 1'b0; 361 | end 362 | end 363 | end 364 | 365 | // When we are waiting for hazard to be cleared, there is a chance for one more 366 | // read to appear (due to the propogation delay of ready signal). In that case. 367 | // we need to stage that read to be considered after the hazard is cleared. 368 | always @(posedge clk or negedge nreset_i) begin 369 | if(~nreset_i) begin 370 | read_idx_valid_stage_r <= 1'b0; 371 | read_will_update_stage_r<= 1'b0; 372 | read_idx_stage_r <= {ADDR_WIDTH{1'b0}}; 373 | end 374 | else begin 375 | if(read_event & (read_state == WAIT)) begin 376 | read_idx_valid_stage_r <= read_idx_valid_i; 377 | read_will_update_stage_r<= read_will_update; 378 | read_idx_stage_r <= read_idx_i + head; 379 | end 380 | end 381 | end 382 | 383 | // Ready is pulled low whenever (1) You detect a hazard (2) waiting to resolve a hazard 384 | // (3) there is a staged data already. 385 | always @(posedge clk or negedge nreset_i) begin 386 | if(~nreset_i) begin 387 | read_idx_ready_o_r <= 1'b0; 388 | end 389 | else 390 | read_idx_ready_o_r <= ( 391 | ((read_state == READY)&(read_idx_valid_i_r)&(stall))| 392 | (read_state == WAIT) | 393 | ((read_idx_valid_stage_r) & (read_state == DISPATCH)) 394 | )? 1'b0 : 1'b1; 395 | end 396 | 397 | // --------------- Pipeline Output -- if clean, send output ------// 398 | 399 | always @(posedge clk or negedge nreset_i) begin 400 | if(~nreset_i) 401 | read_idx_valid_o_r <= 1'b0; 402 | else 403 | read_idx_valid_o_r <= (read_state == DISPATCH)?1'b1:1'b0; 404 | end 405 | 406 | always @(posedge clk) begin 407 | if(read_state == DISPATCH) 408 | read_idx_o_r <= read_idx_i_r; 409 | end 410 | 411 | //----------------------------------------------------------------------------------// 412 | 413 | //**********************// 414 | // *** Shrink Logic ***// 415 | //**********************// 416 | 417 | // If a valid shrink comes in, we update the head. (This is the only driver for head reg) 418 | always @(posedge clk or negedge nreset_i) begin 419 | if(~nreset_i) begin 420 | head <= {ADDR_WIDTH{1'b0}}; 421 | end 422 | else begin 423 | if(shrink_event) 424 | head <= head + read_idx_i; 425 | end 426 | end 427 | 428 | //----------------------------------------------------------------------------------// 429 | // 430 | //***********************// 431 | // ***Scoreboard Logic***// 432 | //**********************// 433 | 434 | // Need to do two things: 435 | // 1. When a read says it will update, add an entry 436 | // 2. When an update comes, clear the entry 437 | always @(posedge clk or negedge nreset_i) begin 438 | if(~nreset_i) 439 | scoreboard_valid <= {`SCOREBOARD_SIZE{1'b0}}; 440 | else begin 441 | if((read_state == DISPATCH) && (read_will_update_r)) begin 442 | scoreboard_valid[next_entry] <= 1'b1; 443 | scoreboard[next_entry] <= read_idx_i_r; 444 | end 445 | if(update_valid_i_r) begin 446 | scoreboard_valid[match_addr] <= 1'b0; 447 | end 448 | end 449 | end 450 | 451 | //------------------------------------------------------------------ 452 | // ASSIGN OUTPUTS 453 | //------------------------------------------------------------------ 454 | 455 | // Credits 456 | assign credit_out = credit_out_r; 457 | assign credit_valid = credit_valid_r; 458 | // Push 459 | assign push_idx_o = push_idx_o_r; 460 | assign push_data_o = push_data_o_r; 461 | assign push_data_valid_o = push_data_valid_o_r; 462 | // Update 463 | assign update_data_o = update_data_o_r; 464 | assign update_idx_o = update_idx_o_r; 465 | assign update_valid_o = update_valid_o_r; 466 | // Always ready to take the updates in 467 | assign update_ready_o = 1'b1; 468 | // Read 469 | assign read_idx_o = read_idx_o_r; 470 | assign read_idx_valid_o = read_idx_valid_o_r; 471 | assign read_idx_ready_o = read_idx_ready_o_r; 472 | 473 | endmodule 474 | --------------------------------------------------------------------------------