Skip to content

Add NanoXplore NG-ULTRA accelerator backend - #1510

Draft
nghielme wants to merge 15 commits into
fastmachinelearning:mainfrom
nghielme:pr2-accelerator-layer
Draft

Add NanoXplore NG-ULTRA accelerator backend#1510
nghielme wants to merge 15 commits into
fastmachinelearning:mainfrom
nghielme:pr2-accelerator-layer

Conversation

@nghielme

@nghielme nghielme commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Description

Adds an accelerator layer on top of the Bambu backend that wraps a synthesized model in
AXI4 RTL and targets the NanoXplore NG-ULTRA FPGA.

Stacked on #1509 (the Bambu backend PR) — review/merge that first. This
PR is opened against main because its true base branch lives in the fork; until the
Bambu backend PR merges, the diff here includes its commits. It will shrink automatically
once that PR lands, at which point this can be retargeted.

Design — public/private seam. Bitstream generation needs NanoXplore's proprietary
nxpython toolchain, which can't live in hls4ml. The boundary is a manifest + CLI
contract:

  • BambuAcceleratorBackend (abstract) writes manifest.json describing the built
    project (clock period, IO flow, RTL/mem files, port geometry, device).
  • NanoXploreAcceleratorBackend (concrete, registered) shells out to
    hls4ml-nanoxplore-bitstream <project_dir>, a separately-distributed driver that reads
    the manifest and runs nxpython. hls4ml ships no vendor internals; everything up to
    RTL + manifest works from hls4ml alone (model.build() with the default
    bitstream=False).

Contents:

  • hls4ml/backends/bambu_accelerator/ — abstract accelerator layer + PLL solver + wrapper
  • hls4ml/backends/nanoxplore_accelerator/ — NG-ULTRA concrete backend
  • hls4ml/templates/bambu_accelerator/rtl/ — AXI4 slave + top-level templates
  • pyproject.tomlhls4ml[nanoxplore] extra (ortools, for the PLL solver)
  • docs/backend/nanoxplore.rst

Stack & dependencies

Part of a three-PR stack, review in order:

  1. Add Bambu (PandA) HLS backend #1509 — Bambu backend
  2. Add NanoXplore NG-ULTRA accelerator backend #1510 — NanoXplore NG-ULTRA accelerator backend (this PR)
  3. CI: route Bambu tests to a dedicated image #1511 — CI routing for Bambu tests

Builds on #1509; the whole stack depends on #1448 merging first (vendored in #1509's
first commit, dropped on rebase once #1448 lands).

Type of change

  • New feature (non-breaking change which adds functionality)

Tests

test/pytest/test_build_bambu_accelerator.py covers the accelerator layer end to end
(RTL + manifest emission; the vendor bitstream step is off by default so the test runs
without nxpython).

Test Configuration: same Bambu image as the backend PR.

CI note

GitHub PR checks here cover pre-commit, the docs build, and the packaging step. The
pytest suite runs on hls4ml's self-hosted GitLab pipeline, triggered by a maintainer
with the please test label. The accelerator test needs the private Bambu image, so
it is gated off on upstream runners — its result comes from running that suite in the
Bambu CI image locally, not from upstream CI.

Acknowledgements

The Bambu backend grew out of a collaboration with Politecnico di Milano, developed over 30 merged PRs on the bambu-backend branch of this fork.

Thanks to Prof. Fabrizio Ferrandi (@fabrizioferrandi), who supervised the effort and contributed the nnet_utils foundation for Bambu, a redesign of the hls::stream class, softplus support, and numerical fixes to the activation helpers (exp_fcn_float overflow, iv_base initialisation, ac_int).

And to the students who worked on it:

  • Brendan Rios (@brios-polimi) — the BambuBackend.build() flow, refactored over several rounds into the Vitis-like interface this PR ships, plus the build_bambu.sh rewrite.
  • Tommaso Pauselli (@tommasopau) — compile-time activation LUTs (tanh, legacy softmax), softmax bug fixes and the Bambu cases in test_softmax.py, and initialisation fixes in nnet_conv1d_resource.h.
  • Daniele Cursano (@danielecursano) — the ac_channel constructor fix, compile-time SELU/ELU tables, and the fixes that got Bambu C-simulation running end to end.
  • Francesco Bazzano (@frabazz) — adapted the unrolled Dense codegen to Bambu and enabled test_resource_unrolled_dense, added Bambu to test_dense, and raised build_lib.sh to C++14.
  • Andrea Oggioni (@etabeta1) — dropped the vendored ap_types copy in favour of the headers Bambu ships, and enabled cosimulation in the pooling tests.
  • Pietro Benecchi (@pietrobenecchi) — a gcem-based compile-time softsign.
  • Gabriele Santandrea (@Gab-San) — carried the pytest 9 fixture rework (Remove parametrized fixtures from pytests for pytest 9 compatability #1417) through the Bambu tests.
  • Filippo Garofalo (@FilippoGarofalo) — a string bug fix in test_pooling.py.

Checklist

  • I have read the guidelines for contributing.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have made corresponding changes to the documentation.
  • My changes generate no new warnings.
  • I have installed and run pre-commit on the files I edited or added.
  • I have added tests that prove my fix is effective or that my feature works.

nghielme added 2 commits July 20, 2026 15:12
…earning#1448)

Vendored here so this branch builds and tests on its own. This code is
NOT part of this PR's contribution -- it is the content of open PR fastmachinelearning#1448
by the same author, on which the Bambu backend depends:

  * parse_vivado_report() split into per-report helpers; bambu_report.py
    reuses _parse_csim_results, _parse_rtl_cosim_results,
    _parse_implementation_report, _parse_timing_report, _parse_power_report
  * PATHS table and _path(), including the vivado_reports/post_route_*.rpt
    locations Bambu writes its own results into
  * statistics.tcl and the vivado_synth.tcl rewrite that produce them

Review it in fastmachinelearning#1448, not here. When fastmachinelearning#1448 merges, rebasing drops this
commit.
Firmware templates, build scripts and the nnet_utils header library used
by the Bambu backend. Two submodules come with them:

  templates/bambu/ac_types        ferrandi/ac_types, Bambu's fork
  templates/bambu/nnet_utils/gcem kthohr/gcem, constexpr math for tables

ac_types diverges from the hlslibs/ac_types already vendored for Catapult,
so it cannot be shared with that backend.
@nghielme
nghielme force-pushed the pr2-accelerator-layer branch 2 times, most recently from bb86ddf to cd42621 Compare July 24, 2026 00:20
@fabrizioferrandi

Copy link
Copy Markdown

#1510 adds test_build_bambu_accelerator.py, but this file is present in
neither BAMBU_SHARED_TESTS nor BAMBU_ONLY_TESTS. Consequently #1511 does
not generate a job in the Bambu image for this new test and does not exclude
it from standard CI. Once the stack is merged, the test can therefore be
collected in an environment without Bambu, while the accelerator is not
actually validated in the dedicated environment. Could we treat it as a
Bambu-only test: explicitly exclude it from standard CI and generate a
.pytest-bambu job for it?

Proposed fix:

Extend the generator with a Bambu-only file category (or add the file's nodeids
to the existing category) so that it performs both actions:

  1. excludes the entire file from the standard CI matrix;
  2. runs the file in the dedicated Bambu image.

nghielme added 5 commits July 24, 2026 13:55
BambuBackend converts hls4ml models to HLS C++ and drives Bambu/PandA to
synthesizable Verilog, for both io_parallel and io_stream. partname_to_bambu
maps hls4ml part names onto Bambu device names.

Not registered yet -- registration lands with the writer and report modules
it depends on.
bambu_writer.py emits the HLS C++ project; bambu_report.py parses Bambu's
XML results and reuses the Vivado report helpers for the post-route
utilization, timing and power numbers.
Only the Bambu lines: the accelerator layer registers itself separately.
test_build_bambu.py drives csim, cosim, synth and vsynth on the default
Xilinx part. The existing keras/activation/softmax/pooling suites gain
'Bambu' alongside the other backends.

conftest.py grows --backend-filter / --backend-exclude / --ci-exclude-nodeid
so a pipeline without the Bambu toolchain can deselect these cases; the CI
wiring that uses them is a separate PR.
@nghielme
nghielme force-pushed the pr2-accelerator-layer branch from cd42621 to 317660b Compare July 24, 2026 11:57
nghielme added 8 commits July 28, 2026 13:32
Picks up six upstream commits on ferrandi/ac_types dev/panda-hls since
35e101a, mostly hls::stream/ac_channel work: DEPTH template parameter on
the stream copy constructor and assignment operator (#3), peek support
(#4), a gcc 15 / system C++ library fix (#5), improved hls::stream
channel data type support (#6), a cosim regression fix (#7), and better
set_slc/set_slc2 (#8).
Since ac_types became a git submodule the headers live under
ac_types/include, not at the top level, so the USE_HLS4ML_AC_TYPES
escape hatch was passing an include path with no headers in it.
AXI4-Lite slave for both IO flows, the top-level wrappers that instantiate
the Bambu-generated core alongside it, and the supporting skid buffer, FIFO
and address-increment modules.
BambuAcceleratorBackend turns a Bambu-generated HLS core into an integrable
accelerator: float I/O wrapper, AXI slave instantiation, PLL configuration
and a versioned manifest.json describing the artefact.

The backend never imports a vendor tool. Everything a place-and-route flow
needs -- RTL file list, clock, port map, data widths -- goes into
manifest.json, and the concrete backend shells out to a single CLI. The one
abstract method is _generate_bitstream, so this layer is fully testable
without a vendor licence.

pll_solver.py solves an NX_PLL_U configuration for a requested clock period
so the hardware clock and the timing constraint agree by construction. It
needs OR-Tools, an optional dependency.

Abstract and unregistered; the concrete backend follows.
Concrete BambuAccelerator subclass: NG-ULTRA defaults (nx2h540tsc, 20 ns to
match the DevKit's 50 MHz oscillator) and the place-and-route call, made
through the CLI seam (hls4ml-nanoxplore-bitstream <project_dir>, overridable
via BitStreamCommand) with report.json read back.
Adds the accelerator half of the registration files, on top of the Bambu
half from the previous PR. BambuAcceleratorBackend stays unregistered --
it is abstract.

The pll extra ships as optional-dependencies.nanoxplore (OR-Tools).
Builds both IO flows with bitstream=False, so the tests need Bambu but no
vendor place-and-route tool, and assert on the manifest contract, the RTL
file list, and the generated top-level localparams.

The model shapes are deliberately not powers of two. Bambu rounds each BRAM
address port up to the next power of two, so the element count and the BRAM
depth differ -- which matters because HLS_*_N_WORDS means the depth on
io_parallel and the element count on io_stream. With power-of-two shapes the
two coincide and the assertions pass under either convention, so a dedicated
test guards the shapes themselves.
@nghielme
nghielme force-pushed the pr2-accelerator-layer branch from 317660b to f2fe150 Compare July 28, 2026 11:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants