diff --git a/.ci/docker/uhd-builder-ubuntu1804.Dockerfile b/.ci/docker/uhd-builder-ubuntu1804.Dockerfile index 9b86e06f74..5ac62457a0 100644 --- a/.ci/docker/uhd-builder-ubuntu1804.Dockerfile +++ b/.ci/docker/uhd-builder-ubuntu1804.Dockerfile @@ -27,7 +27,6 @@ RUN apt-get update && \ sudo \ # Install UHD dependencies abi-dumper \ - cmake \ doxygen \ dpdk \ libboost-all-dev \ @@ -85,3 +84,10 @@ RUN python3 -m pip install \ click-plugins \ zmq \ scipy + +RUN wget https://cmake.org/files/v3.12/cmake-3.12.4-Linux-x86_64.tar.gz -O /tmp/cmake.tar.gz && \ + (echo "486edd6710b5250946b4b199406ccbf8f567ef0e23cfe38f7938b8c78a2ffa5f /tmp/cmake.tar.gz" | sha256sum --check --status ) && \ + tar -zxvf /tmp/cmake.tar.gz -C /opt && \ + cp -r /opt/cmake-3.12.4-Linux-x86_64/bin/* /usr/local/bin/ && \ + cp -r /opt/cmake-3.12.4-Linux-x86_64/share/* /usr/local/share/ && \ + rm /tmp/cmake.tar.gz diff --git a/.ci/templates/job-analyze-changeset.yml b/.ci/templates/job-analyze-changeset.yml index 20cee51f71..c8877796c2 100644 --- a/.ci/templates/job-analyze-changeset.yml +++ b/.ci/templates/job-analyze-changeset.yml @@ -40,7 +40,7 @@ jobs: $AGENT_TEMPDIRECTORY/ac_venv/$VENV_BIN_DIR/python3 tools/changeset_testlist.py \ --target-branch $TARGET_BRANCH \ --set-azdo-var UhdTestList \ - --list-tests $EXTRA_ARGS + --list-tests --verbose $EXTRA_ARGS name: gen_testlist displayName: Generate Test-List env: diff --git a/.ci/templates/job-uhd-rf-tests-pebbles.yml b/.ci/templates/job-uhd-rf-tests-pebbles.yml index f1e6d12ba5..b52e1aa93c 100644 --- a/.ci/templates/job-uhd-rf-tests-pebbles.yml +++ b/.ci/templates/job-uhd-rf-tests-pebbles.yml @@ -37,8 +37,8 @@ jobs: devType: 'n3xx' devModel: 'n310' devName: pebbles-n310-0 - devSerial: '3176DDF' - devHostname: 'ni-n3xx-3176DDF' + devSerial: '$(n310_devSerial)' + devHostname: 'ni-n3xx-$(n310_devSerial)' devBus: 'ip' devAddr: '192.168.40.17' sfpAddrs: '192.168.10.17,192.168.40.17' diff --git a/.ci/templates/job-uhd-streaming-tests-beauty.yml b/.ci/templates/job-uhd-streaming-tests-beauty.yml index 0aaac1a03e..8c7fd8546d 100644 --- a/.ci/templates/job-uhd-streaming-tests-beauty.yml +++ b/.ci/templates/job-uhd-streaming-tests-beauty.yml @@ -46,55 +46,58 @@ jobs: beauty-X310-0: dutName: 'beauty-X310-0' dutType: 'X310' - dutAddr: '192.168.10.3' - dutSecondAddr: '192.168.20.3' + dutAddr: '$(x310_dutAddr)' + dutSecondAddr: '$(x310_dutSecondAddr)' dutFPGA: 'XG' dutNameId: '' dutNumRecvFrames: '' dutNumSendFrames: '' jtagSerial: '251635138E94' - sfpInt0: 'ens4f0' - sfpInt1: 'ens4f1' + sfpInt0: '$(x310_sfpInt0)' + sfpInt1: '$(x310_sfpInt1)' # beauty-X410-0 X4_200: # dutName: 'beauty-X410-0' # dutFamily: 'x4xx' # dutType: 'x410' - # dutAddr: '192.168.10.2' - # dutSecondAddr: '192.168.20.2' + # dutAddr: '$(x410_dutAddr)' + # dutSecondAddr: '$(x410_dutSecondAddr)' # dutFPGA:'X4_200' # dutNameId: '' + # dutEmbeddedImagesArtifact: 'x4xx-images' + # uartSerial: '$(x410_uartSerial)' # dutNumRecvFrames: '' # dutNumSendFrames: '' - # sfpInt0: 'ens6f0' - # sfpInt1: 'ens6f1' + # sfpInt0: '$(x410_sfpInt0)' + # sfpInt1: '$(x410_sfpInt1)' beauty-X410-0 CG_400: dutName: 'beauty-X410-0' dutFamily: 'x4xx' dutType: 'x410' - dutAddr: '192.168.110.2' - dutSecondAddr: '192.168.120.2' + dutAddr: '$(x410_dutAddr)' + dutSecondAddr: '$(x410_dutSecondAddr)' dutFPGA: 'CG_400' dutNameId: '' dutEmbeddedImagesArtifact: 'x4xx-images' - uartSerial: '2516351E2C9A' + uartSerial: '$(x410_uartSerial)' dutNumRecvFrames: '' dutNumSendFrames: '' - sfpInt0: 'ens6f0' - sfpInt1: 'ens6f1' + sfpInt0: '$(x410_sfpInt0)' + sfpInt1: '$(x410_sfpInt1)' beauty-X410-0 UC_200: dutName: 'beauty-X410-0' dutFamily: 'x4xx' dutType: 'x410' - dutAddr: '192.168.120.2' - dutSecondAddr: '192.168.120.2' + # UC image: device is reachable only via dutSecondAddr + dutAddr: '$(x410_dutSecondAddr)' + dutSecondAddr: '$(x410_dutSecondAddr)' dutFPGA: 'UC_200' dutNameId: '' dutEmbeddedImagesArtifact: 'x4xx-images' - uartSerial: '2516351E2C9A' + uartSerial: '$(x410_uartSerial)' dutNumRecvFrames: '' dutNumSendFrames: '' - sfpInt0: 'ens6f0' - sfpInt1: 'ens6f1' + sfpInt0: '$(x410_sfpInt0)' + sfpInt1: '$(x410_sfpInt1)' # beauty-E320-0: # dutName: 'beauty-E320-0' # dutType: 'E320' diff --git a/.ci/templates/job-uhd-streaming-tests-x440.yml b/.ci/templates/job-uhd-streaming-tests-x440.yml index c76500a9de..a57779ae42 100644 --- a/.ci/templates/job-uhd-streaming-tests-x440.yml +++ b/.ci/templates/job-uhd-streaming-tests-x440.yml @@ -39,27 +39,27 @@ jobs: dutName: 'streaming-X440-0' dutFamily: 'x4xx' dutType: 'x440' - dutAddr: '192.168.110.2' - dutSecondAddr: '192.168.120.2' + dutAddr: '$(x440_dutAddr)' + dutSecondAddr: '$(x440_dutSecondAddr)' dutFPGA: 'CG_400' dutNameId: '' dutEmbeddedImagesArtifact: 'x4xx-images' - uartSerial: '251635271947' + uartSerial: '$(x440_uartSerial)' dutNumRecvFrames: '' dutNumSendFrames: '' - sfpInt0: 'enp1s0f0np0' - sfpInt1: 'enp1s0f1np1' + sfpInt0: '$(x440_sfpInt0)' + sfpInt1: '$(x440_sfpInt1)' X440-0 CG_1600: dutName: 'streaming-X440-0' dutFamily: 'x4xx' dutType: 'x440' - dutAddr: '192.168.110.2' - dutSecondAddr: '192.168.120.2' + dutAddr: '$(x440_dutAddr)' + dutSecondAddr: '$(x440_dutSecondAddr)' dutFPGA: 'CG_1600' dutNameId: '' dutEmbeddedImagesArtifact: 'x4xx-images' - uartSerial: '251635271947' + uartSerial: '$(x440_uartSerial)' dutNumRecvFrames: '' dutNumSendFrames: '' - sfpInt0: 'enp1s0f0np0' - sfpInt1: 'enp1s0f1np1' + sfpInt0: '$(x440_sfpInt0)' + sfpInt1: '$(x440_sfpInt1)' diff --git a/.ci/templates/stages-uhd-pipeline.yml b/.ci/templates/stages-uhd-pipeline.yml index fa444e8236..e5425840b1 100644 --- a/.ci/templates/stages-uhd-pipeline.yml +++ b/.ci/templates/stages-uhd-pipeline.yml @@ -253,21 +253,16 @@ stages: cache_sstate: ${{ parameters.cache_sstate }} cache_downloads: False machines: - ${{ if parameters.build_e310_sg1 }}: - e310_sg1: - machineName: e310_sg1 - ${{ if parameters.build_e310_sg3 }}: - e310_sg3: - machineName: e310_sg3 - ${{ if parameters.build_e320 }}: - e320: - machineName: e320 - ${{ if parameters.build_n3xx }}: - n3xx: - machineName: n3xx - ${{ if parameters.build_x4xx }}: - x4xx: - machineName: x4xx + - ${{ if parameters.build_e310_sg1 }}: + - e310_sg1 + - ${{ if parameters.build_e310_sg3 }}: + - e310_sg3 + - ${{ if parameters.build_e320 }}: + - e320 + - ${{ if parameters.build_n3xx }}: + - n3xx + - ${{ if parameters.build_x4xx }}: + - x4xx auto_conf: $AUTO_CONF run_from_external_repo: true prebuild_steps: @@ -380,7 +375,7 @@ stages: installer: nsis - stage: devtest_uhd_x3xx_b2xx_stage - displayName: devtest UHD x3xx b2xx + displayName: Dev Test UHD x3xx b2xx dependsOn: - build_uhd_stage_linux - analyze_changeset @@ -404,7 +399,7 @@ stages: testDevices: 'x3xx,b2xx' - stage: devtest_uhd_n3xx_e320_stage - displayName: devtest UHD n3xx e320 + displayName: Dev Test UHD n3xx e320 dependsOn: - build_uhd_stage_linux - build_uhd_embedded_system_images @@ -428,8 +423,8 @@ stages: fpga_imgs_source: ${{ parameters.fpga_imgs_source }} testDevices: 'n3xx,e320' -- stage: test_uhd_x4xx_stage - displayName: Test UHD x4xx +- stage: test_uhd_x4xx_sdrtest0_stage + displayName: RF Test UHD x4xx sdr-test0 dependsOn: - build_uhd_stage_linux - build_uhd_embedded_system_images @@ -463,6 +458,32 @@ stages: testOS: ubuntu1804 uhdFpgaArtifactSource: uhd_fpga_pipeline fpga_imgs_source: ${{ parameters.fpga_imgs_source }} + +- stage: test_uhd_x4xx_pebbles_stage + displayName: RF Test UHD x4xx pebbles + dependsOn: + - build_uhd_stage_linux + - build_uhd_embedded_system_images + - build_gnuradio_stage_linux + - analyze_changeset + # This will make $(UhdTestList) available to jobs/steps/tasks, but not for the + # condition. + variables: + UhdTestList: $[stageDependencies.analyze_changeset.analyze.outputs['gen_testlist.UhdTestList']] + condition: > + and( + succeeded(), + or( + ${{ parameters.skip_analyze_changeset }}, + contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.rf.all'), + contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.rf.x4xx'), + contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.rf.x410'), + contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.rf.x440'), + contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.all'), + contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.x410'), + contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.x440') + )) + jobs: - template: tests/job-uhd-x410-hardware-tests-pebbles.yml parameters: testOS: ubuntu1804 @@ -475,6 +496,32 @@ stages: uhdFpgaArtifactSource: uhd_fpga_pipeline fpga_imgs_source: ${{ parameters.fpga_imgs_source }} testLength: ${{ parameters.testLength }} + +- stage: test_uhd_x4xx_saison_stage + displayName: RF Test UHD x4xx saison + dependsOn: + - build_uhd_stage_linux + - build_uhd_embedded_system_images + - build_gnuradio_stage_linux + - analyze_changeset + # This will make $(UhdTestList) available to jobs/steps/tasks, but not for the + # condition. + variables: + UhdTestList: $[stageDependencies.analyze_changeset.analyze.outputs['gen_testlist.UhdTestList']] + condition: > + and( + succeeded(), + or( + ${{ parameters.skip_analyze_changeset }}, + contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.rf.all'), + contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.rf.x4xx'), + contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.rf.x410'), + contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.rf.x440'), + contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.all'), + contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.x410'), + contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.x440') + )) + jobs: - template: tests/job-uhd-x440-hardware-tests-saison.yml parameters: testOS: ubuntu2204 @@ -484,7 +531,7 @@ stages: testLength: ${{ parameters.testLength }} - stage: test_uhd_rf_test_stage - displayName: Run rf tests n3xx + displayName: RF Test UHD n3xx pebbles dependsOn: - analyze_changeset - build_uhd_stage_linux @@ -509,8 +556,8 @@ stages: testOS: ubuntu1804 testDevices: 'n3xx' -- stage: test_streaming_stage - displayName: Test UHD Streaming +- stage: test_streaming_beauty_stage + displayName: Test UHD Streaming beauty dependsOn: - analyze_changeset - build_uhd_stage_linux @@ -533,6 +580,24 @@ stages: uhdFpgaArtifactSource: uhd_fpga_pipeline fpga_imgs_source: ${{ parameters.fpga_imgs_source }} testLength: ${{ parameters.testLength }} + +- stage: test_streaming_x440_stage + displayName: Test UHD Streaming x440 + dependsOn: + - analyze_changeset + - build_uhd_stage_linux + - build_uhd_embedded_system_images + condition: > + and( + succeeded('build_uhd_stage_linux'), + succeeded('build_uhd_embedded_system_images'), + ${{ parameters.run_streaming_tests }}, + or( + ${{ parameters.skip_analyze_changeset }}, + contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.streaming') + ) + ) + jobs: - template: job-uhd-streaming-tests-x440.yml parameters: testOS: ubuntu2204 diff --git a/.ci/templates/tests/job-uhd-x410-hardware-tests-pebbles.yml b/.ci/templates/tests/job-uhd-x410-hardware-tests-pebbles.yml index d9a1d3f55d..c66b18f225 100644 --- a/.ci/templates/tests/job-uhd-x410-hardware-tests-pebbles.yml +++ b/.ci/templates/tests/job-uhd-x410-hardware-tests-pebbles.yml @@ -38,7 +38,7 @@ jobs: devtestPattern: 'x4x0' dutFPGA: 'X4_200' dutEmbeddedImagesArtifact: 'x4xx-images' - uartSerial: '2516351DDCC0' + uartSerial: $(x410_uartSerial) pipelineAgent: pebbles-agent-1 pytestAtsConfig: uhd_oss_ats pytestDUT: 'x410' diff --git a/.ci/templates/tests/job-uhd-x410-hardware-tests-sdr-test0.yml b/.ci/templates/tests/job-uhd-x410-hardware-tests-sdr-test0.yml index af91d82572..757b826aec 100644 --- a/.ci/templates/tests/job-uhd-x410-hardware-tests-sdr-test0.yml +++ b/.ci/templates/tests/job-uhd-x410-hardware-tests-sdr-test0.yml @@ -31,6 +31,6 @@ jobs: devtestPattern: 'x410' dutFPGA: 'X4_200' dutEmbeddedImagesArtifact: 'x4xx-images' - uartSerial: '2516351FE64E' + uartSerial: $(x410_uartSerial) pytestDUT: 'x410' pipelineAgent: sdr-test0 diff --git a/.ci/templates/tests/job-uhd-x440-hardware-tests-pebbles.yml b/.ci/templates/tests/job-uhd-x440-hardware-tests-pebbles.yml index 18c87ef194..2bbd9b5848 100644 --- a/.ci/templates/tests/job-uhd-x440-hardware-tests-pebbles.yml +++ b/.ci/templates/tests/job-uhd-x440-hardware-tests-pebbles.yml @@ -38,7 +38,7 @@ jobs: devtestPattern: 'x4x0' dutFPGA: 'X4_400' dutEmbeddedImagesArtifact: 'x4xx-images' - uartSerial: '251635284FC9' + uartSerial: $(x440_uartSerial) pipelineAgent: pebbles-agent-1 pytestAtsConfig: uhd_oss_ats pytestDUT: 'x440' diff --git a/.ci/templates/tests/job-uhd-x440-hardware-tests-saison.yml b/.ci/templates/tests/job-uhd-x440-hardware-tests-saison.yml index 543b26a71d..824b2c45e2 100644 --- a/.ci/templates/tests/job-uhd-x440-hardware-tests-saison.yml +++ b/.ci/templates/tests/job-uhd-x440-hardware-tests-saison.yml @@ -45,7 +45,7 @@ jobs: devtestPattern: 'x4x0' dutFPGA: 'X4_400' dutEmbeddedImagesArtifact: 'x4xx-images' - uartSerial: '251635284FCA' + uartSerial: $(x440_uartSerial) pipelineAgent: saison-agent-1 pytestAtsConfig: saison_multichan_ats pytestDUT: 'x440' diff --git a/.ci/templates/tests/job-uhd-x440-hardware-tests-sdr-test0.yml b/.ci/templates/tests/job-uhd-x440-hardware-tests-sdr-test0.yml index 8e83c90009..61a22ed569 100644 --- a/.ci/templates/tests/job-uhd-x440-hardware-tests-sdr-test0.yml +++ b/.ci/templates/tests/job-uhd-x440-hardware-tests-sdr-test0.yml @@ -32,6 +32,6 @@ jobs: master_clock_rate: '125e6' dutFPGA: 'X4_400' dutEmbeddedImagesArtifact: 'x4xx-images' - uartSerial: '25163525D2B3' + uartSerial: $(x440_uartSerial) pytestDUT: 'x440' pipelineAgent: sdr-test0 diff --git a/.ci/templates/tests/pebbles-labgrid/exporter-conf/exporter.yaml b/.ci/templates/tests/pebbles-labgrid/exporter-conf/exporter.yaml index 57aae25c51..693f6413bc 100755 --- a/.ci/templates/tests/pebbles-labgrid/exporter-conf/exporter.yaml +++ b/.ci/templates/tests/pebbles-labgrid/exporter-conf/exporter.yaml @@ -2,13 +2,13 @@ pebbles-n310-0-group: console-scu: cls: USBSerialPort match: - ID_SERIAL: 'Silicon_Labs_CP2105_Dual_USB_to_UART_Bridge_Controller_0097D46F' + ID_SERIAL: 'Silicon_Labs_CP2105_Dual_USB_to_UART_Bridge_Controller_00B56895' ID_USB_INTERFACE_NUM: '01' speed: 115200 console-linux: cls: USBSerialPort match: - ID_SERIAL: 'Silicon_Labs_CP2105_Dual_USB_to_UART_Bridge_Controller_0097D46F' + ID_SERIAL: 'Silicon_Labs_CP2105_Dual_USB_to_UART_Bridge_Controller_00B56895' ID_USB_INTERFACE_NUM: '00' speed: 115200 USBSDMuxDevice: diff --git a/.ci/uhd-hardware-test-weekly-pipeline.yml b/.ci/uhd-hardware-test-weekly-pipeline.yml new file mode 100644 index 0000000000..a785b8766b --- /dev/null +++ b/.ci/uhd-hardware-test-weekly-pipeline.yml @@ -0,0 +1,150 @@ +trigger: none +schedules: +- cron: '0 13 * * 6' + displayName: Weekly Test + branches: + include: + - master + always: true + +pr: none + +parameters: +- name: fpga_imgs_source + type: string + values: + - 'Filesytem/Images Downloader' + - 'FPGA Pipeline' + - 'FPGA Pipeline PR' + displayName: FPGA Images Source + default: 'FPGA Pipeline' +- name: testLength + type: string + values: + - 'smoke' + - 'full' + - 'stress' + default: 'smoke' + +variables: + - name: uhd_fpga_artifact_source + ${{ if eq(parameters.fpga_imgs_source, 'FPGA Pipeline PR') }}: + value: uhd_fpga_pr_pipeline + ${{ else }}: + value: uhd_fpga_pipeline + +resources: + pipelines: + - pipeline: uhd_mono_pipeline + source: 'uhddev mono pipeline' + branch: master + - pipeline: usrp-kas-pipeline + source: 'usrp-kas' + branch: kirkstone + - pipeline: uhd_fpga_pipeline + source: 'uhddev fpga pipeline' + branch: master + - pipeline: uhd_fpga_pr_pipeline + source: 'uhddev fpga pipeline PR' + branch: master + repositories: + - repository: meta-ettus + type: github + name: EttusResearch/meta-ettus-dev + endpoint: EttusResearch + ref: kirkstone + - repository: gr-ettus + type: github + name: EttusResearch/gr-ettusdev + endpoint: EttusResearch + ref: maint-3.8-uhd4.0 + - repository: ettus-rts + type: github + endpoint: EttusResearch + name: EttusResearch/ettus-rts + ref: master + +stages: +- stage: test_uhd_devtest_stage + displayName: Test UHD Devtest + dependsOn: [] + jobs: + - template: templates/tests/job-uhd-x410-hardware-tests-sdr-test0.yml + parameters: + testOS: 'ubuntu1804' + uhdArtifactSource: uhd_mono_pipeline + uhdFpgaArtifactSource: ${{ variables.uhd_fpga_artifact_source }} + fpga_imgs_source: ${{ parameters.fpga_imgs_source }} + - template: templates/tests/job-uhd-x440-hardware-tests-sdr-test0.yml + parameters: + testOS: 'ubuntu1804' + uhdArtifactSource: uhd_mono_pipeline + uhdFpgaArtifactSource: ${{ variables.uhd_fpga_artifact_source }} + fpga_imgs_source: ${{ parameters.fpga_imgs_source }} + - template: templates/job-uhd-devtest-rhombus.yml + parameters: + testOS: 'ubuntu2004' + uhdSrcDir: $(Build.SourcesDirectory) + uhdArtifactSource: uhd_mono_pipeline + uhdFpgaArtifactSource: ${{ variables.uhd_fpga_artifact_source }} + fpga_imgs_source: ${{ parameters.fpga_imgs_source }} + +- stage: test_streaming_stage + displayName: Test UHD Streaming + dependsOn: [] + jobs: + - template: templates/job-uhd-streaming-tests-beauty.yml + parameters: + testOS: 'ubuntu2004' + uhdSrcDir: $(Build.SourcesDirectory)/uhddev + uhdArtifactSource: uhd_mono_pipeline + uhdFpgaArtifactSource: ${{ variables.uhd_fpga_artifact_source }} + fpga_imgs_source: ${{ parameters.fpga_imgs_source }} + testLength: ${{ parameters.testLength }} + - template: templates/job-uhd-streaming-tests-x440.yml + parameters: + testOS: 'ubuntu2204' + uhdSrcDir: $(Build.SourcesDirectory)/uhddev + uhdArtifactSource: uhd_mono_pipeline + uhdFpgaArtifactSource: ${{ variables.uhd_fpga_artifact_source }} + fpga_imgs_source: ${{ parameters.fpga_imgs_source }} + testLength: ${{ parameters.testLength }} + +- stage: test_rf_tests_stage + displayName: Test UHD RF Tests + dependsOn: [] + jobs: + - template: templates/job-uhd-rf-tests-pebbles.yml + parameters: + testOS: 'ubuntu1804' + uhdSrcDir: $(Build.SourcesDirectory)/uhddev + uhdArtifactSource: uhd_mono_pipeline + uhdFpgaArtifactSource: ${{ variables.uhd_fpga_artifact_source }} + fpga_imgs_source: ${{ parameters.fpga_imgs_source }} + - template: templates/tests/job-uhd-x410-hardware-tests-pebbles.yml + parameters: + testOS: 'ubuntu1804' + uhdArtifactSource: uhd_mono_pipeline + uhdFpgaArtifactSource: ${{ variables.uhd_fpga_artifact_source }} + fpga_imgs_source: ${{ parameters.fpga_imgs_source }} + testLength: ${{ parameters.testLength }} + - template: templates/tests/job-uhd-x440-hardware-tests-pebbles.yml + parameters: + testOS: 'ubuntu1804' + uhdArtifactSource: uhd_mono_pipeline + uhdFpgaArtifactSource: ${{ variables.uhd_fpga_artifact_source }} + fpga_imgs_source: ${{ parameters.fpga_imgs_source }} + testLength: ${{ parameters.testLength }} + +- stage: test_uhd_phase_tests + displayName: Test UHD Phase Tests + dependsOn: [] + jobs: + - template: templates/tests/job-uhd-x440-hardware-tests-saison.yml + parameters: + testOS: 'ubuntu2204' + uhdArtifactSource: uhd_mono_pipeline + uhdFpgaArtifactSource: ${{ variables.uhd_fpga_artifact_source }} + fpga_imgs_source: ${{ parameters.fpga_imgs_source }} + testLength: ${{ parameters.testLength }} + extra_rf_test_args: --test_selector test_rx_phase_coherence.py test_tx_phase_coherence.py diff --git a/CHANGELOG b/CHANGELOG index 58fb24c131..9e7d785bf2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,69 @@ Change Log for Releases ============================== +## Next Release +* New Features: + - Image Builder: + - Add GRC support. This allows designing RFNoC bitfiles from GNU Radio + Companion (GRC). + - Improve clock connection checks and checks for duplicate connections. + - Improve IO port compat check (e.g., check if wire widths match). + - Allow default clocks in domain checks. + - Simplify image core YAMLs by better usage of RADIO_NIPC parameter. + - Improve parameter resolution. + - Add --SYNTH and --CHECK options. + - Add support for building an FPGA image using multiple parallel jobs and + unique seeds with repeat_fpga_build.py and the --fpga-jobs option. + - rfnoc-gain (OOT RFNoC example): + - Overhaul directory structure, and rename to rfnoc-gain from + rfnoc-example. + - Simplify dynamic loading of OOT DLLs by using the new modules.d feature. + - Add CE clock support to gain block. This enables the example on X4x0. + - Add a GNU Radio subdirectory with examples of how to run the gain block + in GNU Radio. + - Add rfnoc_modtool. This is a command line utility to help design OOT RFNoC + blocks. + - RFNoC: + - Add tune requests. This allows tuning a complete graph as known from + multi_usrp instead of single blocks individually. + - multi_usrp: + - Add Python bindings for get_user_settings_iface() + - General UHD: + - Add modules.d support +* Bug Fixes + - E320: Ensure consistent sequencing when powering on/off GPSDO + - RFNoC: + - RFNoC DDC/DUC block (used in all Gen-3 USRPs and X410): Fix fractional + frequency offset. + - Fix AIS/spp calculation (e.g., for connecting FFT blocks). + - Image Builder: + - Fix colors + - Fix error message for missing control SEP + - Improve error reporting for invalid connections + - Accept ~ and ~user on command line + - Fix deprecated usage of yaml.load() + - Ensure correct device tree files generation when chosing a custom build + directory + - rfnoc_modtool: + - Fix generation of noc_shells + - General UHD: + - Fix compatibility with DPDK >= 22.11 + - Fix compiler warnings for better compatibility with C++ 17 and 20. + - Add logic for loading uhd.dll from the correct path for Windows with Python3.8+. + - Remove duplicate results from find + - Release GIL when calling find from Python which improves response time for large setups. + - MPM: + - Allow images without RF frontend (will only be initialized if FPGA reports availability) +* Validated OS Environments + (Versions for build and runtime dependencies can be determined from the + docker container definitions in the UHD repository at .ci/docker/...) + - Linux: + - Ubuntu: bionic (18.04), focal (20.04), jammy (22.04), noble (24.04) + - Fedora: 39, 40, 41 + - Windows: 10 21H2, 11 21H2 + - MacOS: Monterey (12.6) + + ## 004.007.000.000 * Highlights / Main Changes - Major updates to rfnoc_image_builder (a98ce26). This change adds support for diff --git a/fpga/usrp3/tools/utils/repeat_fpga_build.py b/fpga/usrp3/tools/utils/repeat_fpga_build.py index b38ef35b0a..0003debce5 100755 --- a/fpga/usrp3/tools/utils/repeat_fpga_build.py +++ b/fpga/usrp3/tools/utils/repeat_fpga_build.py @@ -202,6 +202,12 @@ def parse_args(): "location (e.g., /tools/Xilinx/Vivado).", default=None, ) + parser.add_argument( + "--ignore-warnings", + "-W", + help="Run build even when there are warnings from the RFNoC image builder", + action="store_true", + ) parser.add_argument( "--num", "-n", @@ -282,6 +288,7 @@ def rfnoc_image_builder_cmd( image_core_name, fpga_dir, vivado_path, + ignore_warnings, build_num=None, ): """ @@ -300,6 +307,8 @@ def rfnoc_image_builder_cmd( cmd += f"--fpga-dir {fpga_dir} " if vivado_path: cmd += f"--vivado-path {vivado_path} " + if ignore_warnings: + cmd += "--ignore-warnings " return cmd @@ -313,6 +322,7 @@ def run_fpga_build( image_core_name, fpga_dir, vivado_path, + ignore_warnings, parallel_builds, ): """Performs one iteration of an FPGA build. @@ -327,6 +337,8 @@ def run_fpga_build( image_core_name: --image-core-name argument to be passed fpga_dir: --fpga-dir argument to be passed vivado_path: --vivado-path argument to be passed + ignore_warnings: --ignore-warnings argument to be passed + parallel_builds: Indicates if we are doing builds in parallel Returns: Status.SUCCESS: The build succeeded @@ -343,6 +355,7 @@ def run_fpga_build( image_core_name, fpga_dir, vivado_path, + ignore_warnings, cmd_build_num, ) logging.info(f"Running FPGA build command: {cmd}") @@ -388,7 +401,9 @@ def run_fpga_build( return status -def run_ip_build(ip_jobs, target, image_core, image_core_name, fpga_dir, vivado_path): +def run_ip_build( + ip_jobs, target, image_core, image_core_name, fpga_dir, vivado_path, ignore_warnings +): """Performs the IP build. This is done separately so that when we do parallel FPGA builds, they don't @@ -401,13 +416,21 @@ def run_ip_build(ip_jobs, target, image_core, image_core_name, fpga_dir, vivado_ image_core_name: --image-core-name argument to be passed fpga_dir: --fpga-dir argument to be passed vivado_path: --vivado-path argument to be passed + ignore_warnings: --ignore-warnings argument to be passed Returns: 0: The IP build succeeded non-zero: The IP build failed """ cmd = rfnoc_image_builder_cmd( - None, target, image_core, image_core_name, fpga_dir, vivado_path, None + None, + target, + image_core, + image_core_name, + fpga_dir, + vivado_path, + ignore_warnings, + None, ) cmd += f" --ip-only --jobs {ip_jobs}" logging.info(f"Running IP build with command: {cmd}") @@ -475,6 +498,7 @@ def main(): args.image_core_name, args.fpga_dir, args.vivado_path, + args.ignore_warnings, ) if status != Status.SUCCESS: return status.value @@ -502,6 +526,7 @@ def main(): args.image_core_name, args.fpga_dir, args.vivado_path, + args.ignore_warnings, args.fpga_jobs > 1, ), ) diff --git a/fpga/usrp3/top/x400/.gitignore b/fpga/usrp3/top/x400/.gitignore index d96446d270..21b2f49993 100644 --- a/fpga/usrp3/top/x400/.gitignore +++ b/fpga/usrp3/top/x400/.gitignore @@ -4,4 +4,3 @@ build-* *.jou vivado*.str ip/*/sim/ -dts/x4*0-version-info.dtsi diff --git a/fpga/usrp3/top/x400/Makefile b/fpga/usrp3/top/x400/Makefile index 72be08fa51..6e9f6b1a64 100644 --- a/fpga/usrp3/top/x400/Makefile +++ b/fpga/usrp3/top/x400/Makefile @@ -134,24 +134,24 @@ X410_IP: ##Build X410 IP only. Use the -j option to build multiple IP block X440_IP: ##Build X440 IP only. Use the -j option to build multiple IP blocks simultaneously. +$(call vivado_ip,X440,$(DEFS) X440=1) -dts/x410-version-info.dtsi: regmap/x410/versioning_regs_regmap_utils.vh +$(BUILD_DIR)/x410-version-info.dtsi: regmap/x410/versioning_regs_regmap_utils.vh tools/parse_versions_for_dts.py \ --input $< --output $@ \ --components fpga,cpld_ifc,db_gpio_ifc,rf_core_100m,rf_core_400m -dts/x440-version-info.dtsi: regmap/x440/versioning_regs_regmap_utils.vh +$(BUILD_DIR)/x440-version-info.dtsi: regmap/x440/versioning_regs_regmap_utils.vh tools/parse_versions_for_dts.py \ --input $< --output $@ \ --components fpga,cpld_ifc,db_gpio_ifc,rf_core_full # The device tree source file $(BUILD_DIR)/device_tree.dts is generated by the # RFNoC image builder. -$(BUILD_DIR)/X410.dts: $(BUILD_DIR)/device_tree.dts dts/x410-version-info.dtsi dts/*.dtsi +$(BUILD_DIR)/X410.dts: $(BUILD_DIR)/device_tree.dts $(BUILD_DIR)/x410-version-info.dtsi dts/*.dtsi ${CC} -o $@ -C -E -I dts -nostdinc -undef -x assembler-with-cpp -D__DTS__ $< # The device tree source file $(BUILD_DIR)/device_tree.dts is generated by the # RFNoC image builder. -$(BUILD_DIR)/X440.dts: $(BUILD_DIR)/device_tree.dts dts/x440-version-info.dtsi dts/*.dtsi +$(BUILD_DIR)/X440.dts: $(BUILD_DIR)/device_tree.dts $(BUILD_DIR)/x440-version-info.dtsi dts/*.dtsi ${CC} -o $@ -C -E -I dts -nostdinc -undef -x assembler-with-cpp -D__DTS__ $< clean: ##Clean up all target build outputs. diff --git a/fpga/usrp3/top/x400/regmap/radio_ctrlport_regmap_utils.vh b/fpga/usrp3/top/x400/regmap/radio_ctrlport_regmap_utils.vh index 861752b4a3..4633d97fec 100644 --- a/fpga/usrp3/top/x400/regmap/radio_ctrlport_regmap_utils.vh +++ b/fpga/usrp3/top/x400/regmap/radio_ctrlport_regmap_utils.vh @@ -13,6 +13,7 @@ // DB_WINDOW : 0x0 (x4xx_core_common.v) // RFDC_TIMING_WINDOW : 0x8000 (x4xx_core_common.v) + // RF_CORE_WINDOW : 0xA000 (x4xx_core_common.v) // DIO_WINDOW : 0xC000 (x4xx_core_common.v) //=============================================================================== @@ -29,7 +30,11 @@ // RFDC_TIMING_WINDOW Window (from x4xx_core_common.v) localparam RFDC_TIMING_WINDOW = 'h8000; // Window Offset - localparam RFDC_TIMING_WINDOW_SIZE = 'h4000; // size in bytes + localparam RFDC_TIMING_WINDOW_SIZE = 'h2000; // size in bytes + + // RF_CORE_WINDOW Window (from x4xx_core_common.v) + localparam RF_CORE_WINDOW = 'hA000; // Window Offset + localparam RF_CORE_WINDOW_SIZE = 'h2000; // size in bytes // DIO_WINDOW Window (from x4xx_core_common.v) localparam DIO_WINDOW = 'hC000; // Window Offset diff --git a/fpga/usrp3/top/x400/x4xx.sv b/fpga/usrp3/top/x400/x4xx.sv index c73b9e9342..80b6e427f1 100644 --- a/fpga/usrp3/top/x400/x4xx.sv +++ b/fpga/usrp3/top/x400/x4xx.sv @@ -272,6 +272,7 @@ module x4xx ( `include "regmap/global_regs_regmap_utils.vh" `include "regmap/versioning_utils.vh" + `include "../../lib/rfnoc/core/ctrlport.vh" //--------------------------------------------------------------------------- @@ -1922,6 +1923,15 @@ module x4xx ( wire [31:0] db_ctrlport_resp_data [0:NUM_DBOARDS-1]; wire [ 1:0] db_ctrlport_resp_status [0:NUM_DBOARDS-1]; + // RF core ctrlport interface + wire rf_core_ctrlport_req_rd [0:NUM_DBOARDS-1]; + wire rf_core_ctrlport_req_wr [0:NUM_DBOARDS-1]; + wire [19:0] rf_core_ctrlport_req_addr [0:NUM_DBOARDS-1]; + wire [31:0] rf_core_ctrlport_req_data [0:NUM_DBOARDS-1]; + wire rf_core_ctrlport_resp_ack [0:NUM_DBOARDS-1]; + wire [31:0] rf_core_ctrlport_resp_data [0:NUM_DBOARDS-1]; + wire [ 1:0] rf_core_ctrlport_resp_status [0:NUM_DBOARDS-1]; + // GPIO to CPLD ctrlport interface wire db_to_cpld_ctrlport_req_rd [0:NUM_DBOARDS-1]; wire db_to_cpld_ctrlport_req_wr [0:NUM_DBOARDS-1]; @@ -2162,6 +2172,22 @@ module x4xx ( .version_info (rf_core_version[db_i]) ); end // gen_rf_core_full + + // terminate unused rf core ctrlport interfaces + ctrlport_terminator #( + .START_ADDRESS (0), + .LAST_ADDRESS (2**CTRLPORT_ADDR_W-1) + ) pl_terminator ( + .ctrlport_clk (rfdc_clk[db_i]), + .ctrlport_rst (radio_rst[db_i]), + .s_ctrlport_req_wr (rf_core_ctrlport_req_wr[db_i]), + .s_ctrlport_req_rd (rf_core_ctrlport_req_rd[db_i]), + .s_ctrlport_req_addr (rf_core_ctrlport_req_addr[db_i]), + .s_ctrlport_req_data (rf_core_ctrlport_req_data[db_i]), + .s_ctrlport_resp_ack (rf_core_ctrlport_resp_ack[db_i]), + .s_ctrlport_resp_status (rf_core_ctrlport_resp_status[db_i]), + .s_ctrlport_resp_data (rf_core_ctrlport_resp_data[db_i]) + ); end // gen_rf_cores `ifdef X440 @@ -2975,6 +3001,13 @@ module x4xx ( .m_ctrlport_radio_resp_ack ({ db_ctrlport_resp_ack [1], db_ctrlport_resp_ack [0] }), .m_ctrlport_radio_resp_status ({ db_ctrlport_resp_status [1], db_ctrlport_resp_status [0] }), .m_ctrlport_radio_resp_data ({ db_ctrlport_resp_data [1], db_ctrlport_resp_data [0] }), + .m_ctrlport_rf_core_req_wr ({ rf_core_ctrlport_req_wr [1], rf_core_ctrlport_req_wr [0] }), + .m_ctrlport_rf_core_req_rd ({ rf_core_ctrlport_req_rd [1], rf_core_ctrlport_req_rd [0] }), + .m_ctrlport_rf_core_req_addr ({ rf_core_ctrlport_req_addr [1], rf_core_ctrlport_req_addr [0] }), + .m_ctrlport_rf_core_req_data ({ rf_core_ctrlport_req_data [1], rf_core_ctrlport_req_data [0] }), + .m_ctrlport_rf_core_resp_ack ({ rf_core_ctrlport_resp_ack [1], rf_core_ctrlport_resp_ack [0] }), + .m_ctrlport_rf_core_resp_status({ rf_core_ctrlport_resp_status [1], rf_core_ctrlport_resp_status [0] }), + .m_ctrlport_rf_core_resp_data ({ rf_core_ctrlport_resp_data [1], rf_core_ctrlport_resp_data [0] }), .start_nco_reset (start_nco_reset), .nco_reset_done (nco_reset_done), .adc_reset_pulse (adc_reset_pulse), diff --git a/fpga/usrp3/top/x400/x4xx_core.v b/fpga/usrp3/top/x400/x4xx_core.v index 638dca1af2..4294c63d31 100644 --- a/fpga/usrp3/top/x400/x4xx_core.v +++ b/fpga/usrp3/top/x400/x4xx_core.v @@ -326,6 +326,14 @@ module x4xx_core #( input wire [ 2*NUM_DBOARDS-1:0] m_ctrlport_radio_resp_status, input wire [ 32*NUM_DBOARDS-1:0] m_ctrlport_radio_resp_data, + output wire [ 1*NUM_DBOARDS-1:0] m_ctrlport_rf_core_req_wr, + output wire [ 1*NUM_DBOARDS-1:0] m_ctrlport_rf_core_req_rd, + output wire [ 20*NUM_DBOARDS-1:0] m_ctrlport_rf_core_req_addr, + output wire [ 32*NUM_DBOARDS-1:0] m_ctrlport_rf_core_req_data, + input wire [ 1*NUM_DBOARDS-1:0] m_ctrlport_rf_core_resp_ack, + input wire [ 2*NUM_DBOARDS-1:0] m_ctrlport_rf_core_resp_status, + input wire [ 32*NUM_DBOARDS-1:0] m_ctrlport_rf_core_resp_data, + // RF Reset Control output wire start_nco_reset, input wire nco_reset_done, @@ -501,6 +509,13 @@ module x4xx_core #( .m_radio_ctrlport_resp_ack (m_ctrlport_radio_resp_ack), .m_radio_ctrlport_resp_status (m_ctrlport_radio_resp_status), .m_radio_ctrlport_resp_data (m_ctrlport_radio_resp_data), + .m_rf_core_ctrlport_req_wr (m_ctrlport_rf_core_req_wr), + .m_rf_core_ctrlport_req_rd (m_ctrlport_rf_core_req_rd), + .m_rf_core_ctrlport_req_addr (m_ctrlport_rf_core_req_addr), + .m_rf_core_ctrlport_req_data (m_ctrlport_rf_core_req_data), + .m_rf_core_ctrlport_resp_ack (m_ctrlport_rf_core_resp_ack), + .m_rf_core_ctrlport_resp_status (m_ctrlport_rf_core_resp_status), + .m_rf_core_ctrlport_resp_data (m_ctrlport_rf_core_resp_data), .start_nco_reset (start_nco_reset), .nco_reset_done (nco_reset_done), .adc_reset_pulse (adc_reset_pulse), diff --git a/fpga/usrp3/top/x400/x4xx_core_common.v b/fpga/usrp3/top/x400/x4xx_core_common.v index ca82abd07a..c816941f37 100644 --- a/fpga/usrp3/top/x400/x4xx_core_common.v +++ b/fpga/usrp3/top/x400/x4xx_core_common.v @@ -133,6 +133,15 @@ module x4xx_core_common #( input wire [ 2*NUM_DBOARDS-1:0] m_radio_ctrlport_resp_status, input wire [ 32*NUM_DBOARDS-1:0] m_radio_ctrlport_resp_data, + // CtrlPort Master (to RF cores, Domain: radio_clk) + output wire [ 1*NUM_DBOARDS-1:0] m_rf_core_ctrlport_req_wr, + output wire [ 1*NUM_DBOARDS-1:0] m_rf_core_ctrlport_req_rd, + output wire [ 20*NUM_DBOARDS-1:0] m_rf_core_ctrlport_req_addr, + output wire [ 32*NUM_DBOARDS-1:0] m_rf_core_ctrlport_req_data, + input wire [ 1*NUM_DBOARDS-1:0] m_rf_core_ctrlport_resp_ack, + input wire [ 2*NUM_DBOARDS-1:0] m_rf_core_ctrlport_resp_status, + input wire [ 32*NUM_DBOARDS-1:0] m_rf_core_ctrlport_resp_data, + // RF Reset Control output wire start_nco_reset, input wire nco_reset_done, @@ -477,6 +486,7 @@ module x4xx_core_common #( localparam [19:0] DIGITAL_IFC_OFFSET = DIO_WINDOW + DIGITAL_IFC_REGS; // Register space size calculation + localparam [31:0] RF_CORE_WINDOW_SIZE_W = $clog2(RF_CORE_WINDOW_SIZE); localparam [31:0] RFDC_TIMING_WINDOW_SIZE_W = $clog2(RFDC_TIMING_WINDOW_SIZE); localparam [31:0] DB_WINDOW_SIZE_W = $clog2(DB_WINDOW_SIZE); localparam [31:0] DIO_SOURCE_CONTROL_SIZE_W = $clog2(DIO_SOURCE_CONTROL_SIZE); @@ -484,16 +494,18 @@ module x4xx_core_common #( localparam [31:0] DIGITAL_IFC_REGS_SIZE_W = $clog2(DIGITAL_IFC_REGS_SIZE); ctrlport_decoder_param #( - .NUM_SLAVES (5), + .NUM_SLAVES (6), .PORT_BASE ({ DIGITAL_IFC_OFFSET, DIO_SOURCE_CONTROL_OFFSET, RADIO_GPIO_ATR_OFFSET, + RF_CORE_WINDOW[19:0], RFDC_TIMING_WINDOW[19:0], DB_WINDOW[19:0] }), .PORT_ADDR_W ({ DIGITAL_IFC_REGS_SIZE_W, DIO_SOURCE_CONTROL_SIZE_W, RADIO_GPIO_ATR_SIZE_W, + RF_CORE_WINDOW_SIZE_W, RFDC_TIMING_WINDOW_SIZE_W, DB_WINDOW_SIZE_W }) @@ -513,21 +525,25 @@ module x4xx_core_common #( .m_ctrlport_req_wr ({ gpio_spi_ctrlport_req_wr [ 1*db_i+: 1], radio_dio_req_wr [ 1*db_i+: 1], gpio_atr_ctrlport_req_wr [ 1*db_i+: 1], + m_rf_core_ctrlport_req_wr [ 1*db_i+: 1], rf_ctrlport_req_wr [ 1*db_i+: 1], m_radio_ctrlport_req_wr [ 1*db_i+: 1] }), .m_ctrlport_req_rd ({ gpio_spi_ctrlport_req_rd [ 1*db_i+: 1], radio_dio_req_rd [ 1*db_i+: 1], gpio_atr_ctrlport_req_rd [ 1*db_i+: 1], + m_rf_core_ctrlport_req_rd [ 1*db_i+: 1], rf_ctrlport_req_rd [ 1*db_i+: 1], m_radio_ctrlport_req_rd [ 1*db_i+: 1] }), .m_ctrlport_req_addr ({ gpio_spi_ctrlport_req_addr [20*db_i+:20], radio_dio_req_addr [20*db_i+:20], gpio_atr_ctrlport_req_addr [20*db_i+:20], + m_rf_core_ctrlport_req_addr [20*db_i+:20], rf_ctrlport_req_addr [20*db_i+:20], m_radio_ctrlport_req_addr [20*db_i+:20] }), .m_ctrlport_req_data ({ gpio_spi_ctrlport_req_data [32*db_i+:32], radio_dio_req_data [32*db_i+:32], gpio_atr_ctrlport_req_data [32*db_i+:32], + m_rf_core_ctrlport_req_data [32*db_i+:32], rf_ctrlport_req_data [32*db_i+:32], m_radio_ctrlport_req_data [32*db_i+:32] }), .m_ctrlport_req_byte_en (), @@ -536,16 +552,19 @@ module x4xx_core_common #( .m_ctrlport_resp_ack ({ gpio_spi_ctrlport_resp_ack [ 1*db_i+: 1], radio_dio_resp_ack [ 1*db_i+: 1], gpio_atr_ctrlport_resp_ack [ 1*db_i+: 1], + m_rf_core_ctrlport_resp_ack [ 1*db_i+: 1], rf_ctrlport_resp_ack [ 1*db_i+: 1], m_radio_ctrlport_resp_ack [ 1*db_i+: 1] }), .m_ctrlport_resp_status ({ gpio_spi_ctrlport_resp_status [ 2*db_i+: 2], radio_dio_resp_status [ 2*db_i+: 2], gpio_atr_ctrlport_resp_status [ 2*db_i+: 2], + m_rf_core_ctrlport_resp_status [ 2*db_i+: 2], rf_ctrlport_resp_status [ 2*db_i+: 2], m_radio_ctrlport_resp_status [ 2*db_i+: 2] }), .m_ctrlport_resp_data ({ gpio_spi_ctrlport_resp_data [32*db_i+:32], radio_dio_resp_data [32*db_i+:32], gpio_atr_ctrlport_resp_data [32*db_i+:32], + m_rf_core_ctrlport_resp_data [32*db_i+:32], rf_ctrlport_resp_data [32*db_i+:32], m_radio_ctrlport_resp_data [32*db_i+:32] }) ); @@ -891,9 +910,12 @@ endmodule // Daughterboard GPIO interface. Register access within this space // is directed to the associated daughterboard CPLD. // -// +// // RFDC timing control interface. // +// +// Interface for the RF core. +// // // DIO control interface. Interacts with the DIO source selection // block, ATR-based DIO control and the DIO digital interface diff --git a/host/docs/install.dox b/host/docs/install.dox index 6ec796daae..16be561865 100644 --- a/host/docs/install.dox +++ b/host/docs/install.dox @@ -40,7 +40,7 @@ and compiler version used for the UHD binaries. This detail is indicated in the name of the installer executable; for example, uhd_4.7.0.0-release_Win64_VS2017.exe indicates it was built for the 64-bit Windows platform using the Visual Studio 2017 compiler suite. -Copy all files from the path \VS20XX\MSYY\dll within the archive to the bin folder +Copy all files from the path \\VS20XX\\MSYY\\dll within the archive to the bin folder of the UHD installation location (by default "C:\Program Files\UHD\bin"). VS20XX is the Visual Studio version that you find in the name of the UHD installer. YY is the bitness of your Windows version (32 or 64). diff --git a/host/docs/sphinx/environment.yml b/host/docs/sphinx/environment.yml index e4eb11154f..9e78686275 100644 --- a/host/docs/sphinx/environment.yml +++ b/host/docs/sphinx/environment.yml @@ -3,6 +3,7 @@ channels: - conda-forge - defaults dependencies: + - python=3.12 - pip - cmake - compilers diff --git a/host/docs/sphinx/source/conf.py b/host/docs/sphinx/source/conf.py index e8a7c413c1..c662521e12 100644 --- a/host/docs/sphinx/source/conf.py +++ b/host/docs/sphinx/source/conf.py @@ -60,7 +60,7 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = 'en' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -87,7 +87,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +# html_static_path = ['_static'] # Custom sidebar templates, must be a dictionary that maps document names # to template names. diff --git a/host/include/uhd/rfnoc/rf_control/core_iface.hpp b/host/include/uhd/rfnoc/rf_control/core_iface.hpp index a557bf9df4..c297e154b2 100644 --- a/host/include/uhd/rfnoc/rf_control/core_iface.hpp +++ b/host/include/uhd/rfnoc/rf_control/core_iface.hpp @@ -79,6 +79,15 @@ class core_iface * If there is a single LO in this radio, and we're doing direct conversion, * then this is the LO frequency. * + * Note that unlike the uhd::usrp::multi_usrp::set_tx_freq() API, this does + * not attempt to tune any attached digital frequency shifter, unless it is + * part of the radio. That is why this API only returns a double value (the + * actual frequency) instead of a uhd::tune_result_t. If a combined tuning + * of digital frequency correction and LO tuning is desired (the same way + * that uhd::usrp::multi_usrp does by default), then the caller has to + * either also call uhd::rfnoc::dc_block_control::set_freq() with the + * residual frequency, or tune through the graph. + * * \param freq Frequency in Hz * \param chan Channel to tune * @@ -110,6 +119,15 @@ class core_iface * If there is a single LO in this radio, and we're doing direct conversion, * then this is the LO frequency. * + * Note that unlike the uhd::usrp::multi_usrp::set_rx_freq() API, this does + * not attempt to tune any attached digital frequency shifter, unless it is + * part of the radio. That is why this API only returns a double value (the + * actual frequency) instead of a uhd::tune_result_t. If a combined tuning + * of digital frequency correction and LO tuning is desired (the same way + * that uhd::usrp::multi_usrp does by default), then the caller has to + * either also call uhd::rfnoc::ddc_block_control::set_freq() with the + * residual frequency, or tune through the graph. + * * \param freq Requested frequency * \param chan Channel number. * \return The actual frequency. diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt index 683c1a4cce..93166624a4 100644 --- a/host/lib/CMakeLists.txt +++ b/host/lib/CMakeLists.txt @@ -143,7 +143,6 @@ LIBUHD_APPEND_LIBS(uhd_rc) # Add DLL resource file to Windows build ######################################################################## if(MSVC) - math(EXPR RC_VERSION_MAJOR_API "${UHD_VERSION_MAJOR} * 1000 + ${UHD_VERSION_API}") set(RC_VERSION_PATCH ${UHD_VERSION_PATCH}) if(UHD_VERSION_DEVEL) set(RC_VERSION_PATCH "999") diff --git a/host/lib/deps/rpclib/include/rpc/msgpack/predef/architecture/arm.h b/host/lib/deps/rpclib/include/rpc/msgpack/predef/architecture/arm.h index bfc4a3404a..737a066e1e 100644 --- a/host/lib/deps/rpclib/include/rpc/msgpack/predef/architecture/arm.h +++ b/host/lib/deps/rpclib/include/rpc/msgpack/predef/architecture/arm.h @@ -27,11 +27,13 @@ Distributed under the Boost Software License, Version 1.0. [[`__TARGET_ARCH_ARM`] [__predef_detection__]] [[`__TARGET_ARCH_THUMB`] [__predef_detection__]] [[`_M_ARM`] [__predef_detection__]] + [[`_M_ARM64`] [__predef_detection__]] [[`__arm64`] [8.0.0]] [[`__TARGET_ARCH_ARM`] [V.0.0]] [[`__TARGET_ARCH_THUMB`] [V.0.0]] [[`_M_ARM`] [V.0.0]] + [[`_M_ARM64`] [V.0.0]] ] */ @@ -39,7 +41,7 @@ Distributed under the Boost Software License, Version 1.0. #if defined(__arm__) || defined(__arm64) || defined(__thumb__) || \ defined(__TARGET_ARCH_ARM) || defined(__TARGET_ARCH_THUMB) || \ - defined(_M_ARM) + defined(_M_ARM) || defined(_M_ARM64) # undef MSGPACK_ARCH_ARM # if !defined(MSGPACK_ARCH_ARM) && defined(__arm64) # define MSGPACK_ARCH_ARM MSGPACK_VERSION_NUMBER(8,0,0) @@ -53,6 +55,9 @@ Distributed under the Boost Software License, Version 1.0. # if !defined(MSGPACK_ARCH_ARM) && defined(_M_ARM) # define MSGPACK_ARCH_ARM MSGPACK_VERSION_NUMBER(_M_ARM,0,0) # endif +# if !defined(MSGPACK_ARCH_ARM) && defined(_M_ARM64) +# define MSGPACK_ARCH_ARM MSGPACK_VERSION_NUMBER(_M_ARM64,0,0) +# endif # if !defined(MSGPACK_ARCH_ARM) # define MSGPACK_ARCH_ARM MSGPACK_VERSION_NUMBER_AVAILABLE # endif diff --git a/host/lib/include/uhdlib/usrp/cores/dsp_core_utils.hpp b/host/lib/include/uhdlib/usrp/cores/dsp_core_utils.hpp index 6371eb2e52..d83a8239a1 100644 --- a/host/lib/include/uhdlib/usrp/cores/dsp_core_utils.hpp +++ b/host/lib/include/uhdlib/usrp/cores/dsp_core_utils.hpp @@ -7,21 +7,66 @@ #pragma once +#include +#include +#include +#include +#include #include /*! For a requested frequency, sampling rate, and frequency word width (in * number of bits), return the correct frequency word (to set the CORDIC or * DDS) and the actual frequency. */ +template void get_freq_and_freq_word(const double requested_freq, const double tick_rate, double& actual_freq, - int32_t& freq_word, - int word_width = 32); + int32_t& freq_word) +{ + constexpr int32_t MAX_FREQ_WORD = std::numeric_limits::max(); + constexpr int32_t MIN_FREQ_WORD = std::numeric_limits::min(); + constexpr double scale_factor = static_cast(uint64_t(1) << word_width); + // Frequency normalized by sampling rate, and wrapped to [-0.5, 0.5). + const double freq_norm_scaled = + uhd::math::wrap_frequency(requested_freq / tick_rate, 1.0) * scale_factor; + + // confirm that the target frequency is within range of the CORDIC + UHD_ASSERT_THROW(std::abs(freq_norm_scaled) - 1 <= MAX_FREQ_WORD); + + /* Now calculate the frequency word. It is possible for this calculation + * to cause an overflow. As the requested DSP frequency approaches the + * master clock rate, that ratio multiplied by the scaling factor (2^32) + * will generally overflow within the last few kHz of tunable range. + * Thus, we check to see if the operation will overflow before doing it, + * and if it will, we set it to the integer min or max of this system. + */ + if (freq_norm_scaled >= MAX_FREQ_WORD) { + /* Operation would have caused a positive overflow of int32. */ + freq_word = MAX_FREQ_WORD; + + } else if (freq_norm_scaled <= MIN_FREQ_WORD) { + /* Operation would have caused a negative overflow of int32. */ + freq_word = MIN_FREQ_WORD; + + } else { + /* The operation is safe. Perform normally. */ + freq_word = int32_t(std::lround(freq_norm_scaled)); + } + + actual_freq = (double(freq_word) / scale_factor) * tick_rate; +} /*! For a requested frequency, sampling rate, and frequency word width (in * number of bits), return the correct frequency word (to set the CORDIC or * DDS) and the actual frequency. */ +template std::tuple get_freq_and_freq_word( - const double requested_freq, const double tick_rate, int word_width = 32); + const double requested_freq, const double tick_rate) +{ + double actual_freq; + int32_t freq_word; + get_freq_and_freq_word(requested_freq, tick_rate, actual_freq, freq_word); + return {actual_freq, freq_word}; +} diff --git a/host/lib/include/uhdlib/usrp/dboard/fbx/fbx_constants.hpp b/host/lib/include/uhdlib/usrp/dboard/fbx/fbx_constants.hpp index eefb173360..fd23fb8efd 100644 --- a/host/lib/include/uhdlib/usrp/dboard/fbx/fbx_constants.hpp +++ b/host/lib/include/uhdlib/usrp/dboard/fbx/fbx_constants.hpp @@ -72,11 +72,12 @@ static const std::vector FBX_LOS = {RFDC_NCO}; static constexpr size_t FBX_MAX_NUM_CHANS = 4; // These are addresses for the various table-based registers -static constexpr uint32_t ATR_ADDR_0X = 0; -static constexpr uint32_t ATR_ADDR_RX = 1; -static constexpr uint32_t ATR_ADDR_TX = 2; -static constexpr uint32_t ATR_ADDR_XX = 3; // Full-duplex +static constexpr uint32_t ATR_ADDR_0X = 0; +static constexpr uint32_t ATR_ADDR_RX = 1; +static constexpr uint32_t ATR_ADDR_TX = 2; +static constexpr uint32_t ATR_ADDR_XX = 3; // Full-duplex +static constexpr uint32_t NUM_ATR_STATES = 4; // Helper for looping -static constexpr std::array ATR_ADDRS{0, 1, 2, 3}; +static constexpr std::array ATR_ADDRS{0, 1, 2, 3}; }}} // namespace uhd::usrp::fbx diff --git a/host/lib/include/uhdlib/usrp/dboard/zbx/zbx_constants.hpp b/host/lib/include/uhdlib/usrp/dboard/zbx/zbx_constants.hpp index 63762be4ee..501abb28b4 100644 --- a/host/lib/include/uhdlib/usrp/dboard/zbx/zbx_constants.hpp +++ b/host/lib/include/uhdlib/usrp/dboard/zbx/zbx_constants.hpp @@ -184,12 +184,13 @@ static constexpr std::array ZBX_CHANNELS{0, 1}; static constexpr double ZBX_MIX1_MN_THRESHOLD = 4e9; // These are addresses for the various table-based registers -static constexpr uint32_t ATR_ADDR_0X = 0; -static constexpr uint32_t ATR_ADDR_RX = 1; -static constexpr uint32_t ATR_ADDR_TX = 2; -static constexpr uint32_t ATR_ADDR_XX = 3; // Full-duplex +static constexpr uint32_t ATR_ADDR_0X = 0; +static constexpr uint32_t ATR_ADDR_RX = 1; +static constexpr uint32_t ATR_ADDR_TX = 2; +static constexpr uint32_t ATR_ADDR_XX = 3; // Full-duplex +static constexpr uint32_t NUM_ATR_STATES = 4; // Helper for looping -static constexpr std::array ATR_ADDRS{0, 1, 2, 3}; +static constexpr std::array ATR_ADDRS{0, 1, 2, 3}; // Turn clang-formatting off so it doesn't compress these tables into a mess. // clang-format off diff --git a/host/lib/uhd.rc.in b/host/lib/uhd.rc.in index dee6bb8a3d..89045400ed 100644 --- a/host/lib/uhd.rc.in +++ b/host/lib/uhd.rc.in @@ -1,8 +1,8 @@ #include VS_VERSION_INFO VERSIONINFO - FILEVERSION @RC_VERSION_MAJOR_API@,@UHD_VERSION_ABI@,@RC_VERSION_PATCH@,@UHD_GIT_COUNT@ - PRODUCTVERSION @RC_VERSION_MAJOR_API@,@UHD_VERSION_ABI@,@RC_VERSION_PATCH@,@UHD_GIT_COUNT@ + FILEVERSION @UHD_VERSION_MAJOR@,@UHD_VERSION_API@,@UHD_VERSION_ABI@,@RC_VERSION_PATCH@ + PRODUCTVERSION @UHD_VERSION_MAJOR@,@UHD_VERSION_API@,@UHD_VERSION_ABI@,@RC_VERSION_PATCH@ FILEFLAGSMASK 0x3fL #ifndef NDEBUG FILEFLAGS 0x0L diff --git a/host/lib/usrp/cores/CMakeLists.txt b/host/lib/usrp/cores/CMakeLists.txt index 2e2132ad3b..f43161a571 100644 --- a/host/lib/usrp/cores/CMakeLists.txt +++ b/host/lib/usrp/cores/CMakeLists.txt @@ -28,7 +28,6 @@ endif() LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/dma_fifo_core_3000.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/dsp_core_utils.cpp ${CMAKE_CURRENT_SOURCE_DIR}/gpio_atr_3000.cpp ${CMAKE_CURRENT_SOURCE_DIR}/i2c_core_100_wb32.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rx_dsp_core_3000.cpp diff --git a/host/lib/usrp/cores/dsp_core_utils.cpp b/host/lib/usrp/cores/dsp_core_utils.cpp deleted file mode 100644 index 5575d97759..0000000000 --- a/host/lib/usrp/cores/dsp_core_utils.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// -// Copyright 2016 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#include -#include -#include -#include -#include - -static const int32_t MAX_FREQ_WORD = std::numeric_limits::max(); -static const int32_t MIN_FREQ_WORD = std::numeric_limits::min(); - -void get_freq_and_freq_word(const double requested_freq, - const double tick_rate, - double& actual_freq, - int32_t& freq_word, - int word_width) -{ - const double freq = uhd::math::wrap_frequency(requested_freq, tick_rate); - - // confirm that the target frequency is within range of the CORDIC - UHD_ASSERT_THROW(std::abs(freq) <= tick_rate / 2.0); - - /* Now calculate the frequency word. It is possible for this calculation - * to cause an overflow. As the requested DSP frequency approaches the - * master clock rate, that ratio multiplied by the scaling factor (2^32) - * will generally overflow within the last few kHz of tunable range. - * Thus, we check to see if the operation will overflow before doing it, - * and if it will, we set it to the integer min or max of this system. - */ - freq_word = 0; - - static const double scale_factor = std::pow(2.0, word_width); - if ((freq / tick_rate) >= (MAX_FREQ_WORD / scale_factor)) { - /* Operation would have caused a positive overflow of int32. */ - freq_word = MAX_FREQ_WORD; - - } else if ((freq / tick_rate) <= (MIN_FREQ_WORD / scale_factor)) { - /* Operation would have caused a negative overflow of int32. */ - freq_word = MIN_FREQ_WORD; - - } else { - /* The operation is safe. Perform normally. */ - freq_word = int32_t(std::lround((freq / tick_rate) * scale_factor)); - } - - actual_freq = (double(freq_word) / scale_factor) * tick_rate; -} - -std::tuple get_freq_and_freq_word( - const double requested_freq, const double tick_rate, int word_width) -{ - double actual_freq; - int32_t freq_word; - get_freq_and_freq_word(requested_freq, tick_rate, actual_freq, freq_word, word_width); - return std::make_tuple(actual_freq, freq_word); -} diff --git a/host/lib/usrp/dboard/magnesium/magnesium_radio_control.cpp b/host/lib/usrp/dboard/magnesium/magnesium_radio_control.cpp index ca4fd17079..636c9551ac 100644 --- a/host/lib/usrp/dboard/magnesium/magnesium_radio_control.cpp +++ b/host/lib/usrp/dboard/magnesium/magnesium_radio_control.cpp @@ -897,7 +897,7 @@ double magnesium_radio_control_impl::get_rx_lo_freq( std::string source = this->get_rx_lo_source(name, chan); if (name == MAGNESIUM_LO1) { return _ad9371_freq.at(RX_DIRECTION); - } else if (name == "adf4531") { + } else if (name == MAGNESIUM_LO2) { return _adf4351_freq.at(RX_DIRECTION); } else { RFNOC_LOG_ERROR("get_rx_lo_freq(): No such LO: " << name); diff --git a/tools/changeset_testlist.py b/tools/changeset_testlist.py index cd668edcb2..2b9a96bda6 100644 --- a/tools/changeset_testlist.py +++ b/tools/changeset_testlist.py @@ -55,10 +55,96 @@ def parse_args(): return parser.parse_args() -def get_changed_files(repo_path, target_branch, source_branch, include_target): +def check_changeset_content(file, **kwargs): + """Check changeset content for code and/or comments changes + + For example, if a .cpp file only has a comment change, we can use this to + remove it from the "changed files" list. This is useful when we only want to + only include tests if the underlying code was actually changed, and not just + a comment (which cannot change the outcome of a hardware test). + + The values of the include_content argument have the following meaning: + - 'code': at least one line with code changed + - 'code-only': all changed lines must be code changes + - 'comments': at least one line with comments changed + - 'comments-only': all changed lines must be comment changes + - 'code-and-comments': either code or comments changes (always True) + + Arguments: + file: List of files that have been changed. + repo_path: Path to the UHD repository. + git_cmd: Path to the git command. + include_content: allowed values are: code, code-only, comments, + comments-only, code-and-comments + + Returns: True if the file should be included, False otherwise. """ - Returns a list of paths in the UHD repository that have are different between - two branches. + + def identify_comment_line_cpp(line): + """Identify if a line is a comment in a C++ file.""" + line = line.strip() + return line.startswith("//") or line.startswith("/*") or line.startswith("*") + + def identify_comment_line_py(line): + """Identify if a line is a comment in a Python file.""" + line = line.strip() + return line.startswith("#") + + comment_identifer = { + ".cpp": identify_comment_line_cpp, + ".hpp": identify_comment_line_cpp, + ".c": identify_comment_line_cpp, + ".h": identify_comment_line_cpp, + ".py": identify_comment_line_py, + } + + include_content = kwargs.get("include_content", None) + if include_content == "code-and-comments": + # No need to check, result will always be true + return True + elif include_content in ["code", "code-only"]: + invert = True + elif include_content in ["comments", "comments-only"]: + invert = False + else: + raise ValueError(f"Unsupported argument: include_content={include_content}") + + target_branch = kwargs["target_branch"] + if kwargs.get("include_target"): + target_branch += "..." + get_diff_args = [ + shutil.which("git"), + "-C", + kwargs["repo_path"], + "diff", + "--no-color", + "--unified=0", + target_branch, + "--", + ] + + ext = os.path.splitext(file)[1] + if ext not in comment_identifer: + # If we have no rule to check, then always include the file. + return True + diff_lines = ( + subprocess.check_output(get_diff_args + [file], encoding="utf-8").strip().split("\n")[4:] + ) + line_matches = [comment_identifer[ext](line[1:])^invert for line in diff_lines if line[0] in ("-", "+")] + if include_content.endswith("-only"): + return all(line_matches) + else: + return any(line_matches) + + +def get_changed_files(repo_path, target_branch, source_branch, include_target): + """Return a list of paths in the UHD repository that have are different between two branches. + + Arguments: + repo_path: Path to the UHD repository. + target_branch: Branch to compare against (e.g., master). + source_branch: Branch to compare from. Defaults to the current branch. + include_target: Include changes that originate from the target branch. """ assert target_branch # If include_target is false, then current (unstaged/uncommited) changes are @@ -84,14 +170,16 @@ def load_rules(rule_file): class RuleApplier: """Helper class to update an internal test list based on a set of rules.""" - def __init__(self, rules, labels): + def __init__(self, rules, labels, **kwargs): """Initialize. Arguments: rules: List of rules to apply. + labels: List of labels relevant to the current changeset. """ self.rules = rules self.labels = labels + self.args = kwargs self.test_list = set() def apply(self, filename, verbose=False): @@ -130,9 +218,20 @@ def _apply_rule(self, rule, filename, verbose=False): if verbose: sys.stderr.write(f"Filename {filename} matches rule: {rule}\n") if "label" in rule and not rule["label"] in self.labels: + if verbose: + sys.stderr.write(f"Skipping file based on missing label: {rule['label']}\n") return False if "label" in rule and verbose: sys.stderr.write(f"Label {rule['label']} found\n") + include_content = rule.get("include_content", "code") + if not check_changeset_content( + filename, **self.args, include_content=include_content + ): + if verbose: + sys.stderr.write( + f"Skipping {filename} based on content rule: include_content='{include_content}'\n" + ) + return False if "add" in rule: self.test_list.update(rule["add"]) if "remove" in rule: @@ -170,7 +269,15 @@ def main(): args.include_target, ) labels = get_labels(args.github_label_api_endpoint, args.github_token) - rule_applier = RuleApplier(load_rules(rule_file), labels) + rule_applier = RuleApplier( + load_rules(rule_file), + labels, + repo_path=args.repo_path, + target_branch=args.target_branch, + source_branch=args.source_branch, + include_target=args.include_target, + verbose=args.verbose, + ) for filename in file_list: rule_applier.apply(filename, args.verbose) rule_applier.apply_labels() diff --git a/tools/changeset_testlist.yaml b/tools/changeset_testlist.yaml index 71dcc280b1..ebbac6157c 100644 --- a/tools/changeset_testlist.yaml +++ b/tools/changeset_testlist.yaml @@ -25,6 +25,13 @@ - re: ^host/docs/ add: - uhd.docs +# If a comment in a public header changed, then most likely the documentation +# changed, but we still want to run other tests, too +- re: ^host/include/uhd/.+\.hpp$ + add: + - uhd.docs + include_content: comments + stop: False # Device-specific changes. These should trigger HW tests only on those devices # they affect. We start with daughterboard rules, then motherboard rules. - re: host/lib/usrp/dboard/zbx/ @@ -196,7 +203,36 @@ ############################################################################### # CI CHANGES ############################################################################### -- re: .ci/templates/tests/templates/job-uhd-x4xx-hardware-tests.yml +# Changes to pipeline infrastructure or build jobs -> build and test all +- re: .ci/templates/stages-.*.yml + add: + - uhd.build.all + - hw.streaming.all + - hw.rf.all + - devtest.all +- re: .ci/templates/.*-build.*.yml + add: + - uhd.build.all + - hw.streaming.all + - hw.rf.all + - devtest.all +# Devtest +- re: .ci/templates/.*-devtest.*.yml + add: + - uhd.build.linux + - devtest.all +# RF Test +- re: .ci/templates/.*-rf-tests.*.yml + add: + - uhd.build.linux + - hw.rf.all +# Streaming Test +- re: .ci/templates/.*-streaming.*.yml + add: + - uhd.build.linux + - hw.streaming.all +# RF Test x4xx +- re: .ci/templates/tests/.*.yml add: - uhd.build.linux - hw.rf.x4xx