Implement generic MAX2771 raw Ethernet recorder

This commit is contained in:
onur
2026-08-01 01:15:47 +03:00
commit 79d07fdda7
57 changed files with 4810 additions and 0 deletions
+75
View File
@@ -0,0 +1,75 @@
#!/usr/bin/env bash
set -euo pipefail
target_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
build_dir=${EFINIX_BUILD_DIR:-"$target_dir/build"}
project=${EFINITY_PROJECT:-ti375_max2771_recorder}
constraint_file=${EFINITY_CONSTRAINT_FILE:-"$target_dir/constraints/ti375_max2771_recorder.sdc"}
if [[ -z ${EFINITY_HOME:-} ]]; then
echo "ERROR: set EFINITY_HOME to the Efinity installation directory" >&2
echo "Example: export EFINITY_HOME=/opt/efinity/2025.2" >&2
exit 2
fi
if [[ ! -f "$EFINITY_HOME/bin/setup.sh" ]]; then
echo "ERROR: $EFINITY_HOME/bin/setup.sh not found" >&2
exit 2
fi
# shellcheck disable=SC1090
export PYTHONPATH=${PYTHONPATH:-}
source "$EFINITY_HOME/bin/setup.sh"
mkdir -p "$build_dir"
export EFINITY_BUILD_DIR="$build_dir"
export EFINITY_PROJECT="$project"
export EFINIX_DEVICE=${EFINIX_DEVICE:-Ti375C529}
export EFINIX_TIMING_MODEL=${EFINIX_TIMING_MODEL:-C4}
rf_clock_period_ns=${RF_CLOCK_PERIOD_NS:-30.547}
if [[ ! $rf_clock_period_ns =~ ^[0-9]+([.][0-9]+)?$ ]]; then
echo "ERROR: RF_CLOCK_PERIOD_NS must be a positive numeric period in ns" >&2
exit 2
fi
cd "$build_dir"
"$EFINITY_HOME/bin/python3" "$target_dir/scripts/generate_project.py"
"$EFINITY_HOME/bin/python3" "$target_dir/interface/create_interface.py"
pt_sdc="$build_dir/outflow/$project.pt.sdc"
if [[ ! -f "$pt_sdc" ]]; then
echo "ERROR: Interface Designer did not generate $pt_sdc" >&2
exit 1
fi
{
# A direct GCLK input has no frequency property in Interface Designer, so
# Efinity emits this documented placeholder for the user clock period.
sed "s/<USER_PERIOD>/$rf_clock_period_ns/g" "$pt_sdc"
sed -n '1,$p' "$constraint_file"
} > "$build_dir/${project}_merged.sdc"
if grep -q '<USER_PERIOD>' "$build_dir/${project}_merged.sdc"; then
echo "ERROR: unresolved RF input-clock period in merged SDC" >&2
exit 1
fi
"$EFINITY_HOME/bin/python3" "$EFINITY_HOME/scripts/efx_run.py" \
"$build_dir/$project.xml" --flow compile
"$EFINITY_HOME/bin/python3" "$target_dir/scripts/check_timing.py" \
"$build_dir/outflow/$project.timing.rpt"
cdc_report="$build_dir/outflow/$project.cdc.rpt"
if [[ ! -f "$cdc_report" ]] || ! grep -q "No Synchronizer warnings to report" "$cdc_report"; then
echo "ERROR: Efinity CDC/synchronizer report is absent or contains warnings" >&2
exit 1
fi
if grep -Eq "No ports matched|No clocks matched|No valid pin\(s\) found for clock" \
"$build_dir/outflow/$project.log"; then
echo "ERROR: major SDC coverage warning found in Efinity log" >&2
exit 1
fi
if [[ ! -f "$build_dir/outflow/$project.bit" ]]; then
echo "ERROR: expected JTAG bitstream was not generated" >&2
exit 1
fi
echo "Bitstream: $build_dir/outflow/$project.bit"
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
target_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
export EFINITY_PROJECT=ti375_ethernet_selftest
export EFINITY_TOP=ti375_ethernet_selftest_top
export EFINITY_BUILD_DIR=${EFINITY_SELFTEST_BUILD_DIR:-"$target_dir/build_selftest"}
exec "$target_dir/scripts/build_efinity.sh"
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/env python3
"""Fail conservatively on Efinity timing violations or absent clock coverage."""
import re
import sys
from pathlib import Path
if len(sys.argv) != 2:
raise SystemExit("usage: check_timing.py OUTflow/project.timing.rpt")
path = Path(sys.argv[1])
if not path.is_file():
raise SystemExit(f"timing report not found: {path}")
text = path.read_text(encoding="utf-8", errors="replace")
if re.search(r"\b(?:VIOLATED|timing\s+not\s+met|negative\s+slack)\b", text, re.I):
raise SystemExit(f"timing violation reported in {path}")
lower = text.lower()
start = lower.find("clock relationship summary")
if start < 0:
raise SystemExit("Clock Relationship Summary is absent; cannot prove timing closure")
end_candidates = [x for x in (
lower.find("path details", start + 1),
lower.find("max critical", start + 1),
) if x >= 0]
summary = text[start:min(end_candidates) if end_candidates else len(text)]
# Relationship rows contain the setup/hold slack values. Reject any negative
# decimal in this summary; clock names on this design contain no negative nums.
if re.search(r"(?<![A-Za-z0-9_])-\d+(?:\.\d+)?", summary):
raise SystemExit("negative setup/hold slack in Clock Relationship Summary")
for required in ("eth_clk", "rf_clk_pmod"):
if required not in text:
raise SystemExit(f"required clock {required!r} is absent from timing report")
print(f"Timing report accepted: {path}")
+101
View File
@@ -0,0 +1,101 @@
#!/usr/bin/env python3
"""Generate an Efinity project XML without a GUI/project database dependency."""
import datetime
import os
from pathlib import Path
import xml.dom.minidom
import xml.etree.ElementTree as ET
TARGET = Path(__file__).resolve().parents[1]
ROOT = TARGET.parents[1]
BUILD = Path(os.environ.get("EFINIX_BUILD_DIR", TARGET / "build")).resolve()
PROJECT = os.environ.get("EFINITY_PROJECT", "ti375_max2771_recorder")
TOP = os.environ.get("EFINITY_TOP", "ti375_max2771_recorder_top")
DEVICE = os.environ.get("EFINIX_DEVICE", "Ti375C529")
TIMING_MODEL = os.environ.get("EFINIX_TIMING_MODEL", "C4")
EFINITY_HOME = Path(os.environ["EFINITY_HOME"]).resolve()
version_file = EFINITY_HOME / "scripts" / "sw_version.txt"
SW_VERSION = version_file.read_text(encoding="utf-8").splitlines()[0].strip()
sources = [
ROOT / "rtl" / "common" / "reset_sync.sv",
ROOT / "rtl" / "common" / "pulse_sync.sv",
ROOT / "rtl" / "common" / "gray_counter_sync.sv",
ROOT / "rtl" / "common" / "async_fifo_gray.sv",
ROOT / "rtl" / "common" / "max2771_capture.sv",
ROOT / "rtl" / "common" / "ethernet_crc32.sv",
ROOT / "rtl" / "common" / "ethernet_packetizer.sv",
ROOT / "rtl" / "common" / "ethernet_tx_mac.sv",
ROOT / "rtl" / "common" / "phy_reset_ctrl.sv",
ROOT / "rtl" / "common" / "mdio_master.sv",
TARGET / "rtl" / "phy_init_rtl8211f.sv",
TARGET / "rtl" / "rgmii_tx_efinix.sv",
TARGET / "rtl" / "ti375_max2771_recorder_top.sv",
TARGET / "rtl" / "ti375_ethernet_selftest_top.sv",
]
for source in sources:
if not source.is_file():
raise SystemExit(f"missing RTL source: {source}")
ns = "http://www.efinixinc.com/enf_proj"
root = ET.Element("efx:project", {
"xmlns:efx": ns,
"name": PROJECT,
"location": str(BUILD),
"sw_version": SW_VERSION,
"last_change_date": "Date : " + datetime.datetime.now().strftime("%Y-%m-%d %H:%M"),
})
device_info = ET.SubElement(root, "efx:device_info")
ET.SubElement(device_info, "efx:family", name="Titanium")
ET.SubElement(device_info, "efx:device", name=DEVICE)
ET.SubElement(device_info, "efx:timing_model", name=TIMING_MODEL)
design_info = ET.SubElement(root, "efx:design_info", {
"def_veri_version": "verilog_2k", "def_vhdl_version": "vhdl_2008"
})
ET.SubElement(design_info, "efx:top_module", name=TOP)
for source in sources:
ET.SubElement(design_info, "efx:design_file", {
"name": str(source), "version": "default", "library": "default"
})
constraint_info = ET.SubElement(root, "efx:constraint_info")
ET.SubElement(constraint_info, "efx:sdc_file", name=str(BUILD / f"{PROJECT}_merged.sdc"))
ET.SubElement(root, "efx:misc_info")
ET.SubElement(root, "efx:ip_info")
def add_param(parent, name, value, value_type="e_string"):
ET.SubElement(parent, "efx:param", name=name, value=str(value), value_type=value_type)
synth = ET.SubElement(root, "efx:synthesis", tool_name="efx_map")
add_param(synth, "work_dir", "work_syn")
add_param(synth, "mode", "speed", "e_option")
add_param(synth, "infer-clk-enable", "3", "e_option")
add_param(synth, "infer-sync-set-reset", "1", "e_option")
add_param(synth, "bram_output_regs_packing", "1", "e_option")
add_param(synth, "retiming", "1", "e_option")
add_param(synth, "seq_opt", "1", "e_option")
pnr = ET.SubElement(root, "efx:place_and_route", tool_name="efx_pnr")
add_param(pnr, "work_dir", "work_pnr")
pgm = ET.SubElement(root, "efx:bitstream_generation", tool_name="efx_pgm")
add_param(pgm, "mode", "active", "e_string")
add_param(pgm, "width", "1", "e_string")
add_param(pgm, "oscillator_clock_divider", "DIV8", "e_option")
add_param(pgm, "spi_low_power_mode", "on", "e_bool")
add_param(pgm, "io_weak_pullup", "on", "e_bool")
add_param(pgm, "enable_roms", "smart", "e_option")
add_param(pgm, "bitstream_compression", "on", "e_bool")
add_param(pgm, "generate_bit", "on", "e_bool")
add_param(pgm, "generate_hex", "on", "e_bool")
ET.SubElement(root, "efx:debugger")
ET.SubElement(root, "efx:security")
BUILD.mkdir(parents=True, exist_ok=True)
raw = ET.tostring(root, encoding="utf-8")
pretty = xml.dom.minidom.parseString(raw).toprettyxml(indent=" ")
(BUILD / f"{PROJECT}.xml").write_text(pretty, encoding="utf-8")
print(BUILD / f"{PROJECT}.xml")
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -euo pipefail
target_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
build_dir=${EFINITY_BUILD_DIR:-"$target_dir/build"}
project=${EFINITY_PROJECT:-ti375_max2771_recorder}
if [[ -z ${EFINITY_HOME:-} || ! -f "$EFINITY_HOME/bin/setup.sh" ]]; then
echo "ERROR: set EFINITY_HOME to a valid Efinity installation" >&2
exit 2
fi
# shellcheck disable=SC1090
export PYTHONPATH=${PYTHONPATH:-}
source "$EFINITY_HOME/bin/setup.sh"
if [[ ! -f "$build_dir/outflow/$project.bit" ]]; then
echo "ERROR: build first; bitstream is absent" >&2
exit 1
fi
cd "$build_dir"
"$EFINITY_HOME/bin/python3" "$EFINITY_HOME/scripts/efx_run.py" \
"$build_dir/$project.xml" --flow program --pgm_opts mode=jtag
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
target_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
export EFINITY_PROJECT=ti375_ethernet_selftest
export EFINITY_BUILD_DIR=${EFINITY_SELFTEST_BUILD_DIR:-"$target_dir/build_selftest"}
exec "$target_dir/scripts/program_efinity.sh"
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env bash
set -euo pipefail
target_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
repo_dir=$(cd "$target_dir/../.." && pwd)
sim_dir="$target_dir/build/questa"
mkdir -p "$sim_dir"
if [[ -d "$sim_dir/work" ]]; then
vdel -lib "$sim_dir/work" -all >/dev/null
fi
vlib "$sim_dir/work"
vlog -work "$sim_dir/work" -sv \
"$repo_dir/rtl/common/reset_sync.sv" \
"$repo_dir/rtl/common/pulse_sync.sv" \
"$repo_dir/rtl/common/gray_counter_sync.sv" \
"$repo_dir/rtl/common/async_fifo_gray.sv" \
"$repo_dir/rtl/common/max2771_capture.sv" \
"$repo_dir/rtl/common/ethernet_crc32.sv" \
"$repo_dir/rtl/common/ethernet_packetizer.sv" \
"$repo_dir/rtl/common/ethernet_tx_mac.sv" \
"$repo_dir/rtl/common/phy_reset_ctrl.sv" \
"$repo_dir/rtl/common/mdio_master.sv" \
"$target_dir/rtl/phy_init_rtl8211f.sv" \
"$target_dir/rtl/rgmii_tx_efinix.sv" \
"$target_dir/rtl/ti375_max2771_recorder_top.sv" \
"$target_dir/tb/tb_rgmii_tx_efinix.sv" \
"$target_dir/tb/tb_phy_init_rtl8211f.sv"
for test in tb_rgmii_tx_efinix tb_phy_init_rtl8211f; do
echo "== $test =="
vsim -c -lib "$sim_dir/work" "$test" -do 'run -all; quit -f'
done
echo "Efinix target unit tests passed. Running the shared recorder regression..."
"$repo_dir/scripts/run_common_sim.sh"