mirror of
https://github.com/NawfalMotii79/PLFM_RADAR.git
synced 2026-06-10 23:41:18 +00:00
refactor(fpga): gen_chirp_mem sources sizing from radar_params.vh
A-3: gen_chirp_mem.py hardcoded 30 us / 0.5 us / 2048-pt / 2 segments, duplicating the same numbers defined as RP_LONG_CHIRP_SAMPLES_3KM, RP_SHORT_CHIRP_SAMPLES, RP_FFT_SIZE, RP_LONG_SEGMENTS_3KM in radar_params.vh. A change on one side would silently desync the .mem files from the RTL sample counts. The script now parses radar_params.vh for integer RP_* macros and derives chirp durations from LONG_CHIRP_SAMPLES / FS_SYS. Physical baseband constants (CHIRP_BW, FS_SYS, SCALE) stay hardcoded since they are chirp-design properties, not FPGA sizing. Regenerated .mem files are byte-identical to pre-change output. 32/32 regression PASS.
This commit is contained in:
@@ -35,22 +35,96 @@ Usage:
|
|||||||
|
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# AERIS-10 Parameters (matching radar_scene.py)
|
# AERIS-10 Parameters
|
||||||
|
#
|
||||||
|
# Sample counts / FFT size / segmentation come from radar_params.vh so a
|
||||||
|
# change there (e.g. a new long-chirp duration) flows into the .mem files
|
||||||
|
# automatically — no risk of Python and RTL disagreeing on buffer sizes.
|
||||||
|
#
|
||||||
|
# Physical chirp design constants (bandwidth, sample rate, Q15 scaling)
|
||||||
|
# stay hardcoded here: they live outside radar_params.vh because they are
|
||||||
|
# baseband-generation properties, not FPGA sizing parameters.
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
|
RADAR_PARAMS_VH = os.path.join(
|
||||||
|
os.path.dirname(os.path.abspath(__file__)), '..', '..', 'radar_params.vh'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_radar_params(path):
|
||||||
|
"""
|
||||||
|
Parse `\\`define RP_<NAME> <integer>` lines from radar_params.vh.
|
||||||
|
Only integer literals are supported (decimal / hex / binary); the
|
||||||
|
macros this script consumes are all integers. Comments / strings /
|
||||||
|
concat macros are ignored by the regex.
|
||||||
|
"""
|
||||||
|
# Matches: `define RP_NAME <int-literal> [// comment]
|
||||||
|
pat = re.compile(
|
||||||
|
r"^\s*`define\s+(RP_\w+)\s+"
|
||||||
|
r"(?:(\d+)'[bdh][0-9a-fA-F_]+|0[xX][0-9a-fA-F]+|\d+)"
|
||||||
|
)
|
||||||
|
# Simpler: grab the whole RHS up to comment, then eval as int.
|
||||||
|
line_pat = re.compile(r"^\s*`define\s+(RP_\w+)\s+([^/\n]+?)(?://.*)?$")
|
||||||
|
params = {}
|
||||||
|
with open(path) as f:
|
||||||
|
for line in f:
|
||||||
|
m = line_pat.match(line)
|
||||||
|
if not m:
|
||||||
|
continue
|
||||||
|
name, rhs = m.group(1), m.group(2).strip()
|
||||||
|
# Strip Verilog sized-literal prefix like 11'd2048 or 2'b00.
|
||||||
|
sized = re.match(r"\d+'([bdh])([0-9a-fA-F_]+)", rhs)
|
||||||
|
if sized:
|
||||||
|
base = {'b': 2, 'd': 10, 'h': 16}[sized.group(1)]
|
||||||
|
try:
|
||||||
|
params[name] = int(sized.group(2).replace('_', ''), base)
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
continue
|
||||||
|
# Plain integer (decimal or 0x...).
|
||||||
|
try:
|
||||||
|
params[name] = int(rhs, 0)
|
||||||
|
except ValueError:
|
||||||
|
# Non-integer macro (string, expression, cross-reference) —
|
||||||
|
# skip; this script only needs the integer sizing macros.
|
||||||
|
continue
|
||||||
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
_RP = _parse_radar_params(RADAR_PARAMS_VH)
|
||||||
|
|
||||||
|
|
||||||
|
def _require(name):
|
||||||
|
if name not in _RP:
|
||||||
|
sys.stderr.write(
|
||||||
|
f"gen_chirp_mem.py: `{name}` not found in radar_params.vh; "
|
||||||
|
f"update the RTL macro or the parser.\n"
|
||||||
|
)
|
||||||
|
sys.exit(2)
|
||||||
|
return _RP[name]
|
||||||
|
|
||||||
|
|
||||||
|
# Physical chirp design constants (not in radar_params.vh — baseband only).
|
||||||
CHIRP_BW = 20e6 # 20 MHz sweep bandwidth
|
CHIRP_BW = 20e6 # 20 MHz sweep bandwidth
|
||||||
FS_SYS = 100e6 # System clock (100 MHz, post-CIC)
|
FS_SYS = 100e6 # System clock (100 MHz, post-CIC)
|
||||||
T_LONG_CHIRP = 30e-6 # 30 us long chirp duration
|
|
||||||
T_SHORT_CHIRP = 0.5e-6 # 0.5 us short chirp duration
|
|
||||||
FFT_SIZE = 2048
|
|
||||||
LONG_CHIRP_SAMPLES = int(T_LONG_CHIRP * FS_SYS) # 3000
|
|
||||||
SHORT_CHIRP_SAMPLES = int(T_SHORT_CHIRP * FS_SYS) # 50
|
|
||||||
LONG_SEGMENTS = 2
|
|
||||||
SCALE = 0.9 # Q15 scaling factor (matches radar_scene.py)
|
SCALE = 0.9 # Q15 scaling factor (matches radar_scene.py)
|
||||||
Q15_MAX = 32767
|
Q15_MAX = 32767
|
||||||
|
|
||||||
|
# Sizing / sample counts sourced from radar_params.vh (single source of truth).
|
||||||
|
FFT_SIZE = _require('RP_FFT_SIZE')
|
||||||
|
LONG_CHIRP_SAMPLES = _require('RP_LONG_CHIRP_SAMPLES_3KM')
|
||||||
|
SHORT_CHIRP_SAMPLES = _require('RP_SHORT_CHIRP_SAMPLES')
|
||||||
|
LONG_SEGMENTS = _require('RP_LONG_SEGMENTS_3KM')
|
||||||
|
|
||||||
|
# Durations are derived from sample counts + FS_SYS so a change to
|
||||||
|
# RP_LONG_CHIRP_SAMPLES_3KM automatically re-targets the chirp rate.
|
||||||
|
T_LONG_CHIRP = LONG_CHIRP_SAMPLES / FS_SYS
|
||||||
|
T_SHORT_CHIRP = SHORT_CHIRP_SAMPLES / FS_SYS
|
||||||
|
|
||||||
# Output directory (FPGA RTL root, where .mem files live)
|
# Output directory (FPGA RTL root, where .mem files live)
|
||||||
MEM_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..')
|
MEM_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..')
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user