mxdotp: LLM-assisted datapath restructuring around narrower pipeline cuts - #34
Open
Zeltrxn2333 wants to merge 4 commits into
Open
mxdotp: LLM-assisted datapath restructuring around narrower pipeline cuts#34Zeltrxn2333 wants to merge 4 commits into
Zeltrxn2333 wants to merge 4 commits into
Conversation
added 4 commits
September 2, 2026 12:42
Move the per-lane alignment shift amounts, the accumulator negate/shift amount and the scale offset into stage 1, fuse the sum-of-products with the accumulator add in stage 2, and replace the normalisation shifter and LZC with a windowed shifter and a radix-4 counter. Pipeline register positions, PipeConfig semantics, interface, latency and results are unchanged. Builds on the LaneWidth/VectorSize parameterisation: all widths are passed per module (SoPFixedWidth), the package carries none of them.
fpnew_mxdotp_norm_window built its first coarse stage as
{sum_magnitude[W0-65:0], 32'b0}, which is the top-W1 window of (X << 64)
only when LZC_SUM_WIDTH == 119 (VectorSize == 8). For VectorSize = 32
(LZC_SUM_WIDTH = 121) the concatenation is two bits too wide and the
assignment silently dropped the two leading bits whenever the
normalisation shift was >= 64.
Write the stage as a constant shift of the full word and take the top
W1 bits, which is correct for any width, and add an elaboration-time
guard for the 64 < LZC_SUM_WIDTH <= 128 range the window assumes.
At VectorSize = 8 the two forms select the same wires.
fpnew_mxdotp_product_shifter, fpnew_mxdotp_adder_tree, fpnew_mxdotp_format_adder, fpnew_mxdotp_add_accumulator_sop and fpnew_mxdotp_norm_shift were superseded by product_exponent/product_align, fused_sop_accumulator and norm_window and have no remaining instances.
The FP6 remainder lanes were read from flat_operands_{a,b}_q at a
hard-coded offset of 48 (= 8 * 6), so FP6/FP6ALT results were wrong for
any VectorSize other than 8. Use VectorSize*6 instead.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This is an LLM-assisted change: the restructuring below was proposed and
iterated by an LLM-driven optimisation flow, then reviewed by hand.
This PR restructures the internal datapath of
fpnew_mxdotp_multi(filessrc/fpnew_mxdotp_multi.sv,src/mxdotp/fpnew_mxdotp_multi_modules.sv) so thatthe pipeline registers sit at narrower cut points and the per-stage logic depth
is shorter. It is a functionally transparent change:
FpSrcFmtConfig,IntSrcFmtConfig,NumPipeRegs,PipeConfig,LaneWidth,VectorSize, ...), handshake andlatency are unchanged;
NUM_INP_REGS,NUM_IM_REGS,NUM_MID_REGS,NUM_MO_EARLY_REGS,NUM_MO_LATE_REGS,NUM_OUT_REGS) andthe
PipeConfigsemantics are byte-identical toeb505fe;eb505fefor every source format and pipelineconfiguration, at every
VectorSizefor which the FP6 remainder interfaceis well defined (
8*VectorSize mod 6 == 4, e.g. 8, 32; atVectorSize = 16FP6/FP6ALT read past the 2-bit
operands_*_fp6_rem_iport oneb505feand on this branch alike).
fpnew_mxdotp_multi_pkg.svand everything outsidemxdotpare untouched.The PR is four commits: (1) the datapath restructuring, (2) a width-generic
rewrite of one stage in the new normalisation window, (3) removal of the
submodules that are no longer instantiated, (4) a one-line fix of a
pre-existing FP6 lane-extraction bug for
VectorSize != 8.Scope: tuned for
NumPipeRegs = 3The restructuring was done for one pipeline configuration only:
NumPipeRegs = 3,PipeConfig = INSIDE, i.e. register banks at IM, MID andMO_LATE (
NUM_IM_REGS = NUM_MID_REGS = NUM_MO_LATE_REGS = 1, all others 0)."Stage n" below means the combinational logic between consecutive banks in
that configuration:
shift-amount arithmetic);
Every other
NumPipeRegs/PipeConfigstill elaborates and produces thesame results as
eb505fe(the bank positions are untouched), but no effortwas made to balance the logic for those configurations; in particular the
combinational (
NumPipeRegs = 0) depth is the sum of the three stages eitherway.
What changed
1. Re-cut pipeline banks
The bank positions (
NUM_*_REGS,PipeConfig) are unchanged; what each bankholds is not. Widths quoted for
VectorSize = 8, FP32 accumulator.eb505fefp_info_t, scaleexponent_majorfp_info_t, scale (accumulator aligned and added behind the bank)exponent_majorsum_productword carried along for the zero test, 32-bit special result + 5-bit statussum_product_is_zero, 4-bit special verdict{result_is_special, nv, is_inf, inf_sign}re-expanded at the output (fpnew_mxdotp_special_assemble)So the alignment barrel shifters and the fused adder now sit in the same stage
(between INP-MID and MID), while stage 1 is the multipliers plus the narrow
exponent/shift-amount arithmetic hoisted in front of them.
2. Stage 1: hoist everything that does not depend on the products
fpnew_mxdotp_product_exponent(new, replaces the early half offpnew_mxdotp_product_shifter): computes the per-lane alignment shift amountin stage 1 with the constant offset (
SOP_SHIFT= 28 for FP8, 4 for FP6)folded into the exponent adder, so stage 2 no longer has an adder in front of
the barrel shifter. For the FP8 instance the INT8 and FP8 alignment arms are
made disjoint: on INT8 the shift amount is forced out of range
(
AmtWidth = ExpWidth+1bits, all ones ≥OutputWidth), so the 67-bit × 8-laneformat multiplexer in the aligner collapses into an OR that is free on the
low
ANCHORbits.fpnew_mxdotp_product_align(new, the late half of the old shifter): justthe variable barrel shift, behind the INP-MID bank.
fpnew_mxdotp_accumulator_prep(new, split offfpnew_mxdotp_accumulator_shiftat its narrow waist): the 24-bit conditional negate of the accumulator mantissa
and the 4-term accumulator shift-amount sum move into stage 1; the INP-MID bank
carries
signed_mantissa+ shift amount instead of the info word.exponent_major(= scale + constant offset) is computed once by the stage-1scale adder and carried through the banks instead of the raw scale, so the
10-bit constant adder in
fpnew_mxdotp_norm_finalizedisappears.choice of the operand packing that holds when
src_fmt == fmt(its output isonly ever read through
[src_fmt]), removing thesrc_fmtmultiplexer infront of the classifier bank.
fpnew_mxdotp_signed_vector_multiplier: the FP8 sign is taken out ofthe front of the multiplier array. Instead of negating
mantissa_abeforethe multiply,
mantissa_ais one's-complemented (one XOR) andmantissa_bis added as one extra partial-product row, using
(-a)*b == (~a)*b + b.product_signedis bit-identical.3. Stage 2: fused sum-of-products + accumulator
fpnew_mxdotp_fused_sop_accumulator(new, replaces theadder_tree/format_adder/add_accumulator_sopchain): one signedreduction of the FP8 lanes, the FP6/FP4 lane sums and the aligned accumulator.
SoPFixedWidthbits (70 atVectorSize = 8) instead of the full 95-bit container; the exact SoP magnitudeis bounded below
2**SoPFixedWidth, so the two tests agree (argument in thesource comment).
result_is_accumulatoroperands) are registeredraw and combined behind the MID bank.
4. Stage 3: normalisation
fpnew_mxdotp_norm_window(new, replacesfpnew_mxdotp_norm_shift): thenormalisation left shift is three coarse binary stages (
shamt[6:4]) followedby a single 16-way select for the fine amount (
shamt[3:0]), with the stickyof the dropped bits carried on 7/3/1 bits instead of full-width windows.
fpnew_mxdotp_lzc119(new): a radix-4 leading-zero counter, bit-identicalto
lzc #(.MODE(1))fromcommon_cellson every input including all-zero.fpnew_mxdotp_twos_complhands over the one's complement magnitude; thepending
+1is re-applied innorm_finalizeand its effect on the LZC(
lzc_dec) is folded intoexponent_major. The correction is dropped fromnorm_shamtitself, where the window provably cannot observe it.so the rounder tail sees one 2:1 level.
5. Output
taking the status cone off the rounding incrementer.
Modules
New:
fpnew_mxdotp_special_assemble,fpnew_mxdotp_product_exponent,fpnew_mxdotp_product_align,fpnew_mxdotp_accumulator_prep,fpnew_mxdotp_fused_sop_accumulator,fpnew_mxdotp_norm_window,fpnew_mxdotp_lzc119.Removed (third commit, no remaining instances):
fpnew_mxdotp_product_shifter,fpnew_mxdotp_adder_tree,fpnew_mxdotp_format_adder,fpnew_mxdotp_add_accumulator_sop,fpnew_mxdotp_norm_shift.VectorSize != 8fix (second commit)The first cut of
fpnew_mxdotp_norm_windowbuilt its shift-by-64 stage as{sum_magnitude[W0-65:0], 32'b0}, which is only the top-87-bit window ofX << 64whenLZC_SUM_WIDTH == 119(i.e.VectorSize == 8). AtVectorSize = 32(LZC_SUM_WIDTH = 121) it silently dropped two leading bitsfor normalisation shifts ≥ 64. The second commit writes the stage as a constant
shift so it is correct for any width, and adds an elaboration-time
$fatalguard for the
64 < LZC_SUM_WIDTH <= 128range the window assumes(
VectorSize <= 4096). AtVectorSize = 8the two forms select the same wires.FP6 lane extraction for
VectorSize != 8(fourth commit)Pre-existing on
eb505fe:fpnew_mxdotp_multi.svreads the FP6 remainder lanesfrom
flat_operands_{a,b}_qat a hard-coded offset of48(= 8·6), soFP6/FP6ALT results were wrong for any
VectorSizeother than 8 (e.g. 32). Theoffset is now
VectorSize*6.VectorSize = 8is unchanged. This does notchange the FP6 remainder interface itself, so
VectorSizevalues whose8*VectorSize mod 6 != 4(e.g. 16) remain unsupported for FP6/FP6ALT as before.