Skip to content
New issue

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

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

Already on GitHub? Sign in 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
Contributor

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.

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

No branches or pull requests

1 participant