├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── LICENSE ├── README.md ├── async_fifo.core ├── doc ├── specification.rst └── testplan.rst ├── flow.sh ├── rtl ├── async_bidir_fifo.list ├── async_bidir_fifo.v ├── async_bidir_ramif_fifo.list ├── async_bidir_ramif_fifo.v ├── async_fifo.list ├── async_fifo.v ├── fifomem.v ├── fifomem_dp.v ├── rptr_empty.v ├── sync_ptr.v ├── sync_r2w.v ├── sync_w2r.v └── wptr_full.v ├── script └── setup.sh ├── sim ├── Makefile ├── async_fifo_unit_test.sv ├── files.f ├── svut_h.sv └── wave.gtkw └── syn ├── cmos.lib ├── fifo.ys ├── syn_asic.sh └── vsclib013.lib /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: Async_FIFO 2 | on: [push] 3 | jobs: 4 | Lint: 5 | runs-on: macos-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - run: brew install verilator 9 | - run: ./flow.sh lint 10 | Simulation: 11 | runs-on: macos-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - run: brew install verilator 15 | - run: brew install icarus-verilog 16 | - run: ./flow.sh sim 17 | Synthesis: 18 | runs-on: macos-latest 19 | steps: 20 | - uses: actions/checkout@v2 21 | - run: brew install yosys 22 | - run: ./flow.sh syn 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.out 2 | *.vcd 3 | *.lxt 4 | .DS_Store 5 | tags 6 | lint.log script src 7 | async_fifo_syn.v 8 | lint.log 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 5 | associated documentation files (the "Software"), to deal in the Software without restriction, 6 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 7 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial 11 | portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 14 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 15 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 16 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 17 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. limitations under the License. 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Asynchronous dual clock FIFO 2 | 3 | ![CI](https://github.com/dpretet/async_fifo/actions/workflows/ci.yaml/badge.svg?branch=master) 4 | [![GitHub issues](https://img.shields.io/github/issues/dpretet/async_fifo)](https://github.com/dpretet/async_fifo/issues) 5 | [![GitHub forks](https://img.shields.io/github/forks/dpretet/async_fifo)](https://github.com/dpretet/async_fifo/network) 6 | [![GitHub stars](https://img.shields.io/github/stars/dpretet/async_fifo)](https://github.com/dpretet/async_fifo/stargazers) 7 | [![GitHub license](https://img.shields.io/github/license/dpretet/async_fifo)](https://github.com/dpretet/async_fifo/blob/master/LICENSE) 8 | 9 | # Overview 10 | 11 | This repository stores a verilog description of dual clock FIFO. A FIFO is 12 | a convenient circuit to exchange data between two clock domains. It manages 13 | the RAM addressing internally, the clock domain crossing and informs the user 14 | of the FIFO fillness with "full" and "empty" flags. 15 | 16 | It is widely inspired by the excellent article from Clifford Cummings, 17 | [Simulation and Synthesis Techniques for Asynchronous FIFO 18 | Design](http://www.sunburst-design.com/papers/CummingsSNUG2002SJ_FIFO1.pdf). 19 | 20 | The simulation testcases available use [Icarus Verilog](http://iverilog.icarus.com) and [SVUT](https://github.com/dpretet/svut) tool to run the tests. 21 | 22 | The FIFO is fully functional and used in many successful projects. 23 | 24 | # Usage 25 | 26 | RTL sources are present in RTL folder under three flavors: 27 | - `rtl/async_fifo.v`: a basic asynchronous dual-clock FIFO 28 | - `rtl/async_bidir_fifo.v`: two instance of the first one into a single top level for full-duplex channel 29 | - `rtl/async_bidir_ramif_fifo.v`: same than previous but with external RAM 30 | 31 | The three FIFOs have a list file to get the associated fileset. 32 | 33 | The testbench in `sim/` provides an example about the instance and the configuration. 34 | 35 | All three top levels have the same parameters: 36 | - `DSIZE`: the size in bits of the datapath 37 | - `ASIZE`: the size in bits of the internal RAM address bus. This implies the FIFO can be configured only with power of 2 depth 38 | - `FALLTHROUGH`: allow to reduce the inner latency and propagate faster the data through the FIFO 39 | 40 | 41 | # License 42 | 43 | Permission is hereby granted, free of charge, to any person obtaining a copy of 44 | this software and associated documentation files (the "Software"), to deal in 45 | the Software without restriction, including without limitation the rights to 46 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 47 | of the Software, and to permit persons to whom the Software is furnished to do 48 | so, subject to the following conditions: 49 | 50 | The above copyright notice and this permission notice shall be included in all 51 | copies or substantial portions of the Software. 52 | 53 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 54 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 55 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 56 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 57 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 58 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 59 | SOFTWARE. imitations under the License. 60 | -------------------------------------------------------------------------------- /async_fifo.core: -------------------------------------------------------------------------------- 1 | CAPI=2: 2 | 3 | name : ::async_fifo:1.1.0-r1 4 | 5 | filesets: 6 | rtl: 7 | files: 8 | - src/vlog/sync_r2w.v 9 | - src/vlog/rptr_empty.v 10 | - src/vlog/sync_w2r.v 11 | - src/vlog/wptr_full.v 12 | - src/vlog/fifo_2mem.v 13 | - src/vlog/async_fifo.v 14 | - src/vlog/sync_ptr.v 15 | file_type : verilogSource 16 | tb: 17 | files: 18 | - sim/test/async_fifo_unit_test.sv : {file_type : systemVerilogSource} 19 | depend : [svut, vlog_tb_utils] 20 | 21 | targets: 22 | default: {filesets : [rtl]} 23 | 24 | sim: 25 | default_tool : icarus 26 | filesets: [rtl, tb] 27 | parameters: [USE_VLOG_TB_UTILS=true] 28 | tools: 29 | icarus: 30 | iverilog_options: [-g2012] 31 | modelsim: 32 | vlog_options: [-timescale=1ns/1ps] 33 | xsim: 34 | xelab_options: [--timescale, 1ns/1ns] 35 | toplevel : async_fifo_unit_test 36 | 37 | parameters: 38 | USE_VLOG_TB_UTILS: 39 | datatype: bool 40 | description: Use vlog_tb_utils for VCD and timeout handling 41 | paramtype: vlogdefine 42 | 43 | provider: 44 | name : github 45 | user : dpretet 46 | repo : async_fifo 47 | version : v1.0.0 48 | -------------------------------------------------------------------------------- /doc/specification.rst: -------------------------------------------------------------------------------- 1 | Specification 2 | ============= 3 | 4 | * The module is a full synchronized module, working on clock rising edge. 5 | 6 | * It can be put under reset on both sides. For proper behavior, both sides 7 | have to be reset at the same time before any usage. 8 | 9 | * It can be synthetized either for ASIC or FPGAs. 10 | 11 | * It can be configured for any data bus width, specified in bits. 12 | 13 | * Its depth can be configured in bytes. 14 | 15 | * If the FIFO depth is not modulo the datapath, the real FIFO depth infered 16 | is round up to the next datapath width. For instance, if the datapath width 17 | is 16 bytes and the depth specified being 20 bytes, the effective FIFO size 18 | will be 32 bytes 19 | 20 | * If the depth is modulo the datapath, the specified depth will be the 21 | effective depth 22 | 23 | 24 | * Write interface usage: 25 | 26 | * A write enable control (`wren`), enabling the data recording. This control 27 | increments the write pointer to point the next RAM address to write. `wren` 28 | doesn't have to be asserted when "full" flag is asserted. The word passed 29 | to the write side will be losted. `wren` can be asserted continuously, or 30 | occasionally. 31 | 32 | * A full flag is asserted when the FIFO is full. The flag is asserted on the 33 | next clock cycle the last available word has been written if no data has 34 | been read. 35 | 36 | * Read interface usage: 37 | 38 | * A read enable control (`rden`), enabling the data read. This control 39 | increments the read pointer to address the next word to read. `rden` 40 | doesn't have to be asserted when empty flag is enabled. If asserted, the 41 | data under read can be a valid data. `rden` can be asserted continuously, 42 | or occasionally. 43 | 44 | * An empty flag is asserted when the FIFO is empty. The flag is asserted on 45 | the next clock cycle last available word has been read if no further info 46 | has been stored. 47 | -------------------------------------------------------------------------------- /doc/testplan.rst: -------------------------------------------------------------------------------- 1 | Testplan 2 | ======== 3 | 4 | This document describes the faetures to test, 5 | and the general scenarios to put in place to stress 6 | the IP behavior: 7 | 8 | Clock frequency relationship 9 | ---------------------------- 10 | 11 | A first focus is put in write vs read frequency relation: 12 | 13 | 1. Test the same clock frequency on both sides. Phases can be equal or not. 14 | 15 | 2. Test slower clock on write side 16 | - clock can be close to read frequency, but slower 17 | - clock can be very regarding read frequency 18 | 19 | 3. Test higher clock on write side 20 | - clock can be close to read frequency, but higher 21 | - clock can be very high regarding read frequency 22 | 23 | Read/Write enable control assertion 24 | ----------------------------------- 25 | 26 | A second focus is put on read/write enable assertion: 27 | 28 | 1. Read enable is always enable, unless empty = 1 29 | - Write enable is always asserted, data are not corrupted 30 | - Write enable can be occasionaly asserted, data are not corrupted 31 | 32 | 2. Write enable is always enable, unless full = 1 33 | - Read enable is always asserted, data are not corrupted 34 | - Read enable can be occasionaly asserted, data are not corrupted 35 | 36 | 3. Read and Write enable can be occasionaly asserted 37 | - Assertion frequency (either read or write) is periodic (1/2, 1/3, 1/2, ...) 38 | - Assertion frequecy is (pseudo) random 39 | 40 | Test coverage 41 | ------------- 42 | 43 | To ensure a wide feature feature coverage is performed, both clock frequency scale and 44 | read/write enable assertions have to be tested together. Big range over higher frequency 45 | scale factor doesn't have to considered. Only few combinations can be tested for 46 | good confidence on the IP behavior. 47 | -------------------------------------------------------------------------------- /flow.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # -e: exit if one command fails 4 | # -u: treat unset variable as an error 5 | # -f: disable filename expansion upon seeing *, ?, ... 6 | # -o pipefail: causes a pipeline to fail if any command fails 7 | set -e -o pipefail 8 | 9 | # Current script path; doesn't support symlink 10 | FIFO_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 11 | 12 | 13 | # Bash color codes 14 | Red='\033[0;31m' 15 | Green='\033[0;32m' 16 | Yellow='\033[0;33m' 17 | Blue='\033[0;34m' 18 | # Reset 19 | NC='\033[0m' 20 | 21 | function printerror { 22 | echo -e "${Red}ERROR: ${1}${NC}" 23 | } 24 | 25 | function printwarning { 26 | echo -e "${Yellow}WARNING: ${1}${NC}" 27 | } 28 | 29 | function printinfo { 30 | echo -e "${Blue}INFO: ${1}${NC}" 31 | } 32 | 33 | function printsuccess { 34 | echo -e "${Green}SUCCESS: ${1}${NC}" 35 | } 36 | 37 | help() { 38 | echo -e "${Blue}" 39 | echo "" 40 | echo "NAME" 41 | echo "" 42 | echo " Async FIFO Flow" 43 | echo "" 44 | echo "SYNOPSIS" 45 | echo "" 46 | echo " ./flow.sh -h" 47 | echo "" 48 | echo " ./flow.sh help" 49 | echo "" 50 | echo " ./flow.sh syn" 51 | echo "" 52 | echo " ./flow.sh sim" 53 | echo "" 54 | echo "DESCRIPTION" 55 | echo "" 56 | echo " This flow handles the different operations available" 57 | echo "" 58 | echo " ./flow.sh help|-h" 59 | echo "" 60 | echo " Print the help menu" 61 | echo "" 62 | echo " ./flow.sh syn" 63 | echo "" 64 | echo " Launch the synthesis script relying on Yosys" 65 | echo "" 66 | echo " ./flow.sh sim" 67 | echo -e "${NC}" 68 | } 69 | 70 | 71 | run_sims() { 72 | printinfo "Start simulation" 73 | cd "$FIFO_DIR"/sim 74 | svutRun -f files.f -test async_fifo_unit_test.sv -sim icarus 75 | return $? 76 | } 77 | 78 | run_syn() { 79 | printinfo "Start synthesis" 80 | cd "$FIFO_DIR/syn" 81 | ./syn_asic.sh 82 | return $? 83 | } 84 | 85 | 86 | run_lint() { 87 | set +e 88 | 89 | printinfo "Start lint" 90 | verilator --lint-only +1800-2017ext+sv \ 91 | -Wall -Wpedantic \ 92 | -Wno-VARHIDDEN \ 93 | -Wno-PINCONNECTEMPTY \ 94 | -Wno-PINMISSING \ 95 | ./rtl/async_fifo.v \ 96 | ./rtl/fifomem.v \ 97 | ./rtl/rptr_empty.v \ 98 | ./rtl/sync_r2w.v \ 99 | ./rtl/sync_w2r.v \ 100 | ./rtl/wptr_full.v \ 101 | --top-module async_fifo 2> lint.log 102 | 103 | set -e 104 | 105 | ec=$(grep -c "%Error:" lint.log) 106 | 107 | if [[ $ec -gt 1 ]]; then 108 | printerror "Lint failed, check ./lint.log for further details" 109 | return 1 110 | else 111 | printsuccess "Lint ran successfully" 112 | return 0 113 | fi 114 | 115 | } 116 | 117 | check_setup() { 118 | 119 | source script/setup.sh 120 | 121 | if [[ ! $(type iverilog) ]]; then 122 | printerror "Icarus-Verilog is not installed" 123 | exit 1 124 | fi 125 | if [[ ! $(type verilator) ]]; then 126 | printerror "Verilator is not installed" 127 | exit 1 128 | fi 129 | } 130 | 131 | 132 | main() { 133 | 134 | echo "" 135 | printinfo "Start Aync FIFO Flow" 136 | 137 | # If no argument provided, preint help and exit 138 | if [[ $# -eq 0 ]]; then 139 | help 140 | exit 1 141 | fi 142 | 143 | # Print help 144 | if [[ $1 == "-h" || $1 == "help" ]]; then 145 | help 146 | exit 0 147 | fi 148 | 149 | if [[ $1 == "lint" ]]; then 150 | run_lint 151 | exit $? 152 | fi 153 | 154 | if [[ $1 == "sim" ]]; then 155 | check_setup 156 | run_sims 157 | exit $? 158 | fi 159 | 160 | if [[ $1 == "syn" ]]; then 161 | run_syn 162 | return $? 163 | fi 164 | } 165 | 166 | 167 | main "$@" 168 | -------------------------------------------------------------------------------- /rtl/async_bidir_fifo.list: -------------------------------------------------------------------------------- 1 | async_bidir_fifo.v 2 | fifomem.v 3 | fifomem_dp.v 4 | rptr_empty.v 5 | sync_ptr.v 6 | sync_r2w.v 7 | sync_w2r.v 8 | wptr_full.v 9 | -------------------------------------------------------------------------------- /rtl/async_bidir_fifo.v: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // Copyright 2017 Damien Pretet ThotIP 3 | // Copyright 2018 Julius Baxter 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | //----------------------------------------------------------------------------- 17 | 18 | `timescale 1 ns / 1 ps 19 | `default_nettype none 20 | 21 | module async_bidir_fifo 22 | 23 | #( 24 | parameter DSIZE = 8, 25 | parameter ASIZE = 4, 26 | parameter FALLTHROUGH = "TRUE" // First word fall-through 27 | ) ( 28 | input wire a_clk, 29 | input wire a_rst_n, 30 | input wire a_winc, 31 | input wire [DSIZE-1:0] a_wdata, 32 | input wire a_rinc, 33 | output wire [DSIZE-1:0] a_rdata, 34 | output wire a_full, 35 | output wire a_afull, 36 | output wire a_empty, 37 | output wire a_aempty, 38 | input wire a_dir, // dir = 1: this side is writing, dir = 0: this side is reading 39 | 40 | 41 | input wire b_clk, 42 | input wire b_rst_n, 43 | input wire b_winc, 44 | input wire [DSIZE-1:0] b_wdata, 45 | input wire b_rinc, 46 | output wire [DSIZE-1:0] b_rdata, 47 | output wire b_full, 48 | output wire b_afull, 49 | output wire b_empty, 50 | output wire b_aempty, 51 | input wire b_dir // dir = 1: this side is writing, dir = 0: this side is reading 52 | ); 53 | 54 | wire [ASIZE-1:0] a_addr, b_addr; 55 | wire [ASIZE-1:0] a_waddr, a_raddr, b_waddr, b_raddr; 56 | wire [ ASIZE:0] a_wptr, b_rptr, a2b_wptr, b2a_rptr; 57 | wire [ ASIZE:0] a_rptr, b_wptr, a2b_rptr, b2a_wptr; 58 | 59 | assign a_addr = a_dir ? a_waddr : a_raddr; 60 | assign b_addr = b_dir ? b_waddr : b_raddr; 61 | 62 | ////////////////////////////////////////////////////////////////////////////// 63 | // A-side logic 64 | ////////////////////////////////////////////////////////////////////////////// 65 | 66 | // Sync b write pointer to a domain 67 | sync_ptr #(ASIZE) 68 | sync_b2a_wptr 69 | ( 70 | .dest_clk (a_clk), 71 | .dest_rst_n (a_rst_n), 72 | .src_ptr (b_wptr), 73 | .dest_ptr (b2a_wptr) 74 | ); 75 | 76 | // Sync b read pointer to a domain 77 | sync_ptr #(ASIZE) 78 | sync_b2a_rptr 79 | ( 80 | .dest_clk (a_clk), 81 | .dest_rst_n (a_rst_n), 82 | .src_ptr (b_rptr), 83 | .dest_ptr (b2a_rptr) 84 | ); 85 | 86 | // The module handling the write requests 87 | // outputs valid when dir == 0 (a is writing) 88 | wptr_full #(ASIZE) 89 | a_wptr_inst 90 | ( 91 | .wclk (a_clk), 92 | .wrst_n (a_rst_n), 93 | .winc (a_winc), 94 | .wq2_rptr (b2a_rptr), 95 | .awfull (a_afull), 96 | .wfull (a_full), 97 | .waddr (a_waddr), 98 | .wptr (a_wptr) 99 | ); 100 | 101 | // dir == 1 read pointer on a side calculation 102 | rptr_empty #(ASIZE) 103 | a_rptr_inst 104 | ( 105 | .rclk (a_clk), 106 | .rrst_n (a_rst_n), 107 | .rinc (a_rinc), 108 | .rq2_wptr (b2a_wptr), 109 | .arempty (a_aempty), 110 | .rempty (a_empty), 111 | .raddr (a_raddr), 112 | .rptr (a_rptr) 113 | ); 114 | 115 | ////////////////////////////////////////////////////////////////////////////// 116 | // B-side logic 117 | ////////////////////////////////////////////////////////////////////////////// 118 | 119 | // Sync a write pointer to b domain 120 | sync_ptr #(ASIZE) 121 | sync_a2b_wptr 122 | ( 123 | .dest_clk (b_clk), 124 | .dest_rst_n (b_rst_n), 125 | .src_ptr (a_wptr), 126 | .dest_ptr (a2b_wptr) 127 | ); 128 | 129 | // Sync a read pointer to b domain 130 | sync_ptr #(ASIZE) 131 | sync_a2b_rptr 132 | ( 133 | .dest_clk (b_clk), 134 | .dest_rst_n (b_rst_n), 135 | .src_ptr (a_rptr), 136 | .dest_ptr (a2b_rptr) 137 | ); 138 | 139 | // The module handling the write requests 140 | // outputs valid when dir == 0 (b is writing) 141 | wptr_full #(ASIZE) 142 | b_wptr_inst 143 | ( 144 | .wclk (b_clk), 145 | .wrst_n (b_rst_n), 146 | .winc (b_winc), 147 | .wq2_rptr (a2b_rptr), 148 | .awfull (b_afull), 149 | .wfull (b_full), 150 | .waddr (b_waddr), 151 | .wptr (b_wptr) 152 | ); 153 | 154 | // dir == 1 read pointer on b side calculation 155 | rptr_empty #(ASIZE) 156 | b_rptr_inst 157 | ( 158 | .rclk (b_clk), 159 | .rrst_n (b_rst_n), 160 | .rinc (b_rinc), 161 | .rq2_wptr (a2b_wptr), 162 | .arempty (b_aempty), 163 | .rempty (b_empty), 164 | .raddr (b_raddr), 165 | .rptr (b_rptr) 166 | ); 167 | 168 | ////////////////////////////////////////////////////////////////////////////// 169 | // FIFO RAM 170 | ////////////////////////////////////////////////////////////////////////////// 171 | 172 | fifomem_dp #(DSIZE, ASIZE, FALLTHROUGH) 173 | fifomem_dp 174 | ( 175 | .a_clk (a_clk), 176 | .a_wdata (a_wdata), 177 | .a_rdata (a_rdata), 178 | .a_addr (a_addr), 179 | .a_rinc (a_rinc & !a_dir), 180 | .a_winc (a_winc & a_dir), 181 | 182 | .b_clk (b_clk), 183 | .b_wdata (b_wdata), 184 | .b_rdata (b_rdata), 185 | .b_addr (b_addr), 186 | .b_rinc (b_rinc & !b_dir), 187 | .b_winc (b_winc & b_dir) 188 | ); 189 | 190 | 191 | 192 | endmodule 193 | 194 | `resetall 195 | -------------------------------------------------------------------------------- /rtl/async_bidir_ramif_fifo.list: -------------------------------------------------------------------------------- 1 | async_bidir_ramif_fifo.v 2 | fifomem.v 3 | fifomem_dp.v 4 | rptr_empty.v 5 | sync_ptr.v 6 | sync_r2w.v 7 | sync_w2r.v 8 | wptr_full.v 9 | -------------------------------------------------------------------------------- /rtl/async_bidir_ramif_fifo.v: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // Copyright 2017 Damien Pretet ThotIP 3 | // Copyright 2018 Julius Baxter 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | //----------------------------------------------------------------------------- 17 | 18 | `timescale 1 ns / 1 ps 19 | `default_nettype none 20 | 21 | module async_bidir_ramif_fifo 22 | 23 | #( 24 | parameter DSIZE = 8, 25 | parameter ASIZE = 4, 26 | parameter FALLTHROUGH = "FALSE" // First word fall-through, not sure it can be disabled for this 27 | ) ( 28 | input wire a_clk, 29 | input wire a_rst_n, 30 | input wire a_winc, 31 | input wire [DSIZE-1:0] a_wdata, 32 | input wire a_rinc, 33 | output wire [DSIZE-1:0] a_rdata, 34 | output wire a_full, 35 | output wire a_afull, 36 | output wire a_empty, 37 | output wire a_aempty, 38 | input wire a_dir, // dir = 1: this side is writing, dir = 0: this side is reading 39 | 40 | 41 | input wire b_clk, 42 | input wire b_rst_n, 43 | input wire b_winc, 44 | input wire [DSIZE-1:0] b_wdata, 45 | input wire b_rinc, 46 | output wire [DSIZE-1:0] b_rdata, 47 | output wire b_full, 48 | output wire b_afull, 49 | output wire b_empty, 50 | output wire b_aempty, 51 | input wire b_dir, // dir = 1: this side is writing, dir = 0: this side is reading 52 | 53 | // Dual-port RAM interface 54 | output wire o_ram_a_clk, 55 | output wire [DSIZE-1:0] o_ram_a_wdata, 56 | input wire [DSIZE-1:0] i_ram_a_rdata, 57 | output wire [ASIZE-1:0] o_ram_a_addr, 58 | output wire o_ram_a_rinc, 59 | output wire o_ram_a_winc, 60 | output wire o_ram_b_clk, 61 | output wire [DSIZE-1:0] o_ram_b_wdata, 62 | input wire [DSIZE-1:0] i_ram_b_rdata, 63 | output wire [ASIZE-1:0] o_ram_b_addr, 64 | output wire o_ram_b_rinc, 65 | output wire o_ram_b_winc 66 | ); 67 | 68 | wire [ASIZE-1:0] a_addr, b_addr; 69 | wire [ASIZE-1:0] a_waddr, a_raddr, b_waddr, b_raddr; 70 | wire [ ASIZE:0] a_wptr, b_rptr, a2b_wptr, b2a_rptr; 71 | wire [ ASIZE:0] a_rptr, b_wptr, a2b_rptr, b2a_wptr; 72 | 73 | assign a_addr = a_dir ? a_waddr : a_raddr; 74 | assign b_addr = b_dir ? b_waddr : b_raddr; 75 | 76 | ////////////////////////////////////////////////////////////////////////////// 77 | // A-side logic 78 | ////////////////////////////////////////////////////////////////////////////// 79 | 80 | // Sync b write pointer to a domain 81 | sync_ptr #(ASIZE) 82 | sync_b2a_wptr 83 | ( 84 | .dest_clk (a_clk), 85 | .dest_rst_n (a_rst_n), 86 | .src_ptr (b_wptr), 87 | .dest_ptr (b2a_wptr) 88 | ); 89 | 90 | // Sync b read pointer to a domain 91 | sync_ptr #(ASIZE) 92 | sync_b2a_rptr 93 | ( 94 | .dest_clk (a_clk), 95 | .dest_rst_n (a_rst_n), 96 | .src_ptr (b_rptr), 97 | .dest_ptr (b2a_rptr) 98 | ); 99 | 100 | // The module handling the write requests 101 | // outputs valid when dir == 0 (a is writing) 102 | wptr_full #(ASIZE) 103 | a_wptr_inst 104 | ( 105 | .wclk (a_clk), 106 | .wrst_n (a_rst_n), 107 | .winc (a_winc), 108 | .wq2_rptr (b2a_rptr), 109 | .awfull (a_afull), 110 | .wfull (a_full), 111 | .waddr (a_waddr), 112 | .wptr (a_wptr) 113 | ); 114 | 115 | // dir == 1 read pointer on a side calculation 116 | rptr_empty #(ASIZE) 117 | a_rptr_inst 118 | ( 119 | .rclk (a_clk), 120 | .rrst_n (a_rst_n), 121 | .rinc (a_rinc), 122 | .rq2_wptr (b2a_wptr), 123 | .arempty (a_aempty), 124 | .rempty (a_empty), 125 | .raddr (a_raddr), 126 | .rptr (a_rptr) 127 | ); 128 | 129 | ////////////////////////////////////////////////////////////////////////////// 130 | // B-side logic 131 | ////////////////////////////////////////////////////////////////////////////// 132 | 133 | // Sync a write pointer to b domain 134 | sync_ptr #(ASIZE) 135 | sync_a2b_wptr 136 | ( 137 | .dest_clk (b_clk), 138 | .dest_rst_n (b_rst_n), 139 | .src_ptr (a_wptr), 140 | .dest_ptr (a2b_wptr) 141 | ); 142 | 143 | // Sync a read pointer to b domain 144 | sync_ptr #(ASIZE) 145 | sync_a2b_rptr 146 | ( 147 | .dest_clk (b_clk), 148 | .dest_rst_n (b_rst_n), 149 | .src_ptr (a_rptr), 150 | .dest_ptr (a2b_rptr) 151 | ); 152 | 153 | // The module handling the write requests 154 | // outputs valid when dir == 0 (b is writing) 155 | wptr_full #(ASIZE) 156 | b_wptr_inst 157 | ( 158 | .wclk (b_clk), 159 | .wrst_n (b_rst_n), 160 | .winc (b_winc), 161 | .wq2_rptr (a2b_rptr), 162 | .awfull (b_afull), 163 | .wfull (b_full), 164 | .waddr (b_waddr), 165 | .wptr (b_wptr) 166 | ); 167 | 168 | // dir == 1 read pointer on b side calculation 169 | rptr_empty #(ASIZE) 170 | b_rptr_inst 171 | ( 172 | .rclk (b_clk), 173 | .rrst_n (b_rst_n), 174 | .rinc (b_rinc), 175 | .rq2_wptr (a2b_wptr), 176 | .arempty (b_aempty), 177 | .rempty (b_empty), 178 | .raddr (b_raddr), 179 | .rptr (b_rptr) 180 | ); 181 | 182 | ////////////////////////////////////////////////////////////////////////////// 183 | // FIFO RAM interface 184 | ////////////////////////////////////////////////////////////////////////////// 185 | 186 | assign o_ram_a_clk = a_clk; 187 | assign o_ram_a_wdata = a_wdata; 188 | assign a_rdata = i_ram_a_rdata; 189 | assign o_ram_a_addr = a_addr; 190 | assign o_ram_a_rinc = a_rinc & !a_dir; 191 | assign o_ram_a_winc = a_winc & a_dir; 192 | assign o_ram_b_clk = b_clk; 193 | assign o_ram_b_wdata = b_wdata; 194 | assign b_rdata = i_ram_b_rdata; 195 | assign o_ram_b_addr = b_addr; 196 | assign o_ram_b_rinc = b_rinc & !b_dir; 197 | assign o_ram_b_winc = b_winc & b_dir; 198 | 199 | endmodule 200 | 201 | `resetall 202 | -------------------------------------------------------------------------------- /rtl/async_fifo.list: -------------------------------------------------------------------------------- 1 | async_fifo.v 2 | fifomem.v 3 | fifomem_dp.v 4 | hdl.list 5 | rptr_empty.v 6 | sync_ptr.v 7 | sync_r2w.v 8 | sync_w2r.v 9 | wptr_full.v 10 | -------------------------------------------------------------------------------- /rtl/async_fifo.v: -------------------------------------------------------------------------------- 1 | // distributed under the mit license 2 | // https://opensource.org/licenses/mit-license.php 3 | 4 | `timescale 1 ns / 1 ps 5 | `default_nettype none 6 | 7 | module async_fifo 8 | 9 | #( 10 | parameter DSIZE = 8, 11 | parameter ASIZE = 4, 12 | parameter FALLTHROUGH = "TRUE" // First word fall-through without latency 13 | )( 14 | input wire wclk, 15 | input wire wrst_n, 16 | input wire winc, 17 | input wire [DSIZE-1:0] wdata, 18 | output wire wfull, 19 | output wire awfull, 20 | input wire rclk, 21 | input wire rrst_n, 22 | input wire rinc, 23 | output wire [DSIZE-1:0] rdata, 24 | output wire rempty, 25 | output wire arempty 26 | ); 27 | 28 | wire [ASIZE-1:0] waddr, raddr; 29 | wire [ASIZE :0] wptr, rptr, wq2_rptr, rq2_wptr; 30 | 31 | // The module synchronizing the read point 32 | // from read to write domain 33 | sync_r2w 34 | #(ASIZE) 35 | sync_r2w ( 36 | .wq2_rptr (wq2_rptr), 37 | .rptr (rptr), 38 | .wclk (wclk), 39 | .wrst_n (wrst_n) 40 | ); 41 | 42 | // The module synchronizing the write point 43 | // from write to read domain 44 | sync_w2r 45 | #(ASIZE) 46 | sync_w2r ( 47 | .rq2_wptr (rq2_wptr), 48 | .wptr (wptr), 49 | .rclk (rclk), 50 | .rrst_n (rrst_n) 51 | ); 52 | 53 | // The module handling the write requests 54 | wptr_full 55 | #(ASIZE) 56 | wptr_full ( 57 | .awfull (awfull), 58 | .wfull (wfull), 59 | .waddr (waddr), 60 | .wptr (wptr), 61 | .wq2_rptr (wq2_rptr), 62 | .winc (winc), 63 | .wclk (wclk), 64 | .wrst_n (wrst_n) 65 | ); 66 | 67 | // The DC-RAM 68 | fifomem 69 | #(DSIZE, ASIZE, FALLTHROUGH) 70 | fifomem ( 71 | .rclken (rinc), 72 | .rclk (rclk), 73 | .rdata (rdata), 74 | .wdata (wdata), 75 | .waddr (waddr), 76 | .raddr (raddr), 77 | .wclken (winc), 78 | .wfull (wfull), 79 | .wclk (wclk) 80 | ); 81 | 82 | // The module handling read requests 83 | rptr_empty 84 | #(ASIZE) 85 | rptr_empty ( 86 | .arempty (arempty), 87 | .rempty (rempty), 88 | .raddr (raddr), 89 | .rptr (rptr), 90 | .rq2_wptr (rq2_wptr), 91 | .rinc (rinc), 92 | .rclk (rclk), 93 | .rrst_n (rrst_n) 94 | ); 95 | 96 | endmodule 97 | 98 | `resetall 99 | -------------------------------------------------------------------------------- /rtl/fifomem.v: -------------------------------------------------------------------------------- 1 | // distributed under the mit license 2 | // https://opensource.org/licenses/mit-license.php 3 | 4 | `timescale 1 ns / 1 ps 5 | `default_nettype none 6 | 7 | module fifomem 8 | 9 | #( 10 | parameter DATASIZE = 8, // Memory data word width 11 | parameter ADDRSIZE = 4, // Number of mem address bits 12 | parameter FALLTHROUGH = "TRUE" // First word fall-through 13 | ) ( 14 | input wire wclk, 15 | input wire wclken, 16 | input wire [ADDRSIZE-1:0] waddr, 17 | input wire [DATASIZE-1:0] wdata, 18 | input wire wfull, 19 | input wire rclk, 20 | input wire rclken, 21 | input wire [ADDRSIZE-1:0] raddr, 22 | output wire [DATASIZE-1:0] rdata 23 | ); 24 | 25 | localparam DEPTH = 1<> 1) ^ rbinnext; 42 | assign rgraynextm1 = ((rbinnext + 1'b1) >> 1) ^ (rbinnext + 1'b1); 43 | 44 | //--------------------------------------------------------------- 45 | // FIFO empty when the next rptr == synchronized wptr or on reset 46 | //--------------------------------------------------------------- 47 | assign rempty_val = (rgraynext == rq2_wptr); 48 | assign arempty_val = (rgraynextm1 == rq2_wptr); 49 | 50 | always @ (posedge rclk or negedge rrst_n) begin 51 | 52 | if (!rrst_n) begin 53 | arempty <= 1'b0; 54 | rempty <= 1'b1; 55 | end 56 | else begin 57 | arempty <= arempty_val; 58 | rempty <= rempty_val; 59 | end 60 | 61 | end 62 | 63 | endmodule 64 | 65 | `resetall 66 | -------------------------------------------------------------------------------- /rtl/sync_ptr.v: -------------------------------------------------------------------------------- 1 | // distributed under the mit license 2 | // https://opensource.org/licenses/mit-license.php 3 | 4 | `timescale 1 ns / 1 ps 5 | `default_nettype none 6 | 7 | module sync_ptr 8 | 9 | #( 10 | parameter ASIZE = 4 11 | )( 12 | input wire dest_clk, 13 | input wire dest_rst_n, 14 | input wire [ASIZE:0] src_ptr, 15 | output reg [ASIZE:0] dest_ptr 16 | ); 17 | 18 | reg [ASIZE:0] ptr_x; 19 | 20 | always @(posedge dest_clk or negedge dest_rst_n) begin 21 | 22 | if (!dest_rst_n) 23 | {dest_ptr,ptr_x} <= 0; 24 | else 25 | {dest_ptr,ptr_x} <= {ptr_x,src_ptr}; 26 | end 27 | 28 | endmodule 29 | 30 | `resetall 31 | -------------------------------------------------------------------------------- /rtl/sync_r2w.v: -------------------------------------------------------------------------------- 1 | // distributed under the mit license 2 | // https://opensource.org/licenses/mit-license.php 3 | 4 | `timescale 1 ns / 1 ps 5 | `default_nettype none 6 | 7 | module sync_r2w 8 | 9 | #( 10 | parameter ASIZE = 4 11 | )( 12 | input wire wclk, 13 | input wire wrst_n, 14 | input wire [ASIZE:0] rptr, 15 | output reg [ASIZE:0] wq2_rptr 16 | ); 17 | 18 | reg [ASIZE:0] wq1_rptr; 19 | 20 | always @(posedge wclk or negedge wrst_n) begin 21 | 22 | if (!wrst_n) 23 | {wq2_rptr,wq1_rptr} <= 0; 24 | else 25 | {wq2_rptr,wq1_rptr} <= {wq1_rptr,rptr}; 26 | 27 | end 28 | 29 | endmodule 30 | 31 | `resetall 32 | -------------------------------------------------------------------------------- /rtl/sync_w2r.v: -------------------------------------------------------------------------------- 1 | // distributed under the mit license 2 | // https://opensource.org/licenses/mit-license.php 3 | 4 | `timescale 1 ns / 1 ps 5 | `default_nettype none 6 | 7 | module sync_w2r 8 | 9 | #( 10 | parameter ASIZE = 4 11 | )( 12 | input wire rclk, 13 | input wire rrst_n, 14 | output reg [ASIZE:0] rq2_wptr, 15 | input wire [ASIZE:0] wptr 16 | ); 17 | 18 | reg [ASIZE:0] rq1_wptr; 19 | 20 | always @(posedge rclk or negedge rrst_n) begin 21 | 22 | if (!rrst_n) 23 | {rq2_wptr,rq1_wptr} <= 0; 24 | else 25 | {rq2_wptr,rq1_wptr} <= {rq1_wptr,wptr}; 26 | 27 | end 28 | 29 | endmodule 30 | 31 | `resetall 32 | -------------------------------------------------------------------------------- /rtl/wptr_full.v: -------------------------------------------------------------------------------- 1 | // distributed under the mit license 2 | // https://opensource.org/licenses/mit-license.php 3 | 4 | `timescale 1 ns / 1 ps 5 | `default_nettype none 6 | 7 | module wptr_full 8 | 9 | #( 10 | parameter ADDRSIZE = 4 11 | )( 12 | input wire wclk, 13 | input wire wrst_n, 14 | input wire winc, 15 | input wire [ADDRSIZE :0] wq2_rptr, 16 | output reg wfull, 17 | output reg awfull, 18 | output wire [ADDRSIZE-1:0] waddr, 19 | output reg [ADDRSIZE :0] wptr 20 | ); 21 | 22 | reg [ADDRSIZE:0] wbin; 23 | wire [ADDRSIZE:0] wgraynext, wbinnext, wgraynextp1; 24 | wire awfull_val, wfull_val; 25 | 26 | // GRAYSTYLE2 pointer 27 | always @(posedge wclk or negedge wrst_n) begin 28 | 29 | if (!wrst_n) 30 | {wbin, wptr} <= 0; 31 | else 32 | {wbin, wptr} <= {wbinnext, wgraynext}; 33 | 34 | end 35 | 36 | // Memory write-address pointer (okay to use binary to address memory) 37 | assign waddr = wbin[ADDRSIZE-1:0]; 38 | assign wbinnext = wbin + (winc & ~wfull); 39 | assign wgraynext = (wbinnext >> 1) ^ wbinnext; 40 | assign wgraynextp1 = ((wbinnext + 1'b1) >> 1) ^ (wbinnext + 1'b1); 41 | 42 | //------------------------------------------------------------------ 43 | // Simplified version of the three necessary full-tests: 44 | // assign wfull_val=((wgnext[ADDRSIZE] !=wq2_rptr[ADDRSIZE] ) && 45 | // (wgnext[ADDRSIZE-1] !=wq2_rptr[ADDRSIZE-1]) && 46 | // (wgnext[ADDRSIZE-2:0]==wq2_rptr[ADDRSIZE-2:0])); 47 | //------------------------------------------------------------------ 48 | 49 | assign wfull_val = (wgraynext == {~wq2_rptr[ADDRSIZE:ADDRSIZE-1],wq2_rptr[ADDRSIZE-2:0]}); 50 | assign awfull_val = (wgraynextp1 == {~wq2_rptr[ADDRSIZE:ADDRSIZE-1],wq2_rptr[ADDRSIZE-2:0]}); 51 | 52 | always @(posedge wclk or negedge wrst_n) begin 53 | 54 | if (!wrst_n) begin 55 | awfull <= 1'b0; 56 | wfull <= 1'b0; 57 | end else begin 58 | awfull <= awfull_val; 59 | wfull <= wfull_val; 60 | end 61 | end 62 | 63 | endmodule 64 | 65 | `resetall 66 | -------------------------------------------------------------------------------- /script/setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # get current script path (applicable even if is a symlink) 4 | SOURCE="${BASH_SOURCE[0]}" 5 | while [ -h "$SOURCE" ]; do 6 | DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" 7 | SOURCE="$(readlink "$SOURCE")" 8 | [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" 9 | done 10 | DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" 11 | 12 | if [[ ! $(type svutRun) ]]; 13 | then 14 | svut_dir="$DIR/.svut" 15 | if [[ ! -d $svut_dir ]]; then 16 | echo "INFO: Install SVUT (https://github.com/dpretet/svut)" 17 | git clone "https://github.com/dpretet/svut.git" "$svut_dir" 18 | fi 19 | echo "INFO: Enable SVUT in PATH" 20 | export PATH=$svut_dir/:$PATH 21 | fi 22 | -------------------------------------------------------------------------------- /sim/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @svutRun -f files.f -define "AFULL=1;AEMPTY=1" 3 | @svutRun -f files.f -define "AFULL=3;AEMPTY=2" 4 | 5 | test-dry-run: 6 | @svutRun -f files.f -dry-run 7 | 8 | gui: 9 | @svutRun -f files.f -gui 10 | 11 | clean: 12 | @-rm -f *.vcd 13 | @-rm -f *.lxt* 14 | @-rm -f *.vvp* 15 | @-rm -f *.out 16 | 17 | -------------------------------------------------------------------------------- /sim/async_fifo_unit_test.sv: -------------------------------------------------------------------------------- 1 | `include "svut_h.sv" 2 | `timescale 1 ns / 1 ps 3 | 4 | module async_fifo_unit_test; 5 | 6 | `SVUT_SETUP 7 | 8 | `ifndef AEMPTY 9 | `define AEMPTY 1 10 | `endif 11 | 12 | `ifndef AFULL 13 | `define AFULL 1 14 | `endif 15 | 16 | `ifndef FALLTHROUGH 17 | `define FALLTHROUGH "TRUE" 18 | `endif 19 | 20 | parameter DSIZE = 32; 21 | parameter ASIZE = 4; 22 | parameter FALLTHROUGH = `FALLTHROUGH; 23 | parameter MAX_TRAFFIC = 10; 24 | 25 | integer timeout; 26 | 27 | reg wclk; 28 | reg wrst_n; 29 | reg winc; 30 | reg [DSIZE-1:0] wdata; 31 | wire wfull; 32 | wire awfull; 33 | reg rclk; 34 | reg rrst_n; 35 | reg rinc; 36 | wire [DSIZE-1:0] rdata; 37 | wire rempty; 38 | wire arempty; 39 | 40 | async_fifo 41 | #( 42 | .DSIZE (DSIZE), 43 | .ASIZE (ASIZE), 44 | .FALLTHROUGH (FALLTHROUGH) 45 | ) 46 | dut 47 | ( 48 | wclk, 49 | wrst_n, 50 | winc, 51 | wdata, 52 | wfull, 53 | awfull, 54 | rclk, 55 | rrst_n, 56 | rinc, 57 | rdata, 58 | rempty, 59 | arempty 60 | ); 61 | 62 | // An example to create a clock 63 | initial wclk = 1'b0; 64 | always #2 wclk <= ~wclk; 65 | initial rclk = 1'b0; 66 | always #3 rclk <= ~rclk; 67 | 68 | // An example to dump data for visualization 69 | initial begin 70 | $dumpfile("async_fifo_unit_test.vcd"); 71 | $dumpvars(0, async_fifo_unit_test); 72 | end 73 | 74 | task setup(msg="Setup testcase"); 75 | begin 76 | 77 | wrst_n = 1'b0; 78 | winc = 1'b0; 79 | wdata = 0; 80 | rrst_n = 1'b0; 81 | rinc = 1'b0; 82 | #100; 83 | wrst_n = 1; 84 | rrst_n = 1; 85 | #50; 86 | timeout = 0; 87 | @(posedge wclk); 88 | 89 | end 90 | endtask 91 | 92 | task teardown(msg="Tearing down"); 93 | begin 94 | #50; 95 | end 96 | endtask 97 | 98 | `TEST_SUITE("ASYNCFIFO") 99 | 100 | `UNIT_TEST("TEST_IDLE") 101 | 102 | `FAIL_IF(wfull); 103 | `FAIL_IF(!rempty); 104 | 105 | `UNIT_TEST_END 106 | 107 | `UNIT_TEST("TEST_SINGLE_WRITE_THEN_READ") 108 | 109 | @(posedge wclk) 110 | 111 | winc = 1; 112 | wdata = 32'hA; 113 | 114 | @(posedge wclk) 115 | 116 | winc = 0; 117 | 118 | @(posedge rclk) 119 | 120 | wait (rempty == 1'b0); 121 | 122 | rinc = 1; 123 | @(negedge rclk) 124 | 125 | `FAIL_IF_NOT_EQUAL(rdata, 32'hA); 126 | 127 | `UNIT_TEST_END 128 | 129 | `UNIT_TEST("TEST_MULTIPLE_WRITE_THEN_READ") 130 | 131 | for (int i=0; i<10; i=i+1) begin 132 | @(negedge wclk); 133 | winc = 1; 134 | wdata = i; 135 | end 136 | @(negedge wclk); 137 | winc = 0; 138 | 139 | #100; 140 | 141 | @(posedge rclk); 142 | 143 | rinc = 1; 144 | for (int i=0; i<10; i=i+1) begin 145 | @(posedge rclk); 146 | `FAIL_IF_NOT_EQUAL(rdata, i); 147 | end 148 | 149 | `UNIT_TEST_END 150 | 151 | `UNIT_TEST("TEST_FULL_FLAG") 152 | 153 | winc = 1; 154 | 155 | for (int i=0; i<2**ASIZE; i=i+1) begin 156 | @(negedge wclk) 157 | wdata = i; 158 | end 159 | 160 | @(negedge wclk); 161 | winc = 0; 162 | 163 | @(posedge wclk) 164 | `FAIL_IF_NOT_EQUAL(wfull, 1); 165 | 166 | `UNIT_TEST_END 167 | 168 | `UNIT_TEST("TEST_EMPTY_FLAG") 169 | 170 | `FAIL_IF_NOT_EQUAL(rempty, 1); 171 | 172 | for (int i=0; i<2**ASIZE; i=i+1) begin 173 | @(posedge wclk) 174 | winc = 1; 175 | wdata = i; 176 | end 177 | 178 | `FAIL_IF_NOT_EQUAL(rempty, 0); 179 | 180 | `UNIT_TEST_END 181 | 182 | `UNIT_TEST("TEST_ALMOST_EMPTY_FLAG") 183 | 184 | `FAIL_IF_NOT_EQUAL(arempty, 0); 185 | 186 | winc = 1; 187 | for (int i=0; i<1; i=i+1) begin 188 | 189 | @(negedge wclk) 190 | wdata = i; 191 | 192 | end 193 | 194 | @(negedge wclk); 195 | winc = 0; 196 | 197 | #100; 198 | `FAIL_IF_NOT_EQUAL(arempty, 1); 199 | 200 | `UNIT_TEST_END 201 | 202 | `UNIT_TEST("TEST_ALMOST_FULL_FLAG") 203 | 204 | winc = 1; 205 | for (int i=0; i<2**ASIZE-1; i=i+1) begin 206 | 207 | @(negedge wclk) 208 | wdata = i; 209 | 210 | end 211 | 212 | @(negedge wclk); 213 | winc = 0; 214 | 215 | @(posedge wclk) 216 | `FAIL_IF_NOT_EQUAL(awfull, 1); 217 | 218 | `UNIT_TEST_END 219 | 220 | `UNIT_TEST("TEST_CONCURRENT_WRITE_READ") 221 | 222 | fork 223 | // Concurrent accesses 224 | begin 225 | fork 226 | // Write source 227 | begin 228 | winc = 1; 229 | for (int i=0; i 0 129 | `define FAIL_IF_NOT(exp, message="") \ 130 | svut_status = 0; \ 131 | svut_msg = create_msg("FAIL_IF_NOT", message); \ 132 | if (!exp) begin \ 133 | `ERROR(svut_msg); \ 134 | svut_status = 1; \ 135 | end 136 | 137 | /// This check fails if both input are equal 138 | `define FAIL_IF_EQUAL(a,b, message="") \ 139 | svut_status = 0; \ 140 | svut_msg = create_msg("FAIL_IF_EQUAL", message); \ 141 | if (a === b) begin \ 142 | `ERROR(svut_msg); \ 143 | svut_status = 1; \ 144 | end 145 | 146 | /// This check fails if both input are not equal 147 | `define FAIL_IF_NOT_EQUAL(a,b, message="") \ 148 | svut_status = 0; \ 149 | svut_msg = create_msg("FAIL_IF_NOT_EQUAL", message); \ 150 | if (a !== b) begin \ 151 | `ERROR(svut_msg); \ 152 | svut_status = 1; \ 153 | end 154 | 155 | /// This check fails if expression is not = 0 156 | `define ASSERT(exp, message="") \ 157 | svut_status = 0; \ 158 | svut_msg = create_msg("ASSERT", message); \ 159 | if (!exp) begin \ 160 | `ERROR(svut_msg); \ 161 | svut_status = 1; \ 162 | end 163 | 164 | /// This header must be placed to start a test suite execution 165 | `define TEST_SUITE(name="") \ 166 | task run(msg=""); \ 167 | begin \ 168 | svut_suite_name = name; \ 169 | $display("");\ 170 | svut_msg = {"Start testsuite << ", name, " >>"}; \ 171 | `INFO(svut_msg); 172 | 173 | /// This header must be placed to start a test execution 174 | `define UNIT_TEST(name="") \ 175 | begin \ 176 | $display("");\ 177 | $sformat(testnum, "%0d", svut_test_number); \ 178 | svut_msg = {"Starting << ", "Test ", testnum, ": ", name, " >>"}; \ 179 | `INFO(svut_msg); \ 180 | setup(); \ 181 | svut_test_name = name; \ 182 | svut_error = 0; \ 183 | svut_nb_test = svut_nb_test + 1; 184 | 185 | /// This footer must be placed to close a test 186 | `define UNIT_TEST_END \ 187 | teardown(); \ 188 | if (svut_error == 0) begin \ 189 | svut_nb_test_success = svut_nb_test_success + 1; \ 190 | svut_msg = {"Test ", testnum, " pass"}; \ 191 | `SUCCESS(svut_msg); \ 192 | end else begin \ 193 | svut_msg = {"Test ", testnum, " fail"}; \ 194 | `ERROR(svut_msg); \ 195 | svut_fail_list = {svut_fail_list, " '", svut_test_name, "'"}; \ 196 | svut_error_total += svut_error; \ 197 | end \ 198 | svut_test_number = svut_test_number + 1; \ 199 | end 200 | 201 | /// This footer must be placed to close a test suite 202 | `define TEST_SUITE_END \ 203 | end \ 204 | endtask \ 205 | initial begin\ 206 | run(); \ 207 | $display("");\ 208 | svut_msg = {"Stop testsuite '", svut_suite_name, "'"}; \ 209 | `INFO(svut_msg); \ 210 | if (svut_error_total > 0) begin \ 211 | $display("\033[1;31m"); \ 212 | $display(svut_fail_list); \ 213 | $display(""); \ 214 | end \ 215 | $display(" \033[1;33m- Warning number: %0d\033[0m", svut_warning); \ 216 | $display(" \033[1;35m- Critical number: %0d\033[0m", svut_critical); \ 217 | $display(" \033[1;31m- Error number: %0d\033[0m", svut_error_total); \ 218 | if (svut_nb_test_success != svut_nb_test) begin \ 219 | $display(" \033[1;31m- STATUS: %0d/%0d test(s) passed\033[0m\n", svut_nb_test_success, svut_nb_test); \ 220 | end else begin \ 221 | $display(" \033[0;32m- STATUS: %0d/%0d test(s) passed\033[0m\n", svut_nb_test_success, svut_nb_test); \ 222 | end \ 223 | $finish(); \ 224 | end 225 | 226 | `endif 227 | -------------------------------------------------------------------------------- /sim/wave.gtkw: -------------------------------------------------------------------------------- 1 | [*] 2 | [*] GTKWave Analyzer v3.4.0 (w)1999-2022 BSI 3 | [*] Sat Mar 30 09:48:17 2024 4 | [*] 5 | [dumpfile] "/Users/damien/workspace/hdl/async_fifo/sim/async_fifo_unit_test.vcd" 6 | [dumpfile_mtime] "Sat Mar 30 09:47:41 2024" 7 | [dumpfile_size] 35394 8 | [savefile] "wave.gtkw" 9 | [timestart] 2131420 10 | [size] 1440 784 11 | [pos] -1 -1 12 | *-13.015144 2119510 -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] async_fifo_unit_test. 14 | [treeopen] async_fifo_unit_test.dut. 15 | [sst_width] 253 16 | [signals_width] 150 17 | [sst_expanded] 1 18 | [sst_vpaned_height] 357 19 | @200 20 | -TB 21 | @420 22 | async_fifo_unit_test.svut_nb_test 23 | @28 24 | [color] 7 25 | async_fifo_unit_test.dut.wclk 26 | [color] 7 27 | async_fifo_unit_test.dut.wrst_n 28 | [color] 7 29 | async_fifo_unit_test.dut.winc 30 | @22 31 | [color] 7 32 | async_fifo_unit_test.wdata[31:0] 33 | @28 34 | [color] 7 35 | async_fifo_unit_test.dut.wfull 36 | [color] 7 37 | async_fifo_unit_test.dut.awfull 38 | @29 39 | [color] 2 40 | async_fifo_unit_test.dut.rclk 41 | @28 42 | [color] 2 43 | async_fifo_unit_test.dut.rrst_n 44 | [color] 2 45 | async_fifo_unit_test.dut.rinc 46 | @22 47 | [color] 2 48 | async_fifo_unit_test.rdata[31:0] 49 | @28 50 | [color] 2 51 | async_fifo_unit_test.dut.rempty 52 | [color] 2 53 | async_fifo_unit_test.dut.arempty 54 | @200 55 | -DUT 56 | @22 57 | async_fifo_unit_test.dut.raddr[3:0] 58 | async_fifo_unit_test.dut.rdata[31:0] 59 | async_fifo_unit_test.dut.rptr[4:0] 60 | async_fifo_unit_test.dut.rq2_wptr[4:0] 61 | async_fifo_unit_test.dut.wdata[31:0] 62 | async_fifo_unit_test.dut.wptr[4:0] 63 | async_fifo_unit_test.dut.wq2_rptr[4:0] 64 | @200 65 | -FIFO MEM 66 | @22 67 | async_fifo_unit_test.dut.fifomem.waddr[3:0] 68 | async_fifo_unit_test.dut.fifomem.wdata[31:0] 69 | async_fifo_unit_test.dut.fifomem.raddr[3:0] 70 | async_fifo_unit_test.dut.fifomem.rdata[31:0] 71 | @200 72 | -RD PTR EMPTY 73 | @22 74 | async_fifo_unit_test.dut.rptr_empty.raddr[3:0] 75 | async_fifo_unit_test.dut.rptr_empty.rbin[4:0] 76 | async_fifo_unit_test.dut.rptr_empty.rbinnext[4:0] 77 | async_fifo_unit_test.dut.rptr_empty.rgraynext[4:0] 78 | async_fifo_unit_test.dut.rptr_empty.rgraynextm1[4:0] 79 | async_fifo_unit_test.dut.rptr_empty.rptr[4:0] 80 | async_fifo_unit_test.dut.rptr_empty.rq2_wptr[4:0] 81 | [pattern_trace] 1 82 | [pattern_trace] 0 83 | -------------------------------------------------------------------------------- /syn/cmos.lib: -------------------------------------------------------------------------------- 1 | // test comment 2 | /* test comment */ 3 | library(demo) { 4 | cell(BUF) { 5 | area: 6; 6 | pin(A) { direction: input; } 7 | pin(Y) { direction: output; 8 | function: "A"; } 9 | } 10 | cell(NOT) { 11 | area: 3; 12 | pin(A) { direction: input; } 13 | pin(Y) { direction: output; 14 | function: "A'"; } 15 | } 16 | cell(NAND) { 17 | area: 4; 18 | pin(A) { direction: input; } 19 | pin(B) { direction: input; } 20 | pin(Y) { direction: output; 21 | function: "(A*B)'"; } 22 | } 23 | cell(NOR) { 24 | area: 4; 25 | pin(A) { direction: input; } 26 | pin(B) { direction: input; } 27 | pin(Y) { direction: output; 28 | function: "(A+B)'"; } 29 | } 30 | cell(DFF) { 31 | area: 18; 32 | ff(IQ, IQN) { clocked_on: C; 33 | next_state: D; } 34 | pin(C) { direction: input; 35 | clock: true; } 36 | pin(D) { direction: input; } 37 | pin(Q) { direction: output; 38 | function: "IQ"; } 39 | } 40 | cell(DFFSR) { 41 | area: 18; 42 | ff("IQ", "IQN") { clocked_on: C; 43 | next_state: D; 44 | preset: S; 45 | clear: R; } 46 | pin(C) { direction: input; 47 | clock: true; } 48 | pin(D) { direction: input; } 49 | pin(Q) { direction: output; 50 | function: "IQ"; } 51 | pin(S) { direction: input; } 52 | pin(R) { direction: input; } 53 | ; // empty statement 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /syn/fifo.ys: -------------------------------------------------------------------------------- 1 | # read design modules 2 | read -sv2012 ../rtl/async_fifo.v 3 | read -sv2012 ../rtl/fifomem.v 4 | read -sv2012 ../rtl/rptr_empty.v 5 | read -sv2012 ../rtl/sync_r2w.v 6 | read -sv2012 ../rtl/sync_w2r.v 7 | read -sv2012 ../rtl/wptr_full.v 8 | # synthsize the core 9 | synth -top async_fifo 10 | 11 | # convert design to (logical) gate-level netlists 12 | # +/adff2dff.v convert async reset to sync reset, used to mapp FFD correctly 13 | techmap +/adff2dff.v; opt 14 | # dffunmap 15 | 16 | # map internal register types to the ones from the cell library 17 | dfflibmap -liberty cmos.lib 18 | 19 | # use ABC to map remaining logic to cells from the cell library 20 | abc -liberty cmos.lib 21 | 22 | # cleanup 23 | clean 24 | 25 | # write synthesized design 26 | write_verilog async_fifo_syn.v 27 | -------------------------------------------------------------------------------- /syn/syn_asic.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # -e: exit if one command fails 4 | # -o pipefail: causes a pipeline to fail if any command fails 5 | set -e -o pipefail 6 | 7 | design="./fifo.ys" 8 | 9 | # if [[ ! -f "./vsclib013.lib" ]]; then 10 | # echo "INFO: Download library for synthesis" 11 | # wget http://www.vlsitechnology.org/synopsys/vsclib013.lib 12 | # fi 13 | 14 | # Check if a design is specified 15 | if [[ -n $1 ]]; then 16 | echo "INFO: will start $1 synthesis" 17 | design="$1" 18 | fi 19 | 20 | echo "INFO: Start synthesis flow" 21 | yosys -V 22 | 23 | yosys "$design" 24 | 25 | exit 0 26 | --------------------------------------------------------------------------------