fix: full-repo ruff lint cleanup and CI migration to uv
Resolve all 374 ruff errors across 36 Python files (E501, E702, E722, E741, F821, F841, invalid-syntax) bringing `ruff check .` to zero errors repo-wide with line-length=100. Rewrite CI workflow to use uv for dependency management, whole-repo `ruff check .`, py_compile syntax gate, and merged python-tests job. Add pyproject.toml with ruff config and uv dependency groups. CI structure proposed by hcm444.
This commit is contained in:
@@ -358,7 +358,10 @@ def compare_scenario(scenario_name):
|
||||
|
||||
# ---- First/last sample comparison ----
|
||||
print("\nFirst 10 samples (after alignment):")
|
||||
print(f" {'idx':>4s} {'RTL_I':>8s} {'Py_I':>8s} {'Err_I':>6s} {'RTL_Q':>8s} {'Py_Q':>8s} {'Err_Q':>6s}")
|
||||
print(
|
||||
f" {'idx':>4s} {'RTL_I':>8s} {'Py_I':>8s} {'Err_I':>6s} "
|
||||
f"{'RTL_Q':>8s} {'Py_Q':>8s} {'Err_Q':>6s}"
|
||||
)
|
||||
for k in range(min(10, aligned_len)):
|
||||
ei = aligned_rtl_i[k] - aligned_py_i[k]
|
||||
eq = aligned_rtl_q[k] - aligned_py_q[k]
|
||||
|
||||
@@ -199,14 +199,17 @@ class NCO:
|
||||
# Wait - let me re-derive. The Verilog is:
|
||||
# phase_accumulator <= phase_accumulator + frequency_tuning_word;
|
||||
# phase_accum_reg <= phase_accumulator; // OLD value (NBA)
|
||||
# phase_with_offset <= phase_accum_reg + {phase_offset, 16'b0}; // OLD phase_accum_reg
|
||||
# phase_with_offset <= phase_accum_reg + {phase_offset, 16'b0};
|
||||
# // OLD phase_accum_reg
|
||||
# Since all are NBA (<=), they all read the values from BEFORE this edge.
|
||||
# So: new_phase_accumulator = old_phase_accumulator + ftw
|
||||
# new_phase_accum_reg = old_phase_accumulator
|
||||
# new_phase_with_offset = old_phase_accum_reg + offset
|
||||
old_phase_accumulator = (self.phase_accumulator - ftw) & 0xFFFFFFFF # reconstruct
|
||||
self.phase_accum_reg = old_phase_accumulator
|
||||
self.phase_with_offset = (old_phase_accum_reg + ((phase_offset << 16) & 0xFFFFFFFF)) & 0xFFFFFFFF
|
||||
self.phase_with_offset = (
|
||||
old_phase_accum_reg + ((phase_offset << 16) & 0xFFFFFFFF)
|
||||
) & 0xFFFFFFFF
|
||||
# phase_accumulator was already updated above
|
||||
|
||||
# ---- Stage 3a: Register LUT address + quadrant ----
|
||||
@@ -607,8 +610,14 @@ class FIRFilter:
|
||||
if (old_valid_pipe >> 0) & 1:
|
||||
for i in range(16):
|
||||
# Sign-extend products to ACCUM_WIDTH
|
||||
a = sign_extend(mult_results[2*i] & ((1 << self.PRODUCT_WIDTH) - 1), self.PRODUCT_WIDTH)
|
||||
b = sign_extend(mult_results[2*i+1] & ((1 << self.PRODUCT_WIDTH) - 1), self.PRODUCT_WIDTH)
|
||||
a = sign_extend(
|
||||
mult_results[2 * i] & ((1 << self.PRODUCT_WIDTH) - 1),
|
||||
self.PRODUCT_WIDTH,
|
||||
)
|
||||
b = sign_extend(
|
||||
mult_results[2 * i + 1] & ((1 << self.PRODUCT_WIDTH) - 1),
|
||||
self.PRODUCT_WIDTH,
|
||||
)
|
||||
self.add_l0[i] = a + b
|
||||
|
||||
# ---- Stage 2 (Level 1): 8 pairwise sums ----
|
||||
@@ -1365,7 +1374,10 @@ def _self_test():
|
||||
mag_sq = s * s + c * c
|
||||
expected = 32767 * 32767
|
||||
error_pct = abs(mag_sq - expected) / expected * 100
|
||||
print(f" Quadrature check: sin^2+cos^2={mag_sq}, expected~{expected}, error={error_pct:.2f}%")
|
||||
print(
|
||||
f" Quadrature check: sin^2+cos^2={mag_sq}, "
|
||||
f"expected~{expected}, error={error_pct:.2f}%"
|
||||
)
|
||||
print(" NCO: OK")
|
||||
|
||||
# --- Mixer test ---
|
||||
|
||||
@@ -218,7 +218,8 @@ def generate_long_chirp_test():
|
||||
if seg == 0:
|
||||
buffer_write_ptr = 0
|
||||
else:
|
||||
# Overlap-save: copy buffer[SEGMENT_ADVANCE:SEGMENT_ADVANCE+OVERLAP] -> buffer[0:OVERLAP]
|
||||
# Overlap-save: copy
|
||||
# buffer[SEGMENT_ADVANCE:SEGMENT_ADVANCE+OVERLAP] -> buffer[0:OVERLAP]
|
||||
for i in range(OVERLAP_SAMPLES):
|
||||
input_buffer_i[i] = input_buffer_i[i + SEGMENT_ADVANCE]
|
||||
input_buffer_q[i] = input_buffer_q[i + SEGMENT_ADVANCE]
|
||||
|
||||
@@ -335,7 +335,9 @@ def run_ddc(adc_samples):
|
||||
for n in range(n_samples):
|
||||
integrators[0][n + 1] = (integrators[0][n] + mixed_i[n]) & ((1 << CIC_ACC_WIDTH) - 1)
|
||||
for s in range(1, CIC_STAGES):
|
||||
integrators[s][n + 1] = (integrators[s][n] + integrators[s - 1][n + 1]) & ((1 << CIC_ACC_WIDTH) - 1)
|
||||
integrators[s][n + 1] = (
|
||||
integrators[s][n] + integrators[s - 1][n + 1]
|
||||
) & ((1 << CIC_ACC_WIDTH) - 1)
|
||||
|
||||
# Downsample by 4
|
||||
n_decimated = n_samples // CIC_DECIMATION
|
||||
@@ -580,8 +582,11 @@ def run_range_bin_decimator(range_fft_i, range_fft_q,
|
||||
decimated_i = np.zeros((n_chirps, output_bins), dtype=np.int64)
|
||||
decimated_q = np.zeros((n_chirps, output_bins), dtype=np.int64)
|
||||
|
||||
print(f"[DECIM] Decimating {n_in}→{output_bins} bins, mode={'peak' if mode==1 else 'avg' if mode==2 else 'simple'}, "
|
||||
f"start_bin={start_bin}, {n_chirps} chirps")
|
||||
mode_str = 'peak' if mode == 1 else 'avg' if mode == 2 else 'simple'
|
||||
print(
|
||||
f"[DECIM] Decimating {n_in}→{output_bins} bins, mode={mode_str}, "
|
||||
f"start_bin={start_bin}, {n_chirps} chirps"
|
||||
)
|
||||
|
||||
for c in range(n_chirps):
|
||||
# Index into input, skip start_bin
|
||||
@@ -678,7 +683,9 @@ def run_doppler_fft(range_data_i, range_data_q, twiddle_file_16=None):
|
||||
if twiddle_file_16 and os.path.exists(twiddle_file_16):
|
||||
cos_rom_16 = load_twiddle_rom(twiddle_file_16)
|
||||
else:
|
||||
cos_rom_16 = np.round(32767 * np.cos(2 * np.pi * np.arange(n_fft // 4) / n_fft)).astype(np.int64)
|
||||
cos_rom_16 = np.round(
|
||||
32767 * np.cos(2 * np.pi * np.arange(n_fft // 4) / n_fft)
|
||||
).astype(np.int64)
|
||||
|
||||
LOG2N_16 = 4
|
||||
doppler_map_i = np.zeros((n_range, n_total), dtype=np.int64)
|
||||
@@ -835,7 +842,10 @@ def run_dc_notch(doppler_i, doppler_q, width=2):
|
||||
notched_i = doppler_i.copy()
|
||||
notched_q = doppler_q.copy()
|
||||
|
||||
print(f"[DC NOTCH] width={width}, {n_range} range bins x {n_doppler} Doppler bins (dual sub-frame)")
|
||||
print(
|
||||
f"[DC NOTCH] width={width}, {n_range} range bins x "
|
||||
f"{n_doppler} Doppler bins (dual sub-frame)"
|
||||
)
|
||||
|
||||
if width == 0:
|
||||
print(" Pass-through (width=0)")
|
||||
@@ -1167,7 +1177,12 @@ def main():
|
||||
parser = argparse.ArgumentParser(description="AERIS-10 FPGA golden reference model")
|
||||
parser.add_argument('--frame', type=int, default=0, help='Frame index to process')
|
||||
parser.add_argument('--plot', action='store_true', help='Show plots')
|
||||
parser.add_argument('--threshold', type=int, default=10000, help='Detection threshold (L1 magnitude)')
|
||||
parser.add_argument(
|
||||
'--threshold',
|
||||
type=int,
|
||||
default=10000,
|
||||
help='Detection threshold (L1 magnitude)'
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Paths
|
||||
@@ -1175,7 +1190,11 @@ def main():
|
||||
fpga_dir = os.path.abspath(os.path.join(script_dir, '..', '..', '..'))
|
||||
data_base = os.path.expanduser("~/Downloads/adi_radar_data")
|
||||
amp_data = os.path.join(data_base, "amp_radar", "phaser_amp_4MSPS_500M_300u_256_m3dB.npy")
|
||||
amp_config = os.path.join(data_base, "amp_radar", "phaser_amp_4MSPS_500M_300u_256_m3dB_config.npy")
|
||||
amp_config = os.path.join(
|
||||
data_base,
|
||||
"amp_radar",
|
||||
"phaser_amp_4MSPS_500M_300u_256_m3dB_config.npy"
|
||||
)
|
||||
twiddle_1024 = os.path.join(fpga_dir, "fft_twiddle_1024.mem")
|
||||
output_dir = os.path.join(script_dir, "hex")
|
||||
|
||||
@@ -1290,7 +1309,10 @@ def main():
|
||||
q_val = int(fc_doppler_q[rbin, dbin]) & 0xFFFF
|
||||
packed = (q_val << 16) | i_val
|
||||
f.write(f"{packed:08X}\n")
|
||||
print(f" Wrote {fc_doppler_packed_file} ({DOPPLER_RANGE_BINS * DOPPLER_TOTAL_BINS} packed IQ words)")
|
||||
print(
|
||||
f" Wrote {fc_doppler_packed_file} ("
|
||||
f"{DOPPLER_RANGE_BINS * DOPPLER_TOTAL_BINS} packed IQ words)"
|
||||
)
|
||||
|
||||
# Save numpy arrays for the full-chain path
|
||||
np.save(os.path.join(output_dir, "decimated_range_i.npy"), decim_i)
|
||||
@@ -1336,7 +1358,10 @@ def main():
|
||||
q_val = int(notched_q[rbin, dbin]) & 0xFFFF
|
||||
packed = (q_val << 16) | i_val
|
||||
f.write(f"{packed:08X}\n")
|
||||
print(f" Wrote {fc_notched_packed_file} ({DOPPLER_RANGE_BINS * DOPPLER_TOTAL_BINS} packed IQ words)")
|
||||
print(
|
||||
f" Wrote {fc_notched_packed_file} ("
|
||||
f"{DOPPLER_RANGE_BINS * DOPPLER_TOTAL_BINS} packed IQ words)"
|
||||
)
|
||||
|
||||
# CFAR on DC-notched data
|
||||
CFAR_GUARD = 2
|
||||
@@ -1385,7 +1410,10 @@ def main():
|
||||
with open(cfar_det_list_file, 'w') as f:
|
||||
f.write("# AERIS-10 Full-Chain CFAR Detection List\n")
|
||||
f.write(f"# Chain: decim -> MTI -> Doppler -> DC notch(w={DC_NOTCH_WIDTH}) -> CA-CFAR\n")
|
||||
f.write(f"# CFAR: guard={CFAR_GUARD}, train={CFAR_TRAIN}, alpha=0x{CFAR_ALPHA:02X}, mode={CFAR_MODE}\n")
|
||||
f.write(
|
||||
f"# CFAR: guard={CFAR_GUARD}, train={CFAR_TRAIN}, "
|
||||
f"alpha=0x{CFAR_ALPHA:02X}, mode={CFAR_MODE}\n"
|
||||
)
|
||||
f.write("# Format: range_bin doppler_bin magnitude threshold\n")
|
||||
for det in cfar_detections:
|
||||
r, d = det
|
||||
@@ -1481,12 +1509,18 @@ def main():
|
||||
print(f" Chirps processed: {DOPPLER_CHIRPS}")
|
||||
print(f" Samples/chirp: {FFT_SIZE}")
|
||||
print(f" Range FFT: {FFT_SIZE}-point → {snr_range:.1f} dB vs float")
|
||||
print(f" Doppler FFT (direct): {DOPPLER_FFT_SIZE}-point Hamming → {snr_doppler:.1f} dB vs float")
|
||||
print(
|
||||
f" Doppler FFT (direct): {DOPPLER_FFT_SIZE}-point Hamming "
|
||||
f"→ {snr_doppler:.1f} dB vs float"
|
||||
)
|
||||
print(f" Detections (direct): {len(detections)} (threshold={args.threshold})")
|
||||
print(" Full-chain decimator: 1024→64 peak detection")
|
||||
print(f" Full-chain detections: {len(fc_detections)} (threshold={args.threshold})")
|
||||
print(f" MTI+CFAR chain: decim → MTI → Doppler → DC notch(w={DC_NOTCH_WIDTH}) → CA-CFAR")
|
||||
print(f" CFAR detections: {len(cfar_detections)} (guard={CFAR_GUARD}, train={CFAR_TRAIN}, alpha=0x{CFAR_ALPHA:02X})")
|
||||
print(
|
||||
f" CFAR detections: {len(cfar_detections)} "
|
||||
f"(guard={CFAR_GUARD}, train={CFAR_TRAIN}, alpha=0x{CFAR_ALPHA:02X})"
|
||||
)
|
||||
print(f" Hex stimulus files: {output_dir}/")
|
||||
print(" Ready for RTL co-simulation with Icarus Verilog")
|
||||
|
||||
|
||||
@@ -199,7 +199,10 @@ def test_long_chirp():
|
||||
avg_mag = sum(magnitudes) / len(magnitudes)
|
||||
|
||||
print(f" Magnitude: min={min_mag:.1f}, max={max_mag:.1f}, avg={avg_mag:.1f}")
|
||||
print(f" Max magnitude as fraction of Q15 range: {max_mag/32767:.4f} ({max_mag/32767*100:.2f}%)")
|
||||
print(
|
||||
f" Max magnitude as fraction of Q15 range: "
|
||||
f"{max_mag/32767:.4f} ({max_mag/32767*100:.2f}%)"
|
||||
)
|
||||
|
||||
# Check if this looks like it came from generate_reference_chirp_q15
|
||||
# That function uses 32767 * 0.9 scaling => max magnitude ~29490
|
||||
@@ -262,7 +265,10 @@ def test_long_chirp():
|
||||
# Check if bandwidth roughly matches expected
|
||||
bw_match = abs(f_range - CHIRP_BW) / CHIRP_BW < 0.5 # within 50%
|
||||
if bw_match:
|
||||
print(f" Bandwidth {f_range/1e6:.2f} MHz roughly matches expected {CHIRP_BW/1e6:.2f} MHz")
|
||||
print(
|
||||
f" Bandwidth {f_range/1e6:.2f} MHz roughly matches expected "
|
||||
f"{CHIRP_BW/1e6:.2f} MHz"
|
||||
)
|
||||
else:
|
||||
warn(f"Bandwidth {f_range/1e6:.2f} MHz does NOT match expected {CHIRP_BW/1e6:.2f} MHz")
|
||||
|
||||
@@ -415,8 +421,11 @@ def test_chirp_vs_model():
|
||||
print(f" Max phase diff: {max_phase_diff:.4f} rad ({math.degrees(max_phase_diff):.2f} deg)")
|
||||
|
||||
phase_match = max_phase_diff < 0.5 # within 0.5 rad
|
||||
check(phase_match,
|
||||
f"Phase shape match: max diff = {math.degrees(max_phase_diff):.1f} deg (tolerance: 28.6 deg)")
|
||||
check(
|
||||
phase_match,
|
||||
f"Phase shape match: max diff = {math.degrees(max_phase_diff):.1f} deg "
|
||||
f"(tolerance: 28.6 deg)",
|
||||
)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
@@ -521,8 +530,11 @@ def test_memory_addressing():
|
||||
addr_from_concat = (seg << 10) | 0 # {seg[1:0], 10'b0}
|
||||
addr_end = (seg << 10) | 1023
|
||||
|
||||
check(addr_from_concat == base,
|
||||
f"Seg {seg} base address: {{{seg}[1:0], 10'b0}} = {addr_from_concat} (expected {base})")
|
||||
check(
|
||||
addr_from_concat == base,
|
||||
f"Seg {seg} base address: {{{seg}[1:0], 10'b0}} = {addr_from_concat} "
|
||||
f"(expected {base})",
|
||||
)
|
||||
check(addr_end == end,
|
||||
f"Seg {seg} end address: {{{seg}[1:0], 10'h3FF}} = {addr_end} (expected {end})")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user