clash-lang / clash-compiler

Haskell to VHDL/Verilog/SystemVerilog compiler
https://clash-lang.org/
Other
1.4k stars 147 forks source link

Clash doesn't clear `Id` scopes when rendering different modules #2722

Open martijnbastiaan opened 1 month ago

martijnbastiaan commented 1 month ago

That is, a function primtive rendered in two modules primitive.vhdl and primitive_0.vhdl will generate variables x and x_0 respectively when asking for x. While usually not impactful, primitives such as a VIO and ILA rely on name generation to name their probes. In turn, this can cause problems when these VIOs/ILAs are used programmatically: i.e., where external scripts expect certain probe names to exist.

Reproducer:

{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}

-- | Test whether two instantiations of the same primitive rendered in their own
-- module can use the same local identifier.
module T2722 where

import Clash.Explicit.Prelude

import Clash.Annotations.Primitive (Primitive(..))
import Clash.Backend (blockDecl)
import Data.Monoid (Ap(getAp))

import qualified Clash.Netlist.Types as N
import qualified Clash.Netlist.Id as Id

bbTF :: N.TemplateFunction
bbTF = N.TemplateFunction used valid $ \_bbCtx -> do
  x <- Id.make "x"

  () <- case Id.toText x of
    "x" -> pure ()
    xName -> error $ "Unexpected name: " <> show xName <> ". Expected: x."

  getAp $ blockDecl x [N.NetDecl Nothing x N.Bit]
 where
  used    = [0,1]
  valid _ = True

{-# ANN bb (InlinePrimitive [minBound..] "[ { \"BlackBox\" : { \"name\" : \"T2722.bb\", \"kind\": \"Declaration\", \"workInfo\": \"Always\", \"format\": \"Haskell\", \"templateFunction\": \"T2722.bbTF\"}} ]") #-}
bb :: Signal System Bit
bb = pure low
{-# CLASH_OPAQUE bb #-}

-- | 'bbWrapper' is marked as opaque, so Clash will generate a separate HDL module
-- for it. Note that it accepts an extra (unused) argument to prevent Clash's
-- specialization from caching it.
bbWrapper :: Bit -> Signal System Bit
bbWrapper !_ = bb
{-# CLASH_OPAQUE bbWrapper #-}

topEntity :: Signal System Bit
topEntity = bbWrapper low + bbWrapper high

Gives:

$ rm -rf vhdl/ && cabal run clash -- -itests/shouldwork/Issues T2722 --vhdl -Wall -Werror -DCLASH_OPAQUE=OPAQUE
Resolving dependencies...
Loaded package environment from /home/martijn/code/clash-compiler/.ghc.environment.x86_64-linux-9.4.8
GHC: Setting up GHC took: 0.250s
GHC: Compiling and loading modules took: 1.201s
Hint: Interpreting T2722.bbTF
Clash: Parsing and compiling primitives took 1.546s
GHC+Clash: Loading modules cumulatively took 3.130s
Clash: Compiling T2722.topEntity
Clash: Normalization took 0.001s
Clash: Netlist generation took 0.000s

<no location info>: error:
    Clash error call:
    Unexpected name: "x_0". Expected: x.
    CallStack (from HasCallStack):
      error, called at tests/shouldwork/Issues/T2722.hs:23:14 in main:T2722