clash-lang / clash-compiler

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

Custom BitRep and derive Enum don't mesh #1795

Open DigitalBrains1 opened 3 years ago

DigitalBrains1 commented 3 years ago

The following code makes Clash crash:

module ToEnum where

import Clash.Prelude

import Clash.Annotations.BitRepresentation

data Color = R | G | B
  deriving (Enum, Show)
{-# ANN module (DataReprAnn
                  $(liftQ [t|Color|])
                  2
                  [ ConstrRepr 'R 0b11 0b00 []
                  , ConstrRepr 'G 0b11 0b01 []
                  , ConstrRepr 'B 0b11 0b10 []
                  ]) #-}

topEntity
  :: Int
  -> Color
topEntity = toEnum

(an alternative avoiding Int with Unsigned 2 and toEnum . fromIntegral also does not work)

clash --vhdl crashes with:

<no location info>: error:
    Clash error call:
    Clash.Backend.VHDL(1652): DataTag (CustomSum "ToEnum.Color" (DataRepr' {drType = ConstTy' "ToEnum.Color", drSize = 2, drConstrs = [ConstrRepr' {crName = "ToEnum.R", crPosition = 0, crMask = 3, crValue = 0, crFieldAnns = []},ConstrRepr' {crName = "ToEnum.G", crPosition = 1, crMask = 3, crValue = 1, crFieldAnns = []},ConstrRepr' {crName = "ToEnum.B", crPosition = 2, crMask = 3, crValue = 2, crFieldAnns = []}]}) 2 [(ConstrRepr' {crName = "ToEnum.R", crPosition = 0, crMask = 3, crValue = 0, crFieldAnns = []},"ToEnum.R"),(ConstrRepr' {crName = "ToEnum.G", crPosition = 1, crMask = 3, crValue = 1, crFieldAnns = []},"ToEnum.G"),(ConstrRepr' {crName = "ToEnum.B", crPosition = 2, crMask = 3, crValue = 2, crFieldAnns = []},"ToEnum.B")]) (Left (RawIdentifier "x" Nothing [("unsafeMake",SrcLoc {srcLocPackage = "clash-lib-1.5.0-inplace", srcLocModule = "Clash.Netlist.Util", srcLocFile = "src/Clash/Netlist/Util.hs", srcLocStartLine = 995, srcLocStartCol = 17, srcLocEndLine = 995, srcLocEndCol = 30})]))
    CallStack (from HasCallStack):
      error, called at src/Clash/Backend/VHDL.hs:1652:13 in clash-lib-1.5.0-inplace:Clash.Backend.VHDL
      expr_, called at src/Clash/Backend/VHDL.hs:1359:36 in clash-lib-1.5.0-inplace:Clash.Backend.VHDL
christiaanb commented 3 years ago

So the tricky bit is that the derived Enum instance uses tagToEnum# as the implementation for toEnum. Before we had custom data representation we could simply translate tagToEnum# as a bit-slice as we used a binary encoding and because GHC assumes 0-based index for the "tag" corresponding to a constructor. Bit-slicing can simply be an expression (as opposed to a statement) in our supported HDLs, so the internals of Clash are already committed to translating tagToEnum# to an expression in HDL.

However, with custom bit-encodings, in the general case, tagToEnum# must be translated to a statement expressing an n-ary choice. I guess the way forward is to have BlackBoxHaskell primitive entry for tagToEnum# that can decide between creating a declaration (statement) BlackBox for types with a custom bit-encoding, or creating an expression BlackBox for types without a custom bit-encoding.