diff --git a/fpga/lib/axi/.github/workflows/regression-tests.yml b/fpga/lib/axi/.github/workflows/regression-tests.yml index d7688d46a..a5243a10e 100644 --- a/fpga/lib/axi/.github/workflows/regression-tests.yml +++ b/fpga/lib/axi/.github/workflows/regression-tests.yml @@ -30,4 +30,4 @@ jobs: pip install tox tox-gh-actions - name: Test with tox - run: tox -- --splits 10 --group ${{ matrix.group }} + run: tox -- --splits 10 --group ${{ matrix.group }} --splitting-algorithm least_duration diff --git a/fpga/lib/axi/.test_durations b/fpga/lib/axi/.test_durations index a0bc46122..95aa5169c 100644 --- a/fpga/lib/axi/.test_durations +++ b/fpga/lib/axi/.test_durations @@ -1,518 +1,518 @@ [ - [ - "tb/axi_axil_adapter/test_axi_axil_adapter.py::test_axi_axil_adapter[8-32]", - 92.34029131208081 - ], - [ - "tb/axi_axil_adapter/test_axi_axil_adapter.py::test_axi_axil_adapter[8-16]", - 116.95964422891848 - ], - [ - "tb/axi_axil_adapter/test_axi_axil_adapter.py::test_axi_axil_adapter[8-8]", - 160.44773711299058 - ], - [ - "tb/axi_axil_adapter/test_axi_axil_adapter.py::test_axi_axil_adapter[16-32]", - 137.5760340038687 - ], - [ - "tb/axi_adapter/test_axi_adapter.py::test_axi_adapter[8-16]", - 74.270280775032 - ], - [ - "tb/axi_axil_adapter/test_axi_axil_adapter.py::test_axi_axil_adapter[32-16]", - 400.71982841100544 - ], - [ - "tb/axi_cdma/test_axi_cdma.py::test_axi_dma[8-0]", - 15.593242217088118 - ], - [ - "tb/axi_adapter/test_axi_adapter.py::test_axi_adapter[8-32]", - 72.42707156692632 - ], - [ - "tb/axi_adapter/test_axi_adapter.py::test_axi_adapter[8-8]", - 81.75273804098833 - ], - [ - "tb/axi_cdma/test_axi_cdma.py::test_axi_dma[8-1]", - 15.523522189934738 - ], - [ - "tb/axi_axil_adapter/test_axi_axil_adapter.py::test_axi_axil_adapter[16-8]", - 275.6350795699982 - ], - [ - "tb/axi_axil_adapter/test_axi_axil_adapter.py::test_axi_axil_adapter[16-16]", - 187.96441543195397 - ], - [ - "tb/axi_axil_adapter/test_axi_axil_adapter.py::test_axi_axil_adapter[32-32]", - 332.42191640788224 - ], - [ - "tb/axi_adapter/test_axi_adapter.py::test_axi_adapter[32-8]", - 299.3205338130938 - ], - [ - "tb/axi_adapter/test_axi_adapter.py::test_axi_adapter[32-16]", - 225.4822902668966 - ], - [ - "tb/axi_adapter/test_axi_adapter.py::test_axi_adapter[16-8]", - 153.9310424028663 - ], - [ - "tb/axi_cdma/test_axi_cdma.py::test_axi_dma[16-0]", - 11.925561288022436 - ], [ "tb/axi_adapter/test_axi_adapter.py::test_axi_adapter[16-16]", - 105.30798149493057 + 154.80171429924667 ], [ "tb/axi_adapter/test_axi_adapter.py::test_axi_adapter[16-32]", - 90.9395789729897 + 129.46799642406404 ], [ - "tb/axi_crossbar/test_axi_crossbar.py::test_axi_crossbar[1-1-16]", - 62.324559034081176 + "tb/axi_adapter/test_axi_adapter.py::test_axi_adapter[16-8]", + 231.553730818443 ], [ - "tb/axi_axil_adapter/test_axi_axil_adapter.py::test_axi_axil_adapter[32-8]", - 593.6646482730284 - ], - [ - "tb/axi_cdma/test_axi_cdma.py::test_axi_dma[16-1]", - 49.035118295112625 - ], - [ - "tb/axi_cdma/test_axi_cdma.py::test_axi_dma[32-0]", - 13.027022278984077 - ], - [ - "tb/axi_cdma/test_axi_cdma.py::test_axi_dma[32-1]", - 206.7382632988738 - ], - [ - "tb/axi_crossbar/test_axi_crossbar.py::test_axi_crossbar[4-1-8]", - 203.24327663891017 - ], - [ - "tb/axi_crossbar/test_axi_crossbar.py::test_axi_crossbar[1-4-8]", - 212.6529240750242 - ], - [ - "tb/axi_crossbar/test_axi_crossbar.py::test_axi_crossbar[4-1-16]", - 173.30611170106567 - ], - [ - "tb/axi_crossbar/test_axi_crossbar.py::test_axi_crossbar[4-1-32]", - 165.20796160399914 - ], - [ - "tb/axi_crossbar/test_axi_crossbar.py::test_axi_crossbar[1-1-8]", - 82.1008072240511 + "tb/axi_adapter/test_axi_adapter.py::test_axi_adapter[32-16]", + 330.9125733766705 ], [ "tb/axi_adapter/test_axi_adapter.py::test_axi_adapter[32-32]", - 186.82002451398876 + 248.16152153350413 ], [ - "tb/axi_crossbar/test_axi_crossbar.py::test_axi_crossbar[1-4-16]", - 168.1181099790847 + "tb/axi_adapter/test_axi_adapter.py::test_axi_adapter[32-8]", + 460.55696750525385 + ], + [ + "tb/axi_adapter/test_axi_adapter.py::test_axi_adapter[8-16]", + 61.18986997473985 + ], + [ + "tb/axi_adapter/test_axi_adapter.py::test_axi_adapter[8-32]", + 105.168587657623 + ], + [ + "tb/axi_adapter/test_axi_adapter.py::test_axi_adapter[8-8]", + 103.56085381656885 + ], + [ + "tb/axi_axil_adapter/test_axi_axil_adapter.py::test_axi_axil_adapter[16-16]", + 276.60084352083504 + ], + [ + "tb/axi_axil_adapter/test_axi_axil_adapter.py::test_axi_axil_adapter[16-32]", + 207.58588458877057 + ], + [ + "tb/axi_axil_adapter/test_axi_axil_adapter.py::test_axi_axil_adapter[16-8]", + 455.6177165629342 + ], + [ + "tb/axi_axil_adapter/test_axi_axil_adapter.py::test_axi_axil_adapter[32-16]", + 647.3827625270933 + ], + [ + "tb/axi_axil_adapter/test_axi_axil_adapter.py::test_axi_axil_adapter[32-32]", + 518.5061077158898 + ], + [ + "tb/axi_axil_adapter/test_axi_axil_adapter.py::test_axi_axil_adapter[32-8]", + 944.8823729595169 + ], + [ + "tb/axi_axil_adapter/test_axi_axil_adapter.py::test_axi_axil_adapter[8-16]", + 197.43744012899697 + ], + [ + "tb/axi_axil_adapter/test_axi_axil_adapter.py::test_axi_axil_adapter[8-32]", + 145.78048656322062 + ], + [ + "tb/axi_axil_adapter/test_axi_axil_adapter.py::test_axi_axil_adapter[8-8]", + 280.12128001917154 + ], + [ + "tb/axi_cdma/test_axi_cdma.py::test_axi_dma[16-0]", + 17.06440698262304 + ], + [ + "tb/axi_cdma/test_axi_cdma.py::test_axi_dma[16-1]", + 71.17776732705534 + ], + [ + "tb/axi_cdma/test_axi_cdma.py::test_axi_dma[32-0]", + 19.746693274006248 + ], + [ + "tb/axi_cdma/test_axi_cdma.py::test_axi_dma[32-1]", + 333.3781503010541 + ], + [ + "tb/axi_cdma/test_axi_cdma.py::test_axi_dma[8-0]", + 22.358843713067472 + ], + [ + "tb/axi_cdma/test_axi_cdma.py::test_axi_dma[8-1]", + 23.01292904186994 + ], + [ + "tb/axi_crossbar/test_axi_crossbar.py::test_axi_crossbar[1-1-16]", + 82.07929699122906 ], [ "tb/axi_crossbar/test_axi_crossbar.py::test_axi_crossbar[1-1-32]", - 51.11953857005574 + 66.04357133992016 + ], + [ + "tb/axi_crossbar/test_axi_crossbar.py::test_axi_crossbar[1-1-8]", + 116.83163902442902 + ], + [ + "tb/axi_crossbar/test_axi_crossbar.py::test_axi_crossbar[1-4-16]", + 226.835347446613 ], [ "tb/axi_crossbar/test_axi_crossbar.py::test_axi_crossbar[1-4-32]", - 167.10660719079897 + 221.82670008204877 ], [ - "tb/axi_dma/test_axi_dma.py::test_axi_dma[0-8]", - 38.593522747047246 + "tb/axi_crossbar/test_axi_crossbar.py::test_axi_crossbar[1-4-8]", + 278.4844740750268 ], [ - "tb/axi_dma/test_axi_dma.py::test_axi_dma[0-16]", - 26.12306741985958 + "tb/axi_crossbar/test_axi_crossbar.py::test_axi_crossbar[4-1-16]", + 147.22683315817267 + ], + [ + "tb/axi_crossbar/test_axi_crossbar.py::test_axi_crossbar[4-1-32]", + 166.78969807736576 + ], + [ + "tb/axi_crossbar/test_axi_crossbar.py::test_axi_crossbar[4-1-8]", + 278.06333569251 ], [ "tb/axi_crossbar/test_axi_crossbar.py::test_axi_crossbar[4-4-16]", - 385.9775214729598 - ], - [ - "tb/axi_dma_rd/test_axi_dma_rd.py::test_axi_dma_rd[16-0]", - 2.330480069038458 - ], - [ - "tb/axi_dma_wr/test_axi_dma_wr.py::test_axi_dma_wr[8-0]", - 18.85620612394996 - ], - [ - "tb/axi_dma/test_axi_dma.py::test_axi_dma[1-16]", - 54.40660252713133 + 549.59216252435 ], [ "tb/axi_crossbar/test_axi_crossbar.py::test_axi_crossbar[4-4-32]", - 421.9630580791272 + 455.38514183368534 ], [ "tb/axi_crossbar/test_axi_crossbar.py::test_axi_crossbar[4-4-8]", - 404.845993772964 + 588.921606304124 ], [ - "tb/axi_dma_wr/test_axi_dma_wr.py::test_axi_dma_wr[8-1]", - 19.358775094966404 - ], - [ - "tb/axi_dma_rd/test_axi_dma_rd.py::test_axi_dma_rd[8-1]", - 3.2852856101235375 + "tb/axi_dma/test_axi_dma.py::test_axi_dma[0-16]", + 36.3299577338621 ], [ "tb/axi_dma/test_axi_dma.py::test_axi_dma[0-32]", - 27.126582405064255 + 39.596242003142834 ], [ - "tb/axi_dma_rd/test_axi_dma_rd.py::test_axi_dma_rd[8-0]", - 3.397583018988371 + "tb/axi_dma/test_axi_dma.py::test_axi_dma[0-8]", + 54.620901849120855 ], [ - "tb/axi_dma/test_axi_dma.py::test_axi_dma[1-8]", - 39.06702446111012 + "tb/axi_dma/test_axi_dma.py::test_axi_dma[1-16]", + 70.15106952376664 ], [ "tb/axi_dma/test_axi_dma.py::test_axi_dma[1-32]", - 109.1842717891559 + 157.4396074814722 ], [ - "tb/axi_dma_rd/test_axi_dma_rd.py::test_axi_dma_rd[32-1]", - 10.748354449984618 + "tb/axi_dma/test_axi_dma.py::test_axi_dma[1-8]", + 56.511911587789655 ], [ - "tb/axi_dma_wr/test_axi_dma_wr.py::test_axi_dma_wr[16-0]", - 14.207220426993445 - ], - [ - "tb/axi_dma_rd/test_axi_dma_rd.py::test_axi_dma_rd[32-0]", - 4.082128511974588 + "tb/axi_dma_rd/test_axi_dma_rd.py::test_axi_dma_rd[16-0]", + 3.7544524213299155 ], [ "tb/axi_dma_rd/test_axi_dma_rd.py::test_axi_dma_rd[16-1]", - 4.67441277008038 + 7.172512795776129 + ], + [ + "tb/axi_dma_rd/test_axi_dma_rd.py::test_axi_dma_rd[32-0]", + 4.666332081891596 + ], + [ + "tb/axi_dma_rd/test_axi_dma_rd.py::test_axi_dma_rd[32-1]", + 23.44465008098632 + ], + [ + "tb/axi_dma_rd/test_axi_dma_rd.py::test_axi_dma_rd[8-0]", + 5.142332099378109 + ], + [ + "tb/axi_dma_rd/test_axi_dma_rd.py::test_axi_dma_rd[8-1]", + 6.475921370089054 + ], + [ + "tb/axi_dma_wr/test_axi_dma_wr.py::test_axi_dma_wr[16-0]", + 20.40422752313316 ], [ "tb/axi_dma_wr/test_axi_dma_wr.py::test_axi_dma_wr[16-1]", - 26.38754628202878 - ], - [ - "tb/axi_dp_ram/test_axi_dp_ram.py::test_axi_dp_ram[8]", - 190.6977087969426 - ], - [ - "tb/axi_dma_wr/test_axi_dma_wr.py::test_axi_dma_wr[32-1]", - 61.13482341193594 - ], - [ - "tb/axi_dp_ram/test_axi_dp_ram.py::test_axi_dp_ram[32]", - 631.3725310519803 - ], - [ - "tb/axi_fifo/test_axi_fifo.py::test_axi_fifo[16-1]", - 116.16801285289694 + 46.69374061562121 ], [ "tb/axi_dma_wr/test_axi_dma_wr.py::test_axi_dma_wr[32-0]", - 14.180392780923285 + 22.988098615780473 ], [ - "tb/axi_interconnect/test_axi_interconnect.py::test_axi_interconnect[1-1-8]", - 110.69283434608951 + "tb/axi_dma_wr/test_axi_dma_wr.py::test_axi_dma_wr[32-1]", + 89.21624889411032 ], [ - "tb/axi_fifo/test_axi_fifo.py::test_axi_fifo[32-1]", - 235.51755335007329 + "tb/axi_dma_wr/test_axi_dma_wr.py::test_axi_dma_wr[8-0]", + 29.688831198960543 ], [ - "tb/axi_fifo/test_axi_fifo.py::test_axi_fifo[16-0]", - 114.71273985889275 + "tb/axi_dma_wr/test_axi_dma_wr.py::test_axi_dma_wr[8-1]", + 27.773485894314945 ], [ "tb/axi_dp_ram/test_axi_dp_ram.py::test_axi_dp_ram[16]", - 339.84432155790273 + 557.706888041459 ], [ - "tb/axi_fifo/test_axi_fifo.py::test_axi_fifo[8-1]", - 81.9772285890067 + "tb/axi_dp_ram/test_axi_dp_ram.py::test_axi_dp_ram[32]", + 875.9048622418195 ], [ - "tb/axi_fifo/test_axi_fifo.py::test_axi_fifo[8-0]", - 80.0548778581433 + "tb/axi_dp_ram/test_axi_dp_ram.py::test_axi_dp_ram[8]", + 293.5553059997037 + ], + [ + "tb/axi_fifo/test_axi_fifo.py::test_axi_fifo[16-0]", + 128.83496031537652 + ], + [ + "tb/axi_fifo/test_axi_fifo.py::test_axi_fifo[16-1]", + 173.11390095669776 ], [ "tb/axi_fifo/test_axi_fifo.py::test_axi_fifo[32-0]", - 217.3123359469464 + 343.3222937248647 ], [ - "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[1-4-8]", - 59.05071274796501 + "tb/axi_fifo/test_axi_fifo.py::test_axi_fifo[32-1]", + 350.7498745601624 ], [ - "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[1-1-32]", - 10.958538840059191 + "tb/axi_fifo/test_axi_fifo.py::test_axi_fifo[8-0]", + 105.13950155116618 ], [ - "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[1-1-16]", - 16.18374707410112 - ], - [ - "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[1-4-32]", - 30.99582407809794 - ], - [ - "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[1-4-16]", - 32.26239267003257 - ], - [ - "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[4-1-8]", - 55.87296755902935 - ], - [ - "tb/axi_register/test_axi_register.py::test_axi_register[16-1]", - 134.0978170959279 - ], - [ - "tb/axi_register/test_axi_register.py::test_axi_register[32-None]", - 233.25326886295807 - ], - [ - "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[4-4-32]", - 53.02282854996156 - ], - [ - "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[4-4-8]", - 82.27994647994637 - ], - [ - "tb/axi_register/test_axi_register.py::test_axi_register[16-0]", - 111.6125350688817 - ], - [ - "tb/axi_register/test_axi_register.py::test_axi_register[16-None]", - 114.57908571104053 - ], - [ - "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[4-1-16]", - 31.820328426081687 - ], - [ - "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[4-1-32]", - 27.239373297896236 - ], - [ - "tb/axil_cdc/test_axil_cdc.py::test_axil_cdc[32]", - 18.885881599970162 - ], - [ - "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[4-4-16]", - 47.431341870105825 - ], - [ - "tb/axil_dp_ram/test_axil_dp_ram.py::test_axil_dp_ram[8]", - 6.083705611992627 - ], - [ - "tb/axil_cdc/test_axil_cdc.py::test_axil_cdc[16]", - 23.918021908029914 - ], - [ - "tb/axil_dp_ram/test_axil_dp_ram.py::test_axil_dp_ram[16]", - 5.008629633928649 - ], - [ - "tb/axi_register/test_axi_register.py::test_axi_register[16-2]", - 109.07862244499847 - ], - [ - "tb/axil_adapter/test_axil_adapter.py::test_axil_adapter[8-8]", - 11.255430878023617 - ], - [ - "tb/axil_dp_ram/test_axil_dp_ram.py::test_axil_dp_ram[32]", - 9.214697457035072 - ], - [ - "tb/axil_ram/test_axil_ram.py::test_axil_ram[8]", - 3.814734523068182 - ], - [ - "tb/axil_register/test_axil_register.py::test_axil_register[32-2]", - 3.088388371048495 - ], - [ - "tb/axi_register/test_axi_register.py::test_axi_register[32-2]", - 210.00476461392827 - ], - [ - "tb/axi_interconnect/test_axi_interconnect.py::test_axi_interconnect[1-4-8]", - 232.57985687395558 - ], - [ - "tb/axi_interconnect/test_axi_interconnect.py::test_axi_interconnect[1-1-32]", - 54.11528235801961 - ], - [ - "tb/axil_register/test_axil_register.py::test_axil_register[8-1]", - 6.881038482999429 + "tb/axi_fifo/test_axi_fifo.py::test_axi_fifo[8-1]", + 121.01873329374939 ], [ "tb/axi_interconnect/test_axi_interconnect.py::test_axi_interconnect[1-1-16]", - 67.14825033093803 + 102.19653648044914 ], [ - "tb/axil_register/test_axil_register.py::test_axil_register[8-2]", - 4.017322617932223 + "tb/axi_interconnect/test_axi_interconnect.py::test_axi_interconnect[1-1-32]", + 82.02585211675614 ], [ - "tb/axi_register/test_axi_register.py::test_axi_register[32-1]", - 226.09505098406225 - ], - [ - "tb/axi_interconnect/test_axi_interconnect.py::test_axi_interconnect[4-1-8]", - 228.00826906599104 - ], - [ - "tb/axi_ram/test_axi_ram.py::test_axi_ram[8]", - 59.478687431896105 - ], - [ - "tb/axi_interconnect/test_axi_interconnect.py::test_axi_interconnect[1-4-32]", - 158.42456139600836 - ], - [ - "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[1-1-8]", - 24.357805628911592 - ], - [ - "tb/axil_register/test_axil_register.py::test_axil_register[32-1]", - 3.4112419869052246 - ], - [ - "tb/axil_register/test_axil_register.py::test_axil_register[16-0]", - 2.5862843709765 - ], - [ - "tb/axil_register/test_axil_register.py::test_axil_register[16-1]", - 3.946968311909586 - ], - [ - "tb/axi_interconnect/test_axi_interconnect.py::test_axi_interconnect[4-4-32]", - 400.17059198615607 - ], - [ - "tb/axi_register/test_axi_register.py::test_axi_register[8-None]", - 67.14530843799002 + "tb/axi_interconnect/test_axi_interconnect.py::test_axi_interconnect[1-1-8]", + 161.5555182993412 ], [ "tb/axi_interconnect/test_axi_interconnect.py::test_axi_interconnect[1-4-16]", - 177.14258515485562 + 257.6017242670059 ], [ - "tb/axi_interconnect/test_axi_interconnect.py::test_axi_interconnect[4-4-16]", - 400.5200162229594 + "tb/axi_interconnect/test_axi_interconnect.py::test_axi_interconnect[1-4-32]", + 234.7367910388857 ], [ - "tb/axil_adapter/test_axil_adapter.py::test_axil_adapter[32-8]", - 11.39128352503758 - ], - [ - "tb/axil_adapter/test_axil_adapter.py::test_axil_adapter[16-8]", - 8.046741236932576 - ], - [ - "tb/axil_adapter/test_axil_adapter.py::test_axil_adapter[8-32]", - 9.869203691952862 - ], - [ - "tb/axil_adapter/test_axil_adapter.py::test_axil_adapter[16-32]", - 5.44320015097037 - ], - [ - "tb/axi_interconnect/test_axi_interconnect.py::test_axi_interconnect[4-4-8]", - 463.621744088945 - ], - [ - "tb/axi_ram/test_axi_ram.py::test_axi_ram[32]", - 169.53810201212764 - ], - [ - "tb/axil_ram/test_axil_ram.py::test_axil_ram[16]", - 2.378014532965608 - ], - [ - "tb/axil_adapter/test_axil_adapter.py::test_axil_adapter[16-16]", - 5.53720978286583 - ], - [ - "tb/axi_register/test_axi_register.py::test_axi_register[32-0]", - 190.57033147790935 - ], - [ - "tb/axil_adapter/test_axil_adapter.py::test_axil_adapter[8-16]", - 9.342879572999664 - ], - [ - "tb/axil_register/test_axil_register.py::test_axil_register[16-2]", - 2.4198110720608383 - ], - [ - "tb/axi_interconnect/test_axi_interconnect.py::test_axi_interconnect[4-1-32]", - 152.4494464310119 - ], - [ - "tb/axil_register/test_axil_register.py::test_axil_register[32-0]", - 2.364597169100307 - ], - [ - "tb/axi_ram/test_axi_ram.py::test_axi_ram[16]", - 90.49995070195291 + "tb/axi_interconnect/test_axi_interconnect.py::test_axi_interconnect[1-4-8]", + 347.6929644905031 ], [ "tb/axi_interconnect/test_axi_interconnect.py::test_axi_interconnect[4-1-16]", - 166.57522002898622 + 246.63431577198207 ], [ - "tb/axi_register/test_axi_register.py::test_axi_register[8-2]", - 64.4880481789587 + "tb/axi_interconnect/test_axi_interconnect.py::test_axi_interconnect[4-1-32]", + 172.08718999754637 ], [ - "tb/axil_ram/test_axil_ram.py::test_axil_ram[32]", - 2.7770232169423252 + "tb/axi_interconnect/test_axi_interconnect.py::test_axi_interconnect[4-1-8]", + 321.3593363221735 ], [ - "tb/axil_adapter/test_axil_adapter.py::test_axil_adapter[32-32]", - 4.392885608016513 + "tb/axi_interconnect/test_axi_interconnect.py::test_axi_interconnect[4-4-16]", + 574.1888536829501 ], [ - "tb/axi_register/test_axi_register.py::test_axi_register[8-1]", - 95.14355011994485 + "tb/axi_interconnect/test_axi_interconnect.py::test_axi_interconnect[4-4-32]", + 410.4118745010346 ], [ - "tb/axil_adapter/test_axil_adapter.py::test_axil_adapter[32-16]", - 6.293487015995197 + "tb/axi_interconnect/test_axi_interconnect.py::test_axi_interconnect[4-4-8]", + 735.5517066828907 ], [ - "tb/axil_cdc/test_axil_cdc.py::test_axil_cdc[8]", - 31.775441756122746 + "tb/axi_ram/test_axi_ram.py::test_axi_ram[16]", + 157.18584052287042 + ], + [ + "tb/axi_ram/test_axi_ram.py::test_axi_ram[32]", + 284.1904304390773 + ], + [ + "tb/axi_ram/test_axi_ram.py::test_axi_ram[8]", + 71.96863563451916 + ], + [ + "tb/axi_register/test_axi_register.py::test_axi_register[16-0]", + 137.7420165129006 + ], + [ + "tb/axi_register/test_axi_register.py::test_axi_register[16-1]", + 195.76706382073462 + ], + [ + "tb/axi_register/test_axi_register.py::test_axi_register[16-2]", + 165.80640455987304 + ], + [ + "tb/axi_register/test_axi_register.py::test_axi_register[16-None]", + 161.71680294163525 + ], + [ + "tb/axi_register/test_axi_register.py::test_axi_register[32-0]", + 312.6301383897662 + ], + [ + "tb/axi_register/test_axi_register.py::test_axi_register[32-1]", + 363.7704292088747 + ], + [ + "tb/axi_register/test_axi_register.py::test_axi_register[32-2]", + 327.6335842087865 + ], + [ + "tb/axi_register/test_axi_register.py::test_axi_register[32-None]", + 342.006927145645 ], [ "tb/axi_register/test_axi_register.py::test_axi_register[8-0]", - 63.281225918093696 + 102.75957749877125 + ], + [ + "tb/axi_register/test_axi_register.py::test_axi_register[8-1]", + 150.65885178279132 + ], + [ + "tb/axi_register/test_axi_register.py::test_axi_register[8-2]", + 116.52124607004225 + ], + [ + "tb/axi_register/test_axi_register.py::test_axi_register[8-None]", + 112.02143712155521 + ], + [ + "tb/axil_adapter/test_axil_adapter.py::test_axil_adapter[16-16]", + 9.414873909205198 + ], + [ + "tb/axil_adapter/test_axil_adapter.py::test_axil_adapter[16-32]", + 10.720249651931226 + ], + [ + "tb/axil_adapter/test_axil_adapter.py::test_axil_adapter[16-8]", + 15.94476203713566 + ], + [ + "tb/axil_adapter/test_axil_adapter.py::test_axil_adapter[32-16]", + 10.456397350877523 + ], + [ + "tb/axil_adapter/test_axil_adapter.py::test_axil_adapter[32-32]", + 9.175778770819306 + ], + [ + "tb/axil_adapter/test_axil_adapter.py::test_axil_adapter[32-8]", + 20.37657857965678 + ], + [ + "tb/axil_adapter/test_axil_adapter.py::test_axil_adapter[8-16]", + 18.28249195497483 + ], + [ + "tb/axil_adapter/test_axil_adapter.py::test_axil_adapter[8-32]", + 18.1448409659788 + ], + [ + "tb/axil_adapter/test_axil_adapter.py::test_axil_adapter[8-8]", + 18.509287000633776 + ], + [ + "tb/axil_cdc/test_axil_cdc.py::test_axil_cdc[16]", + 32.789682413451374 + ], + [ + "tb/axil_cdc/test_axil_cdc.py::test_axil_cdc[32]", + 27.674326405860484 + ], + [ + "tb/axil_cdc/test_axil_cdc.py::test_axil_cdc[8]", + 58.896160850301385 + ], + [ + "tb/axil_dp_ram/test_axil_dp_ram.py::test_axil_dp_ram[16]", + 8.325086586177349 + ], + [ + "tb/axil_dp_ram/test_axil_dp_ram.py::test_axil_dp_ram[32]", + 14.020466917194426 + ], + [ + "tb/axil_dp_ram/test_axil_dp_ram.py::test_axil_dp_ram[8]", + 9.97084455192089 + ], + [ + "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[1-1-16]", + 23.2663188977167 + ], + [ + "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[1-1-32]", + 23.612671780399978 + ], + [ + "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[1-1-8]", + 42.97799370903522 + ], + [ + "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[1-4-16]", + 44.34295728802681 + ], + [ + "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[1-4-32]", + 35.623706634156406 + ], + [ + "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[1-4-8]", + 81.04713163338602 + ], + [ + "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[4-1-16]", + 43.093923680484295 + ], + [ + "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[4-1-32]", + 34.437351221218705 + ], + [ + "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[4-1-8]", + 73.75804248824716 + ], + [ + "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[4-4-16]", + 64.2899643778801 + ], + [ + "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[4-4-32]", + 67.7834402685985 + ], + [ + "tb/axil_interconnect/test_axil_interconnect.py::test_axil_interconnect[4-4-8]", + 87.84026487357914 + ], + [ + "tb/axil_ram/test_axil_ram.py::test_axil_ram[16]", + 4.694024303928018 + ], + [ + "tb/axil_ram/test_axil_ram.py::test_axil_ram[32]", + 5.4611031180247664 + ], + [ + "tb/axil_ram/test_axil_ram.py::test_axil_ram[8]", + 7.07833203766495 + ], + [ + "tb/axil_register/test_axil_register.py::test_axil_register[16-0]", + 4.518617523834109 + ], + [ + "tb/axil_register/test_axil_register.py::test_axil_register[16-1]", + 6.114853054285049 + ], + [ + "tb/axil_register/test_axil_register.py::test_axil_register[16-2]", + 4.118833149783313 + ], + [ + "tb/axil_register/test_axil_register.py::test_axil_register[32-0]", + 4.708139616064727 + ], + [ + "tb/axil_register/test_axil_register.py::test_axil_register[32-1]", + 5.979810021817684 + ], + [ + "tb/axil_register/test_axil_register.py::test_axil_register[32-2]", + 5.279176370240748 ], [ "tb/axil_register/test_axil_register.py::test_axil_register[8-0]", - 3.50379631400574 + 6.333443800918758 + ], + [ + "tb/axil_register/test_axil_register.py::test_axil_register[8-1]", + 10.641177451238036 + ], + [ + "tb/axil_register/test_axil_register.py::test_axil_register[8-2]", + 7.088530145585537 ] ] \ No newline at end of file diff --git a/fpga/lib/axi/README.md b/fpga/lib/axi/README.md index 96e418e0d..a09712d22 100644 --- a/fpga/lib/axi/README.md +++ b/fpga/lib/axi/README.md @@ -62,8 +62,8 @@ distributing responses. AXI nonblocking crossbar interconnect with parametrizable data and address interface widths and master and slave interface counts. Supports all burst -types. Fully nonblocking with completely separate read and write paths; ID -based transaction ordering protection logic; and per-port address decode, +types. Fully nonblocking with completely separate read and write paths; +ID-based transaction ordering protection logic; and per-port address decode, admission control, and decode error handling. Wrapper for `axi_crossbar_rd` and `axi_crossbar_wr`. @@ -78,7 +78,7 @@ Address decode and admission control module for AXI nonblocking crossbar interco AXI nonblocking crossbar interconnect with parametrizable data and address interface widths and master and slave interface counts. Read interface only. Supports all burst types. Fully nonblocking with completely separate read and -write paths; ID based transaction ordering protection logic; and per-port +write paths; ID-based transaction ordering protection logic; and per-port address decode, admission control, and decode error handling. ### `axi_crossbar_wr` module @@ -86,7 +86,7 @@ address decode, admission control, and decode error handling. AXI nonblocking crossbar interconnect with parametrizable data and address interface widths and master and slave interface counts. Write interface only. Supports all burst types. Fully nonblocking with completely separate read and -write paths; ID based transaction ordering protection logic; and per-port +write paths; ID-based transaction ordering protection logic; and per-port address decode, admission control, and decode error handling. ### `axi_dma` module @@ -226,6 +226,36 @@ interface widths. AXI lite clock domain crossing module with parametrizable data and address interface widths. +### `axil_crossbar` module + +AXI lite nonblocking crossbar interconnect with parametrizable data and address +interface widths and master and slave interface counts. Fully nonblocking +with completely separate read and write paths; FIFO-based transaction ordering +protection logic; and per-port address decode, admission control, and decode +error handling. Wrapper for `axil_crossbar_rd` and `axil_crossbar_wr`. + +Wrappers can generated with `axil_crossbar_wrap.py`. + +### `axil_crossbar_addr` module + +Address decode and admission control module for AXI lite nonblocking crossbar interconnect. + +### `axil_crossbar_rd` module + +AXI lite nonblocking crossbar interconnect with parametrizable data and address +interface widths and master and slave interface counts. Read interface only. +Fully nonblocking with completely separate read and write paths; FIFO-based +transaction ordering protection logic; and per-port address decode, admission +control, and decode error handling. + +### `axil_crossbar_wr` module + +AXI lite nonblocking crossbar interconnect with parametrizable data and address +interface widths and master and slave interface counts. Write interface only. +Fully nonblocking with completely separate read and write paths; FIFO-based +transaction ordering protection logic; and per-port address decode, admission +control, and decode error handling. + ### `axil_interconnect` module AXI lite shared interconnect with parametrizable data and address interface @@ -333,8 +363,8 @@ registers can be individually bypassed. rtl/axi_cdma_desc_mux.v : AXI CDMA descriptor mux rtl/axi_crossbar.v : AXI nonblocking crossbar interconnect rtl/axi_crossbar_addr.v : AXI crossbar address module - rtl/axi_crossbar_rd.v : AXI crossbar read module - rtl/axi_crossbar_wr.v : AXI crossbar write module + rtl/axi_crossbar_rd.v : AXI crossbar interconnect (read) + rtl/axi_crossbar_wr.v : AXI crossbar interconnect (write) rtl/axi_dma.v : AXI DMA engine rtl/axi_dma_desc_mux.v : AXI DMA descriptor mux rtl/axi_dma_rd.v : AXI DMA engine (read) @@ -357,6 +387,10 @@ registers can be individually bypassed. rtl/axil_cdc.v : AXI lite CDC rtl/axil_cdc_rd.v : AXI lite CDC (read) rtl/axil_cdc_wr.v : AXI lite CDC (write) + rtl/axil_crossbar.v : AXI lite nonblocking crossbar interconnect + rtl/axil_crossbar_addr.v : AXI lite crossbar address module + rtl/axil_crossbar_rd.v : AXI lite crossbar interconnect (read) + rtl/axil_crossbar_wr.v : AXI lite crossbar interconnect (write) rtl/axil_interconnect.v : AXI lite shared interconnect rtl/axil_ram.v : AXI lite RAM rtl/axil_register.v : AXI lite register diff --git a/fpga/lib/axi/rtl/axi_cdma.v b/fpga/lib/axi/rtl/axi_cdma.v index b30b79da5..06dad0fa2 100644 --- a/fpga/lib/axi/rtl/axi_cdma.v +++ b/fpga/lib/axi/rtl/axi_cdma.v @@ -66,6 +66,7 @@ module axi_cdma # * AXI descriptor status output */ output wire [TAG_WIDTH-1:0] m_axis_desc_status_tag, + output wire [3:0] m_axis_desc_status_error, output wire m_axis_desc_status_valid, /* @@ -128,6 +129,7 @@ parameter ADDR_MASK = {AXI_ADDR_WIDTH{1'b1}} << $clog2(AXI_STRB_WIDTH); parameter CYCLE_COUNT_WIDTH = LEN_WIDTH - AXI_BURST_SIZE + 1; parameter STATUS_FIFO_ADDR_WIDTH = 5; +parameter OUTPUT_FIFO_ADDR_WIDTH = 5; // bus width assertions initial begin @@ -147,6 +149,25 @@ initial begin end end +localparam [1:0] + AXI_RESP_OKAY = 2'b00, + AXI_RESP_EXOKAY = 2'b01, + AXI_RESP_SLVERR = 2'b10, + AXI_RESP_DECERR = 2'b11; + +localparam [3:0] + DMA_ERROR_NONE = 4'd0, + DMA_ERROR_TIMEOUT = 4'd1, + DMA_ERROR_PARITY = 4'd2, + DMA_ERROR_AXI_RD_SLVERR = 4'd4, + DMA_ERROR_AXI_RD_DECERR = 4'd5, + DMA_ERROR_AXI_WR_SLVERR = 4'd6, + DMA_ERROR_AXI_WR_DECERR = 4'd7, + DMA_ERROR_PCIE_FLR = 4'd8, + DMA_ERROR_PCIE_CPL_POISONED = 4'd9, + DMA_ERROR_PCIE_CPL_STATUS_UR = 4'd10, + DMA_ERROR_PCIE_CPL_STATUS_CA = 4'd11; + localparam [1:0] READ_STATE_IDLE = 2'd0, READ_STATE_START = 2'd1, @@ -194,14 +215,18 @@ reg first_input_cycle_reg = 1'b0, first_input_cycle_next; reg first_output_cycle_reg = 1'b0, first_output_cycle_next; reg output_last_cycle_reg = 1'b0, output_last_cycle_next; reg last_transfer_reg = 1'b0, last_transfer_next; +reg [1:0] rresp_reg = AXI_RESP_OKAY, rresp_next; +reg [1:0] bresp_reg = AXI_RESP_OKAY, bresp_next; reg [TAG_WIDTH-1:0] tag_reg = {TAG_WIDTH{1'b0}}, tag_next; reg [STATUS_FIFO_ADDR_WIDTH+1-1:0] status_fifo_wr_ptr_reg = 0; reg [STATUS_FIFO_ADDR_WIDTH+1-1:0] status_fifo_rd_ptr_reg = 0, status_fifo_rd_ptr_next; reg [TAG_WIDTH-1:0] status_fifo_tag[(2**STATUS_FIFO_ADDR_WIDTH)-1:0]; +reg [1:0] status_fifo_resp[(2**STATUS_FIFO_ADDR_WIDTH)-1:0]; reg status_fifo_last[(2**STATUS_FIFO_ADDR_WIDTH)-1:0]; reg [TAG_WIDTH-1:0] status_fifo_wr_tag; +reg [1:0] status_fifo_wr_resp; reg status_fifo_wr_last; reg [STATUS_FIFO_ADDR_WIDTH+1-1:0] active_count_reg = 0; @@ -212,6 +237,7 @@ reg dec_active; reg s_axis_desc_ready_reg = 1'b0, s_axis_desc_ready_next; reg [TAG_WIDTH-1:0] m_axis_desc_status_tag_reg = {TAG_WIDTH{1'b0}}, m_axis_desc_status_tag_next; +reg [3:0] m_axis_desc_status_error_reg = 4'd0, m_axis_desc_status_error_next; reg m_axis_desc_status_valid_reg = 1'b0, m_axis_desc_status_valid_next; reg [AXI_ADDR_WIDTH-1:0] m_axi_araddr_reg = {AXI_ADDR_WIDTH{1'b0}}, m_axi_araddr_next; @@ -233,12 +259,12 @@ reg [AXI_DATA_WIDTH-1:0] m_axi_wdata_int; reg [AXI_STRB_WIDTH-1:0] m_axi_wstrb_int; reg m_axi_wlast_int; reg m_axi_wvalid_int; -reg m_axi_wready_int_reg = 1'b0; -wire m_axi_wready_int_early; +wire m_axi_wready_int; assign s_axis_desc_ready = s_axis_desc_ready_reg; assign m_axis_desc_status_tag = m_axis_desc_status_tag_reg; +assign m_axis_desc_status_error = m_axis_desc_status_error_reg; assign m_axis_desc_status_valid = m_axis_desc_status_valid_reg; assign m_axi_arid = {AXI_ID_WIDTH{1'b0}}; @@ -418,6 +444,7 @@ always @* begin axi_state_next = AXI_STATE_IDLE; m_axis_desc_status_tag_next = m_axis_desc_status_tag_reg; + m_axis_desc_status_error_next = m_axis_desc_status_error_reg; m_axis_desc_status_valid_next = 1'b0; m_axi_awaddr_next = m_axi_awaddr_reg; @@ -454,7 +481,20 @@ always @* begin dec_active = 1'b0; + if (m_axi_rready && m_axi_rvalid && (m_axi_rresp == AXI_RESP_SLVERR || m_axi_rresp == AXI_RESP_DECERR)) begin + rresp_next = m_axi_rresp; + end else begin + rresp_next = rresp_reg; + end + + if (m_axi_bready && m_axi_bvalid && (m_axi_bresp == AXI_RESP_SLVERR || m_axi_bresp == AXI_RESP_DECERR)) begin + bresp_next = m_axi_bresp; + end else begin + bresp_next = bresp_reg; + end + status_fifo_wr_tag = tag_reg; + status_fifo_wr_resp = rresp_next; status_fifo_wr_last = 1'b0; case (axi_state_reg) @@ -490,15 +530,15 @@ always @* begin m_axi_awlen_next = axi_cmd_output_cycle_count_reg; m_axi_awvalid_next = 1'b1; - m_axi_rready_next = m_axi_wready_int_early; + m_axi_rready_next = m_axi_wready_int; axi_state_next = AXI_STATE_WRITE; end end AXI_STATE_WRITE: begin // handle AXI read data - m_axi_rready_next = m_axi_wready_int_early && input_active_reg; + m_axi_rready_next = m_axi_wready_int && input_active_reg; - if (m_axi_wready_int_reg && ((m_axi_rready && m_axi_rvalid) || !input_active_reg)) begin + if ((m_axi_rready && m_axi_rvalid) || !input_active_reg) begin // transfer in AXI read data transfer_in_save = m_axi_rready && m_axi_rvalid; @@ -510,7 +550,7 @@ always @* begin bubble_cycle_next = 1'b0; first_input_cycle_next = 1'b0; - m_axi_rready_next = m_axi_wready_int_early && input_active_next; + m_axi_rready_next = m_axi_wready_int && input_active_next; axi_state_next = AXI_STATE_WRITE; end else begin // update counters @@ -545,8 +585,13 @@ always @* begin status_fifo_we = 1'b1; status_fifo_wr_tag = tag_reg; + status_fifo_wr_resp = rresp_next; status_fifo_wr_last = last_transfer_reg; + if (last_transfer_reg) begin + rresp_next = AXI_RESP_OKAY; + end + m_axi_rready_next = 1'b0; axi_state_next = AXI_STATE_IDLE; end else begin @@ -565,10 +610,25 @@ always @* begin if (m_axi_bready && m_axi_bvalid) begin // got write completion, pop and return status m_axis_desc_status_tag_next = status_fifo_tag[status_fifo_rd_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]]; + if (status_fifo_resp[status_fifo_rd_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]] == AXI_RESP_SLVERR) begin + m_axis_desc_status_error_next = DMA_ERROR_AXI_RD_SLVERR; + end else if (status_fifo_resp[status_fifo_rd_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]] == AXI_RESP_DECERR) begin + m_axis_desc_status_error_next = DMA_ERROR_AXI_RD_DECERR; + end else if (bresp_next == AXI_RESP_SLVERR) begin + m_axis_desc_status_error_next = DMA_ERROR_AXI_WR_SLVERR; + end else if (bresp_next == AXI_RESP_DECERR) begin + m_axis_desc_status_error_next = DMA_ERROR_AXI_WR_DECERR; + end else begin + m_axis_desc_status_error_next = DMA_ERROR_NONE; + end m_axis_desc_status_valid_next = status_fifo_last[status_fifo_rd_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]]; status_fifo_rd_ptr_next = status_fifo_rd_ptr_reg + 1; m_axi_bready_next = 1'b0; + if (status_fifo_last[status_fifo_rd_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]]) begin + bresp_next = AXI_RESP_OKAY; + end + dec_active = 1'b1; end else begin // wait for write completion @@ -584,6 +644,7 @@ always @(posedge clk) begin s_axis_desc_ready_reg <= s_axis_desc_ready_next; m_axis_desc_status_tag_reg <= m_axis_desc_status_tag_next; + m_axis_desc_status_error_reg <= m_axis_desc_status_error_next; m_axis_desc_status_valid_reg <= m_axis_desc_status_valid_next; m_axi_awaddr_reg <= m_axi_awaddr_next; @@ -624,6 +685,8 @@ always @(posedge clk) begin first_output_cycle_reg <= first_output_cycle_next; output_last_cycle_reg <= output_last_cycle_next; last_transfer_reg <= last_transfer_next; + rresp_reg <= rresp_next; + bresp_reg <= bresp_next; tag_reg <= tag_next; @@ -633,6 +696,7 @@ always @(posedge clk) begin if (status_fifo_we) begin status_fifo_tag[status_fifo_wr_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]] <= status_fifo_wr_tag; + status_fifo_resp[status_fifo_wr_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]] <= status_fifo_wr_resp; status_fifo_last[status_fifo_wr_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]] <= status_fifo_wr_last; status_fifo_wr_ptr_reg <= status_fifo_wr_ptr_reg + 1; end @@ -652,8 +716,6 @@ always @(posedge clk) begin read_state_reg <= READ_STATE_IDLE; axi_state_reg <= AXI_STATE_IDLE; - axi_cmd_valid_reg <= 1'b0; - s_axis_desc_ready_reg <= 1'b0; m_axis_desc_status_valid_reg <= 1'b0; @@ -662,6 +724,11 @@ always @(posedge clk) begin m_axi_arvalid_reg <= 1'b0; m_axi_rready_reg <= 1'b0; + axi_cmd_valid_reg <= 1'b0; + + rresp_reg <= AXI_RESP_OKAY; + bresp_reg <= AXI_RESP_OKAY; + status_fifo_wr_ptr_reg <= 0; status_fifo_rd_ptr_reg <= 0; @@ -674,80 +741,53 @@ end reg [AXI_DATA_WIDTH-1:0] m_axi_wdata_reg = {AXI_DATA_WIDTH{1'b0}}; reg [AXI_STRB_WIDTH-1:0] m_axi_wstrb_reg = {AXI_STRB_WIDTH{1'b0}}; reg m_axi_wlast_reg = 1'b0; -reg m_axi_wvalid_reg = 1'b0, m_axi_wvalid_next; +reg m_axi_wvalid_reg = 1'b0; -reg [AXI_DATA_WIDTH-1:0] temp_m_axi_wdata_reg = {AXI_DATA_WIDTH{1'b0}}; -reg [AXI_STRB_WIDTH-1:0] temp_m_axi_wstrb_reg = {AXI_STRB_WIDTH{1'b0}}; -reg temp_m_axi_wlast_reg = 1'b0; -reg temp_m_axi_wvalid_reg = 1'b0, temp_m_axi_wvalid_next; +reg [OUTPUT_FIFO_ADDR_WIDTH+1-1:0] out_fifo_wr_ptr_reg = 0; +reg [OUTPUT_FIFO_ADDR_WIDTH+1-1:0] out_fifo_rd_ptr_reg = 0; +reg out_fifo_half_full_reg = 1'b0; -// datapath control -reg store_axi_w_int_to_output; -reg store_axi_w_int_to_temp; -reg store_axi_w_temp_to_output; +wire out_fifo_full = out_fifo_wr_ptr_reg == (out_fifo_rd_ptr_reg ^ {1'b1, {OUTPUT_FIFO_ADDR_WIDTH{1'b0}}}); +wire out_fifo_empty = out_fifo_wr_ptr_reg == out_fifo_rd_ptr_reg; + +(* ram_style = "distributed" *) +reg [AXI_DATA_WIDTH-1:0] out_fifo_wdata[2**OUTPUT_FIFO_ADDR_WIDTH-1:0]; +(* ram_style = "distributed" *) +reg [AXI_STRB_WIDTH-1:0] out_fifo_wstrb[2**OUTPUT_FIFO_ADDR_WIDTH-1:0]; +(* ram_style = "distributed" *) +reg out_fifo_wlast[2**OUTPUT_FIFO_ADDR_WIDTH-1:0]; + +assign m_axi_wready_int = !out_fifo_half_full_reg; assign m_axi_wdata = m_axi_wdata_reg; assign m_axi_wstrb = m_axi_wstrb_reg; assign m_axi_wvalid = m_axi_wvalid_reg; assign m_axi_wlast = m_axi_wlast_reg; -// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) -assign m_axi_wready_int_early = m_axi_wready || (!temp_m_axi_wvalid_reg && (!m_axi_wvalid_reg || !m_axi_wvalid_int)); - -always @* begin - // transfer sink ready state to source - m_axi_wvalid_next = m_axi_wvalid_reg; - temp_m_axi_wvalid_next = temp_m_axi_wvalid_reg; - - store_axi_w_int_to_output = 1'b0; - store_axi_w_int_to_temp = 1'b0; - store_axi_w_temp_to_output = 1'b0; - - if (m_axi_wready_int_reg) begin - // input is ready - if (m_axi_wready || !m_axi_wvalid_reg) begin - // output is ready or currently not valid, transfer data to output - m_axi_wvalid_next = m_axi_wvalid_int; - store_axi_w_int_to_output = 1'b1; - end else begin - // output is not ready, store input in temp - temp_m_axi_wvalid_next = m_axi_wvalid_int; - store_axi_w_int_to_temp = 1'b1; - end - end else if (m_axi_wready) begin - // input is not ready, but output is ready - m_axi_wvalid_next = temp_m_axi_wvalid_reg; - temp_m_axi_wvalid_next = 1'b0; - store_axi_w_temp_to_output = 1'b1; - end -end - always @(posedge clk) begin + m_axi_wvalid_reg <= m_axi_wvalid_reg && !m_axi_wready; + + out_fifo_half_full_reg <= $unsigned(out_fifo_wr_ptr_reg - out_fifo_rd_ptr_reg) >= 2**(OUTPUT_FIFO_ADDR_WIDTH-1); + + if (!out_fifo_full && m_axi_wvalid_int) begin + out_fifo_wdata[out_fifo_wr_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]] <= m_axi_wdata_int; + out_fifo_wstrb[out_fifo_wr_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]] <= m_axi_wstrb_int; + out_fifo_wlast[out_fifo_wr_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]] <= m_axi_wlast_int; + out_fifo_wr_ptr_reg <= out_fifo_wr_ptr_reg + 1; + end + + if (!out_fifo_empty && (!m_axi_wvalid_reg || m_axi_wready)) begin + m_axi_wdata_reg <= out_fifo_wdata[out_fifo_rd_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]]; + m_axi_wstrb_reg <= out_fifo_wstrb[out_fifo_rd_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]]; + m_axi_wlast_reg <= out_fifo_wlast[out_fifo_rd_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]]; + m_axi_wvalid_reg <= 1'b1; + out_fifo_rd_ptr_reg <= out_fifo_rd_ptr_reg + 1; + end + if (rst) begin + out_fifo_wr_ptr_reg <= 0; + out_fifo_rd_ptr_reg <= 0; m_axi_wvalid_reg <= 1'b0; - m_axi_wready_int_reg <= 1'b0; - temp_m_axi_wvalid_reg <= 1'b0; - end else begin - m_axi_wvalid_reg <= m_axi_wvalid_next; - m_axi_wready_int_reg <= m_axi_wready_int_early; - temp_m_axi_wvalid_reg <= temp_m_axi_wvalid_next; - end - - // datapath - if (store_axi_w_int_to_output) begin - m_axi_wdata_reg <= m_axi_wdata_int; - m_axi_wstrb_reg <= m_axi_wstrb_int; - m_axi_wlast_reg <= m_axi_wlast_int; - end else if (store_axi_w_temp_to_output) begin - m_axi_wdata_reg <= temp_m_axi_wdata_reg; - m_axi_wstrb_reg <= temp_m_axi_wstrb_reg; - m_axi_wlast_reg <= temp_m_axi_wlast_reg; - end - - if (store_axi_w_int_to_temp) begin - temp_m_axi_wdata_reg <= m_axi_wdata_int; - temp_m_axi_wstrb_reg <= m_axi_wstrb_int; - temp_m_axi_wlast_reg <= m_axi_wlast_int; end end diff --git a/fpga/lib/axi/rtl/axi_cdma_desc_mux.v b/fpga/lib/axi/rtl/axi_cdma_desc_mux.v index 0acb3840d..9aa3ddc6f 100644 --- a/fpga/lib/axi/rtl/axi_cdma_desc_mux.v +++ b/fpga/lib/axi/rtl/axi_cdma_desc_mux.v @@ -65,6 +65,7 @@ module axi_cdma_desc_mux # * Descriptor status input (from AXI CDMA core) */ input wire [M_TAG_WIDTH-1:0] s_axis_desc_status_tag, + input wire [3:0] s_axis_desc_status_error, input wire s_axis_desc_status_valid, /* @@ -81,6 +82,7 @@ module axi_cdma_desc_mux # * Descriptor status output */ output wire [PORTS*S_TAG_WIDTH-1:0] m_axis_desc_status_tag, + output wire [PORTS*4-1:0] m_axis_desc_status_error, output wire [PORTS-1:0] m_axis_desc_status_valid ); @@ -238,9 +240,11 @@ end // descriptor status demux reg [S_TAG_WIDTH-1:0] m_axis_desc_status_tag_reg = {S_TAG_WIDTH{1'b0}}; +reg [3:0] m_axis_desc_status_error_reg = 4'd0; reg [PORTS-1:0] m_axis_desc_status_valid_reg = {PORTS{1'b0}}; assign m_axis_desc_status_tag = {PORTS{m_axis_desc_status_tag_reg}}; +assign m_axis_desc_status_error = {PORTS{m_axis_desc_status_error_reg}}; assign m_axis_desc_status_valid = m_axis_desc_status_valid_reg; always @(posedge clk) begin @@ -251,6 +255,7 @@ always @(posedge clk) begin end m_axis_desc_status_tag_reg <= s_axis_desc_status_tag; + m_axis_desc_status_error_reg <= s_axis_desc_status_error; end endmodule diff --git a/fpga/lib/axi/rtl/axi_crossbar_addr.v b/fpga/lib/axi/rtl/axi_crossbar_addr.v index 361fb289f..6ca2c980d 100644 --- a/fpga/lib/axi/rtl/axi_crossbar_addr.v +++ b/fpga/lib/axi/rtl/axi_crossbar_addr.v @@ -279,7 +279,6 @@ always @* begin m_axi_aregion_next = m_axi_aregion_reg; m_select_next = m_select_reg; - m_select_next = m_select_reg; m_axi_avalid_next = m_axi_avalid_reg && !m_axi_aready; m_decerr_next = m_decerr_reg; m_wc_valid_next = m_wc_valid_reg && !m_wc_ready; diff --git a/fpga/lib/axi/rtl/axi_dma.v b/fpga/lib/axi/rtl/axi_dma.v index b4a3f3ac4..5e82d7711 100644 --- a/fpga/lib/axi/rtl/axi_dma.v +++ b/fpga/lib/axi/rtl/axi_dma.v @@ -91,6 +91,7 @@ module axi_dma # * AXI read descriptor status output */ output wire [TAG_WIDTH-1:0] m_axis_read_desc_status_tag, + output wire [3:0] m_axis_read_desc_status_error, output wire m_axis_read_desc_status_valid, /* @@ -122,6 +123,7 @@ module axi_dma # output wire [AXIS_ID_WIDTH-1:0] m_axis_write_desc_status_id, output wire [AXIS_DEST_WIDTH-1:0] m_axis_write_desc_status_dest, output wire [AXIS_USER_WIDTH-1:0] m_axis_write_desc_status_user, + output wire [3:0] m_axis_write_desc_status_error, output wire m_axis_write_desc_status_valid, /* @@ -224,6 +226,7 @@ axi_dma_rd_inst ( * AXI read descriptor status output */ .m_axis_read_desc_status_tag(m_axis_read_desc_status_tag), + .m_axis_read_desc_status_error(m_axis_read_desc_status_error), .m_axis_read_desc_status_valid(m_axis_read_desc_status_valid), /* @@ -306,6 +309,7 @@ axi_dma_wr_inst ( .m_axis_write_desc_status_id(m_axis_write_desc_status_id), .m_axis_write_desc_status_dest(m_axis_write_desc_status_dest), .m_axis_write_desc_status_user(m_axis_write_desc_status_user), + .m_axis_write_desc_status_error(m_axis_write_desc_status_error), .m_axis_write_desc_status_valid(m_axis_write_desc_status_valid), /* diff --git a/fpga/lib/axi/rtl/axi_dma_desc_mux.v b/fpga/lib/axi/rtl/axi_dma_desc_mux.v index e52f0f415..662f6b708 100644 --- a/fpga/lib/axi/rtl/axi_dma_desc_mux.v +++ b/fpga/lib/axi/rtl/axi_dma_desc_mux.v @@ -83,6 +83,7 @@ module axi_dma_desc_mux # input wire [AXIS_ID_WIDTH-1:0] s_axis_desc_status_id, input wire [AXIS_DEST_WIDTH-1:0] s_axis_desc_status_dest, input wire [AXIS_USER_WIDTH-1:0] s_axis_desc_status_user, + input wire [3:0] s_axis_desc_status_error, input wire s_axis_desc_status_valid, /* @@ -105,6 +106,7 @@ module axi_dma_desc_mux # output wire [PORTS*AXIS_ID_WIDTH-1:0] m_axis_desc_status_id, output wire [PORTS*AXIS_DEST_WIDTH-1:0] m_axis_desc_status_dest, output wire [PORTS*AXIS_USER_WIDTH-1:0] m_axis_desc_status_user, + output wire [PORTS*4-1:0] m_axis_desc_status_error, output wire [PORTS-1:0] m_axis_desc_status_valid ); @@ -284,6 +286,7 @@ reg [S_TAG_WIDTH-1:0] m_axis_desc_status_tag_reg = {S_TAG_WIDTH{1'b0}}; reg [AXIS_ID_WIDTH-1:0] m_axis_desc_status_id_reg = {AXIS_ID_WIDTH{1'b0}}; reg [AXIS_DEST_WIDTH-1:0] m_axis_desc_status_dest_reg = {AXIS_DEST_WIDTH{1'b0}}; reg [AXIS_USER_WIDTH-1:0] m_axis_desc_status_user_reg = {AXIS_USER_WIDTH{1'b0}}; +reg [3:0] m_axis_desc_status_error_reg = 4'd0; reg [PORTS-1:0] m_axis_desc_status_valid_reg = {PORTS{1'b0}}; assign m_axis_desc_status_len = {PORTS{m_axis_desc_status_len_reg}}; @@ -291,6 +294,7 @@ assign m_axis_desc_status_tag = {PORTS{m_axis_desc_status_tag_reg}}; assign m_axis_desc_status_id = AXIS_ID_ENABLE ? {PORTS{m_axis_desc_status_id_reg}} : {AXIS_ID_WIDTH*PORTS{1'b0}}; assign m_axis_desc_status_dest = AXIS_DEST_ENABLE ? {PORTS{m_axis_desc_status_dest_reg}} : {AXIS_DEST_WIDTH*PORTS{1'b0}}; assign m_axis_desc_status_user = AXIS_USER_ENABLE ? {PORTS{m_axis_desc_status_user_reg}} : {AXIS_USER_WIDTH*PORTS{1'b0}}; +assign m_axis_desc_status_error = {PORTS{m_axis_desc_status_error_reg}}; assign m_axis_desc_status_valid = m_axis_desc_status_valid_reg; always @(posedge clk) begin @@ -305,6 +309,7 @@ always @(posedge clk) begin m_axis_desc_status_id_reg <= s_axis_desc_status_id; m_axis_desc_status_dest_reg <= s_axis_desc_status_dest; m_axis_desc_status_user_reg <= s_axis_desc_status_user; + m_axis_desc_status_error_reg <= s_axis_desc_status_error; end endmodule diff --git a/fpga/lib/axi/rtl/axi_dma_rd.v b/fpga/lib/axi/rtl/axi_dma_rd.v index 08102fd9c..57f05439f 100644 --- a/fpga/lib/axi/rtl/axi_dma_rd.v +++ b/fpga/lib/axi/rtl/axi_dma_rd.v @@ -91,6 +91,7 @@ module axi_dma_rd # * AXI read descriptor status output */ output wire [TAG_WIDTH-1:0] m_axis_read_desc_status_tag, + output wire [3:0] m_axis_read_desc_status_error, output wire m_axis_read_desc_status_valid, /* @@ -145,6 +146,8 @@ parameter OFFSET_MASK = AXI_STRB_WIDTH > 1 ? {OFFSET_WIDTH{1'b1}} : 0; parameter ADDR_MASK = {AXI_ADDR_WIDTH{1'b1}} << $clog2(AXI_STRB_WIDTH); parameter CYCLE_COUNT_WIDTH = LEN_WIDTH - AXI_BURST_SIZE + 1; +parameter OUTPUT_FIFO_ADDR_WIDTH = 5; + // bus width assertions initial begin if (AXI_WORD_SIZE * AXI_STRB_WIDTH != AXI_DATA_WIDTH) begin @@ -183,6 +186,25 @@ initial begin end end +localparam [1:0] + AXI_RESP_OKAY = 2'b00, + AXI_RESP_EXOKAY = 2'b01, + AXI_RESP_SLVERR = 2'b10, + AXI_RESP_DECERR = 2'b11; + +localparam [3:0] + DMA_ERROR_NONE = 4'd0, + DMA_ERROR_TIMEOUT = 4'd1, + DMA_ERROR_PARITY = 4'd2, + DMA_ERROR_AXI_RD_SLVERR = 4'd4, + DMA_ERROR_AXI_RD_DECERR = 4'd5, + DMA_ERROR_AXI_WR_SLVERR = 4'd6, + DMA_ERROR_AXI_WR_DECERR = 4'd7, + DMA_ERROR_PCIE_FLR = 4'd8, + DMA_ERROR_PCIE_CPL_POISONED = 4'd9, + DMA_ERROR_PCIE_CPL_STATUS_UR = 4'd10, + DMA_ERROR_PCIE_CPL_STATUS_CA = 4'd11; + localparam [0:0] AXI_STATE_IDLE = 1'd0, AXI_STATE_START = 1'd1; @@ -223,6 +245,7 @@ reg output_active_reg = 1'b0, output_active_next; reg bubble_cycle_reg = 1'b0, bubble_cycle_next; reg first_cycle_reg = 1'b0, first_cycle_next; reg output_last_cycle_reg = 1'b0, output_last_cycle_next; +reg [1:0] rresp_reg = AXI_RESP_OKAY, rresp_next; reg [TAG_WIDTH-1:0] tag_reg = {TAG_WIDTH{1'b0}}, tag_next; reg [AXIS_ID_WIDTH-1:0] axis_id_reg = {AXIS_ID_WIDTH{1'b0}}, axis_id_next; @@ -232,6 +255,7 @@ reg [AXIS_USER_WIDTH-1:0] axis_user_reg = {AXIS_USER_WIDTH{1'b0}}, axis_user_nex reg s_axis_read_desc_ready_reg = 1'b0, s_axis_read_desc_ready_next; reg [TAG_WIDTH-1:0] m_axis_read_desc_status_tag_reg = {TAG_WIDTH{1'b0}}, m_axis_read_desc_status_tag_next; +reg [3:0] m_axis_read_desc_status_error_reg = 4'd0, m_axis_read_desc_status_error_next; reg m_axis_read_desc_status_valid_reg = 1'b0, m_axis_read_desc_status_valid_next; reg [AXI_ADDR_WIDTH-1:0] m_axi_araddr_reg = {AXI_ADDR_WIDTH{1'b0}}, m_axi_araddr_next; @@ -247,16 +271,16 @@ wire [AXI_DATA_WIDTH-1:0] shift_axi_rdata = {m_axi_rdata, save_axi_rdata_reg} >> reg [AXIS_DATA_WIDTH-1:0] m_axis_read_data_tdata_int; reg [AXIS_KEEP_WIDTH-1:0] m_axis_read_data_tkeep_int; reg m_axis_read_data_tvalid_int; -reg m_axis_read_data_tready_int_reg = 1'b0; +wire m_axis_read_data_tready_int; reg m_axis_read_data_tlast_int; reg [AXIS_ID_WIDTH-1:0] m_axis_read_data_tid_int; reg [AXIS_DEST_WIDTH-1:0] m_axis_read_data_tdest_int; reg [AXIS_USER_WIDTH-1:0] m_axis_read_data_tuser_int; -wire m_axis_read_data_tready_int_early; assign s_axis_read_desc_ready = s_axis_read_desc_ready_reg; assign m_axis_read_desc_status_tag = m_axis_read_desc_status_tag_reg; +assign m_axis_read_desc_status_error = m_axis_read_desc_status_error_reg; assign m_axis_read_desc_status_valid = m_axis_read_desc_status_valid_reg; assign m_axi_arid = {AXI_ID_WIDTH{1'b0}}; @@ -384,6 +408,7 @@ always @* begin axis_state_next = AXIS_STATE_IDLE; m_axis_read_desc_status_tag_next = m_axis_read_desc_status_tag_reg; + m_axis_read_desc_status_error_next = m_axis_read_desc_status_error_reg; m_axis_read_desc_status_valid_next = 1'b0; m_axis_read_data_tdata_int = shift_axi_rdata; @@ -414,6 +439,12 @@ always @* begin axis_dest_next = axis_dest_reg; axis_user_next = axis_user_reg; + if (m_axi_rready && m_axi_rvalid && (m_axi_rresp == AXI_RESP_SLVERR || m_axi_rresp == AXI_RESP_DECERR)) begin + rresp_next = m_axi_rresp; + end else begin + rresp_next = rresp_reg; + end + case (axis_state_reg) AXIS_STATE_IDLE: begin // idle state - load new descriptor to start operation @@ -441,15 +472,15 @@ always @* begin if (axis_cmd_valid_reg) begin axis_cmd_ready = 1'b1; - m_axi_rready_next = m_axis_read_data_tready_int_early; + m_axi_rready_next = m_axis_read_data_tready_int; axis_state_next = AXIS_STATE_READ; end end AXIS_STATE_READ: begin // handle AXI read data - m_axi_rready_next = m_axis_read_data_tready_int_early && input_active_reg; + m_axi_rready_next = m_axis_read_data_tready_int && input_active_reg; - if (m_axis_read_data_tready_int_reg && ((m_axi_rready && m_axi_rvalid) || !input_active_reg)) begin + if ((m_axi_rready && m_axi_rvalid) || !input_active_reg) begin // transfer in AXI read data transfer_in_save = m_axi_rready && m_axi_rvalid; @@ -461,7 +492,7 @@ always @* begin bubble_cycle_next = 1'b0; first_cycle_next = 1'b0; - m_axi_rready_next = m_axis_read_data_tready_int_early && input_active_next; + m_axi_rready_next = m_axis_read_data_tready_int && input_active_next; axis_state_next = AXIS_STATE_READ; end else begin // update counters @@ -490,13 +521,22 @@ always @* begin m_axis_read_data_tlast_int = 1'b1; m_axis_read_desc_status_tag_next = tag_reg; + if (rresp_next == AXI_RESP_SLVERR) begin + m_axis_read_desc_status_error_next = DMA_ERROR_AXI_RD_SLVERR; + end else if (rresp_next == AXI_RESP_DECERR) begin + m_axis_read_desc_status_error_next = DMA_ERROR_AXI_RD_DECERR; + end else begin + m_axis_read_desc_status_error_next = DMA_ERROR_NONE; + end m_axis_read_desc_status_valid_next = 1'b1; + rresp_next = AXI_RESP_OKAY; + m_axi_rready_next = 1'b0; axis_state_next = AXIS_STATE_IDLE; end else begin // more cycles in AXI transfer - m_axi_rready_next = m_axis_read_data_tready_int_early && input_active_next; + m_axi_rready_next = m_axis_read_data_tready_int && input_active_next; axis_state_next = AXIS_STATE_READ; end end @@ -513,8 +553,9 @@ always @(posedge clk) begin s_axis_read_desc_ready_reg <= s_axis_read_desc_ready_next; - m_axis_read_desc_status_valid_reg <= m_axis_read_desc_status_valid_next; m_axis_read_desc_status_tag_reg <= m_axis_read_desc_status_tag_next; + m_axis_read_desc_status_error_reg <= m_axis_read_desc_status_error_next; + m_axis_read_desc_status_valid_reg <= m_axis_read_desc_status_valid_next; m_axi_araddr_reg <= m_axi_araddr_next; m_axi_arlen_reg <= m_axi_arlen_next; @@ -545,6 +586,7 @@ always @(posedge clk) begin bubble_cycle_reg <= bubble_cycle_next; first_cycle_reg <= first_cycle_next; output_last_cycle_reg <= output_last_cycle_next; + rresp_reg <= rresp_next; tag_reg <= tag_next; axis_id_reg <= axis_id_next; @@ -566,30 +608,41 @@ always @(posedge clk) begin m_axis_read_desc_status_valid_reg <= 1'b0; m_axi_arvalid_reg <= 1'b0; m_axi_rready_reg <= 1'b0; + + rresp_reg <= AXI_RESP_OKAY; end end // output datapath logic reg [AXIS_DATA_WIDTH-1:0] m_axis_read_data_tdata_reg = {AXIS_DATA_WIDTH{1'b0}}; reg [AXIS_KEEP_WIDTH-1:0] m_axis_read_data_tkeep_reg = {AXIS_KEEP_WIDTH{1'b0}}; -reg m_axis_read_data_tvalid_reg = 1'b0, m_axis_read_data_tvalid_next; +reg m_axis_read_data_tvalid_reg = 1'b0; reg m_axis_read_data_tlast_reg = 1'b0; reg [AXIS_ID_WIDTH-1:0] m_axis_read_data_tid_reg = {AXIS_ID_WIDTH{1'b0}}; reg [AXIS_DEST_WIDTH-1:0] m_axis_read_data_tdest_reg = {AXIS_DEST_WIDTH{1'b0}}; reg [AXIS_USER_WIDTH-1:0] m_axis_read_data_tuser_reg = {AXIS_USER_WIDTH{1'b0}}; -reg [AXIS_DATA_WIDTH-1:0] temp_m_axis_read_data_tdata_reg = {AXIS_DATA_WIDTH{1'b0}}; -reg [AXIS_KEEP_WIDTH-1:0] temp_m_axis_read_data_tkeep_reg = {AXIS_KEEP_WIDTH{1'b0}}; -reg temp_m_axis_read_data_tvalid_reg = 1'b0, temp_m_axis_read_data_tvalid_next; -reg temp_m_axis_read_data_tlast_reg = 1'b0; -reg [AXIS_ID_WIDTH-1:0] temp_m_axis_read_data_tid_reg = {AXIS_ID_WIDTH{1'b0}}; -reg [AXIS_DEST_WIDTH-1:0] temp_m_axis_read_data_tdest_reg = {AXIS_DEST_WIDTH{1'b0}}; -reg [AXIS_USER_WIDTH-1:0] temp_m_axis_read_data_tuser_reg = {AXIS_USER_WIDTH{1'b0}}; +reg [OUTPUT_FIFO_ADDR_WIDTH+1-1:0] out_fifo_wr_ptr_reg = 0; +reg [OUTPUT_FIFO_ADDR_WIDTH+1-1:0] out_fifo_rd_ptr_reg = 0; +reg out_fifo_half_full_reg = 1'b0; -// datapath control -reg store_axis_int_to_output; -reg store_axis_int_to_temp; -reg store_axis_temp_to_output; +wire out_fifo_full = out_fifo_wr_ptr_reg == (out_fifo_rd_ptr_reg ^ {1'b1, {OUTPUT_FIFO_ADDR_WIDTH{1'b0}}}); +wire out_fifo_empty = out_fifo_wr_ptr_reg == out_fifo_rd_ptr_reg; + +(* ram_style = "distributed" *) +reg [AXIS_DATA_WIDTH-1:0] out_fifo_tdata[2**OUTPUT_FIFO_ADDR_WIDTH-1:0]; +(* ram_style = "distributed" *) +reg [AXIS_KEEP_WIDTH-1:0] out_fifo_tkeep[2**OUTPUT_FIFO_ADDR_WIDTH-1:0]; +(* ram_style = "distributed" *) +reg out_fifo_tlast[2**OUTPUT_FIFO_ADDR_WIDTH-1:0]; +(* ram_style = "distributed" *) +reg [AXIS_ID_WIDTH-1:0] out_fifo_tid[2**OUTPUT_FIFO_ADDR_WIDTH-1:0]; +(* ram_style = "distributed" *) +reg [AXIS_DEST_WIDTH-1:0] out_fifo_tdest[2**OUTPUT_FIFO_ADDR_WIDTH-1:0]; +(* ram_style = "distributed" *) +reg [AXIS_USER_WIDTH-1:0] out_fifo_tuser[2**OUTPUT_FIFO_ADDR_WIDTH-1:0]; + +assign m_axis_read_data_tready_int = !out_fifo_half_full_reg; assign m_axis_read_data_tdata = m_axis_read_data_tdata_reg; assign m_axis_read_data_tkeep = AXIS_KEEP_ENABLE ? m_axis_read_data_tkeep_reg : {AXIS_KEEP_WIDTH{1'b1}}; @@ -599,72 +652,36 @@ assign m_axis_read_data_tid = AXIS_ID_ENABLE ? m_axis_read_data_tid_reg : assign m_axis_read_data_tdest = AXIS_DEST_ENABLE ? m_axis_read_data_tdest_reg : {AXIS_DEST_WIDTH{1'b0}}; assign m_axis_read_data_tuser = AXIS_USER_ENABLE ? m_axis_read_data_tuser_reg : {AXIS_USER_WIDTH{1'b0}}; -// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) -assign m_axis_read_data_tready_int_early = m_axis_read_data_tready || (!temp_m_axis_read_data_tvalid_reg && (!m_axis_read_data_tvalid_reg || !m_axis_read_data_tvalid_int)); - -always @* begin - // transfer sink ready state to source - m_axis_read_data_tvalid_next = m_axis_read_data_tvalid_reg; - temp_m_axis_read_data_tvalid_next = temp_m_axis_read_data_tvalid_reg; - - store_axis_int_to_output = 1'b0; - store_axis_int_to_temp = 1'b0; - store_axis_temp_to_output = 1'b0; - - if (m_axis_read_data_tready_int_reg) begin - // input is ready - if (m_axis_read_data_tready || !m_axis_read_data_tvalid_reg) begin - // output is ready or currently not valid, transfer data to output - m_axis_read_data_tvalid_next = m_axis_read_data_tvalid_int; - store_axis_int_to_output = 1'b1; - end else begin - // output is not ready, store input in temp - temp_m_axis_read_data_tvalid_next = m_axis_read_data_tvalid_int; - store_axis_int_to_temp = 1'b1; - end - end else if (m_axis_read_data_tready) begin - // input is not ready, but output is ready - m_axis_read_data_tvalid_next = temp_m_axis_read_data_tvalid_reg; - temp_m_axis_read_data_tvalid_next = 1'b0; - store_axis_temp_to_output = 1'b1; - end -end - always @(posedge clk) begin + m_axis_read_data_tvalid_reg <= m_axis_read_data_tvalid_reg && !m_axis_read_data_tready; + + out_fifo_half_full_reg <= $unsigned(out_fifo_wr_ptr_reg - out_fifo_rd_ptr_reg) >= 2**(OUTPUT_FIFO_ADDR_WIDTH-1); + + if (!out_fifo_full && m_axis_read_data_tvalid_int) begin + out_fifo_tdata[out_fifo_wr_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]] <= m_axis_read_data_tdata_int; + out_fifo_tkeep[out_fifo_wr_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]] <= m_axis_read_data_tkeep_int; + out_fifo_tlast[out_fifo_wr_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]] <= m_axis_read_data_tlast_int; + out_fifo_tid[out_fifo_wr_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]] <= m_axis_read_data_tid_int; + out_fifo_tdest[out_fifo_wr_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]] <= m_axis_read_data_tdest_int; + out_fifo_tuser[out_fifo_wr_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]] <= m_axis_read_data_tuser_int; + out_fifo_wr_ptr_reg <= out_fifo_wr_ptr_reg + 1; + end + + if (!out_fifo_empty && (!m_axis_read_data_tvalid_reg || m_axis_read_data_tready)) begin + m_axis_read_data_tdata_reg <= out_fifo_tdata[out_fifo_rd_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]]; + m_axis_read_data_tkeep_reg <= out_fifo_tkeep[out_fifo_rd_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]]; + m_axis_read_data_tvalid_reg <= 1'b1; + m_axis_read_data_tlast_reg <= out_fifo_tlast[out_fifo_rd_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]]; + m_axis_read_data_tid_reg <= out_fifo_tid[out_fifo_rd_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]]; + m_axis_read_data_tdest_reg <= out_fifo_tdest[out_fifo_rd_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]]; + m_axis_read_data_tuser_reg <= out_fifo_tuser[out_fifo_rd_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]]; + out_fifo_rd_ptr_reg <= out_fifo_rd_ptr_reg + 1; + end + if (rst) begin + out_fifo_wr_ptr_reg <= 0; + out_fifo_rd_ptr_reg <= 0; m_axis_read_data_tvalid_reg <= 1'b0; - m_axis_read_data_tready_int_reg <= 1'b0; - temp_m_axis_read_data_tvalid_reg <= 1'b0; - end else begin - m_axis_read_data_tvalid_reg <= m_axis_read_data_tvalid_next; - m_axis_read_data_tready_int_reg <= m_axis_read_data_tready_int_early; - temp_m_axis_read_data_tvalid_reg <= temp_m_axis_read_data_tvalid_next; - end - - // datapath - if (store_axis_int_to_output) begin - m_axis_read_data_tdata_reg <= m_axis_read_data_tdata_int; - m_axis_read_data_tkeep_reg <= m_axis_read_data_tkeep_int; - m_axis_read_data_tlast_reg <= m_axis_read_data_tlast_int; - m_axis_read_data_tid_reg <= m_axis_read_data_tid_int; - m_axis_read_data_tdest_reg <= m_axis_read_data_tdest_int; - m_axis_read_data_tuser_reg <= m_axis_read_data_tuser_int; - end else if (store_axis_temp_to_output) begin - m_axis_read_data_tdata_reg <= temp_m_axis_read_data_tdata_reg; - m_axis_read_data_tkeep_reg <= temp_m_axis_read_data_tkeep_reg; - m_axis_read_data_tlast_reg <= temp_m_axis_read_data_tlast_reg; - m_axis_read_data_tid_reg <= temp_m_axis_read_data_tid_reg; - m_axis_read_data_tdest_reg <= temp_m_axis_read_data_tdest_reg; - m_axis_read_data_tuser_reg <= temp_m_axis_read_data_tuser_reg; - end - - if (store_axis_int_to_temp) begin - temp_m_axis_read_data_tdata_reg <= m_axis_read_data_tdata_int; - temp_m_axis_read_data_tkeep_reg <= m_axis_read_data_tkeep_int; - temp_m_axis_read_data_tlast_reg <= m_axis_read_data_tlast_int; - temp_m_axis_read_data_tid_reg <= m_axis_read_data_tid_int; - temp_m_axis_read_data_tdest_reg <= m_axis_read_data_tdest_int; - temp_m_axis_read_data_tuser_reg <= m_axis_read_data_tuser_int; end end diff --git a/fpga/lib/axi/rtl/axi_dma_wr.v b/fpga/lib/axi/rtl/axi_dma_wr.v index f314d6173..55be3812f 100644 --- a/fpga/lib/axi/rtl/axi_dma_wr.v +++ b/fpga/lib/axi/rtl/axi_dma_wr.v @@ -92,6 +92,7 @@ module axi_dma_wr # output wire [AXIS_ID_WIDTH-1:0] m_axis_write_desc_status_id, output wire [AXIS_DEST_WIDTH-1:0] m_axis_write_desc_status_dest, output wire [AXIS_USER_WIDTH-1:0] m_axis_write_desc_status_user, + output wire [3:0] m_axis_write_desc_status_error, output wire m_axis_write_desc_status_valid, /* @@ -151,6 +152,7 @@ parameter ADDR_MASK = {AXI_ADDR_WIDTH{1'b1}} << $clog2(AXI_STRB_WIDTH); parameter CYCLE_COUNT_WIDTH = LEN_WIDTH - AXI_BURST_SIZE + 1; parameter STATUS_FIFO_ADDR_WIDTH = 5; +parameter OUTPUT_FIFO_ADDR_WIDTH = 5; // bus width assertions initial begin @@ -190,6 +192,25 @@ initial begin end end +localparam [1:0] + AXI_RESP_OKAY = 2'b00, + AXI_RESP_EXOKAY = 2'b01, + AXI_RESP_SLVERR = 2'b10, + AXI_RESP_DECERR = 2'b11; + +localparam [3:0] + DMA_ERROR_NONE = 4'd0, + DMA_ERROR_TIMEOUT = 4'd1, + DMA_ERROR_PARITY = 4'd2, + DMA_ERROR_AXI_RD_SLVERR = 4'd4, + DMA_ERROR_AXI_RD_DECERR = 4'd5, + DMA_ERROR_AXI_WR_SLVERR = 4'd6, + DMA_ERROR_AXI_WR_DECERR = 4'd7, + DMA_ERROR_PCIE_FLR = 4'd8, + DMA_ERROR_PCIE_CPL_POISONED = 4'd9, + DMA_ERROR_PCIE_CPL_STATUS_UR = 4'd10, + DMA_ERROR_PCIE_CPL_STATUS_CA = 4'd11; + localparam [2:0] STATE_IDLE = 3'd0, STATE_START = 3'd1, @@ -223,6 +244,7 @@ reg first_cycle_reg = 1'b0, first_cycle_next; reg input_last_cycle_reg = 1'b0, input_last_cycle_next; reg output_last_cycle_reg = 1'b0, output_last_cycle_next; reg last_transfer_reg = 1'b0, last_transfer_next; +reg [1:0] bresp_reg = AXI_RESP_OKAY, bresp_next; reg [TAG_WIDTH-1:0] tag_reg = {TAG_WIDTH{1'b0}}, tag_next; reg [AXIS_ID_WIDTH-1:0] axis_id_reg = {AXIS_ID_WIDTH{1'b0}}, axis_id_next; @@ -256,6 +278,7 @@ reg [TAG_WIDTH-1:0] m_axis_write_desc_status_tag_reg = {TAG_WIDTH{1'b0}}, m_axis reg [AXIS_ID_WIDTH-1:0] m_axis_write_desc_status_id_reg = {AXIS_ID_WIDTH{1'b0}}, m_axis_write_desc_status_id_next; reg [AXIS_DEST_WIDTH-1:0] m_axis_write_desc_status_dest_reg = {AXIS_DEST_WIDTH{1'b0}}, m_axis_write_desc_status_dest_next; reg [AXIS_USER_WIDTH-1:0] m_axis_write_desc_status_user_reg = {AXIS_USER_WIDTH{1'b0}}, m_axis_write_desc_status_user_next; +reg [3:0] m_axis_write_desc_status_error_reg = 4'd0, m_axis_write_desc_status_error_next; reg m_axis_write_desc_status_valid_reg = 1'b0, m_axis_write_desc_status_valid_next; reg [AXI_ADDR_WIDTH-1:0] m_axi_awaddr_reg = {AXI_ADDR_WIDTH{1'b0}}, m_axi_awaddr_next; @@ -281,8 +304,7 @@ reg [AXI_DATA_WIDTH-1:0] m_axi_wdata_int; reg [AXI_STRB_WIDTH-1:0] m_axi_wstrb_int; reg m_axi_wlast_int; reg m_axi_wvalid_int; -reg m_axi_wready_int_reg = 1'b0; -wire m_axi_wready_int_early; +wire m_axi_wready_int; assign s_axis_write_desc_ready = s_axis_write_desc_ready_reg; @@ -291,6 +313,7 @@ assign m_axis_write_desc_status_tag = m_axis_write_desc_status_tag_reg; assign m_axis_write_desc_status_id = m_axis_write_desc_status_id_reg; assign m_axis_write_desc_status_dest = m_axis_write_desc_status_dest_reg; assign m_axis_write_desc_status_user = m_axis_write_desc_status_user_reg; +assign m_axis_write_desc_status_error = m_axis_write_desc_status_error_reg; assign m_axis_write_desc_status_valid = m_axis_write_desc_status_valid_reg; assign s_axis_write_data_tready = s_axis_write_data_tready_reg; @@ -345,6 +368,7 @@ always @* begin m_axis_write_desc_status_id_next = m_axis_write_desc_status_id_reg; m_axis_write_desc_status_dest_next = m_axis_write_desc_status_dest_reg; m_axis_write_desc_status_user_next = m_axis_write_desc_status_user_reg; + m_axis_write_desc_status_error_next = m_axis_write_desc_status_error_reg; m_axis_write_desc_status_valid_next = 1'b0; s_axis_write_data_tready_next = 1'b0; @@ -397,6 +421,12 @@ always @* begin status_fifo_wr_user = axis_user_reg; status_fifo_wr_last = 1'b0; + if (m_axi_bready && m_axi_bvalid && (m_axi_bresp == AXI_RESP_SLVERR || m_axi_bresp == AXI_RESP_DECERR)) begin + bresp_next = m_axi_bresp; + end else begin + bresp_next = bresp_reg; + end + case (state_reg) STATE_IDLE: begin // idle state - load new descriptor to start operation @@ -480,7 +510,7 @@ always @* begin addr_next = addr_reg + tr_word_count_next; op_word_count_next = op_word_count_reg - tr_word_count_next; - s_axis_write_data_tready_next = m_axi_wready_int_early && input_active_next; + s_axis_write_data_tready_next = m_axi_wready_int && input_active_next; inc_active = 1'b1; @@ -493,9 +523,9 @@ always @* begin end end STATE_WRITE: begin - s_axis_write_data_tready_next = m_axi_wready_int_early && (last_transfer_reg || input_active_reg) && shift_axis_input_tready; + s_axis_write_data_tready_next = m_axi_wready_int && (last_transfer_reg || input_active_reg) && shift_axis_input_tready; - if (m_axi_wready_int_reg && ((s_axis_write_data_tready && shift_axis_tvalid) || (!input_active_reg && !last_transfer_reg) || !shift_axis_input_tready)) begin + if ((s_axis_write_data_tready && shift_axis_tvalid) || (!input_active_reg && !last_transfer_reg) || !shift_axis_input_tready) begin if (s_axis_write_data_tready && s_axis_write_data_tvalid) begin transfer_in_save = 1'b1; @@ -659,7 +689,7 @@ always @* begin end end end else begin - s_axis_write_data_tready_next = m_axi_wready_int_early && (last_transfer_reg || input_active_next) && shift_axis_input_tready; + s_axis_write_data_tready_next = m_axi_wready_int && (last_transfer_reg || input_active_next) && shift_axis_input_tready; state_next = STATE_WRITE; end end else begin @@ -669,7 +699,7 @@ always @* begin STATE_FINISH_BURST: begin // finish current AXI burst - if (m_axi_wready_int_reg) begin + if (m_axi_wready_int) begin // update counters if (input_active_reg) begin input_cycle_count_next = input_cycle_count_reg - 1; @@ -729,10 +759,21 @@ always @* begin m_axis_write_desc_status_id_next = status_fifo_id[status_fifo_rd_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]]; m_axis_write_desc_status_dest_next = status_fifo_dest[status_fifo_rd_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]]; m_axis_write_desc_status_user_next = status_fifo_user[status_fifo_rd_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]]; + if (bresp_next == AXI_RESP_SLVERR) begin + m_axis_write_desc_status_error_next = DMA_ERROR_AXI_WR_SLVERR; + end else if (bresp_next == AXI_RESP_DECERR) begin + m_axis_write_desc_status_error_next = DMA_ERROR_AXI_WR_DECERR; + end else begin + m_axis_write_desc_status_error_next = DMA_ERROR_NONE; + end m_axis_write_desc_status_valid_next = status_fifo_last[status_fifo_rd_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]]; status_fifo_rd_ptr_next = status_fifo_rd_ptr_reg + 1; m_axi_bready_next = 1'b0; + if (status_fifo_last[status_fifo_rd_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]]) begin + bresp_next = AXI_RESP_OKAY; + end + dec_active = 1'b1; end else begin // wait for write completion @@ -751,6 +792,7 @@ always @(posedge clk) begin m_axis_write_desc_status_id_reg <= m_axis_write_desc_status_id_next; m_axis_write_desc_status_dest_reg <= m_axis_write_desc_status_dest_next; m_axis_write_desc_status_user_reg <= m_axis_write_desc_status_user_next; + m_axis_write_desc_status_error_reg <= m_axis_write_desc_status_error_next; m_axis_write_desc_status_valid_reg <= m_axis_write_desc_status_valid_next; s_axis_write_data_tready_reg <= s_axis_write_data_tready_next; @@ -775,6 +817,7 @@ always @(posedge clk) begin input_last_cycle_reg <= input_last_cycle_next; output_last_cycle_reg <= output_last_cycle_next; last_transfer_reg <= last_transfer_next; + bresp_reg <= bresp_next; tag_reg <= tag_next; axis_id_reg <= axis_id_next; @@ -825,6 +868,8 @@ always @(posedge clk) begin m_axi_awvalid_reg <= 1'b0; m_axi_bready_reg <= 1'b0; + bresp_reg <= AXI_RESP_OKAY; + save_axis_tlast_reg <= 1'b0; shift_axis_extra_cycle_reg <= 1'b0; @@ -840,80 +885,53 @@ end reg [AXI_DATA_WIDTH-1:0] m_axi_wdata_reg = {AXI_DATA_WIDTH{1'b0}}; reg [AXI_STRB_WIDTH-1:0] m_axi_wstrb_reg = {AXI_STRB_WIDTH{1'b0}}; reg m_axi_wlast_reg = 1'b0; -reg m_axi_wvalid_reg = 1'b0, m_axi_wvalid_next; +reg m_axi_wvalid_reg = 1'b0; -reg [AXI_DATA_WIDTH-1:0] temp_m_axi_wdata_reg = {AXI_DATA_WIDTH{1'b0}}; -reg [AXI_STRB_WIDTH-1:0] temp_m_axi_wstrb_reg = {AXI_STRB_WIDTH{1'b0}}; -reg temp_m_axi_wlast_reg = 1'b0; -reg temp_m_axi_wvalid_reg = 1'b0, temp_m_axi_wvalid_next; +reg [OUTPUT_FIFO_ADDR_WIDTH+1-1:0] out_fifo_wr_ptr_reg = 0; +reg [OUTPUT_FIFO_ADDR_WIDTH+1-1:0] out_fifo_rd_ptr_reg = 0; +reg out_fifo_half_full_reg = 1'b0; -// datapath control -reg store_axi_w_int_to_output; -reg store_axi_w_int_to_temp; -reg store_axi_w_temp_to_output; +wire out_fifo_full = out_fifo_wr_ptr_reg == (out_fifo_rd_ptr_reg ^ {1'b1, {OUTPUT_FIFO_ADDR_WIDTH{1'b0}}}); +wire out_fifo_empty = out_fifo_wr_ptr_reg == out_fifo_rd_ptr_reg; + +(* ram_style = "distributed" *) +reg [AXI_DATA_WIDTH-1:0] out_fifo_wdata[2**OUTPUT_FIFO_ADDR_WIDTH-1:0]; +(* ram_style = "distributed" *) +reg [AXI_STRB_WIDTH-1:0] out_fifo_wstrb[2**OUTPUT_FIFO_ADDR_WIDTH-1:0]; +(* ram_style = "distributed" *) +reg out_fifo_wlast[2**OUTPUT_FIFO_ADDR_WIDTH-1:0]; + +assign m_axi_wready_int = !out_fifo_half_full_reg; assign m_axi_wdata = m_axi_wdata_reg; assign m_axi_wstrb = m_axi_wstrb_reg; assign m_axi_wvalid = m_axi_wvalid_reg; assign m_axi_wlast = m_axi_wlast_reg; -// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) -assign m_axi_wready_int_early = m_axi_wready || (!temp_m_axi_wvalid_reg && (!m_axi_wvalid_reg || !m_axi_wvalid_int)); - -always @* begin - // transfer sink ready state to source - m_axi_wvalid_next = m_axi_wvalid_reg; - temp_m_axi_wvalid_next = temp_m_axi_wvalid_reg; - - store_axi_w_int_to_output = 1'b0; - store_axi_w_int_to_temp = 1'b0; - store_axi_w_temp_to_output = 1'b0; - - if (m_axi_wready_int_reg) begin - // input is ready - if (m_axi_wready || !m_axi_wvalid_reg) begin - // output is ready or currently not valid, transfer data to output - m_axi_wvalid_next = m_axi_wvalid_int; - store_axi_w_int_to_output = 1'b1; - end else begin - // output is not ready, store input in temp - temp_m_axi_wvalid_next = m_axi_wvalid_int; - store_axi_w_int_to_temp = 1'b1; - end - end else if (m_axi_wready) begin - // input is not ready, but output is ready - m_axi_wvalid_next = temp_m_axi_wvalid_reg; - temp_m_axi_wvalid_next = 1'b0; - store_axi_w_temp_to_output = 1'b1; - end -end - always @(posedge clk) begin + m_axi_wvalid_reg <= m_axi_wvalid_reg && !m_axi_wready; + + out_fifo_half_full_reg <= $unsigned(out_fifo_wr_ptr_reg - out_fifo_rd_ptr_reg) >= 2**(OUTPUT_FIFO_ADDR_WIDTH-1); + + if (!out_fifo_full && m_axi_wvalid_int) begin + out_fifo_wdata[out_fifo_wr_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]] <= m_axi_wdata_int; + out_fifo_wstrb[out_fifo_wr_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]] <= m_axi_wstrb_int; + out_fifo_wlast[out_fifo_wr_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]] <= m_axi_wlast_int; + out_fifo_wr_ptr_reg <= out_fifo_wr_ptr_reg + 1; + end + + if (!out_fifo_empty && (!m_axi_wvalid_reg || m_axi_wready)) begin + m_axi_wdata_reg <= out_fifo_wdata[out_fifo_rd_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]]; + m_axi_wstrb_reg <= out_fifo_wstrb[out_fifo_rd_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]]; + m_axi_wlast_reg <= out_fifo_wlast[out_fifo_rd_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]]; + m_axi_wvalid_reg <= 1'b1; + out_fifo_rd_ptr_reg <= out_fifo_rd_ptr_reg + 1; + end + if (rst) begin + out_fifo_wr_ptr_reg <= 0; + out_fifo_rd_ptr_reg <= 0; m_axi_wvalid_reg <= 1'b0; - m_axi_wready_int_reg <= 1'b0; - temp_m_axi_wvalid_reg <= 1'b0; - end else begin - m_axi_wvalid_reg <= m_axi_wvalid_next; - m_axi_wready_int_reg <= m_axi_wready_int_early; - temp_m_axi_wvalid_reg <= temp_m_axi_wvalid_next; - end - - // datapath - if (store_axi_w_int_to_output) begin - m_axi_wdata_reg <= m_axi_wdata_int; - m_axi_wstrb_reg <= m_axi_wstrb_int; - m_axi_wlast_reg <= m_axi_wlast_int; - end else if (store_axi_w_temp_to_output) begin - m_axi_wdata_reg <= temp_m_axi_wdata_reg; - m_axi_wstrb_reg <= temp_m_axi_wstrb_reg; - m_axi_wlast_reg <= temp_m_axi_wlast_reg; - end - - if (store_axi_w_int_to_temp) begin - temp_m_axi_wdata_reg <= m_axi_wdata_int; - temp_m_axi_wstrb_reg <= m_axi_wstrb_int; - temp_m_axi_wlast_reg <= m_axi_wlast_int; end end diff --git a/fpga/lib/axi/rtl/axil_crossbar.v b/fpga/lib/axi/rtl/axil_crossbar.v new file mode 100644 index 000000000..0340a2edf --- /dev/null +++ b/fpga/lib/axi/rtl/axil_crossbar.v @@ -0,0 +1,247 @@ +/* + +Copyright (c) 2021 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 lite crossbar + */ +module axil_crossbar # +( + // Number of AXI inputs (slave interfaces) + parameter S_COUNT = 4, + // Number of AXI outputs (master interfaces) + parameter M_COUNT = 4, + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Number of concurrent operations for each slave interface + // S_COUNT concatenated fields of 32 bits + parameter S_ACCEPT = {S_COUNT{32'd16}}, + // Number of regions per master interface + parameter M_REGIONS = 1, + // Master interface base addresses + // M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_WIDTH bits + // set to zero for default addressing based on M_ADDR_WIDTH + parameter M_BASE_ADDR = 0, + // Master interface address widths + // M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits + parameter M_ADDR_WIDTH = {M_COUNT{{M_REGIONS{32'd24}}}}, + // Read connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT_READ = {M_COUNT{{S_COUNT{1'b1}}}}, + // Write connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT_WRITE = {M_COUNT{{S_COUNT{1'b1}}}}, + // Number of concurrent operations for each master interface + // M_COUNT concatenated fields of 32 bits + parameter M_ISSUE = {M_COUNT{32'd16}}, + // Secure master (fail operations based on awprot/arprot) + // M_COUNT bits + parameter M_SECURE = {M_COUNT{1'b0}}, + // Slave interface AW channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_AW_REG_TYPE = {S_COUNT{2'd0}}, + // Slave interface W channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_W_REG_TYPE = {S_COUNT{2'd0}}, + // Slave interface B channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_B_REG_TYPE = {S_COUNT{2'd1}}, + // Slave interface AR channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_AR_REG_TYPE = {S_COUNT{2'd0}}, + // Slave interface R channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_R_REG_TYPE = {S_COUNT{2'd2}}, + // Master interface AW channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_AW_REG_TYPE = {M_COUNT{2'd1}}, + // Master interface W channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_W_REG_TYPE = {M_COUNT{2'd2}}, + // Master interface B channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_B_REG_TYPE = {M_COUNT{2'd0}}, + // Master interface AR channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_AR_REG_TYPE = {M_COUNT{2'd1}}, + // Master interface R channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_R_REG_TYPE = {M_COUNT{2'd0}} +) +( + input wire clk, + input wire rst, + + /* + * AXI lite slave interfaces + */ + input wire [S_COUNT*ADDR_WIDTH-1:0] s_axil_awaddr, + input wire [S_COUNT*3-1:0] s_axil_awprot, + input wire [S_COUNT-1:0] s_axil_awvalid, + output wire [S_COUNT-1:0] s_axil_awready, + input wire [S_COUNT*DATA_WIDTH-1:0] s_axil_wdata, + input wire [S_COUNT*STRB_WIDTH-1:0] s_axil_wstrb, + input wire [S_COUNT-1:0] s_axil_wvalid, + output wire [S_COUNT-1:0] s_axil_wready, + output wire [S_COUNT*2-1:0] s_axil_bresp, + output wire [S_COUNT-1:0] s_axil_bvalid, + input wire [S_COUNT-1:0] s_axil_bready, + input wire [S_COUNT*ADDR_WIDTH-1:0] s_axil_araddr, + input wire [S_COUNT*3-1:0] s_axil_arprot, + input wire [S_COUNT-1:0] s_axil_arvalid, + output wire [S_COUNT-1:0] s_axil_arready, + output wire [S_COUNT*DATA_WIDTH-1:0] s_axil_rdata, + output wire [S_COUNT*2-1:0] s_axil_rresp, + output wire [S_COUNT-1:0] s_axil_rvalid, + input wire [S_COUNT-1:0] s_axil_rready, + + /* + * AXI lite master interfaces + */ + output wire [M_COUNT*ADDR_WIDTH-1:0] m_axil_awaddr, + output wire [M_COUNT*3-1:0] m_axil_awprot, + output wire [M_COUNT-1:0] m_axil_awvalid, + input wire [M_COUNT-1:0] m_axil_awready, + output wire [M_COUNT*DATA_WIDTH-1:0] m_axil_wdata, + output wire [M_COUNT*STRB_WIDTH-1:0] m_axil_wstrb, + output wire [M_COUNT-1:0] m_axil_wvalid, + input wire [M_COUNT-1:0] m_axil_wready, + input wire [M_COUNT*2-1:0] m_axil_bresp, + input wire [M_COUNT-1:0] m_axil_bvalid, + output wire [M_COUNT-1:0] m_axil_bready, + output wire [M_COUNT*ADDR_WIDTH-1:0] m_axil_araddr, + output wire [M_COUNT*3-1:0] m_axil_arprot, + output wire [M_COUNT-1:0] m_axil_arvalid, + input wire [M_COUNT-1:0] m_axil_arready, + input wire [M_COUNT*DATA_WIDTH-1:0] m_axil_rdata, + input wire [M_COUNT*2-1:0] m_axil_rresp, + input wire [M_COUNT-1:0] m_axil_rvalid, + output wire [M_COUNT-1:0] m_axil_rready +); + +axil_crossbar_wr #( + .S_COUNT(S_COUNT), + .M_COUNT(M_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .S_ACCEPT(S_ACCEPT), + .M_REGIONS(M_REGIONS), + .M_BASE_ADDR(M_BASE_ADDR), + .M_ADDR_WIDTH(M_ADDR_WIDTH), + .M_CONNECT(M_CONNECT_WRITE), + .M_ISSUE(M_ISSUE), + .M_SECURE(M_SECURE), + .S_AW_REG_TYPE(S_AW_REG_TYPE), + .S_W_REG_TYPE (S_W_REG_TYPE), + .S_B_REG_TYPE (S_B_REG_TYPE) +) +axil_crossbar_wr_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI lite slave interfaces + */ + .s_axil_awaddr(s_axil_awaddr), + .s_axil_awprot(s_axil_awprot), + .s_axil_awvalid(s_axil_awvalid), + .s_axil_awready(s_axil_awready), + .s_axil_wdata(s_axil_wdata), + .s_axil_wstrb(s_axil_wstrb), + .s_axil_wvalid(s_axil_wvalid), + .s_axil_wready(s_axil_wready), + .s_axil_bresp(s_axil_bresp), + .s_axil_bvalid(s_axil_bvalid), + .s_axil_bready(s_axil_bready), + + /* + * AXI lite master interfaces + */ + .m_axil_awaddr(m_axil_awaddr), + .m_axil_awprot(m_axil_awprot), + .m_axil_awvalid(m_axil_awvalid), + .m_axil_awready(m_axil_awready), + .m_axil_wdata(m_axil_wdata), + .m_axil_wstrb(m_axil_wstrb), + .m_axil_wvalid(m_axil_wvalid), + .m_axil_wready(m_axil_wready), + .m_axil_bresp(m_axil_bresp), + .m_axil_bvalid(m_axil_bvalid), + .m_axil_bready(m_axil_bready) +); + +axil_crossbar_rd #( + .S_COUNT(S_COUNT), + .M_COUNT(M_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .S_ACCEPT(S_ACCEPT), + .M_REGIONS(M_REGIONS), + .M_BASE_ADDR(M_BASE_ADDR), + .M_ADDR_WIDTH(M_ADDR_WIDTH), + .M_CONNECT(M_CONNECT_READ), + .M_ISSUE(M_ISSUE), + .M_SECURE(M_SECURE), + .S_AR_REG_TYPE(S_AR_REG_TYPE), + .S_R_REG_TYPE (S_R_REG_TYPE) +) +axil_crossbar_rd_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI lite slave interfaces + */ + .s_axil_araddr(s_axil_araddr), + .s_axil_arprot(s_axil_arprot), + .s_axil_arvalid(s_axil_arvalid), + .s_axil_arready(s_axil_arready), + .s_axil_rdata(s_axil_rdata), + .s_axil_rresp(s_axil_rresp), + .s_axil_rvalid(s_axil_rvalid), + .s_axil_rready(s_axil_rready), + + /* + * AXI lite master interfaces + */ + .m_axil_araddr(m_axil_araddr), + .m_axil_arprot(m_axil_arprot), + .m_axil_arvalid(m_axil_arvalid), + .m_axil_arready(m_axil_arready), + .m_axil_rdata(m_axil_rdata), + .m_axil_rresp(m_axil_rresp), + .m_axil_rvalid(m_axil_rvalid), + .m_axil_rready(m_axil_rready) +); + +endmodule diff --git a/fpga/lib/axi/rtl/axil_crossbar_addr.v b/fpga/lib/axi/rtl/axil_crossbar_addr.v new file mode 100644 index 000000000..1283bcb47 --- /dev/null +++ b/fpga/lib/axi/rtl/axil_crossbar_addr.v @@ -0,0 +1,263 @@ +/* + +Copyright (c) 2021 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 lite crossbar address decode and admission control + */ +module axil_crossbar_addr # +( + // Slave interface index + parameter S = 0, + // Number of AXI inputs (slave interfaces) + parameter S_COUNT = 4, + // Number of AXI outputs (master interfaces) + parameter M_COUNT = 4, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Number of regions per master interface + parameter M_REGIONS = 1, + // Master interface base addresses + // M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_WIDTH bits + // set to zero for default addressing based on M_ADDR_WIDTH + parameter M_BASE_ADDR = 0, + // Master interface address widths + // M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits + parameter M_ADDR_WIDTH = {M_COUNT{{M_REGIONS{32'd24}}}}, + // Connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT = {M_COUNT{{S_COUNT{1'b1}}}}, + // Secure master (fail operations based on awprot/arprot) + // M_COUNT bits + parameter M_SECURE = {M_COUNT{1'b0}}, + // Enable write command output + parameter WC_OUTPUT = 0 +) +( + input wire clk, + input wire rst, + + /* + * Address input + */ + input wire [ADDR_WIDTH-1:0] s_axil_aaddr, + input wire [2:0] s_axil_aprot, + input wire s_axil_avalid, + output wire s_axil_aready, + + /* + * Address output + */ + output wire [$clog2(M_COUNT)-1:0] m_select, + output wire m_axil_avalid, + input wire m_axil_aready, + + /* + * Write command output + */ + output wire [$clog2(M_COUNT)-1:0] m_wc_select, + output wire m_wc_decerr, + output wire m_wc_valid, + input wire m_wc_ready, + + /* + * Reply command output + */ + output wire [$clog2(M_COUNT)-1:0] m_rc_select, + output wire m_rc_decerr, + output wire m_rc_valid, + input wire m_rc_ready +); + +parameter CL_S_COUNT = $clog2(S_COUNT); +parameter CL_M_COUNT = $clog2(M_COUNT); + +// default address computation +function [M_COUNT*M_REGIONS*ADDR_WIDTH-1:0] calcBaseAddrs(input [31:0] dummy); + integer i; + reg [ADDR_WIDTH-1:0] base; + begin + calcBaseAddrs = {M_COUNT*M_REGIONS*ADDR_WIDTH{1'b0}}; + base = 0; + for (i = 1; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32]) begin + base = base + 2**M_ADDR_WIDTH[(i-1)*32 +: 32]; // increment + base = base - (base % 2**M_ADDR_WIDTH[i*32 +: 32]); // align + calcBaseAddrs[i * ADDR_WIDTH +: ADDR_WIDTH] = base; + end + end + end +endfunction + +parameter M_BASE_ADDR_INT = M_BASE_ADDR ? M_BASE_ADDR : calcBaseAddrs(0); + +integer i, j; + +// check configuration +initial begin + if (M_REGIONS < 1) begin + $error("Error: need at least 1 region (instance %m)"); + $finish; + end + + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32] && (M_ADDR_WIDTH[i*32 +: 32] < 12 || M_ADDR_WIDTH[i*32 +: 32] > ADDR_WIDTH)) begin + $error("Error: address width out of range (instance %m)"); + $finish; + end + end + + $display("Addressing configuration for axi_crossbar_addr instance %m"); + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32]) begin + $display("%2d (%2d): %x / %02d -- %x-%x", i/M_REGIONS, i%M_REGIONS, M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[i*32 +: 32], M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))); + end + end + + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + for (j = i+1; j < M_COUNT*M_REGIONS; j = j + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32] && M_ADDR_WIDTH[j*32 +: 32]) begin + if (((M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32])) <= (M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32])))) && ((M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32])) <= (M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))))) begin + $display("Overlapping regions:"); + $display("%2d (%2d): %x / %2d -- %x-%x", i/M_REGIONS, i%M_REGIONS, M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[i*32 +: 32], M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))); + $display("%2d (%2d): %x / %2d -- %x-%x", j/M_REGIONS, j%M_REGIONS, M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[j*32 +: 32], M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32]), M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32]))); + $error("Error: address ranges overlap (instance %m)"); + $finish; + end + end + end + end +end + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_DECODE = 3'd1; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +reg s_axil_aready_reg = 0, s_axil_aready_next; + +reg [CL_M_COUNT-1:0] m_select_reg = 0, m_select_next; +reg m_axil_avalid_reg = 1'b0, m_axil_avalid_next; +reg m_decerr_reg = 1'b0, m_decerr_next; +reg m_wc_valid_reg = 1'b0, m_wc_valid_next; +reg m_rc_valid_reg = 1'b0, m_rc_valid_next; + +assign s_axil_aready = s_axil_aready_reg; + +assign m_select = m_select_reg; +assign m_axil_avalid = m_axil_avalid_reg; + +assign m_wc_select = m_select_reg; +assign m_wc_decerr = m_decerr_reg; +assign m_wc_valid = m_wc_valid_reg; + +assign m_rc_select = m_select_reg; +assign m_rc_decerr = m_decerr_reg; +assign m_rc_valid = m_rc_valid_reg; + +reg match; + +always @* begin + state_next = STATE_IDLE; + + match = 1'b0; + + s_axil_aready_next = 1'b0; + + m_select_next = m_select_reg; + m_axil_avalid_next = m_axil_avalid_reg && !m_axil_aready; + m_decerr_next = m_decerr_reg; + m_wc_valid_next = m_wc_valid_reg && !m_wc_ready; + m_rc_valid_next = m_rc_valid_reg && !m_rc_ready; + + case (state_reg) + STATE_IDLE: begin + // idle state, store values + s_axil_aready_next = 1'b0; + + if (s_axil_avalid && !s_axil_aready) begin + match = 1'b0; + for (i = 0; i < M_COUNT; i = i + 1) begin + for (j = 0; j < M_REGIONS; j = j + 1) begin + if (M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32] && (!M_SECURE[i] || !s_axil_aprot[1]) && (M_CONNECT & (1 << (S+i*S_COUNT))) && (s_axil_aaddr >> M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32]) == (M_BASE_ADDR_INT[(i*M_REGIONS+j)*ADDR_WIDTH +: ADDR_WIDTH] >> M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32])) begin + m_select_next = i; + match = 1'b1; + end + end + end + + if (match) begin + // address decode successful + m_axil_avalid_next = 1'b1; + m_decerr_next = 1'b0; + m_wc_valid_next = WC_OUTPUT; + m_rc_valid_next = 1'b1; + state_next = STATE_DECODE; + end else begin + // decode error + m_axil_avalid_next = 1'b0; + m_decerr_next = 1'b1; + m_wc_valid_next = WC_OUTPUT; + m_rc_valid_next = 1'b1; + state_next = STATE_DECODE; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_DECODE: begin + if (!m_axil_avalid_next && (!m_wc_valid_next || !WC_OUTPUT) && !m_rc_valid_next) begin + s_axil_aready_next = 1'b1; + state_next = STATE_IDLE; + end else begin + state_next = STATE_DECODE; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_axil_aready_reg <= 1'b0; + m_axil_avalid_reg <= 1'b0; + m_wc_valid_reg <= 1'b0; + m_rc_valid_reg <= 1'b0; + end else begin + state_reg <= state_next; + s_axil_aready_reg <= s_axil_aready_next; + m_axil_avalid_reg <= m_axil_avalid_next; + m_wc_valid_reg <= m_wc_valid_next; + m_rc_valid_reg <= m_rc_valid_next; + end + + m_select_reg <= m_select_next; + m_decerr_reg <= m_decerr_next; +end + +endmodule diff --git a/fpga/lib/axi/rtl/axil_crossbar_rd.v b/fpga/lib/axi/rtl/axil_crossbar_rd.v new file mode 100644 index 000000000..35989b176 --- /dev/null +++ b/fpga/lib/axi/rtl/axil_crossbar_rd.v @@ -0,0 +1,421 @@ +/* + +Copyright (c) 2021 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 lite crossbar (read) + */ +module axil_crossbar_rd # +( + // Number of AXI inputs (slave interfaces) + parameter S_COUNT = 4, + // Number of AXI outputs (master interfaces) + parameter M_COUNT = 4, + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Number of concurrent operations for each slave interface + // S_COUNT concatenated fields of 32 bits + parameter S_ACCEPT = {S_COUNT{32'd16}}, + // Number of regions per master interface + parameter M_REGIONS = 1, + // Master interface base addresses + // M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_WIDTH bits + // set to zero for default addressing based on M_ADDR_WIDTH + parameter M_BASE_ADDR = 0, + // Master interface address widths + // M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits + parameter M_ADDR_WIDTH = {M_COUNT{{M_REGIONS{32'd24}}}}, + // Read connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT = {M_COUNT{{S_COUNT{1'b1}}}}, + // Number of concurrent operations for each master interface + // M_COUNT concatenated fields of 32 bits + parameter M_ISSUE = {M_COUNT{32'd16}}, + // Secure master (fail operations based on awprot/arprot) + // M_COUNT bits + parameter M_SECURE = {M_COUNT{1'b0}}, + // Slave interface AR channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_AR_REG_TYPE = {S_COUNT{2'd0}}, + // Slave interface R channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_R_REG_TYPE = {S_COUNT{2'd2}}, + // Master interface AR channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_AR_REG_TYPE = {M_COUNT{2'd1}}, + // Master interface R channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_R_REG_TYPE = {M_COUNT{2'd0}} +) +( + input wire clk, + input wire rst, + + /* + * AXI lite slave interfaces + */ + input wire [S_COUNT*ADDR_WIDTH-1:0] s_axil_araddr, + input wire [S_COUNT*3-1:0] s_axil_arprot, + input wire [S_COUNT-1:0] s_axil_arvalid, + output wire [S_COUNT-1:0] s_axil_arready, + output wire [S_COUNT*DATA_WIDTH-1:0] s_axil_rdata, + output wire [S_COUNT*2-1:0] s_axil_rresp, + output wire [S_COUNT-1:0] s_axil_rvalid, + input wire [S_COUNT-1:0] s_axil_rready, + + /* + * AXI lite master interfaces + */ + output wire [M_COUNT*ADDR_WIDTH-1:0] m_axil_araddr, + output wire [M_COUNT*3-1:0] m_axil_arprot, + output wire [M_COUNT-1:0] m_axil_arvalid, + input wire [M_COUNT-1:0] m_axil_arready, + input wire [M_COUNT*DATA_WIDTH-1:0] m_axil_rdata, + input wire [M_COUNT*2-1:0] m_axil_rresp, + input wire [M_COUNT-1:0] m_axil_rvalid, + output wire [M_COUNT-1:0] m_axil_rready +); + +parameter CL_S_COUNT = $clog2(S_COUNT); +parameter CL_M_COUNT = $clog2(M_COUNT); +parameter M_COUNT_P1 = M_COUNT+1; +parameter CL_M_COUNT_P1 = $clog2(M_COUNT_P1); + +integer i; + +// check configuration +initial begin + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32] && (M_ADDR_WIDTH[i*32 +: 32] < 12 || M_ADDR_WIDTH[i*32 +: 32] > ADDR_WIDTH)) begin + $error("Error: value out of range (instance %m)"); + $finish; + end + end +end + +wire [S_COUNT*ADDR_WIDTH-1:0] int_s_axil_araddr; +wire [S_COUNT*3-1:0] int_s_axil_arprot; +wire [S_COUNT-1:0] int_s_axil_arvalid; +wire [S_COUNT-1:0] int_s_axil_arready; + +wire [S_COUNT*M_COUNT-1:0] int_axil_arvalid; +wire [M_COUNT*S_COUNT-1:0] int_axil_arready; + +wire [M_COUNT*DATA_WIDTH-1:0] int_m_axil_rdata; +wire [M_COUNT*2-1:0] int_m_axil_rresp; +wire [M_COUNT-1:0] int_m_axil_rvalid; +wire [M_COUNT-1:0] int_m_axil_rready; + +wire [M_COUNT*S_COUNT-1:0] int_axil_rvalid; +wire [S_COUNT*M_COUNT-1:0] int_axil_rready; + +generate + + genvar m, n; + + for (m = 0; m < S_COUNT; m = m + 1) begin : s_ifaces + // response routing FIFO + localparam FIFO_ADDR_WIDTH = $clog2(S_ACCEPT[m*32 +: 32])+1; + + reg [FIFO_ADDR_WIDTH+1-1:0] fifo_wr_ptr_reg = 0; + reg [FIFO_ADDR_WIDTH+1-1:0] fifo_rd_ptr_reg = 0; + + reg [CL_M_COUNT-1:0] fifo_select[(2**FIFO_ADDR_WIDTH)-1:0]; + reg fifo_decerr[(2**FIFO_ADDR_WIDTH)-1:0]; + wire [CL_M_COUNT-1:0] fifo_wr_select; + wire fifo_wr_decerr; + wire fifo_wr_en; + wire fifo_rd_en; + reg fifo_half_full_reg = 1'b0; + + wire fifo_empty = fifo_rd_ptr_reg == fifo_wr_ptr_reg; + + integer i; + + initial begin + for (i = 0; i < 2**FIFO_ADDR_WIDTH; i = i + 1) begin + fifo_select[i] = 0; + fifo_decerr[i] = 0; + end + end + + always @(posedge clk) begin + if (fifo_wr_en) begin + fifo_select[fifo_wr_ptr_reg[FIFO_ADDR_WIDTH-1:0]] <= fifo_wr_select; + fifo_decerr[fifo_wr_ptr_reg[FIFO_ADDR_WIDTH-1:0]] <= fifo_wr_decerr; + fifo_wr_ptr_reg <= fifo_wr_ptr_reg + 1; + end + if (fifo_rd_en) begin + fifo_rd_ptr_reg <= fifo_rd_ptr_reg + 1; + end + + fifo_half_full_reg <= $unsigned(fifo_wr_ptr_reg - fifo_rd_ptr_reg) >= 2**(FIFO_ADDR_WIDTH-1); + + if (rst) begin + fifo_wr_ptr_reg <= 0; + fifo_rd_ptr_reg <= 0; + end + end + + // address decode and admission control + wire [CL_M_COUNT-1:0] a_select; + + wire m_axil_avalid; + wire m_axil_aready; + + wire [CL_M_COUNT-1:0] m_rc_select; + wire m_rc_decerr; + wire m_rc_valid; + wire m_rc_ready; + + axil_crossbar_addr #( + .S(m), + .S_COUNT(S_COUNT), + .M_COUNT(M_COUNT), + .ADDR_WIDTH(ADDR_WIDTH), + .M_REGIONS(M_REGIONS), + .M_BASE_ADDR(M_BASE_ADDR), + .M_ADDR_WIDTH(M_ADDR_WIDTH), + .M_CONNECT(M_CONNECT), + .M_SECURE(M_SECURE), + .WC_OUTPUT(0) + ) + addr_inst ( + .clk(clk), + .rst(rst), + + /* + * Address input + */ + .s_axil_aaddr(int_s_axil_araddr[m*ADDR_WIDTH +: ADDR_WIDTH]), + .s_axil_aprot(int_s_axil_arprot[m*3 +: 3]), + .s_axil_avalid(int_s_axil_arvalid[m]), + .s_axil_aready(int_s_axil_arready[m]), + + /* + * Address output + */ + .m_select(a_select), + .m_axil_avalid(m_axil_avalid), + .m_axil_aready(m_axil_aready), + + /* + * Write command output + */ + .m_wc_select(), + .m_wc_decerr(), + .m_wc_valid(), + .m_wc_ready(1'b1), + + /* + * Response command output + */ + .m_rc_select(m_rc_select), + .m_rc_decerr(m_rc_decerr), + .m_rc_valid(m_rc_valid), + .m_rc_ready(m_rc_ready) + ); + + assign int_axil_arvalid[m*M_COUNT +: M_COUNT] = m_axil_avalid << a_select; + assign m_axil_aready = int_axil_arready[a_select*S_COUNT+m]; + + // response handling + assign fifo_wr_select = m_rc_select; + assign fifo_wr_decerr = m_rc_decerr; + assign fifo_wr_en = m_rc_valid && !fifo_half_full_reg; + assign m_rc_ready = !fifo_half_full_reg; + + // write response handling + wire [CL_M_COUNT-1:0] r_select = M_COUNT > 1 ? fifo_select[fifo_rd_ptr_reg[FIFO_ADDR_WIDTH-1:0]] : 0; + wire r_decerr = fifo_decerr[fifo_rd_ptr_reg[FIFO_ADDR_WIDTH-1:0]]; + wire r_valid = !fifo_empty; + + // read response mux + wire [DATA_WIDTH-1:0] m_axil_rdata_mux = r_decerr ? {DATA_WIDTH{1'b0}} : int_m_axil_rdata[r_select*DATA_WIDTH +: DATA_WIDTH]; + wire [1:0] m_axil_rresp_mux = r_decerr ? 2'b11 : int_m_axil_rresp[r_select*2 +: 2]; + wire m_axil_rvalid_mux = (r_decerr ? 1'b1 : int_axil_rvalid[r_select*S_COUNT+m]) && r_valid; + wire m_axil_rready_mux; + + assign int_axil_rready[m*M_COUNT +: M_COUNT] = (r_valid && m_axil_rready_mux) << r_select; + + assign fifo_rd_en = m_axil_rvalid_mux && m_axil_rready_mux && r_valid; + + // S side register + axil_register_rd #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .AR_REG_TYPE(S_AR_REG_TYPE[m*2 +: 2]), + .R_REG_TYPE(S_R_REG_TYPE[m*2 +: 2]) + ) + reg_inst ( + .clk(clk), + .rst(rst), + .s_axil_araddr(s_axil_araddr[m*ADDR_WIDTH +: ADDR_WIDTH]), + .s_axil_arprot(s_axil_arprot[m*3 +: 3]), + .s_axil_arvalid(s_axil_arvalid[m]), + .s_axil_arready(s_axil_arready[m]), + .s_axil_rdata(s_axil_rdata[m*DATA_WIDTH +: DATA_WIDTH]), + .s_axil_rresp(s_axil_rresp[m*2 +: 2]), + .s_axil_rvalid(s_axil_rvalid[m]), + .s_axil_rready(s_axil_rready[m]), + .m_axil_araddr(int_s_axil_araddr[m*ADDR_WIDTH +: ADDR_WIDTH]), + .m_axil_arprot(int_s_axil_arprot[m*3 +: 3]), + .m_axil_arvalid(int_s_axil_arvalid[m]), + .m_axil_arready(int_s_axil_arready[m]), + .m_axil_rdata(m_axil_rdata_mux), + .m_axil_rresp(m_axil_rresp_mux), + .m_axil_rvalid(m_axil_rvalid_mux), + .m_axil_rready(m_axil_rready_mux) + ); + end // s_ifaces + + for (n = 0; n < M_COUNT; n = n + 1) begin : m_ifaces + // response routing FIFO + localparam FIFO_ADDR_WIDTH = $clog2(M_ISSUE[n*32 +: 32])+1; + + reg [FIFO_ADDR_WIDTH+1-1:0] fifo_wr_ptr_reg = 0; + reg [FIFO_ADDR_WIDTH+1-1:0] fifo_rd_ptr_reg = 0; + + reg [CL_S_COUNT-1:0] fifo_select[(2**FIFO_ADDR_WIDTH)-1:0]; + wire [CL_S_COUNT-1:0] fifo_wr_select; + wire fifo_wr_en; + wire fifo_rd_en; + reg fifo_half_full_reg = 1'b0; + + wire fifo_empty = fifo_rd_ptr_reg == fifo_wr_ptr_reg; + + integer i; + + initial begin + for (i = 0; i < 2**FIFO_ADDR_WIDTH; i = i + 1) begin + fifo_select[i] = 0; + end + end + + always @(posedge clk) begin + if (fifo_wr_en) begin + fifo_select[fifo_wr_ptr_reg[FIFO_ADDR_WIDTH-1:0]] <= fifo_wr_select; + fifo_wr_ptr_reg <= fifo_wr_ptr_reg + 1; + end + if (fifo_rd_en) begin + fifo_rd_ptr_reg <= fifo_rd_ptr_reg + 1; + end + + fifo_half_full_reg <= $unsigned(fifo_wr_ptr_reg - fifo_rd_ptr_reg) >= 2**(FIFO_ADDR_WIDTH-1); + + if (rst) begin + fifo_wr_ptr_reg <= 0; + fifo_rd_ptr_reg <= 0; + end + end + + // address arbitration + wire [S_COUNT-1:0] a_request; + wire [S_COUNT-1:0] a_acknowledge; + wire [S_COUNT-1:0] a_grant; + wire a_grant_valid; + wire [CL_S_COUNT-1:0] a_grant_encoded; + + arbiter #( + .PORTS(S_COUNT), + .ARB_TYPE_ROUND_ROBIN(1), + .ARB_BLOCK(1), + .ARB_BLOCK_ACK(1), + .ARB_LSB_HIGH_PRIORITY(1) + ) + a_arb_inst ( + .clk(clk), + .rst(rst), + .request(a_request), + .acknowledge(a_acknowledge), + .grant(a_grant), + .grant_valid(a_grant_valid), + .grant_encoded(a_grant_encoded) + ); + + // address mux + wire [ADDR_WIDTH-1:0] s_axil_araddr_mux = int_s_axil_araddr[a_grant_encoded*ADDR_WIDTH +: ADDR_WIDTH]; + wire [2:0] s_axil_arprot_mux = int_s_axil_arprot[a_grant_encoded*3 +: 3]; + wire s_axil_arvalid_mux = int_axil_arvalid[a_grant_encoded*M_COUNT+n] && a_grant_valid; + wire s_axil_arready_mux; + + assign int_axil_arready[n*S_COUNT +: S_COUNT] = (a_grant_valid && s_axil_arready_mux) << a_grant_encoded; + + for (m = 0; m < S_COUNT; m = m + 1) begin + assign a_request[m] = int_axil_arvalid[m*M_COUNT+n] && !a_grant[m] && !fifo_half_full_reg; + assign a_acknowledge[m] = a_grant[m] && int_axil_arvalid[m*M_COUNT+n] && s_axil_arready_mux; + end + + assign fifo_wr_select = a_grant_encoded; + assign fifo_wr_en = s_axil_arvalid_mux && s_axil_arready_mux && a_grant_valid; + + // read response forwarding + wire [CL_S_COUNT-1:0] r_select = S_COUNT > 1 ? fifo_select[fifo_rd_ptr_reg[FIFO_ADDR_WIDTH-1:0]] : 0; + + assign int_axil_rvalid[n*S_COUNT +: S_COUNT] = int_m_axil_rvalid[n] << r_select; + assign int_m_axil_rready[n] = int_axil_rready[r_select*M_COUNT+n]; + + assign fifo_rd_en = int_m_axil_rvalid[n] && int_m_axil_rready[n]; + + // M side register + axil_register_rd #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .AR_REG_TYPE(M_AR_REG_TYPE[n*2 +: 2]), + .R_REG_TYPE(M_R_REG_TYPE[n*2 +: 2]) + ) + reg_inst ( + .clk(clk), + .rst(rst), + .s_axil_araddr(s_axil_araddr_mux), + .s_axil_arprot(s_axil_arprot_mux), + .s_axil_arvalid(s_axil_arvalid_mux), + .s_axil_arready(s_axil_arready_mux), + .s_axil_rdata(int_m_axil_rdata[n*DATA_WIDTH +: DATA_WIDTH]), + .s_axil_rresp(int_m_axil_rresp[n*2 +: 2]), + .s_axil_rvalid(int_m_axil_rvalid[n]), + .s_axil_rready(int_m_axil_rready[n]), + .m_axil_araddr(m_axil_araddr[n*ADDR_WIDTH +: ADDR_WIDTH]), + .m_axil_arprot(m_axil_arprot[n*3 +: 3]), + .m_axil_arvalid(m_axil_arvalid[n]), + .m_axil_arready(m_axil_arready[n]), + .m_axil_rdata(m_axil_rdata[n*DATA_WIDTH +: DATA_WIDTH]), + .m_axil_rresp(m_axil_rresp[n*2 +: 2]), + .m_axil_rvalid(m_axil_rvalid[n]), + .m_axil_rready(m_axil_rready[n]) + ); + end // m_ifaces + +endgenerate + +endmodule diff --git a/fpga/lib/axi/rtl/axil_crossbar_wr.v b/fpga/lib/axi/rtl/axil_crossbar_wr.v new file mode 100644 index 000000000..56ccf3704 --- /dev/null +++ b/fpga/lib/axi/rtl/axil_crossbar_wr.v @@ -0,0 +1,529 @@ +/* + +Copyright (c) 2021 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 lite crossbar (write) + */ +module axil_crossbar_wr # +( + // Number of AXI inputs (slave interfaces) + parameter S_COUNT = 4, + // Number of AXI outputs (master interfaces) + parameter M_COUNT = 4, + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Number of concurrent operations for each slave interface + // S_COUNT concatenated fields of 32 bits + parameter S_ACCEPT = {S_COUNT{32'd16}}, + // Number of regions per master interface + parameter M_REGIONS = 1, + // Master interface base addresses + // M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_WIDTH bits + // set to zero for default addressing based on M_ADDR_WIDTH + parameter M_BASE_ADDR = 0, + // Master interface address widths + // M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits + parameter M_ADDR_WIDTH = {M_COUNT{{M_REGIONS{32'd24}}}}, + // Write connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT = {M_COUNT{{S_COUNT{1'b1}}}}, + // Number of concurrent operations for each master interface + // M_COUNT concatenated fields of 32 bits + parameter M_ISSUE = {M_COUNT{32'd16}}, + // Secure master (fail operations based on awprot/arprot) + // M_COUNT bits + parameter M_SECURE = {M_COUNT{1'b0}}, + // Slave interface AW channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_AW_REG_TYPE = {S_COUNT{2'd0}}, + // Slave interface W channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_W_REG_TYPE = {S_COUNT{2'd0}}, + // Slave interface B channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_B_REG_TYPE = {S_COUNT{2'd1}}, + // Master interface AW channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_AW_REG_TYPE = {M_COUNT{2'd1}}, + // Master interface W channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_W_REG_TYPE = {M_COUNT{2'd2}}, + // Master interface B channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_B_REG_TYPE = {M_COUNT{2'd0}} +) +( + input wire clk, + input wire rst, + + /* + * AXI lite slave interfaces + */ + input wire [S_COUNT*ADDR_WIDTH-1:0] s_axil_awaddr, + input wire [S_COUNT*3-1:0] s_axil_awprot, + input wire [S_COUNT-1:0] s_axil_awvalid, + output wire [S_COUNT-1:0] s_axil_awready, + input wire [S_COUNT*DATA_WIDTH-1:0] s_axil_wdata, + input wire [S_COUNT*STRB_WIDTH-1:0] s_axil_wstrb, + input wire [S_COUNT-1:0] s_axil_wvalid, + output wire [S_COUNT-1:0] s_axil_wready, + output wire [S_COUNT*2-1:0] s_axil_bresp, + output wire [S_COUNT-1:0] s_axil_bvalid, + input wire [S_COUNT-1:0] s_axil_bready, + + /* + * AXI lite master interfaces + */ + output wire [M_COUNT*ADDR_WIDTH-1:0] m_axil_awaddr, + output wire [M_COUNT*3-1:0] m_axil_awprot, + output wire [M_COUNT-1:0] m_axil_awvalid, + input wire [M_COUNT-1:0] m_axil_awready, + output wire [M_COUNT*DATA_WIDTH-1:0] m_axil_wdata, + output wire [M_COUNT*STRB_WIDTH-1:0] m_axil_wstrb, + output wire [M_COUNT-1:0] m_axil_wvalid, + input wire [M_COUNT-1:0] m_axil_wready, + input wire [M_COUNT*2-1:0] m_axil_bresp, + input wire [M_COUNT-1:0] m_axil_bvalid, + output wire [M_COUNT-1:0] m_axil_bready +); + +parameter CL_S_COUNT = $clog2(S_COUNT); +parameter CL_M_COUNT = $clog2(M_COUNT); +parameter M_COUNT_P1 = M_COUNT+1; +parameter CL_M_COUNT_P1 = $clog2(M_COUNT_P1); + +integer i; + +// check configuration +initial begin + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32] && (M_ADDR_WIDTH[i*32 +: 32] < 12 || M_ADDR_WIDTH[i*32 +: 32] > ADDR_WIDTH)) begin + $error("Error: value out of range (instance %m)"); + $finish; + end + end +end + +wire [S_COUNT*ADDR_WIDTH-1:0] int_s_axil_awaddr; +wire [S_COUNT*3-1:0] int_s_axil_awprot; +wire [S_COUNT-1:0] int_s_axil_awvalid; +wire [S_COUNT-1:0] int_s_axil_awready; + +wire [S_COUNT*M_COUNT-1:0] int_axil_awvalid; +wire [M_COUNT*S_COUNT-1:0] int_axil_awready; + +wire [S_COUNT*DATA_WIDTH-1:0] int_s_axil_wdata; +wire [S_COUNT*STRB_WIDTH-1:0] int_s_axil_wstrb; +wire [S_COUNT-1:0] int_s_axil_wvalid; +wire [S_COUNT-1:0] int_s_axil_wready; + +wire [S_COUNT*M_COUNT-1:0] int_axil_wvalid; +wire [M_COUNT*S_COUNT-1:0] int_axil_wready; + +wire [M_COUNT*2-1:0] int_m_axil_bresp; +wire [M_COUNT-1:0] int_m_axil_bvalid; +wire [M_COUNT-1:0] int_m_axil_bready; + +wire [M_COUNT*S_COUNT-1:0] int_axil_bvalid; +wire [S_COUNT*M_COUNT-1:0] int_axil_bready; + +generate + + genvar m, n; + + for (m = 0; m < S_COUNT; m = m + 1) begin : s_ifaces + // response routing FIFO + localparam FIFO_ADDR_WIDTH = $clog2(S_ACCEPT[m*32 +: 32])+1; + + reg [FIFO_ADDR_WIDTH+1-1:0] fifo_wr_ptr_reg = 0; + reg [FIFO_ADDR_WIDTH+1-1:0] fifo_rd_ptr_reg = 0; + + reg [CL_M_COUNT-1:0] fifo_select[(2**FIFO_ADDR_WIDTH)-1:0]; + reg fifo_decerr[(2**FIFO_ADDR_WIDTH)-1:0]; + wire [CL_M_COUNT-1:0] fifo_wr_select; + wire fifo_wr_decerr; + wire fifo_wr_en; + wire fifo_rd_en; + reg fifo_half_full_reg = 1'b0; + + wire fifo_empty = fifo_rd_ptr_reg == fifo_wr_ptr_reg; + + integer i; + + initial begin + for (i = 0; i < 2**FIFO_ADDR_WIDTH; i = i + 1) begin + fifo_select[i] = 0; + fifo_decerr[i] = 0; + end + end + + always @(posedge clk) begin + if (fifo_wr_en) begin + fifo_select[fifo_wr_ptr_reg[FIFO_ADDR_WIDTH-1:0]] <= fifo_wr_select; + fifo_decerr[fifo_wr_ptr_reg[FIFO_ADDR_WIDTH-1:0]] <= fifo_wr_decerr; + fifo_wr_ptr_reg <= fifo_wr_ptr_reg + 1; + end + if (fifo_rd_en) begin + fifo_rd_ptr_reg <= fifo_rd_ptr_reg + 1; + end + + fifo_half_full_reg <= $unsigned(fifo_wr_ptr_reg - fifo_rd_ptr_reg) >= 2**(FIFO_ADDR_WIDTH-1); + + if (rst) begin + fifo_wr_ptr_reg <= 0; + fifo_rd_ptr_reg <= 0; + end + end + + // address decode and admission control + wire [CL_M_COUNT-1:0] a_select; + + wire m_axil_avalid; + wire m_axil_aready; + + wire [CL_M_COUNT-1:0] m_wc_select; + wire m_wc_decerr; + wire m_wc_valid; + wire m_wc_ready; + + wire [CL_M_COUNT-1:0] m_rc_select; + wire m_rc_decerr; + wire m_rc_valid; + wire m_rc_ready; + + axil_crossbar_addr #( + .S(m), + .S_COUNT(S_COUNT), + .M_COUNT(M_COUNT), + .ADDR_WIDTH(ADDR_WIDTH), + .M_REGIONS(M_REGIONS), + .M_BASE_ADDR(M_BASE_ADDR), + .M_ADDR_WIDTH(M_ADDR_WIDTH), + .M_CONNECT(M_CONNECT), + .M_SECURE(M_SECURE), + .WC_OUTPUT(1) + ) + addr_inst ( + .clk(clk), + .rst(rst), + + /* + * Address input + */ + .s_axil_aaddr(int_s_axil_awaddr[m*ADDR_WIDTH +: ADDR_WIDTH]), + .s_axil_aprot(int_s_axil_awprot[m*3 +: 3]), + .s_axil_avalid(int_s_axil_awvalid[m]), + .s_axil_aready(int_s_axil_awready[m]), + + /* + * Address output + */ + .m_select(a_select), + .m_axil_avalid(m_axil_avalid), + .m_axil_aready(m_axil_aready), + + /* + * Write command output + */ + .m_wc_select(m_wc_select), + .m_wc_decerr(m_wc_decerr), + .m_wc_valid(m_wc_valid), + .m_wc_ready(m_wc_ready), + + /* + * Response command output + */ + .m_rc_select(m_rc_select), + .m_rc_decerr(m_rc_decerr), + .m_rc_valid(m_rc_valid), + .m_rc_ready(m_rc_ready) + ); + + assign int_axil_awvalid[m*M_COUNT +: M_COUNT] = m_axil_avalid << a_select; + assign m_axil_aready = int_axil_awready[a_select*S_COUNT+m]; + + // write command handling + reg [CL_M_COUNT-1:0] w_select_reg = 0, w_select_next; + reg w_drop_reg = 1'b0, w_drop_next; + reg w_select_valid_reg = 1'b0, w_select_valid_next; + + assign m_wc_ready = !w_select_valid_reg; + + always @* begin + w_select_next = w_select_reg; + w_drop_next = w_drop_reg && !(int_s_axil_wvalid[m] && int_s_axil_wready[m]); + w_select_valid_next = w_select_valid_reg && !(int_s_axil_wvalid[m] && int_s_axil_wready[m]); + + if (m_wc_valid && !w_select_valid_reg) begin + w_select_next = m_wc_select; + w_drop_next = m_wc_decerr; + w_select_valid_next = m_wc_valid; + end + end + + always @(posedge clk) begin + if (rst) begin + w_select_valid_reg <= 1'b0; + end else begin + w_select_valid_reg <= w_select_valid_next; + end + + w_select_reg <= w_select_next; + w_drop_reg <= w_drop_next; + end + + // write data forwarding + assign int_axil_wvalid[m*M_COUNT +: M_COUNT] = (int_s_axil_wvalid[m] && w_select_valid_reg && !w_drop_reg) << w_select_reg; + assign int_s_axil_wready[m] = int_axil_wready[w_select_reg*S_COUNT+m] || w_drop_reg; + + // response handling + assign fifo_wr_select = m_rc_select; + assign fifo_wr_decerr = m_rc_decerr; + assign fifo_wr_en = m_rc_valid && !fifo_half_full_reg; + assign m_rc_ready = !fifo_half_full_reg; + + // write response handling + wire [CL_M_COUNT-1:0] b_select = M_COUNT > 1 ? fifo_select[fifo_rd_ptr_reg[FIFO_ADDR_WIDTH-1:0]] : 0; + wire b_decerr = fifo_decerr[fifo_rd_ptr_reg[FIFO_ADDR_WIDTH-1:0]]; + wire b_valid = !fifo_empty; + + // write response mux + wire [1:0] m_axil_bresp_mux = b_decerr ? 2'b11 : int_m_axil_bresp[b_select*2 +: 2]; + wire m_axil_bvalid_mux = (b_decerr ? 1'b1 : int_axil_bvalid[b_select*S_COUNT+m]) && b_valid; + wire m_axil_bready_mux; + + assign int_axil_bready[m*M_COUNT +: M_COUNT] = (b_valid && m_axil_bready_mux) << b_select; + + assign fifo_rd_en = m_axil_bvalid_mux && m_axil_bready_mux && b_valid; + + // S side register + axil_register_wr #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .AW_REG_TYPE(S_AW_REG_TYPE[m*2 +: 2]), + .W_REG_TYPE(S_W_REG_TYPE[m*2 +: 2]), + .B_REG_TYPE(S_B_REG_TYPE[m*2 +: 2]) + ) + reg_inst ( + .clk(clk), + .rst(rst), + .s_axil_awaddr(s_axil_awaddr[m*ADDR_WIDTH +: ADDR_WIDTH]), + .s_axil_awprot(s_axil_awprot[m*3 +: 3]), + .s_axil_awvalid(s_axil_awvalid[m]), + .s_axil_awready(s_axil_awready[m]), + .s_axil_wdata(s_axil_wdata[m*DATA_WIDTH +: DATA_WIDTH]), + .s_axil_wstrb(s_axil_wstrb[m*STRB_WIDTH +: STRB_WIDTH]), + .s_axil_wvalid(s_axil_wvalid[m]), + .s_axil_wready(s_axil_wready[m]), + .s_axil_bresp(s_axil_bresp[m*2 +: 2]), + .s_axil_bvalid(s_axil_bvalid[m]), + .s_axil_bready(s_axil_bready[m]), + .m_axil_awaddr(int_s_axil_awaddr[m*ADDR_WIDTH +: ADDR_WIDTH]), + .m_axil_awprot(int_s_axil_awprot[m*3 +: 3]), + .m_axil_awvalid(int_s_axil_awvalid[m]), + .m_axil_awready(int_s_axil_awready[m]), + .m_axil_wdata(int_s_axil_wdata[m*DATA_WIDTH +: DATA_WIDTH]), + .m_axil_wstrb(int_s_axil_wstrb[m*STRB_WIDTH +: STRB_WIDTH]), + .m_axil_wvalid(int_s_axil_wvalid[m]), + .m_axil_wready(int_s_axil_wready[m]), + .m_axil_bresp(m_axil_bresp_mux), + .m_axil_bvalid(m_axil_bvalid_mux), + .m_axil_bready(m_axil_bready_mux) + ); + end // s_ifaces + + for (n = 0; n < M_COUNT; n = n + 1) begin : m_ifaces + // response routing FIFO + localparam FIFO_ADDR_WIDTH = $clog2(M_ISSUE[n*32 +: 32])+1; + + reg [FIFO_ADDR_WIDTH+1-1:0] fifo_wr_ptr_reg = 0; + reg [FIFO_ADDR_WIDTH+1-1:0] fifo_rd_ptr_reg = 0; + + reg [CL_S_COUNT-1:0] fifo_select[(2**FIFO_ADDR_WIDTH)-1:0]; + wire [CL_S_COUNT-1:0] fifo_wr_select; + wire fifo_wr_en; + wire fifo_rd_en; + reg fifo_half_full_reg = 1'b0; + + wire fifo_empty = fifo_rd_ptr_reg == fifo_wr_ptr_reg; + + integer i; + + initial begin + for (i = 0; i < 2**FIFO_ADDR_WIDTH; i = i + 1) begin + fifo_select[i] = 0; + end + end + + always @(posedge clk) begin + if (fifo_wr_en) begin + fifo_select[fifo_wr_ptr_reg[FIFO_ADDR_WIDTH-1:0]] <= fifo_wr_select; + fifo_wr_ptr_reg <= fifo_wr_ptr_reg + 1; + end + if (fifo_rd_en) begin + fifo_rd_ptr_reg <= fifo_rd_ptr_reg + 1; + end + + fifo_half_full_reg <= $unsigned(fifo_wr_ptr_reg - fifo_rd_ptr_reg) >= 2**(FIFO_ADDR_WIDTH-1); + + if (rst) begin + fifo_wr_ptr_reg <= 0; + fifo_rd_ptr_reg <= 0; + end + end + + // address arbitration + reg [CL_S_COUNT-1:0] w_select_reg = 0, w_select_next = 0; + reg w_select_valid_reg = 1'b0, w_select_valid_next; + reg w_select_new_reg = 1'b0, w_select_new_next; + + wire [S_COUNT-1:0] a_request; + wire [S_COUNT-1:0] a_acknowledge; + wire [S_COUNT-1:0] a_grant; + wire a_grant_valid; + wire [CL_S_COUNT-1:0] a_grant_encoded; + + arbiter #( + .PORTS(S_COUNT), + .ARB_TYPE_ROUND_ROBIN(1), + .ARB_BLOCK(1), + .ARB_BLOCK_ACK(1), + .ARB_LSB_HIGH_PRIORITY(1) + ) + a_arb_inst ( + .clk(clk), + .rst(rst), + .request(a_request), + .acknowledge(a_acknowledge), + .grant(a_grant), + .grant_valid(a_grant_valid), + .grant_encoded(a_grant_encoded) + ); + + // address mux + wire [ADDR_WIDTH-1:0] s_axil_awaddr_mux = int_s_axil_awaddr[a_grant_encoded*ADDR_WIDTH +: ADDR_WIDTH]; + wire [2:0] s_axil_awprot_mux = int_s_axil_awprot[a_grant_encoded*3 +: 3]; + wire s_axil_awvalid_mux = int_axil_awvalid[a_grant_encoded*M_COUNT+n] && a_grant_valid; + wire s_axil_awready_mux; + + assign int_axil_awready[n*S_COUNT +: S_COUNT] = (a_grant_valid && s_axil_awready_mux) << a_grant_encoded; + + for (m = 0; m < S_COUNT; m = m + 1) begin + assign a_request[m] = int_axil_awvalid[m*M_COUNT+n] && !a_grant[m] && !fifo_half_full_reg && !w_select_valid_next; + assign a_acknowledge[m] = a_grant[m] && int_axil_awvalid[m*M_COUNT+n] && s_axil_awready_mux; + end + + assign fifo_wr_select = a_grant_encoded; + assign fifo_wr_en = s_axil_awvalid_mux && s_axil_awready_mux && a_grant_valid; + + // write data mux + wire [DATA_WIDTH-1:0] s_axil_wdata_mux = int_s_axil_wdata[w_select_reg*DATA_WIDTH +: DATA_WIDTH]; + wire [STRB_WIDTH-1:0] s_axil_wstrb_mux = int_s_axil_wstrb[w_select_reg*STRB_WIDTH +: STRB_WIDTH]; + wire s_axil_wvalid_mux = int_axil_wvalid[w_select_reg*M_COUNT+n] && w_select_valid_reg; + wire s_axil_wready_mux; + + assign int_axil_wready[n*S_COUNT +: S_COUNT] = (w_select_valid_reg && s_axil_wready_mux) << w_select_reg; + + // write data routing + always @* begin + w_select_next = w_select_reg; + w_select_valid_next = w_select_valid_reg && !(s_axil_wvalid_mux && s_axil_wready_mux); + w_select_new_next = w_select_new_reg || !a_grant_valid || a_acknowledge; + + if (a_grant_valid && !w_select_valid_reg && w_select_new_reg) begin + w_select_next = a_grant_encoded; + w_select_valid_next = a_grant_valid; + w_select_new_next = 1'b0; + end + end + + always @(posedge clk) begin + if (rst) begin + w_select_valid_reg <= 1'b0; + w_select_new_reg <= 1'b1; + end else begin + w_select_valid_reg <= w_select_valid_next; + w_select_new_reg <= w_select_new_next; + end + + w_select_reg <= w_select_next; + end + + // write response forwarding + wire [CL_S_COUNT-1:0] b_select = S_COUNT > 1 ? fifo_select[fifo_rd_ptr_reg[FIFO_ADDR_WIDTH-1:0]] : 0; + + assign int_axil_bvalid[n*S_COUNT +: S_COUNT] = int_m_axil_bvalid[n] << b_select; + assign int_m_axil_bready[n] = int_axil_bready[b_select*M_COUNT+n]; + + assign fifo_rd_en = int_m_axil_bvalid[n] && int_m_axil_bready[n]; + + // M side register + axil_register_wr #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .AW_REG_TYPE(M_AW_REG_TYPE[n*2 +: 2]), + .W_REG_TYPE(M_W_REG_TYPE[n*2 +: 2]), + .B_REG_TYPE(M_B_REG_TYPE[n*2 +: 2]) + ) + reg_inst ( + .clk(clk), + .rst(rst), + .s_axil_awaddr(s_axil_awaddr_mux), + .s_axil_awprot(s_axil_awprot_mux), + .s_axil_awvalid(s_axil_awvalid_mux), + .s_axil_awready(s_axil_awready_mux), + .s_axil_wdata(s_axil_wdata_mux), + .s_axil_wstrb(s_axil_wstrb_mux), + .s_axil_wvalid(s_axil_wvalid_mux), + .s_axil_wready(s_axil_wready_mux), + .s_axil_bresp(int_m_axil_bresp[n*2 +: 2]), + .s_axil_bvalid(int_m_axil_bvalid[n]), + .s_axil_bready(int_m_axil_bready[n]), + .m_axil_awaddr(m_axil_awaddr[n*ADDR_WIDTH +: ADDR_WIDTH]), + .m_axil_awprot(m_axil_awprot[n*3 +: 3]), + .m_axil_awvalid(m_axil_awvalid[n]), + .m_axil_awready(m_axil_awready[n]), + .m_axil_wdata(m_axil_wdata[n*DATA_WIDTH +: DATA_WIDTH]), + .m_axil_wstrb(m_axil_wstrb[n*STRB_WIDTH +: STRB_WIDTH]), + .m_axil_wvalid(m_axil_wvalid[n]), + .m_axil_wready(m_axil_wready[n]), + .m_axil_bresp(m_axil_bresp[n*2 +: 2]), + .m_axil_bvalid(m_axil_bvalid[n]), + .m_axil_bready(m_axil_bready[n]) + ); + end // m_ifaces + +endgenerate + +endmodule diff --git a/fpga/lib/axi/rtl/axil_crossbar_wrap.py b/fpga/lib/axi/rtl/axil_crossbar_wrap.py new file mode 100755 index 000000000..be54eea80 --- /dev/null +++ b/fpga/lib/axi/rtl/axil_crossbar_wrap.py @@ -0,0 +1,312 @@ +#!/usr/bin/env python +""" +Generates an AXI lite crossbar wrapper with the specified number of ports +""" + +import argparse +from jinja2 import Template + + +def main(): + parser = argparse.ArgumentParser(description=__doc__.strip()) + parser.add_argument('-p', '--ports', type=int, default=[4], nargs='+', help="number of ports") + parser.add_argument('-n', '--name', type=str, help="module name") + parser.add_argument('-o', '--output', type=str, help="output file name") + + args = parser.parse_args() + + try: + generate(**args.__dict__) + except IOError as ex: + print(ex) + exit(1) + + +def generate(ports=4, name=None, output=None): + if type(ports) is int: + m = n = ports + elif len(ports) == 1: + m = n = ports[0] + else: + m, n = ports + + if name is None: + name = "axil_crossbar_wrap_{0}x{1}".format(m, n) + + if output is None: + output = name + ".v" + + print("Generating {0}x{1} port AXI lite crossbar wrapper {2}...".format(m, n, name)) + + cm = (m-1).bit_length() + cn = (n-1).bit_length() + + t = Template(u"""/* + +Copyright (c) 2021 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 lite {{m}}x{{n}} crossbar (wrapper) + */ +module {{name}} # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), +{%- for p in range(m) %} + // Number of concurrent operations + parameter S{{'%02d'%p}}_ACCEPT = 16, +{%- endfor %} + // Number of regions per master interface + parameter M_REGIONS = 1, +{%- for p in range(n) %} + // Master interface base addresses + // M_REGIONS concatenated fields of ADDR_WIDTH bits + parameter M{{'%02d'%p}}_BASE_ADDR = 0, + // Master interface address widths + // M_REGIONS concatenated fields of 32 bits + parameter M{{'%02d'%p}}_ADDR_WIDTH = {M_REGIONS{32'd24}}, + // Read connections between interfaces + // S_COUNT bits + parameter M{{'%02d'%p}}_CONNECT_READ = {{m}}'b{% for p in range(m) %}1{% endfor %}, + // Write connections between interfaces + // S_COUNT bits + parameter M{{'%02d'%p}}_CONNECT_WRITE = {{m}}'b{% for p in range(m) %}1{% endfor %}, + // Number of concurrent operations for each master interface + parameter M{{'%02d'%p}}_ISSUE = 16, + // Secure master (fail operations based on awprot/arprot) + parameter M{{'%02d'%p}}_SECURE = 0, +{%- endfor %} +{%- for p in range(m) %} + // Slave interface AW channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S{{'%02d'%p}}_AW_REG_TYPE = 0, + // Slave interface W channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S{{'%02d'%p}}_W_REG_TYPE = 0, + // Slave interface B channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S{{'%02d'%p}}_B_REG_TYPE = 1, + // Slave interface AR channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S{{'%02d'%p}}_AR_REG_TYPE = 0, + // Slave interface R channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S{{'%02d'%p}}_R_REG_TYPE = 2, +{%- endfor %} +{%- for p in range(n) %} + // Master interface AW channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M{{'%02d'%p}}_AW_REG_TYPE = 1, + // Master interface W channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M{{'%02d'%p}}_W_REG_TYPE = 2, + // Master interface B channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M{{'%02d'%p}}_B_REG_TYPE = 0, + // Master interface AR channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M{{'%02d'%p}}_AR_REG_TYPE = 1, + // Master interface R channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M{{'%02d'%p}}_R_REG_TYPE = 0{% if not loop.last %},{% endif %} +{%- endfor %} +) +( + input wire clk, + input wire rst, + + /* + * AXI lite slave interfaces + */ +{%- for p in range(m) %} + input wire [ADDR_WIDTH-1:0] s{{'%02d'%p}}_axil_awaddr, + input wire [2:0] s{{'%02d'%p}}_axil_awprot, + input wire s{{'%02d'%p}}_axil_awvalid, + output wire s{{'%02d'%p}}_axil_awready, + input wire [DATA_WIDTH-1:0] s{{'%02d'%p}}_axil_wdata, + input wire [STRB_WIDTH-1:0] s{{'%02d'%p}}_axil_wstrb, + input wire s{{'%02d'%p}}_axil_wvalid, + output wire s{{'%02d'%p}}_axil_wready, + output wire [1:0] s{{'%02d'%p}}_axil_bresp, + output wire s{{'%02d'%p}}_axil_bvalid, + input wire s{{'%02d'%p}}_axil_bready, + input wire [ADDR_WIDTH-1:0] s{{'%02d'%p}}_axil_araddr, + input wire [2:0] s{{'%02d'%p}}_axil_arprot, + input wire s{{'%02d'%p}}_axil_arvalid, + output wire s{{'%02d'%p}}_axil_arready, + output wire [DATA_WIDTH-1:0] s{{'%02d'%p}}_axil_rdata, + output wire [1:0] s{{'%02d'%p}}_axil_rresp, + output wire s{{'%02d'%p}}_axil_rvalid, + input wire s{{'%02d'%p}}_axil_rready, +{% endfor %} + /* + * AXI lite master interfaces + */ +{%- for p in range(n) %} + output wire [ADDR_WIDTH-1:0] m{{'%02d'%p}}_axil_awaddr, + output wire [2:0] m{{'%02d'%p}}_axil_awprot, + output wire m{{'%02d'%p}}_axil_awvalid, + input wire m{{'%02d'%p}}_axil_awready, + output wire [DATA_WIDTH-1:0] m{{'%02d'%p}}_axil_wdata, + output wire [STRB_WIDTH-1:0] m{{'%02d'%p}}_axil_wstrb, + output wire m{{'%02d'%p}}_axil_wvalid, + input wire m{{'%02d'%p}}_axil_wready, + input wire [1:0] m{{'%02d'%p}}_axil_bresp, + input wire m{{'%02d'%p}}_axil_bvalid, + output wire m{{'%02d'%p}}_axil_bready, + output wire [ADDR_WIDTH-1:0] m{{'%02d'%p}}_axil_araddr, + output wire [2:0] m{{'%02d'%p}}_axil_arprot, + output wire m{{'%02d'%p}}_axil_arvalid, + input wire m{{'%02d'%p}}_axil_arready, + input wire [DATA_WIDTH-1:0] m{{'%02d'%p}}_axil_rdata, + input wire [1:0] m{{'%02d'%p}}_axil_rresp, + input wire m{{'%02d'%p}}_axil_rvalid, + output wire m{{'%02d'%p}}_axil_rready{% if not loop.last %},{% endif %} +{% endfor -%} +); + +localparam S_COUNT = {{m}}; +localparam M_COUNT = {{n}}; + +// parameter sizing helpers +function [ADDR_WIDTH*M_REGIONS-1:0] w_a_r(input [ADDR_WIDTH*M_REGIONS-1:0] val); + w_a_r = val; +endfunction + +function [32*M_REGIONS-1:0] w_32_r(input [32*M_REGIONS-1:0] val); + w_32_r = val; +endfunction + +function [S_COUNT-1:0] w_s(input [S_COUNT-1:0] val); + w_s = val; +endfunction + +function [31:0] w_32(input [31:0] val); + w_32 = val; +endfunction + +function [1:0] w_2(input [1:0] val); + w_2 = val; +endfunction + +function w_1(input val); + w_1 = val; +endfunction + +axil_crossbar #( + .S_COUNT(S_COUNT), + .M_COUNT(M_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .S_ACCEPT({ {% for p in range(m-1,-1,-1) %}w_32(S{{'%02d'%p}}_ACCEPT){% if not loop.last %}, {% endif %}{% endfor %} }), + .M_REGIONS(M_REGIONS), + .M_BASE_ADDR({ {% for p in range(n-1,-1,-1) %}w_a_r(M{{'%02d'%p}}_BASE_ADDR){% if not loop.last %}, {% endif %}{% endfor %} }), + .M_ADDR_WIDTH({ {% for p in range(n-1,-1,-1) %}w_32_r(M{{'%02d'%p}}_ADDR_WIDTH){% if not loop.last %}, {% endif %}{% endfor %} }), + .M_CONNECT_READ({ {% for p in range(n-1,-1,-1) %}w_s(M{{'%02d'%p}}_CONNECT_READ){% if not loop.last %}, {% endif %}{% endfor %} }), + .M_CONNECT_WRITE({ {% for p in range(n-1,-1,-1) %}w_s(M{{'%02d'%p}}_CONNECT_WRITE){% if not loop.last %}, {% endif %}{% endfor %} }), + .M_ISSUE({ {% for p in range(n-1,-1,-1) %}w_32(M{{'%02d'%p}}_ISSUE){% if not loop.last %}, {% endif %}{% endfor %} }), + .M_SECURE({ {% for p in range(n-1,-1,-1) %}w_1(M{{'%02d'%p}}_SECURE){% if not loop.last %}, {% endif %}{% endfor %} }), + .S_AR_REG_TYPE({ {% for p in range(m-1,-1,-1) %}w_2(S{{'%02d'%p}}_AR_REG_TYPE){% if not loop.last %}, {% endif %}{% endfor %} }), + .S_R_REG_TYPE({ {% for p in range(m-1,-1,-1) %}w_2(S{{'%02d'%p}}_R_REG_TYPE){% if not loop.last %}, {% endif %}{% endfor %} }), + .S_AW_REG_TYPE({ {% for p in range(m-1,-1,-1) %}w_2(S{{'%02d'%p}}_AW_REG_TYPE){% if not loop.last %}, {% endif %}{% endfor %} }), + .S_W_REG_TYPE({ {% for p in range(m-1,-1,-1) %}w_2(S{{'%02d'%p}}_W_REG_TYPE){% if not loop.last %}, {% endif %}{% endfor %} }), + .S_B_REG_TYPE({ {% for p in range(m-1,-1,-1) %}w_2(S{{'%02d'%p}}_B_REG_TYPE){% if not loop.last %}, {% endif %}{% endfor %} }), + .M_AR_REG_TYPE({ {% for p in range(n-1,-1,-1) %}w_2(M{{'%02d'%p}}_AR_REG_TYPE){% if not loop.last %}, {% endif %}{% endfor %} }), + .M_R_REG_TYPE({ {% for p in range(n-1,-1,-1) %}w_2(M{{'%02d'%p}}_R_REG_TYPE){% if not loop.last %}, {% endif %}{% endfor %} }), + .M_AW_REG_TYPE({ {% for p in range(n-1,-1,-1) %}w_2(M{{'%02d'%p}}_AW_REG_TYPE){% if not loop.last %}, {% endif %}{% endfor %} }), + .M_W_REG_TYPE({ {% for p in range(n-1,-1,-1) %}w_2(M{{'%02d'%p}}_W_REG_TYPE){% if not loop.last %}, {% endif %}{% endfor %} }), + .M_B_REG_TYPE({ {% for p in range(n-1,-1,-1) %}w_2(M{{'%02d'%p}}_B_REG_TYPE){% if not loop.last %}, {% endif %}{% endfor %} }) +) +axil_crossbar_inst ( + .clk(clk), + .rst(rst), + .s_axil_awaddr({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axil_awaddr{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axil_awprot({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axil_awprot{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axil_awvalid({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axil_awvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axil_awready({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axil_awready{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axil_wdata({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axil_wdata{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axil_wstrb({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axil_wstrb{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axil_wvalid({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axil_wvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axil_wready({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axil_wready{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axil_bresp({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axil_bresp{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axil_bvalid({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axil_bvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axil_bready({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axil_bready{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axil_araddr({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axil_araddr{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axil_arprot({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axil_arprot{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axil_arvalid({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axil_arvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axil_arready({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axil_arready{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axil_rdata({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axil_rdata{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axil_rresp({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axil_rresp{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axil_rvalid({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axil_rvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axil_rready({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axil_rready{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axil_awaddr({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axil_awaddr{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axil_awprot({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axil_awprot{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axil_awvalid({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axil_awvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axil_awready({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axil_awready{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axil_wdata({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axil_wdata{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axil_wstrb({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axil_wstrb{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axil_wvalid({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axil_wvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axil_wready({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axil_wready{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axil_bresp({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axil_bresp{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axil_bvalid({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axil_bvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axil_bready({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axil_bready{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axil_araddr({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axil_araddr{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axil_arprot({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axil_arprot{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axil_arvalid({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axil_arvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axil_arready({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axil_arready{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axil_rdata({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axil_rdata{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axil_rresp({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axil_rresp{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axil_rvalid({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axil_rvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axil_rready({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axil_rready{% if not loop.last %}, {% endif %}{% endfor %} }) +); + +endmodule + +""") + + print(f"Writing file '{output}'...") + + with open(output, 'w') as f: + f.write(t.render( + m=m, + n=n, + cm=cm, + cn=cn, + name=name + )) + f.flush() + + print("Done") + + +if __name__ == "__main__": + main() diff --git a/fpga/lib/axi/tb/axi_cdma/test_axi_cdma.py b/fpga/lib/axi/tb/axi_cdma/test_axi_cdma.py index 407208e9c..775803b9c 100644 --- a/fpga/lib/axi/tb/axi_cdma/test_axi_cdma.py +++ b/fpga/lib/axi/tb/axi_cdma/test_axi_cdma.py @@ -42,7 +42,7 @@ DescBus, DescTransaction, DescSource, DescSink, DescMonitor = define_stream("Des ) DescStatusBus, DescStatusTransaction, DescStatusSource, DescStatusSink, DescStatusMonitor = define_stream("DescStatus", - signals=["tag", "valid"] + signals=["tag", "error", "valid"] ) @@ -123,6 +123,7 @@ async def run_test(dut, data_in=None, idle_inserter=None, backpressure_inserter= tb.log.info("status: %s", status) assert int(status.tag) == cur_tag + assert int(status.error) == 0 tb.log.debug("%s", tb.axi_ram.hexdump_str((write_addr & ~0xf)-16, (((write_addr & 0xf)+length-1) & ~0xf)+48)) @@ -156,7 +157,7 @@ rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl')) @pytest.mark.parametrize("unaligned", [0, 1]) @pytest.mark.parametrize("axi_data_width", [8, 16, 32]) -def test_axi_dma(request, axi_data_width, unaligned): +def test_axi_cdma(request, axi_data_width, unaligned): dut = "axi_cdma" module = os.path.splitext(os.path.basename(__file__))[0] toplevel = dut diff --git a/fpga/lib/axi/tb/axi_dma/test_axi_dma.py b/fpga/lib/axi/tb/axi_dma/test_axi_dma.py index 716dcdc2d..382c120ab 100644 --- a/fpga/lib/axi/tb/axi_dma/test_axi_dma.py +++ b/fpga/lib/axi/tb/axi_dma/test_axi_dma.py @@ -45,7 +45,7 @@ DescBus, DescTransaction, DescSource, DescSink, DescMonitor = define_stream("Des ) DescStatusBus, DescStatusTransaction, DescStatusSource, DescStatusSink, DescStatusMonitor = define_stream("DescStatus", - signals=["tag", "valid"], + signals=["tag", "error", "valid"], optional_signals=["len", "id", "dest", "user"] ) @@ -144,6 +144,7 @@ async def run_test_write(dut, data_in=None, idle_inserter=None, backpressure_ins assert int(status.len) == min(len(test_data), len(test_data2)) assert int(status.tag) == cur_tag assert int(status.id) == cur_tag + assert int(status.error) == 0 tb.log.debug("%s", tb.axi_ram.hexdump_str((addr & ~0xf)-16, (((addr & 0xf)+length-1) & ~0xf)+48)) @@ -197,6 +198,7 @@ async def run_test_read(dut, data_in=None, idle_inserter=None, backpressure_inse tb.log.info("read_data: %s", read_data) assert int(status.tag) == cur_tag + assert int(status.error) == 0 assert read_data.tdata == test_data assert read_data.tid == cur_tag diff --git a/fpga/lib/axi/tb/axi_dma_rd/test_axi_dma_rd.py b/fpga/lib/axi/tb/axi_dma_rd/test_axi_dma_rd.py index 1be9ef3b2..cfabcc192 100644 --- a/fpga/lib/axi/tb/axi_dma_rd/test_axi_dma_rd.py +++ b/fpga/lib/axi/tb/axi_dma_rd/test_axi_dma_rd.py @@ -44,7 +44,7 @@ DescBus, DescTransaction, DescSource, DescSink, DescMonitor = define_stream("Des ) DescStatusBus, DescStatusTransaction, DescStatusSource, DescStatusSink, DescStatusMonitor = define_stream("DescStatus", - signals=["tag", "valid"], + signals=["tag", "error", "valid"], optional_signals=["len", "id", "dest", "user"] ) @@ -129,6 +129,7 @@ async def run_test_read(dut, data_in=None, idle_inserter=None, backpressure_inse tb.log.info("read_data: %s", read_data) assert int(status.tag) == cur_tag + assert int(status.error) == 0 assert read_data.tdata == test_data assert read_data.tid == cur_tag diff --git a/fpga/lib/axi/tb/axi_dma_wr/test_axi_dma_wr.py b/fpga/lib/axi/tb/axi_dma_wr/test_axi_dma_wr.py index 48195f6ee..806750b9a 100644 --- a/fpga/lib/axi/tb/axi_dma_wr/test_axi_dma_wr.py +++ b/fpga/lib/axi/tb/axi_dma_wr/test_axi_dma_wr.py @@ -44,7 +44,7 @@ DescBus, DescTransaction, DescSource, DescSink, DescMonitor = define_stream("Des ) DescStatusBus, DescStatusTransaction, DescStatusSource, DescStatusSink, DescStatusMonitor = define_stream("DescStatus", - signals=["tag", "valid"], + signals=["tag", "error", "valid"], optional_signals=["len", "id", "dest", "user"] ) @@ -132,6 +132,7 @@ async def run_test_write(dut, data_in=None, idle_inserter=None, backpressure_ins assert int(status.len) == min(len(test_data), len(test_data2)) assert int(status.tag) == cur_tag assert int(status.id) == cur_tag + assert int(status.error) == 0 tb.log.debug("%s", tb.axi_ram.hexdump_str((addr & ~0xf)-16, (((addr & 0xf)+length-1) & ~0xf)+48)) diff --git a/fpga/lib/axi/tb/axil_crossbar/Makefile b/fpga/lib/axi/tb/axil_crossbar/Makefile new file mode 100644 index 000000000..67d7a80d1 --- /dev/null +++ b/fpga/lib/axi/tb/axil_crossbar/Makefile @@ -0,0 +1,93 @@ +# Copyright (c) 2021 Alex Forencich +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +TOPLEVEL_LANG = verilog + +SIM ?= icarus +WAVES ?= 0 + +COCOTB_HDL_TIMEUNIT = 1ns +COCOTB_HDL_TIMEPRECISION = 1ps + +export PARAM_S_COUNT ?= 4 +export PARAM_M_COUNT ?= 4 + +DUT = axil_crossbar +WRAPPER = $(DUT)_wrap_$(PARAM_S_COUNT)x$(PARAM_M_COUNT) +TOPLEVEL = $(WRAPPER) +MODULE = test_$(DUT) +VERILOG_SOURCES += $(WRAPPER).v +VERILOG_SOURCES += ../../rtl/$(DUT).v +VERILOG_SOURCES += ../../rtl/$(DUT)_addr.v +VERILOG_SOURCES += ../../rtl/$(DUT)_rd.v +VERILOG_SOURCES += ../../rtl/$(DUT)_wr.v +VERILOG_SOURCES += ../../rtl/axil_register_rd.v +VERILOG_SOURCES += ../../rtl/axil_register_wr.v +VERILOG_SOURCES += ../../rtl/arbiter.v +VERILOG_SOURCES += ../../rtl/priority_encoder.v + +# module parameters +export PARAM_DATA_WIDTH ?= 32 +export PARAM_ADDR_WIDTH ?= 32 +export PARAM_STRB_WIDTH ?= $(shell expr $(PARAM_DATA_WIDTH) / 8 ) +export PARAM_M_REGIONS ?= 1 + +ifeq ($(SIM), icarus) + PLUSARGS += -fst + + COMPILE_ARGS += -P $(TOPLEVEL).DATA_WIDTH=$(PARAM_DATA_WIDTH) + COMPILE_ARGS += -P $(TOPLEVEL).ADDR_WIDTH=$(PARAM_ADDR_WIDTH) + COMPILE_ARGS += -P $(TOPLEVEL).STRB_WIDTH=$(PARAM_STRB_WIDTH) + COMPILE_ARGS += -P $(TOPLEVEL).M_REGIONS=$(PARAM_M_REGIONS) + + ifeq ($(WAVES), 1) + VERILOG_SOURCES += iverilog_dump.v + COMPILE_ARGS += -s iverilog_dump + endif +else ifeq ($(SIM), verilator) + COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH + + COMPILE_ARGS += -GDATA_WIDTH=$(PARAM_DATA_WIDTH) + COMPILE_ARGS += -GADDR_WIDTH=$(PARAM_ADDR_WIDTH) + COMPILE_ARGS += -GSTRB_WIDTH=$(PARAM_STRB_WIDTH) + COMPILE_ARGS += -GM_REGIONS=$(PARAM_M_REGIONS) + + ifeq ($(WAVES), 1) + COMPILE_ARGS += --trace-fst + endif +endif + +include $(shell cocotb-config --makefiles)/Makefile.sim + +$(WRAPPER).v: ../../rtl/$(DUT)_wrap.py + $< -p $(PARAM_S_COUNT) $(PARAM_M_COUNT) + +iverilog_dump.v: + echo 'module iverilog_dump();' > $@ + echo 'initial begin' >> $@ + echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@ + echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@ + echo 'end' >> $@ + echo 'endmodule' >> $@ + +clean:: + @rm -rf iverilog_dump.v + @rm -rf dump.fst $(TOPLEVEL).fst + @rm -rf *_wrap_*.v diff --git a/fpga/lib/axi/tb/axil_crossbar/test_axil_crossbar.py b/fpga/lib/axi/tb/axil_crossbar/test_axil_crossbar.py new file mode 100644 index 000000000..23ef20951 --- /dev/null +++ b/fpga/lib/axi/tb/axil_crossbar/test_axil_crossbar.py @@ -0,0 +1,266 @@ +""" + +Copyright (c) 2021 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +import itertools +import logging +import os +import random +import subprocess + +import cocotb_test.simulator +import pytest + +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import RisingEdge, Timer +from cocotb.regression import TestFactory + +from cocotbext.axi import AxiLiteBus, AxiLiteMaster, AxiLiteRam + + +class TB(object): + def __init__(self, dut): + self.dut = dut + + s_count = len(dut.axil_crossbar_inst.s_axil_awvalid) + m_count = len(dut.axil_crossbar_inst.m_axil_awvalid) + + self.log = logging.getLogger("cocotb.tb") + self.log.setLevel(logging.DEBUG) + + cocotb.fork(Clock(dut.clk, 10, units="ns").start()) + + self.axil_master = [AxiLiteMaster(AxiLiteBus.from_prefix(dut, f"s{k:02d}_axil"), dut.clk, dut.rst) for k in range(s_count)] + self.axil_ram = [AxiLiteRam(AxiLiteBus.from_prefix(dut, f"m{k:02d}_axil"), dut.clk, dut.rst, size=2**16) for k in range(m_count)] + + def set_idle_generator(self, generator=None): + if generator: + for master in self.axil_master: + master.write_if.aw_channel.set_pause_generator(generator()) + master.write_if.w_channel.set_pause_generator(generator()) + master.read_if.ar_channel.set_pause_generator(generator()) + for ram in self.axil_ram: + ram.write_if.b_channel.set_pause_generator(generator()) + ram.read_if.r_channel.set_pause_generator(generator()) + + def set_backpressure_generator(self, generator=None): + if generator: + for master in self.axil_master: + master.write_if.b_channel.set_pause_generator(generator()) + master.read_if.r_channel.set_pause_generator(generator()) + for ram in self.axil_ram: + ram.write_if.aw_channel.set_pause_generator(generator()) + ram.write_if.w_channel.set_pause_generator(generator()) + ram.read_if.ar_channel.set_pause_generator(generator()) + + async def cycle_reset(self): + self.dut.rst.setimmediatevalue(0) + await RisingEdge(self.dut.clk) + await RisingEdge(self.dut.clk) + self.dut.rst <= 1 + await RisingEdge(self.dut.clk) + await RisingEdge(self.dut.clk) + self.dut.rst <= 0 + await RisingEdge(self.dut.clk) + await RisingEdge(self.dut.clk) + + +async def run_test_write(dut, data_in=None, idle_inserter=None, backpressure_inserter=None, s=0, m=0): + + tb = TB(dut) + + byte_lanes = tb.axil_master[s].write_if.byte_lanes + + await tb.cycle_reset() + + tb.set_idle_generator(idle_inserter) + tb.set_backpressure_generator(backpressure_inserter) + + for length in range(1, byte_lanes*2): + for offset in range(byte_lanes): + tb.log.info("length %d, offset %d", length, offset) + ram_addr = offset+0x1000 + addr = ram_addr + m*0x1000000 + test_data = bytearray([x % 256 for x in range(length)]) + + tb.axil_ram[m].write(ram_addr-128, b'\xaa'*(length+256)) + + await tb.axil_master[s].write(addr, test_data) + + tb.log.debug("%s", tb.axil_ram[m].hexdump_str((ram_addr & ~0xf)-16, (((ram_addr & 0xf)+length-1) & ~0xf)+48)) + + assert tb.axil_ram[m].read(ram_addr, length) == test_data + assert tb.axil_ram[m].read(ram_addr-1, 1) == b'\xaa' + assert tb.axil_ram[m].read(ram_addr+length, 1) == b'\xaa' + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + +async def run_test_read(dut, data_in=None, idle_inserter=None, backpressure_inserter=None, s=0, m=0): + + tb = TB(dut) + + byte_lanes = tb.axil_master[s].write_if.byte_lanes + + await tb.cycle_reset() + + tb.set_idle_generator(idle_inserter) + tb.set_backpressure_generator(backpressure_inserter) + + for length in range(1, byte_lanes*2): + for offset in range(byte_lanes): + tb.log.info("length %d, offset %d", length, offset) + ram_addr = offset+0x1000 + addr = ram_addr + m*0x1000000 + test_data = bytearray([x % 256 for x in range(length)]) + + tb.axil_ram[m].write(ram_addr, test_data) + + data = await tb.axil_master[s].read(addr, length) + + assert data.data == test_data + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + +async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None): + + tb = TB(dut) + + await tb.cycle_reset() + + tb.set_idle_generator(idle_inserter) + tb.set_backpressure_generator(backpressure_inserter) + + async def worker(master, offset, aperture, count=16): + for k in range(count): + m = random.randrange(len(tb.axil_ram)) + length = random.randint(1, min(32, aperture)) + addr = offset+random.randint(0, aperture-length) + m*0x1000000 + test_data = bytearray([x % 256 for x in range(length)]) + + await Timer(random.randint(1, 100), 'ns') + + await master.write(addr, test_data) + + await Timer(random.randint(1, 100), 'ns') + + data = await master.read(addr, length) + assert data.data == test_data + + workers = [] + + for k in range(16): + workers.append(cocotb.fork(worker(tb.axil_master[k % len(tb.axil_master)], k*0x1000, 0x1000, count=16))) + + while workers: + await workers.pop(0).join() + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + +def cycle_pause(): + return itertools.cycle([1, 1, 1, 0]) + + +if cocotb.SIM_NAME: + + s_count = len(cocotb.top.axil_crossbar_inst.s_axil_awvalid) + m_count = len(cocotb.top.axil_crossbar_inst.m_axil_awvalid) + + for test in [run_test_write, run_test_read]: + + factory = TestFactory(test) + factory.add_option("idle_inserter", [None, cycle_pause]) + factory.add_option("backpressure_inserter", [None, cycle_pause]) + factory.add_option("s", range(min(s_count, 2))) + factory.add_option("m", range(min(m_count, 2))) + factory.generate_tests() + + factory = TestFactory(run_stress_test) + factory.generate_tests() + + +# cocotb-test + +tests_dir = os.path.abspath(os.path.dirname(__file__)) +rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl')) + + +@pytest.mark.parametrize("data_width", [8, 16, 32]) +@pytest.mark.parametrize("m_count", [1, 4]) +@pytest.mark.parametrize("s_count", [1, 4]) +def test_axil_crossbar(request, s_count, m_count, data_width): + dut = "axil_crossbar" + wrapper = f"{dut}_wrap_{s_count}x{m_count}" + module = os.path.splitext(os.path.basename(__file__))[0] + toplevel = wrapper + + # generate wrapper + wrapper_file = os.path.join(tests_dir, f"{wrapper}.v") + if not os.path.exists(wrapper_file): + subprocess.Popen( + [os.path.join(rtl_dir, f"{dut}_wrap.py"), "-p", f"{s_count}", f"{m_count}"], + cwd=tests_dir + ).wait() + + verilog_sources = [ + wrapper_file, + os.path.join(rtl_dir, f"{dut}.v"), + os.path.join(rtl_dir, f"{dut}_addr.v"), + os.path.join(rtl_dir, f"{dut}_rd.v"), + os.path.join(rtl_dir, f"{dut}_wr.v"), + os.path.join(rtl_dir, "axil_register_rd.v"), + os.path.join(rtl_dir, "axil_register_wr.v"), + os.path.join(rtl_dir, "arbiter.v"), + os.path.join(rtl_dir, "priority_encoder.v"), + ] + + parameters = {} + + parameters['S_COUNT'] = s_count + parameters['M_COUNT'] = m_count + + parameters['DATA_WIDTH'] = data_width + parameters['ADDR_WIDTH'] = 32 + parameters['STRB_WIDTH'] = parameters['DATA_WIDTH'] // 8 + parameters['M_REGIONS'] = 1 + + extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()} + + sim_build = os.path.join(tests_dir, "sim_build", + request.node.name.replace('[', '-').replace(']', '')) + + cocotb_test.simulator.run( + python_search=[tests_dir], + verilog_sources=verilog_sources, + toplevel=toplevel, + module=module, + parameters=parameters, + sim_build=sim_build, + extra_env=extra_env, + )