├── .gitignore ├── LICENSE ├── cocotb ├── Makefile ├── test_dut.py └── waveforms.gtkw ├── rtl ├── blk_mem.v ├── cross_clock_enable.v ├── ppfifo.v ├── ppfifo_sink.v └── ppfifo_source.v └── sim └── tb_ppfifo.v /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | test/fake/temp 3 | wip 4 | 5 | # VIM temporary files 6 | *~ 7 | *.swp 8 | *.swo 9 | 10 | #Simulation Things 11 | .sconsign.dblite 12 | temp.txt 13 | *.sim 14 | *.vcd 15 | *.vvp 16 | results.xml 17 | 18 | # C extensions 19 | *.so 20 | 21 | # Packages 22 | *.egg 23 | *.egg-info 24 | dist 25 | build 26 | eggs 27 | parts 28 | #bin 29 | var 30 | sdist 31 | develop-eggs 32 | .installed.cfg 33 | #lib 34 | lib64 35 | 36 | # Installer logs 37 | pip-log.txt 38 | 39 | # Unit test / coverage reports 40 | .coverage 41 | .tox 42 | nosetests.xml 43 | 44 | # Translations 45 | *.mo 46 | 47 | # Mr Developer 48 | .mr.developer.cfg 49 | .project 50 | .pydevproject 51 | 52 | 53 | # Documentation Output 54 | docs/html 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /cocotb/Makefile: -------------------------------------------------------------------------------- 1 | TOPLEVEL_LANG ?= verilog 2 | PWD=$(shell pwd) 3 | TOPDIR=$(PWD)/.. 4 | COCOTB := $(shell $(python) nysa paths -c -s) 5 | 6 | PYTHONPATH := ./model:$(PYTHONPATH) 7 | export PYTHONPATH 8 | export PYTHONHOME=$(shell python -c "from distutils.sysconfig import get_config_var; print(get_config_var('prefix'))") 9 | 10 | EXTRA_ARGS+=-I$(TOPDIR)/rtl 11 | 12 | #Dependencies 13 | VERILOG_SOURCES = ${TOPDIR}/rtl/cross_clock_enable.v 14 | VERILOG_SOURCES += ${TOPDIR}/rtl/blk_mem.v 15 | VERILOG_SOURCES += ${TOPDIR}/rtl/ppfifo.v 16 | VERILOG_SOURCES += ${TOPDIR}/rtl/ppfifo_sink.v 17 | VERILOG_SOURCES += ${TOPDIR}/rtl/ppfifo_source.v 18 | 19 | #DUT 20 | VERILOG_SOURCES += $(TOPDIR)/sim/tb_ppfifo.v 21 | 22 | TOPLEVEL = tb_ppfifo 23 | 24 | GPI_IMPL := vpi 25 | 26 | export TOPLEVEL_LANG 27 | MODULE=test_dut 28 | 29 | include $(COCOTB)/makefiles/Makefile.inc 30 | include $(COCOTB)/makefiles/Makefile.sim 31 | 32 | .PHONY: wave test 33 | wave: 34 | gtkwave waveforms.gtkw & 35 | 36 | 37 | -------------------------------------------------------------------------------- /cocotb/test_dut.py: -------------------------------------------------------------------------------- 1 | 2 | import cocotb 3 | from cocotb.triggers import RisingEdge 4 | 5 | 6 | @cocotb.test(skip = False) 7 | def first_test(dut): 8 | dut.log.info("Testing...") 9 | 10 | COUNT = 1000 11 | count = 0 12 | 13 | while count < COUNT: 14 | yield RisingEdge(dut.clk) 15 | count += 1 16 | 17 | dut.log.info("Done!") 18 | -------------------------------------------------------------------------------- /cocotb/waveforms.gtkw: -------------------------------------------------------------------------------- 1 | [*] 2 | [*] GTKWave Analyzer v3.3.66 (w)1999-2015 BSI 3 | [*] Mon May 2 17:13:55 2016 4 | [*] 5 | [dumpfile] "/home/cospan/Projects/verilog_ppfifo_demo/cocotb/design.vcd" 6 | [dumpfile_mtime] "Mon May 2 17:08:00 2016" 7 | [dumpfile_size] 143473 8 | [savefile] "/home/cospan/Projects/verilog_ppfifo_demo/cocotb/waveforms.gtkw" 9 | [timestart] 0 10 | [size] 1888 789 11 | [pos] -3 -1 12 | *-21.451616 1739000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 13 | [treeopen] tb_ppfifo. 14 | [sst_width] 210 15 | [signals_width] 232 16 | [sst_expanded] 1 17 | [sst_vpaned_height] 208 18 | @28 19 | tb_ppfifo.clk 20 | tb_ppfifo.rd_clk 21 | tb_ppfifo.rst 22 | @200 23 | - 24 | @800200 25 | -Source 26 | @28 27 | tb_ppfifo.source.clk 28 | tb_ppfifo.source.i_enable 29 | @200 30 | - 31 | @28 32 | tb_ppfifo.source.i_wr_rdy[1:0] 33 | tb_ppfifo.source.o_wr_act[1:0] 34 | @200 35 | - 36 | @28 37 | tb_ppfifo.source.o_wr_stb 38 | @22 39 | tb_ppfifo.source.o_wr_data[31:0] 40 | @200 41 | - 42 | @22 43 | tb_ppfifo.source.i_wr_size[23:0] 44 | tb_ppfifo.source.r_count[23:0] 45 | @1000200 46 | -Source 47 | @200 48 | - 49 | @800200 50 | -Sink 51 | @28 52 | tb_ppfifo.sink.clk 53 | @200 54 | - 55 | @28 56 | tb_ppfifo.sink.i_rd_rdy 57 | tb_ppfifo.sink.o_rd_act 58 | @200 59 | - 60 | @29 61 | tb_ppfifo.sink.o_rd_stb 62 | @22 63 | tb_ppfifo.sink.i_rd_data[31:0] 64 | @200 65 | - 66 | @22 67 | tb_ppfifo.sink.i_rd_size[23:0] 68 | tb_ppfifo.sink.r_count[23:0] 69 | @1000200 70 | -Sink 71 | [pattern_trace] 1 72 | [pattern_trace] 0 73 | -------------------------------------------------------------------------------- /rtl/blk_mem.v: -------------------------------------------------------------------------------- 1 | //library ieee; 2 | //use ieee.std_logic_1164.all; 3 | //use ieee.std_logic_unsigned.all; 4 | 5 | //----------------------------------------------------- 6 | // Design Name : ram_dp_sr_sw 7 | // File Name : ram_dp_sr_sw.v 8 | // Function : Synchronous read write RAM 9 | // Coder : Deepak Kumar Tala 10 | //----------------------------------------------------- 11 | 12 | `timescale 1ns/1ps 13 | 14 | module blk_mem #( 15 | parameter DATA_WIDTH = 8, 16 | parameter ADDRESS_WIDTH = 4, 17 | parameter INC_NUM_PATTERN = 0 18 | )( 19 | input clka, 20 | input wea, 21 | input [ADDRESS_WIDTH - 1 :0] addra, 22 | input [DATA_WIDTH - 1:0] dina, 23 | input clkb, 24 | input [ADDRESS_WIDTH - 1:0] addrb, 25 | output [DATA_WIDTH - 1:0] doutb 26 | ); 27 | 28 | //Parameters 29 | //Registers/Wires 30 | reg [DATA_WIDTH - 1:0] mem [0:2 ** ADDRESS_WIDTH]; 31 | reg [DATA_WIDTH - 1:0] dout; 32 | 33 | //Submodules 34 | //Asynchronous Logic 35 | assign doutb = dout; 36 | 37 | //Synchronous Logic 38 | //write only on the A side 39 | `ifdef SIMULATION 40 | integer i; 41 | initial begin 42 | i = 0; 43 | for (i = 0; i < (2 ** ADDRESS_WIDTH); i = i + 1) begin 44 | if (INC_NUM_PATTERN) begin 45 | mem[i] <= i; 46 | end 47 | else begin 48 | //Zero Everything Out 49 | mem[i] <= 0; 50 | end 51 | end 52 | end 53 | `endif 54 | 55 | always @ (posedge clka) 56 | begin 57 | if ( wea ) begin 58 | mem[addra] <= dina; 59 | end 60 | end 61 | 62 | //read only on the b side 63 | always @ (posedge clkb) 64 | begin 65 | dout <= mem[addrb]; 66 | end 67 | 68 | 69 | endmodule 70 | 71 | -------------------------------------------------------------------------------- /rtl/cross_clock_enable.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns/1ps 2 | 3 | module cross_clock_enable ( 4 | input rst, 5 | input in_en, 6 | 7 | input out_clk, 8 | output reg out_en 9 | 10 | ); 11 | 12 | //Parameters 13 | //Registers/Wires 14 | reg [2:0] out_en_sync; 15 | //Submodules 16 | //Asynchronous Logic 17 | //Synchronous Logic 18 | always @ (posedge out_clk) begin 19 | if (rst) begin 20 | out_en_sync <= 0; 21 | out_en <= 0; 22 | end 23 | else begin 24 | if (out_en_sync[2:1] == 2'b11) begin 25 | out_en <= 1; 26 | end 27 | else if (out_en_sync[2:1] == 2'b00) begin 28 | out_en <= 0; 29 | end 30 | out_en_sync <= {out_en_sync[1:0], in_en}; 31 | end 32 | end 33 | endmodule 34 | -------------------------------------------------------------------------------- /rtl/ppfifo.v: -------------------------------------------------------------------------------- 1 | /* 2 | Distributed under the MIT licesnse. 3 | Copyright (c) 2011 Dave McCoy (dave.mccoy@cospandesign.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | */ 23 | 24 | `timescale 1ns/1ps 25 | 26 | //XXX: NOTE: All counts are 24bits long, this could be a parameter in the future 27 | 28 | 29 | module ppfifo 30 | #(parameter DATA_WIDTH = 8, 31 | ADDRESS_WIDTH = 4 32 | )( 33 | 34 | //universal input 35 | input reset, 36 | 37 | //write side 38 | input write_clock, 39 | output reg [1:0] write_ready, 40 | input [1:0] write_activate, 41 | output [23:0] write_fifo_size, 42 | input write_strobe, 43 | input [DATA_WIDTH - 1: 0] write_data, 44 | output starved, 45 | 46 | //read side 47 | input read_clock, 48 | input read_strobe, 49 | output reg read_ready, 50 | input read_activate, 51 | output reg [23:0] read_count, 52 | output [DATA_WIDTH - 1: 0] read_data, 53 | 54 | output inactive 55 | 56 | ); 57 | 58 | localparam FIFO_DEPTH = (1 << ADDRESS_WIDTH); 59 | 60 | //Local Registers/Wires 61 | 62 | //Write Side 63 | wire ppfifo_ready; // The write side only needs to 64 | // know were ready if we don't 65 | // write anything read won't start 66 | wire [ADDRESS_WIDTH: 0] addr_in; // Actual address to the BRAM 67 | reg [ADDRESS_WIDTH - 1: 0]write_address; 68 | reg r_wselect; // Select the FIFO to work with 69 | reg write_enable; // Enable a write to the BRAM 70 | 71 | reg [1:0] wcc_read_ready;// Tell read side a FIFO is ready 72 | wire [1:0] wcc_read_done; 73 | // write status of the read 74 | // available 75 | reg wcc_tie_select; //because it's possible if the read 76 | //side is slower than the write 77 | //side it might be unknown which 78 | //side was selected first, so use 79 | //this signal to break ties 80 | 81 | reg [23:0] w_count[1:0]; // save the write count for the read 82 | // side 83 | reg w_empty[1:0]; 84 | reg w_reset; //write side reset 85 | reg [4:0] w_reset_timeout; 86 | wire ready; 87 | 88 | //Read Side 89 | wire [ADDRESS_WIDTH: 0] addr_out; //Actual address to the BRAM 90 | reg r_reset; 91 | reg [4:0] r_reset_timeout; 92 | 93 | reg [ADDRESS_WIDTH - 1: 0]r_address; //Address to access a bank 94 | reg r_rselect; //Select a bank (Select a FIFO) 95 | 96 | wire [1:0] rcc_read_ready; 97 | // Write side says X is ready 98 | reg [1:0] rcc_read_done;// Tell write side X is ready 99 | wire rcc_tie_select; 100 | //If there is a tie, then this will 101 | //break it 102 | 103 | reg [23:0] r_size[1:0]; // Size of FX read 104 | 105 | reg [1:0] r_ready; //FIFO is ready 106 | reg [1:0] r_wait; //Waiting for write side to send data 107 | reg [1:0] r_activate; //Controls which FIFO is activated 108 | 109 | reg r_next_fifo; //If both FIFOs are availalbe use this 110 | 111 | reg [1:0] r_pre_activate; //Used to delay the clock cycle by 112 | //one when the user activates the 113 | //FIFO 114 | 115 | reg r_pre_strobe; 116 | reg r_pre_read_wait;//Wait an extra cycle so the registered data has a chance to set 117 | //the data to be registered 118 | wire [DATA_WIDTH - 1: 0] w_read_data; //data from the read FIFO 119 | reg [DATA_WIDTH - 1: 0] r_read_data; //data from the read FIFO 120 | 121 | 122 | 123 | //assign r_wselect = (write_activate == 2'b00) ? 1'b0 : 124 | // (write_activate == 2'b01) ? 1'b0 : 125 | // (write_activate == 2'b10) ? 1'b1 : 126 | // reset ? 1'b0 : 127 | // r_wselect; 128 | // //I know this can be shortened down but it's more 129 | // //readible thi way 130 | 131 | assign write_fifo_size = FIFO_DEPTH; 132 | 133 | assign addr_in = {r_wselect, write_address}; 134 | //assign write_enable = (write_activate > 0) && write_strobe; 135 | assign ppfifo_ready = !(w_reset || r_reset); 136 | assign ready = ppfifo_ready; 137 | 138 | //assign wcc_tie_select = (wcc_read_ready == 2'b00) ? 1'b0 : 139 | // (wcc_read_ready == 2'b01) ? 1'b0 : 140 | // (wcc_read_ready == 2'b10) ? 1'b1 : 141 | // wcc_tie_select; 142 | // If the first FIFO is ready, 143 | // then both FIFOs are ready then 144 | // keep the first FIFO 145 | 146 | assign addr_out = {r_rselect, r_address}; 147 | 148 | 149 | //Debug 150 | //wire [23:0] debug_f0_w_count; 151 | //wire [23:0] debug_f1_w_count; 152 | 153 | //wire [23:0] debug_f0_r_size; 154 | //wire [23:0] debug_f1_r_size; 155 | 156 | //wire [23:0] debug_f0_r_count; 157 | //wire [23:0] debug_f1_r_count; 158 | 159 | //assign debug_f0_w_count = w_count[0]; 160 | //assign debug_f1_w_count = w_count[1]; 161 | 162 | //assign debug_f0_r_size = r_size[0]; 163 | //assign debug_f1_r_size = r_size[1]; 164 | 165 | assign inactive = (w_count[0] == 0) && 166 | (w_count[1] == 0) && 167 | (write_ready == 2'b11) && 168 | (!write_strobe); 169 | 170 | 171 | assign read_data = (r_pre_strobe) ? w_read_data : r_read_data; 172 | 173 | 174 | //Submodules 175 | blk_mem #( 176 | .DATA_WIDTH(DATA_WIDTH), 177 | .ADDRESS_WIDTH(ADDRESS_WIDTH + 1) 178 | ) fifo0 ( 179 | //Write 180 | .clka (write_clock ), 181 | .wea (write_enable ), //This may just be replaced with write activate 182 | .dina (write_data ), 183 | .addra (addr_in ), 184 | 185 | .clkb (read_clock ), 186 | .doutb (w_read_data ), 187 | .addrb (addr_out ) 188 | ); 189 | 190 | //W - R FIFO 0 191 | cross_clock_enable ccwf0 ( 192 | .rst (reset ), 193 | .in_en (wcc_read_ready[0] ), 194 | 195 | .out_clk (read_clock ), 196 | .out_en (rcc_read_ready[0] ) 197 | ); 198 | //W - R FIFO 1 199 | cross_clock_enable ccwf1 ( 200 | .rst (reset ), 201 | .in_en (wcc_read_ready[1] ), 202 | 203 | .out_clk (read_clock ), 204 | .out_en (rcc_read_ready[1] ) 205 | 206 | ); 207 | 208 | //W - R Tie Select 209 | cross_clock_enable ccts ( 210 | .rst (reset ), 211 | .in_en (wcc_tie_select ), 212 | 213 | .out_clk (read_clock ), 214 | .out_en (rcc_tie_select ) 215 | ); 216 | 217 | //R - W FIFO 0 218 | cross_clock_enable ccrf0 ( 219 | .rst (reset ), 220 | .in_en (rcc_read_done[0] ), 221 | 222 | .out_clk (read_clock ), 223 | .out_en (wcc_read_done[0] ) 224 | ); 225 | //R - W FIFO 1 226 | cross_clock_enable ccrf1 ( 227 | .rst (reset ), 228 | .in_en (rcc_read_done[1] ), 229 | 230 | .out_clk (read_clock ), 231 | .out_en (wcc_read_done[1] ) 232 | ); 233 | 234 | //R - W Reset 235 | cross_clock_enable cc_starved( 236 | .rst (reset ), 237 | .in_en (!read_ready && !read_activate ), 238 | 239 | .out_clk (write_clock ), 240 | .out_en (starved ) 241 | ); 242 | 243 | 244 | 245 | 246 | //asynchronous logic 247 | always @ (*) begin 248 | case (wcc_read_ready) 249 | 2'b00: begin 250 | wcc_tie_select = 1'b0; 251 | end 252 | 2'b01: begin 253 | wcc_tie_select = 1'b0; 254 | end 255 | 2'b10: begin 256 | wcc_tie_select = 1'b1; 257 | end 258 | default: begin 259 | wcc_tie_select = 1'b0; 260 | end 261 | endcase 262 | end 263 | always @ (*) begin 264 | case (write_activate) 265 | 2'b00: begin 266 | r_wselect = 1'b0; 267 | end 268 | 2'b01: begin 269 | r_wselect = 1'b0; 270 | end 271 | 2'b10: begin 272 | r_wselect = 1'b1; 273 | end 274 | default: begin 275 | r_wselect = 1'b0; 276 | end 277 | endcase 278 | end 279 | 280 | always @ (*) begin 281 | if (write_activate > 0 && write_strobe) begin 282 | write_enable = 1'b1; 283 | end 284 | else begin 285 | write_enable = 1'b0; 286 | end 287 | end 288 | //synchronous logic 289 | 290 | //Reset Logic 291 | always @ (posedge write_clock) begin 292 | if (reset) begin 293 | w_reset <= 1; 294 | w_reset_timeout <= 0; 295 | end 296 | else begin 297 | if (w_reset && (w_reset_timeout < 5'h4)) begin 298 | w_reset_timeout <= w_reset_timeout + 5'h1; 299 | end 300 | else begin 301 | w_reset <= 0; 302 | end 303 | end 304 | end 305 | 306 | always @ (posedge read_clock) begin 307 | if (reset) begin 308 | r_reset <= 1; 309 | r_reset_timeout <= 0; 310 | end 311 | else begin 312 | if (r_reset && (r_reset_timeout < 5'h4)) begin 313 | r_reset_timeout <= r_reset_timeout + 5'h1; 314 | end 315 | else begin 316 | r_reset <= 0; 317 | end 318 | end 319 | end 320 | 321 | //---------------Write Side--------------- 322 | initial begin 323 | write_address = 0; 324 | 325 | wcc_read_ready = 2'b00; 326 | write_ready = 2'b00; 327 | w_reset = 1; 328 | w_reset_timeout = 0; 329 | write_enable = 0; 330 | r_wselect = 0; 331 | wcc_tie_select = 0; 332 | end 333 | 334 | always @ (posedge write_clock) begin 335 | if (reset) begin 336 | write_ready <= 0; 337 | end 338 | else if (ready) begin 339 | //Logic for the Write Enable 340 | if (write_activate[0] && write_strobe) begin 341 | write_ready[0] <= 1'b0; 342 | end 343 | if (write_activate[1] && write_strobe) begin 344 | write_ready[1] <= 1'b0; 345 | end 346 | 347 | if (!write_activate[0] && (w_count[0] == 0) && wcc_read_done[0]) begin 348 | //FIFO 0 is not accessed by the read side, and user has not activated 349 | write_ready[0] <= 1; 350 | end 351 | if (!write_activate[1] && (w_count[1] == 0) && wcc_read_done[1]) begin 352 | //FIFO 1 is not accessed by the read side, and user has not activated 353 | write_ready[1] <= 1; 354 | end 355 | 356 | //When the user is finished reading the ready signal might go high 357 | //I SHOULD MAKE A CONDITION WHERE THE WRITE COUND MUST BE 0 358 | end 359 | end 360 | 361 | always @ (posedge write_clock) begin 362 | if (reset) begin //Asynchronous Reset 363 | wcc_read_ready <= 2'b00; 364 | write_address <= 0; 365 | w_count[0] <= 24'h0; 366 | w_count[1] <= 24'h0; 367 | end 368 | else begin 369 | if (write_activate > 0 && write_strobe) begin 370 | write_address <= write_address + 1; 371 | if (write_activate[0]) begin 372 | w_count[0] <= w_count[0] + 1; 373 | end 374 | if (write_activate[1]) begin 375 | w_count[1] <= w_count[1] + 1; 376 | end 377 | end 378 | if (write_activate == 0) begin 379 | write_address <= 0; 380 | if (w_count[0] > 0) begin 381 | wcc_read_ready[0] <= 1; 382 | end 383 | if (w_count[1] > 0) begin 384 | wcc_read_ready[1] <= 1; 385 | end 386 | end 387 | //I can't reset the w_count until the read side has indicated that it 388 | //is done but that may be mistriggered when the write side finishes 389 | //a write. So how do 390 | 391 | //Only reset the write count when the done signal has been de-asserted 392 | 393 | //Deassert w_readX_ready when we see the read has de-asserted r_readX_done 394 | if (!wcc_read_done[0]) begin 395 | //Only reset write count when the read side has said it's busy so it 396 | //must be done reading 397 | w_count[0] <= 0; 398 | wcc_read_ready[0] <= 0; 399 | end 400 | if (!wcc_read_done[1]) begin 401 | w_count[1] <= 0; 402 | wcc_read_ready[1] <= 0; 403 | end 404 | end 405 | end 406 | 407 | 408 | //---------------Read Side--------------- 409 | initial begin 410 | r_rselect = 0; 411 | r_address = 0; 412 | 413 | rcc_read_done = 2'b00; 414 | 415 | r_size[0] = 24'h0; 416 | r_size[1] = 24'h0; 417 | 418 | r_wait = 2'b11; 419 | r_ready = 2'b00; 420 | r_activate = 2'b00; 421 | r_pre_activate = 0; 422 | 423 | r_next_fifo = 0; 424 | r_reset = 1; 425 | r_reset_timeout = 0; 426 | read_ready = 0; 427 | r_read_data = 32'h0; 428 | r_pre_read_wait = 0; 429 | end 430 | 431 | always @ (posedge read_clock) begin 432 | if (reset) begin //Asynchronous Reset 433 | r_rselect <= 0; 434 | r_address <= 0; 435 | rcc_read_done <= 2'b11; 436 | read_count <= 0; 437 | r_activate <= 2'b00; 438 | r_pre_activate <= 2'b00; 439 | r_pre_read_wait <= 0; 440 | 441 | r_size[0] <= 24'h0; 442 | r_size[1] <= 24'h0; 443 | 444 | //are these signals redundant?? can I just use the done? 445 | r_wait <= 2'b11; 446 | 447 | r_ready <= 2'b00; 448 | 449 | r_next_fifo <= 0; 450 | r_read_data <= 0; 451 | 452 | end 453 | else begin 454 | r_pre_strobe <= read_strobe; 455 | //Handle user enable and ready 456 | if (!read_activate && !r_pre_activate) begin 457 | //User has not activated the read side 458 | 459 | //Prepare the read side 460 | //Reset the address 461 | if (r_activate == 0) begin 462 | read_count <= 0; 463 | r_address <= 0; 464 | r_pre_read_wait <= 0; 465 | if (r_ready > 0) begin 466 | //This goes to one instead of activate 467 | //Output select 468 | //read_ready <= 1; 469 | 470 | if (r_ready[0] && r_ready[1]) begin 471 | //$display ("Tie"); 472 | //Tie 473 | r_rselect <= r_next_fifo; 474 | r_pre_activate[r_next_fifo] <= 1; 475 | r_pre_activate[~r_next_fifo]<= 0; 476 | r_next_fifo <= ~r_next_fifo; 477 | read_count <= r_size[r_next_fifo]; 478 | end 479 | else begin 480 | //Only one side is ready 481 | if (r_ready[0]) begin 482 | //$display ("select 0"); 483 | r_rselect <= 0; 484 | r_pre_activate[0] <= 1; 485 | r_pre_activate[1] <= 0; 486 | r_next_fifo <= 1; 487 | read_count <= r_size[0]; 488 | end 489 | else begin 490 | //$display ("select 1"); 491 | r_rselect <= 1; 492 | r_pre_activate[0] <= 0; 493 | r_pre_activate[1] <= 1; 494 | r_next_fifo <= 0; 495 | read_count <= r_size[1]; 496 | end 497 | end 498 | end 499 | end 500 | //User has finished reading something 501 | else if (r_activate[r_rselect] && !r_ready[r_rselect]) begin 502 | r_activate[r_rselect] <= 0; 503 | rcc_read_done[r_rselect] <= 1; 504 | end 505 | end 506 | else begin 507 | if ((r_pre_activate > 0) && r_pre_read_wait) begin 508 | read_ready <= 1; 509 | end 510 | 511 | 512 | if (r_activate) begin 513 | read_ready <= 0; 514 | //User is requesting an accss 515 | //Handle read strobes 516 | //Only handle strobes when we are enabled 517 | r_ready[r_rselect] <= 0; 518 | end 519 | 520 | //XXX: There should be a better way to handle these edge conditions 521 | if (!r_activate && r_pre_read_wait) begin 522 | r_read_data <= w_read_data; 523 | r_address <= r_address + 1; 524 | r_activate <= r_pre_activate; 525 | r_pre_activate <= 0; 526 | end 527 | else if (!r_pre_read_wait) begin 528 | r_pre_read_wait <= 1; 529 | end 530 | 531 | if (read_strobe && (r_address < (r_size[r_rselect] + 1))) begin 532 | //Increment the address 533 | r_read_data <= w_read_data; 534 | r_address <= r_address + 1; 535 | end 536 | if (r_pre_strobe && !read_strobe) begin 537 | r_read_data <= w_read_data; 538 | end 539 | end 540 | if (!rcc_read_ready[0] && !r_ready[0] && !r_activate[0]) begin 541 | r_wait[0] <= 1; 542 | end 543 | if (!rcc_read_ready[1] && !r_ready[1] && !r_activate[1]) begin 544 | r_wait[1] <= 1; 545 | end 546 | 547 | 548 | //Check if the write side has sent over some data 549 | if (rcc_read_ready > 0) begin 550 | if ((r_wait == 2'b11) && (rcc_read_ready == 2'b11) && (w_count[0] > 0) && (w_count[1] > 0)) begin 551 | //An ambiguous sitution that can arrise if the read side is much 552 | //slower than the write side, both sides can arrise seemingly at the 553 | //same time, so there needs to be a tie breaker 554 | //$display ("Combo breaker goes to: %h", rcc_tie_select); 555 | r_next_fifo <= rcc_tie_select; 556 | end 557 | 558 | 559 | 560 | 561 | if (r_wait[0] && rcc_read_ready[0]) begin 562 | //$display ("write has send over data t othe 0 side"); 563 | //Normally it would not be cool to transfer data over a clock domain 564 | //but the w_count[x] is stable before the write side sends over a write 565 | //strobe 566 | if (w_count[0] > 0) begin 567 | //Only enable when count > 0 568 | if ((r_activate == 0) && (!rcc_read_ready[1])) begin 569 | //$display ("select 0"); 570 | r_next_fifo <= 0; 571 | end 572 | r_size[0] <= w_count[0]; 573 | r_ready[0] <= 1; 574 | r_wait[0] <= 0; 575 | rcc_read_done[0] <= 0; 576 | end 577 | end 578 | 579 | if (r_wait[1] && rcc_read_ready[1]) begin 580 | //$display ("write has send over data t othe 1 side"); 581 | //Write side has sent some data over 582 | if (w_count[1] > 0) begin 583 | if ((r_activate == 0) && (!rcc_read_ready[0])) begin 584 | //$display ("select 1"); 585 | r_next_fifo <= 1; 586 | end 587 | r_size[1] <= w_count[1]; 588 | r_ready[1] <= 1; 589 | r_wait[1] <= 0; 590 | rcc_read_done[1] <= 0; 591 | end 592 | end 593 | end 594 | end 595 | end 596 | 597 | endmodule 598 | -------------------------------------------------------------------------------- /rtl/ppfifo_sink.v: -------------------------------------------------------------------------------- 1 | /* Module: ppfifo_sink 2 | * 3 | * Description: Whenever data is available within the FIFO activate it and read it all 4 | */ 5 | 6 | module ppfifo_sink #( 7 | parameter DATA_WIDTH = 8 8 | )( 9 | input clk, 10 | input rst, 11 | 12 | //Ping Pong FIFO Interface 13 | input i_rd_rdy, 14 | output reg o_rd_act, 15 | input [23:0] i_rd_size, 16 | output reg o_rd_stb, 17 | input [DATA_WIDTH - 1:0] i_rd_data 18 | ); 19 | 20 | //Local Parameters 21 | //Registers/Wires 22 | reg [23:0] r_count; 23 | //Submodules 24 | //Asynchronous Logic 25 | //Synchronous Logic 26 | always @ (posedge clk) begin 27 | //De-Assert Strobes 28 | o_rd_stb <= 0; 29 | 30 | if (rst) begin 31 | o_rd_act <= 0; 32 | r_count <= 0; 33 | o_rd_stb <= 0; 34 | end 35 | else begin 36 | if (i_rd_rdy && !o_rd_act) begin 37 | r_count <= 0; 38 | o_rd_act <= 1; 39 | end 40 | else if (o_rd_act) begin 41 | if (r_count < i_rd_size) begin 42 | o_rd_stb <= 1; 43 | r_count <= r_count + 1; 44 | end 45 | else begin 46 | o_rd_act <= 0; 47 | end 48 | end 49 | end 50 | end 51 | 52 | 53 | endmodule 54 | -------------------------------------------------------------------------------- /rtl/ppfifo_source.v: -------------------------------------------------------------------------------- 1 | /* Module: ppfifo_source 2 | * 3 | * Description: Populate a Ping Pong FIFO with an incrementing number pattern 4 | */ 5 | 6 | module ppfifo_source #( 7 | parameter DATA_WIDTH = 8 8 | )( 9 | input clk, 10 | input rst, 11 | input i_enable, 12 | 13 | //Ping Pong FIFO Interface 14 | input [1:0] i_wr_rdy, 15 | output reg [1:0] o_wr_act, 16 | input [23:0] i_wr_size, 17 | output reg o_wr_stb, 18 | output reg [DATA_WIDTH - 1:0] o_wr_data 19 | ); 20 | 21 | //Local Parameters 22 | //Registers/Wires 23 | reg [23:0] r_count; 24 | //Submodules 25 | //Asynchronous Logic 26 | //Synchronous Logic 27 | always @ (posedge clk) begin 28 | //De-assert Strobes 29 | o_wr_stb <= 0; 30 | 31 | if (rst) begin 32 | o_wr_act <= 0; 33 | o_wr_stb <= 0; 34 | o_wr_data <= 0; 35 | r_count <= 0; 36 | end 37 | else begin 38 | if (i_enable) begin 39 | if ((i_wr_rdy > 0) && (o_wr_act == 0))begin 40 | r_count <= 0; 41 | if (i_wr_rdy[0]) begin 42 | //Channel 0 is open 43 | o_wr_act[0] <= 1; 44 | end 45 | else begin 46 | //Channel 1 is open 47 | o_wr_act[1] <= 1; 48 | end 49 | end 50 | else if (o_wr_act > 0) begin 51 | if (r_count < i_wr_size) begin 52 | //More room left in the buffer 53 | r_count <= r_count + 1; 54 | o_wr_stb <= 1; 55 | //put the count in the data 56 | o_wr_data <= r_count; 57 | end 58 | else begin 59 | //Filled up the buffer, release it 60 | o_wr_act <= 0; 61 | end 62 | end 63 | end 64 | end 65 | end 66 | 67 | endmodule 68 | -------------------------------------------------------------------------------- /sim/tb_ppfifo.v: -------------------------------------------------------------------------------- 1 | /* 2 | Distributed under the MIT license. 3 | Copyright (c) 2015 Dave McCoy (dave.mccoy@cospandesign.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | */ 23 | 24 | /* 25 | * Author: David McCoy (dave.mccoy@cospandesign.com) 26 | * Description: 27 | * Test Bench for PPFIFO: 28 | * Demonstrates reading and writing to/from a PPFIFO 29 | * 30 | * Changes: 31 | */ 32 | 33 | `timescale 1ns/1ps 34 | 35 | `define CLK_HALF_PERIOD 10 36 | `define CLK_PERIOD (2 * `CLK_HALF_PERIOD) 37 | 38 | `define SLEEP_HALF_CLK #(`CLK_HALF_PERIOD) 39 | `define SLEEP_FULL_CLK #(`CLK_PERIOD) 40 | 41 | //Sleep a number of clock cycles 42 | `define SLEEP_CLK(x) #(x * `CLK_PERIOD) 43 | 44 | 45 | 46 | /* 47 | * CHANGE THIS VALUE TO TEST CLOCK RATES 48 | * NOTE: BE AWARE THAT YOU MIGHT NEED TO ADJUST THE RESET SLEEP PERIOD 49 | */ 50 | 51 | `define RD_CLK_HALF_PERIOD 10 52 | `define RD_CLK_PERIOD (2 * `RD_CLK_HALF_PERIOD) 53 | 54 | 55 | 56 | module tb_ppfifo ( 57 | ); 58 | //local parameters 59 | localparam DATA_WIDTH = 32; //32-bit data 60 | localparam ADDR_WIDTH = 4; //2 ** 4 = 16 positions 61 | 62 | //registes/wires 63 | reg clk = 0; 64 | reg rd_clk = 0; 65 | 66 | reg rst = 0; 67 | 68 | 69 | //write side 70 | wire [1:0] write_ready; 71 | wire [1:0] write_activate; 72 | wire [23:0] write_fifo_size; 73 | wire write_strobe; 74 | wire [DATA_WIDTH - 1: 0] write_data; 75 | wire starved; 76 | 77 | //read side 78 | wire read_strobe; 79 | wire read_ready; 80 | wire read_activate; 81 | wire [23:0] read_count; 82 | wire [DATA_WIDTH - 1: 0] read_data; 83 | 84 | wire inactive; 85 | 86 | 87 | 88 | //submodules 89 | 90 | //Write Side 91 | ppfifo_source #( 92 | .DATA_WIDTH (DATA_WIDTH ) 93 | ) source ( 94 | .clk (clk ), 95 | .rst (rst ), 96 | .i_enable (1'b1 ), 97 | 98 | //Ping Pong FIFO Interface 99 | .i_wr_rdy (write_ready ), 100 | .o_wr_act (write_activate ), 101 | .i_wr_size (write_fifo_size ), 102 | .o_wr_stb (write_strobe ), 103 | .o_wr_data (write_data ) 104 | ); 105 | 106 | //PPFIFO 107 | ppfifo #( 108 | .DATA_WIDTH (DATA_WIDTH ), 109 | .ADDRESS_WIDTH (ADDR_WIDTH ) 110 | ) f ( 111 | 112 | //universal input 113 | .reset (rst ), 114 | 115 | //write side 116 | .write_clock (clk ), 117 | .write_ready (write_ready ), 118 | .write_activate (write_activate ), 119 | .write_fifo_size (write_fifo_size ), 120 | .write_strobe (write_strobe ), 121 | .write_data (write_data ), 122 | .starved (starved ), 123 | 124 | //read side 125 | .read_clock (rd_clk ), 126 | .read_strobe (read_strobe ), 127 | .read_ready (read_ready ), 128 | .read_activate (read_activate ), 129 | .read_count (read_count ), 130 | .read_data (read_data ), 131 | 132 | .inactive (inactive ) 133 | ); 134 | 135 | //Read Side 136 | ppfifo_sink #( 137 | .DATA_WIDTH (DATA_WIDTH ) 138 | ) sink ( 139 | .clk (rd_clk ), 140 | .rst (rst ), 141 | 142 | //Ping Pong FIFO Interface 143 | .i_rd_rdy (read_ready ), 144 | .o_rd_act (read_activate ), 145 | .i_rd_size (read_count ), 146 | .o_rd_stb (read_strobe ), 147 | .i_rd_data (read_data ) 148 | ); 149 | 150 | 151 | //asynchronous logic 152 | //synchronous logic 153 | 154 | always #`CLK_HALF_PERIOD clk = ~clk; 155 | always #`RD_CLK_HALF_PERIOD rd_clk = ~rd_clk; 156 | 157 | initial begin 158 | $dumpfile ("design.vcd"); 159 | $dumpvars(0, tb_ppfifo); 160 | 161 | 162 | rst <= 0; 163 | `SLEEP_CLK(10); 164 | rst <= 1; 165 | `SLEEP_CLK(10); 166 | rst <= 0; 167 | end 168 | 169 | endmodule 170 | --------------------------------------------------------------------------------