diff --git a/changelog/2026-02-10T18_38_12+01_00_fix_iteratei_verilog_reg b/changelog/2026-02-10T18_38_12+01_00_fix_iteratei_verilog_reg new file mode 100644 index 0000000000..83b0ddb565 --- /dev/null +++ b/changelog/2026-02-10T18_38_12+01_00_fix_iteratei_verilog_reg @@ -0,0 +1 @@ +FIXED: HO blackboxes not propagating usage metadata for generated results [#3147](https://github.com/clash-lang/clash-compiler/issues/3141) diff --git a/clash-lib/src/Clash/Primitives/DSL.hs b/clash-lib/src/Clash/Primitives/DSL.hs index e2c44b506d..8619312571 100644 --- a/clash-lib/src/Clash/Primitives/DSL.hs +++ b/clash-lib/src/Clash/Primitives/DSL.hs @@ -1,6 +1,6 @@ {-| Copyright : (C) 2019, Myrtle Software Ltd. - 2020-2024, QBayLogic B.V. + 2020-2026, QBayLogic B.V. 2021, Myrtle.ai 2022-2023, Google Inc License : BSD2 (see the file LICENSE) @@ -102,7 +102,7 @@ import Data.IntMap (IntMap) import qualified Data.IntMap as IntMap import Data.List (intersperse) import Data.List.Extra (zipEqual) -import Data.Maybe (fromMaybe) +import Data.Maybe (fromMaybe, listToMaybe) import Data.Monoid (Ap(getAp)) import Data.Semigroup hiding (Product) import Data.String @@ -835,6 +835,10 @@ instHO bbCtx fPos (resTy, bbResTy) argsWithTypes = do resName <- declare' (ctxName <> "_" <> "ho" <> showt fPos <> "_" <> showt fSubPos <> "_res") resTy + -- Pick the fSubPos-th HO instantiation (if present) and reuse its usage. + case IntMap.lookup fPos (bbFunctions bbCtx) >>= listToMaybe . drop fSubPos of + Just (_, usage, _, _, _, _) -> declareUseOnce usage resName + Nothing -> pure () let res = ([Text (Id.toLazyText resName)], bbResTy) -- Render HO argument to plain text diff --git a/tests/Main.hs b/tests/Main.hs index 975d8407d6..2ac88d7e5b 100755 --- a/tests/Main.hs +++ b/tests/Main.hs @@ -669,6 +669,7 @@ runClashTest = defaultMain , let _opts = def { hdlTargets = [VHDL], hdlLoad = [], hdlSim = []} in runTest "T3084" _opts , runTest "T3141" def{hdlSim=[], hdlLoad=[], clashFlags=["-itests/shouldwork/Issues/T3141", "-itests/shouldwork/Issues/T3141"]} + , outputTest "T3147" def{hdlTargets=[Verilog], hdlSim=[]} ] <> if compiledWith == Cabal then -- This tests fails without environment files present, which are only diff --git a/tests/shouldwork/Issues/T3147.hs b/tests/shouldwork/Issues/T3147.hs new file mode 100644 index 0000000000..bb728550ce --- /dev/null +++ b/tests/shouldwork/Issues/T3147.hs @@ -0,0 +1,36 @@ +module T3147 where + +import Clash.Prelude +import Data.List (isInfixOf) +import System.Environment (getArgs) +import System.FilePath (()) +import qualified Prelude as P + +delayedCounter :: + forall dom d n. + (HiddenClockResetEnable dom, KnownNat d, KnownNat n) => + SNat d -> + Signal dom (Unsigned n) +delayedCounter d = last $ iterate (succSNat d) dflipflop cnt + where + cnt = register 0 (cnt + 1) + +topEntity :: Clock System -> Reset System -> Enable System -> Signal System (Unsigned 8) +topEntity = exposeClockResetEnable $ delayedCounter d3 + +assertIn :: String -> String -> IO () +assertIn needle haystack + | needle `isInfixOf` haystack = return () + | otherwise = + P.error $ P.concat ["Expected:\n\n ", needle, "\n\nIn:\n\n", haystack] + +mainVerilog :: IO () +mainVerilog = do + [topDir] <- getArgs + content <- readFile (topDir show 'topEntity "topEntity.v") + assertIn "reg [7:0] c$bb_res_res" content + assertIn "reg [7:0] c$bb_res_res_0" content + assertIn "reg [7:0] c$bb_res_res_1" content + assertIn "assign iterateI_ho1_0_res = c$bb_res_res3;" content + assertIn "assign iterateI_ho1_1_res = c$bb_res_res_0;" content + assertIn "assign iterateI_ho1_2_res = c$bb_res_res_1;" content