diff options
-rw-r--r-- | .ci/templates/job-uhd-build-src.yml | 12 | ||||
-rw-r--r-- | .ci/templates/job-uhd-devtest-rhombus.yml | 128 | ||||
-rw-r--r-- | .ci/templates/job-uhd-devtest.yml | 91 | ||||
-rw-r--r-- | .ci/uhd-pipeline.yml | 33 | ||||
-rw-r--r-- | .ci/utils/jtag/viv_hardware_utils.tcl | 97 | ||||
-rw-r--r-- | .ci/utils/mutex_hardware.py | 78 |
6 files changed, 424 insertions, 15 deletions
diff --git a/.ci/templates/job-uhd-build-src.yml b/.ci/templates/job-uhd-build-src.yml index 6e945d789..366bb7509 100644 --- a/.ci/templates/job-uhd-build-src.yml +++ b/.ci/templates/job-uhd-build-src.yml @@ -65,11 +65,15 @@ jobs: vsArch: $(vsArch) vsYear: $(vsYear) - - task: CopyFiles@2 + - task: ArchiveFiles@2 inputs: - sourceFolder: $(Build.BinariesDirectory) - targetFolder: $(Build.ArtifactStagingDirectory) - displayName: Copy build files to artifact folder + rootFolderOrFile: $(Build.BinariesDirectory) + includeRootFolder: false + archiveType: tar + tarCompression: gz + archiveFile: $(Build.ArtifactStagingDirectory)/$(dockerOSName)-${{ parameters.toolset }}.tar.gz + replaceExistingArchive: true + displayName: Compress build files - task: CopyFiles@2 inputs: diff --git a/.ci/templates/job-uhd-devtest-rhombus.yml b/.ci/templates/job-uhd-devtest-rhombus.yml new file mode 100644 index 000000000..8bcc55054 --- /dev/null +++ b/.ci/templates/job-uhd-devtest-rhombus.yml @@ -0,0 +1,128 @@ +parameters: +- name: testOS + type: string + values: + - ubuntu2004 +- name: uhdSrcDir + type: string + +jobs: +- template: job-uhd-devtest.yml + parameters: + suiteName: 'rhombus' + testOS: '${{ parameters.testOS }}' + knownHost: 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE+SZhHi7YOvHW6xmVGhhZGLtqlZoPkOqGdr5WqnmLBN root@ubuntu' + toolset: 'make' + uhdSrcDir: '${{ parameters.uhdSrcDir }}' + redisHost: 'sdr-rhombus' + dutMatrix: + rhombus-x300-UBX-0 XG: + devType: 'x300' + devModel: 'x300' + devName: 'rhombus-x300-UBX-0' + devSerial: '30A6019' + devBus: 'ip' + devAddr: '192.168.40.2' + devFpga: 'XG' + devtestPattern: 'x3x0' + jtagSerial: '2516350A6019' + jtagServer: 'nitest@sdr-rhombus' + rhombus-x310-UBX-0 XG: + devType: 'x300' + devModel: 'x310' + devName: 'rhombus-x310-UBX-0' + devSerial: '3138EF5' + devBus: 'ip' + devAddr: '192.168.40.3' + devFpga: 'XG' + devtestPattern: 'x3x0' + jtagSerial: '251635138E98' + jtagServer: 'nitest@sdr-rhombus' + rhombus-x310-CBX-0 XG: + devType: 'x300' + devModel: 'x310' + devName: 'rhombus-x310-CBX-0' + devSerial: '30796C2' + devBus: 'ip' + devAddr: '192.168.40.4' + devFpga: 'XG' + devtestPattern: 'x3x0' + jtagSerial: '2516350796C2' + jtagServer: 'nitest@sdr-rhombus' + rhombus-x310-WBX-0 XG: + devType: 'x300' + devModel: 'x310' + devName: 'rhombus-x310-WBX-0' + devSerial: '30C5BFF' + devBus: 'ip' + devAddr: '192.168.40.5' + devFpga: 'XG' + devtestPattern: 'x3x0' + jtagSerial: '2516350C5BFF' + jtagServer: 'nitest@sdr-rhombus' + rhombus-x310-SBX-0 XG: + devType: 'x300' + devModel: 'x310' + devName: rhombus-x310-SBX-0 + devSerial: 'F43D13' + devBus: 'ip' + devAddr: '192.168.40.6' + devFpga: 'XG' + devtestPattern: 'x3x0' + jtagSerial: '251635F43D13' + jtagServer: 'nitest@sdr-rhombus' + rhombus-x300-UBX-0 HG: + devType: 'x300' + devModel: 'x300' + devName: 'rhombus-x300-UBX-0' + devSerial: '30A6019' + devBus: 'ip' + devAddr: '192.168.40.2' + devFpga: 'HG' + devtestPattern: 'x3x0' + jtagSerial: '2516350A6019' + jtagServer: 'nitest@sdr-rhombus' + rhombus-x310-UBX-0 HG: + devType: 'x300' + devModel: 'x310' + devName: 'rhombus-x310-UBX-0' + devSerial: '3138EF5' + devBus: 'ip' + devAddr: '192.168.40.3' + devFpga: 'HG' + devtestPattern: 'x3x0' + jtagSerial: '251635138E98' + jtagServer: 'nitest@sdr-rhombus' + rhombus-x310-CBX-0 HG: + devType: 'x300' + devModel: 'x310' + devName: 'rhombus-x310-CBX-0' + devSerial: '30796C2' + devBus: 'ip' + devAddr: '192.168.40.4' + devFpga: 'HG' + devtestPattern: 'x3x0' + jtagSerial: '2516350796C2' + jtagServer: 'nitest@sdr-rhombus' + rhombus-x310-WBX-0 HG: + devType: 'x300' + devModel: 'x310' + devName: 'rhombus-x310-WBX-0' + devSerial: '30C5BFF' + devBus: 'ip' + devAddr: '192.168.40.5' + devFpga: 'HG' + devtestPattern: 'x3x0' + jtagSerial: '2516350C5BFF' + jtagServer: 'nitest@sdr-rhombus' + rhombus-x310-SBX-0 HG: + devType: 'x300' + devModel: 'x310' + devName: rhombus-x310-SBX-0 + devSerial: 'F43D13' + devBus: 'ip' + devAddr: '192.168.40.6' + devFpga: 'HG' + devtestPattern: 'x3x0' + jtagSerial: '251635F43D13' + jtagServer: 'nitest@sdr-rhombus' diff --git a/.ci/templates/job-uhd-devtest.yml b/.ci/templates/job-uhd-devtest.yml new file mode 100644 index 000000000..81733bd88 --- /dev/null +++ b/.ci/templates/job-uhd-devtest.yml @@ -0,0 +1,91 @@ +parameters: +- name: suiteName + type: string +- name: testOS + type: string + values: + - ubuntu2004 +- name: knownHost + type: string +- name: toolset + type: string + values: + - make +- name: uhdSrcDir + type: string +- name: redisHost + type: string +- name: dutMatrix + type: object + +jobs: +- job: uhd_devtest_${{ parameters.suiteName }}_${{ parameters.testOS }} + displayName: uhd devtest ${{ parameters.suiteName }} ${{ parameters.testOS }} + pool: + name: de-dre-lab + demands: + - suiteName -equals ${{ parameters.suiteName }} + - testOS -equals ${{ parameters.testOS }} + variables: + - group: sdr-pipeline-vars + strategy: + matrix: ${{ parameters.dutMatrix }} + workspace: + clean: outputs + steps: + - checkout: self + clean: true + - task: InstallSSHKey@0 + displayName: 'Install Ettus SSH key' + inputs: + knownHostsEntry: '${{ parameters.knownHost }}' + sshPublicKey: '$(ettus_ssh_pubkey)' + sshKeySecureFile: 'id_rsa.ettus' + - download: current + artifact: ${{ parameters.testOS }}-${{ parameters.toolset }} + displayName: Download pipeline artifact ${{ parameters.testOS }}-${{ parameters.toolset }} + - task: ExtractFiles@1 + inputs: + archiveFilePatterns: $(Pipeline.Workspace)/${{ parameters.testOS }}-${{ parameters.toolset }}/${{ parameters.testOS }}-${{ parameters.toolset }}.tar.gz + destinationFolder: $(Build.BinariesDirectory) + cleanDestinationFolder: true + - script: | + cd $(Build.BinariesDirectory)/uhddev/build + mkdir -p fpga_images + rm -rf fpga_images/* + python3 utils/uhd_images_downloader.py -t $(devModel) -i fpga_images \ + -b $(sdr-fileserver) + displayName: Download FPGA Images + - script: | + mkdir -p $(Common.TestResultsDirectory)/devtest + cd $(Common.TestResultsDirectory)/devtest + export PATH=$(Build.BinariesDirectory)/uhddev/build/utils:$(Build.BinariesDirectory)/uhddev/build/examples:$PATH + export LD_LIBRARY_PATH=$(Build.BinariesDirectory)/uhddev/build/lib:$LD_LIBRARY_PATH + python3 ${{ parameters.uhdSrcDir }}/.ci/utils/mutex_hardware.py \ + --jtag_x3xx $(jtagServer),$(jtagSerial),$(Build.BinariesDirectory)/uhddev/build/fpga_images/usrp_$(devModel)_fpga_$(devFpga).bit \ + ${{ parameters.redisHost }} $(devName) \ + "$(Build.BinariesDirectory)/uhddev/build/utils/uhd_usrp_probe --args addr=$(devAddr)" \ + "python3 ${{ parameters.uhdSrcDir }}/host/tests/devtest/run_testsuite.py \ + --src-dir ${{ parameters.uhdSrcDir }}/host/tests/devtest \ + --devtest-pattern $(devtestPattern) --args addr=$(devAddr),type=$(devType) \ + --build-type Release --build-dir $(Build.BinariesDirectory)/uhddev/build \ + --python-interp python3 --xml" + continueOnError: true + condition: and(succeeded(), eq(variables.devType, 'x300'), eq(variables.devBus, 'ip')) + displayName: Run devtest on $(devName) $(devFpga) + - script: | + cd $(Common.TestResultsDirectory)/devtest + python3 ${{ parameters.uhdSrcDir }}/.ci/utils/format_devtest_junitxml.py \ + $(Common.TestResultsDirectory)/devtest \ + $(Common.TestResultsDirectory)/devtest/devtestresults.xml + continueOnError: true + displayName: Format devtest xml + - task: PublishTestResults@2 + inputs: + testResultsFormat: 'JUnit' + testResultsFiles: '$(Common.TestResultsDirectory)/devtest/devtestresults.xml' + testRunTitle: $(devName) $(devFpga) devtest + buildConfiguration: 'Release' + mergeTestResults: true + failTaskOnFailedTests: true + displayName: Upload devtest results diff --git a/.ci/uhd-pipeline.yml b/.ci/uhd-pipeline.yml index 58db21be3..481e5a03d 100644 --- a/.ci/uhd-pipeline.yml +++ b/.ci/uhd-pipeline.yml @@ -25,16 +25,27 @@ resources: - pipeline: uhd_build_docker_container source: 'uhddev Build Docker Containers' branch: master +stages: +- stage: build_uhd_stage + displayName: Build UHD + jobs: + - template: templates/job-get-latest-uhd-docker.yml -jobs: -- template: templates/job-get-latest-uhd-docker.yml + - template: templates/job-uhd-build-src.yml + parameters: + toolset: make + - template: templates/job-uhd-build-src.yml + parameters: + toolset: ninja + - template: templates/job-uhd-build-src.yml + parameters: + toolset: msbuild -- template: templates/job-uhd-build-src.yml - parameters: - toolset: make -- template: templates/job-uhd-build-src.yml - parameters: - toolset: ninja -- template: templates/job-uhd-build-src.yml - parameters: - toolset: msbuild +- stage: test_uhd_stage + displayName: Test UHD + dependsOn: build_uhd_stage + jobs: + - template: templates/job-uhd-devtest-rhombus.yml + parameters: + testOS: ubuntu2004 + uhdSrcDir: $(Build.SourcesDirectory) diff --git a/.ci/utils/jtag/viv_hardware_utils.tcl b/.ci/utils/jtag/viv_hardware_utils.tcl new file mode 100644 index 000000000..49ad74c0f --- /dev/null +++ b/.ci/utils/jtag/viv_hardware_utils.tcl @@ -0,0 +1,97 @@ +# Function definitions +proc ::connect_server { {hostname localhost} {port 3121} } { + if { [string compare [current_hw_server -quiet] ""] != 0 } { + disconnect_server + } + connect_hw_server -url $hostname:$port +} + +proc ::disconnect_server { } { + disconnect_hw_server [current_hw_server] +} + +proc ::jtag_list {} { + # Iterate through all hardware targets + set hw_targets [get_hw_targets -of_objects [current_hw_server -quiet] -quiet] + set idx_t 0 + foreach hw_target $hw_targets { + puts "== Target${idx_t}: $hw_target ==" + open_hw_target $hw_target -quiet + # Iterate through all hardware devices + set hw_devices [get_hw_devices] + set idx_d 0 + foreach hw_device $hw_devices { + puts "--- Device${idx_d}: $hw_device (Address = ${idx_t}:${idx_d})" + set idx_d [expr $idx_d + 1] + } + close_hw_target -quiet + set idx_t [expr $idx_t + 1] + } +} + +proc ::jtag_program { filepath {serial "."} {address "0:0"} } { + set idx_t [lindex [split $address :] 0] + set idx_d [lindex [split $address :] 1] + + set hw_targets [get_hw_targets -of_objects [current_hw_server]] + set hw_targets_regexp {} + + foreach target $hw_targets { + if { [regexp $serial $target] } { + set hw_targets_regexp [concat $hw_targets_regexp $target] + } + } + + set hw_target [lindex $hw_targets_regexp $idx_t] + + if { [string compare $hw_target ""] == 0 } { + error "ERROR: Could not open hw_target $idx_t. Either the address $address is incorrect or the device is not connected." + } else { + open_hw_target $hw_target -quiet + } + + set hw_device [lindex [get_hw_devices] $idx_d] + if { [string compare $hw_device ""] == 0 } { + close_hw_target -quiet + error "ERROR: Could not open hw_device $idx_d. Either the address $address is incorrect or the device is not connected." + } else { + puts "- Target: $hw_target" + puts "- Device: $hw_device" + puts "- Filename: $filepath" + puts "Programming..." + current_hw_device $hw_device + set_property PROBES.FILE {} [current_hw_device] + set_property PROGRAM.FILE $filepath [current_hw_device] + program_hw_devices [current_hw_device] + close_hw_target -quiet + puts "Programming DONE" + } +} + +# Initialization sequence +open_hw_manager +connect_server + +if [expr $argc > 0] { + #Execute a command and exit + set cmd [lindex $argv 0] + if { [string compare $cmd "list"] == 0 } { + jtag_list + } elseif { [string compare $cmd "program"] == 0 } { + set filepath [lindex $argv 1] + if [expr $argc == 3] { + set serial [lindex $argv 2] + jtag_program $filepath $serial + } elseif [expr $argc > 3] { + set serial [lindex $argv 2] + set devaddr [lindex $argv 3] + jtag_program $filepath $serial $devaddr + } else { + jtag_program $filepath + } + } else { + error "Invalid command: $cmd" + } + disconnect_server + exit +} diff --git a/.ci/utils/mutex_hardware.py b/.ci/utils/mutex_hardware.py new file mode 100644 index 000000000..563ec5b1a --- /dev/null +++ b/.ci/utils/mutex_hardware.py @@ -0,0 +1,78 @@ +# mutex_hardware uses redis get a lock on hardware +# to prevent other Azure Pipeline agents from use. +# It also provides helper functions to get devices +# into a state where it can be used for testing. + +import argparse +import os +import pathlib +import shlex +import subprocess +import sys +import time + +from fabric import Connection +from pottery import Redlock +from redis import Redis + + +def jtag_x3xx(jtag_args, redis_server): + remote_working_dir = "pipeline_fpga" + vivado_program_jtag = "/opt/Xilinx/Vivado_Lab/2020.1/bin/vivado_lab -mode batch -source {}/viv_hardware_utils.tcl -nolog -nojournal -tclargs program".format( + remote_working_dir) + jtag_server, jtag_serial, fpga_path = jtag_args.split(",") + print("Waiting on jtag mutex for {}".format(jtag_server), flush=True) + with Redlock(key="hw_jtag_{}".format(jtag_server), + masters=redis_server, auto_release_time=1000 * 60 * 5): + print("Got jtag mutex for {}".format(jtag_server), flush=True) + with Connection(host=jtag_server) as jtag_host: + jtag_host.run("mkdir -p " + remote_working_dir) + jtag_host.run("rm -rf {}/*".format(remote_working_dir)) + jtag_host.put( + os.path.join(pathlib.Path( + __file__).parent.absolute(), "jtag/viv_hardware_utils.tcl"), + remote=remote_working_dir) + jtag_host.put(fpga_path, remote=remote_working_dir) + jtag_host.run(vivado_program_jtag + " " + + os.path.join(remote_working_dir, os.path.basename(fpga_path)) + + " " + jtag_serial) + print("Waiting 15 seconds for device to come back up", flush=True) + time.sleep(15) + + +def main(args): + redis_server = {Redis.from_url( + "redis://{}:6379/0".format(args.redis_server))} + print("Waiting to acquire mutex for {}".format(args.dut_name), flush=True) + with Redlock(key=args.dut_name, masters=redis_server, auto_release_time=1000 * 60 * args.dut_timeout): + print("Got mutex for {}".format(args.dut_name), flush=True) + if(args.jtag_x3xx != None): + jtag_x3xx(args.jtag_x3xx, redis_server) + for command in args.test_commands: + result = subprocess.run(shlex.split(command)) + if(result.returncode != 0): + sys.exit(result.returncode) + sys.exit(0) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + # jtag_x3xx will flash the fpga for a given jtag_serial using + # Vivado on jtag_server. It uses SSH to control jtag_server. + # Provide fpga_path as a local path and it will be copied + # to jtag_server. + parser.add_argument("--jtag_x3xx", type=str, + help="user@jtag_server,jtag_serial,fpga_path") + parser.add_argument("--dut_timeout", type=int, default=30, + help="Dut mutex timeout in minutes") + parser.add_argument("redis_server", type=str, + help="Redis server for mutex") + parser.add_argument("dut_name", type=str, + help="Unique identifier for device under test") + # test_commands allows for any number of shell commands + # to execute. Call into mutex_hardware with an unlimited + # number of commands in string format as the last positional arguments. + parser.add_argument("test_commands", type=str, + nargs="+", help="Commands to run") + args = parser.parse_args() + main(args) |