mirror of
https://github.com/alexforencich/verilog-ethernet.git
synced 2025-01-14 06:43:18 +08:00
merged changes in axis
This commit is contained in:
commit
f236e7dff1
33
lib/axis/.github/workflows/regression-tests.yml
vendored
Normal file
33
lib/axis/.github/workflows/regression-tests.yml
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
name: Regression Tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Python ${{ matrix.python-version }} (${{ matrix.group }}/10)
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.9]
|
||||
group: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install Icarus Verilog
|
||||
run: |
|
||||
sudo apt install -y --no-install-recommends iverilog
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install tox tox-gh-actions
|
||||
|
||||
- name: Test with tox
|
||||
run: tox -- --splits 10 --group ${{ matrix.group }}
|
566
lib/axis/.test_durations
Normal file
566
lib/axis/.test_durations
Normal file
@ -0,0 +1,566 @@
|
||||
[
|
||||
[
|
||||
"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
|
||||
],
|
||||
[
|
||||
"tb/axis_adapter/test_axis_adapter.py::test_axis_register[16-32]",
|
||||
3.9831731799058616
|
||||
],
|
||||
[
|
||||
"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[32-8]",
|
||||
5.087842009961605
|
||||
],
|
||||
[
|
||||
"tb/axis_adapter/test_axis_adapter.py::test_axis_register[32-32]",
|
||||
3.063372714910656
|
||||
],
|
||||
[
|
||||
"tb/axis_adapter/test_axis_adapter.py::test_axis_register[8-32]",
|
||||
5.046062843874097
|
||||
],
|
||||
[
|
||||
"tb/axis_arb_mux/test_axis_arb_mux.py::test_axis_arb_mux[4-8]",
|
||||
19.940929262898862
|
||||
],
|
||||
[
|
||||
"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[16-8]",
|
||||
5.021060193190351
|
||||
],
|
||||
[
|
||||
"tb/axis_arb_mux/test_axis_arb_mux.py::test_axis_arb_mux[1-16]",
|
||||
3.669280304107815
|
||||
],
|
||||
[
|
||||
"tb/axis_async_fifo/test_axis_async_fifo.py::test_axis_async_fifo[16-1]",
|
||||
9.14714803174138
|
||||
],
|
||||
[
|
||||
"tb/axis_async_fifo/test_axis_async_fifo.py::test_axis_async_fifo[16-0]",
|
||||
6.992672564228997
|
||||
],
|
||||
[
|
||||
"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-32]",
|
||||
10.364595271181315
|
||||
],
|
||||
[
|
||||
"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[1-32]",
|
||||
3.247817731462419
|
||||
],
|
||||
[
|
||||
"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[32-1]",
|
||||
7.402053208090365
|
||||
],
|
||||
[
|
||||
"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[64-1]",
|
||||
7.729181434959173
|
||||
],
|
||||
[
|
||||
"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_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[8-8-1]",
|
||||
12.24883275711909
|
||||
],
|
||||
[
|
||||
"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_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[16-16-0]",
|
||||
7.650945161934942
|
||||
],
|
||||
[
|
||||
"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[8-32-1]",
|
||||
10.332504970021546
|
||||
],
|
||||
[
|
||||
"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-8-0]",
|
||||
10.321694103768095
|
||||
],
|
||||
[
|
||||
"tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[16-16-1]",
|
||||
8.53622887772508
|
||||
],
|
||||
[
|
||||
"tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[3-8]",
|
||||
6.011481063906103
|
||||
],
|
||||
[
|
||||
"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-32-0]",
|
||||
6.661400060867891
|
||||
],
|
||||
[
|
||||
"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_fifo/test_axis_fifo.py::test_axis_fifo[32-1]",
|
||||
5.509672118816525
|
||||
],
|
||||
[
|
||||
"tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[8-0]",
|
||||
6.509261818835512
|
||||
],
|
||||
[
|
||||
"tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[64-0]",
|
||||
5.037088224897161
|
||||
],
|
||||
[
|
||||
"tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[32-0]",
|
||||
4.089887966867536
|
||||
],
|
||||
[
|
||||
"tb/axis_demux/test_axis_demux.py::test_axis_demux[4-16]",
|
||||
11.541036840062588
|
||||
],
|
||||
[
|
||||
"tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[2-8]",
|
||||
5.955809738021344
|
||||
],
|
||||
[
|
||||
"tb/axis_demux/test_axis_demux.py::test_axis_demux[4-8]",
|
||||
17.149116148008034
|
||||
],
|
||||
[
|
||||
"tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[16-1]",
|
||||
6.595326299080625
|
||||
],
|
||||
[
|
||||
"tb/axis_cobs_decode/test_axis_cobs_decode.py::test_axis_cobs_decode",
|
||||
55.33392608910799
|
||||
],
|
||||
[
|
||||
"tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[8-1]",
|
||||
8.583656460046768
|
||||
],
|
||||
[
|
||||
"tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[2-32]",
|
||||
1.9311450261157006
|
||||
],
|
||||
[
|
||||
"tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[16-32-0]",
|
||||
6.75402541924268
|
||||
],
|
||||
[
|
||||
"tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[32-8-0]",
|
||||
5.700722533976659
|
||||
],
|
||||
[
|
||||
"tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[16-0]",
|
||||
5.485037375707179
|
||||
],
|
||||
[
|
||||
"tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[32-16-0]",
|
||||
7.350736285792664
|
||||
],
|
||||
[
|
||||
"tb/axis_fifo/test_axis_fifo.py::test_axis_fifo[64-1]",
|
||||
5.567466985201463
|
||||
],
|
||||
[
|
||||
"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-16]",
|
||||
4.969628504943103
|
||||
],
|
||||
[
|
||||
"tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[32-8-1]",
|
||||
6.349259327631444
|
||||
],
|
||||
[
|
||||
"tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[8-8-1]",
|
||||
8.99701732210815
|
||||
],
|
||||
[
|
||||
"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-32]",
|
||||
9.538748257094994
|
||||
],
|
||||
[
|
||||
"tb/axis_async_fifo_adapter/test_axis_async_fifo_adapter.py::test_axis_async_fifo_adapter[32-32-1]",
|
||||
7.745282106800005
|
||||
],
|
||||
[
|
||||
"tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[8-16-1]",
|
||||
6.823330983985215
|
||||
],
|
||||
[
|
||||
"tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[8-8-0]",
|
||||
5.227903796825558
|
||||
],
|
||||
[
|
||||
"tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[2-16]",
|
||||
4.625557336257771
|
||||
],
|
||||
[
|
||||
"tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[16-16-1]",
|
||||
4.071335948072374
|
||||
],
|
||||
[
|
||||
"tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[16-8-1]",
|
||||
4.733443915843964
|
||||
],
|
||||
[
|
||||
"tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[8-32-1]",
|
||||
5.415835375897586
|
||||
],
|
||||
[
|
||||
"tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[32-32-0]",
|
||||
4.996851528994739
|
||||
],
|
||||
[
|
||||
"tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[32-16-1]",
|
||||
6.541619814001024
|
||||
],
|
||||
[
|
||||
"tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[16-32-1]",
|
||||
7.052604428259656
|
||||
],
|
||||
[
|
||||
"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-32-0]",
|
||||
5.533007082296535
|
||||
],
|
||||
[
|
||||
"tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[16-8-0]",
|
||||
7.243967947084457
|
||||
],
|
||||
[
|
||||
"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[32-16-0]",
|
||||
6.444722287822515
|
||||
],
|
||||
[
|
||||
"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[8-16-0]",
|
||||
7.083118926966563
|
||||
],
|
||||
[
|
||||
"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[16-16-0]",
|
||||
4.102237894199789
|
||||
],
|
||||
[
|
||||
"tb/axis_frame_length_adjust/test_axis_frame_length_adjust.py::test_axis_frame_length_adjust[32]",
|
||||
5.109332735883072
|
||||
],
|
||||
[
|
||||
"tb/axis_frame_length_adjust_fifo/test_axis_frame_length_adjust_fifo.py::test_axis_frame_length_adjust_fifo[8]",
|
||||
31.400041729211807
|
||||
],
|
||||
[
|
||||
"tb/axis_frame_length_adjust/test_axis_frame_length_adjust.py::test_axis_frame_length_adjust[8]",
|
||||
6.68104299204424
|
||||
],
|
||||
[
|
||||
"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[32-32-1]",
|
||||
6.242845824919641
|
||||
],
|
||||
[
|
||||
"tb/axis_fifo_adapter/test_axis_fifo_adapter.py::test_axis_fifo_adapter[32-8-1]",
|
||||
7.743526546051726
|
||||
],
|
||||
[
|
||||
"tb/axis_broadcast/test_axis_broadcast.py::test_axis_broadcast[1-16]",
|
||||
3.705136342905462
|
||||
],
|
||||
[
|
||||
"tb/axis_frame_length_adjust_fifo/test_axis_frame_length_adjust_fifo.py::test_axis_frame_length_adjust_fifo[16]",
|
||||
20.34973389096558
|
||||
],
|
||||
[
|
||||
"tb/axis_frame_length_adjust_fifo/test_axis_frame_length_adjust_fifo.py::test_axis_frame_length_adjust_fifo[32]",
|
||||
16.08616164396517
|
||||
],
|
||||
[
|
||||
"tb/axis_mux/test_axis_mux.py::test_axis_mux[1-16]",
|
||||
3.7994882098864764
|
||||
],
|
||||
[
|
||||
"tb/axis_mux/test_axis_mux.py::test_axis_mux[1-8]",
|
||||
5.135501990094781
|
||||
],
|
||||
[
|
||||
"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-8]",
|
||||
20.19369739596732
|
||||
],
|
||||
[
|
||||
"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[1-8-2]",
|
||||
5.337176482891664
|
||||
],
|
||||
[
|
||||
"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-8-2]",
|
||||
5.699582742061466
|
||||
],
|
||||
[
|
||||
"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-1]",
|
||||
2.0261589840520173
|
||||
],
|
||||
[
|
||||
"tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[1-32-1]",
|
||||
3.11552363820374
|
||||
],
|
||||
[
|
||||
"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-8-1]",
|
||||
5.923924314090982
|
||||
],
|
||||
[
|
||||
"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[2-32-1]",
|
||||
3.0416711198631674
|
||||
],
|
||||
[
|
||||
"tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[2-32-2]",
|
||||
3.31060864077881
|
||||
],
|
||||
[
|
||||
"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-16-1]",
|
||||
2.2487674469593912
|
||||
],
|
||||
[
|
||||
"tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[2-8-2]",
|
||||
5.267716252012178
|
||||
],
|
||||
[
|
||||
"tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-8-1]",
|
||||
4.380764984991401
|
||||
],
|
||||
[
|
||||
"tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[2-8-1]",
|
||||
5.923575797583908
|
||||
],
|
||||
[
|
||||
"tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-32-0]",
|
||||
3.125814629253
|
||||
],
|
||||
[
|
||||
"tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-16-1]",
|
||||
3.336507275234908
|
||||
],
|
||||
[
|
||||
"tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[1-16-2]",
|
||||
1.9632220172788948
|
||||
],
|
||||
[
|
||||
"tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-16-2]",
|
||||
3.883476712042466
|
||||
],
|
||||
[
|
||||
"tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[2-32-0]",
|
||||
3.093082787003368
|
||||
],
|
||||
[
|
||||
"tb/axis_rate_limit/test_axis_rate_limit.py::test_axis_rate_limit[32]",
|
||||
10.130301585188136
|
||||
],
|
||||
[
|
||||
"tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[2-16-2]",
|
||||
4.012627676827833
|
||||
],
|
||||
[
|
||||
"tb/axis_mux/test_axis_mux.py::test_axis_mux[1-32]",
|
||||
2.9808353721164167
|
||||
],
|
||||
[
|
||||
"tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[1-8-0]",
|
||||
5.803714094217867
|
||||
],
|
||||
[
|
||||
"tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-32-1]",
|
||||
3.003260609926656
|
||||
],
|
||||
[
|
||||
"tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[1-32-2]",
|
||||
3.2507454978767782
|
||||
],
|
||||
[
|
||||
"tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[2-16-0]",
|
||||
3.6894755298271775
|
||||
],
|
||||
[
|
||||
"tb/axis_rate_limit/test_axis_rate_limit.py::test_axis_rate_limit[16]",
|
||||
8.963756704237312
|
||||
],
|
||||
[
|
||||
"tb/axis_rate_limit/test_axis_rate_limit.py::test_axis_rate_limit[64]",
|
||||
10.402460905024782
|
||||
],
|
||||
[
|
||||
"tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[0-16-0]",
|
||||
3.8650066470727324
|
||||
],
|
||||
[
|
||||
"tb/axis_pipeline_register/test_axis_pipeline_register.py::test_axis_pipeline_register[2-8-0]",
|
||||
5.027584437979385
|
||||
],
|
||||
[
|
||||
"tb/axis_register/test_axis_register.py::test_axis_register[32-2]",
|
||||
1.9581461090128869
|
||||
],
|
||||
[
|
||||
"tb/axis_srl_fifo/test_axis_srl_fifo.py::test_axis_srl_fifo[32]",
|
||||
3.736734739970416
|
||||
],
|
||||
[
|
||||
"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-1]",
|
||||
2.9044135720469058
|
||||
],
|
||||
[
|
||||
"tb/axis_register/test_axis_register.py::test_axis_register[16-2]",
|
||||
2.8243840648792684
|
||||
],
|
||||
[
|
||||
"tb/axis_register/test_axis_register.py::test_axis_register[32-1]",
|
||||
2.0222588209435344
|
||||
],
|
||||
[
|
||||
"tb/axis_register/test_axis_register.py::test_axis_register[16-0]",
|
||||
2.4645475880242884
|
||||
],
|
||||
[
|
||||
"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-1]",
|
||||
3.5214195190928876
|
||||
],
|
||||
[
|
||||
"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-0]",
|
||||
3.4010211310815066
|
||||
],
|
||||
[
|
||||
"tb/axis_srl_fifo/test_axis_srl_fifo.py::test_axis_srl_fifo[8]",
|
||||
6.477508798940107
|
||||
],
|
||||
[
|
||||
"tb/axis_srl_register/test_axis_srl_register.py::test_axis_srl_register[32]",
|
||||
1.7259511651936918
|
||||
],
|
||||
[
|
||||
"tb/axis_srl_register/test_axis_srl_register.py::test_axis_srl_register[16]",
|
||||
2.1865312785375863
|
||||
],
|
||||
[
|
||||
"tb/axis_srl_register/test_axis_srl_register.py::test_axis_srl_register[8]",
|
||||
3.358409102773294
|
||||
],
|
||||
[
|
||||
"tb/axis_srl_fifo/test_axis_srl_fifo.py::test_axis_srl_fifo[64]",
|
||||
5.105467068264261
|
||||
]
|
||||
]
|
@ -1,15 +0,0 @@
|
||||
language: python
|
||||
python:
|
||||
- "3.6"
|
||||
before_install:
|
||||
- export d=`pwd`
|
||||
- export PYTHON_EXE=`which python`
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -y iverilog
|
||||
- git clone https://github.com/jandecaluwe/myhdl.git
|
||||
- cd $d/myhdl && sudo $PYTHON_EXE setup.py install
|
||||
- cd $d/myhdl/cosimulation/icarus && make && sudo install -m 0755 -D ./myhdl.vpi /usr/lib/x86_64-linux-gnu/ivl/myhdl.vpi
|
||||
- cd $d
|
||||
script:
|
||||
- cd tb && IVERILOG_DUMPER=none py.test
|
||||
|
@ -1,5 +1,7 @@
|
||||
# Verilog AXI Stream Components Readme
|
||||
|
||||
[![Build Status](https://github.com/alexforencich/verilog-axis/workflows/Regression%20Tests/badge.svg?branch=master)](https://github.com/alexforencich/verilog-axis/actions/)
|
||||
|
||||
For more information and updates: http://alexforencich.com/wiki/en/verilog/axis/start
|
||||
|
||||
GitHub repository: https://github.com/alexforencich/verilog-axis
|
||||
@ -7,8 +9,8 @@ GitHub repository: https://github.com/alexforencich/verilog-axis
|
||||
## Introduction
|
||||
|
||||
Collection of AXI Stream bus components. Most components are fully
|
||||
parametrizable in interface widths. Includes full MyHDL testbench with
|
||||
intelligent bus cosimulation endpoints.
|
||||
parametrizable in interface widths. Includes full cocotb testbenches that
|
||||
utilize [cocotbext-axi](https://github.com/alexforencich/cocotbext-axi).
|
||||
|
||||
## Documentation
|
||||
|
||||
@ -294,12 +296,4 @@ bad frame
|
||||
|
||||
## Testing
|
||||
|
||||
Running the included testbenches requires MyHDL and Icarus Verilog. Make sure
|
||||
that myhdl.vpi is installed properly for cosimulation to work correctly. The
|
||||
testbenches can be run with a Python test runner like nose or py.test, or the
|
||||
individual test scripts can be run with python directly.
|
||||
|
||||
### Testbench Files
|
||||
|
||||
tb/axis_ep.py : MyHDL AXI Stream endpoints
|
||||
tb/ll_ep.py : MyHDL LocalLink endpoints
|
||||
Running the included testbenches requires [cocotb](https://github.com/cocotb/cocotb), [cocotbext-axi](https://github.com/alexforencich/cocotbext-axi), and [Icarus Verilog](http://iverilog.icarus.com/). The testbenches can be run with pytest directly (requires [cocotb-test](https://github.com/themperek/cocotb-test)), pytest via tox, or via cocotb makefiles.
|
||||
|
@ -3,12 +3,10 @@
|
||||
Generates an AXI Stream arbitrated mux wrapper with the specified number of ports
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import math
|
||||
from jinja2 import Template
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__.strip())
|
||||
parser.add_argument('-p', '--ports', type=int, default=4, help="number of ports")
|
||||
@ -23,6 +21,7 @@ def main():
|
||||
print(ex)
|
||||
exit(1)
|
||||
|
||||
|
||||
def generate(ports=4, name=None, output=None):
|
||||
n = ports
|
||||
|
||||
@ -32,17 +31,13 @@ def generate(ports=4, name=None, output=None):
|
||||
if output is None:
|
||||
output = name + ".v"
|
||||
|
||||
print("Opening file '{0}'...".format(output))
|
||||
|
||||
output_file = open(output, 'w')
|
||||
|
||||
print("Generating {0} port AXI stream arbitrated mux wrapper {1}...".format(n, name))
|
||||
|
||||
cn = int(math.ceil(math.log(n, 2)))
|
||||
cn = (n-1).bit_length()
|
||||
|
||||
t = Template(u"""/*
|
||||
|
||||
Copyright (c) 2018 Alex Forencich
|
||||
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
|
||||
@ -167,14 +162,18 @@ endmodule
|
||||
|
||||
""")
|
||||
|
||||
output_file.write(t.render(
|
||||
n=n,
|
||||
cn=cn,
|
||||
name=name
|
||||
))
|
||||
print(f"Writing file '{output}'...")
|
||||
|
||||
with open(output, 'w') as f:
|
||||
f.write(t.render(
|
||||
n=n,
|
||||
cn=cn,
|
||||
name=name
|
||||
))
|
||||
f.flush()
|
||||
|
||||
print("Done")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
177
lib/axis/rtl/axis_broadcast_wrap.py
Executable file
177
lib/axis/rtl/axis_broadcast_wrap.py
Executable file
@ -0,0 +1,177 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Generates an AXI Stream broadcast 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, 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):
|
||||
n = ports
|
||||
|
||||
if name is None:
|
||||
name = "axis_broadcast_wrap_{0}".format(n)
|
||||
|
||||
if output is None:
|
||||
output = name + ".v"
|
||||
|
||||
print("Generating {0} port AXI stream broadcast wrapper {1}...".format(n, name))
|
||||
|
||||
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 {{n}} port broadcast (wrapper)
|
||||
*/
|
||||
module {{name}} #
|
||||
(
|
||||
// Width of AXI stream interfaces in bits
|
||||
parameter DATA_WIDTH = 8,
|
||||
// Propagate tkeep signal
|
||||
parameter KEEP_ENABLE = (DATA_WIDTH>8),
|
||||
// tkeep signal width (words per cycle)
|
||||
parameter KEEP_WIDTH = (DATA_WIDTH/8),
|
||||
// Propagate tlast signal
|
||||
parameter LAST_ENABLE = 1,
|
||||
// Propagate tid signal
|
||||
parameter ID_ENABLE = 0,
|
||||
// tid signal width
|
||||
parameter ID_WIDTH = 8,
|
||||
// Propagate tdest signal
|
||||
parameter DEST_ENABLE = 0,
|
||||
// tdest signal width
|
||||
parameter DEST_WIDTH = 8,
|
||||
// Propagate tuser signal
|
||||
parameter USER_ENABLE = 1,
|
||||
// tuser signal width
|
||||
parameter USER_WIDTH = 1
|
||||
)
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* AXI Stream input
|
||||
*/
|
||||
input wire [DATA_WIDTH-1:0] s_axis_tdata,
|
||||
input wire [KEEP_WIDTH-1:0] s_axis_tkeep,
|
||||
input wire s_axis_tvalid,
|
||||
output wire s_axis_tready,
|
||||
input wire s_axis_tlast,
|
||||
input wire [ID_WIDTH-1:0] s_axis_tid,
|
||||
input wire [DEST_WIDTH-1:0] s_axis_tdest,
|
||||
input wire [USER_WIDTH-1:0] s_axis_tuser,
|
||||
|
||||
/*
|
||||
* AXI Stream outputs
|
||||
*/
|
||||
{%- for p in range(n) %}
|
||||
output wire [DATA_WIDTH-1:0] m{{'%02d'%p}}_axis_tdata,
|
||||
output wire [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{% if not loop.last %},{% endif %}
|
||||
{% endfor -%}
|
||||
);
|
||||
|
||||
axis_broadcast #(
|
||||
.M_COUNT({{n}}),
|
||||
.DATA_WIDTH(DATA_WIDTH),
|
||||
.KEEP_ENABLE(KEEP_ENABLE),
|
||||
.KEEP_WIDTH(KEEP_WIDTH),
|
||||
.LAST_ENABLE(LAST_ENABLE),
|
||||
.ID_ENABLE(ID_ENABLE),
|
||||
.ID_WIDTH(ID_WIDTH),
|
||||
.DEST_ENABLE(DEST_ENABLE),
|
||||
.DEST_WIDTH(DEST_WIDTH),
|
||||
.USER_ENABLE(USER_ENABLE),
|
||||
.USER_WIDTH(USER_WIDTH)
|
||||
)
|
||||
axis_broadcast_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
// AXI input
|
||||
.s_axis_tdata(s_axis_tdata),
|
||||
.s_axis_tkeep(s_axis_tkeep),
|
||||
.s_axis_tvalid(s_axis_tvalid),
|
||||
.s_axis_tready(s_axis_tready),
|
||||
.s_axis_tlast(s_axis_tlast),
|
||||
.s_axis_tid(s_axis_tid),
|
||||
.s_axis_tdest(s_axis_tdest),
|
||||
.s_axis_tuser(s_axis_tuser),
|
||||
// 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 %} })
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
""")
|
||||
|
||||
print(f"Writing file '{output}'...")
|
||||
|
||||
with open(output, 'w') as f:
|
||||
f.write(t.render(
|
||||
n=n,
|
||||
cn=cn,
|
||||
name=name
|
||||
))
|
||||
f.flush()
|
||||
|
||||
print("Done")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -98,6 +98,16 @@ wire code_fifo_out_tlast;
|
||||
wire code_fifo_out_tuser;
|
||||
reg code_fifo_out_tready;
|
||||
|
||||
reg [7:0] data_fifo_in_tdata;
|
||||
reg data_fifo_in_tvalid;
|
||||
reg data_fifo_in_tlast;
|
||||
wire data_fifo_in_tready;
|
||||
|
||||
wire [7:0] data_fifo_out_tdata;
|
||||
wire data_fifo_out_tvalid;
|
||||
wire data_fifo_out_tlast;
|
||||
reg data_fifo_out_tready;
|
||||
|
||||
assign s_axis_tready = code_fifo_in_tready && data_fifo_in_tready && s_axis_tready_mask;
|
||||
|
||||
axis_fifo #(
|
||||
@ -138,16 +148,6 @@ code_fifo_inst (
|
||||
.status_good_frame()
|
||||
);
|
||||
|
||||
reg [7:0] data_fifo_in_tdata;
|
||||
reg data_fifo_in_tvalid;
|
||||
reg data_fifo_in_tlast;
|
||||
wire data_fifo_in_tready;
|
||||
|
||||
wire [7:0] data_fifo_out_tdata;
|
||||
wire data_fifo_out_tvalid;
|
||||
wire data_fifo_out_tlast;
|
||||
reg data_fifo_out_tready;
|
||||
|
||||
axis_fifo #(
|
||||
.DEPTH(256),
|
||||
.DATA_WIDTH(8),
|
||||
|
@ -126,7 +126,7 @@ always @(posedge clk) begin
|
||||
end else begin
|
||||
s_axis_tvalid_reg <= s_axis_tvalid;
|
||||
for (i = 0; i < M_COUNT; i = i + 1) begin
|
||||
m_axis_tvalid_reg[i] = s_axis_tvalid_reg[select_reg[i*CL_S_COUNT +: CL_S_COUNT]];
|
||||
m_axis_tvalid_reg[i] <= s_axis_tvalid_reg[select_reg[i*CL_S_COUNT +: CL_S_COUNT]];
|
||||
end
|
||||
select_reg <= select;
|
||||
end
|
||||
|
@ -3,12 +3,10 @@
|
||||
Generates an AXI Stream crosspoint wrapper with the specified number of ports
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import math
|
||||
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")
|
||||
@ -23,6 +21,7 @@ def main():
|
||||
print(ex)
|
||||
exit(1)
|
||||
|
||||
|
||||
def generate(ports=4, name=None, output=None):
|
||||
if type(ports) is int:
|
||||
m = n = ports
|
||||
@ -37,18 +36,14 @@ def generate(ports=4, name=None, output=None):
|
||||
if output is None:
|
||||
output = name + ".v"
|
||||
|
||||
print("Opening file '{0}'...".format(output))
|
||||
|
||||
output_file = open(output, 'w')
|
||||
|
||||
print("Generating {0}x{1} port AXI stream crosspoint wrapper {2}...".format(m, n, name))
|
||||
|
||||
cm = int(math.ceil(math.log(m, 2)))
|
||||
cn = int(math.ceil(math.log(n, 2)))
|
||||
cm = (m-1).bit_length()
|
||||
cn = (n-1).bit_length()
|
||||
|
||||
t = Template(u"""/*
|
||||
|
||||
Copyright (c) 2018 Alex Forencich
|
||||
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
|
||||
@ -177,16 +172,20 @@ endmodule
|
||||
|
||||
""")
|
||||
|
||||
output_file.write(t.render(
|
||||
m=m,
|
||||
n=n,
|
||||
cm=cm,
|
||||
cn=cn,
|
||||
name=name
|
||||
))
|
||||
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()
|
||||
|
||||
|
@ -3,12 +3,10 @@
|
||||
Generates an AXI Stream demux wrapper with the specified number of ports
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import math
|
||||
from jinja2 import Template
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__.strip())
|
||||
parser.add_argument('-p', '--ports', type=int, default=4, help="number of ports")
|
||||
@ -23,6 +21,7 @@ def main():
|
||||
print(ex)
|
||||
exit(1)
|
||||
|
||||
|
||||
def generate(ports=4, name=None, output=None):
|
||||
n = ports
|
||||
|
||||
@ -32,17 +31,13 @@ def generate(ports=4, name=None, output=None):
|
||||
if output is None:
|
||||
output = name + ".v"
|
||||
|
||||
print("Opening file '{0}'...".format(output))
|
||||
|
||||
output_file = open(output, 'w')
|
||||
|
||||
print("Generating {0} port AXI stream demux wrapper {1}...".format(n, name))
|
||||
|
||||
cn = int(math.ceil(math.log(n, 2)))
|
||||
cn = (n-1).bit_length()
|
||||
|
||||
t = Template(u"""/*
|
||||
|
||||
Copyright (c) 2018 Alex Forencich
|
||||
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
|
||||
@ -173,14 +168,18 @@ endmodule
|
||||
|
||||
""")
|
||||
|
||||
output_file.write(t.render(
|
||||
n=n,
|
||||
cn=cn,
|
||||
name=name
|
||||
))
|
||||
print(f"Writing file '{output}'...")
|
||||
|
||||
with open(output, 'w') as f:
|
||||
f.write(t.render(
|
||||
n=n,
|
||||
cn=cn,
|
||||
name=name
|
||||
))
|
||||
f.flush()
|
||||
|
||||
print("Done")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
@ -3,12 +3,10 @@
|
||||
Generates an AXI Stream frame joiner wrapper with the specified number of ports
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import math
|
||||
from jinja2 import Template
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__.strip())
|
||||
parser.add_argument('-p', '--ports', type=int, default=4, help="number of ports")
|
||||
@ -23,6 +21,7 @@ def main():
|
||||
print(ex)
|
||||
exit(1)
|
||||
|
||||
|
||||
def generate(ports=4, name=None, output=None):
|
||||
n = ports
|
||||
|
||||
@ -32,17 +31,13 @@ def generate(ports=4, name=None, output=None):
|
||||
if output is None:
|
||||
output = name + ".v"
|
||||
|
||||
print("Opening file '{0}'...".format(output))
|
||||
|
||||
output_file = open(output, 'w')
|
||||
|
||||
print("Generating {0} port AXI stream frame joiner wrapper {1}...".format(n, name))
|
||||
|
||||
cn = int(math.ceil(math.log(n, 2)))
|
||||
cn = (n-1).bit_length()
|
||||
|
||||
t = Template(u"""/*
|
||||
|
||||
Copyright (c) 2018 Alex Forencich
|
||||
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
|
||||
@ -145,14 +140,18 @@ endmodule
|
||||
|
||||
""")
|
||||
|
||||
output_file.write(t.render(
|
||||
n=n,
|
||||
cn=cn,
|
||||
name=name
|
||||
))
|
||||
print(f"Writing file '{output}'...")
|
||||
|
||||
with open(output, 'w') as f:
|
||||
f.write(t.render(
|
||||
n=n,
|
||||
cn=cn,
|
||||
name=name
|
||||
))
|
||||
f.flush()
|
||||
|
||||
print("Done")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
@ -3,12 +3,10 @@
|
||||
Generates an AXI Stream mux wrapper with the specified number of ports
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import math
|
||||
from jinja2 import Template
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__.strip())
|
||||
parser.add_argument('-p', '--ports', type=int, default=4, help="number of ports")
|
||||
@ -23,6 +21,7 @@ def main():
|
||||
print(ex)
|
||||
exit(1)
|
||||
|
||||
|
||||
def generate(ports=4, name=None, output=None):
|
||||
n = ports
|
||||
|
||||
@ -32,17 +31,13 @@ def generate(ports=4, name=None, output=None):
|
||||
if output is None:
|
||||
output = name + ".v"
|
||||
|
||||
print("Opening file '{0}'...".format(output))
|
||||
|
||||
output_file = open(output, 'w')
|
||||
|
||||
print("Generating {0} port AXI stream mux wrapper {1}...".format(n, name))
|
||||
|
||||
cn = int(math.ceil(math.log(n, 2)))
|
||||
cn = (n-1).bit_length()
|
||||
|
||||
t = Template(u"""/*
|
||||
|
||||
Copyright (c) 2018 Alex Forencich
|
||||
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
|
||||
@ -170,14 +165,18 @@ endmodule
|
||||
|
||||
""")
|
||||
|
||||
output_file.write(t.render(
|
||||
n=n,
|
||||
cn=cn,
|
||||
name=name
|
||||
))
|
||||
print(f"Writing file '{output}'...")
|
||||
|
||||
with open(output, 'w') as f:
|
||||
f.write(t.render(
|
||||
n=n,
|
||||
cn=cn,
|
||||
name=name
|
||||
))
|
||||
f.flush()
|
||||
|
||||
print("Done")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
@ -3,12 +3,10 @@
|
||||
Generates an AXI Stream switch wrapper with the specified number of ports
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import math
|
||||
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")
|
||||
@ -23,6 +21,7 @@ def main():
|
||||
print(ex)
|
||||
exit(1)
|
||||
|
||||
|
||||
def generate(ports=4, name=None, output=None):
|
||||
if type(ports) is int:
|
||||
m = n = ports
|
||||
@ -37,18 +36,14 @@ def generate(ports=4, name=None, output=None):
|
||||
if output is None:
|
||||
output = name + ".v"
|
||||
|
||||
print("Opening file '{0}'...".format(output))
|
||||
|
||||
output_file = open(output, 'w')
|
||||
|
||||
print("Generating {0}x{1} port AXI stream switch wrapper {2}...".format(m, n, name))
|
||||
|
||||
cm = int(math.ceil(math.log(m, 2)))
|
||||
cn = int(math.ceil(math.log(n, 2)))
|
||||
cm = (m-1).bit_length()
|
||||
cn = (n-1).bit_length()
|
||||
|
||||
t = Template(u"""/*
|
||||
|
||||
Copyright (c) 2018 Alex Forencich
|
||||
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
|
||||
@ -204,16 +199,20 @@ endmodule
|
||||
|
||||
""")
|
||||
|
||||
output_file.write(t.render(
|
||||
m=m,
|
||||
n=n,
|
||||
cm=cm,
|
||||
cn=cn,
|
||||
name=name
|
||||
))
|
||||
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()
|
||||
|
||||
|
53
lib/axis/syn/quartus_pro/axis_async_fifo.sdc
Normal file
53
lib/axis/syn/quartus_pro/axis_async_fifo.sdc
Normal file
@ -0,0 +1,53 @@
|
||||
# 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.
|
||||
|
||||
# AXI stream asynchronous FIFO timing constraints
|
||||
|
||||
proc constrain_axis_async_fifo_inst { inst } {
|
||||
puts "Inserting timing constraints for axis_async_fifo instance $inst"
|
||||
|
||||
# reset synchronization
|
||||
set_false_path -from * -to [get_registers "$inst|s_rst_sync*_reg $inst|m_rst_sync*_reg"]
|
||||
|
||||
if {[get_collection_size [get_registers -nowarn "$inst|s_rst_sync2_reg"]]} {
|
||||
set_data_delay -from [get_registers "$inst|s_rst_sync2_reg"] -to [get_registers "$inst|s_rst_sync3_reg"] -override -get_value_from_clock_period min_clock_period -value_multiplier 0.8
|
||||
}
|
||||
|
||||
if {[get_collection_size [get_registers -nowarn "$inst|m_rst_sync2_reg"]]} {
|
||||
set_data_delay -from [get_registers "$inst|m_rst_sync2_reg"] -to [get_registers "$inst|m_rst_sync3_reg"] -override -get_value_from_clock_period min_clock_period -value_multiplier 0.8
|
||||
}
|
||||
|
||||
# pointer synchronization
|
||||
set_data_delay -from [get_registers "$inst|rd_ptr_reg[*] $inst|rd_ptr_gray_reg[*]"] -to [get_registers "$inst|rd_ptr_gray_sync1_reg[*]"] -override -get_value_from_clock_period dst_clock_period -value_multiplier 0.8
|
||||
set_max_skew -from [get_keepers "$inst|rd_ptr_reg[*] $inst|rd_ptr_gray_reg[*]"] -to [get_keepers "$inst|rd_ptr_gray_sync1_reg[*]"] -get_skew_value_from_clock_period min_clock_period -skew_value_multiplier 0.8
|
||||
set_data_delay -from [get_registers "$inst|wr_ptr_reg[*] $inst|wr_ptr_gray_reg[*] $inst|wr_ptr_sync_gray_reg[*]"] -to [get_registers "$inst|wr_ptr_gray_sync1_reg[*]"] -override -get_value_from_clock_period dst_clock_period -value_multiplier 0.8
|
||||
set_max_skew -from [get_keepers "$inst|wr_ptr_reg[*] $inst|wr_ptr_gray_reg[*] $inst|wr_ptr_sync_gray_reg[*]"] -to [get_keepers "$inst|wr_ptr_gray_sync1_reg[*]"] -get_skew_value_from_clock_period min_clock_period -skew_value_multiplier 0.8
|
||||
|
||||
# frame FIFO pointer update synchronization
|
||||
set_data_delay -from [get_registers "$inst|wr_ptr_update_reg"] -to [get_registers "$inst|wr_ptr_update_sync1_reg"] -override -get_value_from_clock_period dst_clock_period -value_multiplier 0.8
|
||||
set_data_delay -from [get_registers "$inst|wr_ptr_update_sync3_reg"] -to [get_registers "$inst|wr_ptr_update_ack_sync1_reg"] -override -get_value_from_clock_period dst_clock_period -value_multiplier 0.8
|
||||
|
||||
# status synchronization
|
||||
foreach i {overflow bad_frame good_frame} {
|
||||
if {[get_collection_size [get_registers -nowarn "$inst|${i}_sync*_reg"]]} {
|
||||
set_data_delay -from [get_registers "$inst|${i}_sync1_reg"] -to [get_registers "$inst|${i}_sync2_reg"] -override -get_value_from_clock_period dst_clock_period -value_multiplier 0.8
|
||||
}
|
||||
}
|
||||
}
|
28
lib/axis/syn/quartus_pro/sync_reset.sdc
Normal file
28
lib/axis/syn/quartus_pro/sync_reset.sdc
Normal file
@ -0,0 +1,28 @@
|
||||
# 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.
|
||||
|
||||
# reset synchronizer timing constraints
|
||||
|
||||
proc constrain_sync_reset_inst { inst } {
|
||||
puts "Inserting timing constraints for sync_reset instance $inst"
|
||||
|
||||
# reset synchronization
|
||||
set_false_path -from * -to [get_registers "$inst|sync_reg[*]"]
|
||||
}
|
30
lib/axis/tb/Makefile
Normal file
30
lib/axis/tb/Makefile
Normal file
@ -0,0 +1,30 @@
|
||||
# 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.
|
||||
|
||||
TOPTARGETS := all clean
|
||||
|
||||
SUBDIRS := $(wildcard */.)
|
||||
|
||||
$(TOPTARGETS): $(SUBDIRS)
|
||||
$(SUBDIRS):
|
||||
$(MAKE) -C $@ $(MAKECMDGOALS)
|
||||
|
||||
.PHONY: $(TOPTARGETS) $(SUBDIRS)
|
||||
|
102
lib/axis/tb/axis_adapter/Makefile
Normal file
102
lib/axis/tb/axis_adapter/Makefile
Normal file
@ -0,0 +1,102 @@
|
||||
# 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
|
||||
|
||||
DUT = axis_adapter
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
|
||||
# module parameters
|
||||
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_LAST_ENABLE ?= 1
|
||||
export PARAM_ID_ENABLE ?= 1
|
||||
export PARAM_ID_WIDTH ?= 8
|
||||
export PARAM_DEST_ENABLE ?= 1
|
||||
export PARAM_DEST_WIDTH ?= 8
|
||||
export PARAM_USER_ENABLE ?= 1
|
||||
export PARAM_USER_WIDTH ?= 1
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
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_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
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)
|
||||
|
||||
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 += -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_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
COMPILE_ARGS += -GDEST_WIDTH=$(PARAM_DEST_WIDTH)
|
||||
COMPILE_ARGS += -GUSER_ENABLE=$(PARAM_USER_ENABLE)
|
||||
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
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
|
181
lib/axis/tb/axis_adapter/test_axis_adapter.py
Normal file
181
lib/axis/tb/axis_adapter/test_axis_adapter.py
Normal file
@ -0,0 +1,181 @@
|
||||
#!/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 cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
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, "s_axis"), dut.clk, dut.rst)
|
||||
self.sink = AxiStreamSink(AxiStreamBus.from_prefix(dut, "m_axis"), dut.clk, dut.rst)
|
||||
|
||||
def set_idle_generator(self, generator=None):
|
||||
if generator:
|
||||
self.source.set_pause_generator(generator())
|
||||
|
||||
def set_backpressure_generator(self, generator=None):
|
||||
if generator:
|
||||
self.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):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
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 test_data in [payload_data(x) for x in payload_lengths()]:
|
||||
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")))
|
||||
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:
|
||||
|
||||
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.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, 16, 32])
|
||||
@pytest.mark.parametrize("s_data_width", [8, 16, 32])
|
||||
def test_axis_register(request, s_data_width, m_data_width):
|
||||
dut = "axis_adapter"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
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'] = 8
|
||||
parameters['DEST_ENABLE'] = 1
|
||||
parameters['DEST_WIDTH'] = 8
|
||||
parameters['USER_ENABLE'] = 1
|
||||
parameters['USER_WIDTH'] = 1
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir, "sim_build",
|
||||
request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
102
lib/axis/tb/axis_arb_mux/Makefile
Normal file
102
lib/axis/tb/axis_arb_mux/Makefile
Normal file
@ -0,0 +1,102 @@
|
||||
# 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_PORTS ?= 4
|
||||
|
||||
DUT = axis_arb_mux
|
||||
WRAPPER = $(DUT)_wrap_$(PARAM_PORTS)
|
||||
TOPLEVEL = $(WRAPPER)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += $(WRAPPER).v
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).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 ?= 8
|
||||
export PARAM_DEST_ENABLE ?= 1
|
||||
export PARAM_DEST_WIDTH ?= 8
|
||||
export PARAM_USER_ENABLE ?= 1
|
||||
export PARAM_USER_WIDTH ?= 1
|
||||
|
||||
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_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
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)
|
||||
|
||||
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_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
COMPILE_ARGS += -GDEST_WIDTH=$(PARAM_DEST_WIDTH)
|
||||
COMPILE_ARGS += -GUSER_ENABLE=$(PARAM_USER_ENABLE)
|
||||
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
$(WRAPPER).v: ../../rtl/$(DUT)_wrap.py
|
||||
$< -p $(PARAM_PORTS)
|
||||
|
||||
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
|
199
lib/axis/tb/axis_arb_mux/test_axis_arb_mux.py
Normal file
199
lib/axis/tb/axis_arb_mux/test_axis_arb_mux.py
Normal file
@ -0,0 +1,199 @@
|
||||
#!/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 subprocess
|
||||
|
||||
import cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
ports = int(os.getenv("PARAM_PORTS"))
|
||||
|
||||
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(ports)]
|
||||
self.sink = AxiStreamSink(AxiStreamBus.from_prefix(dut, "m_axis"), dut.clk, dut.rst)
|
||||
|
||||
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:
|
||||
self.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, port=0):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
id_count = 2**len(tb.source[port].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 = cur_id
|
||||
|
||||
test_frames.append(test_frame)
|
||||
await tb.source[port].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 = int(os.getenv("PARAM_DATA_WIDTH"))
|
||||
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:
|
||||
|
||||
ports = int(os.getenv("PARAM_PORTS"))
|
||||
|
||||
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("port", list(range(ports)))
|
||||
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("ports", [1, 4])
|
||||
def test_axis_arb_mux(request, ports, data_width):
|
||||
dut = "axis_arb_mux"
|
||||
wrapper = f"{dut}_wrap_{ports}"
|
||||
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"{ports}"],
|
||||
cwd=tests_dir
|
||||
).wait()
|
||||
|
||||
verilog_sources = [
|
||||
wrapper_file,
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, "arbiter.v"),
|
||||
os.path.join(rtl_dir, "priority_encoder.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['PORTS'] = ports
|
||||
|
||||
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'] = 8
|
||||
parameters['DEST_ENABLE'] = 1
|
||||
parameters['DEST_WIDTH'] = 8
|
||||
parameters['USER_ENABLE'] = 1
|
||||
parameters['USER_WIDTH'] = 1
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir, "sim_build",
|
||||
request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
116
lib/axis/tb/axis_async_fifo/Makefile
Normal file
116
lib/axis/tb/axis_async_fifo/Makefile
Normal file
@ -0,0 +1,116 @@
|
||||
# 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
|
||||
|
||||
DUT = axis_async_fifo
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
|
||||
# module parameters
|
||||
export PARAM_DEPTH ?= 1024
|
||||
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_LAST_ENABLE ?= 1
|
||||
export PARAM_ID_ENABLE ?= 1
|
||||
export PARAM_ID_WIDTH ?= 8
|
||||
export PARAM_DEST_ENABLE ?= 1
|
||||
export PARAM_DEST_WIDTH ?= 8
|
||||
export PARAM_USER_ENABLE ?= 1
|
||||
export PARAM_USER_WIDTH ?= 1
|
||||
export PARAM_PIPELINE_OUTPUT ?= 2
|
||||
export PARAM_FRAME_FIFO ?= 1
|
||||
export PARAM_USER_BAD_FRAME_VALUE ?= 1
|
||||
export PARAM_USER_BAD_FRAME_MASK ?= 1
|
||||
export PARAM_DROP_BAD_FRAME ?= $(PARAM_FRAME_FIFO)
|
||||
export PARAM_DROP_WHEN_FULL ?= 0
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DEPTH=$(PARAM_DEPTH)
|
||||
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).LAST_ENABLE=$(PARAM_LAST_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ID_ENABLE=$(PARAM_ID_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ID_WIDTH=$(PARAM_ID_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DEST_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
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).PIPELINE_OUTPUT=$(PARAM_PIPELINE_OUTPUT)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).FRAME_FIFO=$(PARAM_FRAME_FIFO)
|
||||
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)
|
||||
|
||||
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 += -GDEPTH=$(PARAM_DEPTH)
|
||||
COMPILE_ARGS += -GDATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -GKEEP_ENABLE=$(PARAM_KEEP_ENABLE)
|
||||
COMPILE_ARGS += -GKEEP_WIDTH=$(PARAM_KEEP_WIDTH)
|
||||
COMPILE_ARGS += -GLAST_ENABLE=$(PARAM_LAST_ENABLE)
|
||||
COMPILE_ARGS += -GID_ENABLE=$(PARAM_ID_ENABLE)
|
||||
COMPILE_ARGS += -GID_WIDTH=$(PARAM_ID_WIDTH)
|
||||
COMPILE_ARGS += -GDEST_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
COMPILE_ARGS += -GDEST_WIDTH=$(PARAM_DEST_WIDTH)
|
||||
COMPILE_ARGS += -GUSER_ENABLE=$(PARAM_USER_ENABLE)
|
||||
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
COMPILE_ARGS += -GPIPELINE_OUTPUT=$(PARAM_PIPELINE_OUTPUT)
|
||||
COMPILE_ARGS += -GFRAME_FIFO=$(PARAM_FRAME_FIFO)
|
||||
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)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
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
|
306
lib/axis/tb/axis_async_fifo/test_axis_async_fifo.py
Normal file
306
lib/axis/tb/axis_async_fifo/test_axis_async_fifo.py
Normal file
@ -0,0 +1,306 @@
|
||||
#!/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 cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
cocotb.fork(Clock(dut.s_clk, 10, units="ns").start())
|
||||
cocotb.fork(Clock(dut.m_clk, 11, units="ns").start())
|
||||
|
||||
self.source = AxiStreamSource(AxiStreamBus.from_prefix(dut, "s_axis"), dut.s_clk, dut.async_rst)
|
||||
self.sink = AxiStreamSink(AxiStreamBus.from_prefix(dut, "m_axis"), dut.m_clk, dut.async_rst)
|
||||
|
||||
def set_idle_generator(self, generator=None):
|
||||
if generator:
|
||||
self.source.set_pause_generator(generator())
|
||||
|
||||
def set_backpressure_generator(self, generator=None):
|
||||
if generator:
|
||||
self.sink.set_pause_generator(generator())
|
||||
|
||||
async def reset(self):
|
||||
self.dut.async_rst.setimmediatevalue(0)
|
||||
for k in range(10):
|
||||
await RisingEdge(self.dut.s_clk)
|
||||
self.dut.async_rst <= 1
|
||||
for k in range(10):
|
||||
await RisingEdge(self.dut.s_clk)
|
||||
self.dut.async_rst <= 0
|
||||
for k in range(10):
|
||||
await RisingEdge(self.dut.s_clk)
|
||||
|
||||
|
||||
async def run_test(dut, payload_lengths=None, payload_data=None, idle_inserter=None, backpressure_inserter=None):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
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 test_data in [payload_data(x) for x in payload_lengths()]:
|
||||
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)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
if int(os.getenv("PARAM_FRAME_FIFO")):
|
||||
for k in range(64):
|
||||
await RisingEdge(dut.s_clk)
|
||||
|
||||
else:
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
assert rx_frame.tuser
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.s_clk)
|
||||
await RisingEdge(dut.s_clk)
|
||||
|
||||
|
||||
async def run_test_init_sink_pause(dut):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = True
|
||||
|
||||
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 32))
|
||||
test_frame = AxiStreamFrame(test_data)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for k in range(64):
|
||||
await RisingEdge(dut.s_clk)
|
||||
|
||||
tb.sink.pause = False
|
||||
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
assert not rx_frame.tuser
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.s_clk)
|
||||
await RisingEdge(dut.s_clk)
|
||||
|
||||
|
||||
async def run_test_init_sink_pause_reset(dut):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = True
|
||||
|
||||
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 32))
|
||||
test_frame = AxiStreamFrame(test_data)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for k in range(64):
|
||||
await RisingEdge(dut.s_clk)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = False
|
||||
|
||||
for k in range(64):
|
||||
await RisingEdge(dut.s_clk)
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.s_clk)
|
||||
await RisingEdge(dut.s_clk)
|
||||
|
||||
|
||||
async def run_test_overflow(dut):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = True
|
||||
|
||||
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 2048))
|
||||
test_frame = AxiStreamFrame(test_data)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for k in range(2048):
|
||||
await RisingEdge(dut.s_clk)
|
||||
|
||||
tb.sink.pause = False
|
||||
|
||||
if int(os.getenv("PARAM_FRAME_FIFO")):
|
||||
for k in range(2048):
|
||||
await RisingEdge(dut.s_clk)
|
||||
|
||||
else:
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
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])
|
||||
|
||||
|
||||
def size_list():
|
||||
data_width = len(cocotb.top.m_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:
|
||||
|
||||
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.generate_tests()
|
||||
|
||||
for test in [run_test_tuser_assert, run_test_init_sink_pause, run_test_init_sink_pause_reset, run_test_overflow]:
|
||||
factory = TestFactory(test)
|
||||
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("frame_fifo", [0, 1])
|
||||
@pytest.mark.parametrize("data_width", [8, 16, 32, 64])
|
||||
def test_axis_async_fifo(request, data_width, frame_fifo):
|
||||
dut = "axis_async_fifo"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['DEPTH'] = 1024
|
||||
parameters['DATA_WIDTH'] = data_width
|
||||
parameters['KEEP_ENABLE'] = int(parameters['DATA_WIDTH'] > 8)
|
||||
parameters['KEEP_WIDTH'] = parameters['DATA_WIDTH'] // 8
|
||||
parameters['LAST_ENABLE'] = 1
|
||||
parameters['ID_ENABLE'] = 1
|
||||
parameters['ID_WIDTH'] = 8
|
||||
parameters['DEST_ENABLE'] = 1
|
||||
parameters['DEST_WIDTH'] = 8
|
||||
parameters['USER_ENABLE'] = 1
|
||||
parameters['USER_WIDTH'] = 1
|
||||
parameters['PIPELINE_OUTPUT'] = 2
|
||||
parameters['FRAME_FIFO'] = frame_fifo
|
||||
parameters['USER_BAD_FRAME_VALUE'] = 1
|
||||
parameters['USER_BAD_FRAME_MASK'] = 1
|
||||
parameters['DROP_BAD_FRAME'] = frame_fifo
|
||||
parameters['DROP_WHEN_FULL'] = 0
|
||||
|
||||
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,
|
||||
)
|
122
lib/axis/tb/axis_async_fifo_adapter/Makefile
Normal file
122
lib/axis/tb/axis_async_fifo_adapter/Makefile
Normal file
@ -0,0 +1,122 @@
|
||||
# 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
|
||||
|
||||
DUT = axis_async_fifo_adapter
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
VERILOG_SOURCES += ../../rtl/axis_async_fifo.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_adapter.v
|
||||
|
||||
# module parameters
|
||||
export PARAM_DEPTH ?= 1024
|
||||
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_LAST_ENABLE ?= 1
|
||||
export PARAM_ID_ENABLE ?= 1
|
||||
export PARAM_ID_WIDTH ?= 8
|
||||
export PARAM_DEST_ENABLE ?= 1
|
||||
export PARAM_DEST_WIDTH ?= 8
|
||||
export PARAM_USER_ENABLE ?= 1
|
||||
export PARAM_USER_WIDTH ?= 1
|
||||
export PARAM_FRAME_FIFO ?= 1
|
||||
export PARAM_USER_BAD_FRAME_VALUE ?= 1
|
||||
export PARAM_USER_BAD_FRAME_MASK ?= 1
|
||||
export PARAM_DROP_BAD_FRAME ?= $(PARAM_FRAME_FIFO)
|
||||
export PARAM_DROP_WHEN_FULL ?= 0
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DEPTH=$(PARAM_DEPTH)
|
||||
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_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
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).FRAME_FIFO=$(PARAM_FRAME_FIFO)
|
||||
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)
|
||||
|
||||
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 += -GDEPTH=$(PARAM_DEPTH)
|
||||
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_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
COMPILE_ARGS += -GDEST_WIDTH=$(PARAM_DEST_WIDTH)
|
||||
COMPILE_ARGS += -GUSER_ENABLE=$(PARAM_USER_ENABLE)
|
||||
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
COMPILE_ARGS += -GFRAME_FIFO=$(PARAM_FRAME_FIFO)
|
||||
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)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
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
|
@ -0,0 +1,313 @@
|
||||
#!/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 cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
cocotb.fork(Clock(dut.s_clk, 10, units="ns").start())
|
||||
cocotb.fork(Clock(dut.m_clk, 11, units="ns").start())
|
||||
|
||||
self.source = AxiStreamSource(AxiStreamBus.from_prefix(dut, "s_axis"), dut.s_clk, dut.s_rst)
|
||||
self.sink = AxiStreamSink(AxiStreamBus.from_prefix(dut, "m_axis"), dut.m_clk, dut.m_rst)
|
||||
|
||||
def set_idle_generator(self, generator=None):
|
||||
if generator:
|
||||
self.source.set_pause_generator(generator())
|
||||
|
||||
def set_backpressure_generator(self, generator=None):
|
||||
if generator:
|
||||
self.sink.set_pause_generator(generator())
|
||||
|
||||
async def reset(self):
|
||||
self.dut.m_rst.setimmediatevalue(0)
|
||||
self.dut.s_rst.setimmediatevalue(0)
|
||||
for k in range(10):
|
||||
await RisingEdge(self.dut.s_clk)
|
||||
self.dut.m_rst <= 1
|
||||
self.dut.s_rst <= 1
|
||||
for k in range(10):
|
||||
await RisingEdge(self.dut.s_clk)
|
||||
self.dut.m_rst <= 0
|
||||
self.dut.s_rst <= 0
|
||||
for k in range(10):
|
||||
await RisingEdge(self.dut.s_clk)
|
||||
|
||||
|
||||
async def run_test(dut, payload_lengths=None, payload_data=None, idle_inserter=None, backpressure_inserter=None):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
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 test_data in [payload_data(x) for x in payload_lengths()]:
|
||||
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)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
if int(os.getenv("PARAM_FRAME_FIFO")):
|
||||
for k in range(64):
|
||||
await RisingEdge(dut.s_clk)
|
||||
|
||||
else:
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
assert rx_frame.tuser
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.s_clk)
|
||||
await RisingEdge(dut.s_clk)
|
||||
|
||||
|
||||
async def run_test_init_sink_pause(dut):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = True
|
||||
|
||||
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 32))
|
||||
test_frame = AxiStreamFrame(test_data)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for k in range(64):
|
||||
await RisingEdge(dut.s_clk)
|
||||
|
||||
tb.sink.pause = False
|
||||
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
assert not rx_frame.tuser
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.s_clk)
|
||||
await RisingEdge(dut.s_clk)
|
||||
|
||||
|
||||
async def run_test_init_sink_pause_reset(dut):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = True
|
||||
|
||||
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 32))
|
||||
test_frame = AxiStreamFrame(test_data)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for k in range(64):
|
||||
await RisingEdge(dut.s_clk)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = False
|
||||
|
||||
for k in range(64):
|
||||
await RisingEdge(dut.s_clk)
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.s_clk)
|
||||
await RisingEdge(dut.s_clk)
|
||||
|
||||
|
||||
async def run_test_overflow(dut):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = True
|
||||
|
||||
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 2048))
|
||||
test_frame = AxiStreamFrame(test_data)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for k in range(2048):
|
||||
await RisingEdge(dut.s_clk)
|
||||
|
||||
tb.sink.pause = False
|
||||
|
||||
if int(os.getenv("PARAM_FRAME_FIFO")):
|
||||
for k in range(2048):
|
||||
await RisingEdge(dut.s_clk)
|
||||
|
||||
else:
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
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])
|
||||
|
||||
|
||||
def size_list():
|
||||
data_width = max(len(cocotb.top.m_axis_tdata), len(cocotb.top.s_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:
|
||||
|
||||
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.generate_tests()
|
||||
|
||||
for test in [run_test_tuser_assert, run_test_init_sink_pause, run_test_init_sink_pause_reset, run_test_overflow]:
|
||||
factory = TestFactory(test)
|
||||
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("frame_fifo", [0, 1])
|
||||
@pytest.mark.parametrize("m_data_width", [8, 16, 32])
|
||||
@pytest.mark.parametrize("s_data_width", [8, 16, 32])
|
||||
def test_axis_async_fifo_adapter(request, s_data_width, m_data_width, frame_fifo):
|
||||
dut = "axis_async_fifo_adapter"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, "axis_async_fifo.v"),
|
||||
os.path.join(rtl_dir, "axis_adapter.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['DEPTH'] = 1024
|
||||
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'] = 8
|
||||
parameters['DEST_ENABLE'] = 1
|
||||
parameters['DEST_WIDTH'] = 8
|
||||
parameters['USER_ENABLE'] = 1
|
||||
parameters['USER_WIDTH'] = 1
|
||||
parameters['FRAME_FIFO'] = frame_fifo
|
||||
parameters['USER_BAD_FRAME_VALUE'] = 1
|
||||
parameters['USER_BAD_FRAME_MASK'] = 1
|
||||
parameters['DROP_BAD_FRAME'] = frame_fifo
|
||||
parameters['DROP_WHEN_FULL'] = 0
|
||||
|
||||
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,
|
||||
)
|
103
lib/axis/tb/axis_broadcast/Makefile
Normal file
103
lib/axis/tb/axis_broadcast/Makefile
Normal file
@ -0,0 +1,103 @@
|
||||
# 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 PORTS ?= 4
|
||||
|
||||
DUT = axis_broadcast
|
||||
WRAPPER = $(DUT)_wrap_$(PORTS)
|
||||
TOPLEVEL = $(WRAPPER)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += $(WRAPPER).v
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).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_LAST_ENABLE ?= 1
|
||||
export PARAM_ID_ENABLE ?= 1
|
||||
export PARAM_ID_WIDTH ?= 8
|
||||
export PARAM_DEST_ENABLE ?= 1
|
||||
export PARAM_DEST_WIDTH ?= 8
|
||||
export PARAM_USER_ENABLE ?= 1
|
||||
export PARAM_USER_WIDTH ?= 1
|
||||
|
||||
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).LAST_ENABLE=$(PARAM_LAST_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ID_ENABLE=$(PARAM_ID_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ID_WIDTH=$(PARAM_ID_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DEST_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
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)
|
||||
|
||||
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 += -GLAST_ENABLE=$(PARAM_LAST_ENABLE)
|
||||
COMPILE_ARGS += -GID_ENABLE=$(PARAM_ID_ENABLE)
|
||||
COMPILE_ARGS += -GID_WIDTH=$(PARAM_ID_WIDTH)
|
||||
COMPILE_ARGS += -GDEST_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
COMPILE_ARGS += -GDEST_WIDTH=$(PARAM_DEST_WIDTH)
|
||||
COMPILE_ARGS += -GUSER_ENABLE=$(PARAM_USER_ENABLE)
|
||||
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
$(WRAPPER).v: ../../rtl/$(DUT)_wrap.py
|
||||
$< -p $(PORTS)
|
||||
|
||||
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
|
196
lib/axis/tb/axis_broadcast/test_axis_broadcast.py
Normal file
196
lib/axis/tb/axis_broadcast/test_axis_broadcast.py
Normal file
@ -0,0 +1,196 @@
|
||||
#!/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 subprocess
|
||||
|
||||
import cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
ports = int(os.getenv("PORTS"))
|
||||
|
||||
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, "s_axis"), dut.clk, dut.rst)
|
||||
self.sink = [AxiStreamSink(AxiStreamBus.from_prefix(dut, f"m{k:02d}_axis"), dut.clk, dut.rst) for k in range(ports)]
|
||||
|
||||
def set_idle_generator(self, generator=None):
|
||||
if generator:
|
||||
self.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):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
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 test_data in [payload_data(x) for x in payload_lengths()]:
|
||||
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:
|
||||
for sink in tb.sink:
|
||||
rx_frame = await 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
|
||||
|
||||
for sink in tb.sink:
|
||||
assert 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 = len(cocotb.top.s_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:
|
||||
|
||||
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.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("ports", range(1, 4))
|
||||
def test_axis_broadcast(request, ports, data_width):
|
||||
dut = "axis_broadcast"
|
||||
wrapper = f"{dut}_wrap_{ports}"
|
||||
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"{ports}"],
|
||||
cwd=tests_dir
|
||||
).wait()
|
||||
|
||||
verilog_sources = [
|
||||
wrapper_file,
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
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'] = 8
|
||||
parameters['DEST_ENABLE'] = 1
|
||||
parameters['DEST_WIDTH'] = 8
|
||||
parameters['USER_ENABLE'] = 1
|
||||
parameters['USER_WIDTH'] = 1
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
extra_env['PORTS'] = str(ports)
|
||||
|
||||
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,
|
||||
)
|
68
lib/axis/tb/axis_cobs_decode/Makefile
Normal file
68
lib/axis/tb/axis_cobs_decode/Makefile
Normal file
@ -0,0 +1,68 @@
|
||||
# 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
|
||||
|
||||
DUT = axis_cobs_decode
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
|
||||
# module parameters
|
||||
# export PARAM_APPEND_ZERO ?= 0
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
#COMPILE_ARGS += -P $(TOPLEVEL).APPEND_ZERO=$(PARAM_APPEND_ZERO)
|
||||
|
||||
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 += -GAPPEND_ZERO=$(PARAM_APPEND_ZERO)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
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
|
237
lib/axis/tb/axis_cobs_decode/test_axis_cobs_decode.py
Normal file
237
lib/axis/tb/axis_cobs_decode/test_axis_cobs_decode.py
Normal file
@ -0,0 +1,237 @@
|
||||
#!/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 cocotb_test.simulator
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
def cobs_encode(block):
|
||||
block = bytearray(block)
|
||||
enc = bytearray()
|
||||
|
||||
seg = bytearray()
|
||||
code = 1
|
||||
|
||||
new_data = True
|
||||
|
||||
for b in block:
|
||||
if b == 0:
|
||||
enc.append(code)
|
||||
enc.extend(seg)
|
||||
code = 1
|
||||
seg = bytearray()
|
||||
new_data = True
|
||||
else:
|
||||
code += 1
|
||||
seg.append(b)
|
||||
new_data = True
|
||||
if code == 255:
|
||||
enc.append(code)
|
||||
enc.extend(seg)
|
||||
code = 1
|
||||
seg = bytearray()
|
||||
new_data = False
|
||||
|
||||
if new_data:
|
||||
enc.append(code)
|
||||
enc.extend(seg)
|
||||
|
||||
return bytes(enc)
|
||||
|
||||
|
||||
def cobs_decode(block):
|
||||
block = bytearray(block)
|
||||
dec = bytearray()
|
||||
|
||||
code = 0
|
||||
|
||||
i = 0
|
||||
|
||||
if 0 in block:
|
||||
return None
|
||||
|
||||
while i < len(block):
|
||||
code = block[i]
|
||||
i += 1
|
||||
if i+code-1 > len(block):
|
||||
return None
|
||||
dec.extend(block[i:i+code-1])
|
||||
i += code-1
|
||||
if code < 255 and i < len(block):
|
||||
dec.append(0)
|
||||
|
||||
return bytes(dec)
|
||||
|
||||
|
||||
def prbs31(state=0x7fffffff):
|
||||
while True:
|
||||
for i in range(8):
|
||||
if bool(state & 0x08000000) ^ bool(state & 0x40000000):
|
||||
state = ((state & 0x3fffffff) << 1) | 1
|
||||
else:
|
||||
state = (state & 0x3fffffff) << 1
|
||||
yield state & 0xff
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
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, "s_axis"), dut.clk, dut.rst)
|
||||
self.sink = AxiStreamSink(AxiStreamBus.from_prefix(dut, "m_axis"), dut.clk, dut.rst)
|
||||
|
||||
def set_idle_generator(self, generator=None):
|
||||
if generator:
|
||||
self.source.set_pause_generator(generator())
|
||||
|
||||
def set_backpressure_generator(self, generator=None):
|
||||
if generator:
|
||||
self.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):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.set_idle_generator(idle_inserter)
|
||||
tb.set_backpressure_generator(backpressure_inserter)
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
enc = cobs_encode(test_data)
|
||||
test_frame = AxiStreamFrame(enc)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
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():
|
||||
return list(range(1, 33))+list(range(253, 259))+[512]+[1]*64
|
||||
|
||||
|
||||
def zero_payload(length):
|
||||
return bytearray(length)
|
||||
|
||||
|
||||
def incrementing_payload(length):
|
||||
return bytearray(itertools.islice(itertools.cycle(range(256)), length))
|
||||
|
||||
|
||||
def nonzero_incrementing_payload(length):
|
||||
return bytearray(itertools.islice(itertools.cycle(range(1, 256)), length))
|
||||
|
||||
|
||||
def nonzero_incrementing_payload_zero_framed(length):
|
||||
return bytearray([0]+list(itertools.islice(itertools.cycle(range(1, 256)), length))+[0])
|
||||
|
||||
|
||||
def prbs_payload(length):
|
||||
gen = prbs31()
|
||||
return bytearray([next(gen) for x in range(length)])
|
||||
|
||||
|
||||
if cocotb.SIM_NAME:
|
||||
|
||||
factory = TestFactory(run_test)
|
||||
factory.add_option("payload_lengths", [size_list])
|
||||
factory.add_option("payload_data", [zero_payload, nonzero_incrementing_payload, nonzero_incrementing_payload_zero_framed, prbs_payload])
|
||||
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'))
|
||||
|
||||
|
||||
def test_axis_cobs_decode(request):
|
||||
dut = "axis_cobs_decode"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
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,
|
||||
)
|
69
lib/axis/tb/axis_cobs_encode/Makefile
Normal file
69
lib/axis/tb/axis_cobs_encode/Makefile
Normal file
@ -0,0 +1,69 @@
|
||||
# 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
|
||||
|
||||
DUT = axis_cobs_encode
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
VERILOG_SOURCES += ../../rtl/axis_fifo.v
|
||||
|
||||
# module parameters
|
||||
export PARAM_APPEND_ZERO ?= 0
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).APPEND_ZERO=$(PARAM_APPEND_ZERO)
|
||||
|
||||
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 += -GAPPEND_ZERO=$(PARAM_APPEND_ZERO)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
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
|
248
lib/axis/tb/axis_cobs_encode/test_axis_cobs_encode.py
Normal file
248
lib/axis/tb/axis_cobs_encode/test_axis_cobs_encode.py
Normal file
@ -0,0 +1,248 @@
|
||||
#!/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 cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
def cobs_encode(block):
|
||||
block = bytearray(block)
|
||||
enc = bytearray()
|
||||
|
||||
seg = bytearray()
|
||||
code = 1
|
||||
|
||||
new_data = True
|
||||
|
||||
for b in block:
|
||||
if b == 0:
|
||||
enc.append(code)
|
||||
enc.extend(seg)
|
||||
code = 1
|
||||
seg = bytearray()
|
||||
new_data = True
|
||||
else:
|
||||
code += 1
|
||||
seg.append(b)
|
||||
new_data = True
|
||||
if code == 255:
|
||||
enc.append(code)
|
||||
enc.extend(seg)
|
||||
code = 1
|
||||
seg = bytearray()
|
||||
new_data = False
|
||||
|
||||
if new_data:
|
||||
enc.append(code)
|
||||
enc.extend(seg)
|
||||
|
||||
return bytes(enc)
|
||||
|
||||
|
||||
def cobs_decode(block):
|
||||
block = bytearray(block)
|
||||
dec = bytearray()
|
||||
|
||||
code = 0
|
||||
|
||||
i = 0
|
||||
|
||||
if 0 in block:
|
||||
return None
|
||||
|
||||
while i < len(block):
|
||||
code = block[i]
|
||||
i += 1
|
||||
if i+code-1 > len(block):
|
||||
return None
|
||||
dec.extend(block[i:i+code-1])
|
||||
i += code-1
|
||||
if code < 255 and i < len(block):
|
||||
dec.append(0)
|
||||
|
||||
return bytes(dec)
|
||||
|
||||
|
||||
def prbs31(state=0x7fffffff):
|
||||
while True:
|
||||
for i in range(8):
|
||||
if bool(state & 0x08000000) ^ bool(state & 0x40000000):
|
||||
state = ((state & 0x3fffffff) << 1) | 1
|
||||
else:
|
||||
state = (state & 0x3fffffff) << 1
|
||||
yield state & 0xff
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
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, "s_axis"), dut.clk, dut.rst)
|
||||
self.sink = AxiStreamSink(AxiStreamBus.from_prefix(dut, "m_axis"), dut.clk, dut.rst)
|
||||
|
||||
def set_idle_generator(self, generator=None):
|
||||
if generator:
|
||||
self.source.set_pause_generator(generator())
|
||||
|
||||
def set_backpressure_generator(self, generator=None):
|
||||
if generator:
|
||||
self.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):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
append_zero = int(os.getenv("PARAM_APPEND_ZERO"))
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.set_idle_generator(idle_inserter)
|
||||
tb.set_backpressure_generator(backpressure_inserter)
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
test_frame = AxiStreamFrame(test_data)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
if append_zero:
|
||||
assert rx_frame.tdata == cobs_encode(test_data)+b'\x00'
|
||||
assert cobs_decode(rx_frame.tdata[:-1]) == test_data
|
||||
else:
|
||||
assert rx_frame.tdata == cobs_encode(test_data)
|
||||
assert cobs_decode(rx_frame.tdata) == test_data
|
||||
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():
|
||||
return list(range(1, 33))+list(range(253, 259))+[512]+[1]*64
|
||||
|
||||
|
||||
def zero_payload(length):
|
||||
return bytearray(length)
|
||||
|
||||
|
||||
def incrementing_payload(length):
|
||||
return bytearray(itertools.islice(itertools.cycle(range(256)), length))
|
||||
|
||||
|
||||
def nonzero_incrementing_payload(length):
|
||||
return bytearray(itertools.islice(itertools.cycle(range(1, 256)), length))
|
||||
|
||||
|
||||
def nonzero_incrementing_payload_zero_framed(length):
|
||||
return bytearray([0]+list(itertools.islice(itertools.cycle(range(1, 256)), length))+[0])
|
||||
|
||||
|
||||
def prbs_payload(length):
|
||||
gen = prbs31()
|
||||
return bytearray([next(gen) for x in range(length)])
|
||||
|
||||
|
||||
if cocotb.SIM_NAME:
|
||||
|
||||
factory = TestFactory(run_test)
|
||||
factory.add_option("payload_lengths", [size_list])
|
||||
factory.add_option("payload_data", [zero_payload, nonzero_incrementing_payload, nonzero_incrementing_payload_zero_framed, prbs_payload])
|
||||
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("append_zero", [0, 1])
|
||||
def test_axis_cobs_encode(request, append_zero):
|
||||
dut = "axis_cobs_encode"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, "axis_fifo.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['APPEND_ZERO'] = append_zero
|
||||
|
||||
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,
|
||||
)
|
100
lib/axis/tb/axis_demux/Makefile
Normal file
100
lib/axis/tb/axis_demux/Makefile
Normal file
@ -0,0 +1,100 @@
|
||||
# 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 PORTS ?= 4
|
||||
|
||||
DUT = axis_demux
|
||||
WRAPPER = $(DUT)_wrap_$(PORTS)
|
||||
TOPLEVEL = $(WRAPPER)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += $(WRAPPER).v
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).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 ?= 8
|
||||
export PARAM_DEST_ENABLE ?= 1
|
||||
export PARAM_DEST_WIDTH ?= 8
|
||||
export PARAM_USER_ENABLE ?= 1
|
||||
export PARAM_USER_WIDTH ?= 1
|
||||
|
||||
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_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
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)
|
||||
|
||||
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_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
COMPILE_ARGS += -GDEST_WIDTH=$(PARAM_DEST_WIDTH)
|
||||
COMPILE_ARGS += -GUSER_ENABLE=$(PARAM_USER_ENABLE)
|
||||
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
$(WRAPPER).v: ../../rtl/$(DUT)_wrap.py
|
||||
$< -p $(PORTS)
|
||||
|
||||
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
|
205
lib/axis/tb/axis_demux/test_axis_demux.py
Normal file
205
lib/axis/tb/axis_demux/test_axis_demux.py
Normal file
@ -0,0 +1,205 @@
|
||||
#!/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 subprocess
|
||||
|
||||
import cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
ports = int(os.getenv("PORTS"))
|
||||
|
||||
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, "s_axis"), dut.clk, dut.rst)
|
||||
self.sink = [AxiStreamSink(AxiStreamBus.from_prefix(dut, f"m{k:02d}_axis"), dut.clk, dut.rst) for k in range(ports)]
|
||||
|
||||
dut.enable.setimmediatevalue(0)
|
||||
dut.drop.setimmediatevalue(0)
|
||||
dut.select.setimmediatevalue(0)
|
||||
|
||||
def set_idle_generator(self, generator=None):
|
||||
if generator:
|
||||
self.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, port=0):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
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 = []
|
||||
|
||||
dut.enable.setimmediatevalue(1)
|
||||
dut.drop.setimmediatevalue(0)
|
||||
dut.select.setimmediatevalue(port)
|
||||
|
||||
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 = 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[port].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[port].empty()
|
||||
|
||||
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.s_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:
|
||||
|
||||
ports = int(os.getenv("PORTS"))
|
||||
|
||||
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("port", list(range(ports)))
|
||||
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("ports", [4])
|
||||
def test_axis_demux(request, ports, data_width):
|
||||
dut = "axis_demux"
|
||||
wrapper = f"{dut}_wrap_{ports}"
|
||||
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"{ports}"],
|
||||
cwd=tests_dir
|
||||
).wait()
|
||||
|
||||
verilog_sources = [
|
||||
wrapper_file,
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
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'] = 8
|
||||
parameters['DEST_ENABLE'] = 1
|
||||
parameters['DEST_WIDTH'] = 8
|
||||
parameters['USER_ENABLE'] = 1
|
||||
parameters['USER_WIDTH'] = 1
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
extra_env['PORTS'] = str(ports)
|
||||
|
||||
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,
|
||||
)
|
116
lib/axis/tb/axis_fifo/Makefile
Normal file
116
lib/axis/tb/axis_fifo/Makefile
Normal file
@ -0,0 +1,116 @@
|
||||
# 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
|
||||
|
||||
DUT = axis_fifo
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
|
||||
# module parameters
|
||||
export PARAM_DEPTH ?= 1024
|
||||
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_LAST_ENABLE ?= 1
|
||||
export PARAM_ID_ENABLE ?= 1
|
||||
export PARAM_ID_WIDTH ?= 8
|
||||
export PARAM_DEST_ENABLE ?= 1
|
||||
export PARAM_DEST_WIDTH ?= 8
|
||||
export PARAM_USER_ENABLE ?= 1
|
||||
export PARAM_USER_WIDTH ?= 1
|
||||
export PARAM_PIPELINE_OUTPUT ?= 2
|
||||
export PARAM_FRAME_FIFO ?= 1
|
||||
export PARAM_USER_BAD_FRAME_VALUE ?= 1
|
||||
export PARAM_USER_BAD_FRAME_MASK ?= 1
|
||||
export PARAM_DROP_BAD_FRAME ?= $(PARAM_FRAME_FIFO)
|
||||
export PARAM_DROP_WHEN_FULL ?= 0
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DEPTH=$(PARAM_DEPTH)
|
||||
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).LAST_ENABLE=$(PARAM_LAST_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ID_ENABLE=$(PARAM_ID_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ID_WIDTH=$(PARAM_ID_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DEST_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
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).PIPELINE_OUTPUT=$(PARAM_PIPELINE_OUTPUT)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).FRAME_FIFO=$(PARAM_FRAME_FIFO)
|
||||
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)
|
||||
|
||||
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 += -GDEPTH=$(PARAM_DEPTH)
|
||||
COMPILE_ARGS += -GDATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -GKEEP_ENABLE=$(PARAM_KEEP_ENABLE)
|
||||
COMPILE_ARGS += -GKEEP_WIDTH=$(PARAM_KEEP_WIDTH)
|
||||
COMPILE_ARGS += -GLAST_ENABLE=$(PARAM_LAST_ENABLE)
|
||||
COMPILE_ARGS += -GID_ENABLE=$(PARAM_ID_ENABLE)
|
||||
COMPILE_ARGS += -GID_WIDTH=$(PARAM_ID_WIDTH)
|
||||
COMPILE_ARGS += -GDEST_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
COMPILE_ARGS += -GDEST_WIDTH=$(PARAM_DEST_WIDTH)
|
||||
COMPILE_ARGS += -GUSER_ENABLE=$(PARAM_USER_ENABLE)
|
||||
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
COMPILE_ARGS += -GPIPELINE_OUTPUT=$(PARAM_PIPELINE_OUTPUT)
|
||||
COMPILE_ARGS += -GFRAME_FIFO=$(PARAM_FRAME_FIFO)
|
||||
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)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
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
|
305
lib/axis/tb/axis_fifo/test_axis_fifo.py
Normal file
305
lib/axis/tb/axis_fifo/test_axis_fifo.py
Normal file
@ -0,0 +1,305 @@
|
||||
#!/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 cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
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, "s_axis"), dut.clk, dut.rst)
|
||||
self.sink = AxiStreamSink(AxiStreamBus.from_prefix(dut, "m_axis"), dut.clk, dut.rst)
|
||||
|
||||
def set_idle_generator(self, generator=None):
|
||||
if generator:
|
||||
self.source.set_pause_generator(generator())
|
||||
|
||||
def set_backpressure_generator(self, generator=None):
|
||||
if generator:
|
||||
self.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):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
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 test_data in [payload_data(x) for x in payload_lengths()]:
|
||||
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)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
if int(os.getenv("PARAM_FRAME_FIFO")):
|
||||
for k in range(64):
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
else:
|
||||
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_test_init_sink_pause(dut):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = True
|
||||
|
||||
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 32))
|
||||
test_frame = AxiStreamFrame(test_data)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for k in range(64):
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
tb.sink.pause = False
|
||||
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
assert not rx_frame.tuser
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
async def run_test_init_sink_pause_reset(dut):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = True
|
||||
|
||||
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 32))
|
||||
test_frame = AxiStreamFrame(test_data)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for k in range(64):
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = False
|
||||
|
||||
for k in range(64):
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
async def run_test_overflow(dut):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = True
|
||||
|
||||
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 2048))
|
||||
test_frame = AxiStreamFrame(test_data)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for k in range(2048):
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
tb.sink.pause = False
|
||||
|
||||
if int(os.getenv("PARAM_FRAME_FIFO")):
|
||||
for k in range(2048):
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
else:
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
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 = len(cocotb.top.m_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:
|
||||
|
||||
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.generate_tests()
|
||||
|
||||
for test in [run_test_tuser_assert, run_test_init_sink_pause, run_test_init_sink_pause_reset, run_test_overflow]:
|
||||
factory = TestFactory(test)
|
||||
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("frame_fifo", [0, 1])
|
||||
@pytest.mark.parametrize("data_width", [8, 16, 32, 64])
|
||||
def test_axis_fifo(request, data_width, frame_fifo):
|
||||
dut = "axis_fifo"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['DEPTH'] = 1024
|
||||
parameters['DATA_WIDTH'] = data_width
|
||||
parameters['KEEP_ENABLE'] = int(parameters['DATA_WIDTH'] > 8)
|
||||
parameters['KEEP_WIDTH'] = parameters['DATA_WIDTH'] // 8
|
||||
parameters['LAST_ENABLE'] = 1
|
||||
parameters['ID_ENABLE'] = 1
|
||||
parameters['ID_WIDTH'] = 8
|
||||
parameters['DEST_ENABLE'] = 1
|
||||
parameters['DEST_WIDTH'] = 8
|
||||
parameters['USER_ENABLE'] = 1
|
||||
parameters['USER_WIDTH'] = 1
|
||||
parameters['PIPELINE_OUTPUT'] = 2
|
||||
parameters['FRAME_FIFO'] = frame_fifo
|
||||
parameters['USER_BAD_FRAME_VALUE'] = 1
|
||||
parameters['USER_BAD_FRAME_MASK'] = 1
|
||||
parameters['DROP_BAD_FRAME'] = frame_fifo
|
||||
parameters['DROP_WHEN_FULL'] = 0
|
||||
|
||||
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,
|
||||
)
|
122
lib/axis/tb/axis_fifo_adapter/Makefile
Normal file
122
lib/axis/tb/axis_fifo_adapter/Makefile
Normal file
@ -0,0 +1,122 @@
|
||||
# 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
|
||||
|
||||
DUT = axis_fifo_adapter
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
VERILOG_SOURCES += ../../rtl/axis_fifo.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_adapter.v
|
||||
|
||||
# module parameters
|
||||
export PARAM_DEPTH ?= 1024
|
||||
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_LAST_ENABLE ?= 1
|
||||
export PARAM_ID_ENABLE ?= 1
|
||||
export PARAM_ID_WIDTH ?= 8
|
||||
export PARAM_DEST_ENABLE ?= 1
|
||||
export PARAM_DEST_WIDTH ?= 8
|
||||
export PARAM_USER_ENABLE ?= 1
|
||||
export PARAM_USER_WIDTH ?= 1
|
||||
export PARAM_FRAME_FIFO ?= 1
|
||||
export PARAM_USER_BAD_FRAME_VALUE ?= 1
|
||||
export PARAM_USER_BAD_FRAME_MASK ?= 1
|
||||
export PARAM_DROP_BAD_FRAME ?= $(PARAM_FRAME_FIFO)
|
||||
export PARAM_DROP_WHEN_FULL ?= 0
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DEPTH=$(PARAM_DEPTH)
|
||||
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_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
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).FRAME_FIFO=$(PARAM_FRAME_FIFO)
|
||||
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)
|
||||
|
||||
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 += -GDEPTH=$(PARAM_DEPTH)
|
||||
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_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
COMPILE_ARGS += -GDEST_WIDTH=$(PARAM_DEST_WIDTH)
|
||||
COMPILE_ARGS += -GUSER_ENABLE=$(PARAM_USER_ENABLE)
|
||||
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
COMPILE_ARGS += -GFRAME_FIFO=$(PARAM_FRAME_FIFO)
|
||||
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)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
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
|
309
lib/axis/tb/axis_fifo_adapter/test_axis_fifo_adapter.py
Normal file
309
lib/axis/tb/axis_fifo_adapter/test_axis_fifo_adapter.py
Normal file
@ -0,0 +1,309 @@
|
||||
#!/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 cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
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, "s_axis"), dut.clk, dut.rst)
|
||||
self.sink = AxiStreamSink(AxiStreamBus.from_prefix(dut, "m_axis"), dut.clk, dut.rst)
|
||||
|
||||
def set_idle_generator(self, generator=None):
|
||||
if generator:
|
||||
self.source.set_pause_generator(generator())
|
||||
|
||||
def set_backpressure_generator(self, generator=None):
|
||||
if generator:
|
||||
self.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):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
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 test_data in [payload_data(x) for x in payload_lengths()]:
|
||||
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)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
if int(os.getenv("PARAM_FRAME_FIFO")):
|
||||
for k in range(64):
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
else:
|
||||
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_test_init_sink_pause(dut):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = True
|
||||
|
||||
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 32))
|
||||
test_frame = AxiStreamFrame(test_data)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for k in range(64):
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
tb.sink.pause = False
|
||||
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
assert not rx_frame.tuser
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
async def run_test_init_sink_pause_reset(dut):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = True
|
||||
|
||||
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 32))
|
||||
test_frame = AxiStreamFrame(test_data)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for k in range(64):
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = False
|
||||
|
||||
for k in range(64):
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
async def run_test_overflow(dut):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = True
|
||||
|
||||
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 2048))
|
||||
test_frame = AxiStreamFrame(test_data)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for k in range(2048):
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
tb.sink.pause = False
|
||||
|
||||
if int(os.getenv("PARAM_FRAME_FIFO")):
|
||||
for k in range(2048):
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
else:
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
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(len(cocotb.top.m_axis_tdata), len(cocotb.top.s_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:
|
||||
|
||||
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.generate_tests()
|
||||
|
||||
for test in [run_test_tuser_assert, run_test_init_sink_pause, run_test_init_sink_pause_reset, run_test_overflow]:
|
||||
factory = TestFactory(test)
|
||||
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("frame_fifo", [0, 1])
|
||||
@pytest.mark.parametrize("m_data_width", [8, 16, 32])
|
||||
@pytest.mark.parametrize("s_data_width", [8, 16, 32])
|
||||
def test_axis_fifo_adapter(request, s_data_width, m_data_width, frame_fifo):
|
||||
dut = "axis_fifo_adapter"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, "axis_fifo.v"),
|
||||
os.path.join(rtl_dir, "axis_adapter.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['DEPTH'] = 1024
|
||||
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'] = 8
|
||||
parameters['DEST_ENABLE'] = 1
|
||||
parameters['DEST_WIDTH'] = 8
|
||||
parameters['USER_ENABLE'] = 1
|
||||
parameters['USER_WIDTH'] = 1
|
||||
parameters['FRAME_FIFO'] = frame_fifo
|
||||
parameters['USER_BAD_FRAME_VALUE'] = 1
|
||||
parameters['USER_BAD_FRAME_MASK'] = 1
|
||||
parameters['DROP_BAD_FRAME'] = frame_fifo
|
||||
parameters['DROP_WHEN_FULL'] = 0
|
||||
|
||||
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,
|
||||
)
|
95
lib/axis/tb/axis_frame_length_adjust/Makefile
Normal file
95
lib/axis/tb/axis_frame_length_adjust/Makefile
Normal file
@ -0,0 +1,95 @@
|
||||
# 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
|
||||
|
||||
DUT = axis_frame_length_adjust
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).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 ?= 8
|
||||
export PARAM_DEST_ENABLE ?= 1
|
||||
export PARAM_DEST_WIDTH ?= 8
|
||||
export PARAM_USER_ENABLE ?= 1
|
||||
export PARAM_USER_WIDTH ?= 1
|
||||
export PARAM_LEN_WIDTH ?= 16
|
||||
|
||||
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_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
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).LEN_WIDTH=$(PARAM_LEN_WIDTH)
|
||||
|
||||
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_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
COMPILE_ARGS += -GDEST_WIDTH=$(PARAM_DEST_WIDTH)
|
||||
COMPILE_ARGS += -GUSER_ENABLE=$(PARAM_USER_ENABLE)
|
||||
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
COMPILE_ARGS += -GLEN_WIDTH=$(PARAM_LEN_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
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
|
@ -0,0 +1,239 @@
|
||||
#!/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 cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink
|
||||
from cocotbext.axi.stream import define_stream
|
||||
|
||||
StatusBus, StatusTransaction, StatusSource, StatusSink, StatusMonitor = define_stream("Status",
|
||||
signals=["frame_pad", "frame_truncate", "frame_length", "frame_original_length", "valid"],
|
||||
optional_signals=["ready"]
|
||||
)
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
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, "s_axis"), dut.clk, dut.rst)
|
||||
self.sink = AxiStreamSink(AxiStreamBus.from_prefix(dut, "m_axis"), dut.clk, dut.rst)
|
||||
|
||||
# Status
|
||||
self.status_sink = StatusSink(StatusBus.from_prefix(dut, "status"), dut.clk, dut.rst)
|
||||
|
||||
self.dut.length_min.setimmediatevalue(0)
|
||||
self.dut.length_max.setimmediatevalue(2048)
|
||||
|
||||
def set_idle_generator(self, generator=None):
|
||||
if generator:
|
||||
self.source.set_pause_generator(generator())
|
||||
|
||||
def set_backpressure_generator(self, generator=None):
|
||||
if generator:
|
||||
self.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):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
data_width = len(tb.source.bus.tkeep)
|
||||
byte_width = data_width // 8
|
||||
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)
|
||||
|
||||
for length_max in range(1, byte_width*2+2):
|
||||
for length_min in range(0, length_max+1):
|
||||
tb.log.info("length_min %d, length_max %d", length_min, length_max)
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
tb.dut.length_min <= length_min
|
||||
tb.dut.length_max <= length_max
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
test_frames = []
|
||||
|
||||
for test_data in [payload_data(x) for x in payload_lengths()]:
|
||||
test_frame = AxiStreamFrame(test_data, tid=cur_id, 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()
|
||||
|
||||
len_rx = len(rx_frame.tdata)
|
||||
len_test = len(test_frame.tdata)
|
||||
len_min = min(len_rx, len_test)
|
||||
|
||||
assert len_rx >= length_min
|
||||
assert len_rx <= length_max
|
||||
assert rx_frame.tdata[:len_min] == test_frame.tdata[:len_min]
|
||||
assert rx_frame.tid == test_frame.tid
|
||||
assert rx_frame.tdest == test_frame.tdest
|
||||
assert not rx_frame.tuser
|
||||
|
||||
status = await tb.status_sink.recv()
|
||||
|
||||
tb.log.info("Status: %s", status)
|
||||
|
||||
assert status.frame_pad == int(len_test < length_min)
|
||||
assert status.frame_truncate == int(len_test > length_max)
|
||||
assert status.frame_length == len_rx
|
||||
assert status.frame_original_length == len_test
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
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)
|
||||
|
||||
|
||||
def cycle_pause():
|
||||
return itertools.cycle([1, 1, 1, 0])
|
||||
|
||||
|
||||
def size_list():
|
||||
data_width = len(cocotb.top.m_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:
|
||||
|
||||
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.generate_tests()
|
||||
|
||||
for test in [run_test_tuser_assert]:
|
||||
factory = TestFactory(test)
|
||||
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])
|
||||
def test_axis_frame_length_adjust(request, data_width):
|
||||
dut = "axis_frame_length_adjust"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
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'] = 8
|
||||
parameters['DEST_ENABLE'] = 1
|
||||
parameters['DEST_WIDTH'] = 8
|
||||
parameters['USER_ENABLE'] = 1
|
||||
parameters['USER_WIDTH'] = 1
|
||||
parameters['LEN_WIDTH'] = 16
|
||||
|
||||
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,
|
||||
)
|
103
lib/axis/tb/axis_frame_length_adjust_fifo/Makefile
Normal file
103
lib/axis/tb/axis_frame_length_adjust_fifo/Makefile
Normal file
@ -0,0 +1,103 @@
|
||||
# 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
|
||||
|
||||
DUT = axis_frame_length_adjust_fifo
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
VERILOG_SOURCES += ../../rtl/axis_frame_length_adjust.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_fifo.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 ?= 8
|
||||
export PARAM_DEST_ENABLE ?= 1
|
||||
export PARAM_DEST_WIDTH ?= 8
|
||||
export PARAM_USER_ENABLE ?= 1
|
||||
export PARAM_USER_WIDTH ?= 1
|
||||
export PARAM_LEN_WIDTH ?= 16
|
||||
export PARAM_FRAME_FIFO_DEPTH ?= 1024
|
||||
export PARAM_HEADER_FIFO_DEPTH ?= 8
|
||||
|
||||
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_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
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).LEN_WIDTH=$(PARAM_LEN_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).FRAME_FIFO_DEPTH=$(PARAM_FRAME_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).HEADER_FIFO_DEPTH=$(PARAM_HEADER_FIFO_DEPTH)
|
||||
|
||||
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_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
COMPILE_ARGS += -GDEST_WIDTH=$(PARAM_DEST_WIDTH)
|
||||
COMPILE_ARGS += -GUSER_ENABLE=$(PARAM_USER_ENABLE)
|
||||
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
COMPILE_ARGS += -GLEN_WIDTH=$(PARAM_LEN_WIDTH)
|
||||
COMPILE_ARGS += -GFRAME_FIFO_DEPTH=$(PARAM_FRAME_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -GHEADER_FIFO_DEPTH=$(PARAM_HEADER_FIFO_DEPTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
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
|
@ -0,0 +1,327 @@
|
||||
#!/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 cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink
|
||||
from cocotbext.axi.stream import define_stream
|
||||
|
||||
StatusBus, StatusTransaction, StatusSource, StatusSink, StatusMonitor = define_stream("Status",
|
||||
signals=["frame_pad", "frame_truncate", "frame_length", "frame_original_length", "valid"],
|
||||
optional_signals=["ready"]
|
||||
)
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
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, "s_axis"), dut.clk, dut.rst)
|
||||
self.sink = AxiStreamSink(AxiStreamBus.from_prefix(dut, "m_axis"), dut.clk, dut.rst)
|
||||
|
||||
# Status
|
||||
self.status_sink = StatusSink(StatusBus.from_prefix(dut, "status"), dut.clk, dut.rst)
|
||||
|
||||
self.dut.length_min.setimmediatevalue(0)
|
||||
self.dut.length_max.setimmediatevalue(2048)
|
||||
|
||||
def set_idle_generator(self, generator=None):
|
||||
if generator:
|
||||
self.source.set_pause_generator(generator())
|
||||
|
||||
def set_backpressure_generator(self, generator=None):
|
||||
if generator:
|
||||
self.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):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
data_width = len(tb.source.bus.tkeep)
|
||||
byte_width = data_width // 8
|
||||
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)
|
||||
|
||||
for length_max in range(1, 4):
|
||||
for length_min in range(0, length_max+1):
|
||||
tb.log.info("length_min %d, length_max %d", length_min, length_max)
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
tb.dut.length_min <= length_min
|
||||
tb.dut.length_max <= length_max
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
test_frames = []
|
||||
|
||||
for test_data in [payload_data(x) for x in payload_lengths()]:
|
||||
test_frame = AxiStreamFrame(test_data, tid=cur_id, 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()
|
||||
|
||||
len_rx = len(rx_frame.tdata)
|
||||
len_test = len(test_frame.tdata)
|
||||
len_min = min(len_rx, len_test)
|
||||
|
||||
assert len_rx >= length_min
|
||||
assert len_rx <= length_max
|
||||
assert rx_frame.tdata[:len_min] == test_frame.tdata[:len_min]
|
||||
assert rx_frame.tid == test_frame.tid
|
||||
assert rx_frame.tdest == test_frame.tdest
|
||||
assert not rx_frame.tuser
|
||||
|
||||
status = await tb.status_sink.recv()
|
||||
|
||||
tb.log.info("Status: %s", status)
|
||||
|
||||
assert status.frame_pad == int(len_test < length_min)
|
||||
assert status.frame_truncate == int(len_test > length_max)
|
||||
assert status.frame_length == len_rx
|
||||
assert status.frame_original_length == len_test
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
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_test_init_sink_pause(dut):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = True
|
||||
|
||||
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 32))
|
||||
test_frame = AxiStreamFrame(test_data)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for k in range(64):
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
tb.sink.pause = False
|
||||
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
assert not rx_frame.tuser
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
async def run_test_init_sink_pause_reset(dut):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = True
|
||||
|
||||
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 32))
|
||||
test_frame = AxiStreamFrame(test_data)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for k in range(64):
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = False
|
||||
|
||||
for k in range(64):
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
async def run_test_overflow(dut):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = True
|
||||
|
||||
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 2048))
|
||||
test_frame = AxiStreamFrame(test_data)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for k in range(2048):
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
tb.sink.pause = False
|
||||
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
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 = len(cocotb.top.m_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:
|
||||
|
||||
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.generate_tests()
|
||||
|
||||
for test in [run_test_tuser_assert, run_test_init_sink_pause, run_test_init_sink_pause_reset, run_test_overflow]:
|
||||
factory = TestFactory(test)
|
||||
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])
|
||||
def test_axis_frame_length_adjust_fifo(request, data_width):
|
||||
dut = "axis_frame_length_adjust_fifo"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, f"axis_frame_length_adjust.v"),
|
||||
os.path.join(rtl_dir, f"axis_fifo.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
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'] = 8
|
||||
parameters['DEST_ENABLE'] = 1
|
||||
parameters['DEST_WIDTH'] = 8
|
||||
parameters['USER_ENABLE'] = 1
|
||||
parameters['USER_WIDTH'] = 1
|
||||
parameters['LEN_WIDTH'] = 16
|
||||
parameters['FRAME_FIFO_DEPTH'] = 1024
|
||||
parameters['HEADER_FIFO_DEPTH'] = 8
|
||||
|
||||
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,
|
||||
)
|
100
lib/axis/tb/axis_mux/Makefile
Normal file
100
lib/axis/tb/axis_mux/Makefile
Normal file
@ -0,0 +1,100 @@
|
||||
# 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 PORTS ?= 4
|
||||
|
||||
DUT = axis_mux
|
||||
WRAPPER = $(DUT)_wrap_$(PORTS)
|
||||
TOPLEVEL = $(WRAPPER)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += $(WRAPPER).v
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).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 ?= 8
|
||||
export PARAM_DEST_ENABLE ?= 1
|
||||
export PARAM_DEST_WIDTH ?= 8
|
||||
export PARAM_USER_ENABLE ?= 1
|
||||
export PARAM_USER_WIDTH ?= 1
|
||||
|
||||
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_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
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)
|
||||
|
||||
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_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
COMPILE_ARGS += -GDEST_WIDTH=$(PARAM_DEST_WIDTH)
|
||||
COMPILE_ARGS += -GUSER_ENABLE=$(PARAM_USER_ENABLE)
|
||||
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
$(WRAPPER).v: ../../rtl/$(DUT)_wrap.py
|
||||
$< -p $(PORTS)
|
||||
|
||||
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
|
203
lib/axis/tb/axis_mux/test_axis_mux.py
Normal file
203
lib/axis/tb/axis_mux/test_axis_mux.py
Normal file
@ -0,0 +1,203 @@
|
||||
#!/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 subprocess
|
||||
|
||||
import cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
ports = int(os.getenv("PORTS"))
|
||||
|
||||
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(ports)]
|
||||
self.sink = AxiStreamSink(AxiStreamBus.from_prefix(dut, "m_axis"), dut.clk, dut.rst)
|
||||
|
||||
dut.enable.setimmediatevalue(0)
|
||||
dut.select.setimmediatevalue(0)
|
||||
|
||||
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:
|
||||
self.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, port=0):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
id_count = 2**len(tb.source[port].bus.tid)
|
||||
|
||||
cur_id = 1
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.set_idle_generator(idle_inserter)
|
||||
tb.set_backpressure_generator(backpressure_inserter)
|
||||
|
||||
test_frames = []
|
||||
|
||||
dut.enable.setimmediatevalue(1)
|
||||
dut.select.setimmediatevalue(port)
|
||||
|
||||
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 = cur_id
|
||||
|
||||
test_frames.append(test_frame)
|
||||
await tb.source[port].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 = len(cocotb.top.m_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:
|
||||
|
||||
ports = int(os.getenv("PORTS"))
|
||||
|
||||
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("port", list(range(ports)))
|
||||
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("ports", [1, 4])
|
||||
def test_axis_mux(request, ports, data_width):
|
||||
dut = "axis_mux"
|
||||
wrapper = f"{dut}_wrap_{ports}"
|
||||
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"{ports}"],
|
||||
cwd=tests_dir
|
||||
).wait()
|
||||
|
||||
verilog_sources = [
|
||||
wrapper_file,
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
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'] = 8
|
||||
parameters['DEST_ENABLE'] = 1
|
||||
parameters['DEST_WIDTH'] = 8
|
||||
parameters['USER_ENABLE'] = 1
|
||||
parameters['USER_WIDTH'] = 1
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
extra_env['PORTS'] = str(ports)
|
||||
|
||||
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,
|
||||
)
|
102
lib/axis/tb/axis_pipeline_register/Makefile
Normal file
102
lib/axis/tb/axis_pipeline_register/Makefile
Normal file
@ -0,0 +1,102 @@
|
||||
# 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
|
||||
|
||||
DUT = axis_pipeline_register
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
VERILOG_SOURCES += ../../rtl/axis_register.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_LAST_ENABLE ?= 1
|
||||
export PARAM_ID_ENABLE ?= 1
|
||||
export PARAM_ID_WIDTH ?= 8
|
||||
export PARAM_DEST_ENABLE ?= 1
|
||||
export PARAM_DEST_WIDTH ?= 8
|
||||
export PARAM_USER_ENABLE ?= 1
|
||||
export PARAM_USER_WIDTH ?= 1
|
||||
export PARAM_REG_TYPE ?= 2
|
||||
export PARAM_LENGTH ?= 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).LAST_ENABLE=$(PARAM_LAST_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ID_ENABLE=$(PARAM_ID_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ID_WIDTH=$(PARAM_ID_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DEST_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
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).REG_TYPE=$(PARAM_REG_TYPE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).LENGTH=$(PARAM_LENGTH)
|
||||
|
||||
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 += -GLAST_ENABLE=$(PARAM_LAST_ENABLE)
|
||||
COMPILE_ARGS += -GID_ENABLE=$(PARAM_ID_ENABLE)
|
||||
COMPILE_ARGS += -GID_WIDTH=$(PARAM_ID_WIDTH)
|
||||
COMPILE_ARGS += -GDEST_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
COMPILE_ARGS += -GDEST_WIDTH=$(PARAM_DEST_WIDTH)
|
||||
COMPILE_ARGS += -GUSER_ENABLE=$(PARAM_USER_ENABLE)
|
||||
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
COMPILE_ARGS += -GREG_TYPE=$(PARAM_REG_TYPE)
|
||||
COMPILE_ARGS += -GLENGTH=$(PARAM_LENGTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
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
|
@ -0,0 +1,183 @@
|
||||
#!/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 cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
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, "s_axis"), dut.clk, dut.rst)
|
||||
self.sink = AxiStreamSink(AxiStreamBus.from_prefix(dut, "m_axis"), dut.clk, dut.rst)
|
||||
|
||||
def set_idle_generator(self, generator=None):
|
||||
if generator:
|
||||
self.source.set_pause_generator(generator())
|
||||
|
||||
def set_backpressure_generator(self, generator=None):
|
||||
if generator:
|
||||
self.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):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
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 test_data in [payload_data(x) for x in payload_lengths()]:
|
||||
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 = len(cocotb.top.m_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:
|
||||
|
||||
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.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.dirname(__file__)
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("reg_type", [0, 1, 2])
|
||||
@pytest.mark.parametrize("data_width", [8, 16, 32])
|
||||
@pytest.mark.parametrize("length", [0, 1, 2])
|
||||
def test_axis_pipeline_register(request, length, data_width, reg_type):
|
||||
dut = "axis_pipeline_register"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, "axis_register.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['DATA_WIDTH'] = data_width
|
||||
parameters['KEEP_ENABLE'] = int(parameters['DATA_WIDTH'] > 8)
|
||||
parameters['KEEP_WIDTH'] = parameters['DATA_WIDTH'] // 8
|
||||
parameters['LAST_ENABLE'] = 1
|
||||
parameters['ID_ENABLE'] = 1
|
||||
parameters['ID_WIDTH'] = 8
|
||||
parameters['DEST_ENABLE'] = 1
|
||||
parameters['DEST_WIDTH'] = 8
|
||||
parameters['USER_ENABLE'] = 1
|
||||
parameters['USER_WIDTH'] = 1
|
||||
parameters['REG_TYPE'] = reg_type
|
||||
parameters['LENGTH'] = length
|
||||
|
||||
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,
|
||||
)
|
95
lib/axis/tb/axis_rate_limit/Makefile
Normal file
95
lib/axis/tb/axis_rate_limit/Makefile
Normal file
@ -0,0 +1,95 @@
|
||||
# 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
|
||||
|
||||
DUT = axis_rate_limit
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).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_LAST_ENABLE ?= 1
|
||||
export PARAM_ID_ENABLE ?= 1
|
||||
export PARAM_ID_WIDTH ?= 8
|
||||
export PARAM_DEST_ENABLE ?= 1
|
||||
export PARAM_DEST_WIDTH ?= 8
|
||||
export PARAM_USER_ENABLE ?= 1
|
||||
export PARAM_USER_WIDTH ?= 1
|
||||
|
||||
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).LAST_ENABLE=$(PARAM_LAST_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ID_ENABLE=$(PARAM_ID_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ID_WIDTH=$(PARAM_ID_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DEST_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
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)
|
||||
|
||||
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 += -GLAST_ENABLE=$(PARAM_LAST_ENABLE)
|
||||
COMPILE_ARGS += -GID_ENABLE=$(PARAM_ID_ENABLE)
|
||||
COMPILE_ARGS += -GID_WIDTH=$(PARAM_ID_WIDTH)
|
||||
COMPILE_ARGS += -GDEST_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
COMPILE_ARGS += -GDEST_WIDTH=$(PARAM_DEST_WIDTH)
|
||||
COMPILE_ARGS += -GUSER_ENABLE=$(PARAM_USER_ENABLE)
|
||||
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
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
|
212
lib/axis/tb/axis_rate_limit/test_axis_rate_limit.py
Normal file
212
lib/axis/tb/axis_rate_limit/test_axis_rate_limit.py
Normal file
@ -0,0 +1,212 @@
|
||||
#!/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 cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
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, "s_axis"), dut.clk, dut.rst)
|
||||
self.sink = AxiStreamSink(AxiStreamBus.from_prefix(dut, "m_axis"), dut.clk, dut.rst)
|
||||
|
||||
dut.rate_num.setimmediatevalue(1)
|
||||
dut.rate_denom.setimmediatevalue(1)
|
||||
dut.rate_by_frame.setimmediatevalue(0)
|
||||
|
||||
def set_idle_generator(self, generator=None):
|
||||
if generator:
|
||||
self.source.set_pause_generator(generator())
|
||||
|
||||
def set_backpressure_generator(self, generator=None):
|
||||
if generator:
|
||||
self.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, rate=(1, 1)):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
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)
|
||||
|
||||
dut.rate_num <= rate[0]
|
||||
dut.rate_denom <= rate[1]
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
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 = 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)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def cycle_pause():
|
||||
return itertools.cycle([1, 1, 1, 0])
|
||||
|
||||
|
||||
def size_list():
|
||||
data_width = len(cocotb.top.m_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:
|
||||
|
||||
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("rate", [(1, 1), (1, 2), (1, 10), (2, 3)])
|
||||
factory.generate_tests()
|
||||
|
||||
for test in [run_test_tuser_assert]:
|
||||
factory = TestFactory(test)
|
||||
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, 64])
|
||||
def test_axis_rate_limit(request, data_width):
|
||||
dut = "axis_rate_limit"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['DATA_WIDTH'] = data_width
|
||||
parameters['KEEP_ENABLE'] = int(parameters['DATA_WIDTH'] > 8)
|
||||
parameters['KEEP_WIDTH'] = parameters['DATA_WIDTH'] // 8
|
||||
parameters['LAST_ENABLE'] = 1
|
||||
parameters['ID_ENABLE'] = 1
|
||||
parameters['ID_WIDTH'] = 8
|
||||
parameters['DEST_ENABLE'] = 1
|
||||
parameters['DEST_WIDTH'] = 8
|
||||
parameters['USER_ENABLE'] = 1
|
||||
parameters['USER_WIDTH'] = 1
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir, "sim_build",
|
||||
request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
98
lib/axis/tb/axis_register/Makefile
Normal file
98
lib/axis/tb/axis_register/Makefile
Normal file
@ -0,0 +1,98 @@
|
||||
# Copyright (c) 2020 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
|
||||
|
||||
DUT = axis_register
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).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_LAST_ENABLE ?= 1
|
||||
export PARAM_ID_ENABLE ?= 1
|
||||
export PARAM_ID_WIDTH ?= 8
|
||||
export PARAM_DEST_ENABLE ?= 1
|
||||
export PARAM_DEST_WIDTH ?= 8
|
||||
export PARAM_USER_ENABLE ?= 1
|
||||
export PARAM_USER_WIDTH ?= 1
|
||||
export PARAM_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).LAST_ENABLE=$(PARAM_LAST_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ID_ENABLE=$(PARAM_ID_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ID_WIDTH=$(PARAM_ID_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DEST_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
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).REG_TYPE=$(PARAM_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 += -GLAST_ENABLE=$(PARAM_LAST_ENABLE)
|
||||
COMPILE_ARGS += -GID_ENABLE=$(PARAM_ID_ENABLE)
|
||||
COMPILE_ARGS += -GID_WIDTH=$(PARAM_ID_WIDTH)
|
||||
COMPILE_ARGS += -GDEST_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
COMPILE_ARGS += -GDEST_WIDTH=$(PARAM_DEST_WIDTH)
|
||||
COMPILE_ARGS += -GUSER_ENABLE=$(PARAM_USER_ENABLE)
|
||||
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
COMPILE_ARGS += -GREG_TYPE=$(PARAM_REG_TYPE)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
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
|
180
lib/axis/tb/axis_register/test_axis_register.py
Normal file
180
lib/axis/tb/axis_register/test_axis_register.py
Normal file
@ -0,0 +1,180 @@
|
||||
#!/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 cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
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, "s_axis"), dut.clk, dut.rst)
|
||||
self.sink = AxiStreamSink(AxiStreamBus.from_prefix(dut, "m_axis"), dut.clk, dut.rst)
|
||||
|
||||
def set_idle_generator(self, generator=None):
|
||||
if generator:
|
||||
self.source.set_pause_generator(generator())
|
||||
|
||||
def set_backpressure_generator(self, generator=None):
|
||||
if generator:
|
||||
self.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):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
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 test_data in [payload_data(x) for x in payload_lengths()]:
|
||||
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 = len(cocotb.top.m_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:
|
||||
|
||||
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.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.dirname(__file__)
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("reg_type", [0, 1, 2])
|
||||
@pytest.mark.parametrize("data_width", [8, 16, 32])
|
||||
def test_axis_register(request, data_width, reg_type):
|
||||
dut = "axis_register"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['DATA_WIDTH'] = data_width
|
||||
parameters['KEEP_ENABLE'] = int(parameters['DATA_WIDTH'] > 8)
|
||||
parameters['KEEP_WIDTH'] = parameters['DATA_WIDTH'] // 8
|
||||
parameters['LAST_ENABLE'] = 1
|
||||
parameters['ID_ENABLE'] = 1
|
||||
parameters['ID_WIDTH'] = 8
|
||||
parameters['DEST_ENABLE'] = 1
|
||||
parameters['DEST_WIDTH'] = 8
|
||||
parameters['USER_ENABLE'] = 1
|
||||
parameters['USER_WIDTH'] = 1
|
||||
parameters['REG_TYPE'] = reg_type
|
||||
|
||||
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,
|
||||
)
|
95
lib/axis/tb/axis_srl_fifo/Makefile
Normal file
95
lib/axis/tb/axis_srl_fifo/Makefile
Normal file
@ -0,0 +1,95 @@
|
||||
# Copyright (c) 2020 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
|
||||
|
||||
DUT = axis_srl_fifo
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
|
||||
# module parameters
|
||||
export PARAM_DEPTH ?= 1024
|
||||
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 ?= 8
|
||||
export PARAM_DEST_ENABLE ?= 1
|
||||
export PARAM_DEST_WIDTH ?= 8
|
||||
export PARAM_USER_ENABLE ?= 1
|
||||
export PARAM_USER_WIDTH ?= 1
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DEPTH=$(PARAM_DEPTH)
|
||||
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_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
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)
|
||||
|
||||
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 += -GDEPTH=$(PARAM_DEPTH)
|
||||
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_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
COMPILE_ARGS += -GDEST_WIDTH=$(PARAM_DEST_WIDTH)
|
||||
COMPILE_ARGS += -GUSER_ENABLE=$(PARAM_USER_ENABLE)
|
||||
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
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
|
288
lib/axis/tb/axis_srl_fifo/test_axis_srl_fifo.py
Normal file
288
lib/axis/tb/axis_srl_fifo/test_axis_srl_fifo.py
Normal file
@ -0,0 +1,288 @@
|
||||
#!/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 cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
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, "s_axis"), dut.clk, dut.rst)
|
||||
self.sink = AxiStreamSink(AxiStreamBus.from_prefix(dut, "m_axis"), dut.clk, dut.rst)
|
||||
|
||||
def set_idle_generator(self, generator=None):
|
||||
if generator:
|
||||
self.source.set_pause_generator(generator())
|
||||
|
||||
def set_backpressure_generator(self, generator=None):
|
||||
if generator:
|
||||
self.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):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
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 test_data in [payload_data(x) for x in payload_lengths()]:
|
||||
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)
|
||||
|
||||
|
||||
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_test_init_sink_pause(dut):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = True
|
||||
|
||||
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 32))
|
||||
test_frame = AxiStreamFrame(test_data)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for k in range(64):
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
tb.sink.pause = False
|
||||
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
assert not rx_frame.tuser
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
async def run_test_init_sink_pause_reset(dut):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = True
|
||||
|
||||
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 32))
|
||||
test_frame = AxiStreamFrame(test_data)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for k in range(64):
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = False
|
||||
|
||||
for k in range(64):
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
async def run_test_overflow(dut):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
tb.sink.pause = True
|
||||
|
||||
test_data = bytearray(itertools.islice(itertools.cycle(range(256)), 2048))
|
||||
test_frame = AxiStreamFrame(test_data)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for k in range(2048):
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
tb.sink.pause = False
|
||||
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
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 = len(cocotb.top.m_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:
|
||||
|
||||
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.generate_tests()
|
||||
|
||||
for test in [run_test_tuser_assert, run_test_init_sink_pause, run_test_init_sink_pause_reset, run_test_overflow]:
|
||||
factory = TestFactory(test)
|
||||
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, 64])
|
||||
def test_axis_srl_fifo(request, data_width):
|
||||
dut = "axis_srl_fifo"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['DEPTH'] = 1024
|
||||
parameters['DATA_WIDTH'] = data_width
|
||||
parameters['KEEP_ENABLE'] = int(parameters['DATA_WIDTH'] > 8)
|
||||
parameters['KEEP_WIDTH'] = parameters['DATA_WIDTH'] // 8
|
||||
parameters['LAST_ENABLE'] = 1
|
||||
parameters['ID_ENABLE'] = 1
|
||||
parameters['ID_WIDTH'] = 8
|
||||
parameters['DEST_ENABLE'] = 1
|
||||
parameters['DEST_WIDTH'] = 8
|
||||
parameters['USER_ENABLE'] = 1
|
||||
parameters['USER_WIDTH'] = 1
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir, "sim_build",
|
||||
request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
95
lib/axis/tb/axis_srl_register/Makefile
Normal file
95
lib/axis/tb/axis_srl_register/Makefile
Normal file
@ -0,0 +1,95 @@
|
||||
# Copyright (c) 2020 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
|
||||
|
||||
DUT = axis_srl_register
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).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_LAST_ENABLE ?= 1
|
||||
export PARAM_ID_ENABLE ?= 1
|
||||
export PARAM_ID_WIDTH ?= 8
|
||||
export PARAM_DEST_ENABLE ?= 1
|
||||
export PARAM_DEST_WIDTH ?= 8
|
||||
export PARAM_USER_ENABLE ?= 1
|
||||
export PARAM_USER_WIDTH ?= 1
|
||||
|
||||
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).LAST_ENABLE=$(PARAM_LAST_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ID_ENABLE=$(PARAM_ID_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ID_WIDTH=$(PARAM_ID_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DEST_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
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)
|
||||
|
||||
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 += -GLAST_ENABLE=$(PARAM_LAST_ENABLE)
|
||||
COMPILE_ARGS += -GID_ENABLE=$(PARAM_ID_ENABLE)
|
||||
COMPILE_ARGS += -GID_WIDTH=$(PARAM_ID_WIDTH)
|
||||
COMPILE_ARGS += -GDEST_ENABLE=$(PARAM_DEST_ENABLE)
|
||||
COMPILE_ARGS += -GDEST_WIDTH=$(PARAM_DEST_WIDTH)
|
||||
COMPILE_ARGS += -GUSER_ENABLE=$(PARAM_USER_ENABLE)
|
||||
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
||||
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
|
178
lib/axis/tb/axis_srl_register/test_axis_srl_register.py
Normal file
178
lib/axis/tb/axis_srl_register/test_axis_srl_register.py
Normal file
@ -0,0 +1,178 @@
|
||||
#!/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 cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB(object):
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
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, "s_axis"), dut.clk, dut.rst)
|
||||
self.sink = AxiStreamSink(AxiStreamBus.from_prefix(dut, "m_axis"), dut.clk, dut.rst)
|
||||
|
||||
def set_idle_generator(self, generator=None):
|
||||
if generator:
|
||||
self.source.set_pause_generator(generator())
|
||||
|
||||
def set_backpressure_generator(self, generator=None):
|
||||
if generator:
|
||||
self.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):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
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 test_data in [payload_data(x) for x in payload_lengths()]:
|
||||
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 = len(cocotb.top.m_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:
|
||||
|
||||
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.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])
|
||||
def test_axis_srl_register(request, data_width):
|
||||
dut = "axis_srl_register"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['DATA_WIDTH'] = data_width
|
||||
parameters['KEEP_ENABLE'] = int(parameters['DATA_WIDTH'] > 8)
|
||||
parameters['KEEP_WIDTH'] = parameters['DATA_WIDTH'] // 8
|
||||
parameters['LAST_ENABLE'] = 1
|
||||
parameters['ID_ENABLE'] = 1
|
||||
parameters['ID_WIDTH'] = 8
|
||||
parameters['DEST_ENABLE'] = 1
|
||||
parameters['DEST_WIDTH'] = 8
|
||||
parameters['USER_ENABLE'] = 1
|
||||
parameters['USER_WIDTH'] = 1
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir, "sim_build",
|
||||
request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
29
lib/axis/tox.ini
Normal file
29
lib/axis/tox.ini
Normal file
@ -0,0 +1,29 @@
|
||||
# tox configuration
|
||||
[tox]
|
||||
envlist = py39
|
||||
skipsdist = True
|
||||
|
||||
[gh-actions]
|
||||
python =
|
||||
3.9: py39
|
||||
|
||||
[testenv]
|
||||
deps =
|
||||
pytest
|
||||
pytest-xdist
|
||||
pytest-split
|
||||
cocotb
|
||||
cocotb-test
|
||||
cocotbext-axi
|
||||
jinja2
|
||||
|
||||
commands =
|
||||
pytest -n auto {posargs}
|
||||
|
||||
# pytest configuration
|
||||
[pytest]
|
||||
testpaths =
|
||||
tb
|
||||
addopts =
|
||||
--ignore-glob=tb/test_*.py
|
||||
--import-mode importlib
|
Loading…
x
Reference in New Issue
Block a user