ci: support for x3xx devtest on rhombus

Rhombus is a test suite that is intended run devtests for multiple
devices in parallel. This commit adds support for x300 and x310
support. This uses redis to mutex access to the hardware between
Azure Pipeline agents. This also updates the fpga using Vivado
over ssh to the host machine.

Signed-off-by: Steven Koo <steven.koo@ni.com>
This commit is contained in:
Steven Koo 2021-03-22 11:53:37 -05:00 committed by Aaron Rossetto
parent 1efdc3a7f6
commit 64ea8d91f8
6 changed files with 424 additions and 15 deletions

View file

@ -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:

View file

@ -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'

View file

@ -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

View file

@ -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)

View file

@ -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
}

View file

@ -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)