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

exception :verilog and :vhdl #1648

Open
postoroniy opened this issue Feb 5, 2021 · 38 comments
Open

exception :verilog and :vhdl #1648

postoroniy opened this issue Feb 5, 2021 · 38 comments
Labels

Comments

@postoroniy
Copy link

postoroniy commented Feb 5, 2021

clash hash 833ac34

Can't provide whole source.
but suspecting problem in usage clash.
I need to calculate filter coefficients( function is designed and works as expected in Haskell Prelude)
but attempt to use it I got exception below. Can you guys point me to right direction? how to calc coefficients in runtime and feed them into topEntity?

for :verilog option

GHC: Parsing and optimising modules took: 2.329s
GHC: Loading external modules from interface files took: 0.000s
GHC: Parsing annotations took: 0.000s
Clash: Parsing and compiling primitives took 0.156s
GHC+Clash: Loading modules cumulatively took 2.807s
Clash: Compiling Main.topEntity
Clash.Normalize.Transformations(441): InlineNonRep: c$Main.topEntity_go[574466] already inlined 20 times in:c$Main.topEntity_go[590593]
Type of the subject is: GHC.Types.[][3674937295934324784] Main.Myriota_rational[8214565720324352836]
Function c$Main.topEntity_go[590593] will not reach a normal form, and compilation might fail.
Run with '-fclash-inline-limit=N' to increase the inlining limit to N.
Clash.Normalize.Transformations(441): InlineNonRep: c$Main.topEntity_go[574466] already inlined 20 times in:c$Main.topEntity_go[590593]
Type of the subject is: GHC.Types.[][3674937295934324784] Main.Myriota_rational[8214565720324352836]
Function c$Main.topEntity_go[590593] will not reach a normal form, and compilation might fail.
Run with '-fclash-inline-limit=N' to increase the inlining limit to N.
Clash.Normalize.Transformations(441): InlineNonRep: c$Main.topEntity_go[574466] already inlined 20 times in:c$Main.topEntity_go[590593]
Type of the subject is: GHC.Types.[][3674937295934324784] Main.Myriota_rational[8214565720324352836]
Function c$Main.topEntity_go[590593] will not reach a normal form, and compilation might fail.
Run with '-fclash-inline-limit=N' to increase the inlining limit to N.
Clash.Normalize.Transformations(441): InlineNonRep: c$Main.topEntity_go[574466] already inlined 20 times in:c$Main.topEntity_go[590593]
Type of the subject is: GHC.Types.[][3674937295934324784] Main.Myriota_rational[8214565720324352836]
Function c$Main.topEntity_go[590593] will not reach a normal form, and compilation might fail.
Run with '-fclash-inline-limit=N' to increase the inlining limit to N.
[1 of 1] Compiling Main             ( Resampler_ip.hs, interpreted )
Ok, one module loaded.
*** Exception: Evaluator.instantiate: Not a tylambda: PrimVal (PrimInfo {primName = "Control.Exception.Base.absentError", primType = ForAllTy (TyVar {varName = Name {nameSort = User, nameOcc = "a", nameUniq = 3530822107858468865, nameLoc = UnhelpfulSpan "<no location info>"}, varUniq = 3530822107858468865, varType = AppTy (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Prim.TYPE", nameUniq = 3674937295934324912, nameLoc = UnhelpfulSpan "<no location info>"}))) (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Types.LiftedRep", nameUniq = 3891110078048108766, nameLoc = UnhelpfulSpan "<no location info>"})))}) (AppTy (AppTy (ConstTy Arrow) (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Prim.Addr#", nameUniq = 3674937295934324738, nameLoc = UnhelpfulSpan "<no location info>"})))) (VarTy (TyVar {varName = Name {nameSort = User, nameOcc = "a", nameUniq = 3530822107858468865, nameLoc = UnhelpfulSpan "<no location info>"}, varUniq = 3530822107858468865, varType = AppTy (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Prim.TYPE", nameUniq = 3674937295934324912, nameLoc = UnhelpfulSpan "<no location info>"}))) (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Types.LiftedRep", nameUniq = 3891110078048108766, nameLoc = UnhelpfulSpan "<no location info>"})))}))), primWorkInfo = WorkConstant, primMultiResult = SingleResult}) [ForAllTy (TyVar {varName = Name {nameSort = User, nameOcc = "a", nameUniq = 8214565720324547887, nameLoc = UnhelpfulSpan "<no location info>"}, varUniq = 8214565720324547887, varType = AppTy (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Prim.TYPE", nameUniq = 3674937295934324912, nameLoc = UnhelpfulSpan "<no location info>"}))) (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Types.LiftedRep", nameUniq = 3891110078048108766, nameLoc = UnhelpfulSpan "<no location info>"})))}) (VarTy (TyVar {varName = Name {nameSort = User, nameOcc = "a", nameUniq = 8214565720324547887, nameLoc = UnhelpfulSpan "<no location info>"}, varUniq = 8214565720324547887, varType = AppTy (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Prim.TYPE", nameUniq = 3674937295934324912, nameLoc = UnhelpfulSpan "<no location info>"}))) (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Types.LiftedRep", nameUniq = 3891110078048108766, nameLoc = UnhelpfulSpan "<no location info>"})))}))] [Lit (StringLiteral "no_unfolding negIndex")]
CallStack (from HasCallStack):
  error, called at src-ghc/Clash/GHC/Evaluator.hs:331:23 in clash-ghc-1.3.0-inplace:Clash.GHC.Evaluator

for :vhdl option

GHC: Parsing and optimising modules took: 2.303s
GHC: Loading external modules from interface files took: 0.000s
GHC: Parsing annotations took: 0.000s
Clash: Parsing and compiling primitives took 0.138s
GHC+Clash: Loading modules cumulatively took 3.135s
Clash: Compiling Main.topEntity
Clash.Normalize.Transformations(441): InlineNonRep: c$Main.topEntity_go[2] already inlined 20 times in:c$Main.topEntity_go[16129]
Type of the subject is: GHC.Types.[][3674937295934324784] Main.Myriota_rational[8214565720324073827]
Function c$Main.topEntity_go[16129] will not reach a normal form, and compilation might fail.
Run with '-fclash-inline-limit=N' to increase the inlining limit to N.
Clash.Normalize.Transformations(441): InlineNonRep: c$Main.topEntity_go[2] already inlined 20 times in:c$Main.topEntity_go[16129]
Type of the subject is: GHC.Types.[][3674937295934324784] Main.Myriota_rational[8214565720324073827]
Function c$Main.topEntity_go[16129] will not reach a normal form, and compilation might fail.
Run with '-fclash-inline-limit=N' to increase the inlining limit to N.
Clash.Normalize.Transformations(441): InlineNonRep: c$Main.topEntity_go[2] already inlined 20 times in:c$Main.topEntity_go[16129]
Type of the subject is: GHC.Types.[][3674937295934324784] Main.Myriota_rational[8214565720324073827]
Function c$Main.topEntity_go[16129] will not reach a normal form, and compilation might fail.
Run with '-fclash-inline-limit=N' to increase the inlining limit to N.
Clash.Normalize.Transformations(441): InlineNonRep: c$Main.topEntity_go[2] already inlined 20 times in:c$Main.topEntity_go[16129]
Type of the subject is: GHC.Types.[][3674937295934324784] Main.Myriota_rational[8214565720324073827]
Function c$Main.topEntity_go[16129] will not reach a normal form, and compilation might fail.
Run with '-fclash-inline-limit=N' to increase the inlining limit to N.
*** Exception: Evaluator.instantiate: Not a tylambda: PrimVal (PrimInfo {primName = "Control.Exception.Base.absentError", primType = ForAllTy (TyVar {varName = Name {nameSort = User, nameOcc = "a", nameUniq = 3530822107858468865, nameLoc = UnhelpfulSpan "<no location info>"}, varUniq = 3530822107858468865, varType = AppTy (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Prim.TYPE", nameUniq = 3674937295934324912, nameLoc = UnhelpfulSpan "<no location info>"}))) (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Types.LiftedRep", nameUniq = 3891110078048108766, nameLoc = UnhelpfulSpan "<no location info>"})))}) (AppTy (AppTy (ConstTy Arrow) (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Prim.Addr#", nameUniq = 3674937295934324738, nameLoc = UnhelpfulSpan "<no location info>"})))) (VarTy (TyVar {varName = Name {nameSort = User, nameOcc = "a", nameUniq = 3530822107858468865, nameLoc = UnhelpfulSpan "<no location info>"}, varUniq = 3530822107858468865, varType = AppTy (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Prim.TYPE", nameUniq = 3674937295934324912, nameLoc = UnhelpfulSpan "<no location info>"}))) (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Types.LiftedRep", nameUniq = 3891110078048108766, nameLoc = UnhelpfulSpan "<no location info>"})))}))), primWorkInfo = WorkConstant, primMultiResult = SingleResult}) [ForAllTy (TyVar {varName = Name {nameSort = User, nameOcc = "a", nameUniq = 8214565720324268878, nameLoc = UnhelpfulSpan "<no location info>"}, varUniq = 8214565720324268878, varType = AppTy (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Prim.TYPE", nameUniq = 3674937295934324912, nameLoc = UnhelpfulSpan "<no location info>"}))) (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Types.LiftedRep", nameUniq = 3891110078048108766, nameLoc = UnhelpfulSpan "<no location info>"})))}) (VarTy (TyVar {varName = Name {nameSort = User, nameOcc = "a", nameUniq = 8214565720324268878, nameLoc = UnhelpfulSpan "<no location info>"}, varUniq = 8214565720324268878, varType = AppTy (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Prim.TYPE", nameUniq = 3674937295934324912, nameLoc = UnhelpfulSpan "<no location info>"}))) (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Types.LiftedRep", nameUniq = 3891110078048108766, nameLoc = UnhelpfulSpan "<no location info>"})))}))] [Lit (StringLiteral "no_unfolding negIndex")]
CallStack (from HasCallStack):
  error, called at src-ghc/Clash/GHC/Evaluator.hs:331:23 in clash-ghc-1.3.0-inplace:Clash.GHC.Evaluator

@alex-mckenna
Copy link
Contributor

Can't provide whole source.

Is there any way you could provide a MWE that you can share? There may be a simple source level fix to the problem.

@postoroniy
Copy link
Author

will try..
atm I fixed it by using ListToVecTH and placing generated data in list, which is nasty..
Also, initial thoughts - clash should not give exception

@christiaanb christiaanb added the bug label Feb 5, 2021
@christiaanb
Copy link
Member

Okay, we know what the bug is. The compile time evaluator never took impredicative instantiation into account. That is, given:

absentError :: forall a . String -> a

we never took into account that a could be instantiated with another forall a . ... which is what's happening here:

absentError @(forall a . a) "no_unfolding negIndex" @Something

We mistakenly assumed that for "primitives" such as absentError that all type arguments come before the term arguments (the "no_unfolding negIndex" string literal in this case). But when primitives are type-instantiated impredicatively, then type arguments could follow the term arguments.

NB: We're not saying that you're using impredicative types and that you shouldn't do that. It's most likely that GHC is actually using them under the hood somewhere, i.e. you're most likely not using impredicative types explicitly at all. Regardless, impredicative types are something that Clash should just handle properly.

@postoroniy
Copy link
Author

@christiaanb @alex-mckenna different question - How I can use data from imported module and feed it to "top_entity`? using list is not great idea (eg listToVecTH)

@christiaanb
Copy link
Member

You mean as a work-around for this issue?

@postoroniy
Copy link
Author

as I understand only ListToVecTH is available. or I missed something similar/proper/better?

@postoroniy
Copy link
Author

work around is only ListToVecTH atm. but I'd prefer to pack data in custom data type rather then unpack individual elements (of the same type!) from List and convert to Vec

@alex-mckenna
Copy link
Contributor

You can define a custom type and pass that around normally. If you also derive BitPack for it then you're free to convert that type to/from BitVectors of the same width. If you want a vector though, at some point you'll have to create a vector...

Would it be possible to share just the go function in your topEntity? That seems to be the cause of the problem from the compiler output. It might be possible to recreate the error without needing to know how you're producing / using the data given to go

@christiaanb
Copy link
Member

Ah, another work-around would be to use lift and TH. That is wherever you used to have:

g (f 4)

where f 4 is basically a "constant" (but not a "literal"), you can do:

g $(lift (f 4))

This will use TH to force that "constant", f 4, to become a "literal".

To give a concrete example, let's say that;

f :: Int -> Bool
f 0 = False
f _ = True

then lift and TH will rewrite:

g $(lift (f 4))

into

g True

at TH compile-time, which happens before Clash does it compile-time evaluation

@postoroniy
Copy link
Author

@christiaanb Cool!...and Can I pass "4" into that example as a parameter from command line?
@alex-mckenna I'll do it in next hour :)

@christiaanb
Copy link
Member

Ehm.... yes... using the C pre processor....

{-# LANGUAGE CPP #-}

g $(lift (f MY_FANCY_NUMER))

and then call Clash with:

clash -DMY_FANCY_NUMBER=4

But this is veering into hacky ducktape monkeypatch territory...

@postoroniy
Copy link
Author

well. The idea to have same source(in clash) but able to generate different hardware without any extra tool(s) eg. not use matlab/python/anything else/ or without editing external file(s)
kind of surprise that is hacky way :(

@christiaanb
Copy link
Member

I see your point... you could also do:

versionX :: ...
versionX = ....  $(lift (f 3)) ...
{-# ANN versionX (defSyn "versionX") #-} 

versionY :: ...
versionY = ....  $(lift (f 8)) ...
{-# ANN versionY (defSyn "versionY") #-}

and then call Clash with:

clash -main-is versionX

to compile versionX and do:

clash -main-is versionY

to compile versionY

@postoroniy
Copy link
Author

@christiaanb I am afraid I can't use this approach as I need to generate a bunch of different Filters with different sample rates and frequencies responses, where for example the number of coefficients would depend on pass band, stop band and other paramaters.

@alex-mckenna
Copy link
Contributor

You could create a generic one, then have clash produce entities for specialised instantiations. i.e.

myGenericFilter :: <some type>

filterConfigA = myGenericFilter <specialize arguments>
filterConfigB = myGenericFilter <specialize arguments>

if you add a Synthesize Annotation then Clash will generate different HDL files for the different specialised versions

@alex-mckenna
Copy link
Contributor

alex-mckenna commented Feb 5, 2021

An example:

-- some generic definition of an adder, polymorphic over number repr and width
adder :: forall (repr :: Nat -> Type) (width :: Nat). repr width -> repr width -> repr width
adder = -- some definition

{-# ANN unsigned4Adder (Synthesize { t_name = "unsigned_4_adder"
                                   , t_inputs = [PortName "x", PortName "y"]
                                   , t_output = PortName "r"
                                   }) #-}
unsigned4Adder :: Unsigned 4 -> Unsigned 4 -> Unsigned 4
unsigned4Adder = adder

would give you a VHDL module something similar to

entity unsigned_4_adder is
  port ( x, y : in std_logic_vector(3 downto 0);
           r  : out std_logic_vector(3 downto 0)
  );
end unsigned_4_adder;

(assuming I haven't made a mistake here)

@christiaanb
Copy link
Member

@postoroniy Do you need a general solution that will work once this issue #1648 is fixed? or a work-around until this issue #1648 is fixed?

@postoroniy
Copy link
Author

@alex-mckenna thanks for given examples, but I can't see how I can avoid to have different ANNs (not adding them at all) and rather to use a parameter from command line.
what I've done already is generating coefficients(by haskell in clash !) and use them via ListToVecTH and then parsing vector into specific things such as accamulator width, number of taps and coefficients by themselves.

@postoroniy
Copy link
Author

@christiaanb I have workaround as I said, but I need the general and concrete solution for things I mentioned above :)

@christiaanb
Copy link
Member

Another option is to have the following directory structure:

src/MyCircuit.hs
srcX/Coeffs.hs
srcY/Coeffs.hs

and then have src/MyCircuit.hs be:

module MyCircuit where

import Coeffs

topEntity = g coeffs

and have srcX/Coeffs.hs be:

module Coeffs where

coefss = $(lift (f 4))

and have srcY/Coeffs.hs be::

module Coeffs where

coefss = $(lift (f 8))

and then call Clash with:

clash -isrc -isrcX -outputdir versionX MyCircuit

to generate the hardware with the "X" set of coefficients, and output the HDL in the versionX directory

and do:

clash -isrc -isrcY -outputdir versionY MyCircuit

to generate the hardware with the "Y" set of coefficients, and output the HDL in the versionY directory

@postoroniy
Copy link
Author

@christiaanb thanks, as I can see only extra file.. will think about it
@alex-mckenna I can't make smaller and will try later on
thanks for quick reply guys!
Appreciated!

@DigitalBrains1
Copy link
Member

well. The idea to have same source(in clash) but able to generate different hardware without any extra tool(s) eg. not use matlab/python/anything else/ or without editing external file(s)
kind of surprise that is hacky way :(

I know some people approach their Clash hardware designs with a two-phase compilation process. The first compilation generates .hs files and the second compilation uses both static and generated .hs files to generate the hardware. It's not precisely what you're looking for, but it does offer the option to just pass regular command line arguments to the first phase, which then alters the generated .hs files.

@postoroniy
Copy link
Author

@DigitalBrains1
that's understandable and let's say... at this stage I don't see that clash is attractive solution as tool for hardware(!) engineers. yes it can do some for hardware...
But! already tried nmigen and it's much easier! can be the complete solution and replacement for matlab at some degree.

@christiaanb
Copy link
Member

Yes, design is a creative process, so some approaches click well with some, while other approaches click better with others. So it might be that nmigen works better for the creation of some hardware designs, and Clash works better on other hardware designs.

With Clash, we allow the designer to use native Haskell features, such as e.g case-expressions, to describe the switching behavior of the circuit. With nmigen, you have to use its with m.Switch(xxx): functions/combinators to create circuits with switching behavior. The benefits of Clash' approach is that you can use things like the Maybe type or even more advanced sum-of-product types to describe the functionality of the circuit. I haven't tried this with nmigen, but definitely with Clash you will also get the source location when the case-expressions is non-exhaustive (again, I haven't used nmigen alot, perhaps it can also print stack traces to error/warn about non-exhaustive with m.Switch(xxx) usage).

Now, the trade-off that we had to make for this is that in order to turn a Clash/Haskell description into HDL code is that we have to do a lot of static analysis. This makes it a lot harder to have the "shape" of a circuit depends on non-source inputs, e.g. data-files that you read from disks, or arguments from stdin. All of these things are very straightforward in nmigen because nmigen is implemented as an embedded DSL. If you want to see a modern embedded DSL approach to circuit design using Haskell, I would suggest you take a look at: https://github.com/blarney-lang/blarney

Again, I genuinely believe that there are designs where being able to use sum-of-product types, pattern matching, and other Haskell features natively in the behavior of your circuits make Clash "better" than other approaches. On the other hand, if you're not using any of those features, I can definitely see an EDSL approach, whether it's blarney or nmigen, being more productive.

@postoroniy
Copy link
Author

postoroniy commented Feb 5, 2021

@christiaanb I didn't mean to blame clash and you guys, I've just noted that clash as such is not simple to use and has some solutions/workarounds which are neither obvious nor in tutorial/FAQ( is it true?). And may be "2 files" solution is better for my case.
But the real example like above - generate constants on-fly and plug them to creating "hw" , where constants are depending on parameter(s) passed from command line is what I am very keen!
Another(very different) thing -> I am still can't use repl in atom (ghc-mod) with clash ( I just gave up)
while it did work well on python+nmigen.
The good thing about clash - generation time(verilog file) for my case ~7 sec vs ~60 sec in nmigen( rtlil file).
if the time would be the same I won't continue :)
I had look on blarney very briefly and decided to not evaluate at this stage..as it's better to check bluespec (classic+verilog)

@alex-mckenna
Copy link
Contributor

I am still can't use repl in atom (ghc-mod) with clash ( I just gave up)

AFAIK ghc-mod is abandoned at this point. Either way, I know a few people have had success using haskell-language-server and VSCode (with the Haskell plugin) for their development environment.

@postoroniy
Copy link
Author

@DigitalBrains1 thanks for your "the 2 file" solution :)

@postoroniy
Copy link
Author

@alex-mckenna yep that's what I found too, but I burnt too much time on it :), my fault...and just imagine to have this things/thoughts/recommendations in FAQ :)))

@postoroniy
Copy link
Author

postoroniy commented Feb 5, 2021

@alex-mckenna
ok...minimal example and I think it failed only when list of list is used, if just list it's ok!
file TestLib.hs

module TestLib (
  TestT(..)
  ,myConfig
  ,myConfigC
)where

import Prelude
import GHC.Generics

data TestT0 = TestT0{
  p :: Integer,
  q :: Integer
} deriving (Generic, Show, Eq, Ord)

data TestT = TestT{
  testT0 :: TestT0,
  coeff_list:: [[Integer]]
} deriving (Generic, Show, Eq, Ord)


myConfig:: Integer ->Integer-> TestT
myConfig i j = o
  where
    iT = TestT0 j (j+1)
    o = TestT iT [[j+1,j+2],[j+30,j+40]]

myConfigC = myConfig 100 99

file test.hs @alex-mckenna sorry before here was wrong source and I had to fix it

import TestLib
import Clash.Prelude
import qualified Data.List as L

topEntity:: Integer->Integer->Integer
topEntity i j = o
  where
    coeff_l = coeff_list(myConfigC)
    coeff1 = coeff_l L.!!0
    o = coeff1 L.!! 0

only tested on clashi
exception is

*** Exception: Evaluator.instantiate: Not a tylambda: PrimVal (PrimInfo {primName = "Control.Exception.Base.absentError", primType = ForAllTy (TyVar {varName = Name {nameSort = User, nameOcc = "a", nameUniq = 3530822107858468865, nameLoc = UnhelpfulSpan "<no location info>"}, varUniq = 3530822107858468865, varType = AppTy (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Prim.TYPE", nameUniq = 3674937295934324912, nameLoc = UnhelpfulSpan "<no location info>"}))) (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Types.LiftedRep", nameUniq = 3891110078048108766, nameLoc = UnhelpfulSpan "<no location info>"})))}) (AppTy (AppTy (ConstTy Arrow) (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Prim.Addr#", nameUniq = 3674937295934324738, nameLoc = UnhelpfulSpan "<no location info>"})))) (VarTy (TyVar {varName = Name {nameSort = User, nameOcc = "a", nameUniq = 3530822107858468865, nameLoc = UnhelpfulSpan "<no location info>"}, varUniq = 3530822107858468865, varType = AppTy (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Prim.TYPE", nameUniq = 3674937295934324912, nameLoc = UnhelpfulSpan "<no location info>"}))) (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Types.LiftedRep", nameUniq = 3891110078048108766, nameLoc = UnhelpfulSpan "<no location info>"})))}))), primWorkInfo = WorkConstant, primMultiResult = SingleResult}) [ForAllTy (TyVar {varName = Name {nameSort = User, nameOcc = "a", nameUniq = 8214565720325334921, nameLoc = UnhelpfulSpan "<no location info>"}, varUniq = 8214565720325334921, varType = AppTy (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Prim.TYPE", nameUniq = 3674937295934324912, nameLoc = UnhelpfulSpan "<no location info>"}))) (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Types.LiftedRep", nameUniq = 3891110078048108766, nameLoc = UnhelpfulSpan "<no location info>"})))}) (VarTy (TyVar {varName = Name {nameSort = User, nameOcc = "a", nameUniq = 8214565720325334921, nameLoc = UnhelpfulSpan "<no location info>"}, varUniq = 8214565720325334921, varType = AppTy (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Prim.TYPE", nameUniq = 3674937295934324912, nameLoc = UnhelpfulSpan "<no location info>"}))) (ConstTy (TyCon (Name {nameSort = User, nameOcc = "GHC.Types.LiftedRep", nameUniq = 3891110078048108766, nameLoc = UnhelpfulSpan "<no location info>"})))}))] [Lit (StringLiteral "no_unfolding negIndex")]
CallStack (from HasCallStack):
  error, called at src-ghc/Clash/GHC/Evaluator.hs:331:23 in clash-ghc-1.3.0-inplace:Clash.GHC.Evaluator

@basile-henry
Copy link
Collaborator

I am wondering how you are using the vector that you generate?

If by any chance it is/could be used to initialize a ROM/RAM, maybe you could use the version of the primitive that uses a memory initialization file: https://hackage.haskell.org/package/clash-prelude-1.2.5/docs/Clash-Prelude-ROM-File.html#v:romFile

If that is the case, you should be able to generate the file as a separate step using any language. This has the added benefit that you can re-generate the ROM file even after Clash compilation as the generated Verilog/VHDL references that file.

@postoroniy
Copy link
Author

postoroniy commented Feb 5, 2021

@basile-henry Generated vector is in fact coefficients for FIR filter.
That's it, they are plugged into DSPs/ multipliers as the constants.
And again I'd like to stay in one "language" and would love to use only one source file. As two file solution still ok though..just more things to do and keep in mind.
so primitive is not good idea to use as a filter can be different eg. taps/interpolation/decimation and etc

@alex-mckenna
Copy link
Contributor

It may not be a complete solution for your entire program, but this commit succeeds in building the MWE

@alex-mckenna
Copy link
Contributor

It does nothing for the not a tylam error, but if the commit works for your program I'll merge it and fix the other issue in a separate PR

@postoroniy
Copy link
Author

@alex-mckenna I don't see exception for given example :) and created verilog is what I'd expect to see

@basile-henry
Copy link
Collaborator

If you need to be sure that the constants are fully inlined close to the DSPs this might not be the best approach. But just to comment on this:

I'd like to stay in one "language" and would love to use only one source file.

That is possible with the approach I mentioned!

{-# LANGUAGE DataKinds         #-}
{-# LANGUAGE NoImplicitPrelude #-}

import           Clash.Prelude
import           Numeric
import qualified Prelude
import           System.Environment

myRomPath :: FilePath
myRomPath = "my_rom.bin"

------------------------------
-- Runtime generation of ROM

main :: IO ()
main = do
  x <- Prelude.read . Prelude.head <$> getArgs

  listToFile myRomPath [ x * i | i <- [0..15] ]

listToFile :: FilePath -> [Unsigned 8] -> IO ()
listToFile path content = do
  let binRepr x = showIntAtBase 2 (\b ->
        case b of
          0 -> '0'
          1 -> '1'
        )
        x
        ""

      contentString = unlines (fmap binRepr content)

  Prelude.writeFile path contentString

------------------------------
-- Hardware description

topEntity :: Unsigned 4 -> Unsigned 8
topEntity = unpack . asyncRomFile d4 myRomPath

Then you can generate the ROM with:

runhaskell Example.hs 3

(where 3 is an arg that is parsed by main)

And generate Verilog with:

clash -outputdir out --verilog Example.hs

You can still simulate the top entity in plain Haskell, for example with clashi/ghci:

➜ clashi -e "topEntity 2" Example.hs
6

@christiaanb
Copy link
Member

I think what we should learn from this (and probably most of us knew this already) is that Clash would really benefit from a lot more ("soft") documentation in the form of FAQs, HowTo's and "best" practices

@postoroniy
Copy link
Author

fixed on master at 70718d0
can't reproduce
closing

@alex-mckenna
Copy link
Contributor

Reopening for now: this issue mentions some other things which should be done / have new issues made for:

  • make old evaluator able to handle impredicatie instantiation (cause of Not a tylambda error)
  • improve soft documentation for clash (FAQs, best practices, clash "design patterns")

@alex-mckenna alex-mckenna reopened this Feb 8, 2021
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

5 participants