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

Internal error after normalization when synthesising vhdl in 1.6.2 #2149

Closed
pbreuer opened this issue Mar 26, 2022 · 26 comments · Fixed by #2157
Closed

Internal error after normalization when synthesising vhdl in 1.6.2 #2149

pbreuer opened this issue Mar 26, 2022 · 26 comments · Fixed by #2157

Comments

@pbreuer
Copy link

pbreuer commented Mar 26, 2022

This follows on from issue #2126 . Christiaan's patch cured that bug, but a different issue arises later on in the vhdl synthesis with 1.6.2 (works fine in some 1.5.0pre) that is also deadly. I presume it happens during netlist generation, but it may be just prior ... or after. Christiaan asked me to re-run with a patch that produces more error reporting, and this is the output:

% clash -XCPP -fconstraint-solver-iterations=0 -package silently -fclash-spec-limit=80 -fclash-inline-limit=80 --vhdl Test/Trace.hs &
[1] 19000
GHC: Setting up GHC took: 0.780s
GHC: Compiling and loading modules took: 25m46s
Clash: Parsing and compiling primitives took 2.185s
GHC+Clash: Loading modules cumulatively took 2h13m39s
Clash: Compiling Test.Trace.kpu_test32
Clash: Normalization took 1d3h23m26s
[WARNING] Dubious primitive instantiation for Clash.Signal.Internal.clockGen: Clash.Signal.Internal.clockGen is not synthesizable! (disable with -fclash-no-prim-warn)
[WARNING] Dubious primitive instantiation for GHC.Integer.Type.integerToInt: GHC.Integer.Type.integerToInt: Integers are dynamically sized in simulation, but fixed-length after synthesis. Use carefully. (disable with -fclash-no-prim-warn)
[WARNING] Dubious primitive instantiation for GHC.Integer.Type.eqInteger#: GHC.Integer.Type.eqInteger#: Integers are dynamically sized in simulation, but fixed-length after synthesis. Use carefully. (disable with -fclash-no-prim-warn)
[WARNING] Dubious primitive instantiation for GHC.Integer.Type.integerToWord: GHC.Integer.Type.integerToWord: Integers are dynamically sized in simulation, but fixed-length after synthesis. Use carefully. (disable with -fclash-no-prim-warn)
[WARNING] Dubious primitive instantiation for GHC.Integer.Type.wordToInteger: GHC.Integer.Type.wordToInteger: Integers are dynamically sized in simulation, but fixed-length after synthesis. Use carefully. (disable with -fclash-no-prim-warn)
[WARNING] Dubious primitive instantiation for GHC.Integer.Type.smallInteger: GHC.Integer.Type.smallInteger: Integers are dynamically sized in simulation, but fixed-length after synthesis. Use carefully. (disable with -fclash-no-prim-warn)

: error:
Clash error call:
[(Literal (Just (Unsigned 64,64)) (NumLit 32),Unsigned 64,True),(BlackBoxE "GHC.Integer.Type.wordToInteger" [] [] [] BBTemplate [Text "signed(std_logic_vector(",Arg 0,Text "))"] (Context {bbName = "GHC.Integer.Type.wordToInteger", bbResults = [(Identifier (RawIdentifier "result_6" Nothing []) Nothing,Signed 64)], bbInputs = [(Identifier (RawIdentifier "\x#\" Nothing []) Nothing,Unsigned 64,False)], bbFunctions = fromList [], bbQsysIncName = [], bbLevel = 4, bbCompName = UniqueIdentifier {i_baseName = "Test_Trace_kpu_test32_crypto_decryptA", i_baseNameCaseFold = "test_trace_kpu_test32_crypto_decrypta", i_extensionsRev = [], i_idType = Basic, i_hdl = VHDL, i_provenance = []}, bbCtxName = Just "$s$fFiniteBitsSigned_$s$fBitsSigned_$s$fBitsSigned6_result_6"}) True,Signed 64,False)]
CallStack (from HasCallStack):
error, called at src/Clash/Primitives/Sized/Signed.hs:29:13 in clash-lib-1.6.2-9DlpiycIEEQav411QKO6Y:Clash.Primitives.Sized.Signed

[1] Exit 1 clash -XCPP -fconstraint-solver-iterations=0 -package silently -fclash-spec-limit=80 -fclash-inline-limit=80 --vhdl Test/Trace.hs

That is from this line in the patched source (I'll fix the indenting later):

fromIntegerTF :: TemplateFunction
fromIntegerTF = TemplateFunction used valid fromIntegerTFTemplate
where
used = [0,1]
valid bbCtx = case bbInputs bbCtx of
[(Literal _ (NumLit ),,True),v] -> case v of
(Identifier {}, Signed {}, False) -> True
(Literal {}, _, True) -> True
_ -> error (show (bbInputs bbCtx)) <--------- HERE
_ -> error (show (bbInputs bbCtx))

fromIntegerTFTemplate
:: Backend s
=> BlackBoxContext
-> State s Doc
fromIntegerTFTemplate bbCtx = getAp $ do
let [(Literal _ (NumLit sz),,), (i@(Identifier iV m), Signed szI, _)] = bbIn
puts bbCtx
case compare sz (toInteger szI) of
LT -> let sl = Sliced (Signed szI,fromInteger sz-1,0)
m1 = Just (maybe sl (Nested sl) m)
in expr False (Identifier iV m1)
EQ -> expr False i
GT -> "resize" <> tupled (sequenceA [expr False i
,expr False (Literal Nothing (NumLit sz)
)])

Regards

PTB

@leonschoorl
Copy link
Member

I think this is about what's happening:

topEntity :: Word -> Signed 32
topEntity = fromIntegral

Which results in basically the same error:

Main.topEntity :: GHC.Types.Word -> Clash.Sized.Internal.Signed.Signed 32 after flattenExpr:

λ(c$arg :: GHC.Types.Word) ->
letrec
  x# :: GHC.Prim.Word#
  = case c$arg[LocalId] of
      GHC.Types.W# (c$sel :: GHC.Prim.Word#) ->
        c$sel[LocalId]
  result :: Clash.Sized.Internal.Signed.Signed 32
  = Clash.Sized.Internal.Signed.fromInteger# @32 32
      <prefixName>"$fIntegralWord_$ctoInteger"
      (GHC.Integer.Type.wordToInteger x#[LocalId])
in result[LocalId]

Clash: Applied 14 transformations
Clash: Normalization took 0.003s
[WARNING] Dubious primitive instantiation for GHC.Integer.Type.wordToInteger: GHC.Integer.Type.wordToInteger: Integers are dynamically sized in simulation, but fixed-length after synthesis. Use carefully. (disable with -fclash-no-prim-warn)

<no location info>: error:
    Clash error call:
    [(Literal (Just (Unsigned 64,64)) (NumLit 32),Unsigned 64,True),(BlackBoxE "GHC.Integer.Type.wordToInteger" [] [] [] (BBTemplate [Text "signed(std_logic_vector(",Arg 0,Text "))"]) (Context {bbName = "GHC.Integer.Type.wordToInteger", bbResults = [(Identifier (RawIdentifier "result" Nothing [("unsafeMake",SrcLoc {srcLocPackage = "clash-lib-1.7.0-inplace", srcLocModule = "Clash.Netlist.Util", srcLocFile = "src/Clash/Netlist/Util.hs", srcLocStartLine = 1003, srcLocStartCol = 17, srcLocEndLine = 1003, srcLocEndCol = 30})]) Nothing,Signed 64)], bbInputs = [(Identifier (RawIdentifier "\\x#\\" Nothing [("unsafeMake",SrcLoc {srcLocPackage = "clash-lib-1.7.0-inplace", srcLocModule = "Clash.Netlist.Util", srcLocFile = "src/Clash/Netlist/Util.hs", srcLocStartLine = 1003, srcLocStartCol = 17, srcLocEndLine = 1003, srcLocEndCol = 30})]) Nothing,Unsigned 64,False)], bbFunctions = fromList [], bbQsysIncName = [], bbLevel = 0, bbCompName = UniqueIdentifier {i_baseName = "topEntity", i_baseNameCaseFold = "topentity", i_extensionsRev = [], i_idType = Basic, i_hdl = VHDL, i_provenance = [("make##",SrcLoc {srcLocPackage = "clash-lib-1.7.0-inplace", srcLocModule = "Clash.Netlist.Id.Internal", srcLocFile = "src/Clash/Netlist/Id/Internal.hs", srcLocStartLine = 129, srcLocStartCol = 9, srcLocEndLine = 129, srcLocEndCol = 73}),("make#",SrcLoc {srcLocPackage = "clash-lib-1.7.0-inplace", srcLocModule = "Clash.Netlist.Id.Internal", srcLocFile = "src/Clash/Netlist/Id/Internal.hs", srcLocStartLine = 133, srcLocStartCol = 18, srcLocEndLine = 133, srcLocEndCol = 27}),("makeBasic#",SrcLoc {srcLocPackage = "clash-lib-1.7.0-inplace", srcLocModule = "Clash.Netlist.Id", srcLocFile = "src/Clash/Netlist/Id.hs", srcLocStartLine = 179, srcLocStartCol = 32, srcLocEndLine = 179, srcLocEndCol = 42}),("makeBasic",SrcLoc {srcLocPackage = "clash-lib-1.7.0-inplace", srcLocModule = "Clash.Netlist", srcLocFile = "src/Clash/Netlist.hs", srcLocStartLine = 247, srcLocStartCol = 14, srcLocEndLine = 247, srcLocEndCol = 62})]}, bbCtxName = Just "$fIntegralWord_$ctoInteger_result"}) True,Signed 64,False)]
    CallStack (from HasCallStack):
      error, called at src/Clash/Primitives/Sized/Signed.hs:29:12 in clash-lib-1.7.0-inplace:Clash.Primitives.Sized.Signed

@pbreuer
Copy link
Author

pbreuer commented Mar 28, 2022

None of the errors occur in 1.5.0pre. I'll check that your example above causes no error there. Yes, that is so:

Test> :vhdl
[1 of 1] Compiling Test ( Test.hs, Test.o )
Ok, one module loaded.
GHC: Parsing and optimising modules took: 0.460s
GHC: Loading external modules from interface files took: 0.001s
GHC: Parsing annotations took: 0.001s
Clash: Parsing and compiling primitives took 0.250s
GHC+Clash: Loading modules cumulatively took 0.913s
Clash: Compiling Test.topEntity
Clash: Normalization took 0.003s
Clash: Netlist generation took 0.001s
Clash: Total compilation took 0.932s

But it doesn't look really likely to be relevant to me as everything in sight is a stream to stream function, including topEntity.

{-# ANN kpu_test32
(Synthesize { t_name = "KPU_Test"
, t_inputs = [ ]
, t_output = PortName "trace"
}) #-}

One thing I do notice is that the top level is a foo = bar ... where bar :: ....; bar = ... . I think there's a reason for that but I forget what. Something to do with specialization. It looks like a silly thing to do, but it is done, so presumably there is a reason.

Thanks for the suggestion.

PTB

I could try locating it by module, but it'll be a lot of work.

@pbreuer
Copy link
Author

pbreuer commented Mar 29, 2022

I would suggest varying the patch to not produce error but instead just carry on leaving some mark that I can look for in the eventual output, and hence locate where the cause of this is, hopefully.

@martijnbastiaan
Copy link
Member

I think this is about what's happening:

@leonschoorl Does this happen pre 1.5 too? If it doesn't, that's a pretty good indication this is @pbreuer's issue.

@pbreuer
Copy link
Author

pbreuer commented Mar 29, 2022

The only earlier version I have compiled up is 1.1.0 on a 32-bit machine. the 1.5.0pre I have is built July 7 2021. I can locate it more exactly if you point at a definitive changelog to look at. There is a CHANGELOG.md.save0 but that goes up to 1.4.2 only (May 18 2021).

I really don't recognize anything like that Word->Signed function at top level. It's all stream to stream. Of course there must be tons like that at a lower level, but I would expect it to be normalized out (my impression is that normalization is failing). Perhaps I should look for inadvertent NOINLINEs that magically were disregarded earlier ...

I can also try bisecting between versions to locate the problem. Meanwhile I'm begging for a patch that carries on regardless where before it errored, but leaves some recognizable mark in the final output.

Any suggestions?

On a strategic level, the vhdl produced by 1.5.0pre runs fine under simulation in ghdl, for thousands of cycles requiring about 100GB of RAM so there appears to be nothing wrong with what 1.5.0pre does.

Can you perhaps give me an idea of the abstract problem hinted at here? I may get an idea from that. Should 1.6.2 synthesise that Word->Signed function or shouldn't it? It looks synthesizable to me as it just the identity on 32 1-bit inputs! That is 32 straight through wires, or one straight-through 32-bit wide cable. Do you synthesize for straight stateless logic functions? I'm sorry - I don't know because everything I have written has clocks and state in.

Otherwise .. please send patch that leaves a mark in the vhdl output but carries on. I don't care what it does after that point. It could die, as far as I care for this purpose, so long as there is output to look at.

Regards

PTB

PS. I have just tried synthesizing that Word->Signed function under 1.1.0 and it works fine:

Clashi, version 1.1.0 (using clash-lib, version 1.1.0):
https://clash-lang.org/ :? for help
Loaded package environment from /usr/share/clash-lib/ghc.environment.i386-linux-8.6.5
Loaded package environment from /usr/share/clash-lib/ghc.environment.i386-linux-8.6.5
[1 of 1] Compiling Test ( Test.hs, interpreted )
Ok, one module loaded.
*Test> :vhdl
Loaded package environment from /usr/share/clash-lib/ghc.environment.i386-linux-8.6.5
[1 of 1] Compiling Test ( Test.hs, Test.o )
Ok, one module loaded.
Loaded package environment from /usr/share/clash-lib/ghc.environment.i386-linux-8.6.5
GHC: Parsing and optimising modules took: 2.884s
GHC: Loading external modules from interface files took: 0.207s
GHC: Parsing annotations took: 0.003s
Clash: Parsing and compiling primitives took 4.491s
GHC+Clash: Loading modules cumulatively took 9.069s
Clash: Compiling Test.topEntity
Clash: Applied 10 transformations
Clash: Normalisation took 0.099s
Clash: Netlist generation took 0.016s
Clash: Total compilation took 9.197s

So the problem with that (if it is a problem) appears to be in 1.6.2.

@martijnbastiaan
Copy link
Member

PS. I have just tried synthesizing that Word->Signed function under 1.1.0 and it works fine:

Thanks, that's very helpful! That's a pretty strong indicator that that's the root of the issue. I'm not sure how Clash could carry on after encountering this error, from the looks of it is simpler just fixing this bug than try to figure out how to carry on :-).

@pbreuer
Copy link
Author

pbreuer commented Mar 29, 2022

A patch that manages to carry on would treat the error case as though the case statement had been provided with the most trivial instance of a non-error case imaginable maybe involving some number like 0x12345678 that I could later search for. Or 0xdeaddead.

Regards

PTB

@pbreuer
Copy link
Author

pbreuer commented Mar 29, 2022

Actually we shouldn't have believed my success with 1.1.0 but further testing shows it was right, though I was wrong to say so. The issue is that was on a 32-bit machine so Word -> Signed 32 does not involve a size change whereas on a 64-bit machine it does. But I retried with Word -> Signed 16 and it still fine so the result should be believed.

On the 64-bit machine running 1.6.2 I have tried replacing fromIntegral for Word -> Signed 32 with unpack . resize . pack and it synthesizes fine.

I have also tried replacing Word -> Signed 32 with Word -> Signed 64 with fromIntegral still the implementation in 1.6.2 and that still errors.

So the problem seems (superficially) to be with fromIntegral in 1.6.2 .

There is precisely ONE use of fromIntegral in my 50,000 lines of code. I'll try replacing it and report.

@leonschoorl
Copy link
Member

It's caused by this VHDL specific primitive introduce in 2e4096a
Which was added somewhere between clash 1.4 and 1.6.

It requires its argument to be a Identifier, and fail for any other type of Expr.
Except for the special code path that handles Literal: #2131 (comment)
But all other kinds of Expr's fail.

If only he had an equivalent of ~VAR[n] for use in TemplateFunction, then we could easily force the argument to be an Identifier.

@pbreuer
Copy link
Author

pbreuer commented Mar 29, 2022

Can you do that by assigning a new identifier for the Expr argument of fromInteger, during the translation?

@martijnbastiaan
Copy link
Member

@pbreuer
Copy link
Author

pbreuer commented Mar 29, 2022

I've eliminated explicit uses of fromInteger in the code but something must remain because I'm still getting a (different) error at that same point. This time by sheer luck a NOINLINE indicates which module this is in and I should be able to narrow it down. It still seems to be unhappy about precisely wordToInteger :

: error:
Clash error call:
[(Literal (Just (Unsigned 64,64)) (NumLit 32),Unsigned 64,True),(BlackBoxE "
GHC.Integer.Type.wordToInteger" [] [] [] BBTemplate [Text "signed(std_logic_vector(",Arg 0,Text "))"] (Context {bbName = "GHC.Integer.Type.wordToInteger", bbResults = [(Identifier (RawIdentifier "result_6" Nothing []) Nothing,Signed 64)], bbInputs = [(Identifier (RawIdentifier "\x#\" Nothing []) Nothing,Unsigned 64,False)], bbFunctions = fromList [], bbQsysIncName = [], bbLevel = 4, bbCompName = UniqueIdentifier {i_baseName = "Test_Trace_kpu_test32_crypto_decryptA", i_baseNameCaseFold = "test_trace_kpu_test32_crypto_decrypta", i_extensionsRev = [], i_idType = Basic, i_hdl = VHDL, i_provenance = []}, bbCtxName = Just "$s$fFiniteBitsSigned_$s$fBitsSigned_$s$fBitsSigned6_result_6"}) True,Signed 64,False)]
CallStack (from HasCallStack):
error, called at src/Clash/Primitives/Sized/Signed.hs:29:13 in clash-lib-1.6.2-9DlpiycIEEQav411QKO6Y:Clash.Primitives.Sized.Signed

If we take the warnings as Gospel truth, it is worried about primitives for
GHC.Integer.Type.integerToInt
GHC.Integer.Type.eqInteger#
GHC.Integer.Type.integerToWord
GHC.Integer.Type.wordToInteger
GHC.Integer.Type.smallInteger

The warnings are all "Dubious primitive instantiation for ...: ...: Integers are dynamically sized in simulation, but fixed-length after synthesis. Use carefully"

Regards

PTB

@pbreuer
Copy link
Author

pbreuer commented Mar 31, 2022

(partially) reversing patch 2e4096a allowed vhdl synthesis to complete with 1.6.2.

GHC: Compiling and loading modules took: 9m0s
Clash: Parsing and compiling primitives took 0.967s
GHC+Clash: Loading modules cumulatively took 1h0m49s
Clash: Compiling Test.Trace.kpu_test32
Clash: Normalization took 2h0m14s
Clash: Netlist generation took 23.570s
Clash: Compiling Test.Trace.kpu_test32 took 2h3m25s
Clash: Total compilation took 3h4m15s

(I used a bigger computer than before! That is why it is about 10x faster).

The patch was introduced July 18 2021 and the working 1.5.0pre I have been using until 1.6.2 was built July 7, so by luck I happened to have more or less the last build without this particular bug, which explains some things.

The patch introduces Primitives/Sized/Signed.hs as backing for a change in prims/vhdl/Clash_Sized_Internal_Signed.primitives that modified the behaviour of Clash.Sized.Internal.Signed.fromInteger# . I don't understand that change (it's commented of course but the comment needs more to ping my understanding) but I reversed it. I was not able to drop Signed.hs as something else seems to reference it somewhere else in 1.6.2. I couldn't find what.

Yes, I made the reversal in the .primitives.yaml file instead of the .primitives file as those are no longer what are used nowadays, but that's minor.

(the semantics was "VHDL generated forSigned.fromInteger now truncates, like the Clash simulation, when the result is smaller than the argument" ..... that must be evoked in Word64 -> Signed32 or similar, and how Integer gets into it I don't know, but smaller probably means shorter in bits, not smaller in value, and it is saying to just truncate instead of anything else, but WHAT else is it that was done and no longer should be done but that I have resuscitated? Was/is the sign bit retained?)

leonschoorl added a commit that referenced this issue Apr 1, 2022
Previously it could only handle Identifier.
(And Literal, which is handled seperately in Clash.Backend.VHDL.expr_)

I've also renamed it to make it clear this blackbox is VHDL only.

Fixes #2149
leonschoorl added a commit that referenced this issue Apr 1, 2022
Previously it could only handle Identifier.
(And Literal, which is handled seperately in Clash.Backend.VHDL.expr_)

I've also renamed it to make it clear this blackbox is VHDL only.

Fixes #2149
@pbreuer
Copy link
Author

pbreuer commented Apr 1, 2022

Of technical interest only - but I was not able to pull Primitives/Sized/Signed.hs even though it had been newly introduced in the patch that I reversed because there is a purely formal reference to its fromIntegerTF introduced later in Clash/Driver.hs, as part of the knownTemplateFunctions table.
Remove the reference from there too and one can completely reverse 2e4096a . It doesn't fix the synthesis any better than it was fixed, but now we know.
That is not the correct fix, just a working hack. The correct fix is either to use the toIdentifier function to wrap expressions passed to the template function (above), as identifier expressions are already validated as argument, or work out if anybody really cares when one gets a more generic expression and if not just drop the validation in the template, or just figure out what other expression forms should be validated by the template and do something about each of them.
I know nothing about the templating thing so I am afraid cannot help efficiently.
Regards
PTB

@leonschoorl
Copy link
Member

I'll try to explain what's going on.

Integers in haskell are unbounded in sized.
That's a problem for clash because it wants to pick a fixed size for every type it translates to a HDL type.
It picks 64 bits1 to represent Integers in HDL.

Because Integers are unbounded in Haskell and bounded in HDL this can create differences in behavior between the generated HDL and simulations run in Haskell, when these Integer values under/overflow in HDL.
That's why it's we'd recommend against using Integers in your designs, and why clash generates all these Integers are dynamically sized in simulation, but fixed-length after synthesis.-warnings.

The Signed.fromInteger# change was to make the behavior of Signed.fromInterger# in VHDL match what it does in Haskell.
What it does is:

  • when expanding: it does sign-extension
  • when shrinking it: it just truncates higher bits, causing the value to wraparound, so the resulting sign may change
clashi> pack (-127 :: Signed 8)
0b1000_0001
clashi> toInteger (-127 :: Signed 8)
-127
clashi> i = toInteger (-127 :: Signed 8)

clashi> -- Shrinking:
clashi> fromInteger i :: Signed 7
1
clashi> pack it
0b000_0001

clashi> -- Expanding:
clashi> fromInteger i :: Signed 9
-127
clashi> pack it
0b1_1000_0001

Previously the VHDL implementation incorrectly preserved the sign when shrinking too.

Footnotes

  1. actually it picks the native machine word width, which is mostly 64 bits nowadays, but could be 32bits, and this can also be modified with -fclash-intwidth

@pbreuer
Copy link
Author

pbreuer commented Apr 1, 2022

Thanks for the explanation Leon!
Integers appear nowhere in my design (of course I don't want unbounded length things) and fromIntegral no longer appeared anywhere (I had used it once as a convenience to implement a toEnum as I recall, and I replaced it with unpack . resize . pack), yet the error was still appearing. I assume it is an implicit use somewhere.
Didn't you see that via your (great!) example of Word 64 -> Signed 32? No use of Integer in sight, yet fromInteger# being used via fromIntegral.
Why is Integer introduced? Surely the introduction must be temporary and be on the way to being eliminated? If the error interrupts that sequence, then isn't that a normalisation failure?
Your explanation that "the sign bit is retained" sounds right for what goes wrong when using vhdl resize to do truncation having introduced (signed) Integer as an unexpected intermediate on the way from an Unsigned 64 to a Signed 32 type. The change of Unsigned 64 (Word) to Integer could (wrongly) take large positive to negative number and from then on it is all bad as the vhdl resize flushes the sign bit down with it while doing the shrinking.
But the code doesn't mention Integer so why is it introduced? Moving to Integer loest information on whether the original source type was signed or unsigned so there is always going to be guesswork going on here and it is hard to imagine it not being wrong guesswork in at least one case, whatever you do! The information is not there. How can the change intended by the patch make it any better at guessing without the information needed?

Perhaps you could break down fromIntegral :: Word -> Signed 32 into parts so I can see how the fromInteger and its translation to resize() appeared in its synthesis before the patch replaced that.

Tx

PTB

leonschoorl added a commit that referenced this issue Apr 4, 2022
…xpr (#2157)

Previously it could only handle Identifier.
(And Literal, which is handled seperately in Clash.Backend.VHDL.expr_)

I've also renamed it to make it clear this blackbox is VHDL only.

Fixes #2149
mergify bot pushed a commit that referenced this issue Apr 4, 2022
…xpr (#2157)

Previously it could only handle Identifier.
(And Literal, which is handled seperately in Clash.Backend.VHDL.expr_)

I've also renamed it to make it clear this blackbox is VHDL only.

Fixes #2149

(cherry picked from commit 5a86ccf)
leonschoorl added a commit that referenced this issue Apr 4, 2022
…xpr (#2157) (#2160)

Previously it could only handle Identifier.
(And Literal, which is handled seperately in Clash.Backend.VHDL.expr_)

I've also renamed it to make it clear this blackbox is VHDL only.

Fixes #2149

(cherry picked from commit 5a86ccf)

Co-authored-by: Leon Schoorl <leon@qbaylogic.com>
@martijnbastiaan
Copy link
Member

We've released v1.6.3, which includes a fix for this issue.

@pbreuer
Copy link
Author

pbreuer commented Apr 8, 2022

Thank you Martijn
I did try it last night and can confirm it "works for me". Synthesis goes all the way.

The colleague who told me about it got a tycon error, however. It sounds to me like a residue of a previous compilation.

KPU/RFC7693/Blake2b.hs:500:1: error:

Clash.Netlist.BlackBox(680): Can't translate non-tycon type:
Clash.Sized.Internal.Unsigned.Unsigned[8214565720323789636]
64
-> Clash.Sized.Internal.Unsigned.Unsigned[8214565720323789636]
64

That;s impossible. So we'll figure what's up later.

I might be interested in making all the extra warnings go away. My guess is that Int, not Integer, is involved as the variable sized thing it does not like.

[WARNING] Dubious primitive instantiation for GHC.Integer.Type.smallInteger: GHC.Integer.Type.smallInteger: Integers are dynamically sized in simulation, but fixed-length after synthesis. Use carefully. (disable with -fclash-no-prim-warn)

I wrote the code on a 32 bit machine originally, and later moved it to a 64 bit machine, and I would have done the port by making things like Word over to a fixed length Word everywhere. One can be pretty sure I wouldn't have left anything that can change size by context hanging around ... with the excepton of Int, which is used as an indexing type in so many functions and intermediates (e.g. fromEnum) that I decided to leave it be.

I remember consciously deciding that moving to fixed size Int wouldn't help as I'd still need it to engage with all the library functions like shiftL.

Could that be the source of those warnings? They don't issue in 1.5.0pre.

Regards

PTB

@alex-mckenna
Copy link
Contributor

Those warnings appear in designs when you have / use functions which go via Integer / Natural to get to their result. Most of the time this is benign, and if Clash provides a black box for such a function the warning is suppressed. Before 1.6.0 was released (i.e. some 1.5.0 builds will have it, others won't) we added these warnings to the primitives which were missing them which made it a lot noisier to warn in some cases. It's really an instance of most of the time this is fine, but in the rare instance it isn't fine you don't want the warning to just be suppressed.

So in terms of what's happening during compilation, nothing has changed - Clash is now just more vocal about it. We've already made some more black boxes to reduce these warnings (see #2066).

@pbreuer
Copy link
Author

pbreuer commented Apr 8, 2022

There is no instance of "Integer" anywhere in the code proper, but there is in the extra TestBench code base that forms part of the test rig, although it should not be in the compiled TestBench executable because it is used in the Elf loader that builds the insides of a PROM (from an Elf executable file) for use at simulation time, and only the PROM should appear in the synthesized code.
I suppose I could demonstrate that with a little better separation of the files ... but logically, I know it so I am not enthused. Perhaps there is some purely syntactic inclusion that I overlooked. I will see if I can make it go away.

Is there something I could look for in the VHDL?

Thanks

PTB

@alex-mckenna
Copy link
Contributor

You can mark test benches with

{-# ANN myTestBench (TestBench 'entityBeingTested) #-}

and it should suppress the primitive warnings

@pbreuer
Copy link
Author

pbreuer commented Apr 8, 2022

I removed all instances of Integer everywhere (it was only used in the Elf loader as a union type of Word 32 and Word 64, so I aliased it to Word and goodbye and good luck to anyone who wants to cross-compile - now it'll always be expecting 64 bit Elf on a 64 bit compile platform, etc). Still the same warnings.
I would use that ANN above but it needs a bit more explanation for me. Is myTestBench what is also in my ANN myTestBench (Synthesize ...) statement?
Then what is entityBeingTested if not that too?

Thanks again

PTB

@alex-mckenna
Copy link
Contributor

Ah, my mistake on that annotation usage: I got the order of the names mixed up. A better example from our documentation is this:

f :: Bool -> Bool
f = -- some entity
{-# ANN f (defSyn "f") #-}
{-# ANN f (TestBench 'g) #-}

g :: Signal Bool
g = -- some test bench

The annotation tells Clash g is the test bench for the entity f, similar to how a Synthesize annotation tells Clash this is the HDL interface I want you to generate. The test bench annotation (as far as I know) is just there so you can suppress things like warnings about non-synthesizable code, since they don't matter for test benches.

With regard to removing Integer / Natural entirely: this is actually somewhat difficult to do for non-trivial Haskell. There are a lot of functions (such as those which do type conversions) which go via Integer, which is why you still see things like the smallInteger primitive appear after normalization. When I have time I'll have a look through clash-prelude and see if there are more functions we define which use Integer which may now be producing these pointless warnings

@pbreuer
Copy link
Author

pbreuer commented Apr 8, 2022

OK ... am I right in that you are saying g is the thing I am synthesizing, comprising a test bench composed of a test rig connected with f, the thing notionally being tested?
And the effect will be to suppress warnings in g until the synthesis hits f. Surely I have to NOINLINE f and g to make sure they are seen? And what do I do about the current annotation saying that g is to be synthesized? Leave it? Remove it? it is:

{-# ANN kpu_test32
(Synthesize { t_name = "KPU_Test"
, t_inputs = [ ]
, t_output = PortName "trace"
}) #-}

That would be "g".

@leonschoorl
Copy link
Member

Ah, my mistake on that annotation usage: I got the order of the names mixed up. A better example from our documentation is this:
[...]

@alex-mckenna You did get it right the first time. The example in the documentation is wrong.
(Or at least it doesn't match clash's behaviour.)
See #1750

So the example should be:

f = -- some entity
{-# NOINLINE f #-}
{-# ANN f (defSyn "f") #-}

{-# ANN g (TestBench 'f) #-}
g :: Signal Bool
g = -- some test bench testing f

And you don't need an extra Synthesize1 annotation on g. The TestBench annotation will cause g to be synthesized by itself.
That is because both are constructors of the same type TopEntity. And clash will synthesize any top-level binder with an annotation of that type on it.

Footnotes

  1. Either directly using Synthesize, via defSyn or even via makeTopEntity

@DigitalBrains1
Copy link
Member

I quite like the explicitness of not naming it f and g but picking Alex's names:

{-# ANN myTestBench (TestBench 'entityBeingTested) #-}

I'll soon fix this. (I could have sworn we already had. I actually did swear when I noticed we had not.)

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

Successfully merging a pull request may close this issue.

5 participants