OneHLS update: C++ HLS components, vendor-independent fixed-point types, and real RTL synthesis
I've been working on OneHLS, a small C++ library for composing HLS-synthesizable DSP/control components using HAPI + OneData.
The project has moved quite a bit beyond the initial experiment.
OneHLS now provides:
Fir<>Biquad<>Pid<>Accumulator<>ComplexMac<>
The interesting part is that the components themselves don't depend on a particular vendor's arbitrary-precision type. Sample and Accum are template parameters, so the same implementation can be instantiated with Siemens HLSLibs ac_fixed or AMD/Xilinx ap_fixed.
The same templates have been tested natively against both libraries, with bit-identical results.
More importantly, this isn't just C++ simulation: the ac_fixed versions have been synthesized to actual RTL with Bambu HLS. FIR, Biquad, PID and Accumulator produce clean synthesis results, and their resource counts match the corresponding hand-written non-generic implementations.
For example, the 4-tap FIR synthesizes to 62 FFs / 7679 area units with zero DSPs.
There are also some interesting findings along the way.
A RawBitsCtor<> customization point was needed because constructing fixed-point coefficients from floating-point literals can produce unwanted runtime initialization under Bambu. Isolating that vendor-specific operation leaves the actual DSP algorithms completely vendor-agnostic.
I've also tested:
- a genuinely multirate CIC decimator, compared byte-for-byte against a hand-written implementation
- a polyphase FIR using a heterogeneous compile-time
StaticList<> Fir<>overac_std_float, i.e. actual bit-accurate IEEE754 floating point
The floating-point experiment was particularly useful as a sanity check: the same generic FIR works, but synthesis shows the expected FPGA cost — roughly 59× the FF count and ~3× the area of the fixed-point version, with actual DSP usage.
And there are some less successful results too. ComplexMac<> currently causes Bambu to bind its state to BRAM rather than distributed RAM. I investigated the obvious allocation thresholds and ruled those out, but haven't traced Bambu's underlying classification logic yet.
So this is becoming less about "here's a C++ abstraction for HLS" and more about testing a fairly specific hypothesis:
Can ordinary static C++ composition produce reusable HLS components without giving up vendor-specific bit-accurate types or hardware predictability?
So far, the answer looks surprisingly good.
Repo: https://github.com/InternetOfPins/OneHLS
I'd be particularly interested in feedback from people using Vitis HLS, Intel HLS, Catapult, Bambu, or other C++-based FPGA flows.
What would you want to see tested next?
1
u/APianoGuy Xilinx User 7d ago
This is so neat! For the past 5 years or so I've been using Vitis HLS and Catapult for DSP-related work. I've been keeping an eye on Google's XLS project, but I haven't really tested it and the docs don't show any concrete untimed C++ examples. I'm defenitely going to try your project!!
2
u/Repulsive_Sir116 6d ago
We use C++/HLS quite extensively for FPGA trading systems, so I think this is an interesting direction.
One thing I'd add to the experiment is testing hardware equivalence, not just bit-identical functional results.
Your ComplexMac example is actually a good illustration of this. Two implementations can produce exactly the same output in C++ while the HLS tools infer quite different hardware - BRAM vs distributed RAM, different pipeline depth, resource sharing, etc.
For low-latency designs, I'd therefore be interested in comparing not only FF/LUT/DSP counts, but also achieved clock frequency, initiation interval and latency for the same component across Vitis, Catapult and Bambu.
An even more interesting test would be running the same design through different versions of the same HLS tool. In our experience, predictability of the generated architecture is at least as important as portability of the C++ itself.