Copilot-Language / copilot-bluespec

A Copilot backend for generating Bluespec code suitable for FPGAs.
Other
2 stars 0 forks source link

Translating `(<)` and friends results in code that fails to compile #15

Closed RyanGlScott closed 1 month ago

RyanGlScott commented 1 month ago

(Spun off from a discussion in https://github.com/Copilot-Language/copilot-bluespec/issues/14#issuecomment-2220273226.)

This Copilot specification:

-- Bug.hs
{-# LANGUAGE NoImplicitPrelude #-}
module Main (main) where

import Copilot.Compile.Bluespec
import Language.Copilot

spec :: Spec
spec = trigger "trig" (constD 0 < extern "e" Nothing) []

main :: IO ()
main = do
  spec' <- reify spec
  compile "Bug" spec'

Will result in Bluespec code that fails to compile against this Top.bs file:

-- Top.bs
package Top where

import FloatingPoint

import Bug
import BugIfc
import BugTypes

bugIfc :: Module BugIfc
bugIfc =
  module
    e_impl :: Reg Double <- mkReg 0
    interface
      e = e_impl
      trig =
        $display "Hello, World!"

mkTop :: Module Empty
mkTop = mkBug bugIfc
$ runghc Bug.hs
$ bsc -sim -g mkTop -u Top.bs
checking package dependencies
compiling Top.bs
Warning: "Top.bs", line 11, column 4: (T0054)
  Field not defined: `e'
code generation for mkTop starts
Error: "FloatingPoint.bsv", line 948, column 86: (S0015)
  Bluespec evaluation-time error: Unordered comparison of type
  `FloatingPoint'.
  During elaboration of the explicit condition of rule `trig' at "Bug.bs",
  line 18, column 8.
  During elaboration of `mkTop' at "Top.bs", line 15, column 0.

The same problem arises if you use (<=), (>), or (>=) instead of (<).

The culprit is this part of the generated Bug.bs file:

mkBug :: Prelude.Module BugIfc -> Prelude.Module Prelude.Empty;
mkBug ifcMod = 
    module {
      ifc <- ifcMod;
      let { trig_guard :: Prelude.Bool;
            trig_guard =  (Prelude.<) (0.0::Double) ifc.e._read; };
      rules {

        "trig":  when trig_guard   ==> ifc.trig
      }
    }

Here, we are using Bluespec's (<) function to compare the value of ifc.e._read, a Double value that comes from a register. This is bad, as Bluespec's (<) function is partial and will error on NaN values, resulting in the evaulation-time error seen above. See https://github.com/B-Lang-org/bsc/discussions/711 for more information on why this happens.

The solution is to not translate the Copilot expression x < y to the Bluespec expression x < y. Instead, we should translate it to something like compareFP x y == LT (where compareFP is defined here). Unlike (<) and friends, compareFP is total and will not error if one of the inputs is a NaN value.