Implement generic MAX2771 raw Ethernet recorder
This commit is contained in:
+75
@@ -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
@@ -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
@@ -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
@@ -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"
|
||||
Executable
+36
@@ -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"
|
||||
Reference in New Issue
Block a user