clash-lang / clash-compiler

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

Cannot use domain configurations from implicit arguments at synthesis boundaries #2648

Closed christiaanb closed 8 months ago

christiaanb commented 8 months ago

The following:

module Test where

import Clash.Prelude

topEntity ::
  HiddenClock System =>
  Signal System (Index (DomainPeriod System))
topEntity = dflipflop (pure maxBound)

fails with:

Test.hs:8:1: error:

    Clash.Netlist.BlackBox(219): Couldn't instantiate blackbox for Clash.Sized.Internal.Index.maxBound#. Verification procedure reported:

    Argument 0 should be literal, as blackbox used ~LIT[0], but was:

    Identifier (RawIdentifier "c$sel" Nothing [("unsafeMake",SrcLoc {srcLocPackage = "clash-lib-1.9.0-inplace", srcLocModule = "Clash.Netlist.Id", srcLocFile = "src/Clash/Netlist/Id.hs", srcLocStartLine = 244, srcLocStartCol = 20, srcLocEndLine = 244, srcLocEndCol = 30}),("unsafeFromCoreId",SrcLoc {srcLocPackage = "clash-lib-1.9.0-inplace", srcLocModule = "Clash.Netlist", srcLocFile = "src/Clash/Netlist.hs", srcLocStartLine = 951, srcLocStartCol = 22, srcLocEndLine = 951, srcLocEndCol = 41})]) (Just (Indexed (Void (Just (KnownDomain "System" 10000 Rising Asynchronous Defined ActiveHigh)),0,1)))

    The source location of the error is not exact, only indicative, as it is acquired 
    after optimizations. The actual location of the error can be in a function that is 
    inlined. To prevent inlining of those functions, annotate them with a NOINLINE pragma.
  |
8 | topEntity = dflipflop (pure maxBound)
  | ^^^^^^^^^

Looking at the debug log, there's really no hope:

Test.topEntity[8214565720323798486] before normalization:

λ($d(%,%)[6989586621679026617] :: GHC.Classes.(%,%)[7710162562058289156]
                                    (GHC.Classes.IP[3602879701896396849]
                                       (GHC.TypeLits.AppendSymbol[3674937295934325126]
                                          "System"
                                          "_clk")
                                       (Clash.Signal.Internal.Clock[8214565720323788305]
                                          "System"))
                                    (Clash.Signal.Internal.KnownDomain[8214565720323788331]
                                       "System")) ->
let
  $dKnownDomain[8286623314361731535] :: Clash.Signal.Internal.KnownDomain[8214565720323788331]
                                          "System"
  = GHC.Classes.$p1(%,%)[7638104968020361729][GlobalId]
      @(GHC.Classes.IP[3602879701896396849]
          (GHC.TypeLits.AppendSymbol[3674937295934325126]
             "System"
             "_clk")
          (Clash.Signal.Internal.Clock[8214565720323788305]
             "System"))
      @(Clash.Signal.Internal.KnownDomain[8214565720323788331]
          "System")
      $d(%,%)[6989586621679026617][LocalId]
in Clash.Signal.Internal.delay# @"System"
     @(Clash.Sized.Internal.Index.Index[8214565720323788393]
         10000)
     $dKnownDomain[8286623314361731535][LocalId]
     (Clash.Sized.Internal.Index.$fNFDataXIndex[8214565720323799994][GlobalId]
        @10000)
     (GHC.Classes.$p0(%,%)[7638104968020361728][GlobalId]
        @(GHC.Classes.IP[3602879701896396849]
            (GHC.TypeLits.AppendSymbol[3674937295934325126]
               "System"
               "_clk")
            (Clash.Signal.Internal.Clock[8214565720323788305]
               "System"))
        @(Clash.Signal.Internal.KnownDomain[8214565720323788331]
            "System")
        $d(%,%)[6989586621679026617][LocalId])
     (Clash.Signal.Internal.Enable[8214565720323798495]
        @"System"
        GHC.Types.True[3891110078048108586])
     (Clash.Sized.Internal.Index.$fNFDataXIndex_$cdeepErrorX[8214565720323799995][GlobalId]
        @10000
        GHC.Stack.Types.EmptyCallStack[8214565720323799735]
        (GHC.CString.unpackCString#
           "First value of dflipflop undefined"))
     (Clash.Sized.Internal.Index.maxBound# @10000
        (Clash.Signal.Internal.$p2KnownDomain[8214565720323799793][GlobalId]
           @"System"
           $dKnownDomain[8286623314361731535][LocalId]))

The KnownDomain System is passed in as an argument at the top-level.

The problem is: https://github.com/clash-lang/clash-compiler/blob/5e01ae2aec13f7b91cc8a6f5475feb4f24bf50b5/clash-prelude/src/Clash/Signal.hs#L602-L604 Where we combine an implicit parameter and a class constraint in a single constraint.

The solution to the class constraint should be unique, so really we shouldn't have a KnownDomain System argument. But because GHC encodes the combination of the implicit parameter and the class constraint as a constraint tuple, we don't get a "partial" solution.

christiaanb commented 8 months ago

As a work-around you can simply make the implicit arguments explicit:

module Test where

import Clash.Prelude

topEntity ::
  Clock System ->
  Signal System (Index (DomainPeriod System))
topEntity clk = withClock clk foo

foo ::
  HiddenClock System =>
  Signal System (Index (DomainPeriod System))
foo = dflipflop (pure maxBound)
martijnbastiaan commented 8 months ago

Duplicate of https://github.com/clash-lang/clash-compiler/issues/1028?

christiaanb commented 8 months ago

It is.