clash-lang / clash-compiler

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

Synthesis fails for newtypes of types with multiple fields. #2698

Open lmbollen opened 3 months ago

lmbollen commented 3 months ago

Clash fails to generate a hdl for designs that contain newtype wrappers of e.g. records

Reproducer:

import Clash.Prelude
import Clash.Annotations.TH

data Foo a = Foo a
data Bar = Bar
  { a :: Int
  , b :: Int
  }

top :: Foo Bar
top = undefined

{-# ANN top (Synthesize
  { t_name   = "top"
  , t_inputs = []
  , t_output = PortProduct "" [PortProduct "" [PortName "a", PortName "b"]]
  })

Error:

ghci> main
GHC: Setting up GHC took: 0.093s
GHC: Compiling and loading modules took: 0.465s
Clash: Parsing and compiling primitives took 0.173s
Loading dependencies took 1.101s
Clash: Ignoring previously made caches
Clash: Compiling Main.top
Clash: Normalization took 0.
*** Exception: Saw a PortProduct in a Synthesize annotation:

  PortProduct "" [PortProduct "" [PortName "a",PortName "b"]]

but the port type:

  Product "Main.Bar" (Just ["a","b"]) [Signed 64,Signed 64]

is not a product!
CallStack (from HasCallStack):
  error, called at clash-lib/src/Clash/Netlist/Util.hs:1942:8 in main:Clash.Netlist.Util
  expandTopEntityOrErrM, called at clash-lib/src/Clash/Netlist/Util.hs:784:8 in main:Clash.Netlist.Util
  mkUniqueNormalized, called at clash-lib/src/Clash/Netlist.hs:273:9 in main:Clash.Netlist
  genComponentT, called at clash-lib/src/Clash/Netlist.hs:247:41 in main:Clash.Netlist
  genComponent, called at clash-lib/src/Clash/Netlist.hs:125:9 in main:Clash.Netlist
ghci> 

Error is generated by Clash.Netlist.Util.goPort, relevant arguments: FilteredHwType:

FilteredHWType
    (Product "Main.Bar" (Just [ "a" , "b" ]) [ Signed 64 , Signed 64 ])
    [ [ ( False
        , FilteredHWType
            (Product "Main.Bar" (Just [ "a" , "b" ]) [ Signed 64 , Signed 64 ])
            [ [ ( False
                , FilteredHWType
                    (Signed 64) [ [ ( False , FilteredHWType (Signed 64) [] ) ] ]
                )
              , ( False
                , FilteredHWType
                    (Signed 64) [ [ ( False , FilteredHWType (Signed 64) [] ) ] ]
                )
              ]
            ]
        )
      ]
    ]

PortName:

PortProduct "" [ PortProduct "" [ PortName "a" , PortName "b" ] ]

The FilteredHwType is not considered a product by the isProduct function, causing a fall through. Adjusting isProduct makes hdl generation succeed:

  -- Vector and RTree are hardcoded as product types, even when instantiated as
  -- /Vec 1 a/ or /RTree 0 a/ respectively.
  isProduct :: FilteredHWType -> Bool
  isProduct (FilteredHWType (CustomProduct {}) _) =
    -- CustomProducts are not yet support in mkTopInput/mkTopOutput so we can't treat
    -- them as product types.
    -- FIXME: Support CustomProduct in top entity annotations
    False
  isProduct (FilteredHWType (Vector {}) _) = True
  isProduct (FilteredHWType (RTree {}) _) = True
  ----------------------------------------------
  -- Introduced pattern match
  isProduct (FilteredHWType (Product _ _ _) [[(_, fwHwTy)]]) = isProduct fwHwTy
  ----------------------------------------------
  isProduct (FilteredHWType _ [(_:_:_)]) = True
  isProduct _ = False