Initial JPEG-LS FPGA encoder baseline with tooling and timeout fix
This commit is contained in:
225
fpga/sim/run_jls_pattern_charls_regression.ps1
Normal file
225
fpga/sim/run_jls_pattern_charls_regression.ps1
Normal file
@@ -0,0 +1,225 @@
|
||||
param(
|
||||
[string] $StartAtCase = "",
|
||||
[switch] $Resume,
|
||||
[ValidateSet(1, 2, 4, 8)]
|
||||
[int] $ConfiguredCompressionRatio = 1,
|
||||
[string] $OnlyCase = ""
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
function Invoke-CheckedCommand {
|
||||
param(
|
||||
[string] $Description,
|
||||
[scriptblock] $Command
|
||||
)
|
||||
|
||||
Write-Host "[jls-pattern-regression] $Description"
|
||||
$CommandOutput = & $Command 2>&1
|
||||
$CommandExitCode = $LASTEXITCODE
|
||||
$CommandText = $CommandOutput | Out-String
|
||||
$CommandOutput | ForEach-Object { Write-Host $_ }
|
||||
|
||||
if ($CommandExitCode -ne 0) {
|
||||
throw ("Command failed with exit code {0}: {1}" -f $CommandExitCode, $Description)
|
||||
}
|
||||
if (($CommandText -match "\*\* Fatal") -or
|
||||
($CommandText -match "\*\* Error") -or
|
||||
($CommandText -match "Errors[=:]\s*[1-9]")) {
|
||||
throw "Command reported simulator errors: $Description"
|
||||
}
|
||||
}
|
||||
|
||||
function To-PosixPath {
|
||||
param([string] $Path)
|
||||
return ($Path -replace "\\", "/")
|
||||
}
|
||||
|
||||
function Get-RtlRatioPortValue {
|
||||
param([int] $ConfiguredCompressionRatio)
|
||||
|
||||
switch ($ConfiguredCompressionRatio) {
|
||||
1 { return 0 }
|
||||
2 { return 1 }
|
||||
4 { return 2 }
|
||||
8 { return 3 }
|
||||
default {
|
||||
throw "Unsupported configured compression ratio: $ConfiguredCompressionRatio"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$RtlRatioPort = Get-RtlRatioPortValue -ConfiguredCompressionRatio $ConfiguredCompressionRatio
|
||||
$RepoRoot = (Resolve-Path ".").Path
|
||||
$OutDir = Join-Path $RepoRoot ("tools\jls_compat\out\pattern_regression_cr" + $ConfiguredCompressionRatio)
|
||||
$DepPath = Join-Path $RepoRoot "tools\jls_compat\.deps"
|
||||
$SummaryCsv = Join-Path $OutDir "pattern_regression_summary.csv"
|
||||
$QuestaBin = "C:\questasim64_2020.1\win64"
|
||||
$VlogExe = Join-Path $QuestaBin "vlog.exe"
|
||||
$VsimExe = Join-Path $QuestaBin "vsim.exe"
|
||||
|
||||
if (!(Test-Path -Path $VlogExe)) {
|
||||
throw "Questa vlog.exe not found at $VlogExe"
|
||||
}
|
||||
if (!(Test-Path -Path $VsimExe)) {
|
||||
throw "Questa vsim.exe not found at $VsimExe"
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $OutDir | Out-Null
|
||||
if (!$Resume) {
|
||||
Remove-Item -Path (Join-Path $OutDir "*.jls") -ErrorAction SilentlyContinue
|
||||
Remove-Item -Path (Join-Path $OutDir "*.rtljls") -ErrorAction SilentlyContinue
|
||||
Remove-Item -Path (Join-Path $OutDir "*.log") -ErrorAction SilentlyContinue
|
||||
Remove-Item -Path (Join-Path $OutDir "*.json") -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
if ((!$Resume) -or !(Test-Path -Path $SummaryCsv)) {
|
||||
"case_name,configured_compression_ratio,rtl_ratio_port,source_pgm,reference_jls,rtl_jls,width,height,max_value,expected_frames,status,detail" |
|
||||
Set-Content -Path $SummaryCsv -Encoding ascii
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile RTL top smoke testbench for file-driven pattern regression" {
|
||||
& $VlogExe -sv -f fpga/verilog/jpeg_ls_rtl.f fpga/sim/tb_jpeg_ls_encoder_top_run_smoke.sv
|
||||
}
|
||||
|
||||
$env:JLS_COMPAT_PYDEPS = $DepPath
|
||||
$StartRunning = [string]::IsNullOrEmpty($StartAtCase)
|
||||
|
||||
$PatternFiles = Get-ChildItem -Path (Join-Path $RepoRoot "img\patterns") -Filter "*.pgm" |
|
||||
Sort-Object -Property Name
|
||||
|
||||
foreach ($PatternFile in $PatternFiles) {
|
||||
$SourcePgm = $PatternFile.FullName
|
||||
$PatternLeaf = $PatternFile.Name
|
||||
$CaseName = [System.IO.Path]::GetFileNameWithoutExtension($SourcePgm)
|
||||
|
||||
if (![string]::IsNullOrEmpty($OnlyCase) -and ($CaseName -ne $OnlyCase)) {
|
||||
continue
|
||||
}
|
||||
|
||||
$ReferenceJls = Join-Path $RepoRoot (
|
||||
"img\reference\charls\" + $CaseName + "-r" + $RtlRatioPort + ".charlsjls"
|
||||
)
|
||||
|
||||
if (!$StartRunning) {
|
||||
if ($CaseName -eq $StartAtCase) {
|
||||
$StartRunning = $true
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
$PgmFd = [System.IO.File]::OpenRead($SourcePgm)
|
||||
$Reader = New-Object System.IO.BinaryReader($PgmFd)
|
||||
try {
|
||||
$Magic = [System.Text.Encoding]::ASCII.GetString($Reader.ReadBytes(2))
|
||||
if ($Magic -ne "P5") {
|
||||
throw "Input file is not P5 PGM: $SourcePgm"
|
||||
}
|
||||
|
||||
function Read-PgmToken {
|
||||
param([System.IO.BinaryReader] $TokenReader)
|
||||
|
||||
while ($true) {
|
||||
$Byte = $TokenReader.ReadByte()
|
||||
if ($Byte -eq 35) {
|
||||
while ($TokenReader.ReadByte() -ne 10) {
|
||||
}
|
||||
continue
|
||||
}
|
||||
if (($Byte -eq 32) -or ($Byte -eq 9) -or ($Byte -eq 10) -or ($Byte -eq 13)) {
|
||||
continue
|
||||
}
|
||||
|
||||
$Bytes = New-Object System.Collections.Generic.List[byte]
|
||||
$Bytes.Add([byte]$Byte)
|
||||
while ($true) {
|
||||
$Peek = $TokenReader.PeekChar()
|
||||
if ($Peek -lt 0) {
|
||||
break
|
||||
}
|
||||
if (($Peek -eq 35) -or ($Peek -eq 32) -or ($Peek -eq 9) -or
|
||||
($Peek -eq 10) -or ($Peek -eq 13)) {
|
||||
break
|
||||
}
|
||||
$Bytes.Add($TokenReader.ReadByte())
|
||||
}
|
||||
return [System.Text.Encoding]::ASCII.GetString($Bytes.ToArray())
|
||||
}
|
||||
}
|
||||
|
||||
$Width = [int](Read-PgmToken $Reader)
|
||||
$Height = [int](Read-PgmToken $Reader)
|
||||
$MaxValue = [int](Read-PgmToken $Reader)
|
||||
}
|
||||
finally {
|
||||
$Reader.Close()
|
||||
$PgmFd.Close()
|
||||
}
|
||||
|
||||
$RtlJls = Join-Path $OutDir ($CaseName + ".rtljls")
|
||||
$TranscriptLog = Join-Path $OutDir ($CaseName + ".vsim.log")
|
||||
$CaseJson = Join-Path $OutDir ($CaseName + ".summary.json")
|
||||
$ExpectedFrames = [int]($Height / 16)
|
||||
|
||||
$VsimArgs = @(
|
||||
"-c",
|
||||
"tb_jpeg_ls_encoder_top_run_smoke",
|
||||
"-gPIX_WIDTH=16",
|
||||
("-gPIC_COL=" + $Width),
|
||||
("-gPIC_ROW=" + $Height),
|
||||
("+IN_PGM=" + (To-PosixPath $SourcePgm)),
|
||||
("+RATIO=" + $RtlRatioPort),
|
||||
("+OUT=" + (To-PosixPath $RtlJls)),
|
||||
("+CASE=" + $CaseName),
|
||||
"-do",
|
||||
"run -all; quit"
|
||||
)
|
||||
|
||||
try {
|
||||
Invoke-CheckedCommand "run Questa RTL regression for $CaseName" {
|
||||
& $VsimExe @VsimArgs 2>&1 | Tee-Object -FilePath $TranscriptLog
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "decode and compare RTL output against CharLS reference for $CaseName" {
|
||||
python tools/jls_compat/validate_dynamic_near_stream.py `
|
||||
$RtlJls `
|
||||
--reference-pgm $SourcePgm `
|
||||
--configured-compression-ratio $ConfiguredCompressionRatio `
|
||||
--expected-frames $ExpectedFrames `
|
||||
--output-reference-jls $ReferenceJls `
|
||||
--summary-json $CaseJson
|
||||
}
|
||||
|
||||
"{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},PASS,OK" -f `
|
||||
$CaseName, `
|
||||
$ConfiguredCompressionRatio, `
|
||||
$RtlRatioPort, `
|
||||
(To-PosixPath ("img/patterns/" + $PatternLeaf)), `
|
||||
(To-PosixPath ((Resolve-Path -LiteralPath $ReferenceJls).Path)), `
|
||||
(To-PosixPath $RtlJls), `
|
||||
$Width, `
|
||||
$Height, `
|
||||
$MaxValue, `
|
||||
$ExpectedFrames | Add-Content -Path $SummaryCsv -Encoding ascii
|
||||
}
|
||||
catch {
|
||||
$Detail = $_.Exception.Message.Replace(",", ";").Replace("`r", " ").Replace("`n", " ")
|
||||
Write-Warning "[jls-pattern-regression] FAIL $CaseName : $Detail"
|
||||
"{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},FAIL,{10}" -f `
|
||||
$CaseName, `
|
||||
$ConfiguredCompressionRatio, `
|
||||
$RtlRatioPort, `
|
||||
(To-PosixPath ("img/patterns/" + $PatternLeaf)), `
|
||||
(To-PosixPath ("img/reference/charls/" + $CaseName + "-r" + $RtlRatioPort + ".charlsjls")), `
|
||||
(To-PosixPath $RtlJls), `
|
||||
$Width, `
|
||||
$Height, `
|
||||
$MaxValue, `
|
||||
$ExpectedFrames, `
|
||||
$Detail | Add-Content -Path $SummaryCsv -Encoding ascii
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "[jls-pattern-regression] DONE"
|
||||
Write-Host "[jls-pattern-regression] Summary: $SummaryCsv"
|
||||
231
fpga/sim/run_jls_smoke.ps1
Normal file
231
fpga/sim/run_jls_smoke.ps1
Normal file
@@ -0,0 +1,231 @@
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
function Invoke-CheckedCommand {
|
||||
param(
|
||||
[string] $Description,
|
||||
[scriptblock] $Command
|
||||
)
|
||||
|
||||
Write-Host "[jls-smoke] $Description"
|
||||
$CommandOutput = & $Command 2>&1
|
||||
$CommandExitCode = $LASTEXITCODE
|
||||
$CommandText = $CommandOutput | Out-String
|
||||
$CommandOutput | ForEach-Object { Write-Host $_ }
|
||||
|
||||
if ($CommandExitCode -ne 0) {
|
||||
exit $CommandExitCode
|
||||
}
|
||||
if (($CommandText -match "\*\* Fatal") -or ($CommandText -match "Errors[=:]\s*[1-9]")) {
|
||||
Write-Error "Command reported simulator errors: $Description"
|
||||
}
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile RTL filelist" {
|
||||
vlog -sv -f fpga/verilog/jpeg_ls_rtl.f
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile top-level idle smoke test" {
|
||||
vlog -sv -f fpga/verilog/jpeg_ls_rtl.f fpga/sim/tb_jpeg_ls_encoder_top_idle.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run top-level idle smoke test" {
|
||||
vsim -c tb_jpeg_ls_encoder_top_idle -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile top-level run-mode smoke test" {
|
||||
vlog -sv -f fpga/verilog/jpeg_ls_rtl.f fpga/sim/tb_jpeg_ls_encoder_top_run_smoke.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run top-level run-mode smoke test" {
|
||||
vsim -c tb_jpeg_ls_encoder_top_run_smoke -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run top-level multi-image smoke test" {
|
||||
vsim -c tb_jpeg_ls_encoder_top_run_smoke -gIMAGE_COUNT=2 +OUT=tools/jls_compat/out/rtl_top_zero_8b_2image.jls -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile preset-defaults smoke test" {
|
||||
vlog -sv fpga/verilog/jls_preset_defaults.sv fpga/sim/tb_jls_preset_defaults.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run preset-defaults smoke test" {
|
||||
vsim -c tb_jls_preset_defaults -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile coding-params smoke test" {
|
||||
vlog -sv fpga/verilog/jls_coding_params.sv fpga/sim/tb_jls_coding_params.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run coding-params smoke test" {
|
||||
vsim -c tb_jls_coding_params -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile input-control smoke test" {
|
||||
vlog -sv fpga/verilog/jls_common_pkg.sv fpga/verilog/jls_input_ctrl.sv fpga/sim/tb_jls_input_ctrl.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run input-control smoke test" {
|
||||
vsim -c tb_jls_input_ctrl -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile scan-control smoke test" {
|
||||
vlog -sv fpga/verilog/jls_scan_ctrl.sv fpga/sim/tb_jls_scan_ctrl.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run scan-control smoke test" {
|
||||
vsim -c tb_jls_scan_ctrl -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile neighbor-provider smoke test" {
|
||||
vlog -sv fpga/verilog/jls_neighbor_provider.sv fpga/sim/tb_jls_neighbor_provider.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run neighbor-provider smoke test" {
|
||||
vsim -c tb_jls_neighbor_provider -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile neighbor-provider lossless-fast smoke test" {
|
||||
vlog -sv fpga/verilog/jls_neighbor_provider.sv fpga/sim/tb_jls_neighbor_provider_lossless_fast.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run neighbor-provider lossless-fast smoke test" {
|
||||
vsim -c tb_jls_neighbor_provider_lossless_fast -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile neighbor-provider near-bypass smoke test" {
|
||||
vlog -sv fpga/verilog/jls_neighbor_provider.sv fpga/sim/tb_jls_neighbor_provider_near_bypass.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run neighbor-provider near-bypass smoke test" {
|
||||
vsim -c tb_jls_neighbor_provider_near_bypass -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile mode-router smoke test" {
|
||||
vlog -sv fpga/verilog/jls_mode_router.sv fpga/sim/tb_jls_mode_router.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run mode-router smoke test" {
|
||||
vsim -c tb_jls_mode_router -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile predictor smoke test" {
|
||||
vlog -sv fpga/verilog/jls_predictor.sv fpga/sim/tb_jls_predictor.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run predictor smoke test" {
|
||||
vsim -c tb_jls_predictor -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile context-quantizer smoke test" {
|
||||
vlog -sv fpga/verilog/jls_context_quantizer.sv fpga/sim/tb_jls_context_quantizer.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run context-quantizer smoke test" {
|
||||
vsim -c tb_jls_context_quantizer -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile context-model smoke test" {
|
||||
vlog -sv fpga/verilog/jls_context_memory.sv fpga/verilog/jls_context_model.sv fpga/sim/tb_jls_context_model.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run context-model smoke test" {
|
||||
vsim -c tb_jls_context_model -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile prediction-corrector smoke test" {
|
||||
vlog -sv fpga/verilog/jls_prediction_corrector.sv fpga/sim/tb_jls_prediction_corrector.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run prediction-corrector smoke test" {
|
||||
vsim -c tb_jls_prediction_corrector -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile regular-error-quantizer smoke test" {
|
||||
vlog -sv fpga/verilog/jls_regular_error_quantizer.sv fpga/sim/tb_jls_regular_error_quantizer.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run regular-error-quantizer smoke test" {
|
||||
vsim -c tb_jls_regular_error_quantizer -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile header-writer smoke test" {
|
||||
vlog -sv fpga/verilog/jls_common_pkg.sv fpga/verilog/jls_header_writer.sv fpga/sim/tb_jls_header_writer.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run header-writer smoke test" {
|
||||
vsim -c tb_jls_header_writer -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile near-control smoke test" {
|
||||
vlog -sv fpga/verilog/jls_near_ctrl.sv fpga/sim/tb_jls_near_ctrl.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run near-control smoke test" {
|
||||
vsim -c tb_jls_near_ctrl -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile context-memory smoke test" {
|
||||
vlog -sv fpga/verilog/jls_context_memory.sv fpga/sim/tb_jls_context_memory.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run context-memory smoke test" {
|
||||
vsim -c tb_jls_context_memory -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile context-update smoke test" {
|
||||
vlog -sv fpga/verilog/jls_context_update.sv fpga/sim/tb_jls_context_update.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run context-update smoke test" {
|
||||
vsim -c tb_jls_context_update -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile error-mapper smoke test" {
|
||||
vlog -sv fpga/verilog/jls_error_mapper.sv fpga/sim/tb_jls_error_mapper.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run error-mapper smoke test" {
|
||||
vsim -c tb_jls_error_mapper -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile run-mode smoke test" {
|
||||
vlog -sv fpga/verilog/jls_run_mode.sv fpga/sim/tb_jls_run_mode.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run run-mode smoke test" {
|
||||
vsim -c tb_jls_run_mode -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile Golomb encoder smoke test" {
|
||||
vlog -sv fpga/verilog/jls_golomb_encoder.sv fpga/sim/tb_jls_golomb_encoder.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run Golomb encoder smoke test" {
|
||||
vsim -c tb_jls_golomb_encoder -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile bit-packer smoke test" {
|
||||
vlog -sv fpga/verilog/jls_bit_packer.sv fpga/sim/tb_jls_bit_packer.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run bit-packer smoke test" {
|
||||
vsim -c tb_jls_bit_packer -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile byte-arbiter smoke test" {
|
||||
vlog -sv fpga/verilog/jls_byte_arbiter.sv fpga/sim/tb_jls_byte_arbiter.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run byte-arbiter smoke test" {
|
||||
vsim -c tb_jls_byte_arbiter -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile output-buffer smoke test" {
|
||||
vlog -sv fpga/verilog/jls_output_buffer.sv fpga/sim/tb_jls_output_buffer.sv
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "run output-buffer smoke test" {
|
||||
vsim -c tb_jls_output_buffer -do "run -all; quit"
|
||||
}
|
||||
|
||||
Write-Host "[jls-smoke] PASS"
|
||||
90
fpga/sim/run_jls_throughput_regression.ps1
Normal file
90
fpga/sim/run_jls_throughput_regression.ps1
Normal file
@@ -0,0 +1,90 @@
|
||||
param(
|
||||
# Supported grayscale precisions to exercise. Keep the default narrow enough
|
||||
# for a staged run; full regression should pass -BitsList 8,10,12,14,16.
|
||||
[int[]] $BitsList = @(8),
|
||||
|
||||
# Dynamic-NEAR compression targets covered by the hard throughput requirement.
|
||||
[int[]] $Ratios = @(1, 2, 3),
|
||||
|
||||
# SRS default image size used for the formal throughput test.
|
||||
[int] $Width = 6144,
|
||||
[int] $Height = 256,
|
||||
[int] $ImageCount = 10,
|
||||
[int] $ScanRows = 16,
|
||||
|
||||
# PATTERN=9 rotates ten deterministic representative images.
|
||||
[int] $Pattern = 9,
|
||||
|
||||
# Utility switch for script bring-up on tiny images. Formal throughput runs
|
||||
# must leave this unset.
|
||||
[switch] $SkipThroughputCheck
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
function Invoke-CheckedCommand {
|
||||
param(
|
||||
[string] $Description,
|
||||
[scriptblock] $Command
|
||||
)
|
||||
|
||||
Write-Host "[jls-throughput] $Description"
|
||||
$CommandOutput = & $Command 2>&1
|
||||
$CommandExitCode = $LASTEXITCODE
|
||||
$CommandText = $CommandOutput | Out-String
|
||||
$CommandOutput | ForEach-Object { Write-Host $_ }
|
||||
|
||||
if ($CommandExitCode -ne 0) {
|
||||
exit $CommandExitCode
|
||||
}
|
||||
if (($CommandText -match "\*\* Fatal") -or ($CommandText -match "Errors[=:]\s*[1-9]")) {
|
||||
Write-Error "Command reported simulator errors: $Description"
|
||||
}
|
||||
}
|
||||
|
||||
$OutDir = Join-Path (Resolve-Path ".").Path "tools\jls_compat\out"
|
||||
New-Item -ItemType Directory -Force -Path $OutDir | Out-Null
|
||||
|
||||
$StatsPath = Join-Path $OutDir "rtl_throughput_stats.csv"
|
||||
Set-Content -Encoding ASCII -Path $StatsPath `
|
||||
-Value "case_id,pix_width,pic_col,pic_row,image_count,ratio,pattern,frame_count,output_bytes,input_reads,input_cycles,throughput_mpix_x1000"
|
||||
|
||||
$CheckThroughput = 1
|
||||
if ($SkipThroughputCheck) {
|
||||
$CheckThroughput = 0
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile RTL top throughput regression testbench" {
|
||||
vlog -sv -f fpga/verilog/jpeg_ls_rtl.f fpga/sim/tb_jpeg_ls_encoder_top_run_smoke.sv
|
||||
}
|
||||
|
||||
foreach ($Bits in $BitsList) {
|
||||
foreach ($Ratio in $Ratios) {
|
||||
$CaseId = "throughput_${Bits}b_ratio${Ratio}_${Width}x${Height}_${ImageCount}img"
|
||||
$Stem = "rtl_${CaseId}"
|
||||
$JlsPlusArg = "+OUT=tools/jls_compat/out/$Stem.jls"
|
||||
$StatsPlusArg = "+STATS=tools/jls_compat/out/rtl_throughput_stats.csv"
|
||||
$CasePlusArg = "+CASE=$CaseId"
|
||||
$PatternPlusArg = "+PATTERN=$Pattern"
|
||||
$RatioPlusArg = "+RATIO=$Ratio"
|
||||
|
||||
Invoke-CheckedCommand "run $CaseId" {
|
||||
vsim -c tb_jpeg_ls_encoder_top_run_smoke `
|
||||
"-gPIX_WIDTH=$Bits" `
|
||||
"-gPIC_COL=$Width" `
|
||||
"-gPIC_ROW=$Height" `
|
||||
"-gSCAN_ROWS=$ScanRows" `
|
||||
"-gIMAGE_COUNT=$ImageCount" `
|
||||
$PatternPlusArg `
|
||||
$RatioPlusArg `
|
||||
$JlsPlusArg `
|
||||
$StatsPlusArg `
|
||||
$CasePlusArg `
|
||||
+CHECK_THROUGHPUT=$CheckThroughput `
|
||||
-do "run -all; quit"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "[jls-throughput] Stats: $StatsPath"
|
||||
Write-Host "[jls-throughput] PASS"
|
||||
163
fpga/sim/run_jls_top_compat_smoke.ps1
Normal file
163
fpga/sim/run_jls_top_compat_smoke.ps1
Normal file
@@ -0,0 +1,163 @@
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
function Invoke-CheckedCommand {
|
||||
param(
|
||||
[string] $Description,
|
||||
[scriptblock] $Command
|
||||
)
|
||||
|
||||
Write-Host "[jls-top-compat] $Description"
|
||||
$CommandOutput = & $Command 2>&1
|
||||
$CommandExitCode = $LASTEXITCODE
|
||||
$CommandText = $CommandOutput | Out-String
|
||||
$CommandOutput | ForEach-Object { Write-Host $_ }
|
||||
|
||||
if ($CommandExitCode -ne 0) {
|
||||
exit $CommandExitCode
|
||||
}
|
||||
if (($CommandText -match "\*\* Fatal") -or ($CommandText -match "Errors[=:]\s*[1-9]")) {
|
||||
Write-Error "Command reported simulator errors: $Description"
|
||||
}
|
||||
}
|
||||
|
||||
$OutDir = Join-Path (Resolve-Path ".").Path "tools\jls_compat\out"
|
||||
$DepPath = Join-Path (Resolve-Path ".").Path "tools\jls_compat\.deps"
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $OutDir | Out-Null
|
||||
|
||||
function Write-Pgm {
|
||||
param(
|
||||
[string] $Path,
|
||||
[int] $Pattern,
|
||||
[int] $BitsPerSample,
|
||||
[int] $Width,
|
||||
[int] $Height
|
||||
)
|
||||
|
||||
$MaxValue = (1 -shl $BitsPerSample) - 1
|
||||
$Header = [System.Text.Encoding]::ASCII.GetBytes("P5`n$Width $Height`n$MaxValue`n")
|
||||
|
||||
if ($BitsPerSample -le 8) {
|
||||
$Payload = [byte[]]::new($Width * $Height)
|
||||
} else {
|
||||
$Payload = [byte[]]::new($Width * $Height * 2)
|
||||
}
|
||||
|
||||
for ($Index = 0; $Index -lt ($Width * $Height); $Index = $Index + 1) {
|
||||
$Sample = 0
|
||||
$X = $Index % $Width
|
||||
$Y = [int][Math]::Floor($Index / $Width)
|
||||
|
||||
switch ($Pattern) {
|
||||
1 {
|
||||
$Sample = $Index -band $MaxValue
|
||||
}
|
||||
2 {
|
||||
if ((($X -band 1) -bxor ($Y -band 1)) -ne 0) {
|
||||
$Sample = $MaxValue
|
||||
}
|
||||
}
|
||||
default {
|
||||
$Sample = 0
|
||||
}
|
||||
}
|
||||
|
||||
if ($BitsPerSample -le 8) {
|
||||
$Payload[$Index] = [byte] $Sample
|
||||
} else {
|
||||
$Payload[2 * $Index] = [byte] (($Sample -shr 8) -band 255)
|
||||
$Payload[(2 * $Index) + 1] = [byte] ($Sample -band 255)
|
||||
}
|
||||
}
|
||||
|
||||
$PgmBytes = [byte[]]::new($Header.Length + $Payload.Length)
|
||||
[System.Array]::Copy($Header, 0, $PgmBytes, 0, $Header.Length)
|
||||
[System.Array]::Copy($Payload, 0, $PgmBytes, $Header.Length, $Payload.Length)
|
||||
[System.IO.File]::WriteAllBytes($Path, $PgmBytes)
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "compile RTL top compatibility smoke" {
|
||||
vlog -sv -f fpga/verilog/jpeg_ls_rtl.f fpga/sim/tb_jpeg_ls_encoder_top_run_smoke.sv
|
||||
}
|
||||
|
||||
$env:JLS_COMPAT_PYDEPS = $DepPath
|
||||
|
||||
foreach ($Bits in @(8, 10, 12, 14, 16)) {
|
||||
foreach ($Pattern in @(0, 1)) {
|
||||
$PatternName = "zero"
|
||||
if ($Pattern -eq 1) {
|
||||
$PatternName = "ramp"
|
||||
}
|
||||
|
||||
$Stem = "rtl_top_${PatternName}_${Bits}b"
|
||||
$JlsPath = Join-Path $OutDir "$Stem.jls"
|
||||
$PgmPath = Join-Path $OutDir "$Stem.pgm"
|
||||
$JlsPlusArg = "+OUT=tools/jls_compat/out/$Stem.jls"
|
||||
$PatternPlusArg = "+PATTERN=$Pattern"
|
||||
$WidthGeneric = "-gPIX_WIDTH=$Bits"
|
||||
|
||||
Write-Pgm $PgmPath $Pattern $Bits 16 16
|
||||
|
||||
Invoke-CheckedCommand "run RTL top compatibility smoke: ${Bits}b $PatternName image" {
|
||||
vsim -c tb_jpeg_ls_encoder_top_run_smoke $WidthGeneric $PatternPlusArg $JlsPlusArg -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "decode ${Bits}b $PatternName RTL output with CharLS and compare reference PGM" {
|
||||
python tools/jls_compat/reference_decode_compare.py $JlsPath --reference-pgm $PgmPath --skip-libjpeg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$CheckerStem = "rtl_top_checker_8b"
|
||||
$CheckerJlsPath = Join-Path $OutDir "$CheckerStem.jls"
|
||||
$CheckerPgmPath = Join-Path $OutDir "$CheckerStem.pgm"
|
||||
Write-Pgm $CheckerPgmPath 2 8 16 16
|
||||
|
||||
Invoke-CheckedCommand "run RTL top compatibility smoke: 8b checker image" {
|
||||
vsim -c tb_jpeg_ls_encoder_top_run_smoke -gPIX_WIDTH=8 +PATTERN=2 +OUT=tools/jls_compat/out/$CheckerStem.jls -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "decode 8b checker RTL output with CharLS and compare reference PGM" {
|
||||
python tools/jls_compat/reference_decode_compare.py $CheckerJlsPath --reference-pgm $CheckerPgmPath --skip-libjpeg
|
||||
}
|
||||
|
||||
$MultiStripStem = "rtl_top_ramp_8b_2strip"
|
||||
$MultiStripJlsPath = Join-Path $OutDir "$MultiStripStem.jls"
|
||||
$MultiStripPgmPath = Join-Path $OutDir "$MultiStripStem.pgm"
|
||||
Write-Pgm $MultiStripPgmPath 1 8 16 32
|
||||
|
||||
Invoke-CheckedCommand "run RTL top compatibility smoke: 8b ramp two-strip image" {
|
||||
vsim -c tb_jpeg_ls_encoder_top_run_smoke -gPIX_WIDTH=8 -gPIC_ROW=32 +PATTERN=1 +OUT=tools/jls_compat/out/$MultiStripStem.jls -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "decode 8b ramp two-strip RTL output with CharLS and compare reference PGM" {
|
||||
python tools/jls_compat/reference_decode_compare.py $MultiStripJlsPath --reference-pgm $MultiStripPgmPath --skip-libjpeg --split-frames --expected-frames 2
|
||||
}
|
||||
|
||||
$MultiImageStem = "rtl_top_zero_8b_2image"
|
||||
$MultiImageJlsPath = Join-Path $OutDir "$MultiImageStem.jls"
|
||||
$MultiImagePgmPath = Join-Path $OutDir "$MultiImageStem.pgm"
|
||||
Write-Pgm $MultiImagePgmPath 0 8 16 32
|
||||
|
||||
Invoke-CheckedCommand "run RTL top compatibility smoke: 8b zero two-image stream" {
|
||||
vsim -c tb_jpeg_ls_encoder_top_run_smoke -gPIX_WIDTH=8 -gIMAGE_COUNT=2 +PATTERN=0 +OUT=tools/jls_compat/out/$MultiImageStem.jls -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "decode 8b zero two-image RTL output with CharLS and compare stitched reference PGM" {
|
||||
python tools/jls_compat/reference_decode_compare.py $MultiImageJlsPath --reference-pgm $MultiImagePgmPath --skip-libjpeg --split-frames --expected-frames 2
|
||||
}
|
||||
|
||||
$NearStripStem = "rtl_top_ramp_8b_2strip_ratio2"
|
||||
$NearStripJlsPath = Join-Path $OutDir "$NearStripStem.jls"
|
||||
$NearStripPgmPath = Join-Path $OutDir "$NearStripStem.pgm"
|
||||
Write-Pgm $NearStripPgmPath 1 8 16 32
|
||||
|
||||
Invoke-CheckedCommand "run RTL top compatibility smoke: 8b ramp two-strip ratio=2 image" {
|
||||
vsim -c tb_jpeg_ls_encoder_top_run_smoke -gPIX_WIDTH=8 -gPIC_ROW=32 +PATTERN=1 +RATIO=2 +OUT=tools/jls_compat/out/$NearStripStem.jls -do "run -all; quit"
|
||||
}
|
||||
|
||||
Invoke-CheckedCommand "decode 8b ramp two-strip ratio=2 RTL output with CharLS and compare reference PGM within NEAR bound" {
|
||||
python tools/jls_compat/reference_decode_compare.py $NearStripJlsPath --reference-pgm $NearStripPgmPath --skip-libjpeg --split-frames --expected-frames 2 --max-abs-diff 31
|
||||
}
|
||||
|
||||
Write-Host "[jls-top-compat] PASS"
|
||||
199
fpga/sim/tb_jls_bit_packer.sv
Normal file
199
fpga/sim/tb_jls_bit_packer.sv
Normal file
@@ -0,0 +1,199 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex C.1-C.4 entropy-coded segment syntax
|
||||
// Figure : N/A
|
||||
// Table : N/A
|
||||
// Pseudocode : JPEG-LS bitstream packing and marker/zero-bit stuffing
|
||||
// Example : Checks FF 7F stuffing and partial-byte flush behavior.
|
||||
//
|
||||
// Smoke test for jls_bit_packer.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_bit_packer;
|
||||
|
||||
// Code-event width used by the smoke test.
|
||||
localparam int MAX_CODE_BITS = 64;
|
||||
|
||||
// Expected byte count:
|
||||
// A5, FF, 7F, A0, FF, 00
|
||||
localparam int EXPECTED_BYTE_COUNT = 6;
|
||||
|
||||
// Main simulation clock and reset.
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
// Code input interface.
|
||||
logic code_valid;
|
||||
logic code_ready;
|
||||
logic [MAX_CODE_BITS-1:0] code_bits;
|
||||
logic [6:0] code_bit_count;
|
||||
|
||||
// Flush interface.
|
||||
logic flush_valid;
|
||||
logic flush_ready;
|
||||
logic flush_done;
|
||||
|
||||
// Byte output interface.
|
||||
logic byte_valid;
|
||||
logic byte_ready;
|
||||
logic [7:0] byte_data;
|
||||
|
||||
// Scoreboard state.
|
||||
logic [7:0] expected_byte_mem [0:EXPECTED_BYTE_COUNT-1];
|
||||
int receive_index;
|
||||
int flush_done_count;
|
||||
logic done_seen;
|
||||
|
||||
jls_bit_packer #(
|
||||
.MAX_CODE_BITS(MAX_CODE_BITS)
|
||||
) dut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.code_valid(code_valid),
|
||||
.code_ready(code_ready),
|
||||
.code_bits(code_bits),
|
||||
.code_bit_count(code_bit_count),
|
||||
.flush_valid(flush_valid),
|
||||
.flush_ready(flush_ready),
|
||||
.flush_done(flush_done),
|
||||
.byte_valid(byte_valid),
|
||||
.byte_ready(byte_ready),
|
||||
.byte_data(byte_data)
|
||||
);
|
||||
|
||||
always begin
|
||||
#2 clk = ~clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
expected_byte_mem[0] = 8'hA5;
|
||||
expected_byte_mem[1] = 8'hFF;
|
||||
expected_byte_mem[2] = 8'h7F;
|
||||
expected_byte_mem[3] = 8'hA0;
|
||||
expected_byte_mem[4] = 8'hFF;
|
||||
expected_byte_mem[5] = 8'h00;
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
code_valid = 1'b0;
|
||||
code_bits = {MAX_CODE_BITS{1'b0}};
|
||||
code_bit_count = 7'd0;
|
||||
flush_valid = 1'b0;
|
||||
byte_ready = 1'b1;
|
||||
|
||||
repeat (5) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
|
||||
wait (code_ready);
|
||||
@(posedge clk);
|
||||
code_valid = 1'b1;
|
||||
code_bits = 64'hA500_0000_0000_0000;
|
||||
code_bit_count = 7'd8;
|
||||
@(posedge clk);
|
||||
code_valid = 1'b0;
|
||||
code_bits = 64'h0000_0000_0000_0000;
|
||||
code_bit_count = 7'd0;
|
||||
|
||||
wait (code_ready);
|
||||
@(posedge clk);
|
||||
code_valid = 1'b1;
|
||||
code_bits = 64'hFF00_0000_0000_0000;
|
||||
code_bit_count = 7'd8;
|
||||
@(posedge clk);
|
||||
code_valid = 1'b0;
|
||||
code_bits = 64'h0000_0000_0000_0000;
|
||||
code_bit_count = 7'd0;
|
||||
|
||||
wait (code_ready);
|
||||
@(posedge clk);
|
||||
code_valid = 1'b1;
|
||||
code_bits = 64'hFE00_0000_0000_0000;
|
||||
code_bit_count = 7'd7;
|
||||
@(posedge clk);
|
||||
code_valid = 1'b0;
|
||||
code_bits = 64'h0000_0000_0000_0000;
|
||||
code_bit_count = 7'd0;
|
||||
|
||||
wait (code_ready);
|
||||
@(posedge clk);
|
||||
code_valid = 1'b1;
|
||||
code_bits = 64'hA000_0000_0000_0000;
|
||||
code_bit_count = 7'd4;
|
||||
@(posedge clk);
|
||||
code_valid = 1'b0;
|
||||
code_bits = 64'h0000_0000_0000_0000;
|
||||
code_bit_count = 7'd0;
|
||||
|
||||
wait (flush_ready);
|
||||
@(posedge clk);
|
||||
flush_valid = 1'b1;
|
||||
@(posedge clk);
|
||||
flush_valid = 1'b0;
|
||||
|
||||
wait (flush_done_count == 1);
|
||||
|
||||
wait (code_ready);
|
||||
@(posedge clk);
|
||||
code_valid = 1'b1;
|
||||
code_bits = 64'hFF00_0000_0000_0000;
|
||||
code_bit_count = 7'd8;
|
||||
@(posedge clk);
|
||||
code_valid = 1'b0;
|
||||
code_bits = 64'h0000_0000_0000_0000;
|
||||
code_bit_count = 7'd0;
|
||||
|
||||
wait (flush_ready);
|
||||
@(posedge clk);
|
||||
flush_valid = 1'b1;
|
||||
@(posedge clk);
|
||||
flush_valid = 1'b0;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
receive_index <= 0;
|
||||
flush_done_count <= 0;
|
||||
done_seen <= 1'b0;
|
||||
end else begin
|
||||
if (byte_valid && byte_ready) begin
|
||||
if (receive_index >= EXPECTED_BYTE_COUNT) begin
|
||||
$fatal(1, "Unexpected extra packed byte 0x%02h", byte_data);
|
||||
end
|
||||
|
||||
if (byte_data !== expected_byte_mem[receive_index]) begin
|
||||
$fatal(1, "packed byte mismatch at %0d: got 0x%02h expected 0x%02h",
|
||||
receive_index, byte_data, expected_byte_mem[receive_index]);
|
||||
end
|
||||
|
||||
receive_index <= receive_index + 1;
|
||||
end
|
||||
|
||||
if (flush_done) begin
|
||||
flush_done_count <= flush_done_count + 1;
|
||||
if (flush_done_count == 1) begin
|
||||
done_seen <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (2000) @(posedge clk);
|
||||
$fatal(1, "Timeout waiting for bit packer smoke");
|
||||
end
|
||||
|
||||
initial begin
|
||||
wait (done_seen);
|
||||
repeat (4) @(posedge clk);
|
||||
if (receive_index !== EXPECTED_BYTE_COUNT || flush_done_count !== 2) begin
|
||||
$fatal(1, "bit packer smoke count mismatch");
|
||||
end
|
||||
$display("PASS: tb_jls_bit_packer");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
78
fpga/sim/tb_jls_byte_arbiter.sv
Normal file
78
fpga/sim/tb_jls_byte_arbiter.sv
Normal file
@@ -0,0 +1,78 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex C.1-C.4 marker stream byte order
|
||||
// Figure : N/A
|
||||
// Table : N/A
|
||||
// Pseudocode : Marker bytes and entropy-coded bytes in stream order
|
||||
// Example : Header byte has priority over a waiting payload byte.
|
||||
//
|
||||
// Smoke test for jls_byte_arbiter.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_byte_arbiter;
|
||||
|
||||
logic header_valid;
|
||||
logic header_ready;
|
||||
logic [7:0] header_data;
|
||||
logic header_original_image_start;
|
||||
logic payload_valid;
|
||||
logic payload_ready;
|
||||
logic [7:0] payload_data;
|
||||
logic byte_valid;
|
||||
logic byte_ready;
|
||||
logic [7:0] byte_data;
|
||||
logic original_image_start;
|
||||
|
||||
jls_byte_arbiter dut (
|
||||
.header_valid(header_valid),
|
||||
.header_ready(header_ready),
|
||||
.header_data(header_data),
|
||||
.header_original_image_start(header_original_image_start),
|
||||
.payload_valid(payload_valid),
|
||||
.payload_ready(payload_ready),
|
||||
.payload_data(payload_data),
|
||||
.byte_valid(byte_valid),
|
||||
.byte_ready(byte_ready),
|
||||
.byte_data(byte_data),
|
||||
.original_image_start(original_image_start)
|
||||
);
|
||||
|
||||
initial begin
|
||||
header_valid = 1'b0;
|
||||
header_data = 8'h00;
|
||||
header_original_image_start = 1'b0;
|
||||
payload_valid = 1'b0;
|
||||
payload_data = 8'h00;
|
||||
byte_ready = 1'b1;
|
||||
|
||||
#1;
|
||||
payload_valid = 1'b1;
|
||||
payload_data = 8'h55;
|
||||
#1;
|
||||
if (!byte_valid || !payload_ready || header_ready ||
|
||||
byte_data !== 8'h55 || original_image_start !== 1'b0) begin
|
||||
$fatal(1, "payload-only arbitration mismatch");
|
||||
end
|
||||
|
||||
header_valid = 1'b1;
|
||||
header_data = 8'hFF;
|
||||
header_original_image_start = 1'b1;
|
||||
#1;
|
||||
if (!byte_valid || !header_ready || payload_ready ||
|
||||
byte_data !== 8'hFF || original_image_start !== 1'b1) begin
|
||||
$fatal(1, "header-priority arbitration mismatch");
|
||||
end
|
||||
|
||||
byte_ready = 1'b0;
|
||||
#1;
|
||||
if (header_ready || payload_ready) begin
|
||||
$fatal(1, "ready should be low when downstream stalls");
|
||||
end
|
||||
|
||||
$display("PASS: tb_jls_byte_arbiter");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
129
fpga/sim/tb_jls_coding_params.sv
Normal file
129
fpga/sim/tb_jls_coding_params.sv
Normal file
@@ -0,0 +1,129 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex A.2 initialization, Annex G.2 variables
|
||||
// Figure : N/A
|
||||
// Table : N/A
|
||||
// Pseudocode : RANGE, qbpp, and LIMIT derivation from MAXVAL and NEAR
|
||||
// Example : Checks representative PIX_WIDTH and NEAR table entries.
|
||||
//
|
||||
// Smoke test for jls_coding_params.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_coding_params;
|
||||
|
||||
// NEAR stimulus shared by all parameterized instances.
|
||||
logic [5:0] near_8;
|
||||
logic [5:0] near_10;
|
||||
logic [5:0] near_12;
|
||||
logic [5:0] near_14;
|
||||
logic [5:0] near_16;
|
||||
|
||||
// DUT outputs.
|
||||
logic [16:0] range_8;
|
||||
logic [16:0] range_10;
|
||||
logic [16:0] range_12;
|
||||
logic [16:0] range_14;
|
||||
logic [16:0] range_16;
|
||||
logic [4:0] qbpp_8;
|
||||
logic [4:0] qbpp_10;
|
||||
logic [4:0] qbpp_12;
|
||||
logic [4:0] qbpp_14;
|
||||
logic [4:0] qbpp_16;
|
||||
logic [6:0] limit_8;
|
||||
logic [6:0] limit_10;
|
||||
logic [6:0] limit_12;
|
||||
logic [6:0] limit_14;
|
||||
logic [6:0] limit_16;
|
||||
|
||||
jls_coding_params #(
|
||||
.PIX_WIDTH(8)
|
||||
) dut_8 (
|
||||
.NEAR(near_8),
|
||||
.RANGE(range_8),
|
||||
.qbpp(qbpp_8),
|
||||
.LIMIT(limit_8)
|
||||
);
|
||||
|
||||
jls_coding_params #(
|
||||
.PIX_WIDTH(10)
|
||||
) dut_10 (
|
||||
.NEAR(near_10),
|
||||
.RANGE(range_10),
|
||||
.qbpp(qbpp_10),
|
||||
.LIMIT(limit_10)
|
||||
);
|
||||
|
||||
jls_coding_params #(
|
||||
.PIX_WIDTH(12)
|
||||
) dut_12 (
|
||||
.NEAR(near_12),
|
||||
.RANGE(range_12),
|
||||
.qbpp(qbpp_12),
|
||||
.LIMIT(limit_12)
|
||||
);
|
||||
|
||||
jls_coding_params #(
|
||||
.PIX_WIDTH(14)
|
||||
) dut_14 (
|
||||
.NEAR(near_14),
|
||||
.RANGE(range_14),
|
||||
.qbpp(qbpp_14),
|
||||
.LIMIT(limit_14)
|
||||
);
|
||||
|
||||
jls_coding_params #(
|
||||
.PIX_WIDTH(16)
|
||||
) dut_16 (
|
||||
.NEAR(near_16),
|
||||
.RANGE(range_16),
|
||||
.qbpp(qbpp_16),
|
||||
.LIMIT(limit_16)
|
||||
);
|
||||
|
||||
initial begin
|
||||
near_8 = 6'd0;
|
||||
near_10 = 6'd16;
|
||||
near_12 = 6'd31;
|
||||
near_14 = 6'd8;
|
||||
near_16 = 6'd0;
|
||||
#1;
|
||||
|
||||
if (range_8 !== 17'd256 || qbpp_8 !== 5'd8 || limit_8 !== 7'd32) begin
|
||||
$fatal(1, "8-bit NEAR=0 coding params mismatch");
|
||||
end
|
||||
|
||||
if (range_10 !== 17'd32 || qbpp_10 !== 5'd5 || limit_10 !== 7'd40) begin
|
||||
$fatal(1, "10-bit NEAR=16 coding params mismatch");
|
||||
end
|
||||
|
||||
if (range_12 !== 17'd66 || qbpp_12 !== 5'd7 || limit_12 !== 7'd48) begin
|
||||
$fatal(1, "12-bit NEAR=31 coding params mismatch");
|
||||
end
|
||||
|
||||
if (range_14 !== 17'd965 || qbpp_14 !== 5'd10 || limit_14 !== 7'd56) begin
|
||||
$fatal(1, "14-bit NEAR=8 coding params mismatch");
|
||||
end
|
||||
|
||||
if (range_16 !== 17'd65536 || qbpp_16 !== 5'd16 || limit_16 !== 7'd64) begin
|
||||
$fatal(1, "16-bit NEAR=0 coding params mismatch");
|
||||
end
|
||||
|
||||
near_8 = 6'd63;
|
||||
near_16 = 6'd31;
|
||||
#1;
|
||||
|
||||
if (range_8 !== 17'd6 || qbpp_8 !== 5'd3 || limit_8 !== 7'd32) begin
|
||||
$fatal(1, "8-bit defensive NEAR clamp mismatch");
|
||||
end
|
||||
|
||||
if (range_16 !== 17'd1042 || qbpp_16 !== 5'd11 || limit_16 !== 7'd64) begin
|
||||
$fatal(1, "16-bit NEAR=31 coding params mismatch");
|
||||
end
|
||||
|
||||
$display("PASS: tb_jls_coding_params");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
193
fpga/sim/tb_jls_context_memory.sv
Normal file
193
fpga/sim/tb_jls_context_memory.sv
Normal file
@@ -0,0 +1,193 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex A.2 initialization, Annex A.6 variables update
|
||||
// Figure : N/A
|
||||
// Table : N/A
|
||||
// Pseudocode : Regular-mode context arrays A/B/C/N
|
||||
// Example : RANGE=256 initializes A to 4 for all 365 contexts.
|
||||
//
|
||||
// Smoke test for jls_context_memory.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_context_memory;
|
||||
|
||||
// Main simulation clock and reset.
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
// Initialization interface.
|
||||
logic init_valid;
|
||||
logic init_ready;
|
||||
logic [16:0] init_RANGE;
|
||||
logic init_busy;
|
||||
logic init_done;
|
||||
|
||||
// Read interface.
|
||||
logic read_valid;
|
||||
logic read_ready;
|
||||
logic [8:0] read_context_index;
|
||||
logic read_result_valid;
|
||||
logic read_result_ready;
|
||||
logic [8:0] read_result_context_index;
|
||||
logic [31:0] read_A;
|
||||
logic signed [31:0] read_B;
|
||||
logic signed [8:0] read_C;
|
||||
logic [15:0] read_N;
|
||||
|
||||
// Write interface.
|
||||
logic write_valid;
|
||||
logic write_ready;
|
||||
logic [8:0] write_context_index;
|
||||
logic [31:0] write_A;
|
||||
logic signed [31:0] write_B;
|
||||
logic signed [8:0] write_C;
|
||||
logic [15:0] write_N;
|
||||
|
||||
logic all_done_seen;
|
||||
|
||||
jls_context_memory dut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.init_valid(init_valid),
|
||||
.init_ready(init_ready),
|
||||
.init_RANGE(init_RANGE),
|
||||
.init_busy(init_busy),
|
||||
.init_done(init_done),
|
||||
.read_valid(read_valid),
|
||||
.read_ready(read_ready),
|
||||
.read_context_index(read_context_index),
|
||||
.read_result_valid(read_result_valid),
|
||||
.read_result_ready(read_result_ready),
|
||||
.read_result_context_index(read_result_context_index),
|
||||
.read_A(read_A),
|
||||
.read_B(read_B),
|
||||
.read_C(read_C),
|
||||
.read_N(read_N),
|
||||
.write_valid(write_valid),
|
||||
.write_ready(write_ready),
|
||||
.write_context_index(write_context_index),
|
||||
.write_A(write_A),
|
||||
.write_B(write_B),
|
||||
.write_C(write_C),
|
||||
.write_N(write_N)
|
||||
);
|
||||
|
||||
always begin
|
||||
#2 clk = ~clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
init_valid = 1'b0;
|
||||
init_RANGE = 17'd0;
|
||||
read_valid = 1'b0;
|
||||
read_context_index = 9'd0;
|
||||
read_result_ready = 1'b1;
|
||||
write_valid = 1'b0;
|
||||
write_context_index = 9'd0;
|
||||
write_A = 32'd0;
|
||||
write_B = 32'sd0;
|
||||
write_C = 9'sd0;
|
||||
write_N = 16'd0;
|
||||
all_done_seen = 1'b0;
|
||||
|
||||
repeat (5) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
|
||||
wait (init_ready);
|
||||
@(posedge clk);
|
||||
init_valid = 1'b1;
|
||||
init_RANGE = 17'd256;
|
||||
@(posedge clk);
|
||||
init_valid = 1'b0;
|
||||
init_RANGE = 17'd65536;
|
||||
|
||||
wait (init_done);
|
||||
repeat (2) @(posedge clk);
|
||||
|
||||
read_context_index = 9'd0;
|
||||
read_valid = 1'b1;
|
||||
@(posedge clk);
|
||||
read_valid = 1'b0;
|
||||
wait (read_result_valid);
|
||||
if (read_result_context_index !== 9'd0 ||
|
||||
read_A !== 32'd4 || read_B !== 32'sd0 ||
|
||||
read_C !== 9'sd0 || read_N !== 16'd1) begin
|
||||
$fatal(1, "context 0 init read mismatch");
|
||||
end
|
||||
@(posedge clk);
|
||||
|
||||
read_context_index = 9'd364;
|
||||
read_valid = 1'b1;
|
||||
@(posedge clk);
|
||||
read_valid = 1'b0;
|
||||
wait (read_result_valid);
|
||||
if (read_result_context_index !== 9'd364 ||
|
||||
read_A !== 32'd4 || read_B !== 32'sd0 ||
|
||||
read_C !== 9'sd0 || read_N !== 16'd1) begin
|
||||
$fatal(1, "context 364 init read mismatch");
|
||||
end
|
||||
@(posedge clk);
|
||||
|
||||
wait (write_ready);
|
||||
@(posedge clk);
|
||||
write_valid = 1'b1;
|
||||
write_context_index = 9'd5;
|
||||
write_A = 32'd7;
|
||||
write_B = -32'sd2;
|
||||
write_C = 9'sd1;
|
||||
write_N = 16'd3;
|
||||
@(posedge clk);
|
||||
write_valid = 1'b0;
|
||||
|
||||
read_context_index = 9'd5;
|
||||
read_valid = 1'b1;
|
||||
@(posedge clk);
|
||||
read_valid = 1'b0;
|
||||
wait (read_result_valid);
|
||||
if (read_result_context_index !== 9'd5 ||
|
||||
read_A !== 32'd7 || read_B !== -32'sd2 ||
|
||||
read_C !== 9'sd1 || read_N !== 16'd3) begin
|
||||
$fatal(1, "context 5 write/read mismatch");
|
||||
end
|
||||
@(posedge clk);
|
||||
|
||||
wait (init_ready);
|
||||
@(posedge clk);
|
||||
init_valid = 1'b1;
|
||||
init_RANGE = 17'd65536;
|
||||
@(posedge clk);
|
||||
init_valid = 1'b0;
|
||||
wait (init_done);
|
||||
repeat (2) @(posedge clk);
|
||||
|
||||
read_context_index = 9'd5;
|
||||
read_valid = 1'b1;
|
||||
@(posedge clk);
|
||||
read_valid = 1'b0;
|
||||
wait (read_result_valid);
|
||||
if (read_result_context_index !== 9'd5 ||
|
||||
read_A !== 32'd1024 || read_B !== 32'sd0 ||
|
||||
read_C !== 9'sd0 || read_N !== 16'd1) begin
|
||||
$fatal(1, "context 5 re-init mismatch");
|
||||
end
|
||||
|
||||
all_done_seen = 1'b1;
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (3000) @(posedge clk);
|
||||
$fatal(1, "Timeout waiting for context memory smoke");
|
||||
end
|
||||
|
||||
initial begin
|
||||
wait (all_done_seen);
|
||||
repeat (4) @(posedge clk);
|
||||
$display("PASS: tb_jls_context_memory");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
286
fpga/sim/tb_jls_context_model.sv
Normal file
286
fpga/sim/tb_jls_context_model.sv
Normal file
@@ -0,0 +1,286 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex A.2 initialization, Annex A.3 context determination
|
||||
// Figure : N/A
|
||||
// Table : N/A
|
||||
// Pseudocode : Read regular-mode context variables A/B/C/N for context Q
|
||||
// Example : Reads context 5 before and after writeback.
|
||||
//
|
||||
// Smoke test for jls_context_model.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_context_model;
|
||||
|
||||
// Test precision.
|
||||
localparam int PIX_WIDTH = 8;
|
||||
|
||||
// Main simulation clock and reset.
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
// Init interface.
|
||||
logic init_valid;
|
||||
logic init_ready;
|
||||
logic [16:0] init_RANGE;
|
||||
logic init_busy;
|
||||
logic init_done;
|
||||
|
||||
// Context input.
|
||||
logic context_valid;
|
||||
logic context_ready;
|
||||
logic [PIX_WIDTH-1:0] context_sample;
|
||||
logic [12:0] context_x;
|
||||
logic [12:0] context_y;
|
||||
logic context_strip_first_pixel;
|
||||
logic context_strip_last_pixel;
|
||||
logic [PIX_WIDTH-1:0] context_Px;
|
||||
logic [8:0] context_index;
|
||||
logic context_negative;
|
||||
logic run_mode_context;
|
||||
|
||||
// Context variable output.
|
||||
logic context_vars_valid;
|
||||
logic context_vars_ready;
|
||||
logic [PIX_WIDTH-1:0] vars_sample;
|
||||
logic [12:0] vars_x;
|
||||
logic [12:0] vars_y;
|
||||
logic vars_strip_first_pixel;
|
||||
logic vars_strip_last_pixel;
|
||||
logic [PIX_WIDTH-1:0] vars_Px;
|
||||
logic [8:0] vars_context_index;
|
||||
logic vars_context_negative;
|
||||
logic vars_run_mode_context;
|
||||
logic [31:0] vars_A;
|
||||
logic signed [31:0] vars_B;
|
||||
logic signed [8:0] vars_C;
|
||||
logic [15:0] vars_N;
|
||||
|
||||
// Writeback interface.
|
||||
logic write_valid;
|
||||
logic write_ready;
|
||||
logic [8:0] write_context_index;
|
||||
logic [31:0] write_A;
|
||||
logic signed [31:0] write_B;
|
||||
logic signed [8:0] write_C;
|
||||
logic [15:0] write_N;
|
||||
|
||||
logic all_done_seen;
|
||||
|
||||
jls_context_model #(
|
||||
.PIX_WIDTH(PIX_WIDTH)
|
||||
) dut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.init_valid(init_valid),
|
||||
.init_ready(init_ready),
|
||||
.init_RANGE(init_RANGE),
|
||||
.init_busy(init_busy),
|
||||
.init_done(init_done),
|
||||
.context_valid(context_valid),
|
||||
.context_ready(context_ready),
|
||||
.context_sample(context_sample),
|
||||
.context_x(context_x),
|
||||
.context_y(context_y),
|
||||
.context_strip_first_pixel(context_strip_first_pixel),
|
||||
.context_strip_last_pixel(context_strip_last_pixel),
|
||||
.context_Px(context_Px),
|
||||
.context_index(context_index),
|
||||
.context_negative(context_negative),
|
||||
.run_mode_context(run_mode_context),
|
||||
.context_vars_valid(context_vars_valid),
|
||||
.context_vars_ready(context_vars_ready),
|
||||
.vars_sample(vars_sample),
|
||||
.vars_x(vars_x),
|
||||
.vars_y(vars_y),
|
||||
.vars_strip_first_pixel(vars_strip_first_pixel),
|
||||
.vars_strip_last_pixel(vars_strip_last_pixel),
|
||||
.vars_Px(vars_Px),
|
||||
.vars_context_index(vars_context_index),
|
||||
.vars_context_negative(vars_context_negative),
|
||||
.vars_run_mode_context(vars_run_mode_context),
|
||||
.vars_A(vars_A),
|
||||
.vars_B(vars_B),
|
||||
.vars_C(vars_C),
|
||||
.vars_N(vars_N),
|
||||
.write_valid(write_valid),
|
||||
.write_ready(write_ready),
|
||||
.write_context_index(write_context_index),
|
||||
.write_A(write_A),
|
||||
.write_B(write_B),
|
||||
.write_C(write_C),
|
||||
.write_N(write_N)
|
||||
);
|
||||
|
||||
always begin
|
||||
#2 clk = ~clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
init_valid = 1'b0;
|
||||
init_RANGE = 17'd0;
|
||||
context_valid = 1'b0;
|
||||
context_sample = 8'd0;
|
||||
context_x = 13'd0;
|
||||
context_y = 13'd0;
|
||||
context_strip_first_pixel = 1'b0;
|
||||
context_strip_last_pixel = 1'b0;
|
||||
context_Px = 8'd0;
|
||||
context_index = 9'd0;
|
||||
context_negative = 1'b0;
|
||||
run_mode_context = 1'b0;
|
||||
context_vars_ready = 1'b1;
|
||||
write_valid = 1'b0;
|
||||
write_context_index = 9'd0;
|
||||
write_A = 32'd0;
|
||||
write_B = 32'sd0;
|
||||
write_C = 9'sd0;
|
||||
write_N = 16'd0;
|
||||
all_done_seen = 1'b0;
|
||||
|
||||
repeat (5) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
|
||||
wait (init_ready);
|
||||
@(posedge clk);
|
||||
init_valid = 1'b1;
|
||||
init_RANGE = 17'd256;
|
||||
@(posedge clk);
|
||||
init_valid = 1'b0;
|
||||
wait (init_done);
|
||||
repeat (2) @(posedge clk);
|
||||
|
||||
@(negedge clk);
|
||||
context_valid = 1'b1;
|
||||
context_sample = 8'd33;
|
||||
context_x = 13'd2;
|
||||
context_y = 13'd3;
|
||||
context_strip_first_pixel = 1'b1;
|
||||
context_Px = 8'd31;
|
||||
context_index = 9'd5;
|
||||
context_negative = 1'b0;
|
||||
run_mode_context = 1'b0;
|
||||
wait (context_ready);
|
||||
@(posedge clk);
|
||||
context_valid = 1'b0;
|
||||
context_strip_first_pixel = 1'b0;
|
||||
|
||||
wait (context_vars_valid);
|
||||
#1;
|
||||
if (vars_context_index !== 9'd5 || vars_sample !== 8'd33 ||
|
||||
vars_x !== 13'd2 || vars_y !== 13'd3 ||
|
||||
vars_Px !== 8'd31 || vars_A !== 32'd4 ||
|
||||
vars_B !== 32'sd0 || vars_C !== 9'sd0 || vars_N !== 16'd1 ||
|
||||
vars_strip_first_pixel !== 1'b1) begin
|
||||
$fatal(1, "first context_model read mismatch");
|
||||
end
|
||||
wait (!context_vars_valid);
|
||||
repeat (2) @(posedge clk);
|
||||
|
||||
wait (write_ready);
|
||||
@(posedge clk);
|
||||
write_valid = 1'b1;
|
||||
write_context_index = 9'd5;
|
||||
write_A = 32'd7;
|
||||
write_B = -32'sd2;
|
||||
write_C = 9'sd3;
|
||||
write_N = 16'd4;
|
||||
@(posedge clk);
|
||||
write_valid = 1'b0;
|
||||
|
||||
@(negedge clk);
|
||||
context_valid = 1'b1;
|
||||
context_sample = 8'd44;
|
||||
context_x = 13'd4;
|
||||
context_y = 13'd5;
|
||||
context_Px = 8'd40;
|
||||
context_index = 9'd5;
|
||||
context_negative = 1'b1;
|
||||
wait (context_ready);
|
||||
@(posedge clk);
|
||||
context_valid = 1'b0;
|
||||
|
||||
wait (context_vars_valid && vars_sample == 8'd44);
|
||||
#1;
|
||||
if (vars_context_index !== 9'd5 || vars_sample !== 8'd44 ||
|
||||
vars_x !== 13'd4 || vars_y !== 13'd5 ||
|
||||
vars_Px !== 8'd40 || vars_A !== 32'd7 ||
|
||||
vars_B !== -32'sd2 || vars_C !== 9'sd3 || vars_N !== 16'd4 ||
|
||||
vars_context_negative !== 1'b1) begin
|
||||
$fatal(1, "second context_model read mismatch");
|
||||
end
|
||||
wait (!context_vars_valid);
|
||||
repeat (2) @(posedge clk);
|
||||
|
||||
@(negedge clk);
|
||||
context_valid = 1'b1;
|
||||
context_sample = 8'd55;
|
||||
context_x = 13'd6;
|
||||
context_y = 13'd7;
|
||||
context_Px = 8'd50;
|
||||
context_index = 9'd6;
|
||||
context_negative = 1'b0;
|
||||
wait (context_ready);
|
||||
@(posedge clk);
|
||||
context_valid = 1'b0;
|
||||
|
||||
wait (context_vars_valid && vars_sample == 8'd55);
|
||||
#1;
|
||||
if (vars_context_index !== 9'd6 || vars_A !== 32'd4 ||
|
||||
vars_B !== 32'sd0 || vars_C !== 9'sd0 || vars_N !== 16'd1) begin
|
||||
$fatal(1, "third context_model read mismatch");
|
||||
end
|
||||
wait (!context_vars_valid);
|
||||
repeat (2) @(posedge clk);
|
||||
|
||||
@(negedge clk);
|
||||
context_valid = 1'b1;
|
||||
context_sample = 8'd66;
|
||||
context_x = 13'd8;
|
||||
context_y = 13'd9;
|
||||
context_Px = 8'd60;
|
||||
context_index = 9'd6;
|
||||
wait (context_ready);
|
||||
@(posedge clk);
|
||||
context_valid = 1'b0;
|
||||
repeat (2) @(posedge clk);
|
||||
if (context_vars_valid && vars_sample == 8'd66) begin
|
||||
$fatal(1, "same-context hazard read returned before writeback");
|
||||
end
|
||||
|
||||
@(negedge clk);
|
||||
write_valid = 1'b1;
|
||||
write_context_index = 9'd6;
|
||||
write_A = 32'd11;
|
||||
write_B = -32'sd5;
|
||||
write_C = 9'sd4;
|
||||
write_N = 16'd8;
|
||||
@(posedge clk);
|
||||
write_valid = 1'b0;
|
||||
|
||||
wait (context_vars_valid && vars_sample == 8'd66);
|
||||
#1;
|
||||
if (vars_context_index !== 9'd6 || vars_A !== 32'd11 ||
|
||||
vars_B !== -32'sd5 || vars_C !== 9'sd4 || vars_N !== 16'd8) begin
|
||||
$fatal(1, "same-cycle bypass context_model read mismatch");
|
||||
end
|
||||
|
||||
all_done_seen = 1'b1;
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (3000) @(posedge clk);
|
||||
$fatal(1, "Timeout waiting for context model smoke");
|
||||
end
|
||||
|
||||
initial begin
|
||||
wait (all_done_seen);
|
||||
repeat (4) @(posedge clk);
|
||||
$display("PASS: tb_jls_context_model");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
282
fpga/sim/tb_jls_context_quantizer.sv
Normal file
282
fpga/sim/tb_jls_context_quantizer.sv
Normal file
@@ -0,0 +1,282 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex A.3 context determination, Annex G.1 variables
|
||||
// Figure : N/A
|
||||
// Table : N/A
|
||||
// Pseudocode : Quantize D1/D2/D3 into Q1/Q2/Q3 and compute context ID
|
||||
// Example : Checks zero, positive, negative, and NEAR-zero gradients.
|
||||
//
|
||||
// Smoke test for jls_context_quantizer.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_context_quantizer;
|
||||
|
||||
// Test precision.
|
||||
localparam int PIX_WIDTH = 16;
|
||||
|
||||
// Expected context events.
|
||||
localparam int EXPECTED_EVENT_COUNT = 4;
|
||||
|
||||
// Main simulation clock and reset.
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
// Predicted input event.
|
||||
logic predict_valid;
|
||||
logic predict_ready;
|
||||
logic [PIX_WIDTH-1:0] predict_sample;
|
||||
logic [12:0] predict_x;
|
||||
logic [12:0] predict_y;
|
||||
logic predict_strip_first_pixel;
|
||||
logic predict_strip_last_pixel;
|
||||
logic [PIX_WIDTH-1:0] Ra;
|
||||
logic [PIX_WIDTH-1:0] Rb;
|
||||
logic [PIX_WIDTH-1:0] Rc;
|
||||
logic [PIX_WIDTH-1:0] Rd;
|
||||
logic [PIX_WIDTH-1:0] Px;
|
||||
logic [15:0] T1;
|
||||
logic [15:0] T2;
|
||||
logic [15:0] T3;
|
||||
logic [5:0] NEAR;
|
||||
|
||||
// Quantized context output event.
|
||||
logic context_valid;
|
||||
logic context_ready;
|
||||
logic [PIX_WIDTH-1:0] context_sample;
|
||||
logic [12:0] context_x;
|
||||
logic [12:0] context_y;
|
||||
logic context_strip_first_pixel;
|
||||
logic context_strip_last_pixel;
|
||||
logic [PIX_WIDTH-1:0] context_Px;
|
||||
logic [PIX_WIDTH-1:0] context_Ra;
|
||||
logic [PIX_WIDTH-1:0] context_Rb;
|
||||
logic [PIX_WIDTH-1:0] context_Rc;
|
||||
logic [PIX_WIDTH-1:0] context_Rd;
|
||||
logic signed [3:0] Q1;
|
||||
logic signed [3:0] Q2;
|
||||
logic signed [3:0] Q3;
|
||||
logic [8:0] context_index;
|
||||
logic context_negative;
|
||||
logic run_mode_context;
|
||||
|
||||
// Scoreboard state.
|
||||
logic signed [3:0] expected_q1_mem [0:EXPECTED_EVENT_COUNT-1];
|
||||
logic signed [3:0] expected_q2_mem [0:EXPECTED_EVENT_COUNT-1];
|
||||
logic signed [3:0] expected_q3_mem [0:EXPECTED_EVENT_COUNT-1];
|
||||
logic [8:0] expected_index_mem [0:EXPECTED_EVENT_COUNT-1];
|
||||
logic expected_negative_mem [0:EXPECTED_EVENT_COUNT-1];
|
||||
logic expected_run_mem [0:EXPECTED_EVENT_COUNT-1];
|
||||
int receive_index;
|
||||
logic all_done_seen;
|
||||
|
||||
jls_context_quantizer #(
|
||||
.PIX_WIDTH(PIX_WIDTH)
|
||||
) dut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.predict_valid(predict_valid),
|
||||
.predict_ready(predict_ready),
|
||||
.predict_sample(predict_sample),
|
||||
.predict_x(predict_x),
|
||||
.predict_y(predict_y),
|
||||
.predict_strip_first_pixel(predict_strip_first_pixel),
|
||||
.predict_strip_last_pixel(predict_strip_last_pixel),
|
||||
.Ra(Ra),
|
||||
.Rb(Rb),
|
||||
.Rc(Rc),
|
||||
.Rd(Rd),
|
||||
.Px(Px),
|
||||
.T1(T1),
|
||||
.T2(T2),
|
||||
.T3(T3),
|
||||
.NEAR(NEAR),
|
||||
.context_valid(context_valid),
|
||||
.context_ready(context_ready),
|
||||
.context_sample(context_sample),
|
||||
.context_x(context_x),
|
||||
.context_y(context_y),
|
||||
.context_strip_first_pixel(context_strip_first_pixel),
|
||||
.context_strip_last_pixel(context_strip_last_pixel),
|
||||
.context_Px(context_Px),
|
||||
.context_Ra(context_Ra),
|
||||
.context_Rb(context_Rb),
|
||||
.context_Rc(context_Rc),
|
||||
.context_Rd(context_Rd),
|
||||
.Q1(Q1),
|
||||
.Q2(Q2),
|
||||
.Q3(Q3),
|
||||
.context_index(context_index),
|
||||
.context_negative(context_negative),
|
||||
.run_mode_context(run_mode_context)
|
||||
);
|
||||
|
||||
always begin
|
||||
#2 clk = ~clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
expected_q1_mem[0] = 4'sd0;
|
||||
expected_q2_mem[0] = 4'sd0;
|
||||
expected_q3_mem[0] = 4'sd0;
|
||||
expected_index_mem[0] = 9'd0;
|
||||
expected_negative_mem[0] = 1'b0;
|
||||
expected_run_mem[0] = 1'b1;
|
||||
|
||||
expected_q1_mem[1] = 4'sd4;
|
||||
expected_q2_mem[1] = 4'sd3;
|
||||
expected_q3_mem[1] = 4'sd1;
|
||||
expected_index_mem[1] = 9'd352;
|
||||
expected_negative_mem[1] = 1'b0;
|
||||
expected_run_mem[1] = 1'b0;
|
||||
|
||||
expected_q1_mem[2] = -4'sd4;
|
||||
expected_q2_mem[2] = -4'sd3;
|
||||
expected_q3_mem[2] = -4'sd2;
|
||||
expected_index_mem[2] = 9'd353;
|
||||
expected_negative_mem[2] = 1'b1;
|
||||
expected_run_mem[2] = 1'b0;
|
||||
|
||||
expected_q1_mem[3] = 4'sd0;
|
||||
expected_q2_mem[3] = 4'sd1;
|
||||
expected_q3_mem[3] = -4'sd1;
|
||||
expected_index_mem[3] = 9'd8;
|
||||
expected_negative_mem[3] = 1'b0;
|
||||
expected_run_mem[3] = 1'b0;
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
predict_valid = 1'b0;
|
||||
predict_sample = 16'd0;
|
||||
predict_x = 13'd0;
|
||||
predict_y = 13'd0;
|
||||
predict_strip_first_pixel = 1'b0;
|
||||
predict_strip_last_pixel = 1'b0;
|
||||
Ra = 16'd0;
|
||||
Rb = 16'd0;
|
||||
Rc = 16'd0;
|
||||
Rd = 16'd0;
|
||||
Px = 16'd0;
|
||||
T1 = 16'd3;
|
||||
T2 = 16'd7;
|
||||
T3 = 16'd21;
|
||||
NEAR = 6'd0;
|
||||
context_ready = 1'b1;
|
||||
all_done_seen = 1'b0;
|
||||
|
||||
repeat (5) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
|
||||
wait (predict_ready);
|
||||
@(posedge clk);
|
||||
predict_valid = 1'b1;
|
||||
predict_sample = 16'd100;
|
||||
Ra = 16'd10;
|
||||
Rb = 16'd10;
|
||||
Rc = 16'd10;
|
||||
Rd = 16'd10;
|
||||
Px = 16'd10;
|
||||
@(posedge clk);
|
||||
predict_valid = 1'b0;
|
||||
|
||||
wait (predict_ready);
|
||||
@(posedge clk);
|
||||
predict_valid = 1'b1;
|
||||
predict_sample = 16'd101;
|
||||
predict_x = 13'd1;
|
||||
Ra = 16'd0;
|
||||
Rb = 16'd10;
|
||||
Rc = 16'd2;
|
||||
Rd = 16'd32;
|
||||
Px = 16'd15;
|
||||
@(posedge clk);
|
||||
predict_valid = 1'b0;
|
||||
|
||||
wait (predict_ready);
|
||||
@(posedge clk);
|
||||
predict_valid = 1'b1;
|
||||
predict_sample = 16'd102;
|
||||
predict_x = 13'd2;
|
||||
Ra = 16'd34;
|
||||
Rb = 16'd22;
|
||||
Rc = 16'd30;
|
||||
Rd = 16'd0;
|
||||
Px = 16'd20;
|
||||
@(posedge clk);
|
||||
predict_valid = 1'b0;
|
||||
|
||||
wait (predict_ready);
|
||||
@(posedge clk);
|
||||
predict_valid = 1'b1;
|
||||
predict_sample = 16'd103;
|
||||
predict_x = 13'd3;
|
||||
predict_strip_last_pixel = 1'b1;
|
||||
Ra = 16'd10;
|
||||
Rb = 16'd10;
|
||||
Rc = 16'd7;
|
||||
Rd = 16'd12;
|
||||
Px = 16'd9;
|
||||
T1 = 16'd7;
|
||||
T2 = 16'd10;
|
||||
T3 = 16'd25;
|
||||
NEAR = 6'd2;
|
||||
@(posedge clk);
|
||||
predict_valid = 1'b0;
|
||||
predict_strip_last_pixel = 1'b0;
|
||||
|
||||
wait (receive_index == EXPECTED_EVENT_COUNT);
|
||||
all_done_seen = 1'b1;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
receive_index <= 0;
|
||||
end else if (context_valid && context_ready) begin
|
||||
if (receive_index >= EXPECTED_EVENT_COUNT) begin
|
||||
$fatal(1, "Unexpected extra context event");
|
||||
end
|
||||
|
||||
if (Q1 !== expected_q1_mem[receive_index] ||
|
||||
Q2 !== expected_q2_mem[receive_index] ||
|
||||
Q3 !== expected_q3_mem[receive_index]) begin
|
||||
$fatal(1, "Q mismatch at %0d: got %0d,%0d,%0d",
|
||||
receive_index, Q1, Q2, Q3);
|
||||
end
|
||||
|
||||
if (context_index !== expected_index_mem[receive_index]) begin
|
||||
$fatal(1, "context_index mismatch at %0d: got %0d expected %0d",
|
||||
receive_index, context_index, expected_index_mem[receive_index]);
|
||||
end
|
||||
|
||||
if (context_negative !== expected_negative_mem[receive_index]) begin
|
||||
$fatal(1, "context_negative mismatch at %0d", receive_index);
|
||||
end
|
||||
|
||||
if (run_mode_context !== expected_run_mem[receive_index]) begin
|
||||
$fatal(1, "run_mode_context mismatch at %0d", receive_index);
|
||||
end
|
||||
|
||||
if (context_sample !== (16'd100 + receive_index[15:0])) begin
|
||||
$fatal(1, "context_sample mismatch at %0d", receive_index);
|
||||
end
|
||||
|
||||
receive_index <= receive_index + 1;
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (2000) @(posedge clk);
|
||||
$fatal(1, "Timeout waiting for context quantizer smoke");
|
||||
end
|
||||
|
||||
initial begin
|
||||
wait (all_done_seen);
|
||||
repeat (4) @(posedge clk);
|
||||
$display("PASS: tb_jls_context_quantizer");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
233
fpga/sim/tb_jls_context_update.sv
Normal file
233
fpga/sim/tb_jls_context_update.sv
Normal file
@@ -0,0 +1,233 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex A.5 Golomb parameter, Annex A.6 variables update
|
||||
// Figure : N/A
|
||||
// Table : N/A
|
||||
// Pseudocode : Compute k and update regular-mode A/B/C/N
|
||||
// Example : Checks positive, negative, RESET, and C saturation updates.
|
||||
//
|
||||
// Smoke test for jls_context_update.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_context_update;
|
||||
|
||||
// Expected update events.
|
||||
localparam int EXPECTED_EVENT_COUNT = 4;
|
||||
|
||||
// Main simulation clock and reset.
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
// Input update event.
|
||||
logic update_valid;
|
||||
logic update_ready;
|
||||
logic [31:0] A_in;
|
||||
logic signed [31:0] B_in;
|
||||
logic signed [8:0] C_in;
|
||||
logic [15:0] N_in;
|
||||
logic signed [31:0] Errval;
|
||||
logic [8:0] context_index_in;
|
||||
logic strip_last_pixel_in;
|
||||
logic [4:0] qbpp_in;
|
||||
logic [6:0] LIMIT_in;
|
||||
logic [5:0] NEAR;
|
||||
logic [15:0] RESET;
|
||||
|
||||
// Output update event.
|
||||
logic result_valid;
|
||||
logic result_ready;
|
||||
logic [4:0] k;
|
||||
logic signed [31:0] Errval_out;
|
||||
logic [8:0] context_index_out;
|
||||
logic strip_last_pixel_out;
|
||||
logic [4:0] qbpp_out;
|
||||
logic [6:0] LIMIT_out;
|
||||
logic map_invert;
|
||||
logic [31:0] A_out;
|
||||
logic signed [31:0] B_out;
|
||||
logic signed [8:0] C_out;
|
||||
logic [15:0] N_out;
|
||||
|
||||
// Scoreboard state.
|
||||
logic [4:0] expected_k_mem [0:EXPECTED_EVENT_COUNT-1];
|
||||
logic [31:0] expected_a_mem [0:EXPECTED_EVENT_COUNT-1];
|
||||
logic signed [31:0] expected_b_mem [0:EXPECTED_EVENT_COUNT-1];
|
||||
logic signed [8:0] expected_c_mem [0:EXPECTED_EVENT_COUNT-1];
|
||||
logic [15:0] expected_n_mem [0:EXPECTED_EVENT_COUNT-1];
|
||||
int receive_index;
|
||||
logic all_done_seen;
|
||||
|
||||
jls_context_update dut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.update_valid(update_valid),
|
||||
.update_ready(update_ready),
|
||||
.A_in(A_in),
|
||||
.B_in(B_in),
|
||||
.C_in(C_in),
|
||||
.N_in(N_in),
|
||||
.Errval(Errval),
|
||||
.context_index_in(context_index_in),
|
||||
.strip_last_pixel_in(strip_last_pixel_in),
|
||||
.qbpp_in(qbpp_in),
|
||||
.LIMIT_in(LIMIT_in),
|
||||
.NEAR(NEAR),
|
||||
.RESET(RESET),
|
||||
.result_valid(result_valid),
|
||||
.result_ready(result_ready),
|
||||
.k(k),
|
||||
.Errval_out(Errval_out),
|
||||
.context_index_out(context_index_out),
|
||||
.strip_last_pixel_out(strip_last_pixel_out),
|
||||
.qbpp_out(qbpp_out),
|
||||
.LIMIT_out(LIMIT_out),
|
||||
.map_invert(map_invert),
|
||||
.A_out(A_out),
|
||||
.B_out(B_out),
|
||||
.C_out(C_out),
|
||||
.N_out(N_out)
|
||||
);
|
||||
|
||||
always begin
|
||||
#2 clk = ~clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
expected_k_mem[0] = 5'd2;
|
||||
expected_a_mem[0] = 32'd7;
|
||||
expected_b_mem[0] = 32'sd0;
|
||||
expected_c_mem[0] = 9'sd1;
|
||||
expected_n_mem[0] = 16'd2;
|
||||
|
||||
expected_k_mem[1] = 5'd2;
|
||||
expected_a_mem[1] = 32'd15;
|
||||
expected_b_mem[1] = -32'sd4;
|
||||
expected_c_mem[1] = -9'sd1;
|
||||
expected_n_mem[1] = 16'd5;
|
||||
|
||||
expected_k_mem[2] = 5'd1;
|
||||
expected_a_mem[2] = 32'd52;
|
||||
expected_b_mem[2] = -32'sd21;
|
||||
expected_c_mem[2] = 9'sd6;
|
||||
expected_n_mem[2] = 16'd33;
|
||||
|
||||
expected_k_mem[3] = 5'd0;
|
||||
expected_a_mem[3] = 32'd1;
|
||||
expected_b_mem[3] = 32'sd0;
|
||||
expected_c_mem[3] = 9'sd127;
|
||||
expected_n_mem[3] = 16'd2;
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
update_valid = 1'b0;
|
||||
A_in = 32'd0;
|
||||
B_in = 32'sd0;
|
||||
C_in = 9'sd0;
|
||||
N_in = 16'd1;
|
||||
Errval = 32'sd0;
|
||||
context_index_in = 9'd0;
|
||||
strip_last_pixel_in = 1'b0;
|
||||
qbpp_in = 5'd8;
|
||||
LIMIT_in = 7'd32;
|
||||
NEAR = 6'd0;
|
||||
RESET = 16'd64;
|
||||
result_ready = 1'b1;
|
||||
all_done_seen = 1'b0;
|
||||
|
||||
repeat (5) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
|
||||
wait (update_ready);
|
||||
@(posedge clk);
|
||||
update_valid = 1'b1;
|
||||
A_in = 32'd4;
|
||||
B_in = 32'sd0;
|
||||
C_in = 9'sd0;
|
||||
N_in = 16'd1;
|
||||
Errval = 32'sd3;
|
||||
NEAR = 6'd0;
|
||||
@(posedge clk);
|
||||
update_valid = 1'b0;
|
||||
|
||||
wait (update_ready);
|
||||
@(posedge clk);
|
||||
update_valid = 1'b1;
|
||||
A_in = 32'd10;
|
||||
B_in = -32'sd1;
|
||||
C_in = 9'sd0;
|
||||
N_in = 16'd4;
|
||||
Errval = -32'sd5;
|
||||
NEAR = 6'd1;
|
||||
@(posedge clk);
|
||||
update_valid = 1'b0;
|
||||
|
||||
wait (update_ready);
|
||||
@(posedge clk);
|
||||
update_valid = 1'b1;
|
||||
A_in = 32'd100;
|
||||
B_in = 32'sd20;
|
||||
C_in = 9'sd5;
|
||||
N_in = 16'd64;
|
||||
Errval = 32'sd4;
|
||||
NEAR = 6'd0;
|
||||
@(posedge clk);
|
||||
update_valid = 1'b0;
|
||||
|
||||
wait (update_ready);
|
||||
@(posedge clk);
|
||||
update_valid = 1'b1;
|
||||
A_in = 32'd1;
|
||||
B_in = 32'sd10;
|
||||
C_in = 9'sd127;
|
||||
N_in = 16'd1;
|
||||
Errval = 32'sd0;
|
||||
NEAR = 6'd0;
|
||||
@(posedge clk);
|
||||
update_valid = 1'b0;
|
||||
|
||||
wait (receive_index == EXPECTED_EVENT_COUNT);
|
||||
all_done_seen = 1'b1;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
receive_index <= 0;
|
||||
end else if (result_valid && result_ready) begin
|
||||
if (receive_index >= EXPECTED_EVENT_COUNT) begin
|
||||
$fatal(1, "Unexpected extra context update event");
|
||||
end
|
||||
|
||||
if (k !== expected_k_mem[receive_index]) begin
|
||||
$fatal(1, "k mismatch at %0d: got %0d expected %0d",
|
||||
receive_index, k, expected_k_mem[receive_index]);
|
||||
end
|
||||
|
||||
if (A_out !== expected_a_mem[receive_index] ||
|
||||
B_out !== expected_b_mem[receive_index] ||
|
||||
C_out !== expected_c_mem[receive_index] ||
|
||||
N_out !== expected_n_mem[receive_index]) begin
|
||||
$fatal(1, "context update mismatch at %0d: A=%0d B=%0d C=%0d N=%0d",
|
||||
receive_index, A_out, B_out, C_out, N_out);
|
||||
end
|
||||
|
||||
receive_index <= receive_index + 1;
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (2000) @(posedge clk);
|
||||
$fatal(1, "Timeout waiting for context update smoke");
|
||||
end
|
||||
|
||||
initial begin
|
||||
wait (all_done_seen);
|
||||
repeat (4) @(posedge clk);
|
||||
$display("PASS: tb_jls_context_update");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
195
fpga/sim/tb_jls_error_mapper.sv
Normal file
195
fpga/sim/tb_jls_error_mapper.sv
Normal file
@@ -0,0 +1,195 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex A.5 prediction error encoding
|
||||
// Figure : N/A
|
||||
// Table : N/A
|
||||
// Pseudocode : Map signed Errval into non-negative MErrval
|
||||
// Example : Checks positive, negative, and context-inverted values.
|
||||
//
|
||||
// Smoke test for jls_error_mapper.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_error_mapper;
|
||||
|
||||
// Expected mapped-error events.
|
||||
localparam int EXPECTED_EVENT_COUNT = 4;
|
||||
|
||||
// Main simulation clock and reset.
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
// Error input interface.
|
||||
logic err_valid;
|
||||
logic err_ready;
|
||||
logic signed [31:0] Errval;
|
||||
logic map_invert;
|
||||
logic [4:0] k;
|
||||
logic [6:0] limit;
|
||||
logic [4:0] qbpp;
|
||||
logic strip_last_pixel;
|
||||
|
||||
// Mapped output interface.
|
||||
logic mapped_valid;
|
||||
logic mapped_ready;
|
||||
logic [31:0] MErrval;
|
||||
logic [4:0] mapped_k;
|
||||
logic [6:0] mapped_limit;
|
||||
logic [4:0] mapped_qbpp;
|
||||
logic mapped_strip_last_pixel;
|
||||
|
||||
// Scoreboard state.
|
||||
logic [31:0] expected_merrval_mem [0:EXPECTED_EVENT_COUNT-1];
|
||||
logic [4:0] expected_k_mem [0:EXPECTED_EVENT_COUNT-1];
|
||||
logic [6:0] expected_limit_mem [0:EXPECTED_EVENT_COUNT-1];
|
||||
logic [4:0] expected_qbpp_mem [0:EXPECTED_EVENT_COUNT-1];
|
||||
int receive_index;
|
||||
logic all_done_seen;
|
||||
|
||||
jls_error_mapper dut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.err_valid(err_valid),
|
||||
.err_ready(err_ready),
|
||||
.Errval(Errval),
|
||||
.map_invert(map_invert),
|
||||
.k(k),
|
||||
.limit(limit),
|
||||
.qbpp(qbpp),
|
||||
.strip_last_pixel(strip_last_pixel),
|
||||
.mapped_valid(mapped_valid),
|
||||
.mapped_ready(mapped_ready),
|
||||
.MErrval(MErrval),
|
||||
.mapped_k(mapped_k),
|
||||
.mapped_limit(mapped_limit),
|
||||
.mapped_qbpp(mapped_qbpp),
|
||||
.mapped_strip_last_pixel(mapped_strip_last_pixel)
|
||||
);
|
||||
|
||||
always begin
|
||||
#2 clk = ~clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
expected_merrval_mem[0] = 32'd6;
|
||||
expected_k_mem[0] = 5'd1;
|
||||
expected_limit_mem[0] = 7'd32;
|
||||
expected_qbpp_mem[0] = 5'd8;
|
||||
|
||||
expected_merrval_mem[1] = 32'd5;
|
||||
expected_k_mem[1] = 5'd2;
|
||||
expected_limit_mem[1] = 7'd32;
|
||||
expected_qbpp_mem[1] = 5'd8;
|
||||
|
||||
expected_merrval_mem[2] = 32'd7;
|
||||
expected_k_mem[2] = 5'd3;
|
||||
expected_limit_mem[2] = 7'd31;
|
||||
expected_qbpp_mem[2] = 5'd8;
|
||||
|
||||
expected_merrval_mem[3] = 32'd4;
|
||||
expected_k_mem[3] = 5'd4;
|
||||
expected_limit_mem[3] = 7'd30;
|
||||
expected_qbpp_mem[3] = 5'd7;
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
err_valid = 1'b0;
|
||||
Errval = 32'sd0;
|
||||
map_invert = 1'b0;
|
||||
k = 5'd0;
|
||||
limit = 7'd0;
|
||||
qbpp = 5'd0;
|
||||
strip_last_pixel = 1'b0;
|
||||
mapped_ready = 1'b1;
|
||||
all_done_seen = 1'b0;
|
||||
|
||||
repeat (5) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
|
||||
wait (err_ready);
|
||||
@(posedge clk);
|
||||
err_valid = 1'b1;
|
||||
Errval = 32'sd3;
|
||||
map_invert = 1'b0;
|
||||
k = 5'd1;
|
||||
limit = 7'd32;
|
||||
qbpp = 5'd8;
|
||||
@(posedge clk);
|
||||
err_valid = 1'b0;
|
||||
|
||||
wait (err_ready);
|
||||
@(posedge clk);
|
||||
err_valid = 1'b1;
|
||||
Errval = -32'sd3;
|
||||
map_invert = 1'b0;
|
||||
k = 5'd2;
|
||||
limit = 7'd32;
|
||||
qbpp = 5'd8;
|
||||
@(posedge clk);
|
||||
err_valid = 1'b0;
|
||||
|
||||
wait (err_ready);
|
||||
@(posedge clk);
|
||||
err_valid = 1'b1;
|
||||
Errval = 32'sd3;
|
||||
map_invert = 1'b1;
|
||||
k = 5'd3;
|
||||
limit = 7'd31;
|
||||
qbpp = 5'd8;
|
||||
@(posedge clk);
|
||||
err_valid = 1'b0;
|
||||
|
||||
wait (err_ready);
|
||||
@(posedge clk);
|
||||
err_valid = 1'b1;
|
||||
Errval = -32'sd3;
|
||||
map_invert = 1'b1;
|
||||
k = 5'd4;
|
||||
limit = 7'd30;
|
||||
qbpp = 5'd7;
|
||||
@(posedge clk);
|
||||
err_valid = 1'b0;
|
||||
|
||||
wait (receive_index == EXPECTED_EVENT_COUNT);
|
||||
all_done_seen = 1'b1;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
receive_index <= 0;
|
||||
end else if (mapped_valid && mapped_ready) begin
|
||||
if (receive_index >= EXPECTED_EVENT_COUNT) begin
|
||||
$fatal(1, "Unexpected extra mapped-error event");
|
||||
end
|
||||
|
||||
if (MErrval !== expected_merrval_mem[receive_index]) begin
|
||||
$fatal(1, "MErrval mismatch at %0d: got %0d expected %0d",
|
||||
receive_index, MErrval, expected_merrval_mem[receive_index]);
|
||||
end
|
||||
|
||||
if (mapped_k !== expected_k_mem[receive_index] ||
|
||||
mapped_limit !== expected_limit_mem[receive_index] ||
|
||||
mapped_qbpp !== expected_qbpp_mem[receive_index]) begin
|
||||
$fatal(1, "forwarded Golomb parameter mismatch at %0d", receive_index);
|
||||
end
|
||||
|
||||
receive_index <= receive_index + 1;
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (2000) @(posedge clk);
|
||||
$fatal(1, "Timeout waiting for error mapper smoke");
|
||||
end
|
||||
|
||||
initial begin
|
||||
wait (all_done_seen);
|
||||
repeat (4) @(posedge clk);
|
||||
$display("PASS: tb_jls_error_mapper");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
192
fpga/sim/tb_jls_golomb_encoder.sv
Normal file
192
fpga/sim/tb_jls_golomb_encoder.sv
Normal file
@@ -0,0 +1,192 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex A.5 prediction error encoding, Annex G.2 variables
|
||||
// Figure : N/A
|
||||
// Table : N/A
|
||||
// Pseudocode : Golomb coding of MErrval with k, LIMIT, and qbpp
|
||||
// Example : MErrval=5,k=1 emits prefix bits 0,0,1 then suffix bit 1.
|
||||
//
|
||||
// Smoke test for jls_golomb_encoder. The test watches code events directly:
|
||||
// 1. regular Golomb path with a non-zero k,
|
||||
// 2. regular path with k=0 and no suffix,
|
||||
// 3. LIMIT path with a qbpp-wide suffix.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_golomb_encoder;
|
||||
|
||||
// Code-event width used by the smoke test.
|
||||
localparam int MAX_CODE_BITS = 64;
|
||||
|
||||
// Expected direct code-event count across all three mapped-error samples.
|
||||
localparam int EXPECTED_EVENT_COUNT = 5;
|
||||
|
||||
// Main simulation clock and reset.
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
// Mapped-error input interface.
|
||||
logic mapped_valid;
|
||||
logic mapped_ready;
|
||||
logic [31:0] MErrval;
|
||||
logic [4:0] k;
|
||||
logic [6:0] limit;
|
||||
logic [4:0] qbpp;
|
||||
logic mapped_strip_last_pixel;
|
||||
|
||||
// Code-event output interface.
|
||||
logic code_valid;
|
||||
logic code_ready;
|
||||
logic [MAX_CODE_BITS-1:0] code_bits;
|
||||
logic [6:0] code_bit_count;
|
||||
logic mapped_done;
|
||||
logic mapped_last_done;
|
||||
|
||||
// Scoreboard state.
|
||||
logic [MAX_CODE_BITS-1:0] expected_bits_mem [0:EXPECTED_EVENT_COUNT-1];
|
||||
logic [6:0] expected_count_mem [0:EXPECTED_EVENT_COUNT-1];
|
||||
int receive_index;
|
||||
int done_count;
|
||||
logic all_done_seen;
|
||||
|
||||
jls_golomb_encoder #(
|
||||
.MAX_CODE_BITS(MAX_CODE_BITS)
|
||||
) dut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.mapped_valid(mapped_valid),
|
||||
.mapped_ready(mapped_ready),
|
||||
.MErrval(MErrval),
|
||||
.k(k),
|
||||
.limit(limit),
|
||||
.qbpp(qbpp),
|
||||
.mapped_strip_last_pixel(mapped_strip_last_pixel),
|
||||
.code_valid(code_valid),
|
||||
.code_ready(code_ready),
|
||||
.code_bits(code_bits),
|
||||
.code_bit_count(code_bit_count),
|
||||
.mapped_done(mapped_done),
|
||||
.mapped_last_done(mapped_last_done)
|
||||
);
|
||||
|
||||
always begin
|
||||
#2 clk = ~clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
// Regular path, MErrval=5,k=1:
|
||||
// high_bits=2 -> prefix 0,0,1; suffix is the low one bit, 1.
|
||||
expected_bits_mem[0] = 64'h2000_0000_0000_0000;
|
||||
expected_count_mem[0] = 7'd3;
|
||||
expected_bits_mem[1] = 64'h8000_0000_0000_0000;
|
||||
expected_count_mem[1] = 7'd1;
|
||||
|
||||
// Regular path, MErrval=0,k=0: prefix is just the terminating one bit.
|
||||
expected_bits_mem[2] = 64'h8000_0000_0000_0000;
|
||||
expected_count_mem[2] = 7'd1;
|
||||
|
||||
// LIMIT path, MErrval=5,limit=10,qbpp=8:
|
||||
// limit-qbpp=2 -> prefix 0,1; qbpp suffix uses MErrval-1 = 4.
|
||||
expected_bits_mem[3] = 64'h4000_0000_0000_0000;
|
||||
expected_count_mem[3] = 7'd2;
|
||||
expected_bits_mem[4] = 64'h0400_0000_0000_0000;
|
||||
expected_count_mem[4] = 7'd8;
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
mapped_valid = 1'b0;
|
||||
MErrval = 32'd0;
|
||||
k = 5'd0;
|
||||
limit = 7'd32;
|
||||
qbpp = 5'd8;
|
||||
mapped_strip_last_pixel = 1'b0;
|
||||
code_ready = 1'b1;
|
||||
all_done_seen = 1'b0;
|
||||
|
||||
repeat (5) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
|
||||
wait (mapped_ready);
|
||||
@(posedge clk);
|
||||
MErrval = 32'd5;
|
||||
k = 5'd1;
|
||||
limit = 7'd32;
|
||||
qbpp = 5'd8;
|
||||
mapped_valid = 1'b1;
|
||||
@(posedge clk);
|
||||
mapped_valid = 1'b0;
|
||||
wait (done_count == 1);
|
||||
|
||||
wait (mapped_ready);
|
||||
@(posedge clk);
|
||||
MErrval = 32'd0;
|
||||
k = 5'd0;
|
||||
limit = 7'd32;
|
||||
qbpp = 5'd8;
|
||||
mapped_valid = 1'b1;
|
||||
@(posedge clk);
|
||||
mapped_valid = 1'b0;
|
||||
wait (done_count == 2);
|
||||
|
||||
wait (mapped_ready);
|
||||
@(posedge clk);
|
||||
MErrval = 32'd5;
|
||||
k = 5'd0;
|
||||
limit = 7'd10;
|
||||
qbpp = 5'd8;
|
||||
mapped_valid = 1'b1;
|
||||
@(posedge clk);
|
||||
mapped_valid = 1'b0;
|
||||
wait (done_count == 3);
|
||||
|
||||
all_done_seen = 1'b1;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
receive_index <= 0;
|
||||
done_count <= 0;
|
||||
end else begin
|
||||
if (code_valid && code_ready) begin
|
||||
if (receive_index >= EXPECTED_EVENT_COUNT) begin
|
||||
$fatal(1, "Unexpected extra Golomb code event");
|
||||
end
|
||||
|
||||
if (code_bit_count !== expected_count_mem[receive_index]) begin
|
||||
$fatal(1, "Golomb code count mismatch at %0d: got %0d expected %0d",
|
||||
receive_index, code_bit_count, expected_count_mem[receive_index]);
|
||||
end
|
||||
|
||||
if (code_bits !== expected_bits_mem[receive_index]) begin
|
||||
$fatal(1, "Golomb code bits mismatch at %0d: got 0x%016h expected 0x%016h",
|
||||
receive_index, code_bits, expected_bits_mem[receive_index]);
|
||||
end
|
||||
|
||||
receive_index <= receive_index + 1;
|
||||
end
|
||||
|
||||
if (mapped_done) begin
|
||||
done_count <= done_count + 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (2000) @(posedge clk);
|
||||
$fatal(1, "Timeout waiting for Golomb encoder smoke");
|
||||
end
|
||||
|
||||
initial begin
|
||||
wait (all_done_seen);
|
||||
repeat (4) @(posedge clk);
|
||||
if (receive_index !== EXPECTED_EVENT_COUNT || done_count !== 3) begin
|
||||
$fatal(1, "Golomb encoder smoke count mismatch");
|
||||
end
|
||||
$display("PASS: tb_jls_golomb_encoder");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
215
fpga/sim/tb_jls_header_writer.sv
Normal file
215
fpga/sim/tb_jls_header_writer.sv
Normal file
@@ -0,0 +1,215 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex C.2.2 frame header, C.2.3 scan header, C.2.4.1 LSE
|
||||
// Figure : N/A
|
||||
// Table : Table C.1 preset parameters, Table C.2 RESET, Table C.3 defaults
|
||||
// Pseudocode : JPEG-LS marker segment emission before and after one scan
|
||||
// Example : 8-bit 32x16 strip with NEAR=0 and default LSE parameters.
|
||||
//
|
||||
// Smoke test for jls_header_writer. The test checks exact marker bytes for a
|
||||
// single standalone strip frame header and the trailing EOI marker.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_header_writer;
|
||||
|
||||
// Test precision. 8-bit defaults use MAXVAL=255, T1=3, T2=7, T3=21.
|
||||
localparam int PIX_WIDTH = 8;
|
||||
|
||||
// Expected stream is header bytes plus EOI after a finish command.
|
||||
localparam int HEADER_BYTE_COUNT = 40;
|
||||
localparam int TOTAL_BYTE_COUNT = 42;
|
||||
|
||||
// Main simulation clock and reset.
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
// Header writer command inputs.
|
||||
logic strip_start_valid;
|
||||
logic strip_start_ready;
|
||||
logic original_image_first_strip;
|
||||
logic [12:0] strip_width;
|
||||
logic [12:0] strip_height;
|
||||
logic [5:0] near;
|
||||
logic [15:0] preset_maxval;
|
||||
logic [15:0] preset_t1;
|
||||
logic [15:0] preset_t2;
|
||||
logic [15:0] preset_t3;
|
||||
logic [15:0] preset_reset;
|
||||
logic strip_finish_valid;
|
||||
logic strip_finish_ready;
|
||||
|
||||
// Byte output under test.
|
||||
logic byte_valid;
|
||||
logic byte_ready;
|
||||
logic [7:0] byte_data;
|
||||
logic original_image_start;
|
||||
logic header_done;
|
||||
logic eoi_done;
|
||||
|
||||
// Expected byte stream and scoreboard state.
|
||||
logic [7:0] expected_mem [0:TOTAL_BYTE_COUNT-1];
|
||||
int byte_count;
|
||||
logic header_done_seen;
|
||||
logic done_seen;
|
||||
|
||||
jls_header_writer #(
|
||||
.PIX_WIDTH(PIX_WIDTH)
|
||||
) dut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.strip_start_valid(strip_start_valid),
|
||||
.strip_start_ready(strip_start_ready),
|
||||
.original_image_first_strip(original_image_first_strip),
|
||||
.strip_width(strip_width),
|
||||
.strip_height(strip_height),
|
||||
.near(near),
|
||||
.preset_maxval(preset_maxval),
|
||||
.preset_t1(preset_t1),
|
||||
.preset_t2(preset_t2),
|
||||
.preset_t3(preset_t3),
|
||||
.preset_reset(preset_reset),
|
||||
.strip_finish_valid(strip_finish_valid),
|
||||
.strip_finish_ready(strip_finish_ready),
|
||||
.byte_valid(byte_valid),
|
||||
.byte_ready(byte_ready),
|
||||
.byte_data(byte_data),
|
||||
.original_image_start(original_image_start),
|
||||
.header_done(header_done),
|
||||
.eoi_done(eoi_done)
|
||||
);
|
||||
|
||||
always begin
|
||||
#2 clk = ~clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
expected_mem[0] = 8'hFF;
|
||||
expected_mem[1] = 8'hD8;
|
||||
expected_mem[2] = 8'hFF;
|
||||
expected_mem[3] = 8'hF7;
|
||||
expected_mem[4] = 8'h00;
|
||||
expected_mem[5] = 8'h0B;
|
||||
expected_mem[6] = 8'h08;
|
||||
expected_mem[7] = 8'h00;
|
||||
expected_mem[8] = 8'h10;
|
||||
expected_mem[9] = 8'h00;
|
||||
expected_mem[10] = 8'h20;
|
||||
expected_mem[11] = 8'h01;
|
||||
expected_mem[12] = 8'h01;
|
||||
expected_mem[13] = 8'h11;
|
||||
expected_mem[14] = 8'h00;
|
||||
expected_mem[15] = 8'hFF;
|
||||
expected_mem[16] = 8'hF8;
|
||||
expected_mem[17] = 8'h00;
|
||||
expected_mem[18] = 8'h0D;
|
||||
expected_mem[19] = 8'h01;
|
||||
expected_mem[20] = 8'h00;
|
||||
expected_mem[21] = 8'hFF;
|
||||
expected_mem[22] = 8'h00;
|
||||
expected_mem[23] = 8'h03;
|
||||
expected_mem[24] = 8'h00;
|
||||
expected_mem[25] = 8'h07;
|
||||
expected_mem[26] = 8'h00;
|
||||
expected_mem[27] = 8'h15;
|
||||
expected_mem[28] = 8'h00;
|
||||
expected_mem[29] = 8'h40;
|
||||
expected_mem[30] = 8'hFF;
|
||||
expected_mem[31] = 8'hDA;
|
||||
expected_mem[32] = 8'h00;
|
||||
expected_mem[33] = 8'h08;
|
||||
expected_mem[34] = 8'h01;
|
||||
expected_mem[35] = 8'h01;
|
||||
expected_mem[36] = 8'h00;
|
||||
expected_mem[37] = 8'h00;
|
||||
expected_mem[38] = 8'h00;
|
||||
expected_mem[39] = 8'h00;
|
||||
expected_mem[40] = 8'hFF;
|
||||
expected_mem[41] = 8'hD9;
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
strip_start_valid = 1'b0;
|
||||
original_image_first_strip = 1'b1;
|
||||
strip_width = 13'd32;
|
||||
strip_height = 13'd16;
|
||||
near = 6'd0;
|
||||
preset_maxval = 16'd255;
|
||||
preset_t1 = 16'd3;
|
||||
preset_t2 = 16'd7;
|
||||
preset_t3 = 16'd21;
|
||||
preset_reset = 16'd64;
|
||||
strip_finish_valid = 1'b0;
|
||||
byte_ready = 1'b1;
|
||||
|
||||
repeat (5) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
|
||||
repeat (2) @(posedge clk);
|
||||
strip_start_valid = 1'b1;
|
||||
@(posedge clk);
|
||||
strip_start_valid = 1'b0;
|
||||
|
||||
wait (header_done_seen);
|
||||
repeat (2) @(posedge clk);
|
||||
strip_finish_valid = 1'b1;
|
||||
@(posedge clk);
|
||||
strip_finish_valid = 1'b0;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
byte_count <= 0;
|
||||
header_done_seen <= 1'b0;
|
||||
done_seen <= 1'b0;
|
||||
end else if (byte_valid && byte_ready) begin
|
||||
if (byte_count >= TOTAL_BYTE_COUNT) begin
|
||||
$fatal(1, "Unexpected extra byte 0x%02h", byte_data);
|
||||
end
|
||||
|
||||
if (byte_data !== expected_mem[byte_count]) begin
|
||||
$fatal(1, "byte mismatch at %0d: got 0x%02h expected 0x%02h",
|
||||
byte_count, byte_data, expected_mem[byte_count]);
|
||||
end
|
||||
|
||||
if (byte_count == 0) begin
|
||||
if (original_image_start !== 1'b1) begin
|
||||
$fatal(1, "original_image_start should be high on the first SOI byte");
|
||||
end
|
||||
end else begin
|
||||
if (original_image_start !== 1'b0) begin
|
||||
$fatal(1, "original_image_start should be low at byte %0d", byte_count);
|
||||
end
|
||||
end
|
||||
|
||||
if (byte_count == (HEADER_BYTE_COUNT - 1)) begin
|
||||
header_done_seen <= 1'b1;
|
||||
end
|
||||
|
||||
if (byte_count == (TOTAL_BYTE_COUNT - 1)) begin
|
||||
done_seen <= 1'b1;
|
||||
end
|
||||
|
||||
byte_count <= byte_count + 1;
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (1000) @(posedge clk);
|
||||
$fatal(1, "Timeout waiting for header writer output");
|
||||
end
|
||||
|
||||
initial begin
|
||||
wait (done_seen);
|
||||
repeat (4) @(posedge clk);
|
||||
if (byte_count !== TOTAL_BYTE_COUNT) begin
|
||||
$fatal(1, "byte_count mismatch: got %0d", byte_count);
|
||||
end
|
||||
$display("PASS: tb_jls_header_writer");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
284
fpga/sim/tb_jls_input_ctrl.sv
Normal file
284
fpga/sim/tb_jls_input_ctrl.sv
Normal file
@@ -0,0 +1,284 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex A.8 Control procedure, Annex D.1-D.3 scan control
|
||||
// Figure : N/A
|
||||
// Table : N/A
|
||||
// Pseudocode : Source image sample ordering before JPEG-LS encoding
|
||||
// Example : 16x16 input image split into four 4-row strip frames.
|
||||
//
|
||||
// Smoke test for jls_input_ctrl. The test drives a standard synchronous FIFO
|
||||
// model with one-cycle read latency, discards pre-SOF words, then checks the
|
||||
// generated coordinates and strip/image boundary flags.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_input_ctrl;
|
||||
|
||||
// Test precision. The first smoke uses 16-bit samples because it exercises
|
||||
// the 18-bit packed FIFO word with one unused spare bit.
|
||||
localparam int PIX_WIDTH = 16;
|
||||
|
||||
// Compact test image size. Width and height are kept at the RTL minimum
|
||||
// accepted by jls_input_ctrl.
|
||||
localparam int PIC_COL = 16;
|
||||
localparam int PIC_ROW = 16;
|
||||
|
||||
// Four source rows per standalone strip frame in this smoke test.
|
||||
localparam int SCAN_ROWS = 4;
|
||||
|
||||
// Words before SOF verify that the controller waits for image start.
|
||||
localparam int PREAMBLE_COUNT = 3;
|
||||
|
||||
// Packed input FIFO word: bit 17 is SOF for 16-bit samples, bit 16 is spare.
|
||||
localparam int IFIFO_DATA_WIDTH = ((PIX_WIDTH + 7) / 8) * 9;
|
||||
localparam int SOF_BIT_INDEX = 17;
|
||||
localparam int FIFO_WORD_COUNT = PREAMBLE_COUNT + (PIC_COL * PIC_ROW);
|
||||
|
||||
// Main simulation clock and reset.
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
// Runtime configuration sampled by the design at SOF.
|
||||
logic [12:0] cfg_pic_col;
|
||||
logic [12:0] cfg_pic_row;
|
||||
logic [3:0] ratio;
|
||||
|
||||
// Synchronous input FIFO interface.
|
||||
logic ififo_rclk;
|
||||
logic ififo_rd;
|
||||
logic [IFIFO_DATA_WIDTH-1:0] ififo_rdata;
|
||||
logic ififo_empty;
|
||||
logic ififo_alempty;
|
||||
|
||||
// Downstream flow-control inputs.
|
||||
logic pixel_ready;
|
||||
logic pause_req;
|
||||
|
||||
// Pixel event outputs under test.
|
||||
logic pixel_valid;
|
||||
logic pixel_sof;
|
||||
logic [PIX_WIDTH-1:0] pixel_sample;
|
||||
logic [12:0] pixel_x;
|
||||
logic [12:0] pixel_y;
|
||||
logic strip_first_pixel;
|
||||
logic strip_last_pixel;
|
||||
logic image_first_pixel;
|
||||
logic image_last_pixel;
|
||||
logic [12:0] active_pic_col;
|
||||
logic [12:0] active_pic_row;
|
||||
logic [3:0] active_ratio;
|
||||
logic active_cfg_valid;
|
||||
logic image_active;
|
||||
|
||||
// FIFO memory and scoreboard state.
|
||||
logic [IFIFO_DATA_WIDTH-1:0] fifo_mem [0:FIFO_WORD_COUNT-1];
|
||||
int fifo_rd_index;
|
||||
int init_index;
|
||||
int event_count;
|
||||
logic [12:0] expected_x;
|
||||
logic [12:0] expected_y;
|
||||
logic expected_strip_first;
|
||||
logic expected_strip_last;
|
||||
logic expected_image_first;
|
||||
logic expected_image_last;
|
||||
logic done_seen;
|
||||
|
||||
jls_input_ctrl #(
|
||||
.PIX_WIDTH(PIX_WIDTH),
|
||||
.DEFAULT_PIC_COL(PIC_COL),
|
||||
.DEFAULT_PIC_ROW(PIC_ROW),
|
||||
.MAX_PIC_COL(PIC_COL),
|
||||
.MAX_PIC_ROW(PIC_ROW),
|
||||
.SCAN_ROWS(SCAN_ROWS)
|
||||
) dut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.cfg_pic_col(cfg_pic_col),
|
||||
.cfg_pic_row(cfg_pic_row),
|
||||
.ratio(ratio),
|
||||
.ififo_rclk(ififo_rclk),
|
||||
.ififo_rd(ififo_rd),
|
||||
.ififo_rdata(ififo_rdata),
|
||||
.ififo_empty(ififo_empty),
|
||||
.ififo_alempty(ififo_alempty),
|
||||
.pixel_ready(pixel_ready),
|
||||
.pause_req(pause_req),
|
||||
.pixel_valid(pixel_valid),
|
||||
.pixel_sof(pixel_sof),
|
||||
.pixel_sample(pixel_sample),
|
||||
.pixel_x(pixel_x),
|
||||
.pixel_y(pixel_y),
|
||||
.strip_first_pixel(strip_first_pixel),
|
||||
.strip_last_pixel(strip_last_pixel),
|
||||
.image_first_pixel(image_first_pixel),
|
||||
.image_last_pixel(image_last_pixel),
|
||||
.active_pic_col(active_pic_col),
|
||||
.active_pic_row(active_pic_row),
|
||||
.active_ratio(active_ratio),
|
||||
.active_cfg_valid(active_cfg_valid),
|
||||
.image_active(image_active)
|
||||
);
|
||||
|
||||
always begin
|
||||
#2 clk = ~clk;
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
ififo_empty = 1'b0;
|
||||
ififo_alempty = 1'b0;
|
||||
if (fifo_rd_index >= FIFO_WORD_COUNT) begin
|
||||
ififo_empty = 1'b1;
|
||||
ififo_alempty = 1'b1;
|
||||
end else if (fifo_rd_index >= (FIFO_WORD_COUNT - 1)) begin
|
||||
ififo_alempty = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
expected_strip_first = 1'b0;
|
||||
if (expected_x == 13'd0 && expected_y[1:0] == 2'd0) begin
|
||||
expected_strip_first = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
expected_strip_last = 1'b0;
|
||||
if (expected_x == 13'd15 && expected_y[1:0] == 2'd3) begin
|
||||
expected_strip_last = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
expected_image_first = 1'b0;
|
||||
if (event_count == 0) begin
|
||||
expected_image_first = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
expected_image_last = 1'b0;
|
||||
if (event_count == ((PIC_COL * PIC_ROW) - 1)) begin
|
||||
expected_image_last = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
cfg_pic_col = PIC_COL[12:0];
|
||||
cfg_pic_row = PIC_ROW[12:0];
|
||||
ratio = 4'd2;
|
||||
pixel_ready = 1'b1;
|
||||
pause_req = 1'b0;
|
||||
|
||||
for (init_index = 0; init_index < FIFO_WORD_COUNT; init_index = init_index + 1) begin
|
||||
fifo_mem[init_index] = {IFIFO_DATA_WIDTH{1'b0}};
|
||||
end
|
||||
|
||||
for (init_index = 0; init_index < (PIC_COL * PIC_ROW); init_index = init_index + 1) begin
|
||||
fifo_mem[PREAMBLE_COUNT + init_index][PIX_WIDTH-1:0] = init_index;
|
||||
end
|
||||
|
||||
fifo_mem[PREAMBLE_COUNT][SOF_BIT_INDEX] = 1'b1;
|
||||
|
||||
repeat (5) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
fifo_rd_index <= 0;
|
||||
ififo_rdata <= {IFIFO_DATA_WIDTH{1'b0}};
|
||||
end else if (ififo_rd && !ififo_empty) begin
|
||||
ififo_rdata <= fifo_mem[fifo_rd_index];
|
||||
fifo_rd_index <= fifo_rd_index + 1;
|
||||
end
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
event_count <= 0;
|
||||
expected_x <= 13'd0;
|
||||
expected_y <= 13'd0;
|
||||
done_seen <= 1'b0;
|
||||
end else if (pixel_valid && pixel_ready) begin
|
||||
if (ififo_rclk !== clk) begin
|
||||
$fatal(1, "ififo_rclk is not tied to clk");
|
||||
end
|
||||
|
||||
if (pixel_x !== expected_x) begin
|
||||
$fatal(1, "pixel_x mismatch: got %0d expected %0d", pixel_x, expected_x);
|
||||
end
|
||||
|
||||
if (pixel_y !== expected_y) begin
|
||||
$fatal(1, "pixel_y mismatch: got %0d expected %0d", pixel_y, expected_y);
|
||||
end
|
||||
|
||||
if (pixel_sof !== expected_image_first) begin
|
||||
$fatal(1, "pixel_sof mismatch at event %0d", event_count);
|
||||
end
|
||||
|
||||
if (strip_first_pixel !== expected_strip_first) begin
|
||||
$fatal(1, "strip_first_pixel mismatch at x=%0d y=%0d", expected_x, expected_y);
|
||||
end
|
||||
|
||||
if (strip_last_pixel !== expected_strip_last) begin
|
||||
$fatal(1, "strip_last_pixel mismatch at x=%0d y=%0d", expected_x, expected_y);
|
||||
end
|
||||
|
||||
if (image_first_pixel !== expected_image_first) begin
|
||||
$fatal(1, "image_first_pixel mismatch at event %0d", event_count);
|
||||
end
|
||||
|
||||
if (image_last_pixel !== expected_image_last) begin
|
||||
$fatal(1, "image_last_pixel mismatch at event %0d", event_count);
|
||||
end
|
||||
|
||||
if (active_pic_col !== PIC_COL[12:0]) begin
|
||||
$fatal(1, "active_pic_col mismatch: got %0d", active_pic_col);
|
||||
end
|
||||
|
||||
if (active_pic_row !== PIC_ROW[12:0]) begin
|
||||
$fatal(1, "active_pic_row mismatch: got %0d", active_pic_row);
|
||||
end
|
||||
|
||||
if (active_ratio !== 4'd2) begin
|
||||
$fatal(1, "active_ratio mismatch: got %0d", active_ratio);
|
||||
end
|
||||
|
||||
if (active_cfg_valid !== 1'b1) begin
|
||||
$fatal(1, "active_cfg_valid should be high for this test");
|
||||
end
|
||||
|
||||
if (expected_image_last) begin
|
||||
done_seen <= 1'b1;
|
||||
end
|
||||
|
||||
event_count <= event_count + 1;
|
||||
|
||||
if (expected_x == 13'd15) begin
|
||||
expected_x <= 13'd0;
|
||||
expected_y <= expected_y + 13'd1;
|
||||
end else begin
|
||||
expected_x <= expected_x + 13'd1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (2000) @(posedge clk);
|
||||
$fatal(1, "Timeout waiting for input controller to emit the full image");
|
||||
end
|
||||
|
||||
initial begin
|
||||
wait (done_seen);
|
||||
repeat (4) @(posedge clk);
|
||||
if (event_count !== (PIC_COL * PIC_ROW)) begin
|
||||
$fatal(1, "event_count mismatch: got %0d", event_count);
|
||||
end
|
||||
$display("PASS: tb_jls_input_ctrl");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
381
fpga/sim/tb_jls_mode_router.sv
Normal file
381
fpga/sim/tb_jls_mode_router.sv
Normal file
@@ -0,0 +1,381 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex A.3 context determination, Annex A.7 run mode
|
||||
// Figure : N/A
|
||||
// Table : N/A
|
||||
// Pseudocode : regular/run mode decision and run segment formation
|
||||
// Example : Two matching run pixels ending at EOL create run_length=2 with
|
||||
// no interruption sample.
|
||||
//
|
||||
// Smoke test for jls_mode_router.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_mode_router;
|
||||
|
||||
// Test precision.
|
||||
localparam int PIX_WIDTH = 8;
|
||||
|
||||
// Main simulation clock and reset.
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
// Pixel input.
|
||||
logic pixel_valid;
|
||||
logic pixel_ready;
|
||||
logic [PIX_WIDTH-1:0] pixel_sample;
|
||||
logic [12:0] pixel_x;
|
||||
logic [12:0] pixel_y;
|
||||
logic pixel_strip_first_pixel;
|
||||
logic pixel_strip_last_pixel;
|
||||
logic [PIX_WIDTH-1:0] Ra;
|
||||
logic [PIX_WIDTH-1:0] Rb;
|
||||
logic [PIX_WIDTH-1:0] Rc;
|
||||
logic [PIX_WIDTH-1:0] Rd;
|
||||
logic [12:0] strip_width;
|
||||
logic [5:0] NEAR;
|
||||
|
||||
// Regular output.
|
||||
logic regular_valid;
|
||||
logic regular_ready;
|
||||
logic [PIX_WIDTH-1:0] regular_sample;
|
||||
logic [12:0] regular_x;
|
||||
logic [12:0] regular_y;
|
||||
logic regular_strip_first_pixel;
|
||||
logic regular_strip_last_pixel;
|
||||
logic [PIX_WIDTH-1:0] regular_Ra;
|
||||
logic [PIX_WIDTH-1:0] regular_Rb;
|
||||
logic [PIX_WIDTH-1:0] regular_Rc;
|
||||
logic [PIX_WIDTH-1:0] regular_Rd;
|
||||
|
||||
// Run segment output.
|
||||
logic run_segment_valid;
|
||||
logic run_segment_ready;
|
||||
logic [12:0] run_length;
|
||||
logic run_end_of_line;
|
||||
logic run_interruption_valid;
|
||||
logic [PIX_WIDTH-1:0] run_interruption_sample;
|
||||
logic [12:0] run_interruption_x;
|
||||
logic [12:0] run_interruption_y;
|
||||
logic run_interruption_strip_first_pixel;
|
||||
logic run_interruption_strip_last_pixel;
|
||||
logic [PIX_WIDTH-1:0] run_Ra;
|
||||
logic [PIX_WIDTH-1:0] run_Rb;
|
||||
logic run_segment_done;
|
||||
|
||||
// Direct run-pixel reconstruction.
|
||||
logic run_recon_valid;
|
||||
logic run_recon_ready;
|
||||
logic [PIX_WIDTH-1:0] run_recon_sample;
|
||||
logic [12:0] run_recon_x;
|
||||
logic [12:0] run_recon_y;
|
||||
|
||||
// Scoreboard state.
|
||||
int regular_count;
|
||||
int run_segment_count;
|
||||
int run_recon_count;
|
||||
logic all_done_seen;
|
||||
logic eol_loaded_before_done;
|
||||
|
||||
jls_mode_router #(
|
||||
.PIX_WIDTH(PIX_WIDTH)
|
||||
) dut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.pixel_valid(pixel_valid),
|
||||
.pixel_ready(pixel_ready),
|
||||
.pixel_sample(pixel_sample),
|
||||
.pixel_x(pixel_x),
|
||||
.pixel_y(pixel_y),
|
||||
.pixel_strip_first_pixel(pixel_strip_first_pixel),
|
||||
.pixel_strip_last_pixel(pixel_strip_last_pixel),
|
||||
.Ra(Ra),
|
||||
.Rb(Rb),
|
||||
.Rc(Rc),
|
||||
.Rd(Rd),
|
||||
.strip_width(strip_width),
|
||||
.NEAR(NEAR),
|
||||
.regular_valid(regular_valid),
|
||||
.regular_ready(regular_ready),
|
||||
.regular_sample(regular_sample),
|
||||
.regular_x(regular_x),
|
||||
.regular_y(regular_y),
|
||||
.regular_strip_first_pixel(regular_strip_first_pixel),
|
||||
.regular_strip_last_pixel(regular_strip_last_pixel),
|
||||
.regular_Ra(regular_Ra),
|
||||
.regular_Rb(regular_Rb),
|
||||
.regular_Rc(regular_Rc),
|
||||
.regular_Rd(regular_Rd),
|
||||
.run_segment_valid(run_segment_valid),
|
||||
.run_segment_ready(run_segment_ready),
|
||||
.run_length(run_length),
|
||||
.run_end_of_line(run_end_of_line),
|
||||
.run_interruption_valid(run_interruption_valid),
|
||||
.run_interruption_sample(run_interruption_sample),
|
||||
.run_interruption_x(run_interruption_x),
|
||||
.run_interruption_y(run_interruption_y),
|
||||
.run_interruption_strip_first_pixel(run_interruption_strip_first_pixel),
|
||||
.run_interruption_strip_last_pixel(run_interruption_strip_last_pixel),
|
||||
.run_Ra(run_Ra),
|
||||
.run_Rb(run_Rb),
|
||||
.run_segment_done(run_segment_done),
|
||||
.run_recon_valid(run_recon_valid),
|
||||
.run_recon_ready(run_recon_ready),
|
||||
.run_recon_sample(run_recon_sample),
|
||||
.run_recon_x(run_recon_x),
|
||||
.run_recon_y(run_recon_y)
|
||||
);
|
||||
|
||||
always begin
|
||||
#2 clk = ~clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
pixel_valid = 1'b0;
|
||||
pixel_sample = 8'd0;
|
||||
pixel_x = 13'd0;
|
||||
pixel_y = 13'd0;
|
||||
pixel_strip_first_pixel = 1'b0;
|
||||
pixel_strip_last_pixel = 1'b0;
|
||||
Ra = 8'd0;
|
||||
Rb = 8'd0;
|
||||
Rc = 8'd0;
|
||||
Rd = 8'd0;
|
||||
strip_width = 13'd4;
|
||||
NEAR = 6'd0;
|
||||
regular_ready = 1'b1;
|
||||
run_segment_ready = 1'b1;
|
||||
run_segment_done = 1'b0;
|
||||
run_recon_ready = 1'b1;
|
||||
all_done_seen = 1'b0;
|
||||
eol_loaded_before_done = 1'b0;
|
||||
|
||||
repeat (5) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
|
||||
// Non-run gradients go to the regular path.
|
||||
@(negedge clk);
|
||||
pixel_valid = 1'b1;
|
||||
pixel_sample = 8'd42;
|
||||
pixel_x = 13'd0;
|
||||
pixel_y = 13'd0;
|
||||
pixel_strip_first_pixel = 1'b1;
|
||||
pixel_strip_last_pixel = 1'b0;
|
||||
Ra = 8'd1;
|
||||
Rb = 8'd2;
|
||||
Rc = 8'd3;
|
||||
Rd = 8'd20;
|
||||
wait (pixel_ready);
|
||||
@(posedge clk);
|
||||
pixel_valid = 1'b0;
|
||||
pixel_strip_first_pixel = 1'b0;
|
||||
wait (regular_count == 1);
|
||||
|
||||
// First run pixel, not EOL: immediate reconstructed Ra, no segment yet.
|
||||
@(negedge clk);
|
||||
pixel_valid = 1'b1;
|
||||
pixel_sample = 8'd5;
|
||||
pixel_x = 13'd0;
|
||||
pixel_y = 13'd1;
|
||||
pixel_strip_first_pixel = 1'b1;
|
||||
Ra = 8'd5;
|
||||
Rb = 8'd5;
|
||||
Rc = 8'd5;
|
||||
Rd = 8'd5;
|
||||
wait (pixel_ready);
|
||||
@(posedge clk);
|
||||
pixel_valid = 1'b0;
|
||||
pixel_strip_first_pixel = 1'b0;
|
||||
wait (run_recon_count == 1);
|
||||
|
||||
// Interruption pixel ends the run with run_length=1.
|
||||
@(negedge clk);
|
||||
pixel_valid = 1'b1;
|
||||
pixel_sample = 8'd7;
|
||||
pixel_x = 13'd1;
|
||||
pixel_y = 13'd1;
|
||||
Ra = 8'd5;
|
||||
Rb = 8'd5;
|
||||
Rc = 8'd5;
|
||||
Rd = 8'd5;
|
||||
wait (pixel_ready);
|
||||
@(posedge clk);
|
||||
pixel_valid = 1'b0;
|
||||
wait (run_segment_count == 1);
|
||||
|
||||
// A later non-EOL run pixel has no immediate entropy output, so it can be
|
||||
// accepted while the previous run segment is still waiting for done.
|
||||
@(negedge clk);
|
||||
pixel_valid = 1'b1;
|
||||
pixel_sample = 8'd9;
|
||||
pixel_x = 13'd0;
|
||||
pixel_y = 13'd2;
|
||||
pixel_strip_first_pixel = 1'b1;
|
||||
Ra = 8'd9;
|
||||
Rb = 8'd9;
|
||||
Rc = 8'd9;
|
||||
Rd = 8'd9;
|
||||
wait (pixel_ready);
|
||||
@(posedge clk);
|
||||
pixel_valid = 1'b0;
|
||||
pixel_strip_first_pixel = 1'b0;
|
||||
wait (run_recon_count == 2);
|
||||
|
||||
// The EOL pixel would emit the next run segment. The router may either
|
||||
// backpressure it or cache it in the input stage, but it must not emit the
|
||||
// second segment until the previous segment is reported done.
|
||||
@(negedge clk);
|
||||
pixel_valid = 1'b1;
|
||||
pixel_sample = 8'd9;
|
||||
pixel_x = 13'd3;
|
||||
pixel_y = 13'd2;
|
||||
pixel_strip_last_pixel = 1'b1;
|
||||
Ra = 8'd9;
|
||||
Rb = 8'd9;
|
||||
Rc = 8'd9;
|
||||
Rd = 8'd9;
|
||||
#1;
|
||||
if (pixel_ready === 1'b1) begin
|
||||
eol_loaded_before_done = 1'b1;
|
||||
@(posedge clk);
|
||||
pixel_valid = 1'b0;
|
||||
pixel_strip_last_pixel = 1'b0;
|
||||
if (run_segment_count !== 1) begin
|
||||
$fatal(1, "EOL run segment emitted before previous segment_done");
|
||||
end
|
||||
end
|
||||
|
||||
@(negedge clk);
|
||||
run_segment_done = 1'b1;
|
||||
@(posedge clk);
|
||||
run_segment_done = 1'b0;
|
||||
|
||||
if (!eol_loaded_before_done) begin
|
||||
wait (pixel_ready);
|
||||
@(posedge clk);
|
||||
pixel_valid = 1'b0;
|
||||
pixel_strip_last_pixel = 1'b0;
|
||||
end
|
||||
wait (run_segment_count == 2 && run_recon_count == 3);
|
||||
|
||||
@(negedge clk);
|
||||
run_segment_done = 1'b1;
|
||||
@(posedge clk);
|
||||
run_segment_done = 1'b0;
|
||||
|
||||
// Once a run has started, a later nonmatching sample is a run-interruption
|
||||
// sample even if its gradients would no longer enter run mode from idle.
|
||||
@(negedge clk);
|
||||
pixel_valid = 1'b1;
|
||||
pixel_sample = 8'd12;
|
||||
pixel_x = 13'd0;
|
||||
pixel_y = 13'd3;
|
||||
pixel_strip_first_pixel = 1'b1;
|
||||
Ra = 8'd12;
|
||||
Rb = 8'd12;
|
||||
Rc = 8'd12;
|
||||
Rd = 8'd12;
|
||||
wait (pixel_ready);
|
||||
@(posedge clk);
|
||||
pixel_valid = 1'b0;
|
||||
pixel_strip_first_pixel = 1'b0;
|
||||
wait (run_recon_count == 4);
|
||||
|
||||
@(negedge clk);
|
||||
pixel_valid = 1'b1;
|
||||
pixel_sample = 8'd20;
|
||||
pixel_x = 13'd1;
|
||||
pixel_y = 13'd3;
|
||||
Ra = 8'd12;
|
||||
Rb = 8'd0;
|
||||
Rc = 8'd3;
|
||||
Rd = 8'd40;
|
||||
wait (pixel_ready);
|
||||
@(posedge clk);
|
||||
pixel_valid = 1'b0;
|
||||
wait (run_segment_count == 3);
|
||||
|
||||
@(negedge clk);
|
||||
run_segment_done = 1'b1;
|
||||
@(posedge clk);
|
||||
run_segment_done = 1'b0;
|
||||
|
||||
all_done_seen = 1'b1;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
regular_count <= 0;
|
||||
run_segment_count <= 0;
|
||||
run_recon_count <= 0;
|
||||
end else begin
|
||||
if (regular_valid && regular_ready) begin
|
||||
regular_count <= regular_count + 1;
|
||||
|
||||
if (regular_sample !== 8'd42 || regular_x !== 13'd0 || regular_Rd !== 8'd20) begin
|
||||
$fatal(1, "regular event fields mismatch");
|
||||
end
|
||||
end
|
||||
|
||||
if (run_recon_valid && run_recon_ready) begin
|
||||
run_recon_count <= run_recon_count + 1;
|
||||
|
||||
if (run_recon_sample !== Ra) begin
|
||||
$fatal(1, "run recon sample should equal Ra");
|
||||
end
|
||||
end
|
||||
|
||||
if (run_segment_valid && run_segment_ready) begin
|
||||
run_segment_count <= run_segment_count + 1;
|
||||
|
||||
case (run_segment_count)
|
||||
0: begin
|
||||
if (run_length !== 13'd1 || run_end_of_line !== 1'b0 ||
|
||||
run_interruption_valid !== 1'b1 || run_interruption_sample !== 8'd7 ||
|
||||
run_interruption_x !== 13'd1) begin
|
||||
$fatal(1, "interruption run segment mismatch");
|
||||
end
|
||||
end
|
||||
|
||||
1: begin
|
||||
if (run_length !== 13'd2 || run_end_of_line !== 1'b1 ||
|
||||
run_interruption_valid !== 1'b0) begin
|
||||
$fatal(1, "EOL run segment mismatch");
|
||||
end
|
||||
end
|
||||
|
||||
2: begin
|
||||
if (run_length !== 13'd1 || run_end_of_line !== 1'b0 ||
|
||||
run_interruption_valid !== 1'b1 || run_interruption_sample !== 8'd20 ||
|
||||
run_interruption_x !== 13'd1) begin
|
||||
$fatal(1, "post-run interruption segment mismatch");
|
||||
end
|
||||
end
|
||||
|
||||
default: begin
|
||||
$fatal(1, "Unexpected extra run segment");
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (5000) @(posedge clk);
|
||||
$fatal(1, "Timeout waiting for mode-router smoke");
|
||||
end
|
||||
|
||||
initial begin
|
||||
wait (all_done_seen);
|
||||
repeat (4) @(posedge clk);
|
||||
if (regular_count !== 1 || run_segment_count !== 3 || run_recon_count !== 4) begin
|
||||
$fatal(1, "mode-router count mismatch");
|
||||
end
|
||||
$display("PASS: tb_jls_mode_router");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
162
fpga/sim/tb_jls_near_ctrl.sv
Normal file
162
fpga/sim/tb_jls_near_ctrl.sv
Normal file
@@ -0,0 +1,162 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex C.2.3 scan header NEAR parameter; Annex A uses NEAR in coding
|
||||
// Figure : N/A
|
||||
// Table : N/A
|
||||
// Pseudocode : Project dynamic NEAR control around the standard NEAR parameter
|
||||
// Example : ratio=2 means target bits are source bits divided by 4.
|
||||
//
|
||||
// Smoke test for jls_near_ctrl.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_near_ctrl;
|
||||
|
||||
// Test precision keeps strip source bits easy to inspect.
|
||||
localparam int PIX_WIDTH = 8;
|
||||
|
||||
// Main simulation clock and reset.
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
// Dynamic NEAR controller inputs.
|
||||
logic image_start_valid;
|
||||
logic [3:0] image_ratio;
|
||||
logic strip_done_valid;
|
||||
logic [31:0] strip_pixel_count;
|
||||
logic [31:0] strip_output_bytes;
|
||||
|
||||
// Dynamic NEAR controller outputs.
|
||||
logic [5:0] current_near;
|
||||
logic [47:0] actual_bits_cumulative;
|
||||
logic [47:0] target_bits_cumulative;
|
||||
logic target_miss_at_max_near;
|
||||
logic update_busy;
|
||||
|
||||
// Test loop counter for MAX_NEAR saturation.
|
||||
int loop_index;
|
||||
|
||||
jls_near_ctrl #(
|
||||
.PIX_WIDTH(PIX_WIDTH),
|
||||
.MAX_NEAR(31)
|
||||
) dut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.image_start_valid(image_start_valid),
|
||||
.image_ratio(image_ratio),
|
||||
.strip_done_valid(strip_done_valid),
|
||||
.strip_pixel_count(strip_pixel_count),
|
||||
.strip_output_bytes(strip_output_bytes),
|
||||
.current_near(current_near),
|
||||
.actual_bits_cumulative(actual_bits_cumulative),
|
||||
.target_bits_cumulative(target_bits_cumulative),
|
||||
.target_miss_at_max_near(target_miss_at_max_near),
|
||||
.update_busy(update_busy)
|
||||
);
|
||||
|
||||
always begin
|
||||
#2 clk = ~clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
image_start_valid = 1'b0;
|
||||
image_ratio = 4'd0;
|
||||
strip_done_valid = 1'b0;
|
||||
strip_pixel_count = 32'd256;
|
||||
strip_output_bytes = 32'd0;
|
||||
|
||||
repeat (5) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
|
||||
// ratio=2 targets 1/4 of source bits. A 256-pixel 8-bit strip has
|
||||
// 2048 source bits and 512 target bits.
|
||||
@(posedge clk);
|
||||
image_ratio = 4'd2;
|
||||
image_start_valid = 1'b1;
|
||||
@(posedge clk);
|
||||
image_start_valid = 1'b0;
|
||||
|
||||
@(posedge clk);
|
||||
strip_output_bytes = 32'd100;
|
||||
strip_done_valid = 1'b1;
|
||||
@(posedge clk);
|
||||
strip_done_valid = 1'b0;
|
||||
@(posedge clk);
|
||||
#1;
|
||||
if (current_near !== 6'd1 || actual_bits_cumulative !== 48'd800 ||
|
||||
target_bits_cumulative !== 48'd512) begin
|
||||
$fatal(1, "ratio=2 first strip update mismatch");
|
||||
end
|
||||
|
||||
@(posedge clk);
|
||||
strip_output_bytes = 32'd40;
|
||||
strip_done_valid = 1'b1;
|
||||
@(posedge clk);
|
||||
strip_done_valid = 1'b0;
|
||||
@(posedge clk);
|
||||
#1;
|
||||
if (current_near !== 6'd2 || actual_bits_cumulative !== 48'd1120 ||
|
||||
target_bits_cumulative !== 48'd1024) begin
|
||||
$fatal(1, "ratio=2 second strip update mismatch");
|
||||
end
|
||||
|
||||
@(posedge clk);
|
||||
strip_output_bytes = 32'd10;
|
||||
strip_done_valid = 1'b1;
|
||||
@(posedge clk);
|
||||
strip_done_valid = 1'b0;
|
||||
@(posedge clk);
|
||||
#1;
|
||||
if (current_near !== 6'd1 || actual_bits_cumulative !== 48'd1200 ||
|
||||
target_bits_cumulative !== 48'd1536) begin
|
||||
$fatal(1, "ratio=2 third strip update mismatch");
|
||||
end
|
||||
|
||||
// ratio=0 is lossless mode and must force NEAR back to 0.
|
||||
@(posedge clk);
|
||||
image_ratio = 4'd0;
|
||||
image_start_valid = 1'b1;
|
||||
@(posedge clk);
|
||||
image_start_valid = 1'b0;
|
||||
|
||||
@(posedge clk);
|
||||
strip_output_bytes = 32'd100;
|
||||
strip_done_valid = 1'b1;
|
||||
@(posedge clk);
|
||||
strip_done_valid = 1'b0;
|
||||
@(posedge clk);
|
||||
#1;
|
||||
if (current_near !== 6'd0 || target_miss_at_max_near !== 1'b0) begin
|
||||
$fatal(1, "lossless ratio should force NEAR to 0 without target miss");
|
||||
end
|
||||
|
||||
// Repeated over-target strips saturate at MAX_NEAR and then set the sticky
|
||||
// miss flag on the next over-target completion.
|
||||
@(posedge clk);
|
||||
image_ratio = 4'd1;
|
||||
image_start_valid = 1'b1;
|
||||
@(posedge clk);
|
||||
image_start_valid = 1'b0;
|
||||
|
||||
for (loop_index = 0; loop_index < 32; loop_index = loop_index + 1) begin
|
||||
@(posedge clk);
|
||||
strip_output_bytes = 32'd1000;
|
||||
strip_done_valid = 1'b1;
|
||||
@(posedge clk);
|
||||
strip_done_valid = 1'b0;
|
||||
@(posedge clk);
|
||||
end
|
||||
|
||||
#1;
|
||||
if (current_near !== 6'd31 || target_miss_at_max_near !== 1'b1) begin
|
||||
$fatal(1, "MAX_NEAR saturation or target miss flag mismatch");
|
||||
end
|
||||
|
||||
$display("PASS: tb_jls_near_ctrl");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
242
fpga/sim/tb_jls_neighbor_provider.sv
Normal file
242
fpga/sim/tb_jls_neighbor_provider.sv
Normal file
@@ -0,0 +1,242 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex A.3 context determination, Annex A.4 prediction
|
||||
// Figure : N/A
|
||||
// Table : N/A
|
||||
// Pseudocode : Reconstructed neighborhood selection for Ra/Rb/Rc/Rd
|
||||
// Example : Checks top-row zeros, previous-line neighbors, and x=0 Rc
|
||||
// left-edge extension on the third row.
|
||||
//
|
||||
// Smoke test for jls_neighbor_provider.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_neighbor_provider;
|
||||
|
||||
// Small smoke-test precision and line width.
|
||||
localparam int PIX_WIDTH = 8;
|
||||
localparam int MAX_PIC_COL = 8;
|
||||
localparam int EVENT_COUNT = 10;
|
||||
|
||||
// Main simulation clock and reset.
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
// Source pixel interface.
|
||||
logic pixel_valid;
|
||||
logic pixel_ready;
|
||||
logic [PIX_WIDTH-1:0] pixel_sample;
|
||||
logic [12:0] pixel_x;
|
||||
logic [12:0] pixel_y;
|
||||
logic pixel_row_last;
|
||||
logic strip_first_pixel;
|
||||
logic strip_last_pixel;
|
||||
logic [12:0] strip_width;
|
||||
logic [5:0] NEAR;
|
||||
|
||||
// Neighborhood event interface.
|
||||
logic neigh_valid;
|
||||
logic neigh_ready;
|
||||
logic [PIX_WIDTH-1:0] neigh_sample;
|
||||
logic [12:0] neigh_x;
|
||||
logic [12:0] neigh_y;
|
||||
logic neigh_strip_first_pixel;
|
||||
logic neigh_strip_last_pixel;
|
||||
logic [PIX_WIDTH-1:0] Ra;
|
||||
logic [PIX_WIDTH-1:0] Rb;
|
||||
logic [PIX_WIDTH-1:0] Rc;
|
||||
logic [PIX_WIDTH-1:0] Rd;
|
||||
|
||||
// Reconstructed writeback interface.
|
||||
logic recon_valid;
|
||||
logic recon_ready;
|
||||
logic [PIX_WIDTH-1:0] recon_sample;
|
||||
logic [12:0] recon_x;
|
||||
logic [12:0] recon_y;
|
||||
|
||||
// Test vectors and scoreboard.
|
||||
logic [PIX_WIDTH-1:0] sample_mem [0:EVENT_COUNT-1];
|
||||
logic [12:0] x_mem [0:EVENT_COUNT-1];
|
||||
logic [12:0] y_mem [0:EVENT_COUNT-1];
|
||||
logic first_mem [0:EVENT_COUNT-1];
|
||||
logic last_mem [0:EVENT_COUNT-1];
|
||||
logic [PIX_WIDTH-1:0] recon_mem [0:EVENT_COUNT-1];
|
||||
logic [PIX_WIDTH-1:0] exp_Ra_mem [0:EVENT_COUNT-1];
|
||||
logic [PIX_WIDTH-1:0] exp_Rb_mem [0:EVENT_COUNT-1];
|
||||
logic [PIX_WIDTH-1:0] exp_Rc_mem [0:EVENT_COUNT-1];
|
||||
logic [PIX_WIDTH-1:0] exp_Rd_mem [0:EVENT_COUNT-1];
|
||||
int drive_index;
|
||||
int receive_index;
|
||||
logic all_done_seen;
|
||||
|
||||
jls_neighbor_provider #(
|
||||
.PIX_WIDTH(PIX_WIDTH),
|
||||
.MAX_PIC_COL(MAX_PIC_COL)
|
||||
) dut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.pixel_valid(pixel_valid),
|
||||
.pixel_ready(pixel_ready),
|
||||
.pixel_sample(pixel_sample),
|
||||
.pixel_x(pixel_x),
|
||||
.pixel_y(pixel_y),
|
||||
.pixel_row_last(pixel_row_last),
|
||||
.strip_first_pixel(strip_first_pixel),
|
||||
.strip_last_pixel(strip_last_pixel),
|
||||
.NEAR(NEAR),
|
||||
.neigh_valid(neigh_valid),
|
||||
.neigh_ready(neigh_ready),
|
||||
.neigh_sample(neigh_sample),
|
||||
.neigh_x(neigh_x),
|
||||
.neigh_y(neigh_y),
|
||||
.neigh_strip_first_pixel(neigh_strip_first_pixel),
|
||||
.neigh_strip_last_pixel(neigh_strip_last_pixel),
|
||||
.Ra(Ra),
|
||||
.Rb(Rb),
|
||||
.Rc(Rc),
|
||||
.Rd(Rd),
|
||||
.recon_valid(recon_valid),
|
||||
.recon_ready(recon_ready),
|
||||
.recon_sample(recon_sample),
|
||||
.recon_x(recon_x),
|
||||
.recon_y(recon_y)
|
||||
);
|
||||
|
||||
always begin
|
||||
#2 clk = ~clk;
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
pixel_row_last = 1'b0;
|
||||
if (pixel_x == (strip_width - 13'd1)) begin
|
||||
pixel_row_last = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
sample_mem[0] = 8'd10; x_mem[0] = 13'd0; y_mem[0] = 13'd0; first_mem[0] = 1'b1; last_mem[0] = 1'b0;
|
||||
sample_mem[1] = 8'd20; x_mem[1] = 13'd1; y_mem[1] = 13'd0; first_mem[1] = 1'b0; last_mem[1] = 1'b0;
|
||||
sample_mem[2] = 8'd30; x_mem[2] = 13'd2; y_mem[2] = 13'd0; first_mem[2] = 1'b0; last_mem[2] = 1'b0;
|
||||
sample_mem[3] = 8'd11; x_mem[3] = 13'd0; y_mem[3] = 13'd1; first_mem[3] = 1'b0; last_mem[3] = 1'b0;
|
||||
sample_mem[4] = 8'd21; x_mem[4] = 13'd1; y_mem[4] = 13'd1; first_mem[4] = 1'b0; last_mem[4] = 1'b0;
|
||||
sample_mem[5] = 8'd31; x_mem[5] = 13'd2; y_mem[5] = 13'd1; first_mem[5] = 1'b0; last_mem[5] = 1'b0;
|
||||
sample_mem[6] = 8'd12; x_mem[6] = 13'd0; y_mem[6] = 13'd2; first_mem[6] = 1'b0; last_mem[6] = 1'b0;
|
||||
sample_mem[7] = 8'd22; x_mem[7] = 13'd1; y_mem[7] = 13'd2; first_mem[7] = 1'b0; last_mem[7] = 1'b0;
|
||||
sample_mem[8] = 8'd32; x_mem[8] = 13'd2; y_mem[8] = 13'd2; first_mem[8] = 1'b0; last_mem[8] = 1'b1;
|
||||
sample_mem[9] = 8'd42; x_mem[9] = 13'd0; y_mem[9] = 13'd3; first_mem[9] = 1'b1; last_mem[9] = 1'b0;
|
||||
|
||||
recon_mem[0] = 8'd10;
|
||||
recon_mem[1] = 8'd20;
|
||||
recon_mem[2] = 8'd30;
|
||||
recon_mem[3] = 8'd11;
|
||||
recon_mem[4] = 8'd21;
|
||||
recon_mem[5] = 8'd31;
|
||||
recon_mem[6] = 8'd12;
|
||||
recon_mem[7] = 8'd22;
|
||||
recon_mem[8] = 8'd32;
|
||||
recon_mem[9] = 8'd42;
|
||||
|
||||
exp_Ra_mem[0] = 8'd0; exp_Rb_mem[0] = 8'd0; exp_Rc_mem[0] = 8'd0; exp_Rd_mem[0] = 8'd0;
|
||||
exp_Ra_mem[1] = 8'd10; exp_Rb_mem[1] = 8'd0; exp_Rc_mem[1] = 8'd0; exp_Rd_mem[1] = 8'd0;
|
||||
exp_Ra_mem[2] = 8'd20; exp_Rb_mem[2] = 8'd0; exp_Rc_mem[2] = 8'd0; exp_Rd_mem[2] = 8'd0;
|
||||
exp_Ra_mem[3] = 8'd10; exp_Rb_mem[3] = 8'd10; exp_Rc_mem[3] = 8'd0; exp_Rd_mem[3] = 8'd20;
|
||||
exp_Ra_mem[4] = 8'd11; exp_Rb_mem[4] = 8'd20; exp_Rc_mem[4] = 8'd10; exp_Rd_mem[4] = 8'd30;
|
||||
exp_Ra_mem[5] = 8'd21; exp_Rb_mem[5] = 8'd30; exp_Rc_mem[5] = 8'd20; exp_Rd_mem[5] = 8'd30;
|
||||
exp_Ra_mem[6] = 8'd11; exp_Rb_mem[6] = 8'd11; exp_Rc_mem[6] = 8'd10; exp_Rd_mem[6] = 8'd21;
|
||||
exp_Ra_mem[7] = 8'd12; exp_Rb_mem[7] = 8'd21; exp_Rc_mem[7] = 8'd11; exp_Rd_mem[7] = 8'd31;
|
||||
exp_Ra_mem[8] = 8'd22; exp_Rb_mem[8] = 8'd31; exp_Rc_mem[8] = 8'd21; exp_Rd_mem[8] = 8'd31;
|
||||
exp_Ra_mem[9] = 8'd0; exp_Rb_mem[9] = 8'd0; exp_Rc_mem[9] = 8'd0; exp_Rd_mem[9] = 8'd0;
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
pixel_valid = 1'b0;
|
||||
pixel_sample = 8'd0;
|
||||
pixel_x = 13'd0;
|
||||
pixel_y = 13'd0;
|
||||
strip_first_pixel = 1'b0;
|
||||
strip_last_pixel = 1'b0;
|
||||
strip_width = 13'd3;
|
||||
NEAR = 6'd0;
|
||||
neigh_ready = 1'b1;
|
||||
recon_valid = 1'b0;
|
||||
recon_sample = 8'd0;
|
||||
recon_x = 13'd0;
|
||||
recon_y = 13'd0;
|
||||
all_done_seen = 1'b0;
|
||||
|
||||
repeat (5) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
|
||||
for (drive_index = 0; drive_index < EVENT_COUNT; drive_index = drive_index + 1) begin
|
||||
@(negedge clk);
|
||||
pixel_valid = 1'b1;
|
||||
pixel_sample = sample_mem[drive_index];
|
||||
pixel_x = x_mem[drive_index];
|
||||
pixel_y = y_mem[drive_index];
|
||||
strip_first_pixel = first_mem[drive_index];
|
||||
strip_last_pixel = last_mem[drive_index];
|
||||
wait (pixel_ready);
|
||||
@(posedge clk);
|
||||
pixel_valid = 1'b0;
|
||||
strip_first_pixel = 1'b0;
|
||||
strip_last_pixel = 1'b0;
|
||||
|
||||
wait (neigh_valid);
|
||||
@(negedge clk);
|
||||
recon_valid = 1'b1;
|
||||
recon_sample = recon_mem[drive_index];
|
||||
recon_x = x_mem[drive_index];
|
||||
recon_y = y_mem[drive_index];
|
||||
wait (recon_ready);
|
||||
@(posedge clk);
|
||||
recon_valid = 1'b0;
|
||||
end
|
||||
|
||||
wait (receive_index == EVENT_COUNT);
|
||||
all_done_seen = 1'b1;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
receive_index <= 0;
|
||||
end else if (neigh_valid && neigh_ready) begin
|
||||
if (receive_index >= EVENT_COUNT) begin
|
||||
$fatal(1, "Unexpected extra neighbor event");
|
||||
end
|
||||
|
||||
if (neigh_sample !== sample_mem[receive_index] ||
|
||||
neigh_x !== x_mem[receive_index] ||
|
||||
neigh_y !== y_mem[receive_index] ||
|
||||
neigh_strip_first_pixel !== first_mem[receive_index] ||
|
||||
neigh_strip_last_pixel !== last_mem[receive_index]) begin
|
||||
$fatal(1, "neighbor metadata mismatch at %0d", receive_index);
|
||||
end
|
||||
|
||||
if (Ra !== exp_Ra_mem[receive_index] ||
|
||||
Rb !== exp_Rb_mem[receive_index] ||
|
||||
Rc !== exp_Rc_mem[receive_index] ||
|
||||
Rd !== exp_Rd_mem[receive_index]) begin
|
||||
$fatal(1, "neighbor mismatch at %0d: Ra=%0d Rb=%0d Rc=%0d Rd=%0d",
|
||||
receive_index, Ra, Rb, Rc, Rd);
|
||||
end
|
||||
|
||||
receive_index <= receive_index + 1;
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (2000) @(posedge clk);
|
||||
$fatal(1, "Timeout waiting for neighbor-provider smoke");
|
||||
end
|
||||
|
||||
initial begin
|
||||
wait (all_done_seen);
|
||||
repeat (4) @(posedge clk);
|
||||
$display("PASS: tb_jls_neighbor_provider");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
219
fpga/sim/tb_jls_neighbor_provider_lossless_fast.sv
Normal file
219
fpga/sim/tb_jls_neighbor_provider_lossless_fast.sv
Normal file
@@ -0,0 +1,219 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex A.3 context determination, Annex A.4 prediction
|
||||
// Figure : N/A
|
||||
// Table : N/A
|
||||
// Pseudocode : Reconstructed neighborhood selection for Ra/Rb/Rc/Rd
|
||||
// Example : NEAR=0 lossless strips may commit X as Rx without waiting for
|
||||
// reconstructed writeback.
|
||||
//
|
||||
// Smoke test for the jls_neighbor_provider lossless fast path. Six pixels
|
||||
// are accepted on consecutive cycles while recon_valid stays low; this proves
|
||||
// the NEAR=0 path does not depend on later reconstructed feedback.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_neighbor_provider_lossless_fast;
|
||||
|
||||
// Small smoke-test precision and line width.
|
||||
localparam int PIX_WIDTH = 8;
|
||||
localparam int MAX_PIC_COL = 8;
|
||||
localparam int EVENT_COUNT = 6;
|
||||
|
||||
// Main simulation clock and reset.
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
// Source pixel interface.
|
||||
logic pixel_valid;
|
||||
logic pixel_ready;
|
||||
logic [PIX_WIDTH-1:0] pixel_sample;
|
||||
logic [12:0] pixel_x;
|
||||
logic [12:0] pixel_y;
|
||||
logic pixel_row_last;
|
||||
logic strip_first_pixel;
|
||||
logic strip_last_pixel;
|
||||
logic [12:0] strip_width;
|
||||
logic [5:0] NEAR;
|
||||
|
||||
// Neighborhood event interface.
|
||||
logic neigh_valid;
|
||||
logic neigh_ready;
|
||||
logic [PIX_WIDTH-1:0] neigh_sample;
|
||||
logic [12:0] neigh_x;
|
||||
logic [12:0] neigh_y;
|
||||
logic neigh_strip_first_pixel;
|
||||
logic neigh_strip_last_pixel;
|
||||
logic [PIX_WIDTH-1:0] Ra;
|
||||
logic [PIX_WIDTH-1:0] Rb;
|
||||
logic [PIX_WIDTH-1:0] Rc;
|
||||
logic [PIX_WIDTH-1:0] Rd;
|
||||
|
||||
// Reconstructed writeback interface remains idle in this test.
|
||||
logic recon_valid;
|
||||
logic recon_ready;
|
||||
logic [PIX_WIDTH-1:0] recon_sample;
|
||||
logic [12:0] recon_x;
|
||||
logic [12:0] recon_y;
|
||||
|
||||
// Test vectors and scoreboard.
|
||||
logic [PIX_WIDTH-1:0] sample_mem [0:EVENT_COUNT-1];
|
||||
logic [12:0] x_mem [0:EVENT_COUNT-1];
|
||||
logic [12:0] y_mem [0:EVENT_COUNT-1];
|
||||
logic first_mem [0:EVENT_COUNT-1];
|
||||
logic last_mem [0:EVENT_COUNT-1];
|
||||
logic [PIX_WIDTH-1:0] exp_Ra_mem [0:EVENT_COUNT-1];
|
||||
logic [PIX_WIDTH-1:0] exp_Rb_mem [0:EVENT_COUNT-1];
|
||||
logic [PIX_WIDTH-1:0] exp_Rc_mem [0:EVENT_COUNT-1];
|
||||
logic [PIX_WIDTH-1:0] exp_Rd_mem [0:EVENT_COUNT-1];
|
||||
int drive_index;
|
||||
int receive_index;
|
||||
logic all_done_seen;
|
||||
|
||||
jls_neighbor_provider #(
|
||||
.PIX_WIDTH(PIX_WIDTH),
|
||||
.MAX_PIC_COL(MAX_PIC_COL)
|
||||
) dut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.pixel_valid(pixel_valid),
|
||||
.pixel_ready(pixel_ready),
|
||||
.pixel_sample(pixel_sample),
|
||||
.pixel_x(pixel_x),
|
||||
.pixel_y(pixel_y),
|
||||
.pixel_row_last(pixel_row_last),
|
||||
.strip_first_pixel(strip_first_pixel),
|
||||
.strip_last_pixel(strip_last_pixel),
|
||||
.NEAR(NEAR),
|
||||
.neigh_valid(neigh_valid),
|
||||
.neigh_ready(neigh_ready),
|
||||
.neigh_sample(neigh_sample),
|
||||
.neigh_x(neigh_x),
|
||||
.neigh_y(neigh_y),
|
||||
.neigh_strip_first_pixel(neigh_strip_first_pixel),
|
||||
.neigh_strip_last_pixel(neigh_strip_last_pixel),
|
||||
.Ra(Ra),
|
||||
.Rb(Rb),
|
||||
.Rc(Rc),
|
||||
.Rd(Rd),
|
||||
.recon_valid(recon_valid),
|
||||
.recon_ready(recon_ready),
|
||||
.recon_sample(recon_sample),
|
||||
.recon_x(recon_x),
|
||||
.recon_y(recon_y)
|
||||
);
|
||||
|
||||
always begin
|
||||
#2 clk = ~clk;
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
pixel_row_last = 1'b0;
|
||||
if (pixel_x == (strip_width - 13'd1)) begin
|
||||
pixel_row_last = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
sample_mem[0] = 8'd10; x_mem[0] = 13'd0; y_mem[0] = 13'd0; first_mem[0] = 1'b1; last_mem[0] = 1'b0;
|
||||
sample_mem[1] = 8'd20; x_mem[1] = 13'd1; y_mem[1] = 13'd0; first_mem[1] = 1'b0; last_mem[1] = 1'b0;
|
||||
sample_mem[2] = 8'd30; x_mem[2] = 13'd2; y_mem[2] = 13'd0; first_mem[2] = 1'b0; last_mem[2] = 1'b0;
|
||||
sample_mem[3] = 8'd11; x_mem[3] = 13'd0; y_mem[3] = 13'd1; first_mem[3] = 1'b0; last_mem[3] = 1'b0;
|
||||
sample_mem[4] = 8'd21; x_mem[4] = 13'd1; y_mem[4] = 13'd1; first_mem[4] = 1'b0; last_mem[4] = 1'b0;
|
||||
sample_mem[5] = 8'd31; x_mem[5] = 13'd2; y_mem[5] = 13'd1; first_mem[5] = 1'b0; last_mem[5] = 1'b1;
|
||||
|
||||
exp_Ra_mem[0] = 8'd0; exp_Rb_mem[0] = 8'd0; exp_Rc_mem[0] = 8'd0; exp_Rd_mem[0] = 8'd0;
|
||||
exp_Ra_mem[1] = 8'd10; exp_Rb_mem[1] = 8'd0; exp_Rc_mem[1] = 8'd0; exp_Rd_mem[1] = 8'd0;
|
||||
exp_Ra_mem[2] = 8'd20; exp_Rb_mem[2] = 8'd0; exp_Rc_mem[2] = 8'd0; exp_Rd_mem[2] = 8'd0;
|
||||
exp_Ra_mem[3] = 8'd10; exp_Rb_mem[3] = 8'd10; exp_Rc_mem[3] = 8'd0; exp_Rd_mem[3] = 8'd20;
|
||||
exp_Ra_mem[4] = 8'd11; exp_Rb_mem[4] = 8'd20; exp_Rc_mem[4] = 8'd10; exp_Rd_mem[4] = 8'd30;
|
||||
exp_Ra_mem[5] = 8'd21; exp_Rb_mem[5] = 8'd30; exp_Rc_mem[5] = 8'd20; exp_Rd_mem[5] = 8'd30;
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
pixel_valid = 1'b0;
|
||||
pixel_sample = 8'd0;
|
||||
pixel_x = 13'd0;
|
||||
pixel_y = 13'd0;
|
||||
strip_first_pixel = 1'b0;
|
||||
strip_last_pixel = 1'b0;
|
||||
strip_width = 13'd3;
|
||||
NEAR = 6'd0;
|
||||
neigh_ready = 1'b1;
|
||||
recon_valid = 1'b0;
|
||||
recon_sample = 8'd0;
|
||||
recon_x = 13'd0;
|
||||
recon_y = 13'd0;
|
||||
all_done_seen = 1'b0;
|
||||
|
||||
repeat (5) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
|
||||
for (drive_index = 0; drive_index < EVENT_COUNT; drive_index = drive_index + 1) begin
|
||||
@(negedge clk);
|
||||
pixel_valid = 1'b1;
|
||||
pixel_sample = sample_mem[drive_index];
|
||||
pixel_x = x_mem[drive_index];
|
||||
pixel_y = y_mem[drive_index];
|
||||
strip_first_pixel = first_mem[drive_index];
|
||||
strip_last_pixel = last_mem[drive_index];
|
||||
#1;
|
||||
if (!pixel_ready) begin
|
||||
$fatal(1, "lossless fast path inserted a source stall at %0d", drive_index);
|
||||
end
|
||||
@(posedge clk);
|
||||
end
|
||||
|
||||
@(negedge clk);
|
||||
pixel_valid = 1'b0;
|
||||
strip_first_pixel = 1'b0;
|
||||
strip_last_pixel = 1'b0;
|
||||
|
||||
wait (receive_index == EVENT_COUNT);
|
||||
all_done_seen = 1'b1;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
receive_index <= 0;
|
||||
end else if (neigh_valid && neigh_ready) begin
|
||||
if (receive_index >= EVENT_COUNT) begin
|
||||
$fatal(1, "Unexpected extra neighbor event");
|
||||
end
|
||||
|
||||
if (neigh_sample !== sample_mem[receive_index] ||
|
||||
neigh_x !== x_mem[receive_index] ||
|
||||
neigh_y !== y_mem[receive_index] ||
|
||||
neigh_strip_first_pixel !== first_mem[receive_index] ||
|
||||
neigh_strip_last_pixel !== last_mem[receive_index]) begin
|
||||
$fatal(1, "neighbor metadata mismatch at %0d", receive_index);
|
||||
end
|
||||
|
||||
if (Ra !== exp_Ra_mem[receive_index] ||
|
||||
Rb !== exp_Rb_mem[receive_index] ||
|
||||
Rc !== exp_Rc_mem[receive_index] ||
|
||||
Rd !== exp_Rd_mem[receive_index]) begin
|
||||
$fatal(1, "neighbor mismatch at %0d: Ra=%0d Rb=%0d Rc=%0d Rd=%0d",
|
||||
receive_index, Ra, Rb, Rc, Rd);
|
||||
end
|
||||
|
||||
receive_index <= receive_index + 1;
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (2000) @(posedge clk);
|
||||
$fatal(1, "Timeout waiting for neighbor-provider lossless-fast smoke");
|
||||
end
|
||||
|
||||
initial begin
|
||||
wait (all_done_seen);
|
||||
repeat (4) @(posedge clk);
|
||||
$display("PASS: tb_jls_neighbor_provider_lossless_fast");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
305
fpga/sim/tb_jls_neighbor_provider_near_bypass.sv
Normal file
305
fpga/sim/tb_jls_neighbor_provider_near_bypass.sv
Normal file
@@ -0,0 +1,305 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex A.3 context determination, Annex A.4 prediction
|
||||
// Figure : N/A
|
||||
// Table : N/A
|
||||
// Pseudocode : Reconstructed neighborhood selection for Ra/Rb/Rc/Rd
|
||||
// Example : In NEAR>0, a same-row pixel uses the previous pixel's returned
|
||||
// Rx as Ra after the Rx writeback is committed.
|
||||
//
|
||||
// Smoke test for the NEAR>0 reconstructed-sample writeback boundary. The test
|
||||
// accepts x=1 one cycle after x=0 writeback, and x=2 one cycle after x=1
|
||||
// writeback. It also proves that x=0 of the next row waits for the row-last
|
||||
// writeback before the row transition is accepted.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_neighbor_provider_near_bypass;
|
||||
|
||||
localparam int PIX_WIDTH = 8;
|
||||
localparam int MAX_PIC_COL = 8;
|
||||
localparam int EVENT_COUNT = 4;
|
||||
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
logic pixel_valid;
|
||||
logic pixel_ready;
|
||||
logic [PIX_WIDTH-1:0] pixel_sample;
|
||||
logic [12:0] pixel_x;
|
||||
logic [12:0] pixel_y;
|
||||
logic pixel_row_last;
|
||||
logic strip_first_pixel;
|
||||
logic strip_last_pixel;
|
||||
logic [12:0] strip_width;
|
||||
logic [5:0] NEAR;
|
||||
|
||||
logic neigh_valid;
|
||||
logic neigh_ready;
|
||||
logic [PIX_WIDTH-1:0] neigh_sample;
|
||||
logic [12:0] neigh_x;
|
||||
logic [12:0] neigh_y;
|
||||
logic neigh_strip_first_pixel;
|
||||
logic neigh_strip_last_pixel;
|
||||
logic [PIX_WIDTH-1:0] Ra;
|
||||
logic [PIX_WIDTH-1:0] Rb;
|
||||
logic [PIX_WIDTH-1:0] Rc;
|
||||
logic [PIX_WIDTH-1:0] Rd;
|
||||
|
||||
logic recon_valid;
|
||||
logic recon_ready;
|
||||
logic [PIX_WIDTH-1:0] recon_sample;
|
||||
logic [12:0] recon_x;
|
||||
logic [12:0] recon_y;
|
||||
|
||||
logic [PIX_WIDTH-1:0] sample_mem [0:EVENT_COUNT-1];
|
||||
logic [12:0] x_mem [0:EVENT_COUNT-1];
|
||||
logic [12:0] y_mem [0:EVENT_COUNT-1];
|
||||
logic first_mem [0:EVENT_COUNT-1];
|
||||
logic last_mem [0:EVENT_COUNT-1];
|
||||
logic [PIX_WIDTH-1:0] exp_Ra_mem [0:EVENT_COUNT-1];
|
||||
logic [PIX_WIDTH-1:0] exp_Rb_mem [0:EVENT_COUNT-1];
|
||||
logic [PIX_WIDTH-1:0] exp_Rc_mem [0:EVENT_COUNT-1];
|
||||
logic [PIX_WIDTH-1:0] exp_Rd_mem [0:EVENT_COUNT-1];
|
||||
int receive_index;
|
||||
logic all_done_seen;
|
||||
|
||||
jls_neighbor_provider #(
|
||||
.PIX_WIDTH(PIX_WIDTH),
|
||||
.MAX_PIC_COL(MAX_PIC_COL)
|
||||
) dut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.pixel_valid(pixel_valid),
|
||||
.pixel_ready(pixel_ready),
|
||||
.pixel_sample(pixel_sample),
|
||||
.pixel_x(pixel_x),
|
||||
.pixel_y(pixel_y),
|
||||
.pixel_row_last(pixel_row_last),
|
||||
.strip_first_pixel(strip_first_pixel),
|
||||
.strip_last_pixel(strip_last_pixel),
|
||||
.NEAR(NEAR),
|
||||
.neigh_valid(neigh_valid),
|
||||
.neigh_ready(neigh_ready),
|
||||
.neigh_sample(neigh_sample),
|
||||
.neigh_x(neigh_x),
|
||||
.neigh_y(neigh_y),
|
||||
.neigh_strip_first_pixel(neigh_strip_first_pixel),
|
||||
.neigh_strip_last_pixel(neigh_strip_last_pixel),
|
||||
.Ra(Ra),
|
||||
.Rb(Rb),
|
||||
.Rc(Rc),
|
||||
.Rd(Rd),
|
||||
.recon_valid(recon_valid),
|
||||
.recon_ready(recon_ready),
|
||||
.recon_sample(recon_sample),
|
||||
.recon_x(recon_x),
|
||||
.recon_y(recon_y)
|
||||
);
|
||||
|
||||
always begin
|
||||
#2 clk = ~clk;
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
pixel_row_last = 1'b0;
|
||||
if (pixel_x == (strip_width - 13'd1)) begin
|
||||
pixel_row_last = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
sample_mem[0] = 8'd10; x_mem[0] = 13'd0; y_mem[0] = 13'd0; first_mem[0] = 1'b1; last_mem[0] = 1'b0;
|
||||
sample_mem[1] = 8'd20; x_mem[1] = 13'd1; y_mem[1] = 13'd0; first_mem[1] = 1'b0; last_mem[1] = 1'b0;
|
||||
sample_mem[2] = 8'd30; x_mem[2] = 13'd2; y_mem[2] = 13'd0; first_mem[2] = 1'b0; last_mem[2] = 1'b0;
|
||||
sample_mem[3] = 8'd40; x_mem[3] = 13'd0; y_mem[3] = 13'd1; first_mem[3] = 1'b0; last_mem[3] = 1'b1;
|
||||
|
||||
exp_Ra_mem[0] = 8'd0; exp_Rb_mem[0] = 8'd0; exp_Rc_mem[0] = 8'd0; exp_Rd_mem[0] = 8'd0;
|
||||
exp_Ra_mem[1] = 8'd101; exp_Rb_mem[1] = 8'd0; exp_Rc_mem[1] = 8'd0; exp_Rd_mem[1] = 8'd0;
|
||||
exp_Ra_mem[2] = 8'd102; exp_Rb_mem[2] = 8'd0; exp_Rc_mem[2] = 8'd0; exp_Rd_mem[2] = 8'd0;
|
||||
exp_Ra_mem[3] = 8'd101; exp_Rb_mem[3] = 8'd101; exp_Rc_mem[3] = 8'd0; exp_Rd_mem[3] = 8'd102;
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
pixel_valid = 1'b0;
|
||||
pixel_sample = 8'd0;
|
||||
pixel_x = 13'd0;
|
||||
pixel_y = 13'd0;
|
||||
strip_first_pixel = 1'b0;
|
||||
strip_last_pixel = 1'b0;
|
||||
strip_width = 13'd3;
|
||||
NEAR = 6'd1;
|
||||
neigh_ready = 1'b1;
|
||||
recon_valid = 1'b0;
|
||||
recon_sample = 8'd0;
|
||||
recon_x = 13'd0;
|
||||
recon_y = 13'd0;
|
||||
all_done_seen = 1'b0;
|
||||
|
||||
repeat (5) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
|
||||
@(negedge clk);
|
||||
pixel_valid = 1'b1;
|
||||
pixel_sample = sample_mem[0];
|
||||
pixel_x = x_mem[0];
|
||||
pixel_y = y_mem[0];
|
||||
strip_first_pixel = first_mem[0];
|
||||
strip_last_pixel = last_mem[0];
|
||||
#1;
|
||||
if (!pixel_ready) begin
|
||||
$fatal(1, "first pixel was not accepted");
|
||||
end
|
||||
@(posedge clk);
|
||||
#1;
|
||||
pixel_valid = 1'b0;
|
||||
strip_first_pixel = 1'b0;
|
||||
|
||||
@(negedge clk);
|
||||
recon_valid = 1'b1;
|
||||
recon_sample = 8'd101;
|
||||
recon_x = x_mem[0];
|
||||
recon_y = y_mem[0];
|
||||
pixel_valid = 1'b1;
|
||||
pixel_sample = sample_mem[1];
|
||||
pixel_x = x_mem[1];
|
||||
pixel_y = y_mem[1];
|
||||
#1;
|
||||
if (!recon_ready) begin
|
||||
$fatal(1, "x=0 writeback was not ready");
|
||||
end
|
||||
if (pixel_ready) begin
|
||||
$fatal(1, "x=1 was accepted before x=0 writeback committed");
|
||||
end
|
||||
@(posedge clk);
|
||||
#1;
|
||||
recon_valid = 1'b0;
|
||||
@(negedge clk);
|
||||
#1;
|
||||
if (!pixel_ready) begin
|
||||
$fatal(1, "x=1 was not accepted after x=0 writeback committed");
|
||||
end
|
||||
@(posedge clk);
|
||||
#1;
|
||||
pixel_valid = 1'b0;
|
||||
|
||||
@(negedge clk);
|
||||
recon_valid = 1'b1;
|
||||
recon_sample = 8'd102;
|
||||
recon_x = x_mem[1];
|
||||
recon_y = y_mem[1];
|
||||
pixel_valid = 1'b1;
|
||||
pixel_sample = sample_mem[2];
|
||||
pixel_x = x_mem[2];
|
||||
pixel_y = y_mem[2];
|
||||
#1;
|
||||
if (!recon_ready) begin
|
||||
$fatal(1, "x=1 writeback was not ready");
|
||||
end
|
||||
if (pixel_ready) begin
|
||||
$fatal(1, "x=2 was accepted before x=1 writeback committed");
|
||||
end
|
||||
@(posedge clk);
|
||||
#1;
|
||||
recon_valid = 1'b0;
|
||||
@(negedge clk);
|
||||
#1;
|
||||
if (!pixel_ready) begin
|
||||
$fatal(1, "x=2 was not accepted after x=1 writeback committed");
|
||||
end
|
||||
@(posedge clk);
|
||||
#1;
|
||||
pixel_valid = 1'b0;
|
||||
|
||||
@(negedge clk);
|
||||
recon_valid = 1'b1;
|
||||
recon_sample = 8'd103;
|
||||
recon_x = x_mem[2];
|
||||
recon_y = y_mem[2];
|
||||
pixel_valid = 1'b1;
|
||||
pixel_sample = sample_mem[3];
|
||||
pixel_x = x_mem[3];
|
||||
pixel_y = y_mem[3];
|
||||
strip_last_pixel = last_mem[3];
|
||||
#1;
|
||||
if (!recon_ready) begin
|
||||
$fatal(1, "row-last writeback was not ready");
|
||||
end
|
||||
if (pixel_ready) begin
|
||||
$fatal(1, "row transition was incorrectly bypassed");
|
||||
end
|
||||
@(posedge clk);
|
||||
#1;
|
||||
recon_valid = 1'b0;
|
||||
|
||||
@(negedge clk);
|
||||
#1;
|
||||
if (!pixel_ready) begin
|
||||
$fatal(1, "next row first pixel was not accepted after row transition");
|
||||
end
|
||||
@(posedge clk);
|
||||
#1;
|
||||
pixel_valid = 1'b0;
|
||||
strip_last_pixel = 1'b0;
|
||||
|
||||
@(negedge clk);
|
||||
recon_valid = 1'b1;
|
||||
recon_sample = 8'd104;
|
||||
recon_x = x_mem[3];
|
||||
recon_y = y_mem[3];
|
||||
#1;
|
||||
if (!recon_ready) begin
|
||||
$fatal(1, "final writeback was not ready");
|
||||
end
|
||||
@(posedge clk);
|
||||
#1;
|
||||
recon_valid = 1'b0;
|
||||
|
||||
wait (receive_index == EVENT_COUNT);
|
||||
all_done_seen = 1'b1;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
receive_index <= 0;
|
||||
end else if (neigh_valid && neigh_ready) begin
|
||||
if (receive_index >= EVENT_COUNT) begin
|
||||
$fatal(1, "Unexpected extra neighbor event");
|
||||
end
|
||||
|
||||
if (neigh_sample !== sample_mem[receive_index] ||
|
||||
neigh_x !== x_mem[receive_index] ||
|
||||
neigh_y !== y_mem[receive_index] ||
|
||||
neigh_strip_first_pixel !== first_mem[receive_index] ||
|
||||
neigh_strip_last_pixel !== last_mem[receive_index]) begin
|
||||
$fatal(1, "neighbor metadata mismatch at %0d", receive_index);
|
||||
end
|
||||
|
||||
if (Ra !== exp_Ra_mem[receive_index] ||
|
||||
Rb !== exp_Rb_mem[receive_index] ||
|
||||
Rc !== exp_Rc_mem[receive_index] ||
|
||||
Rd !== exp_Rd_mem[receive_index]) begin
|
||||
$fatal(1, "neighbor mismatch at %0d: Ra=%0d Rb=%0d Rc=%0d Rd=%0d",
|
||||
receive_index, Ra, Rb, Rc, Rd);
|
||||
end
|
||||
|
||||
receive_index <= receive_index + 1;
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (2000) @(posedge clk);
|
||||
$fatal(1, "Timeout waiting for neighbor-provider near-bypass smoke");
|
||||
end
|
||||
|
||||
initial begin
|
||||
wait (all_done_seen);
|
||||
repeat (4) @(posedge clk);
|
||||
$display("PASS: tb_jls_neighbor_provider_near_bypass");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
167
fpga/sim/tb_jls_output_buffer.sv
Normal file
167
fpga/sim/tb_jls_output_buffer.sv
Normal file
@@ -0,0 +1,167 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex C.1-C.4 marker stream byte order
|
||||
// Figure : N/A
|
||||
// Table : N/A
|
||||
// Pseudocode : Encoded byte stream delivery after JPEG-LS bit packing
|
||||
// Example : Checks byte order and ofifo_wdata[8] placement.
|
||||
//
|
||||
// Smoke test for jls_output_buffer.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_output_buffer;
|
||||
|
||||
// Small buffer for fast smoke simulation.
|
||||
localparam int OUT_BUF_BYTES = 8;
|
||||
localparam int OUT_BUF_AFULL_MARGIN = 2;
|
||||
localparam int TEST_BYTE_COUNT = 6;
|
||||
|
||||
// Main simulation clock and reset.
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
// Encoded byte event interface.
|
||||
logic byte_valid;
|
||||
logic byte_ready;
|
||||
logic [7:0] byte_data;
|
||||
logic original_image_start;
|
||||
logic byte_accepted;
|
||||
logic pause_req;
|
||||
logic [$clog2(OUT_BUF_BYTES + 1)-1:0] buffer_level;
|
||||
|
||||
// Output FIFO interface under test.
|
||||
logic ofifo_wclk;
|
||||
logic ofifo_wr;
|
||||
logic [8:0] ofifo_wdata;
|
||||
logic ofifo_full;
|
||||
logic ofifo_alfull;
|
||||
|
||||
// Test input and expected output streams.
|
||||
logic [7:0] input_byte_mem [0:TEST_BYTE_COUNT-1];
|
||||
logic input_start_mem [0:TEST_BYTE_COUNT-1];
|
||||
logic [8:0] expected_word_mem [0:TEST_BYTE_COUNT-1];
|
||||
int send_index;
|
||||
int receive_index;
|
||||
logic done_seen;
|
||||
|
||||
jls_output_buffer #(
|
||||
.OUT_BUF_BYTES(OUT_BUF_BYTES),
|
||||
.OUT_BUF_AFULL_MARGIN(OUT_BUF_AFULL_MARGIN)
|
||||
) dut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.byte_valid(byte_valid),
|
||||
.byte_ready(byte_ready),
|
||||
.byte_data(byte_data),
|
||||
.original_image_start(original_image_start),
|
||||
.byte_accepted(byte_accepted),
|
||||
.pause_req(pause_req),
|
||||
.buffer_level(buffer_level),
|
||||
.ofifo_wclk(ofifo_wclk),
|
||||
.ofifo_wr(ofifo_wr),
|
||||
.ofifo_wdata(ofifo_wdata),
|
||||
.ofifo_full(ofifo_full),
|
||||
.ofifo_alfull(ofifo_alfull)
|
||||
);
|
||||
|
||||
always begin
|
||||
#2 clk = ~clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
input_byte_mem[0] = 8'hFF;
|
||||
input_byte_mem[1] = 8'hD8;
|
||||
input_byte_mem[2] = 8'h12;
|
||||
input_byte_mem[3] = 8'h34;
|
||||
input_byte_mem[4] = 8'h56;
|
||||
input_byte_mem[5] = 8'hD9;
|
||||
|
||||
input_start_mem[0] = 1'b1;
|
||||
input_start_mem[1] = 1'b0;
|
||||
input_start_mem[2] = 1'b0;
|
||||
input_start_mem[3] = 1'b0;
|
||||
input_start_mem[4] = 1'b0;
|
||||
input_start_mem[5] = 1'b0;
|
||||
|
||||
expected_word_mem[0] = 9'h1FF;
|
||||
expected_word_mem[1] = 9'h0D8;
|
||||
expected_word_mem[2] = 9'h012;
|
||||
expected_word_mem[3] = 9'h034;
|
||||
expected_word_mem[4] = 9'h056;
|
||||
expected_word_mem[5] = 9'h0D9;
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
byte_valid = 1'b0;
|
||||
byte_data = 8'h00;
|
||||
original_image_start = 1'b0;
|
||||
ofifo_full = 1'b0;
|
||||
ofifo_alfull = 1'b0;
|
||||
|
||||
repeat (5) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
|
||||
for (send_index = 0; send_index < TEST_BYTE_COUNT; send_index = send_index + 1) begin
|
||||
@(posedge clk);
|
||||
byte_valid = 1'b1;
|
||||
byte_data = input_byte_mem[send_index];
|
||||
original_image_start = input_start_mem[send_index];
|
||||
end
|
||||
|
||||
@(posedge clk);
|
||||
byte_valid = 1'b0;
|
||||
byte_data = 8'h00;
|
||||
original_image_start = 1'b0;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
receive_index <= 0;
|
||||
done_seen <= 1'b0;
|
||||
end else begin
|
||||
if (ofifo_wclk !== clk) begin
|
||||
$fatal(1, "ofifo_wclk is not tied to clk");
|
||||
end
|
||||
|
||||
if (byte_valid && !byte_ready) begin
|
||||
$fatal(1, "byte_ready unexpectedly deasserted in smoke test");
|
||||
end
|
||||
|
||||
if (ofifo_wr) begin
|
||||
if (receive_index >= TEST_BYTE_COUNT) begin
|
||||
$fatal(1, "Unexpected extra output word 0x%03h", ofifo_wdata);
|
||||
end
|
||||
|
||||
if (ofifo_wdata !== expected_word_mem[receive_index]) begin
|
||||
$fatal(1, "ofifo word mismatch at %0d: got 0x%03h expected 0x%03h",
|
||||
receive_index, ofifo_wdata, expected_word_mem[receive_index]);
|
||||
end
|
||||
|
||||
receive_index <= receive_index + 1;
|
||||
if (receive_index == (TEST_BYTE_COUNT - 1)) begin
|
||||
done_seen <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (1000) @(posedge clk);
|
||||
$fatal(1, "Timeout waiting for output buffer stream");
|
||||
end
|
||||
|
||||
initial begin
|
||||
wait (done_seen);
|
||||
repeat (4) @(posedge clk);
|
||||
if (receive_index !== TEST_BYTE_COUNT) begin
|
||||
$fatal(1, "receive_index mismatch: got %0d", receive_index);
|
||||
end
|
||||
$display("PASS: tb_jls_output_buffer");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
238
fpga/sim/tb_jls_prediction_corrector.sv
Normal file
238
fpga/sim/tb_jls_prediction_corrector.sv
Normal file
@@ -0,0 +1,238 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex A.5 prediction error encoding, Annex A.6 bias variables
|
||||
// Figure : N/A
|
||||
// Table : N/A
|
||||
// Pseudocode : Px correction by C[Q] followed by bounds correction
|
||||
// Example : Checks normal correction and 0..MAXVAL saturation.
|
||||
//
|
||||
// Smoke test for jls_prediction_corrector.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_prediction_corrector;
|
||||
|
||||
// Test precision.
|
||||
localparam int PIX_WIDTH = 8;
|
||||
|
||||
// Expected corrected events.
|
||||
localparam int EXPECTED_EVENT_COUNT = 4;
|
||||
|
||||
// Main simulation clock and reset.
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
// Input context event.
|
||||
logic context_valid;
|
||||
logic context_ready;
|
||||
logic [PIX_WIDTH-1:0] context_sample;
|
||||
logic [12:0] context_x;
|
||||
logic [12:0] context_y;
|
||||
logic context_strip_first_pixel;
|
||||
logic context_strip_last_pixel;
|
||||
logic [PIX_WIDTH-1:0] Px;
|
||||
logic [31:0] A;
|
||||
logic signed [31:0] B;
|
||||
logic signed [8:0] C;
|
||||
logic [15:0] N;
|
||||
logic context_negative;
|
||||
logic [8:0] context_index;
|
||||
logic run_mode_context;
|
||||
|
||||
// Corrected output event.
|
||||
logic corrected_valid;
|
||||
logic corrected_ready;
|
||||
logic [PIX_WIDTH-1:0] corrected_sample;
|
||||
logic [12:0] corrected_x;
|
||||
logic [12:0] corrected_y;
|
||||
logic corrected_strip_first_pixel;
|
||||
logic corrected_strip_last_pixel;
|
||||
logic [PIX_WIDTH-1:0] corrected_Px;
|
||||
logic [8:0] corrected_context_index;
|
||||
logic corrected_context_negative;
|
||||
logic corrected_run_mode_context;
|
||||
logic [31:0] corrected_A;
|
||||
logic signed [31:0] corrected_B;
|
||||
logic signed [8:0] corrected_C;
|
||||
logic [15:0] corrected_N;
|
||||
|
||||
// Scoreboard state.
|
||||
logic [PIX_WIDTH-1:0] expected_px_mem [0:EXPECTED_EVENT_COUNT-1];
|
||||
int receive_index;
|
||||
logic all_done_seen;
|
||||
|
||||
jls_prediction_corrector #(
|
||||
.PIX_WIDTH(PIX_WIDTH)
|
||||
) dut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.context_valid(context_valid),
|
||||
.context_ready(context_ready),
|
||||
.context_sample(context_sample),
|
||||
.context_x(context_x),
|
||||
.context_y(context_y),
|
||||
.context_strip_first_pixel(context_strip_first_pixel),
|
||||
.context_strip_last_pixel(context_strip_last_pixel),
|
||||
.Px(Px),
|
||||
.A(A),
|
||||
.B(B),
|
||||
.C(C),
|
||||
.N(N),
|
||||
.context_negative(context_negative),
|
||||
.context_index(context_index),
|
||||
.run_mode_context(run_mode_context),
|
||||
.corrected_valid(corrected_valid),
|
||||
.corrected_ready(corrected_ready),
|
||||
.corrected_sample(corrected_sample),
|
||||
.corrected_x(corrected_x),
|
||||
.corrected_y(corrected_y),
|
||||
.corrected_strip_first_pixel(corrected_strip_first_pixel),
|
||||
.corrected_strip_last_pixel(corrected_strip_last_pixel),
|
||||
.corrected_Px(corrected_Px),
|
||||
.corrected_context_index(corrected_context_index),
|
||||
.corrected_context_negative(corrected_context_negative),
|
||||
.corrected_run_mode_context(corrected_run_mode_context),
|
||||
.corrected_A(corrected_A),
|
||||
.corrected_B(corrected_B),
|
||||
.corrected_C(corrected_C),
|
||||
.corrected_N(corrected_N)
|
||||
);
|
||||
|
||||
always begin
|
||||
#2 clk = ~clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
expected_px_mem[0] = 8'd17;
|
||||
expected_px_mem[1] = 8'd23;
|
||||
expected_px_mem[2] = 8'd0;
|
||||
expected_px_mem[3] = 8'd255;
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
context_valid = 1'b0;
|
||||
context_sample = 8'd0;
|
||||
context_x = 13'd0;
|
||||
context_y = 13'd0;
|
||||
context_strip_first_pixel = 1'b0;
|
||||
context_strip_last_pixel = 1'b0;
|
||||
Px = 8'd0;
|
||||
A = 32'd4;
|
||||
B = 32'sd0;
|
||||
C = 9'sd0;
|
||||
N = 16'd1;
|
||||
context_negative = 1'b0;
|
||||
context_index = 9'd0;
|
||||
run_mode_context = 1'b0;
|
||||
corrected_ready = 1'b1;
|
||||
all_done_seen = 1'b0;
|
||||
|
||||
repeat (5) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
|
||||
wait (context_ready);
|
||||
@(posedge clk);
|
||||
context_valid = 1'b1;
|
||||
context_sample = 8'd100;
|
||||
context_x = 13'd1;
|
||||
context_y = 13'd2;
|
||||
context_strip_first_pixel = 1'b1;
|
||||
Px = 8'd20;
|
||||
C = -9'sd3;
|
||||
context_negative = 1'b0;
|
||||
context_index = 9'd7;
|
||||
@(posedge clk);
|
||||
context_valid = 1'b0;
|
||||
context_strip_first_pixel = 1'b0;
|
||||
|
||||
wait (context_ready);
|
||||
@(posedge clk);
|
||||
context_valid = 1'b1;
|
||||
context_sample = 8'd101;
|
||||
context_x = 13'd2;
|
||||
Px = 8'd20;
|
||||
C = -9'sd3;
|
||||
context_negative = 1'b1;
|
||||
context_index = 9'd8;
|
||||
@(posedge clk);
|
||||
context_valid = 1'b0;
|
||||
|
||||
wait (context_ready);
|
||||
@(posedge clk);
|
||||
context_valid = 1'b1;
|
||||
context_sample = 8'd102;
|
||||
context_x = 13'd3;
|
||||
Px = 8'd2;
|
||||
C = -9'sd5;
|
||||
context_negative = 1'b0;
|
||||
context_index = 9'd9;
|
||||
@(posedge clk);
|
||||
context_valid = 1'b0;
|
||||
|
||||
wait (context_ready);
|
||||
@(posedge clk);
|
||||
context_valid = 1'b1;
|
||||
context_sample = 8'd103;
|
||||
context_x = 13'd4;
|
||||
context_strip_last_pixel = 1'b1;
|
||||
Px = 8'd250;
|
||||
C = 9'sd10;
|
||||
context_negative = 1'b0;
|
||||
context_index = 9'd10;
|
||||
@(posedge clk);
|
||||
context_valid = 1'b0;
|
||||
context_strip_last_pixel = 1'b0;
|
||||
|
||||
wait (receive_index == EXPECTED_EVENT_COUNT);
|
||||
all_done_seen = 1'b1;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
receive_index <= 0;
|
||||
end else if (corrected_valid && corrected_ready) begin
|
||||
if (receive_index >= EXPECTED_EVENT_COUNT) begin
|
||||
$fatal(1, "Unexpected extra corrected-prediction event");
|
||||
end
|
||||
|
||||
if (corrected_Px !== expected_px_mem[receive_index]) begin
|
||||
$fatal(1, "corrected_Px mismatch at %0d: got %0d expected %0d",
|
||||
receive_index, corrected_Px, expected_px_mem[receive_index]);
|
||||
end
|
||||
|
||||
if (corrected_sample !== (8'd100 + receive_index[7:0])) begin
|
||||
$fatal(1, "corrected_sample mismatch at %0d", receive_index);
|
||||
end
|
||||
|
||||
if (corrected_context_index !== (9'd7 + receive_index[8:0])) begin
|
||||
$fatal(1, "corrected_context_index mismatch at %0d", receive_index);
|
||||
end
|
||||
|
||||
if (receive_index == 0 && corrected_strip_first_pixel !== 1'b1) begin
|
||||
$fatal(1, "first event should preserve strip_first_pixel");
|
||||
end
|
||||
|
||||
if (receive_index == 3 && corrected_strip_last_pixel !== 1'b1) begin
|
||||
$fatal(1, "last event should preserve strip_last_pixel");
|
||||
end
|
||||
|
||||
receive_index <= receive_index + 1;
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (2000) @(posedge clk);
|
||||
$fatal(1, "Timeout waiting for prediction corrector smoke");
|
||||
end
|
||||
|
||||
initial begin
|
||||
wait (all_done_seen);
|
||||
repeat (4) @(posedge clk);
|
||||
$display("PASS: tb_jls_prediction_corrector");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
239
fpga/sim/tb_jls_predictor.sv
Normal file
239
fpga/sim/tb_jls_predictor.sv
Normal file
@@ -0,0 +1,239 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex A.4 prediction
|
||||
// Figure : N/A
|
||||
// Table : N/A
|
||||
// Pseudocode : MED predictor / Px calculation from Ra, Rb, and Rc
|
||||
// Example : Covers Rc inside, above, and below the Ra/Rb interval.
|
||||
//
|
||||
// Smoke test for jls_predictor.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_predictor;
|
||||
|
||||
// Test precision.
|
||||
localparam int PIX_WIDTH = 16;
|
||||
|
||||
// Expected predicted events.
|
||||
localparam int EXPECTED_EVENT_COUNT = 4;
|
||||
|
||||
// Main simulation clock and reset.
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
// Input pixel/neighborhood interface.
|
||||
logic pixel_valid;
|
||||
logic pixel_ready;
|
||||
logic [PIX_WIDTH-1:0] pixel_sample;
|
||||
logic [12:0] pixel_x;
|
||||
logic [12:0] pixel_y;
|
||||
logic strip_first_pixel;
|
||||
logic strip_last_pixel;
|
||||
logic [PIX_WIDTH-1:0] Ra;
|
||||
logic [PIX_WIDTH-1:0] Rb;
|
||||
logic [PIX_WIDTH-1:0] Rc;
|
||||
logic [PIX_WIDTH-1:0] Rd;
|
||||
|
||||
// Output predicted event.
|
||||
logic predict_valid;
|
||||
logic predict_ready;
|
||||
logic [PIX_WIDTH-1:0] predict_sample;
|
||||
logic [12:0] predict_x;
|
||||
logic [12:0] predict_y;
|
||||
logic predict_strip_first_pixel;
|
||||
logic predict_strip_last_pixel;
|
||||
logic [PIX_WIDTH-1:0] predict_Ra;
|
||||
logic [PIX_WIDTH-1:0] predict_Rb;
|
||||
logic [PIX_WIDTH-1:0] predict_Rc;
|
||||
logic [PIX_WIDTH-1:0] predict_Rd;
|
||||
logic [PIX_WIDTH-1:0] Px;
|
||||
|
||||
// Scoreboard state.
|
||||
logic [PIX_WIDTH-1:0] expected_px_mem [0:EXPECTED_EVENT_COUNT-1];
|
||||
logic [PIX_WIDTH-1:0] expected_sample_mem [0:EXPECTED_EVENT_COUNT-1];
|
||||
int receive_index;
|
||||
logic all_done_seen;
|
||||
|
||||
jls_predictor #(
|
||||
.PIX_WIDTH(PIX_WIDTH)
|
||||
) dut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.pixel_valid(pixel_valid),
|
||||
.pixel_ready(pixel_ready),
|
||||
.pixel_sample(pixel_sample),
|
||||
.pixel_x(pixel_x),
|
||||
.pixel_y(pixel_y),
|
||||
.strip_first_pixel(strip_first_pixel),
|
||||
.strip_last_pixel(strip_last_pixel),
|
||||
.Ra(Ra),
|
||||
.Rb(Rb),
|
||||
.Rc(Rc),
|
||||
.Rd(Rd),
|
||||
.predict_valid(predict_valid),
|
||||
.predict_ready(predict_ready),
|
||||
.predict_sample(predict_sample),
|
||||
.predict_x(predict_x),
|
||||
.predict_y(predict_y),
|
||||
.predict_strip_first_pixel(predict_strip_first_pixel),
|
||||
.predict_strip_last_pixel(predict_strip_last_pixel),
|
||||
.predict_Ra(predict_Ra),
|
||||
.predict_Rb(predict_Rb),
|
||||
.predict_Rc(predict_Rc),
|
||||
.predict_Rd(predict_Rd),
|
||||
.Px(Px)
|
||||
);
|
||||
|
||||
always begin
|
||||
#2 clk = ~clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
expected_px_mem[0] = 16'd15;
|
||||
expected_sample_mem[0] = 16'd100;
|
||||
expected_px_mem[1] = 16'd10;
|
||||
expected_sample_mem[1] = 16'd101;
|
||||
expected_px_mem[2] = 16'd20;
|
||||
expected_sample_mem[2] = 16'd102;
|
||||
expected_px_mem[3] = 16'd15;
|
||||
expected_sample_mem[3] = 16'd103;
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
pixel_valid = 1'b0;
|
||||
pixel_sample = 16'd0;
|
||||
pixel_x = 13'd0;
|
||||
pixel_y = 13'd0;
|
||||
strip_first_pixel = 1'b0;
|
||||
strip_last_pixel = 1'b0;
|
||||
Ra = 16'd0;
|
||||
Rb = 16'd0;
|
||||
Rc = 16'd0;
|
||||
Rd = 16'd0;
|
||||
predict_ready = 1'b1;
|
||||
all_done_seen = 1'b0;
|
||||
|
||||
repeat (5) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
|
||||
wait (pixel_ready);
|
||||
@(posedge clk);
|
||||
pixel_valid = 1'b1;
|
||||
pixel_sample = 16'd100;
|
||||
pixel_x = 13'd1;
|
||||
pixel_y = 13'd2;
|
||||
strip_first_pixel = 1'b1;
|
||||
strip_last_pixel = 1'b0;
|
||||
Ra = 16'd10;
|
||||
Rb = 16'd20;
|
||||
Rc = 16'd15;
|
||||
Rd = 16'd22;
|
||||
@(posedge clk);
|
||||
pixel_valid = 1'b0;
|
||||
strip_first_pixel = 1'b0;
|
||||
|
||||
// Hold the first prediction event in the output register and prove the
|
||||
// local two-entry queue accepts one more MED prediction before applying
|
||||
// backpressure. This intentionally breaks downstream ready fan-in from
|
||||
// the upstream mode-router path.
|
||||
predict_ready = 1'b0;
|
||||
@(negedge clk);
|
||||
pixel_valid = 1'b1;
|
||||
pixel_sample = 16'd101;
|
||||
pixel_x = 13'd2;
|
||||
pixel_y = 13'd2;
|
||||
Ra = 16'd10;
|
||||
Rb = 16'd20;
|
||||
Rc = 16'd25;
|
||||
Rd = 16'd30;
|
||||
if (pixel_ready !== 1'b1) begin
|
||||
$fatal(1, "pixel_ready should accept one queued prediction while downstream is stalled");
|
||||
end
|
||||
@(posedge clk);
|
||||
@(negedge clk);
|
||||
pixel_valid = 1'b0;
|
||||
if (pixel_ready !== 1'b0) begin
|
||||
$fatal(1, "pixel_ready should be low after the predictor queue becomes full");
|
||||
end
|
||||
predict_ready = 1'b1;
|
||||
wait (pixel_ready);
|
||||
@(posedge clk);
|
||||
|
||||
wait (pixel_ready);
|
||||
@(posedge clk);
|
||||
pixel_valid = 1'b1;
|
||||
pixel_sample = 16'd102;
|
||||
pixel_x = 13'd3;
|
||||
pixel_y = 13'd2;
|
||||
Ra = 16'd10;
|
||||
Rb = 16'd20;
|
||||
Rc = 16'd5;
|
||||
Rd = 16'd6;
|
||||
@(posedge clk);
|
||||
pixel_valid = 1'b0;
|
||||
|
||||
wait (pixel_ready);
|
||||
@(posedge clk);
|
||||
pixel_valid = 1'b1;
|
||||
pixel_sample = 16'd103;
|
||||
pixel_x = 13'd4;
|
||||
pixel_y = 13'd2;
|
||||
strip_last_pixel = 1'b1;
|
||||
Ra = 16'd30;
|
||||
Rb = 16'd5;
|
||||
Rc = 16'd20;
|
||||
Rd = 16'd7;
|
||||
@(posedge clk);
|
||||
pixel_valid = 1'b0;
|
||||
strip_last_pixel = 1'b0;
|
||||
|
||||
wait (receive_index == EXPECTED_EVENT_COUNT);
|
||||
all_done_seen = 1'b1;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
receive_index <= 0;
|
||||
end else if (predict_valid && predict_ready) begin
|
||||
if (receive_index >= EXPECTED_EVENT_COUNT) begin
|
||||
$fatal(1, "Unexpected extra predicted event");
|
||||
end
|
||||
|
||||
if (Px !== expected_px_mem[receive_index]) begin
|
||||
$fatal(1, "Px mismatch at %0d: got %0d expected %0d",
|
||||
receive_index, Px, expected_px_mem[receive_index]);
|
||||
end
|
||||
|
||||
if (predict_sample !== expected_sample_mem[receive_index]) begin
|
||||
$fatal(1, "predict_sample mismatch at %0d", receive_index);
|
||||
end
|
||||
|
||||
if (receive_index == 0 && predict_strip_first_pixel !== 1'b1) begin
|
||||
$fatal(1, "first event should preserve strip_first_pixel");
|
||||
end
|
||||
|
||||
if (receive_index == 3 && predict_strip_last_pixel !== 1'b1) begin
|
||||
$fatal(1, "last event should preserve strip_last_pixel");
|
||||
end
|
||||
|
||||
receive_index <= receive_index + 1;
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (2000) @(posedge clk);
|
||||
$fatal(1, "Timeout waiting for predictor smoke");
|
||||
end
|
||||
|
||||
initial begin
|
||||
wait (all_done_seen);
|
||||
repeat (4) @(posedge clk);
|
||||
$display("PASS: tb_jls_predictor");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
172
fpga/sim/tb_jls_preset_defaults.sv
Normal file
172
fpga/sim/tb_jls_preset_defaults.sv
Normal file
@@ -0,0 +1,172 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex C.2.4.1.1 preset coding parameters
|
||||
// Figure : C.3 clamping function, referenced by default threshold rules
|
||||
// Table : Table C.1 valid preset parameters, Table C.2 RESET, Table C.3 defaults
|
||||
// Pseudocode : Default threshold calculation for MAXVAL >= 128
|
||||
// Example : Checks 8/10/12/14/16-bit defaults and NEAR clamp behavior.
|
||||
//
|
||||
// Smoke test for jls_preset_defaults.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_preset_defaults;
|
||||
|
||||
// Test NEAR inputs.
|
||||
logic [5:0] near_8b_0;
|
||||
logic [5:0] near_8b_31;
|
||||
logic [5:0] near_10b_4;
|
||||
logic [5:0] near_12b_1;
|
||||
logic [5:0] near_14b_2;
|
||||
logic [5:0] near_16b_63;
|
||||
|
||||
// Preset outputs for each instance under test.
|
||||
logic [15:0] maxval_8b_0;
|
||||
logic [15:0] t1_8b_0;
|
||||
logic [15:0] t2_8b_0;
|
||||
logic [15:0] t3_8b_0;
|
||||
logic [15:0] reset_8b_0;
|
||||
logic [15:0] maxval_8b_31;
|
||||
logic [15:0] t1_8b_31;
|
||||
logic [15:0] t2_8b_31;
|
||||
logic [15:0] t3_8b_31;
|
||||
logic [15:0] reset_8b_31;
|
||||
logic [15:0] maxval_10b_4;
|
||||
logic [15:0] t1_10b_4;
|
||||
logic [15:0] t2_10b_4;
|
||||
logic [15:0] t3_10b_4;
|
||||
logic [15:0] reset_10b_4;
|
||||
logic [15:0] maxval_12b_1;
|
||||
logic [15:0] t1_12b_1;
|
||||
logic [15:0] t2_12b_1;
|
||||
logic [15:0] t3_12b_1;
|
||||
logic [15:0] reset_12b_1;
|
||||
logic [15:0] maxval_14b_2;
|
||||
logic [15:0] t1_14b_2;
|
||||
logic [15:0] t2_14b_2;
|
||||
logic [15:0] t3_14b_2;
|
||||
logic [15:0] reset_14b_2;
|
||||
logic [15:0] maxval_16b_63;
|
||||
logic [15:0] t1_16b_63;
|
||||
logic [15:0] t2_16b_63;
|
||||
logic [15:0] t3_16b_63;
|
||||
logic [15:0] reset_16b_63;
|
||||
|
||||
jls_preset_defaults #(
|
||||
.PIX_WIDTH(8)
|
||||
) dut_8b_0 (
|
||||
.near(near_8b_0),
|
||||
.preset_maxval(maxval_8b_0),
|
||||
.preset_t1(t1_8b_0),
|
||||
.preset_t2(t2_8b_0),
|
||||
.preset_t3(t3_8b_0),
|
||||
.preset_reset(reset_8b_0)
|
||||
);
|
||||
|
||||
jls_preset_defaults #(
|
||||
.PIX_WIDTH(8)
|
||||
) dut_8b_31 (
|
||||
.near(near_8b_31),
|
||||
.preset_maxval(maxval_8b_31),
|
||||
.preset_t1(t1_8b_31),
|
||||
.preset_t2(t2_8b_31),
|
||||
.preset_t3(t3_8b_31),
|
||||
.preset_reset(reset_8b_31)
|
||||
);
|
||||
|
||||
jls_preset_defaults #(
|
||||
.PIX_WIDTH(10)
|
||||
) dut_10b_4 (
|
||||
.near(near_10b_4),
|
||||
.preset_maxval(maxval_10b_4),
|
||||
.preset_t1(t1_10b_4),
|
||||
.preset_t2(t2_10b_4),
|
||||
.preset_t3(t3_10b_4),
|
||||
.preset_reset(reset_10b_4)
|
||||
);
|
||||
|
||||
jls_preset_defaults #(
|
||||
.PIX_WIDTH(12)
|
||||
) dut_12b_1 (
|
||||
.near(near_12b_1),
|
||||
.preset_maxval(maxval_12b_1),
|
||||
.preset_t1(t1_12b_1),
|
||||
.preset_t2(t2_12b_1),
|
||||
.preset_t3(t3_12b_1),
|
||||
.preset_reset(reset_12b_1)
|
||||
);
|
||||
|
||||
jls_preset_defaults #(
|
||||
.PIX_WIDTH(14)
|
||||
) dut_14b_2 (
|
||||
.near(near_14b_2),
|
||||
.preset_maxval(maxval_14b_2),
|
||||
.preset_t1(t1_14b_2),
|
||||
.preset_t2(t2_14b_2),
|
||||
.preset_t3(t3_14b_2),
|
||||
.preset_reset(reset_14b_2)
|
||||
);
|
||||
|
||||
jls_preset_defaults #(
|
||||
.PIX_WIDTH(16)
|
||||
) dut_16b_63 (
|
||||
.near(near_16b_63),
|
||||
.preset_maxval(maxval_16b_63),
|
||||
.preset_t1(t1_16b_63),
|
||||
.preset_t2(t2_16b_63),
|
||||
.preset_t3(t3_16b_63),
|
||||
.preset_reset(reset_16b_63)
|
||||
);
|
||||
|
||||
initial begin
|
||||
near_8b_0 = 6'd0;
|
||||
near_8b_31 = 6'd31;
|
||||
near_10b_4 = 6'd4;
|
||||
near_12b_1 = 6'd1;
|
||||
near_14b_2 = 6'd2;
|
||||
near_16b_63 = 6'd63;
|
||||
|
||||
#1;
|
||||
|
||||
if (maxval_8b_0 !== 16'd255 || t1_8b_0 !== 16'd3 ||
|
||||
t2_8b_0 !== 16'd7 || t3_8b_0 !== 16'd21 ||
|
||||
reset_8b_0 !== 16'd64) begin
|
||||
$fatal(1, "8-bit NEAR=0 defaults mismatch");
|
||||
end
|
||||
|
||||
if (maxval_8b_31 !== 16'd255 || t1_8b_31 !== 16'd96 ||
|
||||
t2_8b_31 !== 16'd162 || t3_8b_31 !== 16'd238 ||
|
||||
reset_8b_31 !== 16'd64) begin
|
||||
$fatal(1, "8-bit NEAR=31 defaults mismatch");
|
||||
end
|
||||
|
||||
if (maxval_10b_4 !== 16'd1023 || t1_10b_4 !== 16'd18 ||
|
||||
t2_10b_4 !== 16'd39 || t3_10b_4 !== 16'd100 ||
|
||||
reset_10b_4 !== 16'd64) begin
|
||||
$fatal(1, "10-bit NEAR=4 defaults mismatch");
|
||||
end
|
||||
|
||||
if (maxval_12b_1 !== 16'd4095 || t1_12b_1 !== 16'd21 ||
|
||||
t2_12b_1 !== 16'd72 || t3_12b_1 !== 16'd283 ||
|
||||
reset_12b_1 !== 16'd64) begin
|
||||
$fatal(1, "12-bit NEAR=1 defaults mismatch");
|
||||
end
|
||||
|
||||
if (maxval_14b_2 !== 16'd16383 || t1_14b_2 !== 16'd24 ||
|
||||
t2_14b_2 !== 16'd77 || t3_14b_2 !== 16'd290 ||
|
||||
reset_14b_2 !== 16'd64) begin
|
||||
$fatal(1, "14-bit NEAR=2 defaults mismatch");
|
||||
end
|
||||
|
||||
if (maxval_16b_63 !== 16'hFFFF || t1_16b_63 !== 16'd111 ||
|
||||
t2_16b_63 !== 16'd222 || t3_16b_63 !== 16'd493 ||
|
||||
reset_16b_63 !== 16'd64) begin
|
||||
$fatal(1, "16-bit NEAR clamp defaults mismatch");
|
||||
end
|
||||
|
||||
$display("PASS: tb_jls_preset_defaults");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
296
fpga/sim/tb_jls_regular_error_quantizer.sv
Normal file
296
fpga/sim/tb_jls_regular_error_quantizer.sv
Normal file
@@ -0,0 +1,296 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex A.5 prediction error encoding, Annex A.2 RANGE
|
||||
// Figure : N/A
|
||||
// Table : N/A
|
||||
// Pseudocode : Errval quantization/modulo and reconstructed sample computation
|
||||
// Example : Checks lossless, NEAR=1, context sign, and modulo reconstruction.
|
||||
//
|
||||
// Smoke test for jls_regular_error_quantizer.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_regular_error_quantizer;
|
||||
|
||||
// Test precision.
|
||||
localparam int PIX_WIDTH = 8;
|
||||
|
||||
// Expected events.
|
||||
localparam int EXPECTED_EVENT_COUNT = 6;
|
||||
|
||||
// Main simulation clock and reset.
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
// Corrected prediction input.
|
||||
logic corrected_valid;
|
||||
logic corrected_ready;
|
||||
logic [PIX_WIDTH-1:0] corrected_sample;
|
||||
logic [12:0] corrected_x;
|
||||
logic [12:0] corrected_y;
|
||||
logic corrected_strip_first_pixel;
|
||||
logic corrected_strip_last_pixel;
|
||||
logic [PIX_WIDTH-1:0] corrected_Px;
|
||||
logic [8:0] corrected_context_index;
|
||||
logic corrected_context_negative;
|
||||
logic corrected_run_mode_context;
|
||||
logic [31:0] corrected_A;
|
||||
logic signed [31:0] corrected_B;
|
||||
logic signed [8:0] corrected_C;
|
||||
logic [15:0] corrected_N;
|
||||
logic [16:0] RANGE;
|
||||
logic [4:0] qbpp;
|
||||
logic [6:0] LIMIT;
|
||||
logic [5:0] NEAR;
|
||||
|
||||
// Quantized error output.
|
||||
logic err_valid;
|
||||
logic err_ready;
|
||||
logic signed [31:0] Errval;
|
||||
logic [PIX_WIDTH-1:0] reconstructed_sample;
|
||||
logic [12:0] err_x;
|
||||
logic [12:0] err_y;
|
||||
logic err_strip_first_pixel;
|
||||
logic err_strip_last_pixel;
|
||||
logic [8:0] err_context_index;
|
||||
logic err_context_negative;
|
||||
logic err_run_mode_context;
|
||||
logic [4:0] err_qbpp;
|
||||
logic [6:0] err_LIMIT;
|
||||
logic [31:0] err_A;
|
||||
logic signed [31:0] err_B;
|
||||
logic signed [8:0] err_C;
|
||||
logic [15:0] err_N;
|
||||
|
||||
// Scoreboard state.
|
||||
logic signed [31:0] expected_err_mem [0:EXPECTED_EVENT_COUNT-1];
|
||||
logic [PIX_WIDTH-1:0] expected_rx_mem [0:EXPECTED_EVENT_COUNT-1];
|
||||
int receive_index;
|
||||
logic all_done_seen;
|
||||
|
||||
jls_regular_error_quantizer #(
|
||||
.PIX_WIDTH(PIX_WIDTH)
|
||||
) dut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.corrected_valid(corrected_valid),
|
||||
.corrected_ready(corrected_ready),
|
||||
.corrected_sample(corrected_sample),
|
||||
.corrected_x(corrected_x),
|
||||
.corrected_y(corrected_y),
|
||||
.corrected_strip_first_pixel(corrected_strip_first_pixel),
|
||||
.corrected_strip_last_pixel(corrected_strip_last_pixel),
|
||||
.corrected_Px(corrected_Px),
|
||||
.corrected_context_index(corrected_context_index),
|
||||
.corrected_context_negative(corrected_context_negative),
|
||||
.corrected_run_mode_context(corrected_run_mode_context),
|
||||
.corrected_A(corrected_A),
|
||||
.corrected_B(corrected_B),
|
||||
.corrected_C(corrected_C),
|
||||
.corrected_N(corrected_N),
|
||||
.RANGE(RANGE),
|
||||
.qbpp(qbpp),
|
||||
.LIMIT(LIMIT),
|
||||
.NEAR(NEAR),
|
||||
.err_valid(err_valid),
|
||||
.err_ready(err_ready),
|
||||
.Errval(Errval),
|
||||
.reconstructed_sample(reconstructed_sample),
|
||||
.err_x(err_x),
|
||||
.err_y(err_y),
|
||||
.err_strip_first_pixel(err_strip_first_pixel),
|
||||
.err_strip_last_pixel(err_strip_last_pixel),
|
||||
.err_context_index(err_context_index),
|
||||
.err_context_negative(err_context_negative),
|
||||
.err_run_mode_context(err_run_mode_context),
|
||||
.err_qbpp(err_qbpp),
|
||||
.err_LIMIT(err_LIMIT),
|
||||
.err_A(err_A),
|
||||
.err_B(err_B),
|
||||
.err_C(err_C),
|
||||
.err_N(err_N)
|
||||
);
|
||||
|
||||
always begin
|
||||
#2 clk = ~clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
expected_err_mem[0] = 32'sd4;
|
||||
expected_rx_mem[0] = 8'd24;
|
||||
expected_err_mem[1] = 32'sd1;
|
||||
expected_rx_mem[1] = 8'd23;
|
||||
expected_err_mem[2] = -32'sd1;
|
||||
expected_rx_mem[2] = 8'd21;
|
||||
expected_err_mem[3] = -32'sd1;
|
||||
expected_rx_mem[3] = 8'd23;
|
||||
expected_err_mem[4] = 32'sd1;
|
||||
expected_rx_mem[4] = 8'd0;
|
||||
expected_err_mem[5] = -32'sd3;
|
||||
expected_rx_mem[5] = 8'd189;
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
corrected_valid = 1'b0;
|
||||
corrected_sample = 8'd0;
|
||||
corrected_x = 13'd0;
|
||||
corrected_y = 13'd0;
|
||||
corrected_strip_first_pixel = 1'b0;
|
||||
corrected_strip_last_pixel = 1'b0;
|
||||
corrected_Px = 8'd0;
|
||||
corrected_context_index = 9'd0;
|
||||
corrected_context_negative = 1'b0;
|
||||
corrected_run_mode_context = 1'b0;
|
||||
corrected_A = 32'd4;
|
||||
corrected_B = 32'sd0;
|
||||
corrected_C = 9'sd0;
|
||||
corrected_N = 16'd1;
|
||||
RANGE = 17'd256;
|
||||
qbpp = 5'd8;
|
||||
LIMIT = 7'd32;
|
||||
NEAR = 6'd0;
|
||||
err_ready = 1'b1;
|
||||
all_done_seen = 1'b0;
|
||||
|
||||
repeat (5) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
|
||||
@(negedge clk);
|
||||
corrected_valid = 1'b1;
|
||||
corrected_sample = 8'd24;
|
||||
corrected_Px = 8'd20;
|
||||
corrected_context_index = 9'd5;
|
||||
corrected_strip_first_pixel = 1'b1;
|
||||
RANGE = 17'd256;
|
||||
qbpp = 5'd8;
|
||||
LIMIT = 7'd32;
|
||||
NEAR = 6'd0;
|
||||
wait (corrected_ready);
|
||||
@(posedge clk);
|
||||
corrected_valid = 1'b0;
|
||||
corrected_strip_first_pixel = 1'b0;
|
||||
|
||||
@(negedge clk);
|
||||
corrected_valid = 1'b1;
|
||||
corrected_sample = 8'd24;
|
||||
corrected_Px = 8'd20;
|
||||
corrected_context_index = 9'd6;
|
||||
RANGE = 17'd86;
|
||||
qbpp = 5'd7;
|
||||
LIMIT = 7'd32;
|
||||
NEAR = 6'd1;
|
||||
wait (corrected_ready);
|
||||
@(posedge clk);
|
||||
corrected_valid = 1'b0;
|
||||
|
||||
@(negedge clk);
|
||||
corrected_valid = 1'b1;
|
||||
corrected_sample = 8'd20;
|
||||
corrected_Px = 8'd24;
|
||||
corrected_context_index = 9'd7;
|
||||
corrected_context_negative = 1'b0;
|
||||
RANGE = 17'd86;
|
||||
qbpp = 5'd7;
|
||||
LIMIT = 7'd32;
|
||||
NEAR = 6'd1;
|
||||
wait (corrected_ready);
|
||||
@(posedge clk);
|
||||
corrected_valid = 1'b0;
|
||||
|
||||
@(negedge clk);
|
||||
corrected_valid = 1'b1;
|
||||
corrected_sample = 8'd24;
|
||||
corrected_Px = 8'd20;
|
||||
corrected_context_index = 9'd8;
|
||||
corrected_context_negative = 1'b1;
|
||||
RANGE = 17'd86;
|
||||
qbpp = 5'd7;
|
||||
LIMIT = 7'd32;
|
||||
NEAR = 6'd1;
|
||||
wait (corrected_ready);
|
||||
@(posedge clk);
|
||||
corrected_valid = 1'b0;
|
||||
corrected_context_negative = 1'b0;
|
||||
|
||||
@(negedge clk);
|
||||
corrected_valid = 1'b1;
|
||||
corrected_sample = 8'd0;
|
||||
corrected_Px = 8'd255;
|
||||
corrected_context_index = 9'd9;
|
||||
corrected_strip_last_pixel = 1'b1;
|
||||
RANGE = 17'd256;
|
||||
qbpp = 5'd8;
|
||||
LIMIT = 7'd32;
|
||||
NEAR = 6'd0;
|
||||
wait (corrected_ready);
|
||||
@(posedge clk);
|
||||
corrected_valid = 1'b0;
|
||||
corrected_strip_last_pixel = 1'b0;
|
||||
|
||||
@(negedge clk);
|
||||
corrected_valid = 1'b1;
|
||||
corrected_sample = 8'd200;
|
||||
corrected_Px = 8'd0;
|
||||
corrected_context_index = 9'd10;
|
||||
RANGE = 17'd6;
|
||||
qbpp = 5'd3;
|
||||
LIMIT = 7'd32;
|
||||
NEAR = 6'd31;
|
||||
wait (corrected_ready);
|
||||
@(posedge clk);
|
||||
corrected_valid = 1'b0;
|
||||
|
||||
wait (receive_index == EXPECTED_EVENT_COUNT);
|
||||
all_done_seen = 1'b1;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
receive_index <= 0;
|
||||
end else if (err_valid && err_ready) begin
|
||||
if (receive_index >= EXPECTED_EVENT_COUNT) begin
|
||||
$fatal(1, "Unexpected extra regular-error event");
|
||||
end
|
||||
|
||||
if (Errval !== expected_err_mem[receive_index]) begin
|
||||
$fatal(1, "Errval mismatch at %0d: got %0d expected %0d",
|
||||
receive_index, Errval, expected_err_mem[receive_index]);
|
||||
end
|
||||
|
||||
if (reconstructed_sample !== expected_rx_mem[receive_index]) begin
|
||||
$fatal(1, "reconstructed sample mismatch at %0d: got %0d expected %0d",
|
||||
receive_index, reconstructed_sample, expected_rx_mem[receive_index]);
|
||||
end
|
||||
|
||||
if (err_context_index !== (9'd5 + receive_index[8:0])) begin
|
||||
$fatal(1, "err_context_index mismatch at %0d", receive_index);
|
||||
end
|
||||
|
||||
if (receive_index == 0 && err_strip_first_pixel !== 1'b1) begin
|
||||
$fatal(1, "first event should preserve strip_first_pixel");
|
||||
end
|
||||
|
||||
if (receive_index == 4 && err_strip_last_pixel !== 1'b1) begin
|
||||
$fatal(1, "last event should preserve strip_last_pixel");
|
||||
end
|
||||
|
||||
receive_index <= receive_index + 1;
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (5000) @(posedge clk);
|
||||
$fatal(1, "Timeout waiting for regular error quantizer smoke");
|
||||
end
|
||||
|
||||
initial begin
|
||||
wait (all_done_seen);
|
||||
repeat (4) @(posedge clk);
|
||||
$display("PASS: tb_jls_regular_error_quantizer");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
419
fpga/sim/tb_jls_run_mode.sv
Normal file
419
fpga/sim/tb_jls_run_mode.sv
Normal file
@@ -0,0 +1,419 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex A.7 run mode, Annex A.5 Golomb coding
|
||||
// Figure : N/A
|
||||
// Table : Annex A RUNindex/J table
|
||||
// Pseudocode : run-length bits, RItype, map, MErrval, and RI context update
|
||||
// Example : A zero-length run at RUNindex=0 emits one zero bit before
|
||||
// run-interruption MErrval.
|
||||
//
|
||||
// Smoke test for jls_run_mode. It checks:
|
||||
// 1. zero-length RItype=1 interruption,
|
||||
// 2. zero-length RItype=0 interruption with negative Errval mapping,
|
||||
// 3. end-of-line run chunks that advance RUNindex without interruption,
|
||||
// 4. NEAR=31 run-interruption reciprocal-division path.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_run_mode;
|
||||
|
||||
// Test precision and direct code-event width.
|
||||
localparam int PIX_WIDTH = 8;
|
||||
localparam int MAX_CODE_BITS = 64;
|
||||
|
||||
// Expected output counts.
|
||||
localparam int EXPECTED_CODE_EVENT_COUNT = 6;
|
||||
localparam int EXPECTED_MAPPED_EVENT_COUNT = 3;
|
||||
localparam int EXPECTED_RECON_EVENT_COUNT = 3;
|
||||
localparam int EXPECTED_DONE_COUNT = 4;
|
||||
|
||||
// Main simulation clock and reset.
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
// Strip coding parameters.
|
||||
logic strip_init_valid;
|
||||
logic strip_init_ready;
|
||||
logic [16:0] RANGE;
|
||||
logic [4:0] qbpp;
|
||||
logic [6:0] LIMIT;
|
||||
logic [5:0] NEAR;
|
||||
logic [15:0] RESET;
|
||||
|
||||
// Run-segment input.
|
||||
logic segment_valid;
|
||||
logic segment_ready;
|
||||
logic [12:0] run_length;
|
||||
logic run_end_of_line;
|
||||
logic interruption_valid;
|
||||
logic [PIX_WIDTH-1:0] interruption_sample;
|
||||
logic [12:0] interruption_x;
|
||||
logic [12:0] interruption_y;
|
||||
logic interruption_strip_first_pixel;
|
||||
logic interruption_strip_last_pixel;
|
||||
logic [PIX_WIDTH-1:0] Ra;
|
||||
logic [PIX_WIDTH-1:0] Rb;
|
||||
|
||||
// Direct run-code output.
|
||||
logic run_code_valid;
|
||||
logic run_code_ready;
|
||||
logic [MAX_CODE_BITS-1:0] run_code_bits;
|
||||
logic [6:0] run_code_bit_count;
|
||||
|
||||
// Mapped interruption output.
|
||||
logic mapped_valid;
|
||||
logic mapped_ready;
|
||||
logic [31:0] MErrval;
|
||||
logic [4:0] k;
|
||||
logic [6:0] limit;
|
||||
logic [4:0] mapped_qbpp;
|
||||
logic mapped_strip_last_pixel;
|
||||
|
||||
// Reconstructed interruption output.
|
||||
logic recon_valid;
|
||||
logic recon_ready;
|
||||
logic [PIX_WIDTH-1:0] reconstructed_sample;
|
||||
logic [12:0] recon_x;
|
||||
logic [12:0] recon_y;
|
||||
logic recon_strip_first_pixel;
|
||||
logic recon_strip_last_pixel;
|
||||
|
||||
// Segment completion.
|
||||
logic segment_done;
|
||||
logic segment_last_done;
|
||||
|
||||
// Scoreboard state.
|
||||
logic [MAX_CODE_BITS-1:0] expected_code_bits_mem [0:EXPECTED_CODE_EVENT_COUNT-1];
|
||||
logic [6:0] expected_code_count_mem [0:EXPECTED_CODE_EVENT_COUNT-1];
|
||||
logic [31:0] expected_MErrval_mem [0:EXPECTED_MAPPED_EVENT_COUNT-1];
|
||||
logic [4:0] expected_k_mem [0:EXPECTED_MAPPED_EVENT_COUNT-1];
|
||||
logic [6:0] expected_limit_mem [0:EXPECTED_MAPPED_EVENT_COUNT-1];
|
||||
logic [4:0] expected_qbpp_mem [0:EXPECTED_MAPPED_EVENT_COUNT-1];
|
||||
logic [PIX_WIDTH-1:0] expected_rx_mem [0:EXPECTED_RECON_EVENT_COUNT-1];
|
||||
int code_index;
|
||||
int mapped_index;
|
||||
int recon_index;
|
||||
int done_count;
|
||||
logic all_done_seen;
|
||||
|
||||
jls_run_mode #(
|
||||
.PIX_WIDTH(PIX_WIDTH),
|
||||
.MAX_CODE_BITS(MAX_CODE_BITS)
|
||||
) dut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.strip_init_valid(strip_init_valid),
|
||||
.strip_init_ready(strip_init_ready),
|
||||
.RANGE(RANGE),
|
||||
.qbpp(qbpp),
|
||||
.LIMIT(LIMIT),
|
||||
.NEAR(NEAR),
|
||||
.RESET(RESET),
|
||||
.segment_valid(segment_valid),
|
||||
.segment_ready(segment_ready),
|
||||
.run_length(run_length),
|
||||
.run_end_of_line(run_end_of_line),
|
||||
.interruption_valid(interruption_valid),
|
||||
.interruption_sample(interruption_sample),
|
||||
.interruption_x(interruption_x),
|
||||
.interruption_y(interruption_y),
|
||||
.interruption_strip_first_pixel(interruption_strip_first_pixel),
|
||||
.interruption_strip_last_pixel(interruption_strip_last_pixel),
|
||||
.Ra(Ra),
|
||||
.Rb(Rb),
|
||||
.run_code_valid(run_code_valid),
|
||||
.run_code_ready(run_code_ready),
|
||||
.run_code_bits(run_code_bits),
|
||||
.run_code_bit_count(run_code_bit_count),
|
||||
.mapped_valid(mapped_valid),
|
||||
.mapped_ready(mapped_ready),
|
||||
.MErrval(MErrval),
|
||||
.k(k),
|
||||
.limit(limit),
|
||||
.mapped_qbpp(mapped_qbpp),
|
||||
.mapped_strip_last_pixel(mapped_strip_last_pixel),
|
||||
.recon_valid(recon_valid),
|
||||
.recon_ready(recon_ready),
|
||||
.reconstructed_sample(reconstructed_sample),
|
||||
.recon_x(recon_x),
|
||||
.recon_y(recon_y),
|
||||
.recon_strip_first_pixel(recon_strip_first_pixel),
|
||||
.recon_strip_last_pixel(recon_strip_last_pixel),
|
||||
.segment_done(segment_done),
|
||||
.segment_last_done(segment_last_done)
|
||||
);
|
||||
|
||||
always begin
|
||||
#2 clk = ~clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
// Segment 0: run_length=0 at RUNindex=0 emits one zero bit.
|
||||
expected_code_bits_mem[0] = 64'h0000_0000_0000_0000;
|
||||
expected_code_count_mem[0] = 7'd1;
|
||||
|
||||
// Segment 1: another zero-length run, still RUNindex=0.
|
||||
expected_code_bits_mem[1] = 64'h0000_0000_0000_0000;
|
||||
expected_code_count_mem[1] = 7'd1;
|
||||
|
||||
// Segment 2: EOL run_length=3 starts at RUNindex=0 and emits three full
|
||||
// run chunks because J[0],J[1],J[2] are all zero.
|
||||
expected_code_bits_mem[2] = 64'h8000_0000_0000_0000;
|
||||
expected_code_count_mem[2] = 7'd1;
|
||||
expected_code_bits_mem[3] = 64'h8000_0000_0000_0000;
|
||||
expected_code_count_mem[3] = 7'd1;
|
||||
expected_code_bits_mem[4] = 64'h8000_0000_0000_0000;
|
||||
expected_code_count_mem[4] = 7'd1;
|
||||
|
||||
// Segment 3: NEAR=31 zero-length RItype=1 interruption.
|
||||
expected_code_bits_mem[5] = 64'h0000_0000_0000_0000;
|
||||
expected_code_count_mem[5] = 7'd1;
|
||||
|
||||
// RItype=1: A=4,N=1 => k=2, Errval=3, map=0, MErrval=5.
|
||||
expected_MErrval_mem[0] = 32'd5;
|
||||
expected_k_mem[0] = 5'd2;
|
||||
expected_limit_mem[0] = 7'd31;
|
||||
expected_qbpp_mem[0] = 5'd8;
|
||||
expected_rx_mem[0] = 8'd13;
|
||||
|
||||
// RItype=0: A=4,N=1 => k=2, Errval=-2, map=1, MErrval=3.
|
||||
expected_MErrval_mem[1] = 32'd3;
|
||||
expected_k_mem[1] = 5'd2;
|
||||
expected_limit_mem[1] = 7'd31;
|
||||
expected_qbpp_mem[1] = 5'd8;
|
||||
expected_rx_mem[1] = 8'd7;
|
||||
|
||||
// NEAR=31, RANGE=6: Errval=3 is modulo-mapped to -3, RItype=1 and
|
||||
// run_map=1 produce MErrval=4; reconstruction wraps to 189.
|
||||
expected_MErrval_mem[2] = 32'd4;
|
||||
expected_k_mem[2] = 5'd1;
|
||||
expected_limit_mem[2] = 7'd31;
|
||||
expected_qbpp_mem[2] = 5'd3;
|
||||
expected_rx_mem[2] = 8'd189;
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
strip_init_valid = 1'b0;
|
||||
RANGE = 17'd256;
|
||||
qbpp = 5'd8;
|
||||
LIMIT = 7'd32;
|
||||
NEAR = 6'd0;
|
||||
RESET = 16'd64;
|
||||
segment_valid = 1'b0;
|
||||
run_length = 13'd0;
|
||||
run_end_of_line = 1'b0;
|
||||
interruption_valid = 1'b0;
|
||||
interruption_sample = 8'd0;
|
||||
interruption_x = 13'd0;
|
||||
interruption_y = 13'd0;
|
||||
interruption_strip_first_pixel = 1'b0;
|
||||
interruption_strip_last_pixel = 1'b0;
|
||||
Ra = 8'd0;
|
||||
Rb = 8'd0;
|
||||
run_code_ready = 1'b1;
|
||||
mapped_ready = 1'b1;
|
||||
recon_ready = 1'b1;
|
||||
all_done_seen = 1'b0;
|
||||
|
||||
repeat (5) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
|
||||
@(negedge clk);
|
||||
strip_init_valid = 1'b1;
|
||||
wait (strip_init_ready);
|
||||
@(posedge clk);
|
||||
strip_init_valid = 1'b0;
|
||||
|
||||
// Zero-length RItype=1 interruption: |Ra-Rb| <= NEAR.
|
||||
@(negedge clk);
|
||||
segment_valid = 1'b1;
|
||||
run_length = 13'd0;
|
||||
run_end_of_line = 1'b0;
|
||||
interruption_valid = 1'b1;
|
||||
interruption_sample = 8'd13;
|
||||
interruption_x = 13'd4;
|
||||
interruption_y = 13'd1;
|
||||
interruption_strip_first_pixel = 1'b1;
|
||||
interruption_strip_last_pixel = 1'b0;
|
||||
Ra = 8'd10;
|
||||
Rb = 8'd10;
|
||||
wait (segment_ready);
|
||||
@(posedge clk);
|
||||
segment_valid = 1'b0;
|
||||
interruption_strip_first_pixel = 1'b0;
|
||||
wait (done_count == 1);
|
||||
|
||||
// Zero-length RItype=0 interruption: sign is positive because Rb > Ra.
|
||||
@(negedge clk);
|
||||
segment_valid = 1'b1;
|
||||
run_length = 13'd0;
|
||||
run_end_of_line = 1'b0;
|
||||
interruption_valid = 1'b1;
|
||||
interruption_sample = 8'd7;
|
||||
interruption_x = 13'd8;
|
||||
interruption_y = 13'd1;
|
||||
interruption_strip_last_pixel = 1'b0;
|
||||
Ra = 8'd5;
|
||||
Rb = 8'd9;
|
||||
wait (segment_ready);
|
||||
@(posedge clk);
|
||||
segment_valid = 1'b0;
|
||||
wait (done_count == 2);
|
||||
|
||||
// EOL run_length=3 with no interruption.
|
||||
@(negedge clk);
|
||||
segment_valid = 1'b1;
|
||||
run_length = 13'd3;
|
||||
run_end_of_line = 1'b1;
|
||||
interruption_valid = 1'b0;
|
||||
interruption_sample = 8'd0;
|
||||
interruption_x = 13'd15;
|
||||
interruption_y = 13'd1;
|
||||
interruption_strip_last_pixel = 1'b1;
|
||||
Ra = 8'd22;
|
||||
Rb = 8'd22;
|
||||
wait (segment_ready);
|
||||
@(posedge clk);
|
||||
segment_valid = 1'b0;
|
||||
interruption_strip_last_pixel = 1'b0;
|
||||
wait (done_count == 3);
|
||||
|
||||
@(negedge clk);
|
||||
RANGE = 17'd6;
|
||||
qbpp = 5'd3;
|
||||
LIMIT = 7'd32;
|
||||
NEAR = 6'd31;
|
||||
strip_init_valid = 1'b1;
|
||||
wait (strip_init_ready);
|
||||
@(posedge clk);
|
||||
strip_init_valid = 1'b0;
|
||||
|
||||
// NEAR=31 zero-length RItype=1 interruption exercises the reciprocal LUT.
|
||||
@(negedge clk);
|
||||
segment_valid = 1'b1;
|
||||
run_length = 13'd0;
|
||||
run_end_of_line = 1'b0;
|
||||
interruption_valid = 1'b1;
|
||||
interruption_sample = 8'd200;
|
||||
interruption_x = 13'd2;
|
||||
interruption_y = 13'd2;
|
||||
interruption_strip_last_pixel = 1'b1;
|
||||
Ra = 8'd0;
|
||||
Rb = 8'd0;
|
||||
wait (segment_ready);
|
||||
@(posedge clk);
|
||||
segment_valid = 1'b0;
|
||||
interruption_strip_last_pixel = 1'b0;
|
||||
wait (done_count == EXPECTED_DONE_COUNT);
|
||||
|
||||
all_done_seen = 1'b1;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
code_index <= 0;
|
||||
mapped_index <= 0;
|
||||
recon_index <= 0;
|
||||
done_count <= 0;
|
||||
end else begin
|
||||
if (run_code_valid && run_code_ready) begin
|
||||
if (code_index >= EXPECTED_CODE_EVENT_COUNT) begin
|
||||
$fatal(1, "Unexpected extra run code event");
|
||||
end
|
||||
|
||||
if (run_code_bit_count !== expected_code_count_mem[code_index]) begin
|
||||
$fatal(1, "run code count mismatch at %0d: got %0d expected %0d",
|
||||
code_index, run_code_bit_count, expected_code_count_mem[code_index]);
|
||||
end
|
||||
|
||||
if (run_code_bits !== expected_code_bits_mem[code_index]) begin
|
||||
$fatal(1, "run code bits mismatch at %0d: got 0x%016h expected 0x%016h",
|
||||
code_index, run_code_bits, expected_code_bits_mem[code_index]);
|
||||
end
|
||||
|
||||
code_index <= code_index + 1;
|
||||
end
|
||||
|
||||
if (mapped_valid && mapped_ready) begin
|
||||
if (mapped_index >= EXPECTED_MAPPED_EVENT_COUNT) begin
|
||||
$fatal(1, "Unexpected extra mapped run-interruption event");
|
||||
end
|
||||
|
||||
if (MErrval !== expected_MErrval_mem[mapped_index]) begin
|
||||
$fatal(1, "MErrval mismatch at %0d: got %0d expected %0d",
|
||||
mapped_index, MErrval, expected_MErrval_mem[mapped_index]);
|
||||
end
|
||||
|
||||
if (k !== expected_k_mem[mapped_index]) begin
|
||||
$fatal(1, "k mismatch at %0d: got %0d expected %0d",
|
||||
mapped_index, k, expected_k_mem[mapped_index]);
|
||||
end
|
||||
|
||||
if (limit !== expected_limit_mem[mapped_index]) begin
|
||||
$fatal(1, "limit mismatch at %0d: got %0d expected %0d",
|
||||
mapped_index, limit, expected_limit_mem[mapped_index]);
|
||||
end
|
||||
|
||||
if (mapped_qbpp !== expected_qbpp_mem[mapped_index]) begin
|
||||
$fatal(1, "mapped_qbpp mismatch at %0d", mapped_index);
|
||||
end
|
||||
|
||||
mapped_index <= mapped_index + 1;
|
||||
end
|
||||
|
||||
if (recon_valid && recon_ready) begin
|
||||
if (recon_index >= EXPECTED_RECON_EVENT_COUNT) begin
|
||||
$fatal(1, "Unexpected extra reconstructed run-interruption event");
|
||||
end
|
||||
|
||||
if (reconstructed_sample !== expected_rx_mem[recon_index]) begin
|
||||
$fatal(1, "reconstructed sample mismatch at %0d: got %0d expected %0d",
|
||||
recon_index, reconstructed_sample, expected_rx_mem[recon_index]);
|
||||
end
|
||||
|
||||
if (recon_index == 0 && recon_strip_first_pixel !== 1'b1) begin
|
||||
$fatal(1, "first run-interruption should preserve strip_first_pixel");
|
||||
end
|
||||
|
||||
recon_index <= recon_index + 1;
|
||||
end
|
||||
|
||||
if (segment_done) begin
|
||||
done_count <= done_count + 1;
|
||||
|
||||
if (done_count == 2 && segment_last_done !== 1'b1) begin
|
||||
$fatal(1, "third segment should report segment_last_done");
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (5000) @(posedge clk);
|
||||
$fatal(1, "Timeout waiting for run-mode smoke");
|
||||
end
|
||||
|
||||
initial begin
|
||||
wait (all_done_seen);
|
||||
repeat (4) @(posedge clk);
|
||||
|
||||
if (code_index !== EXPECTED_CODE_EVENT_COUNT) begin
|
||||
$fatal(1, "run code count mismatch: got %0d", code_index);
|
||||
end
|
||||
|
||||
if (mapped_index !== EXPECTED_MAPPED_EVENT_COUNT) begin
|
||||
$fatal(1, "mapped count mismatch: got %0d", mapped_index);
|
||||
end
|
||||
|
||||
if (recon_index !== EXPECTED_RECON_EVENT_COUNT) begin
|
||||
$fatal(1, "recon count mismatch: got %0d", recon_index);
|
||||
end
|
||||
|
||||
$display("PASS: tb_jls_run_mode");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
219
fpga/sim/tb_jls_scan_ctrl.sv
Normal file
219
fpga/sim/tb_jls_scan_ctrl.sv
Normal file
@@ -0,0 +1,219 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex A.8 control procedure, Annex D.1-D.3 scan control
|
||||
// Figure : N/A
|
||||
// Table : N/A
|
||||
// Pseudocode : Start one JPEG-LS scan per standalone strip frame
|
||||
// Example : Four-pixel smoke strip emits one start and one finish event.
|
||||
//
|
||||
// Smoke test for jls_scan_ctrl.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jls_scan_ctrl;
|
||||
|
||||
// Test precision and strip height.
|
||||
localparam int PIX_WIDTH = 8;
|
||||
localparam int SCAN_ROWS = 4;
|
||||
localparam int TEST_PIXEL_COUNT = 4;
|
||||
|
||||
// Main simulation clock and reset.
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
// Input pixel event.
|
||||
logic pixel_valid;
|
||||
logic pixel_ready;
|
||||
logic [PIX_WIDTH-1:0] pixel_sample;
|
||||
logic [12:0] pixel_x;
|
||||
logic [12:0] pixel_y;
|
||||
logic strip_first_pixel;
|
||||
logic strip_last_pixel;
|
||||
logic image_first_pixel;
|
||||
logic image_last_pixel;
|
||||
logic [12:0] active_pic_col;
|
||||
logic [3:0] active_ratio;
|
||||
logic [5:0] current_near;
|
||||
|
||||
// Forwarded encode pixel event.
|
||||
logic enc_pixel_valid;
|
||||
logic enc_pixel_ready;
|
||||
logic [PIX_WIDTH-1:0] enc_pixel_sample;
|
||||
logic [12:0] enc_pixel_x;
|
||||
logic [12:0] enc_pixel_y;
|
||||
logic enc_row_last_pixel;
|
||||
logic enc_strip_first_pixel;
|
||||
logic enc_strip_last_pixel;
|
||||
|
||||
// Strip control events.
|
||||
logic strip_start_valid;
|
||||
logic strip_start_ready;
|
||||
logic original_image_first_strip;
|
||||
logic [12:0] strip_width;
|
||||
logic [12:0] strip_height;
|
||||
logic [5:0] strip_near;
|
||||
logic strip_finish_valid;
|
||||
logic strip_finish_ready;
|
||||
logic original_image_last_strip;
|
||||
logic [31:0] strip_pixel_count;
|
||||
logic near_image_start_valid;
|
||||
logic [3:0] near_image_ratio;
|
||||
|
||||
// Scoreboard state.
|
||||
int send_index;
|
||||
int receive_index;
|
||||
int start_count;
|
||||
int finish_count;
|
||||
logic done_seen;
|
||||
|
||||
jls_scan_ctrl #(
|
||||
.PIX_WIDTH(PIX_WIDTH),
|
||||
.SCAN_ROWS(SCAN_ROWS)
|
||||
) dut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.pixel_valid(pixel_valid),
|
||||
.pixel_ready(pixel_ready),
|
||||
.pixel_sample(pixel_sample),
|
||||
.pixel_x(pixel_x),
|
||||
.pixel_y(pixel_y),
|
||||
.strip_first_pixel(strip_first_pixel),
|
||||
.strip_last_pixel(strip_last_pixel),
|
||||
.image_first_pixel(image_first_pixel),
|
||||
.image_last_pixel(image_last_pixel),
|
||||
.active_pic_col(active_pic_col),
|
||||
.active_ratio(active_ratio),
|
||||
.current_near(current_near),
|
||||
.enc_pixel_valid(enc_pixel_valid),
|
||||
.enc_pixel_ready(enc_pixel_ready),
|
||||
.enc_pixel_sample(enc_pixel_sample),
|
||||
.enc_pixel_x(enc_pixel_x),
|
||||
.enc_pixel_y(enc_pixel_y),
|
||||
.enc_row_last_pixel(enc_row_last_pixel),
|
||||
.enc_strip_first_pixel(enc_strip_first_pixel),
|
||||
.enc_strip_last_pixel(enc_strip_last_pixel),
|
||||
.strip_start_valid(strip_start_valid),
|
||||
.strip_start_ready(strip_start_ready),
|
||||
.original_image_first_strip(original_image_first_strip),
|
||||
.strip_width(strip_width),
|
||||
.strip_height(strip_height),
|
||||
.strip_near(strip_near),
|
||||
.strip_finish_valid(strip_finish_valid),
|
||||
.strip_finish_ready(strip_finish_ready),
|
||||
.original_image_last_strip(original_image_last_strip),
|
||||
.strip_pixel_count(strip_pixel_count),
|
||||
.near_image_start_valid(near_image_start_valid),
|
||||
.near_image_ratio(near_image_ratio)
|
||||
);
|
||||
|
||||
always begin
|
||||
#2 clk = ~clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
pixel_valid = 1'b0;
|
||||
pixel_sample = 8'h00;
|
||||
pixel_x = 13'd0;
|
||||
pixel_y = 13'd0;
|
||||
strip_first_pixel = 1'b0;
|
||||
strip_last_pixel = 1'b0;
|
||||
image_first_pixel = 1'b0;
|
||||
image_last_pixel = 1'b0;
|
||||
active_pic_col = 13'd4;
|
||||
active_ratio = 4'd2;
|
||||
current_near = 6'd7;
|
||||
enc_pixel_ready = 1'b1;
|
||||
strip_start_ready = 1'b1;
|
||||
strip_finish_ready = 1'b1;
|
||||
|
||||
repeat (5) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
|
||||
for (send_index = 0; send_index < TEST_PIXEL_COUNT; send_index = send_index + 1) begin
|
||||
@(posedge clk);
|
||||
pixel_valid = 1'b1;
|
||||
pixel_sample = send_index[7:0];
|
||||
pixel_x = send_index[12:0];
|
||||
pixel_y = 13'd0;
|
||||
strip_first_pixel = (send_index == 0);
|
||||
strip_last_pixel = (send_index == (TEST_PIXEL_COUNT - 1));
|
||||
image_first_pixel = (send_index == 0);
|
||||
image_last_pixel = (send_index == (TEST_PIXEL_COUNT - 1));
|
||||
end
|
||||
|
||||
@(posedge clk);
|
||||
pixel_valid = 1'b0;
|
||||
pixel_sample = 8'h00;
|
||||
pixel_x = 13'd0;
|
||||
strip_first_pixel = 1'b0;
|
||||
strip_last_pixel = 1'b0;
|
||||
image_first_pixel = 1'b0;
|
||||
image_last_pixel = 1'b0;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
receive_index <= 0;
|
||||
start_count <= 0;
|
||||
finish_count <= 0;
|
||||
done_seen <= 1'b0;
|
||||
end else begin
|
||||
if (pixel_valid && !pixel_ready) begin
|
||||
$fatal(1, "pixel_ready unexpectedly low in scan smoke");
|
||||
end
|
||||
|
||||
if (enc_pixel_valid && enc_pixel_ready) begin
|
||||
if (enc_pixel_sample !== receive_index[7:0]) begin
|
||||
$fatal(1, "enc_pixel_sample mismatch at %0d", receive_index);
|
||||
end
|
||||
if (enc_row_last_pixel !== (receive_index == (TEST_PIXEL_COUNT - 1))) begin
|
||||
$fatal(1, "enc_row_last_pixel mismatch at %0d", receive_index);
|
||||
end
|
||||
receive_index <= receive_index + 1;
|
||||
end
|
||||
|
||||
if (strip_start_valid && strip_start_ready) begin
|
||||
start_count <= start_count + 1;
|
||||
if (original_image_first_strip !== 1'b1) begin
|
||||
$fatal(1, "original_image_first_strip should be high in this smoke");
|
||||
end
|
||||
if (strip_width !== 13'd4 || strip_height !== 13'd4 || strip_near !== 6'd0) begin
|
||||
$fatal(1, "strip start fields mismatch");
|
||||
end
|
||||
end
|
||||
|
||||
if (near_image_start_valid) begin
|
||||
if (near_image_ratio !== 4'd2) begin
|
||||
$fatal(1, "near_image_ratio mismatch");
|
||||
end
|
||||
end
|
||||
|
||||
if (strip_finish_valid && strip_finish_ready) begin
|
||||
finish_count <= finish_count + 1;
|
||||
if (original_image_last_strip !== 1'b1 || strip_pixel_count !== 32'd4) begin
|
||||
$fatal(1, "strip finish fields mismatch");
|
||||
end
|
||||
done_seen <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (1000) @(posedge clk);
|
||||
$fatal(1, "Timeout waiting for scan controller smoke");
|
||||
end
|
||||
|
||||
initial begin
|
||||
wait (done_seen);
|
||||
repeat (4) @(posedge clk);
|
||||
if (receive_index !== TEST_PIXEL_COUNT || start_count !== 1 || finish_count !== 1) begin
|
||||
$fatal(1, "scan smoke count mismatch");
|
||||
end
|
||||
$display("PASS: tb_jls_scan_ctrl");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
96
fpga/sim/tb_jpeg_ls_encoder_top_idle.sv
Normal file
96
fpga/sim/tb_jpeg_ls_encoder_top_idle.sv
Normal file
@@ -0,0 +1,96 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex A.8 control procedure, Annex C.1-C.4 marker stream syntax
|
||||
// Figure : N/A
|
||||
// Table : N/A
|
||||
// Pseudocode : Top-level strip-frame encoder idle integration
|
||||
// Example : Empty input FIFO keeps the encoder idle and output quiet.
|
||||
//
|
||||
// Idle smoke test for jpeg_ls_encoder_top. This checks elaboration of the
|
||||
// integrated RTL without feeding pixels into the still-pending run-mode path.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jpeg_ls_encoder_top_idle;
|
||||
|
||||
// Use 8-bit configuration for the fastest top-level smoke.
|
||||
localparam int PIX_WIDTH = 8;
|
||||
localparam int IFIFO_DATA_WIDTH = ((PIX_WIDTH + 7) / 8) * 9;
|
||||
|
||||
// Main simulation clock and reset.
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
// Top-level configuration and FIFO ports.
|
||||
logic [12:0] cfg_pic_col;
|
||||
logic [12:0] cfg_pic_row;
|
||||
logic [3:0] ratio;
|
||||
logic ififo_rclk;
|
||||
logic ififo_rd;
|
||||
logic [IFIFO_DATA_WIDTH-1:0] ififo_rdata;
|
||||
logic ififo_empty;
|
||||
logic ififo_alempty;
|
||||
logic ofifo_wclk;
|
||||
logic ofifo_wr;
|
||||
logic [8:0] ofifo_wdata;
|
||||
logic ofifo_full;
|
||||
logic ofifo_alfull;
|
||||
|
||||
jpeg_ls_encoder_top #(
|
||||
.PIX_WIDTH(PIX_WIDTH),
|
||||
.DEFAULT_PIC_COL(16),
|
||||
.DEFAULT_PIC_ROW(16),
|
||||
.MAX_PIC_COL(64),
|
||||
.MAX_PIC_ROW(64),
|
||||
.SCAN_ROWS(16),
|
||||
.OUT_BUF_BYTES(256),
|
||||
.OUT_BUF_AFULL_MARGIN(16),
|
||||
.IFIFO_DATA_WIDTH(IFIFO_DATA_WIDTH)
|
||||
) dut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.cfg_pic_col(cfg_pic_col),
|
||||
.cfg_pic_row(cfg_pic_row),
|
||||
.ratio(ratio),
|
||||
.ififo_rclk(ififo_rclk),
|
||||
.ififo_rd(ififo_rd),
|
||||
.ififo_rdata(ififo_rdata),
|
||||
.ififo_empty(ififo_empty),
|
||||
.ififo_alempty(ififo_alempty),
|
||||
.ofifo_wclk(ofifo_wclk),
|
||||
.ofifo_wr(ofifo_wr),
|
||||
.ofifo_wdata(ofifo_wdata),
|
||||
.ofifo_full(ofifo_full),
|
||||
.ofifo_alfull(ofifo_alfull)
|
||||
);
|
||||
|
||||
always begin
|
||||
#2 clk = ~clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
cfg_pic_col = 13'd16;
|
||||
cfg_pic_row = 13'd16;
|
||||
ratio = 4'd0;
|
||||
ififo_rdata = {IFIFO_DATA_WIDTH{1'b0}};
|
||||
ififo_empty = 1'b1;
|
||||
ififo_alempty = 1'b1;
|
||||
ofifo_full = 1'b0;
|
||||
ofifo_alfull = 1'b0;
|
||||
|
||||
repeat (5) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
|
||||
repeat (100) @(posedge clk);
|
||||
if (ififo_rd !== 1'b0 || ofifo_wr !== 1'b0) begin
|
||||
$fatal(1, "top idle smoke expected no FIFO activity");
|
||||
end
|
||||
|
||||
$display("PASS: tb_jpeg_ls_encoder_top_idle");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
473
fpga/sim/tb_jpeg_ls_encoder_top_run_smoke.sv
Normal file
473
fpga/sim/tb_jpeg_ls_encoder_top_run_smoke.sv
Normal file
@@ -0,0 +1,473 @@
|
||||
// Standard : ITU-T T.87 (06/1998) / ISO/IEC 14495-1 JPEG-LS Baseline
|
||||
// Clause : Annex A.7 run mode, Annex C.1-C.4 marker stream syntax
|
||||
// Figure : N/A
|
||||
// Table : N/A
|
||||
// Pseudocode : Small all-zero image exercises run-mode strip closure
|
||||
// Example : 16x16 zero image should produce SOI ... EOI and one original
|
||||
// image-start sideband byte.
|
||||
//
|
||||
// Non-empty top-level smoke. This is a tiny compatibility-oriented smoke for
|
||||
// the integrated path. Use plusargs:
|
||||
// +PATTERN=0 for all-zero run-mode heavy image,
|
||||
// +PATTERN=1 for row-major ramp regular-mode heavy image,
|
||||
// +PATTERN=2 for checkerboard,
|
||||
// +PATTERN=3 for a vertical edge,
|
||||
// +PATTERN=4 for deterministic pseudo-noise/texture,
|
||||
// +PATTERN=9 to rotate ten representative patterns per image,
|
||||
// +IN_PGM=<path> to load a binary P5 PGM image instead of a synthetic pattern,
|
||||
// +RATIO=<0..15> to drive the top-level dynamic NEAR ratio port,
|
||||
// +OUT=<path> to choose the emitted .jls file,
|
||||
// +CASE=<name> to tag an optional CSV stats line,
|
||||
// +STATS=<path> to append a CSV stats line,
|
||||
// +CHECK_THROUGHPUT=1 to require at least 200 MPixel/s at 250 MHz.
|
||||
// The IMAGE_COUNT parameter repeats complete original images back-to-back for
|
||||
// a small continuous-input smoke. Each repeated image has its own SOF bit.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_jpeg_ls_encoder_top_run_smoke;
|
||||
|
||||
// Default to 8-bit for the fastest top-level smoke; scripts can override
|
||||
// PIX_WIDTH to cover the other required grayscale precisions.
|
||||
parameter int PIX_WIDTH = 8;
|
||||
parameter int PIC_COL = 16;
|
||||
parameter int PIC_ROW = 16;
|
||||
parameter int SCAN_ROWS = 16;
|
||||
parameter int IMAGE_COUNT = 1;
|
||||
parameter int TIMEOUT_PER_PIXEL = 512;
|
||||
localparam int IFIFO_DATA_WIDTH = ((PIX_WIDTH + 7) / 8) * 9;
|
||||
localparam int SOF_BIT_INDEX = (PIX_WIDTH == 8) ? 8 : 17;
|
||||
localparam int PIXELS_PER_IMAGE = PIC_COL * PIC_ROW;
|
||||
localparam int FIFO_WORD_COUNT = PIXELS_PER_IMAGE * IMAGE_COUNT;
|
||||
localparam int PGM_BYTES_PER_SAMPLE = (PIX_WIDTH <= 8) ? 1 : 2;
|
||||
localparam int PGM_BYTES_PER_IMAGE = PIXELS_PER_IMAGE * PGM_BYTES_PER_SAMPLE;
|
||||
localparam int EXPECTED_FRAME_COUNT = (PIC_ROW / SCAN_ROWS) * IMAGE_COUNT;
|
||||
localparam longint SIM_TIMEOUT_CYCLES =
|
||||
(longint'(FIFO_WORD_COUNT) * TIMEOUT_PER_PIXEL) + 200000;
|
||||
localparam int SAMPLE_MAX_VALUE = (1 << PIX_WIDTH) - 1;
|
||||
|
||||
// Main simulation clock and reset.
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
// Top-level configuration and FIFO ports.
|
||||
logic [12:0] cfg_pic_col;
|
||||
logic [12:0] cfg_pic_row;
|
||||
logic [3:0] ratio;
|
||||
logic ififo_rclk;
|
||||
logic ififo_rd;
|
||||
logic [IFIFO_DATA_WIDTH-1:0] ififo_rdata;
|
||||
logic ififo_empty;
|
||||
logic ififo_alempty;
|
||||
logic ofifo_wclk;
|
||||
logic ofifo_wr;
|
||||
logic [8:0] ofifo_wdata;
|
||||
logic ofifo_full;
|
||||
logic ofifo_alfull;
|
||||
|
||||
// FIFO model and output scoreboard.
|
||||
logic [IFIFO_DATA_WIDTH-1:0] fifo_mem [0:FIFO_WORD_COUNT-1];
|
||||
byte unsigned pgm_payload [0:PGM_BYTES_PER_IMAGE-1];
|
||||
int fifo_rd_index;
|
||||
int init_index;
|
||||
int output_byte_count;
|
||||
int image_start_sideband_count;
|
||||
int jls_fd;
|
||||
int pattern_id;
|
||||
int ratio_id;
|
||||
int check_throughput;
|
||||
int trace_entropy;
|
||||
int image_pixel_index;
|
||||
int image_index;
|
||||
int pixel_x;
|
||||
int pixel_y;
|
||||
int pattern_select;
|
||||
int sample_value;
|
||||
int pgm_fd;
|
||||
int pgm_header_width;
|
||||
int pgm_header_height;
|
||||
int pgm_header_max_value;
|
||||
int pgm_bytes_read;
|
||||
int pgm_byte_index;
|
||||
int cycle_count;
|
||||
int input_read_count;
|
||||
int input_first_read_cycle;
|
||||
int input_last_read_cycle;
|
||||
longint input_active_cycles;
|
||||
longint throughput_mpix_x1000;
|
||||
longint throughput_left_side;
|
||||
longint throughput_right_side;
|
||||
int stats_fd;
|
||||
string jls_output_path;
|
||||
string stats_output_path;
|
||||
string case_id;
|
||||
logic first_output_seen;
|
||||
logic last_output_was_ff;
|
||||
logic input_read_started;
|
||||
int eoi_count;
|
||||
string input_pgm_path;
|
||||
string pgm_header_line;
|
||||
|
||||
jpeg_ls_encoder_top #(
|
||||
.PIX_WIDTH(PIX_WIDTH),
|
||||
.DEFAULT_PIC_COL(PIC_COL),
|
||||
.DEFAULT_PIC_ROW(PIC_ROW),
|
||||
.MAX_PIC_COL(PIC_COL),
|
||||
.MAX_PIC_ROW(PIC_ROW),
|
||||
.SCAN_ROWS(SCAN_ROWS),
|
||||
.OUT_BUF_BYTES(512),
|
||||
.OUT_BUF_AFULL_MARGIN(32),
|
||||
.IFIFO_DATA_WIDTH(IFIFO_DATA_WIDTH)
|
||||
) dut (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.cfg_pic_col(cfg_pic_col),
|
||||
.cfg_pic_row(cfg_pic_row),
|
||||
.ratio(ratio),
|
||||
.ififo_rclk(ififo_rclk),
|
||||
.ififo_rd(ififo_rd),
|
||||
.ififo_rdata(ififo_rdata),
|
||||
.ififo_empty(ififo_empty),
|
||||
.ififo_alempty(ififo_alempty),
|
||||
.ofifo_wclk(ofifo_wclk),
|
||||
.ofifo_wr(ofifo_wr),
|
||||
.ofifo_wdata(ofifo_wdata),
|
||||
.ofifo_full(ofifo_full),
|
||||
.ofifo_alfull(ofifo_alfull)
|
||||
);
|
||||
|
||||
always begin
|
||||
#2 clk = ~clk;
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
ififo_empty = 1'b0;
|
||||
ififo_alempty = 1'b0;
|
||||
if (fifo_rd_index >= FIFO_WORD_COUNT) begin
|
||||
ififo_empty = 1'b1;
|
||||
ififo_alempty = 1'b1;
|
||||
end else if (fifo_rd_index >= (FIFO_WORD_COUNT - 1)) begin
|
||||
ififo_alempty = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
function automatic bit read_non_comment_line(input int fd, output string line);
|
||||
int line_status;
|
||||
|
||||
line = "";
|
||||
line_status = $fgets(line, fd);
|
||||
while (line_status != 0) begin
|
||||
if ((line.len() != 0) && (line.getc(0) == "#")) begin
|
||||
line_status = $fgets(line, fd);
|
||||
end else begin
|
||||
return 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
return 1'b0;
|
||||
endfunction
|
||||
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
rst = 1'b1;
|
||||
cfg_pic_col = PIC_COL[12:0];
|
||||
cfg_pic_row = PIC_ROW[12:0];
|
||||
ratio = 4'd0;
|
||||
ofifo_full = 1'b0;
|
||||
ofifo_alfull = 1'b0;
|
||||
pattern_id = 0;
|
||||
ratio_id = 0;
|
||||
check_throughput = 0;
|
||||
trace_entropy = 0;
|
||||
input_pgm_path = "";
|
||||
jls_output_path = "tools/jls_compat/out/rtl_top_zero_8b.jls";
|
||||
stats_output_path = "";
|
||||
case_id = "top_smoke";
|
||||
|
||||
void'($value$plusargs("PATTERN=%d", pattern_id));
|
||||
void'($value$plusargs("IN_PGM=%s", input_pgm_path));
|
||||
void'($value$plusargs("RATIO=%d", ratio_id));
|
||||
void'($value$plusargs("OUT=%s", jls_output_path));
|
||||
void'($value$plusargs("STATS=%s", stats_output_path));
|
||||
void'($value$plusargs("CASE=%s", case_id));
|
||||
void'($value$plusargs("CHECK_THROUGHPUT=%d", check_throughput));
|
||||
void'($value$plusargs("TRACE_ENTROPY=%d", trace_entropy));
|
||||
ratio = ratio_id[3:0];
|
||||
|
||||
if (input_pgm_path != "") begin
|
||||
pgm_fd = $fopen(input_pgm_path, "rb");
|
||||
if (pgm_fd == 0) begin
|
||||
$fatal(1, "Failed to open input PGM file: %s", input_pgm_path);
|
||||
end
|
||||
|
||||
if (!read_non_comment_line(pgm_fd, pgm_header_line)) begin
|
||||
$fatal(1, "Failed to read PGM magic header from %s", input_pgm_path);
|
||||
end
|
||||
if ((pgm_header_line.len() < 2) ||
|
||||
(pgm_header_line.getc(0) != "P") ||
|
||||
(pgm_header_line.getc(1) != "5")) begin
|
||||
$fatal(1, "Input file is not a binary P5 PGM: %s", input_pgm_path);
|
||||
end
|
||||
|
||||
if (!read_non_comment_line(pgm_fd, pgm_header_line) ||
|
||||
($sscanf(pgm_header_line, "%d %d", pgm_header_width, pgm_header_height) != 2)) begin
|
||||
$fatal(1, "Failed to parse PGM dimensions from %s", input_pgm_path);
|
||||
end
|
||||
if ((pgm_header_width != PIC_COL) || (pgm_header_height != PIC_ROW)) begin
|
||||
$fatal(1, "PGM dimensions %0dx%0d do not match testbench generics %0dx%0d for %s",
|
||||
pgm_header_width, pgm_header_height, PIC_COL, PIC_ROW, input_pgm_path);
|
||||
end
|
||||
|
||||
if (!read_non_comment_line(pgm_fd, pgm_header_line) ||
|
||||
($sscanf(pgm_header_line, "%d", pgm_header_max_value) != 1)) begin
|
||||
$fatal(1, "Failed to parse PGM max value from %s", input_pgm_path);
|
||||
end
|
||||
if (pgm_header_max_value != SAMPLE_MAX_VALUE) begin
|
||||
$fatal(1, "PGM max value %0d does not match PIX_WIDTH=%0d sample max %0d for %s",
|
||||
pgm_header_max_value, PIX_WIDTH, SAMPLE_MAX_VALUE, input_pgm_path);
|
||||
end
|
||||
|
||||
pgm_bytes_read = $fread(pgm_payload, pgm_fd);
|
||||
if (pgm_bytes_read != PGM_BYTES_PER_IMAGE) begin
|
||||
$fatal(1, "PGM payload bytes %0d do not match expected %0d for %s",
|
||||
pgm_bytes_read, PGM_BYTES_PER_IMAGE, input_pgm_path);
|
||||
end
|
||||
if ($fgetc(pgm_fd) != -1) begin
|
||||
$fatal(1, "Unexpected trailing data after PGM payload in %s", input_pgm_path);
|
||||
end
|
||||
$fclose(pgm_fd);
|
||||
end
|
||||
|
||||
for (init_index = 0; init_index < FIFO_WORD_COUNT; init_index = init_index + 1) begin
|
||||
fifo_mem[init_index] = {IFIFO_DATA_WIDTH{1'b0}};
|
||||
image_index = init_index / PIXELS_PER_IMAGE;
|
||||
image_pixel_index = init_index % PIXELS_PER_IMAGE;
|
||||
pixel_x = image_pixel_index % PIC_COL;
|
||||
pixel_y = image_pixel_index / PIC_COL;
|
||||
|
||||
sample_value = 0;
|
||||
if (input_pgm_path != "") begin
|
||||
pgm_byte_index = image_pixel_index * PGM_BYTES_PER_SAMPLE;
|
||||
if (PGM_BYTES_PER_SAMPLE == 1) begin
|
||||
sample_value = pgm_payload[pgm_byte_index];
|
||||
end else begin
|
||||
sample_value = {pgm_payload[pgm_byte_index], pgm_payload[pgm_byte_index + 1]};
|
||||
end
|
||||
end else begin
|
||||
pattern_select = pattern_id;
|
||||
if (pattern_id == 9) begin
|
||||
pattern_select = image_index % 10;
|
||||
end
|
||||
|
||||
case (pattern_select)
|
||||
1: begin
|
||||
sample_value = image_pixel_index & SAMPLE_MAX_VALUE;
|
||||
end
|
||||
2: begin
|
||||
if ((pixel_x[0] ^ pixel_y[0]) != 0) begin
|
||||
sample_value = SAMPLE_MAX_VALUE;
|
||||
end
|
||||
end
|
||||
3: begin
|
||||
if (pixel_x >= (PIC_COL / 2)) begin
|
||||
sample_value = SAMPLE_MAX_VALUE;
|
||||
end
|
||||
end
|
||||
4: begin
|
||||
sample_value = ((pixel_x * 13) + (pixel_y * 29) +
|
||||
((pixel_x ^ pixel_y) * 7) + (image_index * 17)) &
|
||||
SAMPLE_MAX_VALUE;
|
||||
end
|
||||
5: begin
|
||||
sample_value = SAMPLE_MAX_VALUE - (image_pixel_index & SAMPLE_MAX_VALUE);
|
||||
end
|
||||
6: begin
|
||||
if (pixel_x >= pixel_y) begin
|
||||
sample_value = SAMPLE_MAX_VALUE;
|
||||
end
|
||||
end
|
||||
7: begin
|
||||
sample_value = ((pixel_x + pixel_y + image_index) >> 3) & SAMPLE_MAX_VALUE;
|
||||
end
|
||||
8: begin
|
||||
if (pixel_x[3] != 0) begin
|
||||
sample_value = SAMPLE_MAX_VALUE;
|
||||
end
|
||||
end
|
||||
9: begin
|
||||
sample_value = ((image_pixel_index * 37) + (image_index * 101) +
|
||||
((image_pixel_index >> 3) * 11)) & SAMPLE_MAX_VALUE;
|
||||
end
|
||||
default: begin
|
||||
sample_value = 0;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
fifo_mem[init_index][PIX_WIDTH-1:0] = sample_value[PIX_WIDTH-1:0];
|
||||
|
||||
if (image_pixel_index == 0) begin
|
||||
fifo_mem[init_index][SOF_BIT_INDEX] = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
jls_fd = $fopen(jls_output_path, "wb");
|
||||
if (jls_fd == 0) begin
|
||||
$fatal(1, "Failed to open RTL top smoke output file");
|
||||
end
|
||||
|
||||
repeat (5) @(posedge clk);
|
||||
rst = 1'b0;
|
||||
|
||||
wait (eoi_count == EXPECTED_FRAME_COUNT);
|
||||
repeat (32) @(posedge clk);
|
||||
|
||||
if (image_start_sideband_count !== IMAGE_COUNT) begin
|
||||
$fatal(1, "Expected %0d original-image-start sideband bytes, got %0d",
|
||||
IMAGE_COUNT, image_start_sideband_count);
|
||||
end
|
||||
|
||||
if (output_byte_count < 32) begin
|
||||
$fatal(1, "Expected a nontrivial JPEG-LS strip frame, got only %0d bytes",
|
||||
output_byte_count);
|
||||
end
|
||||
|
||||
input_active_cycles = input_last_read_cycle - input_first_read_cycle + 1;
|
||||
if (input_active_cycles <= 0) begin
|
||||
$fatal(1, "Input throughput statistic is invalid: input_cycles=%0d",
|
||||
input_active_cycles);
|
||||
end
|
||||
|
||||
throughput_mpix_x1000 = (longint'(input_read_count) * 250000) / input_active_cycles;
|
||||
throughput_left_side = longint'(input_read_count) * 5;
|
||||
throughput_right_side = input_active_cycles * 4;
|
||||
|
||||
if ((check_throughput != 0) && (throughput_left_side < throughput_right_side)) begin
|
||||
$fatal(1, "Input throughput below 200 MPixel/s: reads=%0d cycles=%0d mpix_x1000=%0d",
|
||||
input_read_count, input_active_cycles, throughput_mpix_x1000);
|
||||
end
|
||||
|
||||
if (stats_output_path != "") begin
|
||||
stats_fd = $fopen(stats_output_path, "a");
|
||||
if (stats_fd == 0) begin
|
||||
$fatal(1, "Failed to open throughput stats output file");
|
||||
end
|
||||
$fdisplay(stats_fd, "%s,%0d,%0d,%0d,%0d,%0d,%0d,%0d,%0d,%0d,%0d,%0d",
|
||||
case_id, PIX_WIDTH, PIC_COL, PIC_ROW, IMAGE_COUNT, ratio_id,
|
||||
pattern_id, eoi_count, output_byte_count, input_read_count,
|
||||
input_active_cycles, throughput_mpix_x1000);
|
||||
$fclose(stats_fd);
|
||||
end
|
||||
|
||||
$display("PASS: tb_jpeg_ls_encoder_top_run_smoke pattern=%0d images=%0d frames=%0d bytes=%0d input_reads=%0d input_cycles=%0d throughput_mpix_x1000=%0d",
|
||||
pattern_id, IMAGE_COUNT, eoi_count, output_byte_count, input_read_count,
|
||||
input_active_cycles, throughput_mpix_x1000);
|
||||
$fclose(jls_fd);
|
||||
$finish;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
fifo_rd_index <= 0;
|
||||
ififo_rdata <= {IFIFO_DATA_WIDTH{1'b0}};
|
||||
cycle_count <= 0;
|
||||
input_read_count <= 0;
|
||||
input_first_read_cycle <= 0;
|
||||
input_last_read_cycle <= 0;
|
||||
input_read_started <= 1'b0;
|
||||
end else if (ififo_rd && !ififo_empty) begin
|
||||
cycle_count <= cycle_count + 1;
|
||||
ififo_rdata <= fifo_mem[fifo_rd_index];
|
||||
fifo_rd_index <= fifo_rd_index + 1;
|
||||
input_read_count <= input_read_count + 1;
|
||||
input_last_read_cycle <= cycle_count;
|
||||
if (!input_read_started) begin
|
||||
input_read_started <= 1'b1;
|
||||
input_first_read_cycle <= cycle_count;
|
||||
end
|
||||
end else begin
|
||||
cycle_count <= cycle_count + 1;
|
||||
end
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
output_byte_count <= 0;
|
||||
image_start_sideband_count <= 0;
|
||||
first_output_seen <= 1'b0;
|
||||
last_output_was_ff <= 1'b0;
|
||||
eoi_count <= 0;
|
||||
end else if (ofifo_wr) begin
|
||||
output_byte_count <= output_byte_count + 1;
|
||||
$fwrite(jls_fd, "%c", ofifo_wdata[7:0]);
|
||||
|
||||
if (ofifo_wdata[8]) begin
|
||||
image_start_sideband_count <= image_start_sideband_count + 1;
|
||||
end
|
||||
|
||||
if (!first_output_seen) begin
|
||||
first_output_seen <= 1'b1;
|
||||
if (ofifo_wdata !== 9'h1FF) begin
|
||||
$fatal(1, "First output byte should be SOI marker prefix with sideband, got 0x%03h",
|
||||
ofifo_wdata);
|
||||
end
|
||||
end
|
||||
|
||||
if (last_output_was_ff && ofifo_wdata[7:0] == 8'hD9) begin
|
||||
eoi_count <= eoi_count + 1;
|
||||
end
|
||||
|
||||
last_output_was_ff <= (ofifo_wdata[7:0] == 8'hFF);
|
||||
end
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (!rst && trace_entropy != 0) begin
|
||||
if (dut.mode_regular_valid && dut.mode_regular_ready) begin
|
||||
$display("TRACE regular x=%0d y=%0d X=%0d Ra=%0d Rb=%0d Rc=%0d Rd=%0d regpend=%0d",
|
||||
dut.mode_regular_x, dut.mode_regular_y, dut.mode_regular_sample,
|
||||
dut.mode_regular_Ra, dut.mode_regular_Rb, dut.mode_regular_Rc,
|
||||
dut.mode_regular_Rd, dut.regular_entropy_pending_count);
|
||||
end
|
||||
|
||||
if (dut.mode_run_segment_valid && dut.mode_run_segment_ready) begin
|
||||
$display("TRACE runseg len=%0d eol=%0d X=%0d x=%0d y=%0d Ra=%0d Rb=%0d regpend=%0d raw_ready=%0d",
|
||||
dut.mode_run_length, dut.mode_run_end_of_line,
|
||||
dut.mode_run_interruption_sample, dut.mode_run_interruption_x,
|
||||
dut.mode_run_interruption_y, dut.mode_run_Ra, dut.mode_run_Rb,
|
||||
dut.regular_entropy_pending_count, dut.run_core_segment_ready);
|
||||
end
|
||||
|
||||
if (dut.run_code_valid && dut.run_code_ready) begin
|
||||
$display("TRACE runcode count=%0d bits=0x%016h regpend=%0d",
|
||||
dut.run_code_bit_count, dut.run_code_bits,
|
||||
dut.regular_entropy_pending_count);
|
||||
end
|
||||
|
||||
if (dut.run_mapped_valid && dut.run_mapped_ready) begin
|
||||
$display("TRACE runmap M=%0d k=%0d limit=%0d qbpp=%0d runidx=%0d Jcur=%0d Jreg=%0d",
|
||||
dut.run_MErrval, dut.run_mapped_k, dut.run_mapped_limit,
|
||||
dut.run_mapped_qbpp, dut.run_mode_i.RUNindex,
|
||||
dut.run_mode_i.J_value, dut.run_mode_i.run_remainder_j_reg);
|
||||
end
|
||||
|
||||
if (dut.regular_mapped_valid && dut.regular_mapped_ready) begin
|
||||
$display("TRACE regmap M=%0d k=%0d limit=%0d qbpp=%0d",
|
||||
dut.regular_MErrval, dut.regular_mapped_k,
|
||||
dut.regular_mapped_limit, dut.regular_mapped_qbpp);
|
||||
end
|
||||
|
||||
if (dut.code_valid && dut.code_ready) begin
|
||||
$display("TRACE code count=%0d bits=0x%016h", dut.code_bit_count, dut.code_bits);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (SIM_TIMEOUT_CYCLES) @(posedge clk);
|
||||
$fatal(1, "Timeout waiting for top-level run smoke EOI");
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`default_nettype wire
|
||||
Reference in New Issue
Block a user