Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

"Other error: divide by zero" on input file #2815

Open
kleinreact opened this issue Oct 1, 2024 · 0 comments
Open

"Other error: divide by zero" on input file #2815

kleinreact opened this issue Oct 1, 2024 · 0 comments
Labels

Comments

@kleinreact
Copy link
Member

I get some

<no location info>: error:
    Other error:
    divide by zero

if I compile the code attached below with clash Top.hs --verilog -fclash-spec-limit=100. Simulation of the topEntity works fine. It also works, if I replace the 26 in topEntity by 25 or any smaller value. Unfortunately, I could not come up with a smaller reproducer so far.

Reproducer: Top.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE UndecidableInstances #-}

{-# OPTIONS_GHC -fno-warn-orphans #-}
module Top where

import Clash.Prelude

import Control.Monad
import Data.Constraint
import Data.Proxy
import Data.Type.Bool
import Data.Type.Equality
import GHC.TypeLits.KnownNat
import Unsafe.Coerce

topEntity ::
  HiddenClockResetEnable System =>
  Signal System Bool
topEntity
  = toSignal
  $ distributeStages (SNat @26) False (repeat @64 id)
  $ fromSignal
  $ pure True

-- | Evenly distributes @d@ registers between @n@ combinational
-- computations. The registers are all initialized with the provided
-- initial value. The introduced delay is tracked using 'DSignal'.
distributeStages ::
  forall (d :: Nat) (n :: Nat) (a :: Type).
  (KnownNat n, NFDataX a) =>
  SNat d ->
  a ->
  Vec n (a -> a) ->
  forall (dom :: Domain) (k :: Nat).
  (KnownDomain dom, HiddenClockResetEnable dom) =>
  DSignal dom k a ->
  DSignal dom (k + d) a
distributeStages d@SNat x = distributeStages' d0 d
 where
  distributeStages' ::
    forall (m :: Nat) (i :: Nat) (r :: Nat).
    (KnownNat m, NFDataX a) =>
    SNat i -> SNat r ->
    Vec m (a -> a) ->
    forall (dom :: Domain) (k :: Nat).
    (KnownDomain dom, HiddenClockResetEnable dom) =>
    DSignal dom k a ->
    DSignal dom (k + r) a
  distributeStages' i@SNat r@SNat cs = case toUNat @m SNat of
    UZero   -> delayedI x
    USucc _ -> case toUNat r of
      UZero   -> fmap (head cs)
               . distributeStages' (succSNat i) r (tail cs)
      USucc _ | Dict <- atMostOnePerStage @m @d @i
              , Dict <- leTrans @(DistributedStages m d i) @1 @(r - 1 + 1)
              -> delayedI @(DistributedStages m d i) x
               . fmap (head cs)
               . distributeStages'
                   (succSNat i)
                   (SNat @(r - DistributedStages m d i))
                   (tail cs)

  -- We never distribute more than one register per stage. The
  -- property trivially holds, as the only possible return values of
  -- 'DistributedStages' are zero or one.
  atMostOnePerStage :: forall x y z. Dict (DistributedStages x y z <= 1)
  atMostOnePerStage = unsafeCoerce (Dict :: Dict (0 <= 0))

  -- We don't use any dictionaries of 'Data.Constraint.Nat', as they suffer
  -- from https://github.com/clash-lang/clash-compiler/issues/2376
  leTrans :: forall x y z. (y <= z, x <= y) => Dict (x <= z)
  leTrans = unsafeCoerce (Dict :: Dict (0 <= 0))

-- | A type family for calculating the positions at which we need to
-- put a register in front, if we like to evenly distribute m
-- registers between a chain of n circuit blocks, where m < n.
-- Unfortunately, there are a lot of repetitions, as we don't 
-- have let bindings in type families. See the 'KnownNat3'
-- instance below for a more readable version.
type DistributedStages :: Nat -> Nat -> Nat -> Nat
type family DistributedStages n d i where
  DistributedStages _ 0 _ = 0
  DistributedStages n d i =
    If (n <=? d)
      1
      ( If (  1 <=? i
              -- ^ we don't place a register before the first element
           && If (i <=? Mod n (d + 1) * (Div n (d + 1) + 1))
                 -- ^ distribute the hangover blocks to the first r chains
                (Mod i (Div n (d + 1) + 1) == 0)
                (Mod
                   (i - Mod n (d + 1) * (Div n (d + 1) + 1))
                   (Div n (d + 1)) == 0)
           )
          1 0
      )

instance
  (KnownNat n, KnownNat m, KnownNat i) =>
  KnownNat3 $(nameToSymbol ''DistributedStages) n m i
 where
  natSing3 =
    let
      n = natToNatural @n
      m = natToNatural @m
      i = natToNatural @i
      r = f n m i
    in
      SNatKn r
   where
    f :: Nat -> Nat -> Nat -> Nat
    f n m i
      | n == 0 = 0
      | n <= m = 1
      | otherwise =
          let k = n `div` (m + 1)
              r = n `mod` (m + 1)
              b = (k + 1) * r
           in if 1 <= i &&
                   if i <= b
                   then mod i (k + 1) == 0
                   else mod (i - b) k == 0
              then 1
              else 0
  {-# INLINE natSing3 #-}

-- | Some quick test code for seeing the 'DistributedStages' type 
-- family in action. This is only for those who like to understand
-- the code. It is not required for reproducing the bug.
placeRegister :: Nat -> Nat -> IO ()
placeRegister n m = do
  print (k, r, b)
  putStrLn "---"
  forM_ chain $ \(i, c) -> do
    when c $ putStrLn "[R]"
    putStr " "
    print i
 where
  -- minimum size of chained blocks without a register in between
  k = n `div` (m + 1)
  -- number of hangover blocks
  r = n `mod` (m + 1)
  -- add-one-more range bound
  b = (k + 1) * r

  chain
    | m <= 0    = ( , False) <$> [0..n-1]
    | m >= n    = (0, False) : (( , True) <$> [1..n-1])
    | otherwise =
        [ (i, i > 0 && cond)
        | i <- [0..n-1]
        , let cond = if i <= b
                     then i `mod` (k + 1) == 0
                     else (i - b) `mod` k == 0
        ]

Also thanks to @leonschoorl for figuring out that there is no error when compiling Clash with GHC 9.10. This at least gives a workaround.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant