final synthesis questions

43 views
Skip to first unread message

peter.t...@gmail.com

unread,
Apr 8, 2021, 10:55:00 PMApr 8
to Clash - Hardware Description Language
Essentially, how do I execute the vhdl, verilog or systemverilog generated in some open source simulator? I have  ghdl  for vhdl, iverilog  for verilog, and nothing really  for systemverilog (verilator translates to c++ but is ... not in a good enough condition to use, in my opinion, having examined the code quite closely).

OK, so it's not exactly "clash", but you must be testing the output SOMEHOW! What are you doing, if it is not these?

1) the vhdl generated contains a few __VOID__ constructs in just one module. The verilog and systemverilog do not.

 process(vec_index_26,karg,__VOID__,x_18)
          variable ivec_9 : testbench_types.array_of_maybe_98(0 to 3);
        begin
          ivec_9 := karg.iqueueentry_sel7_insn_reg_i;
          ivec_9(vec_index_26) := std_logic_vector'("1" & (std_logic_vector(unsi
gned((std_logic_vector'((std_logic_vector(x_18))))))));
          \c$app_arg_109\ <= ivec_9;
        end process;
      end block;
      -- replace end

That was the kind of thing that appears. Is that translating something common, like a vector copy? (I've just glanced at it - I see no arithmetic operators, so what else can it be?)

What was the trick for getting references to the Clash code in? The systemverilog seems to have it - I don't know if I did anything special for that.

2) For verilog and iverilog, I have little idea what the words in the manual pages mean, because they seem to be using terms I have never heard of, or using common terms like "scope" in some special way that does not conform to the common meaning. I believe I am supposed to make a .vpp file which then can be executed by the runtime engine  "vpp", and I have produced a 3GB file:

  -rwxr-xr-x 1 ptb 3100909942 Apr 8 07:45 TestBench.vpp*
  (file:) TestBench.vpp: a /usr/bin/vvp script, ASCII text executable, with very long lines

I ran "iverilog -oTestBench.vvp   ....long list of .v files" (I think) to build that, and it dropped out about 17h later. But am I supposed to name some entry point, somehow? The manual talks about -mmodule as " Add this module to the list of VPI modules to be loaded by the simulation." but what is the base set of modules and what do I have to add to that, if anything, and does "loaded" mean or imply "run", which is what I want, and what is a "module" in their terms, anyway! All clash has produced is files, with a v ending. There is no tie-up. What I want to do is run what is described in those files. HOW?

Can somebody give me an example, please. Surely you are doing something of the sort?

3) Does "testbench" mean something special to hardware folks?  What? I want to run things as a test, not compare the output with the output from some previous run to get a boolean. I have defined via ANN something for synthesis that produces not a Bool signal, but the signal I am interested in seeing, which carries type Trace, for my data type Trace.

Is that going to work in a simulator? I have picked up the use of stopping the testbench clock at some point when the trace shows STOP from the example given in the Clash manual, but I have three clocks, at different speeds. Do all of them have to be stopped to stop the simulation? How?

My testbench function has no inputs, and just one output:

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

Is that going to work in a simulator? I noticed when looking at the verilator translator for systemverilog that their  manual pages talked abut needing a clock signal to start the simulation. Should I be leaving the/a clock as an input rather than generating it internally?

When I run what is supposed to be the iverilog simulation engine over the vpp file, it produces some messages, but then just sits there at 100% cpu (in bursts). Does it
know what it is supposed to be doing, or how to display the output?

  % vvp -Nsv TestBench.vpp
Compiling VVP ...
 ... VVP file version 12.0 (devel)
Compile cleanup...
 ... Linking
 ... Removing symbol tables
 ... Compiletf functions
 ... 14859537 functors (net_fun pool=649592832 bytes)
            8788675 logic
                  0 bufif
                  0 resolv
             100374 signals
 ... 14321292 filters (net_fil pool=1719140352 bytes)
 ...  5573941 opcodes (133914624 bytes)
 ...  6810502 nets
 ... 14859537 vvp_nets (832543936 bytes)
 ...    30677 arrays (1828926 words)
 ...       93 memories
                 93 logic (366248 words)
                  0 real (0 words)
 ...  3219839 scopes
 ... 402.589 seconds, 13306812.0/2151332.0/2804.0 KBytes size/rss/shared
Running ...
 ...execute EndOfCompile callbacks
 ...propagate initialization events

(and nothing).

Does any of that mean anything to anyone?

4) I am even more at sea with how to run ghdl on .vhdl files. It appears that I have to select an order of appearance on the command line, so things are defined before they are referenced.

The best I have managed is

  ghdl -a --std=93c --workdir=. --work=testbench testbench_types.vhdl ... testbench.vhdl

but that finishes with various complaints due to the  __VOID__ that appears in one of the files. Have  I got the right standard? This one is (also) complaining about variables starting with "_" and containing two "_"s in a row. Other standards choices complain about other things. This one seems least conflictive.
 

Please can I have an example of execution of generated code on some (hopefully open source) simulation engine? Failing that, any hints at all are welcomed here, including translations of jargon words and concepts.

[I heard yesterday that a US patent has now been allowed for the underlying technology that this implements/exercses/demonstrates. Clash has allowed  me to both test the underlying computational theory  behaviorally and confirm  it can be  synthesized in conventional hardware, which was not a priori clear - now I would like to complete that direction of investigation by running the synthesised code. Perhaps  I can get some timing bounds out!]










 




ÉRDI Gergő

unread,
Apr 9, 2021, 2:49:49 AMApr 9
to Clash - Hardware Description Language
On Thu, 8 Apr 2021, peter.t...@gmail.com wrote:

> Essentially, how do I execute the vhdl, verilog or systemverilog generated in some
> open source simulator? I have  ghdl  for vhdl, iverilog  for verilog, and nothing
> really  for systemverilog (verilator translates to c++ but is ... not in a good enough
> condition to use, in my opinion, having examined the code quite closely).

Have you seen my Clashilator project, that integrated Verilator and Clash?
If you're interested, I think the following is a good starting project,
since the circuit itself is very simple, but not trivial:

https://github.com/gergoerdi/clash-pong

If you build it with the `verilator` flag, you will get a simulator that
uses Verilator to run the full circuit to get its VGA output, and then
uses Haskell to interpret that VGA signal by drawing it into an SDL
window.

Martijn Bastiaan

unread,
Apr 9, 2021, 3:54:27 AMApr 9
to clash-l...@googlegroups.com
but that finishes with various complaints due to the  __VOID__ that appears in one of the files. Have  I got the right standard? This one is (also) complaining about variables starting with "_" and containing two "_"s in a row. Other standards choices complain about other things. This one seems least conflictive.

 This is a bug in the Clash compiler (or a custom primitive). Do you have any small examples that this triggers on, so we can investigate?

Op vr 9 apr. 2021 om 08:49 schreef ÉRDI Gergő <ge...@erdi.hu>:
--
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/alpine.DEB.2.22.394.2104091447470.245873%40galaxy.

Christiaan Baaij

unread,
Apr 9, 2021, 4:16:10 AMApr 9
to clash-l...@googlegroups.com
The __VOID__ thing could also be from forgetting to compile with -fclash-float-support when using the `Float` or `Double` data type.

peter.t...@gmail.com

unread,
Apr 9, 2021, 4:54:23 AMApr 9
to Clash - Hardware Description Language
Christiaan ..  I think the possibility that Float or Double is involved can be ruled out because (a) the verilog and systemverilog syntheses do not have the __VOID__ and I know I would have been equally forgetful about command line parameters for those, as I have been repeatedly doing those over the past few days (b) I am no longer using Float or Double types internally, except for convenience in interactive printouts :

data FLOAT  = FLOAT UInt32
     deriving (Eq,Default,NFDataX,Generic,BitPack)
instance Show FLOAT
  where show (FLOAT x) = show (cast x :: Float)

Only if the vhdl synthesis somehow used the Show of my FLOAT (or DOUBLE) would it  possibly be involved, and I also can see ... that (c) neither my FLOAT nor DOUBLE that I use throughout nstead of Float and Double are imported into the problematic module, nor do types Float nor Double appear raw in it. Nor can I think of a way those would be involved semantically.

I might be able to locate what this is from. I can see the equivalent paragraph before it in the systemverilog, where lines in the clash source are referenced.

This is "Clash, version 1.2.4 (using clash-lib, version: 1.2.4)". The command launched is

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

(s/vhdl/verlog/ etc)

I'll get to everyone else shortly.

peter.t...@gmail.com

unread,
Apr 9, 2021, 6:08:50 AMApr 9
to Clash - Hardware Description Language
Martijn .. I think the equivalent sections in the VHDL and Verilog are these (this is going to be horrible layout).

VHDL first. Verilog second. I am keying off the signal assignment to c$case_scrut_37 just above the guilty piece of code, which only occurs in one place in both VHDL and Verilog.

     \c$case_scrut_37\ <= \c$vec_2\(vec_index_25);                 
end block;
-- index end

-- replace begin
replacevec_9 : block
  signal vec_index_26 : integer range 0 to 4-1;
begin
  vec_index_26 <= to_integer(to_signed(0,64))
  -- pragma translate_off
               mod 4
  -- pragma translate_on
               ;


  process(vec_index_26,karg,__VOID__,x_18)
    variable ivec_9 : testbench_types.array_of_maybe_98(0 to 3);
  begin
    ivec_9 := karg.iqueueentry_sel7_insn_reg_i;
    ivec_9(vec_index_26) :=
      std_logic_vector'("1" & (std_logic_vector(unsigned((std_logic_vector'((std
_logic_vector(x_18))))))));
    \c$app_arg_109\ <= ivec_9;
  end process;
end block;
-- replace end

What I believe is the equivalent Verilog is:

    assign c$case_scrut_37 = karg[3014:2998];
   
    assign c$vec_9 = karg[3014:2947];
   
    // vector replace begin
    genvar i_66;
   
    for (i_66=0;i_66<4;i_66=i_66+1) begin : vector_replace_9
      assign c$app_arg_104[(3-i_66)*17+:17] = (64'sd0) == i_66 ? {1'b1,((x_18))}
 : c$vec_9[(3-i_66)*17+:17];
    end
   
    // vector replace end
   
    assign c$vec_10 = karg[2946:2879];

The system verilog seems to have two of those things in, one after the other, both referencing the same source:

     assign c$case_scrut_37 = c$vec_2[(64'sd0)];

    // KPU/Transcode.hs:3063:45-77
    // KPU/UInsn.hs:331:22-31
    // replaceVec start
    always_comb begin
      c$app_arg_104 = TestBench_types::array_of_4_logic_vector_17_from_lv(karg.I
queueEntry_sel7);
      c$app_arg_104[(64'sd0)] = {1'b1,((x_18))};
    end
    // replaceVec end
   
    // KPU/Transcode.hs:3044:45-77
    // KPU/UInsn.hs:332:22-31
    // replaceVec start
    always_comb begin
      c$app_arg_105 = TestBench_types::array_of_4_logic_vector_17_from_lv(karg.I
queueEntry_sel8);
      c$app_arg_105[(64'sd0)] = {1'b1,((x_18))};
    end
    // replaceVec end

The Uinsn.hs references are to elements of a data structure:

                     -- data holders
                   , insn_reg_i :: Vec 4 (Maybe Spr16)  -- up to 2 reg/spr refs  (line 331)
                   , insn_reg_o :: Vec 4 (Maybe Spr16)  -- up to 2 reg/spr refs (line 332)

The Transcode.hs references are to the module that comes out with the __VOID__ in after vhdl synthesis.

Line 3044 is:

dinsn DD16 =  f -- unsigned, reg index, dest (0)
                   where
                   f (s,x) =
                       let a = zero_extend (sr16 x)
                       in (s { insn_reg_o = replace 0 (Just a) (insn_reg_o s)     <----- here
                             }, rotateLeftS x d1)

Line 3063 is:

dinsn AA16 =  f -- unsigned, reg index, source (1)
                   where
                   f (s,x) =
                       let a = zero_extend (sr16 x)
                       in (s { insn_reg_i = replace 0 (Just a) (insn_reg_i s)      <----- here
                             }, rotateLeftS x d1)

There are other clauses in the dinsn function definition with more or less the same thing. They may or may not give rise to the same complaint. I'm not chasing further. That looks like replace in a vector with Maybe elements. It should be innoccuous. So .. yes, bug.


OK?

PTB

peter.t...@gmail.com

unread,
Apr 9, 2021, 8:45:09 AMApr 9
to Clash - Hardware Description Language
Gergo - I am looking at clashilator  https://github.com/gergoerdi/clashilator/tree/c1125a706fe378c1cd877a937fc9c317aaef49ed but I don't see how to compile it (or whatever one does!).

There is a README.md, but it loses me at "you put this in your Cabal file:" . I don't have a Cabal file. Never used cabal. Hacked it up a few times in making Clash and some supporting Haskell modules. probably with the aid of cabal-debian, but that's all. I don't want a cabal file, so it's no good telling me the solution to "don't have a cabal file" is "make one", if you are about to say that :-). The *.yaml files look like  a packaging mechanism, but I don't know it (too lazy to google!). Launching clash or haskell on any of the .hs files complains about missing Driver stuff:

Clash/Clashilator.hs:5:1: error: Could not find module `Clash.Driver.Manifest' Use -v (or `:set -v` in ghci) to see a list of the files searched for. | 5 | import Clash.Driver.Manifest

So there's a barrier there.

Now I think it is a very good idea to REPLACE verilator, because "basically it's a load of trash written by people who don't know what they are doing or who have forgotten how, if they ever knew" (quoting somebody who will be nameless that I asked after getting to the same conclusion myself). There is no need to use that obscurantist C++ in the first place, rather than simple C, and one needs to keep it simple because it must be clear that what verilator does is right, not so unclear that nobody can tell if it is wrong, which is the state of things now. I got the impression that they don't know they are trying to render a normal form of some kind for the generated code, probably a simple loop of one clock cycle divided up into up to100 internal delta cycles, and they don't know what that form is, nor do they realize that if they don't know then they will get a complexity explosion in which the code blows up exponentially as they apply successive processes to it, as in "x" becoming "x & y | x & ~y" in the boolean case.

Indeed, I've seen that complexity blowup  - I eventually got  verilator to translate one systemverilog module of 12900 lines for me, as produced by Clash, and it took nearly a full day to generate any output, went 20 or 30GB into swap, and the result was about 3.5 million lines of C++ code long,  as I recall. It took a week of otherwise fruitless experiment for me to get to that point. It will not work, no way, on the full set of output modules from my Clash project. I think it produces a single top object with an eval_step() method that is a delta cycle (or clock cycle?) long, but I never even attempted to link it and confirm.

The verilator process itself (it is called process() !) is a sequence of about 100 substeps, which appear to do things like pushing constants through the abstract syntax tree to evaluate them as far as possible. I could see no documentation on what they are each intended to do, and it is my impression that the expertise to know that has been lost from the project. There are obvious strictness problems  ... as far as I could see it translates down both sides of a branch or trinary expression, even though the conditional may be known. Several of the process steps are repeated, in apparently ad-hoc manner, without explanation.

Moreover, I don't think it is known to the present authors that it does normalizations .. I think they think it renders system verilog"as is", taking system verilog expressions and writing them as C++ expressions. Well ... if it does that, that would be great, and it wouldn't blow up Clash output 2000 times! I was tempted to take their abstract syntax tree, forget the C++ aspect, use just static stuff, and just stick a C code transformer on the front of that that borrows such translation elements as I can find in their code, and make sure that no further transformation is done beyond the minimum required to lay it out as a loop of assignment statements.

But I don't have even the week or two that would take.

So I am very happy to hear that you may have done precisely that work, and at the same time discouraged to hear that verilator is a working PART of the setup you have, because in my view it cannot be relied on, as sort-of ranted about above. Sorry, but I already put a week into it and that was where I got to.

If you could do what I suggested, take the system verilog parser and just stick a statement-for-statement transformer on the front, that would be much preferable! Let clash render the systemverilog.

Regards

Peter

ÉRDI Gergő

unread,
Apr 9, 2021, 9:02:02 AMApr 9
to Clash - Hardware Description Language
On Fri, 9 Apr 2021, peter.t...@gmail.com wrote:

> Gergo - I am looking at clashilator https://github.com/gergoerdi/clashilator/tree/c1125a706fe378c1cd877a937fc9c317aaef49e
> d but I don't see how to compile it (or whatever one does!).
>
> There is a README.md, but it loses me at "you put this in your Cabal file:" . I don't
> have a Cabal file. Never used cabal. Hacked it up a few times in making Clash and some
> supporting Haskell modules. probably with the aid of cabal-debian, but that's all. I
> don't want a cabal file, so it's no good telling me the solution to "don't have a
> cabal file" is "make one", if you are about to say that :-). The *.yaml files look
> like  a packaging mechanism, but I don't know it (too lazy to google!).

Clashilator's Cabal integration is not the only way to use it, but it is
the most automated one for the usual use case. If you don't use Cabal at
all, you can build Clashilator into a standalone executable:

$ clashilator --help
Clashilator - Clash <-> Verilator interface

Usage: clashilator (-i|--input FILENAME) (-o|--output DIRECTORY)
[-c|--clock NAME]
Generate Verilator source files and FFI interface from Clash manifest

Available options:
-i,--input FILENAME Clash manifest file
-o,--output DIRECTORY Where to put generated files
-c,--clock NAME Clock signal name
-h,--help Show this help text

Now, building Clashilator itself requires Cabal, but no more than Clash or
any other Haskell package. So if you have a cabal-debian-based workflow
set up, just use that.

Note that Clashilator is geared towards my use case of synchronous,
single-domain circuits which you want to run one clock cycle at a time.

peter.t...@gmail.com

unread,
Apr 9, 2021, 9:41:11 AMApr 9
to Clash - Hardware Description Language
> building Clashilator itself requires Cabal,

What is it that one has to do, please? There is a Setup.hs and a Cabal.hs buried at the end of  the src/ subdirectory hierarchy, and a main.hs at the top, but that's all the clues I can see!

peter.t...@gmail.com

unread,
Apr 9, 2021, 10:58:29 AMApr 9
to Clash - Hardware Description Language
Perhaps the problem is that clashilator is not built, but is instead a library for incorporation in other stuff? In which case the greater problem is that I don't know what it is for!

But if it is built, in some sense, I would be glad of explicit instructions. I don't know anything about cabal, if that is needed.

As I recall, I had some problems figuring out how to use verilator. I think the trick was

a)  verilator -Ifoo/systemverilog/ --prefix=TestBench TestBench_types ....Testbench

where TestBench_types and TestBench appear both of them on the command line, first and last respectively. I have forgotten if I had to list every other module too, but ... maybe, yes probably.   One has to tell it in which hierarchy to search for everything via -I. I got most of that by reading the source ...

Alternatively:

b) edit TestBench.sv to have `include "TestBench_types.sv"  and  import TestBench_types::*; at the top.

I am sorry, but I gave up on verilator as hopeless, and that's all I remember. I think args --debugi 3 --no-dump-tree --no-debug-leak -Wno-fatal -Wno-lint --cc were also essential! Otherwise one will get early death, combined with no progress indicators.

peter.t...@gmail.com

unread,
Apr 9, 2021, 2:57:06 PMApr 9
to Clash - Hardware Description Language
Martijn - I wondered why only the uses of replace in that one module of about 50 modules led to problems in the synthesized VHDL there and nowhere else. I thought perhaps the imports to the module might be peculiar in some way, but no, they are standard:

import Clash.Prelude
import Control.Monad.State

plus local imports in the same hierarchy. All the local imports are controlled explicitly in the import clause, and replace is not among them.

I wondered if all the uses of replace in that module had been compromised, or just those in which the index parameter to replace is a constant. That is rather hard to determine. There are 38 appearances of __VOID__ in the vhdl for the module,  and there are 4 appearances of replace with a variable argument in the clash source and 17 with a constant parameter (either 0, 1, 2 or 3). So I am confused. Perhaps 2*17+4=38 ?

I will try a local implementation of replace, just to vary something and see what it does.

I locate the translation for replace in the vhdl Clash_Sized_Vector.json as follows (the VOID appears as the third argument of four to the process):

, { "BlackBox" :
    { "name"      : "Clash.Sized.Vector.replace_int"
    , "kind"      : "Declaration"
    , "type"      : "replace_int :: KnownNat n => Vec n a -> Int -> a -> Vec n a
"
    , "template"  :
"-- replace begin
~GENSYM[replaceVec][0] : block
  signal ~GENSYM[vec_index][1] : integer range 0 to ~LIT[0]-1;
begin
  ~SYM[1] <= to_integer(~ARG[2])
  -- pragma translate_off
               mod ~LIT[0]
  -- pragma translate_on
               ;

  process(~SYM[1]~VARS[1]~VARS[3])
    variable ~GENSYM[ivec][2] : ~TYP[1];
  begin
    ~SYM[2] := ~ARG[1];~IF ~VIVADO ~THEN
    ~SYM[2](~SYM[1]) := ~TOBV[~ARG[3]][~TYP[3]];~ELSE
    ~SYM[2](~SYM[1]) := ~ARG[3];~FI
    ~RESULT <= ~SYM[2];

  end process;
end block;
-- replace end"
    }
  }


This is what appears in the vhdl output (see earlier post):

  ... process(vec_index_26,karg,__VOID__,x_18)...

SYM[1] is vec_index_26, SYM[2] is ivec_9. There appear to be THREE arguments to the process in the template, but FOUR arguments have appeared in the vhdl generated.

I confirm VARS[1] is karg, VARS[3] is x_18. I don't think anything else is involved. You should look at why four, not three, arguments to "process" ae generated by the generating mechanism, whatever it is.  I do not understand how the assignment just befre the signal update  can appear as

  ivec_9(vec_index_26) := std_logic_vector'("1" & (std_logic_vector(unsigned((std_logic_vector'((std_logic_vector(x_18))))))));

yet it does. Should it not be x_18 alone?

That's all I can offer as bug search help for now. Sorry!

Peter

peter.t...@gmail.com

unread,
Apr 9, 2021, 9:09:05 PMApr 9
to Clash - Hardware Description Language
Gergo - I think the fundamental question I need answered in "what is clashilator for" is: (a) is it to allow haskell/clash programmers to make use of and talk to the C++ output of verilator in further functions run or compiled from haskell/clash, or (b) is it to allow C++ programmers to incorporate subroutines from haskell/clash in further C++ functions that use the C++ output of verilator"?

I don't need either of those in themselves - I have no particular problem programming in C/C++ and do not intend to use anything else with the C/C++ from verilator. If the question is one of using haskell/clash print and show functions to help display the output from verilator produced C++ code, that's not a valuable goal for me, but it might wellsolve a maintenance problem in keeping things in step in general in heterogenously based projects, and would be helpful there.

The problem for me is (A) I don't in principle know how to APPLY verilator to clash system verilog output, and (B) verilator output is insufficiently specified and documented and plain wrong. Producing 3 million lines of C++ code from 12900 lines of clash vhdl output is wrong.

The answer to (A) is roughly verilator -Ifoo/src --prefix TestBench  -sv -cc TestBench_types -cc TestBench, plus a whole bunch of flags to stop it barfing and to make it give progress reports, but not compain about everything. The answer to (B) is that those folks figure out what they are doing and say it, and check they are doing it, or to just skip their stuff entirely.  Their parser may be useful, and other parts of their apparatus that traverses the abstract syntax tree to build C++ code too, if one can figure out which those are and/or what they do. But honestly, that all would be done better in haskell. Haskell is not going to be inline in the final C++ output, and converters from one abstract syntax tree to another are much more trivial to do in Haskell. It's a classic compiler compiler application. And I would prefer C output to C++.

One basic question not answered by the verilator project is what concept ("model") they transform to. I think it should be a network of processes producing and consuming streams connected via unix sockets or something similar (or dissimilar!). But I don't know if that is so or not, and I guess they don't really either. Anoher question is if those processes contain/encapsulate subprocesses also producing and consuming in the same way, or if the processes are all peers at top level.  It is my belief that either  Clash or Verilator ought to be aiming for that simpler model. It may even be that Verilator aims for an even simpler final model that that in which there is ONE process, consuming one stream of inputs and producing one stream of outputs, running ONE loop. That means substituting the code for one process into another and pushing its loop out to top level in the combo, repeat and stir. THAT is what I think verilator does, butI think it is doing "wrong".

I also think it is what Clash is doing, and doing right. I think what verilator aims to do at that conceptual level is done already by Clash, and done properly. In particular, I think questions of strictness are handled correctly by Clash, and incorrectly by Verilator. I think that part of the combinatorial explosion that Verilator obviously suffers in handling output from Clash is due to that incorrectness. But I simply can't tell and they can't tell, for lack of clarity as to aims and means.

So I would rather NOT see verilator code integrated, for any sense of the word, but instead I would like to see verilator simplified and the rump rewritten in haskell so it only handles the output of Clash. But then, why bother with even that? All one needs to do is parse the system verilog produced by Clash into an abstract syntax tree in haskell, and then write haskell code to interpret it as a network of functions or processes or whatever, as I described, then rely on the haskell compiler to render that efficiently into assembler/object code/C  or whatever (the difference between C and assembler is not so great that I could not break it back into C, modulo variable names).

peter.t...@gmail.com

unread,
Apr 10, 2021, 9:59:31 AMApr 10
to Clash - Hardware Description Language
Martijn - I have not been able to isolate the vhdl synthesis bug and it is not likely that I will be able to. It appears to be context dependent.
I can get the context down to a cuple of thousand lines, but further refinement would be a major odyssey. What I know:

The bug derives from a use of replace in a clause of a function that is applied via imap or map what appears to be 19 times (I deduce the map is over a 19 element vector). The use of replace is always with a constant index, 0, 1, 2 or 3, but I do not think that is significant.

Other uses of replace in the same module do not give rise to the problem.

The function in which the replace is used is like this:

dinsn DD6 (s,x) =
                       let a = zero_extend (rr6_ x)
                       in s { insn_reg_o = replace 0 (Just a) (insn_reg_o s) }

and that function has 25 stanzas, predicated on a different first argument pattern and 23 of those stanzas use replace as above, varying the "0" and the "insn_reg_o s" (to "insn_reg_i s" or "insn_const s", sometimes). Any use of replace anywhere in that function gives rise to 19 __VOID__ appearances in the vhdl. If replace is used twice in any one stanza, then 38 __VOID__ appearances result. It doesn't matter how many replaces I leave hanging around other than that. Only 19 or 38 __VOID__ appearances is ever the result.

I cannot say anything more useful for you, however. It is too deeply buried. The  function is part of a state changing operation, the state being "s", and rather large. The operation forms part of the transform step of a mealy machine, and that is what eventually the synthesis sees. I believe the context is responsible for handing arguments to the json template that are somehow misinterpreted, and you need to figure out what those are from the vhdl evidence (disclaimer: I may well be wrong! If I ever knew what I was doing I wouldn't write things with bugs).

I do note that the use of "dinsn" is rather unusual, however, and that may be a factor, so  I feel I can't get away with keeping you in the dark about that. The dinsn function above  loads up the state s with a component from token x in a place defined by the parameter "DD6". The overall function is to DECODE a microcode description in a table into a new microinstruction, the state "s" here. The microcode description is a line of tokens x.  The unusual aspect is that instead of applying the dinsn function in sequence to create a microinstruction, one application per token in the microcode description line, it is applied _in parallel_ to create, apparently, 19 different fresh microinstructions, each different from the default in one component. Those are then combined into one, the nondefault entries are picked from each, and some Deity is trusted to for the descriptions not overlapping.

Believe it or not, that lowers the computational complexity greatly. It's parallel parsing. But I should do better ...

Martijn Bastiaan

unread,
Apr 10, 2021, 2:13:47 PMApr 10
to clash-l...@googlegroups.com
Hi Peter,

__VOID__ usually shows up in the context of zero-width constructs. For example, "Index 1", "()", or "Unsigned 0". Does that trigger any thoughts?

My guess is that `~VARS` should ignore any zero-width constructs, but -at least as how the code is structured now- it's not going to be super easy. It would be super nice if we could get a small reproducer!

Martijn

Op za 10 apr. 2021 om 15:59 schreef peter.t...@gmail.com <peter.t...@gmail.com>:
--
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,
Apr 10, 2021, 3:14:41 PMApr 10
to Clash - Hardware Description Language
Martijn - I have been known to use zero width constructs, but it is difficult to envisage one here. All I can imagine is a microinstruction with a zero length definition, but I have checked the list and all have nonzero length descriptions for the part treated by "dinsn", in which the problem arises. There are definitions with zero length descriptions but only in the part NOT treated by "dinsn". A typical one such looks like:

      DData {  dflags = Nil                        <--- zero length but not treated by dinsn
                  , dinsns =  OP8 :> Nil           <-- nonzero length and treated by dinsn
                  , ...

That cannot influence. (So, yes, I may be wrong. Facts trump thoughts.)

Can you tell me anything about the language that is used to deliver arguments to the json templates and return the results? It is my impression that it is not call-by-value, which would imply that great care has to be taken in protecting  the arguments from unintended (mis)interpretations. The process() text produced has FOUR arguments, and the template has THREE (if I read it right). There must be a misinterpretation of an argument that has been handed to it!

I think VARS[3] searches out and produces a list of free variables that appear in the expression denoted by ARG[3]. I don't know at all what SYM[-] does. But the json template says

             process(~SYM[1]~VARS[1]~VARS[3])

and the vhdl produced has

             process(vec_index_26,karg,__VOID__,x_18)

where ARG[3] is std_logic_vector'("1" & (std_logic_vector(unsigned((std_logic_vector'((std_logic_vector(x_18))))))) .
And ARG[1] is karg.iqueueentry_sel7_insn_reg_i .

Can you try calculating VARS of those and see what you get in the substitution in "process(...)"?

Regards

Peter

peter.t...@gmail.com

unread,
Apr 12, 2021, 4:48:33 AMApr 12
to Clash - Hardware Description Language
Hi - can somebody remond me of what standard is the VHDL produced? I am getting this error compiling it with ghdl --std=93c  (93 accepting 97 syntax):

Test/TestBench/snoop_irq_r2_unit.vhdl:37:31:error: architecture "structural" of "snoop_irq_ack_unit" is obsoleted by entity "snoop_sr_sm_unit" ghdl-gcc:error: compilation error

architecture structural of snoop_irq_r2_unit is
  signal result                       : testbench_types.maybe_15;
  -- KPU/Snoop.hs:(86,1)-(89,38)     
...
begin
  snoop_irq_ack_unit_result : entity snoop_irq_ack_unit           <------------ here
    port map
      ( \c$case_alt\     => result


[I ask instead of try because it has taken days to get to the point of this error late in the compilation. Sorry! ]

The Clash source has:

snoop_irq_R2_unit ma mws = out
 where out = fmap f (bundle(snoop_irq_ack_unit ma mws,mws)) 
                <------- it's obviously from this!
 ...

Clues? Anything accepted with grateful thanks!

[I am trying to keep this short, but I have not forgotten about locating the __VOID__ problem in generated vhdl. I am going be looking at the constant index case for the uses of replace next. But I really could do with news on which of the four instantiated args to process() in the generated vhdl that I showed with __VOID__ in comes from VARS[1] in the json template, and which from VARS[3], so I know in which the __VOID__ is generated.  Otherwise I have a very warm positive feeling towards the vhdl generated by Clash. The language may be rebarbative, but the open source ghdl-gcc compiler for simulation looks solid as a rock (it starts with "g", which is always good news) and makes sense to me in what it does and says it does. It even says in its manual page for the first time that I have seen anywhere that engineers have their own different private language for the computing concepts involved, which explains a lot, and I am grateful! Apparently the h/w term for compiling  is "analysis"! And the h/w term for linking is "elaboration"!!  Am I wrong? I also think the mysterious "hierarchy" business that gets mumbled about everywhere is simply the dependency (directed) graph of what component definiton needs what other component definition inside, made over into a tree by splitting every node with two entering edges  into two copies of the same node, each with one entering edge. Has clash's "manifest" file ordered the corresponding list of files/modules/nodes so that each comes before its parent? I haven't checked! But that would be useful.]

PTB

Christiaan Baaij

unread,
Apr 12, 2021, 6:34:45 AMApr 12
to clash-l...@googlegroups.com
Clash, generates VHDL-93, and that entity instantiation is definitely '93 compliant:

Anyhow, googling the error does indeed seem to suggest something to do with ordering of analysis and elaboration.
The manifest file generated by Clash 1.4+ does indeed include a list of generated files that is sorted in (inverse) topologically order, i.e. loading them in that order should prevent any dependency issues.



peter.t...@gmail.com

unread,
Apr 12, 2021, 8:17:25 AMApr 12
to Clash - Hardware Description Language


Thank you Christiaan. I am following that up. I will eventually learn to google errors. I just note the manifest list does not appear to be "complete" in the fullest sense, now that I am using it  ...

(makefile)

LIST = $(shell sed -e '/Manifest/s/.*componentNames = //' < $(DIR)/$(MAIN).manifest | tr -d '[]"}' | tr ',' ' ')
FILES = $(DIR)/testbench_types.vhdl $(LIST:%=%.vhdl)

(I had to additionally also hack in the types declarations file there!)

% make
ghdl-gcc -a --work=TestBench --workdir=Test0/TestBench -Wno-hide -v Test0/TestBench/testbench_types.vhdl Test0/TestBench/dmm_unit.vhdl Test0/TestBench/dcache_unit.vhdl Test0/TestBench/snoop_sr_dce_unit.vhdl ...  Test0/TestBench/TestBench.vhdl
/usr/lib/ghdl/gcc/libexec/gcc/x86_64-pc-linux-gnu/10.2.1/ghdl1 --ghdl--work=TestBench --ghdl--workdir=Test0/TestBench --ghdl-Wno-hide -P/usr/lib/ghdl/gcc/vhdl/ieee/v93/ -P/usr/lib/ghdl/gcc/vhdl/ -quiet -o Test0/TestBench/testbench_types.s Test0/TestBench/testbench_types.vhdl /usr/bin/as -o Test0/TestBench/testbench_types.o Test0/TestBench/testbench_types.s
...
/usr/lib/ghdl/gcc/libexec/gcc/x86_64-pc-linux-gnu/10.2.1/ghdl1 --ghdl--work=TestBench --ghdl--workdir=Test0/TestBench --ghdl-Wno-hide -P/usr/lib/ghdl/gcc/vhdl/ieee/v93/ -P/usr/lib/ghdl/gcc/vhdl/ -quiet -o Test0/TestBench/pic16_unit.s Test0/TestBench/pic16_unit.vhdl
Test0/TestBench/pic16_unit.vhdl:38:26:error: no declaration for "ctrl_f" Test0/TestBench/pic16_unit.vhdl:45:30:error: no declaration for "pic_unit" ghdl-gcc:error: compilation error

The list of extra NOINLINEd purely internal subcomponents is also needed.

It's not a deal breaker on anything for me personally, but it might be very convenient to have the full list.

[The  .vhdl -> .s -> .o happening in-directory above rather than via /tmp is because I added a rule %.o : %.vhdl ; /usr/lib/ghdl/gcc/libexec/gcc/x86_64-pc-linux-gnu/10.2.1/ghdl1 -P/usr/lib/ghdl/gcc/vhdl/ieee/v93/  -P/usr/lib/ghdl/gcc/vhdl/  -quiet -o $(@:%.o=%.s); /usr/bin/as -o $(@:%.o=%.s) $^   and yes,  just %.s: %.vhdl without the assembly to a .o would work too and be shorter, since .s to .o is automatic ]

peter.t...@gmail.com

unread,
Apr 12, 2021, 9:05:30 AMApr 12
to Clash - Hardware Description Language
Actually the order of files in the manifest does not seem to be completely appropriate ... I am having to move entries earlier in the list to satisfy ghdl -a.

Is it possible that each entry has inadvertently been placed just before the _last_ of the files that need it in the list, instead of just before the _first_? It might be something like that. Just a guess.

peter.t...@gmail.com

unread,
Apr 12, 2021, 9:59:05 AMApr 12
to Clash - Hardware Description Language
I did think I had the list backward, because of the "reverse" mention, but no, TestBench is at the end of the list in the manifest, and that is the main target, so it cannot come first and surely must be last. Maybe it should be first, and then I should reverse the rest of the list? It's one of those things that's just too hard to tell much about by looking alone, and I don't have time for more right now, but the order in the manifest is neither right for ghdl -a in the forward direction, nor in the backward direction. I was  wrong in saying it missed  things, not noticing at first that the things claimed as missing were in fact there, just later in the list.
For example, these errors are noted from ghdl -a on the list as-is from the manifest:

Test0/TestBench/tdcache2_unit.vhdl:1879:36:error: no declaration for "tdcache_unit_0"
Test0/TestBench/tdcache2_unit.vhdl:2921:35:error: no declaration for "tdcache_unit"

And the manifest has:

...,"tacache_unit","tlb s_unit","pmdc","pmm_unit","tmmb_unit","tdcache2_unit","gpru","snoop_irq_r2_unit" ,"ws2xr_unit","spr_arbiter","gpr_arbiter","snoop_sr_sm_unit","pipe","dmmu","snoo p_sr_dme_unit","snoop_dmmucr_unit","imm_unit","tmm_unit","tracer","ctrl_f","pic_ unit","uniqe_id","itlb_unit","uniq_id_4","uniq_id_3","uniq_id_2","kmru","kmwu"," snoop_irq_ack_unit","reg_unit","uniq_id_1","uniq_id_0","uniq_id","xkub","write_s tage","dtlb_unit","tdcache_unit_0","tdcache_unit" ..., "TestBench"

It can't be reverse topological because TestBench is at the end. My general impression is that more complex stuff is later in the list. But I can't tell for sure by looking at it.

Martijn Bastiaan

unread,
Apr 12, 2021, 11:52:13 AMApr 12
to clash-l...@googlegroups.com
Hi Peter,

Pre 1.4 Clash doesn't promise any specific ordering. With 1.4 we've made quite some progress with external tool integration though, so it does! It now also stores files as JSON, so it should be more easily parseable by external tooling. If you're looking to parse it in Haskell though, use the readManifest function.

I've attached an example of a manifest file.

Cheers,
Martijn



Op ma 12 apr. 2021 om 15:59 schreef peter.t...@gmail.com <peter.t...@gmail.com>:
clash-manifest.json

peter.t...@gmail.com

unread,
Apr 12, 2021, 12:18:37 PMApr 12
to Clash - Hardware Description Language
That's the "update to 1.4 command"! :-)

Don't worry, it's no problem now I know the list isn't particularly ordered (but it looks to me as though it IS ordered, just not right). I expect ghdl -i will make a dependency list, or I can do do it from the haskell dependency hierarchy, or I can just plain plan the tree instead of ask what it is.

Thanks!

Peter

peter.t...@gmail.com

unread,
Apr 12, 2021, 9:27:43 PMApr 12
to Clash - Hardware Description Language
This is how to set up for open source -based simulation using Clash's vhdl output. It's the only option that has given me the sensation of solidity and robustness among those that I have investigated so far (iverilator for systemverilog, iverilog for verilog).

1) use ghdl! Use ghdl! Really! In the directory containing the .vhdl files, run
     ghdl-gcc --gen-makefile -g --work=TestBench -Wno-hide -v -frelaxed-rules TestBench > makefile
(assuming you have been making something wth t_name="TestBench" in the ANN synthesis clause of the Clash sources -- good luck, I am not at all clear on the naming conventions but you will likely be in a subdirectory called vhdl/TestBench at this point)
2) or 0) I am also not at all clear on whether one had to make the $(*$P&)& testbench-obj93.cf file first, but either now or 10s previously one had to run
  ghdl-gcc -i -g --work=TestBench  testbench_types.vhdl ... foo.vhdl .... TestBench.vhdl
where that stuff on the end is the list of .vhdl files in the directory beginning with testbench_types.vhdl and ending with TestBench.vhdl. In the middle the order may or may not count with -i, depending on the day of the week. If it does not matter, happy days. If it does matter, you will just have to reorder and repeat so that files eventualu come after other files they depend on, deleting any partial testbench-obj93.cf file that is improperly made because you got fed up and pressed ctrl-c or something else went wrong. At any rate, you will get testbench-obj93.cf eventually.
I have no real idea what this is for, since it looks to be a poor mans substitute for a makefile, but go with the flow.
3) the real joy is in the makefile you made in step 1), which may actually be 2) if the last step, called 2) was 0). That says everything, plain as day.
At the end of it is a section called
# Files dependences [sic!  "File dependenciEs", really ]
with an entry at the end:
TestBench.o: testbench_types.o ... foo.o ...
which actually does have all the files that you should have got in order by yourself in 2) in order to make this makefile in the order that you should have chosen. Clear? So if you made a mistake, you now know what it is, and can do it again, properly. Or not. Since by now everything is OK anyway. The files you  needed to have listed in the first place in 2) were testbench_types.vhdl ... foo.vhdl ... testBench.vhdl , in that order. OK?
You can check, because the files (modulo the wrong suffix) also appear in the dsired order reading down this section of the makefile in the left column before the colon:
testbench_types.o: ...
...
foo.o: ...
...
TestBench.o: ..
So that solves the dependency ordering problem, n ways.
4) the makefile also makes clear that you can forget all the useless instructions hanging around places like the ghdl info file, the web, the manual page etc, because it says explicitly what you have to do in the section just above the File dependencies section. That section has dependency+action entries of the form
foo.o : foo.vhdl bar.vhdl ....
    ghdl-gcc something
You can start with the most trivial, independent of everything file foo.vhdl and run the ghdl something command.
   ghdl-gcc -a foo.-g --work=TestBench foo.vhdl bar.vhdl ...
where the dots indicate any more files needed as predependencies. With any luck, there won't be any, not even one, not even a bar.vhdl.
You can start there and work your way up, by hand, or you can just not bother, and say "make TestBench.o", and everything will get done, in the right order, via magic makefile-ness.
That "-a" should morally be a "-c", because this is compiling the .vhdl to a .o, but they call it "analysis", so it's a "-a".
5) after compile, must come link to make an executable. That's a "-e", because they call it "elaboration". The magic entry giving the clue to that is higher up the makefile still, in a section called "Elaboration target". It looks like
testbench: testbench_types.o ... foo.o ... TestBench.o
    ghdl-gcc -e --work=TestBench -g testbench
where the list of files is the standard ordered list. All those .o files were made in 4). This links them into an executable called testbench, "this" being the ghdl-gcc -e -g testbench command given. That is real magic, because the command doesn't mention the .o files by name, so I suppose the testbench-obj93.cf file prepared in 0) or 1) or 2) or thereabouts must be being used by ghdl to supply the missing information. Personally I would have guessed one could just list the object files on the command line, but who knows! This is pretty much obfuscation of a perfectly simple, standard process, via calling everything something else and throwing in time-dependent undocumented effects from a secret  private make scheme.
6) at this point one can just run the executable. I haven't tried yet. The makefile suggests one may need some help with that from ghdl still, but surely not. The "Run target" section says to run ghdl-gcc -r testbench, but what can that do beyond supplying a few environment variables that specify places to look for libraries? It should be no trouble to skip and run ./testbench directly, having set LD_LIBRARY_PATH appropriately. Stracing it will show what's needed. Does it need any further hooks, like a non-trivial runtime envioriment that ghdl-gcc -r supplies and which the executable will hook into? Maybe. But I'm fed up with this deliberate obscurantism. It's just not that hard.

But solid that process is, once one has removed the appalling documentation from the picture. The makefile tells it all, once one hgas got it.

peter.t...@gmail.com

unread,
Apr 13, 2021, 4:43:18 AMApr 13
to Clash - Hardware Description Language
Another way of putting that compile process for simulation from Clash vhdl output is in terms of the underlying computing and phooey to analysis/elaboration:
A) do /usr/lib/ghdl/gcc/libexec/gcc/x86_64-pc-linux-gnu/10.2.1/ghdl1 -g --ghdl--work=TestBench --ghdl-Wno-hide --ghdl-frelaxed-rules -P/usr/lib/ghdl/gcc/vhdl/ieee/v93/ -P/usr/lib/ghdl/gcc/vhdl/ -quiet -o foo.s foo.vhdl
for every single individual foo.vhdl file to produce a corresponding foo.s assembler file.
B) assemble each of those foo.s assembler files to a corresponding foo.o object code file with /usr/bin/as -o foo.o foo.s
C) do an extra  semi-magic step to make a testbench-obj93.cf  file with ghdl-gcc -i -g --work=TestBench  testbench_types.vhdl ... foo.vhdl .... TestBench.vhdl (maybe it already got built incrementally as one built the assembler files one by one - I'm not clear on that) and then make an extra assembler file e~testbench.s and a list of vhdl source files e~testbench.lst from it as follows:
  /usr/lib/ghdl/gcc/libexec/gcc/x86_64-pc-linux-gnu/10.2.1/ghdl1 -g --ghdl--work=TestBench --ghdl-Wno-hide --ghdl-frelaxed-rules -P/usr/lib/ghdl/gcc/vhdl/ieee/v93/ -P/usr/lib/ghdl/gcc/vhdl/ --elab testbench -l e~testbench.lst -quiet -o e~testbench.s e~testbench .
Assemble the e~testbench.s assembler file to an extra object  code e~testbench.o file with /usr/bin/as -o e~testbench.o e~testbench.s .
D) link all those .o files into a testbench executable with /usr/bin/gcc -o testbench e~testbench.o /usr/lib/ghdl/gcc/vhdl/std/v93/std_standard.o /usr/lib/ghdl/gcc/vhdl/ieee/v93/std_logic_1164.o /usr/lib/ghdl/gcc/vhdl/ieee/v93/std_logic_1164-body.o /usr/lib/ghdl/gcc/vhdl/ieee/v93/numeric_std.o /usr/lib/ghdl/gcc/vhdl/ieee/v93/numeric_std-body.o /usr/lib/ghdl/gcc/vhdl/ieee/v93/math_real.o /usr/lib/ghdl/gcc/vhdl/ieee/v93/math_real-body.o /usr/lib/ghdl/gcc/vhdl/std/v93/textio.o /usr/lib/ghdl/gcc/vhdl/std/v93/textio-body.o  *.o -g /usr/lib/ghdl/gcc/vhdl/libgrt.a -ldl -lm /usr/lib/ghdl/gcc/vhdl/libbacktrace.a -L./ -lz -ldl -Wl,--version-script=/usr/lib/ghdl/gcc/vhdl/grt.ver -Wl,--export-dynamic .  
E) run with ./testbench . The symbol table is still there so one can usefully run under gdb with gdb ./testbench  and "r"(return) in the interactive window and view the call stack with  (interrupt and) "bt" (return). Continue with "c" (return). I have little idea what the executable does as yet because it seems to be setting up stuff more than doing anything so far ... the running process is 15GB into swap at 0% cpu and the attached gdb is at 890MB of ram and 100% cpu, but this is a simulation executable.

peter.t...@gmail.com

unread,
Apr 17, 2021, 3:08:54 AMApr 17
to Clash - Hardware Description Language
Suddenly, clash is producing a very short section of a very long sequence of signal declarations in which the signal names contain a dollar sign but do not start with a backslash, causing ghdl to error on the vhdl, there and nowhere else:

 transcode1.vhdl:10493:11:error: character '$' can only be used in strings or comments

The code sequence is as follows. The first bold line is line 10493. There are no other instances of lines starting "signal c[$]". This vhdl source file is 60687 lines long. This has never happened before in other productions of vhdl by Clash and I am very surprised by it:

  signal result_3_sel_alt_res_0                          : testbench_types.array_of_maybe_91(0 to 3);
  signal result_3_sel_alt_res_0_res                      : testbench_types.array_of_array_of_1_maybe_91(0 to 3);
  signal \c$vec_0\                                       : testbench_types.array_of_array_of_4_maybe_91(0 to 0);
  -- KPU/Transcode.hs:3016:1-11
  signal c$ws_case_alt                                   : testbench_types.maybe_47;
  -- KPU/Transcode.hs:3016:1-11
  signal c$wild4_app_arg                                 : signed(63 downto 0);
  signal result                                          : signed(127 downto 0);
  signal c$case_scrut                                    : testbench_types.maybe_47;
  signal result                                          : signed(127 downto 0);
  -- KPU/Transcode.hs:3016:1-11
  signal wild4                                           : signed(63 downto 0);
  -- KPU/Transcode.hs:3016:1-11
  signal ws                                              : testbench_types.array_of_maybe_47(0 to 1);
  signal \c$ws_case_alt_projection\                      : testbench_types.array_of_maybe_47(0 to 0);
  signal \c$vec_1\                                       : testbench_types.array_of_signed_128(0 to 0);
  -- KPU/Transcode.hs:3016:1-11
  signal c$ws_case_alt                                   : testbench_types.maybe_47;
  -- KPU/Transcode.hs:3016:1-11
  signal c$wild4_app_arg                                 : signed(63 downto 0);

  signal result : signed(127 downto 0);
  signal c$case_scrut                                    : testbench_types.maybe_47;
  signal result                                          : signed(127 downto 0);
  -- KPU/Transcode.hs:3016:1-11
  signal wild4                                           : signed(63 downto 0);
  -- KPU/Transcode.hs:3016:1-11
  signal ws                                              : testbench_types.array_of_maybe_47(0 to 1);
  signal \c$ws_case_alt_projection_0\                    : testbench_types.array_of_maybe_47(0 to 0);
  signal \c$vec_2\                                       : testbench_types.array_of_signed_128(0 to 0);

Line 3016 of Transcode.hs, where allegedly all those come from, looks completely innoccuous:

-- pick out first non-default value in a vector
findnotdflt :: (Default x, Eq x, KnownNat n, 1 <= n)
      => Vec n x -> x
findnotdflt v = case findIndex (/= def) v of                          <----- here
                  Just i  -> v !! i
                  Nothing -> def

Yes, in other places in the vhdl where those lines are allegedly the source, the backslashes appear (from line 320 here):

  -- KPU/Transcode.hs:493:1-15
  signal key                                             : unsigned(127 downto 0);
  signal result_3                                        : testbench_types.tup29;
  -- KPU/Transcode.hs:3016:1-11
  signal ws                                              : testbench_types.array_of_maybe_53(0 to 4);
  signal result_4                                        : signed(127 downto 0);
  signal \c$case_scrut_65\                               : testbench_types.maybe_53;
  -- KPU/Transcode.hs:3016:1-11
  signal i                                               : testbench_types.index_4;
  -- KPU/Transcode.hs:3016:1-11
  signal wild4                                           : signed(63 downto 0);
  signal result_5                                        : signed(127 downto 0);
  -- KPU/Transcode.hs:3016:1-11
  signal \c$wild4_app_arg\                               : signed(63 downto 0);
  -- KPU/Transcode.hs:3016:1-11
  signal \c$ws_app_arg\                                  : testbench_types.array_of_maybe_53(0 to 3);
  -- KPU/Transcode.hs:3016:1-11
  signal \c$ws_app_arg_0\                                : testbench_types.array_of_tup2_470(0 to 3);
  signal \c$tupIn_app_arg\                               : testbench_types.array_of_signed_128(0 to 3);
  -- KPU/Transcode.hs:3016:1-11
  signal ws_0                                            : testbench_types.array_of_maybe_53(0 to 4);
  signal result_6                                        : signed(127 downto 0);
  signal \c$case_scrut_66\                               : testbench_types.maybe_53;
  -- KPU/Transcode.hs:3016:1-11
  signal i_0                                             : testbench_types.index_4;
  -- KPU/Transcode.hs:3016:1-11
  signal wild4_0                                         : signed(63 downto 0);
  signal result_7                                        : signed(127 downto 0);
  -- KPU/Transcode.hs:3016:1-11
  signal \c$wild4_app_arg_0\                             : signed(63 downto 0);
  -- KPU/Transcode.hs:3016:1-11


That's all I know!

Clash 1.2.4

Regards

PTB

[I still haven't got a small example for the VOID compilation problem, but am making progress.]

peter.t...@gmail.com

unread,
Apr 18, 2021, 9:12:41 AMApr 18
to Clash - Hardware Description Language
News: this synthesizes to vhdl (with the error in the vhdl noted in the post above ... a signal called c$foo declared and referenced without backslashes front and back - repeatable, so apparently not a processor bit error!) but its homologue does not:

A) transcode_user1 = register def . fmap (fmap (uncurry transcode_step1_s)) . fmap (fmap (,False))

This does not synthesize:

B) transcode_user1 = register def . fmap (fmap (uncurry transcode_step1_s) . fmap (,False) )

The error reported during the compilation phase is seemingly not directly related but is a clue and I'll deal with that below:

KPU/Transcode.hs:540:1: error: Clash.Netlist(736): Can't translate non-tycon type:
Maybe (Index 4 -> (Maybe DFlag, Maybe (Index  4))                  [I cleaned that up]
The source location of the error is not exact ...
540 | transcode_user1 = register def

What leaps out at me is that never mind the actual error, composition under fmap (of Signal) of fmap (of Maybe) is rendered with failure, but composition of fmap of fmap is rendered successfully. Clash sees them as different and those two ought not to be different!

Am I wrong in understanding those as semantically equal, or is there something that I overlook there? That raises the hair on the back of my neck.

The relation with the problem noted in the post above is that the error seems to point to an auxiliary function that also seems to be referenced in the faulty vhdl that is produced. It is (super and user are evil twins, user as above, super with True instead of False, don't worry about it):

transcode_super1.vhdl:10189:11:error: character '$' can only be used in strings or comments

-- KPU/Transcode.hs:3100:1-11
signal c$ws_case_alt : transcode_types.maybe _11;           <-- 10189, should be \c$ws...\, but then it shoud be ..._alt_11 too, not ..._alt.
-- KPU/Transcode.hs:3100:1-11

-- pick out first non-default value in a vector
findnotdflt :: (Default x, Eq x, KnownNat n, 1 <= n)  => Vec n x -> x
findnotdflt v = case findIndex (/= def) v of                            <-- 3100

                  Just i  -> v !! i
                  Nothing -> def


That function is always indicated in the sections of the generated vhdl that seem to be wrong. It appears to be translated without change of variable names for every copy (sometimes?) and the names are not backslashed when they ought to be, and are not suffixed with _99 numbers to make them unique.

Does that ring any bells?

I would say "try 1.4 and see if it is different form 1.2.4". But I am too exhausted to start on that right now. Do you have  a login to a machine with 1.4 on that I could use to see if it makes a difference? (please .. maybe consider setting up a web-based service for testing).


The background, if it is of interest, is that  I have been trying to reduce to a small example to isolate the VOID problem.  I have generally been reducing size and complexity. I have been able to work around the VOID problem but not to isolate it. (A) is a part of the reduced code. To reduce further, I want to split the stateless component:

             transcode1 :: Signal dom (Maybe x, Bool) -> Signal dom (Maybe y)

It's nothing but a 140-case case-statement on each x, with the boolean as an extra parameter inside each case. I wish to force partial evaluation  into two functions, one with the Bool  True, and one with it  False.  The function in A/B) above is the one with False:

          transcode_user1 :: Signal dom (Maybe x) -> Signal dom (Maybe y)

This and its evil ("super1") twin can work in parallel on the same Maybe x input signal and which to take for output may be selected via the Bool parameter. 

The complexity of each twin should be at most half of the complexity of the whole and hopefully a whole lot less, but that remains to be seen.

Definition (A) seems to fail to trigger partial evaluation. Definition (B) seems to get partially evaluated as I expect, with the False propagated through, and then the compilation of the findnotdflt function fails. I have no idea how that can even be.

Ideas welcome!

Regards

PTB

peter.t...@gmail.com

unread,
Apr 18, 2021, 5:57:24 PMApr 18
to Clash - Hardware Description Language
1) Rewriting the simple function findnotdflt listed above has made all problems go away (vhdl is generated, it  is bigger, but valid, in that it compiles). 2) you should protect input and output ports named in an ANN synthesis directive with backslashes in the vhdl, as in \foo\, if they contain characters that are valid in Clash/haskell identifiers but are not valid in vhdl identifiers. That is "prime" (apostrophe). More?

For completeness, the rewrite of findnotdflt was not noteworthy. I did what I could to make it semantically different so Clash won't end up treating it the same, which is really silly for something that should do exactly the same thing. The new version requires  only at most one non-default value to be found among the vector of values that it selects from, and needs BitPack x instead of  Default x,Eq x. The pack should also not introduce undefined bits. The default value of x must pack to all 0s. Shrug. As needs must. I needed it to be different.  This is usually in practice going to be used as g(findnotdflt (map f v)) where f and g are inverses into and out of bitvectors with no undefined bits.

findnotdflt :: (KnownNat i, BitPack x) => Vec i x -> x
findnotdflt v = unpack(v2bv x')
     where m  = map (bv2v . pack) v
           m' = transpose m
           x' = map (\u -> if v2bv u /= 0 then 1 else 0) m'

(I left most of the types off to make it harder for you)

If hasUndefined synthesizes, one can put that in there ("not (hasUndefined(v2bv u)) && ...") and ditch some requirements.

Martijn Bastiaan

unread,
Apr 19, 2021, 9:47:43 AMApr 19
to clash-l...@googlegroups.com
Thanks Peter for the reports.

Am I wrong in understanding those as semantically equal, or is there something that I overlook there? That raises the hair on the back of my neck.

They should be equivalent. Is transcode_step1_s a function you could share? I've tried compiling findnotdflt in isolation, but that works fine.

you should protect input and output ports named in an ANN synthesis directive with backslashes in the vhdl, as in \foo\, if they contain characters that are valid in Clash/haskell identifiers but are not valid in vhdl identifiers. That is "prime" (apostrophe).

Identifiers mentioned in Synthesis pragmas are inserted at verbatim on purpose. We don't really know what tools support what, so we can't really tell what's a good identifier and what's not. Any identifier generated by Clash itself should fall within a subset supported by all synthesis tools though.


Op zo 18 apr. 2021 om 23:57 schreef peter.t...@gmail.com <peter.t...@gmail.com>:

peter.t...@gmail.com

unread,
Apr 20, 2021, 5:16:36 PMApr 20
to Clash - Hardware Description Language
I did not check in code that I already knew failed to synthesise, so it has been difficult to recreate the problem. Reversing the change I noted above nowadays fails to produce a vhdl synthesis error. "Fortunately" I did have  a check-in of earlier code, when I did not know it failed to synthesise, but before I had boiled the problem down as noted in posts above. Checking out that code and compiling/synthesizing it produces bad vhdl again:

transcode1.vhdl:21325:11:error: character '$' can only be used in strings or comments
transcode1.vhdl:21325:12:error: ':' is expected instead of "ws_case_alt"
transcode1.vhdl:21325:23:error: missing ";" at end of object declaration
transcode1.vhdl:21327:11:error: character '$' can only be used in strings or comments
...

  -- KPU/Transcode.hs:2933:1-11
  signal c$ws_case_alt                                   : transcode_types.maybe_7;                     <--- 21325 (missing backslashes)
  -- KPU/Transcode.hs:2933:1-11

  signal c$wild4_app_arg                                 : signed(63 downto 0);
  signal result                                          : signed(127 downto 0);                                     <---
  signal c$case_scrut                                    : transcode_types.maybe_7;
  signal result                                          : signed(127 downto 0);                                     <--- argh! repeat

UNfortunately, I have been unable to reproduce the problem when I create a single self-contained file for the source code, containing all the original imports. I have tried two or three times now. It amounts to 4000 and some lines, and compiles and synthesizes perfectly, to vhdl that compiles perfectly.

Linecounts:

(bad)
     51 Transcode.vhdl
 118174 transcode1.vhdl
    226 transcode_buffer.vhdl
     64 transcode_stage.vhdl
   2317 transcode_types.vhdl

(good)
      51 Transcode.vhdl
  152537 transcode1.vhdl
     226 transcode_buffer.vhdl
      64 transcode_stage.vhdl
    2353 transcode_types.vhdl

That seems to say that semantics stuff is missing in the bad output, as well as being wrong.

In the types declarations, the good-to-bad diff shows first difference as

-  function toSLV (p : transcode_types.tup2_6) return std_logic_vector is
+  function toSLV (p : transcode_types.tup2_5) return std_logic_vector is

and there are a lot of minor things like that. all I can see that is significant is that the bad types decls have this extra part  at the end which seems to repeat the same thing by name over and over with different body:

...
+  alias islv : std_logic_vector(0 to slv'length - 1) is slv;
+  begin
+    return (fromSLV(islv(0 to 523)),fromSLV(islv(524 to 1047)));
+  end;
+  function toSLV (p : transcode_types.tup2_29) return std_logic_vector is
+  begin
+    return (toSLV(p.tup2_29_sel0_array_of_a_tok_0) & toSLV(p.tup2_29_sel1_array_of_a_tok_1));
+  end;
+  function fromSLV (slv : in std_logic_vector) return transcode_types.tup2_29 is
+  alias islv : std_logic_vector(0 to slv'length - 1) is slv;
+  begin
+    return (fromSLV(islv(0 to 130)),fromSLV(islv(131 to 654)));
+  end;
+  function toSLV (p : transcode_types.tup2_27) return std_logic_vector is
+  begin
+    return (toSLV(p.tup2_27_sel0_array_of_a_tok_0) & toSLV(p.tup2_27_sel1_array_of_a_tok_1));
+  end;
+  function fromSLV (slv : in std_logic_vector) return transcode_types.tup2_27 is
+  alias islv : std_logic_vector(0 to slv'length - 1) is slv;
+  begin
+    return (fromSLV(islv(0 to 261)),fromSLV(islv(262 to 785)));
+  end;
 end;
 

There are so many minor naming differences in the good/bad behavioral part of th ecode  (transcode1.vhdl) that I can't make anything of it all. It's all more or less like this:

-    vec_index_317 <= to_integer((wild1_36))
+    vec_index_233 <= to_integer((wild_2))

It would take a lot of work to blank out that kind of thing!

I might try again.

PTB

Martijn Bastiaan

unread,
Apr 22, 2021, 4:10:56 AMApr 22
to clash-l...@googlegroups.com
Alright, thanks for the effort Peter, I've taken note of the issue.

I can't really debug this without the Haskell/Clash code. If it happens again, we should work on creating a minimal Haskell example that exposes the issue.

Again thanks for the effort,
Martijn

Op di 20 apr. 2021 om 23:16 schreef peter.t...@gmail.com <peter.t...@gmail.com>:
Reply all
Reply to author
Forward
0 new messages