diff --git a/lib/axis/.test_durations b/lib/axis/.test_durations index edcbacf3c..f3789db61 100644 --- a/lib/axis/.test_durations +++ b/lib/axis/.test_durations @@ -1,566 +1,678 @@ [ - [ - "tb/axis_adapter/test_axis_adapter.py::test_axis_register[32-16]", - 4.064560260856524 - ], - [ - "tb/axis_arb_mux/test_axis_arb_mux.py::test_axis_arb_mux[4-16]", - 13.143233641982079 - ], - [ - "tb/axis_adapter/test_axis_adapter.py::test_axis_register[8-8]", - 5.109369816957042 - ], - [ - "tb/axis_adapter/test_axis_adapter.py::test_axis_register[8-16]", - 5.058328598272055 - ], [ "tb/axis_adapter/test_axis_adapter.py::test_axis_register[16-16]", - 3.893145425245166 + 8.593190127052367 ], [ "tb/axis_adapter/test_axis_adapter.py::test_axis_register[16-32]", - 3.9831731799058616 + 12.195417289622128 ], [ - "tb/axis_arb_mux/test_axis_arb_mux.py::test_axis_arb_mux[1-8]", - 5.490476330043748 + "tb/axis_adapter/test_axis_adapter.py::test_axis_register[16-8]", + 9.789422770030797 ], [ - "tb/axis_adapter/test_axis_adapter.py::test_axis_register[32-8]", - 5.087842009961605 + "tb/axis_adapter/test_axis_adapter.py::test_axis_register[32-16]", + 12.671152506023645 ], [ "tb/axis_adapter/test_axis_adapter.py::test_axis_register[32-32]", - 3.063372714910656 + 7.9916584407910705 ], [ - "tb/axis_adapter/test_axis_adapter.py::test_axis_register[8-32]", - 5.046062843874097 + "tb/axis_adapter/test_axis_adapter.py::test_axis_register[32-8]", + 18.75230613257736 ], [ - "tb/axis_arb_mux/test_axis_arb_mux.py::test_axis_arb_mux[4-8]", - 19.940929262898862 + "tb/axis_adapter/test_axis_adapter.py::test_axis_register[8-16]", + 12.944865978322923 ], [ - "tb/axis_async_fifo/test_axis_async_fifo.py::test_axis_async_fifo[8-0]", - 10.152885030955076 + "tb/axis_adapter/test_axis_adapter.py::test_axis_register[8-32]", + 14.129353470169008 ], [ - "tb/axis_adapter/test_axis_adapter.py::test_axis_register[16-8]", - 5.021060193190351 + "tb/axis_adapter/test_axis_adapter.py::test_axis_register[8-8]", + 9.817764451727271 ], [ "tb/axis_arb_mux/test_axis_arb_mux.py::test_axis_arb_mux[1-16]", - 3.669280304107815 + 8.91946775931865 ], [ - "tb/axis_async_fifo/test_axis_async_fifo.py::test_axis_async_fifo[16-1]", - 9.14714803174138 + "tb/axis_arb_mux/test_axis_arb_mux.py::test_axis_arb_mux[1-32]", + 8.098858617246151 ], [ - "tb/axis_async_fifo/test_axis_async_fifo.py::test_axis_async_fifo[16-0]", - 6.992672564228997 + "tb/axis_arb_mux/test_axis_arb_mux.py::test_axis_arb_mux[1-8]", + 9.076799966394901 ], [ - "tb/axis_async_fifo/test_axis_async_fifo.py::test_axis_async_fifo[8-1]", - 11.191074956208467 + "tb/axis_arb_mux/test_axis_arb_mux.py::test_axis_arb_mux[4-16]", + 41.119202432222664 ], [ "tb/axis_arb_mux/test_axis_arb_mux.py::test_axis_arb_mux[4-32]", - 10.364595271181315 + 39.96406711637974 ], [ - "tb/axis_async_fifo/test_axis_async_fifo.py::test_axis_async_fifo[32-0]", - 5.5928368042223155 + "tb/axis_arb_mux/test_axis_arb_mux.py::test_axis_arb_mux[4-8]", + 48.189331891946495 ], [ - "tb/axis_arb_mux/test_axis_arb_mux.py::test_axis_arb_mux[1-32]", - 3.247817731462419 + "tb/axis_async_fifo/test_axis_async_fifo.py::test_axis_async_fifo[16-0]", + 11.827675406821072 ], [ - "tb/axis_async_fifo/test_axis_async_fifo.py::test_axis_async_fifo[64-0]", - 6.674111388158053 + "tb/axis_async_fifo/test_axis_async_fifo.py::test_axis_async_fifo[16-1]", + 16.27723410911858 ], [ - "tb/axis_async_fifo/test_axis_async_fifo.py::test_axis_async_fifo[32-1]", - 7.402053208090365 + "tb/axis_async_fifo/test_axis_async_fifo.py::test_axis_async_fifo[32-0]", + 12.823232500813901 ], [ - "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[8-8-0]", - 10.20524234091863 + "tb/axis_async_fifo/test_axis_async_fifo.py::test_axis_async_fifo[32-1]", + 16.625567510724068 ], [ - "tb/axis_async_fifo/test_axis_async_fifo.py::test_axis_async_fifo[64-1]", - 7.729181434959173 + "tb/axis_async_fifo/test_axis_async_fifo.py::test_axis_async_fifo[64-0]", + 14.016773122362792 ], [ - "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[8-16-0]", - 9.401650847867131 + "tb/axis_async_fifo/test_axis_async_fifo.py::test_axis_async_fifo[64-1]", + 13.324867684394121 ], [ - "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[8-8-1]", - 12.24883275711909 + "tb/axis_async_fifo/test_axis_async_fifo.py::test_axis_async_fifo[8-0]", + 17.48489771410823 ], [ - "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[16-8-1]", - 10.960868348134682 + "tb/axis_async_fifo/test_axis_async_fifo.py::test_axis_async_fifo[8-1]", + 20.536784620955586 ], [ "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[16-16-0]", - 7.650945161934942 + 15.515931718982756 ], [ - "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[8-32-0]", - 8.842617793940008 + "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[16-16-1]", + 15.86473059374839 ], [ - "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[8-32-1]", - 10.332504970021546 + "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[16-32-0]", + 19.79045902378857 ], [ - "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[8-16-1]", - 11.13730639917776 + "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[16-32-1]", + 22.031130514107645 ], [ "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[16-8-0]", - 10.321694103768095 + 23.1467945240438 ], [ - "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[16-16-1]", - 8.53622887772508 + "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[16-8-1]", + 24.25652244873345 ], [ - "tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[3-8]", - 6.011481063906103 + "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[32-16-0]", + 21.167339676991105 ], [ - "tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[3-32]", - 4.004094589035958 + "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[32-16-1]", + 15.328569454140961 ], [ "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[32-32-0]", - 6.661400060867891 + 13.75000267289579 ], [ - "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[16-32-1]", - 9.30316348792985 - ], - [ - "tb/axis_cobs_encode/test_axis_cobs_encode.py::test_axis_cobs_encode[0]", - 59.72865728009492 + "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[32-32-1]", + 14.697162045165896 ], [ - "tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[32-1]", - 5.509672118816525 + "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[32-8-0]", + 34.25803263951093 ], [ - "tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[8-0]", - 6.509261818835512 + "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[32-8-1]", + 20.77494945563376 ], [ - "tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[64-0]", - 5.037088224897161 + "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[8-16-0]", + 22.107110468670726 ], [ - "tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[32-0]", - 4.089887966867536 + "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[8-16-1]", + 24.52229151222855 ], [ - "tb/axis_demux/test_axis_demux.py::test_axis_demux[4-16]", - 11.541036840062588 + "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[8-32-0]", + 32.68651257175952 ], [ - "tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[2-8]", - 5.955809738021344 + "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[8-32-1]", + 34.53164793737233 ], [ - "tb/axis_demux/test_axis_demux.py::test_axis_demux[4-8]", - 17.149116148008034 + "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[8-8-0]", + 18.034708897583187 ], [ - "tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[16-1]", - 6.595326299080625 + "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[8-8-1]", + 21.166472426615655 ], [ - "tb/axis_cobs_decode/test_axis_cobs_decode.py::test_axis_cobs_decode", - 55.33392608910799 + "tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[1-16]", + 4.1349410535767674 ], [ - "tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[8-1]", - 8.583656460046768 + "tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[1-32]", + 1.9841334708034992 ], [ - "tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[2-32]", - 1.9311450261157006 + "tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[1-8]", + 4.198510793969035 ], [ - "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[16-32-0]", - 6.75402541924268 + "tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[2-16]", + 2.692502399906516 ], [ - "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[32-8-0]", - 5.700722533976659 + "tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[2-32]", + 2.386414110660553 ], [ - "tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[16-0]", - 5.485037375707179 + "tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[2-8]", + 4.363627223297954 ], [ - "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[32-16-0]", - 7.350736285792664 + "tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[3-16]", + 3.1008117590099573 ], [ - "tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[64-1]", - 5.567466985201463 + "tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[3-32]", + 2.8256580103188753 ], [ - "tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[1-32]", - 3.0928381660487503 + "tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[3-8]", + 4.709306005388498 ], [ - "tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[3-16]", - 4.969628504943103 + "tb/axis_cobs_decode/test_axis_cobs_decode.py::test_axis_cobs_decode", + 54.703362938947976 ], [ - "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[32-8-1]", - 6.349259327631444 + "tb/axis_cobs_encode/test_axis_cobs_encode.py::test_axis_cobs_encode[0]", + 59.61654491070658 ], [ - "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[8-8-1]", - 8.99701732210815 + "tb/axis_cobs_encode/test_axis_cobs_encode.py::test_axis_cobs_encode[1]", + 58.38117606379092 ], [ - "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[32-16-1]", - 9.235969598637894 + "tb/axis_demux/test_axis_demux.py::test_axis_demux[4-16]", + 9.495323976501822 ], [ "tb/axis_demux/test_axis_demux.py::test_axis_demux[4-32]", - 9.538748257094994 + 6.288495775312185 ], [ - "tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[32-32-1]", - 7.745282106800005 + "tb/axis_demux/test_axis_demux.py::test_axis_demux[4-8]", + 18.37746755219996 ], [ - "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[8-16-1]", - 6.823330983985215 + "tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[16-0]", + 10.393654933199286 ], [ - "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[8-8-0]", - 5.227903796825558 + "tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[16-1]", + 10.415633418597281 ], [ - "tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[2-16]", - 4.625557336257771 + "tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[32-0]", + 8.87678967602551 ], [ - "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[16-16-1]", - 4.071335948072374 + "tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[32-1]", + 11.098749899305403 ], [ - "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[16-8-1]", - 4.733443915843964 + "tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[64-0]", + 9.422987031750381 ], [ - "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[8-32-1]", - 5.415835375897586 + "tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[64-1]", + 9.699089953675866 ], [ - "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[32-32-0]", - 4.996851528994739 + "tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[8-0]", + 11.044142582453787 ], [ - "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[32-16-1]", - 6.541619814001024 + "tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[8-1]", + 10.83289519045502 ], [ - "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[16-32-1]", - 7.052604428259656 + "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[16-16-0]", + 10.219957860186696 ], [ - "tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[1-8]", - 5.601683816872537 + "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[16-16-1]", + 10.486625554971397 ], [ "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[16-32-0]", - 5.533007082296535 + 14.431692690588534 + ], + [ + "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[16-32-1]", + 15.22019578423351 ], [ "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[16-8-0]", - 7.243967947084457 + 14.902987204492092 ], [ - "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[32-8-0]", - 7.279493370791897 + "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[16-8-1]", + 11.420198984444141 ], [ "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[32-16-0]", - 6.444722287822515 + 13.592619517818093 ], [ - "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[8-32-0]", - 5.101694668177515 + "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[32-16-1]", + 14.155559001490474 ], [ - "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[8-16-0]", - 7.083118926966563 + "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[32-32-0]", + 9.66715748142451 ], [ - "tb/axis_cobs_encode/test_axis_cobs_encode.py::test_axis_cobs_encode[1]", - 59.118370523210615 + "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[32-32-1]", + 9.999405917711556 ], [ - "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[16-16-0]", - 4.102237894199789 + "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[32-8-0]", + 22.81020149681717 ], [ - "tb/axis_frame_length_adjust/test_axis_frame_length_adjust.py::test_axis_frame_length_adjust[32]", - 5.109332735883072 + "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[32-8-1]", + 22.00941563770175 ], [ - "tb/axis_frame_length_adjust_fifo/test_axis_frame_length_adjust_fifo.py::test_axis_frame_length_adjust_fifo[8]", - 31.400041729211807 + "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[8-16-0]", + 15.273434751667082 ], [ - "tb/axis_frame_length_adjust/test_axis_frame_length_adjust.py::test_axis_frame_length_adjust[8]", - 6.68104299204424 + "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[8-16-1]", + 16.60779592487961 ], [ - "tb/axis_frame_length_adjust/test_axis_frame_length_adjust.py::test_axis_frame_length_adjust[16]", - 6.474900439847261 + "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[8-32-0]", + 22.687564965337515 ], [ - "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[32-32-1]", - 6.242845824919641 + "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[8-32-1]", + 23.706279216334224 ], [ - "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[32-8-1]", - 7.743526546051726 + "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[8-8-0]", + 11.400899667292833 ], [ - "tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[1-16]", - 3.705136342905462 + "tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[8-8-1]", + 12.362589475698769 + ], + [ + "tb/axis_frame_length_adjust/test_axis_frame_length_adjust.py::test_axis_frame_length_adjust[16]", + 4.168933109380305 + ], + [ + "tb/axis_frame_length_adjust/test_axis_frame_length_adjust.py::test_axis_frame_length_adjust[32]", + 3.4525004364550114 + ], + [ + "tb/axis_frame_length_adjust/test_axis_frame_length_adjust.py::test_axis_frame_length_adjust[8]", + 6.410637116059661 ], [ "tb/axis_frame_length_adjust_fifo/test_axis_frame_length_adjust_fifo.py::test_axis_frame_length_adjust_fifo[16]", - 20.34973389096558 + 18.54528176225722 ], [ "tb/axis_frame_length_adjust_fifo/test_axis_frame_length_adjust_fifo.py::test_axis_frame_length_adjust_fifo[32]", - 16.08616164396517 + 14.18258341960609 + ], + [ + "tb/axis_frame_length_adjust_fifo/test_axis_frame_length_adjust_fifo.py::test_axis_frame_length_adjust_fifo[8]", + 29.039860404096544 ], [ "tb/axis_mux/test_axis_mux.py::test_axis_mux[1-16]", - 3.7994882098864764 + 2.7313873413950205 + ], + [ + "tb/axis_mux/test_axis_mux.py::test_axis_mux[1-32]", + 2.1904209554195404 ], [ "tb/axis_mux/test_axis_mux.py::test_axis_mux[1-8]", - 5.135501990094781 + 3.747754485346377 ], [ - "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-8-0]", - 5.485889535630122 + "tb/axis_mux/test_axis_mux.py::test_axis_mux[4-16]", + 11.27847404498607 + ], + [ + "tb/axis_mux/test_axis_mux.py::test_axis_mux[4-32]", + 8.642733175307512 ], [ "tb/axis_mux/test_axis_mux.py::test_axis_mux[4-8]", - 20.19369739596732 + 18.31573899462819 ], [ - "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[1-32-0]", - 1.561464497121051 + "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-16-0]", + 7.748892365954816 ], [ - "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[1-8-2]", - 5.337176482891664 + "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-16-1]", + 8.249635859392583 ], [ - "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[1-16-0]", - 2.4496163791045547 + "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-16-2]", + 7.861832721158862 + ], + [ + "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-32-0]", + 7.814906599000096 + ], + [ + "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-32-1]", + 7.478627904318273 + ], + [ + "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-32-2]", + 8.728103519417346 + ], + [ + "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-8-0]", + 8.562991634011269 + ], + [ + "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-8-1]", + 8.853606965392828 ], [ "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-8-2]", - 5.699582742061466 + 10.302599406801164 ], [ - "tb/axis_mux/test_axis_mux.py::test_axis_mux[4-16]", - 13.788990666391328 + "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[1-16-0]", + 8.787222282029688 ], [ "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[1-16-1]", - 2.0261589840520173 + 9.04017788078636 + ], + [ + "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[1-16-2]", + 8.097946026362479 + ], + [ + "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[1-32-0]", + 6.282582161016762 ], [ "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[1-32-1]", - 3.11552363820374 + 8.129383312538266 ], [ - "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-32-2]", - 3.1700994120910764 + "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[1-32-2]", + 7.848937609232962 + ], + [ + "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[1-8-0]", + 6.842636492103338 ], [ "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[1-8-1]", - 5.923924314090982 + 9.389978448860347 ], [ - "tb/axis_mux/test_axis_mux.py::test_axis_mux[4-32]", - 7.972323116846383 + "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[1-8-2]", + 9.679912436753511 + ], + [ + "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[2-16-0]", + 7.3867769334465265 + ], + [ + "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[2-16-1]", + 8.3051818581298 + ], + [ + "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[2-16-2]", + 8.126775786280632 + ], + [ + "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[2-32-0]", + 7.643013674765825 ], [ "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[2-32-1]", - 3.0416711198631674 + 8.875891353935003 ], [ "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[2-32-2]", - 3.31060864077881 + 8.881751327775419 ], [ - "tb/axis_rate_limit/test_axis_rate_limit.py::test_axis_rate_limit[8]", - 20.769642549101263 + "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[2-8-0]", + 9.265739167109132 ], [ - "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[2-16-1]", - 2.2487674469593912 + "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[2-8-1]", + 10.11152211111039 ], [ "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[2-8-2]", - 5.267716252012178 + 8.9882518267259 ], [ - "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-8-1]", - 4.380764984991401 + "tb/axis_ram_switch/test_axis_ram_switch.py::test_axis_ram_switch[1-1-32-32]", + 10.515457849018276 ], [ - "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[2-8-1]", - 5.923575797583908 + "tb/axis_ram_switch/test_axis_ram_switch.py::test_axis_ram_switch[1-1-32-8]", + 23.015697445720434 ], [ - "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-32-0]", - 3.125814629253 + "tb/axis_ram_switch/test_axis_ram_switch.py::test_axis_ram_switch[1-1-8-32]", + 23.339799378067255 ], [ - "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-16-1]", - 3.336507275234908 + "tb/axis_ram_switch/test_axis_ram_switch.py::test_axis_ram_switch[1-1-8-8]", + 13.376416123472154 ], [ - "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[1-16-2]", - 1.9632220172788948 + "tb/axis_ram_switch/test_axis_ram_switch.py::test_axis_ram_switch[1-4-32-32]", + 15.32683065906167 ], [ - "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-16-2]", - 3.883476712042466 + "tb/axis_ram_switch/test_axis_ram_switch.py::test_axis_ram_switch[1-4-32-8]", + 25.25002766493708 ], [ - "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[2-32-0]", - 3.093082787003368 + "tb/axis_ram_switch/test_axis_ram_switch.py::test_axis_ram_switch[1-4-8-32]", + 38.70766987465322 ], [ - "tb/axis_rate_limit/test_axis_rate_limit.py::test_axis_rate_limit[32]", - 10.130301585188136 + "tb/axis_ram_switch/test_axis_ram_switch.py::test_axis_ram_switch[1-4-8-8]", + 23.433873833157122 ], [ - "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[2-16-2]", - 4.012627676827833 + "tb/axis_ram_switch/test_axis_ram_switch.py::test_axis_ram_switch[4-1-32-32]", + 38.13711098767817 ], [ - "tb/axis_mux/test_axis_mux.py::test_axis_mux[1-32]", - 2.9808353721164167 + "tb/axis_ram_switch/test_axis_ram_switch.py::test_axis_ram_switch[4-1-32-8]", + 108.21325397957116 ], [ - "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[1-8-0]", - 5.803714094217867 + "tb/axis_ram_switch/test_axis_ram_switch.py::test_axis_ram_switch[4-1-8-32]", + 31.623636212199926 ], [ - "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-32-1]", - 3.003260609926656 + "tb/axis_ram_switch/test_axis_ram_switch.py::test_axis_ram_switch[4-1-8-8]", + 46.20649198163301 ], [ - "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[1-32-2]", - 3.2507454978767782 + "tb/axis_ram_switch/test_axis_ram_switch.py::test_axis_ram_switch[4-4-32-32]", + 36.73501186538488 ], [ - "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[2-16-0]", - 3.6894755298271775 + "tb/axis_ram_switch/test_axis_ram_switch.py::test_axis_ram_switch[4-4-32-8]", + 74.66561388224363 ], [ - "tb/axis_rate_limit/test_axis_rate_limit.py::test_axis_rate_limit[16]", - 8.963756704237312 + "tb/axis_ram_switch/test_axis_ram_switch.py::test_axis_ram_switch[4-4-8-32]", + 51.076979514211416 ], [ - "tb/axis_rate_limit/test_axis_rate_limit.py::test_axis_rate_limit[64]", - 10.402460905024782 + "tb/axis_ram_switch/test_axis_ram_switch.py::test_axis_ram_switch[4-4-8-8]", + 57.79113897960633 ], [ - "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-16-0]", - 3.8650066470727324 + "tb/axis_rate_limit/test_axis_rate_limit.py::test_axis_rate_limit[16]", + 11.168463669717312 ], [ - "tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[2-8-0]", - 5.027584437979385 + "tb/axis_rate_limit/test_axis_rate_limit.py::test_axis_rate_limit[32]", + 8.627638365142047 ], [ - "tb/axis_register/test_axis_register.py::test_axis_register[32-2]", - 1.9581461090128869 + "tb/axis_rate_limit/test_axis_rate_limit.py::test_axis_rate_limit[64]", + 8.261535592377186 ], [ - "tb/axis_srl_fifo/test_axis_srl_fifo.py::test_axis_srl_fifo[32]", - 3.736734739970416 + "tb/axis_rate_limit/test_axis_rate_limit.py::test_axis_rate_limit[8]", + 18.147929472848773 ], [ - "tb/axis_srl_fifo/test_axis_srl_fifo.py::test_axis_srl_fifo[16]", - 8.996261154068634 + "tb/axis_register/test_axis_register.py::test_axis_register[16-0]", + 7.894782732240856 ], [ "tb/axis_register/test_axis_register.py::test_axis_register[16-1]", - 2.9044135720469058 + 8.63187304791063 ], [ "tb/axis_register/test_axis_register.py::test_axis_register[16-2]", - 2.8243840648792684 + 7.384906925261021 + ], + [ + "tb/axis_register/test_axis_register.py::test_axis_register[32-0]", + 7.757337849587202 ], [ "tb/axis_register/test_axis_register.py::test_axis_register[32-1]", - 2.0222588209435344 + 8.349224615842104 ], [ - "tb/axis_register/test_axis_register.py::test_axis_register[16-0]", - 2.4645475880242884 + "tb/axis_register/test_axis_register.py::test_axis_register[32-2]", + 8.202552252449095 ], [ - "tb/axis_register/test_axis_register.py::test_axis_register[8-2]", - 3.2331926051992923 + "tb/axis_register/test_axis_register.py::test_axis_register[8-0]", + 9.517546870745718 ], [ "tb/axis_register/test_axis_register.py::test_axis_register[8-1]", - 3.5214195190928876 + 9.37106759659946 ], [ - "tb/axis_register/test_axis_register.py::test_axis_register[32-0]", - 1.7708753109909594 + "tb/axis_register/test_axis_register.py::test_axis_register[8-2]", + 8.858125889673829 ], [ - "tb/axis_register/test_axis_register.py::test_axis_register[8-0]", - 3.4010211310815066 + "tb/axis_srl_fifo/test_axis_srl_fifo.py::test_axis_srl_fifo[16]", + 21.84353820141405 ], [ - "tb/axis_srl_fifo/test_axis_srl_fifo.py::test_axis_srl_fifo[8]", - 6.477508798940107 + "tb/axis_srl_fifo/test_axis_srl_fifo.py::test_axis_srl_fifo[32]", + 19.996354782022536 ], [ - "tb/axis_srl_register/test_axis_srl_register.py::test_axis_srl_register[32]", - 1.7259511651936918 + "tb/axis_srl_fifo/test_axis_srl_fifo.py::test_axis_srl_fifo[64]", + 21.48448957223445 + ], + [ + "tb/axis_srl_fifo/test_axis_srl_fifo.py::test_axis_srl_fifo[8]", + 26.7858392810449 ], [ "tb/axis_srl_register/test_axis_srl_register.py::test_axis_srl_register[16]", - 2.1865312785375863 + 8.400416224263608 + ], + [ + "tb/axis_srl_register/test_axis_srl_register.py::test_axis_srl_register[32]", + 7.900802923366427 ], [ "tb/axis_srl_register/test_axis_srl_register.py::test_axis_srl_register[8]", - 3.358409102773294 + 8.795997133478522 ], [ - "tb/axis_srl_fifo/test_axis_srl_fifo.py::test_axis_srl_fifo[64]", - 5.105467068264261 + "tb/axis_switch/test_axis_switch.py::test_axis_switch[1-1-16]", + 8.356694316491485 + ], + [ + "tb/axis_switch/test_axis_switch.py::test_axis_switch[1-1-32]", + 7.599317821674049 + ], + [ + "tb/axis_switch/test_axis_switch.py::test_axis_switch[1-1-8]", + 9.621531581506133 + ], + [ + "tb/axis_switch/test_axis_switch.py::test_axis_switch[1-4-16]", + 13.667238024994731 + ], + [ + "tb/axis_switch/test_axis_switch.py::test_axis_switch[1-4-32]", + 13.290190611034632 + ], + [ + "tb/axis_switch/test_axis_switch.py::test_axis_switch[1-4-8]", + 16.950868975371122 + ], + [ + "tb/axis_switch/test_axis_switch.py::test_axis_switch[4-1-16]", + 33.594604125246406 + ], + [ + "tb/axis_switch/test_axis_switch.py::test_axis_switch[4-1-32]", + 33.071954203769565 + ], + [ + "tb/axis_switch/test_axis_switch.py::test_axis_switch[4-1-8]", + 36.24359125737101 + ], + [ + "tb/axis_switch/test_axis_switch.py::test_axis_switch[4-4-16]", + 33.63313777372241 + ], + [ + "tb/axis_switch/test_axis_switch.py::test_axis_switch[4-4-32]", + 22.27076547406614 + ], + [ + "tb/axis_switch/test_axis_switch.py::test_axis_switch[4-4-8]", + 39.87212748918682 ] ] \ No newline at end of file diff --git a/lib/axis/README.md b/lib/axis/README.md index e37a26355..408e40f2b 100644 --- a/lib/axis/README.md +++ b/lib/axis/README.md @@ -14,14 +14,14 @@ utilize [cocotbext-axi](https://github.com/alexforencich/cocotbext-axi). ## Documentation -### arbiter module +### `arbiter` module General-purpose parametrizable arbiter. Supports priority and round-robin arbitration. Supports blocking until request release or acknowledge. -### axis_adapter module +### `axis_adapter` module -The axis_adapter module bridges AXI stream busses of differing widths. The +The `axis_adapter` module bridges AXI stream buses of differing widths. The module is parametrizable, but there are certain restrictions. First, the bus word widths must be identical (e.g. one 8-bit lane and eight 8-bit lanes, but not one 16-bit lane and one 32-bit lane). Second, the bus widths must be @@ -29,80 +29,80 @@ related by an integer multiple (e.g. 2 words and 6 words, but not 4 words and 6 words). Wait states will be inserted on the wider bus side when necessary. -### axis_arb_mux module +### `axis_arb_mux` module -Frame-aware AXI stream arbitrated muliplexer with parametrizable data width +Frame-aware AXI stream arbitrated multiplexer with parametrizable data width and port count. Supports priority and round-robin arbitration. -Wrappers can generated with axis_arb_mux_wrap.py. +Wrappers can generated with `axis_arb_mux_wrap.py`. -### axis_async_fifo module +### `axis_async_fifo` module Configurable word-based or frame-based asynchronous FIFO with parametrizable data width, depth, type, and bad frame detection. Supports power of two depths only. -### axis_async_fifo_adapter module +### `axis_async_fifo_adapter` module Configurable word-based or frame-based asynchronous FIFO with parametrizable data width, depth, type, and bad frame detection. Supports different input and output data widths, inserting an axis_adapter instance appropriately. Supports power of two depths only. -### axis_broadcast module +### `axis_broadcast` module AXI stream broadcaster. Duplicates one input stream across multiple output streams. -### axis_cobs_decode +### `axis_cobs_decode` Consistent Overhead Byte Stuffing (COBS) decoder. Fixed 8 bit width. -### axis_cobs_encode +### `axis_cobs_encode` Consistent Overhead Byte Stuffing (COBS) encoder. Fixed 8 bit width. Configurable zero insertion. -### axis_crosspoint module +### `axis_crosspoint` module -Basic crosspoint switch. tready signal not supported. Parametrizable data +Basic crosspoint switch. `tready` signal not supported. Parametrizable data width. -Wrappers can generated with axis_crosspoint_wrap.py. +Wrappers can generated with `axis_crosspoint_wrap.py`. -### axis_demux module +### `axis_demux` module -Frame-aware AXI stream demuliplexer with parametrizable data width and port +Frame-aware AXI stream demultiplexer with parametrizable data width and port count. -### axis_fifo module +### `axis_fifo` module Configurable word-based or frame-based synchronous FIFO with parametrizable data width, depth, type, and bad frame detection. Supports power of two depths only. -### axis_fifo_adapter module +### `axis_fifo_adapter` module Configurable word-based or frame-based synchronous FIFO with parametrizable data width, depth, type, and bad frame detection. Supports different input and output data widths, inserting an axis_adapter instance appropriately. Supports power of two depths only. -### axis_frame_join module +### `axis_frame_join` module Frame joiner with optional tag and parametrizable port count. 8 bit data path only. -Wrappers can generated with axis_frame_join_wrap.py. +Wrappers can generated with `axis_frame_join_wrap.py`. -### axis_frame_length_adjust module +### `axis_frame_length_adjust` module Frame length adjuster module. Truncates or pads frames as necessary to meet the specified minimum and maximum length. Reports the original and current lengths as well as whether the packet was truncated or padded. Length limits are configurable at run time. -### axis_frame_length_adjust_fifo module +### `axis_frame_length_adjust_fifo` module Frame length adjuster module with FIFO. Truncates or pads frames as necessary to meet the specified minimum and maximum length. Reports the original and @@ -110,75 +110,75 @@ current lengths as well as whether the packet was truncated or padded. FIFOs are used so that the status information can be read before the packet itself. Length limits are configurable at run time. -### axis_ll_bridge module +### `axis_ll_bridge` module AXI stream to LocalLink bridge. -### axis_mux module +### `axis_mux` module -Frame-aware AXI stream muliplexer with parametrizable data width and port +Frame-aware AXI stream multiplexer with parametrizable data width and port count. -Wrappers can generated with axis_mux_wrap.py. +Wrappers can generated with `axis_mux_wrap.py`. -### axis_pipeline_register module +### `axis_pipeline_register` module Parametrizable register pipeline. LENGTH parameter determines number of register stages. -### axis_ram_switch module +### `axis_ram_switch` module Frame-aware AXI stream RAM switch with parametrizable data width, port count, and FIFO size. Uses block RAM for storing packets in transit, time-sharing the RAM interface between ports. Functionally equivalent to a combination of per-port frame FIFOs and width converters connected to an AXI stream switch. -### axis_rate_limit module +### `axis_rate_limit` module Fractional rate limiter, supports word and frame modes. Inserts wait states to limit data rate to specified ratio. Frame mode inserts wait states at end of frames, word mode ignores frames and inserts wait states at any point. Parametrizable data width. Rate and mode are configurable at run time. -### axis_register module +### `axis_register` module Datapath register with parameter to select between skid buffer, simple buffer, and bypass. Use to improve timing for long routes. -### axis_srl_fifo module +### `axis_srl_fifo` module SRL-based FIFO. Good for small FIFOs. SRLs on Xilinx FPGAs have a very fast input setup time, so this module can be used to aid in timing closure. -### axis_srl_register module +### `axis_srl_register` module SRL-based register. SRLs on Xilinx FPGAs have a very fast input setup time, so this module can be used to aid in timing closure. -### axis_stat_counter module +### `axis_stat_counter` module Statistics counter module. Counts bytes and frames passing through monitored AXI stream interface. Trigger signal used to reset and dump counts out of AXI -interface, along with tag value. Use with axis_frame_join_N to form a single +interface, along with tag value. Use with `axis_frame_join` to form a single monolithic frame from multiple monitored points with the same trigger. -### axis_switch module +### `axis_switch` module Frame-aware AXI stream switch with parametrizable data width and port count. -Wrappers can generated with axis_switch_wrap.py. +Wrappers can generated with `axis_switch_wrap.py`. -### axis_tap module +### `axis_tap` module AXI stream tap module. Used to make a copy of an AXI stream bus without affecting the bus. Back-pressure on the output results in truncated frames -with tuser set. +with `tuser` set. -### ll_axis_bridge module +### `ll_axis_bridge` module LocalLink to AXI stream bridge. -### priority_encoder module +### `priority_encoder` module Parametrizable priority encoder. diff --git a/lib/axis/rtl/axis_ram_switch.v b/lib/axis/rtl/axis_ram_switch.v index e5bb03c11..f12cfaba8 100644 --- a/lib/axis/rtl/axis_ram_switch.v +++ b/lib/axis/rtl/axis_ram_switch.v @@ -184,8 +184,18 @@ initial begin if (M_BASE == 0) begin // M_BASE is zero, route with tdest as port index + $display("Addressing configuration for axis_switch instance %m"); + for (i = 0; i < M_COUNT; i = i + 1) begin + $display("%d: %08x-%08x (connect mask %b)", i, i << (DEST_WIDTH-CL_M_COUNT), ((i+1) << (DEST_WIDTH-CL_M_COUNT))-1, M_CONNECT[i*S_COUNT +: S_COUNT]); + end + end else if (M_TOP == 0) begin // M_TOP is zero, assume equal to M_BASE + $display("Addressing configuration for axis_switch instance %m"); + for (i = 0; i < M_COUNT; i = i + 1) begin + $display("%d: %08x (connect mask %b)", i, M_BASE[i*DEST_WIDTH +: DEST_WIDTH], M_CONNECT[i*S_COUNT +: S_COUNT]); + end + for (i = 0; i < M_COUNT; i = i + 1) begin for (j = i+1; j < M_COUNT; j = j + 1) begin if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] == M_BASE[j*DEST_WIDTH +: DEST_WIDTH]) begin @@ -197,6 +207,11 @@ initial begin end end end else begin + $display("Addressing configuration for axis_switch instance %m"); + for (i = 0; i < M_COUNT; i = i + 1) begin + $display("%d: %08x-%08x (connect mask %b)", i, M_BASE[i*DEST_WIDTH +: DEST_WIDTH], M_TOP[i*DEST_WIDTH +: DEST_WIDTH], M_CONNECT[i*S_COUNT +: S_COUNT]); + end + for (i = 0; i < M_COUNT; i = i + 1) begin if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] > M_TOP[i*DEST_WIDTH +: DEST_WIDTH]) begin $error("Error: invalid range (instance %m)"); @@ -413,16 +428,23 @@ generate select_valid_next = select_valid_reg && !(port_axis_tvalid && port_axis_tready && port_axis_tlast); if (port_axis_tvalid && !select_valid_reg && !drop_reg) begin - select_next = 1'b0; + select_next = 0; select_valid_next = 1'b0; drop_next = 1'b1; for (k = 0; k < M_COUNT; k = k + 1) begin if (M_BASE == 0) begin - // M_BASE is zero, route with $clog2(M_COUNT) MSBs of tdest as port index - if (port_axis_tdest[DEST_WIDTH-CL_M_COUNT +: CL_M_COUNT] == k && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin - select_next = k; + if (M_COUNT == 1) begin + // M_BASE is zero with only one output port, ignore tdest + select_next = 0; select_valid_next = 1'b1; drop_next = 1'b0; + end else begin + // M_BASE is zero, route with $clog2(M_COUNT) MSBs of tdest as port index + if (port_axis_tdest[DEST_WIDTH-CL_M_COUNT +: CL_M_COUNT] == k && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin + select_next = k; + select_valid_next = 1'b1; + drop_next = 1'b0; + end end end else if (M_TOP == 0) begin // M_TOP is zero, assume equal to M_BASE @@ -657,7 +679,7 @@ generate cmd_table_start_addr_end = wr_ptr_cur_reg + 1; cmd_table_start_len = len_reg; cmd_table_start_select = select_reg; - cmd_table_start_tkeep = S_KEEP_ENABLE ? port_axis_tkeep : 1'b1; + cmd_table_start_tkeep = port_axis_tkeep; cmd_table_start_tid = port_axis_tid; cmd_table_start_tdest = port_axis_tdest; cmd_table_start_tuser = port_axis_tuser; @@ -669,7 +691,7 @@ generate // read cmd_valid_next = cmd_valid_reg & ~port_cmd_ready; - if (!cmd_valid_reg && cmd_table_active[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]] && cmd_table_read_ptr_reg != cmd_table_start_ptr_reg) begin + if (!cmd_valid_reg && cmd_table_active[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]] && cmd_table_read_ptr_reg != cmd_table_start_ptr_reg && (!ram_wr_en_reg || ram_wr_ack)) begin cmd_table_read_en = 1'b1; cmd_addr_next = cmd_table_addr_start[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]]; cmd_len_next = cmd_table_len[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]]; @@ -747,7 +769,7 @@ generate if (cmd_table_finish_en) begin cmd_table_finish_ptr_reg <= cmd_table_finish_ptr_reg + 1; - cmd_table_active[cmd_table_finish_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= 1'b1; + cmd_table_active[cmd_table_finish_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= 1'b0; end if (rst) begin diff --git a/lib/axis/rtl/axis_ram_switch_wrap.py b/lib/axis/rtl/axis_ram_switch_wrap.py new file mode 100755 index 000000000..32bbd8e3c --- /dev/null +++ b/lib/axis/rtl/axis_ram_switch_wrap.py @@ -0,0 +1,258 @@ +#!/usr/bin/env python +""" +Generates an AXI Stream switch 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 = "axis_ram_switch_wrap_{0}x{1}".format(m, n) + + if output is None: + output = name + ".v" + + print("Generating {0}x{1} port AXI stream RAM switch wrapper {2}...".format(m, n, name)) + + cm = (m-1).bit_length() + cn = (n-1).bit_length() + + t = Template(u"""/* + +Copyright (c) 2018-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-Stream {{m}}x{{n}} RAM switch (wrapper) + */ +module {{name}} # +( + // FIFO depth in words (each virtual FIFO) + // KEEP_WIDTH words per cycle if KEEP_ENABLE set + // Rounded up to nearest power of 2 cycles + parameter FIFO_DEPTH = 4096, + // Command FIFO depth (each virtual FIFO) + // Rounded up to nearest power of 2 + parameter CMD_FIFO_DEPTH = 32, + // Speedup factor (internal data width scaling factor) + // Speedup of 0 scales internal width to provide maximum bandwidth + parameter SPEEDUP = 0, + // Width of input AXI stream interfaces in bits + parameter S_DATA_WIDTH = 8, + // Propagate tkeep signal + parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8), + // Width of output AXI stream interfaces in bits + parameter M_DATA_WIDTH = 8, + // Propagate tkeep signal + parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8), + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // tdest signal width + // must be wide enough to uniquely address outputs + parameter DEST_WIDTH = {{cn}}, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1, + // tuser value for bad frame marker + parameter USER_BAD_FRAME_VALUE = 1'b1, + // tuser mask for bad frame marker + parameter USER_BAD_FRAME_MASK = 1'b1, + // Drop frames marked bad + parameter DROP_BAD_FRAME = 0, + // Drop incoming frames when full + // When set, s_axis_tready is always asserted + parameter DROP_WHEN_FULL = 0, +{%- for p in range(n) %} + // Output interface routing base tdest selection + // Port selected if M_BASE <= tdest <= M_TOP + parameter M{{'%02d'%p}}_BASE = {{p}}, + // Output interface routing top tdest selection + // Port selected if M_BASE <= tdest <= M_TOP + parameter M{{'%02d'%p}}_TOP = {{p}}, + // Interface connection control + parameter M{{'%02d'%p}}_CONNECT = {{m}}'b{% for p in range(m) %}1{% endfor %}, +{%- endfor %} + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "ROUND_ROBIN", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH", + // RAM read data output pipeline stages + parameter RAM_PIPELINE = 2 +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ +{%- for p in range(m) %} + input wire [S_DATA_WIDTH-1:0] s{{'%02d'%p}}_axis_tdata, + input wire [S_KEEP_WIDTH-1:0] s{{'%02d'%p}}_axis_tkeep, + input wire s{{'%02d'%p}}_axis_tvalid, + output wire s{{'%02d'%p}}_axis_tready, + input wire s{{'%02d'%p}}_axis_tlast, + input wire [ID_WIDTH-1:0] s{{'%02d'%p}}_axis_tid, + input wire [DEST_WIDTH-1:0] s{{'%02d'%p}}_axis_tdest, + input wire [USER_WIDTH-1:0] s{{'%02d'%p}}_axis_tuser, +{% endfor %} + /* + * AXI Stream outputs + */ +{%- for p in range(n) %} + output wire [M_DATA_WIDTH-1:0] m{{'%02d'%p}}_axis_tdata, + output wire [M_KEEP_WIDTH-1:0] m{{'%02d'%p}}_axis_tkeep, + output wire m{{'%02d'%p}}_axis_tvalid, + input wire m{{'%02d'%p}}_axis_tready, + output wire m{{'%02d'%p}}_axis_tlast, + output wire [ID_WIDTH-1:0] m{{'%02d'%p}}_axis_tid, + output wire [DEST_WIDTH-1:0] m{{'%02d'%p}}_axis_tdest, + output wire [USER_WIDTH-1:0] m{{'%02d'%p}}_axis_tuser, +{% endfor %} + /* + * Status + */ + output wire [{{m-1}}:0] status_overflow, + output wire [{{m-1}}:0] status_bad_frame, + output wire [{{m-1}}:0] status_good_frame +); + +// parameter sizing helpers +function [DEST_WIDTH-1:0] w_dw(input [DEST_WIDTH-1:0] val); + w_dw = val; +endfunction + +function [{{m-1}}:0] w_s(input [{{m-1}}:0] val); + w_s = val; +endfunction + +axis_ram_switch #( + .FIFO_DEPTH(FIFO_DEPTH), + .CMD_FIFO_DEPTH(CMD_FIFO_DEPTH), + .SPEEDUP(SPEEDUP), + .S_COUNT({{m}}), + .M_COUNT({{n}}), + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_KEEP_ENABLE(S_KEEP_ENABLE), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK), + .DROP_BAD_FRAME(DROP_BAD_FRAME), + .DROP_WHEN_FULL(DROP_WHEN_FULL), + .M_BASE({ {% for p in range(n-1,-1,-1) %}w_dw(M{{'%02d'%p}}_BASE){% if not loop.last %}, {% endif %}{% endfor %} }), + .M_TOP({ {% for p in range(n-1,-1,-1) %}w_dw(M{{'%02d'%p}}_TOP){% if not loop.last %}, {% endif %}{% endfor %} }), + .M_CONNECT({ {% for p in range(n-1,-1,-1) %}w_s(M{{'%02d'%p}}_CONNECT){% if not loop.last %}, {% endif %}{% endfor %} }), + .ARB_TYPE(ARB_TYPE), + .LSB_PRIORITY(LSB_PRIORITY), + .RAM_PIPELINE(RAM_PIPELINE) +) +axis_ram_switch_inst ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tdata{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tkeep({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tkeep{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tvalid({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tready({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tready{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tlast({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tlast{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tid({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tdest({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tdest{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tuser({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tuser{% if not loop.last %}, {% endif %}{% endfor %} }), + // AXI outputs + .m_axis_tdata({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tdata{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tkeep({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tkeep{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tvalid({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tready({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tready{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tlast({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tlast{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tid({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tid{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tdest({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tdest{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tuser({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tuser{% if not loop.last %}, {% endif %}{% endfor %} }), + // Status + .status_overflow(status_overflow), + .status_bad_frame(status_bad_frame), + .status_good_frame(status_good_frame) +); + +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/lib/axis/rtl/axis_switch.v b/lib/axis/rtl/axis_switch.v index 0e785851e..0a58a8ef0 100644 --- a/lib/axis/rtl/axis_switch.v +++ b/lib/axis/rtl/axis_switch.v @@ -119,8 +119,18 @@ initial begin if (M_BASE == 0) begin // M_BASE is zero, route with tdest as port index + $display("Addressing configuration for axis_switch instance %m"); + for (i = 0; i < M_COUNT; i = i + 1) begin + $display("%d: %08x-%08x (connect mask %b)", i, i << (DEST_WIDTH-CL_M_COUNT), ((i+1) << (DEST_WIDTH-CL_M_COUNT))-1, M_CONNECT[i*S_COUNT +: S_COUNT]); + end + end else if (M_TOP == 0) begin // M_TOP is zero, assume equal to M_BASE + $display("Addressing configuration for axis_switch instance %m"); + for (i = 0; i < M_COUNT; i = i + 1) begin + $display("%d: %08x (connect mask %b)", i, M_BASE[i*DEST_WIDTH +: DEST_WIDTH], M_CONNECT[i*S_COUNT +: S_COUNT]); + end + for (i = 0; i < M_COUNT; i = i + 1) begin for (j = i+1; j < M_COUNT; j = j + 1) begin if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] == M_BASE[j*DEST_WIDTH +: DEST_WIDTH]) begin @@ -132,6 +142,11 @@ initial begin end end end else begin + $display("Addressing configuration for axis_switch instance %m"); + for (i = 0; i < M_COUNT; i = i + 1) begin + $display("%d: %08x-%08x (connect mask %b)", i, M_BASE[i*DEST_WIDTH +: DEST_WIDTH], M_TOP[i*DEST_WIDTH +: DEST_WIDTH], M_CONNECT[i*S_COUNT +: S_COUNT]); + end + for (i = 0; i < M_COUNT; i = i + 1) begin if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] > M_TOP[i*DEST_WIDTH +: DEST_WIDTH]) begin $error("Error: invalid range (instance %m)"); @@ -182,17 +197,24 @@ generate drop_next = drop_reg && !(int_s_axis_tvalid[m] && int_s_axis_tready[m] && int_s_axis_tlast[m]); select_valid_next = select_valid_reg && !(int_s_axis_tvalid[m] && int_s_axis_tready[m] && int_s_axis_tlast[m]); - if (int_s_axis_tvalid[m] && !select_valid_reg) begin - select_next = 1'b0; + if (int_s_axis_tvalid[m] && !select_valid_reg && !drop_reg) begin + select_next = 0; select_valid_next = 1'b0; drop_next = 1'b1; for (k = 0; k < M_COUNT; k = k + 1) begin if (M_BASE == 0) begin - // M_BASE is zero, route with $clog2(M_COUNT) MSBs of tdest as port index - if (int_s_axis_tdest[m*DEST_WIDTH+(DEST_WIDTH-CL_M_COUNT) +: CL_M_COUNT] == k && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin - select_next = k; + if (M_COUNT == 1) begin + // M_BASE is zero with only one output port, ignore tdest + select_next = 0; select_valid_next = 1'b1; drop_next = 1'b0; + end else begin + // M_BASE is zero, route with $clog2(M_COUNT) MSBs of tdest as port index + if (int_s_axis_tdest[m*DEST_WIDTH+(DEST_WIDTH-CL_M_COUNT) +: CL_M_COUNT] == k && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin + select_next = k; + select_valid_next = 1'b1; + drop_next = 1'b0; + end end end else if (M_TOP == 0) begin // M_TOP is zero, assume equal to M_BASE diff --git a/lib/axis/rtl/axis_switch_wrap.py b/lib/axis/rtl/axis_switch_wrap.py index 88cc8e842..c82600fa0 100755 --- a/lib/axis/rtl/axis_switch_wrap.py +++ b/lib/axis/rtl/axis_switch_wrap.py @@ -86,7 +86,7 @@ def generate(ports=4, name=None, output=None): parameter ID_WIDTH = 8, // tdest signal width // must be wide enough to uniquely address outputs - parameter DEST_WIDTH = {{cm}}, + parameter DEST_WIDTH = {{cn}}, // Propagate tuser signal parameter USER_ENABLE = 1, // tuser signal width @@ -145,11 +145,11 @@ def generate(ports=4, name=None, output=None): ); // parameter sizing helpers -function [31:0] w_32(input [31:0] val); - w_32 = val; +function [DEST_WIDTH-1:0] w_dw(input [DEST_WIDTH-1:0] val); + w_dw = val; endfunction -function [S_COUNT-1:0] w_s(input [S_COUNT-1:0] val); +function [{{m-1}}:0] w_s(input [{{m-1}}:0] val); w_s = val; endfunction @@ -164,8 +164,8 @@ def generate(ports=4, name=None, output=None): .DEST_WIDTH(DEST_WIDTH), .USER_ENABLE(USER_ENABLE), .USER_WIDTH(USER_WIDTH), - .M_BASE({ {% for p in range(n-1,-1,-1) %}w_32(M{{'%02d'%p}}_BASE){% if not loop.last %}, {% endif %}{% endfor %} }), - .M_TOP({ {% for p in range(n-1,-1,-1) %}w_32(M{{'%02d'%p}}_TOP){% if not loop.last %}, {% endif %}{% endfor %} }), + .M_BASE({ {% for p in range(n-1,-1,-1) %}w_dw(M{{'%02d'%p}}_BASE){% if not loop.last %}, {% endif %}{% endfor %} }), + .M_TOP({ {% for p in range(n-1,-1,-1) %}w_dw(M{{'%02d'%p}}_TOP){% if not loop.last %}, {% endif %}{% endfor %} }), .M_CONNECT({ {% for p in range(n-1,-1,-1) %}w_s(M{{'%02d'%p}}_CONNECT){% if not loop.last %}, {% endif %}{% endfor %} }), .S_REG_TYPE(S_REG_TYPE), .M_REG_TYPE(M_REG_TYPE), diff --git a/lib/axis/tb/axis_adapter/test_axis_adapter.py b/lib/axis/tb/axis_adapter/test_axis_adapter.py index 39df48baf..11a2268bf 100644 --- a/lib/axis/tb/axis_adapter/test_axis_adapter.py +++ b/lib/axis/tb/axis_adapter/test_axis_adapter.py @@ -26,6 +26,7 @@ import itertools import logging import os +import random import cocotb_test.simulator import pytest @@ -109,12 +110,75 @@ async def run_test(dut, payload_lengths=None, payload_data=None, idle_inserter=N await RisingEdge(dut.clk) +async def run_test_tuser_assert(dut): + + tb = TB(dut) + + await tb.reset() + + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 32)) + test_frame = AxiStreamFrame(test_data, tuser=1) + await tb.source.send(test_frame) + + rx_frame = await tb.sink.recv() + + assert rx_frame.tdata == test_data + assert rx_frame.tuser + + assert tb.sink.empty() + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + +async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None): + + tb = TB(dut) + + byte_lanes = max(tb.source.byte_lanes, tb.sink.byte_lanes) + id_count = 2**len(tb.source.bus.tid) + + cur_id = 1 + + await tb.reset() + + tb.set_idle_generator(idle_inserter) + tb.set_backpressure_generator(backpressure_inserter) + + test_frames = [] + + for k in range(128): + length = random.randint(1, byte_lanes*16) + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), length)) + test_frame = AxiStreamFrame(test_data) + test_frame.tid = cur_id + test_frame.tdest = cur_id + + test_frames.append(test_frame) + await tb.source.send(test_frame) + + cur_id = (cur_id + 1) % id_count + + for test_frame in test_frames: + rx_frame = await tb.sink.recv() + + assert rx_frame.tdata == test_frame.tdata + assert rx_frame.tid == test_frame.tid + assert rx_frame.tdest == test_frame.tdest + assert not rx_frame.tuser + + assert tb.sink.empty() + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + def cycle_pause(): return itertools.cycle([1, 1, 1, 0]) def size_list(): - data_width = max(int(os.getenv("PARAM_S_DATA_WIDTH")), int(os.getenv("PARAM_M_DATA_WIDTH"))) + data_width = max(len(cocotb.top.s_axis_tdata), len(cocotb.top.m_axis_tdata)) byte_width = data_width // 8 return list(range(1, byte_width*4+1))+[512]+[1]*64 @@ -132,6 +196,15 @@ def incrementing_payload(length): factory.add_option("backpressure_inserter", [None, cycle_pause]) factory.generate_tests() + for test in [run_test_tuser_assert]: + factory = TestFactory(test) + factory.generate_tests() + + factory = TestFactory(run_stress_test) + factory.add_option("idle_inserter", [None, cycle_pause]) + factory.add_option("backpressure_inserter", [None, cycle_pause]) + factory.generate_tests() + # cocotb-test diff --git a/lib/axis/tb/axis_arb_mux/test_axis_arb_mux.py b/lib/axis/tb/axis_arb_mux/test_axis_arb_mux.py index fbf8385f6..cf7e87781 100644 --- a/lib/axis/tb/axis_arb_mux/test_axis_arb_mux.py +++ b/lib/axis/tb/axis_arb_mux/test_axis_arb_mux.py @@ -26,6 +26,7 @@ import itertools import logging import os +import random import subprocess import cocotb_test.simulator @@ -33,7 +34,7 @@ import cocotb from cocotb.clock import Clock -from cocotb.triggers import RisingEdge +from cocotb.triggers import RisingEdge, Event from cocotb.regression import TestFactory from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink @@ -43,7 +44,7 @@ class TB(object): def __init__(self, dut): self.dut = dut - ports = int(os.getenv("PARAM_PORTS")) + ports = len(dut.axis_arb_mux_inst.s_axis_tvalid) self.log = logging.getLogger("cocotb.tb") self.log.setLevel(logging.DEBUG) @@ -113,12 +114,135 @@ async def run_test(dut, payload_lengths=None, payload_data=None, idle_inserter=N await RisingEdge(dut.clk) +async def run_test_tuser_assert(dut, port=0): + + tb = TB(dut) + + await tb.reset() + + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 32)) + test_frame = AxiStreamFrame(test_data, tuser=1) + await tb.source[port].send(test_frame) + + rx_frame = await tb.sink.recv() + + assert rx_frame.tdata == test_data + assert rx_frame.tuser + + assert tb.sink.empty() + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + +async def run_arb_test(dut): + + tb = TB(dut) + + byte_lanes = tb.source[0].byte_lanes + id_count = 2**len(tb.source[0].bus.tid) + + cur_id = 1 + + await tb.reset() + + test_frames = [] + + length = byte_lanes*16 + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), length)) + + for k in range(5): + test_frame = AxiStreamFrame(test_data, tx_complete=Event()) + test_frame.tid = cur_id + + if k == 0: + test_frame.tdest = 0 + elif k == 4: + await test_frames[1].tx_complete.wait() + for j in range(8): + await RisingEdge(dut.clk) + test_frame.tdest = 0 + else: + test_frame.tdest = 1 + + test_frames.append(test_frame) + await tb.source[test_frame.tdest].send(test_frame) + + cur_id = (cur_id + 1) % id_count + + for k in [0, 1, 2, 4, 3]: + test_frame = test_frames[k] + rx_frame = await tb.sink.recv() + + assert rx_frame.tdata == test_frame.tdata + assert rx_frame.tid == test_frame.tid + assert rx_frame.tdest == test_frame.tdest + assert not rx_frame.tuser + + assert tb.sink.empty() + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + +async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None): + + tb = TB(dut) + + byte_lanes = tb.source[0].byte_lanes + id_count = 2**len(tb.source[0].bus.tid) + + cur_id = 1 + + await tb.reset() + + tb.set_idle_generator(idle_inserter) + tb.set_backpressure_generator(backpressure_inserter) + + test_frames = [list() for x in tb.source] + + for p in range(len(tb.source)): + for k in range(128): + length = random.randint(1, byte_lanes*16) + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), length)) + test_frame = AxiStreamFrame(test_data) + test_frame.tid = p + test_frame.tdest = cur_id + + test_frames[p].append(test_frame) + await tb.source[p].send(test_frame) + + cur_id = (cur_id + 1) % id_count + + while any(test_frames): + rx_frame = await tb.sink.recv() + + test_frame = None + + for lst in test_frames: + if lst and lst[0].tid == rx_frame.tid: + test_frame = lst.pop(0) + break + + assert test_frame is not None + + assert rx_frame.tdata == test_frame.tdata + assert rx_frame.tid == test_frame.tid + assert rx_frame.tdest == test_frame.tdest + assert not rx_frame.tuser + + assert tb.sink.empty() + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + def cycle_pause(): return itertools.cycle([1, 1, 1, 0]) def size_list(): - data_width = int(os.getenv("PARAM_DATA_WIDTH")) + data_width = len(cocotb.top.s00_axis_tdata) byte_width = data_width // 8 return list(range(1, byte_width*4+1))+[512]+[1]*64 @@ -129,7 +253,7 @@ def incrementing_payload(length): if cocotb.SIM_NAME: - ports = int(os.getenv("PARAM_PORTS")) + ports = len(cocotb.top.axis_arb_mux_inst.s_axis_tvalid) factory = TestFactory(run_test) factory.add_option("payload_lengths", [size_list]) @@ -139,6 +263,20 @@ def incrementing_payload(length): factory.add_option("port", list(range(ports))) factory.generate_tests() + for test in [run_test_tuser_assert]: + factory = TestFactory(test) + factory.add_option("port", list(range(ports))) + factory.generate_tests() + + if ports > 1: + factory = TestFactory(run_arb_test) + factory.generate_tests() + + factory = TestFactory(run_stress_test) + factory.add_option("idle_inserter", [None, cycle_pause]) + factory.add_option("backpressure_inserter", [None, cycle_pause]) + factory.generate_tests() + # cocotb-test diff --git a/lib/axis/tb/axis_async_fifo/test_axis_async_fifo.py b/lib/axis/tb/axis_async_fifo/test_axis_async_fifo.py index f5eea3afd..b3693ca3d 100644 --- a/lib/axis/tb/axis_async_fifo/test_axis_async_fifo.py +++ b/lib/axis/tb/axis_async_fifo/test_axis_async_fifo.py @@ -26,6 +26,7 @@ import itertools import logging import os +import random import cocotb_test.simulator import pytest @@ -225,6 +226,48 @@ async def run_test_overflow(dut): await RisingEdge(dut.s_clk) +async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None): + + tb = TB(dut) + + byte_lanes = tb.source.byte_lanes + id_count = 2**len(tb.source.bus.tid) + + cur_id = 1 + + await tb.reset() + + tb.set_idle_generator(idle_inserter) + tb.set_backpressure_generator(backpressure_inserter) + + test_frames = [] + + for k in range(128): + length = random.randint(1, byte_lanes*16) + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), length)) + test_frame = AxiStreamFrame(test_data) + test_frame.tid = cur_id + test_frame.tdest = cur_id + + test_frames.append(test_frame) + await tb.source.send(test_frame) + + cur_id = (cur_id + 1) % id_count + + for test_frame in test_frames: + rx_frame = await tb.sink.recv() + + assert rx_frame.tdata == test_frame.tdata + assert rx_frame.tid == test_frame.tid + assert rx_frame.tdest == test_frame.tdest + assert not rx_frame.tuser + + assert tb.sink.empty() + + await RisingEdge(dut.s_clk) + await RisingEdge(dut.s_clk) + + def cycle_pause(): return itertools.cycle([1, 1, 1, 0]) @@ -252,6 +295,11 @@ def incrementing_payload(length): factory = TestFactory(test) factory.generate_tests() + factory = TestFactory(run_stress_test) + factory.add_option("idle_inserter", [None, cycle_pause]) + factory.add_option("backpressure_inserter", [None, cycle_pause]) + factory.generate_tests() + # cocotb-test diff --git a/lib/axis/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py b/lib/axis/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py index c2a4761e6..5bcef7600 100644 --- a/lib/axis/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py +++ b/lib/axis/tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py @@ -26,6 +26,7 @@ import itertools import logging import os +import random import cocotb_test.simulator import pytest @@ -228,6 +229,48 @@ async def run_test_overflow(dut): await RisingEdge(dut.s_clk) +async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None): + + tb = TB(dut) + + byte_lanes = max(tb.source.byte_lanes, tb.sink.byte_lanes) + id_count = 2**len(tb.source.bus.tid) + + cur_id = 1 + + await tb.reset() + + tb.set_idle_generator(idle_inserter) + tb.set_backpressure_generator(backpressure_inserter) + + test_frames = [] + + for k in range(128): + length = random.randint(1, byte_lanes*16) + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), length)) + test_frame = AxiStreamFrame(test_data) + test_frame.tid = cur_id + test_frame.tdest = cur_id + + test_frames.append(test_frame) + await tb.source.send(test_frame) + + cur_id = (cur_id + 1) % id_count + + for test_frame in test_frames: + rx_frame = await tb.sink.recv() + + assert rx_frame.tdata == test_frame.tdata + assert rx_frame.tid == test_frame.tid + assert rx_frame.tdest == test_frame.tdest + assert not rx_frame.tuser + + assert tb.sink.empty() + + await RisingEdge(dut.s_clk) + await RisingEdge(dut.s_clk) + + def cycle_pause(): return itertools.cycle([1, 1, 1, 0]) @@ -255,6 +298,11 @@ def incrementing_payload(length): factory = TestFactory(test) factory.generate_tests() + factory = TestFactory(run_stress_test) + factory.add_option("idle_inserter", [None, cycle_pause]) + factory.add_option("backpressure_inserter", [None, cycle_pause]) + factory.generate_tests() + # cocotb-test diff --git a/lib/axis/tb/axis_fifo/test_axis_fifo.py b/lib/axis/tb/axis_fifo/test_axis_fifo.py index 0cf880dcb..7eb5ff240 100644 --- a/lib/axis/tb/axis_fifo/test_axis_fifo.py +++ b/lib/axis/tb/axis_fifo/test_axis_fifo.py @@ -26,6 +26,7 @@ import itertools import logging import os +import random import cocotb_test.simulator import pytest @@ -224,6 +225,48 @@ async def run_test_overflow(dut): await RisingEdge(dut.clk) +async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None): + + tb = TB(dut) + + byte_lanes = tb.source.byte_lanes + id_count = 2**len(tb.source.bus.tid) + + cur_id = 1 + + await tb.reset() + + tb.set_idle_generator(idle_inserter) + tb.set_backpressure_generator(backpressure_inserter) + + test_frames = [] + + for k in range(128): + length = random.randint(1, byte_lanes*16) + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), length)) + test_frame = AxiStreamFrame(test_data) + test_frame.tid = cur_id + test_frame.tdest = cur_id + + test_frames.append(test_frame) + await tb.source.send(test_frame) + + cur_id = (cur_id + 1) % id_count + + for test_frame in test_frames: + rx_frame = await tb.sink.recv() + + assert rx_frame.tdata == test_frame.tdata + assert rx_frame.tid == test_frame.tid + assert rx_frame.tdest == test_frame.tdest + assert not rx_frame.tuser + + assert tb.sink.empty() + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + def cycle_pause(): return itertools.cycle([1, 1, 1, 0]) @@ -251,6 +294,11 @@ def incrementing_payload(length): factory = TestFactory(test) factory.generate_tests() + factory = TestFactory(run_stress_test) + factory.add_option("idle_inserter", [None, cycle_pause]) + factory.add_option("backpressure_inserter", [None, cycle_pause]) + factory.generate_tests() + # cocotb-test diff --git a/lib/axis/tb/axis_fifo_adapter/test_axis_fifo_adapter.py b/lib/axis/tb/axis_fifo_adapter/test_axis_fifo_adapter.py index d6f91ac5c..2a5c56214 100644 --- a/lib/axis/tb/axis_fifo_adapter/test_axis_fifo_adapter.py +++ b/lib/axis/tb/axis_fifo_adapter/test_axis_fifo_adapter.py @@ -26,6 +26,7 @@ import itertools import logging import os +import random import cocotb_test.simulator import pytest @@ -224,6 +225,48 @@ async def run_test_overflow(dut): await RisingEdge(dut.clk) +async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None): + + tb = TB(dut) + + byte_lanes = max(tb.source.byte_lanes, tb.sink.byte_lanes) + id_count = 2**len(tb.source.bus.tid) + + cur_id = 1 + + await tb.reset() + + tb.set_idle_generator(idle_inserter) + tb.set_backpressure_generator(backpressure_inserter) + + test_frames = [] + + for k in range(128): + length = random.randint(1, byte_lanes*16) + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), length)) + test_frame = AxiStreamFrame(test_data) + test_frame.tid = cur_id + test_frame.tdest = cur_id + + test_frames.append(test_frame) + await tb.source.send(test_frame) + + cur_id = (cur_id + 1) % id_count + + for test_frame in test_frames: + rx_frame = await tb.sink.recv() + + assert rx_frame.tdata == test_frame.tdata + assert rx_frame.tid == test_frame.tid + assert rx_frame.tdest == test_frame.tdest + assert not rx_frame.tuser + + assert tb.sink.empty() + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + def cycle_pause(): return itertools.cycle([1, 1, 1, 0]) @@ -251,6 +294,11 @@ def incrementing_payload(length): factory = TestFactory(test) factory.generate_tests() + factory = TestFactory(run_stress_test) + factory.add_option("idle_inserter", [None, cycle_pause]) + factory.add_option("backpressure_inserter", [None, cycle_pause]) + factory.generate_tests() + # cocotb-test diff --git a/lib/axis/tb/axis_mux/test_axis_mux.py b/lib/axis/tb/axis_mux/test_axis_mux.py index fb9a26d52..12a06efa0 100644 --- a/lib/axis/tb/axis_mux/test_axis_mux.py +++ b/lib/axis/tb/axis_mux/test_axis_mux.py @@ -43,7 +43,7 @@ class TB(object): def __init__(self, dut): self.dut = dut - ports = int(os.getenv("PORTS")) + ports = len(dut.axis_mux_inst.s_axis_tvalid) self.log = logging.getLogger("cocotb.tb") self.log.setLevel(logging.DEBUG) @@ -119,6 +119,30 @@ async def run_test(dut, payload_lengths=None, payload_data=None, idle_inserter=N await RisingEdge(dut.clk) +async def run_test_tuser_assert(dut, port=0): + + tb = TB(dut) + + await tb.reset() + + dut.enable.setimmediatevalue(1) + dut.select.setimmediatevalue(port) + + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 32)) + test_frame = AxiStreamFrame(test_data, tuser=1) + await tb.source[port].send(test_frame) + + rx_frame = await tb.sink.recv() + + assert rx_frame.tdata == test_data + assert rx_frame.tuser + + assert tb.sink.empty() + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + def cycle_pause(): return itertools.cycle([1, 1, 1, 0]) @@ -135,7 +159,7 @@ def incrementing_payload(length): if cocotb.SIM_NAME: - ports = int(os.getenv("PORTS")) + ports = len(cocotb.top.axis_mux_inst.s_axis_tvalid) factory = TestFactory(run_test) factory.add_option("payload_lengths", [size_list]) @@ -145,6 +169,11 @@ def incrementing_payload(length): factory.add_option("port", list(range(ports))) factory.generate_tests() + for test in [run_test_tuser_assert]: + factory = TestFactory(test) + factory.add_option("port", list(range(ports))) + factory.generate_tests() + # cocotb-test diff --git a/lib/axis/tb/axis_pipeline_register/test_axis_pipeline_register.py b/lib/axis/tb/axis_pipeline_register/test_axis_pipeline_register.py index 7a0f86809..482ee5618 100644 --- a/lib/axis/tb/axis_pipeline_register/test_axis_pipeline_register.py +++ b/lib/axis/tb/axis_pipeline_register/test_axis_pipeline_register.py @@ -26,6 +26,7 @@ import itertools import logging import os +import random import cocotb_test.simulator import pytest @@ -109,6 +110,69 @@ async def run_test(dut, payload_lengths=None, payload_data=None, idle_inserter=N await RisingEdge(dut.clk) +async def run_test_tuser_assert(dut): + + tb = TB(dut) + + await tb.reset() + + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 32)) + test_frame = AxiStreamFrame(test_data, tuser=1) + await tb.source.send(test_frame) + + rx_frame = await tb.sink.recv() + + assert rx_frame.tdata == test_data + assert rx_frame.tuser + + assert tb.sink.empty() + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + +async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None): + + tb = TB(dut) + + byte_lanes = tb.source.byte_lanes + id_count = 2**len(tb.source.bus.tid) + + cur_id = 1 + + await tb.reset() + + tb.set_idle_generator(idle_inserter) + tb.set_backpressure_generator(backpressure_inserter) + + test_frames = [] + + for k in range(128): + length = random.randint(1, byte_lanes*16) + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), length)) + test_frame = AxiStreamFrame(test_data) + test_frame.tid = cur_id + test_frame.tdest = cur_id + + test_frames.append(test_frame) + await tb.source.send(test_frame) + + cur_id = (cur_id + 1) % id_count + + for test_frame in test_frames: + rx_frame = await tb.sink.recv() + + assert rx_frame.tdata == test_frame.tdata + assert rx_frame.tid == test_frame.tid + assert rx_frame.tdest == test_frame.tdest + assert not rx_frame.tuser + + assert tb.sink.empty() + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + def cycle_pause(): return itertools.cycle([1, 1, 1, 0]) @@ -132,6 +196,15 @@ def incrementing_payload(length): factory.add_option("backpressure_inserter", [None, cycle_pause]) factory.generate_tests() + for test in [run_test_tuser_assert]: + factory = TestFactory(test) + factory.generate_tests() + + factory = TestFactory(run_stress_test) + factory.add_option("idle_inserter", [None, cycle_pause]) + factory.add_option("backpressure_inserter", [None, cycle_pause]) + factory.generate_tests() + # cocotb-test diff --git a/lib/axis/tb/axis_ram_switch/Makefile b/lib/axis/tb/axis_ram_switch/Makefile new file mode 100644 index 000000000..79e898ce6 --- /dev/null +++ b/lib/axis/tb/axis_ram_switch/Makefile @@ -0,0 +1,134 @@ +# 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 = axis_ram_switch +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/axis_adapter.v +VERILOG_SOURCES += ../../rtl/arbiter.v +VERILOG_SOURCES += ../../rtl/priority_encoder.v + +# module parameters +export PARAM_FIFO_DEPTH ?= 4096 +export PARAM_CMD_FIFO_DEPTH ?= 32 +export PARAM_SPEEDUP ?= 0 +export PARAM_S_DATA_WIDTH ?= 8 +export PARAM_S_KEEP_ENABLE ?= $(shell expr $(PARAM_S_DATA_WIDTH) \> 8 ) +export PARAM_S_KEEP_WIDTH ?= $(shell expr $(PARAM_S_DATA_WIDTH) / 8 ) +export PARAM_M_DATA_WIDTH ?= 8 +export PARAM_M_KEEP_ENABLE ?= $(shell expr $(PARAM_M_DATA_WIDTH) \> 8 ) +export PARAM_M_KEEP_WIDTH ?= $(shell expr $(PARAM_M_DATA_WIDTH) / 8 ) +export PARAM_ID_ENABLE ?= 1 +export PARAM_ID_WIDTH ?= 16 +export PARAM_DEST_WIDTH ?= 8 +export PARAM_USER_ENABLE ?= 1 +export PARAM_USER_WIDTH ?= 1 +export PARAM_USER_BAD_FRAME_VALUE ?= 1 +export PARAM_USER_BAD_FRAME_MASK ?= 1 +export PARAM_DROP_BAD_FRAME ?= 0 +export PARAM_DROP_WHEN_FULL ?= 0 +export PARAM_RAM_PIPELINE ?= 2 + +ifeq ($(SIM), icarus) + PLUSARGS += -fst + + COMPILE_ARGS += -P $(TOPLEVEL).FIFO_DEPTH=$(PARAM_FIFO_DEPTH) + COMPILE_ARGS += -P $(TOPLEVEL).CMD_FIFO_DEPTH=$(PARAM_CMD_FIFO_DEPTH) + COMPILE_ARGS += -P $(TOPLEVEL).SPEEDUP=$(PARAM_SPEEDUP) + COMPILE_ARGS += -P $(TOPLEVEL).S_DATA_WIDTH=$(PARAM_S_DATA_WIDTH) + COMPILE_ARGS += -P $(TOPLEVEL).S_KEEP_ENABLE=$(PARAM_S_KEEP_ENABLE) + COMPILE_ARGS += -P $(TOPLEVEL).S_KEEP_WIDTH=$(PARAM_S_KEEP_WIDTH) + COMPILE_ARGS += -P $(TOPLEVEL).M_DATA_WIDTH=$(PARAM_M_DATA_WIDTH) + COMPILE_ARGS += -P $(TOPLEVEL).M_KEEP_ENABLE=$(PARAM_M_KEEP_ENABLE) + COMPILE_ARGS += -P $(TOPLEVEL).M_KEEP_WIDTH=$(PARAM_M_KEEP_WIDTH) + COMPILE_ARGS += -P $(TOPLEVEL).ID_ENABLE=$(PARAM_ID_ENABLE) + COMPILE_ARGS += -P $(TOPLEVEL).ID_WIDTH=$(PARAM_ID_WIDTH) + COMPILE_ARGS += -P $(TOPLEVEL).DEST_WIDTH=$(PARAM_DEST_WIDTH) + COMPILE_ARGS += -P $(TOPLEVEL).USER_ENABLE=$(PARAM_USER_ENABLE) + COMPILE_ARGS += -P $(TOPLEVEL).USER_WIDTH=$(PARAM_USER_WIDTH) + COMPILE_ARGS += -P $(TOPLEVEL).USER_BAD_FRAME_VALUE=$(PARAM_USER_BAD_FRAME_VALUE) + COMPILE_ARGS += -P $(TOPLEVEL).USER_BAD_FRAME_MASK=$(PARAM_USER_BAD_FRAME_MASK) + COMPILE_ARGS += -P $(TOPLEVEL).DROP_BAD_FRAME=$(PARAM_DROP_BAD_FRAME) + COMPILE_ARGS += -P $(TOPLEVEL).DROP_WHEN_FULL=$(PARAM_DROP_WHEN_FULL) + COMPILE_ARGS += -P $(TOPLEVEL).RAM_PIPELINE=$(PARAM_RAM_PIPELINE) + + 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 += -GFIFO_DEPTH=$(PARAM_FIFO_DEPTH) + COMPILE_ARGS += -GCMD_FIFO_DEPTH=$(PARAM_CMD_FIFO_DEPTH) + COMPILE_ARGS += -GSPEEDUP=$(PARAM_SPEEDUP) + COMPILE_ARGS += -GS_DATA_WIDTH=$(PARAM_S_DATA_WIDTH) + COMPILE_ARGS += -GS_KEEP_ENABLE=$(PARAM_S_KEEP_ENABLE) + COMPILE_ARGS += -GS_KEEP_WIDTH=$(PARAM_S_KEEP_WIDTH) + COMPILE_ARGS += -GM_DATA_WIDTH=$(PARAM_M_DATA_WIDTH) + COMPILE_ARGS += -GM_KEEP_ENABLE=$(PARAM_M_KEEP_ENABLE) + COMPILE_ARGS += -GM_KEEP_WIDTH=$(PARAM_M_KEEP_WIDTH) + COMPILE_ARGS += -GID_ENABLE=$(PARAM_ID_ENABLE) + COMPILE_ARGS += -GID_WIDTH=$(PARAM_ID_WIDTH) + COMPILE_ARGS += -GDEST_WIDTH=$(PARAM_DEST_WIDTH) + COMPILE_ARGS += -GUSER_ENABLE=$(PARAM_USER_ENABLE) + COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH) + COMPILE_ARGS += -GUSER_BAD_FRAME_VALUE=$(PARAM_USER_BAD_FRAME_VALUE) + COMPILE_ARGS += -GUSER_BAD_FRAME_MASK=$(PARAM_USER_BAD_FRAME_MASK) + COMPILE_ARGS += -GDROP_BAD_FRAME=$(PARAM_DROP_BAD_FRAME) + COMPILE_ARGS += -GDROP_WHEN_FULL=$(PARAM_DROP_WHEN_FULL) + COMPILE_ARGS += -GRAM_PIPELINE=$(PARAM_RAM_PIPELINE) + + 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/lib/axis/tb/axis_ram_switch/test_axis_ram_switch.py b/lib/axis/tb/axis_ram_switch/test_axis_ram_switch.py new file mode 100644 index 000000000..adc4ab699 --- /dev/null +++ b/lib/axis/tb/axis_ram_switch/test_axis_ram_switch.py @@ -0,0 +1,361 @@ +#!/usr/bin/env python +""" + +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, Event +from cocotb.regression import TestFactory + +from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink + + +class TB(object): + def __init__(self, dut): + self.dut = dut + + s_count = len(dut.axis_ram_switch_inst.s_axis_tvalid) + m_count = len(dut.axis_ram_switch_inst.m_axis_tvalid) + + self.log = logging.getLogger("cocotb.tb") + self.log.setLevel(logging.DEBUG) + + cocotb.fork(Clock(dut.clk, 10, units="ns").start()) + + self.source = [AxiStreamSource(AxiStreamBus.from_prefix(dut, f"s{k:02d}_axis"), dut.clk, dut.rst) for k in range(s_count)] + self.sink = [AxiStreamSink(AxiStreamBus.from_prefix(dut, f"m{k:02d}_axis"), dut.clk, dut.rst) for k in range(m_count)] + + def set_idle_generator(self, generator=None): + if generator: + for source in self.source: + source.set_pause_generator(generator()) + + def set_backpressure_generator(self, generator=None): + if generator: + for sink in self.sink: + sink.set_pause_generator(generator()) + + async def 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(dut, payload_lengths=None, payload_data=None, idle_inserter=None, backpressure_inserter=None, s=0, m=0): + + tb = TB(dut) + + id_count = 2**len(tb.source[s].bus.tid) + + cur_id = 1 + + await tb.reset() + + tb.set_idle_generator(idle_inserter) + tb.set_backpressure_generator(backpressure_inserter) + + test_frames = [] + + for test_data in [payload_data(x) for x in payload_lengths()]: + test_frame = AxiStreamFrame(test_data) + test_frame.tid = cur_id + test_frame.tdest = m + + test_frames.append(test_frame) + await tb.source[s].send(test_frame) + + cur_id = (cur_id + 1) % id_count + + for test_frame in test_frames: + rx_frame = await tb.sink[m].recv() + + assert rx_frame.tdata == test_frame.tdata + assert rx_frame.tid == test_frame.tid + assert rx_frame.tdest == test_frame.tdest + assert not rx_frame.tuser + + assert all(s.empty() for s in tb.sink) + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + +async def run_test_tuser_assert(dut, s=0, m=0): + + tb = TB(dut) + + await tb.reset() + + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 32)) + test_frame = AxiStreamFrame(test_data, tuser=1, tdest=m) + await tb.source[s].send(test_frame) + + rx_frame = await tb.sink[m].recv() + + assert rx_frame.tdata == test_data + assert rx_frame.tuser + + assert all(s.empty() for s in tb.sink) + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + +async def run_arb_test(dut): + + tb = TB(dut) + + byte_lanes = max(tb.source[0].byte_lanes, tb.sink[0].byte_lanes) + id_count = 2**len(tb.source[0].bus.tid) + + cur_id = 1 + + await tb.reset() + + test_frames = [] + + length = byte_lanes*16 + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), length)) + + for k in range(5): + test_frame = AxiStreamFrame(test_data, tx_complete=Event()) + test_frame.tid = cur_id + test_frame.tdest = 0 + + src_ind = 0 + + if k == 0: + src_ind = 0 + elif k == 4: + await test_frames[1].tx_complete.wait() + for j in range(8): + await RisingEdge(dut.clk) + src_ind = 0 + else: + src_ind = 1 + + test_frames.append(test_frame) + await tb.source[src_ind].send(test_frame) + + cur_id = (cur_id + 1) % id_count + + for k in [0, 1, 2, 4, 3]: + test_frame = test_frames[k] + rx_frame = await tb.sink[0].recv() + + assert rx_frame.tdata == test_frame.tdata + assert rx_frame.tid == test_frame.tid + assert rx_frame.tdest == test_frame.tdest + assert not rx_frame.tuser + + assert all(s.empty() for s in tb.sink) + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + +async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None): + + tb = TB(dut) + + byte_lanes = max(tb.source[0].byte_lanes, tb.sink[0].byte_lanes) + id_count = 2**len(tb.source[0].bus.tid) + + cur_id = 1 + + await tb.reset() + + tb.set_idle_generator(idle_inserter) + tb.set_backpressure_generator(backpressure_inserter) + + test_frames = [[list() for y in tb.sink] for x in tb.source] + + for p in range(len(tb.source)): + for k in range(128): + length = random.randint(1, byte_lanes*16) + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), length)) + test_frame = AxiStreamFrame(test_data) + test_frame.tid = cur_id + test_frame.tdest = random.randrange(len(tb.sink)) + + test_frames[p][test_frame.tdest].append(test_frame) + await tb.source[p].send(test_frame) + + cur_id = (cur_id + 1) % id_count + + for lst in test_frames: + while any(lst): + rx_frame = await tb.sink[[x for x in lst if x][0][0].tdest].recv() + + test_frame = None + + for lst_a in test_frames: + for lst_b in lst_a: + if lst_b and lst_b[0].tid == rx_frame.tid: + test_frame = lst_b.pop(0) + break + + assert test_frame is not None + + assert rx_frame.tdata == test_frame.tdata + assert rx_frame.tid == test_frame.tid + assert rx_frame.tdest == test_frame.tdest + assert not rx_frame.tuser + + assert all(s.empty() for s in tb.sink) + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + +def cycle_pause(): + return itertools.cycle([1, 1, 1, 0]) + + +def size_list(): + data_width = max(len(cocotb.top.s00_axis_tdata), len(cocotb.top.m00_axis_tdata)) + byte_width = data_width // 8 + return list(range(1, byte_width*4+1))+[512]+[1]*64 + + +def incrementing_payload(length): + return bytearray(itertools.islice(itertools.cycle(range(256)), length)) + + +if cocotb.SIM_NAME: + + s_count = len(cocotb.top.axis_ram_switch_inst.s_axis_tvalid) + m_count = len(cocotb.top.axis_ram_switch_inst.m_axis_tvalid) + + factory = TestFactory(run_test) + factory.add_option("payload_lengths", [size_list]) + factory.add_option("payload_data", [incrementing_payload]) + 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() + + for test in [run_test_tuser_assert]: + factory = TestFactory(test) + factory.add_option("s", range(min(s_count, 2))) + factory.add_option("m", range(min(m_count, 2))) + factory.generate_tests() + + if s_count > 1: + factory = TestFactory(run_arb_test) + factory.generate_tests() + + factory = TestFactory(run_stress_test) + factory.add_option("idle_inserter", [None, cycle_pause]) + factory.add_option("backpressure_inserter", [None, cycle_pause]) + factory.generate_tests() + + +# cocotb-test + +tests_dir = os.path.dirname(__file__) +rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl')) + + +@pytest.mark.parametrize("m_data_width", [8, 32]) +@pytest.mark.parametrize("s_data_width", [8, 32]) +@pytest.mark.parametrize("m_count", [1, 4]) +@pytest.mark.parametrize("s_count", [1, 4]) +def test_axis_ram_switch(request, s_count, m_count, s_data_width, m_data_width): + dut = "axis_ram_switch" + 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, "axis_adapter.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['FIFO_DEPTH'] = 4096 + parameters['CMD_FIFO_DEPTH'] = 32 + parameters['SPEEDUP'] = 0 + parameters['S_DATA_WIDTH'] = s_data_width + parameters['S_KEEP_ENABLE'] = int(parameters['S_DATA_WIDTH'] > 8) + parameters['S_KEEP_WIDTH'] = parameters['S_DATA_WIDTH'] // 8 + parameters['M_DATA_WIDTH'] = m_data_width + parameters['M_KEEP_ENABLE'] = int(parameters['M_DATA_WIDTH'] > 8) + parameters['M_KEEP_WIDTH'] = parameters['M_DATA_WIDTH'] // 8 + parameters['ID_ENABLE'] = 1 + parameters['ID_WIDTH'] = 16 + parameters['DEST_WIDTH'] = 8 + parameters['USER_ENABLE'] = 1 + parameters['USER_WIDTH'] = 1 + parameters['USER_BAD_FRAME_VALUE'] = 1 + parameters['USER_BAD_FRAME_MASK'] = 1 + parameters['DROP_BAD_FRAME'] = 0 + parameters['DROP_WHEN_FULL'] = 0 + parameters['RAM_PIPELINE'] = 2 + + 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, + ) diff --git a/lib/axis/tb/axis_register/test_axis_register.py b/lib/axis/tb/axis_register/test_axis_register.py index 26b11fe88..198622bd7 100644 --- a/lib/axis/tb/axis_register/test_axis_register.py +++ b/lib/axis/tb/axis_register/test_axis_register.py @@ -26,6 +26,7 @@ import itertools import logging import os +import random import cocotb_test.simulator import pytest @@ -109,6 +110,69 @@ async def run_test(dut, payload_lengths=None, payload_data=None, idle_inserter=N await RisingEdge(dut.clk) +async def run_test_tuser_assert(dut): + + tb = TB(dut) + + await tb.reset() + + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 32)) + test_frame = AxiStreamFrame(test_data, tuser=1) + await tb.source.send(test_frame) + + rx_frame = await tb.sink.recv() + + assert rx_frame.tdata == test_data + assert rx_frame.tuser + + assert tb.sink.empty() + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + +async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None): + + tb = TB(dut) + + byte_lanes = tb.source.byte_lanes + id_count = 2**len(tb.source.bus.tid) + + cur_id = 1 + + await tb.reset() + + tb.set_idle_generator(idle_inserter) + tb.set_backpressure_generator(backpressure_inserter) + + test_frames = [] + + for k in range(128): + length = random.randint(1, byte_lanes*16) + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), length)) + test_frame = AxiStreamFrame(test_data) + test_frame.tid = cur_id + test_frame.tdest = cur_id + + test_frames.append(test_frame) + await tb.source.send(test_frame) + + cur_id = (cur_id + 1) % id_count + + for test_frame in test_frames: + rx_frame = await tb.sink.recv() + + assert rx_frame.tdata == test_frame.tdata + assert rx_frame.tid == test_frame.tid + assert rx_frame.tdest == test_frame.tdest + assert not rx_frame.tuser + + assert tb.sink.empty() + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + def cycle_pause(): return itertools.cycle([1, 1, 1, 0]) @@ -132,6 +196,15 @@ def incrementing_payload(length): factory.add_option("backpressure_inserter", [None, cycle_pause]) factory.generate_tests() + for test in [run_test_tuser_assert]: + factory = TestFactory(test) + factory.generate_tests() + + factory = TestFactory(run_stress_test) + factory.add_option("idle_inserter", [None, cycle_pause]) + factory.add_option("backpressure_inserter", [None, cycle_pause]) + factory.generate_tests() + # cocotb-test diff --git a/lib/axis/tb/axis_srl_fifo/test_axis_srl_fifo.py b/lib/axis/tb/axis_srl_fifo/test_axis_srl_fifo.py index 73b16678b..8bdc70671 100644 --- a/lib/axis/tb/axis_srl_fifo/test_axis_srl_fifo.py +++ b/lib/axis/tb/axis_srl_fifo/test_axis_srl_fifo.py @@ -26,6 +26,7 @@ import itertools import logging import os +import random import cocotb_test.simulator import pytest @@ -214,6 +215,48 @@ async def run_test_overflow(dut): await RisingEdge(dut.clk) +async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None): + + tb = TB(dut) + + byte_lanes = tb.source.byte_lanes + id_count = 2**len(tb.source.bus.tid) + + cur_id = 1 + + await tb.reset() + + tb.set_idle_generator(idle_inserter) + tb.set_backpressure_generator(backpressure_inserter) + + test_frames = [] + + for k in range(128): + length = random.randint(1, byte_lanes*16) + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), length)) + test_frame = AxiStreamFrame(test_data) + test_frame.tid = cur_id + test_frame.tdest = cur_id + + test_frames.append(test_frame) + await tb.source.send(test_frame) + + cur_id = (cur_id + 1) % id_count + + for test_frame in test_frames: + rx_frame = await tb.sink.recv() + + assert rx_frame.tdata == test_frame.tdata + assert rx_frame.tid == test_frame.tid + assert rx_frame.tdest == test_frame.tdest + assert not rx_frame.tuser + + assert tb.sink.empty() + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + def cycle_pause(): return itertools.cycle([1, 1, 1, 0]) @@ -241,6 +284,11 @@ def incrementing_payload(length): factory = TestFactory(test) factory.generate_tests() + factory = TestFactory(run_stress_test) + factory.add_option("idle_inserter", [None, cycle_pause]) + factory.add_option("backpressure_inserter", [None, cycle_pause]) + factory.generate_tests() + # cocotb-test diff --git a/lib/axis/tb/axis_srl_register/test_axis_srl_register.py b/lib/axis/tb/axis_srl_register/test_axis_srl_register.py index 9f298d679..de826611d 100644 --- a/lib/axis/tb/axis_srl_register/test_axis_srl_register.py +++ b/lib/axis/tb/axis_srl_register/test_axis_srl_register.py @@ -26,6 +26,7 @@ import itertools import logging import os +import random import cocotb_test.simulator import pytest @@ -109,6 +110,69 @@ async def run_test(dut, payload_lengths=None, payload_data=None, idle_inserter=N await RisingEdge(dut.clk) +async def run_test_tuser_assert(dut): + + tb = TB(dut) + + await tb.reset() + + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 32)) + test_frame = AxiStreamFrame(test_data, tuser=1) + await tb.source.send(test_frame) + + rx_frame = await tb.sink.recv() + + assert rx_frame.tdata == test_data + assert rx_frame.tuser + + assert tb.sink.empty() + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + +async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None): + + tb = TB(dut) + + byte_lanes = tb.source.byte_lanes + id_count = 2**len(tb.source.bus.tid) + + cur_id = 1 + + await tb.reset() + + tb.set_idle_generator(idle_inserter) + tb.set_backpressure_generator(backpressure_inserter) + + test_frames = [] + + for k in range(128): + length = random.randint(1, byte_lanes*16) + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), length)) + test_frame = AxiStreamFrame(test_data) + test_frame.tid = cur_id + test_frame.tdest = cur_id + + test_frames.append(test_frame) + await tb.source.send(test_frame) + + cur_id = (cur_id + 1) % id_count + + for test_frame in test_frames: + rx_frame = await tb.sink.recv() + + assert rx_frame.tdata == test_frame.tdata + assert rx_frame.tid == test_frame.tid + assert rx_frame.tdest == test_frame.tdest + assert not rx_frame.tuser + + assert tb.sink.empty() + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + def cycle_pause(): return itertools.cycle([1, 1, 1, 0]) @@ -132,6 +196,15 @@ def incrementing_payload(length): factory.add_option("backpressure_inserter", [None, cycle_pause]) factory.generate_tests() + for test in [run_test_tuser_assert]: + factory = TestFactory(test) + factory.generate_tests() + + factory = TestFactory(run_stress_test) + factory.add_option("idle_inserter", [None, cycle_pause]) + factory.add_option("backpressure_inserter", [None, cycle_pause]) + factory.generate_tests() + # cocotb-test diff --git a/lib/axis/tb/axis_switch/Makefile b/lib/axis/tb/axis_switch/Makefile new file mode 100644 index 000000000..cd925f645 --- /dev/null +++ b/lib/axis/tb/axis_switch/Makefile @@ -0,0 +1,107 @@ +# 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 = axis_switch +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/axis_register.v +VERILOG_SOURCES += ../../rtl/arbiter.v +VERILOG_SOURCES += ../../rtl/priority_encoder.v + +# module parameters +export PARAM_DATA_WIDTH ?= 8 +export PARAM_KEEP_ENABLE ?= $(shell expr $(PARAM_DATA_WIDTH) \> 8 ) +export PARAM_KEEP_WIDTH ?= $(shell expr $(PARAM_DATA_WIDTH) / 8 ) +export PARAM_ID_ENABLE ?= 1 +export PARAM_ID_WIDTH ?= 16 +export PARAM_DEST_WIDTH ?= 8 +export PARAM_USER_ENABLE ?= 1 +export PARAM_USER_WIDTH ?= 1 +export PARAM_S_REG_TYPE ?= 0 +export PARAM_M_REG_TYPE ?= 2 + +ifeq ($(SIM), icarus) + PLUSARGS += -fst + + COMPILE_ARGS += -P $(TOPLEVEL).DATA_WIDTH=$(PARAM_DATA_WIDTH) + COMPILE_ARGS += -P $(TOPLEVEL).KEEP_ENABLE=$(PARAM_KEEP_ENABLE) + COMPILE_ARGS += -P $(TOPLEVEL).KEEP_WIDTH=$(PARAM_KEEP_WIDTH) + COMPILE_ARGS += -P $(TOPLEVEL).ID_ENABLE=$(PARAM_ID_ENABLE) + COMPILE_ARGS += -P $(TOPLEVEL).ID_WIDTH=$(PARAM_ID_WIDTH) + COMPILE_ARGS += -P $(TOPLEVEL).DEST_WIDTH=$(PARAM_DEST_WIDTH) + COMPILE_ARGS += -P $(TOPLEVEL).USER_ENABLE=$(PARAM_USER_ENABLE) + COMPILE_ARGS += -P $(TOPLEVEL).USER_WIDTH=$(PARAM_USER_WIDTH) + COMPILE_ARGS += -P $(TOPLEVEL).S_REG_TYPE=$(PARAM_S_REG_TYPE) + COMPILE_ARGS += -P $(TOPLEVEL).M_REG_TYPE=$(PARAM_M_REG_TYPE) + + 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 += -GKEEP_ENABLE=$(PARAM_KEEP_ENABLE) + COMPILE_ARGS += -GKEEP_WIDTH=$(PARAM_KEEP_WIDTH) + COMPILE_ARGS += -GID_ENABLE=$(PARAM_ID_ENABLE) + COMPILE_ARGS += -GID_WIDTH=$(PARAM_ID_WIDTH) + COMPILE_ARGS += -GDEST_WIDTH=$(PARAM_DEST_WIDTH) + COMPILE_ARGS += -GUSER_ENABLE=$(PARAM_USER_ENABLE) + COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH) + COMPILE_ARGS += -GS_REG_TYPE=$(PARAM_S_REG_TYPE) + COMPILE_ARGS += -GM_REG_TYPE=$(PARAM_M_REG_TYPE) + + 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/lib/axis/tb/axis_switch/test_axis_switch.py b/lib/axis/tb/axis_switch/test_axis_switch.py new file mode 100644 index 000000000..1bdceceec --- /dev/null +++ b/lib/axis/tb/axis_switch/test_axis_switch.py @@ -0,0 +1,351 @@ +#!/usr/bin/env python +""" + +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, Event +from cocotb.regression import TestFactory + +from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink + + +class TB(object): + def __init__(self, dut): + self.dut = dut + + s_count = len(dut.axis_switch_inst.s_axis_tvalid) + m_count = len(dut.axis_switch_inst.m_axis_tvalid) + + self.log = logging.getLogger("cocotb.tb") + self.log.setLevel(logging.DEBUG) + + cocotb.fork(Clock(dut.clk, 10, units="ns").start()) + + self.source = [AxiStreamSource(AxiStreamBus.from_prefix(dut, f"s{k:02d}_axis"), dut.clk, dut.rst) for k in range(s_count)] + self.sink = [AxiStreamSink(AxiStreamBus.from_prefix(dut, f"m{k:02d}_axis"), dut.clk, dut.rst) for k in range(m_count)] + + def set_idle_generator(self, generator=None): + if generator: + for source in self.source: + source.set_pause_generator(generator()) + + def set_backpressure_generator(self, generator=None): + if generator: + for sink in self.sink: + sink.set_pause_generator(generator()) + + async def 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(dut, payload_lengths=None, payload_data=None, idle_inserter=None, backpressure_inserter=None, s=0, m=0): + + tb = TB(dut) + + id_count = 2**len(tb.source[s].bus.tid) + + cur_id = 1 + + await tb.reset() + + tb.set_idle_generator(idle_inserter) + tb.set_backpressure_generator(backpressure_inserter) + + test_frames = [] + + for test_data in [payload_data(x) for x in payload_lengths()]: + test_frame = AxiStreamFrame(test_data) + test_frame.tid = cur_id + test_frame.tdest = m + + test_frames.append(test_frame) + await tb.source[s].send(test_frame) + + cur_id = (cur_id + 1) % id_count + + for test_frame in test_frames: + rx_frame = await tb.sink[m].recv() + + assert rx_frame.tdata == test_frame.tdata + assert rx_frame.tid == test_frame.tid + assert rx_frame.tdest == test_frame.tdest + assert not rx_frame.tuser + + assert all(s.empty() for s in tb.sink) + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + +async def run_test_tuser_assert(dut, s=0, m=0): + + tb = TB(dut) + + await tb.reset() + + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 32)) + test_frame = AxiStreamFrame(test_data, tuser=1, tdest=m) + await tb.source[s].send(test_frame) + + rx_frame = await tb.sink[m].recv() + + assert rx_frame.tdata == test_data + assert rx_frame.tuser + + assert all(s.empty() for s in tb.sink) + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + +async def run_arb_test(dut): + + tb = TB(dut) + + byte_lanes = tb.source[0].byte_lanes + id_count = 2**len(tb.source[0].bus.tid) + + cur_id = 1 + + await tb.reset() + + test_frames = [] + + length = byte_lanes*16 + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), length)) + + for k in range(5): + test_frame = AxiStreamFrame(test_data, tx_complete=Event()) + test_frame.tid = cur_id + test_frame.tdest = 0 + + src_ind = 0 + + if k == 0: + src_ind = 0 + elif k == 4: + await test_frames[1].tx_complete.wait() + for j in range(8): + await RisingEdge(dut.clk) + src_ind = 0 + else: + src_ind = 1 + + test_frames.append(test_frame) + await tb.source[src_ind].send(test_frame) + + cur_id = (cur_id + 1) % id_count + + for k in [0, 1, 2, 4, 3]: + test_frame = test_frames[k] + rx_frame = await tb.sink[0].recv() + + assert rx_frame.tdata == test_frame.tdata + assert rx_frame.tid == test_frame.tid + assert rx_frame.tdest == test_frame.tdest + assert not rx_frame.tuser + + assert all(s.empty() for s in tb.sink) + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + +async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None): + + tb = TB(dut) + + byte_lanes = tb.source[0].byte_lanes + id_count = 2**len(tb.source[0].bus.tid) + + cur_id = 1 + + await tb.reset() + + tb.set_idle_generator(idle_inserter) + tb.set_backpressure_generator(backpressure_inserter) + + test_frames = [[list() for y in tb.sink] for x in tb.source] + + for p in range(len(tb.source)): + for k in range(128): + length = random.randint(1, byte_lanes*16) + test_data = bytearray(itertools.islice(itertools.cycle(range(256)), length)) + test_frame = AxiStreamFrame(test_data) + test_frame.tid = cur_id + test_frame.tdest = random.randrange(len(tb.sink)) + + test_frames[p][test_frame.tdest].append(test_frame) + await tb.source[p].send(test_frame) + + cur_id = (cur_id + 1) % id_count + + for lst in test_frames: + while any(lst): + rx_frame = await tb.sink[[x for x in lst if x][0][0].tdest].recv() + + test_frame = None + + for lst_a in test_frames: + for lst_b in lst_a: + if lst_b and lst_b[0].tid == rx_frame.tid: + test_frame = lst_b.pop(0) + break + + assert test_frame is not None + + assert rx_frame.tdata == test_frame.tdata + assert rx_frame.tid == test_frame.tid + assert rx_frame.tdest == test_frame.tdest + assert not rx_frame.tuser + + assert all(s.empty() for s in tb.sink) + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + +def cycle_pause(): + return itertools.cycle([1, 1, 1, 0]) + + +def size_list(): + data_width = len(cocotb.top.s00_axis_tdata) + byte_width = data_width // 8 + return list(range(1, byte_width*4+1))+[512]+[1]*64 + + +def incrementing_payload(length): + return bytearray(itertools.islice(itertools.cycle(range(256)), length)) + + +if cocotb.SIM_NAME: + + s_count = len(cocotb.top.axis_switch_inst.s_axis_tvalid) + m_count = len(cocotb.top.axis_switch_inst.m_axis_tvalid) + + factory = TestFactory(run_test) + factory.add_option("payload_lengths", [size_list]) + factory.add_option("payload_data", [incrementing_payload]) + 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() + + for test in [run_test_tuser_assert]: + factory = TestFactory(test) + factory.add_option("s", range(min(s_count, 2))) + factory.add_option("m", range(min(m_count, 2))) + factory.generate_tests() + + if s_count > 1: + factory = TestFactory(run_arb_test) + factory.generate_tests() + + factory = TestFactory(run_stress_test) + factory.add_option("idle_inserter", [None, cycle_pause]) + factory.add_option("backpressure_inserter", [None, cycle_pause]) + factory.generate_tests() + + +# cocotb-test + +tests_dir = 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_axis_switch(request, s_count, m_count, data_width): + dut = "axis_switch" + 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, "axis_register.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['KEEP_ENABLE'] = int(parameters['DATA_WIDTH'] > 8) + parameters['KEEP_WIDTH'] = parameters['DATA_WIDTH'] // 8 + parameters['ID_ENABLE'] = 1 + parameters['ID_WIDTH'] = 16 + parameters['DEST_WIDTH'] = 8 + parameters['USER_ENABLE'] = 1 + parameters['USER_WIDTH'] = 1 + parameters['S_REG_TYPE'] = 0 + parameters['M_REG_TYPE'] = 2 + + 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, + )