VHDL runtime problem? How does one make a testbench print something?

685 views
Skip to first unread message

peter.t...@gmail.com

unread,
May 15, 2021, 5:12:29 PM5/15/21
to Clash - Hardware Description Language

I have finally gotten the VHDL generated by Clash to compile  in GHDL and then run.

Long story, but there was a bug in GHDL that prevented it working with lots of signals (an internal table of values is dynamically resized, but the size  is incorrectly cast to 64-bit from 32-bit, which means that everything goes haywire at the 2GB frontier, which is 256M   64-bit ints in the table. Yes, clash has managed to generate that much of something, which is fair enough! The executable is 330MB, stripped).

But in the actual VHDL simulation phase, after "elaboration" and "initialization", I am getting tons of warnings:

../../src/ieee/v93/numeric_std-body.vhdl:1613:7:@0ms:(assertion warning): NUMERIC_STD."=": metavalue detected, returning FALSE
../../src/ieee/v93/numeric_std-body.vhdl:2124:7:@0ms:(assertion warning): NUMERIC_STD.TO_INTEGER: metavalue detected, returning 0

Does anyone have any insight into this? What might I be doing wrong, if it is me, or what might GHDL be getting wrong if it is it, or what might Clash be doing in terms of emitted VHDL that isproblematic, if it is that?

I have no clue at this moment, but I don't like things being done that I did not ask for. The reference is to what I think is the IEEE standard library. There are murmurings in various places about everyone really using nonstandard libraries. Should that be what I am doing? Is Clash generating VHDL directed at the IEEE standard libraries ? Or not?

(I might try verilog soon, as I am gaining confidence that the VHDL is at least potentially correct, so the verilog might be correct too, even though I have less possibility of debugging it as I have no verilog compiler, just an interpreter ... I think ... so I cannot use gdb on it).

Finally, can somebody give me a hint as to how to get Clash to generate VHDL (or verilog) that prints something or otherwise emits something visible, perferably every cycle? I suspect that "testbench" has a special meaning in the jargon to which I am not party. The VHDL I am running is generated from  the Clash ANN signature:

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

So there are no input signals and it is producing something. Will a VHDL simulator on this produce a sequence of binary outputs corresponding to my Trace dataype, one per cycle, or will it just stay silent, because I have not used some magic PRINT command?

(I can follow what is happening with the debugger, but at the moment it is still on cycle 0, as the warnings state).

Hints welcomed.

Regards

Peter


peter.t...@gmail.com

unread,
May 15, 2021, 5:49:23 PM5/15/21
to Clash - Hardware Description Language

For your convenience, here is a piece of the IEEE standard library emitting that warning:

-- Id: C.26
  function "=" (L, R: SIGNED) return BOOLEAN is
    constant L_LEFT: INTEGER := L'LENGTH-1;
    constant R_LEFT: INTEGER := R'LENGTH-1;
    alias XL: SIGNED(L_LEFT downto 0) is L;
    alias XR: SIGNED(R_LEFT downto 0) is R;
    constant SIZE: NATURAL := MAX(L'LENGTH, R'LENGTH);
    variable L01 : SIGNED(L_LEFT downto 0);
    variable R01 : SIGNED(R_LEFT downto 0);
  begin
    if ((L'LENGTH < 1) or (R'LENGTH < 1)) then
      assert NO_WARNING
          report "NUMERIC_STD.""="": null argument detected, returning FALSE"
          severity WARNING;
      return FALSE;
    end if;
    L01 := TO_01(XL, 'X');
    R01 := TO_01(XR, 'X');
    if ((L01(L01'LEFT)='X') or (R01(R01'LEFT)='X')) then
      assert NO_WARNING
          report "NUMERIC_STD.""="": metavalue detected, returning FALSE"   <-- here!
          severity WARNING;
      return FALSE;
    end if;
    return SIGNED_EQUAL(RESIZE(L01, SIZE), RESIZE(R01, SIZE));
  end "=";

Something to do with an X or no X in a number?  Is it talking about literal constants?

Regards

Peter

peter.t...@gmail.com

unread,
May 16, 2021, 3:27:56 AM5/16/21
to Clash - Hardware Description Language
Spoke too soon about being confident the generated vhdl is correct. An error is raised on a simple SHIFT translation.

../../src/ieee/v93/numeric_std-body.vhdl:2124:7:@0ms:(assertion warning): NUMERIC_STD.TO_INTEGER: metavalue detected, returning 0
/home/ptb/lang/clash/KPU/trunk/vhdl/Test/TestBench/testbench:error: bound check failure at dhu.vhdl:764
in process .testbench(structural).dhu_result_18@dhu(structural).foldr.foldr_loop(63).fun_1_0.P11
  from: [unknown caller]
  from: .libc_start_main at libc-start.c:308
/home/ptb/lang/clash/KPU/trunk/vhdl/Test/TestBench/testbench:error: simulation failed

The line is a translaton of a shift in a 64-bit integer:

      \c$case_scrut_6\ <= to_signed(1,64) when x1_1 >= to_signed(64,64) else to_
signed(0,64);

      with (\c$case_scrut_7\) select
        result_36 <= intermediate(i_15+1) when x"0000000000000001",
                     (\x#2\ xor (shift_right(to_signed(283,64),to_integer(b))))      <--  L764 here!
when others;


b is a locally generated signal and it is not assigned till afterwards.

      -- KPU/AES/Round/Aux.hs:14:1-6
      signal b 
                           : signed(63 downto 0);

(that's in the declarations for the function, before the "begin"). There is no assignment to b until line 768:

      b <= -x1_1;    <-- L768

I vaguely understand the VHDL as creating a signed 64-bit constant equal to 283, and shifting it right by the value of b. That is a signal assignment, so it is scheduled rather than done, and the scheduled change may be for +1 cycle as far as I can guess from line 763 (i don't speak very much vhdl at all!).

The shift is negative by 1. I suppose -x1_1 is .... ah, maybe  -1. Surely x1_1 is defining 1 bit of a 1-bit number as 1 ? Then - applied to it should be to complement the one bit and end-around carry 1, giving 0+1=1? Then mapping that to the 64-bit b maybe does a signed extension, which gives 111......1. That may indeed be -1. So that is 283 >> -1.

All I can imagine is that it does not like right shift by -1. Not the same as left shift by 1, maybe?

The Clash from which it came ...

--type BinaryPolynomial = FiniteBits a => a
modF2m :: (FiniteBits a, Num a, KnownNat(BitSize a), 1 <= BitSize a)
       => a -- ^ Modulus
       -> a
       -> a
modF2m fx i                                           <----- Line 14 here!
    | isNeg fx || isNeg i
                = error "modF2m: negative number represent no binary polynomial"
    | fx == 0   = error "modF2m: cannot divide by zero polynomial"
    | fx == 1   = 0
    | otherwise = goMod i fx


(I didn't write that - it must be copied from somewhere ... it looks incomprehensible to me now! I guess it is calculating the remainder - mod - after division of one "binary" polynomial by another, the polynomials being represented really obscurely, maybe as a sequence of 1/0 coefficients, a bit vector?)

The shift must be in the goMod business end of things. Yes it is.

goMod :: (FiniteBits a, KnownNat(BitSize a), 1 <= BitSize a)
      => a -> a -> a
goMod n fx = foldr f n (indices (lengthI n))
             where f i n = if testBit n (fromEnum i) && s >= 0
                           then n `xor` shift fx s
                           else n
                            where s = log2 n - lfx
                   lfx = log2 fx    -- no. bits in


I may have written that but I have little idea now what it does in detail .. it does contain "shift", and that shift accepts both right and left shift numbers.

But the problem may be in isNeg. That is defined as follows (test top bit). It has an explicit "1" in it, and it is a 1 going left, which is morally the same as a -1 going right :

isNeg :: Bits a => a -> Bool
isNeg x = testBit (rotateL x 1) 0

Is the problem maybe that the shift_right in the VHDL code does not go "right" by -1?



Regards

Peter

Christiaan Baaij

unread,
May 16, 2021, 4:37:14 AM5/16/21
to clash-l...@googlegroups.com
# With regards to the warning about metavalue:
At the very start of the simulation, many signals are in an unknown state, represented by 'U' in std_logic_vector (and its subtypes: signed and unsigned).
Then usually at the first rising edge of the clock signal (or the assertion of the reset line if the registers have a asynchronous/level-sensitive reset-port), all your registers and ports will go to some defined state (bit 1 or bit 0).
However, as you can see the VHDL simulator already starts simulating everything before this first rising edge, causing all kinds of interactions with "unknown" values.
You're basically free to ignore those warnings.

# With regard to the shift error:
So this is a bug, the VHDL language standard says that the shift amount must be a natural number.
However, the default implementation for Haskell's `shift` is:

```
    x `shift`   i | i<0       = x `shiftR` (-i)
                  | i>0       = x `shiftL` i
                  | otherwise = x
```

Now, `shift` is a class method of the `Bits` class, so for Clash' `Signed` and `Unsigned` and `BitVector` types we can make new primitives for the `shift` method implementation.
That will enable us to ensure that we do not translate `shift` in such a way that the `shiftR` and `shiftL` happen concurrently, thus preventing bound check error during simulation.
However, this is not something we'll be able to do for the `Int`, `Word`, `Integer` and `Natural` bits instances.
Anyhow, I'll raise this as a bug on the issue tracker.

# With regards to printing:
I'll come back to that later



--
You received this message because you are subscribed to the Google Groups "Clash - Hardware Description Language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clash-languag...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/clash-language/e6f36cd4-b802-4dec-b625-45af44cb31c9n%40googlegroups.com.

peter.t...@gmail.com

unread,
May 16, 2021, 6:18:43 AM5/16/21
to Clash - Hardware Description Language
Thank you. I look forward to the remarks about print. Please don't forget that!

I didn't say, but a concern is that the VHDL implementation is stricter than the Clash. That code that evoked the shift bug is not executed at all at runtime in Clash in that testbench setup. It is more or less placeholder code waiting for some more serious and future attack on it,  and I have never really executed it in anger (other than to check "it seems it work" on some examples) and is definitively not triggered in the sequence that arises in the testbench. It just has to repeat some vaguely cryptographically plausible function action n times. That seems to be substitution in a polynomial over some finite algebraic field, mod some other polynomial. It could be replaced by "error foo" here and the testbench code would work as well as ever in Clash, as it is not used. There is not the slightest, tiniest, possibility that it is active. It is in a hardware module that needs to be addressed to do anything, and an address translation entry that would/might point at it is not present in the testbench setup. It should be a potential thing, not an active thing. There is a diagram of the structure at https://www.researchgate.net/profile/Peter-Breuer-2/project/Encrypted-Computing/attachment/601c5dbf61fb57000101f207/AS:984180427083776@1611658307194/download/KPU.pdf . The unit in question ("dhu") is the box in green labelled "Diffie Hellman" at lower right. It is not being used because it would install new encryption keys, and there are default encryption keys already built in (at the end of the green line that extends from the unit) that are being used instead ("by default"). We know that, because nothing would work in the testbench if they were changed.

Should I be using enable signals a bit more liberally?

As soon as a bugfix is in for that, I will definitely make the effort and clone the latest repository and compile Clash from it, and retry. Hopefully I will still be able to work that compilation! It seems  you have an immediate fix in mind for the case that has cropped up, but something more serious is required for other cases.


Regards

Peter

Christiaan Baaij

unread,
May 16, 2021, 11:30:32 AM5/16/21
to clash-l...@googlegroups.com
Alright, you can find an initial attempt at tracing/printing the values of a Signal to stdout during VHDL simulation at: https://gist.github.com/christiaanb/18a08eac6a0b6cdf00c58725087cf689
It include `Report.hs` module that exports a function:
```
report :: (Render a, KnownDomain dom) => Clock dom -> Signal dom a -> Signal dom a
```
which behaves very much like Haskell `Debug.Trace.trace` function, where it will report the value of the `Signal dom a` argument for every clock cycle of the `Clock dom`.

I've also created a simple demonstrator:
```
module ReportTest where

import Clash.Prelude
import qualified Clash.Explicit.Prelude as E
import qualified Clash.Explicit.Testbench as E

import Report

topEntity :: Signal System (Pair (Vec 2 (Unsigned 4), Vec 2 (Unsigned 4)) (Unsigned 4))
topEntity =
  let
    s   = E.register clk resetGen enableGen 0 (s + 1)
    clk = E.tbClockGen (s .<. 5)
  in report clk (Pair <$> (bundle (bundle (replicate d2 s), bundle (replicate d2 s))) <*> s)
```
That basically has a counter, `s`,  and a clock generator that will stop toggling when the counter reaches 5 (so that simulation stops).
The counter value is put into some data structures for demonstration purposes.
Running the simulation in GHDL for the generated VHDL will give you:
```
christiaan@DESKTOP-O5QKI8I:~/clash-compiler/vhdl/ReportTest.topEntity$ ./topentity
topEntity.vhdl:43:7:@8ns:(report note): Pair { left = (0 :> 0 :> Nil, 0 :> 0 :> Nil), right = 0}
topEntity.vhdl:43:7:@18ns:(report note): Pair { left = (1 :> 1 :> Nil, 1 :> 1 :> Nil), right = 1}
topEntity.vhdl:43:7:@28ns:(report note): Pair { left = (2 :> 2 :> Nil, 2 :> 2 :> Nil), right = 2}
topEntity.vhdl:43:7:@38ns:(report note): Pair { left = (3 :> 3 :> Nil, 3 :> 3 :> Nil), right = 3}
topEntity.vhdl:43:7:@48ns:(report note): Pair { left = (4 :> 4 :> Nil, 4 :> 4 :> Nil), right = 4}
topEntity.vhdl:43:7:@58ns:(report note): Pair { left = (5 :> 5 :> Nil, 5 :> 5 :> Nil), right = 5}
```

Things that can be rendered during VHDL simulation must have an instance of the `Render` class.
Currently `Render.hs` defines instances for the following data types:
- Bool
- Signed
- Unsigned
- Vec n a
- (a, b)
- A custom data type defined in `Render.hs`:   data Pair a b = Pair { left :: a, right :: b }

If you need more instances you will need to create them yourself.
I did some experiments creating `Render (Either a b)` instance, i.e. a render instance for a sum type, but the `String` result of the `render` function is currently messing up the Clash compiler.





Christiaan Baaij

unread,
May 16, 2021, 12:00:30 PM5/16/21
to clash-l...@googlegroups.com
Actually, the sum type thing was just a red herring. The solution I provided is really brittle.

The problem really is the `String` type:
- Haskell's `String` type doesn't have a statically known length
- VHDL's `string` type _must_ be declared with a known length
- This means that we can never create a VHDL signal corresponding to a Haskell `String`
- This means that `String` is considered non-representable
- This in turns causes certain transformations not to fire
- This in turns causes Clash being unable to translate certains expressions to VHDL

So yeah... this problem cannot be solved as a library.
What will need to happen is for Clash to generate VHDL functions which are equivalent to Haskell's `show` function for all the types generated by Clash.

I guess in the meantime, if you want to observe your circuits is to run the GHDL-generated binary with either the `--wave=` or `--vcd=` arguments: https://ghdl.readthedocs.io/en/latest/using/Simulation.html#export-waveforms
Then you can observe the results in GTKwave.

peter.t...@gmail.com

unread,
May 16, 2021, 6:33:27 PM5/16/21
to Clash - Hardware Description Language
OK, thank you, very much! I see you made an analog of Show called Render that more or less turns everything (except extra data types I have defined, and I can fix that on an individual basis) into a String, but String is not good for turning into anything in VHDL, because VHDL doesn't have arbitrary length strings and String is arbitrary length strings.

Your tuple construction does \a b -> a <> " :> " <> b and the template says "~ARG[0] & \\" :> \\" & ~ARG[1]" too! That's the same thing twice! Oh ..  one is for constant expressions constructed by clash/haskell and finally passed to vhdl code as literal constants, and the other (the template) is for vhdl to use when the values are provided only at runtime in VHDL? Yes? And you are saying that one can't even write & of different length string expressions in vhdl, to put in a signal?

A bit of googling seems to tell me that one has to decide on producing, say, a signal of length 80 chars, whatever that is as a type in VHDL, and then use your Render idea (recursion over types! Nice!) to produce constants of that type via  "take 80 ( ... `mappend` eighty_spaces)" wrapped round every one of your constructs.

For the template, from what I gleaned from googling, "&" will do some unknown random join on two 80-char strings in VHDL, and that could be a mess. One had better define ones own binary operator.


Oh, it contains str_replace, and one could do   str_substr(str_replace(str_replace("%0 :> %1","%0",~ARG[0]),"%1",~ARG[1]),0,80) for the template, maybe? I.e., substitute ARG[0] for the %0 in the format string, then substitute ARG[1] for the %1, then cut to 80 characters. Pad that with up to 80 spaces for consistency too. That would work in the template, would't it?

peter.t...@gmail.com

unread,
May 19, 2021, 4:34:08 AM5/19/21
to Clash - Hardware Description Language
I've done what I can to help out by rewriting that "render" class to produce 80-element Vectors of Char, rather than String.

I think that was part of the problem. I.e., VHDL needs the length of strings to be declared, so produce a fixed array of Char. My hope is that Vec 80 Char will fit in to existing mechanisms and allow the right stuff in VHDL  to be produced without further intervention.

It's a pain because I didn't dare use String-to-Vector-of-Char functions, for fear of recursion. So I  hacked something simple and ugly for SIgned n and Unsigned n. Hex printout only.  I don't see any problem with Int or Nat! Just first cast to Signed (BitSize Int) and Unsigned (BitSize Nat) respectively, then apply render.

File attached.

I have left FIXMEs pointing at what I don't know how to do. Please fix!

In Clash/haskell I don't know what should replace "trace", which works on string, not vectors of char. I commented out two stanzas there which  now need replacing in toto. Please replace. Hopefully that doesn't mean "rewrite a really huge and complicated trace function".

In VHDL "ANN" template sections, I don't know if one has to pad the constant strings like "False" to 80 chars for emission in a signal/trace/report. Maybe it just happens as required per case in VHDL.   I left FIXMEs on each ANN, because I really have no clue what the mechanisms  are that I should be trying to fit into.

I also kept the valid range of the vector as an extra Int parameter in a pair in what (I think) is passed to VHDL, so it will get a pair of an array and an int (I think - for whatever passes as "pair" in VHDL) as that ~ARG[0] stuff. Or maybe it won't. I don't know. Maybe it will just get an ARRAY 80 of Char, or something like that. Maybe it will get two arguments, not one. I left a function FST in the templates around the ~ARG[0] things that should be defined to cope with whatever is necessary there. There are FIXMEs pointing at each.

One needs to use the range indicator to generate a substrng of the maximal 80 chars. FST should do that. That's the only library-ish thing required in VHDL, and how difficult can substring be? That was the best I could do.

Please change 80 to something more general.

If there is more or more sensible work I can do, just point me at it. I am anxious to get some printout and all I can do is wait for incoming right now (yes, I read that about "wave" - it doesn't appeal that much!).

Regards

Peter
MyReport.hs

peter.t...@gmail.com

unread,
May 20, 2021, 3:26:15 AM5/20/21
to Clash - Hardware Description Language

Nearly working. Clash is generating VHDL report signals of type array 80 of char (I think, provided char really is "unsigned_21"):

  -- Test/Report/MyReport.hs:35:1-11
  signal \c$ws_app_arg_10\     : test_types.array_of_tup2(0 to 79);
  signal result_11             : test_types.array_of_unsigned_21(0 to 79);  <--- HERE!

I believe the corresponding Clash type is Vec 80 Char.

Trying to compile the VHDL tells me that I need to fix things in the VHDL templates that I don't know how to fix, but it may be a minor thing. Rather than continuing to guess and try, I'll ask about it here, if I may.

What gets generated by Clash  is VHDL lines like

 if (falling_edge(\Test.Report.ReportTest.topEntity_clk\))
 then
   report (("Pair { left = " & ("(" & result_9 & ", " & result_3 & ")") & ",  right = " &    (natural'image(to_integer(to_unsigned(4,64)))) & "}"));   <- line 193 (LOOOOOONG)
 end if;

topentity.vhdl:193:36:error: type std_ulogic does not define character '('
topentity.vhdl:193:53:error: type std_ulogic does not define character ','
topentity.vhdl:193:71:error: type std_ulogic does not define character ')'
topentity.vhdl:193:16:error: type std_ulogic does not define character 'P'
topentity.vhdl:193:16:error: type std_ulogic does not define character 'a'
topentity.vhdl:193:16:error: type std_ulogic does not define character 'i'
topentity.vhdl:193:16:error: type std_ulogic does not define character 'r'
topentity.vhdl:193:16:error: type std_ulogic does not define character ' '
...

It seems to be complaining about the open parenthesis following "report".  There is no grouping error with the parentheses (I have checked). The template is as in your original:


{-# ANN renderPair (InlinePrimitive [VHDL]
       $ unindent [i| [ {
             "BlackBox" : {
               "name" : "Test.Report.MyReport.renderPair" , "kind": "Expression"
             , "template" : "\\"Pair { left = \\" & ~ARG[0] & \\", right = \\" & ~ARG[1] & \\"}\\""
             , "workInfo" : "Constant"
             }
           } ] |]) #-}


The result types being combined are array 80 of char (I think):

signal result_3  : test_types.array_of_unsigned_21(0 to 79);
signal result_9  : test_types.array_of_unsigned_21(0 to 79);


Is there an easy fix for the VHDL?

I am also concerned by

topentity.vhdl:193:91:error: no function declarations for operator "&"

(same line) but that may be a consequence. I am worried  that  when whatever the first error  is is fixed, then "&" will show up as not working on 80-char strings terminated with lots and lots of nulls. I see warnings on the web also that some VHDLs don't like such strings (and the cure allegedly is to terminate with back-ticks instead). I can grind through and fix ghdl to be more accepting, if that is the issue. I will not be able to fix "&" itself in VHDL without help at that point, if it is the issue. It might have to be substringtonull(arg1) & substringtonull(arg2).

Any help there? (definitions attached as files).

Regards

Peter
ReportTest.hs
MyReport.hs

peter.t...@gmail.com

unread,
May 20, 2021, 5:43:35 AM5/20/21
to Clash - Hardware Description Language
The trouble seems to be with the primitives for numbers, because Pair of Bool works fine as a running report (and so does a pair tuple of Bool):

topentity.vhdl:39:7:@8ns:(report note): Pair { left = true, right = false}
topentity.vhdl:39:7:@18ns:(report note): Pair { left = false, right = true}
topentity.vhdl:39:7:@28ns:(report note): Pair { left = true, right = false}
topentity.vhdl:39:7:@38ns:(report note): Pair { left = false, right = true}
topentity.vhdl:39:7:@48ns:(report note): Pair { left = true, right = false}
topentity.vhdl:39:7:@58ns:(report note): Pair { left = false, right = true}

But ... I get the impression from that that the Clash atomic-level code (renderBool, renderUnsigned, etc)  is completely supplanted by the ANN templates so whatever I write in Clash to treat the atomics (Bool, Unsigned, etc) makes no difference whatsoever to the VHDL generated (I specified "True", not "true", in the Clash code). Therefore I don't see how the combinators I write in Clash can make any difference either.  It must all be the ANN templates.

I think this approach is unnecesary. I don't want to print out any old type! I am quite happy to print out just one type, say an 80-character string. THAT string can be constructed via "render" in Clash code, and your recursion through type-constructors trick with the Render class.

The 80-char string can be protected by a data constructor, say STR80, and a primitive replacement for that can be defined via a template that does a report for it. How would I define a template that replaced a STR80 data constructor?

I see what you mean about fragile, but this will work and be robust, when the right approach is chosen.


PTB

peter.t...@gmail.com

unread,
May 20, 2021, 7:06:15 AM5/20/21
to Clash - Hardware Description Language
So I did what I said, removed all those intercepts for renderFOO for primitive types and type constructs  FOO as they mean my  own generation of fixed-length strings in Clash is ignored and replaced, and told report# in clash  to trace(showSTR80 foo), not trace(render foo).  The intercept for report# is still there and creates  "report (~ARG[2])" in the VHDL, which has

     report (\c$case_alt\);

and

   signal \c$case_alt\   : test_types.array_of_unsigned_21(0 to 79);

That looks close enough to join up the dots.  I think that type is meant to be an 80-char string.

How in VHDL do I turn it into a string of the kind that report will accept? I'll add that function into the template and hopefully be done.

PTB

peter.t...@gmail.com

unread,
May 21, 2021, 8:26:07 AM5/21/21
to Clash - Hardware Description Language
Got the vhdl to_string() function needed  (to convert array of 80 unsigned 21 bit nunbers representing ascii/UTF codes to an 80 char string). The report process seems to be working now:

topentity.vhdl:65:7:@8ns:(report note): Pair { left = (<0x0,0x0>,<0x0,0x0>), right = 0x0}
topentity.vhdl:65:7:@18ns:(report note): Pair { left = (<0x1,0x1>,<0x1,0x1>), right = 0x1}
topentity.vhdl:65:7:@28ns:(report note): Pair { left = (<0x2,0x2>,<0x2,0x2>), right = 0x2}
topentity.vhdl:65:7:@38ns:(report note): Pair { left = (<0x3,0x3>,<0x3,0x3>), right = 0x3}
topentity.vhdl:65:7:@48ns:(report note): Pair { left = (<0x4,0x4>,<0x4,0x4>), right = 0x4}
topentity.vhdl:65:7:@58ns:(report note): Pair { left = (<0x5,0x5>,<0x5,0x5>), right = 0x5}

That to_string() conversion function is standard as conv_integer() in a later issue of the IEEE standard library (2008?).  I would like to get this substitute for it put  into the types.vhdl file emitted by Clash  but don't know how to cause that to happen.

For the record, the conversion function is:

function to_string (lvec: in test_types.array_of_unsigned_21(0 to 79)) return string is
    variable text: string(lvec'length-1 downto 0);
  begin
    for k in lvec'range loop
        text(k) := character'val(TO_INTEGER(lvec(k)));
    end loop;
    return text;
  end function;

I am currently emitting it as a declaration in the report process that Christiaan supplied via the ANN templates.

That was my very first vhdl program. I figured  "unsigned 21" is an array of bits, and  addressing its bits individually succeeds for building an integer representing the ascii code bitwise via shift and add, but then I found the TO_INTEGER() in the 93 library which does the whole job.  An integer is needed for val to be applied to give a char. The assignment expression amounts to a Char = (toEnum . fromEnum ) (Unsigned 21)  in Clash. I have no clue if the index ranges are backwards or forwards.

The compilation warns that my variable text:string declaration is somehow bad news:

topentity.vhdl:55:29:warning: static range violates bounds [-Wruntime-error]
topentity.vhdl:55:29:warning: static range violates bounds [-Wruntime-error]

Shrug?

I have NOINLINEd every element of the 80-char string building function "render" that parallels "show" in Clash, so there is no possibility of accidentally getting two of something when Clash has finished translating it. I am trying to avoid Christiaan's "fragile" call on it.

To resume: my problem was that the 80-char strings produced for a report mesage in Clash are translated by Clash to array (80) of unsigned 21 in VHDL, not string. That is solved by putting the VHDL conversion function above inside the template for the "report#" primitive. It produced this vhdl running report process:

(topentity)
begin
  -- pragma translate_off
  report_process : process(\Test.Report.ReportTest.topEntity_clk\) is
    [my to_string() defn here]
  begin
    if (falling_edge(\Test.Report.ReportTest.topEntity_clk\)  then  report (to_string((\c$app_arg\)));   <------ report here
    end if;
  end process;
  -- pragma translate_on
  result <= ( pair_sel0_left => a1_0, pair_sel1_right => \c$Test.Report.ReportTest.topEntity_s_0\ );
  ...

The c$app_arg is type stream of array_of_unsigned_21.

It looks like that all works, but I would have preferred the to_string to be in the expression template for showStr80, which produces a Clash String from a  Vec 80 Char, and passes  it to report#.

Instead I have the intercept for showStr80 behaving like the identity function, and the to_string in the report# intercept. It's not (translated) type correct! I can't put the to_string in the showStr80 because it's not declared at top-level in the vhdl, and the showStr80 won't see it.

It is still fragile. Though showStr80 really does nothing, not declaring it NOINLINE kills the vhdl synthesis. Why is that?

Regards and thanks


Peter
MyReport.hs
ReportTest.hs

peter.t...@gmail.com

unread,
May 22, 2021, 6:23:31 AM5/22/21
to Clash - Hardware Description Language
Christiaan - re: "I'll raise this as a bug on the issue tracker." Did you get a chance to do that? I want to set a notification for me on that, as it is critical path in what I am doing.

Otherwise I may have to try tracking it down and that may cause havoc ...

Regards

Peter

peter.t...@gmail.com

unread,
May 31, 2021, 8:14:48 AM5/31/21
to Clash - Hardware Description Language
I would like to produce a work-around for this bug in generated vhdl, as noted at the beginning of this thread (evidence recapitulated for convenience of reference):

```
error: bound check failure at dhu.vhdl:764
in process .testbench(structural).dhu_result_18@dhu(structural).foldr.foldr_loop(63).fun_1_0.P11
  from: [unknown caller]
  from: .libc_start_main at libc-start.c:308
trunk/vhdl/Test/TestBench/testbench:error: simulation failed

KPU/AES/Round/Aux.hs:14:1-6
      signal b 
                           : signed(63 downto 0);
...
      \c$case_scrut_6\ <= to_signed(1,64) when x1_1 >= to_signed(64,64) else to_signed(0,64);
      with (\c$case_scrut_7\) select
        result_36 <= intermediate(i_15+1) when x"0000000000000001",
                     (\x#2\ xor (shift_right(to_signed(283,64),to_integer(b))))      <--  L764 here!
        when others;
```

The Clash from which it came ...

```
type BinaryPolynomial = FiniteBits a => a
modF2m :: (FiniteBits a, Num a, KnownNat(BitSize a), 1 <= BitSize a)
       => a -- ^ Modulus
       -> a
       -> a
modF2m fx i                                           <----- Line 14 here!
    | isNeg fx || isNeg i
                = error "modF2m: negative number represents no binary polynomial"

    | fx == 0   = error "modF2m: cannot divide by zero polynomial"
    | fx == 1   = 0
    | otherwise = goMod i fx
...
goMod :: (FiniteBits a, KnownNat(BitSize a), 1 <= BitSize a)
      => a -> a -> a
goMod n fx = foldr f n (indices (lengthI n))
             where f i n = if testBit n (fromEnum i) && s >= 0
                           then n `xor` shift fx s                                           <---- error from here, as far as I can determine

                           else n
                            where s = log2 n - lfx
                   lfx = log2 fx    -- no. bits in

```
There are two easy things I can try as work-arounds, but I don't know which is more likely to succeed, or if it will take something more difficult, or more profound. As it takes many hours to try a single experiment, I would appreciate guidance/guesses  (I'll state some theories below this on what is causing this bug, but I don't have it clear). Options:

1) replace all references to "shift" with explcit references to "shiftR" and/or "shiftL", everywhere.
2) replace all references to "shiftR" and "shiftL" with references to "shift" everywhere.

Those are the two directions to go in. They are opposed. I do not know which to try first.

As a third option if neither works I will declare my own versions of shift, shiftR and shiftL as NOINLINE,  and give explicit vhdl primitives for each of them.  I only need them on BitVector (or can make it so).  Since I know almost zero vhdl and could spend hours or days working myself out of any vhdl quagmire, I will try one of (1) or (2) above first.

I don't really know which to try first because I don't really understand the bug. We seem to have determined that shift is coming out translated in vhdl as running shift left and shift right in parallel, with only one of the results taken, and vhdl is complaining about one side or the other getting the wrong sign shift at runtime.

But in the /prims/ directory I only see primitives for shiftL and shiftR, not shift. E.g.

{ "BlackBox" : { "name" : "Clash.Sized.Internal.BitVector.shiftL#" , "kind" : "Expression" , "type" : "shiftL# :: KnownNat n => BitVector n -> Int -> BitVector n" , "template" : "std_logic_vector(shift_left(unsigned(~ARG[1]),to_integer(~ARG[2])))" } }

I infer that what is happening is that clash is at some level translating shift into if n >= 0 then shiftL else shiftR or something similar. That may be via a newly declared class instance of Bits in my code where shift is not explicitly defined and instead picks up a default definition like that in terms of shiftL and shiftR.

(for (1) I will go through explicitly defining shift in terms of shiftL and shiftR in each new class instance of Bits I have defined)

But how can a translation like that end up wrong in vhdl? Yes, I can see that as circuitry, the circuit might be OK running an if then else as both branches in parallel, but surely it must GUARD each branch with the switching condition (and its negative, respectively)?

Is instead no guard put on the branches running in parallel, simply one OUTPUT or the other taken, according to the condition?("mux"!)

If so, that looks like a generic fault in synthesizing  conditionals, which would be nasty, and should have shown up elsewhere in some kind of testing, so is inherently improbable.

So that is why I do not have a clear picture of what the bug may be.  I am reduced to planning to try the two alternatives above to see if either fixes anything, just because but maybe they'll fix this particular instance of a more general problem, and solve nothing in the end. Can anyone offer any bets/advice before I devote hours to trying (1) and (2) above?

Regards

Peter













al...@qbaylogic.com

unread,
Jun 1, 2021, 2:58:10 AM6/1/21
to Clash - Hardware Description Language
Hi Peter,

Sorry about this, we copied the issue to the issue tracker and I didn't think to come back here and message when a fix was merged. As of 5ad7bb2ae9eb5a97a45cd222ce015221137ee3f7 there should be a fix for rendering shift/rotate in VHDL such that negative shift/rotate amounts to the Haskell function don't generate invalid VHDL. i.e. you get something more like

```
result <= shift_left(x, y)
  -- pragma translate_off
  when (y >= 0) else (others => 'X')
  -- pragma translate_on
  ;
```

which matches the behaviour in Clash simulation (the types in clash-prelude throw an XException when you attempt to shift/rotate a negative amount). This fix is also backported to the 1.4 branch if you want something more stable to work from, but is not included in the recently released version 1.4.2 (so you would need to build from source). Links below:

Hope that helps

  - Alex

peter.t...@gmail.com

unread,
Jun 1, 2021, 11:03:09 AM6/1/21
to Clash - Hardware Description Language
Thx. I'll catch up with that tomorrow. Refereeing today. I am a little worried that there is a generic fault behind this, though, not just a particular issue. Can you explain what the fault was and what the cure is? I understood neither of them, not speaking more than a few phrases of VHDL.

I'll try. Your code above starts by conditionally scheduling the result (signal, in one delta cycle of future time) as x << y (C notation) provided y>=0, but what does the others =>'X' mean? Is that a variable assignment? Is that else just to write something meaningless and harmless because there  has to be an "else" in VHDL so not to bother myself about it?

As a guarded scheduling of a shift left. It would be fine. Was the mistake that  shift x y in Clash was translated first to if y>=0 then shiftL x y else shiftR x (-y),  and then the shiftL part was translated to just  result <= shiftL x y  with no guard?  Didn't the Clash  if  then else already translate to if then else in VHDL, thus getting the guard condition in place? So why would even the unguarded shiftL translation lead to a fault? I don't get it. Explanation welcome!  It seems to me you just added an extra guard condition when there already was one there.

I replaced the uses of shift in my code with shiftL and shiftR in each instance, as appropriate, even putting in an explicit if then else when I didn't know the sign of the shift at compile time. That cured the original error and I got a lot farther.

The original VHDL translation was valid, by the way! It compiled. It was not correct, overall, but I am not convinced the fault is there. Now I am getting an error

../../src/ieee/v93/numeric_std-body.vhdl:173:5:@0ms:(assertion error): DIV, MOD, or REM by zero

(this rest of the error spew below is just fluff because the IEE library, after getting -1 for looking for the position of the MSB in 0 and issuing an error notice from an assert statement, carries on and gets the bounds wrong in the rest of the routine ... nuts!)

./testbench:error: index (33) out of bounds (32 downto 0) at ../../src/ieee/v93/ numeric_std-body.vhdl:179 in process .testbench(structural).pipe_result_43@pipe(structural).write_stage_re sult_3@write_stage(structural).exec_stageb_result_2@exec_stageb(structural).exec b_result_0@execb(structural).alub_result_125@alub(structural).divsigned.P3

That is at least specific about where the error is from, and I GUARANTEE that it is not exercised neither in the Clash runtime nor in any possible Universe. It is from code for an ALU in Clash:

case alu_cmd of
  ...
  AL_DIV -> runState (alu_div vi) s_
  ...

I guarantee that guard is not triggered.

The alu_div subroutine itself is not guarded:

alu_div :: Vec 4 Val -> State ALS (Vec 4 (Maybe Val))
alu_div vi = do
                    let (x :> y :> _ ) = vi
                          (z,o) = divs_c x y
                    modify (\alu -> alu { ov = o, except = o })
                    return (Just z :> repeat def)

However, there IS a guard on the int32 division it calls:

divs_c :: KnownNat n => Signed n -> Signed n -> (Signed n,Bool)
divs_c x y = (z,o)   where (z,o) = if 0 == y || (minBound == x && -1 == y) then (0, True) else (x `div` y, False)

Now, AL_DIV is not the alu command in any instruction received on cycle zero (and no instruction at all would have got as far along the pipeline as the stage in which either of the two ALUs are situated - it is ALU(b) that complains -  by then!) or later. It appears nowhere in the program executed by the hardware. There is no "mod" either, before you ask!

Furthermore, the actual low level routine DOES have a guard. It should sliently emit zero if it gets told to divide by zero.

I deduce/hypthesize  that the circuits involved are being actuated despite the guards on them, on cycle zero, and they are getting zeros on input  which in Clash cause nothing to happen because the output of those circuits is ignored and Clash is lazy, so never executes them.

But they are being translated to vhdl that evokes an error condition when they are executed, despite the output being discarded, so the semantics causes an abort, where it does not cause an abort in Clash, so the semantics of the translation differ, and it is not correct.

What do I have to do to work around this? (what you need to do to fix it is up to you :-).

[I think the translation of if then else is at fault. How ... I would need your explanation of what you think was wrong and why you think it is fixed!]

Thx again. I'll pick up the code tomorrow, I hope.

Peter

al...@qbaylogic.com

unread,
Jun 1, 2021, 11:45:04 AM6/1/21
to Clash - Hardware Description Language
> Can you explain what the fault was and what the cure is? I understood neither of them, not speaking more than a few phrases of VHDL.

Haskell's shift/rotate functions take an Int for the amount to shift/rotate by. This is somewhat of a bad API, as shiftL/shiftR/rotateL/rotateR forbid negative amounts anyway (for most types at least; including ones builtin to GHC). In VHDL however, the amount to shift/rotate by is a natural (e.g. the maximum value of a twos complement 32-bit number in GHDL, and probably many commercial simulators). This means the black boxes for these functions produced incorrect VHDL by simply rendering the arguments directly, because the VHDL simulator would crash evaluating the shift/rotate.

> what does the others =>'X' mean? Is that a variable assignment? Is that else just to write something meaningless and harmless because there  has to be an "else" in VHDL so not to bother myself about it?

It means set all bits in the result to 'X' (strong drive, unknown value). This matches the Haskell / Clash semantics of throwing an XException when you use shiftL/shiftR/rotateL/rotateR with a negative shift/rotate amount. The else is required: the VHDL standard specifies "There must be a final unconditional else expression". Another important detail (you didn't mention, but for completeness if anyone else reads this) is the pragma translate_off / translate_on. Anything between these won't be synthesized, so the resulting logic is still just the shift you asked for, but it won't crash in simulation.

> Was the mistake that  shift x y in Clash was translated first to if y>=0 then shiftL x y else shiftR x (-y),  and then the shiftL part was translated to just  result <= shiftL x y  with no guard?

Kind of. The Haskell definition had the guards so would only ever translate to a shift by a non-negative amount or VHDL with guards in. The shiftL/shiftR/rotateL/rotateR don't have guards, so when used directly would produce VHDL without guards which might break the invariant that the amount to shift/rotate by is non-negative.

> It seems to me you just added an extra guard condition when there already was one there.

Yes, but only when using shift/rotate. If you use shiftL/shiftR/rotateL/rotateR directly there was no guard. The extra guard isn't synthesized anyway so it won't affect the resulting netlist etc.

> The original VHDL translation was valid, by the way! It compiled. It was not correct, overall, but I am not convinced the fault is there.

Valid in a sense. However, it was willing to break invariants of shift_left / shift_right / rotate_left / rotate_right.

> What do I have to do to work around this?

I think it was Shakespeare who said "there is no darkness but ignorance". I think I'm a bit too in the dark to give you any particularly useful suggestion here. If you can find something that shows Clash's semantics differing to it's generated HDL we can try and fix it for you though, so with that in mind perhaps trying to minimize your test bench such that it still shows the issue, but is small / self-contained as an example.

Hope that helps,

  - Alex

peter.t...@gmail.com

unread,
Jun 1, 2021, 7:27:30 PM6/1/21
to Clash - Hardware Description Language
Thanks very much! But I admit to it still being low on detail for me. It seems that some 32/64-bit signedness problem is mixed in to trigger the problem? Have you a specific example with actual numbers? That would do it for me!

The  theory should explain why replacing every instance of "shift x y" with "if y>=0 then shiftL x y else shiftR x (-y)" makes the problem go away for those numbers (it did it for me). As I understand it, that should have been what is done anyway.

(could my perception of when y>=0 with certainty be better than a compiler's - possibly so)

I think the problem occurred when y in shift x y was either the constant 1 or -1 in a 1-bit range, and got sign-extended to 64-bits, if that's a clue. I can find out which it is ...

al...@qbaylogic.com

unread,
Jun 2, 2021, 3:02:33 AM6/2/21
to Clash - Hardware Description Language
> It seems that some 32/64-bit signedness problem is mixed in to trigger the problem?

If you're using GHDL (I imagine it's the same in most other simulators) then integer is a 32-bit signed number. If you're running on a 64-bit machine then Int/Word will be 64 bits in Clash. So it's certainly possible to have some problems from too large a shift amount becoming a negative number in VHDL, and throwing an error in simulation.

> replacing every instance of "shift x y" with "if y>=0 then shiftL x y else shiftR x (-y)" makes the problem go away

I'm not sure why this would be the case, it's very similar to the original Haskell definition:

```
x `shift` i
  | i<0 = x `shiftR` (-i)
  | i>0 = x `shiftL` i
  | otherwise = x
```

  - Alex

Peter Breuer

unread,
Jun 2, 2021, 4:54:17 AM6/2/21
to clash-l...@googlegroups.com
On Wed, 2 Jun 2021 00:02:33 -0700 (PDT)
"al...@qbaylogic.com" <al...@qbaylogic.com> wrote:
> > y
> else shiftR x (-y)" makes the problem go away
>
> I'm not sure why this would be the case, it's very similar to the
> original Haskell definition:

Me neither. But I have checked and the error at the start of this
thread comes from Clash's

shift (283::Int64) (1::Int64)

(not sure if those args both really are constants .. maybe; they are
expressions in my code, which may be deducibly constant)

That has ended up in vhdl with a shiftR (!!) and a negative (!!) shift
value, as follows:

signal b : signed(63 downto 0);

... shift_right(to_signed(283,64),to_integer(b))) ...

b <= -x1_1;

Can you interpret for me the vhdl scheduling assignment for b?
Particularly the expression. I read that as a 1-bit (signed?) number
(x1_1) being negated and cast to 64-bit signed. Is that it? If so, it
would be sign-extended and end up as -1 in 64-bit signed (am I right?).

Or is it negated in 1-bit (same thing), and then cast? Was it in
64-bit all the while?

Q: WHY was shift_RIGHT ever introduced into the vhdl?

Two possible answers IMO:

1) a mistranslation of Clash shift to shift_right ALONE.
Some weird confusion arose that selected shift_right, which needs
fixing.

2) a morally correct translation of Clash shift to a vhdl
compound in which shift_right figures but is not intended to
be executed, but is, and that needs fixing. That would be
a bigger error.

[I recorded the vhdl around the shift_right as
c$case_scrut_6\ <= to_signed(1,64) when x1_1 >= to_signed(64,64)
else to_signed(0,64);
with (\c$case_scrut_7\) select
result_36 <= intermediate(i_15+1) when x"0000000000000001",
(\x#2\ xor (shift_right(to_signed(283,64),to_integer(b)))
when others;
and I see no shift_left alternative nearby, so I tentatively rule out
(2) in this case. ]

Now, for (1) ... you are telling me that the haskell is translated, not
the Clash, and haskell has split the shift up into three clauses, one
for shiftR, one for shiftL (and one for nothing :). Only one of those
has survived but it is the _wrong_ clause.

x `shift` i | i<0 = x `shiftR` (-i)

HOW can anything think that what ends up as "x1_1" is less than zero?

It can if it reads it as 1-bit and sees the top bit is set!

Is that what happened?

Peter

Christiaan Baaij

unread,
Jun 2, 2021, 5:58:39 AM6/2/21
to clash-l...@googlegroups.com
The "issue" is that we translate almost everything to concurrent VHDL statements.
So the following Haskell code:

if i < 0 then x `shiftR` (-i) else x `shiftL` i

used to be translated to the following concurrent VHDL statements:

b <= -i
thenBranch <= shift_right(x, b);
elseBranch <= shift_left(x, i);
ifThenElseResult <= thenBranch when (i < 0) else elseBrach;

Which is perfectly fine from a circuit point of view: ifThenElseResult would never "observe" the "illegal" shift_right-by-a-negative-amount result.
However, the VHDL simulator does complain, as it wants to evaluate all the signal concurrently (to e.g. display them in the waveform).
So that's why we now changed how we translate `shiftR`.
And as Alex said, using the translate_on/off pragmas combined with the (others => 'x') branch will ensure that any circuit created by logic synthesis will not be any larger than a "naked" `shift_right`.



--
You received this message because you are subscribed to the Google Groups "Clash - Hardware Description Language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clash-languag...@googlegroups.com.

peter.t...@gmail.com

unread,
Jun 2, 2021, 7:34:33 AM6/2/21
to Clash - Hardware Description Language
Thank you Christiaan, that is exactly as I have been imagining what the compiler must be writing ( the launch/synthesis of two processes/circuits, both of which listen to the same input, but only one of the outputs from which is taken any notice of).  I just never saw the other branch of the IF/paralelel IF anywhere nearby in the originally syntehsized VHDL code and have been worrying since that  an early  evaluation to constants might result in only one branch being compiled/synthesized, and it is somehow the wrong branch.

I still can't tell that for sure because I never kept the original VHDL code and it would take 5h to resynthesize (so I won't! [Screams]).

You have additionally clarified that the semantic problem is most nearly attributable to GHDL, which is promoting a local warning that would have no further significance for synthesis into a global stop-working order in simulation!  Yes, it doesn't matter that the rightwards branch gets a negative number. It's not going to cause blue smoke in "reality", just some unintended value  on a wire that is gated away.

However, yes, there WILL be more problems of that sort. Every case statement and if statement in Clash/Haskell will give rise to the same parallel execution/connection of every case and branch,  though the code in that path supposes it will not execute in circumstances other than the guard allows.

One needs the same treatment (guard!) on division, for example, to stop divide by zero, but you can't tell that the author intended 0 to be guarded away as a divisor ... the author's guard might be far back, out of scope for the compiler.

The only general cure   is to migrate the guards beyond the branch point into the parallel processes/circuits that are launched/connected (with the others=>'X' as an else alternative on each)

As I understood Alex's explanation, instead there is now an extra guard on the vulnerable atomic primitives, shiftR, shiftL here. But it looks like division is  next on that action list,  (assuming the author did not INTEND it to get a zero divisor) and will there be more like that. Mod? Can addition overflow? Underflow? Multiplication? All the float stuff to come?

IMO you should not  guard the primitives, but instead push the guards on cases and conditionals past the branch points. Doesn't the dIvision  by zero possibility show that?

One might do some path analysis for primitives on the execution path beyond that which are vulnerable and not bother when there aren't any, in order to remove unneeded guard circuit elements, reducing costs.

Anyway, all I should care about is how to deal with it at my level and leave all that to you :-). How did my putting my own guards on the shift statements cure the problem for me? (I put shift x y = if y >= 0 then shiftL x y else shiftR x (-y) in every class that might otherwise have got a default definition, and removed shift in favor of shiftL or shiftR wherever I could guarantee it would be one or the other). This is the vhdl that is emitted now:


signal x1_1 : signed(63 downto 0);
...
with (\c$case_scrut_6\) select \c$case_alt_5\ <= intermediate(i_15+1) when x"0000000000000001",  
                                                                                    (\x#2\ xor (shift_left(to_signed(283,64),to_integer(x1_1)))) when others;
\c$case_scrut_6\ <= to_signed(1,64) when x1_1 >= to_signed(64,64) else to_ signed(0,64);
\c$app_arg\ <= to_signed(1,64) when x1_1 >= to_signed(0,64) else to_signed (0,64);
x1_1 <= (to_signed(63,64) - (signed(std_logic_vector(\c$x1_app_arg\)))) - to_signed(8,64);

and I still do not see another shift for the alternate branch anywhere near it, before or after. There is no shift_right in the vhdl file. It seems unarguable to me that if two branches/cricuits were generated originally at some level, then one has subsequently been eliminated by the compiler.

Before one had:

      \c$case_scrut_6\ <= to_signed(1,64) when x1_1 >= to_signed(64,64) else to_signed(0,64);

      with (\c$case_scrut_7\) select
        result_36 <= intermediate(i_15+1) when x"0000000000000001",
                     (\x#2\ xor (shift_right(to_signed(283,64),to_integer(b)))) when others;

with
      signal b    : signed(63 downto 0);
      b <= -x1_1; 

It is still my guess, in virtue of being nearly VHDL-blind, that  the wrong branch was eliminated. Maybe via constant evaluation and path pruning?
(I see now that x1_1 is a signal name, not a literal hex constant!  Somebody might have told me of my glaring error  ...)

Does that give any insight? Something was wrong. A known change has been made (write shift with guards in Clash), and now it is right.

Peter

peter.t...@gmail.com

unread,
Jun 2, 2021, 10:25:43 AM6/2/21
to Clash - Hardware Description Language
Correction: I have put back the original shift expression in Clash and resynthesized the VHDL (I was curious enough to invest these few hours!), and yes, I do see both branches of the conditional, laid out in parallel, in the original translation. So Christiaan's diagnosis is correct and also absolutely complete:

with (\c$case_scrut_6\) select \c$case_alt_5\ <= intermediate(i_15+1) when x"0000000000000001",
     (\x#2\ xor (shift_left(to_signed(283,64),to_integer(x1_1)))) when others;
\c$case_scrut_6\ <= to_signed(1,64) when x1_1 >= to_signed(64,64) else to_ signed(0,64);
with (\c$case_scrut_7\) select result_36 <= intermediate(i_15+1) when x"0000000000000001",
   (\x#2\ xor (shift_right(to_signed(283,64),to_integer(b)))) when others;
...
b <= -x1_1;


The later rewrite of the clash code must have made the direction of the shift unique  by allowing the compiler to substitute through in the conditional. Or something.

Now for division ... (but with confidence in a full understanding, now - tx to all!)


Peter

peter.t...@gmail.com

unread,
Jun 3, 2021, 11:22:39 AM6/3/21
to Clash - Hardware Description Language
Back to division. Here is the division problem (evokes error for divide by zero in simulation in GHDL):

divu_c :: KnownNat n => Unsigned n -> Unsigned n -> (Unsigned n,Bool)
divu_c x y = (z,o) where (z,o) = case y of 0 -> (0, True);  _ -> (x `div` y, False)

The problem (as I understand it) is that in case y=0 the "div x" remains connected to the y input, even though the output is ignored. I am surprised it is close enugh to the branching point to catch the unguarded onward transmission of prior data lines, but, shrug, apparently.

Here is a proposed solution:

divu_c :: KnownNat n => Unsigned n -> Unsigned n -> (Unsigned n,Bool)
divu_c x y = (z,o) where (z,o) = case y of 0 -> (0, True);  _ -> (x `mydivu` y, False)

mydivu :: KnownNat n   => Unsigned n -> Unsigned n -> Unsigned n
mydivu x y = div x (max y (y-1))

I hope that max on unsigned doesn't branch ... if it does, please somebody suggest something fairly primitive that will produce any nonzero result at all when y=0. The idea is to force the wire carrying y to div to be broken up.


peter.t...@gmail.com

unread,
Jun 6, 2021, 10:02:40 AM6/6/21
to Clash - Hardware Description Language


For completeness, (I believe) I fixed the problem that divide by 0 is running and  erroring  when it should not in simulation in GHDL. I eventually used

mydivu :: KnownNat n => Unsigned n -> Unsigned n -> Unsigned n
mydivu x y = div x (y .|. (signum y `xor` 1))

I'm avoiding if y==0 then 1 else div x y   because the y is still connected to the division circuit in GHDL even when the conditional says to discard the _result_, from that and it errors, stopping the simulation.

I believe that broke the  connection  y -> div in the erroring branch. It  should be just an extra little circuit that changes the bottom bit when 0 is detected. There is a maybe-small chance that one of Clash or vhdl/ghdl will detect that the top 63 bits of y are not touched and do that efficiently.
Reply all
Reply to author
Forward
0 new messages