mirror of
https://github.com/EttusResearch/uhd.git
synced 2026-10-25 17:36:10 +00:00
Compare commits
246 Commits
15264cdc06
...
d1be63490e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1be63490e | ||
|
|
30aab2a4c7 | ||
|
|
dd1dbad6e7 | ||
|
|
eab0f7fe89 | ||
|
|
c4ae50a9cb | ||
|
|
ca8d6c9fab | ||
|
|
621fda4633 | ||
|
|
b1e77b24e4 | ||
|
|
5745c8a640 | ||
|
|
0b7f642d4a | ||
|
|
f9ab8f3aae | ||
|
|
020608b971 | ||
|
|
00c16e56e3 | ||
|
|
dad6f906f9 | ||
|
|
54f185ecdd | ||
|
|
e4672044f9 | ||
|
|
0cf40c3276 | ||
|
|
8ad3b4961d | ||
|
|
5709e8b546 | ||
|
|
1907032f7b | ||
|
|
155dfbf352 | ||
|
|
416988b3b6 | ||
|
|
3ed25c39c7 | ||
|
|
b1e4016c2b | ||
|
|
148c861163 | ||
|
|
e6470cf8ce | ||
|
|
772e682503 | ||
|
|
440ed5ae76 | ||
|
|
ec86cd7b2c | ||
|
|
7a44637627 | ||
|
|
65b9c0270e | ||
|
|
81c53bed91 | ||
|
|
0df07dcde8 | ||
|
|
f3d6cd6c01 | ||
|
|
4e8fa298dd | ||
|
|
2a036118de | ||
|
|
f8240b6871 | ||
|
|
91ae70080e | ||
|
|
31ff7ce148 | ||
|
|
1e80ef2fe9 | ||
|
|
edac52a8d9 | ||
|
|
626bcb32ea | ||
|
|
34a43948aa | ||
|
|
ef0a814a3d | ||
|
|
7351887ab5 | ||
|
|
04a7f8c6bf | ||
|
|
c6e4c50945 | ||
|
|
9f8defc537 | ||
|
|
66c66a4d2b | ||
|
|
4eeee62aad | ||
|
|
8720fe87ec | ||
|
|
633782b405 | ||
|
|
3fe3610eb8 | ||
|
|
3f46b68c6d | ||
|
|
50a47f75de | ||
|
|
6e11078807 | ||
|
|
cf14224aa6 | ||
|
|
ec32eb04ec | ||
|
|
a76ab6ffc9 | ||
|
|
99fe17071f | ||
|
|
2feedf3338 | ||
|
|
a48ccdb0a0 | ||
|
|
2fa91b605a | ||
|
|
5ff8ca11f3 | ||
|
|
ddb7e6866b | ||
|
|
15283f106b | ||
|
|
5e9888c204 | ||
|
|
aaaca5bff9 | ||
|
|
58ab7a88cc | ||
|
|
5b1bcabcf8 | ||
|
|
e5c05cfde1 | ||
|
|
c1540c92f1 | ||
|
|
b7a92cd9d9 | ||
|
|
efa47018e4 | ||
|
|
d394ae626a | ||
|
|
5ea031a946 | ||
|
|
9365ae9b6a | ||
|
|
4904d06da2 | ||
|
|
57df0ca685 | ||
|
|
285cce69a2 | ||
|
|
eb785ff23b | ||
|
|
0d7ed3b1a4 | ||
|
|
6fbadafde6 | ||
|
|
c57e8cb30d | ||
|
|
72d896bd25 | ||
|
|
d687c4ebbe | ||
|
|
6cb889675f | ||
|
|
9e8b5b9553 | ||
|
|
4caf56aa0d | ||
|
|
94fd6241b8 | ||
|
|
323d066e64 | ||
|
|
9a3a47bb69 | ||
|
|
7bd5f8047a | ||
|
|
635e73e7e0 | ||
|
|
856a3e7dd2 | ||
|
|
7d26845c08 | ||
|
|
218975dd22 | ||
|
|
b5718dc22b | ||
|
|
394768bcff | ||
|
|
31ca7a5497 | ||
|
|
b8114103c7 | ||
|
|
081429caa9 | ||
|
|
85e163a0d9 | ||
|
|
6a8a56afb2 | ||
|
|
d110c060f2 | ||
|
|
881f19db7c | ||
|
|
4e3912efd8 | ||
|
|
9da28f06f7 | ||
|
|
be791eb065 | ||
|
|
0e680f9cb6 | ||
|
|
e866ff81f9 | ||
|
|
3cfb163340 | ||
|
|
17c0114771 | ||
|
|
31be5b875a | ||
|
|
02f11f970c | ||
|
|
53a1ef762e | ||
|
|
000cd8e3cb | ||
|
|
0e3b241021 | ||
|
|
84d6af328b | ||
|
|
44ecdb1f39 | ||
|
|
5a8d5b3a6e | ||
|
|
874c196fec | ||
|
|
dffdfef5f5 | ||
|
|
d539960867 | ||
|
|
f0b4bf59f7 | ||
|
|
0dda23bb52 | ||
|
|
1da439c020 | ||
|
|
ec7e94bd4b | ||
|
|
167c179816 | ||
|
|
44791931c8 | ||
|
|
538a2e3fb3 | ||
|
|
05443d301d | ||
|
|
a076ee6dd7 | ||
|
|
dfc1ad17c3 | ||
|
|
29c459f1b3 | ||
|
|
f9e8c5896f | ||
|
|
e23454654f | ||
|
|
c4cf03925a | ||
|
|
f0b5d454cc | ||
|
|
344c6793ac | ||
|
|
8d9c53c83e | ||
|
|
6ab55e0cf4 | ||
|
|
c0be7431c0 | ||
|
|
cc29eb4496 | ||
|
|
add2d9efa6 | ||
|
|
0e9a62834f | ||
|
|
e98e866709 | ||
|
|
bc64ff0ad6 | ||
|
|
9291d7e18b | ||
|
|
e8079eca45 | ||
|
|
b8d34e36be | ||
|
|
3716dd0884 | ||
|
|
a9ddd67fb9 | ||
|
|
2ff1a639eb | ||
|
|
2f0fb8a1fa | ||
|
|
879ed08bb1 | ||
|
|
27e8768431 | ||
|
|
5cb4e4f57f | ||
|
|
f88231c7c1 | ||
|
|
6efbd7986b | ||
|
|
414e99f509 | ||
|
|
b9f346664a | ||
|
|
9015e01d3c | ||
|
|
e7ea67c2c7 | ||
|
|
27cd1c5a6f | ||
|
|
b035928f19 | ||
|
|
cbcf8a46c6 | ||
|
|
af4058bd64 | ||
|
|
fc6e355f24 | ||
|
|
7537a2a909 | ||
|
|
04bb66663e | ||
|
|
0b80e7f011 | ||
|
|
9c3959cdb0 | ||
|
|
87ee25d152 | ||
|
|
e503aaff88 | ||
|
|
1c0dfa23d8 | ||
|
|
ed156ae6cf | ||
|
|
99a05593d8 | ||
|
|
8676b3e257 | ||
|
|
a9aed26306 | ||
|
|
c17c6a4c3a | ||
|
|
8745af720e | ||
|
|
556b378912 | ||
|
|
fdb3a497a5 | ||
|
|
4d0c9c8dfd | ||
|
|
c20e60a594 | ||
|
|
d91c2293d1 | ||
|
|
45ea2365b1 | ||
|
|
9849f85918 | ||
|
|
984bc86793 | ||
|
|
4a9b264b86 | ||
|
|
e90b0b4643 | ||
|
|
76ddd382f9 | ||
|
|
1bafc03e08 | ||
|
|
00826ca054 | ||
|
|
3ec3ea3ef6 | ||
|
|
dd7607fc17 | ||
|
|
30a4b7fc2b | ||
|
|
33a0dc16bd | ||
|
|
2618b51cb8 | ||
|
|
aa2665d6f4 | ||
|
|
752b878a08 | ||
|
|
d46c011757 | ||
|
|
6284863e14 | ||
|
|
d4828aa208 | ||
|
|
eb21a19deb | ||
|
|
4bf18365d8 | ||
|
|
294ced36f6 | ||
|
|
a58a06f50c | ||
|
|
09b0346838 | ||
|
|
7a0a5f8085 | ||
|
|
0b47148652 | ||
|
|
82fb06bfe9 | ||
|
|
a3d8778a84 | ||
|
|
943692b9c7 | ||
|
|
2788ac9e6d | ||
|
|
0fd2f32cb8 | ||
|
|
72bac0df60 | ||
|
|
6883bb4d39 | ||
|
|
f60ae0e17d | ||
|
|
a1f558f16a | ||
|
|
66769905bb | ||
|
|
e466f427fd | ||
|
|
b4aeb78438 | ||
|
|
4ef8c18981 | ||
|
|
a18579e3e5 | ||
|
|
e0f685a4aa | ||
|
|
b6286bab13 | ||
|
|
2cde73ab6f | ||
|
|
d4b827abea | ||
|
|
dd708a46f4 | ||
|
|
e98bf35840 | ||
|
|
83553e2dd5 | ||
|
|
5bfe6a47ce | ||
|
|
de040602ee | ||
|
|
c8a8a3d931 | ||
|
|
1af4625e84 | ||
|
|
26c0033d1e | ||
|
|
12d9916fc3 | ||
|
|
c1b344b732 | ||
|
|
2951e32013 | ||
|
|
62aec9a0a0 | ||
|
|
74b818000f | ||
|
|
63ea9328aa | ||
|
|
2a2ce50abe | ||
|
|
e416bffa95 |
107
.ci/docker/scripts/install-choco-package.ps1
Normal file
107
.ci/docker/scripts/install-choco-package.ps1
Normal file
@ -0,0 +1,107 @@
|
||||
param(
|
||||
[string]$PackageName,
|
||||
|
||||
[string]$Version = "",
|
||||
|
||||
[string]$InstallArgs = "",
|
||||
|
||||
[ValidateRange(0, 20)]
|
||||
[int]$RetryCount = 2,
|
||||
|
||||
[ValidateRange(1, 3600)]
|
||||
[int]$RetryDelaySeconds = 30,
|
||||
|
||||
[ValidateRange(0, 100000)]
|
||||
[int]$LogTailLines = 300,
|
||||
|
||||
[Alias('h')]
|
||||
[switch]$Help,
|
||||
|
||||
[Parameter(ValueFromRemainingArguments = $true)]
|
||||
[string[]]$ExtraArgs
|
||||
)
|
||||
|
||||
function Show-Usage {
|
||||
Write-Host "Usage:"
|
||||
Write-Host " powershell -NoProfile -ExecutionPolicy Bypass -File install-choco-package.ps1 -PackageName <name> [-Version <version>] [-InstallArgs <args>] [-RetryCount <n>] [-RetryDelaySeconds <sec>]"
|
||||
Write-Host ""
|
||||
Write-Host "Options:"
|
||||
Write-Host " -PackageName Chocolatey package ID (required)."
|
||||
Write-Host " -Version Optional package version passed as --version."
|
||||
Write-Host " -InstallArgs Optional installer args passed as --installargs."
|
||||
Write-Host " -RetryCount Number of retries after the first failed attempt (default: 2)."
|
||||
Write-Host " -RetryDelaySeconds Wait time between retries in seconds (default: 30)."
|
||||
Write-Host " -LogTailLines Number of log lines to show on final failure (default: 300, use 0 to disable)."
|
||||
Write-Host " -Help, --help Show this help text."
|
||||
Write-Host ""
|
||||
Write-Host "Examples:"
|
||||
Write-Host " powershell -NoProfile -ExecutionPolicy Bypass -File C:\Temp\install-choco-package.ps1 -PackageName doxygen.install -Version 1.9.8"
|
||||
Write-Host " powershell -NoProfile -ExecutionPolicy Bypass -File C:\Temp\install-choco-package.ps1 -PackageName cmake.install -Version 3.22.3 -InstallArgs \"ADD_CMAKE_TO_PATH=System\""
|
||||
Write-Host " powershell -NoProfile -ExecutionPolicy Bypass -File C:\Temp\install-choco-package.ps1 -PackageName git"
|
||||
Write-Host " powershell -NoProfile -ExecutionPolicy Bypass -File C:\Temp\install-choco-package.ps1 -PackageName doxygen.install -Version 1.9.8 -RetryCount 3 -RetryDelaySeconds 30"
|
||||
Write-Host " powershell -NoProfile -ExecutionPolicy Bypass -File C:\Temp\install-choco-package.ps1 -PackageName doxygen.install -Version 1.9.8 -LogTailLines 1000"
|
||||
}
|
||||
|
||||
if ($Help -or ($ExtraArgs -contains '--help')) {
|
||||
Show-Usage
|
||||
exit 0
|
||||
}
|
||||
|
||||
if (-not $PackageName) {
|
||||
Write-Host "Error: -PackageName is required."
|
||||
Write-Host "Run with --help to see usage."
|
||||
exit 2
|
||||
}
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$chocoRoot = if ($env:ChocolateyInstall) { $env:ChocolateyInstall } else { 'C:\ProgramData\chocolatey' }
|
||||
$logPath = Join-Path $chocoRoot 'logs\chocolatey.log'
|
||||
$packageLibPath = Join-Path (Join-Path $chocoRoot 'lib') $PackageName
|
||||
$maxAttempts = $RetryCount + 1
|
||||
|
||||
$chocoArgs = @('install', '-y', $PackageName)
|
||||
if ($Version) {
|
||||
$chocoArgs += @('--version', $Version)
|
||||
}
|
||||
if ($InstallArgs) {
|
||||
$chocoArgs += @('--installargs', $InstallArgs)
|
||||
}
|
||||
|
||||
for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) {
|
||||
Write-Host "Installing Chocolatey package: $PackageName (attempt $attempt of $maxAttempts)"
|
||||
& choco @chocoArgs
|
||||
$exitCode = $LASTEXITCODE
|
||||
|
||||
if ($exitCode -eq 0) {
|
||||
Write-Host "Successfully installed Chocolatey package: $PackageName"
|
||||
exit 0
|
||||
}
|
||||
|
||||
Write-Host "Chocolatey install failed for package '$PackageName' (exit code $exitCode)."
|
||||
|
||||
if ($attempt -lt $maxAttempts) {
|
||||
Write-Host "Retrying in $RetryDelaySeconds seconds..."
|
||||
Start-Sleep -Seconds $RetryDelaySeconds
|
||||
}
|
||||
}
|
||||
|
||||
if ($LogTailLines -gt 0) {
|
||||
if (Test-Path $logPath) {
|
||||
Write-Host "Showing last $LogTailLines lines from $logPath"
|
||||
Get-Content $logPath -Tail $LogTailLines
|
||||
}
|
||||
else {
|
||||
Write-Host "Chocolatey log not found at $logPath"
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Host "Skipping Chocolatey log output because -LogTailLines is set to 0"
|
||||
}
|
||||
|
||||
if (Test-Path $packageLibPath) {
|
||||
Write-Host "Contents of $packageLibPath"
|
||||
Get-ChildItem -Path $packageLibPath -Force
|
||||
}
|
||||
|
||||
exit $exitCode
|
||||
176
.ci/docker/scripts/install-vcpkg-triplet.ps1
Normal file
176
.ci/docker/scripts/install-vcpkg-triplet.ps1
Normal file
@ -0,0 +1,176 @@
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ManifestDir,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Triplet,
|
||||
|
||||
[string]$VcpkgRoot = 'C:\vcpkg'
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$vcpkgExe = Join-Path $VcpkgRoot 'vcpkg.exe'
|
||||
if (-not (Test-Path -LiteralPath $vcpkgExe)) {
|
||||
Write-Error "vcpkg executable not found at: $vcpkgExe"
|
||||
exit 2
|
||||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $ManifestDir)) {
|
||||
Write-Error "Manifest directory not found: $ManifestDir"
|
||||
exit 2
|
||||
}
|
||||
|
||||
Write-Host "Running vcpkg install in: $ManifestDir"
|
||||
Write-Host "Triplet: $Triplet"
|
||||
|
||||
Push-Location $ManifestDir
|
||||
try {
|
||||
& $vcpkgExe install --triplet $Triplet --clean-after-build
|
||||
$vcpkgExitCode = $LASTEXITCODE
|
||||
}
|
||||
finally {
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
if ($vcpkgExitCode -eq 0) {
|
||||
exit 0
|
||||
}
|
||||
|
||||
Write-Host "##[error]vcpkg install failed with exit code: $vcpkgExitCode"
|
||||
|
||||
$buildtreesRoot = Join-Path $VcpkgRoot 'buildtrees'
|
||||
if (-not (Test-Path -LiteralPath $buildtreesRoot)) {
|
||||
Write-Host "No buildtrees directory found at $buildtreesRoot"
|
||||
exit $vcpkgExitCode
|
||||
}
|
||||
|
||||
function Write-LogIfExists {
|
||||
param([string]$Path)
|
||||
if (Test-Path -LiteralPath $Path) {
|
||||
$logInfo = Get-Item -LiteralPath $Path
|
||||
Write-Host "===== BEGIN LOG: $Path ($([int64]$logInfo.Length) bytes) ====="
|
||||
Get-Content -LiteralPath $Path -Raw
|
||||
Write-Host "===== END LOG: $Path ====="
|
||||
return $true
|
||||
}
|
||||
return $false
|
||||
}
|
||||
|
||||
function Write-LogFiles {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Directory,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Triplet
|
||||
)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $Directory)) {
|
||||
return
|
||||
}
|
||||
|
||||
function Get-LogPriority {
|
||||
param([System.IO.FileInfo]$File)
|
||||
|
||||
if ($File.Name -match '^config-') {
|
||||
return 0
|
||||
}
|
||||
if ($File.Name -match '^stdout-') {
|
||||
return 1
|
||||
}
|
||||
if ($File.Name -match '^stderr-') {
|
||||
return 2
|
||||
}
|
||||
return 3
|
||||
}
|
||||
|
||||
$priorityLogs = Get-ChildItem -LiteralPath $Directory -File -Filter "*${Triplet}*.log" -ErrorAction SilentlyContinue |
|
||||
Sort-Object @{ Expression = { Get-LogPriority -File $_ } },
|
||||
@{ Expression = { $_.LastWriteTimeUtc }; Descending = $true },
|
||||
Name
|
||||
|
||||
if (-not $priorityLogs) {
|
||||
$priorityLogs = Get-ChildItem -LiteralPath $Directory -File -Filter '*.log' -ErrorAction SilentlyContinue |
|
||||
Sort-Object @{ Expression = { Get-LogPriority -File $_ } },
|
||||
@{ Expression = { $_.LastWriteTimeUtc }; Descending = $true },
|
||||
Name
|
||||
}
|
||||
|
||||
if (-not $priorityLogs) {
|
||||
Write-Host "No log files found in $Directory"
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host "Dumping log files from $Directory"
|
||||
foreach ($log in $priorityLogs) {
|
||||
if ($log.Length -eq 0) {
|
||||
Write-Host "===== SKIP EMPTY LOG: $($log.FullName) ====="
|
||||
continue
|
||||
}
|
||||
|
||||
Write-Host "===== BEGIN LOG: $($log.FullName) ($([int64]$log.Length) bytes) ====="
|
||||
Get-Content -LiteralPath $log.FullName -Raw
|
||||
Write-Host "===== END LOG: $($log.FullName) ====="
|
||||
}
|
||||
}
|
||||
|
||||
# Use the most recently modified log of any type to identify the failing port.
|
||||
# This catches failures that occur before configure (fetch/extract/patch stage)
|
||||
# where no config-*-out.log is ever created.
|
||||
$candidate = Get-ChildItem -LiteralPath $buildtreesRoot -Recurse -File -Filter '*.log' -ErrorAction SilentlyContinue |
|
||||
Sort-Object LastWriteTimeUtc -Descending |
|
||||
Select-Object -First 1
|
||||
|
||||
# If the most recent log is not a config-*-out.log, also try to find the newest
|
||||
# config-*-out.log in the same port directory to seed the related-log dump,
|
||||
# since config logs contain the most actionable CMake failure details.
|
||||
if ($candidate) {
|
||||
$portDirFromRecent = $candidate.DirectoryName
|
||||
$configInSamePort = Get-ChildItem -LiteralPath $portDirFromRecent -File -Filter "config-$Triplet-*-out.log" -ErrorAction SilentlyContinue |
|
||||
Sort-Object LastWriteTimeUtc -Descending |
|
||||
Select-Object -First 1
|
||||
if ($configInSamePort) {
|
||||
$candidate = $configInSamePort
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $candidate) {
|
||||
Write-Host "No log files found under $buildtreesRoot"
|
||||
exit $vcpkgExitCode
|
||||
}
|
||||
|
||||
$portDir = $candidate.DirectoryName
|
||||
$portName = Split-Path -Leaf $portDir
|
||||
|
||||
Write-Host "Most recent failing dependency candidate: $portName"
|
||||
Write-Host "Selected log seed: $($candidate.FullName)"
|
||||
|
||||
$seedName = [System.IO.Path]::GetFileNameWithoutExtension($candidate.Name)
|
||||
if ($seedName.EndsWith('-out', [System.StringComparison]::OrdinalIgnoreCase)) {
|
||||
$seedPrefix = $seedName.Substring(0, $seedName.Length - 4)
|
||||
$relatedLogs = @(
|
||||
(Join-Path $portDir ($seedPrefix + '-CMakeCache.txt.log')),
|
||||
(Join-Path $portDir ($seedPrefix + '-CMakeConfigureLog.yaml.log')),
|
||||
(Join-Path $portDir ($seedPrefix + '-out.log'))
|
||||
)
|
||||
|
||||
foreach ($log in $relatedLogs) {
|
||||
[void](Write-LogIfExists -Path $log)
|
||||
}
|
||||
} else {
|
||||
[void](Write-LogIfExists -Path $candidate.FullName)
|
||||
}
|
||||
|
||||
Write-LogFiles -Directory $portDir -Triplet $Triplet
|
||||
|
||||
Write-Host 'Recent buildtrees logs (top 20 by timestamp):'
|
||||
Get-ChildItem -LiteralPath $buildtreesRoot -Recurse -File -Filter '*.log' -ErrorAction SilentlyContinue |
|
||||
Sort-Object LastWriteTimeUtc -Descending |
|
||||
Select-Object -First 20 FullName, LastWriteTimeUtc |
|
||||
Format-Table -AutoSize |
|
||||
Out-String |
|
||||
Write-Host
|
||||
|
||||
exit $vcpkgExitCode
|
||||
101
.ci/docker/scripts/install-vs-buildtools.ps1
Normal file
101
.ci/docker/scripts/install-vs-buildtools.ps1
Normal file
@ -0,0 +1,101 @@
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Url,
|
||||
|
||||
# Optional base temp directory for download and installer extraction.
|
||||
# A random subfolder is always created under this base path and cleaned up unless -PreserveTemp is set.
|
||||
[string]$TempDir = '',
|
||||
|
||||
# Preserve the generated temp working directory for debugging.
|
||||
[switch]$PreserveTemp,
|
||||
|
||||
# All remaining arguments are passed directly to vs_buildtools.exe.
|
||||
# Example: -InstallerArgs '--quiet','--wait','--norestart','--installPath','C:\VS','--add','Microsoft.VisualStudio.Workload.VCTools','--includeRecommended'
|
||||
[string[]]$InstallerArgs = @('--quiet', '--wait', '--norestart', '--add', 'Microsoft.VisualStudio.Workload.VCTools', '--includeRecommended')
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
# --- URL validation (mirrors check-url.ps1 logic) ---
|
||||
if ([string]::IsNullOrWhiteSpace($Url)) {
|
||||
throw "VS_BUILD_TOOLS_URL is empty. Provide a valid download URL."
|
||||
}
|
||||
|
||||
Write-Host "Validating VS Build Tools URL: $Url"
|
||||
$resp = Invoke-WebRequest -Uri $Url -UseBasicParsing -Method Head
|
||||
if ($resp.StatusCode -ne 200) {
|
||||
throw "URL check failed: StatusCode=$($resp.StatusCode)"
|
||||
}
|
||||
$ct = $resp.Headers['Content-Type']
|
||||
if ($ct -and $ct -match 'text/html') {
|
||||
throw "URL check failed: Content-Type=$ct (expected binary)"
|
||||
}
|
||||
$cl = [int64]($resp.Headers['Content-Length'] | Select-Object -First 1)
|
||||
if ($cl -and $cl -lt 1048576) {
|
||||
throw "URL check failed: Content-Length=$cl (suspiciously small)"
|
||||
}
|
||||
Write-Host "URL validation passed (Content-Type: $ct, Content-Length: $cl bytes)."
|
||||
|
||||
# --- Setup temp directory ---
|
||||
if (-not [string]::IsNullOrWhiteSpace($TempDir)) {
|
||||
$temp = Join-Path $TempDir ([System.IO.Path]::GetRandomFileName())
|
||||
} else {
|
||||
$temp = Join-Path $env:TEMP ([System.IO.Path]::GetRandomFileName())
|
||||
}
|
||||
Write-Host "Using temp directory: $temp"
|
||||
New-Item -ItemType Directory -Path $temp -Force | Out-Null
|
||||
$env:TEMP = $temp
|
||||
$env:TMP = $temp
|
||||
|
||||
# --- Download ---
|
||||
$exe = Join-Path $temp 'vs_buildtools.exe'
|
||||
Write-Host "Downloading VS Build Tools to $exe ..."
|
||||
Invoke-WebRequest $Url -OutFile $exe -UseBasicParsing
|
||||
Write-Host "Download complete: $exe"
|
||||
|
||||
# --- Install ---
|
||||
Push-Location $temp
|
||||
try {
|
||||
$fullCmd = ".\vs_buildtools.exe " + ($InstallerArgs -join ' ')
|
||||
Write-Host "Install command: $fullCmd"
|
||||
$p = Start-Process -FilePath '.\vs_buildtools.exe' -ArgumentList $InstallerArgs -Wait -PassThru
|
||||
$code = $p.ExitCode
|
||||
Write-Host "VS installer exit code: $code"
|
||||
|
||||
if ($code -ne 0 -and $code -ne 3010) {
|
||||
# Dump installer logs to stdout before cleanup
|
||||
Get-ChildItem -Path $env:TEMP -Recurse -File -Include 'dd_*', '*.log' -ErrorAction SilentlyContinue |
|
||||
Sort-Object LastWriteTime -Descending |
|
||||
Select-Object -First 20 -ExpandProperty FullName |
|
||||
ForEach-Object { Write-Host ('==== ' + $_ + ' ===='); Get-Content $_ -Tail 250 }
|
||||
|
||||
$b1 = Join-Path $env:ProgramData 'Microsoft\VisualStudio\Packages\_bootstrapper'
|
||||
if (Test-Path $b1) {
|
||||
Get-ChildItem -Path $b1 -Recurse -File -Filter '*.log' -ErrorAction SilentlyContinue |
|
||||
Sort-Object LastWriteTime -Descending |
|
||||
Select-Object -First 20 -ExpandProperty FullName |
|
||||
ForEach-Object { Write-Host ('==== ' + $_ + ' ===='); Get-Content $_ -Tail 250 }
|
||||
}
|
||||
|
||||
throw "Installer failed with exit code: $code"
|
||||
}
|
||||
}
|
||||
finally {
|
||||
Pop-Location
|
||||
if ($PreserveTemp) {
|
||||
Write-Host "Preserving temp directory: $temp"
|
||||
} else {
|
||||
Remove-Item -LiteralPath $temp -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
# Give Windows time to flush pending filesystem writes (e.g. msiexec deferred
|
||||
# operations) before Docker captures the layer. Without this, hcsshim::ImportLayer
|
||||
# can fail with ERROR_PATH_NOT_FOUND (0x3) due to a race condition between
|
||||
# NTFS lazy-write completion and the layer snapshot.
|
||||
# Runs unconditionally: even a failed install may have partial writes pending.
|
||||
Write-Host "Waiting 10 seconds for filesystem writes to flush..."
|
||||
Start-Sleep -Seconds 10
|
||||
Write-Host "Filesystem flush wait complete."
|
||||
}
|
||||
|
||||
Write-Host "VS Build Tools installed successfully."
|
||||
87
.ci/docker/uhd-builder-ubuntu2604.Dockerfile
Normal file
87
.ci/docker/uhd-builder-ubuntu2604.Dockerfile
Normal file
@ -0,0 +1,87 @@
|
||||
# please follow docker best practices
|
||||
# https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/
|
||||
|
||||
FROM ubuntu:26.04
|
||||
LABEL maintainer="Ettus Research"
|
||||
|
||||
ARG PIP_INDEX_HOST
|
||||
ARG PIP_INDEX_URL
|
||||
# This will make apt-get install without question
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get -y upgrade && \
|
||||
apt-get -y install -q \
|
||||
build-essential \
|
||||
ccache \
|
||||
clang \
|
||||
curl \
|
||||
git \
|
||||
sudo \
|
||||
# Install UHD dependencies
|
||||
abi-dumper \
|
||||
cmake \
|
||||
doxygen \
|
||||
dpdk \
|
||||
libboost-all-dev \
|
||||
libdpdk-dev \
|
||||
libgps-dev \
|
||||
libudev-dev \
|
||||
libusb-1.0-0-dev \
|
||||
ncompress \
|
||||
ninja-build \
|
||||
python3-dev \
|
||||
python3-docutils \
|
||||
python3-mako \
|
||||
python3-numpy \
|
||||
python3-pip \
|
||||
python3-requests \
|
||||
python3-setuptools \
|
||||
pybind11-dev \
|
||||
# gRPC baseline
|
||||
libgrpc++-dev \
|
||||
libprotobuf-dev \
|
||||
# gRPC protobuf compiler
|
||||
protobuf-compiler-grpc \
|
||||
# gRPC Python runtime
|
||||
python3-grpcio \
|
||||
# Install deb dependencies
|
||||
debootstrap \
|
||||
devscripts \
|
||||
pbuilder \
|
||||
debhelper \
|
||||
libncurses5-dev \
|
||||
python3-ruamel.yaml \
|
||||
# Install GNURadio dependencies
|
||||
python3-sphinx \
|
||||
python3-lxml \
|
||||
libsdl1.2-dev \
|
||||
libgsl-dev \
|
||||
libqwt-qt5-dev \
|
||||
libqt5opengl5-dev \
|
||||
libgmp3-dev \
|
||||
libfftw3-dev \
|
||||
gir1.2-gtk-3.0 \
|
||||
libpango1.0-dev \
|
||||
python3-pyqt5 \
|
||||
liblog4cpp5-dev \
|
||||
libzmq3-dev \
|
||||
python3-click \
|
||||
python3-click-plugins \
|
||||
python3-zmq \
|
||||
python3-scipy \
|
||||
python3-gi-cairo \
|
||||
python3-pygccxml \
|
||||
python3-jsonschema \
|
||||
libspdlog-dev \
|
||||
libsndfile1-dev \
|
||||
&& \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Optionally use cached index.
|
||||
RUN if [ -n "$PIP_INDEX_URL" ]; then \
|
||||
python3 -m pip config --global set global.index-url $PIP_INDEX_URL && \
|
||||
python3 -m pip config --global set global.trusted-host $PIP_INDEX_HOST; \
|
||||
fi
|
||||
|
||||
RUN python3 -m pip config list
|
||||
@ -1,53 +0,0 @@
|
||||
# Docker build context must be uhd/uhddev root
|
||||
|
||||
# please follow docker best practices
|
||||
# https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/
|
||||
|
||||
FROM mcr.microsoft.com/windows/servercore:ltsc2025
|
||||
LABEL maintainer="Ettus Research"
|
||||
|
||||
ARG PIP_INDEX_HOST=""
|
||||
ARG PIP_INDEX_URL=""
|
||||
ENV VCPKG_DISABLE_METRICS=1
|
||||
|
||||
RUN setx chocolateyVersion 1.4.0 /m
|
||||
RUN @"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" \
|
||||
-NoProfile -InputFormat None -ExecutionPolicy Bypass \
|
||||
-Command "[System.Net.ServicePointManager]::SecurityProtocol = 3072; \
|
||||
iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && \
|
||||
SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
|
||||
RUN choco install -y cmake.install --installargs 'ADD_CMAKE_TO_PATH=System' --version=3.22.3
|
||||
RUN choco install -y doxygen.install --version=1.9.8
|
||||
RUN choco install -y git
|
||||
RUN choco install -y NSIS --version=3.06.1
|
||||
RUN choco install -y vim
|
||||
RUN choco install -y python3 --version=3.10.11
|
||||
|
||||
# Optionally use cached index.
|
||||
RUN if defined PIP_INDEX_URL ( \
|
||||
pip config --global set global.index-url %PIP_INDEX_URL% && \
|
||||
pip config --global set global.trusted-host %PIP_INDEX_HOST% \
|
||||
)
|
||||
|
||||
RUN pip config list
|
||||
RUN python -m pip install --upgrade pip
|
||||
RUN pip install mako requests numpy ruamel.yaml grpcio
|
||||
|
||||
RUN powershell -NoProfile -ExecutionPolicy Bypass -Command \
|
||||
Invoke-WebRequest "https://aka.ms/vs/17/release/vs_buildtools.exe" \
|
||||
-OutFile "%TEMP%\vs_buildtools.exe" -UseBasicParsing
|
||||
RUN "%TEMP%\vs_buildtools.exe" --quiet --wait --norestart --noUpdateInstaller \
|
||||
--add Microsoft.VisualStudio.Workload.VCTools \
|
||||
--includeRecommended
|
||||
|
||||
RUN setx VCPKG_INSTALL_DIR "c:\\vcpkg" /m
|
||||
RUN git clone https://github.com/microsoft/vcpkg %VCPKG_INSTALL_DIR% && \
|
||||
cd %VCPKG_INSTALL_DIR% && \
|
||||
bootstrap-vcpkg.bat
|
||||
# Add custom UHD vcpkg triplet
|
||||
COPY host/cmake/vcpkg/* c:/vcpkg/triplets/
|
||||
RUN mkdir c:\\uhd-vcpkg
|
||||
COPY .ci\\docker\\vcpkg\\vcpkg.json c:/uhd-vcpkg/vcpkg.json
|
||||
RUN cd c:\\uhd-vcpkg && %VCPKG_INSTALL_DIR%\vcpkg.exe install \
|
||||
--triplet uhd-x64-windows-static-md \
|
||||
--clean-after-build
|
||||
@ -1,53 +0,0 @@
|
||||
# Docker build context must be uhd/uhddev root
|
||||
|
||||
# please follow docker best practices
|
||||
# https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/
|
||||
|
||||
FROM mcr.microsoft.com/windows/servercore:ltsc2025
|
||||
LABEL maintainer="Ettus Research"
|
||||
|
||||
ARG PIP_INDEX_HOST=""
|
||||
ARG PIP_INDEX_URL=""
|
||||
ENV VCPKG_DISABLE_METRICS=1
|
||||
|
||||
RUN setx chocolateyVersion 1.4.0 /m
|
||||
RUN @"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" \
|
||||
-NoProfile -InputFormat None -ExecutionPolicy Bypass \
|
||||
-Command "[System.Net.ServicePointManager]::SecurityProtocol = 3072; \
|
||||
iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && \
|
||||
SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
|
||||
RUN choco install -y cmake.install --installargs 'ADD_CMAKE_TO_PATH=System' --version=3.22.3
|
||||
RUN choco install -y doxygen.install --version=1.9.8
|
||||
RUN choco install -y git
|
||||
RUN choco install -y NSIS --version=3.06.1
|
||||
RUN choco install -y vim
|
||||
RUN choco install -y python3 --version=3.12.6
|
||||
|
||||
# Optionally use cached index.
|
||||
RUN if defined PIP_INDEX_URL ( \
|
||||
pip config --global set global.index-url %PIP_INDEX_URL% && \
|
||||
pip config --global set global.trusted-host %PIP_INDEX_HOST% \
|
||||
)
|
||||
|
||||
RUN pip config list
|
||||
RUN python -m pip install --upgrade pip
|
||||
RUN pip install mako requests numpy ruamel.yaml setuptools grpcio
|
||||
|
||||
RUN powershell -NoProfile -ExecutionPolicy Bypass -Command \
|
||||
Invoke-WebRequest "https://aka.ms/vs/17/release/vs_buildtools.exe" \
|
||||
-OutFile "%TEMP%\vs_buildtools.exe" -UseBasicParsing
|
||||
RUN "%TEMP%\vs_buildtools.exe" --quiet --wait --norestart --noUpdateInstaller \
|
||||
--add Microsoft.VisualStudio.Workload.VCTools \
|
||||
--includeRecommended
|
||||
|
||||
RUN setx VCPKG_INSTALL_DIR "c:\\vcpkg" /m
|
||||
RUN git clone https://github.com/microsoft/vcpkg %VCPKG_INSTALL_DIR% && \
|
||||
cd %VCPKG_INSTALL_DIR% && \
|
||||
bootstrap-vcpkg.bat
|
||||
# Add custom UHD vcpkg triplet
|
||||
COPY host/cmake/vcpkg/* c:/vcpkg/triplets/
|
||||
RUN mkdir c:\\uhd-vcpkg
|
||||
COPY .ci\\docker\\vcpkg\\vcpkg.json c:/uhd-vcpkg/vcpkg.json
|
||||
RUN cd c:\\uhd-vcpkg && %VCPKG_INSTALL_DIR%\vcpkg.exe install \
|
||||
--triplet uhd-x64-windows-static-md \
|
||||
--clean-after-build
|
||||
96
.ci/docker/uhd-builder-vs2022-v143-x64.Dockerfile
Normal file
96
.ci/docker/uhd-builder-vs2022-v143-x64.Dockerfile
Normal file
@ -0,0 +1,96 @@
|
||||
# Docker build context must be uhd/uhddev root
|
||||
|
||||
# please follow docker best practices
|
||||
# https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/
|
||||
|
||||
# Defaults for variables used in FROM statement
|
||||
ARG WIN_BASE_TAG=ltsc2025
|
||||
|
||||
FROM mcr.microsoft.com/windows/servercore:${WIN_BASE_TAG}
|
||||
LABEL maintainer="Ettus Research"
|
||||
|
||||
# Minimum required argument
|
||||
ARG VS_BUILD_TOOLS_URL
|
||||
# Optional arguments for customization
|
||||
ARG PYTHON_VERSION=3.10.11
|
||||
ARG VCPKG_MANIFEST_FILE=vcpkg.json
|
||||
ARG PIP_INDEX_HOST=""
|
||||
ARG PIP_INDEX_URL=""
|
||||
ENV VCPKG_DISABLE_METRICS=1
|
||||
# Optional argument for vcpkg binary cache directory
|
||||
ARG VCPKG_BINARY_CACHE_DIR="C:\vcpkg-cache"
|
||||
ENV VCPKG_BINARY_SOURCES=clear;files,${VCPKG_BINARY_CACHE_DIR},readwrite
|
||||
|
||||
# Enable long file paths (>260 characters)
|
||||
RUN reg add HKLM\SYSTEM\CurrentControlSet\Control\FileSystem /v LongPathsEnabled /t REG_DWORD /d 1 /f
|
||||
|
||||
# Install vs build tools
|
||||
COPY .ci/docker/scripts/install-vs-buildtools.ps1 C:/Temp/install-vs-buildtools.ps1
|
||||
RUN powershell -NoProfile -ExecutionPolicy Bypass -Command \
|
||||
"& 'C:\Temp\install-vs-buildtools.ps1' \
|
||||
-Url '%VS_BUILD_TOOLS_URL%' \
|
||||
-InstallerArgs '--quiet','--wait','--norestart','--nocache', \
|
||||
'--add','Microsoft.VisualStudio.Workload.VCTools', \
|
||||
'--includeRecommended'"
|
||||
|
||||
RUN setx chocolateyVersion 1.4.0 /m
|
||||
RUN @"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" \
|
||||
-NoProfile -InputFormat None -ExecutionPolicy Bypass \
|
||||
-Command "[System.Net.ServicePointManager]::SecurityProtocol = 3072; \
|
||||
iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && \
|
||||
SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin" && \
|
||||
choco config set webRequestTimeoutSeconds 600
|
||||
COPY .ci/docker/scripts/install-choco-package.ps1 C:/Temp/install-choco-package.ps1
|
||||
RUN powershell -NoProfile -ExecutionPolicy Bypass -File C:\Temp\install-choco-package.ps1 \
|
||||
-PackageName doxygen.install -Version 1.9.8 && \
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File C:\Temp\install-choco-package.ps1 \
|
||||
-PackageName cmake.install -Version 3.22.3 -InstallArgs ADD_CMAKE_TO_PATH=System && \
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File C:\Temp\install-choco-package.ps1 \
|
||||
-PackageName git && \
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File C:\Temp\install-choco-package.ps1 \
|
||||
-PackageName NSIS -Version 3.06.1 && \
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File C:\Temp\install-choco-package.ps1 \
|
||||
-PackageName vim && \
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File C:\Temp\install-choco-package.ps1 \
|
||||
-PackageName python3 -Version %PYTHON_VERSION%
|
||||
|
||||
# Optionally use cached index.
|
||||
RUN if defined PIP_INDEX_URL ( \
|
||||
pip config --global set global.index-url %PIP_INDEX_URL% && \
|
||||
pip config --global set global.trusted-host %PIP_INDEX_HOST% \
|
||||
)
|
||||
|
||||
COPY host/python/requirements.txt C:/Temp/requirements.txt
|
||||
COPY host/python/requirements_np2.txt C:/Temp/requirements_np2.txt
|
||||
RUN pip config list && \
|
||||
python -m pip install --upgrade pip && \
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -Command \
|
||||
"if ([version]('%PYTHON_VERSION%') -ge [version]('3.13')) { \
|
||||
pip install -r C:/Temp/requirements_np2.txt \
|
||||
} else { \
|
||||
pip install -r C:/Temp/requirements.txt \
|
||||
}; \
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }"
|
||||
|
||||
RUN setx VCPKG_INSTALL_DIR "c:\\vcpkg" /m
|
||||
RUN git clone https://github.com/microsoft/vcpkg %VCPKG_INSTALL_DIR% && \
|
||||
cd %VCPKG_INSTALL_DIR% && \
|
||||
bootstrap-vcpkg.bat
|
||||
# Add custom UHD vcpkg triplet
|
||||
COPY host/cmake/vcpkg/* c:/vcpkg/triplets/
|
||||
# Define triplet to be used by this container
|
||||
RUN setx VCPKG_TARGET_TRIPLET "uhd-x64-windows-static-md" /m
|
||||
RUN echo set(VCPKG_BUILD_TYPE release)>>"%VCPKG_INSTALL_DIR%\triplets\%VCPKG_TARGET_TRIPLET%.cmake"
|
||||
RUN type "%VCPKG_INSTALL_DIR%\triplets\%VCPKG_TARGET_TRIPLET%.cmake"
|
||||
# Copy vcpkg cache from mapped folder to
|
||||
RUN echo Current path %cd%
|
||||
COPY .cache/vcpkg ${VCPKG_BINARY_CACHE_DIR}
|
||||
|
||||
RUN powershell -NoProfile -ExecutionPolicy Bypass -Command \
|
||||
"New-Item -ItemType Directory -Path 'c:\uhd-vcpkg' -Force | Out-Null"
|
||||
COPY .ci/docker/vcpkg/${VCPKG_MANIFEST_FILE} c:/uhd-vcpkg/vcpkg.json
|
||||
COPY .ci/docker/scripts/install-vcpkg-triplet.ps1 C:/Temp/install-vcpkg-triplet.ps1
|
||||
RUN powershell -NoProfile -ExecutionPolicy Bypass -File C:\Temp\install-vcpkg-triplet.ps1 \
|
||||
-ManifestDir C:\uhd-vcpkg \
|
||||
-Triplet %VCPKG_TARGET_TRIPLET% \
|
||||
-VcpkgRoot %VCPKG_INSTALL_DIR%
|
||||
@ -17,32 +17,42 @@ ARG VCPKG_MANIFEST_FILE=vcpkg-vs2026.json
|
||||
ARG PIP_INDEX_HOST=""
|
||||
ARG PIP_INDEX_URL=""
|
||||
ENV VCPKG_DISABLE_METRICS=1
|
||||
# Optional argument for vcpkg binary cache directory
|
||||
ARG VCPKG_BINARY_CACHE_DIR="C:\vcpkg-cache"
|
||||
ENV VCPKG_BINARY_SOURCES=clear;files,${VCPKG_BINARY_CACHE_DIR},readwrite
|
||||
|
||||
# Enable long file paths (>260 characters)
|
||||
RUN reg add HKLM\SYSTEM\CurrentControlSet\Control\FileSystem /v LongPathsEnabled /t REG_DWORD /d 1 /f
|
||||
|
||||
# Install vs build tools
|
||||
COPY .ci/docker/scripts/install-vs-buildtools.ps1 C:/Temp/install-vs-buildtools.ps1
|
||||
RUN powershell -NoProfile -ExecutionPolicy Bypass -Command \
|
||||
"& 'C:\Temp\install-vs-buildtools.ps1' \
|
||||
-Url '%VS_BUILD_TOOLS_URL%' \
|
||||
-InstallerArgs '--quiet','--wait','--norestart','--nocache', \
|
||||
'--add','Microsoft.VisualStudio.Workload.VCTools', \
|
||||
'--includeRecommended'"
|
||||
|
||||
RUN setx chocolateyVersion 2.5.1 /m
|
||||
RUN @"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" \
|
||||
-NoProfile -InputFormat None -ExecutionPolicy Bypass \
|
||||
-Command "[System.Net.ServicePointManager]::SecurityProtocol = 3072; \
|
||||
iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && \
|
||||
SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
|
||||
RUN choco install -y cmake.install --installargs 'ADD_CMAKE_TO_PATH=System' --version=4.2.1
|
||||
RUN choco install -y doxygen.install --version=1.9.8
|
||||
RUN choco install -y git
|
||||
RUN choco install -y NSIS --version=3.11.0
|
||||
RUN choco install -y vim
|
||||
RUN choco install -y python3 --version=%PYTHON_VERSION%
|
||||
|
||||
COPY .ci/docker/scripts/check-url.ps1 C:/Temp/check-url.ps1
|
||||
RUN powershell -NoProfile -ExecutionPolicy Bypass -File C:\Temp\check-url.ps1 \
|
||||
-Url "%VS_BUILD_TOOLS_URL%"
|
||||
RUN powershell -NoProfile -ExecutionPolicy Bypass -Command \
|
||||
Invoke-WebRequest "%VS_BUILD_TOOLS_URL%" \
|
||||
-OutFile "%TEMP%\vs_buildtools.exe" -UseBasicParsing
|
||||
RUN "%TEMP%\vs_buildtools.exe" --quiet --wait --norestart --noUpdateInstaller \
|
||||
--add Microsoft.VisualStudio.Workload.VCTools \
|
||||
--includeRecommended
|
||||
SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin" && \
|
||||
choco config set webRequestTimeoutSeconds 600
|
||||
COPY .ci/docker/scripts/install-choco-package.ps1 C:/Temp/install-choco-package.ps1
|
||||
RUN powershell -NoProfile -ExecutionPolicy Bypass -File C:\Temp\install-choco-package.ps1 \
|
||||
-PackageName doxygen.install -Version 1.9.8 && \
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File C:\Temp\install-choco-package.ps1 \
|
||||
-PackageName cmake.install -Version 4.2.1 -InstallArgs ADD_CMAKE_TO_PATH=System && \
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File C:\Temp\install-choco-package.ps1 \
|
||||
-PackageName git && \
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File C:\Temp\install-choco-package.ps1 \
|
||||
-PackageName NSIS -Version 3.11.0 && \
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File C:\Temp\install-choco-package.ps1 \
|
||||
-PackageName vim && \
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File C:\Temp\install-choco-package.ps1 \
|
||||
-PackageName python3 -Version %PYTHON_VERSION%
|
||||
|
||||
# Optionally use cached index.
|
||||
RUN if defined PIP_INDEX_URL ( \
|
||||
@ -51,18 +61,36 @@ RUN if defined PIP_INDEX_URL ( \
|
||||
)
|
||||
|
||||
COPY host/python/requirements.txt C:/Temp/requirements.txt
|
||||
RUN pip config list
|
||||
RUN python -m pip install --upgrade pip
|
||||
RUN pip install -r C:/Temp/requirements.txt
|
||||
COPY host/python/requirements_np2.txt C:/Temp/requirements_np2.txt
|
||||
RUN pip config list && \
|
||||
python -m pip install --upgrade pip && \
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -Command \
|
||||
"if ([version]('%PYTHON_VERSION%') -ge [version]('3.13')) { \
|
||||
pip install -r C:/Temp/requirements_np2.txt \
|
||||
} else { \
|
||||
pip install -r C:/Temp/requirements.txt \
|
||||
}; \
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }"
|
||||
|
||||
RUN setx VCPKG_INSTALL_DIR "c:\\vcpkg" /m
|
||||
RUN git clone https://github.com/microsoft/vcpkg %VCPKG_INSTALL_DIR% && \
|
||||
cd %VCPKG_INSTALL_DIR% && \
|
||||
bootstrap-vcpkg.bat
|
||||
# Add custom UHD vcpkg triplet
|
||||
# Add custom UHD vcpkg triplet
|
||||
COPY host/cmake/vcpkg/* c:/vcpkg/triplets/
|
||||
RUN mkdir c:\\uhd-vcpkg
|
||||
# Define triplet to be used by this container
|
||||
RUN setx VCPKG_TARGET_TRIPLET "uhd-x64-windows-static-md" /m
|
||||
RUN echo set(VCPKG_BUILD_TYPE release)>>"%VCPKG_INSTALL_DIR%\triplets\%VCPKG_TARGET_TRIPLET%.cmake"
|
||||
RUN type "%VCPKG_INSTALL_DIR%\triplets\%VCPKG_TARGET_TRIPLET%.cmake"
|
||||
# Copy vcpkg cache from mapped folder to
|
||||
RUN echo Current path %cd%
|
||||
COPY .cache/vcpkg ${VCPKG_BINARY_CACHE_DIR}
|
||||
|
||||
RUN powershell -NoProfile -ExecutionPolicy Bypass -Command \
|
||||
"New-Item -ItemType Directory -Path 'c:\uhd-vcpkg' -Force | Out-Null"
|
||||
COPY .ci/docker/vcpkg/${VCPKG_MANIFEST_FILE} c:/uhd-vcpkg/vcpkg.json
|
||||
RUN cd c:\\uhd-vcpkg && %VCPKG_INSTALL_DIR%\vcpkg.exe install \
|
||||
--triplet uhd-x64-windows-static-md \
|
||||
--clean-after-build
|
||||
COPY .ci/docker/scripts/install-vcpkg-triplet.ps1 C:/Temp/install-vcpkg-triplet.ps1
|
||||
RUN powershell -NoProfile -ExecutionPolicy Bypass -File C:\Temp\install-vcpkg-triplet.ps1 \
|
||||
-ManifestDir C:\uhd-vcpkg \
|
||||
-Triplet %VCPKG_TARGET_TRIPLET% \
|
||||
-VcpkgRoot %VCPKG_INSTALL_DIR%
|
||||
|
||||
108
.ci/fpga/devices/job-build-fpga-b310.yml
Normal file
108
.ci/fpga/devices/job-build-fpga-b310.yml
Normal file
@ -0,0 +1,108 @@
|
||||
#
|
||||
# Copyright 2025 Ettus Research, a National Instruments Brand
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
#
|
||||
# Description:
|
||||
#
|
||||
# This template defines a stages-based pipeline for building B310 FPGA
|
||||
# targets.
|
||||
#
|
||||
# See https://aka.ms/yaml for pipeline YAML documentation.
|
||||
#
|
||||
|
||||
parameters:
|
||||
## Optional parameters
|
||||
# Option to ignore cached artifacts (if available) and perform
|
||||
# a clean IP build.
|
||||
- name: clean_ip_build
|
||||
type: boolean
|
||||
default: false
|
||||
# Number of parallel jobs to use for IP build
|
||||
- name: num_ip_jobs
|
||||
type: number
|
||||
default: 5
|
||||
# Option to publish intermediate files
|
||||
- name: publish_int_files
|
||||
type: boolean
|
||||
default: false
|
||||
# Package and publish images
|
||||
- name: package_and_publish_images
|
||||
type: boolean
|
||||
default: true
|
||||
# Package access in the SDR server ('Internal' or 'Public')
|
||||
- name: package_access
|
||||
type: string
|
||||
default: 'Internal'
|
||||
# From where to pull FPGA bitfiles if we're not building them
|
||||
- name: fpga_imgs_source
|
||||
type: string
|
||||
default: 'Mono Pipeline'
|
||||
# B310 FPGA targets to build
|
||||
- name: b310_full_targets_matrix
|
||||
type: object
|
||||
default:
|
||||
B310_REPLAY:
|
||||
image_core: b310_replay_rfnoc_image_core.yml
|
||||
target_name: B310
|
||||
image_core_name: usrp_b310_fpga_replay
|
||||
artifact_name: B310_REPLAY
|
||||
timeout: 720
|
||||
max_attempts: 2
|
||||
B310_FIFO:
|
||||
image_core: b310_fifo_rfnoc_image_core.yml
|
||||
target_name: B310
|
||||
image_core_name: usrp_b310_fpga_fifo
|
||||
artifact_name: B310_FIFO
|
||||
timeout: 720
|
||||
max_attempts: 2
|
||||
- name: b310_reduced_targets_matrix
|
||||
type: object
|
||||
default:
|
||||
B310_REPLAY:
|
||||
image_core: b310_replay_rfnoc_image_core.yml
|
||||
target_name: B310
|
||||
image_core_name: usrp_b310_fpga_replay
|
||||
artifact_name: B310_REPLAY
|
||||
timeout: 720
|
||||
max_attempts: 2
|
||||
B310_FIFO:
|
||||
image_core: b310_fifo_rfnoc_image_core.yml
|
||||
target_name: B310
|
||||
image_core_name: usrp_b310_fpga_fifo
|
||||
artifact_name: B310_FIFO
|
||||
timeout: 720
|
||||
max_attempts: 2
|
||||
|
||||
|
||||
stages:
|
||||
|
||||
### START: B310 stages
|
||||
|
||||
- stage: provide_b310_fpga_images_stage
|
||||
displayName: Provide B310 FPGA Images
|
||||
dependsOn: analyze_changeset
|
||||
variables:
|
||||
UhdTestList: $[stageDependencies.analyze_changeset.analyze.outputs['gen_testlist.UhdTestList']]
|
||||
BuildRequired: $[or(
|
||||
contains(stageDependencies.analyze_changeset.analyze.outputs['gen_testlist.UhdTestList'], 'fpga.usrp3.b310'),
|
||||
contains(stageDependencies.analyze_changeset.analyze.outputs['gen_testlist.UhdTestList'], 'fpga.usrp3.all')
|
||||
)]
|
||||
jobs:
|
||||
- template: ../templates/jobs-provide-fpga-artifacts.yml
|
||||
parameters:
|
||||
id: b310
|
||||
device_name: B310
|
||||
ip_target: B310_IP
|
||||
package_name: b3xx_b310_fpga_default
|
||||
clean_ip_build: ${{ parameters.clean_ip_build }}
|
||||
num_ip_jobs: ${{ parameters.num_ip_jobs }}
|
||||
publish_int_files: ${{ parameters.publish_int_files }}
|
||||
package_and_publish_images: ${{ parameters.package_and_publish_images }}
|
||||
package_access: ${{ parameters.package_access }}
|
||||
fpga_imgs_source: ${{ parameters.fpga_imgs_source }}
|
||||
top_dir: uhddev/fpga/usrp3/top/b300
|
||||
target_full_matrix: ${{ parameters.b310_full_targets_matrix }}
|
||||
target_reduced_matrix: ${{ parameters.b310_reduced_targets_matrix }}
|
||||
|
||||
### END: B310 stages
|
||||
@ -33,7 +33,7 @@ echo "---- Run hwsetup ----"
|
||||
# variables based on the agent's configuration so we can find the EDA
|
||||
# tools.
|
||||
pushd $BUILD_SOURCESDIRECTORY/uhddev/.ci/hwtools
|
||||
source hwsetup.sh --hwtools=1.5.2
|
||||
source hwsetup.sh
|
||||
popd
|
||||
|
||||
echo "---- Fetch the FPGA simulation libraries ----"
|
||||
@ -52,36 +52,12 @@ python $(dirname $BASH_SOURCE)/merge_modelsim_ini.py \
|
||||
# Removing the temporary files
|
||||
rm -f modelsim.mpf compileFpgaLib.log
|
||||
|
||||
echo "---- Fetch Vivado patches ----"
|
||||
export PATCHES_DIR=$BUILD_SOURCESDIRECTORY/patches
|
||||
mkdir -p $PATCHES_DIR
|
||||
pushd $PATCHES_DIR
|
||||
# Fetch the AR76780 patch for Vivado
|
||||
cp /opt/synced/Xilinx/patches/AR76780_Vivado_2021_1_preliminary_rev1.zip ./AR76780_Vivado_2021_1_preliminary_rev1.zip
|
||||
unzip -q -o ./AR76780_Vivado_2021_1_preliminary_rev1.zip -d ./AR76780_Vivado_2021/
|
||||
rm -f AR76780_Vivado_2021_1_preliminary_rev1.zip
|
||||
# Add the AR76780 patch for the simulation library
|
||||
cp /opt/synced/modelsim/patches/fir_compiler_v7_2_76780.zip ./fir_compiler_v7_2_76780.zip
|
||||
unzip -q -o ./fir_compiler_v7_2_76780.zip -d ./fir_compiler_v7_2_76780/
|
||||
rm -f ./fir_compiler_v7_2_76780.zip
|
||||
popd
|
||||
|
||||
echo "---- Apply the Vivado patches ----"
|
||||
pushd $BUILD_SOURCESDIRECTORY
|
||||
# Apply the patch to Vivado
|
||||
export XILINX_PATH=$PATCHES_DIR/AR76780_Vivado_2021/vivado
|
||||
# Add the FIR compiler simulation model to local modelsim.ini
|
||||
# Delete all lines containing "fir_compiler_v7_2_76780" to avoid duplicates
|
||||
sed -i -e '/fir_compiler_v7_2_76780/d' ./modelsim.ini
|
||||
# Duplicate line containing "fir_compiler_v7_2_16"
|
||||
sed -i -e '/fir_compiler_v7_2_16/p' ./modelsim.ini
|
||||
# Replace the first "fir_compiler_v7_2_16" line with our new library
|
||||
PATCHES_DIR_ESC=$(echo $PATCHES_DIR | sed 's_/_\\/_g')
|
||||
sed -i -e "0,/fir_compiler_v7_2_16/s/^fir_compiler_v7_2_16.*\$/fir_compiler_v7_2_76780 = $PATCHES_DIR_ESC\/fir_compiler_v7_2_76780/" ./modelsim.ini
|
||||
echo "---- Configure modelsim paths ----"
|
||||
# Tell our build tools to use this modelsim.ini for this session
|
||||
export MODELSIM_INI=$(pwd)/modelsim.ini
|
||||
export MSIM_VIV_COMPLIBDIR=$(pwd)
|
||||
popd
|
||||
export MODELSIM_INI=$BUILD_SOURCESDIRECTORY/modelsim.ini
|
||||
export MSIM_VIV_COMPLIBDIR=$BUILD_SOURCESDIRECTORY
|
||||
|
||||
echo "---- Run setupenv ----"
|
||||
source $1/setupenv.sh --vivado-path $(dirname $XILINX_VIVADO) --modelsim-path $(dirname $MODELSIM)
|
||||
# update XILINX_VIVADO to the parent directory
|
||||
echo "Sourcing $1/setupenv.sh with Vivado path: $XILINX_VIVADO and Modelsim path: $(dirname $MODELSIM)"
|
||||
source $1/setupenv.sh --vivado-path $XILINX_VIVADO --modelsim-path $(dirname $MODELSIM)
|
||||
|
||||
@ -127,3 +127,15 @@ stages:
|
||||
rfnoc_include_dir: ${{ parameters.rfnoc_include_dir }}
|
||||
|
||||
### END: E320 stages
|
||||
|
||||
### START: B310 stages
|
||||
|
||||
- template: ../devices/job-build-fpga-b310.yml
|
||||
parameters:
|
||||
clean_ip_build: ${{ parameters.clean_ip_build }}
|
||||
publish_int_files: ${{ parameters.publish_int_files }}
|
||||
package_and_publish_images: ${{ parameters.package_and_publish_images }}
|
||||
package_access: ${{ parameters.package_access }}
|
||||
fpga_imgs_source: ${{ parameters.fpga_imgs_source }}
|
||||
|
||||
### END: B310 stages
|
||||
|
||||
@ -59,7 +59,7 @@ jobs:
|
||||
name: Drivers-NIBuildFarm-RFMIBUILD
|
||||
demands:
|
||||
- Agent.OS -equals Linux
|
||||
- HIGH_CPU_PER_THREAD
|
||||
- SUPER_HIGH_CPU_PER_THREAD
|
||||
variables:
|
||||
- group: sdr-pipeline-vars
|
||||
strategy:
|
||||
@ -91,6 +91,7 @@ jobs:
|
||||
# twice.
|
||||
echo "##[group]Setting up build environment..."
|
||||
source $(Build.SourcesDirectory)/uhddev/.ci/fpga/scripts/run_setup.sh ./
|
||||
python3 -m pip install --upgrade pip
|
||||
pip3 install -U numpy mako "ruamel.yaml<0.18.0" jsonschema
|
||||
set -o xtrace
|
||||
# hwsetup.sh injects garbage cmake versions into PATH, so avoid that by
|
||||
@ -131,7 +132,7 @@ jobs:
|
||||
python3 ../../tools/utils/repeat_fpga_build.py \
|
||||
--image-core $(image_core) $TARGET_ARG $BITFILE_NAME_ARG $INCLUDE_DIR_ARG \
|
||||
--image-core-name $IMAGE_CORE_NAME \
|
||||
--vivado-path $(dirname $XILINX_VIVADO) \
|
||||
--vivado-path $XILINX_VIVADO \
|
||||
--num $(max_attempts) --seed $BUILD_SEED --persistent \
|
||||
$TIMEOUT_ARG
|
||||
pip3 uninstall -y uhd
|
||||
|
||||
@ -55,12 +55,10 @@ jobs:
|
||||
|
||||
# Pipeline caching is leveraged in this job to minimize the time it takes
|
||||
# to build all the IP.
|
||||
# At a high level, the pipeline checks for an available and valid cache
|
||||
# artifact from its scope (target branch in PR or its own branch) and
|
||||
# downloads it to the workspace (Cache hit).
|
||||
# If no valid cache artifact exists (Cache miss), then the build will run
|
||||
# and the pipeline will automatically save the result as a cache artifact
|
||||
# for subsequent runs.
|
||||
# At a high level, the pipeline checks for the newest available cache
|
||||
# artifact for this IP target in its visible scopes and downloads it.
|
||||
# The per-run key forces a miss so restoreKeys selects the newest cache;
|
||||
# successful writable runs then save a new cache entry.
|
||||
# Further details on "Pipeline caching" available online:
|
||||
# https://docs.microsoft.com/en-us/azure/devops/pipelines/release/caching?view=azure-devops
|
||||
# We always allow the Cache task to run, even in clean builds. This way
|
||||
@ -68,7 +66,10 @@ jobs:
|
||||
# artifact for subsequent CI and PR runs to fetch.
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
key: "uhddev-fpga-${{ parameters.ip_target }}"
|
||||
key: |
|
||||
"uhddev-fpga" | "${{ parameters.ip_target }}" | "$(Build.BuildId)"
|
||||
restoreKeys: |
|
||||
"uhddev-fpga" | "${{ parameters.ip_target }}"
|
||||
path: ${{ parameters.directory }}/build-ip
|
||||
cacheHitVar: CACHE_RESTORED
|
||||
displayName: Cache IP
|
||||
@ -80,9 +81,8 @@ jobs:
|
||||
bash $(Build.SourcesDirectory)/uhddev/.ci/fpga/scripts/refresh_ip.sh \
|
||||
`realpath build-ip/` $BUILD_IP_CACHE_HASH
|
||||
workingDirectory: ${{ parameters.directory }}
|
||||
# Refresh IP when caching is allowed (clean_build = false) and a cache
|
||||
# artifact was fetched.
|
||||
condition: and(eq('${{ parameters.clean_build }}', 'false'), eq(variables.CACHE_RESTORED, 'true'))
|
||||
# Refresh IP when caching is allowed and an exact or fallback cache was fetched.
|
||||
condition: and(eq('${{ parameters.clean_build }}', 'false'), or(eq(variables.CACHE_RESTORED, 'true'), eq(variables.CACHE_RESTORED, 'inexact')))
|
||||
displayName: "Refresh IP"
|
||||
|
||||
- bash: |
|
||||
|
||||
@ -36,6 +36,7 @@ jobs:
|
||||
|
||||
echo "---- Run testbenches ----"
|
||||
pushd ./tools/utils
|
||||
python3 -m pip install --upgrade pip
|
||||
pip3 install -r requirements.txt
|
||||
python3 ./run_testbenches.py --logged --simulator ${{ parameters.simulator }} --excludes=${{ variables.excludes_file }}.excludes -j4 --report $(Build.ArtifactStagingDirectory)/junit.xml run
|
||||
popd
|
||||
|
||||
@ -7,11 +7,13 @@
|
||||
|
||||
from hwtools.api import vapi
|
||||
|
||||
|
||||
def do_options():
|
||||
vapi.set_project_name(name="X410_FPGA", pretty_name="USRP X410 FPGA")
|
||||
vapi.set_top_sim_entity("x4xx")
|
||||
vapi.set_top_synth_entity("x4xx")
|
||||
|
||||
|
||||
def do_files():
|
||||
# x400 related files
|
||||
vapi.find_files_and_add_to_fileset(base="repo", path="fpga/usrp3/top/x400", tree=False)
|
||||
@ -23,17 +25,28 @@ def do_files():
|
||||
vapi.find_files_and_add_to_fileset(base="repo", path="fpga/usrp3/top/x400/rf/100m")
|
||||
vapi.find_files_and_add_to_fileset(base="repo", path="fpga/usrp3/top/x400/rf/200m")
|
||||
vapi.find_files_and_add_to_fileset(base="repo", path="fpga/usrp3/top/x400/rf/400m")
|
||||
vapi.find_files_and_add_to_fileset(base="repo", path="fpga/usrp3/top/x400/ip/x4xx_ps_rfdc_bd/common/regmap")
|
||||
vapi.find_files_and_add_to_fileset(base="repo", path="fpga/usrp3/top/x400/ip/x4xx_ps_rfdc_bd/x410_ps_rfdc_bd/regmap")
|
||||
vapi.find_files_and_add_to_fileset(
|
||||
base="repo", path="fpga/usrp3/top/x400/ip/x4xx_ps_rfdc_bd/common/regmap"
|
||||
)
|
||||
vapi.find_files_and_add_to_fileset(
|
||||
base="repo", path="fpga/usrp3/top/x400/ip/x4xx_ps_rfdc_bd/x410_ps_rfdc_bd/regmap"
|
||||
)
|
||||
|
||||
# library
|
||||
vapi.find_files_and_add_to_fileset(base="repo", path="fpga/usrp3/lib/rfnoc")
|
||||
vapi.find_files_and_add_to_fileset(base="repo", path="fpga/usrp3/lib/control")
|
||||
|
||||
# regmap files
|
||||
vapi.find_files_and_add_to_fileset(base="repo", path="fpga/usrp3/top/x400/regmap", tree=False)
|
||||
vapi.find_files_and_add_to_fileset(base="repo", path="fpga/usrp3/top/x400/regmap/x410", tree=False)
|
||||
vapi.find_files_and_add_to_fileset(base="repo", path="fpga/usrp3/top/x400/cpld/regmap", tree=False)
|
||||
vapi.find_files_and_add_to_fileset(base="repo", path="fpga/usrp3/top/x400/cpld/regmap/x410", tree=False)
|
||||
vapi.find_files_and_add_to_fileset(
|
||||
base="repo", path="fpga/usrp3/top/x400/regmap/x410", tree=False
|
||||
)
|
||||
vapi.find_files_and_add_to_fileset(
|
||||
base="repo", path="fpga/usrp3/top/x400/cpld/regmap", tree=False
|
||||
)
|
||||
vapi.find_files_and_add_to_fileset(
|
||||
base="repo", path="fpga/usrp3/top/x400/cpld/regmap/x410", tree=False
|
||||
)
|
||||
|
||||
# simulation files
|
||||
vapi.find_files_and_add_to_fileset(base="repo", path="fpga/usrp3/top/x400/rf/sim")
|
||||
@ -44,17 +57,22 @@ def do_files():
|
||||
vapi.remove_from_fileset(".*lib/rfnoc/.*_tb")
|
||||
vapi.remove_from_fileset("pll-v")
|
||||
|
||||
|
||||
def do_preparse():
|
||||
pass
|
||||
|
||||
|
||||
def do_precompile():
|
||||
pass
|
||||
|
||||
|
||||
def do_presynth():
|
||||
pass
|
||||
|
||||
|
||||
def do_finish():
|
||||
vapi.run_user_function(function="update_xmlparse_files", base="self", path="../common.py")
|
||||
|
||||
|
||||
def vhook_warn_filter(msgstr):
|
||||
return False
|
||||
|
||||
@ -21,10 +21,10 @@ def do_setup():
|
||||
|
||||
# Note: The hwtools version for CI purposes is fixed in run_setup.sh
|
||||
rootapi.add_tool(name="hwtools", version="head")
|
||||
rootapi.add_tool(name="Vivado", version="2021.1", allowlater=False)
|
||||
rootapi.add_tool(name="modelsim", version="2020.4", allowlater=True)
|
||||
rootapi.add_tool(name="Diamond", version="3.12", allowlater=True)
|
||||
rootapi.add_tool(name="Quartus", version="20.1", allowlater=True)
|
||||
rootapi.add_tool(name="Vivado", version="2025.2")
|
||||
rootapi.add_tool(name="Questasim", version="2025.1")
|
||||
rootapi.add_tool(name="Diamond", version="3.12")
|
||||
rootapi.add_tool(name="Quartus", version="20.1")
|
||||
|
||||
|
||||
def do_finish():
|
||||
|
||||
@ -41,12 +41,16 @@ jobs:
|
||||
docker_image_matrix_windows_python_build=$(sed '9q;d' "$artifact_file")
|
||||
elif [ "$line_count" -eq 6 ]; then
|
||||
macos_line=5
|
||||
docker_image_matrix_lin=$(python3 "$derive_script" --input "$artifact_file" --type build --os linux)
|
||||
docker_image_matrix_win=$(python3 "$derive_script" --input "$artifact_file" --type build --os windows)
|
||||
docker_image_matrix_ubuntu_deb=$(python3 "$derive_script" --input "$artifact_file" --type installer --os linux --package deb --filter-out "focal,questing")
|
||||
docker_image_matrix_source_package=$(python3 "$derive_script" --input "$artifact_file" --type source --os linux --select noble)
|
||||
docker_image_matrix_fedora_rpm=$(python3 "$derive_script" --input "$artifact_file" --type installer --os linux --package rpm)
|
||||
docker_image_matrix_windows_python_build=$(python3 "$derive_script" --input "$artifact_file" --section windows-python-build-matrix)
|
||||
# Keep derive failures blocking for strict CI reasons (CI/scheduled),
|
||||
# but allow non-CI/manual debug runs to continue with '{}' fallback.
|
||||
# PR runs also use fallback mode.
|
||||
# The behavior is implemented by derive_docker_image_matrix.py.
|
||||
docker_image_matrix_lin=$(python3 "$derive_script" --empty-on-error-non-ci --input "$artifact_file" --type build --os linux)
|
||||
docker_image_matrix_win=$(python3 "$derive_script" --empty-on-error-non-ci --input "$artifact_file" --type build --os windows)
|
||||
docker_image_matrix_ubuntu_deb=$(python3 "$derive_script" --empty-on-error-non-ci --input "$artifact_file" --type installer --os linux --package deb --filter-out "focal,questing")
|
||||
docker_image_matrix_source_package=$(python3 "$derive_script" --empty-on-error-non-ci --input "$artifact_file" --type source --os linux --select noble)
|
||||
docker_image_matrix_fedora_rpm=$(python3 "$derive_script" --empty-on-error-non-ci --input "$artifact_file" --type installer --os linux --package rpm)
|
||||
docker_image_matrix_windows_python_build=$(python3 "$derive_script" --empty-on-error-non-ci --input "$artifact_file" --section windows-python-build-matrix)
|
||||
else
|
||||
echo "Error: Unexpected DockerImageNames format with $line_count lines; expected 6 or 9"
|
||||
exit 1
|
||||
|
||||
@ -44,6 +44,7 @@ jobs:
|
||||
# Init toolchain
|
||||
source $(Build.SourcesDirectory)/uhddev/.ci/fpga/scripts/run_setup.sh $(Build.SourcesDirectory)/uhddev/fpga/usrp3/top/x300
|
||||
pushd $(Build.SourcesDirectory)/uhddev/fpga/usrp3/tools/utils
|
||||
python3 -m pip install --upgrade pip
|
||||
pip3 install -r requirements.txt
|
||||
popd
|
||||
# Now run the test.
|
||||
|
||||
@ -42,7 +42,7 @@ jobs:
|
||||
timeoutInMinutes: 120
|
||||
variables:
|
||||
# Need to use list format to support inclusion of variable groups.
|
||||
# Variable group used by .ci/templates/steps-windows-sbom.yml@ettus-rts
|
||||
# Variable group used by .ci/templates/steps-windows-sbom.yml@ettus-rts
|
||||
- group: meta-ettus-vars
|
||||
# Variable group needed for code signing (signtool-p4-path)
|
||||
- ${{ if eq(parameters.toolset, 'msbuild') }}:
|
||||
@ -102,14 +102,16 @@ jobs:
|
||||
# Job uses template which causes self checkout to result in different values
|
||||
# for $(Build.SourcesDirectory) due to single vs multiple repositories use.
|
||||
# Switching to use manual path definition $(Pipeline.Workspace)/s/<RepoName>
|
||||
# for consistent behavior to avoid
|
||||
# for consistent behavior to avoid
|
||||
# $(Pipeline.Workspace)/s vs $(Pipeline.Workspace)/s/<RepoName>).
|
||||
# Custom path now includes <RepoName>, which is now hardcoded, rather than
|
||||
# Custom path now includes <RepoName>, which is now hardcoded, rather than
|
||||
# use name from git repo (avoids problem when building from public uhd repo).
|
||||
# Checkout path needs to be defined relative to $(Pipeline.Workspace).
|
||||
- checkout: self
|
||||
clean: true
|
||||
path: 's/uhddev'
|
||||
retryCountOnTaskFailure: 3
|
||||
timeoutInMinutes: 10
|
||||
# Need to use OS specific steps because predefined variable
|
||||
# (i.e. $(Pipeline.Workspace) on Linux is treated as runtime shell expansion,
|
||||
# but actually handled by Azure and the quotes ensure correct, delayed variable
|
||||
@ -128,6 +130,7 @@ jobs:
|
||||
- download: uhd_build_docker_container
|
||||
- template: .ci/templates/steps-setup.yml@ettus-rts
|
||||
- ${{ if eq(parameters.toolset, 'fedora_rpm') }}:
|
||||
- template: steps-install-nib310rio-dev.yml
|
||||
- template: steps-build-uhd-installer-fedora-rpm.yml
|
||||
parameters:
|
||||
uhdSrcDir: $(uhdSrcDir)
|
||||
@ -139,7 +142,7 @@ jobs:
|
||||
- ${{ if parameters.create_sbom }}:
|
||||
- template: .ci/templates/steps-linux-sbom.yml@ettus-rts
|
||||
parameters:
|
||||
toolset: ${{ parameters.toolset }}
|
||||
toolset: ${{ parameters.toolset }}
|
||||
installer: ${{ parameters.installer }}
|
||||
sbomComponentList: 'uhd libuhd libboost libusb'
|
||||
buildOSName: $(buildOSName)
|
||||
@ -148,16 +151,18 @@ jobs:
|
||||
uhdInstallerDir: $(Build.BinariesDirectory)/uhddev-installer
|
||||
releaseBinaries: ${{ parameters.releaseBinaries }}
|
||||
- ${{ if eq(parameters.toolset, 'ubuntu_deb') }}:
|
||||
- template: steps-install-nib310rio-dev.yml
|
||||
- template: steps-build-uhd-installer-ubuntu-deb.yml
|
||||
parameters:
|
||||
uhdSrcDir: $(uhdSrcDir)
|
||||
uhdInstallerDir: $(Build.BinariesDirectory)/uhddev-installer
|
||||
ubuntuReleaseName: $(ubuntuReleaseName)
|
||||
nib310rioDebPackageDir: $(Pipeline.Workspace)/nib310rio_kernel_packages/nib310rio-deb-packages
|
||||
cmake_args: ${{ parameters.cmake_args }}
|
||||
- ${{ if parameters.create_sbom }}:
|
||||
- template: .ci/templates/steps-linux-sbom.yml@ettus-rts
|
||||
parameters:
|
||||
toolset: ${{ parameters.toolset }}
|
||||
toolset: ${{ parameters.toolset }}
|
||||
installer: ${{ parameters.installer }}
|
||||
sbomComponentList: 'uhd libuhd'
|
||||
buildOSName: $(buildOSName)
|
||||
|
||||
@ -87,6 +87,9 @@ jobs:
|
||||
|
||||
- download: uhd_build_docker_container
|
||||
|
||||
- ${{ if not(or(contains(parameters.toolset, 'msbuild'), eq(parameters.toolset, 'make_homebrew_macos'))) }}:
|
||||
- template: steps-install-nib310rio-dev.yml
|
||||
|
||||
- ${{ if eq(parameters.toolset, 'make') }}:
|
||||
- template: steps-build-uhd-make.yml
|
||||
parameters:
|
||||
|
||||
30
.ci/templates/job-uhd-devtest-b310.yml
Normal file
30
.ci/templates/job-uhd-devtest-b310.yml
Normal file
@ -0,0 +1,30 @@
|
||||
parameters:
|
||||
- name: uhdSrcDir
|
||||
type: string
|
||||
- name: uhdArtifactSource
|
||||
type: string
|
||||
default: current
|
||||
- name: uhdFpgaArtifactSource
|
||||
type: string
|
||||
default: current
|
||||
- name: fpga_imgs_source
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
- template: job-uhd-devtest.yml
|
||||
parameters:
|
||||
suiteName: 'b310_test'
|
||||
knownHost: '|1|jkP5jWiUwlZ78hX40IN2iqrBVqk=|0qCDGjJHYWE0Z4pi4ZecrczzKt8= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg='
|
||||
toolset: 'make'
|
||||
uhdSrcDir: '${{ parameters.uhdSrcDir }}'
|
||||
uhdArtifactSource: '${{ parameters.uhdArtifactSource }}'
|
||||
uhdFpgaArtifactSource: '${{ parameters.uhdFpgaArtifactSource }}'
|
||||
fpga_imgs_source: '${{ parameters.fpga_imgs_source }}'
|
||||
dutMatrix:
|
||||
b310:
|
||||
devType: 'b3xx'
|
||||
devModel: 'b310'
|
||||
devName: 'b310-ATS'
|
||||
devSerial: '3665C03'
|
||||
devBus: 'pcie'
|
||||
devtestPattern: 'b3xx'
|
||||
@ -24,7 +24,6 @@ jobs:
|
||||
uhdFpgaArtifactSource: '${{ parameters.uhdFpgaArtifactSource }}'
|
||||
fpga_imgs_source: '${{ parameters.fpga_imgs_source }}'
|
||||
redisHost: 'sdr-rhombus'
|
||||
vivado_dir: '/opt/Xilinx/Vivado_Lab/2020.1/'
|
||||
dutMatrix:
|
||||
${{ if contains(parameters.testDevices, 'x3xx') }}:
|
||||
rhombus-x300-UBX-0:
|
||||
|
||||
@ -24,7 +24,6 @@ jobs:
|
||||
uhdFpgaArtifactSource: '${{ parameters.uhdFpgaArtifactSource }}'
|
||||
fpga_imgs_source: '${{ parameters.fpga_imgs_source }}'
|
||||
redisHost: 'dre-s175'
|
||||
vivado_dir: $(xilinx_location)
|
||||
dutMatrix:
|
||||
${{ if contains(parameters.testDevices, 'b2xx') }}:
|
||||
sdr-s175-dev-tests-b206-0:
|
||||
@ -54,7 +53,6 @@ jobs:
|
||||
uhdFpgaArtifactSource: '${{ parameters.uhdFpgaArtifactSource }}'
|
||||
fpga_imgs_source: '${{ parameters.fpga_imgs_source }}'
|
||||
redisHost: 'dre-s175'
|
||||
vivado_dir: $(xilinx_location)
|
||||
dutMatrix:
|
||||
${{ if contains(parameters.testDevices, 'x3xx') }}:
|
||||
sdr-s175-dev-tests-x310-4:
|
||||
|
||||
@ -20,8 +20,7 @@ jobs:
|
||||
uhdArtifactSource: '${{ parameters.uhdArtifactSource }}'
|
||||
uhdFpgaArtifactSource: '${{ parameters.uhdFpgaArtifactSource }}'
|
||||
fpga_imgs_source: '${{ parameters.fpga_imgs_source }}'
|
||||
redisHost: 'sdr-osmium-test'
|
||||
vivado_dir: '/opt/Xilinx/Vivado_Lab/2023.2/'
|
||||
redisHost: $(host_redisHost)
|
||||
dutMatrix:
|
||||
x310-OBX:
|
||||
devAgent: osmium
|
||||
@ -34,4 +33,4 @@ jobs:
|
||||
devFpga: 'HG,XG'
|
||||
devtestPattern: 'x3x0'
|
||||
jtagSerial: $(x310_OBX_jtagSerial)
|
||||
jtagServer: 'nitest@sdr-osmium-test'
|
||||
jtagServer: $(host_jtagServer)
|
||||
|
||||
@ -20,8 +20,6 @@ parameters:
|
||||
default: ''
|
||||
- name: dutMatrix
|
||||
type: object
|
||||
- name: vivado_dir
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
- job: uhd_devtest_${{ parameters.suiteName }}
|
||||
@ -142,6 +140,7 @@ jobs:
|
||||
cd $(Agent.TempDirectory)
|
||||
python3 -m venv devtest_venv
|
||||
source devtest_venv/bin/activate
|
||||
python3 -m pip install --upgrade pip
|
||||
pip3 install -r $(Build.SourcesDirectory)/uhddev/host/tests/devtest/requirements.txt
|
||||
pip3 install -r $(Build.SourcesDirectory)/uhddev/.ci/utils/requirements.txt
|
||||
mkdir -p $(Common.TestResultsDirectory)/devtest
|
||||
@ -168,6 +167,7 @@ jobs:
|
||||
cd $(Agent.TempDirectory)
|
||||
python3 -m venv devtest_venv
|
||||
source devtest_venv/bin/activate
|
||||
python3 -m pip install --upgrade pip
|
||||
pip3 install -r $(Build.SourcesDirectory)/uhddev/host/tests/devtest/requirements.txt
|
||||
pip3 install -r $(Build.SourcesDirectory)/uhddev/.ci/utils/requirements.txt
|
||||
mkdir -p $(Common.TestResultsDirectory)/devtest
|
||||
@ -194,6 +194,7 @@ jobs:
|
||||
cd $(Agent.TempDirectory)
|
||||
python3 -m venv devtest_venv
|
||||
source devtest_venv/bin/activate
|
||||
python3 -m pip install --upgrade pip
|
||||
pip3 install -r $(Build.SourcesDirectory)/uhddev/host/tests/devtest/requirements.txt
|
||||
pip3 install -r $(Build.SourcesDirectory)/uhddev/.ci/utils/requirements.txt
|
||||
mkdir -p $(Common.TestResultsDirectory)/devtest
|
||||
@ -215,9 +216,11 @@ jobs:
|
||||
displayName: Run b2xx devtest on $(devName)
|
||||
|
||||
- script: |
|
||||
echo PATH: $PATH
|
||||
cd $(Agent.TempDirectory)
|
||||
python3 -m venv devtest_venv
|
||||
source devtest_venv/bin/activate
|
||||
python3 -m pip install --upgrade pip
|
||||
pip3 install -r $(Build.SourcesDirectory)/uhddev/host/tests/devtest/requirements.txt
|
||||
pip3 install -r $(Build.SourcesDirectory)/uhddev/.ci/utils/requirements.txt
|
||||
mkdir -p $(Common.TestResultsDirectory)/devtest
|
||||
@ -225,7 +228,7 @@ jobs:
|
||||
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 }}/uhddev/.ci/utils/mutex_hardware.py \
|
||||
--jtag_x3xx $(devType),$(devModel),$(jtagServer),$(jtagSerial),$(Build.BinariesDirectory)/uhddev/build/fpga_images/,${{ parameters.vivado_dir }} \
|
||||
--jtag_x3xx $(devType),$(devModel),$(jtagServer),$(jtagSerial),$(Build.BinariesDirectory)/uhddev/build/fpga_images/ \
|
||||
--fpgas $(devFpga) \
|
||||
"${{ parameters.redisHost }}" "$(devName)" \
|
||||
"$(Build.BinariesDirectory)/uhddev/build/utils/uhd_usrp_probe --args addr=$(devAddr)" \
|
||||
@ -238,6 +241,30 @@ jobs:
|
||||
condition: and(succeeded(), eq(variables.devType, 'x300'), eq(variables.devBus, 'ip'))
|
||||
displayName: Run x3xx devtest on $(devName) $(devFpga)
|
||||
|
||||
- script: |
|
||||
cd $(Agent.TempDirectory)
|
||||
python3 -m venv devtest_venv
|
||||
source devtest_venv/bin/activate
|
||||
pip3 install -r $(Build.SourcesDirectory)/uhddev/host/tests/devtest/requirements.txt
|
||||
pip3 install -r $(Build.SourcesDirectory)/uhddev/.ci/utils/requirements.txt
|
||||
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
|
||||
export UHD_IMAGES_DIR=$(Build.BinariesDirectory)/uhddev/build/fpga_images
|
||||
uhd_image_loader --args type=$(devType),serial=$(devSerial)
|
||||
sleep 10
|
||||
$(Build.BinariesDirectory)/uhddev/build-installed/bin/uhd_images_downloader -t b3xx -t fw
|
||||
uhd_usrp_probe --args type=$(devType),serial=$(devSerial)
|
||||
python3 ${{ parameters.uhdSrcDir }}/uhddev/host/tests/devtest/run_testsuite.py \
|
||||
--src-dir ${{ parameters.uhdSrcDir }}/uhddev/host/tests/devtest \
|
||||
--devtest-pattern $(devtestPattern) --args type=$(devType),serial=$(devSerial) \
|
||||
--build-type Release --build-dir $(Build.BinariesDirectory)/uhddev/build \
|
||||
--python-interp python3 --xml
|
||||
continueOnError: true
|
||||
condition: and(succeeded(), eq(variables.devType, 'b3xx'))
|
||||
displayName: Run b3xx devtest on $(devName)
|
||||
|
||||
- script: |
|
||||
cat $(Common.TestResultsDirectory)/devtest/log*.log
|
||||
echo ""
|
||||
@ -251,7 +278,7 @@ jobs:
|
||||
$(Common.TestResultsDirectory)/devtest/devtestresults.xml
|
||||
continueOnError: true
|
||||
displayName: Format devtest xml
|
||||
condition: and(always(), eq(variables.devType, 'b200'))
|
||||
condition: and(always(), or(eq(variables.devType, 'b200'), eq(variables.devType, 'b3xx')))
|
||||
|
||||
- script: |
|
||||
cd $(Common.TestResultsDirectory)/devtest
|
||||
@ -261,7 +288,7 @@ jobs:
|
||||
$(Common.TestResultsDirectory)/devtest/devtestresults.xml
|
||||
continueOnError: true
|
||||
displayName: Format devtest xml
|
||||
condition: and(always(), ne(variables.devType, 'b200'))
|
||||
condition: and(always(), and(ne(variables.devType, 'b200'), ne(variables.devType, 'b3xx')))
|
||||
|
||||
- publish: $(Common.TestResultsDirectory)
|
||||
artifact: test-logs-devtest-$(devName)-$(devModel)-$(devFpga)-run$(System.JobAttempt)
|
||||
|
||||
39
.ci/templates/job-uhd-rf-tests-b310.yml
Normal file
39
.ci/templates/job-uhd-rf-tests-b310.yml
Normal file
@ -0,0 +1,39 @@
|
||||
parameters:
|
||||
- name: uhdSrcDir
|
||||
type: string
|
||||
- name: uhdArtifactSource
|
||||
type: string
|
||||
default: current
|
||||
- name: uhdFpgaArtifactSource
|
||||
type: string
|
||||
default: current
|
||||
- name: fpga_imgs_source
|
||||
type: string
|
||||
- name: testDevices
|
||||
type: string
|
||||
default: 'b3xx'
|
||||
- name: extra_rf_test_args
|
||||
type: string
|
||||
default: ''
|
||||
|
||||
jobs:
|
||||
- template: job-uhd-rf-tests.yml
|
||||
parameters:
|
||||
toolset: 'make'
|
||||
uhdSrcDir: '${{ parameters.uhdSrcDir }}'
|
||||
uhdArtifactSource: '${{ parameters.uhdArtifactSource }}'
|
||||
uhdFpgaArtifactSource: ${{ parameters.uhdFpgaArtifactSource }}
|
||||
fpga_imgs_source: '${{ parameters.fpga_imgs_source }}'
|
||||
extra_rf_test_args: '${{ parameters.extra_rf_test_args }}'
|
||||
dutMatrix:
|
||||
b310:
|
||||
devType: 'b3xx'
|
||||
devModel: 'b310'
|
||||
devName: 'b310-ATS'
|
||||
devSerial: '3665C03'
|
||||
devBus: 'pcie'
|
||||
devFpga: 'defaultFPGA'
|
||||
devtestPattern: 'b3xx'
|
||||
pytestDUT: 'b310'
|
||||
pytestAtsConfig: 'b300_ats'
|
||||
pipelineAgent: 'b310-ats'
|
||||
@ -24,8 +24,7 @@ jobs:
|
||||
uhdArtifactSource: '${{ parameters.uhdArtifactSource }}'
|
||||
uhdFpgaArtifactSource: ${{ parameters.uhdFpgaArtifactSource }}
|
||||
fpga_imgs_source: '${{ parameters.fpga_imgs_source }}'
|
||||
redisHost: 'sdr-osmium-test'
|
||||
vivado_dir: '/opt/Xilinx/Vivado_Lab/2023.2/'
|
||||
redisHost: $(host_redisHost)
|
||||
extra_rf_test_args: '${{ parameters.extra_rf_test_args }}'
|
||||
dutMatrix:
|
||||
x310-obx-0:
|
||||
@ -37,12 +36,12 @@ jobs:
|
||||
db1Serial: '34A3BA0'
|
||||
supportsCorrections: true
|
||||
devBus: 'ip'
|
||||
devAddr: '192.168.40.2'
|
||||
sfpAddrs: '192.168.40.2'
|
||||
devAddr: $(x310_OBX_devAddr)
|
||||
sfpAddrs: $(x310_OBX_devAddr)
|
||||
devFpga: 'XG'
|
||||
devtestPattern: 'x300'
|
||||
pytestDUT: 'x310'
|
||||
pytestAtsConfig: 'osmium_ats'
|
||||
pipelineAgent: 'obx-ats-agent'
|
||||
jtagSerial: $(x310_OBX_jtagSerial)
|
||||
jtagServer: 'nitest@sdr-osmium-test'
|
||||
jtagServer: $(host_jtagServer)
|
||||
|
||||
@ -16,9 +16,6 @@ parameters:
|
||||
default: ''
|
||||
- name: dutMatrix
|
||||
type: object
|
||||
- name: vivado_dir
|
||||
type: string
|
||||
default: ''
|
||||
- name: extra_rf_test_args
|
||||
type: string
|
||||
default: ''
|
||||
@ -225,6 +222,7 @@ jobs:
|
||||
fi
|
||||
|
||||
source $(Build.SourcesDirectory)/ettus-rts/config/remote/python_tests/setup_venv.sh
|
||||
python3 -m pip install --upgrade pip
|
||||
pip3 install -r $(Build.SourcesDirectory)/uhddev/.ci/utils/requirements_312.txt
|
||||
|
||||
cd "$(Build.SourcesDirectory)/ettus-rts/config/remote/python_tests"
|
||||
@ -240,10 +238,11 @@ jobs:
|
||||
"python3 ../automated_main.py --ats_config $(pytestAtsConfig) --dut $(pytestDUT) \
|
||||
--test_selector $(Build.SourcesDirectory)/ettus-rts/config/remote/python_tests/pytests \
|
||||
--results_path $(Common.TestResultsDirectory)/pytest/rf/$(devFpga) \
|
||||
--run_vars_overrides "mgmt_addr=$(devAddr)" --run_vars_overrides "addr=$(devAddr)" \
|
||||
$(parameters.extra_rf_test_args)" || rf_test_status=$?
|
||||
elif [ "$(devType)" = "x300" ]; then
|
||||
python3 ${{ parameters.uhdSrcDir }}/.ci/utils/mutex_hardware.py \
|
||||
--jtag_x3xx $(devType),$(devModel),$(jtagServer),$(jtagSerial),$(Build.BinariesDirectory)/uhddev/build/fpga_images/,${{ parameters.vivado_dir }} \
|
||||
--jtag_x3xx $(devType),$(devModel),$(jtagServer),$(jtagSerial),$(Build.BinariesDirectory)/uhddev/build/fpga_images/ \
|
||||
--fpgas $(devFpga) \
|
||||
--sfp_addrs $(sfpAddrs) \
|
||||
"${{ parameters.redisHost }}" "$(devName)" \
|
||||
@ -251,12 +250,32 @@ jobs:
|
||||
"python3 ../automated_main.py --ats_config $(pytestAtsConfig) --dut $(pytestDUT) \
|
||||
--test_selector $(Build.SourcesDirectory)/ettus-rts/config/remote/python_tests/pytests \
|
||||
--results_path $(Common.TestResultsDirectory)/pytest/rf/$(devFpga) \
|
||||
--run_vars_overrides "mgmt_addr=$(devAddr)" --run_vars_overrides "addr=$(devAddr)" \
|
||||
$(parameters.extra_rf_test_args)" || rf_test_status=$?
|
||||
elif [ "$(devType)" = "b3xx" ]; then
|
||||
cd $(Build.SourcesDirectory)/ettus-rts/config/remote/python_tests/pytests
|
||||
uhd_image_loader --args type=$(devType),serial=$(devSerial)
|
||||
sleep 10
|
||||
uhd_images_downloader -t b3xx -t fw
|
||||
uhd_usrp_probe --args type=$(devType),serial=$(devSerial)
|
||||
python3 ../automated_main.py --ats_config $(pytestAtsConfig) --dut $(pytestDUT) \
|
||||
--results_path $(Common.TestResultsDirectory)/pytest/rf/$(devFpga) ${{ parameters.extra_rf_test_args }} || rf_test_status=$?
|
||||
cd $(Build.SourcesDirectory)/ettus-rts/config/remote/python_tests
|
||||
fi
|
||||
|
||||
# only if devBus is "ip" then we need to run the non-rf tests with the same mgmt_addr and addr overrides as the rf tests
|
||||
if [ "$(devBus)" = "ip" ]; then
|
||||
python3 automated_main.py --ats_config $(pytestAtsConfig) --dut $(pytestDUT) \
|
||||
--test_selector $(Build.SourcesDirectory)/ettus-rts/config/remote/python_tests/pytests_non_rf \
|
||||
--results_path $(Common.TestResultsDirectory)/pytest/non_rf/$(devFpga) \
|
||||
--run_vars_overrides "mgmt_addr=$(devAddr)" --run_vars_overrides "addr=$(devAddr)" \
|
||||
${{ parameters.extra_rf_test_args }} || non_rf_test_status=$?
|
||||
else
|
||||
python3 automated_main.py --ats_config $(pytestAtsConfig) --dut $(pytestDUT) \
|
||||
--test_selector $(Build.SourcesDirectory)/ettus-rts/config/remote/python_tests/pytests_non_rf \
|
||||
--results_path $(Common.TestResultsDirectory)/pytest/non_rf/$(devFpga) \
|
||||
${{ parameters.extra_rf_test_args }} || non_rf_test_status=$?
|
||||
fi
|
||||
python3 automated_main.py --ats_config $(pytestAtsConfig) --dut $(pytestDUT) \
|
||||
--test_selector $(Build.SourcesDirectory)/ettus-rts/config/remote/python_tests/pytests_non_rf \
|
||||
--results_path $(Common.TestResultsDirectory)/pytest/non_rf/$(devFpga) \
|
||||
${{ parameters.extra_rf_test_args }} || non_rf_test_status=$?
|
||||
|
||||
if [ $rf_test_status -ne 0 ] || [ $non_rf_test_status -ne 0 ]; then
|
||||
echo "At least one pytest invocation failed (rf=$rf_test_status, non_rf=$non_rf_test_status)."
|
||||
|
||||
@ -1,133 +0,0 @@
|
||||
parameters:
|
||||
- name: uhdSrcDir
|
||||
type: string
|
||||
- name: uhdArtifactSource
|
||||
type: string
|
||||
default: current
|
||||
- name: uhdFpgaArtifactSource
|
||||
type: string
|
||||
default: current
|
||||
- name: testLength
|
||||
type: string
|
||||
- name: dpdkDirs
|
||||
type: object
|
||||
default:
|
||||
- '/opt/dpdk-19.11'
|
||||
- '/opt/dpdk-23.11'
|
||||
|
||||
jobs:
|
||||
- template: job-uhd-streaming-tests.yml
|
||||
parameters:
|
||||
suiteName: 'beauty'
|
||||
testLength: '${{ parameters.testLength }}'
|
||||
toolset: 'make'
|
||||
uhdSrcDir: '${{ parameters.uhdSrcDir }}'
|
||||
uhdArtifactSource: ${{ parameters.uhdArtifactSource }}
|
||||
uhdFpgaArtifactSource: '${{ parameters.uhdFpgaArtifactSource }}'
|
||||
dpdkDirs: '${{ parameters.dpdkDirs }}'
|
||||
dutMatrix:
|
||||
# beauty-N320-0 XG:
|
||||
# dutName: 'beauty-N320-0'
|
||||
# dutType: 'N320'
|
||||
# dutAddr: '192.168.10.4'
|
||||
# dutSecondAddr: '192.168.20.4'
|
||||
# dutFPGA: 'XG'
|
||||
# dutNameId: ''
|
||||
# dutNumRecvFrames: ''
|
||||
# dutNumSendFrames: ''
|
||||
beauty-X310-0:
|
||||
dutName: 'beauty-X310-0'
|
||||
dutType: 'x310'
|
||||
dutAddr: '$(x310_dutAddr)'
|
||||
dutSecondAddr: '$(x310_dutSecondAddr)'
|
||||
dutFPGA: 'XG'
|
||||
dutNameId: ''
|
||||
dutNumRecvFrames: ''
|
||||
dutNumSendFrames: ''
|
||||
jtagSerial: '$(x310_jtagSerial)'
|
||||
sfpInt0: '$(x310_sfpInt0)'
|
||||
sfpInt1: '$(x310_sfpInt1)'
|
||||
# beauty-X410-0 X4_200:
|
||||
# dutName: 'beauty-X410-0'
|
||||
# dutFamily: 'x4xx'
|
||||
# dutType: 'x410'
|
||||
# dutAddr: '$(x410_dutAddr)'
|
||||
# dutSecondAddr: '$(x410_dutSecondAddr)'
|
||||
# dutFPGA:'X4_200'
|
||||
# dutNameId: ''
|
||||
# dutEmbeddedImagesArtifact: 'x4xx-images'
|
||||
# uartSerial: '$(x410_uartSerial)'
|
||||
# dutNumRecvFrames: ''
|
||||
# dutNumSendFrames: ''
|
||||
# sfpInt0: '$(x410_sfpInt0)'
|
||||
# sfpInt1: '$(x410_sfpInt1)'
|
||||
beauty-X410-0 CG_400:
|
||||
dutName: 'beauty-X410-0'
|
||||
dutFamily: 'x4xx'
|
||||
dutType: 'x410'
|
||||
dutAddr: '$(x410_dutAddr)'
|
||||
dutSecondAddr: '$(x410_dutSecondAddr)'
|
||||
dutFPGA: 'CG_400'
|
||||
dutNameId: ''
|
||||
dutEmbeddedImagesArtifact: 'x4xx-images'
|
||||
uartSerial: '$(x410_uartSerial)'
|
||||
dutNumRecvFrames: ''
|
||||
dutNumSendFrames: ''
|
||||
sfpInt0: '$(x410_sfpInt0)'
|
||||
sfpInt1: '$(x410_sfpInt1)'
|
||||
beauty-X410-0 UC_200:
|
||||
dutName: 'beauty-X410-0'
|
||||
dutFamily: 'x4xx'
|
||||
dutType: 'x410'
|
||||
# UC image: device is reachable only via dutSecondAddr
|
||||
dutAddr: '$(x410_dutSecondAddr)'
|
||||
dutSecondAddr: '$(x410_dutSecondAddr)'
|
||||
dutFPGA: 'UC_200'
|
||||
dutNameId: ''
|
||||
dutEmbeddedImagesArtifact: 'x4xx-images'
|
||||
uartSerial: '$(x410_uartSerial)'
|
||||
dutNumRecvFrames: ''
|
||||
dutNumSendFrames: ''
|
||||
sfpInt0: '$(x410_sfpInt0)'
|
||||
sfpInt1: '$(x410_sfpInt1)'
|
||||
# beauty-E320-0:
|
||||
# dutName: 'beauty-E320-0'
|
||||
# dutType: 'E320'
|
||||
# dutAddr: '192.168.10.5'
|
||||
# dutSecondAddr: ''
|
||||
# dutFPGA: 'XG'
|
||||
# dutNameId: ''
|
||||
# dutNumRecvFrames: ''
|
||||
# dutNumSendFrames: ''
|
||||
# beauty-N310-0:
|
||||
# dutName: 'beauty-N310-0'
|
||||
# dutType: 'N310'
|
||||
# dutAddr: '192.168.10.6'
|
||||
# dutSecondAddr: '192.168.20.6'
|
||||
# dutFPGA: 'XG'
|
||||
# dutNameId: ''
|
||||
# dutNumRecvFrames: ''
|
||||
# dutNumSendFrames: ''
|
||||
# beauty-X310_TwinRx-0:
|
||||
# dutName: 'beauty-X310_TwinRx-0'
|
||||
# dutType: 'X310_TwinRx'
|
||||
# dutAddr: '192.168.10.7'
|
||||
# dutSecondAddr: '192.168.20.7'
|
||||
# dutFPGA: 'XG'
|
||||
# dutNameId: ''
|
||||
# dutNumRecvFrames: ''
|
||||
# dutNumSendFrames: ''
|
||||
# sfpInt0: 'ens4f0'
|
||||
# sfpInt1: 'ens4f1'
|
||||
# beauty-B210-0:
|
||||
# dutName: 'beauty-B210-0'
|
||||
# dutFamily: 'b200'
|
||||
# dutType: 'B210'
|
||||
# dutAddr: ''
|
||||
# dutSecondAddr: ''
|
||||
# dutFPGA: ''
|
||||
# dutNameId: 'MyB210'
|
||||
# dutNumRecvFrames: '256'
|
||||
# dutNumSendFrames: '256'
|
||||
# dutGetFpgaImagesMethod: 'uhd_images_downloader'
|
||||
|
||||
@ -1,50 +0,0 @@
|
||||
parameters:
|
||||
- name: uhdSrcDir
|
||||
type: string
|
||||
- name: uhdArtifactSource
|
||||
type: string
|
||||
default: current
|
||||
- name: uhdFpgaArtifactSource
|
||||
type: string
|
||||
default: current
|
||||
- name: testLength
|
||||
type: string
|
||||
- name: dpdkDirs
|
||||
type: object
|
||||
default:
|
||||
- '/opt/dpdk-19.11'
|
||||
- '/opt/dpdk-23.11'
|
||||
|
||||
jobs:
|
||||
- template: job-uhd-streaming-tests.yml
|
||||
parameters:
|
||||
suiteName: 'sdr_s175_tests_b2xx'
|
||||
testLength: '${{ parameters.testLength }}'
|
||||
toolset: 'make'
|
||||
uhdSrcDir: '${{ parameters.uhdSrcDir }}'
|
||||
uhdArtifactSource: ${{ parameters.uhdArtifactSource }}
|
||||
uhdFpgaArtifactSource: '${{ parameters.uhdFpgaArtifactSource }}'
|
||||
dpdkDirs: '${{ parameters.dpdkDirs }}'
|
||||
dutMatrix:
|
||||
sdr-s175-streaming-tests-B206-0:
|
||||
dutName: 'sdr_s175_tests-b206-0'
|
||||
dutFamily: 'b200'
|
||||
dutType: 'B206'
|
||||
dutAddr: ''
|
||||
dutSecondAddr: ''
|
||||
dutFPGA: ''
|
||||
dutNameId: 'B206i'
|
||||
dutNumRecvFrames: '256'
|
||||
dutNumSendFrames: '256'
|
||||
dutGetFpgaImagesMethod: 'uhd_images_downloader'
|
||||
sdr-s175-streaming-tests-B210-0:
|
||||
dutName: 'sdr_s175_tests-b210-0'
|
||||
dutFamily: 'b200'
|
||||
dutType: 'B210'
|
||||
dutAddr: ''
|
||||
dutSecondAddr: ''
|
||||
dutFPGA: ''
|
||||
dutNameId: 'MyB210'
|
||||
dutNumRecvFrames: '256'
|
||||
dutNumSendFrames: '256'
|
||||
dutGetFpgaImagesMethod: 'uhd_images_downloader'
|
||||
@ -1,57 +0,0 @@
|
||||
parameters:
|
||||
- name: uhdSrcDir
|
||||
type: string
|
||||
- name: uhdArtifactSource
|
||||
default: current
|
||||
type: string
|
||||
- name: uhdFpgaArtifactSource
|
||||
type: string
|
||||
default: current
|
||||
- name: testLength
|
||||
type: string
|
||||
- name: dpdkDirs
|
||||
type: object
|
||||
default:
|
||||
- '/opt/dpdk-19.11'
|
||||
- '/opt/dpdk-23.11'
|
||||
|
||||
jobs:
|
||||
- template: job-uhd-streaming-tests.yml
|
||||
parameters:
|
||||
suiteName: 'streamingx440'
|
||||
testLength: '${{ parameters.testLength }}'
|
||||
toolset: 'make'
|
||||
nicType: '100gbe'
|
||||
uhdSrcDir: '${{ parameters.uhdSrcDir }}'
|
||||
uhdArtifactSource: ${{ parameters.uhdArtifactSource }}
|
||||
uhdFpgaArtifactSource: '${{ parameters.uhdFpgaArtifactSource }}'
|
||||
dpdkDirs: '${{ parameters.dpdkDirs }}'
|
||||
dutMatrix:
|
||||
X440-0 CG_400:
|
||||
dutName: 'streaming-X440-0'
|
||||
dutFamily: 'x4xx'
|
||||
dutType: 'x440'
|
||||
dutAddr: '$(x440_dutAddr)'
|
||||
dutSecondAddr: '$(x440_dutSecondAddr)'
|
||||
dutFPGA: 'CG_400'
|
||||
dutNameId: ''
|
||||
dutEmbeddedImagesArtifact: 'x4xx-images'
|
||||
uartSerial: '$(x440_uartSerial)'
|
||||
dutNumRecvFrames: ''
|
||||
dutNumSendFrames: ''
|
||||
sfpInt0: '$(x440_sfpInt0)'
|
||||
sfpInt1: '$(x440_sfpInt1)'
|
||||
X440-0 CG_1600:
|
||||
dutName: 'streaming-X440-0'
|
||||
dutFamily: 'x4xx'
|
||||
dutType: 'x440'
|
||||
dutAddr: '$(x440_dutAddr)'
|
||||
dutSecondAddr: '$(x440_dutSecondAddr)'
|
||||
dutFPGA: 'CG_1600'
|
||||
dutNameId: ''
|
||||
dutEmbeddedImagesArtifact: 'x4xx-images'
|
||||
uartSerial: '$(x440_uartSerial)'
|
||||
dutNumRecvFrames: ''
|
||||
dutNumSendFrames: ''
|
||||
sfpInt0: '$(x440_sfpInt0)'
|
||||
sfpInt1: '$(x440_sfpInt1)'
|
||||
File diff suppressed because it is too large
Load Diff
@ -183,6 +183,7 @@ jobs:
|
||||
py -3.10 -m venv lvtest_venv
|
||||
call lvtest_venv\Scripts\activate.bat
|
||||
echo "Installing required Python packages into the virtual environment"
|
||||
python -m pip install --upgrade pip
|
||||
pip install lvtest
|
||||
displayName: Prepare venv (Python 3.10)
|
||||
|
||||
|
||||
100
.ci/templates/stage-build-uhd-embedded-system-image.yml
Normal file
100
.ci/templates/stage-build-uhd-embedded-system-image.yml
Normal file
@ -0,0 +1,100 @@
|
||||
#
|
||||
# Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
#
|
||||
|
||||
parameters:
|
||||
- name: stage_name
|
||||
type: string
|
||||
- name: display_name
|
||||
type: string
|
||||
- name: machine_name
|
||||
type: string
|
||||
- name: stage_condition
|
||||
type: string
|
||||
- name: enabled
|
||||
type: boolean
|
||||
default: true
|
||||
- name: create_sbom
|
||||
type: boolean
|
||||
- name: build_sdk
|
||||
type: boolean
|
||||
- name: cache_sstate
|
||||
type: boolean
|
||||
- name: ext_fpga
|
||||
type: boolean
|
||||
- name: fpga_imgs_source
|
||||
type: string
|
||||
|
||||
stages:
|
||||
- stage: ${{ parameters.stage_name }}
|
||||
displayName: ${{ parameters.display_name }}
|
||||
dependsOn: analyze_changeset
|
||||
${{ if parameters.enabled }}:
|
||||
condition: >
|
||||
and(
|
||||
succeeded(),
|
||||
${{ parameters.stage_condition }}
|
||||
)
|
||||
${{ else }}:
|
||||
condition: false
|
||||
variables:
|
||||
AUTO_CONF: $(Agent.BuildDirectory)/auto.conf
|
||||
jobs:
|
||||
- template: .ci/templates/job_build_filesystem.yml@meta-ettus
|
||||
parameters:
|
||||
create_sbom: ${{ parameters.create_sbom }}
|
||||
build_sdk: ${{ parameters.build_sdk }}
|
||||
cache_sstate: ${{ parameters.cache_sstate }}
|
||||
cache_downloads: False
|
||||
machines:
|
||||
- ${{ parameters.machine_name }}
|
||||
auto_conf: $AUTO_CONF
|
||||
run_from_external_repo: true
|
||||
prebuild_steps:
|
||||
- script: |
|
||||
rm $(AUTO_CONF)
|
||||
touch $(AUTO_CONF)
|
||||
displayName: create auto.conf
|
||||
- template: .ci/templates/oe_localconf_externalsrc.yml@meta-ettus
|
||||
parameters:
|
||||
conf_file: $AUTO_CONF
|
||||
repository: self
|
||||
package_paths:
|
||||
- package: uhd
|
||||
path: host
|
||||
- package: mpmd
|
||||
path: mpm
|
||||
- ${{ if parameters.ext_fpga }}:
|
||||
- template: .ci/templates/oe_localconf_externalsrc_pipeline.yml@meta-ettus
|
||||
parameters:
|
||||
conf_file: $AUTO_CONF
|
||||
pipeline: uhd_mono_pipeline
|
||||
patterns: |
|
||||
usrp_e310_*_fpga.*
|
||||
usrp_e320_fpga_*.*
|
||||
usrp_n3??_fpga_*.*
|
||||
usrp_x4??_fpga_*.*
|
||||
packages:
|
||||
- uhd-fpga-images
|
||||
flatten: true
|
||||
- script: |
|
||||
ls -l $(Agent.BuildDirectory)/uhd_mono_pipeline
|
||||
displayName: list fpga images
|
||||
- ${{ if contains(parameters.fpga_imgs_source, 'Internal') }}:
|
||||
- bash: |
|
||||
cat <<EOF >>$AUTO_CONF
|
||||
UHD_BASE_URL = "$FILE_SERVER_URL"
|
||||
EOF
|
||||
env:
|
||||
FILE_SERVER_URL: $(InternalFileServerUrl)
|
||||
- bash: |
|
||||
cat <<EOF >>$AUTO_CONF
|
||||
PREFERRED_UHD_VERSION = "4.11%"
|
||||
EOF
|
||||
postbuild_steps:
|
||||
- script: |
|
||||
rm $(AUTO_CONF)
|
||||
displayName: delete auto.conf
|
||||
condition: always()
|
||||
@ -26,6 +26,10 @@ parameters:
|
||||
- name: testLength
|
||||
type: string
|
||||
default: 'smoke'
|
||||
- name: selected_streaming_tests
|
||||
type: string
|
||||
displayName: Streaming Tests Multi-Select (comma-separated)
|
||||
default: 'B206,B210,B310,X310_XG,X410_CG_400,X410_UC_200,X440_CG_400,X440_CG_1600'
|
||||
- name: create_sbom
|
||||
type: boolean
|
||||
displayName: Create SBOMs
|
||||
@ -161,30 +165,35 @@ parameters:
|
||||
X420_CG_1000:
|
||||
image_core: x420_CG_1000_rfnoc_image_core.yml
|
||||
image_core_name: usrp_x420_fpga_CG_1000
|
||||
target_name: X420
|
||||
artifact_name: X420_CG_1000
|
||||
timeout: 720
|
||||
max_attempts: 2
|
||||
X420_X4_1000:
|
||||
image_core: x420_X4_1000_rfnoc_image_core.yml
|
||||
image_core_name: usrp_x420_fpga_X4_1000
|
||||
target_name: X420
|
||||
artifact_name: X420_X4_1000
|
||||
timeout: 720
|
||||
max_attempts: 2
|
||||
X420_CG_400:
|
||||
image_core: x420_CG_400_rfnoc_image_core.yml
|
||||
image_core_name: usrp_x420_fpga_CG_400
|
||||
target_name: X420
|
||||
artifact_name: X420_CG_400
|
||||
timeout: 720
|
||||
max_attempts: 2
|
||||
X420_X4_400:
|
||||
image_core: x420_X4_400_rfnoc_image_core.yml
|
||||
image_core_name: usrp_x420_fpga_X4_400
|
||||
target_name: X420
|
||||
artifact_name: X420_X4_400
|
||||
timeout: 720
|
||||
max_attempts: 2
|
||||
X420_X4_200:
|
||||
image_core: x420_X4_200_rfnoc_image_core.yml
|
||||
image_core_name: usrp_x420_fpga_X4_200
|
||||
target_name: X420
|
||||
artifact_name: X420_X4_200
|
||||
timeout: 720
|
||||
max_attempts: 2
|
||||
@ -200,6 +209,14 @@ parameters:
|
||||
timeout: 720
|
||||
attempt_timeout: 360
|
||||
max_attempts: 2
|
||||
X440_CG_800:
|
||||
image_core: x440_CG_800_rfnoc_image_core.yml
|
||||
image_core_name: usrp_x440_fpga_CG_800
|
||||
target_name: X440
|
||||
artifact_name: X440_CG_800
|
||||
timeout: 720
|
||||
attempt_timeout: 360
|
||||
max_attempts: 2
|
||||
X440_CG_400:
|
||||
image_core: x440_CG_400_rfnoc_image_core.yml
|
||||
image_core_name: usrp_x440_fpga_CG_400
|
||||
@ -216,6 +233,14 @@ parameters:
|
||||
timeout: 720
|
||||
attempt_timeout: 360
|
||||
max_attempts: 2
|
||||
X440_X4_800:
|
||||
image_core: x440_X4_800_rfnoc_image_core.yml
|
||||
image_core_name: usrp_x440_fpga_X4_800
|
||||
target_name: X440
|
||||
artifact_name: X440_X4_800
|
||||
timeout: 720
|
||||
attempt_timeout: 360
|
||||
max_attempts: 2
|
||||
X440_X4_400:
|
||||
image_core: x440_X4_400_rfnoc_image_core.yml
|
||||
image_core_name: usrp_x440_fpga_X4_400
|
||||
@ -232,6 +257,72 @@ parameters:
|
||||
timeout: 720
|
||||
attempt_timeout: 360
|
||||
max_attempts: 2
|
||||
# Next parameters are used to control conditional execution of embedded system image build stages.
|
||||
# They are not parameters that can be set by the user,
|
||||
# but rather they are computed based on the output of the analyze_changeset stage.
|
||||
- name: embedded_stage_condition_e3xx
|
||||
type: string
|
||||
default: >
|
||||
or(
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'uhd.build.all'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'uhd.build.linux'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'mpm.build'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.streaming.all'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.streaming.e3xx'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.streaming.e310'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.streaming.e320'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.rf.all'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.rf.e3xx'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.rf.e310'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.rf.e320'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.all'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.e3xx'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.e310'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.e320')
|
||||
)
|
||||
- name: embedded_stage_condition_n3xx
|
||||
type: string
|
||||
default: >
|
||||
or(
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'uhd.build.all'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'uhd.build.linux'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'mpm.build'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.streaming.all'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.streaming.n3xx'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.streaming.n310'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.streaming.n320'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.rf.all'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.rf.n3xx'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.rf.n310'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.rf.n320'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.all'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.n3xx'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.n310'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.n320')
|
||||
)
|
||||
- name: embedded_stage_condition_x4xx
|
||||
type: string
|
||||
default: >
|
||||
or(
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'uhd.build.all'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'uhd.build.linux'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'mpm.build'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.streaming.all'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.streaming.x4xx'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.streaming.x410'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.streaming.x420'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.streaming.x440'),
|
||||
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.x420'),
|
||||
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.x4xx'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.x410'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.x420'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.x440')
|
||||
)
|
||||
|
||||
variables:
|
||||
- template: ../uhd-pipeline-vars.yml
|
||||
@ -241,6 +332,9 @@ resources:
|
||||
- pipeline: uhd_build_docker_container
|
||||
source: 'uhddev Build Docker Containers'
|
||||
branch: master
|
||||
- pipeline: nib310rio_kernel_packages
|
||||
source: 'usrp-pcie-driver pipeline'
|
||||
branch: main
|
||||
- pipeline: usrp-kas-pipeline
|
||||
source: 'usrp-kas'
|
||||
branch: scarthgap
|
||||
@ -284,7 +378,7 @@ resources:
|
||||
ref: master
|
||||
- repository: hwtools
|
||||
type: git
|
||||
ref: refs/tags/pip/hwsetup/0.1/0.1.3
|
||||
ref: main
|
||||
name: DevCentral/hwtools
|
||||
- repository: labview-usrp-examples
|
||||
type: github
|
||||
@ -407,105 +501,70 @@ stages:
|
||||
custom_boost_version_url: ${{ parameters.custom_boost_version_url }}
|
||||
grpc_version: ${{ parameters.grpc_version }}
|
||||
|
||||
- stage: build_uhd_embedded_system_images
|
||||
displayName: Build UHD Embedded System Images
|
||||
dependsOn: analyze_changeset
|
||||
# This can be improved:
|
||||
# Right now, if any embedded condition is true, then all embedded targets
|
||||
# with a checkbox will be built.
|
||||
condition: >
|
||||
and(
|
||||
succeeded(),
|
||||
or(
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'uhd.build.all'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'uhd.build.linux'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'mpm.build'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.streaming.all'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.streaming.e3'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.streaming.n3'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.streaming.x4'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.rf.all'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.rf.e3'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.rf.n3'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.rf.x4'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.all'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.e3'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.n3'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.x4')
|
||||
)
|
||||
)
|
||||
variables:
|
||||
AUTO_CONF: $(Agent.BuildDirectory)/auto.conf
|
||||
jobs:
|
||||
- template: .ci/templates/job_build_filesystem.yml@meta-ettus
|
||||
parameters:
|
||||
create_sbom: ${{ parameters.create_sbom }}
|
||||
build_sdk: ${{ parameters.build_sdk }}
|
||||
cache_sstate: ${{ parameters.cache_sstate }}
|
||||
cache_downloads: False
|
||||
machines:
|
||||
- ${{ 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:
|
||||
- script: |
|
||||
rm $(AUTO_CONF)
|
||||
touch $(AUTO_CONF)
|
||||
displayName: create auto.conf
|
||||
- template: .ci/templates/oe_localconf_externalsrc.yml@meta-ettus
|
||||
parameters:
|
||||
conf_file: $AUTO_CONF
|
||||
repository: self
|
||||
package_paths:
|
||||
- package: uhd
|
||||
path: host
|
||||
- package: mpmd
|
||||
path: mpm
|
||||
- ${{ if parameters.ext_fpga }}:
|
||||
- template: .ci/templates/oe_localconf_externalsrc_pipeline.yml@meta-ettus
|
||||
parameters:
|
||||
conf_file: $AUTO_CONF
|
||||
pipeline: uhd_mono_pipeline
|
||||
patterns: |
|
||||
usrp_e310_*_fpga.*
|
||||
usrp_e320_fpga_*.*
|
||||
usrp_n3??_fpga_*.*
|
||||
usrp_x4??_fpga_*.*
|
||||
packages:
|
||||
- uhd-fpga-images
|
||||
flatten: true
|
||||
- script: |
|
||||
ls -l $(Agent.BuildDirectory)/uhd_mono_pipeline
|
||||
displayName: list fpga images
|
||||
# If requested, we set a variable "UHD_BASE_URL", which is consumed by
|
||||
# the uhd-fpga-images recipe to an internal file server which is closer
|
||||
# to the build machines. It is simply appended to auto.conf, where
|
||||
# bitbake will pick it up and apply it to the recipe.
|
||||
- ${{ if contains(parameters.fpga_imgs_source, 'Internal') }}:
|
||||
- bash: |
|
||||
cat <<EOF >>$AUTO_CONF
|
||||
UHD_BASE_URL = "$FILE_SERVER_URL"
|
||||
EOF
|
||||
env:
|
||||
FILE_SERVER_URL: $(InternalFileServerUrl)
|
||||
- bash: |
|
||||
cat <<EOF >>$AUTO_CONF
|
||||
PREFERRED_UHD_VERSION = "4.11%"
|
||||
EOF
|
||||
postbuild_steps:
|
||||
- script: |
|
||||
rm $(AUTO_CONF)
|
||||
displayName: delete auto.conf
|
||||
condition: always()
|
||||
- template: stage-build-uhd-embedded-system-image.yml
|
||||
parameters:
|
||||
stage_name: build_uhd_embedded_system_images_e310_sg1
|
||||
display_name: "[e310_sg1] Build UHD Embedded System Images"
|
||||
machine_name: e310_sg1
|
||||
stage_condition: ${{ parameters.embedded_stage_condition_e3xx }}
|
||||
enabled: ${{ parameters.build_e310_sg1 }}
|
||||
create_sbom: ${{ parameters.create_sbom }}
|
||||
build_sdk: ${{ parameters.build_sdk }}
|
||||
cache_sstate: ${{ parameters.cache_sstate }}
|
||||
ext_fpga: ${{ parameters.ext_fpga }}
|
||||
fpga_imgs_source: ${{ parameters.fpga_imgs_source }}
|
||||
|
||||
- template: stage-build-uhd-embedded-system-image.yml
|
||||
parameters:
|
||||
stage_name: build_uhd_embedded_system_images_e310_sg3
|
||||
display_name: "[e310_sg3] Build UHD Embedded System Images"
|
||||
machine_name: e310_sg3
|
||||
stage_condition: ${{ parameters.embedded_stage_condition_e3xx }}
|
||||
enabled: ${{ parameters.build_e310_sg3 }}
|
||||
create_sbom: ${{ parameters.create_sbom }}
|
||||
build_sdk: ${{ parameters.build_sdk }}
|
||||
cache_sstate: ${{ parameters.cache_sstate }}
|
||||
ext_fpga: ${{ parameters.ext_fpga }}
|
||||
fpga_imgs_source: ${{ parameters.fpga_imgs_source }}
|
||||
|
||||
- template: stage-build-uhd-embedded-system-image.yml
|
||||
parameters:
|
||||
stage_name: build_uhd_embedded_system_images_e320
|
||||
display_name: "[e320] Build UHD Embedded System Images"
|
||||
machine_name: e320
|
||||
stage_condition: ${{ parameters.embedded_stage_condition_e3xx }}
|
||||
enabled: ${{ parameters.build_e320 }}
|
||||
create_sbom: ${{ parameters.create_sbom }}
|
||||
build_sdk: ${{ parameters.build_sdk }}
|
||||
cache_sstate: ${{ parameters.cache_sstate }}
|
||||
ext_fpga: ${{ parameters.ext_fpga }}
|
||||
fpga_imgs_source: ${{ parameters.fpga_imgs_source }}
|
||||
|
||||
- template: stage-build-uhd-embedded-system-image.yml
|
||||
parameters:
|
||||
stage_name: build_uhd_embedded_system_images_n3xx
|
||||
display_name: "[n3xx] Build UHD Embedded System Images"
|
||||
machine_name: n3xx
|
||||
stage_condition: ${{ parameters.embedded_stage_condition_n3xx }}
|
||||
enabled: ${{ parameters.build_n3xx }}
|
||||
create_sbom: ${{ parameters.create_sbom }}
|
||||
build_sdk: ${{ parameters.build_sdk }}
|
||||
cache_sstate: ${{ parameters.cache_sstate }}
|
||||
ext_fpga: ${{ parameters.ext_fpga }}
|
||||
fpga_imgs_source: ${{ parameters.fpga_imgs_source }}
|
||||
|
||||
- template: stage-build-uhd-embedded-system-image.yml
|
||||
parameters:
|
||||
stage_name: build_uhd_embedded_system_images_x4xx
|
||||
display_name: "[x4xx] Build UHD Embedded System Images"
|
||||
machine_name: x4xx
|
||||
stage_condition: ${{ parameters.embedded_stage_condition_x4xx }}
|
||||
enabled: ${{ parameters.build_x4xx }}
|
||||
create_sbom: ${{ parameters.create_sbom }}
|
||||
build_sdk: ${{ parameters.build_sdk }}
|
||||
cache_sstate: ${{ parameters.cache_sstate }}
|
||||
ext_fpga: ${{ parameters.ext_fpga }}
|
||||
fpga_imgs_source: ${{ parameters.fpga_imgs_source }}
|
||||
|
||||
- stage: package_uhd_src
|
||||
displayName: Package UHD Source
|
||||
@ -610,6 +669,25 @@ stages:
|
||||
fpga_imgs_source: 'Internal'
|
||||
create_sbom: ${{ parameters.create_sbom }}
|
||||
|
||||
- template: .ci/templates/stages-nisbom-uploads.yml@ettus-rts
|
||||
parameters:
|
||||
artifactSourceMode: current
|
||||
uhdArtifactSource: uhd_mono_pipeline
|
||||
releaseBinaries: false
|
||||
dependsOnGroups:
|
||||
fedoraInstaller:
|
||||
- build_uhd_installer_stage_fedora
|
||||
ubuntuInstaller:
|
||||
- build_uhd_installer_stage_ubuntu
|
||||
winInstaller:
|
||||
- build_uhd_installer_stage_win
|
||||
embeddedImages:
|
||||
- build_uhd_embedded_system_images_e310_sg1
|
||||
- build_uhd_embedded_system_images_e310_sg3
|
||||
- build_uhd_embedded_system_images_e320
|
||||
- build_uhd_embedded_system_images_n3xx
|
||||
- build_uhd_embedded_system_images_x4xx
|
||||
|
||||
###############################################################################
|
||||
# FPGA Stages. Here, we build bitfiles (or at least download the right artifacts)
|
||||
###############################################################################
|
||||
@ -626,7 +704,7 @@ stages:
|
||||
jobs:
|
||||
- template: ../fpga/templates/job-run-testbenches.yml
|
||||
parameters:
|
||||
simulator: modelsim
|
||||
simulator: qsim
|
||||
- template: ../fpga/templates/job-run-testbenches.yml
|
||||
parameters:
|
||||
simulator: xsim
|
||||
@ -727,6 +805,7 @@ stages:
|
||||
parameters:
|
||||
runSimulation: true
|
||||
publish: "false"
|
||||
requestCiPublishPermission: false
|
||||
|
||||
###############################################################################
|
||||
# Hardware Test Stages. These require having valid software and FPGA images.
|
||||
@ -764,7 +843,8 @@ stages:
|
||||
dependsOn:
|
||||
- analyze_changeset
|
||||
- build_uhd_stage_linux
|
||||
- build_uhd_embedded_system_images
|
||||
- build_uhd_embedded_system_images_n3xx
|
||||
- build_uhd_embedded_system_images_e320
|
||||
- provide_n300_fpga_images_stage
|
||||
- provide_n310_fpga_images_stage
|
||||
- provide_n320_fpga_images_stage
|
||||
@ -774,7 +854,7 @@ stages:
|
||||
succeeded(),
|
||||
or(
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.all'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.n3'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.n3xx'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.e320')
|
||||
)
|
||||
)
|
||||
@ -790,7 +870,7 @@ stages:
|
||||
dependsOn:
|
||||
- analyze_changeset
|
||||
- build_uhd_stage_linux
|
||||
- build_uhd_embedded_system_images
|
||||
- build_uhd_embedded_system_images_x4xx
|
||||
- build_gnuradio_stage_linux
|
||||
- provide_x410_fpga_images_stage
|
||||
- provide_x440_fpga_images_stage
|
||||
@ -829,7 +909,7 @@ stages:
|
||||
dependsOn:
|
||||
- analyze_changeset
|
||||
- build_uhd_stage_linux
|
||||
- build_uhd_embedded_system_images
|
||||
- build_uhd_embedded_system_images_x4xx
|
||||
- build_gnuradio_stage_linux
|
||||
- provide_x410_fpga_images_stage
|
||||
- provide_x440_fpga_images_stage
|
||||
@ -853,6 +933,7 @@ stages:
|
||||
fpga_imgs_source: 'Pipeline'
|
||||
${{ else }}:
|
||||
fpga_imgs_source: 'Filesystem'
|
||||
runX410X4400RfSubset: true
|
||||
testLength: ${{ parameters.testLength }}
|
||||
- template: tests/job-uhd-x440-hardware-tests-pebbles.yml
|
||||
parameters:
|
||||
@ -867,7 +948,7 @@ stages:
|
||||
dependsOn:
|
||||
- analyze_changeset
|
||||
- build_uhd_stage_linux
|
||||
- build_uhd_embedded_system_images
|
||||
- build_uhd_embedded_system_images_x4xx
|
||||
- build_gnuradio_stage_linux
|
||||
- provide_x420_fpga_images_stage
|
||||
# This will make $(UhdTestList) available to jobs/steps/tasks, but not for the
|
||||
@ -892,12 +973,16 @@ stages:
|
||||
${{ else }}:
|
||||
fpga_imgs_source: 'Filesystem'
|
||||
testLength: ${{ parameters.testLength }}
|
||||
runPyTest: true
|
||||
runPyTestNonRF: true
|
||||
runDevTest: true
|
||||
runSystemImageTest: true
|
||||
|
||||
- stage: test_uhd_x4xx_saison_stage
|
||||
displayName: RF Test UHD x4xx saison
|
||||
dependsOn:
|
||||
- build_uhd_stage_linux
|
||||
- build_uhd_embedded_system_images
|
||||
- build_uhd_embedded_system_images_x4xx
|
||||
- build_gnuradio_stage_linux
|
||||
- analyze_changeset
|
||||
- provide_x440_fpga_images_stage
|
||||
@ -929,7 +1014,7 @@ stages:
|
||||
dependsOn:
|
||||
- analyze_changeset
|
||||
- build_uhd_stage_linux
|
||||
- build_uhd_embedded_system_images
|
||||
- build_uhd_embedded_system_images_n3xx
|
||||
- build_gnuradio_stage_linux
|
||||
- provide_n300_fpga_images_stage
|
||||
- provide_n310_fpga_images_stage
|
||||
@ -979,36 +1064,48 @@ stages:
|
||||
uhdSrcDir: $(Build.SourcesDirectory)/uhddev
|
||||
fpga_imgs_source: ${{ parameters.fpga_imgs_source }}
|
||||
|
||||
- stage: test_streaming_beauty_stage
|
||||
displayName: Test UHD Streaming beauty
|
||||
- stage: test_uhd_b310_stage
|
||||
displayName: Test UHD B310
|
||||
dependsOn:
|
||||
- analyze_changeset
|
||||
- build_uhd_stage_linux
|
||||
- build_uhd_embedded_system_images
|
||||
- provide_x300_fpga_images_stage
|
||||
- provide_x310_fpga_images_stage
|
||||
- provide_x410_fpga_images_stage
|
||||
- build_gnuradio_stage_linux
|
||||
- provide_b310_fpga_images_stage
|
||||
condition: >
|
||||
and(
|
||||
succeeded(),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.streaming')
|
||||
or(
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.rf.all'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.rf.b3xx'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.all'),
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'devtest.b3xx')
|
||||
)
|
||||
)
|
||||
jobs:
|
||||
- template: job-uhd-streaming-tests-beauty.yml
|
||||
- template: job-uhd-devtest-b310.yml
|
||||
parameters:
|
||||
uhdSrcDir: $(Build.SourcesDirectory)
|
||||
fpga_imgs_source: ${{ parameters.fpga_imgs_source }}
|
||||
- template: job-uhd-rf-tests-b310.yml
|
||||
parameters:
|
||||
uhdSrcDir: $(Build.SourcesDirectory)/uhddev
|
||||
testLength: ${{ parameters.testLength }}
|
||||
- template: job-uhd-streaming-tests-s175.yml
|
||||
parameters:
|
||||
uhdSrcDir: $(Build.SourcesDirectory)/uhddev
|
||||
testLength: ${{ parameters.testLength }}
|
||||
fpga_imgs_source: ${{ parameters.fpga_imgs_source }}
|
||||
|
||||
- stage: test_streaming_x440_stage
|
||||
displayName: Test UHD Streaming x440
|
||||
- stage: uhd_streaming_tests_stage
|
||||
displayName: UHD Streaming Tests
|
||||
dependsOn:
|
||||
- analyze_changeset
|
||||
- build_uhd_stage_linux
|
||||
- build_uhd_embedded_system_images
|
||||
- build_uhd_embedded_system_images_e320
|
||||
- build_uhd_embedded_system_images_n3xx
|
||||
- build_uhd_embedded_system_images_x4xx
|
||||
- provide_e320_fpga_images_stage
|
||||
- provide_n300_fpga_images_stage
|
||||
- provide_n310_fpga_images_stage
|
||||
- provide_n320_fpga_images_stage
|
||||
- provide_x300_fpga_images_stage
|
||||
- provide_x310_fpga_images_stage
|
||||
- provide_x410_fpga_images_stage
|
||||
- provide_x440_fpga_images_stage
|
||||
condition: >
|
||||
and(
|
||||
@ -1016,16 +1113,17 @@ stages:
|
||||
contains(dependencies.analyze_changeset.outputs['analyze.gen_testlist.UhdTestList'], 'hw.streaming')
|
||||
)
|
||||
jobs:
|
||||
- template: job-uhd-streaming-tests-x440.yml
|
||||
- template: job-uhd-streaming-tests.yml
|
||||
parameters:
|
||||
uhdSrcDir: $(Build.SourcesDirectory)/uhddev
|
||||
testLength: ${{ parameters.testLength }}
|
||||
selectedTests: ${{ split(replace(parameters.selected_streaming_tests, ' ', ''), ',') }}
|
||||
|
||||
- stage: test_labview_usrp_examples
|
||||
displayName: Test LabVIEW USRP Examples
|
||||
dependsOn:
|
||||
- analyze_changeset
|
||||
- build_uhd_embedded_system_images
|
||||
- build_uhd_embedded_system_images_x4xx
|
||||
- build_uhd_installer_stage_win
|
||||
- provide_x410_fpga_images_stage
|
||||
jobs:
|
||||
|
||||
@ -35,6 +35,21 @@ parameters:
|
||||
|
||||
steps:
|
||||
- script: |
|
||||
setlocal enabledelayedexpansion
|
||||
echo pythonVersion=$(pythonVersion)
|
||||
if "$(pythonVersion)"=="python310" (
|
||||
set pyVersionType=1
|
||||
) else if "$(pythonVersion)"=="python311" (
|
||||
set pyVersionType=1
|
||||
) else if "$(pythonVersion)"=="python312" (
|
||||
set pyVersionType=1
|
||||
) else (
|
||||
set pyVersionType=2
|
||||
)
|
||||
echo pyVersionType=!pyVersionType!
|
||||
echo ##vso[task.setvariable variable=PYVERSIONTYPE]!pyVersionType!
|
||||
|
||||
echo environment var VCPKG_TARGET_TRIPLET=%VCPKG_TARGET_TRIPLET%
|
||||
call ${{ parameters.uhdSrcDir }}\.ci\utils\extract-vs-version.bat "$(cmakeCompiler)"
|
||||
copy /y ${{ parameters.uhdSrcDir }}\.ci\docker\vcpkg\%vcpkgManifestFile% vcpkg.json
|
||||
%VCPKG_INSTALL_DIR%\vcpkg.exe install --triplet uhd-x64-windows-static-md --clean-after-build
|
||||
@ -43,7 +58,14 @@ steps:
|
||||
|
||||
- script: |
|
||||
pip config list
|
||||
pip install -r ${{ parameters.uhdSrcDir }}\host\python\requirements.txt
|
||||
python -m pip install --upgrade pip
|
||||
if "%PYVERSIONTYPE%"=="1" (
|
||||
pip install -r ${{ parameters.uhdSrcDir }}\host\python\requirements.txt
|
||||
) else (
|
||||
echo Using numpy 2.x requirements for python313 and above
|
||||
pip install -r ${{ parameters.uhdSrcDir }}\host\python\requirements_np2.txt
|
||||
pip install "pybind11>=2.12"
|
||||
)
|
||||
displayName: refresh python dependencies
|
||||
|
||||
- script: |
|
||||
@ -53,7 +75,8 @@ steps:
|
||||
-DVCPKG_TARGET_TRIPLET=uhd-$(vsArch)-windows-static-md ^
|
||||
-DCMAKE_TOOLCHAIN_FILE=%VCPKG_INSTALL_DIR%/scripts/buildsystems/vcpkg.cmake ^
|
||||
-DVCPKG_INSTALLED_DIR=${{ parameters.uhdVcpkgManifestDir }}\vcpkg_installed ^
|
||||
-DVCPKG_MANIFEST_DIR=${{ parameters.uhdVcpkgManifestDir }}
|
||||
-DVCPKG_MANIFEST_DIR=${{ parameters.uhdVcpkgManifestDir }} ^
|
||||
-DVCPKG_MANIFEST_INSTALL=OFF
|
||||
mkdir ${{ parameters.uhdImageDir }}
|
||||
python ${{ parameters.uhdBuildDir }}/utils/uhd_images_downloader.py -i ${{ parameters.uhdImageDir }} ${{ parameters.uhdImagesDownloaderExtraArgs }}
|
||||
displayName: Download uhd-images
|
||||
@ -84,6 +107,7 @@ steps:
|
||||
-DCMAKE_TOOLCHAIN_FILE=%VCPKG_INSTALL_DIR%/scripts/buildsystems/vcpkg.cmake ^
|
||||
-DVCPKG_INSTALLED_DIR=${{ parameters.uhdVcpkgManifestDir }}\vcpkg_installed ^
|
||||
-DVCPKG_MANIFEST_DIR=${{ parameters.uhdVcpkgManifestDir }} ^
|
||||
-DVCPKG_MANIFEST_INSTALL=OFF ^
|
||||
-DUHD_IMAGES_SRC_DIR="${{ parameters.uhdImageDir }}" ^
|
||||
-DUHD_IMAGES_DIR="C:\\Program Files (x86)\\UHD\\share\\uhd\\images,C:\\Program Files\\UHD\\share\\uhd\\images" ^
|
||||
-DENABLE_EXTEND_WIN_PACKAGING=ON ^
|
||||
|
||||
@ -8,13 +8,25 @@ parameters:
|
||||
- name: cmake_args
|
||||
type: string
|
||||
default: " "
|
||||
- name: nib310rioDebPackageDir
|
||||
type: string
|
||||
default: ""
|
||||
|
||||
steps:
|
||||
- script: |
|
||||
set -euo pipefail
|
||||
sudo pbuilder create --debootstrapopts --variant=buildd --distribution ${{ parameters.ubuntuReleaseName }}
|
||||
displayName: Setup pbuilder
|
||||
- script: |
|
||||
set -euo pipefail
|
||||
mkdir -p ${{ parameters.uhdInstallerDir }}
|
||||
cd ${{ parameters.uhdSrcDir }}
|
||||
python3 tools/debs/uhd_ubuntu_deb.py ${{ parameters.ubuntuReleaseName }} --buildpath ${{ parameters.uhdInstallerDir }}
|
||||
pbuilder_args=()
|
||||
if [[ -n "${{ parameters.nib310rioDebPackageDir }}" ]]; then
|
||||
pushd "${{ parameters.nib310rioDebPackageDir }}"
|
||||
dpkg-scanpackages . /dev/null | gzip -9c > Packages.gz
|
||||
popd
|
||||
pbuilder_args+=(--pbuilder-extra-apt-repo "${{ parameters.nib310rioDebPackageDir }}")
|
||||
fi
|
||||
python3 tools/debs/uhd_ubuntu_deb.py ${{ parameters.ubuntuReleaseName }} --buildpath ${{ parameters.uhdInstallerDir }} "${pbuilder_args[@]}"
|
||||
displayName: Build dsc and debs
|
||||
|
||||
@ -51,6 +51,7 @@ steps:
|
||||
cd ${{ parameters.uhdBuildDir }}
|
||||
pyenv exec python3 -m venv ${{ parameters.uhdBuildDir }}/pyenv
|
||||
source ${{ parameters.uhdBuildDir }}/pyenv/bin/activate
|
||||
python3 -m pip install --upgrade pip
|
||||
python3 -m pip install mako numpy requests ruamel.yaml
|
||||
displayName: Install python UHD deps
|
||||
|
||||
|
||||
10
.ci/templates/steps-create-pytest-venv.yml
Normal file
10
.ci/templates/steps-create-pytest-venv.yml
Normal file
@ -0,0 +1,10 @@
|
||||
steps:
|
||||
- script: |
|
||||
cd $(Agent.TempDirectory)
|
||||
python3 -m venv pytest_venv
|
||||
source pytest_venv/bin/activate
|
||||
echo '##vso[task.setvariable variable=PYTEST_VENV]'$(realpath pytest_venv)
|
||||
python3 -m pip install -U pip
|
||||
pip3 install -r $(Build.SourcesDirectory)/ettus-rts/config/remote/streaming/requirements.txt
|
||||
displayName: Create pytest venv
|
||||
|
||||
24
.ci/templates/steps-install-nib310rio-dev.yml
Normal file
24
.ci/templates/steps-install-nib310rio-dev.yml
Normal file
@ -0,0 +1,24 @@
|
||||
steps:
|
||||
- download: nib310rio_kernel_packages
|
||||
artifact: nib310rio-deb-packages
|
||||
displayName: Download nib310rio-dev deb
|
||||
condition: and(succeeded(), contains(variables['buildOSName'], 'ubuntu'))
|
||||
|
||||
- download: nib310rio_kernel_packages
|
||||
artifact: nib310rio-rpm-packages
|
||||
displayName: Download nib310rio-dev rpm
|
||||
condition: and(succeeded(), contains(variables['buildOSName'], 'fedora'))
|
||||
|
||||
- script: |
|
||||
set -euo pipefail
|
||||
if command -v dpkg >/dev/null 2>&1; then
|
||||
pkg=$(find "$(Pipeline.Workspace)/nib310rio_kernel_packages/nib310rio-deb-packages" \
|
||||
-type f -name 'nib310rio-dev_*_all.deb' | sort -V | tail -n 1)
|
||||
sudo dpkg -i "$pkg"
|
||||
else
|
||||
# Search only RPMS/ to avoid picking up the source RPM from SRPMS/
|
||||
pkg=$(find "$(Pipeline.Workspace)/nib310rio_kernel_packages/nib310rio-rpm-packages/RPMS" \
|
||||
-type f -name 'nib310rio-dev-*.rpm' | sort -V | tail -n 1)
|
||||
sudo rpm -i "$pkg"
|
||||
fi
|
||||
displayName: Install nib310rio-dev
|
||||
@ -60,6 +60,7 @@ steps:
|
||||
python3 -m venv rfnoc-block-test-venv
|
||||
source rfnoc-block-test-venv/bin/activate
|
||||
TEST_DIR=$(Build.SourcesDirectory)/uhddev/host/tests/rfnoc_block_tests_on_target
|
||||
python3 -m pip install --upgrade pip
|
||||
pip install -r $TEST_DIR/requirements.txt
|
||||
pytest --args ${{ parameters.uhdArgs }} $TEST_DIR/${{ parameters.testSelector }} -vv --junitxml=$(Common.TestResultsDirectory)/${{ parameters.resultFileNameBase }}.xml
|
||||
displayName: "${{ parameters.resultFileNameBase }}: Run test"
|
||||
|
||||
@ -13,6 +13,9 @@ parameters:
|
||||
- name: extra_rf_test_args
|
||||
type: string
|
||||
default: ''
|
||||
- name: runX410X4400RfSubset
|
||||
type: boolean
|
||||
default: false
|
||||
- name: testLength
|
||||
type: string
|
||||
|
||||
@ -25,6 +28,7 @@ jobs:
|
||||
runPyTestNonRF: false
|
||||
runDevTest: false
|
||||
runSystemImageTest: false
|
||||
runX410X4400RfSubset: ${{ and(parameters.runX410X4400RfSubset, eq(parameters.fpga_imgs_source, 'Pipeline')) }}
|
||||
uhdArtifactSource: ${{ parameters.uhdArtifactSource }}
|
||||
uhdFpgaArtifactSource: ${{ parameters.uhdFpgaArtifactSource }}
|
||||
fpga_imgs_source: ${{ parameters.fpga_imgs_source }}
|
||||
@ -40,5 +44,6 @@ jobs:
|
||||
uartSerial: $(x410_uartSerial)
|
||||
pipelineAgent: pebbles-agent-1
|
||||
pytestAtsConfig: uhd_oss_ats
|
||||
pytestConfigDut: 'x410-400'
|
||||
pytestDUT: 'x410'
|
||||
sfp0ip: '192.168.30.62'
|
||||
|
||||
@ -12,57 +12,75 @@ parameters:
|
||||
default: ''
|
||||
- name: testLength
|
||||
type: string
|
||||
- name: runPyTest
|
||||
type: boolean
|
||||
default: true
|
||||
- name: runPyTestNonRF
|
||||
type: boolean
|
||||
default: true
|
||||
- name: runDevTest
|
||||
type: boolean
|
||||
default: true
|
||||
- name: runSystemImageTest
|
||||
type: boolean
|
||||
default: true
|
||||
- name: forceIQCorrections
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
- template: templates/job-uhd-x4xx-hardware-tests.yml
|
||||
parameters:
|
||||
suiteName: 'dre_s175_x420_3'
|
||||
toolset: 'make'
|
||||
runPyTest: true
|
||||
runPyTestNonRF: false
|
||||
runDevTest: false
|
||||
runSystemImageTest: false
|
||||
master_clock_rate: '245.76e6'
|
||||
uhdArtifactSource: ${{ parameters.uhdArtifactSource }}
|
||||
uhdFpgaArtifactSource: ${{ parameters.uhdFpgaArtifactSource }}
|
||||
fpga_imgs_source: ${{ parameters.fpga_imgs_source }}
|
||||
extra_rf_test_args: '${{ parameters.extra_rf_test_args }}'
|
||||
testLength: '${{ parameters.testLength }}'
|
||||
dutMatrix:
|
||||
dre_s175-x420-3:
|
||||
dutName: 'dre_s175-x420-3'
|
||||
dutType: 'x4xx'
|
||||
devtestPattern: 'x420'
|
||||
dutFPGA: 'CG_1000'
|
||||
dutEmbeddedImagesArtifact: 'x4xx-images'
|
||||
uartSerial: '$(x420_uartSerial)'
|
||||
pipelineAgent: dre-s175-x420-3
|
||||
pytestAtsConfig: ec21_ats
|
||||
pytestDUT: 'x420'
|
||||
sfp0ip: '$(x420_dutAddr)'
|
||||
- template: templates/job-uhd-x4xx-hardware-tests.yml
|
||||
parameters:
|
||||
suiteName: 'dre_s175_x420_4'
|
||||
toolset: 'make'
|
||||
runPyTest: false
|
||||
runPyTestNonRF: true
|
||||
runDevTest: true
|
||||
runSystemImageTest: true
|
||||
master_clock_rate: '245.76e6'
|
||||
uhdArtifactSource: ${{ parameters.uhdArtifactSource }}
|
||||
uhdFpgaArtifactSource: ${{ parameters.uhdFpgaArtifactSource }}
|
||||
fpga_imgs_source: ${{ parameters.fpga_imgs_source }}
|
||||
extra_rf_test_args: '${{ parameters.extra_rf_test_args }}'
|
||||
testLength: '${{ parameters.testLength }}'
|
||||
dutMatrix:
|
||||
dre_s175-x420-4:
|
||||
dutName: 'dre_s175-x420-4'
|
||||
dutType: 'x4xx'
|
||||
devtestPattern: 'x420'
|
||||
dutFPGA: 'CG_1000'
|
||||
dutEmbeddedImagesArtifact: 'x4xx-images'
|
||||
uartSerial: '$(x420_uartSerial)'
|
||||
pipelineAgent: dre-s175-x420-4
|
||||
pytestAtsConfig: dre_s175_ats
|
||||
pytestDUT: 'x420'
|
||||
sfp0ip: '$(x420_dutAddr)'
|
||||
- ${{ if parameters.runPyTest }}:
|
||||
- template: templates/job-uhd-x4xx-hardware-tests.yml
|
||||
parameters:
|
||||
suiteName: 'dre_s175_x420_3'
|
||||
toolset: 'make'
|
||||
runPyTest: true
|
||||
runPyTestNonRF: false
|
||||
runDevTest: false
|
||||
runSystemImageTest: false
|
||||
forceIQCorrections: ${{ parameters.forceIQCorrections }}
|
||||
master_clock_rate: '245.76e6'
|
||||
uhdArtifactSource: ${{ parameters.uhdArtifactSource }}
|
||||
uhdFpgaArtifactSource: ${{ parameters.uhdFpgaArtifactSource }}
|
||||
fpga_imgs_source: ${{ parameters.fpga_imgs_source }}
|
||||
extra_rf_test_args: '${{ parameters.extra_rf_test_args }}'
|
||||
testLength: '${{ parameters.testLength }}'
|
||||
dutMatrix:
|
||||
dre_s175-x420-3:
|
||||
dutName: 'dre_s175-x420-3'
|
||||
dutType: 'x4xx'
|
||||
devtestPattern: 'x420'
|
||||
dutFPGA: 'CG_1000'
|
||||
dutEmbeddedImagesArtifact: 'x4xx-images'
|
||||
uartSerial: '$(x420_uartSerial)'
|
||||
pipelineAgent: dre-s175-x420-3
|
||||
pytestAtsConfig: ec21_ats
|
||||
pytestDUT: 'x420'
|
||||
sfp0ip: '$(x420_dutAddr)'
|
||||
- ${{ if or(parameters.runPyTestNonRF, parameters.runDevTest, parameters.runSystemImageTest) }}:
|
||||
- template: templates/job-uhd-x4xx-hardware-tests.yml
|
||||
parameters:
|
||||
suiteName: 'dre_s175_x420_4'
|
||||
toolset: 'make'
|
||||
runPyTest: false
|
||||
runPyTestNonRF: ${{ parameters.runPyTestNonRF }}
|
||||
runDevTest: ${{ parameters.runDevTest }}
|
||||
runSystemImageTest: ${{ parameters.runSystemImageTest }}
|
||||
master_clock_rate: '245.76e6'
|
||||
uhdArtifactSource: ${{ parameters.uhdArtifactSource }}
|
||||
uhdFpgaArtifactSource: ${{ parameters.uhdFpgaArtifactSource }}
|
||||
fpga_imgs_source: ${{ parameters.fpga_imgs_source }}
|
||||
extra_rf_test_args: '${{ parameters.extra_rf_test_args }}'
|
||||
testLength: '${{ parameters.testLength }}'
|
||||
dutMatrix:
|
||||
dre_s175-x420-4:
|
||||
dutName: 'dre_s175-x420-4'
|
||||
dutType: 'x4xx'
|
||||
devtestPattern: 'x420'
|
||||
dutFPGA: 'CG_1000'
|
||||
dutEmbeddedImagesArtifact: 'x4xx-images'
|
||||
uartSerial: '$(x420_uartSerial)'
|
||||
pipelineAgent: dre-s175-x420-4
|
||||
pytestAtsConfig: dre_s175_ats
|
||||
pytestDUT: 'x420'
|
||||
sfp0ip: '$(x420_dutAddr)'
|
||||
|
||||
@ -18,6 +18,9 @@ parameters:
|
||||
- name: runRfnocBlockTest
|
||||
type: boolean
|
||||
default: false
|
||||
- name: runX410X4400RfSubset
|
||||
type: boolean
|
||||
default: false
|
||||
- name: forceIQCorrections
|
||||
type: boolean
|
||||
default: false
|
||||
@ -249,7 +252,7 @@ jobs:
|
||||
ssh-keygen -f ~/.ssh/known_hosts -R $USRP_EMB_TARGET_IP
|
||||
ssh -o StrictHostKeyChecking=no -tt root@$USRP_EMB_TARGET_IP "rm -rf ~/usrp_sysimg_test && mkdir -p ~/usrp_sysimg_test"
|
||||
scp -o StrictHostKeyChecking=no -r $(Build.SourcesDirectory)/meta-ettus-dev/contrib/test/tests root@$USRP_EMB_TARGET_IP:~/usrp_sysimg_test
|
||||
ssh -o StrictHostKeyChecking=no -tt root@$USRP_EMB_TARGET_IP "pip3 install unittest-xml-reporting && cd ~/usrp_sysimg_test/tests && python3 -m xmlrunner discover . -v"
|
||||
ssh -o StrictHostKeyChecking=no -tt root@$USRP_EMB_TARGET_IP "python3 -m pip install -U pip && pip3 install unittest-xml-reporting && cd ~/usrp_sysimg_test/tests && python3 -m xmlrunner discover . -v"
|
||||
EXITCODE=$?
|
||||
scp -o StrictHostKeyChecking=no -r root@$USRP_EMB_TARGET_IP:~/usrp_sysimg_test .
|
||||
exit $EXITCODE
|
||||
@ -296,6 +299,7 @@ jobs:
|
||||
cd $(Agent.TempDirectory)
|
||||
python3 -m venv devtest_venv
|
||||
source devtest_venv/bin/activate
|
||||
python3 -m pip install --upgrade pip
|
||||
pip3 install -r $(Build.SourcesDirectory)/uhddev/host/tests/devtest/requirements.txt
|
||||
cd $(Common.TestResultsDirectory)/devtest
|
||||
python3 $(Build.SourcesDirectory)/uhddev/host/tests/devtest/run_testsuite.py \
|
||||
@ -342,6 +346,7 @@ jobs:
|
||||
|
||||
# Activate virtual environment and install dependencies
|
||||
source $USRP_EMB_VENV/bin/activate
|
||||
python3 -m pip install --upgrade pip
|
||||
pip3 install -r $(Build.SourcesDirectory)/ettus-rts/.ci/utils/x420_iq_dc_correction_requirements.txt
|
||||
|
||||
# Run the X420 IQ correction script
|
||||
@ -385,7 +390,7 @@ jobs:
|
||||
mkdir -p $(Common.TestResultsDirectory)/pytest/embedded
|
||||
cd $(Common.TestResultsDirectory)/pytest/embedded
|
||||
ssh-keygen -f ~/.ssh/known_hosts -R $USRP_EMB_TARGET_IP
|
||||
ssh -o StrictHostKeyChecking=no -tt root@$USRP_EMB_TARGET_IP "pip3 install --upgrade 'pytest<8.4.0' pytest-helpers-namespace pytest-tempdir pytest-salt"
|
||||
ssh -o StrictHostKeyChecking=no -tt root@$USRP_EMB_TARGET_IP "python3 -m pip install -U pip && pip3 install --upgrade 'pytest<8.4.0' pytest-helpers-namespace pytest-tempdir pytest-salt"
|
||||
ssh -o StrictHostKeyChecking=no -tt root@$USRP_EMB_TARGET_IP "rm -rf ~/usrp_pytest && mkdir -p ~/usrp_pytest"
|
||||
scp -o StrictHostKeyChecking=no -r $(Build.SourcesDirectory)/ettus-rts/config/remote/ root@$USRP_EMB_TARGET_IP:~/usrp_pytest
|
||||
ssh -o StrictHostKeyChecking=no -tt root@$USRP_EMB_TARGET_IP "cd ~/usrp_pytest/remote/python_tests/pytests && \
|
||||
@ -420,6 +425,37 @@ jobs:
|
||||
resultFileNameBase: 'complex_gain_block_test'
|
||||
testSelector: 'test_radio_control_complex_gain.py'
|
||||
|
||||
- ${{ if parameters.runX410X4400RfSubset }}:
|
||||
- template: ../../steps-download-fpga-artifact.yml
|
||||
parameters:
|
||||
dutType: $(pytestDUT)
|
||||
dutFPGA: 'X4_400'
|
||||
fpgaArtifactSource: ${{ parameters.uhdFpgaArtifactSource }}
|
||||
fpga_imgs_source: ${{ parameters.fpga_imgs_source }}
|
||||
|
||||
- script: |
|
||||
UHD_INSTALL_PATH=$(uhd_artifact_directory)/uhddev-$(testOS)-${{ parameters.toolset }}/uhddev/build-installed
|
||||
export PATH=$UHD_INSTALL_PATH/bin:$PATH
|
||||
if [ -d "$UHD_INSTALL_PATH/lib64" ]; then
|
||||
export LD_LIBRARY_PATH=$UHD_INSTALL_PATH/lib64:$LD_LIBRARY_PATH
|
||||
else
|
||||
export LD_LIBRARY_PATH=$UHD_INSTALL_PATH/lib:$LD_LIBRARY_PATH
|
||||
fi
|
||||
uhd_image_loader --args type=$(dutType),addr=$(USRP_EMB_TARGET_IP) \
|
||||
--fpga=$FPGA_ARTIFACT_DIR/$FPGA_BITFILE_NAME.bit
|
||||
displayName: 'X410/X4_400 RF subset: Load FPGA on USRP'
|
||||
|
||||
- template: step-uhd-run-pytest-on-host.yml
|
||||
parameters:
|
||||
runPyTest: true
|
||||
displayString: 'X410/X4_400 RF subset'
|
||||
testFolder: 'pytests'
|
||||
pytestConfigDut: $(pytestConfigDut)
|
||||
testSelector: 'test_ddc_response.py test_rx_offset_tone_sweep.py test_tx_offset_tone_sweep.py test_tx_evm.py test_rx_evm.py'
|
||||
testLength: ${{ parameters.testLength }}
|
||||
warmUpAfterBoot: ${{ parameters.warmUpAfterBoot }}
|
||||
toolset: ${{ parameters.toolset }}
|
||||
|
||||
# Run the mender tests last because they
|
||||
# modify the flashed image on the device
|
||||
- script: |
|
||||
|
||||
@ -10,6 +10,12 @@ parameters:
|
||||
- name: extra_rf_test_args
|
||||
type: string
|
||||
default: ''
|
||||
- name: pytestConfigDut
|
||||
type: string
|
||||
default: '$(pytestDUT)'
|
||||
- name: testSelector
|
||||
type: string
|
||||
default: '.'
|
||||
- name: testLength
|
||||
type: string
|
||||
default: 'smoke'
|
||||
@ -69,7 +75,7 @@ steps:
|
||||
source $(Build.SourcesDirectory)/ettus-rts/config/remote/python_tests/setup_venv.sh
|
||||
|
||||
cd $(Build.SourcesDirectory)/ettus-rts/config/remote/python_tests/${{ parameters.testFolder }}
|
||||
python3 ../automated_main.py --test_selector . --ats_config $(pytestAtsConfig) --dut $(pytestDUT) --run_vars_overrides 'mgmt_addr=$(USRP_EMB_TARGET_IP)' --results_path '$(Common.TestResultsDirectory)/pytest/host' --test_length ${{ parameters.testLength }} ${{ parameters.extra_rf_test_args }}
|
||||
python3 ../automated_main.py --test_selector ${{ parameters.testSelector }} --ats_config $(pytestAtsConfig) --dut ${{ parameters.pytestConfigDut }} --run_vars_overrides 'mgmt_addr=$(USRP_EMB_TARGET_IP)' --results_path '$(Common.TestResultsDirectory)/pytest/host' --test_length ${{ parameters.testLength }} ${{ parameters.extra_rf_test_args }}
|
||||
displayName: Run ${{ parameters.displayString }} on host
|
||||
continueOnError: true
|
||||
condition: and(succeeded(), ${{ parameters.runPyTest }})
|
||||
|
||||
@ -37,6 +37,14 @@ parameters:
|
||||
type: boolean
|
||||
displayName: Build Windows images
|
||||
default: true
|
||||
- name: prune_docker_images
|
||||
type: boolean
|
||||
displayName: Prune dangling Docker images before Windows build
|
||||
default: false
|
||||
- name: use_vcpkg_cache
|
||||
type: boolean
|
||||
displayName: Use vcpkg binary cache for Windows build
|
||||
default: true
|
||||
|
||||
variables:
|
||||
- template: uhd-pipeline-vars.yml
|
||||
@ -49,7 +57,8 @@ variables:
|
||||
'Ubuntu-2004-builder':{'buildOSName':'ubuntu2004','dockerImageName':'uhd-builder-ubuntu2004','releaseName':'focal'},
|
||||
'Ubuntu-2204-builder':{'buildOSName':'ubuntu2204','dockerImageName':'uhd-builder-ubuntu2204','releaseName':'jammy'},
|
||||
'Ubuntu-2404-builder':{'buildOSName':'ubuntu2404','dockerImageName':'uhd-builder-ubuntu2404','releaseName':'noble'},
|
||||
'Ubuntu-2510-builder':{'buildOSName':'ubuntu2510','dockerImageName':'uhd-builder-ubuntu2510','releaseName':'questing'}}"
|
||||
'Ubuntu-2510-builder':{'buildOSName':'ubuntu2510','dockerImageName':'uhd-builder-ubuntu2510','releaseName':'questing'},
|
||||
'Ubuntu-2604-builder':{'buildOSName':'ubuntu2604','dockerImageName':'uhd-builder-ubuntu2604','releaseName':'resolute'}}"
|
||||
- ${{ else }}:
|
||||
- name: linuxDockerImages
|
||||
value:
|
||||
@ -58,6 +67,7 @@ variables:
|
||||
value:
|
||||
"{'VS2022-x64-py310-builder':{'buildOSName':'win-vs22-v143-x64-py310','dockerImageName':'uhd-builder-vs2022-v143-x64-py310','cmakeCompiler':'Visual Studio 17 2022','cmakeArch':'x64','vsArch':'x64','vsYear':'2022','pythonVersion':'python310'},
|
||||
'VS2022-x64-py312-builder':{'buildOSName':'win-vs22-v143-x64-py312','dockerImageName':'uhd-builder-vs2022-v143-x64-py312','cmakeCompiler':'Visual Studio 17 2022','cmakeArch':'x64','vsArch':'x64','vsYear':'2022','pythonVersion':'python312'},
|
||||
'VS2022-x64-py313-builder':{'buildOSName':'win-vs22-v143-x64-py313','dockerImageName':'uhd-builder-vs2022-v143-x64-py313','cmakeCompiler':'Visual Studio 17 2022','cmakeArch':'x64','vsArch':'x64','vsYear':'2022','pythonVersion':'python313'},
|
||||
'VS2026-x64-py310-builder':{'buildOSName':'win-vs26-v145-x64-py310','dockerImageName':'uhd-builder-vs2026-v145-x64-py310','cmakeCompiler':'Visual Studio 18 2026','cmakeArch':'x64','vsArch':'x64','vsYear':'2026','pythonVersion':'python310'},
|
||||
'VS2026-x64-py312-builder':{'buildOSName':'win-vs26-v145-x64-py312','dockerImageName':'uhd-builder-vs2026-v145-x64-py312','cmakeCompiler':'Visual Studio 18 2026','cmakeArch':'x64','vsArch':'x64','vsYear':'2026','pythonVersion':'python312'}}"
|
||||
# temporarily exclude py313 Windows builder
|
||||
@ -67,7 +77,7 @@ variables:
|
||||
- name: python312_version
|
||||
value: '3.12.10'
|
||||
- name: python313_version
|
||||
value: '3.13.11'
|
||||
value: '3.13.14'
|
||||
- ${{ else }}:
|
||||
- name: winDockerImages
|
||||
value:
|
||||
@ -88,6 +98,13 @@ variables:
|
||||
- name: dockerImagePrefix
|
||||
value: 'rnd-docker-pre/ni/rfsdr/'
|
||||
|
||||
- ${{ if or(eq(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.Reason'], 'Manual')) }}:
|
||||
- name: isPRorManualBuild
|
||||
value: 'true'
|
||||
- ${{ else }}:
|
||||
- name: isPRorManualBuild
|
||||
value: 'false'
|
||||
|
||||
jobs:
|
||||
- job: set_uhd_docker_image_version
|
||||
displayName: Set UHD Docker Image Version
|
||||
@ -208,6 +225,8 @@ jobs:
|
||||
timeoutInMinutes: 600
|
||||
variables:
|
||||
uhdDockerImageVersion: $[ dependencies.set_uhd_docker_image_version.outputs['set_uhd_version.uhdDockerImageVersion'] ]
|
||||
VCPKG_BINARY_CACHE_DIR: $(Build.SourcesDirectory)\.cache\vcpkg
|
||||
VCPKG_BINARY_CACHE_CONTAINER_DIR: 'C:\vcpkg-binary-cache'
|
||||
pool:
|
||||
name: Drivers-NIBuildFarm-RFMIBUILD
|
||||
demands:
|
||||
@ -227,8 +246,11 @@ jobs:
|
||||
REM Set DOCKER_CONFIG based on vsYear
|
||||
if "$(vsYear)"=="2026" (
|
||||
echo ##vso[task.setvariable variable=DOCKER_CONFIG]uhd-builder-vs2026-v145-x64
|
||||
) else if "$(vsYear)"=="2022" (
|
||||
echo ##vso[task.setvariable variable=DOCKER_CONFIG]uhd-builder-vs2022-v143-x64
|
||||
) else (
|
||||
echo ##vso[task.setvariable variable=DOCKER_CONFIG]$(dockerImageName)
|
||||
echo ERROR: Unknown vsYear value "$(vsYear)".
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
REM Set PYTHON_VERSION_ARG based on pythonVersion
|
||||
@ -239,8 +261,16 @@ jobs:
|
||||
) else if "$(pythonVersion)"=="python313" (
|
||||
echo ##vso[task.setvariable variable=PYTHON_VERSION_ARG]--build-arg PYTHON_VERSION=$(python313_version)
|
||||
) else (
|
||||
echo WARNING: pythonVersion is not defined or unknown value "$(pythonVersion)".
|
||||
echo ##vso[task.setvariable variable=PYTHON_VERSION_ARG]
|
||||
)
|
||||
|
||||
REM Create vcpkg cache directory if it does not exist (e.g. on a cache miss).
|
||||
REM Docker COPY fails if the source path is absent from the build context.
|
||||
REM mkdir creates the full path including intermediate directories in one call.
|
||||
if not exist "$(VCPKG_BINARY_CACHE_DIR)" (
|
||||
mkdir "$(VCPKG_BINARY_CACHE_DIR)"
|
||||
)
|
||||
displayName: Set dynamic build variables
|
||||
- script: |
|
||||
call $(Build.SourcesDirectory)\.ci\utils\extract-vs-version.bat "$(cmakeCompiler)"
|
||||
@ -248,8 +278,44 @@ jobs:
|
||||
- powershell: |
|
||||
$(Build.SourcesDirectory)\.ci\docker\scripts\check-url.ps1 -Url "$(vsToolsUrl)"
|
||||
displayName: Verify expected VS Tools Installer URL
|
||||
- ${{ if parameters.use_vcpkg_cache }}:
|
||||
- powershell: |
|
||||
$now = Get-Date
|
||||
$calendar = [System.Globalization.CultureInfo]::InvariantCulture.Calendar
|
||||
$week = $calendar.GetWeekOfYear(
|
||||
$now,
|
||||
[System.Globalization.CalendarWeekRule]::FirstFourDayWeek,
|
||||
[System.DayOfWeek]::Monday
|
||||
)
|
||||
$cacheTimestamp = "{0}w{1:D2}" -f $now.Year, $week
|
||||
|
||||
Write-Host "CacheTimestamp=$cacheTimestamp"
|
||||
Write-Host "##vso[task.setvariable variable=CacheTimestamp]$cacheTimestamp"
|
||||
displayName: Set CacheTimestamp
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
key: |
|
||||
"vcpkg" | "$(vsYear)" | "$(CacheTimestamp)"
|
||||
${{ if eq(variables['isPRorManualBuild'], 'true') }}:
|
||||
restoreKeys: |
|
||||
"vcpkg" | "$(vsYear)"
|
||||
path: $(VCPKG_BINARY_CACHE_DIR)
|
||||
displayName: Cache vcpkg binary packages
|
||||
- script: |
|
||||
echo Pruning dangling images and stopped containers to reclaim disk space before build.
|
||||
docker container prune -f
|
||||
REM Pass 1: remove old uhd-builder images from previous runs (largest space consumers).
|
||||
for /f "tokens=3" %%i in ('docker images -a --format table ^| findstr "uhd-builder"') do docker rmi %%i >nul 2>&1
|
||||
REM Pass 2: remove any <none> / <untagged> dangling layers left behind after pass 1.
|
||||
for /f "tokens=3" %%i in ('docker images -a --format table ^| findstr /c:"<none>" /c:"<untagged>"') do docker rmi %%i >nul 2>&1
|
||||
docker images --format table
|
||||
REM Always exit with success (0) so this cleanup step never fails the pipeline.
|
||||
exit /b 0
|
||||
displayName: Prune dangling Docker images
|
||||
condition: and(succeeded(), or(${{ parameters.prune_docker_images }}, ne(variables['isPRorManualBuild'], 'true')))
|
||||
- task: Docker@2
|
||||
displayName: Build Image
|
||||
retryCountOnTaskFailure: 3
|
||||
inputs:
|
||||
command: 'build'
|
||||
Dockerfile: '$(Build.SourcesDirectory)/.ci/docker/$(DOCKER_CONFIG).Dockerfile'
|
||||
@ -262,11 +328,63 @@ jobs:
|
||||
buildContext: $(Build.SourcesDirectory)
|
||||
arguments: >
|
||||
--no-cache --pull
|
||||
--build-arg VCPKG_BINARY_CACHE_DIR=$(VCPKG_BINARY_CACHE_CONTAINER_DIR)
|
||||
--build-arg VS_BUILD_TOOLS_URL=$(vsToolsUrl)
|
||||
--build-arg PIP_INDEX_HOST=$(pip_index_host)
|
||||
--build-arg PIP_INDEX_URL=$(pip_index_url)
|
||||
--build-arg VCPKG_MANIFEST_FILE=$(vcpkgManifestFile)
|
||||
$(PYTHON_VERSION_ARG)
|
||||
- script: |
|
||||
docker images --format table
|
||||
displayName: List Docker images
|
||||
- ${{ if parameters.use_vcpkg_cache }}:
|
||||
- script: |
|
||||
set IMAGE_ID=
|
||||
REM Match image by name:version in plain docker images output, independent of registry hostname prefix.
|
||||
REM docker images --format with Go templates is unreliable on Windows Docker.
|
||||
REM Standard output columns: REPOSITORY TAG IMAGE-ID CREATED SIZE -> tokens=3 gives IMAGE-ID.
|
||||
for /f "tokens=3" %%i in ('docker images --format table ^| findstr /i "$(dockerImageName)" ^| findstr /i "$(uhdDockerImageVersion)"') do set IMAGE_ID=%%i
|
||||
|
||||
if not defined IMAGE_ID (
|
||||
echo WARNING: Could not find locally built image for $(dockerImagePrefix)$(dockerImageName):$(uhdDockerImageVersion)
|
||||
echo WARNING: Skipping vcpkg cache extraction and preventing cache publish for this run.
|
||||
if exist "$(VCPKG_BINARY_CACHE_DIR)" (
|
||||
rmdir /s /q "$(VCPKG_BINARY_CACHE_DIR)"
|
||||
)
|
||||
exit /b 0
|
||||
)
|
||||
|
||||
echo Using local IMAGE_ID=%IMAGE_ID%
|
||||
docker rm -f vcpkgcache >nul 2>&1
|
||||
|
||||
docker create --name vcpkgcache ^
|
||||
%IMAGE_ID%
|
||||
if errorlevel 1 (
|
||||
echo WARNING: Failed to create temporary container for cache extraction.
|
||||
echo WARNING: Skipping vcpkg cache extraction and preventing cache publish for this run.
|
||||
if exist "$(VCPKG_BINARY_CACHE_DIR)" (
|
||||
rmdir /s /q "$(VCPKG_BINARY_CACHE_DIR)"
|
||||
)
|
||||
exit /b 0
|
||||
)
|
||||
|
||||
docker images --format table
|
||||
|
||||
if exist "$(VCPKG_BINARY_CACHE_DIR)" (
|
||||
rmdir /s /q "$(VCPKG_BINARY_CACHE_DIR)"
|
||||
)
|
||||
docker cp vcpkgcache:$(VCPKG_BINARY_CACHE_CONTAINER_DIR) "$(VCPKG_BINARY_CACHE_DIR)"
|
||||
if errorlevel 1 (
|
||||
echo WARNING: Failed to copy vcpkg cache from built image.
|
||||
echo WARNING: Skipping vcpkg cache publish for this run.
|
||||
docker rm -f vcpkgcache >nul 2>&1
|
||||
if exist "$(VCPKG_BINARY_CACHE_DIR)" (
|
||||
rmdir /s /q "$(VCPKG_BINARY_CACHE_DIR)"
|
||||
)
|
||||
exit /b 0
|
||||
)
|
||||
docker rm vcpkgcache
|
||||
displayName: Extract vcpkg cache
|
||||
- task: Docker@2
|
||||
displayName: Push Image
|
||||
inputs:
|
||||
@ -277,6 +395,12 @@ jobs:
|
||||
containerRegistry: rnd-docker-niartifacts-pre-publish
|
||||
repository: '$(dockerImagePrefix)$(dockerImageName)'
|
||||
tags: $(uhdDockerImageVersion)
|
||||
- task: PublishPipelineArtifact@1
|
||||
inputs:
|
||||
targetPath: $(VCPKG_BINARY_CACHE_DIR)
|
||||
artifactName: vcpkg-binary-cache-$(dockerImageName))-run$(Build.BuildId)
|
||||
displayName: Publish vcpkg binary cache as artifact
|
||||
condition: eq(variables['System.Debug'], 'true')
|
||||
|
||||
- job: test_image_win
|
||||
displayName: Test Docker Image Windows
|
||||
|
||||
@ -7,6 +7,10 @@ parameters:
|
||||
type: boolean
|
||||
displayName: Run X410 Embedded Tests
|
||||
default: false
|
||||
- name: run_x420_embedded_tests
|
||||
type: boolean
|
||||
displayName: Run X420 Embedded Tests
|
||||
default: false
|
||||
- name: run_x440_embedded_tests
|
||||
type: boolean
|
||||
displayName: Run X440 Embedded Tests
|
||||
@ -15,10 +19,10 @@ parameters:
|
||||
type: boolean
|
||||
displayName: Run Streaming Tests
|
||||
default: false
|
||||
- name: run_x440_streaming_tests
|
||||
type: boolean
|
||||
displayName: Run X440 Streaming Tests
|
||||
default: false
|
||||
- name: selected_streaming_tests
|
||||
type: string
|
||||
displayName: Streaming Tests Multi-Select (comma-separated)
|
||||
default: 'B206,B210,B310,X310_XG,X410_CG_400,X410_UC_200,X440_CG_400,X440_CG_1600'
|
||||
- name: run_devtests
|
||||
type: boolean
|
||||
displayName: Run devtests
|
||||
@ -55,6 +59,14 @@ parameters:
|
||||
type: boolean
|
||||
displayName: Run LabVIEW test
|
||||
default: false
|
||||
- name: run_b310_devtests
|
||||
type: boolean
|
||||
displayName: Run B310 devtests
|
||||
default: false
|
||||
- name: run_b310_rf_tests
|
||||
type: boolean
|
||||
displayName: Run B310 RF tests
|
||||
default: false
|
||||
- name: fpga_imgs_source
|
||||
type: string
|
||||
values:
|
||||
@ -74,11 +86,12 @@ parameters:
|
||||
- 'smoke'
|
||||
- 'full'
|
||||
- 'stress'
|
||||
- name: dpdkDirs
|
||||
- name: dpdkVersions
|
||||
type: object
|
||||
default:
|
||||
- '/opt/dpdk-19.11'
|
||||
- '/opt/dpdk-23.11'
|
||||
- '21.11'
|
||||
- '23.11'
|
||||
- '25.11'
|
||||
- name: x300_fpga_matrix
|
||||
type: object
|
||||
default:
|
||||
@ -127,6 +140,14 @@ parameters:
|
||||
run: true
|
||||
X440_CG_1600:
|
||||
run: true
|
||||
- name: uploadSbomArtifacts
|
||||
type: boolean
|
||||
displayName: Publish SBOMs to DepTrack
|
||||
default: false
|
||||
- name: sbomExclFromUpload
|
||||
type: string
|
||||
displayName: Don't upload listed SBOMs (space-separated list of image names)
|
||||
default: ' '
|
||||
|
||||
variables:
|
||||
- name: uhdFpgaArtifactSource
|
||||
@ -147,6 +168,9 @@ resources:
|
||||
- pipeline: uhd_mono_pipeline
|
||||
source: 'uhddev mono pipeline'
|
||||
branch: master
|
||||
- pipeline: uhd_mono_pipeline_pr
|
||||
source: 'uhddev mono pipeline PR'
|
||||
branch: master
|
||||
- pipeline: usrp-kas-pipeline
|
||||
source: 'usrp-kas'
|
||||
branch: scarthgap
|
||||
@ -155,9 +179,6 @@ resources:
|
||||
- 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
|
||||
@ -174,10 +195,6 @@ resources:
|
||||
endpoint: EttusResearch
|
||||
name: EttusResearch/ettus-rts
|
||||
ref: master
|
||||
- repository: hwtools
|
||||
type: git
|
||||
ref: refs/tags/pip/hwsetup/0.1/0.1.3
|
||||
name: DevCentral/hwtools
|
||||
- repository: labview-usrp-examples
|
||||
type: github
|
||||
endpoint: EttusResearch
|
||||
@ -196,6 +213,22 @@ stages:
|
||||
uhdFpgaArtifactSource: ${{ variables.uhdFpgaArtifactSource }}
|
||||
fpga_imgs_source: ${{ variables.fpga_imgs_source }}
|
||||
|
||||
- stage: test_uhd_x420_embedded_stage
|
||||
displayName: Test X420 Embedded UHD
|
||||
dependsOn: []
|
||||
condition: ${{ parameters.run_x420_embedded_tests }}
|
||||
jobs:
|
||||
- template: templates/tests/job-uhd-x420-hardware-tests-s175.yml
|
||||
parameters:
|
||||
uhdArtifactSource: uhd_mono_pipeline
|
||||
uhdFpgaArtifactSource: ${{ variables.uhdFpgaArtifactSource }}
|
||||
fpga_imgs_source: ${{ variables.fpga_imgs_source }}
|
||||
testLength: ${{ parameters.testLength }}
|
||||
runPyTest: false
|
||||
runPyTestNonRF: true
|
||||
runDevTest: true
|
||||
runSystemImageTest: true
|
||||
|
||||
- stage: test_uhd_x440_embedded_stage
|
||||
displayName: Test X440 Embedded UHD
|
||||
dependsOn: []
|
||||
@ -207,38 +240,19 @@ stages:
|
||||
uhdFpgaArtifactSource: ${{ variables.uhdFpgaArtifactSource }}
|
||||
fpga_imgs_source: ${{ variables.fpga_imgs_source }}
|
||||
|
||||
- stage: test_streaming_stage
|
||||
displayName: Test UHD Streaming
|
||||
- stage: stage_uhd_streaming_tests
|
||||
displayName: UHD Streaming Tests
|
||||
dependsOn: []
|
||||
condition: ${{ parameters.run_streaming_tests }}
|
||||
jobs:
|
||||
- template: templates/job-uhd-streaming-tests-beauty.yml
|
||||
- template: templates/job-uhd-streaming-tests.yml
|
||||
parameters:
|
||||
uhdSrcDir: $(Build.SourcesDirectory)/uhddev
|
||||
uhdArtifactSource: uhd_mono_pipeline
|
||||
uhdFpgaArtifactSource: ${{ variables.uhdFpgaArtifactSource }}
|
||||
testLength: ${{ parameters.testLength }}
|
||||
dpdkDirs: '${{ parameters.dpdkDirs }}'
|
||||
- template: templates/job-uhd-streaming-tests-s175.yml
|
||||
parameters:
|
||||
uhdSrcDir: $(Build.SourcesDirectory)/uhddev
|
||||
uhdArtifactSource: uhd_mono_pipeline
|
||||
uhdFpgaArtifactSource: ${{ variables.uhdFpgaArtifactSource }}
|
||||
testLength: ${{ parameters.testLength }}
|
||||
dpdkDirs: '${{ parameters.dpdkDirs }}'
|
||||
|
||||
- stage: test_x440_streaming_stage
|
||||
displayName: Test X440 Streaming
|
||||
dependsOn: []
|
||||
condition: ${{ parameters.run_x440_streaming_tests }}
|
||||
jobs:
|
||||
- template: templates/job-uhd-streaming-tests-x440.yml
|
||||
parameters:
|
||||
uhdSrcDir: $(Build.SourcesDirectory)/uhddev
|
||||
uhdArtifactSource: uhd_mono_pipeline
|
||||
uhdFpgaArtifactSource: ${{ variables.uhdFpgaArtifactSource }}
|
||||
testLength: ${{ parameters.testLength }}
|
||||
dpdkDirs: '${{ parameters.dpdkDirs }}'
|
||||
dpdkVersions: ${{ parameters.dpdkVersions }}
|
||||
selectedTests: ${{ split(replace(parameters.selected_streaming_tests, ' ', ''), ',') }}
|
||||
|
||||
- stage: test_uhd_devtest
|
||||
displayName: Test UHD Devtest
|
||||
@ -296,6 +310,31 @@ stages:
|
||||
uhdFpgaArtifactSource: ${{ variables.uhdFpgaArtifactSource }}
|
||||
fpga_imgs_source: ${{ variables.fpga_imgs_source }}
|
||||
|
||||
- stage: test_b310_rf_tests
|
||||
displayName: Test B310 RF Tests
|
||||
dependsOn: []
|
||||
condition: ${{ parameters.run_b310_rf_tests }}
|
||||
jobs:
|
||||
- template: templates/job-uhd-rf-tests-b310.yml
|
||||
parameters:
|
||||
uhdSrcDir: $(Build.SourcesDirectory)/uhddev
|
||||
uhdArtifactSource: uhd_mono_pipeline
|
||||
uhdFpgaArtifactSource: ${{ variables.uhdFpgaArtifactSource }}
|
||||
fpga_imgs_source: ${{ variables.fpga_imgs_source }}
|
||||
extra_rf_test_args: ${{ parameters.extra_rf_test_args }}
|
||||
|
||||
- stage: test_b310_devtest
|
||||
displayName: Test B310 Devtest
|
||||
dependsOn: []
|
||||
condition: ${{ parameters.run_b310_devtests }}
|
||||
jobs:
|
||||
- template: templates/job-uhd-devtest-b310.yml
|
||||
parameters:
|
||||
uhdSrcDir: $(Build.SourcesDirectory)
|
||||
uhdArtifactSource: uhd_mono_pipeline
|
||||
uhdFpgaArtifactSource: ${{ variables.uhdFpgaArtifactSource }}
|
||||
fpga_imgs_source: ${{ variables.fpga_imgs_source }}
|
||||
|
||||
- stage: test_x410_rf_tests
|
||||
displayName: Run X410 RF tests
|
||||
dependsOn: []
|
||||
@ -306,6 +345,7 @@ stages:
|
||||
uhdArtifactSource: uhd_mono_pipeline
|
||||
uhdFpgaArtifactSource: ${{ variables.uhdFpgaArtifactSource }}
|
||||
fpga_imgs_source: ${{ variables.fpga_imgs_source }}
|
||||
runX410X4400RfSubset: true
|
||||
extra_rf_test_args: ${{ parameters.extra_rf_test_args }}
|
||||
testLength: ${{ parameters.testLength }}
|
||||
|
||||
@ -321,6 +361,10 @@ stages:
|
||||
fpga_imgs_source: ${{ variables.fpga_imgs_source }}
|
||||
extra_rf_test_args: ${{ parameters.extra_rf_test_args }}
|
||||
testLength: ${{ parameters.testLength }}
|
||||
runPyTest: true
|
||||
runPyTestNonRF: false
|
||||
runDevTest: false
|
||||
runSystemImageTest: false
|
||||
|
||||
- stage: test_x440_rf_tests
|
||||
displayName: Run X440 RF tests
|
||||
@ -360,3 +404,10 @@ stages:
|
||||
parameters:
|
||||
uhdArtifactSource: uhd_mono_pipeline
|
||||
uhdFpgaArtifactSource: ${{ variables.uhdFpgaArtifactSource }}
|
||||
|
||||
- ${{ if eq(parameters.uploadSbomArtifacts, true) }}:
|
||||
- template: .ci/templates/stages-nisbom-uploads.yml@ettus-rts
|
||||
parameters:
|
||||
sbomExclFromUpload: ${{ parameters.sbomExclFromUpload }}
|
||||
uhdArtifactSource: uhd_mono_pipeline
|
||||
releaseBinaries: false
|
||||
|
||||
@ -12,12 +12,12 @@ pr: none
|
||||
parameters:
|
||||
- name: fpga_imgs_source
|
||||
type: string
|
||||
displayName: FPGA Images Source
|
||||
values:
|
||||
- 'Filesystem/Images Downloader'
|
||||
- 'Mono Pipeline'
|
||||
- 'Mono Pipeline PR'
|
||||
- 'FPGA Pipeline'
|
||||
displayName: FPGA Images Source
|
||||
default: 'Mono Pipeline'
|
||||
- name: testLength
|
||||
type: string
|
||||
@ -26,6 +26,10 @@ parameters:
|
||||
- 'full'
|
||||
- 'stress'
|
||||
default: 'smoke'
|
||||
- name: selected_streaming_tests
|
||||
type: string
|
||||
displayName: Streaming Tests Multi-Select (comma-separated)
|
||||
default: 'B206,B210,X310_XG,X410_CG_400,X410_UC_200,X440_CG_400,X440_CG_1600'
|
||||
|
||||
variables:
|
||||
- name: uhdFpgaArtifactSource
|
||||
@ -73,11 +77,6 @@ resources:
|
||||
endpoint: EttusResearch
|
||||
name: EttusResearch/ettus-rts
|
||||
ref: master
|
||||
- repository: hwtools
|
||||
type: git
|
||||
ref: refs/tags/pip/hwsetup/0.1/0.1.3
|
||||
name: DevCentral/hwtools
|
||||
|
||||
stages:
|
||||
- stage: test_uhd_devtest_stage
|
||||
displayName: Test UHD Devtest
|
||||
@ -88,6 +87,16 @@ stages:
|
||||
uhdArtifactSource: uhd_mono_pipeline
|
||||
uhdFpgaArtifactSource: ${{ variables.uhdFpgaArtifactSource }}
|
||||
fpga_imgs_source: ${{ variables.fpga_imgs_source }}
|
||||
- template: templates/tests/job-uhd-x420-hardware-tests-s175.yml
|
||||
parameters:
|
||||
uhdArtifactSource: uhd_mono_pipeline
|
||||
uhdFpgaArtifactSource: ${{ variables.uhdFpgaArtifactSource }}
|
||||
fpga_imgs_source: ${{ variables.fpga_imgs_source }}
|
||||
testLength: ${{ parameters.testLength }}
|
||||
runPyTest: false
|
||||
runPyTestNonRF: true
|
||||
runDevTest: true
|
||||
runSystemImageTest: true
|
||||
- template: templates/tests/job-uhd-x440-hardware-tests-sdr-test0.yml
|
||||
parameters:
|
||||
uhdArtifactSource: uhd_mono_pipeline
|
||||
@ -104,24 +113,13 @@ stages:
|
||||
displayName: Test UHD Streaming
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/job-uhd-streaming-tests-beauty.yml
|
||||
parameters:
|
||||
uhdSrcDir: $(Build.SourcesDirectory)/uhddev
|
||||
uhdArtifactSource: uhd_mono_pipeline
|
||||
uhdFpgaArtifactSource: ${{ variables.uhdFpgaArtifactSource }}
|
||||
testLength: ${{ parameters.testLength }}
|
||||
- template: templates/job-uhd-streaming-tests-s175.yml
|
||||
parameters:
|
||||
uhdSrcDir: $(Build.SourcesDirectory)/uhddev
|
||||
uhdArtifactSource: uhd_mono_pipeline
|
||||
uhdFpgaArtifactSource: ${{ variables.uhdFpgaArtifactSource }}
|
||||
testLength: ${{ parameters.testLength }}
|
||||
- template: templates/job-uhd-streaming-tests-x440.yml
|
||||
- template: templates/job-uhd-streaming-tests.yml
|
||||
parameters:
|
||||
uhdSrcDir: $(Build.SourcesDirectory)/uhddev
|
||||
uhdArtifactSource: uhd_mono_pipeline
|
||||
uhdFpgaArtifactSource: ${{ variables.uhdFpgaArtifactSource }}
|
||||
testLength: ${{ parameters.testLength }}
|
||||
selectedTests: ${{ split(replace(parameters.selected_streaming_tests, ' ', ''), ',') }}
|
||||
|
||||
- stage: test_rf_tests_stage
|
||||
displayName: Test UHD RF Tests
|
||||
@ -138,7 +136,19 @@ stages:
|
||||
uhdArtifactSource: uhd_mono_pipeline
|
||||
uhdFpgaArtifactSource: ${{ variables.uhdFpgaArtifactSource }}
|
||||
fpga_imgs_source: ${{ variables.fpga_imgs_source }}
|
||||
runX410X4400RfSubset: true
|
||||
testLength: ${{ parameters.testLength }}
|
||||
- template: templates/tests/job-uhd-x420-hardware-tests-s175.yml
|
||||
parameters:
|
||||
uhdArtifactSource: uhd_mono_pipeline
|
||||
uhdFpgaArtifactSource: ${{ variables.uhdFpgaArtifactSource }}
|
||||
fpga_imgs_source: ${{ variables.fpga_imgs_source }}
|
||||
testLength: ${{ parameters.testLength }}
|
||||
runPyTest: true
|
||||
runPyTestNonRF: false
|
||||
runDevTest: false
|
||||
runSystemImageTest: false
|
||||
forceIQCorrections: true
|
||||
- template: templates/tests/job-uhd-x440-hardware-tests-pebbles.yml
|
||||
parameters:
|
||||
uhdArtifactSource: uhd_mono_pipeline
|
||||
|
||||
@ -135,6 +135,10 @@ parameters:
|
||||
- 'files.ettus.com (Public)'
|
||||
displayName: Published Package Access
|
||||
default: 'Internal'
|
||||
- name: selected_streaming_tests
|
||||
type: string
|
||||
displayName: Streaming Tests Multi-Select (comma-separated)
|
||||
default: 'B206,B210,B310,X310_XG,X410_CG_400,X410_UC_200,X440_CG_400,X440_CG_1600'
|
||||
|
||||
trigger: none
|
||||
|
||||
@ -181,3 +185,5 @@ extends:
|
||||
publish_int_files: true
|
||||
package_and_publish_images: ${{ parameters.package_and_publish_images }}
|
||||
package_access: ${{ parameters.package_access }}
|
||||
# Test parameters
|
||||
selected_streaming_tests: ${{ parameters.selected_streaming_tests }}
|
||||
|
||||
@ -118,6 +118,10 @@ parameters:
|
||||
- 'files.ettus.com (Public)'
|
||||
displayName: Published Package Access
|
||||
default: 'Internal'
|
||||
- name: selected_streaming_tests
|
||||
type: string
|
||||
displayName: Streaming Tests Multi-Select (comma-separated)
|
||||
default: 'B206,B210,B310,X310_XG,X410_CG_400,X410_UC_200,X440_CG_400,X440_CG_1600'
|
||||
|
||||
trigger:
|
||||
batch: true
|
||||
@ -161,3 +165,5 @@ extends:
|
||||
publish_int_files: true
|
||||
package_and_publish_images: ${{ parameters.package_and_publish_images }}
|
||||
package_access: ${{ parameters.package_access }}
|
||||
# Test parameters
|
||||
selected_streaming_tests: ${{ parameters.selected_streaming_tests }}
|
||||
|
||||
@ -7,12 +7,24 @@
|
||||
"""Derive package-specific docker image matrices from DockerImageNames artifact data.
|
||||
|
||||
Supports Python 3.8 or later.
|
||||
|
||||
Strictness control:
|
||||
- `UHD_MATRIX_STRICT_CI=true|1|yes|on`: force strict mode (errors are blocking)
|
||||
- `UHD_MATRIX_STRICT_CI=false|0|no|off`: force non-strict mode
|
||||
- `UHD_MATRIX_STRICT_CI` unset: auto-detect strictness
|
||||
- strict for Azure CI reasons `IndividualCI`, `BatchedCI`, `Schedule`
|
||||
- non-strict for Azure manual/debug runs
|
||||
- for non-Azure environments, strict if `CI=true`
|
||||
|
||||
When `--empty-on-error-non-ci` is provided, non-strict mode converts derivation
|
||||
errors to `{}` and emits an Azure warning log issue.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import ast
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
@ -43,7 +55,102 @@ SECTION_DATA_MAP = {
|
||||
}
|
||||
|
||||
|
||||
def is_strict_ci_build() -> bool:
|
||||
"""Return True when matrix validation/derivation failures are blocking.
|
||||
|
||||
Override behavior via `UHD_MATRIX_STRICT_CI`:
|
||||
- true/1/yes/on: force strict
|
||||
- false/0/no/off: force non-strict
|
||||
- auto/unset: use environment-based detection
|
||||
"""
|
||||
override = os.getenv("UHD_MATRIX_STRICT_CI", "").strip().lower()
|
||||
if override in {"1", "true", "yes", "on"}:
|
||||
return True
|
||||
if override in {"0", "false", "no", "off"}:
|
||||
return False
|
||||
|
||||
if os.getenv("TF_BUILD", "").strip().lower() == "true":
|
||||
return os.getenv("BUILD_REASON", "").strip() in {
|
||||
"IndividualCI",
|
||||
"BatchedCI",
|
||||
"Schedule",
|
||||
}
|
||||
|
||||
# Non-Azure fallback used by local and other CI systems.
|
||||
return os.getenv("CI", "").strip().lower() == "true"
|
||||
|
||||
|
||||
def _is_strict_ci_build() -> bool:
|
||||
"""Backward-compatible internal alias."""
|
||||
return is_strict_ci_build()
|
||||
|
||||
|
||||
def _empty_fallback_message(exc: Exception) -> str:
|
||||
"""Format a consistent warning message for non-strict fallback."""
|
||||
return (
|
||||
"derive_docker_image_matrix failed in non-CI build; "
|
||||
"defaulting to empty object. Error: "
|
||||
f"{exc}"
|
||||
)
|
||||
|
||||
|
||||
def _emit_non_strict_fallback(exc: Exception) -> int:
|
||||
"""Emit warning and fallback output for non-strict mode."""
|
||||
message = _empty_fallback_message(exc)
|
||||
print(f"##vso[task.logissue type=warning]{message}", file=sys.stderr)
|
||||
print("{}")
|
||||
return 0
|
||||
|
||||
|
||||
def _emit_failure(exc: Exception) -> int:
|
||||
"""Emit normal error output for strict mode."""
|
||||
print(str(exc), file=sys.stderr)
|
||||
return 1
|
||||
|
||||
|
||||
def _error_exit_for_exception(exc: Exception, empty_on_error_non_ci: bool) -> int:
|
||||
"""Map an exception to exit handling based on strictness and CLI mode."""
|
||||
if empty_on_error_non_ci and not is_strict_ci_build():
|
||||
return _emit_non_strict_fallback(exc)
|
||||
return _emit_failure(exc)
|
||||
|
||||
|
||||
def _format_cli_output(args: argparse.Namespace, matrix_text: str) -> None:
|
||||
"""Write normal CLI output, either plain text or Azure variable format."""
|
||||
if args.vso_variable:
|
||||
print(f"##vso[task.setvariable variable={args.vso_variable};isOutput=true;]{matrix_text}")
|
||||
else:
|
||||
print(matrix_text)
|
||||
|
||||
|
||||
def _derive_cli_value(args: argparse.Namespace, artifact_data: dict[str, Any]) -> Any:
|
||||
"""Resolve the selected CLI mode into an artifact value/matrix."""
|
||||
if args.section:
|
||||
return get_artifact_value(artifact_data, args.section)
|
||||
if args.type:
|
||||
if not args.os:
|
||||
raise ValueError("--os is required when --type is specified")
|
||||
return derive_typed_matrix(
|
||||
artifact_data,
|
||||
args.type,
|
||||
args.os,
|
||||
package=args.package,
|
||||
select=args.select,
|
||||
filter_out=args.filter_out,
|
||||
)
|
||||
|
||||
if not args.package:
|
||||
raise ValueError("Either --section or --type or --package must be specified")
|
||||
if not args.os:
|
||||
raise ValueError("--os is required when --package is specified")
|
||||
if args.select:
|
||||
raise ValueError("--select is only supported with --type")
|
||||
return derive_package_matrix(artifact_data, args.os, args.package, args.filter_out)
|
||||
|
||||
|
||||
def _parse_matrix_string(raw_value: str) -> dict[str, dict[str, Any]]:
|
||||
if not raw_value:
|
||||
return {}
|
||||
parsed = ast.literal_eval(raw_value)
|
||||
if not isinstance(parsed, dict):
|
||||
raise ValueError("Expected matrix content to parse as a dict")
|
||||
@ -331,6 +438,14 @@ def _build_arg_parser() -> argparse.ArgumentParser:
|
||||
default="",
|
||||
help="Optional variable name to emit in Azure DevOps setvariable format",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--empty-on-error-non-ci",
|
||||
action="store_true",
|
||||
help=(
|
||||
"On non-CI Azure builds, convert derivation errors to '{}' and continue. "
|
||||
"CI builds still fail with non-zero exit."
|
||||
),
|
||||
)
|
||||
return parser
|
||||
|
||||
|
||||
@ -340,36 +455,12 @@ def main() -> int:
|
||||
|
||||
try:
|
||||
artifact_data = parse_docker_image_names_file(Path(args.input))
|
||||
if args.section:
|
||||
value = get_artifact_value(artifact_data, args.section)
|
||||
elif args.type:
|
||||
if not args.os:
|
||||
raise ValueError("--os is required when --type is specified")
|
||||
value = derive_typed_matrix(
|
||||
artifact_data,
|
||||
args.type,
|
||||
args.os,
|
||||
package=args.package,
|
||||
select=args.select,
|
||||
filter_out=args.filter_out,
|
||||
)
|
||||
else:
|
||||
if not args.package:
|
||||
raise ValueError("Either --section or --type or --package must be specified")
|
||||
if not args.os:
|
||||
raise ValueError("--os is required when --package is specified")
|
||||
if args.select:
|
||||
raise ValueError("--select is only supported with --type")
|
||||
value = derive_package_matrix(artifact_data, args.os, args.package, args.filter_out)
|
||||
value = _derive_cli_value(args, artifact_data)
|
||||
matrix_text = format_value_for_pipeline(value)
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
print(str(exc), file=sys.stderr)
|
||||
return 1
|
||||
return _error_exit_for_exception(exc, args.empty_on_error_non_ci)
|
||||
|
||||
if args.vso_variable:
|
||||
print(f"##vso[task.setvariable variable={args.vso_variable};isOutput=true;]{matrix_text}")
|
||||
else:
|
||||
print(matrix_text)
|
||||
_format_cli_output(args, matrix_text)
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
@ -12,6 +12,7 @@ import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import shutil
|
||||
|
||||
import labgrid
|
||||
from fabric import Connection
|
||||
@ -52,17 +53,21 @@ class AcquiredPlace:
|
||||
subprocess.run(["labgrid-client", "-c", self.config, "release"])
|
||||
|
||||
|
||||
def jtag_x3xx(
|
||||
dev_type, dev_model, jtag_server, jtag_serial, fpga_folder, fpga, redis_server, vivado_dir
|
||||
):
|
||||
def jtag_x3xx(dev_type, dev_model, jtag_server, jtag_serial, fpga_folder, fpga, redis_server):
|
||||
"""Download the bitfile to the FPGA using jtag interface."""
|
||||
if dev_model not in ["x300", "x310"]:
|
||||
raise RuntimeError(f"{dev_type} not supported with jtag_x3xx")
|
||||
remote_working_dir = "pipeline_fpga"
|
||||
vivado_lab_path = os.path.join(vivado_dir, "bin/vivado_lab")
|
||||
vivado_program_jtag = "{} -mode batch -source {}/viv_hardware_utils.tcl -nolog -nojournal -tclargs program".format(
|
||||
vivado_lab_path, remote_working_dir
|
||||
vivado_lab = shutil.which("vivado_lab")
|
||||
if vivado_lab is None:
|
||||
raise RuntimeError("vivado_lab is not available on PATH")
|
||||
|
||||
vivado_program_jtag = (
|
||||
f"{shlex.quote(vivado_lab)} -mode batch "
|
||||
f"-source {remote_working_dir}/viv_hardware_utils.tcl "
|
||||
"-nolog -nojournal -tclargs program"
|
||||
)
|
||||
|
||||
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
|
||||
@ -329,7 +334,7 @@ def main(args):
|
||||
os.chdir(os.path.join(working_dir, fpga))
|
||||
|
||||
if args.jtag_x3xx:
|
||||
dev_type, dev_model, jtag_server, jtag_serial, fpga_folder, vivado_dir = (
|
||||
dev_type, dev_model, jtag_server, jtag_serial, fpga_folder = (
|
||||
args.jtag_x3xx.split(",")
|
||||
)
|
||||
jtag_x3xx(
|
||||
@ -340,7 +345,6 @@ def main(args):
|
||||
fpga_folder,
|
||||
fpga,
|
||||
get_redis_server(args),
|
||||
vivado_dir,
|
||||
)
|
||||
|
||||
if dev_type and dev_type in ["n3xx", "e3xx"]:
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
4.10.0_20260711.1
|
||||
rnd-docker-ci/ni/rfsdr/
|
||||
{'Fedora-42-builder':{'buildOSName':'fedora42','dockerImageName':'uhd-builder-fedora42','releaseName':'42'}, 'Fedora-43-builder':{'buildOSName':'fedora43','dockerImageName':'uhd-builder-fedora43','releaseName':'43'}, 'Fedora-44-builder':{'buildOSName':'fedora44','dockerImageName':'uhd-builder-fedora44','releaseName':'44'}, 'Ubuntu-2004-builder':{'buildOSName':'ubuntu2004','dockerImageName':'uhd-builder-ubuntu2004','releaseName':'focal'}, 'Ubuntu-2204-builder':{'buildOSName':'ubuntu2204','dockerImageName':'uhd-builder-ubuntu2204','releaseName':'jammy'}, 'Ubuntu-2404-builder':{'buildOSName':'ubuntu2404','dockerImageName':'uhd-builder-ubuntu2404','releaseName':'noble'}, 'Ubuntu-2510-builder':{'buildOSName':'ubuntu2510','dockerImageName':'uhd-builder-ubuntu2510','releaseName':'questing'}}
|
||||
4.10.0_20260727.3
|
||||
rnd-docker-pre/ni/rfsdr/
|
||||
|
||||
{'VS2022-x64-py310-builder':{'buildOSName':'win-vs22-v143-x64-py310','dockerImageName':'uhd-builder-vs2022-v143-x64-py310','cmakeCompiler':'Visual Studio 17 2022','cmakeArch':'x64','vsArch':'x64','vsYear':'2022','pythonVersion':'python310'}, 'VS2022-x64-py312-builder':{'buildOSName':'win-vs22-v143-x64-py312','dockerImageName':'uhd-builder-vs2022-v143-x64-py312','cmakeCompiler':'Visual Studio 17 2022','cmakeArch':'x64','vsArch':'x64','vsYear':'2022','pythonVersion':'python312'}, 'VS2026-x64-py310-builder':{'buildOSName':'win-vs26-v145-x64-py310','dockerImageName':'uhd-builder-vs2026-v145-x64-py310','cmakeCompiler':'Visual Studio 18 2026','cmakeArch':'x64','vsArch':'x64','vsYear':'2026','pythonVersion':'python310'}, 'VS2026-x64-py312-builder':{'buildOSName':'win-vs26-v145-x64-py312','dockerImageName':'uhd-builder-vs2026-v145-x64-py312','cmakeCompiler':'Visual Studio 18 2026','cmakeArch':'x64','vsArch':'x64','vsYear':'2026','pythonVersion':'python312'}}
|
||||
{'macOS-15-x64':{'buildOSName':'macOS-15-x64', 'macOSVersionDemand':'15', 'macOSArchDemand': 'X64'}}
|
||||
{}
|
||||
|
||||
@ -16,9 +16,11 @@ from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import ast
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import unittest
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
@ -33,6 +35,15 @@ DEFAULT_FIXTURE = (
|
||||
ARTIFACT_FILE_OVERRIDE: Optional[Path] = None
|
||||
|
||||
|
||||
def _is_ci_build() -> bool:
|
||||
"""Return True for CI contexts where test failures must remain blocking."""
|
||||
from derive_docker_image_matrix import ( # type: ignore[import-not-found]
|
||||
is_strict_ci_build,
|
||||
)
|
||||
|
||||
return is_strict_ci_build()
|
||||
|
||||
|
||||
class DeriveDockerImageMatrixTests(unittest.TestCase):
|
||||
"""Validate docker image matrix derivation behavior for current artifacts."""
|
||||
|
||||
@ -59,42 +70,93 @@ class DeriveDockerImageMatrixTests(unittest.TestCase):
|
||||
self.fixture = self.__class__.fixture
|
||||
self.using_artifact_override = ARTIFACT_FILE_OVERRIDE is not None
|
||||
self.artifact_data = self.parse_docker_image_names_file(self.fixture)
|
||||
self._warn_for_empty_optional_matrices()
|
||||
|
||||
def _warn_for_empty_optional_matrices(self) -> None:
|
||||
"""Warn if expected matrix sections are present but empty."""
|
||||
line_map = {
|
||||
"linux_matrix": 3,
|
||||
"win_matrix": 4,
|
||||
"macos_builders": 5,
|
||||
}
|
||||
for key, line_no in line_map.items():
|
||||
value = self.artifact_data.get(key)
|
||||
if isinstance(value, dict) and not value:
|
||||
warnings.warn(
|
||||
f"DockerImageNames line {line_no} ({key}) is an empty dict; "
|
||||
"tests requiring populated matrix entries may be skipped.",
|
||||
UserWarning,
|
||||
)
|
||||
|
||||
def _skip_if_matrix_empty(self, key: str, line_no: int) -> None:
|
||||
"""Skip tests that need populated matrix content when section is empty."""
|
||||
value = self.artifact_data.get(key)
|
||||
if isinstance(value, dict) and not value:
|
||||
warnings.warn(
|
||||
f"Skipping test because DockerImageNames line {line_no} ({key}) is empty.",
|
||||
UserWarning,
|
||||
)
|
||||
self.skipTest(f"Empty matrix at line {line_no}: {key}")
|
||||
|
||||
def _ubuntu_linux_entries(self) -> dict[str, dict[str, object]]:
|
||||
"""Return Ubuntu entries from linux_matrix that include a releaseName."""
|
||||
entries: dict[str, dict[str, object]] = {}
|
||||
for key, payload in self.artifact_data["linux_matrix"].items():
|
||||
if key.startswith("Ubuntu-") and payload.get("releaseName"):
|
||||
entries[key] = payload
|
||||
return entries
|
||||
|
||||
def _fedora_linux_entries(self) -> dict[str, dict[str, object]]:
|
||||
"""Return Fedora entries from linux_matrix that include a releaseName."""
|
||||
entries: dict[str, dict[str, object]] = {}
|
||||
for key, payload in self.artifact_data["linux_matrix"].items():
|
||||
if key.startswith("Fedora-") and payload.get("releaseName"):
|
||||
entries[key] = payload
|
||||
return entries
|
||||
|
||||
def test_parses_fixture_file(self):
|
||||
"""Parse the fixture and expose expected top-level fields."""
|
||||
if self.using_artifact_override:
|
||||
self.assertTrue(self.artifact_data["docker_build_number"])
|
||||
else:
|
||||
self.assertEqual(self.artifact_data["docker_build_number"], "4.10.0_20260711.1")
|
||||
self.assertIn("Ubuntu-2404-builder", self.artifact_data["linux_matrix"])
|
||||
self.assertTrue(self.artifact_data["docker_build_number"])
|
||||
self._skip_if_matrix_empty("linux_matrix", 3)
|
||||
self.assertTrue(
|
||||
any(key.startswith("Ubuntu-") for key in self.artifact_data["linux_matrix"]),
|
||||
"Expected at least one Ubuntu entry in linux_matrix",
|
||||
)
|
||||
|
||||
def test_derives_deb_matrix_from_linux_matrix(self):
|
||||
"""Derive Ubuntu deb matrix using releaseName from linux matrix entries."""
|
||||
self._skip_if_matrix_empty("linux_matrix", 3)
|
||||
matrix = self.derive_package_matrix(self.artifact_data, os_name="linux", package="deb")
|
||||
self.assertIn("Ubuntu-2204-builder", matrix)
|
||||
self.assertIn("Ubuntu-2404-builder", matrix)
|
||||
self.assertIn("Ubuntu-2510-builder", matrix)
|
||||
self.assertEqual(matrix["Ubuntu-2204-builder"]["ubuntuReleaseName"], "jammy")
|
||||
self.assertEqual(matrix["Ubuntu-2404-builder"]["ubuntuReleaseName"], "noble")
|
||||
expected = self._ubuntu_linux_entries()
|
||||
self.assertEqual(set(matrix.keys()), set(expected.keys()))
|
||||
for key, payload in expected.items():
|
||||
self.assertEqual(matrix[key]["ubuntuReleaseName"], payload["releaseName"])
|
||||
|
||||
def test_derives_rpm_matrix_from_linux_matrix(self):
|
||||
"""Derive Fedora rpm matrix using releaseName from linux matrix entries."""
|
||||
self._skip_if_matrix_empty("linux_matrix", 3)
|
||||
matrix = self.derive_package_matrix(self.artifact_data, os_name="linux", package="rpm")
|
||||
self.assertIn("Fedora-42-builder", matrix)
|
||||
self.assertEqual(matrix["Fedora-42-builder"]["fedoraReleaseName"], "42")
|
||||
self.assertEqual(matrix["Fedora-44-builder"]["fedoraReleaseName"], "44")
|
||||
expected = self._fedora_linux_entries()
|
||||
self.assertEqual(set(matrix.keys()), set(expected.keys()))
|
||||
for key, payload in expected.items():
|
||||
self.assertEqual(matrix[key]["fedoraReleaseName"], payload["releaseName"])
|
||||
|
||||
def test_filter_out_matches_release_name(self):
|
||||
"""Exclude entries when filter-out matches a derived release field."""
|
||||
self._skip_if_matrix_empty("linux_matrix", 3)
|
||||
base = self.derive_package_matrix(self.artifact_data, os_name="linux", package="deb")
|
||||
if not base:
|
||||
self.skipTest("No Ubuntu entries available for deb filter-out test")
|
||||
|
||||
selected_key, selected_payload = next(iter(base.items()))
|
||||
selected_release = str(selected_payload["ubuntuReleaseName"])
|
||||
matrix = self.derive_package_matrix(
|
||||
self.artifact_data,
|
||||
os_name="linux",
|
||||
package="deb",
|
||||
filter_out="questing,focal",
|
||||
filter_out=selected_release,
|
||||
)
|
||||
self.assertNotIn("Ubuntu-2510-builder", matrix)
|
||||
self.assertNotIn("Ubuntu-2004-builder", matrix)
|
||||
self.assertIn("Ubuntu-2404-builder", matrix)
|
||||
self.assertNotIn(selected_key, matrix)
|
||||
|
||||
def test_invalid_os_package_combination_rejected(self):
|
||||
"""Reject unsupported OS/package combinations."""
|
||||
@ -103,14 +165,19 @@ class DeriveDockerImageMatrixTests(unittest.TestCase):
|
||||
|
||||
def test_build_type_derivation_with_filter_out(self):
|
||||
"""Build type returns linux matrix and honors filter_out by releaseName."""
|
||||
self._skip_if_matrix_empty("linux_matrix", 3)
|
||||
selected_key, selected_payload = next(iter(self.artifact_data["linux_matrix"].items()))
|
||||
selected_release = str(selected_payload.get("releaseName", "")).strip()
|
||||
if not selected_release:
|
||||
self.skipTest("Selected linux matrix entry has no releaseName for filter-out test")
|
||||
|
||||
matrix = self.derive_typed_matrix(
|
||||
self.artifact_data,
|
||||
target_type="build",
|
||||
os_name="linux",
|
||||
filter_out="questing",
|
||||
filter_out=selected_release,
|
||||
)
|
||||
self.assertNotIn("Ubuntu-2510-builder", matrix)
|
||||
self.assertIn("Ubuntu-2404-builder", matrix)
|
||||
self.assertNotIn(selected_key, matrix)
|
||||
|
||||
def test_build_type_derivation_for_windows_returns_win_matrix(self):
|
||||
"""Build type windows path should return the windows matrix unchanged."""
|
||||
@ -123,14 +190,21 @@ class DeriveDockerImageMatrixTests(unittest.TestCase):
|
||||
|
||||
def test_source_type_select_returns_single_entry(self):
|
||||
"""Source type should return a single selected Ubuntu entry."""
|
||||
self._skip_if_matrix_empty("linux_matrix", 3)
|
||||
ubuntu_entries = self._ubuntu_linux_entries()
|
||||
if not ubuntu_entries:
|
||||
self.skipTest("No Ubuntu entries available for source select test")
|
||||
|
||||
selected_key, selected_payload = next(iter(ubuntu_entries.items()))
|
||||
selected_release = str(selected_payload["releaseName"])
|
||||
matrix = self.derive_typed_matrix(
|
||||
self.artifact_data,
|
||||
target_type="source",
|
||||
os_name="linux",
|
||||
select="noble",
|
||||
select=selected_release,
|
||||
)
|
||||
self.assertEqual(set(matrix.keys()), {"Ubuntu-2404-builder"})
|
||||
self.assertEqual(matrix["Ubuntu-2404-builder"]["ubuntuReleaseName"], "noble")
|
||||
self.assertEqual(set(matrix.keys()), {selected_key})
|
||||
self.assertEqual(matrix[selected_key]["ubuntuReleaseName"], selected_release)
|
||||
|
||||
def test_source_type_requires_select(self):
|
||||
"""Source/docs types should require explicit select value."""
|
||||
@ -148,7 +222,7 @@ class DeriveDockerImageMatrixTests(unittest.TestCase):
|
||||
self.artifact_data,
|
||||
target_type="source",
|
||||
os_name="linux",
|
||||
select="jammy,noble",
|
||||
select="__no_such_release__",
|
||||
)
|
||||
|
||||
def test_select_and_filter_out_are_mutually_exclusive(self):
|
||||
@ -188,6 +262,64 @@ class DeriveDockerImageMatrixTests(unittest.TestCase):
|
||||
parsed = ast.literal_eval(result.stdout.strip())
|
||||
self.assertEqual(parsed, self.artifact_data["win_matrix"])
|
||||
|
||||
def test_cli_empty_on_error_manual_build_returns_empty_object(self):
|
||||
"""Manual Azure build should use fallback and return '{}' on derivation errors."""
|
||||
script_path = Path(__file__).resolve().parents[1] / "derive_docker_image_matrix.py"
|
||||
env = os.environ.copy()
|
||||
env["TF_BUILD"] = "true"
|
||||
env["BUILD_REASON"] = "Manual"
|
||||
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(script_path),
|
||||
"--input",
|
||||
str(self.fixture),
|
||||
"--empty-on-error-non-ci",
|
||||
"--type",
|
||||
"source",
|
||||
"--os",
|
||||
"linux",
|
||||
],
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=env,
|
||||
)
|
||||
|
||||
self.assertEqual(result.returncode, 0, msg=result.stderr)
|
||||
self.assertEqual(result.stdout.strip(), "{}")
|
||||
self.assertIn("##vso[task.logissue type=warning]", result.stderr)
|
||||
self.assertIn("defaulting to empty object", result.stderr)
|
||||
|
||||
def test_cli_empty_on_error_non_ci_stays_strict_in_scheduled_ci(self):
|
||||
"""Strict scheduled CI should still fail even when fallback flag is set."""
|
||||
script_path = Path(__file__).resolve().parents[1] / "derive_docker_image_matrix.py"
|
||||
env = os.environ.copy()
|
||||
env["TF_BUILD"] = "true"
|
||||
env["BUILD_REASON"] = "Schedule"
|
||||
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(script_path),
|
||||
"--input",
|
||||
str(self.fixture),
|
||||
"--empty-on-error-non-ci",
|
||||
"--type",
|
||||
"source",
|
||||
"--os",
|
||||
"linux",
|
||||
],
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=env,
|
||||
)
|
||||
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("--select is required", result.stderr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Run this file directly, or use unittest discovery from the repo root.
|
||||
@ -202,4 +334,25 @@ if __name__ == "__main__":
|
||||
if args.artifact_file:
|
||||
ARTIFACT_FILE_OVERRIDE = Path(args.artifact_file)
|
||||
|
||||
unittest.main(argv=[sys.argv[0], *remaining_args])
|
||||
# Preserve unittest CLI filtering behavior via remaining_args.
|
||||
loader = unittest.defaultTestLoader
|
||||
suite = loader.loadTestsFromName(__name__)
|
||||
if remaining_args:
|
||||
suite = loader.loadTestsFromNames(remaining_args, module=sys.modules[__name__])
|
||||
|
||||
result = unittest.TextTestRunner(verbosity=2).run(suite)
|
||||
if result.wasSuccessful():
|
||||
raise SystemExit(0)
|
||||
|
||||
if _is_ci_build():
|
||||
raise SystemExit(1)
|
||||
|
||||
# Non-CI runs should report issues as warnings but not fail callers.
|
||||
message = (
|
||||
"Matrix validation had failures/errors "
|
||||
f"(failures={len(result.failures)}, errors={len(result.errors)}). "
|
||||
"Treating as warnings because this is not a CI build."
|
||||
)
|
||||
warnings.warn(message, UserWarning)
|
||||
print(f"##vso[task.logissue type=warning]{message}")
|
||||
raise SystemExit(0)
|
||||
|
||||
118
CHANGELOG
118
CHANGELOG
@ -2,25 +2,62 @@ Change Log for Releases
|
||||
==============================
|
||||
|
||||
## 004.next
|
||||
* New Features
|
||||
- X4xx: Extended existing GPIO SPI support beyond 32 bits, to 64-bit transfers.
|
||||
- FPGA tools: Added support for DCP files as source inputs.
|
||||
- example: `benchmark_rate` can optionally wait for TX end-of-burst
|
||||
acknowledgements with a configurable timeout (`--tx_eob_ack_timeout`).
|
||||
* Bug Fixes
|
||||
- X4xx: Corrected the RFDC NCO reset sequence and adjusted its default wait time.
|
||||
- B3xx: Corrected the GPS timepulse polarity.
|
||||
- B2xx: Added missing firmware descriptors for B200-mini and B205-mini variants.
|
||||
- MultiUSRP API: RFNoC-backed `set_master_clock_rate()` now updates connected
|
||||
DDC/DUC rates while preserving the requested TX/RX sample rates.
|
||||
- FPGA DSP: Fixed halfband temporal decimation.
|
||||
- FPGA DSP: Improved resource usage, CIC flow-control and reset handling.
|
||||
- RFNoC: The image builder now handles GRC builds with empty parameters and
|
||||
correctly processes the `--CHECK` image-build option.
|
||||
- MPM: Improved X4xx device discovery responsiveness by caching transport
|
||||
information and refreshing it when network interfaces or addresses change.
|
||||
- MPM: RPC requests made during a device reset now return a transient
|
||||
`UNAVAILABLE` status so clients can retry.
|
||||
* API Changes
|
||||
- Added explicit 32-bit and 64-bit SPI transaction methods to the C++ API
|
||||
and RFNoC Python bindings. 64-bit transactions are only available on some
|
||||
devices.
|
||||
* FPGA
|
||||
- Updated FPGA build flow and test benches to Vivado 2025.2.
|
||||
|
||||
## 004.011.000.000
|
||||
* Highlights / Main Changes
|
||||
- Update underlying OE version on embedded devices to Scarthgap (5.0 LTS).
|
||||
Note that some of the MPM updates require this update.
|
||||
- Add support for the new USRP B310, a PCIe/Thunderbolt-based device which
|
||||
supports 2x2 RF channels at up to 100 MHz bandwidth per channel.
|
||||
- Add support for X410, X420 and X440 variants with locked FPGA
|
||||
- Update underlying OE version on embedded devices to Scarthgap (5.0 LTS)
|
||||
and Linux kernel to version 6.12.
|
||||
- Migrated the MPM RPC transport layer from mprpc (msgpack-RPC) to gRPC/Protobuf,
|
||||
impacting all embedded devices running MPM.
|
||||
* New Features
|
||||
- X420 and X440:
|
||||
- The X420_X4_200, X420_CG_400, X420_X4_400 and X440_X4_200 FPGA images now
|
||||
use fixed fabric resamplers (3x) and RFDC resamplers instead of RFDC
|
||||
resamplers alone. This ensures repeatable phase and time offsets between
|
||||
repeatability events on these updated FPGA images. Note that this feature
|
||||
is currently mutually exclusive with dual rate support on X440, and it
|
||||
increases the resource utilization. To revert to the previous image with
|
||||
non-repeatable phase offsets, a custom image must be built.
|
||||
- X420:
|
||||
- Added support for the 400 MHz FPGA variants, including the X420_CG_400
|
||||
and X420_X4_400 images.
|
||||
- X440:
|
||||
- The X440_X4_200 image now uses fabric resamplers instead of RFDC
|
||||
resamplers, which ensures repeatable phase offsets between devices and
|
||||
reboots. Note that this feature is currently mutually exclusive with dual
|
||||
rate support, and it increases the resource utilization. To revert to the
|
||||
previous image with non-repeatable phase offsets, a custom image must be
|
||||
built. By default, it is set to false (i.e., same as old behavior),
|
||||
except for the X440_X4_200 image.
|
||||
- A new BSP parameter (USE_FABRIC_RESAMPLERS) is introduced to toggle this
|
||||
new feature.
|
||||
- X4xx: The ref_locked sensor no longer queries the sticky bits; instead, it
|
||||
only reports the current lock state. The ref_stable sensor is added to
|
||||
include the sticky bits.
|
||||
- Introduced a new BSP parameter (USE_FABRIC_RESAMPLERS) in the X4_200 image
|
||||
to enable easy toggling between using fixed fabric resamplers (the new default)
|
||||
and the previous behavior. The parameter is set to true (use fixed fabric
|
||||
resamplers) in this image and is not available in other X440 FPGA images.
|
||||
- X4xx:
|
||||
- The ref_locked sensor no longer queries the sticky bits; instead, it
|
||||
only reports the current lock state. The ref_stable sensor is added to
|
||||
include the sticky bits.
|
||||
- Python:
|
||||
- The ref_clk_calibration feature is now accessible in the Python API.
|
||||
- Streamers:
|
||||
@ -40,12 +77,32 @@ Change Log for Releases
|
||||
CHDR width).
|
||||
- Allow parallel initialization of RFNoC-based devices for faster session
|
||||
start when using multiple USRPs in a multi_usrp or rfnoc_graph object.
|
||||
- Wideband DDC/DUC:
|
||||
- Updated the RFNoC DDC and DUC blocks to support bandwidths > 200 MHz.
|
||||
With this update, users can add the DDC and DUC blocks to their custom
|
||||
FPGA images for bandwidths up to 1600 MHz (2000 MSps).
|
||||
- Shipping FPGA images:
|
||||
- Added new shipping FPGA images for x420:
|
||||
- x420_X4_400
|
||||
- x420_CG_400
|
||||
- Added DDC and DUC functionality for all channels to the following
|
||||
shipping FPGA images:
|
||||
- x410_CG_400
|
||||
- x420_X4_400
|
||||
- x420_CG_400
|
||||
- x420_X4_1000
|
||||
- x420_CG_1000
|
||||
- x440_X4_800
|
||||
- x440_CG_800
|
||||
- x440_X4_1600
|
||||
- x440_CG_1600
|
||||
For custom designs based on one of these updated rfnoc_image_core.yml
|
||||
files, please note that the new addition of the DDC/DUC blocks to FPGA
|
||||
images will have an impact on how much custom logic can fit into these
|
||||
designs. If needed the DDC/DUC blocks can be removed to revert to the
|
||||
previous behavior.
|
||||
- MPM-based devices can now be initialized in parallel within a MultiUSRP
|
||||
session.
|
||||
- X420:
|
||||
- Added support for the 400 MHz FPGA variants, including the X420_CG_400
|
||||
and X420_X4_400 images.
|
||||
- Added fabric-resampler support to the X420 FPGA designs.
|
||||
- GPS:
|
||||
- Added support for UBLOX communication.
|
||||
- Improved compatibility with GPS receivers by accepting additional
|
||||
@ -73,11 +130,16 @@ Change Log for Releases
|
||||
- Some logging APIs that previously used boost::posix_time are now
|
||||
declared deprecated, and alternatives that use standard C++ constructs have
|
||||
been introduced.
|
||||
- MPM device initialization is parallel by default. The serialize_init=1
|
||||
device argument restores serial initialization.
|
||||
- MPM device initialization is serial by default. The `init_threads=N`
|
||||
device argument enables parallel initialization.
|
||||
- Added an option to resynchronize X4xx SPLL R-dividers after reference-lock
|
||||
loss using reset_spll_r_divider=1 in the synchronization arguments. This
|
||||
is intended for advanced users only.
|
||||
- Minor DDC/DUC C++ API changes as part of the Wideband DDC/DUC update.
|
||||
* FPGA
|
||||
- Many improvements to resource utilization for improved build success rate
|
||||
and compilation time.
|
||||
- Enabled usage of VHDL 2008.
|
||||
* Codebase Management
|
||||
- Fully removed Boost.Thread, Boost.Date_Time, and Boost.Chrono dependencies
|
||||
for libuhd. Other Boost usage has been reduced.
|
||||
@ -91,10 +153,14 @@ Change Log for Releases
|
||||
or via a package manager such as vcpkg.
|
||||
- Improved build reproducibility by removing the build-directory path from
|
||||
embedded build information.
|
||||
* FPGA
|
||||
- Many improvements to resource utilization for improved build success rate
|
||||
and compilation time.
|
||||
- Enabled usage of VHDL 2008.
|
||||
* 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: jammy (22.04), noble (24.04), resolute (26.04)
|
||||
- Fedora: 41, 42, 43
|
||||
- Windows: 10 22H2, 11 24H2
|
||||
- MacOS: Sequoia (15.7)
|
||||
|
||||
## 004.010.000.000
|
||||
* Highlights / Main Changes
|
||||
@ -449,8 +515,8 @@ Changes compared to UHD 4.9.0.0:
|
||||
- Add device DNA feature for X310, X410, X440. This allows reading back the
|
||||
FPGA's DNA, which is a unique identifier.
|
||||
- Embedded OS for E3xx, N3xx, and X4xx devices
|
||||
- With the switch to Yocto 4.0 (kirkstone), the SD card and eMMC images no
|
||||
longer pre-populate the second system partition. Refer to the device
|
||||
- With the switch to Yocto 4.0 (kirkstone), the SD card and eMMC images no
|
||||
longer pre-populate the second system partition. Refer to the device
|
||||
specific "Updating Filesystems" section in the user manual for how to populate it.
|
||||
This reduces the size of compressed .sdimg filesystem images.
|
||||
- X440
|
||||
|
||||
@ -16,10 +16,13 @@ managing the transport from the host to the FPGA by configuring IO and DMA.
|
||||
|
||||
In order to compile the USRP B200 and B210 bootloader and firmware, you will
|
||||
need the FX3 SDK distributed by Cypress Semiconductor. You can download the
|
||||
[FX3 SDK from here](https://www.cypress.com/documentation/software-and-drivers/ez-usb-fx3-software-development-kit)
|
||||
[FX3 SDK from here](https://www.cypress.com/documentation/software-and-drivers/ez-usb-fx3-software-development-kit).
|
||||
Version 1.3.5 is known to be working.
|
||||
|
||||
From the SDK, extract the ARM cross-compile toolchain tarball `ARM-GCC.tar.gz`
|
||||
and the FX3 firmware tarball `fx3_firmware_linux.tar.gz`.
|
||||
Get the latest version of the [GNU Toolchains for ARM from arm.com](https://gitlab.arm.com/tooling/gnu-toolchains-for-arm)
|
||||
and extract it. Version 15.3.rel1 is known to be working.
|
||||
|
||||
Set up the appropriate environment variables for the build:
|
||||
|
||||
@ -32,15 +35,39 @@ Set up the appropriate environment variables for the build:
|
||||
These should look something like this:
|
||||
|
||||
```
|
||||
$ export ARMGCC_INSTALL_PATH=/path/to/my/files/arm-2013.11
|
||||
$ export ARMGCC_VERSION=4.8.1
|
||||
$ export ARMGCC_INSTALL_PATH=/path/to/my/files/arm-gnu-toolchain-15.3.rel1-x86_64-arm-none-eabi
|
||||
$ export ARMGCC_VERSION=15.3.1
|
||||
$ export FX3FWROOT=/path/to/my/files/cyfx3sdk
|
||||
```
|
||||
|
||||
The SDK makefiles invoke `arm-none-eabi-gcc` unqualified and only use
|
||||
`ARMGCC_INSTALL_PATH` to locate the libraries, so the toolchain must also be on
|
||||
your `PATH`:
|
||||
|
||||
```
|
||||
$ export PATH=$ARMGCC_INSTALL_PATH/bin:$PATH
|
||||
```
|
||||
|
||||
Without this, the build either fails with `arm-none-eabi-gcc: No such file or
|
||||
directory` or, if a different ARM toolchain happens to be installed, silently
|
||||
compiles with that compiler while linking against the libraries of the toolchain
|
||||
above. Verify the expected compiler is picked up:
|
||||
|
||||
```
|
||||
$ arm-none-eabi-gcc --version
|
||||
```
|
||||
|
||||
## Applying the Patch to the Toolchain
|
||||
|
||||
Now, you'll need to apply a patch to some files in the Cypress SDK. Apply the
|
||||
patch `b200/fx3_mem_map.patch` as follows:
|
||||
Now, you'll need to apply a patch to some files in the Cypress SDK. Before doing
|
||||
so, ensure all files in the SDK have Unix line endings and are writable for the user:
|
||||
|
||||
```
|
||||
$ cd $FX3FWROOT
|
||||
$ find . -type f -exec dos2unix {} \; -exec chmod u+w {} \;
|
||||
```
|
||||
|
||||
Apply the patch `b200/fx3_mem_map.patch` as follows:
|
||||
|
||||
```
|
||||
$ cd uhd.git/firmware/fx3
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -9,43 +9,46 @@
|
||||
|
||||
#define EEPROM_REV_UNRECOGNIZED -1
|
||||
|
||||
#define EEPROM_SIGNATURE_ADDR 0x0000
|
||||
#define EEPROM_SIGNATURE_ADDR 0x0000
|
||||
#define EEPROM_SIGNATURE_LENGTH 4
|
||||
|
||||
#define EEPROM_REV0_SIGNATURE 0xB2145943
|
||||
#define EEPROM_REV0_SIGNATURE 0xB2145943
|
||||
#define EEPROM_REV1_OR_GREATER_SIGNATURE 0xB01A5943
|
||||
|
||||
#define EEPROM_REV0_VID_ADDR 0x0006
|
||||
#define EEPROM_REV1_VID_ADDR 0x7F06
|
||||
#define EEPROM_VID_LENGTH 2
|
||||
#define EEPROM_VID_LENGTH 2
|
||||
|
||||
#define VID_ETTUS_RESEARCH 0x2500
|
||||
#define VID_ETTUS_RESEARCH 0x2500
|
||||
#define VID_NATIONAL_INSTRUMENTS 0x3923
|
||||
#define VID_CYPRESS 0x04B4
|
||||
#define VID_CYPRESS 0x04B4
|
||||
|
||||
#define EEPROM_REV0_PID_ADDR 0x0004
|
||||
#define EEPROM_REV1_PID_ADDR 0x7F08
|
||||
#define EEPROM_PID_LENGTH 2
|
||||
#define EEPROM_PID_LENGTH 2
|
||||
|
||||
#define PID_ETTUS_B200_B210 0x0020
|
||||
#define PID_NI_USRP_2900 0x7813
|
||||
#define PID_NI_USRP_2901 0x7814
|
||||
#define PID_ETTUS_B200_MINI 0x0021
|
||||
#define PID_ETTUS_B205_MINI 0x0022
|
||||
#define PID_ETTUS_B206_MINI 0x0023
|
||||
#define PID_NI_USRP_2900 0x7813
|
||||
#define PID_NI_USRP_2901 0x7814
|
||||
#define PID_CYPRESS_DEFAULT 0x00F0
|
||||
|
||||
#define EEPROM_REV0_SERIAL_ADDR 0x04F7
|
||||
#define EEPROM_REV1_SERIAL_ADDR 0x7F25
|
||||
#define EEPROM_SERIAL_LENGTH 9
|
||||
#define EEPROM_SERIAL_LENGTH 9
|
||||
|
||||
#define EEPROM_REV1_MAGIC_ADDR 0x7F00
|
||||
#define EEPROM_MAGIC_LENGTH 2
|
||||
#define EEPROM_EXPECTED_MAGIC 0xB200
|
||||
#define EEPROM_MAGIC_LENGTH 2
|
||||
#define EEPROM_EXPECTED_MAGIC 0xB200
|
||||
|
||||
#define EEPROM_REV1_REV_ADDR 0x7F02
|
||||
#define EEPROM_REV_LENGTH 2
|
||||
#define EEPROM_EXPECTED_REV 1
|
||||
#define EEPROM_REV_LENGTH 2
|
||||
#define EEPROM_EXPECTED_REV 1
|
||||
|
||||
#define EEPROM_REV1_COMPAT_ADDR 0x7F04
|
||||
#define EEPROM_COMPAT_LENGTH 2
|
||||
#define EEPROM_EXPECTED_COMPAT 1
|
||||
#define EEPROM_COMPAT_LENGTH 2
|
||||
#define EEPROM_EXPECTED_COMPAT 1
|
||||
|
||||
#endif /* _COMMON_CONST_H */
|
||||
#endif /* _COMMON_CONST_H */
|
||||
@ -9,96 +9,107 @@
|
||||
information read from the EEPROM. */
|
||||
|
||||
/* Standard Device Descriptor for USB 2.0 */
|
||||
unsigned char common_usb2_dev_desc[] =
|
||||
{
|
||||
0x12, /* Descriptor size */
|
||||
0x01,/* CY_U3P_USB_DEVICE_DESCR Device descriptor type */
|
||||
0x10,0x02, /* USB 2.10 */
|
||||
0xFF, /* Device class */
|
||||
0x00, /* Device sub-class */
|
||||
0x00, /* Device protocol */
|
||||
0x40, /* Maxpacket size for EP0 : 64 bytes */
|
||||
0xB4,0x04, /* Vendor ID */
|
||||
0xF0,0x00, /* Product ID */
|
||||
0x00,0x00, /* Device release number */
|
||||
0x01, /* Manufacture string index */
|
||||
0x02, /* Product string index */
|
||||
0x03, /* Serial number string index */
|
||||
0x01 /* Number of configurations */
|
||||
unsigned char common_usb2_dev_desc[] = {
|
||||
0x12, /* Descriptor size */
|
||||
0x01, /* CY_U3P_USB_DEVICE_DESCR Device descriptor type */
|
||||
0x10,
|
||||
0x02, /* USB 2.10 */
|
||||
0xFF, /* Device class */
|
||||
0x00, /* Device sub-class */
|
||||
0x00, /* Device protocol */
|
||||
0x40, /* Maxpacket size for EP0 : 64 bytes */
|
||||
0xB4,
|
||||
0x04, /* Vendor ID */
|
||||
0xF0,
|
||||
0x00, /* Product ID */
|
||||
0x00,
|
||||
0x00, /* Device release number */
|
||||
0x01, /* Manufacture string index */
|
||||
0x02, /* Product string index */
|
||||
0x03, /* Serial number string index */
|
||||
0x01 /* Number of configurations */
|
||||
};
|
||||
|
||||
/* Standard Device Descriptor for USB 3.0 */
|
||||
unsigned char common_usb3_dev_desc[] =
|
||||
{
|
||||
0x12, /* Descriptor size */
|
||||
0x01,/* CY_U3P_USB_DEVICE_DESCR Device descriptor type */
|
||||
0x00,0x03, /* USB 3.0 */
|
||||
0xFF, /* Device class */
|
||||
0x00, /* Device sub-class */
|
||||
0x00, /* Device protocol */
|
||||
0x09, /* Maxpacket size for EP0 : 2^9 */
|
||||
0xB4,0x04, /* Vendor ID */
|
||||
0xF0,0x00, /* Product ID */
|
||||
0x00,0x00, /* Device release number */
|
||||
0x01, /* Manufacture string index */
|
||||
0x02, /* Product string index */
|
||||
0x03, /* Serial number string index */
|
||||
0x01 /* Number of configurations */
|
||||
unsigned char common_usb3_dev_desc[] = {
|
||||
0x12, /* Descriptor size */
|
||||
0x01, /* CY_U3P_USB_DEVICE_DESCR Device descriptor type */
|
||||
0x00,
|
||||
0x03, /* USB 3.0 */
|
||||
0xFF, /* Device class */
|
||||
0x00, /* Device sub-class */
|
||||
0x00, /* Device protocol */
|
||||
0x09, /* Maxpacket size for EP0 : 2^9 */
|
||||
0xB4,
|
||||
0x04, /* Vendor ID */
|
||||
0xF0,
|
||||
0x00, /* Product ID */
|
||||
0x00,
|
||||
0x00, /* Device release number */
|
||||
0x01, /* Manufacture string index */
|
||||
0x02, /* Product string index */
|
||||
0x03, /* Serial number string index */
|
||||
0x01 /* Number of configurations */
|
||||
};
|
||||
|
||||
/* Standard Device Qualifier Descriptor */
|
||||
const unsigned char common_dev_qual_desc[] =
|
||||
{
|
||||
0x0A, /* Descriptor size */
|
||||
0x06,/* CY_U3P_USB_DEVQUAL_DESCR Device qualifier descriptor type */
|
||||
0x00,0x02, /* USB 2.0 */
|
||||
0xFF, /* Device class */
|
||||
0x00, /* Device sub-class */
|
||||
0x00, /* Device protocol */
|
||||
0x40, /* Maxpacket size for EP0 : 64 bytes */
|
||||
0x01, /* Number of configurations */
|
||||
0x00 /* Reserved */
|
||||
const unsigned char common_dev_qual_desc[] = {
|
||||
0x0A, /* Descriptor size */
|
||||
0x06, /* CY_U3P_USB_DEVQUAL_DESCR Device qualifier descriptor type */
|
||||
0x00,
|
||||
0x02, /* USB 2.0 */
|
||||
0xFF, /* Device class */
|
||||
0x00, /* Device sub-class */
|
||||
0x00, /* Device protocol */
|
||||
0x40, /* Maxpacket size for EP0 : 64 bytes */
|
||||
0x01, /* Number of configurations */
|
||||
0x00 /* Reserved */
|
||||
};
|
||||
|
||||
/* Standard Binary Device Object Store Descriptor */
|
||||
const unsigned char common_usb_bos_desc[] =
|
||||
{
|
||||
0x05, /* Descriptor size */
|
||||
0x0F,/* CY_U3P_BOS_DESCR Device descriptor type */
|
||||
0x16,0x00, /* Length of this descriptor and all sub descriptors */
|
||||
0x02, /* Number of device capability descriptors */
|
||||
const unsigned char common_usb_bos_desc[] = {
|
||||
0x05, /* Descriptor size */
|
||||
0x0F, /* CY_U3P_BOS_DESCR Device descriptor type */
|
||||
0x16,
|
||||
0x00, /* Length of this descriptor and all sub descriptors */
|
||||
0x02, /* Number of device capability descriptors */
|
||||
|
||||
/* USB 2.0 extension */
|
||||
0x07, /* Descriptor size */
|
||||
0x10,/* CY_U3P_DEVICE_CAPB_DESCR Device capability type descriptor */
|
||||
0x02,/* CY_U3P_USB2_EXTN_CAPB_TYPE USB 2.0 extension capability type */
|
||||
0x02,0x00,0x00,0x00, /* Supported device level features: LPM support */
|
||||
0x07, /* Descriptor size */
|
||||
0x10, /* CY_U3P_DEVICE_CAPB_DESCR Device capability type descriptor */
|
||||
0x02, /* CY_U3P_USB2_EXTN_CAPB_TYPE USB 2.0 extension capability type */
|
||||
0x02,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00, /* Supported device level features: LPM support */
|
||||
|
||||
/* SuperSpeed device capability */
|
||||
0x0A, /* Descriptor size */
|
||||
0x10,/* CY_U3P_DEVICE_CAPB_DESCR Device capability type descriptor */
|
||||
0x03,/* CY_U3P_SS_USB_CAPB_TYPE SuperSpeed device capability type */
|
||||
0x00, /* Supported device level features */
|
||||
0x0E,0x00, /* Speeds supported by the device : SS, HS and FS */
|
||||
0x03, /* Functionality support */
|
||||
0x00, /* U1 Device Exit latency */
|
||||
0x00,0x00 /* U2 Device Exit latency */
|
||||
0x0A, /* Descriptor size */
|
||||
0x10, /* CY_U3P_DEVICE_CAPB_DESCR Device capability type descriptor */
|
||||
0x03, /* CY_U3P_SS_USB_CAPB_TYPE SuperSpeed device capability type */
|
||||
0x00, /* Supported device level features */
|
||||
0x0E,
|
||||
0x00, /* Speeds supported by the device : SS, HS and FS */
|
||||
0x03, /* Functionality support */
|
||||
0x00, /* U1 Device Exit latency */
|
||||
0x00,
|
||||
0x00 /* U2 Device Exit latency */
|
||||
};
|
||||
|
||||
/* Standard Language ID String Descriptor */
|
||||
const unsigned char common_string_lang_id_desc[] =
|
||||
{
|
||||
0x04, /* Descriptor Size */
|
||||
0x03,/* CY_U3P_USB_STRING_DESCR Device Descriptor Type */
|
||||
0x09,0x04 /* Language ID supported */
|
||||
const unsigned char common_string_lang_id_desc[] = {
|
||||
0x04, /* Descriptor Size */
|
||||
0x03, /* CY_U3P_USB_STRING_DESCR Device Descriptor Type */
|
||||
0x09,
|
||||
0x04 /* Language ID supported */
|
||||
};
|
||||
|
||||
|
||||
// clang-format off
|
||||
/* Ettus Manufacturer String Descriptor */
|
||||
const unsigned char common_ettus_manufacturer_desc[] =
|
||||
const unsigned char common_ettus_manufacturer_desc[] =
|
||||
{
|
||||
0x26, /* Descriptor Size */
|
||||
0x03,/* CY_U3P_USB_STRING_DESCR Device Descriptor Type */
|
||||
0x26, /* Descriptor Size */
|
||||
0x03, /* CY_U3P_USB_STRING_DESCR Device Descriptor Type */
|
||||
'E',0x00,
|
||||
't',0x00,
|
||||
't',0x00,
|
||||
@ -120,10 +131,10 @@ const unsigned char common_ettus_manufacturer_desc[] =
|
||||
};
|
||||
|
||||
/* NI Manufacturer String Descriptor */
|
||||
const unsigned char common_ni_manufacturer_desc[] =
|
||||
const unsigned char common_ni_manufacturer_desc[] =
|
||||
{
|
||||
0x36, /* Descriptor Size */
|
||||
0x03,/* CY_U3P_USB_STRING_DESCR Device Descriptor Type */
|
||||
0x36, /* Descriptor Size */
|
||||
0x03, /* CY_U3P_USB_STRING_DESCR Device Descriptor Type */
|
||||
'N',0x00,
|
||||
'a',0x00,
|
||||
't',0x00,
|
||||
@ -154,10 +165,25 @@ const unsigned char common_ni_manufacturer_desc[] =
|
||||
|
||||
|
||||
/* Ettus Product String Descriptor */
|
||||
const unsigned char common_b200_product_desc[] =
|
||||
const unsigned char common_b200_product_desc[] =
|
||||
{
|
||||
0x14, /* Descriptor Size */
|
||||
0x03,/* CY_U3P_USB_STRING_DESCR Device Descriptor Type */
|
||||
0x14, /* Descriptor Size */
|
||||
0x03, /* CY_U3P_USB_STRING_DESCR Device Descriptor Type */
|
||||
'U',0x00,
|
||||
'S',0x00,
|
||||
'R',0x00,
|
||||
'P',0x00,
|
||||
' ',0x00,
|
||||
'B',0x00,
|
||||
'2',0x00,
|
||||
'x',0x00,
|
||||
'0',0x00
|
||||
};
|
||||
|
||||
const unsigned char common_b200_mini_product_desc[] =
|
||||
{
|
||||
0x1E, /* Descriptor Size */
|
||||
0x03, /* CY_U3P_USB_STRING_DESCR Device Descriptor Type */
|
||||
'U',0x00,
|
||||
'S',0x00,
|
||||
'R',0x00,
|
||||
@ -166,14 +192,59 @@ const unsigned char common_b200_product_desc[] =
|
||||
'B',0x00,
|
||||
'2',0x00,
|
||||
'0',0x00,
|
||||
'0',0x00
|
||||
'0',0x00,
|
||||
' ',0x00,
|
||||
'm',0x00,
|
||||
'i',0x00,
|
||||
'n',0x00,
|
||||
'i',0x00
|
||||
};
|
||||
|
||||
const unsigned char common_b205_mini_product_desc[] =
|
||||
{
|
||||
0x1E, /* Descriptor Size */
|
||||
0x03, /* CY_U3P_USB_STRING_DESCR Device Descriptor Type */
|
||||
'U',0x00,
|
||||
'S',0x00,
|
||||
'R',0x00,
|
||||
'P',0x00,
|
||||
' ',0x00,
|
||||
'B',0x00,
|
||||
'2',0x00,
|
||||
'0',0x00,
|
||||
'5',0x00,
|
||||
' ',0x00,
|
||||
'm',0x00,
|
||||
'i',0x00,
|
||||
'n',0x00,
|
||||
'i',0x00
|
||||
};
|
||||
|
||||
const unsigned char common_b206_mini_product_desc[] =
|
||||
{
|
||||
0x1E, /* Descriptor Size */
|
||||
0x03, /* CY_U3P_USB_STRING_DESCR Device Descriptor Type */
|
||||
'U',0x00,
|
||||
'S',0x00,
|
||||
'R',0x00,
|
||||
'P',0x00,
|
||||
' ',0x00,
|
||||
'B',0x00,
|
||||
'2',0x00,
|
||||
'0',0x00,
|
||||
'6',0x00,
|
||||
' ',0x00,
|
||||
'm',0x00,
|
||||
'i',0x00,
|
||||
'n',0x00,
|
||||
'i',0x00
|
||||
};
|
||||
|
||||
/* NI-USRP 2900 Product String Descriptor */
|
||||
const unsigned char common_niusrp_2900_product_desc[] =
|
||||
const unsigned char common_niusrp_2900_product_desc[] =
|
||||
{
|
||||
0x1A, /* Descriptor Size */
|
||||
0x03,/* CY_U3P_USB_STRING_DESCR Device Descriptor Type */
|
||||
0x1A, /* Descriptor Size */
|
||||
0x03, /* CY_U3P_USB_STRING_DESCR Device Descriptor Type */
|
||||
'N',0x00,
|
||||
'I',0x00,
|
||||
' ',0x00,
|
||||
@ -189,10 +260,10 @@ const unsigned char common_niusrp_2900_product_desc[] =
|
||||
};
|
||||
|
||||
/* NI-USRP 2901 Product String Descriptor */
|
||||
const unsigned char common_niusrp_2901_product_desc[] =
|
||||
const unsigned char common_niusrp_2901_product_desc[] =
|
||||
{
|
||||
0x1A, /* Descriptor Size */
|
||||
0x03,/* CY_U3P_USB_STRING_DESCR Device Descriptor Type */
|
||||
0x1A, /* Descriptor Size */
|
||||
0x03, /* CY_U3P_USB_STRING_DESCR Device Descriptor Type */
|
||||
'N',0x00,
|
||||
'I',0x00,
|
||||
' ',0x00,
|
||||
@ -208,10 +279,10 @@ const unsigned char common_niusrp_2901_product_desc[] =
|
||||
};
|
||||
|
||||
/* Unknown Product String Descriptor */
|
||||
const unsigned char common_unknown_desc[] =
|
||||
const unsigned char common_unknown_desc[] =
|
||||
{
|
||||
0x10, /* Descriptor Size */
|
||||
0x03,/* CY_U3P_USB_STRING_DESCR Device Descriptor Type */
|
||||
0x10, /* Descriptor Size */
|
||||
0x03, /* CY_U3P_USB_STRING_DESCR Device Descriptor Type */
|
||||
'U',0x00,
|
||||
'n',0x00,
|
||||
'k',0x00,
|
||||
@ -221,17 +292,18 @@ const unsigned char common_unknown_desc[] =
|
||||
'n',0x00
|
||||
};
|
||||
|
||||
unsigned char common_dev_serial_desc[] =
|
||||
unsigned char common_dev_serial_desc[] =
|
||||
{
|
||||
0x14, /* Descriptor Size */
|
||||
0x03,/* CY_U3P_USB_STRING_DESCR Device Descriptor Type */
|
||||
'0', 0x00,
|
||||
'0', 0x00,
|
||||
'0', 0x00,
|
||||
'0', 0x00,
|
||||
'0', 0x00,
|
||||
'0', 0x00,
|
||||
'0', 0x00,
|
||||
'0', 0x00,
|
||||
'0', 0x00
|
||||
0x14, /* Descriptor Size */
|
||||
0x03, /* CY_U3P_USB_STRING_DESCR Device Descriptor Type */
|
||||
'0',0x00,
|
||||
'0',0x00,
|
||||
'0',0x00,
|
||||
'0',0x00,
|
||||
'0',0x00,
|
||||
'0',0x00,
|
||||
'0',0x00,
|
||||
'0',0x00,
|
||||
'0',0x00
|
||||
};
|
||||
//clang-format on
|
||||
@ -32,9 +32,18 @@ extern const unsigned char common_ettus_manufacturer_desc[];
|
||||
/* NI Manufacturer String Descriptor */
|
||||
extern const unsigned char common_ni_manufacturer_desc[];
|
||||
|
||||
/* Ettus Product String Descriptor */
|
||||
/* Ettus B200/B210 Product String Descriptor */
|
||||
extern const unsigned char common_b200_product_desc[];
|
||||
|
||||
/* Ettus B200 mini Product String Descriptor */
|
||||
extern const unsigned char common_b200_mini_product_desc[];
|
||||
|
||||
/* Ettus B205 mini Product String Descriptor */
|
||||
extern const unsigned char common_b205_mini_product_desc[];
|
||||
|
||||
/* Ettus B206 mini Product String Descriptor */
|
||||
extern const unsigned char common_b206_mini_product_desc[];
|
||||
|
||||
/* NI-USRP 2900 Product String Descriptor */
|
||||
extern const unsigned char common_niusrp_2900_product_desc[];
|
||||
|
||||
|
||||
@ -12,7 +12,8 @@
|
||||
|
||||
/* Check the rev, magic value, and eeprom compatibility field to determine if
|
||||
our eeprom map is up-to-date with what is written on the device */
|
||||
int eeprom_is_readable(eeprom_read_t read_fn) {
|
||||
int eeprom_is_readable(eeprom_read_t read_fn)
|
||||
{
|
||||
/* Only check this once, then store that info */
|
||||
static int is_readable = UNREAD;
|
||||
|
||||
@ -22,16 +23,14 @@ int eeprom_is_readable(eeprom_read_t read_fn) {
|
||||
|
||||
int rev = get_rev(read_fn);
|
||||
|
||||
if (rev == EEPROM_REV_UNRECOGNIZED)
|
||||
{
|
||||
if (rev == EEPROM_REV_UNRECOGNIZED) {
|
||||
is_readable = 0;
|
||||
return is_readable;
|
||||
}
|
||||
|
||||
|
||||
/* If rev is 0, there's no further checks available */
|
||||
if (rev == 0)
|
||||
{
|
||||
if (rev == 0) {
|
||||
is_readable = 1;
|
||||
return is_readable;
|
||||
}
|
||||
@ -91,7 +90,6 @@ int get_rev(eeprom_read_t read_fn)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Read the vendor ID from EEPROM using the function specified*/
|
||||
uint16_t get_vid(eeprom_read_t read_fn)
|
||||
{
|
||||
@ -189,6 +187,12 @@ const uint8_t* get_product_string_descriptor(uint16_t pid)
|
||||
{
|
||||
if (pid == PID_ETTUS_B200_B210) {
|
||||
return common_b200_product_desc;
|
||||
} else if (pid == PID_ETTUS_B200_MINI) {
|
||||
return common_b200_mini_product_desc;
|
||||
} else if (pid == PID_ETTUS_B205_MINI) {
|
||||
return common_b205_mini_product_desc;
|
||||
} else if (pid == PID_ETTUS_B206_MINI) {
|
||||
return common_b206_mini_product_desc;
|
||||
} else if (pid == PID_NI_USRP_2900) {
|
||||
return common_niusrp_2900_product_desc;
|
||||
} else if (pid == PID_NI_USRP_2901) {
|
||||
@ -196,4 +200,4 @@ const uint8_t* get_product_string_descriptor(uint16_t pid)
|
||||
} else {
|
||||
return common_unknown_desc;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,43 +6,45 @@
|
||||
|
||||
#include "cyu3i2c.h"
|
||||
|
||||
static uint16_t glI2cPageSize = 0x40; /* I2C Page size to be used for transfers. */
|
||||
|
||||
/* I2c initialization for EEPROM programming. */
|
||||
void CyFxI2cInit (uint16_t pageLen) {
|
||||
void CyFxI2cInit(uint16_t pageLen)
|
||||
{
|
||||
CyU3PI2cConfig_t i2cConfig;
|
||||
|
||||
/* Initialize and configure the I2C master module. */
|
||||
CyU3PI2cInit ();
|
||||
CyU3PI2cInit();
|
||||
|
||||
/* Start the I2C master block. The bit rate is set at 100KHz.
|
||||
* The data transfer is done via DMA. */
|
||||
CyU3PMemSet ((uint8_t *)&i2cConfig, 0, sizeof(i2cConfig));
|
||||
CyU3PMemSet((uint8_t*)&i2cConfig, 0, sizeof(i2cConfig));
|
||||
i2cConfig.bitRate = CY_FX_USBI2C_I2C_BITRATE;
|
||||
i2cConfig.busTimeout = 0xFFFFFFFF;
|
||||
i2cConfig.dmaTimeout = 0xFFFF;
|
||||
i2cConfig.isDma = CyFalse;
|
||||
|
||||
CyU3PI2cSetConfig (&i2cConfig, NULL);
|
||||
CyU3PI2cSetConfig(&i2cConfig, NULL);
|
||||
glI2cPageSize = pageLen;
|
||||
}
|
||||
|
||||
/* I2C read / write for programmer application. */
|
||||
void CyFxUsbI2cTransfer (
|
||||
uint16_t byteAddress,
|
||||
uint8_t devAddr,
|
||||
uint16_t byteCount,
|
||||
uint8_t *buffer,
|
||||
CyBool_t isRead)
|
||||
void CyFxUsbI2cTransfer(uint16_t byteAddress,
|
||||
uint8_t devAddr,
|
||||
uint16_t byteCount,
|
||||
uint8_t* buffer,
|
||||
CyBool_t isRead)
|
||||
{
|
||||
CyU3PI2cPreamble_t preamble;
|
||||
uint16_t pageCount = (byteCount / glI2cPageSize);
|
||||
uint16_t resCount = glI2cPageSize;
|
||||
uint16_t resCount = glI2cPageSize;
|
||||
|
||||
if (byteCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((byteCount % glI2cPageSize) != 0) {
|
||||
pageCount ++;
|
||||
pageCount++;
|
||||
resCount = byteCount % glI2cPageSize;
|
||||
}
|
||||
|
||||
@ -56,7 +58,8 @@ void CyFxUsbI2cTransfer (
|
||||
preamble.buffer[3] = (devAddr | 0x01);
|
||||
preamble.ctrlMask = 0x0004;
|
||||
|
||||
CyU3PI2cReceiveBytes (&preamble, buffer, (pageCount == 1) ? resCount : glI2cPageSize, 0);
|
||||
CyU3PI2cReceiveBytes(
|
||||
&preamble, buffer, (pageCount == 1) ? resCount : glI2cPageSize, 0);
|
||||
} else {
|
||||
/* Write. Update the preamble information. */
|
||||
preamble.length = 3;
|
||||
@ -65,18 +68,19 @@ void CyFxUsbI2cTransfer (
|
||||
preamble.buffer[2] = (uint8_t)(byteAddress & 0xFF);
|
||||
preamble.ctrlMask = 0x0000;
|
||||
|
||||
CyU3PI2cTransmitBytes (&preamble, buffer, (pageCount == 1) ? resCount : glI2cPageSize, 0);
|
||||
CyU3PI2cTransmitBytes(
|
||||
&preamble, buffer, (pageCount == 1) ? resCount : glI2cPageSize, 0);
|
||||
/* Wait for the write to complete. */
|
||||
preamble.length = 1;
|
||||
CyU3PI2cWaitForAck(&preamble, 200);
|
||||
}
|
||||
|
||||
/* An additional delay seems to be required after receiving an ACK. */
|
||||
CyU3PThreadSleep (1);
|
||||
CyU3PThreadSleep(1);
|
||||
|
||||
/* Update the parameters */
|
||||
byteAddress += glI2cPageSize;
|
||||
byteAddress += glI2cPageSize;
|
||||
buffer += glI2cPageSize;
|
||||
pageCount --;
|
||||
pageCount--;
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,33 +7,34 @@
|
||||
|
||||
#include "cyu3externcstart.h"
|
||||
|
||||
#include "cyu3usbconst.h"
|
||||
#include "cyu3types.h"
|
||||
#include "cyu3usbconst.h"
|
||||
|
||||
/* Following two definitions made in b200_main.h for consistency. */
|
||||
/* define B200_VREQ_EEPROM_WRITE (uint8_t)(0xBA) */
|
||||
/* define B200_VREQ_EEPROM_READ (uint8_t)(0xBB) */
|
||||
|
||||
static uint16_t glI2cPageSize = 0x40; /* I2C Page size to be used for transfers. */
|
||||
|
||||
/* This application uses EEPROM as the slave I2C device. The I2C EEPROM
|
||||
* part number used is 24LC256. The capacity of the EEPROM is 256K bits */
|
||||
#define CY_FX_USBI2C_I2C_MAX_CAPACITY (32 * 1024) /* Capacity in bytes */
|
||||
#define CY_FX_USBI2C_I2C_MAX_CAPACITY (32 * 1024) /* Capacity in bytes */
|
||||
|
||||
/* The following constant is defined based on the page size that the I2C
|
||||
* device support. 24LC256 support 64 byte page write access. */
|
||||
#define CY_FX_USBI2C_I2C_PAGE_SIZE (64)
|
||||
#define CY_FX_USBI2C_I2C_PAGE_SIZE (64)
|
||||
|
||||
/* I2C Data rate */
|
||||
#define CY_FX_USBI2C_I2C_BITRATE (100000)
|
||||
#define CY_FX_USBI2C_I2C_BITRATE (100000)
|
||||
|
||||
/* Give a timeout value of 5s for any programming. */
|
||||
#define CY_FX_USB_I2C_TIMEOUT (5000)
|
||||
#define CY_FX_USB_I2C_TIMEOUT (5000)
|
||||
|
||||
/* Function forward-declerations. */
|
||||
void CyFxI2cInit (uint16_t pageLen);
|
||||
void CyFxUsbI2cTransfer (uint16_t byteAddress, uint8_t devAddr,
|
||||
uint16_t byteCount, uint8_t *buffer, CyBool_t isRead);
|
||||
void CyFxI2cInit(uint16_t pageLen);
|
||||
void CyFxUsbI2cTransfer(uint16_t byteAddress,
|
||||
uint8_t devAddr,
|
||||
uint16_t byteCount,
|
||||
uint8_t* buffer,
|
||||
CyBool_t isRead);
|
||||
|
||||
#include "cyu3externcend.h"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -18,7 +18,7 @@ endif
|
||||
|
||||
MODULE = b200_main
|
||||
|
||||
SOURCE += $(MODULE).c b200_i2c.c b200_usb_descriptors.c ../common/common_helpers.c ../common/common_descriptors.c cyfxtx.c
|
||||
SOURCE += $(MODULE).c b200_i2c.c b200_usb_descriptors.c syscalls.c ../common/common_helpers.c ../common/common_descriptors.c cyfxtx.c
|
||||
|
||||
INCFLAGS = -I../common
|
||||
|
||||
|
||||
68
firmware/fx3/b200/firmware/syscalls.c
Normal file
68
firmware/fx3/b200/firmware/syscalls.c
Normal file
@ -0,0 +1,68 @@
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
int _close(int file)
|
||||
{
|
||||
(void)file;
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _fstat(int file, struct stat* st)
|
||||
{
|
||||
(void)file;
|
||||
if (st != 0) {
|
||||
st->st_mode = S_IFCHR;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _getpid(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int _isatty(int file)
|
||||
{
|
||||
(void)file;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int _kill(int pid, int sig)
|
||||
{
|
||||
(void)pid;
|
||||
(void)sig;
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _lseek(int file, int ptr, int dir)
|
||||
{
|
||||
(void)file;
|
||||
(void)ptr;
|
||||
(void)dir;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _read(int file, char* ptr, int len)
|
||||
{
|
||||
(void)file;
|
||||
(void)ptr;
|
||||
(void)len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _exit(int status)
|
||||
{
|
||||
(void)status;
|
||||
while (1) {
|
||||
}
|
||||
}
|
||||
|
||||
int _write(int file, const char* ptr, int len)
|
||||
{
|
||||
(void)file;
|
||||
(void)ptr;
|
||||
return len;
|
||||
}
|
||||
@ -1,10 +1,10 @@
|
||||
diff -r -u cyfx3sdk/fw_build/boot_fw/cyfx3.ld cyfx3sdk-modified/fw_build/boot_fw/cyfx3.ld
|
||||
--- cyfx3sdk/fw_build/boot_fw/cyfx3.ld 2018-05-23 02:25:01.000000000 -0700
|
||||
+++ cyfx3sdk-modified/fw_build/boot_fw/cyfx3.ld 2020-08-22 14:54:46.407481500 -0700
|
||||
@@ -39,12 +39,18 @@
|
||||
|
||||
--- cyfx3sdk/fw_build/boot_fw/cyfx3.ld 2026-08-11 15:34:04.645797167 +0200
|
||||
+++ cyfx3sdk-modified/fw_build/boot_fw/cyfx3.ld 2026-08-11 15:51:14.427111162 +0200
|
||||
@@ -41,12 +41,18 @@
|
||||
MEMORY
|
||||
{
|
||||
I-TCM : ORIGIN = 0x100 LENGTH = 0x100
|
||||
+ BLANK : ORIGIN = 0x40070000 LENGTH = 0x0100
|
||||
SYS_MEM : ORIGIN = 0x40078000 LENGTH = 0x7000
|
||||
DATA : ORIGIN = 0x4007F000 LENGTH = 0x1000
|
||||
@ -17,12 +17,12 @@ diff -r -u cyfx3sdk/fw_build/boot_fw/cyfx3.ld cyfx3sdk-modified/fw_build/boot_fw
|
||||
+ . += 0x100;
|
||||
+ } > BLANK
|
||||
+
|
||||
.text :
|
||||
.vectors :
|
||||
{
|
||||
*(RESET)
|
||||
*(CYU3P_ITCM_SECTION)
|
||||
diff -r -u cyfx3sdk/fw_build/fx3_fw/cyfxtx.c cyfx3sdk-modified/fw_build/fx3_fw/cyfxtx.c
|
||||
--- cyfx3sdk/fw_build/fx3_fw/cyfxtx.c 2018-05-23 02:25:01.000000000 -0700
|
||||
+++ cyfx3sdk-modified/fw_build/fx3_fw/cyfxtx.c 2020-08-21 12:34:14.344699800 -0700
|
||||
--- cyfx3sdk/fw_build/fx3_fw/cyfxtx.c 2026-08-11 15:34:04.685797262 +0200
|
||||
+++ cyfx3sdk-modified/fw_build/fx3_fw/cyfxtx.c 2026-08-11 15:34:17.397827996 +0200
|
||||
@@ -98,7 +98,7 @@
|
||||
area which is used by the application code as well as the drivers to allocate thread
|
||||
stacks and other internal data structures.
|
||||
@ -33,8 +33,8 @@ diff -r -u cyfx3sdk/fw_build/fx3_fw/cyfxtx.c cyfx3sdk-modified/fw_build/fx3_fw/c
|
||||
|
||||
/*
|
||||
diff -r -u cyfx3sdk/fw_build/fx3_fw/fx3_512k.ld cyfx3sdk-modified/fw_build/fx3_fw/fx3_512k.ld
|
||||
--- cyfx3sdk/fw_build/fx3_fw/fx3_512k.ld 2018-05-23 02:25:01.000000000 -0700
|
||||
+++ cyfx3sdk-modified/fw_build/fx3_fw/fx3_512k.ld 2020-08-21 12:34:14.345050200 -0700
|
||||
--- cyfx3sdk/fw_build/fx3_fw/fx3_512k.ld 2026-08-11 15:34:04.665797215 +0200
|
||||
+++ cyfx3sdk-modified/fw_build/fx3_fw/fx3_512k.ld 2026-08-11 15:34:17.381827956 +0200
|
||||
@@ -32,11 +32,11 @@
|
||||
The default memory map used for FX3 applications is as follows:
|
||||
|
||||
@ -52,10 +52,10 @@ diff -r -u cyfx3sdk/fw_build/fx3_fw/fx3_512k.ld cyfx3sdk-modified/fw_build/fx3_f
|
||||
|
||||
Interrupt handlers are placed in I-TCM (16KB). The first 256 bytes of ITCM are
|
||||
reserved for Exception Vectors and will be loaded during firmware initialization.
|
||||
@@ -58,8 +58,8 @@
|
||||
@@ -59,8 +59,8 @@
|
||||
MEMORY
|
||||
{
|
||||
I-TCM : ORIGIN = 0x100 LENGTH = 0x3F00
|
||||
I-TCM : ORIGIN = 0x200 LENGTH = 0x3E00
|
||||
- SYS_MEM : ORIGIN = 0x40003000 LENGTH = 0x2D000
|
||||
- DATA : ORIGIN = 0x40030000 LENGTH = 0x8000
|
||||
+ SYS_MEM : ORIGIN = 0x40003000 LENGTH = 0x35000
|
||||
@ -63,7 +63,7 @@ diff -r -u cyfx3sdk/fw_build/fx3_fw/fx3_512k.ld cyfx3sdk-modified/fw_build/fx3_f
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
@@ -114,5 +114,17 @@
|
||||
@@ -115,5 +115,17 @@
|
||||
. = ALIGN(4);
|
||||
} > DATA
|
||||
__exidx_end = .;
|
||||
|
||||
@ -29,6 +29,10 @@ parameters:
|
||||
type: boolean
|
||||
displayName: Build N3xx
|
||||
default: false
|
||||
- name: build_b310
|
||||
type: boolean
|
||||
displayName: Build B310
|
||||
default: false
|
||||
- name: build_e31x
|
||||
type: boolean
|
||||
displayName: Build E31x
|
||||
@ -125,30 +129,35 @@ parameters:
|
||||
X420_CG_1000:
|
||||
image_core: x420_CG_1000_rfnoc_image_core.yml
|
||||
image_core_name: usrp_x420_fpga_CG_1000
|
||||
target_name: X420
|
||||
artifact_name: X420_CG_1000
|
||||
timeout: 720
|
||||
max_attempts: 2
|
||||
X420_X4_1000:
|
||||
image_core: x420_X4_1000_rfnoc_image_core.yml
|
||||
image_core_name: usrp_x420_fpga_X4_1000
|
||||
target_name: X420
|
||||
artifact_name: X420_X4_1000
|
||||
timeout: 720
|
||||
max_attempts: 2
|
||||
X420_CG_400:
|
||||
image_core: x420_CG_400_rfnoc_image_core.yml
|
||||
image_core_name: usrp_x420_fpga_CG_400
|
||||
target_name: X420
|
||||
artifact_name: X420_CG_400
|
||||
timeout: 720
|
||||
max_attempts: 2
|
||||
X420_X4_400:
|
||||
image_core: x420_X4_400_rfnoc_image_core.yml
|
||||
image_core_name: usrp_x420_fpga_X4_400
|
||||
target_name: X420
|
||||
artifact_name: X420_X4_400
|
||||
timeout: 720
|
||||
max_attempts: 2
|
||||
X420_X4_200:
|
||||
image_core: x420_X4_200_rfnoc_image_core.yml
|
||||
image_core_name: usrp_x420_fpga_X4_200
|
||||
target_name: X420
|
||||
artifact_name: X420_X4_200
|
||||
timeout: 720
|
||||
max_attempts: 2
|
||||
@ -164,6 +173,14 @@ parameters:
|
||||
timeout: 720
|
||||
attempt_timeout: 360
|
||||
max_attempts: 2
|
||||
X440_CG_800:
|
||||
image_core: x440_CG_800_rfnoc_image_core.yml
|
||||
image_core_name: usrp_x440_fpga_CG_800
|
||||
target_name: X440
|
||||
artifact_name: X440_CG_800
|
||||
timeout: 1080
|
||||
attempt_timeout: 360
|
||||
max_attempts: 2
|
||||
X440_CG_400:
|
||||
image_core: x440_CG_400_rfnoc_image_core.yml
|
||||
image_core_name: usrp_x440_fpga_CG_400
|
||||
@ -180,6 +197,14 @@ parameters:
|
||||
timeout: 720
|
||||
attempt_timeout: 360
|
||||
max_attempts: 2
|
||||
X440_X4_800:
|
||||
image_core: x440_X4_800_rfnoc_image_core.yml
|
||||
image_core_name: usrp_x440_fpga_X4_800
|
||||
target_name: X440
|
||||
artifact_name: X440_X4_800
|
||||
timeout: 720
|
||||
attempt_timeout: 360
|
||||
max_attempts: 2
|
||||
X440_X4_400:
|
||||
image_core: x440_X4_400_rfnoc_image_core.yml
|
||||
image_core_name: usrp_x440_fpga_X4_400
|
||||
@ -196,6 +221,14 @@ parameters:
|
||||
timeout: 720
|
||||
attempt_timeout: 360
|
||||
max_attempts: 2
|
||||
X440_X1_200:
|
||||
image_core: x440_X1_200_rfnoc_image_core.yml
|
||||
image_core_name: usrp_x440_fpga_X1_200
|
||||
target_name: X440
|
||||
artifact_name: X440_X1_200
|
||||
timeout: 720
|
||||
attempt_timeout: 360
|
||||
max_attempts: 2
|
||||
|
||||
trigger: none
|
||||
pr: none
|
||||
@ -205,10 +238,11 @@ extends:
|
||||
parameters:
|
||||
add_tests: ${{
|
||||
format(
|
||||
'{0};{1};{2};{3};{4};{5};{6};{7};{8};{9};{10};',
|
||||
'{0};{1};{2};{3};{4};{5};{6};{7};{8};{9};{10};{11};',
|
||||
iif(parameters.run_testbenches,'fpga.usrp3.testbenches',''),
|
||||
iif(parameters.build_x3x0,'fpga.usrp3.x3xx',''),
|
||||
iif(parameters.build_n3xx,'fpga.usrp3.n3xx',''),
|
||||
iif(parameters.build_b310,'fpga.usrp3.b310',''),
|
||||
iif(parameters.build_e31x,'fpga.usrp3.e31x',''),
|
||||
iif(parameters.build_e320,'fpga.usrp3.e320',''),
|
||||
iif(parameters.build_x410,'fpga.usrp3.x410',''),
|
||||
|
||||
@ -28,7 +28,7 @@ def print_timing_constraint_summary(twr_file):
|
||||
if 'constraint' in line and 'met' in line: done = True
|
||||
if not keep and done: keep = True
|
||||
if keep: output += line
|
||||
if done: break
|
||||
if done: break
|
||||
print("\n\n"+output)
|
||||
|
||||
if __name__=='__main__': map(print_timing_constraint_summary, sys.argv[1:])
|
||||
|
||||
@ -22,10 +22,8 @@ include $(LIB_DIR)/control/Makefile.srcs
|
||||
include $(LIB_DIR)/fifo/Makefile.srcs
|
||||
include $(LIB_DIR)/timing/Makefile.srcs
|
||||
include $(LIB_DIR)/packet_proc/Makefile.srcs
|
||||
include $(LIB_DIR)/vita/Makefile.srcs
|
||||
include $(LIB_DIR)/dsp/Makefile.srcs
|
||||
include $(LIB_DIR)/axi/Makefile.srcs
|
||||
include $(LIB_DIR)/radio/Makefile.srcs
|
||||
include $(BASE_DIR)/x300/coregen_dsp/Makefile.srcs
|
||||
|
||||
DESIGN_SRCS += $(abspath \
|
||||
@ -33,32 +31,13 @@ $(FIFO_SRCS) \
|
||||
$(CONTROL_LIB_SRCS) \
|
||||
$(TIMING_SRCS) \
|
||||
$(PACKET_PROC_SRCS) \
|
||||
$(VITA_SRCS) \
|
||||
$(DSP_SRCS) \
|
||||
$(AXI_SRCS) \
|
||||
$(RADIO_SRCS) \
|
||||
$(COREGEN_DSP_SRCS) \
|
||||
)
|
||||
|
||||
TOP_MODULE = radio
|
||||
|
||||
#-------------------------------------------------
|
||||
# IP Specific
|
||||
#-------------------------------------------------
|
||||
# If simulation contains IP, define the IP_DIR and point
|
||||
# it to the base level IP directory
|
||||
IP_DIR = $(BASE_DIR)/x300/ip
|
||||
|
||||
# Include makefiles and sources for all IP components
|
||||
# *after* defining the IP_DIR
|
||||
include $(IP_DIR)/fifo_short_2clk/Makefile.inc
|
||||
include $(IP_DIR)/fifo_4k_2clk/Makefile.inc
|
||||
|
||||
DESIGN_SRCS += $(abspath \
|
||||
$(IP_FIFO_4K_2CLK_SRCS) \
|
||||
$(IP_FIFO_SHORT_2CLK_SRCS) \
|
||||
)
|
||||
|
||||
# DESIGN_SRCS and VERILOG_DEFS must be defined
|
||||
.DEFAULT_GOAL := netlist
|
||||
netlist: .prereqs $(DESIGN_SRCS)
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
// 2) Never cross a 4KByte address boundary within a single transaction, this is an AXI4 rule.
|
||||
// 3) 2^SIZE must be greater than 4KB so that the 4KByte page protection also deals with FIFO wrap corner case.
|
||||
//
|
||||
module axi_dma_fifo
|
||||
module axi_dma_fifo
|
||||
#(
|
||||
parameter SIMULATION = 0, // Shorten flush counter for simulation
|
||||
parameter DEFAULT_BASE = 30'h00000000,
|
||||
@ -23,7 +23,7 @@ module axi_dma_fifo
|
||||
parameter MAX_PKT_LEN = 12 // Log2 of maximum packet length
|
||||
) (
|
||||
input bus_clk,
|
||||
input bus_reset,
|
||||
input bus_reset,
|
||||
input dram_clk,
|
||||
input dram_reset,
|
||||
//
|
||||
@ -32,7 +32,7 @@ module axi_dma_fifo
|
||||
output [0 : 0] m_axi_awid, // Write address ID. This signal is the identification tag for the write address signals
|
||||
output [31 : 0] m_axi_awaddr, // Write address. The write address gives the address of the first transfer in a write burst
|
||||
output [7 : 0] m_axi_awlen, // Burst length. The burst length gives the exact number of transfers in a burst.
|
||||
output [2 : 0] m_axi_awsize, // Burst size. This signal indicates the size of each transfer in the burst.
|
||||
output [2 : 0] m_axi_awsize, // Burst size. This signal indicates the size of each transfer in the burst.
|
||||
output [1 : 0] m_axi_awburst, // Burst type. The burst type and the size information, determine how the address is calculated
|
||||
output [0 : 0] m_axi_awlock, // Lock type. Provides additional information about the atomic characteristics of the transfer.
|
||||
output [3 : 0] m_axi_awcache, // Memory type. This signal indicates how transactions are required to progress
|
||||
@ -49,12 +49,12 @@ module axi_dma_fifo
|
||||
output [7 : 0] m_axi_wstrb, // Write strobes. This signal indicates which byte lanes hold valid data.
|
||||
output m_axi_wlast, // Write last. This signal indicates the last transfer in a write burst
|
||||
output [0 : 0] m_axi_wuser, // User signal. Optional User-defined signal in the write data channel.
|
||||
output m_axi_wvalid, // Write valid. This signal indicates that valid write data and strobes are available.
|
||||
output m_axi_wvalid, // Write valid. This signal indicates that valid write data and strobes are available.
|
||||
input m_axi_wready, // Write ready. This signal indicates that the slave can accept the write data.
|
||||
//
|
||||
// AXI Write response channel signals
|
||||
//
|
||||
input [0 : 0] m_axi_bid, // Response ID tag. This signal is the ID tag of the write response.
|
||||
input [0 : 0] m_axi_bid, // Response ID tag. This signal is the ID tag of the write response.
|
||||
input [1 : 0] m_axi_bresp, // Write response. This signal indicates the status of the write transaction.
|
||||
input [0 : 0] m_axi_buser, // User signal. Optional User-defined signal in the write response channel.
|
||||
input m_axi_bvalid, // Write response valid. This signal indicates that the channel is signaling a valid response
|
||||
@ -68,7 +68,7 @@ module axi_dma_fifo
|
||||
output [2 : 0] m_axi_arsize, // Burst size. This signal indicates the size of each transfer in the burst.
|
||||
output [1 : 0] m_axi_arburst, // Burst type. The burst type and the size information determine how the address for each transfer
|
||||
output [0 : 0] m_axi_arlock, // Lock type. This signal provides additional information about the atomic characteristics
|
||||
output [3 : 0] m_axi_arcache, // Memory type. This signal indicates how transactions are required to progress
|
||||
output [3 : 0] m_axi_arcache, // Memory type. This signal indicates how transactions are required to progress
|
||||
output [2 : 0] m_axi_arprot, // Protection type. This signal indicates the privilege and security level of the transaction
|
||||
output [3 : 0] m_axi_arqos, // Quality of Service, QoS. QoS identifier sent for each read transaction.
|
||||
output [3 : 0] m_axi_arregion, // Region identifier. Permits a single physical interface on a slave to be re-used
|
||||
@ -83,7 +83,7 @@ module axi_dma_fifo
|
||||
input [1 : 0] m_axi_rresp, // Read response. This signal indicates the status of the read transfer
|
||||
input m_axi_rlast, // Read last. This signal indicates the last transfer in a read burst.
|
||||
input [0 : 0] m_axi_ruser, // User signal. Optional User-defined signal in the read data channel.
|
||||
input m_axi_rvalid, // Read valid. This signal indicates that the channel is signaling the required read data.
|
||||
input m_axi_rvalid, // Read valid. This signal indicates that the channel is signaling the required read data.
|
||||
output m_axi_rready, // Read ready. This signal indicates that the master can accept the read data and response
|
||||
//
|
||||
// CHDR friendly AXI stream input
|
||||
@ -212,14 +212,14 @@ module axi_dma_fifo
|
||||
|
||||
wire [(72-AWIDTH-29-1):0] set_sync_discard0;
|
||||
wire [(72-(2*AWIDTH)-1):0] set_sync_discard1;
|
||||
fifo_short_2clk set_sync_fifo0(
|
||||
fifo_xpm_2clk #(.WIDTH(72), .DEPTH(32), .TYPE("distributed")) set_sync_fifo0(
|
||||
.rst(bus_reset),
|
||||
.wr_clk(bus_clk), .din({{(72-AWIDTH-29){1'b0}}, timeout_bclk, supress_enable_bclk, supress_threshold_bclk, fifo_base_addr_bclk}),
|
||||
.wr_en(1'b1), .full(), .wr_data_count(),
|
||||
.rd_clk(dram_clk), .dout({set_sync_discard0, set_timeout, set_suppress_en, set_supress_threshold, set_fifo_base_addr}),
|
||||
.rd_en(1'b1), .empty(), .rd_data_count()
|
||||
);
|
||||
fifo_short_2clk set_sync_fifo1(
|
||||
fifo_xpm_2clk #(.WIDTH(72), .DEPTH(32), .TYPE("distributed")) set_sync_fifo1(
|
||||
.rst(bus_reset),
|
||||
.wr_clk(bus_clk), .din({{(72-(2*AWIDTH)){1'b0}}, ~fifo_addr_mask_bclk, fifo_addr_mask_bclk}),
|
||||
.wr_en(1'b1), .full(), .wr_data_count(),
|
||||
@ -233,7 +233,7 @@ module axi_dma_fifo
|
||||
localparam [2:0] INPUT_IDLE = 0;
|
||||
localparam [2:0] INPUT1 = 1;
|
||||
localparam [2:0] INPUT2 = 2;
|
||||
localparam [2:0] INPUT3 = 3;
|
||||
localparam [2:0] INPUT3 = 3;
|
||||
localparam [2:0] INPUT4 = 4;
|
||||
localparam [2:0] INPUT5 = 5;
|
||||
localparam [2:0] INPUT6 = 6;
|
||||
@ -255,7 +255,7 @@ module axi_dma_fifo
|
||||
localparam [2:0] OUTPUT_IDLE = 0;
|
||||
localparam [2:0] OUTPUT1 = 1;
|
||||
localparam [2:0] OUTPUT2 = 2;
|
||||
localparam [2:0] OUTPUT3 = 3;
|
||||
localparam [2:0] OUTPUT3 = 3;
|
||||
localparam [2:0] OUTPUT4 = 4;
|
||||
localparam [2:0] OUTPUT5 = 5;
|
||||
localparam [2:0] OUTPUT6 = 6;
|
||||
@ -267,17 +267,17 @@ module axi_dma_fifo
|
||||
reg [AWIDTH-1:0] read_addr;
|
||||
reg read_ctrl_valid;
|
||||
wire read_ctrl_ready;
|
||||
reg [7:0] read_count = 8'd0;
|
||||
reg [7:0] read_count = 8'd0;
|
||||
reg [8:0] read_count_plus_one = 9'd1; // Maintain a +1 version to break critical timing paths
|
||||
reg update_read;
|
||||
|
||||
|
||||
// Track main FIFO active size.
|
||||
reg [AWIDTH-3:0] space, occupied, occupied_minus_one; // Maintain a -1 version to break critical timing paths
|
||||
reg [AWIDTH-3:0] input_page_boundry, output_page_boundry; // Cache in a register to break critical timing paths
|
||||
|
||||
// Assign FIFO status bits
|
||||
wire [71:0] status_out_bclk;
|
||||
fifo_short_2clk status_fifo_2clk(
|
||||
fifo_xpm_2clk #(.WIDTH(72), .DEPTH(32), .TYPE("distributed")) status_fifo_2clk(
|
||||
.rst(dram_reset),
|
||||
.wr_clk(dram_clk), .din({{(72-(AWIDTH-2)){1'b0}}, occupied}),
|
||||
.wr_en(1'b1), .full(), .wr_data_count(),
|
||||
@ -323,10 +323,10 @@ module axi_dma_fifo
|
||||
|
||||
wire bist_running, bist_done;
|
||||
wire [1:0] bist_error;
|
||||
|
||||
|
||||
axi_chdr_test_pattern #(
|
||||
.DELAY_MODE(EXT_BIST ? "DYNAMIC" : "STATIC"),
|
||||
.SID_MODE(EXT_BIST ? "DYNAMIC" : "STATIC"),
|
||||
.DELAY_MODE(EXT_BIST ? "DYNAMIC" : "STATIC"),
|
||||
.SID_MODE(EXT_BIST ? "DYNAMIC" : "STATIC"),
|
||||
.BW_COUNTER(EXT_BIST ? 1 : 0),
|
||||
.SR_BASE(SR_BASE + 4)
|
||||
) axi_chdr_test_pattern_i (
|
||||
@ -381,7 +381,7 @@ module axi_dma_fifo
|
||||
|
||||
wire [DWIDTH-1:0] i_tdata_i0;
|
||||
wire i_tvalid_i0, i_tready_i0, i_tlast_i0;
|
||||
|
||||
|
||||
wire [DWIDTH-1:0] i_tdata_i1;
|
||||
wire i_tvalid_i1, i_tready_i1, i_tlast_i1;
|
||||
|
||||
@ -399,15 +399,15 @@ module axi_dma_fifo
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
wire write_in, read_in, empty_in, full_in;
|
||||
assign i_tready_fifo = ~full_in;
|
||||
assign write_in = i_tvalid_fifo & i_tready_fifo;
|
||||
assign i_tvalid_i0 = ~empty_in;
|
||||
assign read_in = i_tvalid_i0 & i_tready_i0;
|
||||
wire [6:0] discard_i0;
|
||||
|
||||
fifo_short_2clk fifo_short_2clk_i0 (
|
||||
|
||||
fifo_xpm_2clk #(.WIDTH(72), .DEPTH(32), .TYPE("distributed")) fifo_short_2clk_i0 (
|
||||
.rst(bus_reset),
|
||||
.wr_clk(bus_clk),
|
||||
.din({7'h0,i_tlast_fifo,i_tdata_fifo}), // input [71 : 0] din
|
||||
@ -424,16 +424,16 @@ module axi_dma_fifo
|
||||
|
||||
axi_fifo_flop2 #(.WIDTH(DWIDTH+1)) input_pipe_i0
|
||||
(
|
||||
.clk(dram_clk),
|
||||
.reset(dram_reset),
|
||||
.clk(dram_clk),
|
||||
.reset(dram_reset),
|
||||
.clear(clear),
|
||||
//
|
||||
.i_tdata({i_tlast_i0, i_tdata_i0}),
|
||||
.i_tvalid(i_tvalid_i0),
|
||||
.i_tdata({i_tlast_i0, i_tdata_i0}),
|
||||
.i_tvalid(i_tvalid_i0),
|
||||
.i_tready(i_tready_i0),
|
||||
//
|
||||
.o_tdata({i_tlast_i1, i_tdata_i1}),
|
||||
.o_tvalid(i_tvalid_i1),
|
||||
.o_tdata({i_tlast_i1, i_tdata_i1}),
|
||||
.o_tvalid(i_tvalid_i1),
|
||||
.o_tready(i_tready_i1)
|
||||
);
|
||||
|
||||
@ -453,33 +453,33 @@ module axi_dma_fifo
|
||||
);
|
||||
|
||||
axi_fifo_flop2 #(.WIDTH(DWIDTH)) input_pipe_i1 (
|
||||
.clk(dram_clk),
|
||||
.reset(dram_reset),
|
||||
.clk(dram_clk),
|
||||
.reset(dram_reset),
|
||||
.clear(clear),
|
||||
//
|
||||
.i_tdata(i_tdata_i2),
|
||||
.i_tvalid(i_tvalid_i2),
|
||||
.i_tdata(i_tdata_i2),
|
||||
.i_tvalid(i_tvalid_i2),
|
||||
.i_tready(i_tready_i2),
|
||||
//
|
||||
.o_tdata(i_tdata_i3),
|
||||
.o_tvalid(i_tvalid_i3),
|
||||
.o_tdata(i_tdata_i3),
|
||||
.o_tvalid(i_tvalid_i3),
|
||||
.o_tready(i_tready_i3)
|
||||
);
|
||||
|
||||
axi_fifo #(.WIDTH(DWIDTH),.SIZE(10)) fifo_i1 (
|
||||
.clk(dram_clk),
|
||||
.reset(dram_reset),
|
||||
.clk(dram_clk),
|
||||
.reset(dram_reset),
|
||||
.clear(clear),
|
||||
//
|
||||
.i_tdata(i_tdata_i3),
|
||||
.i_tvalid(i_tvalid_i3),
|
||||
.i_tdata(i_tdata_i3),
|
||||
.i_tvalid(i_tvalid_i3),
|
||||
.i_tready(i_tready_i3),
|
||||
//
|
||||
.o_tdata(i_tdata_input),
|
||||
.o_tvalid(i_tvalid_input),
|
||||
.o_tdata(i_tdata_input),
|
||||
.o_tvalid(i_tvalid_input),
|
||||
.o_tready(i_tready_input),
|
||||
//
|
||||
.space(space_input),
|
||||
.space(space_input),
|
||||
.occupied(occupied_input)
|
||||
);
|
||||
|
||||
@ -488,12 +488,12 @@ module axi_dma_fifo
|
||||
// passing upstream of the DRAM FIFO.
|
||||
// In this situation supress read requests to the DRAM FIFO so that more bandwidth is available to writes.
|
||||
//
|
||||
always @(posedge dram_clk)
|
||||
always @(posedge dram_clk)
|
||||
begin
|
||||
space_input_reg <= space_input;
|
||||
if ((space_input_reg < set_supress_threshold[15:0]) && set_suppress_en)
|
||||
supress_reads <= 1'b1;
|
||||
else
|
||||
else
|
||||
supress_reads <= 1'b0;
|
||||
end
|
||||
|
||||
@ -506,16 +506,16 @@ module axi_dma_fifo
|
||||
|
||||
wire [DWIDTH-1:0] o_tdata_i0;
|
||||
wire o_tvalid_i0, o_tready_i0;
|
||||
|
||||
|
||||
wire [DWIDTH-1:0] o_tdata_i1;
|
||||
wire o_tvalid_i1, o_tready_i1;
|
||||
|
||||
|
||||
wire [DWIDTH-1:0] o_tdata_i2;
|
||||
wire o_tvalid_i2, o_tready_i2;
|
||||
|
||||
|
||||
wire [DWIDTH-1:0] o_tdata_i3;
|
||||
wire o_tvalid_i3, o_tready_i3;
|
||||
|
||||
|
||||
wire [DWIDTH-1:0] o_tdata_i4;
|
||||
wire o_tvalid_i4, o_tready_i4, o_tlast_i4;
|
||||
|
||||
@ -525,69 +525,69 @@ module axi_dma_fifo
|
||||
wire checksum_error;
|
||||
|
||||
axi_fifo #(.WIDTH(DWIDTH),.SIZE(10)) fifo_i2 (
|
||||
.clk(dram_clk),
|
||||
.reset(dram_reset),
|
||||
.clk(dram_clk),
|
||||
.reset(dram_reset),
|
||||
.clear(clear),
|
||||
//
|
||||
.i_tdata(o_tdata_output),
|
||||
.i_tvalid(o_tvalid_output),
|
||||
.i_tdata(o_tdata_output),
|
||||
.i_tvalid(o_tvalid_output),
|
||||
.i_tready(o_tready_output),
|
||||
//
|
||||
.o_tdata(o_tdata_i0),
|
||||
.o_tvalid(o_tvalid_i0),
|
||||
.o_tdata(o_tdata_i0),
|
||||
.o_tvalid(o_tvalid_i0),
|
||||
.o_tready(o_tready_i0),
|
||||
//
|
||||
.space(space_output),
|
||||
.space(space_output),
|
||||
.occupied(occupied_output)
|
||||
);
|
||||
|
||||
// Place FLops straight after SRAM read access for timing.
|
||||
axi_fifo_flop2 #(.WIDTH(DWIDTH)) output_pipe_i0
|
||||
(
|
||||
.clk(dram_clk),
|
||||
.reset(dram_reset),
|
||||
.clk(dram_clk),
|
||||
.reset(dram_reset),
|
||||
.clear(clear),
|
||||
//
|
||||
.i_tdata(o_tdata_i0),
|
||||
.i_tvalid(o_tvalid_i0),
|
||||
.i_tdata(o_tdata_i0),
|
||||
.i_tvalid(o_tvalid_i0),
|
||||
.i_tready(o_tready_i0),
|
||||
//
|
||||
.o_tdata(o_tdata_i1),
|
||||
.o_tvalid(o_tvalid_i1),
|
||||
.o_tdata(o_tdata_i1),
|
||||
.o_tvalid(o_tvalid_i1),
|
||||
.o_tready(o_tready_i1 && ~supress_reads)
|
||||
);
|
||||
|
||||
// Read suppression logic
|
||||
// The CL part of this exists between these
|
||||
// axi_flops
|
||||
// axi_flops
|
||||
axi_fifo_flop2 #(.WIDTH(DWIDTH)) output_pipe_i1
|
||||
(
|
||||
.clk(dram_clk),
|
||||
.reset(dram_reset),
|
||||
.clk(dram_clk),
|
||||
.reset(dram_reset),
|
||||
.clear(clear),
|
||||
//
|
||||
.i_tdata(o_tdata_i1),
|
||||
.i_tvalid(o_tvalid_i1 && ~supress_reads),
|
||||
.i_tdata(o_tdata_i1),
|
||||
.i_tvalid(o_tvalid_i1 && ~supress_reads),
|
||||
.i_tready(o_tready_i1),
|
||||
//
|
||||
.o_tdata(o_tdata_i2),
|
||||
.o_tvalid(o_tvalid_i2),
|
||||
.o_tdata(o_tdata_i2),
|
||||
.o_tvalid(o_tvalid_i2),
|
||||
.o_tready(o_tready_i2)
|
||||
);
|
||||
|
||||
// Pipeline flop before tlast extraction logic
|
||||
axi_fifo_flop2 #(.WIDTH(DWIDTH)) output_pipe_i2
|
||||
(
|
||||
.clk(dram_clk),
|
||||
.reset(dram_reset),
|
||||
.clk(dram_clk),
|
||||
.reset(dram_reset),
|
||||
.clear(clear),
|
||||
//
|
||||
.i_tdata(o_tdata_i2),
|
||||
.i_tvalid(o_tvalid_i2),
|
||||
.i_tdata(o_tdata_i2),
|
||||
.i_tvalid(o_tvalid_i2),
|
||||
.i_tready(o_tready_i2),
|
||||
//
|
||||
.o_tdata(o_tdata_i3),
|
||||
.o_tvalid(o_tvalid_i3),
|
||||
.o_tdata(o_tdata_i3),
|
||||
.o_tvalid(o_tvalid_i3),
|
||||
.o_tready(o_tready_i3)
|
||||
);
|
||||
|
||||
@ -611,16 +611,16 @@ module axi_dma_fifo
|
||||
// Pipeline flop after tlast extraction logic
|
||||
axi_fifo_flop2 #(.WIDTH(DWIDTH+1)) output_pipe_i3
|
||||
(
|
||||
.clk(dram_clk),
|
||||
.reset(dram_reset),
|
||||
.clk(dram_clk),
|
||||
.reset(dram_reset),
|
||||
.clear(clear),
|
||||
//
|
||||
.i_tdata({o_tlast_i4,o_tdata_i4}),
|
||||
.i_tvalid(o_tvalid_i4),
|
||||
.i_tdata({o_tlast_i4,o_tdata_i4}),
|
||||
.i_tvalid(o_tvalid_i4),
|
||||
.i_tready(o_tready_i4),
|
||||
//
|
||||
.o_tdata({o_tlast_i5,o_tdata_i5}),
|
||||
.o_tvalid(o_tvalid_i5),
|
||||
.o_tdata({o_tlast_i5,o_tdata_i5}),
|
||||
.o_tvalid(o_tvalid_i5),
|
||||
.o_tready(o_tready_i5)
|
||||
);
|
||||
|
||||
@ -630,8 +630,8 @@ module axi_dma_fifo
|
||||
assign o_tvalid_fifo = ~empty_out;
|
||||
assign read_out = o_tvalid_fifo & o_tready_fifo;
|
||||
wire [6:0] discard_i1;
|
||||
|
||||
fifo_short_2clk fifo_short_2clk_i1 (
|
||||
|
||||
fifo_xpm_2clk #(.WIDTH(72), .DEPTH(32), .TYPE("distributed")) fifo_short_2clk_i1 (
|
||||
.rst(dram_reset),
|
||||
.wr_clk(dram_clk),
|
||||
.din({7'h0,o_tlast_i5,o_tdata_i5}), // input [71 : 0] din
|
||||
@ -683,7 +683,7 @@ module axi_dma_fifo
|
||||
//
|
||||
// INPUT_IDLE.
|
||||
// To start an input transfer to DRAM need:
|
||||
// 1) Space in the DRAM FIFO
|
||||
// 1) Space in the DRAM FIFO
|
||||
// and either
|
||||
// 2) 256 entrys in the input FIFO
|
||||
// or
|
||||
@ -698,13 +698,13 @@ module axi_dma_fifo
|
||||
input_timeout_reset <= 1'b1;
|
||||
// Calculate number of entries remaining until next 4KB page boundry is crossed minus 1.
|
||||
// Note, units of calculation are 64bit wide words. Address is always 64bit alligned.
|
||||
input_page_boundry <= {write_addr[AWIDTH-1:12],9'h1ff} - write_addr[AWIDTH-1:3];
|
||||
input_page_boundry <= {write_addr[AWIDTH-1:12],9'h1ff} - write_addr[AWIDTH-1:3];
|
||||
end else if (input_timeout_triggered) begin // input FIFO timeout waiting for new data.
|
||||
input_state <= INPUT2;
|
||||
input_timeout_reset <= 1'b1;
|
||||
// Calculate number of entries remaining until next 4KB page boundry is crossed minus 1.
|
||||
// Note, units of calculation are 64bit wide words. Address is always 64bit alligned.
|
||||
input_page_boundry <= {write_addr[AWIDTH-1:12],9'h1ff} - write_addr[AWIDTH-1:3];
|
||||
input_page_boundry <= {write_addr[AWIDTH-1:12],9'h1ff} - write_addr[AWIDTH-1:3];
|
||||
end else begin
|
||||
input_timeout_reset <= 1'b0;
|
||||
input_state <= INPUT_IDLE;
|
||||
@ -771,14 +771,14 @@ module axi_dma_fifo
|
||||
input_state <= INPUT5; // Move on
|
||||
else
|
||||
input_state <= INPUT4; // Wait for deassert
|
||||
end
|
||||
end
|
||||
//
|
||||
// INPUT5.
|
||||
// Transaction has been accepted by AXI4 DMA engine. Now we wait for the re-assertion
|
||||
// of write_ctrl_ready which signals that the AXI4 DMA engine has receieved a response
|
||||
// for the whole write transaction and we assume that this means it is commited to DRAM.
|
||||
// We are now free to update write_addr pointer and go back to idle state.
|
||||
//
|
||||
//
|
||||
INPUT5: begin
|
||||
write_ctrl_valid <= 1'b0;
|
||||
if (write_ctrl_ready) begin
|
||||
@ -798,7 +798,7 @@ module axi_dma_fifo
|
||||
update_write <= 1'b0;
|
||||
end
|
||||
|
||||
default:
|
||||
default:
|
||||
input_state <= INPUT_IDLE;
|
||||
endcase // case(input_state)
|
||||
|
||||
@ -840,7 +840,7 @@ module axi_dma_fifo
|
||||
//
|
||||
// OUTPUT_IDLE.
|
||||
// To start an output tranfer from DRAM
|
||||
// 1) Space in the small output FIFO
|
||||
// 1) Space in the small output FIFO
|
||||
// and either
|
||||
// 2) 256 entrys in the DRAM FIFO
|
||||
// or
|
||||
@ -928,14 +928,14 @@ module axi_dma_fifo
|
||||
output_state <= OUTPUT5; // Move on
|
||||
else
|
||||
output_state <= OUTPUT4; // Wait for deassert
|
||||
end
|
||||
end
|
||||
//
|
||||
// OUTPUT5.
|
||||
// Transaction has been accepted by AXI4 DMA engine. Now we wait for the re-assertion
|
||||
// of read_ctrl_ready which signals that the AXI4 DMA engine has receieved a last signal and good response
|
||||
// for the whole read transaction.
|
||||
// We are now free to update read_addr pointer and go back to idle state.
|
||||
//
|
||||
//
|
||||
OUTPUT5: begin
|
||||
read_ctrl_valid <= 1'b0;
|
||||
if (read_ctrl_ready) begin
|
||||
@ -955,7 +955,7 @@ module axi_dma_fifo
|
||||
output_state <= OUTPUT_IDLE;
|
||||
end
|
||||
|
||||
default:
|
||||
default:
|
||||
output_state <= OUTPUT_IDLE;
|
||||
endcase // case(output_state)
|
||||
|
||||
|
||||
@ -18,17 +18,22 @@
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// DATA_W : The bit width of tdata for all ports.
|
||||
// NUM_PORTS : The number of output ports on which to duplicate the input.
|
||||
// INPUT_REG : Set to 1 to add an input register stage to break combinatorial
|
||||
// paths. Set to 0 to allow combinatorial input paths.
|
||||
// DATA_W : The bit width of tdata for all ports.
|
||||
// NUM_PORTS : The number of output ports on which to duplicate the
|
||||
// input.
|
||||
// INPUT_REG : Set to 1 to add an input register stage to break
|
||||
// combinatorial paths. Set to 0 to allow combinatorial input
|
||||
// paths.
|
||||
// OUT_FIFO_SIZE : Configures the per-port output FIFO size. Defaults to a
|
||||
// short (32-deep) FIFO.
|
||||
//
|
||||
|
||||
|
||||
module axis_split #(
|
||||
parameter DATA_W = 32,
|
||||
parameter NUM_PORTS = 4,
|
||||
parameter INPUT_REG = 0
|
||||
parameter DATA_W = 32,
|
||||
parameter NUM_PORTS = 4,
|
||||
parameter INPUT_REG = 0,
|
||||
parameter OUT_FIFO_SIZE = 5
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
@ -106,11 +111,20 @@ module axis_split #(
|
||||
genvar i;
|
||||
for (i = 0; i < NUM_PORTS; i = i + 1) begin : gen_ports
|
||||
|
||||
// We use axi_fifo_short specifically because it can tolerate tvalid
|
||||
// de-asserting at any time. This is normally not allowed by AXI-Stream.
|
||||
axi_fifo_short #(
|
||||
.WIDTH (DATA_W)
|
||||
) axi_fifo_short_i (
|
||||
// We add a FIFO on each output port because our axi_fifo tolerates tvalid
|
||||
// de-asserting at any time, which is normally not allowed by AXI-Stream.
|
||||
// This may occur here because the output streams are independent and we
|
||||
// block all transfers when any one output stalls. To ensure we have this
|
||||
// protection built in, we do not allow bypass mode.
|
||||
|
||||
if (OUT_FIFO_SIZE < 0) begin : gen_bypass_mode
|
||||
OUT_FIFO_SIZE_must_be_non_negative();
|
||||
end
|
||||
|
||||
axi_fifo #(
|
||||
.WIDTH (DATA_W),
|
||||
.SIZE (OUT_FIFO_SIZE)
|
||||
) axi_fifo_i (
|
||||
.clk (clk),
|
||||
.reset (rst),
|
||||
.clear (1'b0),
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: axis_width_conv
|
||||
// Description:
|
||||
// Description:
|
||||
// An AXI-Stream width conversion module that can convert from
|
||||
// an arbitrary input width to an arbitrary output width. The
|
||||
// module also supports an optional clock crossing. Data bits
|
||||
@ -17,7 +17,7 @@
|
||||
// the AXI standard where the bits are "byte qualifiers". In
|
||||
// this module, tkeep is a "word qualifier" where the width
|
||||
// of a word can be arbitrary. If WORD_W = 8, the behavior
|
||||
// of this module is identical to an AXI width converter.
|
||||
// of this module is identical to an AXI width converter.
|
||||
//
|
||||
// Parameters:
|
||||
// - WORD_W: Bitwidth of a word
|
||||
@ -25,6 +25,8 @@
|
||||
// - OUT_WORDS: Number of words in the output stream
|
||||
// - SYNC_CLKS: Are s_axis_aclk and m_axis_aclk synchronous to each other?
|
||||
// - PIPELINE: Which ports to pipeline? {NONE, IN, OUT, INOUT}
|
||||
// - FIFO_SIZE: The size of the FIFO given as log2 of the number of
|
||||
// WORD_W-bit words.
|
||||
//
|
||||
// Signals:
|
||||
// - s_axis_* : Input sample stream (AXI-Stream)
|
||||
@ -35,9 +37,10 @@ module axis_width_conv #(
|
||||
parameter IN_WORDS = 4,
|
||||
parameter OUT_WORDS = 6,
|
||||
parameter SYNC_CLKS = 0,
|
||||
parameter PIPELINE = "NONE"
|
||||
parameter PIPELINE = "NONE",
|
||||
parameter FIFO_SIZE = 1
|
||||
)(
|
||||
// Data In (AXI-Stream)
|
||||
// Data In (AXI-Stream)
|
||||
input wire s_axis_aclk, // Input stream Clock
|
||||
input wire s_axis_rst, // Input stream Reset
|
||||
input wire [(IN_WORDS*WORD_W)-1:0] s_axis_tdata, // Input stream tdata
|
||||
@ -45,7 +48,7 @@ module axis_width_conv #(
|
||||
input wire s_axis_tlast, // Input stream tlast
|
||||
input wire s_axis_tvalid, // Input stream tvalid
|
||||
output wire s_axis_tready, // Input stream tready
|
||||
// Data Out (AXI-Stream)
|
||||
// Data Out (AXI-Stream)
|
||||
input wire m_axis_aclk, // Output stream Clock
|
||||
input wire m_axis_rst, // Output stream Reset
|
||||
output wire [(OUT_WORDS*WORD_W)-1:0] m_axis_tdata, // Output stream tdata
|
||||
@ -118,13 +121,13 @@ module axis_width_conv #(
|
||||
y = b;
|
||||
while (!done) begin
|
||||
if (x < y) begin
|
||||
swap = x;
|
||||
x = y;
|
||||
y = swap;
|
||||
swap = x;
|
||||
x = y;
|
||||
y = swap;
|
||||
end else if (y != 0) begin
|
||||
x = x - y;
|
||||
end else begin
|
||||
done = 1'b1;
|
||||
done = 1'b1;
|
||||
end
|
||||
end
|
||||
// x is the greatest common divisor
|
||||
@ -148,11 +151,10 @@ module axis_width_conv #(
|
||||
// - The input and output clocks are the same
|
||||
// - The upsizer is effectively a passthrough and input registering is requested
|
||||
// - The downsizer is effectively a passthrough and output registering is requested
|
||||
localparam [0:0] SKIP_FIFO = (SYNC_CLKS == 1) && (
|
||||
localparam [0:0] SKIP_FIFO = (SYNC_CLKS == 1) && (FIFO_SIZE <= 1) && (
|
||||
((PIPELINE == "IN" || PIPELINE == "INOUT") && (UPSIZE_RATIO == 1)) ||
|
||||
((PIPELINE == "OUT" || PIPELINE == "INOUT") && (DOWNSIZE_RATIO == 1))
|
||||
);
|
||||
localparam FIFO_SIZE = 1;
|
||||
|
||||
//----------------------------------------------
|
||||
// In => Upsizer => FIFO => Downsizer => Out
|
||||
@ -190,8 +192,14 @@ module axis_width_conv #(
|
||||
assign fifo_o_tvalid = fifo_i_tvalid;
|
||||
assign fifo_i_tready = fifo_o_tready;
|
||||
end else begin
|
||||
// calculate the actual FIFO size as the width might be different than// WORD_W
|
||||
localparam integer FIFO_ELEMENTS = $ceil((2.0 ** FIFO_SIZE) / INT_KEEP_W);
|
||||
localparam integer FIFO_ELEMENTS_MIN = FIFO_ELEMENTS > 0 ? FIFO_ELEMENTS : 1;
|
||||
localparam integer REQUESTED_FIFO_SIZE = $clog2(FIFO_ELEMENTS_MIN);
|
||||
localparam integer ADJUSTED_FIFO_SIZE = REQUESTED_FIFO_SIZE > 0 ? REQUESTED_FIFO_SIZE : 1;
|
||||
|
||||
if (SYNC_CLKS) begin
|
||||
axi_fifo #(.WIDTH(INT_DATA_W+INT_KEEP_W+1), .SIZE(FIFO_SIZE)) fifo_i (
|
||||
axi_fifo #(.WIDTH(INT_DATA_W+INT_KEEP_W+1), .SIZE(ADJUSTED_FIFO_SIZE)) fifo_i (
|
||||
.clk(s_axis_aclk), .reset(s_axis_rst), .clear(1'b0),
|
||||
.i_tdata({fifo_i_tlast, fifo_i_tkeep, fifo_i_tdata}),
|
||||
.i_tvalid(fifo_i_tvalid), .i_tready(fifo_i_tready),
|
||||
@ -200,7 +208,7 @@ module axis_width_conv #(
|
||||
.space(), .occupied()
|
||||
);
|
||||
end else begin
|
||||
axi_fifo_2clk #(.WIDTH(INT_DATA_W+INT_KEEP_W+1), .SIZE(FIFO_SIZE)) fifo_i (
|
||||
axi_fifo_2clk #(.WIDTH(INT_DATA_W+INT_KEEP_W+1), .SIZE(ADJUSTED_FIFO_SIZE)) fifo_i (
|
||||
.reset(s_axis_rst),
|
||||
.i_aclk(s_axis_aclk),
|
||||
.i_tdata({fifo_i_tlast, fifo_i_tkeep, fifo_i_tdata}),
|
||||
|
||||
@ -18,9 +18,15 @@ axi_forwarding_cam.v \
|
||||
axi_setting_reg.v \
|
||||
axi_slave_mux.v \
|
||||
axi_test_vfifo.v \
|
||||
axis_to_ctrlport.sv \
|
||||
ctrlport_to_axis.sv \
|
||||
axis_spi.sv \
|
||||
bin2gray.v \
|
||||
binary_encoder.v \
|
||||
clock_div.v \
|
||||
../rfnoc/core/ctrlport_pkg.sv \
|
||||
ctrlport_spi_adrv_pkg.sv \
|
||||
ctrlport_spi_adrv.sv \
|
||||
db_control.v \
|
||||
fe_control.v \
|
||||
filter_bad_sid.v \
|
||||
@ -64,7 +70,12 @@ map/axis_muxed_kv_map.v \
|
||||
axil_ctrlport_master.v\
|
||||
handshake.v\
|
||||
handshake_latch.sv\
|
||||
clocking_watchdog.sv\
|
||||
ctrlport_to_regport.v \
|
||||
ctrlport_to_wb_i2c.v \
|
||||
ctrlport_to_wb_uart.v \
|
||||
ctrlport_to_simple_spi.sv \
|
||||
ctrlport_to_jtag.v \
|
||||
glitch_free_mux.v \
|
||||
regport_if.sv \
|
||||
))
|
||||
|
||||
380
fpga/usrp3/lib/control/axis_spi.sv
Normal file
380
fpga/usrp3/lib/control/axis_spi.sv
Normal file
@ -0,0 +1,380 @@
|
||||
//
|
||||
// Copyright 2026 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: axis_spi
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// SPI master (CPOL=0, CPHA=0) with AXI-Stream TX and RX interfaces. Each
|
||||
// byte presented on the input stream is shifted out MSB-first on MOSI while
|
||||
// MISO bits are simultaneously assembled into bytes and presented on the
|
||||
// output stream (full-duplex).
|
||||
//
|
||||
// CS_N is asserted (driven low) and held low for the duration of the
|
||||
// AXI-Stream packet. Between words, CS_N stays asserted and SCLK simply
|
||||
// pauses if the next byte in the packet is not yet available (s_axis_tvalid
|
||||
// == 0) or if there's back-pressure on the output (m_axis_tready == 0).
|
||||
//
|
||||
// After the last SCLK falling edge, CS_N deassertion timing is controlled by
|
||||
// CS_HOLD and CS_GUARD:
|
||||
//
|
||||
// CS_HOLD=0 : CS_N deasserts on the same clock edge as the last SCLK
|
||||
// falling edge.
|
||||
// CS_HOLD=1 : CS_N stays low for one additional half-period before being
|
||||
// deasserted.
|
||||
//
|
||||
// After CS_N deasserts, CS_GUARD determines the number of half-period
|
||||
// cycles before the next CS_N assertion for the next packet:
|
||||
//
|
||||
// CS_GUARD=1 : One half SCLK period before next CS_N assertion.
|
||||
// CS_GUARD=2 : One full SCLK period before next CS_N assertion.
|
||||
//
|
||||
// SPI Clock Rate:
|
||||
//
|
||||
// The SPI clock rate is controlled by setting the desired number of clock
|
||||
// cycles per half SPI clock period, minus 1. In other words, the actual
|
||||
// SPI clock frequency for a given clock frequency will be:
|
||||
//
|
||||
// f_sclk = f_clk / (2 * (half_per + 1))
|
||||
//
|
||||
// Or, the required half period setting to approximate given SPI clock
|
||||
// frequency can be calculated as:
|
||||
//
|
||||
// half_per = round((f_clk / (2 * f_sclk)) - 1)
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// HALF_PER_W : Width of the half-period input port in bits. This determines
|
||||
// the slowest SPI clock rate that can be achieved.
|
||||
// CS_HOLD : Number of half SCLK periods to hold CS_N low after last
|
||||
// falling edge of SCLK. Can be 0 or 1.
|
||||
// CS_GUARD : Number of half SCLK periods to hold CS_N between
|
||||
// transactions. Can be 1 or 2.
|
||||
//
|
||||
// Signals:
|
||||
//
|
||||
// clk / rst : Clock and active-high synchronous reset.
|
||||
// half_per : Half-period value; compared against an internal counter
|
||||
// each cycle. SCLK toggles every (half_per + 1) clock cycles.
|
||||
// Minimum value is 0, which yields the fastest SCLK rate of
|
||||
// clk/2. This port must remain stable for the duration of any
|
||||
// active transaction; it is expected to be driven by a static
|
||||
// constant or an external register that is only updated while
|
||||
// the core is idle.
|
||||
// s_axis_* : AXI-Stream slave (TX data in). Each byte is serialized
|
||||
// MSB-first.
|
||||
// m_axis_* : AXI-Stream master (RX data out). Each byte is serialized
|
||||
// MSB-first.
|
||||
// sclk : SPI clock output
|
||||
// cs_n : Active-low chip select
|
||||
// mosi : Master-out / slave-in
|
||||
// miso : Master-in / slave-out input
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
|
||||
module axis_spi #(
|
||||
int HALF_PER_W = 8,
|
||||
bit CS_HOLD = 1,
|
||||
int CS_GUARD = 2,
|
||||
|
||||
localparam int DATA_W = 8
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
// Half-period control
|
||||
input wire [HALF_PER_W-1:0] half_per,
|
||||
|
||||
// AXI-Stream Slave (TX data in)
|
||||
input wire [DATA_W-1:0] s_axis_tdata,
|
||||
input wire s_axis_tvalid,
|
||||
output logic s_axis_tready,
|
||||
input wire s_axis_tlast,
|
||||
|
||||
// AXI-Stream Master (RX data out)
|
||||
output logic [DATA_W-1:0] m_axis_tdata,
|
||||
output logic m_axis_tvalid,
|
||||
input wire m_axis_tready,
|
||||
output logic m_axis_tlast,
|
||||
|
||||
// SPI Interface
|
||||
output logic sclk = 1'b0,
|
||||
output logic cs_n = 1'b1,
|
||||
output logic mosi = 1'b0,
|
||||
input wire miso
|
||||
);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Parameter Checks
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
if (CS_HOLD < 0 || CS_HOLD > 1) begin : gen_hold_check
|
||||
$error("axis_spi: CS_HOLD must be 0 or 1 (setting is %0d)", CS_HOLD);
|
||||
end
|
||||
|
||||
if (CS_GUARD < 1 || CS_GUARD > 2) begin : gen_guard_check
|
||||
$error("axis_spi: CS_GUARD must be 1 or 2 (setting is %0d)", CS_GUARD);
|
||||
end
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Local Parameters
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// Enough bits to count 0 .. DATA_W-1
|
||||
localparam int BIT_CNT_W = $clog2(DATA_W);
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// State Machine Type
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
typedef enum logic [2:0] {
|
||||
ST_IDLE,
|
||||
ST_SHIFT_A,
|
||||
ST_SHIFT_B,
|
||||
ST_HOLD,
|
||||
ST_GUARD1,
|
||||
ST_GUARD2
|
||||
} state_t;
|
||||
|
||||
state_t state;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Internal Signals
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// Half-period counter. Done asserts when it reaches half_per.
|
||||
logic [HALF_PER_W-1:0] half_per_cnt;
|
||||
logic half_per_done;
|
||||
|
||||
// Bit position counter (0 to DATA_W-1)
|
||||
logic [BIT_CNT_W-1:0] bit_cnt;
|
||||
|
||||
// TX / RX shift registers
|
||||
logic [DATA_W-1:0] mosi_sr;
|
||||
logic [DATA_W-1:0] miso_sr;
|
||||
|
||||
// tlast latched to indicate the end of the SPI transaction
|
||||
logic tlast_reg;
|
||||
|
||||
// True when CS_N is currently asserted (mid-packet)
|
||||
logic in_packet;
|
||||
|
||||
// One-entry registered output buffer
|
||||
logic [DATA_W-1:0] out_data;
|
||||
logic out_valid;
|
||||
logic out_last;
|
||||
|
||||
// True when the output buffer is free this cycle (empty or being drained)
|
||||
logic out_buf_free;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Combinational Assignments
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
assign half_per_done = (half_per_cnt == half_per);
|
||||
assign out_buf_free = !out_valid || m_axis_tready;
|
||||
|
||||
// AXI-Stream output driven from the registered output buffer
|
||||
assign m_axis_tdata = out_data;
|
||||
assign m_axis_tvalid = out_valid;
|
||||
assign m_axis_tlast = out_last;
|
||||
|
||||
// Accept input when idle and the output buffer has room. In ST_IDLE the SPI
|
||||
// bus is paused (SCLK stopped), so CS_N stays asserted while we wait for
|
||||
// the next word when in_packet=1.
|
||||
assign s_axis_tready = (state == ST_IDLE) && out_buf_free;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Half-Period Counter
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Counts up every cycle while a SPI transaction is occurring. Resets to zero
|
||||
// the same cycle half_per_done fires so each half-period is exactly
|
||||
// (half_per + 1) clock cycles.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
half_per_cnt <= '0;
|
||||
end else begin
|
||||
if (state == ST_IDLE) begin
|
||||
half_per_cnt <= '0;
|
||||
end else begin
|
||||
if (half_per_done) begin
|
||||
half_per_cnt <= '0;
|
||||
end else begin
|
||||
half_per_cnt <= half_per_cnt + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// State Machine
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state <= ST_IDLE;
|
||||
sclk <= 1'b0;
|
||||
cs_n <= 1'b1;
|
||||
mosi <= 1'b0;
|
||||
mosi_sr <= 'X;
|
||||
miso_sr <= 'X;
|
||||
bit_cnt <= 'X;
|
||||
tlast_reg <= 1'b0;
|
||||
in_packet <= 1'b0;
|
||||
out_data <= 'X;
|
||||
out_valid <= 1'b0;
|
||||
out_last <= 'X;
|
||||
end else begin
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// Output buffer drain
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
if (m_axis_tready && out_valid) begin
|
||||
out_valid <= 1'b0;
|
||||
end
|
||||
|
||||
unique case (state)
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// IDLE: SCLK stopped. CS_N follows in_packet (stays asserted between
|
||||
// bytes of a packet, deasserted before/after).
|
||||
//
|
||||
// On input handshake: latch word and tlast, pre-drive MOSI with the
|
||||
// MSB, assert CS_N, and enter the shift loop.
|
||||
//---------------------------------------------------------------------
|
||||
ST_IDLE: begin
|
||||
sclk <= 1'b0;
|
||||
cs_n <= !in_packet;
|
||||
bit_cnt <= '0;
|
||||
|
||||
if (s_axis_tvalid && out_buf_free) begin
|
||||
mosi_sr <= s_axis_tdata;
|
||||
tlast_reg <= s_axis_tlast;
|
||||
|
||||
// Assert CS_N and pre-drive MSB onto MOSI before the first
|
||||
// SCLK rising edge (CPHA=0 requirement).
|
||||
cs_n <= 1'b0;
|
||||
mosi <= s_axis_tdata[DATA_W-1];
|
||||
|
||||
in_packet <= 1'b1;
|
||||
state <= ST_SHIFT_A;
|
||||
end
|
||||
end
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// SHIFT_A: First (low) half of the current SCLK bit period.
|
||||
//
|
||||
// SCLK rises at the end of this half, MISO is sampled on the rising
|
||||
// edge.
|
||||
//---------------------------------------------------------------------
|
||||
ST_SHIFT_A: begin
|
||||
if (half_per_done) begin
|
||||
sclk <= 1'b1;
|
||||
// Sample MISO into the LSB of the receive shift register
|
||||
miso_sr <= {miso_sr[DATA_W-2:0], miso};
|
||||
state <= ST_SHIFT_B;
|
||||
end
|
||||
end
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// SHIFT_B: Second (high) half of the current SCLK bit period.
|
||||
//
|
||||
// SCLK falls at the end of this half, MOSI advances to the next bit on
|
||||
// the falling edge.
|
||||
//
|
||||
// On the last bit, push the assembled received byte to the output
|
||||
// buffer and choose the next state.
|
||||
//---------------------------------------------------------------------
|
||||
ST_SHIFT_B: begin
|
||||
if (half_per_done) begin
|
||||
sclk <= 1'b0;
|
||||
bit_cnt <= bit_cnt + 1;
|
||||
|
||||
// Advance MOSI shift register and drive the next bit
|
||||
mosi_sr <= {mosi_sr[DATA_W-2:0], 1'b0};
|
||||
mosi <= mosi_sr[DATA_W-2];
|
||||
|
||||
if (bit_cnt == DATA_W-1) begin
|
||||
// Last bit don. Push received word to output buffer. out_valid
|
||||
// guaranteed 0 here (see block header).
|
||||
out_data <= miso_sr;
|
||||
out_valid <= 1'b1;
|
||||
out_last <= tlast_reg;
|
||||
|
||||
bit_cnt <= '0;
|
||||
|
||||
if (tlast_reg) begin
|
||||
// End of packet. Update CS_N based on CS_HOLD.
|
||||
in_packet <= 1'b0;
|
||||
if (CS_HOLD) begin
|
||||
state <= ST_HOLD;
|
||||
end else begin
|
||||
cs_n <= 1'b1;
|
||||
state <= ST_GUARD1;
|
||||
end
|
||||
end else begin
|
||||
// More words to come. Return to IDLE to accept the next word.
|
||||
// CS_N stays asserted while we wait.
|
||||
state <= ST_IDLE;
|
||||
end
|
||||
end else begin
|
||||
state <= ST_SHIFT_A;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// HOLD: Hold CS_N asserted for one extra half-period after the last
|
||||
// SCLK falling edge (only entered if CS_HOLD=1), then deassert CS_N
|
||||
// and proceed to the guard entry state.
|
||||
//---------------------------------------------------------------------
|
||||
ST_HOLD: begin
|
||||
if (half_per_done) begin
|
||||
cs_n <= 1'b1;
|
||||
state <= ST_GUARD1;
|
||||
end
|
||||
end
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// GUARD1/GUARD2: Hold CS_N deasserted for CS_GUARD half-periods after
|
||||
// the end of the transaction.
|
||||
//---------------------------------------------------------------------
|
||||
ST_GUARD1: begin
|
||||
if (half_per_done) begin
|
||||
state <= (CS_GUARD > 1) ? ST_GUARD2 : ST_IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
ST_GUARD2: begin
|
||||
if (half_per_done) begin
|
||||
state <= ST_IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
default: begin
|
||||
state <= ST_IDLE;
|
||||
end
|
||||
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
endmodule : axis_spi
|
||||
|
||||
|
||||
`default_nettype wire
|
||||
135
fpga/usrp3/lib/control/axis_to_ctrlport.sv
Normal file
135
fpga/usrp3/lib/control/axis_to_ctrlport.sv
Normal file
@ -0,0 +1,135 @@
|
||||
//
|
||||
// Copyright 2026 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: axis_to_ctrlport
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// Converts an AXI-Stream bus into a readable CtrlPort interface. Read
|
||||
// requests get responded to as soon as data becomes available on the
|
||||
// AXI-Stream input.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// WIDTH : Width of the data path in bits
|
||||
// FIFO_SIZE_LOG2 : Log base 2 of the depth of the input AXI-Stream FIFO.
|
||||
// This FIFO is optional. Depth can be set to -1 to remove
|
||||
// it entirely.
|
||||
//
|
||||
// Signals:
|
||||
//
|
||||
// clk : Clock
|
||||
// rst : Active-high synchronous reset
|
||||
// s_axis_tdata : AXI-Stream data input
|
||||
// s_axis_tvalid : AXI-Stream valid
|
||||
// s_axis_tready : AXI-Stream ready
|
||||
// ctrl_rd : CtrlPort read request strobe (single-cycle pulse)
|
||||
// ctrl_data : CtrlPort read data output
|
||||
// ctrl_ack : CtrlPort acknowledgement strobe (single-cycle pulse)
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
|
||||
module axis_to_ctrlport #(
|
||||
parameter int WIDTH = 32,
|
||||
parameter int FIFO_SIZE_LOG2 = 0
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
// AXI-Stream slave
|
||||
input wire [WIDTH-1:0] s_axis_tdata,
|
||||
input wire s_axis_tvalid,
|
||||
output wire s_axis_tready,
|
||||
|
||||
// CtrlPort read interface
|
||||
input wire ctrl_rd,
|
||||
output logic [WIDTH-1:0] ctrl_data,
|
||||
output logic ctrl_ack
|
||||
);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Declarations
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
typedef enum logic { ST_IDLE, ST_WAIT } state_t;
|
||||
state_t state;
|
||||
|
||||
// Internal stream between the input FIFO and the FSM.
|
||||
logic [WIDTH-1:0] fifo_o_tdata;
|
||||
logic fifo_o_tvalid;
|
||||
logic fifo_o_tready;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Input FIFO
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
axi_fifo #(
|
||||
.WIDTH (WIDTH),
|
||||
.SIZE (FIFO_SIZE_LOG2)
|
||||
) input_fifo_i (
|
||||
.clk (clk),
|
||||
.reset (rst),
|
||||
.clear (1'b0),
|
||||
.i_tdata (s_axis_tdata),
|
||||
.i_tvalid (s_axis_tvalid),
|
||||
.i_tready (s_axis_tready),
|
||||
.o_tdata (fifo_o_tdata),
|
||||
.o_tvalid (fifo_o_tvalid),
|
||||
.o_tready (fifo_o_tready),
|
||||
.space (),
|
||||
.occupied ()
|
||||
);
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// State Machine
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
assign ctrl_data = fifo_o_tdata;
|
||||
assign fifo_o_tready = ctrl_ack;
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state <= ST_IDLE;
|
||||
ctrl_ack <= 1'b0;
|
||||
end else begin
|
||||
// Default: deassert ACK every cycle.
|
||||
ctrl_ack <= 1'b0;
|
||||
|
||||
case (state)
|
||||
ST_IDLE: begin
|
||||
if (ctrl_rd) begin
|
||||
if (fifo_o_tvalid) begin
|
||||
// If we have data available, then ACK on next clock cycle.
|
||||
ctrl_ack <= 1'b1;
|
||||
end else begin
|
||||
// If we don't have data yet, then wait until it arrives.
|
||||
state <= ST_WAIT;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ST_WAIT: begin
|
||||
// ACK after we get a new data word
|
||||
if (fifo_o_tvalid) begin
|
||||
ctrl_ack <= 1'b1;
|
||||
state <= ST_IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
default: begin
|
||||
state <= ST_IDLE;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
endmodule : axis_to_ctrlport
|
||||
|
||||
|
||||
`default_nettype wire
|
||||
186
fpga/usrp3/lib/control/clocking_watchdog.sv
Normal file
186
fpga/usrp3/lib/control/clocking_watchdog.sv
Normal file
@ -0,0 +1,186 @@
|
||||
//
|
||||
// Copyright 2026 Ettus Research, A National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: clocking_watchdog
|
||||
//
|
||||
// Description:
|
||||
// Monitors clocking-health indicators and asserts clock_fault if any
|
||||
// indicator is out of spec. Two indicators are checked:
|
||||
//
|
||||
// 1. Lock status (clock_locked)
|
||||
// clock_fault is asserted in the monitor_clk domain whenever clock_locked
|
||||
// is not asserted (after synchronization into monitor_clk). This covers
|
||||
// PLL unlock events regardless of the actual frequency of watched_clk.
|
||||
//
|
||||
// 2. Frequency of watched_clk
|
||||
// The rate of watched_clk is measured by dividing it down to ~1 kHz
|
||||
// with clock_div (N = CLK_DIV_1KHZ, chosen to be even), synchronising
|
||||
// the divided clock into the monitor_clk domain, and counting how many
|
||||
// monitor_clk cycles pass between two consecutive rising edges of the
|
||||
// divided clock. A count below (MS_CYCLES_MON_CLK - 1) means the
|
||||
// watched_clk edge arrived sooner than expected, i.e. watched_clk is
|
||||
// running faster than MAX_CLOCK_RATE, and clock_fault_mon is asserted.
|
||||
// The counter saturates at MS_CYCLES_MON_CLK to prevent rollover when
|
||||
// watched_clk is slower than the maximum rate.
|
||||
//
|
||||
// clock_fault_mon (monitor_clk domain) is synchronised back into the
|
||||
// watched_clk domain before being driven out as clock_fault so that
|
||||
// downstream consumers can safely sample it on either clock.
|
||||
//
|
||||
// Clock domains:
|
||||
// monitor_clk : measurement and fault evaluation domain
|
||||
// watched_clk : clock being monitored; only clock_div runs here
|
||||
//
|
||||
// Reset:
|
||||
// rst is synchronous to monitor_clk. For logic in the watched_clk domain
|
||||
// (clock_div and the clock_fault synchronizer), rst is synchronized into the
|
||||
// watched_clk domain before use.
|
||||
//
|
||||
|
||||
module clocking_watchdog #(
|
||||
// Maximum allowed frequency of watched_clk in Hz. A watched_clk rate
|
||||
// strictly above this value causes clock_fault to be asserted.
|
||||
parameter int MAX_CLOCK_RATE = 125_000_000,
|
||||
// Frequency of monitor_clk in Hz. Used to derive the 1 ms measurement
|
||||
// window (MS_CYCLES_MON_CLK = MONITOR_CLK_FREQ / 1000).
|
||||
parameter int MONITOR_CLK_FREQ = 100_000_000
|
||||
) (
|
||||
// Free-running reference clock used for all measurement logic.
|
||||
input logic monitor_clk,
|
||||
// Synchronous reset (monitor_clk domain). Also drives clk_in_rst of
|
||||
// clock_div so the watched_clk-domain divider is reset as well.
|
||||
input logic rst,
|
||||
// PLL/MMCM lock indicator for watched_clk. Active high; when low,
|
||||
// clock_fault is asserted immediately regardless of the measured rate.
|
||||
input logic clock_locked,
|
||||
// Clock whose frequency is being monitored.
|
||||
input logic watched_clk,
|
||||
// Asserted (1) when a clocking fault is detected, deasserted (0) when
|
||||
// clock_locked is high and watched_clk is within [0, MAX_CLOCK_RATE].
|
||||
// Registered in the watched_clk domain.
|
||||
output logic clock_fault
|
||||
);
|
||||
|
||||
// Number of monitor_clk cycles in 1 ms; defines the measurement window.
|
||||
localparam int unsigned MS_CYCLES_MON_CLK = MONITOR_CLK_FREQ / 1000;
|
||||
// Number of watched_clk cycles in 1 ms at MAX_CLOCK_RATE.
|
||||
localparam int unsigned MAX_COUNT_PER_MS = MAX_CLOCK_RATE / 1000;
|
||||
// Worst-case tolerance of each clock source in ppm. The frequency margin
|
||||
// assumes two independent clocks, so their relative error is 2 * CLOCK_PPM.
|
||||
localparam int unsigned CLOCK_PPM = 100;
|
||||
// Divider ratio for clock_div. clock_div requires an even divisor, so
|
||||
// MAX_COUNT_PER_MS is rounded up to the nearest even integer when needed.
|
||||
localparam int unsigned CLK_DIV_1KHZ = ((MAX_COUNT_PER_MS % 2) != 0) ?
|
||||
(MAX_COUNT_PER_MS + 1) : MAX_COUNT_PER_MS;
|
||||
// Worst-case relative error from two independent CLOCK_PPM clocks over a
|
||||
// 1 ms measurement window. Ceiling rounds any fractional cycle up into the
|
||||
// safety margin.
|
||||
localparam int unsigned FREQ_MARGIN_CYCLES =
|
||||
int'($ceil(real'(MS_CYCLES_MON_CLK * (2 * CLOCK_PPM)) / 1_000_000.0));
|
||||
// One extra monitor_clk cycle for synchronizer/edge-detection quantization
|
||||
// when the divided watched clock is sampled into the monitor_clk domain.
|
||||
localparam int unsigned SYNC_MARGIN_CYCLES = 1;
|
||||
|
||||
// Counts monitor_clk cycles since the last rising edge of the divided
|
||||
// clock. One extra bit to hold MS_CYCLES_MON_CLK without wrap-around.
|
||||
logic unsigned [$clog2(MS_CYCLES_MON_CLK+1)-1:0] ms_tick_count;
|
||||
|
||||
logic clock_locked_mon; // clock_locked synchronised into monitor_clk domain
|
||||
logic watched_clk_div1k; // watched_clk divided by CLK_DIV_1KHZ (~1 kHz)
|
||||
logic watched_clk_div1k_mon; // divided clock synchronised into monitor_clk domain
|
||||
logic watched_clk_div1k_mon_prev; // one-cycle delay for rising-edge detection
|
||||
logic clock_fault_mon = 1'b1; // fault flag evaluated in monitor_clk domain
|
||||
logic rst_watched_clk; // rst synchronized into watched_clk domain
|
||||
|
||||
synchronizer #(
|
||||
.WIDTH(1),
|
||||
.STAGES(2),
|
||||
.INITIAL_VAL(0),
|
||||
.FALSE_PATH_TO_IN(1)
|
||||
) sync_data_clock_locked_i (
|
||||
.clk(monitor_clk),
|
||||
.rst(rst),
|
||||
.in(clock_locked),
|
||||
.out(clock_locked_mon)
|
||||
);
|
||||
|
||||
synchronizer #(
|
||||
.WIDTH(1),
|
||||
.STAGES(2),
|
||||
.INITIAL_VAL(1),
|
||||
.FALSE_PATH_TO_IN(1)
|
||||
) sync_rst_watched_clk_i (
|
||||
.clk(watched_clk),
|
||||
.rst(1'b0),
|
||||
.in(rst),
|
||||
.out(rst_watched_clk)
|
||||
);
|
||||
|
||||
clock_div #(
|
||||
.N(CLK_DIV_1KHZ)
|
||||
) clock_div_1khz_i (
|
||||
.clk_in(watched_clk),
|
||||
.clk_in_rst(rst_watched_clk),
|
||||
.clk_out(watched_clk_div1k)
|
||||
);
|
||||
|
||||
synchronizer #(
|
||||
.WIDTH(1),
|
||||
.STAGES(2),
|
||||
.INITIAL_VAL(0),
|
||||
.FALSE_PATH_TO_IN(1)
|
||||
) sync_watched_clk_1khz_i (
|
||||
.clk(monitor_clk),
|
||||
.rst(rst),
|
||||
.in(watched_clk_div1k),
|
||||
.out(watched_clk_div1k_mon)
|
||||
);
|
||||
|
||||
// Measurement and fault evaluation (monitor_clk domain).
|
||||
//
|
||||
// On each monitor_clk cycle ms_tick_count increments, saturating at
|
||||
// MS_CYCLES_MON_CLK to prevent wrap-around for slow watched_clk rates.
|
||||
//
|
||||
// On each rising edge of the synchronised divided clock:
|
||||
// - clock_fault_mon is set if ms_tick_count is below the nominal 1 ms
|
||||
// count minus both the frequency-tolerance margin and the
|
||||
// synchronizer quantization margin, meaning the interval completed
|
||||
// too early and watched_clk is running above MAX_CLOCK_RATE.
|
||||
// - ms_tick_count is reset to start the next measurement window.
|
||||
//
|
||||
// During reset or when clock_locked is not asserted, clock_fault_mon is
|
||||
// held high (fault asserted) and the counter is held at zero.
|
||||
always_ff @(posedge monitor_clk) begin
|
||||
if (rst || !clock_locked_mon) begin
|
||||
ms_tick_count <= '0;
|
||||
watched_clk_div1k_mon_prev <= '0;
|
||||
clock_fault_mon <= 1'b1;
|
||||
end else begin
|
||||
watched_clk_div1k_mon_prev <= watched_clk_div1k_mon;
|
||||
// Saturate to avoid rollover for slower-than-max watched clocks.
|
||||
if (ms_tick_count < MS_CYCLES_MON_CLK) begin
|
||||
ms_tick_count <= ms_tick_count + 1'b1;
|
||||
end
|
||||
if (watched_clk_div1k_mon && !watched_clk_div1k_mon_prev) begin
|
||||
clock_fault_mon <= ms_tick_count
|
||||
< MS_CYCLES_MON_CLK - FREQ_MARGIN_CYCLES - SYNC_MARGIN_CYCLES;
|
||||
ms_tick_count <= '0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
synchronizer #(
|
||||
.WIDTH(1),
|
||||
.STAGES(2),
|
||||
.INITIAL_VAL(1),
|
||||
.FALSE_PATH_TO_IN(1)
|
||||
) sync_clock_fault_i (
|
||||
.clk(watched_clk),
|
||||
.rst(rst_watched_clk),
|
||||
.in(clock_fault_mon),
|
||||
.out(clock_fault)
|
||||
);
|
||||
|
||||
endmodule
|
||||
991
fpga/usrp3/lib/control/ctrlport_spi_adrv.sv
Normal file
991
fpga/usrp3/lib/control/ctrlport_spi_adrv.sv
Normal file
@ -0,0 +1,991 @@
|
||||
//
|
||||
// Copyright 2026 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: ctrlport_spi_adrv
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// SPI master for the ADI ADRV transceiver family. It is controlled via a
|
||||
// CtrlPort slave interface and it drives a standard 4-wire SPI bus (CPOL=0,
|
||||
// CPHA=0). It is designed to make efficient use of the CtrlPort bus by
|
||||
// eliminating the need to write redundant address bytes (e.g., repeating the
|
||||
// address for writes to the same address, or writes to sequential
|
||||
// addresses), the need to write dummy bytes in order to flush out a read
|
||||
// result, or the need to read back echo bytes in responses. Only the
|
||||
// relevant bytes need to be communicated over CtrlPort.
|
||||
//
|
||||
// Furthermore, the SPI transactions are blocking in the sense that the
|
||||
// CtrlPort interface will not ACK until the request is accepted and
|
||||
// guaranteed to complete. This allows multiple SPI transactions to be
|
||||
// enqueued in the underlying CtrlPort endpoint FIFO without the need for
|
||||
// polling the core for status.
|
||||
//
|
||||
// Two methods are available for issuing SPI transactions: REG_CONTROl and
|
||||
// REG_SI.
|
||||
//
|
||||
// REG_CONTROL Interface
|
||||
//
|
||||
// With this interface, the REG_CONTROL register controls the mode,
|
||||
// direction, and the number of data bytes in the transaction. Four
|
||||
// transaction modes are supported via the REG_CONTROL register.
|
||||
//
|
||||
// RAW : The exact byte stream in the data register is shifted out.
|
||||
// The caller is responsible for embedding any SPI address.
|
||||
//
|
||||
// SI_SEQ : Single instruction, sequential. Each data byte is preceded
|
||||
// by the 16-bit SPI address written in REG_CONTROL, which
|
||||
// auto-increments by one for every byte. Three SPI bytes are
|
||||
// sent per data byte.
|
||||
//
|
||||
// SI_REP : Single instruction, repeated. Same as SI_SEQ but the
|
||||
// address does not increment. The same address is sent
|
||||
// before every data byte.
|
||||
//
|
||||
// STREAMING : The 16-bit SPI address is sent once, followed by all data
|
||||
// bytes back-to-back. Two address bytes plus num_bytes data
|
||||
// bytes are sent total.
|
||||
//
|
||||
//
|
||||
// For write operations in all the above modes, the RX state machine
|
||||
// ignores the incoming bytes from the SPI interface without storing them.
|
||||
// So, it is not necessary to read anything when doing a write.
|
||||
//
|
||||
// For read operations in RAW mode, all MISO bytes are returned such that
|
||||
// you must read the same number of bytes that were written.
|
||||
//
|
||||
// For read operations in SI_SEQ, SI_REP, and STREAMING mode, the state
|
||||
// machine automatically drives MOSI with zeros during the data phase, so
|
||||
// that it is not necessary to write any data bytes to execute a read. And
|
||||
// it automatically captures only the data bytes from MISO; the address
|
||||
// echo bytes are discarded. So, you only need to read the number of data
|
||||
// bytes that were requested to be written (excluding the address bytes).
|
||||
//
|
||||
// REG_SI Interface
|
||||
//
|
||||
// For a single-byte read or write (referred to as "single instruction" in
|
||||
// the ADI code), there is also a REG_SI register to which a single 3-byte
|
||||
// instruction can be written (addr_hi, addr_lo, and data byte). This is
|
||||
// equivalent to using RAW mode with a length of 3 bytes. If the MSB of the
|
||||
// address (bit 15) is set, then it is treated as a read and the 3 RX data
|
||||
// bytes are inserted into the RX FIFO and can be read by reading the
|
||||
// REG_DATA register.
|
||||
//
|
||||
// The benefit of using REG_SI is that you don't need to also write to the
|
||||
// REG_CONTROL register. So, fewer CtrlPort writes are needed for three
|
||||
// instructions or less compared to RAW mode. On the downside, each
|
||||
// instruction is its own SPI transaction (i.e., CS_n deasserts between
|
||||
// each instruction). So, for 2 or more instructions, additional time may
|
||||
// be used on the SPI bus.
|
||||
//
|
||||
// RX Data FIFO
|
||||
//
|
||||
// Received bytes are packed into 32-bit words (first byte in bits [7:0],
|
||||
// second byte in bits [15:8], etc.) and stored in the RX word FIFO. Each
|
||||
// transaction ends on a word boundary; if the number of received bytes is
|
||||
// not a multiple of 4, the remaining bytes in the last word are undefined.
|
||||
// This means that the first byte of a transaction always starts with bits
|
||||
// [7:0] of a new FIFO word, regardless of how many bytes the previous
|
||||
// transaction produced.
|
||||
//
|
||||
// Each REG_SI read request always produces exactly 3 received bytes, which
|
||||
// are padded to a full 32-bit word (one FIFO entry). So each REG_SI read
|
||||
// fills one word of the FIFO, but only bytes [23:0] are valid.
|
||||
//
|
||||
// The host software must not overfill the FIFO. The default depth holds
|
||||
// one complete maximum-size transaction.
|
||||
//
|
||||
// Data Word Byte Ordering
|
||||
//
|
||||
// Words are little-endian packed. That is, byte 0 (the first byte to be
|
||||
// sent over SPI) occupies data[7:0], byte 1 occupies data[15:8], etc. Each
|
||||
// byte is serialized MSB-first. That is, bit 7 leaves the MOSI/MISO pin
|
||||
// first, bit 0 last.
|
||||
//
|
||||
// CS_N Behavior
|
||||
//
|
||||
// Asserts when REG_CONTROL is written and deasserts automatically after
|
||||
// all num_bytes have been shifted and captured. CS_N remains asserted until
|
||||
// the entire transaction has completed; the SPI clock simply pauses
|
||||
// between data bytes when the TX buffer is empty.
|
||||
//
|
||||
// For REG_SI, CS_N asserts/deasserts for each single instruction.
|
||||
//
|
||||
// CtrlPort Flow Control
|
||||
//
|
||||
// REG_CONTROL write : ACKs after the control word has been accepted.
|
||||
// REG_DATA write : ACKs after the write has been accepted and is
|
||||
// guaranteed to complete.
|
||||
// REG_DATA read : ACK'd when the data to be read becomes available.
|
||||
//
|
||||
// SPI Clock Rate
|
||||
//
|
||||
// The SPI clock rate is controlled by setting the desired number of clock
|
||||
// cycles per half SPI clock period, minus 1. In other words, the actual
|
||||
// SPI clock frequency for a given clock frequency will be:
|
||||
//
|
||||
// f_sclk = f_clk / (2 * (half_per + 1))
|
||||
//
|
||||
// Or, the required half period setting for a given SPI clock frequency can
|
||||
// be calculated as:
|
||||
//
|
||||
// half_per = (f_clk / (2 * f_sclk)) - 1
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// BASE_ADDR : CtrlPort base address.
|
||||
// NUM_BYTES_W : Log base 2 of maximum number data bytes supported per
|
||||
// transaction. Must not exceed SPI_NUM_BYTES_W.
|
||||
// HALF_PER : Default sclk half-period length in clk cycles
|
||||
// HALF_PER_W : Width of the half-period register in bits.
|
||||
// HALF_PER_EN : When 1, the half-period register (REG_HALF_PER) exists and
|
||||
// is software-readable and writable. When 0, the register
|
||||
// does not exist; the SCLK rate is fixed at the HALF_PER
|
||||
// parameter value.
|
||||
// CS_HOLD : 1 = hold CS_N low for one extra half-period after the last
|
||||
// SCLK falling edge. 0 = deassert CS_N immediately on the
|
||||
// last SCLK falling edge.
|
||||
// CS_GUARD : Number of CS_N-high half-periods between transactions. Must
|
||||
// be 1 or 2.
|
||||
// RX_FIFO_SIZE : Log base 2 of the depth of the 32-bit wide RX word FIFO. By
|
||||
// default, it holds one complete maximum-size transaction
|
||||
// (2**NUM_BYTES_W bytes / 4 bytes per word). The host
|
||||
// software must NOT overfill the FIFO.
|
||||
//
|
||||
// Signals:
|
||||
//
|
||||
// clk / rst : Clock and active-high synchronous reset.
|
||||
// s_ctrlport_* : CtrlPort slave request/response signals.
|
||||
// sclk : SPI clock output (registered).
|
||||
// cs_n : Active-low chip select (registered).
|
||||
// mosi : Master-out / slave-in data (registered).
|
||||
// miso : Master-in / slave-out data input.
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
|
||||
module ctrlport_spi_adrv
|
||||
import ctrlport_pkg::*;
|
||||
import ctrlport_spi_adrv_pkg::*;
|
||||
#(
|
||||
int BASE_ADDR = 0,
|
||||
int NUM_BYTES_W = 8,
|
||||
int HALF_PER = 9,
|
||||
int HALF_PER_W = 8,
|
||||
bit HALF_PER_EN = 1,
|
||||
bit CS_HOLD = 1,
|
||||
int CS_GUARD = 2,
|
||||
int RX_FIFO_SIZE = NUM_BYTES_W-2
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
// CtrlPort Slave Request
|
||||
input wire s_ctrlport_req_wr,
|
||||
input wire s_ctrlport_req_rd,
|
||||
input wire [CTRLPORT_ADDR_W-1:0] s_ctrlport_req_addr,
|
||||
input wire [CTRLPORT_DATA_W-1:0] s_ctrlport_req_data,
|
||||
|
||||
// CtrlPort Slave Response
|
||||
output logic s_ctrlport_resp_ack,
|
||||
output ctrlport_status_t s_ctrlport_resp_status,
|
||||
output logic [CTRLPORT_DATA_W-1:0] s_ctrlport_resp_data,
|
||||
|
||||
// SPI Interface
|
||||
output logic sclk,
|
||||
output logic cs_n,
|
||||
output logic mosi,
|
||||
input wire miso
|
||||
);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Parameter Checks
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
if (NUM_BYTES_W > SPI_NUM_BYTES_W) begin : gen_error
|
||||
$error("NUM_BYTES_W (%0d) must not exceed SPI_NUM_BYTES_W (%0d).",
|
||||
NUM_BYTES_W, SPI_NUM_BYTES_W);
|
||||
end : gen_error
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Declarations
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// SPI clock half-period. Driven by a software-writable register when
|
||||
// HALF_PER_EN=1; wired to the parameter value when HALF_PER_EN=0.
|
||||
logic [HALF_PER_W-1:0] half_per;
|
||||
|
||||
// TX 32-bit AXI-Stream (from register block into TX width converter)
|
||||
logic [31:0] tx_word_tdata;
|
||||
logic [ 3:0] tx_word_tkeep;
|
||||
logic tx_word_tvalid;
|
||||
logic tx_word_tready;
|
||||
logic tx_word_tlast;
|
||||
|
||||
// TX 8-bit AXI-Stream (from TX width converter into axis_spi)
|
||||
logic [7:0] tx_byte_tdata;
|
||||
logic tx_byte_tvalid;
|
||||
logic tx_byte_tready;
|
||||
logic tx_byte_tlast;
|
||||
|
||||
// RX 8-bit AXI-Stream (from axis_spi into RX FSM)
|
||||
logic [7:0] spi_rx_tdata;
|
||||
logic spi_rx_tvalid;
|
||||
logic spi_rx_tready;
|
||||
logic spi_rx_tlast;
|
||||
|
||||
// RX 8-bit AXI-Stream (from RX FSM into RX width converter)
|
||||
logic [7:0] rx_byte_tdata;
|
||||
logic rx_byte_tvalid;
|
||||
logic rx_byte_tready;
|
||||
logic rx_byte_tlast;
|
||||
|
||||
// RX 32-bit AXI-Stream (from RX width converter into RX FIFO)
|
||||
logic [31:0] rx_word_tdata;
|
||||
logic rx_word_tvalid;
|
||||
logic rx_word_tready;
|
||||
logic rx_word_tlast;
|
||||
|
||||
// RX FIFO output
|
||||
logic [31:0] rx_fifo_tdata;
|
||||
logic rx_fifo_tvalid;
|
||||
logic rx_fifo_tready;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Address Decode
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
logic address_in_range;
|
||||
logic is_si_reg;
|
||||
logic is_ctrl_reg;
|
||||
logic is_data_win;
|
||||
|
||||
assign address_in_range =
|
||||
(s_ctrlport_req_addr[CTRLPORT_ADDR_W-1:REG_ADDR_W] ==
|
||||
BASE_ADDR[CTRLPORT_ADDR_W-1:REG_ADDR_W]);
|
||||
|
||||
assign is_si_reg = address_in_range &&
|
||||
(s_ctrlport_req_addr[0+:REG_ADDR_W] == REG_SI);
|
||||
assign is_ctrl_reg = address_in_range &&
|
||||
(s_ctrlport_req_addr[0+:REG_ADDR_W] == REG_CONTROL);
|
||||
assign is_data_win = address_in_range &&
|
||||
(s_ctrlport_req_addr[0+:REG_ADDR_W] >= REG_DATA) &&
|
||||
(s_ctrlport_req_addr[0+:REG_ADDR_W] < REG_DATA + 64);
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Half-Period Register
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// When HALF_PER_EN=1 the register exists and is software-readable and
|
||||
// writable. When HALF_PER_EN=0 none of this logic exists and half_per is a
|
||||
// constant tied to the HALF_PER parameter.
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
logic half_per_ack;
|
||||
|
||||
if (HALF_PER_EN) begin : gen_half_per_reg
|
||||
logic is_half_per_reg;
|
||||
assign is_half_per_reg = address_in_range &&
|
||||
(s_ctrlport_req_addr[0+:REG_ADDR_W] == REG_HALF_PER);
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
half_per <= HALF_PER;
|
||||
half_per_ack <= 1'b0;
|
||||
end else begin
|
||||
half_per_ack <= 1'b0;
|
||||
|
||||
if (is_half_per_reg) begin
|
||||
if (s_ctrlport_req_wr) begin
|
||||
half_per <= s_ctrlport_req_data[HALF_PER_W-1:0];
|
||||
half_per_ack <= 1'b1;
|
||||
end else if (s_ctrlport_req_rd) begin
|
||||
half_per_ack <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end else begin : gen_half_per_fixed
|
||||
assign half_per = HALF_PER;
|
||||
assign half_per_ack = 1'b0;
|
||||
end : gen_half_per_fixed
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Control Register Write Handling
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// For writes to REG_SI, we must also write a control word equivalent to a
|
||||
// 3-byte RAW control word, with read or write set based on the MSB of the
|
||||
// address.
|
||||
spi_adrv_ctrl_t si_ctrl_word;
|
||||
assign si_ctrl_word = '{
|
||||
dir: s_ctrlport_req_data[7] ? SPI_READ : SPI_WRITE,
|
||||
rsvd1: 'X,
|
||||
mode: MODE_RAW,
|
||||
num_bytes: 3,
|
||||
addr: 'X
|
||||
};
|
||||
|
||||
logic [CTRLPORT_DATA_W-1:0] ctrl_data;
|
||||
logic ctrl_wr;
|
||||
logic ctrl_ack;
|
||||
logic [CTRLPORT_DATA_W-1:0] ctrl_tdata;
|
||||
logic ctrl_tvalid;
|
||||
logic ctrl_tready;
|
||||
|
||||
// Control word split to TX and RX FSMs.
|
||||
spi_adrv_ctrl_t ctrl_tx_tdata;
|
||||
logic ctrl_tx_tvalid;
|
||||
logic ctrl_tx_tready;
|
||||
spi_adrv_ctrl_t ctrl_rx_tdata;
|
||||
logic ctrl_rx_tvalid;
|
||||
logic ctrl_rx_tready;
|
||||
|
||||
// REG_CONTROL writes pass the data word through. REG_SI writes use the
|
||||
// synthesized control word above.
|
||||
assign ctrl_data = is_si_reg ? si_ctrl_word : s_ctrlport_req_data;
|
||||
assign ctrl_wr = s_ctrlport_req_wr && (is_ctrl_reg || is_si_reg);
|
||||
|
||||
ctrlport_to_axis #(
|
||||
.WIDTH (CTRLPORT_DATA_W),
|
||||
.FIFO_SIZE_LOG2 (1)
|
||||
) ctrlport_to_axis_ctrl (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.ctrl_data (ctrl_data),
|
||||
.ctrl_wr (ctrl_wr),
|
||||
.ctrl_ack (ctrl_ack),
|
||||
.m_axis_tdata (ctrl_tdata),
|
||||
.m_axis_tvalid (ctrl_tvalid),
|
||||
.m_axis_tready (ctrl_tready)
|
||||
);
|
||||
|
||||
// Duplicate the control word to the TX FSM (port 0) and RX FSM (port 1).
|
||||
axis_split #(
|
||||
.DATA_W (CTRLPORT_DATA_W),
|
||||
.NUM_PORTS (2),
|
||||
.INPUT_REG (0),
|
||||
.OUT_FIFO_SIZE (1)
|
||||
) ctrl_split_i (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.s_axis_tdata (ctrl_tdata),
|
||||
.s_axis_tvalid (ctrl_tvalid),
|
||||
.s_axis_tready (ctrl_tready),
|
||||
.m_axis_tdata ({ctrl_rx_tdata, ctrl_tx_tdata}),
|
||||
.m_axis_tvalid ({ctrl_rx_tvalid, ctrl_tx_tvalid}),
|
||||
.m_axis_tready ({ctrl_rx_tready, ctrl_tx_tready})
|
||||
);
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Data Register Write Handling
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
logic data_wr;
|
||||
logic data_ack;
|
||||
|
||||
assign data_wr = s_ctrlport_req_wr && (is_data_win || is_si_reg);
|
||||
|
||||
ctrlport_to_axis #(
|
||||
.WIDTH (CTRLPORT_DATA_W),
|
||||
.FIFO_SIZE_LOG2 (0)
|
||||
) ctrlport_to_axis_data (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.ctrl_data (s_ctrlport_req_data),
|
||||
.ctrl_wr (data_wr),
|
||||
.ctrl_ack (data_ack),
|
||||
.m_axis_tdata (tx_word_tdata),
|
||||
.m_axis_tvalid (tx_word_tvalid),
|
||||
.m_axis_tready (tx_word_tready)
|
||||
);
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Data Register Read Handling
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
logic [CTRLPORT_DATA_W-1:0] data_a2c_data;
|
||||
logic data_a2c_ack;
|
||||
logic data_a2c_rd;
|
||||
|
||||
assign data_a2c_rd = s_ctrlport_req_rd && is_data_win;
|
||||
|
||||
axis_to_ctrlport #(
|
||||
.WIDTH (CTRLPORT_DATA_W),
|
||||
.FIFO_SIZE_LOG2 (0)
|
||||
) axis_to_ctrlport_rx (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.s_axis_tdata (rx_fifo_tdata),
|
||||
.s_axis_tvalid (rx_fifo_tvalid),
|
||||
.s_axis_tready (rx_fifo_tready),
|
||||
.ctrl_rd (data_a2c_rd),
|
||||
.ctrl_data (data_a2c_data),
|
||||
.ctrl_ack (data_a2c_ack)
|
||||
);
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// CtrlPort Response Acknowledgement
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// A REG_SI write triggers both ctrl_wr and data_wr simultaneously, so
|
||||
// ctrl_ack and data_ack fire independently at different times. This state
|
||||
// machine ensures exactly one s_ctrlport_resp_ack pulse is issued per
|
||||
// request, once all expected ACKs have arrived.
|
||||
//
|
||||
// ST_ACK_IDLE : Waiting for a write request.
|
||||
// ST_ACK_WAIT_ONE : Expecting one more ACK (REG_CONTROL or REG_DATA write,
|
||||
// or REG_SI write after one of the two ACKs has arrived).
|
||||
// ST_ACK_WAIT_TWO : Expecting two ACKs (REG_SI write, neither arrived yet);
|
||||
// if both arrive on the same cycle, coalesce immediately.
|
||||
//
|
||||
// data_a2c_ack (axis-to-ctrlport for REG_DATA reads) is always single-source
|
||||
// and bypasses the state machine, ORing directly into s_ctrlport_resp_ack.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
typedef enum logic [1:0] {
|
||||
ST_ACK_IDLE = 0,
|
||||
ST_ACK_WAIT_ONE = 1,
|
||||
ST_ACK_WAIT_TWO = 2
|
||||
} ack_state_t;
|
||||
|
||||
ack_state_t ack_state;
|
||||
logic ack_out;
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
ack_state <= ST_ACK_IDLE;
|
||||
ack_out <= 1'b0;
|
||||
end else begin
|
||||
ack_out <= 1'b0;
|
||||
|
||||
case (ack_state)
|
||||
ST_ACK_IDLE : begin
|
||||
if (s_ctrlport_req_wr) begin
|
||||
if (is_si_reg)
|
||||
ack_state <= ST_ACK_WAIT_TWO;
|
||||
else
|
||||
ack_state <= ST_ACK_WAIT_ONE;
|
||||
end
|
||||
end
|
||||
|
||||
ST_ACK_WAIT_ONE : begin
|
||||
if (ctrl_ack || data_ack) begin
|
||||
ack_out <= 1'b1;
|
||||
ack_state <= ST_ACK_IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
ST_ACK_WAIT_TWO : begin
|
||||
if (ctrl_ack && data_ack) begin
|
||||
// Both arrived on the same cycle.
|
||||
ack_out <= 1'b1;
|
||||
ack_state <= ST_ACK_IDLE;
|
||||
end else if (ctrl_ack || data_ack) begin
|
||||
// One arrived; wait for the other.
|
||||
ack_state <= ST_ACK_WAIT_ONE;
|
||||
end
|
||||
end
|
||||
|
||||
default : ack_state <= ST_ACK_IDLE;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
s_ctrlport_resp_data <= 'X;
|
||||
end else begin
|
||||
s_ctrlport_resp_ack <= ack_out | data_a2c_ack | half_per_ack;
|
||||
s_ctrlport_resp_data <= half_per_ack ? half_per : data_a2c_data;
|
||||
end
|
||||
end
|
||||
assign s_ctrlport_resp_status = STS_OKAY;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// TX Width Converter (32-bit -> 8-bit)
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
axis_width_conv #(
|
||||
.WORD_W (8),
|
||||
.IN_WORDS (4),
|
||||
.OUT_WORDS (1),
|
||||
.SYNC_CLKS (1),
|
||||
.PIPELINE ("OUT")
|
||||
) axis_width_conv_tx (
|
||||
.s_axis_aclk (clk),
|
||||
.s_axis_rst (rst),
|
||||
.s_axis_tdata (tx_word_tdata),
|
||||
.s_axis_tkeep ('1),
|
||||
.s_axis_tlast ('0),
|
||||
.s_axis_tvalid (tx_word_tvalid),
|
||||
.s_axis_tready (tx_word_tready),
|
||||
.m_axis_aclk (clk),
|
||||
.m_axis_rst (rst),
|
||||
.m_axis_tdata (tx_byte_tdata),
|
||||
.m_axis_tkeep (),
|
||||
.m_axis_tlast (tx_byte_tlast),
|
||||
.m_axis_tvalid (tx_byte_tvalid),
|
||||
.m_axis_tready (tx_byte_tready)
|
||||
);
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// TX State Machine
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// AXI-Stream from TX FSM into axis_spi.
|
||||
logic [7:0] spi_tx_tdata;
|
||||
logic spi_tx_tvalid;
|
||||
logic spi_tx_tready;
|
||||
logic spi_tx_tlast;
|
||||
|
||||
typedef enum logic [3:0] {
|
||||
ST_TX_IDLE = 0,
|
||||
ST_TX_RAW = 1,
|
||||
ST_TX_SI_ADDR_HI = 2,
|
||||
ST_TX_SI_ADDR_LO = 3,
|
||||
ST_TX_SI_DATA = 4,
|
||||
ST_TX_STREAM_ADDR_HI = 5,
|
||||
ST_TX_STREAM_ADDR_LO = 6,
|
||||
ST_TX_STREAM_DATA = 7,
|
||||
ST_TX_DRAIN = 8
|
||||
} tx_state_t;
|
||||
|
||||
tx_state_t tx_state;
|
||||
logic [SPI_NUM_BYTES_W-1:0] tx_bytes_rem; // Data bytes remaining
|
||||
logic [ 15:0] tx_cur_addr; // Running SPI address
|
||||
logic [ 1:0] tx_drain_cnt; // Padding bytes to drain after transaction
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
tx_state <= ST_TX_IDLE;
|
||||
tx_bytes_rem <= '0;
|
||||
tx_cur_addr <= '0;
|
||||
tx_drain_cnt <= 2'b0;
|
||||
end else begin
|
||||
case (tx_state)
|
||||
ST_TX_IDLE : begin
|
||||
if (ctrl_tx_tvalid) begin
|
||||
tx_bytes_rem <= ctrl_tx_tdata.num_bytes;
|
||||
tx_cur_addr <= ctrl_tx_tdata.addr;
|
||||
tx_drain_cnt <= 2'b0 - ctrl_tx_tdata.num_bytes[1:0];
|
||||
case (ctrl_tx_tdata.mode)
|
||||
MODE_RAW : tx_state <= ST_TX_RAW;
|
||||
MODE_SI_SEQ,
|
||||
MODE_SI_REP : tx_state <= ST_TX_SI_ADDR_HI;
|
||||
MODE_STREAMING : tx_state <= ST_TX_STREAM_ADDR_HI;
|
||||
default : tx_state <= ST_TX_RAW;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
ST_TX_RAW : begin
|
||||
if (spi_tx_tvalid && spi_tx_tready) begin
|
||||
tx_bytes_rem <= tx_bytes_rem - 1;
|
||||
if (tx_bytes_rem == 1) begin
|
||||
tx_state <= (tx_drain_cnt != 0) ? ST_TX_DRAIN : ST_TX_IDLE;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ST_TX_SI_ADDR_HI : begin
|
||||
if (spi_tx_tvalid && spi_tx_tready) begin
|
||||
tx_state <= ST_TX_SI_ADDR_LO;
|
||||
end
|
||||
end
|
||||
|
||||
ST_TX_SI_ADDR_LO : begin
|
||||
if (spi_tx_tvalid && spi_tx_tready) begin
|
||||
tx_state <= ST_TX_SI_DATA;
|
||||
end
|
||||
end
|
||||
|
||||
ST_TX_SI_DATA : begin
|
||||
if (spi_tx_tvalid && spi_tx_tready) begin
|
||||
tx_bytes_rem <= tx_bytes_rem - 1;
|
||||
if (tx_bytes_rem == 1) begin
|
||||
if (ctrl_tx_tdata.dir == SPI_WRITE && tx_drain_cnt != 0) begin
|
||||
tx_state <= ST_TX_DRAIN;
|
||||
end else begin
|
||||
tx_state <= ST_TX_IDLE;
|
||||
end
|
||||
end else begin
|
||||
if (ctrl_tx_tdata.mode == MODE_SI_SEQ) begin
|
||||
tx_cur_addr <= tx_cur_addr + 1;
|
||||
end
|
||||
tx_state <= ST_TX_SI_ADDR_HI;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ST_TX_STREAM_ADDR_HI : begin
|
||||
if (spi_tx_tvalid && spi_tx_tready) begin
|
||||
tx_state <= ST_TX_STREAM_ADDR_LO;
|
||||
end
|
||||
end
|
||||
|
||||
ST_TX_STREAM_ADDR_LO : begin
|
||||
if (spi_tx_tvalid && spi_tx_tready) begin
|
||||
tx_state <= ST_TX_STREAM_DATA;
|
||||
end
|
||||
end
|
||||
|
||||
ST_TX_STREAM_DATA : begin
|
||||
if (spi_tx_tvalid && spi_tx_tready) begin
|
||||
tx_bytes_rem <= tx_bytes_rem - 1;
|
||||
if (tx_bytes_rem == 1) begin
|
||||
if (ctrl_tx_tdata.dir == SPI_WRITE && tx_drain_cnt != 0) begin
|
||||
tx_state <= ST_TX_DRAIN;
|
||||
end else begin
|
||||
tx_state <= ST_TX_IDLE;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ST_TX_DRAIN : begin
|
||||
if (tx_byte_tvalid) begin
|
||||
tx_drain_cnt <= tx_drain_cnt - 1;
|
||||
if (tx_drain_cnt == 1) begin
|
||||
tx_state <= ST_TX_IDLE;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
default : begin
|
||||
tx_state <= ST_TX_IDLE;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
// ctrl_tx_tready is driven combinationally so that the control FIFO word is
|
||||
// consumed on the exact same cycle that the last SPI byte is accepted by
|
||||
// axis_spi. This ensures the FIFO advances before the FSM re-enters
|
||||
// ST_TX_IDLE, so it sees the correct next control word.
|
||||
always_comb begin
|
||||
spi_tx_tdata = 8'h00;
|
||||
spi_tx_tvalid = 1'b0;
|
||||
spi_tx_tlast = 1'b0;
|
||||
tx_byte_tready = 1'b0;
|
||||
ctrl_tx_tready = 1'b0;
|
||||
|
||||
case (tx_state)
|
||||
ST_TX_IDLE : begin
|
||||
// Waiting; nothing to send.
|
||||
end
|
||||
|
||||
ST_TX_RAW : begin
|
||||
// RAW: pass tx_byte bytes straight through regardless of direction.
|
||||
spi_tx_tdata = tx_byte_tdata;
|
||||
spi_tx_tvalid = tx_byte_tvalid;
|
||||
tx_byte_tready = spi_tx_tready;
|
||||
spi_tx_tlast = (tx_bytes_rem == 1);
|
||||
ctrl_tx_tready = spi_tx_tvalid && spi_tx_tready && (tx_bytes_rem == 1);
|
||||
end
|
||||
|
||||
ST_TX_SI_ADDR_HI : begin
|
||||
// addr[15:8] = addr_hi; first byte on the wire.
|
||||
spi_tx_tdata = tx_cur_addr[15:8];
|
||||
spi_tx_tvalid = 1'b1;
|
||||
end
|
||||
|
||||
ST_TX_SI_ADDR_LO : begin
|
||||
// addr[7:0] = addr_lo; second byte on the wire.
|
||||
spi_tx_tdata = tx_cur_addr[7:0];
|
||||
spi_tx_tvalid = 1'b1;
|
||||
end
|
||||
|
||||
ST_TX_SI_DATA : begin
|
||||
if (ctrl_tx_tdata.dir == SPI_READ) begin
|
||||
// Drive MOSI=0 for reads; do not consume TX data FIFO.
|
||||
spi_tx_tdata = 8'h00;
|
||||
spi_tx_tvalid = 1'b1;
|
||||
end else begin
|
||||
spi_tx_tdata = tx_byte_tdata;
|
||||
spi_tx_tvalid = tx_byte_tvalid;
|
||||
tx_byte_tready = spi_tx_tready;
|
||||
end
|
||||
spi_tx_tlast = (tx_bytes_rem == 1);
|
||||
ctrl_tx_tready = spi_tx_tvalid && spi_tx_tready && (tx_bytes_rem == 1);
|
||||
end
|
||||
|
||||
ST_TX_STREAM_ADDR_HI : begin
|
||||
spi_tx_tdata = tx_cur_addr[15:8];
|
||||
spi_tx_tvalid = 1'b1;
|
||||
end
|
||||
|
||||
ST_TX_STREAM_ADDR_LO : begin
|
||||
spi_tx_tdata = tx_cur_addr[7:0];
|
||||
spi_tx_tvalid = 1'b1;
|
||||
end
|
||||
|
||||
ST_TX_STREAM_DATA : begin
|
||||
if (ctrl_tx_tdata.dir == SPI_READ) begin
|
||||
spi_tx_tdata = 8'h00;
|
||||
spi_tx_tvalid = 1'b1;
|
||||
end else begin
|
||||
spi_tx_tdata = tx_byte_tdata;
|
||||
spi_tx_tvalid = tx_byte_tvalid;
|
||||
tx_byte_tready = spi_tx_tready;
|
||||
end
|
||||
spi_tx_tlast = (tx_bytes_rem == 1);
|
||||
ctrl_tx_tready = spi_tx_tvalid && spi_tx_tready && (tx_bytes_rem == 1);
|
||||
end
|
||||
|
||||
ST_TX_DRAIN : begin
|
||||
// Consume width-converter padding bytes without sending them on MOSI.
|
||||
tx_byte_tready = 1'b1;
|
||||
end
|
||||
|
||||
default : begin
|
||||
// Nothing
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// SPI Interface
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
axis_spi #(
|
||||
.HALF_PER_W (HALF_PER_W),
|
||||
.CS_HOLD (CS_HOLD),
|
||||
.CS_GUARD (CS_GUARD)
|
||||
) axis_spi_i (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.half_per (half_per),
|
||||
.s_axis_tdata (spi_tx_tdata),
|
||||
.s_axis_tvalid (spi_tx_tvalid),
|
||||
.s_axis_tready (spi_tx_tready),
|
||||
.s_axis_tlast (spi_tx_tlast),
|
||||
.m_axis_tdata (spi_rx_tdata),
|
||||
.m_axis_tvalid (spi_rx_tvalid),
|
||||
.m_axis_tready (spi_rx_tready),
|
||||
.m_axis_tlast (spi_rx_tlast),
|
||||
.sclk (sclk),
|
||||
.cs_n (cs_n),
|
||||
.mosi (mosi),
|
||||
.miso (miso)
|
||||
);
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// RX State Machine
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Filters bytes from spi_rx_*, forwarding data-phase bytes to rx_byte_*
|
||||
// (axis_width_conv_rx) per the mode table below:
|
||||
//
|
||||
// RAW write : Discard all bytes until tlast.
|
||||
// RAW read : Forward all bytes until tlast.
|
||||
// SI_SEQ/REP : For each group of 3, discard addr_hi, discard addr_lo,
|
||||
// forward data byte; repeat until tlast.
|
||||
// STREAMING : Discard addr_hi, discard addr_lo, then forward all bytes
|
||||
// until tlast.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
typedef enum logic [3:0] {
|
||||
ST_RX_IDLE = 0,
|
||||
ST_RX_RAW_DROP = 1,
|
||||
ST_RX_SI_DISCARD_HI = 2,
|
||||
ST_RX_SI_DISCARD_LO = 3,
|
||||
ST_RX_SI_DATA = 4,
|
||||
ST_RX_STREAM_DISCARD_HI = 5,
|
||||
ST_RX_STREAM_DISCARD_LO = 6,
|
||||
ST_RX_PASS_DATA = 7
|
||||
} rx_state_t;
|
||||
|
||||
rx_state_t rx_state;
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
rx_state <= ST_RX_IDLE;
|
||||
end else begin
|
||||
case (rx_state)
|
||||
ST_RX_IDLE : begin
|
||||
if (ctrl_rx_tvalid) begin
|
||||
case (ctrl_rx_tdata.mode)
|
||||
MODE_RAW : begin
|
||||
rx_state <= ctrl_rx_tdata.dir == SPI_READ ?
|
||||
ST_RX_PASS_DATA : ST_RX_RAW_DROP;
|
||||
end
|
||||
MODE_SI_SEQ,
|
||||
MODE_SI_REP : begin
|
||||
rx_state <= ctrl_rx_tdata.dir == SPI_READ ?
|
||||
ST_RX_SI_DISCARD_HI : ST_RX_RAW_DROP;
|
||||
end
|
||||
MODE_STREAMING : begin
|
||||
rx_state <= ctrl_rx_tdata.dir == SPI_READ ?
|
||||
ST_RX_STREAM_DISCARD_HI : ST_RX_RAW_DROP;
|
||||
end
|
||||
default : rx_state <= ST_RX_RAW_DROP;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
ST_RX_PASS_DATA : begin
|
||||
if (spi_rx_tvalid && spi_rx_tready && spi_rx_tlast) begin
|
||||
rx_state <= ST_RX_IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
ST_RX_RAW_DROP : begin
|
||||
if (spi_rx_tvalid && spi_rx_tready && spi_rx_tlast) begin
|
||||
rx_state <= ST_RX_IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
ST_RX_SI_DISCARD_HI : begin
|
||||
if (spi_rx_tvalid && spi_rx_tready) begin
|
||||
rx_state <= ST_RX_SI_DISCARD_LO;
|
||||
end
|
||||
end
|
||||
|
||||
ST_RX_SI_DISCARD_LO : begin
|
||||
if (spi_rx_tvalid && spi_rx_tready) begin
|
||||
rx_state <= ST_RX_SI_DATA;
|
||||
end
|
||||
end
|
||||
|
||||
ST_RX_SI_DATA : begin
|
||||
if (spi_rx_tvalid && spi_rx_tready) begin
|
||||
if (spi_rx_tlast) begin
|
||||
rx_state <= ST_RX_IDLE;
|
||||
end else begin
|
||||
rx_state <= ST_RX_SI_DISCARD_HI;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ST_RX_STREAM_DISCARD_HI : begin
|
||||
if (spi_rx_tvalid && spi_rx_tready) begin
|
||||
rx_state <= ST_RX_STREAM_DISCARD_LO;
|
||||
end
|
||||
end
|
||||
|
||||
ST_RX_STREAM_DISCARD_LO : begin
|
||||
if (spi_rx_tvalid && spi_rx_tready) begin
|
||||
rx_state <= ST_RX_PASS_DATA;
|
||||
end
|
||||
end
|
||||
|
||||
default : begin
|
||||
rx_state <= ST_RX_IDLE;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
// Default: consume from spi_rx but don't forward (discard).
|
||||
spi_rx_tready = 1'b0;
|
||||
rx_byte_tdata = spi_rx_tdata;
|
||||
rx_byte_tvalid = 1'b0;
|
||||
rx_byte_tlast = spi_rx_tlast;
|
||||
ctrl_rx_tready = 1'b0;
|
||||
|
||||
case (rx_state)
|
||||
ST_RX_IDLE : begin
|
||||
// Waiting; hold spi_rx backpressured.
|
||||
end
|
||||
|
||||
ST_RX_PASS_DATA,
|
||||
ST_RX_SI_DATA : begin
|
||||
// Forward bytes to the width converter.
|
||||
spi_rx_tready = rx_byte_tready;
|
||||
rx_byte_tvalid = spi_rx_tvalid;
|
||||
ctrl_rx_tready = spi_rx_tvalid && spi_rx_tready && spi_rx_tlast;
|
||||
end
|
||||
|
||||
ST_RX_RAW_DROP : begin
|
||||
// Consume and discard; do not forward.
|
||||
spi_rx_tready = 1'b1;
|
||||
ctrl_rx_tready = spi_rx_tvalid && spi_rx_tready && spi_rx_tlast;
|
||||
end
|
||||
|
||||
ST_RX_SI_DISCARD_HI,
|
||||
ST_RX_SI_DISCARD_LO,
|
||||
ST_RX_STREAM_DISCARD_HI,
|
||||
ST_RX_STREAM_DISCARD_LO : begin
|
||||
// Consume and discard; do not forward.
|
||||
spi_rx_tready = 1'b1;
|
||||
end
|
||||
|
||||
default : begin
|
||||
// Discard.
|
||||
spi_rx_tready = 1'b1;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// RX Width Converter (8-bit -> 32-bit)
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
axis_width_conv #(
|
||||
.WORD_W (8),
|
||||
.IN_WORDS (1),
|
||||
.OUT_WORDS (4),
|
||||
.SYNC_CLKS (1),
|
||||
.PIPELINE ("NONE")
|
||||
) axis_width_conv_rx (
|
||||
.s_axis_aclk (clk),
|
||||
.s_axis_rst (rst),
|
||||
.s_axis_tdata (rx_byte_tdata),
|
||||
.s_axis_tkeep (1'b1),
|
||||
.s_axis_tlast (rx_byte_tlast),
|
||||
.s_axis_tvalid (rx_byte_tvalid),
|
||||
.s_axis_tready (rx_byte_tready),
|
||||
.m_axis_aclk (clk),
|
||||
.m_axis_rst (rst),
|
||||
.m_axis_tdata (rx_word_tdata),
|
||||
.m_axis_tkeep (),
|
||||
.m_axis_tlast (rx_word_tlast),
|
||||
.m_axis_tvalid (rx_word_tvalid),
|
||||
.m_axis_tready (rx_word_tready)
|
||||
);
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// RX FIFO
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
axi_fifo #(
|
||||
.WIDTH (32),
|
||||
.SIZE (RX_FIFO_SIZE)
|
||||
) axi_fifo_i (
|
||||
.clk (clk),
|
||||
.reset (rst),
|
||||
.clear (1'b0),
|
||||
.i_tdata (rx_word_tdata),
|
||||
.i_tvalid (rx_word_tvalid),
|
||||
.i_tready (rx_word_tready),
|
||||
.o_tdata (rx_fifo_tdata),
|
||||
.o_tvalid (rx_fifo_tvalid),
|
||||
.o_tready (rx_fifo_tready),
|
||||
.space (),
|
||||
.occupied ()
|
||||
);
|
||||
|
||||
endmodule : ctrlport_spi_adrv
|
||||
|
||||
|
||||
`default_nettype wire
|
||||
|
||||
72
fpga/usrp3/lib/control/ctrlport_spi_adrv_pkg.sv
Normal file
72
fpga/usrp3/lib/control/ctrlport_spi_adrv_pkg.sv
Normal file
@ -0,0 +1,72 @@
|
||||
//
|
||||
// Copyright 2026 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Package: ctrlport_spi_adrv_pkg
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// Shared constants and types for ctrlport_spi_adrv and its testbench.
|
||||
//
|
||||
//
|
||||
|
||||
package ctrlport_spi_adrv_pkg;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Register offsets (byte addresses relative to BASE_ADDRESS)
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
localparam int REG_HALF_PER = 'h00;
|
||||
localparam int REG_SI = 'h04;
|
||||
localparam int REG_CONTROL = 'h3C;
|
||||
localparam int REG_DATA = 'h40;
|
||||
|
||||
// Actual address space required for this block.
|
||||
localparam int REG_ADDR_W = 7;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// CONTROL register field widths
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
localparam int SPI_MODE_W = 2;
|
||||
localparam int SPI_NUM_BYTES_W = 12;
|
||||
localparam int SPI_ADDR_W = 16;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// SPI direction encoding
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
typedef enum logic {
|
||||
SPI_WRITE = 1'b0,
|
||||
SPI_READ = 1'b1
|
||||
} spi_adrv_dir_t;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// SPI mode encoding
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
typedef enum logic [SPI_MODE_W-1:0] {
|
||||
MODE_RAW = 0, // Raw byte stream
|
||||
MODE_SI_SEQ = 1, // Single Instruction, sequential addresses
|
||||
MODE_SI_REP = 2, // Single Instruction, same address repeated
|
||||
MODE_STREAMING = 3 // Send address once, followed by all data bytes back-to-back
|
||||
} spi_adrv_mode_t;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// CONTROL register
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
typedef struct packed {
|
||||
spi_adrv_dir_t dir; // [31]
|
||||
logic rsvd1; // [30]
|
||||
spi_adrv_mode_t mode; // [29:28]
|
||||
logic [SPI_NUM_BYTES_W-1:0] num_bytes; // [27:16]
|
||||
logic [ SPI_ADDR_W-1:0] addr; // [15:0]
|
||||
} spi_adrv_ctrl_t;
|
||||
|
||||
endpackage : ctrlport_spi_adrv_pkg
|
||||
150
fpga/usrp3/lib/control/ctrlport_to_axis.sv
Normal file
150
fpga/usrp3/lib/control/ctrlport_to_axis.sv
Normal file
@ -0,0 +1,150 @@
|
||||
//
|
||||
// Copyright 2026 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: ctrlport_to_axis
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// Converts a CtrlPort write interface into an AXI-Stream bus. The module
|
||||
// contains an internal FIFO and will not acknowledge the write until the
|
||||
// FIFO has emptied enough to guarantee that another write can be accepted.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// WIDTH : Width of the data path in bits.
|
||||
// FIFO_SIZE_LOG2 : Log base 2 of the internal FIFO size.
|
||||
//
|
||||
// Signals:
|
||||
//
|
||||
// clk : Clock
|
||||
// rst : Active-high synchronous reset
|
||||
// ctrl_data : CtrlPort data word
|
||||
// ctrl_wr : CtrlPort write request strobe (single-cycle pulse)
|
||||
// ctrl_ack : CtrlPort acknowledgement strobe (single-cycle pulse)
|
||||
// m_axis_tdata : AXI-Stream data output
|
||||
// m_axis_tvalid : AXI-Stream valid
|
||||
// m_axis_tready : AXI-Stream ready (from downstream)
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
|
||||
module ctrlport_to_axis #(
|
||||
parameter int WIDTH = 32,
|
||||
parameter int FIFO_SIZE_LOG2 = 1
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
// CtrlPort write interface
|
||||
input wire [WIDTH-1:0] ctrl_data,
|
||||
input wire ctrl_wr,
|
||||
output logic ctrl_ack,
|
||||
|
||||
// AXI-Stream master
|
||||
output wire [WIDTH-1:0] m_axis_tdata,
|
||||
output wire m_axis_tvalid,
|
||||
input wire m_axis_tready
|
||||
);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Declarations
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
typedef enum logic { ST_IDLE, ST_FULL } state_t;
|
||||
state_t state;
|
||||
|
||||
logic fifo_i_tready;
|
||||
logic [15:0] fifo_space;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// State Machine
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state <= ST_IDLE;
|
||||
ctrl_ack <= 1'b0;
|
||||
end else begin
|
||||
// Default assignments
|
||||
ctrl_ack <= 1'b0;
|
||||
|
||||
unique case (state)
|
||||
ST_IDLE: begin
|
||||
if (ctrl_wr) begin
|
||||
if (fifo_space > 1 && FIFO_SIZE_LOG2 > 0) begin
|
||||
// If there's 2 or more spaces in the FIFO, acknowledge
|
||||
// immediately. If FIFO_SIZE_LOG2 is 0 (depth of 1), then the
|
||||
// space can never be greater than 1.
|
||||
ctrl_ack <= 1'b1;
|
||||
end else begin
|
||||
// If there's only one space, this write fills the FIFO, and we
|
||||
// can't acknowledge until we have enough room to accept the next
|
||||
// write.
|
||||
state <= ST_FULL;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ST_FULL: begin
|
||||
// Acknowledge the write as soon as space frees up in the FIFO.
|
||||
if (fifo_space > 0) begin
|
||||
ctrl_ack <= 1'b1;
|
||||
state <= ST_IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
default: begin
|
||||
state <= ST_IDLE;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Output FIFO
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
axi_fifo #(
|
||||
.WIDTH (WIDTH),
|
||||
.SIZE (FIFO_SIZE_LOG2)
|
||||
) data_fifo_i (
|
||||
.clk (clk),
|
||||
.reset (rst),
|
||||
.clear (1'b0),
|
||||
.i_tdata (ctrl_data),
|
||||
.i_tvalid (ctrl_wr),
|
||||
.i_tready (fifo_i_tready),
|
||||
.o_tdata (m_axis_tdata),
|
||||
.o_tvalid (m_axis_tvalid),
|
||||
.o_tready (m_axis_tready),
|
||||
.space (fifo_space),
|
||||
.occupied ()
|
||||
);
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Assertions
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
if (FIFO_SIZE_LOG2 < 0) begin : gen_fifo_size_check
|
||||
$error("FIFO_SIZE_LOG2 must be >= 0");
|
||||
end
|
||||
|
||||
//synthesis translate_off
|
||||
always_ff @(posedge clk) begin
|
||||
if (ctrl_wr && !fifo_i_tready) begin
|
||||
$error("ctrlport_to_axis: Write strobe occurred when the FIFO was not ready!");
|
||||
end
|
||||
end
|
||||
//synthesis translate_on
|
||||
|
||||
|
||||
endmodule : ctrlport_to_axis
|
||||
|
||||
|
||||
`default_nettype wire
|
||||
@ -49,8 +49,8 @@ module ctrlport_to_jtag #(
|
||||
output wire tms
|
||||
);
|
||||
|
||||
`include "../../../../lib/rfnoc/core/ctrlport.vh"
|
||||
`include "../regmap/jtag_regmap_utils.vh"
|
||||
`include "../rfnoc/core/ctrlport.vh"
|
||||
`include "jtag_regmap_utils.vh"
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Local Registers
|
||||
371
fpga/usrp3/lib/control/ctrlport_to_simple_spi.sv
Normal file
371
fpga/usrp3/lib/control/ctrlport_to_simple_spi.sv
Normal file
@ -0,0 +1,371 @@
|
||||
//
|
||||
// Copyright 2025 Ettus Research, A National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: ctrlport_to_simple_spi
|
||||
//
|
||||
// Description:
|
||||
// Implements a simple SPI core controlled via CtrlPort. This design
|
||||
// is an adaptation of the simple_spi_core module.
|
||||
//
|
||||
// Settings reg map:
|
||||
//
|
||||
// BASE_ADDRESS+0 divider setting
|
||||
// bits [15:0] spi clock divider
|
||||
//
|
||||
// BASE_ADDRESS+4 configuration input
|
||||
// bits [23:0] slave select, bit0 = slave0 enabled
|
||||
// bits [29:24] num bits (1 through 32)
|
||||
// bit [30] data input edge = in data bit latched on rising edge of clock
|
||||
// bit [31] data output edge = out data bit latched on rising edge of clock
|
||||
//
|
||||
// BASE_ADDRESS+8 output data
|
||||
// Writing this register begins a spi transaction.
|
||||
// Bits are latched out starting from bit 31.
|
||||
// Therefore, data has to be MSB aligned for transmission
|
||||
//
|
||||
// BASE_ADDRESS+12 Status
|
||||
// bit [0] ready signal, high when spi core can begin another transaction
|
||||
//
|
||||
// BASE_ADDRESS+16 Readback
|
||||
// Readback data from the last SPI transaction
|
||||
//
|
||||
|
||||
module ctrlport_to_simple_spi # (
|
||||
//settings register base address
|
||||
int BASE_ADDRESS = 0,
|
||||
|
||||
//width of serial enables (up to 24 is possible)
|
||||
int SEN_WIDTH = 8,
|
||||
|
||||
//idle state of the spi clock
|
||||
bit CLK_IDLE = 0,
|
||||
|
||||
//idle state of the serial enables
|
||||
logic [23:0] SEN_IDLE = '1
|
||||
)
|
||||
(
|
||||
input wire ctrlport_clk,
|
||||
input wire ctrlport_rst,
|
||||
|
||||
// Request
|
||||
input wire s_ctrlport_req_wr,
|
||||
input wire s_ctrlport_req_rd,
|
||||
input wire [19:0] s_ctrlport_req_addr,
|
||||
input wire [31:0] s_ctrlport_req_data,
|
||||
|
||||
// Response
|
||||
output logic s_ctrlport_resp_ack = 1'b0,
|
||||
output logic [ 1:0] s_ctrlport_resp_status = 2'b0,
|
||||
output logic [31:0] s_ctrlport_resp_data = 32'b0,
|
||||
|
||||
//spi interface, slave selects, clock, data in, data out
|
||||
output logic [SEN_WIDTH-1:0] sen,
|
||||
output logic sclk,
|
||||
output logic mosi,
|
||||
input wire miso,
|
||||
|
||||
//optional debug output
|
||||
output logic [31:0] debug
|
||||
);
|
||||
|
||||
`include "../rfnoc/core/ctrlport.vh"
|
||||
`include "ctrlport_to_simple_spi_regmap_utils.vh"
|
||||
|
||||
wire address_in_range =
|
||||
(s_ctrlport_req_addr >= BASE_ADDRESS) &&
|
||||
(s_ctrlport_req_addr < BASE_ADDRESS + 'h14); // 5 registers (20 bytes)
|
||||
|
||||
logic [15:0] sclk_divider;
|
||||
logic [23:0] slave_select;
|
||||
logic [5:0] num_bits;
|
||||
logic datain_edge, dataout_edge;
|
||||
logic [31:0] mosi_data;
|
||||
logic trigger_spi;
|
||||
logic [31:0] datain_reg;
|
||||
logic [23:0] readback_reg;
|
||||
logic readback_stb;
|
||||
logic ready;
|
||||
logic force_cs;
|
||||
|
||||
always_ff @(posedge ctrlport_clk) begin
|
||||
if (ctrlport_rst) begin
|
||||
s_ctrlport_resp_ack <= '0;
|
||||
s_ctrlport_resp_data <= 'x;
|
||||
s_ctrlport_resp_status <= '0;
|
||||
trigger_spi <= 1'b0;
|
||||
end else begin
|
||||
// Reset response
|
||||
s_ctrlport_resp_ack <= '0;
|
||||
s_ctrlport_resp_data <= 'x;
|
||||
s_ctrlport_resp_status <= '0;
|
||||
trigger_spi <= 1'b0;
|
||||
|
||||
// Write request
|
||||
if (s_ctrlport_req_wr) begin
|
||||
s_ctrlport_resp_ack <= '1;
|
||||
|
||||
case (s_ctrlport_req_addr)
|
||||
|
||||
BASE_ADDRESS + SCLK_DIVIDER: begin
|
||||
sclk_divider <= s_ctrlport_req_data[SPI_CLK_DIV_MSB : SPI_CLK_DIV];
|
||||
force_cs <= s_ctrlport_req_data[FORCE_SS];
|
||||
end
|
||||
|
||||
BASE_ADDRESS + SPI_PERSONALITY: begin
|
||||
dataout_edge <= s_ctrlport_req_data[SPI_MOSI_EDGE];
|
||||
datain_edge <= s_ctrlport_req_data[SPI_MISO_EDGE];
|
||||
num_bits <= s_ctrlport_req_data[SPI_NUM_BITS_MSB : SPI_NUM_BITS];
|
||||
slave_select <= s_ctrlport_req_data[SPI_SLAVE_SELECT_MSB : SPI_SLAVE_SELECT];
|
||||
end
|
||||
|
||||
BASE_ADDRESS + SPI_MOSI_DATA: begin
|
||||
mosi_data <= s_ctrlport_req_data[SPI_DATA_OUT_MSB : SPI_DATA_OUT];
|
||||
trigger_spi <= 1'b1; // trigger the SPI transaction
|
||||
end
|
||||
|
||||
// No register implementation for provided address
|
||||
default: begin
|
||||
// Acknowledge and provide error status if address is in range
|
||||
if (address_in_range) begin
|
||||
s_ctrlport_resp_status <= CTRL_STS_CMDERR;
|
||||
|
||||
// No response if out of range
|
||||
end else begin
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
endcase
|
||||
|
||||
end else if (s_ctrlport_req_rd) begin
|
||||
// read request handling
|
||||
case (s_ctrlport_req_addr)
|
||||
BASE_ADDRESS + SCLK_DIVIDER: begin
|
||||
// Divider setting readback
|
||||
s_ctrlport_resp_data[SPI_CLK_DIV_MSB : SPI_CLK_DIV] <= sclk_divider[SPI_CLK_DIV_SIZE-1 : 0];
|
||||
s_ctrlport_resp_data[FORCE_SS] <= force_cs;
|
||||
s_ctrlport_resp_ack <= 1'b1;
|
||||
end
|
||||
|
||||
BASE_ADDRESS + SPI_PERSONALITY: begin
|
||||
// Control register readback
|
||||
s_ctrlport_resp_data[SPI_MOSI_EDGE] <= dataout_edge;
|
||||
s_ctrlport_resp_data[SPI_MISO_EDGE] <= datain_edge;
|
||||
s_ctrlport_resp_data[SPI_NUM_BITS_MSB : SPI_NUM_BITS] <= num_bits;
|
||||
s_ctrlport_resp_data[SPI_SLAVE_SELECT_MSB : SPI_SLAVE_SELECT] <= slave_select;
|
||||
s_ctrlport_resp_ack <= 1'b1;
|
||||
end
|
||||
|
||||
BASE_ADDRESS + SPI_STATUS: begin
|
||||
// Data register readback
|
||||
s_ctrlport_resp_data[SPI_READY] <= ready;
|
||||
s_ctrlport_resp_ack <= 1'b1;
|
||||
end
|
||||
|
||||
BASE_ADDRESS + SPI_READBACK: begin
|
||||
// Readback data from the last SPI transaction
|
||||
s_ctrlport_resp_data[SPI_READBACK_DATA_MSB : SPI_READBACK_DATA] <= readback_reg;
|
||||
s_ctrlport_resp_ack <= 1'b1;
|
||||
end
|
||||
|
||||
// No register implementation for provided address
|
||||
default: begin
|
||||
// Acknowledge and provide error status if address is in range
|
||||
if (address_in_range) begin
|
||||
s_ctrlport_resp_status <= CTRL_STS_CMDERR;
|
||||
|
||||
// No response if out of range
|
||||
end else begin
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
endcase
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Latch readback signal
|
||||
always_ff @(posedge ctrlport_clk) begin
|
||||
if (ctrlport_rst) begin
|
||||
readback_reg <= '0;
|
||||
end else if(readback_stb) begin
|
||||
readback_reg <= datain_reg[23:0]; // readback data is latched from the last SPI transaction
|
||||
end
|
||||
end
|
||||
|
||||
// SPI Engine Implementation
|
||||
// The implementation below is leveraged from the simple_spi_core module.
|
||||
|
||||
typedef enum logic [2:0] {
|
||||
WAIT_TRIG,
|
||||
PRE_IDLE,
|
||||
CLK_REG,
|
||||
CLK_INV,
|
||||
POST_IDLE,
|
||||
IDLE_SEN
|
||||
} case_t;
|
||||
|
||||
case_t spi_state;
|
||||
|
||||
logic ready_reg;
|
||||
|
||||
// De-assert ready when a new transaction is triggered. ready_reg will be deasserted
|
||||
// on the clock cycle after the trigger, and keep the ready signal low until the
|
||||
// transaction is done.
|
||||
assign ready = ready_reg && ~trigger_spi;
|
||||
|
||||
// Serial clock either idles or is in one of two clock states
|
||||
logic sclk_reg;
|
||||
assign sclk = sclk_reg;
|
||||
|
||||
// Serial enables either idle or enabled (based on spi_state or forced
|
||||
// via FORCE_SS register field).
|
||||
// One pipeline stage to break critical path from register in I/O pads.
|
||||
wire sen_is_idle = ((spi_state == WAIT_TRIG) ||
|
||||
(spi_state == IDLE_SEN)) &&
|
||||
~force_cs;
|
||||
|
||||
wire [23:0] sen24 = (sen_is_idle)? SEN_IDLE : (SEN_IDLE ^ slave_select);
|
||||
logic [SEN_WIDTH-1:0] sen_reg = SEN_IDLE;
|
||||
|
||||
always_ff @(posedge ctrlport_clk) begin
|
||||
if (ctrlport_rst) begin
|
||||
sen_reg <= SEN_IDLE;
|
||||
end else begin
|
||||
sen_reg <= sen24[SEN_WIDTH-1:0];
|
||||
end
|
||||
end
|
||||
|
||||
assign sen = sen_reg;
|
||||
|
||||
// Data output shift register
|
||||
// One pipeline stage to break critical path from register in I/O pads.
|
||||
logic [31:0] dataout_reg;
|
||||
wire [31:0] dataout_next = {dataout_reg[30:0], 1'b0};
|
||||
|
||||
always_ff @(posedge ctrlport_clk) begin
|
||||
mosi <= dataout_reg[31];
|
||||
end
|
||||
|
||||
// Data input shift register
|
||||
// Two pipeline stages to break critical path from register in I/O pads.
|
||||
logic miso_pipe, miso_pipe2;
|
||||
always_ff @(posedge ctrlport_clk) begin
|
||||
miso_pipe2 <= miso;
|
||||
miso_pipe <= miso_pipe2;
|
||||
end
|
||||
|
||||
wire [31:0] datain_next = {datain_reg[30:0], miso_pipe};
|
||||
|
||||
// Counter for spi clock
|
||||
logic [15:0] sclk_counter;
|
||||
wire sclk_counter_done = (sclk_counter == sclk_divider);
|
||||
wire [15:0] sclk_counter_next = (sclk_counter_done)? 0 : sclk_counter + 1;
|
||||
|
||||
// Counter for latching bits miso/mosi
|
||||
logic [6:0] bit_counter;
|
||||
wire [6:0] bit_counter_next = bit_counter + 1;
|
||||
wire bit_counter_done = (bit_counter_next == num_bits);
|
||||
|
||||
always_ff @(posedge ctrlport_clk) begin
|
||||
if (ctrlport_rst) begin
|
||||
spi_state <= WAIT_TRIG;
|
||||
sclk_reg <= CLK_IDLE;
|
||||
ready_reg <= 0;
|
||||
readback_stb <= 1'b0;
|
||||
end
|
||||
else begin
|
||||
case (spi_state)
|
||||
|
||||
WAIT_TRIG: begin
|
||||
if (trigger_spi) begin
|
||||
spi_state <= PRE_IDLE;
|
||||
end
|
||||
readback_stb <= 1'b0;
|
||||
ready_reg <= ~trigger_spi;
|
||||
dataout_reg <= mosi_data;
|
||||
sclk_counter <= 0;
|
||||
bit_counter <= 0;
|
||||
sclk_reg <= CLK_IDLE;
|
||||
end
|
||||
|
||||
// Drive clock to idle state. This also drives the serial enables
|
||||
// to their active state.
|
||||
PRE_IDLE: begin
|
||||
if (sclk_counter_done) begin
|
||||
spi_state <= CLK_REG;
|
||||
end
|
||||
sclk_counter <= sclk_counter_next;
|
||||
sclk_reg <= CLK_IDLE;
|
||||
end
|
||||
|
||||
// Clock generation states
|
||||
CLK_REG: begin
|
||||
if (sclk_counter_done) begin
|
||||
spi_state <= CLK_INV;
|
||||
if (datain_edge != CLK_IDLE) begin
|
||||
datain_reg <= datain_next;
|
||||
end
|
||||
if (dataout_edge != CLK_IDLE && bit_counter != 0) begin
|
||||
dataout_reg <= dataout_next;
|
||||
end
|
||||
sclk_reg <= ~CLK_IDLE; //transition to rising when CLK_IDLE == 0
|
||||
end
|
||||
sclk_counter <= sclk_counter_next;
|
||||
end
|
||||
|
||||
CLK_INV: begin
|
||||
if (sclk_counter_done) begin
|
||||
// Have all bits been shifted out?
|
||||
spi_state <= (bit_counter_done)? POST_IDLE : CLK_REG;
|
||||
bit_counter <= bit_counter_next;
|
||||
if (datain_edge == CLK_IDLE) begin
|
||||
datain_reg <= datain_next;
|
||||
end
|
||||
if (dataout_edge == CLK_IDLE && ~bit_counter_done) begin
|
||||
dataout_reg <= dataout_next;
|
||||
end
|
||||
sclk_reg <= CLK_IDLE; //transition to falling when CLK_IDLE == 0
|
||||
end
|
||||
sclk_counter <= sclk_counter_next;
|
||||
end
|
||||
|
||||
// Drive clock to idle state
|
||||
POST_IDLE: begin
|
||||
if (sclk_counter_done) begin
|
||||
spi_state <= IDLE_SEN;
|
||||
end
|
||||
sclk_counter <= sclk_counter_next;
|
||||
sclk_reg <= CLK_IDLE;
|
||||
end
|
||||
|
||||
// Drive serial enables to idle state
|
||||
IDLE_SEN: begin
|
||||
if (sclk_counter_done) begin
|
||||
ready_reg <= 1'b1;
|
||||
readback_stb <= 1'b1;
|
||||
spi_state <= WAIT_TRIG;
|
||||
end
|
||||
sclk_counter <= sclk_counter_next;
|
||||
sclk_reg <= CLK_IDLE;
|
||||
end
|
||||
|
||||
default: spi_state <= WAIT_TRIG;
|
||||
|
||||
endcase //spi_state
|
||||
end
|
||||
end
|
||||
|
||||
assign debug = {
|
||||
8'h00, //8
|
||||
trigger_spi, spi_state, //4
|
||||
sclk, mosi, miso, ready, //4
|
||||
1'b0, bit_counter[6:0], //8
|
||||
sclk_counter_done, bit_counter_done, //2
|
||||
sclk_counter[5:0] //6
|
||||
};
|
||||
|
||||
endmodule : ctrlport_to_simple_spi
|
||||
@ -0,0 +1,86 @@
|
||||
//
|
||||
// Copyright 2026 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: ctrlport_to_simple_spi_regmap_utils.vh
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// The constants in this file are autogenerated by XmlParse.
|
||||
//
|
||||
|
||||
//===============================================================================
|
||||
// A numerically ordered list of registers and their HDL source files
|
||||
//===============================================================================
|
||||
|
||||
// SCLK_DIVIDER : 0x0 (b310_core.sv)
|
||||
// SPI_PERSONALITY : 0x4 (b310_core.sv)
|
||||
// SPI_MOSI_DATA : 0x8 (b310_core.sv)
|
||||
// SPI_STATUS : 0xC (b310_core.sv)
|
||||
// SPI_READBACK : 0x10 (b310_core.sv)
|
||||
|
||||
//===============================================================================
|
||||
// RegTypes
|
||||
//===============================================================================
|
||||
|
||||
//===============================================================================
|
||||
// Register Group CTRLPORT_TO_SIMPLE_SPI_REGMAP
|
||||
//===============================================================================
|
||||
|
||||
// SCLK_DIVIDER Register (from b310_core.sv)
|
||||
localparam SCLK_DIVIDER = 'h0; // Register Offset
|
||||
localparam SCLK_DIVIDER_SIZE = 32; // register width in bits
|
||||
localparam SCLK_DIVIDER_MASK = 32'h1FFFF;
|
||||
localparam SPI_CLK_DIV_SIZE = 16; //SCLK_DIVIDER:SPI_CLK_DIV
|
||||
localparam SPI_CLK_DIV_MSB = 15; //SCLK_DIVIDER:SPI_CLK_DIV
|
||||
localparam SPI_CLK_DIV = 0; //SCLK_DIVIDER:SPI_CLK_DIV
|
||||
localparam FORCE_SS_SIZE = 1; //SCLK_DIVIDER:FORCE_SS
|
||||
localparam FORCE_SS_MSB = 16; //SCLK_DIVIDER:FORCE_SS
|
||||
localparam FORCE_SS = 16; //SCLK_DIVIDER:FORCE_SS
|
||||
|
||||
// SPI_PERSONALITY Register (from b310_core.sv)
|
||||
localparam SPI_PERSONALITY = 'h4; // Register Offset
|
||||
localparam SPI_PERSONALITY_SIZE = 32; // register width in bits
|
||||
localparam SPI_PERSONALITY_MASK = 32'hFFFFFFFF;
|
||||
localparam SPI_SLAVE_SELECT_SIZE = 24; //SPI_PERSONALITY:SPI_SLAVE_SELECT
|
||||
localparam SPI_SLAVE_SELECT_MSB = 23; //SPI_PERSONALITY:SPI_SLAVE_SELECT
|
||||
localparam SPI_SLAVE_SELECT = 0; //SPI_PERSONALITY:SPI_SLAVE_SELECT
|
||||
localparam SPI_NUM_BITS_SIZE = 6; //SPI_PERSONALITY:SPI_NUM_BITS
|
||||
localparam SPI_NUM_BITS_MSB = 29; //SPI_PERSONALITY:SPI_NUM_BITS
|
||||
localparam SPI_NUM_BITS = 24; //SPI_PERSONALITY:SPI_NUM_BITS
|
||||
localparam SPI_MISO_EDGE_SIZE = 1; //SPI_PERSONALITY:SPI_MISO_EDGE
|
||||
localparam SPI_MISO_EDGE_MSB = 30; //SPI_PERSONALITY:SPI_MISO_EDGE
|
||||
localparam SPI_MISO_EDGE = 30; //SPI_PERSONALITY:SPI_MISO_EDGE
|
||||
localparam SPI_MOSI_EDGE_SIZE = 1; //SPI_PERSONALITY:SPI_MOSI_EDGE
|
||||
localparam SPI_MOSI_EDGE_MSB = 31; //SPI_PERSONALITY:SPI_MOSI_EDGE
|
||||
localparam SPI_MOSI_EDGE = 31; //SPI_PERSONALITY:SPI_MOSI_EDGE
|
||||
|
||||
// SPI_MOSI_DATA Register (from b310_core.sv)
|
||||
localparam SPI_MOSI_DATA = 'h8; // Register Offset
|
||||
localparam SPI_MOSI_DATA_SIZE = 32; // register width in bits
|
||||
localparam SPI_MOSI_DATA_MASK = 32'hFFFFFFFF;
|
||||
localparam SPI_DATA_OUT_SIZE = 32; //SPI_MOSI_DATA:SPI_DATA_OUT
|
||||
localparam SPI_DATA_OUT_MSB = 31; //SPI_MOSI_DATA:SPI_DATA_OUT
|
||||
localparam SPI_DATA_OUT = 0; //SPI_MOSI_DATA:SPI_DATA_OUT
|
||||
|
||||
// SPI_STATUS Register (from b310_core.sv)
|
||||
localparam SPI_STATUS = 'hC; // Register Offset
|
||||
localparam SPI_STATUS_SIZE = 32; // register width in bits
|
||||
localparam SPI_STATUS_MASK = 32'h1;
|
||||
localparam SPI_READY_SIZE = 1; //SPI_STATUS:SPI_READY
|
||||
localparam SPI_READY_MSB = 0; //SPI_STATUS:SPI_READY
|
||||
localparam SPI_READY = 0; //SPI_STATUS:SPI_READY
|
||||
|
||||
// SPI_READBACK Register (from b310_core.sv)
|
||||
localparam SPI_READBACK = 'h10; // Register Offset
|
||||
localparam SPI_READBACK_SIZE = 32; // register width in bits
|
||||
localparam SPI_READBACK_MASK = 32'hFFFFFFFF;
|
||||
localparam SPI_READBACK_DATA_SIZE = 32; //SPI_READBACK:SPI_READBACK_DATA
|
||||
localparam SPI_READBACK_DATA_MSB = 31; //SPI_READBACK:SPI_READBACK_DATA
|
||||
localparam SPI_READBACK_DATA = 0; //SPI_READBACK:SPI_READBACK_DATA
|
||||
// function SCLK_DIVIDERRec not implemented for Verilog
|
||||
// function SPI_PERSONALITYRec not implemented for Verilog
|
||||
// function SPI_MOSI_DATARec not implemented for Verilog
|
||||
// function SPI_STATUSRec not implemented for Verilog
|
||||
// function SPI_READBACKRec not implemented for Verilog
|
||||
@ -11,14 +11,16 @@
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// BASE_ADDRESS : Base address for CtrlPort registers.
|
||||
// BASE_ADDRESS : Base address for CtrlPort registers.
|
||||
// REG_STRIDE_SIZE : Address separation between registers.
|
||||
//
|
||||
|
||||
`default_nettype wire
|
||||
|
||||
|
||||
module ctrlport_to_wb_i2c #(
|
||||
parameter BASE_ADDRESS = 0
|
||||
parameter BASE_ADDRESS = 0,
|
||||
parameter REG_STRIDE_SIZE = 1
|
||||
) (
|
||||
//---------------------------------------------------------------
|
||||
// ControlPort Slave
|
||||
@ -64,10 +66,11 @@ module ctrlport_to_wb_i2c #(
|
||||
wire wb_ack_o;
|
||||
wire [31:0] wb_dat_o;
|
||||
|
||||
// Check for address to be in range [base_addr..base_addr+8)
|
||||
localparam NUM_ADDRESSES = 8;
|
||||
// Check for address to be in range [base_addr..base_addr+8*stride)
|
||||
localparam REG_WINDOW_SIZE = 8*REG_STRIDE_SIZE;
|
||||
|
||||
wire address_in_range = (s_ctrlport_req_addr >= BASE_ADDRESS) &&
|
||||
(s_ctrlport_req_addr < BASE_ADDRESS + NUM_ADDRESSES);
|
||||
(s_ctrlport_req_addr < BASE_ADDRESS + REG_WINDOW_SIZE);
|
||||
|
||||
// Following chapter 3.2.3 (classic standard SINGLE WRITE cycle) of
|
||||
// https://cdn.opencores.org/downloads/wbspec_b4.pdf
|
||||
@ -100,59 +103,62 @@ module ctrlport_to_wb_i2c #(
|
||||
end else if (s_ctrlport_req_wr) begin
|
||||
// Assume there is a valid address
|
||||
//1-1 translation of wb to ctrl port interface
|
||||
if (s_ctrlport_req_addr < 8) begin
|
||||
if (address_in_range) begin
|
||||
wb_cyc_i <= 1'b1;
|
||||
wb_we_i <= 1'b1;
|
||||
wb_dat_i <= s_ctrlport_req_data;
|
||||
end
|
||||
case (s_ctrlport_req_addr)
|
||||
BASE_ADDRESS + WB_PRER_LO: begin
|
||||
wb_adr_i = WB_PRER_LO;
|
||||
BASE_ADDRESS + WB_PRER_LO * REG_STRIDE_SIZE: begin
|
||||
wb_adr_i <= WB_PRER_LO;
|
||||
end
|
||||
BASE_ADDRESS + WB_PRER_HI: begin
|
||||
wb_adr_i = WB_PRER_HI;
|
||||
BASE_ADDRESS + WB_PRER_HI * REG_STRIDE_SIZE: begin
|
||||
wb_adr_i <= WB_PRER_HI;
|
||||
end
|
||||
BASE_ADDRESS + WB_CTR: begin
|
||||
wb_adr_i = WB_CTR;
|
||||
BASE_ADDRESS + WB_CTR * REG_STRIDE_SIZE: begin
|
||||
wb_adr_i <= WB_CTR;
|
||||
end
|
||||
BASE_ADDRESS + WB_TXR: begin
|
||||
wb_adr_i = WB_TXR;
|
||||
BASE_ADDRESS + WB_TXR * REG_STRIDE_SIZE: begin
|
||||
wb_adr_i <= WB_TXR;
|
||||
end
|
||||
BASE_ADDRESS + WB_CR: begin
|
||||
wb_adr_i = WB_CR;
|
||||
BASE_ADDRESS + WB_CR * REG_STRIDE_SIZE: begin
|
||||
wb_adr_i <= WB_CR;
|
||||
end
|
||||
|
||||
endcase
|
||||
|
||||
// Read requests
|
||||
end else if (s_ctrlport_req_rd) begin
|
||||
// Assume there is a valid address
|
||||
wb_cyc_i <= 1'b1;
|
||||
wb_we_i <= 1'b0;
|
||||
|
||||
case (s_ctrlport_req_addr)
|
||||
BASE_ADDRESS + WB_PRER_LO: begin
|
||||
wb_adr_i <= WB_PRER_LO;
|
||||
end
|
||||
BASE_ADDRESS + WB_PRER_HI: begin
|
||||
wb_adr_i <= WB_PRER_HI;
|
||||
end
|
||||
BASE_ADDRESS + WB_CTR: begin
|
||||
wb_adr_i <= WB_CTR;
|
||||
end
|
||||
BASE_ADDRESS + WB_RXR: begin
|
||||
wb_adr_i = WB_RXR;
|
||||
end
|
||||
BASE_ADDRESS + WB_SR: begin
|
||||
wb_adr_i = WB_SR;
|
||||
end
|
||||
// Respond with 0
|
||||
default: begin
|
||||
s_ctrlport_resp_ack <= 1'b1;
|
||||
s_ctrlport_resp_data <= 'b0;
|
||||
end
|
||||
if(address_in_range) begin
|
||||
// Assume there is a valid address
|
||||
wb_cyc_i <= 1'b1;
|
||||
wb_we_i <= 1'b0;
|
||||
|
||||
endcase
|
||||
case (s_ctrlport_req_addr)
|
||||
BASE_ADDRESS + WB_PRER_LO * REG_STRIDE_SIZE: begin
|
||||
wb_adr_i <= WB_PRER_LO;
|
||||
end
|
||||
BASE_ADDRESS + WB_PRER_HI * REG_STRIDE_SIZE: begin
|
||||
wb_adr_i <= WB_PRER_HI;
|
||||
end
|
||||
BASE_ADDRESS + WB_CTR * REG_STRIDE_SIZE: begin
|
||||
wb_adr_i <= WB_CTR;
|
||||
end
|
||||
BASE_ADDRESS + WB_RXR * REG_STRIDE_SIZE: begin
|
||||
wb_adr_i <= WB_RXR;
|
||||
end
|
||||
BASE_ADDRESS + WB_SR * REG_STRIDE_SIZE: begin
|
||||
wb_adr_i <= WB_SR;
|
||||
end
|
||||
// Respond with 0
|
||||
default: begin
|
||||
s_ctrlport_resp_ack <= 1'b1;
|
||||
s_ctrlport_resp_data <= 'b0;
|
||||
end
|
||||
|
||||
endcase
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
191
fpga/usrp3/lib/control/ctrlport_to_wb_uart.v
Normal file
191
fpga/usrp3/lib/control/ctrlport_to_wb_uart.v
Normal file
@ -0,0 +1,191 @@
|
||||
//
|
||||
// Copyright 2025 Ettus Research, A National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: ctrlport_to_wb_uart
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// This module wraps a simple UART and provides a ControlPort interface.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// BASE_ADDRESS : Base address for CtrlPort registers.
|
||||
// REG_STRIDE_SIZE : Address separation between registers.
|
||||
//
|
||||
|
||||
`default_nettype wire
|
||||
|
||||
|
||||
module ctrlport_to_wb_uart #(
|
||||
parameter BASE_ADDRESS = 0,
|
||||
parameter REG_STRIDE_SIZE = 1,
|
||||
parameter CLKDIV_DEFAULT = 0,
|
||||
parameter RX_SIZE = 8
|
||||
) (
|
||||
//---------------------------------------------------------------
|
||||
// ControlPort Slave
|
||||
//---------------------------------------------------------------
|
||||
|
||||
input wire ctrlport_clk,
|
||||
input wire ctrlport_rst,
|
||||
|
||||
input wire s_ctrlport_req_wr,
|
||||
input wire s_ctrlport_req_rd,
|
||||
input wire [19:0] s_ctrlport_req_addr,
|
||||
input wire [31:0] s_ctrlport_req_data,
|
||||
|
||||
output reg s_ctrlport_resp_ack,
|
||||
output reg [ 1:0] s_ctrlport_resp_status = 0,
|
||||
output reg [31:0] s_ctrlport_resp_data = 0,
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// UART signals
|
||||
//---------------------------------------------------------------
|
||||
|
||||
input wire uart_rx,
|
||||
output wire uart_tx
|
||||
);
|
||||
|
||||
`include "../rfnoc/core/ctrlport.vh"
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Translating CtrlPort <-> Wishbone UART core
|
||||
//---------------------------------------------------------------
|
||||
|
||||
reg wb_cyc_i; // Active bus cycle
|
||||
reg wb_we_i = 1'b0; // Write access
|
||||
reg [ 2:0] wb_adr_i = 3'b0;
|
||||
reg [31:0] wb_dat_i = 32'b0;
|
||||
wire wb_ack_o;
|
||||
wire [31:0] wb_dat_o;
|
||||
|
||||
localparam SUART_CLKDIV = 0;
|
||||
localparam SUART_TXLEVEL = 1;
|
||||
localparam SUART_RXLEVEL = 2;
|
||||
localparam SUART_TXCHAR = 3;
|
||||
localparam SUART_RXCHAR = 4;
|
||||
|
||||
// Check for address to be in range [base_addr..base_addr+5*stride)
|
||||
localparam REG_WINDOW_SIZE = 5*REG_STRIDE_SIZE;
|
||||
|
||||
wire address_in_range = (s_ctrlport_req_addr >= BASE_ADDRESS) &&
|
||||
(s_ctrlport_req_addr < BASE_ADDRESS + REG_WINDOW_SIZE);
|
||||
|
||||
// Following chapter 3.2.3 (classic standard SINGLE WRITE cycle) of
|
||||
// https://cdn.opencores.org/downloads/wbspec_b4.pdf
|
||||
always @(posedge ctrlport_clk) begin
|
||||
// Reset internal registers and responses
|
||||
if (ctrlport_rst) begin
|
||||
s_ctrlport_resp_ack <= 0;
|
||||
s_ctrlport_resp_data <= 0;
|
||||
s_ctrlport_resp_status <= CTRL_STS_OKAY;
|
||||
wb_cyc_i <= 0;
|
||||
wb_we_i <= 0;
|
||||
wb_dat_i <= 0;
|
||||
wb_adr_i <= 0;
|
||||
end else begin
|
||||
// Request independent default assignments
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
|
||||
// Wait for ack on active bus transactions
|
||||
if (wb_cyc_i) begin
|
||||
if (wb_ack_o) begin
|
||||
// End bus cycle and generate response
|
||||
wb_cyc_i <= 1'b0;
|
||||
s_ctrlport_resp_ack <= 1'b1;
|
||||
s_ctrlport_resp_data <= wb_dat_o;
|
||||
//removed error condition since there is no error signal in UART
|
||||
//core
|
||||
s_ctrlport_resp_status <= CTRL_STS_OKAY;
|
||||
end
|
||||
|
||||
// Write requests
|
||||
end else if (s_ctrlport_req_wr) begin
|
||||
// Assume there is a valid address
|
||||
//1-1 translation of wb to ctrl port interface
|
||||
if (address_in_range) begin
|
||||
wb_cyc_i <= 1'b1;
|
||||
wb_we_i <= 1'b1;
|
||||
wb_dat_i <= s_ctrlport_req_data;
|
||||
end
|
||||
case (s_ctrlport_req_addr)
|
||||
BASE_ADDRESS + SUART_CLKDIV * REG_STRIDE_SIZE: begin
|
||||
wb_adr_i <= SUART_CLKDIV;
|
||||
end
|
||||
BASE_ADDRESS + SUART_TXLEVEL * REG_STRIDE_SIZE: begin
|
||||
wb_adr_i <= SUART_TXLEVEL;
|
||||
end
|
||||
BASE_ADDRESS + SUART_RXLEVEL * REG_STRIDE_SIZE: begin
|
||||
wb_adr_i <= SUART_RXLEVEL;
|
||||
end
|
||||
BASE_ADDRESS + SUART_TXCHAR * REG_STRIDE_SIZE: begin
|
||||
wb_adr_i <= SUART_TXCHAR;
|
||||
end
|
||||
BASE_ADDRESS + SUART_RXCHAR * REG_STRIDE_SIZE: begin
|
||||
wb_adr_i <= SUART_RXCHAR;
|
||||
end
|
||||
|
||||
endcase
|
||||
|
||||
// Read requests
|
||||
end else if (s_ctrlport_req_rd) begin
|
||||
|
||||
if(address_in_range) begin
|
||||
// Assume there is a valid address
|
||||
wb_cyc_i <= 1'b1;
|
||||
wb_we_i <= 1'b0;
|
||||
|
||||
case (s_ctrlport_req_addr)
|
||||
BASE_ADDRESS + SUART_CLKDIV * REG_STRIDE_SIZE: begin
|
||||
wb_adr_i <= SUART_CLKDIV;
|
||||
end
|
||||
BASE_ADDRESS + SUART_TXLEVEL * REG_STRIDE_SIZE: begin
|
||||
wb_adr_i <= SUART_TXLEVEL;
|
||||
end
|
||||
BASE_ADDRESS + SUART_RXLEVEL * REG_STRIDE_SIZE: begin
|
||||
wb_adr_i <= SUART_RXLEVEL;
|
||||
end
|
||||
BASE_ADDRESS + SUART_TXCHAR * REG_STRIDE_SIZE: begin
|
||||
wb_adr_i <= SUART_TXCHAR;
|
||||
end
|
||||
BASE_ADDRESS + SUART_RXCHAR * REG_STRIDE_SIZE: begin
|
||||
wb_adr_i <= SUART_RXCHAR;
|
||||
end
|
||||
// Respond with 0
|
||||
default: begin
|
||||
s_ctrlport_resp_ack <= 1'b1;
|
||||
s_ctrlport_resp_data <= 'b0;
|
||||
end
|
||||
|
||||
endcase
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
//wishbone-based UART core
|
||||
// Makin default baud
|
||||
simple_uart #(
|
||||
.CLKDIV_DEFAULT(CLKDIV_DEFAULT),
|
||||
.RX_SIZE(RX_SIZE)
|
||||
) simple_uart_i (
|
||||
.clk_i(ctrlport_clk),
|
||||
.rst_i(ctrlport_rst),
|
||||
.adr_i(wb_adr_i),
|
||||
.dat_i(wb_dat_i),
|
||||
.dat_o(wb_dat_o),
|
||||
.we_i(wb_we_i),
|
||||
.stb_i(wb_cyc_i),
|
||||
.cyc_i(wb_cyc_i),
|
||||
.ack_o(wb_ack_o),
|
||||
.rx_int_o(),
|
||||
.tx_int_o(),
|
||||
.tx_o(uart_tx),
|
||||
.rx_i(uart_rx),
|
||||
.baud_o()
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
@ -5,6 +5,11 @@
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
#
|
||||
|
||||
##################################################
|
||||
# HB Filter Includes
|
||||
##################################################
|
||||
include $(BASE_DIR)/../lib/dsp/hb_filter/Makefile.srcs
|
||||
|
||||
##################################################
|
||||
# DSP Sources
|
||||
##################################################
|
||||
@ -44,9 +49,26 @@ srl.v \
|
||||
tx_frontend.v \
|
||||
variable_delay_line.v \
|
||||
axi_fir_multisample_filter.sv \
|
||||
axis_fir_ms_preload.sv \
|
||||
complex_multiply.sv \
|
||||
complex_multiply_iq.sv \
|
||||
complex_multiply_stb.sv \
|
||||
timed_complex_gain_regs.svh \
|
||||
timed_complex_gain.sv \
|
||||
utils/cic_utils_pkg.sv \
|
||||
cic_integrator.sv \
|
||||
cic_integrator_core.sv \
|
||||
cic_staircase_pipeline.sv \
|
||||
cic_destaircase_pipeline.sv \
|
||||
dsp48_add3.sv \
|
||||
dsp48_accum.sv \
|
||||
dsp48_prefix_accum.sv \
|
||||
cic_decimator.sv \
|
||||
cic_interpolator.sv \
|
||||
cic_comb_filter.sv \
|
||||
cic_barrel_shift.sv \
|
||||
cic_filter_decim.sv \
|
||||
cic_filter_interp.sv \
|
||||
))
|
||||
|
||||
DSP_SRCS += $(HB_FILTER_SRCS)
|
||||
|
||||
@ -27,13 +27,19 @@
|
||||
// ACCUM_WIDTH - Accumulator width
|
||||
// COEFFS_VEC - Vector of NUM_COEFFS values, each of width COEFF_WIDTH to
|
||||
// initialize coeffs. Defaults to an impulse.
|
||||
// RELOADABLE_COEFFS - Enable (1) or disable (0) reloading coefficients at runtime (via reload bus)
|
||||
// BLANK_OUTPUT - Disable (1) or enable (0) output when initially filling internal pipeline
|
||||
// USE_EMBEDDED_REGS_COEFFS - Reduce register usage by only using embedded registers in DSP slices.
|
||||
// Updating taps while streaming will cause temporary output corruption!
|
||||
//
|
||||
// RELOADABLE_COEFFS - Enable (1) or disable (0) reloading coefficients
|
||||
// at runtime (via reload bus)
|
||||
// BLANK_OUTPUT - Disable (1) or enable (0) output when initially filling internal
|
||||
// pipeline
|
||||
// USE_EMBEDDED_REGS_COEFFS - Reduce register usage by only using embedded registers in DSP
|
||||
// slices. Updating taps while streaming will cause temporary output
|
||||
// corruption!
|
||||
// RESET_DATA_PATH - Reset FIR arithmetic pipeline registers when
|
||||
// reset or clear assert.
|
||||
//
|
||||
// Notes:
|
||||
// - If using USE_EMBEDDED_REGS_COEFFS, coefficients must be written at least once as COEFFS_VEC is ignored!
|
||||
// - If using USE_EMBEDDED_REGS_COEFFS, coefficients must be written at least once as COEFFS_VEC is
|
||||
// ignored!
|
||||
// - If using RELOADABLE_COEFFS, coefficients must be written in reverse order!
|
||||
// - Optimizations: For static coefficients (RELOADABLE_COEFFS = 0), zero and symmetric coefficient
|
||||
// optimizations are automatically enabled to reduce DSP slice utilization.
|
||||
@ -52,7 +58,8 @@ module axi_fir_multisample_filter #(
|
||||
bit [COEFF_WIDTH-1:0] COEFFS_VEC [NUM_COEFFS] = '{0: (1 << (COEFF_WIDTH-1)) - 1, default: 0},
|
||||
bit RELOADABLE_COEFFS = 1,
|
||||
bit BLANK_OUTPUT = 1,
|
||||
bit USE_EMBEDDED_REGS_COEFFS = 1
|
||||
bit USE_EMBEDDED_REGS_COEFFS = 1,
|
||||
bit RESET_DATA_PATH = 1
|
||||
)(
|
||||
// clocks and control signals
|
||||
input logic clk,
|
||||
@ -112,7 +119,8 @@ module axi_fir_multisample_filter #(
|
||||
? check_coeffs_symmetric() : 0;
|
||||
localparam int NUM_SLICES = OPTIMIZE_FOR_SYMMETRIC_COEFFS
|
||||
? NUM_COEFFS/2 + NUM_COEFFS[0] : NUM_COEFFS;
|
||||
localparam int PIPELINE_DELAY = NUM_SLICES + 5 // +4 pipeline depth in fir_filter_slice.v, +1 of shift register
|
||||
// +4 pipeline depth in fir_filter_slice.v, +1 of shift register
|
||||
localparam int PIPELINE_DELAY = NUM_SLICES + 5
|
||||
- (OPTIMIZE_FOR_ZERO_COEFFS ? count_zeros_up_to_index(NUM_SLICES) : 0);
|
||||
localparam int SHIFT_REG_WIDTH = (NUM_SPC + 1) * NUM_COEFFS; // length of shift register
|
||||
|
||||
@ -262,7 +270,8 @@ module axi_fir_multisample_filter #(
|
||||
end else begin
|
||||
localparam int GAP = OPTIMIZE_FOR_ZERO_COEFFS ? count_zeros_up_to_index(j) : 0;
|
||||
localparam bit APPLY_SYMMETRY = (OPTIMIZE_FOR_SYMMETRIC_COEFFS == 1) // enabled?
|
||||
&& !((NUM_COEFFS[0] == 1) && (j == NUM_SLICES-1)); // not the center tap for odd NUM_COEFFS
|
||||
&& !((NUM_COEFFS[0] == 1) && (j == NUM_SLICES-1));
|
||||
// not the center tap for odd NUM_COEFFS
|
||||
|
||||
fir_filter_slice #(
|
||||
.IN_WIDTH(IN_WIDTH),
|
||||
@ -271,8 +280,8 @@ module axi_fir_multisample_filter #(
|
||||
.OUT_WIDTH(ACCUM_WIDTH))
|
||||
fir_filter_slice (
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
.reset(RESET_DATA_PATH ? (reset | clear) : 1'b0),
|
||||
.clear(1'b0),
|
||||
.sample_in_stb(s_axis_data_tvalid & s_axis_data_tready),
|
||||
// NUM_SPC is added j times due to the pipeline delay.
|
||||
// +1 for selecting the next index for the FIR result calculation
|
||||
@ -287,10 +296,13 @@ module axi_fir_multisample_filter #(
|
||||
// Mirror index is NUM_COEFFS-1-j, since sample_in_b inside fir_filter_slice
|
||||
// has one cycle delay less that sample_in_a, we need to take it one cycle
|
||||
// later such that both values are in-sync. Thus we add another NUM_SPC to the index.
|
||||
.sample_in_b(APPLY_SYMMETRY ? data_shift_reg[ (NUM_COEFFS-1-j) + (j-GAP+1)*NUM_SPC + K_FLIPPED ] : '0),
|
||||
.sample_in_b(APPLY_SYMMETRY ?
|
||||
data_shift_reg[ (NUM_COEFFS-1-j) + (j-GAP+1)*NUM_SPC + K_FLIPPED ] : '0),
|
||||
.sample_forward(),
|
||||
// For proper coeffient loading, coeff_forward must be shifted in backwards. coeffs[] is already backwards
|
||||
.coeff_in(((USE_EMBEDDED_REGS_COEFFS == 1) && (RELOADABLE_COEFFS == 1)) ? coeff_forward[j] : coeffs[j]),
|
||||
// For proper coeffient loading, coeff_forward must be shifted in backwards.
|
||||
// coeffs[] is already backwards
|
||||
.coeff_in(((USE_EMBEDDED_REGS_COEFFS == 1) && (RELOADABLE_COEFFS == 1)) ?
|
||||
coeff_forward[j] : coeffs[j]),
|
||||
.coeff_forward(coeff_forward[j+1]),
|
||||
.coeff_load_stb(coeff_load_stb),
|
||||
.sample_accum(sample_accum[j]),
|
||||
|
||||
187
fpga/usrp3/lib/dsp/axis_fir_ms_preload.sv
Normal file
187
fpga/usrp3/lib/dsp/axis_fir_ms_preload.sv
Normal file
@ -0,0 +1,187 @@
|
||||
// Copyright 2026 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: axis_fir_ms_preload
|
||||
//
|
||||
// Description:
|
||||
// Thin wrapper around axi_fir_multisample_filter that optionally pre-fills
|
||||
// the filter pipeline with zeros on reset/clear before allowing real data
|
||||
// through such that the filter behaves like after a cold start.
|
||||
//
|
||||
// When PRELOAD_ZEROES=1: on reset or clear, feeds zeroes
|
||||
// into the FIR while blocking upstream (s_axis_data_tready=0) and
|
||||
// suppressing downstream output (m_axis_data_tvalid=0).
|
||||
// After preloading, the wrapper becomes transparent.
|
||||
//
|
||||
// When PRELOAD_ZEROES=0: transparent pass-through; clear is still wired to
|
||||
// the FIR.
|
||||
//
|
||||
// Parameters:
|
||||
// PRELOAD_ZEROES_NUM_TAPS:
|
||||
// The number of serial pipeline stages (taps) that axi_fir_multisample_filter
|
||||
// actually instantiates after applying its coefficient optimizations.
|
||||
// This determines the number of cycles required to flush the FIR pipeline
|
||||
// by feeding zeros, and thus how long the input/output should be gated during
|
||||
// preload.
|
||||
//
|
||||
// Standard FIR (no optimizations, or RELOADABLE_COEFFS=1, default behavior):
|
||||
// PRELOAD_ZEROES_NUM_TAPS = NUM_COEFFS
|
||||
// Example: 41-tap FIR -> 41 stages, PIPELINE_DELAY = 46
|
||||
//
|
||||
// Halfband FIR (RELOADABLE_COEFFS=0, symmetric + alternating-zero taps):
|
||||
// PRELOAD_ZEROES_NUM_TAPS = (NUM_COEFFS + 5) / 4
|
||||
// Example: HBF47 -> 13 stages, PIPELINE_DELAY = 18
|
||||
// HBF63 -> 17 stages, PIPELINE_DELAY = 22
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module axis_fir_ms_preload #(
|
||||
int IN_WIDTH = 16,
|
||||
int NUM_SPC = 4,
|
||||
int COEFF_WIDTH = 16,
|
||||
int OUT_WIDTH = 16,
|
||||
int NUM_COEFFS = 41,
|
||||
int CLIP_BITS = $clog2(NUM_COEFFS),
|
||||
int ACCUM_WIDTH = IN_WIDTH + COEFF_WIDTH + $clog2(NUM_COEFFS) - 1,
|
||||
bit [COEFF_WIDTH-1:0] COEFFS_VEC [NUM_COEFFS] = '{0: (1 << (COEFF_WIDTH-1)) - 1, default: 0},
|
||||
bit RELOADABLE_COEFFS = 1,
|
||||
bit BLANK_OUTPUT = 1,
|
||||
bit USE_EMBEDDED_REGS_COEFFS = 1,
|
||||
bit PRELOAD_ZEROES = 0,
|
||||
int PRELOAD_ZEROES_NUM_TAPS = NUM_COEFFS // See module description above
|
||||
) (
|
||||
input wire clk,
|
||||
input wire reset,
|
||||
input wire clear,
|
||||
|
||||
input wire [NUM_SPC*IN_WIDTH-1:0] s_axis_data_tdata,
|
||||
input wire s_axis_data_tlast,
|
||||
input wire s_axis_data_tvalid,
|
||||
output logic s_axis_data_tready,
|
||||
|
||||
output logic [NUM_SPC*OUT_WIDTH-1:0] m_axis_data_tdata,
|
||||
output logic m_axis_data_tlast,
|
||||
output logic m_axis_data_tvalid,
|
||||
input wire m_axis_data_tready,
|
||||
|
||||
input wire [COEFF_WIDTH-1:0] s_axis_reload_tdata,
|
||||
input wire s_axis_reload_tvalid,
|
||||
input wire s_axis_reload_tlast,
|
||||
output logic s_axis_reload_tready
|
||||
);
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Preload logic
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
// Goal: after preloading, the filter behaves identically to a cold-start.
|
||||
// The only observable difference is the PIPELINE_DELAY backpressure cycles on reset/clear.
|
||||
//
|
||||
// PRELOAD_ZEROES_NUM_TAPS:
|
||||
// number of serial DSP stages in the wrapped FIR
|
||||
// (after potential optimization, see module description).
|
||||
// The FIR's internal PIPELINE_DELAY equals
|
||||
// PRELOAD_ZEROES_NUM_TAPS + 5
|
||||
// PIPELINE_FILL_CYCLES:
|
||||
// number of cycles needed to fill FIR pipeline with zeroes.
|
||||
// ROUND_CLIP_DELAY:
|
||||
// axi_round_and_clip is not reset by clear and holds 2 in-flight zero beats
|
||||
// after PIPELINE_FILL_CYCLES.
|
||||
// Drain beats drive fir_s_tvalid=0 so they exit through fir_m_tready=1
|
||||
// (suppressed) before the output gate opens, ensuring fir_m_tvalid=0.
|
||||
localparam int FIR_DSP_PIPELINE_DELAY = PRELOAD_ZEROES_NUM_TAPS + 5;
|
||||
localparam int PIPELINE_FILL_CYCLES = NUM_COEFFS + FIR_DSP_PIPELINE_DELAY;
|
||||
localparam int ROUND_CLIP_DELAY = 2; // axi_round (1 cycle) + axi_clip (1 cycle)
|
||||
localparam int PIPELINE_DELAY = PIPELINE_FILL_CYCLES + ROUND_CLIP_DELAY;
|
||||
localparam int CNT_W = $clog2(PIPELINE_DELAY + 1);
|
||||
|
||||
logic [CNT_W-1:0] preload_cnt;
|
||||
logic filling; // fill phase: drive zeros into FIR with tvalid=1
|
||||
logic preloading; // entire preload window (fill + drain), gate input/output
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (reset | clear) begin
|
||||
preload_cnt <= '0;
|
||||
filling <= PRELOAD_ZEROES;
|
||||
preloading <= PRELOAD_ZEROES;
|
||||
end else if (preloading) begin
|
||||
preload_cnt <= preload_cnt + 1'b1;
|
||||
filling <= PRELOAD_ZEROES && (preload_cnt < PIPELINE_FILL_CYCLES - 1);
|
||||
preloading <= PRELOAD_ZEROES && (preload_cnt < PIPELINE_DELAY - 1);
|
||||
end
|
||||
end
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// AXI-Stream muxing
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
// Internal signals to/from the wrapped FIR
|
||||
logic [NUM_SPC*IN_WIDTH-1:0] fir_s_tdata;
|
||||
logic fir_s_tvalid;
|
||||
logic fir_s_tlast;
|
||||
logic fir_s_tready;
|
||||
logic [NUM_SPC*OUT_WIDTH-1:0] fir_m_tdata;
|
||||
logic fir_m_tvalid;
|
||||
logic fir_m_tlast;
|
||||
logic fir_m_tready;
|
||||
|
||||
// Fill phase (filling=1): feed zeros with tvalid=1 to fill the FIR shift register.
|
||||
// Drain phase (filling=0, preloading=1): drive tvalid=0 so round+clip empties its
|
||||
// pipeline, ensuring fir_m_tvalid=0 when the output gate opens.
|
||||
// Normal phase (preloading=0): fully transparent pass-through.
|
||||
// Upstream (s_axis_data_tready) is held low for the entire preload window so no real
|
||||
// data is consumed until the filter is clean.
|
||||
assign fir_s_tdata = filling ? '0 : s_axis_data_tdata;
|
||||
assign fir_s_tvalid = filling ? 1'b1 : (preloading ? 1'b0 : s_axis_data_tvalid);
|
||||
assign fir_s_tlast = filling ? 1'b0 : s_axis_data_tlast;
|
||||
assign s_axis_data_tready = preloading ? 1'b0 : fir_s_tready;
|
||||
|
||||
// Output gate: hold output suppressed for the entire preload window.
|
||||
// fir_m_tready=1 drains round+clip silently during both fill and drain phases.
|
||||
// When the gate opens (preloading=0), fir_m_tvalid is guaranteed to be 0.
|
||||
assign fir_m_tready = preloading ? 1'b1 : m_axis_data_tready;
|
||||
assign m_axis_data_tdata = fir_m_tdata;
|
||||
assign m_axis_data_tlast = fir_m_tlast;
|
||||
assign m_axis_data_tvalid = preloading ? 1'b0 : fir_m_tvalid;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Wrapped FIR instance
|
||||
//----------------------------------------------------------------------------
|
||||
localparam bit RESET_DATA_PATH = !PRELOAD_ZEROES;
|
||||
|
||||
axi_fir_multisample_filter #(
|
||||
.IN_WIDTH (IN_WIDTH),
|
||||
.NUM_SPC (NUM_SPC),
|
||||
.COEFF_WIDTH (COEFF_WIDTH),
|
||||
.OUT_WIDTH (OUT_WIDTH),
|
||||
.NUM_COEFFS (NUM_COEFFS),
|
||||
.CLIP_BITS (CLIP_BITS),
|
||||
.ACCUM_WIDTH (ACCUM_WIDTH),
|
||||
.COEFFS_VEC (COEFFS_VEC),
|
||||
.RELOADABLE_COEFFS (RELOADABLE_COEFFS),
|
||||
.BLANK_OUTPUT (BLANK_OUTPUT),
|
||||
.USE_EMBEDDED_REGS_COEFFS(USE_EMBEDDED_REGS_COEFFS),
|
||||
.RESET_DATA_PATH (RESET_DATA_PATH)
|
||||
) fir_inst (
|
||||
.clk (clk),
|
||||
.reset (reset),
|
||||
.clear (clear),
|
||||
.s_axis_data_tdata (fir_s_tdata),
|
||||
.s_axis_data_tlast (fir_s_tlast),
|
||||
.s_axis_data_tvalid (fir_s_tvalid),
|
||||
.s_axis_data_tready (fir_s_tready),
|
||||
.m_axis_data_tdata (fir_m_tdata),
|
||||
.m_axis_data_tlast (fir_m_tlast),
|
||||
.m_axis_data_tvalid (fir_m_tvalid),
|
||||
.m_axis_data_tready (fir_m_tready),
|
||||
.s_axis_reload_tdata (s_axis_reload_tdata),
|
||||
.s_axis_reload_tvalid(s_axis_reload_tvalid),
|
||||
.s_axis_reload_tlast (s_axis_reload_tlast),
|
||||
.s_axis_reload_tready(s_axis_reload_tready)
|
||||
);
|
||||
|
||||
endmodule : axis_fir_ms_preload
|
||||
|
||||
`default_nettype wire
|
||||
224
fpga/usrp3/lib/dsp/cic_barrel_shift.sv
Normal file
224
fpga/usrp3/lib/dsp/cic_barrel_shift.sv
Normal file
@ -0,0 +1,224 @@
|
||||
//
|
||||
// Copyright 2026 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: cic_barrel_shift
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// Rate-dependent output scaling stage shared by the CIC decimation and
|
||||
// interpolation filters. Accepts SPC I/Q samples at the full accumulator
|
||||
// width (IN_WIDTH bits per sample) and right-shifts each I and Q component
|
||||
// to normalize the CIC gain, then rounds the result to OUT_WIDTH/2 bits per
|
||||
// component.
|
||||
//
|
||||
// Each component enters the shifter scaled up by one bit, so the most
|
||||
// significant bit that the shift would otherwise discard survives as the LSB
|
||||
// of the shifted value. The final quantization is then an ordinary
|
||||
// axi_round_and_clip stage that removes that single bit with
|
||||
// round-to-nearest. Rounding this way keeps the quantization error zero-mean
|
||||
// instead of biasing every sample towards negative infinity as plain
|
||||
// truncation does.
|
||||
//
|
||||
// The required shift for rate factor R is ceil(log2((R*M)^N)), where M=1
|
||||
// (differential delay) and N=ORDER. A compile-time lookup table
|
||||
// (shift_lut[1:MAX_RATE]) pre-computes this value for every supported rate.
|
||||
//
|
||||
// The shift itself is a SHIFT_W-stage binary barrel shifter: stage k
|
||||
// conditionally shifts right by 2^k positions when shift_amount[k] is set.
|
||||
// Pipeline depth is SHIFT_W = ceil(log2(IN_WIDTH/2 - OUT_WIDTH/2 + 1)) cycles
|
||||
// plus the two register stages of the round and clip.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// SPC : Number of samples per clock cycle (power of 2, >= 1).
|
||||
// MAX_RATE : Maximum rate factor (decimation or interpolation ratio).
|
||||
// Sizes the compile-time shift lookup table.
|
||||
// ORDER : CIC filter order (number of integrator/comb stages).
|
||||
// Used to compute the per-rate shift value.
|
||||
//
|
||||
// Ports:
|
||||
//
|
||||
// rate_factor : Active rate factor (1..MAX_RATE). Must be stable while
|
||||
// data is in flight. Sampled through a 2-stage pipeline to
|
||||
// align the shift with data latency through upstream stages.
|
||||
// data_in : AXI-Stream input; tdata is SPC * IN_WIDTH bits wide,
|
||||
// packed as {I[IN_WIDTH/2-1:0], Q[IN_WIDTH/2-1:0]} per sample.
|
||||
// data_out : AXI-Stream output; tdata is SPC * OUT_WIDTH bits wide,
|
||||
// packed as {I[OUT_WIDTH/2-1:0], Q[OUT_WIDTH/2-1:0]} per sample.
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module cic_barrel_shift #(
|
||||
parameter int SPC = 4,
|
||||
parameter int MAX_RATE = 255,
|
||||
parameter int ORDER = 4
|
||||
)(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
input wire clear,
|
||||
// Active rate factor (decimation or interpolation ratio, 1..MAX_RATE).
|
||||
input wire [$clog2(MAX_RATE+1)-1:0] rate_factor,
|
||||
// Data in: SPC samples, each IN_WIDTH bits wide ({I, Q} each IN_WIDTH/2 bits)
|
||||
AxiStreamIf.slave data_in,
|
||||
// Data out: SPC samples, each OUT_WIDTH bits wide ({I, Q} each OUT_WIDTH/2 bits)
|
||||
AxiStreamIf.master data_out
|
||||
);
|
||||
import cic_utils_pkg::*;
|
||||
|
||||
localparam int IN_WIDTH = data_in.DATA_WIDTH / SPC;
|
||||
localparam int OUT_WIDTH = data_out.DATA_WIDTH / SPC;
|
||||
localparam int SHIFT_W = barrel_shift_width(IN_WIDTH, OUT_WIDTH);
|
||||
localparam int IN_COMP_W = IN_WIDTH/2;
|
||||
localparam int OUT_COMP_W = OUT_WIDTH/2;
|
||||
// One extra LSB so the bit dropped by the shift is available for rounding.
|
||||
localparam int SHIFT_COMP_W = IN_COMP_W + 1;
|
||||
// The normalized result fits in the output range; retain the round bit.
|
||||
localparam int ROUND_COMP_W = OUT_COMP_W + 1;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Compile-time shift lookup table
|
||||
//
|
||||
// shift_lut[r] = ceil(log2((r * 1)^ORDER)) = ceil(log2(r**ORDER))
|
||||
// (differential delay M=1 is absorbed into the formula).
|
||||
//---------------------------------------------------------------------------
|
||||
logic [SHIFT_W-1:0] shift_lut [1:MAX_RATE];
|
||||
for (genvar r = 1; r <= MAX_RATE; r++) begin : gen_shift_lut
|
||||
localparam int SHIFT_R = $clog2((longint'(r)) ** ORDER);
|
||||
assign shift_lut[r] = SHIFT_W'(SHIFT_R);
|
||||
end : gen_shift_lut
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Reading the shift amount from the LUT.
|
||||
//
|
||||
// The shift_lut is implemented in a BRAM. In order to improve timing a 2
|
||||
// stage pipeline without any reset is used to allow the address register and
|
||||
// the output register of the BRAM to be activated.
|
||||
// The rate_factor is assumed to be stable while data is in flight, so the shift amount is
|
||||
// guaranteed to be stable when the data arrives at the barrel shifter.
|
||||
//---------------------------------------------------------------------------
|
||||
logic [SHIFT_W-1:0] shift_amount [2];
|
||||
always_ff @(posedge clk) begin
|
||||
shift_amount[0] <= shift_lut[rate_factor];
|
||||
shift_amount[1] <= shift_amount[0];
|
||||
end
|
||||
|
||||
// Lane signals aggregated from the per-sample generate block below.
|
||||
logic [SPC-1:0] lane_tvalid;
|
||||
logic [SPC-1:0] lane_tlast;
|
||||
logic [SPC-1:0] lane_tready;
|
||||
logic [SPC-1:0][OUT_WIDTH-1:0] lane_tdata;
|
||||
|
||||
// All lanes share the same tready (aligned by construction).
|
||||
assign data_in.tready = lane_tready[0];
|
||||
|
||||
for (genvar samp_idx = 0; samp_idx < SPC; samp_idx++) begin : gen_shift_out
|
||||
// Arithmetic right-shift pipeline: stages shift right in descending bit
|
||||
// order, from the most significant set bit to the least significant one.
|
||||
logic signed [SHIFT_COMP_W-1:0] q_shifted[SHIFT_W+1];
|
||||
logic signed [SHIFT_COMP_W-1:0] i_shifted[SHIFT_W+1];
|
||||
logic [SHIFT_W:0] shifter_tvalid;
|
||||
logic shifter_tlast[SHIFT_W+1];
|
||||
|
||||
logic stage_tready [SHIFT_W+1];
|
||||
|
||||
// A rate change leading to a new shift amount is only legal while the
|
||||
// barrel-shifter pipeline is empty. The shift amount is shared by all
|
||||
// pipeline stages, so changing it with valid data in flight would apply the
|
||||
// new amount to part of an old word. This assumption is also used by the
|
||||
// barrel-shifter multicycle path in
|
||||
// fpga/usrp3/top/x400/constraints/timing/common.xdc.
|
||||
// The assertion is done even when reset or clear is present to ensure the
|
||||
// multicycle path is used correctly at all times.
|
||||
// synthesis translate_off
|
||||
assert property (
|
||||
@(posedge clk) $changed(shift_amount[1]) |-> (shifter_tvalid == 0)[*2]
|
||||
) else
|
||||
$error("cic_barrel_shift: data processed before shift_amount was settled");
|
||||
// synthesis translate_on
|
||||
|
||||
// Input stage: scale up by one bit so the shift keeps the round bit.
|
||||
assign q_shifted[SHIFT_W] = {data_in.tdata[IN_WIDTH*samp_idx +: IN_COMP_W], 1'b0};
|
||||
assign i_shifted[SHIFT_W] = {data_in.tdata[IN_WIDTH*samp_idx + IN_COMP_W +: IN_COMP_W], 1'b0};
|
||||
assign shifter_tvalid[SHIFT_W] = data_in.tvalid;
|
||||
assign shifter_tlast[SHIFT_W] = data_in.tlast;
|
||||
|
||||
for (genvar shift_bit = SHIFT_W - 1; shift_bit >= 0; shift_bit--) begin : gen_shift_bits
|
||||
// Combinational next-stage values for this shift bit.
|
||||
logic signed [SHIFT_COMP_W-1:0] q_next;
|
||||
logic signed [SHIFT_COMP_W-1:0] i_next;
|
||||
always_comb begin
|
||||
if (shift_amount[1][shift_bit]) begin
|
||||
q_next = q_shifted[shift_bit + 1] >>> (2 ** shift_bit);
|
||||
i_next = i_shifted[shift_bit + 1] >>> (2 ** shift_bit);
|
||||
end else begin
|
||||
q_next = q_shifted[shift_bit + 1];
|
||||
i_next = i_shifted[shift_bit + 1];
|
||||
end
|
||||
end
|
||||
|
||||
// Register every second stage and pass through the others. This places
|
||||
// a 4:1 mux before each registered stage, which fits LUT6 best for
|
||||
// Xilinx architectures.
|
||||
axi_fifo #(
|
||||
.WIDTH (2*SHIFT_COMP_W + 1), // {tlast, i, q}
|
||||
.SIZE ((shift_bit % 2 == 1) ? 0 : -1)
|
||||
) pipe_ff (
|
||||
.clk (clk),
|
||||
.reset (rst),
|
||||
.clear (clear),
|
||||
.i_tdata ({shifter_tlast[shift_bit + 1], i_next, q_next}),
|
||||
.i_tvalid(shifter_tvalid[shift_bit + 1]),
|
||||
.i_tready(stage_tready[shift_bit + 1]),
|
||||
.o_tdata ({shifter_tlast[shift_bit], i_shifted[shift_bit], q_shifted[shift_bit]}),
|
||||
.o_tvalid(shifter_tvalid[shift_bit]),
|
||||
.o_tready(stage_tready[shift_bit]),
|
||||
.occupied(),
|
||||
.space()
|
||||
);
|
||||
|
||||
end : gen_shift_bits
|
||||
|
||||
// The upper shifted bits are redundant sign extension. Keep the output
|
||||
// component and the extra LSB needed for round-to-nearest.
|
||||
logic [OUT_WIDTH-1:0] rounded;
|
||||
logic rounded_tvalid;
|
||||
logic rounded_tlast;
|
||||
|
||||
axi_round_and_clip_complex #(
|
||||
.WIDTH_IN (ROUND_COMP_W),
|
||||
.WIDTH_OUT(OUT_COMP_W),
|
||||
.CLIP_BITS(0),
|
||||
.FIFOSIZE (1)
|
||||
) round_clip_complex (
|
||||
.clk (clk),
|
||||
// axi_round_and_clip has no clear input, so flush it through reset.
|
||||
.reset (rst | clear),
|
||||
.i_tdata ({i_shifted[0][ROUND_COMP_W-1:0], q_shifted[0][ROUND_COMP_W-1:0]}),
|
||||
.i_tlast (shifter_tlast[0]),
|
||||
.i_tvalid(shifter_tvalid[0]),
|
||||
.i_tready(stage_tready[0]),
|
||||
.o_tdata (rounded),
|
||||
.o_tlast (rounded_tlast),
|
||||
.o_tvalid(rounded_tvalid),
|
||||
.o_tready(data_out.tready)
|
||||
);
|
||||
|
||||
assign lane_tready[samp_idx] = stage_tready[SHIFT_W];
|
||||
assign lane_tvalid[samp_idx] = rounded_tvalid;
|
||||
assign lane_tlast[samp_idx] = rounded_tlast;
|
||||
assign lane_tdata[samp_idx] = rounded;
|
||||
|
||||
end : gen_shift_out
|
||||
|
||||
assign data_out.tdata = lane_tdata;
|
||||
|
||||
// All lanes are aligned; use lane 0 for the aggregate valid/last.
|
||||
assign data_out.tvalid = lane_tvalid[0];
|
||||
assign data_out.tlast = lane_tlast[0];
|
||||
|
||||
endmodule : cic_barrel_shift
|
||||
|
||||
`default_nettype wire
|
||||
213
fpga/usrp3/lib/dsp/cic_comb_filter.sv
Normal file
213
fpga/usrp3/lib/dsp/cic_comb_filter.sv
Normal file
@ -0,0 +1,213 @@
|
||||
//
|
||||
// Copyright 2026 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: cic_comb_filter
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// A CIC comb filter stage that processes multiple samples per clock cycle.
|
||||
// Implements the difference equation y[n] = x[n] - x[n-1] with a fixed
|
||||
// delay of 1 sample.
|
||||
//
|
||||
// With D=1 the delayed sample x[n-1] is always either:
|
||||
// - lane (lane_idx-1) of the current input word (for lanes 1..SPC-1), or
|
||||
// - lane (SPC-1) of the previous input word (for lane 0).
|
||||
//
|
||||
// Only a single register (prev_last_sample) is needed to hold the last
|
||||
// sample of the previous word.
|
||||
//
|
||||
// I and Q components within each sample (each COMP_W bits wide) are
|
||||
// subtracted independently to prevent borrow propagation across the
|
||||
// half-word boundary.
|
||||
//
|
||||
// Pipeline depth is 1 clock cycle (subtraction registered on input
|
||||
// transfer). The output is buffered through a single-stage axi_fifo to
|
||||
// break the combinational backpressure path.
|
||||
//
|
||||
// data_in ------+-------------------------+
|
||||
// (AXI-S) | |
|
||||
// v v
|
||||
// +-------------------+ +----------------------------+
|
||||
// | prev_last_sample | | Subtraction Stage |
|
||||
// | (registered) +->| Lane 0 : x[0] - prev |
|
||||
// +-------------------+ | Lane idx: x[idx] - x[idx-1]|
|
||||
// | (I/Q subtracted sep.) |
|
||||
// +-------------+--------------+
|
||||
// |
|
||||
// v
|
||||
// +-------------+-------------+
|
||||
// | output_buffer (flop) |
|
||||
// +-------------+-------------+
|
||||
// |
|
||||
// v
|
||||
// data_out
|
||||
// (AXI-S)
|
||||
//
|
||||
//
|
||||
// Parameters:
|
||||
// ACCUM_W : Bit width of each input/output sample, accounting for
|
||||
// any register growth of the CIC filter this module is
|
||||
// used in. Must have at least one bit of headroom
|
||||
// compared to the actual input Sample width to
|
||||
// prevent overflow in the subtraction within this module.
|
||||
//
|
||||
// SPC : Number of samples processed per clock cycle. Must be a
|
||||
// power of 2 and at least 1.
|
||||
//
|
||||
// FIFO_SIZE : Log2 of the depth of the output buffer FIFO. Set to 0 for
|
||||
// a single-stage flop.
|
||||
//
|
||||
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module cic_comb_filter #(
|
||||
parameter int ACCUM_W = 96,
|
||||
parameter int SPC = 4,
|
||||
parameter int FIFO_SIZE = 0
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
// Clear comb filter (resets previous-sample register)
|
||||
input wire clr,
|
||||
// Data in
|
||||
AxiStreamIf.slave data_in,
|
||||
// Data out
|
||||
AxiStreamIf.master data_out
|
||||
);
|
||||
|
||||
import cic_utils_pkg::*;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Type definitions
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
typedef cic_utils#(
|
||||
.SPC (SPC),
|
||||
.SAMP_W(ACCUM_W)
|
||||
) util_c;
|
||||
|
||||
typedef util_c::sample_t sample_t; // Single sample (ACCUM_W bits).
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Local parameters
|
||||
//---------------------------------------------------------------------------
|
||||
// Width of each I/Q component within a accumulator-width sample.
|
||||
localparam int COMP_W = ACCUM_W / 2;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Synthesis-time checks
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// SPC must be a power of 2 and at least 1.
|
||||
if (SPC < 1 || (SPC & (SPC - 1)) != 0) begin : gen_check_nspc
|
||||
$error("cic_comb_filter: SPC must be a power of 2 and at least 1, got SPC=%0d", SPC);
|
||||
end
|
||||
|
||||
// AXI stream interface width checks.
|
||||
if (data_in.DATA_WIDTH != ACCUM_W*SPC) begin : gen_check_data_in_width
|
||||
$error("cic_comb_filter: data_in DATA_WIDTH (%0d) must equal ACCUM_W*SPC (%0d)",
|
||||
data_in.DATA_WIDTH, ACCUM_W*SPC);
|
||||
end
|
||||
if (data_out.DATA_WIDTH != ACCUM_W*SPC) begin : gen_check_data_out_width
|
||||
$error("cic_comb_filter: data_out DATA_WIDTH (%0d) must equal ACCUM_W*SPC (%0d)",
|
||||
data_out.DATA_WIDTH, ACCUM_W*SPC);
|
||||
end
|
||||
|
||||
// ACCUM_W must be even to allow separate I and Q components.
|
||||
if (ACCUM_W % 2 != 0) begin : gen_check_samp_w
|
||||
$error({"cic_comb_filter: ACCUM_W must be even to allow separate I and Q components,",
|
||||
" got ACCUM_W=%0d"}, ACCUM_W);
|
||||
end
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Internal signal declarations
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// Input handshake
|
||||
logic input_transfer;
|
||||
assign input_transfer = data_in.tvalid & data_in.tready;
|
||||
|
||||
// Previous word's last sample (lane SPC-1). For lane 0 of the current
|
||||
// word, this provides x[n-1]. Cleared to zero on reset/clear, which
|
||||
// naturally provides the zero initial condition for the first word.
|
||||
sample_t prev_last_sample;
|
||||
|
||||
// Subtraction stage output (combinational, before output FIFO).
|
||||
logic [ACCUM_W*SPC-1:0] sub_tdata;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Previous-sample register
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst || clr) begin
|
||||
prev_last_sample <= '0;
|
||||
end else if (input_transfer) begin
|
||||
prev_last_sample <= data_in.tdata[ACCUM_W*(SPC-1) +: ACCUM_W];
|
||||
end
|
||||
end
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Per-component subtraction (combinational)
|
||||
//
|
||||
// Concatenate {data_in.tdata, prev_last_sample} to form (SPC+1) samples
|
||||
// worth of COMP_W-wide component slots. Then for each output slot k:
|
||||
// sub_tdata[k] = shifted_data[k+2] - shifted_data[k]
|
||||
// where +2 skips one full sample (I and Q components).
|
||||
//
|
||||
// This implements y[n] = x[n] - x[n-1] with I and Q subtracted
|
||||
// independently to prevent borrow propagation across the half-word boundary.
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
logic [ACCUM_W*(SPC+1)-1:0] shifted_data;
|
||||
|
||||
always_comb begin
|
||||
shifted_data = {data_in.tdata, prev_last_sample};
|
||||
end
|
||||
|
||||
// Iterate over all I and Q components of the input word, starting at the first
|
||||
// sample of the current input word (index 2 to skip prev_last_sample).
|
||||
for (genvar comp_idx = 0; comp_idx < SPC*2; comp_idx++) begin : gen_output_lanes
|
||||
always_comb begin
|
||||
sub_tdata[COMP_W*comp_idx +: COMP_W] =
|
||||
data_in.tdata[COMP_W*comp_idx +: COMP_W] -
|
||||
shifted_data[COMP_W*comp_idx +: COMP_W];
|
||||
end
|
||||
end : gen_output_lanes
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Output FIFO (single stage)
|
||||
//
|
||||
// Optionally breaks the combinational path from data_out.tready to
|
||||
// data_in.tready.
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
logic [SPC*ACCUM_W:0] fifo_out_tdata;
|
||||
|
||||
axi_fifo #(
|
||||
.WIDTH(SPC * ACCUM_W + 1),
|
||||
.SIZE (FIFO_SIZE)
|
||||
) output_buffer (
|
||||
.clk (clk),
|
||||
.reset (rst),
|
||||
.clear (clr),
|
||||
.i_tdata ({data_in.tlast, sub_tdata}),
|
||||
.i_tvalid(data_in.tvalid),
|
||||
.i_tready(data_in.tready),
|
||||
.o_tdata (fifo_out_tdata),
|
||||
.o_tvalid(data_out.tvalid),
|
||||
.o_tready(data_out.tready),
|
||||
.space (),
|
||||
.occupied()
|
||||
);
|
||||
|
||||
assign data_out.tdata = fifo_out_tdata[SPC*ACCUM_W-1:0];
|
||||
assign data_out.tlast = fifo_out_tdata[SPC*ACCUM_W];
|
||||
|
||||
endmodule : cic_comb_filter
|
||||
|
||||
`default_nettype wire
|
||||
273
fpga/usrp3/lib/dsp/cic_decimator.sv
Normal file
273
fpga/usrp3/lib/dsp/cic_decimator.sv
Normal file
@ -0,0 +1,273 @@
|
||||
//
|
||||
// Copyright 2026 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: cic_decimator
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// A N-to-1 CIC decimator stage that produces one output per N input samples.
|
||||
// This module is intended for usage in the multisample CIC filter. This
|
||||
// module supports changing the decimation factor at runtime, but changing
|
||||
// the decimation factor while the module is actively processing data may
|
||||
// cause unexpected results and corrupted output data.
|
||||
//
|
||||
// The decimation process is as follows:
|
||||
// 1. On each clock cycle, we receive SPC input samples (one word). A word
|
||||
// counter tracks the position within the current decimation group
|
||||
// (0 to decimation_factor-1).
|
||||
// 2. Each output slot independently knows which word position and sample
|
||||
// within that word contains its needed sample. When the matching word
|
||||
// arrives, the sample is grabbed directly into the output register.
|
||||
// 3. Once all decimation_factor words have been received (the output word
|
||||
// is fully assembled), compute_output fires and the output FIFO
|
||||
// captures the completed output word on the next clock cycle.
|
||||
//
|
||||
// Parameters:
|
||||
// SAMP_W : Bit width of each input/output samples.
|
||||
// SPC : Number of samples processed per clock cycle.
|
||||
// R_MAX : Maximum decimation factor supported. Used to size internal
|
||||
// registers.
|
||||
//
|
||||
// NOTE:
|
||||
// - This module always expects to receive enough input words to produce an
|
||||
// output word. This greatly reduces the complexity of this module as we
|
||||
// do not have to worry about not having enough input words to produce an
|
||||
// output when the end of a burst is reached. The reason we can make this
|
||||
// assumption is because this module is intended to be used in the
|
||||
// multisample CIC filter, which includes the axi_rate_change module at
|
||||
// the beginning of the chain to ensure that groups of words (SPC
|
||||
// samples) are only passed to the chain in increments of SPC. The
|
||||
// axi_rate_change module also ensures that the end of a burst is aligned
|
||||
// with the end of a group, discarding words of a partial group at the
|
||||
// end of a burst if necessary. This allows this downstream module to
|
||||
// operate without needing to worry about partial groups of input samples.
|
||||
// - The decimation factor can be changed at runtime, but will result in
|
||||
// incorrect output data if it changes in the middle of processing a group
|
||||
// of input words. It is the responsibility of the user to only change the
|
||||
// decimation factor when the module is idle (not processing input words).
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module cic_decimator #(
|
||||
parameter int SAMP_W = 32,
|
||||
parameter int SPC = 4,
|
||||
parameter int R_MAX = 255
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
// Clear decimator
|
||||
input wire clr,
|
||||
// Data in
|
||||
AxiStreamIf.slave data_in,
|
||||
// Data out
|
||||
AxiStreamIf.master data_out,
|
||||
// Configuration interface
|
||||
input wire [($clog2(R_MAX+1) - 1):0] decim_factor
|
||||
);
|
||||
|
||||
import cic_utils_pkg::*;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Parameter declarations and Type definitions
|
||||
//---------------------------------------------------------------------------
|
||||
// Module constants
|
||||
localparam int NSPC_LOG2 = (SPC == 1) ? 1 : $clog2(SPC);
|
||||
localparam int R_MAX_LOG2 = (R_MAX == 1) ? 1 : $clog2(R_MAX);
|
||||
// flat_idx needs to hold (SPC-1) * (R_MAX-1), so use R_MAX_LOG2 + NSPC_LOG2 bits.
|
||||
localparam int FLAT_IDX_W = R_MAX_LOG2 + NSPC_LOG2;
|
||||
|
||||
typedef cic_utils#(
|
||||
.SPC (SPC),
|
||||
.SAMP_W(SAMP_W)
|
||||
) util_c;
|
||||
|
||||
typedef util_c::sample_t sample_t;// Single sample packed logic type (SAMP_W bits).
|
||||
typedef util_c::word_t word_t; // Word type, packed array of SPC sample_t.
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Synthesis-time checks
|
||||
//---------------------------------------------------------------------------
|
||||
// Check that SPC is a power of 2 and at least 1
|
||||
if (SPC < 1 || (SPC & (SPC - 1)) != 0) begin
|
||||
$error(
|
||||
{"cic_decimator: SPC must be a power of 2 and at least 1.",
|
||||
" Got SPC=%0d"},
|
||||
SPC
|
||||
);
|
||||
end
|
||||
// Only support R_MAX up to 255 to keep internal registers reasonably sized (R_MAX_LOG2 <= 8).
|
||||
if (R_MAX > 255 || R_MAX < 1) begin
|
||||
$error(
|
||||
{"cic_decimator: This module only supports maximum decimation factors between 1 and 255,",
|
||||
" but R_MAX=%0d was specified."},
|
||||
R_MAX
|
||||
);
|
||||
end
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Internal signal declarations
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
// Output word register: assembled incrementally as input words arrive.
|
||||
// No input buffer needed: each output slot grabs its sample directly from
|
||||
// the current input word when that word's position matches the slot's need.
|
||||
word_t output_word;
|
||||
|
||||
// Word counter: counts input words received in the current decimation group.
|
||||
logic [R_MAX_LOG2-1:0] word_count;
|
||||
|
||||
// Control signals
|
||||
logic compute_output_pre; // Combinational: same cycle as the Rth input_xfer.
|
||||
logic compute_output; // Registered: 1 cycle delayed assembled output_word.
|
||||
|
||||
// tlast accumulation across the decimation group
|
||||
logic tlast_accum;
|
||||
|
||||
// Output FIFO signals
|
||||
logic [SPC*SAMP_W-1:0] packed_output;
|
||||
|
||||
// Input handshake
|
||||
logic input_xfer;
|
||||
assign input_xfer = data_in.tvalid & data_in.tready;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Internal logic
|
||||
//----------------------------------------------------------------------------
|
||||
// Unpack input tdata into a word_t
|
||||
word_t input_word;
|
||||
for (genvar i = 0; i < SPC; i++) begin : gen_unpack_input
|
||||
assign input_word[i] = data_in.tdata[SAMP_W*i+:SAMP_W];
|
||||
end
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Phase 1: Word counter
|
||||
//----------------------------------------------------------------------------
|
||||
// Word counter: counts from 0 to decimation_factor-1
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst || clr) begin
|
||||
word_count <= '0;
|
||||
end else if (input_xfer) begin
|
||||
if (word_count == decim_factor - 1) begin
|
||||
word_count <= '0;
|
||||
end else begin
|
||||
word_count <= word_count + 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// compute_output_pre indicates when word_count is at the last word of the current
|
||||
// decimation group. Combined with data_in.tvalid/tready, this drives compute_output
|
||||
// (1-cycle delayed) once the last word is accepted.
|
||||
assign compute_output_pre = (word_count == decim_factor - R_MAX_LOG2'(1));
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst || clr) begin
|
||||
compute_output <= 1'b0;
|
||||
end else if (data_in.tready) begin
|
||||
compute_output <= compute_output_pre & data_in.tvalid;
|
||||
end
|
||||
end
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Phase 2: Output selection logic
|
||||
//----------------------------------------------------------------------------
|
||||
// SPC parallel processing lanes, one per sample, each containing the logic
|
||||
// to select the correct sample from the input stream for that output slot.
|
||||
// Each lane calculates which input word contains the sample it needs, and at
|
||||
// what position within that word the sample is located.
|
||||
// When that word arrives, the sample is copied directly from the input
|
||||
// into the output register.
|
||||
//
|
||||
// For output sample i (0..SPC-1):
|
||||
// flat_idx = i * decimation_factor (flat sample index in the group)
|
||||
// needed_word = flat_idx / SPC (which word in the group holds it)
|
||||
// needed_samp = flat_idx % SPC (which sample within that word)
|
||||
//
|
||||
// When word_count == needed_word and input_xfer, grab input_word[needed_samp].
|
||||
//
|
||||
// Example (SPC=4, R=3):
|
||||
// output[0]: flat=0, needed_word=0, needed_samp=0 -> grab from 1st word
|
||||
// output[1]: flat=3, needed_word=0, needed_samp=3 -> grab from 1st word
|
||||
// output[2]: flat=6, needed_word=1, needed_samp=2 -> grab from 2nd word
|
||||
// output[3]: flat=9, needed_word=2, needed_samp=1 -> grab from 3rd word
|
||||
//
|
||||
// This logic allows each output slot to grab its needed sample directly from
|
||||
// the input stream with no additional buffering.
|
||||
for (genvar i = 0; i < SPC; i++) begin : gen_output_select
|
||||
// Index computation (combinational)
|
||||
logic [FLAT_IDX_W-1:0] flat_idx = '0;
|
||||
logic [R_MAX_LOG2-1:0] needed_word;
|
||||
logic [ NSPC_LOG2-1:0] needed_samp;
|
||||
|
||||
assign needed_word = flat_idx[NSPC_LOG2+:R_MAX_LOG2];
|
||||
assign needed_samp = flat_idx[NSPC_LOG2-1:0];
|
||||
|
||||
// Grab the sample from the current input word when its position matches
|
||||
always_ff @(posedge clk) begin
|
||||
if (input_xfer && (word_count == needed_word)) begin
|
||||
output_word[i] <= input_word[needed_samp];
|
||||
end
|
||||
// Register flat_idx to ease timing. Decimation factor expected to be stable
|
||||
// for the duration of sample processing, and is expected to only change when
|
||||
// module is idle, so this should not cause issues.
|
||||
flat_idx <= FLAT_IDX_W'(i) * FLAT_IDX_W'(decim_factor);
|
||||
end
|
||||
end
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Phase 3: tlast accumulation
|
||||
//----------------------------------------------------------------------------
|
||||
// OR-accumulate tlast across the decimation group.
|
||||
// At the compute_output cycle (1 cycle after last input_xfer), tlast_accum
|
||||
// holds the OR of all data_in.tlast values seen during the group. The FIFO
|
||||
// captures this value directly alongside packed_output on compute_output.
|
||||
//
|
||||
// Pipeline: input_xfer (accumulate) -> compute_output (FIFO captures tlast_accum)
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst || clr) begin
|
||||
tlast_accum <= 1'b0;
|
||||
end else if (input_xfer) begin
|
||||
if (word_count == 0) begin
|
||||
tlast_accum <= data_in.tlast;
|
||||
end else begin
|
||||
tlast_accum <= tlast_accum | data_in.tlast;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Phase 4: Pack output and output FIFO
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
assign packed_output = output_word;
|
||||
|
||||
// Output FIFO: buffers decimated output for AXI-Stream backpressure handling.
|
||||
// compute_output is used directly as FIFO valid, at this point output_word
|
||||
// and tlast_accum both contain the correct values for the completed group.
|
||||
wire [ SPC*SAMP_W:0] fifo_output;
|
||||
|
||||
axi_fifo #(
|
||||
.WIDTH(SPC * SAMP_W + 1),
|
||||
.SIZE (1)
|
||||
) output_buffer (
|
||||
.clk (clk),
|
||||
.reset (rst),
|
||||
.clear (clr),
|
||||
.i_tdata ({tlast_accum, packed_output}),
|
||||
.i_tvalid(compute_output),
|
||||
.i_tready(data_in.tready),
|
||||
.o_tdata (fifo_output),
|
||||
.o_tvalid(data_out.tvalid),
|
||||
.o_tready(data_out.tready),
|
||||
.space (),
|
||||
.occupied()
|
||||
);
|
||||
|
||||
assign {data_out.tlast, data_out.tdata} = fifo_output;
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
86
fpga/usrp3/lib/dsp/cic_destaircase_pipeline.sv
Normal file
86
fpga/usrp3/lib/dsp/cic_destaircase_pipeline.sv
Normal file
@ -0,0 +1,86 @@
|
||||
//
|
||||
// Copyright 2026 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: cic_destaircase_pipeline
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// Reverses the stagger introduced by cic_staircase_pipeline so that all
|
||||
// lanes are re-aligned to the same clock cycle.
|
||||
//
|
||||
// cic_staircase_pipeline delays lane k by k cycles.
|
||||
// This module delays lane k by (SPC-1-k) cycles, giving every lane a
|
||||
// uniform total latency of (SPC-1) clock-enabled cycles.
|
||||
//
|
||||
// out_data[0] = in_data[0] delayed SPC-1 cycles
|
||||
// out_data[k] = in_data[k] delayed SPC-1-k cycles
|
||||
// out_data[SPC-1] = in_data[SPC-1] (wire, 0 FFs)
|
||||
//
|
||||
// valid_in and valid_out are delayed according to lane 0. This is effectively
|
||||
// a delay by SPC-1 cycles.
|
||||
//
|
||||
// Parameters:
|
||||
// DATA_W : Bit width of each element.
|
||||
// SPC : Number of parallel lanes (samples per clock).
|
||||
//
|
||||
|
||||
module cic_destaircase_pipeline #(
|
||||
parameter int DATA_W = 16,
|
||||
parameter int SPC = 8
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst,
|
||||
input logic en,
|
||||
input logic valid_in,
|
||||
input logic [SPC-1:0][DATA_W-1:0] in_data,
|
||||
output logic valid_out,
|
||||
output logic [SPC-1:0][DATA_W-1:0] out_data
|
||||
);
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Valid pipeline
|
||||
// --------------------------------------------------------------------
|
||||
// valid_pipeline[d] = valid_in delayed by d clock-enabled cycles.
|
||||
// valid_pipeline[0] is a wire (no register).
|
||||
logic valid_pipeline [SPC];
|
||||
assign valid_pipeline[0] = valid_in;
|
||||
for (genvar d = 1; d < SPC; d++) begin : gen_valid_pipeline
|
||||
always_ff @(posedge clk) begin : valid_ff
|
||||
if (rst) begin
|
||||
valid_pipeline[d] <= 1'b0;
|
||||
end else if (en) begin
|
||||
valid_pipeline[d] <= valid_pipeline[d-1];
|
||||
end
|
||||
end : valid_ff
|
||||
end : gen_valid_pipeline
|
||||
assign valid_out = valid_pipeline[SPC-1];
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Data pipeline
|
||||
// --------------------------------------------------------------------
|
||||
// pipeline[k][d] = in_data[k] delayed by d clock-enabled cycles.
|
||||
// pipeline[k][0] is a wire (no register).
|
||||
logic [DATA_W-1:0] pipeline [SPC][SPC];
|
||||
for (genvar k = 0; k < SPC; k++) begin : gen_pipeline_in
|
||||
assign pipeline[k][0] = in_data[k];
|
||||
end : gen_pipeline_in
|
||||
|
||||
// Lane k needs (SPC-1-k) registers; lane SPC-1 needs 0 (wire only).
|
||||
for (genvar k = 0; k < SPC-1; k++) begin : gen_pipeline_lane
|
||||
for (genvar d = 1; d <= SPC-1-k; d++) begin : gen_pipeline_depth
|
||||
always_ff @(posedge clk) begin : pipeline_ff
|
||||
if (en) begin
|
||||
pipeline[k][d] <= pipeline[k][d-1];
|
||||
end
|
||||
end : pipeline_ff
|
||||
end : gen_pipeline_depth
|
||||
end : gen_pipeline_lane
|
||||
|
||||
// Output: lane k taps the d = SPC-1-k entry.
|
||||
for (genvar k = 0; k < SPC; k++) begin : gen_out
|
||||
assign out_data[k] = pipeline[k][SPC-1-k];
|
||||
end : gen_out
|
||||
|
||||
endmodule : cic_destaircase_pipeline
|
||||
318
fpga/usrp3/lib/dsp/cic_filter_decim.sv
Normal file
318
fpga/usrp3/lib/dsp/cic_filter_decim.sv
Normal file
@ -0,0 +1,318 @@
|
||||
//
|
||||
// Copyright 2026 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: cic_filter_decim
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// CIC decimation filter for SPC complex samples per clock cycle. The datapath
|
||||
// implements the classic Hogenauer topology and adds rate-dependent gain
|
||||
// normalization and an output FIFO:
|
||||
//
|
||||
// data_in
|
||||
// (SPC x SAMP_W)
|
||||
// │ tready
|
||||
// v<─────────────────────────────────┐
|
||||
// ┌───────────────────────────────┐ │
|
||||
// │ I/Q sign extension │ │
|
||||
// │ SAMP_W -> ACCUM_W │ │
|
||||
// └──────────────┬────────────────┘ │
|
||||
// │ │
|
||||
// ┌──────────────┼────────────────┐ │
|
||||
// │ ┌────────────────────────┐ │ │
|
||||
// │ │ Integrator 0 │ │ │
|
||||
// │ └───────────┬────────────┘ │ │
|
||||
// │ │ │ ORDER stages │
|
||||
// │ ... │ (full input rate)│
|
||||
// │ │ │ │
|
||||
// │ ┌────────────────────────┐ │ │
|
||||
// │ │ Integrator ORDER-1 │ │ │
|
||||
// │ └───────────┬────────────┘ │ │
|
||||
// └──────────────┼────────────────┘ │
|
||||
// │ │
|
||||
// ┌────────────────────────┐ │
|
||||
// decim_factor ─┤ Decimator (÷R) │ │
|
||||
// └───────────┬────────────┘ │
|
||||
// │ │
|
||||
// ┌──────────────┼────────────────┐ │
|
||||
// │ ┌────────────────────────┐ │ │
|
||||
// │ │ Comb 0 │ │ │
|
||||
// │ └───────────┬────────────┘ │ │
|
||||
// │ │ │ ORDER stages │
|
||||
// │ ... │ (decimated rate) │
|
||||
// │ │ │ │
|
||||
// │ ┌────────────────────────┐ │ │
|
||||
// │ │ Comb ORDER-1 │ │ │
|
||||
// │ └───────────┬────────────┘ │ │
|
||||
// └──────────────┼────────────────┘ │
|
||||
// │ │
|
||||
// ┌───────────────────────────────┐ │
|
||||
// │ Normalize and round │ │
|
||||
// │ ACCUM_W -> SAMP_W │ │
|
||||
// └──────────────┬────────────────┘ │
|
||||
// │ │
|
||||
// ┌───────────────────────────────┐ fifo_space │
|
||||
// │ Output FIFO │──────────────────┘
|
||||
// └──────────────┬────────────────┘
|
||||
// │
|
||||
// v
|
||||
// data_out
|
||||
// (SPC x SAMP_W)
|
||||
//
|
||||
// Each input word contains SPC packed complex samples. The module sign-extends
|
||||
// each I and Q component to ACCUM_W, which includes the maximum CIC bit growth.
|
||||
// The ORDER integrator stages process every input sample. For decimation factor
|
||||
// R, the decimator consumes R input words and constructs one SPC-sample output
|
||||
// word by selecting every R-th integrated sample. The ORDER comb stages then
|
||||
// apply y[n] = x[n] - x[n-1] at the decimated rate.
|
||||
//
|
||||
// The barrel shifter normalizes the CIC gain by right-shifting each accumulator
|
||||
// component by ceil(log2(R**ORDER)). It rounds each result to SAMP_W/2 bits
|
||||
// before packing the SPC output samples.
|
||||
//
|
||||
// Downstream backpressure stops at the output FIFO. A registered credit gate
|
||||
// admits input words only while the FIFO has room for every possible output
|
||||
// already in flight, using the worst-case R=1 rate. Once accepted, a word can
|
||||
// traverse the datapath without stalling on data_out.tready. If the reserved
|
||||
// capacity runs low, data_in.tready deasserts until FIFO space becomes available.
|
||||
//
|
||||
// The decimation factor is driven directly on decim_factor. It must be valid
|
||||
// before reset is released and remain stable while data is actively
|
||||
// processed.
|
||||
//
|
||||
// NOTE: This module is expected to run within axi_rate_change, which ensures
|
||||
// that each packet contains a multiple of R input words and that output tlast
|
||||
// signals are correctly aligned.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// SPC : Number of samples per clock cycle (must be power of 2, >= 1).
|
||||
// SAMP_W : External sample width (I + Q packed, each SAMP_W/2 bits).
|
||||
// MAX_DECIM : Maximum decimation factor supported. Sizes internal registers.
|
||||
// ORDER : CIC filter order (number of integrator and comb stages).
|
||||
//
|
||||
// Signals:
|
||||
// decim_factor : Runtime-configurable decimation ratio (1..MAX_DECIM).
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module cic_filter_decim #(
|
||||
parameter int SPC = 4,
|
||||
parameter int SAMP_W = 32,
|
||||
parameter int MAX_DECIM = 255,
|
||||
parameter int ORDER = 4
|
||||
)(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
input wire clear,
|
||||
input wire [$clog2(MAX_DECIM+1)-1:0] decim_factor,
|
||||
// Data in (SPC * SAMP_W wide)
|
||||
AxiStreamIf.slave data_in,
|
||||
// Data out (SPC * SAMP_W wide)
|
||||
AxiStreamIf.master data_out
|
||||
);
|
||||
//----------------------------------------------------------------------------
|
||||
// Local parameters
|
||||
//----------------------------------------------------------------------------
|
||||
localparam int MAX_DELAY = 1; // Maximum comb delay in samples
|
||||
|
||||
// Accumulator width to accommodate max CIC growth without overflow, based on the formula:
|
||||
localparam int ACCUM_W = 2 * (SAMP_W/2 + $clog2(MAX_DECIM*MAX_DELAY)*ORDER);
|
||||
localparam int COMP_W = ACCUM_W/2; // Width of each I/Q component in the accumulator
|
||||
|
||||
import cic_utils_pkg::*;
|
||||
|
||||
// Reserve for the worst-case R=1 path, where every input word produces an
|
||||
// output word, plus one cycle each for the registered FIFO-space decision
|
||||
// and input_blocked state.
|
||||
localparam int DATAPATH_LATENCY = integrator_latency(SPC, ORDER)
|
||||
+ decimator_latency()
|
||||
+ comb_filter_latency(ORDER)
|
||||
+ barrel_shift_latency(ACCUM_W, SAMP_W)
|
||||
+ 2; // Additional cycles for fifo_ready and input_blocked
|
||||
|
||||
// Set the output FIFO size to twice the datapath latency to allow continuous
|
||||
// downstream traffic.
|
||||
localparam int OUTPUT_FIFO_SIZE = $clog2(2 * DATAPATH_LATENCY);
|
||||
localparam int OUTPUT_FIFO_DEPTH = 1 << OUTPUT_FIFO_SIZE;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Internal AXI-Stream buses
|
||||
//---------------------------------------------------------------------------
|
||||
// Integrator chain: 2 buses (input [0] and output [1] of cic_integrator).
|
||||
AxiStreamIf #(SPC * ACCUM_W) integ_bus [2] (.clk(clk), .rst(rst));
|
||||
// integ_bus[i] for i > 0 carries the output of integrator stage i-1.
|
||||
|
||||
// Comb chain: ORDER+1 buses connecting comb filter inputs/outputs.
|
||||
// comb_bus[0] is driven by the decimator output.
|
||||
// comb_bus[i] for i > 0 carries the output of comb stage i-1.
|
||||
AxiStreamIf #(SPC * ACCUM_W) comb_bus [ORDER+1] (.clk(clk), .rst(rst));
|
||||
|
||||
// The output of the barrel shift stage after the bus width is reduced back
|
||||
// to SAMP_W bits from ACCUM_W bits.
|
||||
AxiStreamIf #(SPC * SAMP_W) barrel_shift_out (.clk(clk), .rst(rst));
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Sign extension to ACCUM_W bits for the input samples
|
||||
//
|
||||
// Each sample in data_in is SAMP_W bits wide ({I[SAMP_W/2-1:0], Q[SAMP_W/2-1:0]}).
|
||||
// Sign-extend each I and Q component independently from SAMP_W/2 to COMP_W
|
||||
// bits, then pack into integ_bus[0].tdata as ACCUM_W-wide samples.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
logic fifo_ready;
|
||||
logic input_blocked;
|
||||
|
||||
// Hold the input blocked until FIFO credit is restored.
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst || clear) begin
|
||||
input_blocked <= 1'b0;
|
||||
end else begin
|
||||
if (fifo_ready) begin
|
||||
input_blocked <= 1'b0;
|
||||
end else if (integ_bus[0].tvalid && integ_bus[0].tready) begin
|
||||
input_blocked <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
integ_bus[0].tvalid = data_in.tvalid & ~input_blocked;
|
||||
integ_bus[0].tlast = data_in.tlast;
|
||||
data_in.tready = integ_bus[0].tready & ~input_blocked;
|
||||
end
|
||||
|
||||
for (genvar samp_idx = 0; samp_idx < SPC; samp_idx++) begin : gen_sign_ext
|
||||
// Q component (lower half of each sample)
|
||||
sign_extend #(
|
||||
.bits_in (SAMP_W/2),
|
||||
.bits_out(COMP_W)
|
||||
) sign_ext_q (
|
||||
.in (data_in.tdata[SAMP_W*samp_idx +: SAMP_W/2]),
|
||||
.out(integ_bus[0].tdata[ACCUM_W*samp_idx +: COMP_W])
|
||||
);
|
||||
// I component (upper half of each sample)
|
||||
sign_extend #(
|
||||
.bits_in (SAMP_W/2),
|
||||
.bits_out(COMP_W)
|
||||
) sign_ext_i (
|
||||
.in (data_in.tdata[SAMP_W*samp_idx + SAMP_W/2 +: SAMP_W/2]),
|
||||
.out(integ_bus[0].tdata[ACCUM_W*samp_idx + COMP_W +: COMP_W])
|
||||
);
|
||||
end : gen_sign_ext
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Integrator chain: N-th order, ORDER stages at full input rate
|
||||
//---------------------------------------------------------------------------
|
||||
cic_integrator #(
|
||||
.SAMP_W(ACCUM_W),
|
||||
.SPC (SPC),
|
||||
.ORDER (ORDER)
|
||||
) cic_integrator_x (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.clr (clear),
|
||||
.data_in (integ_bus[0]),
|
||||
.data_out(integ_bus[1])
|
||||
);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Decimator: reduces sample rate by decim_factor
|
||||
//---------------------------------------------------------------------------
|
||||
cic_decimator #(
|
||||
.SAMP_W (ACCUM_W),
|
||||
.SPC (SPC),
|
||||
.R_MAX (MAX_DECIM)
|
||||
) cic_decimator_i (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.clr (clear),
|
||||
.data_in (integ_bus[1]),
|
||||
.data_out (comb_bus[0]),
|
||||
.decim_factor (decim_factor)
|
||||
);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Comb chain: ORDER stages at decimated rate
|
||||
//---------------------------------------------------------------------------
|
||||
for (genvar stage = 0; stage < ORDER; stage++) begin : gen_combs
|
||||
cic_comb_filter #(
|
||||
.ACCUM_W (ACCUM_W),
|
||||
.SPC (SPC),
|
||||
.FIFO_SIZE((stage == ORDER-1)? 1 : 0)
|
||||
) cic_comb_filter_i (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.clr (clear),
|
||||
.data_in (comb_bus[stage]),
|
||||
.data_out(comb_bus[stage+1])
|
||||
);
|
||||
end
|
||||
|
||||
// Barrel-shift each I/Q component and extract SAMP_W/2 output bits.
|
||||
cic_barrel_shift #(
|
||||
.SPC (SPC),
|
||||
.MAX_RATE (MAX_DECIM),
|
||||
.ORDER (ORDER)
|
||||
) cic_barrel_shift_i (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.clear (clear),
|
||||
.rate_factor (decim_factor),
|
||||
.data_in (comb_bus[ORDER]),
|
||||
.data_out (barrel_shift_out)
|
||||
);
|
||||
|
||||
// Downstream backpressure terminates at the output FIFO. The credit gate at
|
||||
// data_in reserves enough room for every word already in the datapath.
|
||||
assign barrel_shift_out.tready = 1'b1;
|
||||
|
||||
localparam FIFO_WIDTH = data_out.DATA_WIDTH + 1;
|
||||
logic [15:0] fifo_space;
|
||||
logic output_fifo_i_tready;
|
||||
logic [FIFO_WIDTH-1:0] fifo_data_in;
|
||||
logic [FIFO_WIDTH-1:0] fifo_data_out;
|
||||
|
||||
assign fifo_data_in = {barrel_shift_out.tlast, barrel_shift_out.tdata};
|
||||
assign {data_out.tlast, data_out.tdata} = fifo_data_out;
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst || clear) begin
|
||||
fifo_ready <= 1'b0;
|
||||
end else begin
|
||||
fifo_ready <= (fifo_space > DATAPATH_LATENCY);
|
||||
end
|
||||
end
|
||||
|
||||
axi_fifo #(
|
||||
.WIDTH(FIFO_WIDTH),
|
||||
.SIZE (OUTPUT_FIFO_SIZE)
|
||||
) output_fifo (
|
||||
.clk (clk),
|
||||
.reset (rst),
|
||||
.clear (clear),
|
||||
.i_tdata (fifo_data_in),
|
||||
.i_tvalid (barrel_shift_out.tvalid),
|
||||
.i_tready (output_fifo_i_tready),
|
||||
.o_tdata (fifo_data_out),
|
||||
.o_tvalid (data_out.tvalid),
|
||||
.o_tready (data_out.tready),
|
||||
.space (fifo_space),
|
||||
.occupied ()
|
||||
);
|
||||
|
||||
// synthesis translate_off
|
||||
assert property (
|
||||
@(posedge clk) disable iff (rst || clear)
|
||||
barrel_shift_out.tvalid |-> output_fifo_i_tready
|
||||
) else
|
||||
$error("cic_filter_decim: output FIFO not ready for valid data");
|
||||
// synthesis translate_on
|
||||
|
||||
endmodule : cic_filter_decim
|
||||
|
||||
`default_nettype wire
|
||||
357
fpga/usrp3/lib/dsp/cic_filter_interp.sv
Normal file
357
fpga/usrp3/lib/dsp/cic_filter_interp.sv
Normal file
@ -0,0 +1,357 @@
|
||||
//
|
||||
// Copyright 2026 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: cic_filter_interp
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// CIC interpolation filter for multiple samples per clock cycle,
|
||||
// implementing the classic Hogenauer (CIC) interpolation topology:
|
||||
//
|
||||
// data_in
|
||||
// │
|
||||
// v
|
||||
// ┌───────────────────────────────┐
|
||||
// │ ┌────────────────────────┐ │
|
||||
// │ │ Comb 0 │ │
|
||||
// │ └───────────┬────────────┘ │ ORDER stages
|
||||
// │ │ │ (input rate)
|
||||
// │ ... │
|
||||
// │ │ │
|
||||
// │ ┌────────────────────────┐ │
|
||||
// │ │ Comb ORDER-1 │ │
|
||||
// │ └───────────┬────────────┘ │
|
||||
// └──────────────┼────────────────┘
|
||||
// │
|
||||
// ┌────────────────────────┐ tready
|
||||
// interp_factor─┤ Interpolator (×R) │<──────────────┐
|
||||
// └───────────┬────────────┘ │
|
||||
// │ │
|
||||
// ┌──────────────┼────────────────┐ │
|
||||
// │ ┌────────────────────────┐ │ │
|
||||
// │ │ Integrator 0 │ │ │
|
||||
// │ └────────────────────────┘ │ │
|
||||
// │ │ │ ORDER │
|
||||
// │ ··· │ stages │
|
||||
// │ │ │ │
|
||||
// │ ┌────────────────────────┐ │ │
|
||||
// │ │ Integrator ORDER-1 │ │ │
|
||||
// │ └────────────────────────┘ │ │
|
||||
// └──────────────┼────────────────┘ │
|
||||
// │ │
|
||||
// v │
|
||||
// ┌───────────────────────────────┐ │
|
||||
// │ Barrel shift │ │
|
||||
// └──────────────┬────────────────┘ │
|
||||
// │ │
|
||||
// v │
|
||||
// ┌───────────────────────────────┐ │
|
||||
// │ Output FIFO │───────────┘
|
||||
// └───────────────────────────────┘
|
||||
// │
|
||||
// v
|
||||
// data_out
|
||||
//
|
||||
// ORDER comb stages (y[n] = x[n] - x[n-D]) operate at the full input rate.
|
||||
// The single interpolator then produces R output words for each input word.
|
||||
// Finally, ORDER integrator stages accumulate partial sums across SPC
|
||||
// parallel sample lanes at the full output rate.
|
||||
//
|
||||
// Comb and interpolator stages use COMB_W bits. COMB_W provides ORDER bits
|
||||
// of growth per I/Q component for the comb cascade. Integrator stages use
|
||||
// ACCUM_W bits, automatically computed to provide
|
||||
// ORDER*ceil(log2(MAX_INTERP*MAX_DELAY)) bits of growth per component.
|
||||
// Internal stages do not clip or round. The output is normalized back to
|
||||
// SAMP_W with signed round-to-nearest quantization.
|
||||
//
|
||||
// The interpolation factor is driven directly and must remain stable while
|
||||
// the module is actively processing data.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// SPC : Number of samples per clock cycle (must be power of 2, >= 1).
|
||||
// SAMP_W : Bit width of each sample (I + Q packed, each SAMP_W/2 bits).
|
||||
// MAX_INTERP : Maximum interpolation factor supported. Sizes internal registers.
|
||||
// ORDER : CIC filter order (number of comb and integrator stages).
|
||||
//
|
||||
// Signals:
|
||||
//
|
||||
// clk : Clock input.
|
||||
// rst : Synchronous reset input.
|
||||
// clear : State clear input.
|
||||
// interp_factor : Runtime-configurable interpolation ratio (1..MAX_INTERP).
|
||||
// data_in : Input AXI-Stream bus carrying SPC packed SAMP_W-bit
|
||||
// samples per word.
|
||||
// data_out : Output AXI-Stream bus carrying SPC packed SAMP_W-bit
|
||||
// samples per word.
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module cic_filter_interp #(
|
||||
parameter int SPC = 4,
|
||||
parameter int SAMP_W = 32,
|
||||
parameter int MAX_INTERP = 255,
|
||||
parameter int ORDER = 4,
|
||||
|
||||
localparam int FACTOR_W = $clog2(MAX_INTERP+1)
|
||||
)(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
input wire clear,
|
||||
input wire [FACTOR_W-1:0] interp_factor,
|
||||
AxiStreamIf.slave data_in,
|
||||
AxiStreamIf.master data_out
|
||||
);
|
||||
`include "../axi4s_sv/axi4s.vh"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Local parameters
|
||||
//--------------------------------------------------------------------------
|
||||
localparam int MAX_DELAY = 1; // Maximum comb delay in samples
|
||||
|
||||
// Accumulator width for the maximum integrator growth without overflow.
|
||||
localparam int ACCUM_W = 2 * (SAMP_W/2 + $clog2(MAX_INTERP*MAX_DELAY)*ORDER);
|
||||
|
||||
// A cascade of ORDER comb stages has coefficients whose absolute sum is
|
||||
// 2**ORDER, so each component needs ORDER bits of growth. The comb stages
|
||||
// precede the interpolator and can therefore use less width than the full
|
||||
// accumulation path.
|
||||
localparam int COMB_W = 2 * (SAMP_W/2 + ORDER);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Internal AXI-Stream buses
|
||||
//---------------------------------------------------------------------------
|
||||
// Comb chain: ORDER+1 buses connecting comb filter inputs/outputs.
|
||||
// comb_bus[0] is driven by the sign-extended input.
|
||||
// comb_bus[i] for i > 0 carries the output of comb stage i-1.
|
||||
AxiStreamIf #(SPC * COMB_W) comb_bus [ORDER+1] (.clk(clk), .rst(rst));
|
||||
|
||||
// The interpolator operates at COMB_W bits. Its output is sign-extended to
|
||||
// ACCUM_W bits before entering the integrator chain.
|
||||
AxiStreamIf #(SPC * COMB_W) interp_bus (.clk(clk), .rst(rst));
|
||||
AxiStreamIf #(SPC * ACCUM_W) interp_bus_extended (.clk(clk), .rst(rst));
|
||||
|
||||
// Integrator path: two buses connecting the interpolator to the integrator
|
||||
// chain and carrying the chain output.
|
||||
// integ_bus[0] is driven by the interpolator.
|
||||
// integ_bus[1] carries the output of the ORDER-stage integrator chain.
|
||||
AxiStreamIf #(SPC * ACCUM_W) integ_bus [2] (.clk(clk), .rst(rst));
|
||||
|
||||
// Barrel Shift output bus
|
||||
AxiStreamIf #(data_out.DATA_WIDTH) barrel_shift_out (.clk(clk), .rst(rst));
|
||||
AxiStreamIf #(data_out.DATA_WIDTH) output_fifo_in (.clk(clk), .rst(rst));
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Sign extension to COMB_W bits for the input samples
|
||||
//
|
||||
// Each sample in data_in is SAMP_W bits wide ({I, Q}). Sign-extend each I
|
||||
// and Q component independently from SAMP_W/2 to COMB_W/2 bits, then pack
|
||||
// into comb_bus[0].tdata as COMB_W-wide samples.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
assign comb_bus[0].tvalid = data_in.tvalid;
|
||||
assign comb_bus[0].tlast = data_in.tlast;
|
||||
assign data_in.tready = comb_bus[0].tready;
|
||||
|
||||
for (genvar samp_idx = 0; samp_idx < SPC; samp_idx++) begin : gen_sign_ext
|
||||
// Q component (lower half of each sample)
|
||||
sign_extend #(
|
||||
.bits_in (SAMP_W/2),
|
||||
.bits_out(COMB_W/2)
|
||||
) sign_ext_q (
|
||||
.in (data_in.tdata[SAMP_W*samp_idx +: SAMP_W/2]),
|
||||
.out(comb_bus[0].tdata[COMB_W*samp_idx +: COMB_W/2])
|
||||
);
|
||||
// I component (upper half of each sample)
|
||||
sign_extend #(
|
||||
.bits_in (SAMP_W/2),
|
||||
.bits_out(COMB_W/2)
|
||||
) sign_ext_i (
|
||||
.in (data_in.tdata[SAMP_W*samp_idx + SAMP_W/2 +: SAMP_W/2]),
|
||||
.out(comb_bus[0].tdata[COMB_W*samp_idx + COMB_W/2 +: COMB_W/2])
|
||||
);
|
||||
end : gen_sign_ext
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Comb chain: ORDER stages at input rate
|
||||
//---------------------------------------------------------------------------
|
||||
for (genvar stage = 0; stage < ORDER; stage++) begin : gen_combs
|
||||
cic_comb_filter #(
|
||||
.ACCUM_W (COMB_W),
|
||||
.SPC (SPC),
|
||||
.FIFO_SIZE((stage == ORDER-1)? 1 : 0)
|
||||
) cic_comb_filter_i (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.clr (clear),
|
||||
.data_in (comb_bus[stage]),
|
||||
.data_out(comb_bus[stage+1])
|
||||
);
|
||||
end : gen_combs
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Interpolator: increases sample rate by interp_factor
|
||||
//---------------------------------------------------------------------------
|
||||
cic_interpolator #(
|
||||
.SAMP_W (COMB_W),
|
||||
.SPC (SPC),
|
||||
.R_MAX (MAX_INTERP)
|
||||
) cic_interpolator_i (
|
||||
.clk (clk),
|
||||
.rst (rst || clear),
|
||||
.data_in (comb_bus[ORDER]),
|
||||
.data_out (interp_bus),
|
||||
.interp_factor (interp_factor)
|
||||
);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Sign extension from COMB_W to ACCUM_W bits.
|
||||
//
|
||||
// The interpolator does not increase the numeric width. The integrator
|
||||
// stages require ACCUM_W bits to accommodate their accumulated growth.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
assign interp_bus_extended.tvalid = interp_bus.tvalid;
|
||||
assign interp_bus_extended.tlast = interp_bus.tlast;
|
||||
assign interp_bus.tready = interp_bus_extended.tready;
|
||||
|
||||
for (genvar samp_idx = 0; samp_idx < SPC; samp_idx++) begin : gen_interp_ext
|
||||
sign_extend #(
|
||||
.bits_in (COMB_W/2),
|
||||
.bits_out(ACCUM_W/2)
|
||||
) sign_ext_q (
|
||||
.in (interp_bus.tdata[COMB_W*samp_idx +: COMB_W/2]),
|
||||
.out(interp_bus_extended.tdata[ACCUM_W*samp_idx +: ACCUM_W/2])
|
||||
);
|
||||
sign_extend #(
|
||||
.bits_in (COMB_W/2),
|
||||
.bits_out(ACCUM_W/2)
|
||||
) sign_ext_i (
|
||||
.in (interp_bus.tdata[COMB_W*samp_idx + COMB_W/2 +: COMB_W/2]),
|
||||
.out(interp_bus_extended.tdata[ACCUM_W*samp_idx + ACCUM_W/2 +: ACCUM_W/2])
|
||||
);
|
||||
end : gen_interp_ext
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Integrator chain: ORDER stages at full output rate
|
||||
//---------------------------------------------------------------------------
|
||||
// Starting here until the output FIFO the data flow is controlled by the
|
||||
// output FIFO backpressure.
|
||||
logic fifo_ready;
|
||||
logic input_blocked;
|
||||
|
||||
// Align input locking with AXI transfer.
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst || clear) begin
|
||||
input_blocked <= 1'b0;
|
||||
end else begin
|
||||
if (fifo_ready) begin
|
||||
input_blocked <= 1'b0;
|
||||
end else if (interp_bus_extended.tvalid && interp_bus_extended.tready) begin
|
||||
input_blocked <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
`AXI4S_ASSIGN(integ_bus[0], interp_bus_extended)
|
||||
|
||||
// overwrite handshake signal to block input when FIFO is full
|
||||
interp_bus_extended.tready = integ_bus[0].tready & ~input_blocked;
|
||||
integ_bus[0].tvalid = interp_bus_extended.tvalid & ~input_blocked;
|
||||
end
|
||||
|
||||
cic_integrator #(
|
||||
.SAMP_W(ACCUM_W),
|
||||
.SPC (SPC),
|
||||
.ORDER (ORDER)
|
||||
) cic_integrator_i (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.clr (clear),
|
||||
.data_in (integ_bus[0]),
|
||||
.data_out(integ_bus[1])
|
||||
);
|
||||
|
||||
|
||||
// Barrel-shift each I/Q component and extract SAMP_W/2 output bits.
|
||||
cic_barrel_shift #(
|
||||
.SPC (SPC),
|
||||
.MAX_RATE (MAX_INTERP),
|
||||
.ORDER (ORDER-1) // An Nth order CIC interpolation filter has a Gain of
|
||||
// R^(N-1) at DC, so we shift by ceil(log2(R^(N-1))) bits.
|
||||
) cic_barrel_shift_i (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.clear (clear),
|
||||
.rate_factor (interp_factor),
|
||||
.data_in (integ_bus[1]),
|
||||
.data_out (barrel_shift_out)
|
||||
);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Output FIFO to absorb latency of bypassed modules
|
||||
//---------------------------------------------------------------------------
|
||||
import cic_utils_pkg::*;
|
||||
localparam int LATENCY = integrator_latency(SPC, ORDER)
|
||||
+ barrel_shift_latency(ACCUM_W, SAMP_W)
|
||||
+ 1 // fifo_ready register
|
||||
+ 1 // input_blocked register
|
||||
+ 1; // output FIFO output register offset for space calculation
|
||||
|
||||
|
||||
// assign the barrel shift output to the FIFO input, and always ready to
|
||||
// accept data. The FIFO will backpressure the integrator chain when it is full.
|
||||
always_comb begin
|
||||
`AXI4S_ASSIGN(output_fifo_in, barrel_shift_out)
|
||||
barrel_shift_out.tready = 1'b1;
|
||||
end
|
||||
|
||||
// Allow the integrator chain to continue processing as long as the FIFO has
|
||||
// enough space for the entire latency of the bypassed modules.
|
||||
logic [15:0] fifo_space;
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst || clear) begin
|
||||
fifo_ready <= 1'b0;
|
||||
end else begin
|
||||
fifo_ready <= (fifo_space > LATENCY);
|
||||
end
|
||||
end
|
||||
|
||||
// FIFO depth is the next power of two at or above twice the calculated
|
||||
// latency, ensuring sufficient buffering for constant data flow downstream
|
||||
// after stalls.
|
||||
localparam int FIFO_WIDTH = data_out.DATA_WIDTH + 1;
|
||||
logic [FIFO_WIDTH-1:0] fifo_i_tdata;
|
||||
logic [FIFO_WIDTH-1:0] fifo_o_tdata;
|
||||
|
||||
assign fifo_i_tdata = {output_fifo_in.tlast, output_fifo_in.tdata};
|
||||
assign {data_out.tlast, data_out.tdata} = fifo_o_tdata;
|
||||
|
||||
axi_fifo #(
|
||||
.WIDTH(FIFO_WIDTH),
|
||||
.SIZE ($clog2(2*LATENCY))
|
||||
) output_fifo (
|
||||
.clk (clk),
|
||||
.reset (rst | clear),
|
||||
.clear (1'b0),
|
||||
.i_tdata (fifo_i_tdata),
|
||||
.i_tvalid (output_fifo_in.tvalid),
|
||||
.i_tready (output_fifo_in.tready),
|
||||
.o_tdata (fifo_o_tdata),
|
||||
.o_tvalid (data_out.tvalid),
|
||||
.o_tready (data_out.tready),
|
||||
.space (fifo_space),
|
||||
.occupied ()
|
||||
);
|
||||
|
||||
endmodule : cic_filter_interp
|
||||
|
||||
`default_nettype wire
|
||||
211
fpga/usrp3/lib/dsp/cic_integrator.sv
Normal file
211
fpga/usrp3/lib/dsp/cic_integrator.sv
Normal file
@ -0,0 +1,211 @@
|
||||
//
|
||||
// Copyright 2026 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: cic_integrator
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// N-th order CIC integrator for SPC parallel complex IQ samples per clock.
|
||||
// Accepts an AXI-Stream of SPC packed SAMP_W-bit samples, splits them into
|
||||
// independent I and Q components (each SAMP_W/2 bits wide), integrates each
|
||||
// through two parallel cic_integrator_core pipelines, then reassembles the
|
||||
// packed output word.
|
||||
//
|
||||
// Signal chain:
|
||||
//
|
||||
// data_in (SPC × SAMP_W)
|
||||
// │
|
||||
// ├──────────────────────────────┐
|
||||
// ▼ ▼
|
||||
// [I component] [Q component]
|
||||
// cic_integrator_core cic_integrator_core
|
||||
// (ORDER stages) (ORDER stages)
|
||||
// │ │
|
||||
// └──────────────┬───────────────┘
|
||||
// ▼
|
||||
// output FIFO
|
||||
// │
|
||||
// ▼
|
||||
// data_out (SPC × SAMP_W)
|
||||
//
|
||||
// Each cic_integrator_core implements ORDER integration stages using a
|
||||
// staircase input pipeline, ORDER stages of dsp48_prefix_accum adder
|
||||
// chains with running accumulators, and a destaircase output pipeline.
|
||||
//
|
||||
// AXI-Stream handshake and pipeline flush:
|
||||
//
|
||||
// The pipeline CE (en) is tied to fifo_in_ready so the entire data path
|
||||
// stalls in lock-step when the output FIFO is full. valid_in is passed
|
||||
// separately to cic_integrator_core so that accumulators are frozen during
|
||||
// flush cycles (en=1, valid_in=0), preventing inter-packet corruption.
|
||||
//
|
||||
// Three cases for (fifo_in_ready, data_in.tvalid):
|
||||
// (0, x) — FIFO full: pipeline frozen; data_in.tready=0 stalls upstream.
|
||||
// (1, 1) — Live data: pipeline and accumulators both advance.
|
||||
// (1, 0) — Flush/bubble: pipeline advances to drain in-flight data;
|
||||
// accumulators frozen; after CIC_LATENCY cycles the last word
|
||||
// and its tlast have traversed the pipeline.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// SAMP_W : Bit width of each sample (I+Q packed, each SAMP_W/2 bits). Set to
|
||||
// a value that ensures that the integrator stages do not overflow.
|
||||
// Must be a multiple of 2.
|
||||
// SPC : Number of samples per clock cycle (>= 1).
|
||||
// ORDER : CIC filter order (number of integrator stages).
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module cic_integrator #(
|
||||
parameter int SAMP_W = 32,
|
||||
parameter int SPC = 4,
|
||||
parameter int ORDER = 4
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
input wire clr,
|
||||
AxiStreamIf.slave data_in,
|
||||
AxiStreamIf.master data_out
|
||||
);
|
||||
import cic_utils_pkg::*;
|
||||
|
||||
// compile time check
|
||||
if (SAMP_W % 2 != 0) begin
|
||||
$error("cic_integrator: SAMP_W must be a multiple of 2");
|
||||
end
|
||||
|
||||
localparam int COMP_W = SAMP_W / 2;
|
||||
// CIC_LATENCY: pipeline depth of cic_integrator_max from data_in to data_out:
|
||||
// staircase : 0 cycles (valid is a wire)
|
||||
// ORDER × (prefix_accum + spc_delay): ORDER × (2 + SPC-1) cycles
|
||||
// final prefix_accum : 2 cycles
|
||||
// de-staircase : SPC-1 cycles
|
||||
// Total = ORDER*(SPC+1) + SPC + 1 cycles.
|
||||
localparam int CIC_LATENCY = integrator_dsp_latency(SPC, ORDER);
|
||||
// OUT_FIFO_SIZE: The pipeline is gated by fifo_in_ready so it stalls in
|
||||
// lock-step with data_in.tready; the FIFO only needs to absorb the one
|
||||
// word in transit between the pipeline output register and the FIFO input.
|
||||
localparam int OUT_FIFO_SIZE = 1;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// I/Q split
|
||||
//
|
||||
// lane k word: data_in.tdata[SAMP_W*k +: SAMP_W]
|
||||
// Q component: bits [COMP_W-1:0]
|
||||
// I component: bits [SAMP_W-1:COMP_W]
|
||||
//--------------------------------------------------------------------------
|
||||
logic [SPC-1:0][COMP_W-1:0] in_i;
|
||||
logic [SPC-1:0][COMP_W-1:0] in_q;
|
||||
|
||||
for (genvar k = 0; k < SPC; k++) begin : gen_iq_split
|
||||
assign in_q[k] = data_in.tdata[SAMP_W*k +: COMP_W];
|
||||
assign in_i[k] = data_in.tdata[SAMP_W*k+COMP_W +: COMP_W];
|
||||
end : gen_iq_split
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Parallel I and Q integrator branches (cic_integrator_max)
|
||||
//
|
||||
// en = fifo_in_ready — pipeline CE; keeps draining on flush cycles
|
||||
// so tlast is pushed through to the FIFO.
|
||||
// valid_in = data_in.tvalid — gates accumulator updates internally;
|
||||
// flush cycles (en=1, valid_in=0) leave accumulators frozen.
|
||||
// valid_out = pipeline-fill indicator; drives FIFO i_tvalid.
|
||||
//--------------------------------------------------------------------------
|
||||
logic [SPC-1:0][COMP_W-1:0] out_i;
|
||||
logic [SPC-1:0][COMP_W-1:0] out_q;
|
||||
logic fifo_in_ready; // pipeline CE and FIFO write-side ready
|
||||
logic valid_out_i; // CIC pipeline valid output (I branch)
|
||||
|
||||
cic_integrator_core #(
|
||||
.DATA_W (COMP_W),
|
||||
.SPC (SPC),
|
||||
.ORDER (ORDER)
|
||||
) branch_i (
|
||||
.clk (clk),
|
||||
.rst (rst || clr),
|
||||
.en (fifo_in_ready),
|
||||
.valid_in (data_in.tvalid),
|
||||
.in_data (in_i),
|
||||
.valid_out(valid_out_i),
|
||||
.out_data (out_i)
|
||||
);
|
||||
|
||||
cic_integrator_core #(
|
||||
.DATA_W (COMP_W),
|
||||
.SPC (SPC),
|
||||
.ORDER (ORDER)
|
||||
) branch_q (
|
||||
.clk (clk),
|
||||
.rst (rst || clr),
|
||||
.en (fifo_in_ready),
|
||||
.valid_in (data_in.tvalid),
|
||||
.in_data (in_q),
|
||||
.valid_out(),
|
||||
.out_data (out_q)
|
||||
);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// tlast delay pipeline — CIC_LATENCY deep so tlast arrives at the FIFO in
|
||||
// the same cycle as the last output word.
|
||||
//
|
||||
// CE = fifo_in_ready — identical to the data pipeline CE.
|
||||
//--------------------------------------------------------------------------
|
||||
logic [CIC_LATENCY-1:0] last_pipe;
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst || clr) begin
|
||||
last_pipe <= '0;
|
||||
end else if (fifo_in_ready) begin
|
||||
last_pipe <= {last_pipe[CIC_LATENCY-2:0], data_in.tlast & data_in.tvalid};
|
||||
end
|
||||
end
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Output pack: repack I/Q components into per-lane tdata words
|
||||
//--------------------------------------------------------------------------
|
||||
logic [SAMP_W*SPC-1:0] out_tdata;
|
||||
|
||||
for (genvar k = 0; k < SPC; k++) begin : gen_output_pack
|
||||
assign out_tdata[SAMP_W*k +: COMP_W] = out_q[k];
|
||||
assign out_tdata[SAMP_W*k+COMP_W +: COMP_W] = out_i[k];
|
||||
end : gen_output_pack
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Output FIFO
|
||||
//
|
||||
// Decouples data_in.tready from data_out.tready.
|
||||
//--------------------------------------------------------------------------
|
||||
logic [SAMP_W*SPC:0] fifo_out_packed;
|
||||
|
||||
axi_fifo #(
|
||||
.WIDTH(SAMP_W * SPC + 1),
|
||||
.SIZE (OUT_FIFO_SIZE)
|
||||
) output_fifo (
|
||||
.clk (clk),
|
||||
.reset (rst || clr),
|
||||
.clear (rst || clr),
|
||||
.i_tdata ({last_pipe[CIC_LATENCY-1], out_tdata}),
|
||||
.i_tvalid (valid_out_i),
|
||||
.i_tready (fifo_in_ready),
|
||||
.o_tdata (fifo_out_packed),
|
||||
.o_tvalid (data_out.tvalid),
|
||||
.o_tready (data_out.tready),
|
||||
.space (),
|
||||
.occupied ()
|
||||
);
|
||||
|
||||
assign data_out.tdata = fifo_out_packed[SAMP_W*SPC-1:0];
|
||||
assign data_out.tlast = fifo_out_packed[SAMP_W*SPC];
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Input ready: accept when the FIFO has space. The pipeline stalls in
|
||||
// lock-step so there is no in-flight overshoot to worry about.
|
||||
//--------------------------------------------------------------------------
|
||||
assign data_in.tready = fifo_in_ready;
|
||||
|
||||
endmodule : cic_integrator
|
||||
|
||||
`default_nettype wire
|
||||
201
fpga/usrp3/lib/dsp/cic_integrator_core.sv
Normal file
201
fpga/usrp3/lib/dsp/cic_integrator_core.sv
Normal file
@ -0,0 +1,201 @@
|
||||
//
|
||||
// Copyright 2026 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: cic_integrator_core
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// N-th order CIC integrator core for SPC parallel real-valued samples per
|
||||
// clock. Used by cic_integrator as the per-component (I or Q) integration
|
||||
// engine.
|
||||
//
|
||||
// Signal chain:
|
||||
//
|
||||
// in_data[SPC]
|
||||
// │
|
||||
// ▼
|
||||
// cic_staircase_pipeline
|
||||
// Lane k is delayed by k clock cycles to stagger the prefix-sum input.
|
||||
// │
|
||||
// ▼ dsp_chain[0]
|
||||
// ┌──────────────────────────────────────────────────────────────────────┐
|
||||
// │ For i = 0 .. ORDER-1: │
|
||||
// │ │
|
||||
// │ dsp48_prefix_accum (USE_PCIN=1) │
|
||||
// │ Computes the i-th order running prefix sum across all SPC lanes │
|
||||
// │ using a DSP48 PCOUT→PCIN cascade; simultaneously accumulates the │
|
||||
// │ cross-lane total into accu_out[i]. accu_in = 0 for i=0, │
|
||||
// │ accu_out[i-1] for i > 0. │
|
||||
// │ │ │
|
||||
// │ spc_delay (DELAY = SPC-1) │
|
||||
// │ Delays the prefix-sum output by SPC-1 cycles so that accu_out[i] │
|
||||
// │ settles before it is consumed as accu_in by the next stage. │
|
||||
// │ │ │
|
||||
// └──────────────────────────────────────────────────────────────────────┘
|
||||
// │
|
||||
// ▼ dsp_chain[2*ORDER]
|
||||
// dsp48_prefix_accum (USE_PCIN=0, final stage)
|
||||
// Computes the ORDER-th order prefix sum; accumulator output unused.
|
||||
// │
|
||||
// ▼ dsp_chain[2*ORDER+1]
|
||||
// cic_destaircase_pipeline
|
||||
// Re-aligns all SPC lanes to the same clock cycle.
|
||||
// │
|
||||
// ▼ out_data[SPC]
|
||||
//
|
||||
// All lanes are processed at the full input clock rate. Inputs are
|
||||
// sign-extended to a fixed internal precision of INTERNAL_W=48 bits;
|
||||
// outputs are truncated back to DATA_W bits.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// DATA_W : Bit width of each input/output lane element.
|
||||
// SPC : Number of parallel lanes (samples per clock, power of 2 >= 1).
|
||||
// ORDER : CIC filter order (number of integration stages).
|
||||
//
|
||||
|
||||
module cic_integrator_core #(
|
||||
int DATA_W = 16,
|
||||
int SPC = 8,
|
||||
int ORDER = 4
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst,
|
||||
input logic en,
|
||||
|
||||
input logic valid_in,
|
||||
input logic [SPC-1:0][DATA_W-1:0] in_data,
|
||||
|
||||
output logic valid_out,
|
||||
output logic [SPC-1:0][DATA_W-1:0] out_data
|
||||
);
|
||||
// Internal pipeline width: always 48 bits (full DSP48 precision).
|
||||
// Inputs are sign-extended to INTERNAL_W; outputs are truncated back to DATA_W.
|
||||
localparam int INTERNAL_W = 48;
|
||||
|
||||
if (DATA_W > INTERNAL_W) begin
|
||||
$error("cic_integrator_core: DATA_W (%0d) must be <= INTERNAL_W (%0d)", DATA_W, INTERNAL_W);
|
||||
end
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Input sign-extension: widen each lane from DATA_W to INTERNAL_W bits
|
||||
//--------------------------------------------------------------------------
|
||||
logic [SPC-1:0][INTERNAL_W-1:0] in_data_ext;
|
||||
for (genvar k = 0; k < SPC; k++) begin : gen_sign_ext
|
||||
assign in_data_ext[k] = INTERNAL_W'(signed'(in_data[k]));
|
||||
end : gen_sign_ext
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Staircase pipeline: lane k carries in_data[k] delayed by k cycles
|
||||
//--------------------------------------------------------------------------
|
||||
logic [SPC-1:0][INTERNAL_W-1:0] dsp_chain [2*ORDER+2];
|
||||
logic [2*ORDER+1:0] valid_chain;
|
||||
|
||||
cic_staircase_pipeline #(
|
||||
.DATA_W (INTERNAL_W),
|
||||
.SPC (SPC)
|
||||
) staircase_i (
|
||||
.clk (clk),
|
||||
.en (en),
|
||||
.valid_in (valid_in),
|
||||
.in_data (in_data_ext),
|
||||
.valid_out(valid_chain[0]),
|
||||
.out_data (dsp_chain[0])
|
||||
);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Prefix-sum adder chain + accumulator.
|
||||
// dsp_chain[2*i] → data_in port of prefix_accum stage i
|
||||
// PCOUT→PCIN cascade wired inside dsp48_prefix_accum across all SPC lanes
|
||||
// accu_out[i] → running cross-lane accumulator for order i
|
||||
//--------------------------------------------------------------------------
|
||||
logic [INTERNAL_W-1:0] accu_out [ORDER];
|
||||
|
||||
for (genvar i = 0; i < ORDER; i++) begin : gen_dsp_chain
|
||||
dsp48_prefix_accum #(
|
||||
.DATA_W (INTERNAL_W),
|
||||
.SPC (SPC),
|
||||
.USE_PCIN(1)
|
||||
) prefix_accum_i (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.en (en),
|
||||
.data_in (dsp_chain[2*i]),
|
||||
.accu_in ((i == 0) ? '0 : accu_out[i-1]),
|
||||
.valid_in (valid_chain[2*i]),
|
||||
.data_out (dsp_chain[2*i+1]),
|
||||
.accu_out (accu_out[i]),
|
||||
.valid_out(valid_chain[2*i+1])
|
||||
);
|
||||
|
||||
// Delay {data, valid} by SPC-1 cycles to allow accu_out[i] to settle
|
||||
if (SPC > 1) begin : gen_delay
|
||||
logic [SPC*INTERNAL_W:0] shift_reg [SPC-1];
|
||||
always_ff @(posedge clk) begin
|
||||
if (en) begin
|
||||
shift_reg[0] <= {dsp_chain[2*i+1], valid_chain[2*i+1]};
|
||||
for (int d = 1; d < SPC-1; d++) begin
|
||||
shift_reg[d] <= shift_reg[d-1];
|
||||
end
|
||||
end
|
||||
// reset valid chain only
|
||||
if (rst) begin
|
||||
for (int d = 0; d < SPC-1; d++) begin
|
||||
shift_reg[d][0] <= '0;
|
||||
end
|
||||
end
|
||||
end
|
||||
assign {dsp_chain[2*i+2], valid_chain[2*i+2]} = shift_reg[SPC-2];
|
||||
end else begin : gen_passthrough
|
||||
assign dsp_chain[2*i+2] = dsp_chain[2*i+1];
|
||||
assign valid_chain[2*i+2] = valid_chain[2*i+1];
|
||||
end
|
||||
end : gen_dsp_chain
|
||||
|
||||
// Final prefix-sum stage: ORDER-th order summation; accumulator output unused.
|
||||
dsp48_prefix_accum #(
|
||||
.DATA_W (INTERNAL_W),
|
||||
.SPC (SPC),
|
||||
.USE_PCIN(0)
|
||||
) prefix_accum_i (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.en (en),
|
||||
.data_in (dsp_chain[2*ORDER]),
|
||||
.accu_in (accu_out[ORDER-1]),
|
||||
.valid_in (valid_chain[2*ORDER]),
|
||||
.data_out (dsp_chain[2*ORDER+1]),
|
||||
.accu_out (),
|
||||
.valid_out(valid_chain[2*ORDER+1])
|
||||
);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// De-staircase: re-align all lanes to the same clock cycle.
|
||||
//--------------------------------------------------------------------------
|
||||
logic [SPC-1:0][INTERNAL_W-1:0] out_data_int;
|
||||
|
||||
cic_destaircase_pipeline #(
|
||||
.DATA_W (INTERNAL_W),
|
||||
.SPC (SPC)
|
||||
) destaircase_i (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.en (en),
|
||||
.in_data (dsp_chain[2*ORDER+1]),
|
||||
.valid_in (valid_chain[2*ORDER+1]),
|
||||
.valid_out(valid_out),
|
||||
.out_data (out_data_int)
|
||||
);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Output truncation: keep the least-significant DATA_W bits of each lane
|
||||
// (wrap-around / modulo arithmetic) and discard the upper
|
||||
// (INTERNAL_W-DATA_W) bits.
|
||||
//--------------------------------------------------------------------------
|
||||
for (genvar k = 0; k < SPC; k++) begin : gen_trunc
|
||||
assign out_data[k] = out_data_int[k][DATA_W-1:0];
|
||||
end : gen_trunc
|
||||
|
||||
endmodule : cic_integrator_core
|
||||
321
fpga/usrp3/lib/dsp/cic_interpolator.sv
Normal file
321
fpga/usrp3/lib/dsp/cic_interpolator.sv
Normal file
@ -0,0 +1,321 @@
|
||||
//
|
||||
// Copyright 2026 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: cic_interpolator
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// A 1-to-N CIC interpolator stage that produces N output words for every
|
||||
// input word. This interpolation factor can be changed at runtime.
|
||||
//
|
||||
// The interpolation factor can be safely updated between packets while the
|
||||
// module is idle, or after reset is asserted and before data arrives.
|
||||
// Updating the interpolation factor takes a few clock cycles to complete
|
||||
// internally and no data should be input during that time. Updating the
|
||||
// interpolation factor while the module is processing data results in
|
||||
// undefined output.
|
||||
//
|
||||
// The interpolation factor is driven directly on interp_factor. It must be
|
||||
// valid before reset is released and remain stable while data is processed.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// SAMP_W : Bit width of each input/output sample.
|
||||
// SPC : Number of samples processed per clock cycle.
|
||||
// R_MAX : Maximum interpolation factor supported. Used to size internal
|
||||
// registers.
|
||||
//
|
||||
// Signals:
|
||||
//
|
||||
// clk : Clock
|
||||
// rst : Reset
|
||||
// data_in : AXI-Stream data input
|
||||
// data_out : AXI-Stream data output
|
||||
// interp_factor : Interpolation factor to use.
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
|
||||
module cic_interpolator #(
|
||||
parameter int SAMP_W = 32,
|
||||
parameter int SPC = 4,
|
||||
parameter int R_MAX = 255,
|
||||
|
||||
localparam int FACTOR_W = $clog2(R_MAX + 1)
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
AxiStreamIf.slave data_in,
|
||||
AxiStreamIf.master data_out,
|
||||
|
||||
input wire [FACTOR_W-1:0] interp_factor
|
||||
);
|
||||
`include "usrp_utils.svh"
|
||||
|
||||
localparam int WORD_IDX_W = `MAX(1, $clog2(R_MAX));
|
||||
localparam int SAMP_IDX_W = `MAX(1, $clog2(SPC));
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Assertions
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
if (SPC < 1 || SPC != 2**$clog2(SPC)) begin : gen_spc_check
|
||||
$error(
|
||||
"cic_interpolator: SPC must be a power of 2 and at least 1. Got SPC=%0d",
|
||||
SPC
|
||||
);
|
||||
end
|
||||
|
||||
if (R_MAX <= 1) begin : gen_rate_check
|
||||
$error("cic_interpolator: R_MAX must be at least 2. Got R_MAX=%0d", R_MAX);
|
||||
end
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Signal Declarations
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// Create a version of the output bus we can index
|
||||
logic gen_tvalid;
|
||||
logic gen_tready;
|
||||
logic [SPC-1:0][SAMP_W-1:0] gen_tdata;
|
||||
logic gen_tlast;
|
||||
|
||||
// Input register
|
||||
logic input_busy = 1'b0;
|
||||
logic input_last;
|
||||
logic [SPC-1:0][SAMP_W-1:0] input_samps;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Interpolation Factor
|
||||
//---------------------------------------------------------------------------
|
||||
logic [FACTOR_W-1:0] factor_m2 = 0; // Current factor minus 2
|
||||
logic factor_is_one = 1; // High when factor == 1
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
factor_m2 <= interp_factor - 2;
|
||||
factor_is_one <= (interp_factor == 1);
|
||||
end
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Sample and Word Indices
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Determine the output word and sample indices into which each input sample
|
||||
// go after interpolation.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
localparam int ACCUM_W = WORD_IDX_W + SAMP_IDX_W;
|
||||
|
||||
// An array of indices that stores the mapping from input sample to output
|
||||
// sample.
|
||||
logic [ACCUM_W-1:0] accum [SPC];
|
||||
|
||||
// This array contains the output word index for each input sample
|
||||
logic [WORD_IDX_W-1:0] word_idx [SPC];
|
||||
// This array contains the output sample index for each input sample
|
||||
logic [SAMP_IDX_W-1:0] samp_idx [SPC];
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
for (int idx = 0; idx < SPC; idx++) begin
|
||||
accum[idx] <= idx * interp_factor;
|
||||
end
|
||||
end
|
||||
|
||||
// Extract from accumulator the word and sample indices.
|
||||
always_comb begin
|
||||
for (int idx = 0; idx < SPC; idx++) begin
|
||||
word_idx[idx] = accum[idx][SAMP_IDX_W +: WORD_IDX_W];
|
||||
samp_idx[idx] = accum[idx][ 0 +: SAMP_IDX_W];
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Word Counter
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// For each one input word we output factor words. This counter tracks the
|
||||
// number of output words that have been output for each input word.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
logic [WORD_IDX_W-1:0] word_cnt;
|
||||
logic word_cnt_last; // Assert on last word of output group
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
word_cnt <= '0;
|
||||
word_cnt_last <= 1'b1;
|
||||
end else if (!input_busy) begin
|
||||
word_cnt <= '0;
|
||||
word_cnt_last <= factor_is_one;
|
||||
end else if (gen_tvalid && gen_tready) begin
|
||||
if (word_cnt_last) begin
|
||||
word_cnt <= '0;
|
||||
word_cnt_last <= factor_is_one;
|
||||
end else begin
|
||||
word_cnt <= word_cnt + 1;
|
||||
word_cnt_last <= (word_cnt == factor_m2);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Input Word Logic
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Captures the input word when accepted and holds it until all corresponding
|
||||
// output words are generated.
|
||||
//
|
||||
// The input_busy flag tracks when we're holding a loaded input word. It
|
||||
// starts low after reset so the first word is accepted immediately. It sets
|
||||
// when a word is accepted and clears on the last output transfer for the
|
||||
// current input word.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// Assert data_in.tready when not busy, or combinationally on the cycle the
|
||||
// last output word of the current interpolation group transfers.
|
||||
assign data_in.tready = !input_busy ||
|
||||
(gen_tvalid && gen_tready && word_cnt_last);
|
||||
|
||||
// Output is valid whenever we have a loaded input word to work from.
|
||||
assign gen_tvalid = input_busy;
|
||||
|
||||
// Assert tlast on the last output word of the group when the input was last.
|
||||
assign gen_tlast = input_last && word_cnt_last;
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
input_busy <= 1'b0;
|
||||
end else begin
|
||||
if (data_in.tvalid && data_in.tready) begin
|
||||
// Accepted a new input word; busy until end of interpolation group.
|
||||
input_busy <= 1'b1;
|
||||
input_last <= data_in.tlast;
|
||||
input_samps <= data_in.tdata;
|
||||
end else if (gen_tvalid && gen_tready && word_cnt_last) begin
|
||||
// Last output word transferred with no new input; no longer busy.
|
||||
input_busy <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Output Word Generation
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// For each output word, we look at the current word_cnt and use that to
|
||||
// determine which input samples, if any, should be output for each sample
|
||||
// position. We pull the sample values from the stored input_samps register.
|
||||
// We decide where those input samples go on the output by looking up the
|
||||
// word_idx and samp_idx for that sample. All other sample positions are set
|
||||
// to 0.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// Intermediate results to pipeline the array indexing
|
||||
// Stage 1: Determine which input samples match the current word count
|
||||
logic [SPC-1:0] word_idx_match;
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (gen_tready) begin
|
||||
for (int src_samp = 0; src_samp < SPC; src_samp++) begin
|
||||
word_idx_match[src_samp] <= (word_idx[src_samp] == word_cnt);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Stage 2: Generate the sample index for each output sample position
|
||||
logic [SAMP_IDX_W-1:0] out_mux_select [SPC];
|
||||
logic [SPC-1:0] word_idx_en;
|
||||
always_ff @(posedge clk) begin
|
||||
automatic logic [SAMP_IDX_W-1:0] next_out_mux_select [SPC] = '{default: '0};
|
||||
automatic logic [SPC-1:0] next_word_idx_en = '0;
|
||||
|
||||
for (int src_samp = 0; src_samp < SPC; src_samp++) begin
|
||||
if (word_idx_match[src_samp]) begin
|
||||
next_out_mux_select[samp_idx[src_samp]] |= SAMP_IDX_W'(src_samp);
|
||||
next_word_idx_en[samp_idx[src_samp]] = '1;
|
||||
end
|
||||
end
|
||||
|
||||
if (gen_tready) begin
|
||||
out_mux_select <= next_out_mux_select;
|
||||
word_idx_en <= next_word_idx_en;
|
||||
end
|
||||
end
|
||||
|
||||
// create shift register for tdata, tvalid and tlast to pipeline them to the output
|
||||
// FIFO
|
||||
logic [1:0] tvalid_pipe;
|
||||
logic [1:0] tlast_pipe;
|
||||
logic [1:0][SPC-1:0][SAMP_W-1:0] tdata_pipe;
|
||||
always_ff @(posedge clk) begin
|
||||
if (gen_tready) begin
|
||||
tlast_pipe <= {tlast_pipe[0], gen_tlast};
|
||||
tdata_pipe <= {tdata_pipe[0], input_samps};
|
||||
end
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
tvalid_pipe <= 2'b00;
|
||||
end else if (gen_tready) begin
|
||||
tvalid_pipe <= {tvalid_pipe[0], gen_tvalid};
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
always_comb begin
|
||||
// All samples are set to zero by default
|
||||
gen_tdata = '0;
|
||||
|
||||
// Iterate over each sample and see if the word count and sample position
|
||||
// match what we precomputed in samp_idx and word_idx.
|
||||
for (int out_samp = 0; out_samp < SPC; out_samp++) begin
|
||||
if (word_idx_en[out_samp]) begin
|
||||
gen_tdata[out_samp] = tdata_pipe[1][out_mux_select[out_samp]];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Output Register
|
||||
//---------------------------------------------------------------------------
|
||||
wire [SPC*SAMP_W:0] fifo_out;
|
||||
|
||||
axi_fifo #(
|
||||
.WIDTH (SPC*SAMP_W + 1 ),
|
||||
.SIZE (1 )
|
||||
) axi_fifo_i (
|
||||
.clk (clk ),
|
||||
.reset (rst ),
|
||||
.clear (1'b0 ),
|
||||
.i_tdata ({tlast_pipe[1], gen_tdata}),
|
||||
.i_tvalid (tvalid_pipe[1] ),
|
||||
.i_tready (gen_tready ),
|
||||
.o_tdata (fifo_out ),
|
||||
.o_tvalid (data_out.tvalid ),
|
||||
.o_tready (data_out.tready ),
|
||||
.space ( ),
|
||||
.occupied ( )
|
||||
);
|
||||
|
||||
assign {data_out.tlast, data_out.tdata} = fifo_out;
|
||||
|
||||
endmodule : cic_interpolator
|
||||
|
||||
`default_nettype wire
|
||||
65
fpga/usrp3/lib/dsp/cic_staircase_pipeline.sv
Normal file
65
fpga/usrp3/lib/dsp/cic_staircase_pipeline.sv
Normal file
@ -0,0 +1,65 @@
|
||||
//
|
||||
// Copyright 2026 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: cic_staircase_pipeline
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// Produces a staircase-delayed version of the SPC-wide input data vector.
|
||||
//
|
||||
// Lane k of out_data carries in_data[k] delayed by exactly k clock-enabled
|
||||
// cycles:
|
||||
// out_data[0] = in_data[0] (wire, 0 FFs)
|
||||
// out_data[1] = in_data[1] delayed 1 cycle (1 FF)
|
||||
// out_data[k] = in_data[k] delayed k cycles (k FFs)
|
||||
//
|
||||
// This stagger is required by the prefix-sum column stage so that c_in[k]
|
||||
// arrives in the same cycle that p_reg[k-1] holds the previous lane's
|
||||
// registered result — ensuring each column adds samples from the same batch.
|
||||
//
|
||||
// Parameters:
|
||||
// DATA_W : Bit width of each element.
|
||||
// SPC : Number of parallel lanes (samples per clock).
|
||||
//
|
||||
|
||||
module cic_staircase_pipeline #(
|
||||
parameter int DATA_W = 16,
|
||||
parameter int SPC = 8
|
||||
) (
|
||||
input logic clk,
|
||||
input logic en,
|
||||
|
||||
input logic [SPC-1:0][DATA_W-1:0] in_data,
|
||||
input logic valid_in,
|
||||
|
||||
output logic [SPC-1:0][DATA_W-1:0] out_data,
|
||||
output logic valid_out
|
||||
);
|
||||
// Internal shift-register array.
|
||||
// pipeline[k][d] = in_data[k] delayed by d clock-enabled cycles.
|
||||
// pipeline[k][0] is a wire (no register).
|
||||
logic [DATA_W-1:0] pipeline [SPC][SPC];
|
||||
|
||||
for (genvar k = 0; k < SPC; k++) begin : gen_pipeline_in
|
||||
assign pipeline[k][0] = in_data[k];
|
||||
end : gen_pipeline_in
|
||||
|
||||
for (genvar k = 1; k < SPC; k++) begin : gen_pipeline_lane
|
||||
for (genvar d = 1; d <= k; d++) begin : gen_pipeline_depth
|
||||
always_ff @(posedge clk) begin : pipeline_ff
|
||||
if (en) pipeline[k][d] <= pipeline[k][d-1];
|
||||
end : pipeline_ff
|
||||
end : gen_pipeline_depth
|
||||
end : gen_pipeline_lane
|
||||
|
||||
// Output: lane k taps the d=k entry
|
||||
for (genvar k = 0; k < SPC; k++) begin : gen_out
|
||||
assign out_data[k] = pipeline[k][k];
|
||||
end : gen_out
|
||||
|
||||
// lane 0 is a wire, so valid_out is just a copy of valid_in
|
||||
assign valid_out = valid_in;
|
||||
|
||||
endmodule : cic_staircase_pipeline
|
||||
@ -134,6 +134,10 @@ module complex_multiply_iq #(
|
||||
// width is the same as the product. No rounding or clipping is needed in this
|
||||
// case.
|
||||
if (CLIP_BITS == 0 && MULT_OUTPUT_WIDTH == WIDTH_PRODUCT) begin
|
||||
// Questa Workaround for illegal output connection (vsim-3053)
|
||||
logic [product.DATA_WIDTH:0] out_flop_tdata;
|
||||
assign {product.tdata, product.tlast} = out_flop_tdata;
|
||||
|
||||
axi_fifo_flop2 #(
|
||||
.WIDTH(product.DATA_WIDTH + 1)
|
||||
) out_flop (
|
||||
@ -143,7 +147,7 @@ module complex_multiply_iq #(
|
||||
.i_tdata ({product_full.tdata, product_full.tlast}),
|
||||
.i_tvalid(product_full.tvalid),
|
||||
.i_tready(product_full.tready),
|
||||
.o_tdata ({product.tdata, product.tlast}),
|
||||
.o_tdata (out_flop_tdata),
|
||||
.o_tvalid(product.tvalid),
|
||||
.o_tready(product.tready),
|
||||
.space (),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user