Version: 2022-06-04

See also:

The next links describe a specific application where this approach is used:

../../../spe/html/SpeA-FPGA/SpeA-JavaFPGA.html This is the description of a FPGA design for SinglePairEthernet PHY layer. Work in progress.

../../../spe/html/SpeA-FPGA/SpeA-FPGA.html The older description based on only VHDL and Aldec-HDL simulation tool using. Comparing both descriptions you can also see advantages in the documentation of the behavior of the FPGA.

This is a preliminary information. The tool Java2Vhdl translator is in progress.

1. Motivation

VHDL was originally created in the 1980th for timing simulations of the behavior of ASICs See https://en.wikipedia.org/wiki/VHDL: In 1983, VHDL was originally developed at the behest of the U.S. Department of Defense in order to document the behavior of the ASICs that supplier companies were including in equipment. …​ (requested on 2022-05-25)

As a second approach it was later used to determine the content of FPGAs (input for routing).

In the earlier time of FPGA development, till mid of 1990th, usual the timing behavior was evaluated after the routing process, either by timing simulation, or by examination all delays of paths. With some manually settings (placing, special lines manually routed) the routing is repeated till all is met. For that also the VHDL was proper.

From approximately mid of 1990th usage of "timing constraints" become more importance. With timing constraints the router can decide by itself for using routing resources. It means constrains + logical description is sufficient to get a proper FPGA content. The importance of timing simulation is no longer given, at least for most of the FPGA content. Only for clock synchronization approaches or asynchronous parts of logic it is necessary.

The FPGA tool suites support the formulation of timing constraints usual in a special kind. VHDL has not an meaningful contribution for that.

Also the simulation of hardware designs needs often special tools. Test approaches with comprehensive functional tests are a special topic.

For all that reasons another way to formulate hardware design content inclusively timing constraints was searched.

This article offers the idea to formulate hardware design with timing constraints and elaborately possible functional tests, in Java language. The design is oriented to a one clock system as synchronous state machines. The source code which describes that and which is used for the functional tests is then translated to VHDL for the routing process. It means, the full FPGA content can be developed with functional simulation outside of FPGA tool suites. From the FPGA tools so much as necessary can be used, for example only the routine process with timing report (to check whether all is met), or for example also a timing simulation for specific signals outside the functional logic (synchronization with more as one clock or such one). That is all possible of course, but firstly the synchronous part of the design can be planned and tested completely only using a Java development environment (such as Eclipse). This supports also elaborately usage of test possibilities (input signal preparation, evaluation, test control etc.).

2. Why Java and not another language

Java is one of the most known and used programming languages. In opposite to for example C++ Java is a save language. The commonly small programming mistakes by the users do not cause a complicated behavior, they are obviously.

Java knows the final keyword, which is proper to formulate assignments in a unique kind. That is one of the benefits of the language itself. The const keyword in C/C++ cannot fulfill the same.

A second benefit is the possibility of formulate annotations, which are recognized also in the run time environment, not only for translation.

See the examples and patterns.

3. Writing hardware logic in Java, principles

There are two different approaches for Fpga content:

  • a) Pure hardware logic

  • b) Using FPGA as platform instead a microcontroller: A microcontroller can react in step times down to ~ 10 µs. 50 µs are often usual. For faster especially controlling algorithm with step times of ~ 1 µs the usage of FPGAs becomes more and more familiar, because the FPGA have enough space, are no more so expensive with much space and the tool suites are more and more proper.

The approach b) is not handled here. They are other tools such as https://hdl.github.io/awesome/items/synthesijer/ or programming FPGA in C++ (known since ~ 2005) which fulfill this approaches.

This work is related to pure hardware logic.

Writing hardware logic in Java requires knowledge about hardware in FPGA. On the first hand should both familiar with the internal structures of logic blocks in your FPGA. Also which such thinks as floorplanner, physical view, timing reports. On the other hand you should familiar with VHDL as hardware description. The VHDL is used as bridge between the Java hardware sources and the place&route tools of the FPGA.

The benefit of writing hardware logic in Java is: Also with a simple tool chain for VHDL and FPGA you can do elaborately tests. The modularity is better supported. Java has a proper software technology (ObjectOrientation, is used also for hardware). Java is often proper known from a large spread of persons. The tool support is well. That are detail advantages.

3.1. Principle of functional simulation of synchronous state machines

Typically, the bulk of the content of an FPGA should be a synchronous design. It means, all input signals are gathered by only one clock, and then furthermore used only clock-synchronous.

All new states are based on the given states with logical relations, which are then adopted as current states with the clock edge.

Simple synchron logic

Qn+1 = fn( Qn)

Whereby the input signals are also part of the Q (means all FlipFlop states). The fn (function) is a logical relation.

To do the same in any sequential programming language, firstly all new states should be calculated from the current states without using the new states itself for calcualtion. That should be done for the whole functionality (not only module per module), because elsewhere new states from already calculated modules may be used by mistake. This new states are the representation of the signal levels on the D inputs of the FlipFlops.

Then the clock edge comes in hardware, that is in software the copy process to declare the new calculated states as current ones. This should be done without logic combinations, only the values are copied.

After that, output values can be calculated from the new current states. Alternatively the output values can also be calculated as the D inputs firstly as new output values, and then copied to the current state of outputs. That are immediately clocked outputs.

For that some step() operations are used in the Java sources, and after them update().

Java: Principle for step and update with new instances
  mdl1.step();
  mdl2.step();
  mdl1.update();
  mdl2.update();

Using Java, some advantages can be uses:

  • The final keyword in Java allows to control settings of variables only one time in a unique kind. Any variable is exactly defined in any control branch. This property of Java can be used to write a clean code.

But the final approach regarding the old (current) states and new states can be only used in the constructors of a class (construction of the instance). Applicated to the above shown step(); update() approach it means:

  • The step() create a new instance invoking the constructor and using the final approach for all instance variables. The new instance is not used for other step().

  • The update() copies the new calculated values in the new instances to the current one. In Java all variable to instances are references. Hence it means, the new instances are created with new in the heap, and referenced with an own reference, and the update copies only the references.

Java: Principle for step and update with new instances
 Type state = new Type();    //initial settings (reset state)
 Type state_d;               //yet null

 ... in a loop:
 state_d = new Type(state, ... more other states );
 ... done firstly for all modules, the step() operations
 state = state_d;            // establish the new states as current
 ... done for all modules, the update() operations.

This is the schema to work in Java for hardware simulation, and also the writing style for translation to VHDL.

3.2. Timing relations

The time to build the logical output from given inputs in the combinatoric logic and lines in the FPGA should be lesser than the time between the clock edges. Then the logical functionality is also the real functionality.

If delays are longer, the behavior is undefined. That is not admissible. It is controlled by the timing constraints.

But not all paths should consider the minimal time between the clock edges. For example you can have a fast clock signal of 200 MHz (5 ns). Some paths should switch in this speed. But not all paths can met the constraint of 5 ns. If the logic is complex, it is a too much requirement. Usage of different clocks for different logic parts is really not a good idea, it requires additional effort for synchronization between the systems. There is a better solution.

synchron logic with CE

Usual the FlipFlops in the Logic Block of a FPGA have a CE input. It is Clock Enable. If this in input is 0, then the signal on the D input has no effect. The FF does not change its state. It means, you can free a FF for switching with a longer period though the clock frequency is high. Only the CE signal should be provided in the required speed for the clock period. The other signals to build the logic need only the CE period respectively the time between two CE='1'.

Scope CE signal and delay

You see the clock in the last track, each rising edge changes the FF state. But the CE signal controls that only each 5th clock edge force switch. In this example the CE is built by a counter which outputs CE exactly after each 5th clock edge. But you see also that CE is delayed. It does not come immediately after the rising clock edge, it does come a little bit later and goes later. The importance is, that CE should be stable on the rising clock, with a minimal necessary setup time. CE can also be delayed, but lesser as one clock period.

In this example the red signal in the first track switches after any rising clock edge, not guarded with CE. But the green signal switches with clock and CE. The next track shows a long delayed signal. It is presented here as ramp, because for simulation Simulink (© Mathworks) is used (not VHDL and a FPGA tool suite). But before the next CE guarded clock edge, the signal arrives its necessary state, and that is sufficient. So the next CE-enabled clock edge gets the state of the delayed signal without failure and produces the next dark green output. This output can also in turn have a delay to its next output, feeding the D input of a FF etc. pp.

In VHDL such behavior can be written in the following form:

PROCESS(CLK, CE) BEGIN
  IF rising_edge(CLK) THEN
    IF CE='1' THEN
      Q2 <= Q1 AND someOtherLogic;
    END IF;
  END IF;
END PROCESS

In VHDL it is determined that nothing should be done if CE is not ='1'.

As ideal case CE is an output of a FlipFlop. This output is immediately routed to the CE input of all using FF. For routing a stable clock net with high fan out is used.

But the router decides by itself how the CE input of the FF in the FPGA are used. See the next slightly changed example:

PROCESS(CLK, CE) BEGIN
  IF rising_edge(CLK) THEN
    IF CE='1' THEN
      IF(steadyState='1') THEN
        Q2 <= Q1 AND someOtherLogic;
      END IF;
    END IF;
  END IF;
END PROCESS

There is another signal as IF condition, here named as steadyState. Then the routing process can assemble this signal also to build the CE for the FF inputs. For example if you have a register of 16 or more FF instead the single Q2 and Q1, it saves a lot of routing resources to do so.

synchron logic with CE

Above you see which may be produced by the router, drawn with ordinary gatter logic. In the real FPGA LUTs (Look Up Tables) are used instead gatter, of course.

How this is related to writing logic in Java

The goal is, determine timeing constrains (see next chapter) for some paths. In Java you can measure the number of clocks between changing signals, respectively the number of clocks how long is a given signal in the steady state. It is very simple. If the signal (variable) is written newly, similar the current time is also stored. The time is a simple incremented int value (32 bit, sufficient for 1 billion clock steps). Also a long value may be usable, but seems to be not necessary.

If the signal is used for combinatoric, the current time is compared with the stored time of the input signals. Yet an assertion or warning message can be built if this time difference is lesser than expected.

If you know your own logic, then you know that some signals are set a certain way in respect to the clock because of their functionality. Then you can check in all tests (simulation) whether this programmed and expected behavior is really true - for the test cases. Of course, it depends on the completeness of the tests whether all possible situations are considered. But the logic should not be so complicated that it is non obviously.

Java: Principle check timing and constrains
Process ( int time, ....) {    //The constructor for new values (see above)
  Fpga.checkTime(time, ref.time, 1);   //assertion with the referenced signal, only 1 clock cycle admissible
  if(ref.ce) {                 //your logic, use a clock enable
    Fpga.checkTime(time, z.time, 10);  //assertion with the own signals, here delay 10 clock cycle admissible
    this.time = time;          //store the time due to the timestamp of possible signal change

    this.signal = z.a && z.b;  //a combinatoric, build a new value, example.

Of course, not every single signal is to be checked, rather the signals are combined into groups with a timestamp in the group. See also chapter Timing constrains for place and route

The expected delay checked with assertions can be used to formulate timing constrains, see following chapter.

3.3. Timing constraints for CE and steadyState

Because the CE is built fastly which each clock edge, it should have a less delay. All FF which should have such an fast delay can be assembled to a FF group, and for that group a constraint can be written (here for Lattice Diamond FPGA tool):

MAXDELAY FROM GROUP "FFfast" 4.5 ns;

But maybe that the steadyState signal does not switch frequently. It switches only also with the CE signal as shown in the schema above. Then it can have a longer delay because the fast switching CE signal itself determines the used CE1 for CE. It means the FF for steadyState may be a member of the group for CE-driven FF and can be have a longer delay:

MAXDELAY FROM GROUP "FF_CE" 23.0 ns;

Now the router can use a longer path for the steadyState signal to build a CE signal for other FF. The longer delay is possible because this signal does not switch independent, it switches only with CE itself.

It is not expected that the logic for the CE1 with the longer delayed signal steadyState or some more other signals produces hazards or glitches because the CE itself is also used and it has the short delay. It determines the output of the LUT to build the CE1 to 0 so long CE='0'.

3.4. Be careful because of glitches in logic

But you should take care that really one signal with a short path is always member of such an IF construct. If you write in VHDL

PROCESS(CLK, CE) BEGIN
  IF rising_edge(CLK) THEN
    IF (signal1 XOR signal2)='1' THEN
      Q2 <= Q1 AND someOtherLogic;
    END IF;
  END IF;
END PROCESS

then the router can built a CE to control switching the FF Q2 too. If the signal1 and signal2 constrain a longer delay as the clock period then it is possible that a faulty intermediate state can occur where the signals are detected as different (XOR delivers '1') though the signals are not different after the delay. Then the CE can be '1' by mistake during a clock edge. It means this is a bad formulation in VHDL. At least one fast signal should be used as AND logic.

CE Glitch scope

Only for info: In Simulink this is produced by the following schema:

CE Glitch sch

The TimeSignals S-Function-Block is programmed with the given input pattern:

%time:0.000000000: LogicInput.signal1 = 0, LogicInput.signal2 = 0;
%time:0.000000050: LogicInput.signal1 = 0, LogicInput.signal2 = 0;
%time:0.000000100: LogicInput.signal1 => 1.0;
%time:0.000000080: LogicInput.signal2 => 1.0;

The signals itself are float with ramps, they are converted by the comparison block to boolean, with that approach a delay can be simulated. Because the first signal1 reaches the 1-level earlier than the signal2, temporary the '1' value is built by the XOR. That is a glitch. If the clock edge comes during the glitch, it is effective and causes a faulty behavior.

Note that the Simulink is not used for FPGA design for this example. It is only used to get simulation results of this hardware. I have used Simulink because it is my familiar simulation environment not only for FPGA, but more for controlling approaches. It should be possible for everybody to use him/here likely tool to get results. The results should be comparable and transferable between the tools.

Automatic check on Java level

The above shown situation can come also from a Java formulated design. It should be possible to check such constructs statically on Java level, yet not clarified. The check should test whether at least one signal is in AND constellation for the whole condition, and this signal should have the less constrain for delay.

3.5. Data types in Java for Fpga design in VHDL

  • VHDL distinguishes between the BIT type which has two states '1' and '0' (similar as boolean in Java) and adequate also for the STD_LOGIC type which may have also only the both values '1' and '0' if no more is used on the one hand …​

  • And on the other hand VHDL knows a boolean type which is strongly used in IF THEN statements.

There is no automatic conversion between both.

The typical boolean operators AND, OR are valid for all three, the boolean type and also for BIT and STD_LOGIC but of course with different results. The result follows the inputs, a mix is not possible. This may be one of the "safety" of the VHDL language, strongly distinguish, but it can be seen also as difficult and confusing.

In Java the situation is clarified with a boolean type which is also strongly and safe:

  • boolean in Java has two states, true and false.

  • Better than in C language the true representation is strongly defined.

  • More strongly as in C and C++ language a if statement needs a boolean expression. All other is faulty.

For the Java representation of a Fpga design with view to VHDL the following is defined:

  • A boolean variable represents a SIGNAL of BIT type. The values true and false are associated to 1 and 0. Negative logic is not supported as language feature. It is a feature of the user’s semantic.

  • A BIT_VECTOR is represented by a int or long value (with up to 64 bit). This is better to test in Java as a boolean vector, because BIT VECTOR is often used as register values, also for shift and compare operations.

  • To access one bit of such an vector, there is a special access operation Fpga.getBit(vector, 7) where 7 is a bit number. The result of this operation in Java is boolean which is a BIT type.

  • Exact the same can be done with a STD_LOGIC_VECTOR, also represented in Java with int or long, distinguished only by the annotation @Fpga.BITVECTOR or @Fpga.STDVECTOR

  • Yet numerical values are not supported, done in the (near) future. The types short and int for 16 and 32 bit bit width without additional annotations should be used for 16- and 32 bit with algorithm (long also for 64 bit).

3.6. boolean expression and SIGNAL types with view to VHDL

In VHDL, if you have a boolean operation with a BIT or also STD_LOGIC type, you can write:

result <= (a AND NOT b) OR c;

If you do the same for an IF construct you must write:

IF (a='1' AND b='0') OR c='1' THEN

It means the operands should fistly converted to a boolean one. But it is also possible to write:

IF ((a AND NOT b) OR c) ='1' THEN

Here the expression is calculated as bit and then converted to boolean as last action for the IF usage.

But if you have a mix of BIT and STD_LOGIC it is not easy to write. If b1 .. b3 are BIT types and s1 .. s3 are STD_LOGIC you can write

b3 <= b1 AND b2;
s3 <= s1 AND s3;

But you cannot write a mix of both.

s3 = a1 AND b1;

For that it is necessary to write:

IF(a1='1' AND b1='1') THEN s3 = '1'; ELSE s3 = '0'; END IF;

Now the check is done on compilation level as boolean and the results are set. For the routing process and the logic in FPGA it is exactly the same if there are no tristate or wired lines or such one, or better: If the STD_LOGIC signals have in any case only the values '0' and '1'. It is a property of the language VHDL, which regards that a STD_LOGIC can or may have also other values then '1' and '0' as possibility. The given line does not give an information about that.

Hence it is a little bit complicated for the code generation.

3.7. Use cases of STD_LOGIC definitions

The STD_LOGIC was introduced in VHDL firstly to support simulation with the values 'U', 'X' etc. That is unrelated for the Java2Vhdl, because simulation is done in pure VHDL. But it may be important: If Simulation should be done with VHDL then all SIGNALs should be defined as STD_LOGIC and not as BIT.

Secondly, also important for Java2Vhdl, the Tristate and a wired or or wired and (pullup, pulldown lines) in the FPGA hardware can only defined with STD_LOGIC. For that the type

@Fpga.STDLOGIC char mySignal;

supports definition of a data type with the 9 possible states. They are given as characters for the char variable and adequate used for Java simulation.

The other possibility is to define

@Fpga.STDLOGIC boolean mySignal;

In this case this variable is translated to STD_LOGIC, but only the two states '1' and '0' are supported.

4. Modularity, with Object Oriented Approach

Of course, Java is an Object Oriented Language. It is important for ordinary software programming. The important common features for Object Orientation and their relations to hardware description approaches are shown in the following sub chapter.

4.1. Object Orientation approaches and their possibility for VHDL with the Java2Vhdl translator

  • OO: Some related data are combined in a class.

  • VHDL: This is also familiar in VHDL, using a TYPE RECORD and its instances. Secondly the classic modularity with one VHDL file per module fullfils this approach.

  • OO: Some operations are related to the data, the operations which works with the data.

  • VHDL: it is possible to build RECORDs, and PROCESSes, which touches only data of exactly one record instance. It means the RECORD definitions are oriented to the PROCESS definitions. The PROCESS is the operation or "method" related to the data. That is possible, not necessary in VHDL but may be seen as recommended. But other than in Object Oriented writing style the PROCESS can be related only to one RECORD instance and not to the TYPE RECORD, to the type as in Object Orientation. For the Java2VHDL translator the operation is defined type related of course (because of other reason in the constructor). The translator generates the PROCESSes from that constructor code, but instance related. In the normal software Object Orientation the instance relation is made on run time via the arguments of the method (in particular the instance pointer this, but also via other referenced instances as arguments). The Java2Vhdl translator resolves this arguments and builds PROCESSes for each instance as result of translation. It can be seen that the realization of the VHDL design is associated with a runtime, while the description in Java is the source for compilation.

  • OO: Encapsulation of data. Data can be designated as private or protected. In both cases the data cannot be accessed from any other module, only from the own one ore derived ones in case of protected. The package private designation in Java (without public keyword) is a furthermore possibility similar to the friend designation in C++: Some modules can be associated to access there data one together, but the access is not possible from modules outside of this group.

    This feature is not firstly for protection, is supports a proper modularity without too much data dependencies.

  • VHDL: In a module in an own VHDL file the data are also encapsulated. But the Java2VHDL translator supports also a flattened style with one VHDL file for some modules and some TYPE RECORD inside. But because of encapsulation of the data in Java, the access to the records are also sorted.

  • OO: aggregated modules: Any module can have references to other modules. In UML (Unified Modelling Language) this references are designated as composition (own sub module), aggregation (hard referenced other module, not changable) and association (changeable). But the access to properties of the other module are organized. Only public or package private members can be accessed, see topic above. It is able to clarify whether an access can be done only via access operaions ("getter") or also to public data. The data can be final especially in Java to forbid writing, changing of the data in another module. This is an essential idea to decrease to much functional dependencies between modules.

  • VHDL: In the classic VHDL only the data flow via the Interface data (ENTITY PORT) is possible. This is not an aggregation concept, it is a dataflow concept.

    • But if a TYPE RECORD is seen as a module, in a flattened design (one file for more modules), then the access to the data to other modules is immediately possible. Last not least, for the deployed design in the FPGA it is the same as using more as one VHDL file with ports. But from the view point of the VHDL source it is maybe more obviosly and more simple to read what`s happen. This is one of the basic idea of the flattened design of the generated VHDL file.

    • From view point of the FPGA content description in Java, Aggregations are used, see chapter More possibilities with Java2VHDL: References (aggregations) in Object Orientation kind. Via the aggregations either public data can be accessed immediately, or the better approach is using the interface concept, see Interface technology in Java for VHDL. This is possible via modules in one flattened design.

  • OO: There is the possibility to use derived types from an abstract type: The thinking should not be: Have any type, and derive it. The better thinking way is: Have some different types, how to find common properties and their embodiment in an abstract type of all these different types. It means the derivation is the secons approach, the abstraction is the first one. This is done because dealing with common properties (abstraction) is a good idea.

  • OO: Related to Java programming languate, firstly the interface concept is one of the abstraction: Defining of a comman access possibility. The interface is implemented then in any type (class).

  • OO: This feature of Object Orientation is very important for flexibility and testing.

    • A module can be connected to different similar but not necessary equal other types (aggregation in UML).

    • For testing an aggregated module can be replaced by a stub or mock which fullfils the interface for test approaches, to execute module-related (unit) tests.

  • VHDL: This is an interesting topic. Also in VHDL a module / unit test may be seen as important. The test can be done in a reduced real desing in an FPGA, and also on software level. Exactly this topic is realized in the Java2Vhdl translator. See chapter Interface technology in Java for VHDL.

4.2. Modularity in classic VHDL

VHDL was developed in a time as Object Orientation was not familiar. And of course, is Object Orientation proper for hardware approaches? Not from the eyes of the 1980th. But from the eyes of hardware test approaches and translation possibilities of the 2020th!

The modularity of VHDL is classic, it is a dataflow approach:

  • Define an interface to a module, this is in the module the ENTITY PORT definition part.

  • This definition part should be repeated on usage as COMPONENT definition with the same content (a disadvantage, In C header files for that are used).

  • On usage secondly in a PORT MAP signals should be connected. The connection of two modules needs extra signals.

CardsPluggedCable For that the writing and maintain effort is high. This is especially a problem on refactoring. The importance of refactoring in modern software technology is some more higher as in the 1980th, related with the topics agile program development, complexity of solutions.

The image right side is only an illustration. It shows how two cards with simple plugs can be connected in a simple way. You are responsible to all by yourself, and you can do what ever you want, in this example have a resistor in the line. This is similar as wiring of modules in VHDL. You need an effort, but you can insert your own logic on module plugging level between the modules.

For hardware description in Java a similar approach is possible using an In and Out inner class for the input and output signals. This is necessary if one module should translated as one module to VHDL.

4.3. More possibilities with Java2VHDL: References (aggregations) in Object Orientation kind

The following example code snippet comes from the example project in the linked zip file on start of this document:

The modules in Java can be implemented in a flattend form in VHDL. Then each inner class from one Java file (module) is one TYPE RECORD definition in VHDL. The top level VHDL file or also a module can have some such RECORD TYPE definitions.

Example start of an inner class in a module MyModule.java:

Java: inner class for a PROCESS in VHDL
//fpga/exmpl/modules/ClockDivider.java
  /**Inner PROCESS class builds a TYPEDEF RECORD in VHDL and a PROCESS for each instance.
   * Note: Need public because here the interface technology is not used (negative, but simple understandable pattern).
   * Compare with {@link Reset.Q}
   */ 
  @Fpga.VHDL_PROCESS public static final class Q{

    @Fpga.STDVECTOR(4) final int ct;
  
    /**This is the variable of the record accessed from outside. 
     * Note: Need public because here the interface technology is not used (negative, but simple understandable pattern).
     * Compare with {@link Reset.Q#res}
     */
    public final boolean ce;  
  
    /**Time of the latest set operation of any of the variables. */
    public int time;

Builds the record in VHDL:

TYPE ClockDivider_Q_REC IS RECORD
  ct : STD_LOGIC_VECTOR(3 DOWNTO 0);
  ce : BIT;
END RECORD ClockDivider_Q_REC;

The RECORD is instantiated, here with one instance, but also more as one instances are possible:

SIGNAL ce_Q : ClockDivider_Q_REC;

In VHDL from inside any PROCESS a variable of another module (RECORD instance) can be accessed, because of the flattened form with RECORDS.

ct_Q_PRC: PROCESS ( clk )
BEGIN IF(clk'event AND clK='1') THEN

  IF ce_Q.ce='1' THEN

That is possible because of the flattened property. This is simple also for the routing process for FPGA tools. The signal ce in the record instance ce_Q is immediately accessed. But:

The idea working flattened is bad for the real sources. It means that is not an approach for the Java sources. But instead, the Java sources can work with aggregations. The adequate line in Java for the process of the other module looks like:

Java: constructor for a PROCESS in VHDL usning ce
//fpga/exmpl/modules/BlinkingLedCt.java
    @Fpga.VHDL_PROCESS Q(int time, Q z, Ref ref, Modules modules) {
      Fpga.checkTime(time, ref.clkDiv.q.time, 1);  // for the ce signal, constraint with 1 clock delay.
      if(modules.ct_clkDiv.q.ce) {
  • In Java the access is not done immediately to the variable in the other class. Instead a reference ref.clkDiv is used.

  • Whereby ref is a module specific instance which holds all references, clkDiv is the reference to the other module.

  • The name after ref.clkDiv is not related to Vhdl. It is a local name in the module class in Java, not related to the really used instance.

  • This is due to modularity. A module should not know which concrete instance of another module is used. This is not to be clarified in the module. It must be clarified in the higher-level programming which uses the modules.

How it is related: Any module can contain a Ref class with a Ref ref instance, here:

Java: Reference usage
//fpga/exmpl/modules/BlinkingLedCt.java
  private static class Ref {

    /**Common module for save creation of a reset signal. */
    final Reset_ifc reset;
    
    final BlinkingLedCfg_ifc cfg;
    
    /**Specific module for clock pre-division. */
    final ClockDivider clkDiv;
    
    Ref(Reset_ifc reset, BlinkingLedCfg_ifc cfg, ClockDivider clkDiv) {
      this.reset = reset;
      this.cfg = cfg;
      this.clkDiv = clkDiv;
    }
  }
  
  private Ref ref;

That are aggregations in UML wording (Unified Modeling Language for Object Orientated software technology).

As you see the name of the referenced module clkDiv is a private name, not the name of an existing instance in the parent module. This is per se unknown, not determined which modules and also which type of modules are used.

The constructor of the Ref have to be called in the constructor of the module, because ref is final:

Java: constructor and init operation for the Ref class
//fpga/exmpl/modules/BlinkingLedCt.java
  /**Module constructor with public access to instantiate.
   * <br>
   * Note: The arguments should have the exact same name and type as in the {@link Ref#Ref(Reset_ifc, ClockDivider)} inner class.
   *  
   * @param reset module provide the reset signal on power on and as input
   * @param clkDiv module provide a clock enable signal: {@link ClockDivider.Q#ce}
   */
  public BlinkingLedCt ( Reset_ifc reset, BlinkingLedCfg_ifc cfg, ClockDivider clkDiv) {
    this.ref = new Ref(reset, cfg, clkDiv);
  }
  
  /**Non parametrized constructor if the aggregations are not existing yet. Use {@link #init(Reset_ifc, BlinkingLedCfg_ifc, ClockDivider)}
   * for aggregation. */
  public BlinkingLedCt () {}
  /**The init operation should be used instead the parameterized constructor with arguments if there are circular dependencies:
   * The modules should be known each other. Then only one module can be instantiated with the parameterized constructor.
   * The other module can only be instantiated firstly without aggregations, then get the aggregation via this init operation.
   * <br>
   * Note: The arguments should have the exact same name and type as in the {@link Ref#Ref(Reset_ifc, ClockDivider)} inner class.
   * @param reset aggregation to the reset module.
   * @param cfg aggregation to the configuration
   * @param clkDiv aggregation to the clock divider.
   */
  public void init ( Reset_ifc reset, BlinkingLedCfg_ifc cfg, ClockDivider clkDiv) {
    this.ref = new Ref(reset, cfg, clkDiv);
  }

Here it is important that the name of the arguments in the constructor is the same as the name of the aggregation reference in the Ref class. That is not a problem and also a good style. It is necessary for the translation to VHDL.

Now, in the top level in Java all modules should be instantiated. The Java-toplevel decides which modules are used. For that there is a Modules inner class:

Java: Module class defines the used modules with their relations
//fpga/exmpl/modules/BlinkingLedCt.java
  /**The modules which are part of this Fpga for test. */
  public class Modules {
    
    /**The i/o pins of the top level FPGA should have exact this name ioPins. */
    public BlinkingLed_FpgaInOutput ioPins = new BlinkingLed_FpgaInOutput();
    
    /**Build a reset signal high active for reset. Initial or also with the reset_Pin. 
     * This module is immediately connected to one of the inputFpga pins
     * via specific interface, see constructor argument type.
     */
    public final Reset res = new Reset(this.ioPins.reset_Inpin);
    
    public final Test_Combinatoric_BlinkingLed vhdl_Combinatoric = new Test_Combinatoric_BlinkingLed();
    
    public final BlinkingLedCt ct = new BlinkingLedCt(this.res, BlinkingLed_Fpga.this.blinkingLedCfg, this.ce);    //cfg implemented in extra class in this file.
    
    public final ClockDivider ce = new ClockDivider(this.res, this.ct);
    
    Modules ( ) {
      //aggregate the module afterwards
      this.ct.init(this.res, BlinkingLed_Fpga.this.blinkingLedCfg, this.ce);    //cfg implemented in extra class in this file.
    }
  }

  public final Modules modules;

Here three modules are defined which are used in the top level of the FPGA. In the constructor of the modules the aggregations to the other modules are defined. Note that also a module.init(…​) can be used beside the constructor. This is necessary if circular dependencies are needed. For the Java2VHDL translation both can be used, arguments of the constructor and arguments in the associated init(…​), not shown here.

The name of the modules (the composite reference name in Java) build the name of the RECORD instances in VHDL:

SIGNAL ce_Q : ClockDivider_Q_REC;
SIGNAL ct_Q : BlinkingLedCt_Q_REC;
SIGNAL res_Q : Reset_Q_REC;

On translation from Java to VHDL an index (TreeMap in Java) is built for each module which associates the intern name of a reference to a module with the real used module. This index is internally used for translation, but also reported in the report file (option -rep:path/to/file.txt)

== Module: ct
  localName         | accessed module     {@link J2Vhdl_ModuleInstance#idxAggregatedModules}
--------------------+----------------
  cfg               | BlinkingLed_Fpga : BlinkingLed_Fpga
  clkDiv            | ce : ClockDivider
  reset             | res : Reset
--------------------+----------------

Now, while generating the PROCESS for the module ct, this association index is used to assign the clkDiv in Java with ce as name of the RECORD instance in VHDL.

How is this index built:

  • The name of the instance in Java given as actual argument of the constructor respectively the init(…​) operation BlinkingLedCt as this.ce. this. is only for Java internals, ce is used.

  • The name of the formal argument of the constructor or init(…​) of BlinkingLedCt is clkDiv. It is also gathered.

  • This both names are stored in the index shown above.

  • The name of the actual argument of the Ref constructor and also the name of the reference itself should be the same: clkDiv. This is necessary as style guide. It should not be a problem. Test and translation of this stuff may be also possible, but a non necessary effort.

So the Object orientated writing style in Java is translated to a flattened immediately access to the correct SIGNAL (usual a Flipflop, a register etc.).

4.4. Interface technology in Java for VHDL

The last chapter has shown using Object Orientation with aggregations and their association to instances. That is necessary for flexibility in module usage (which combination) and also for the test of modules

There is one more approach: using interface technology.

4.4.1. Basics of interface technology

In the chapter above it is able to associate, which module is used. But the access inside the module is inflexible. The variable name of the module is immediately used, in the example q.ce.

Two things should be free to do:

  • Changing of internal names in a module for further development. Using modules should not be refactored. That is also the basic idea of private encapsulation in the Object Orientation.

  • Using another module should be possible with similar properties for using but another internal design This idea is the abstraction and inheritance in Object Orientation. It is like in daily life. You need a car to drive. Which car is not important. All cars are basically similar, there are exactly equal in the required properties.

  • The last point is also for test: You can replace a module with a test bed emulation of the original module to make an independent module or unit test. The test bed replacement has another inner structure.

Look to another part of the example:

Java: interface usage
//fpga/exmpl/modules/BlinkingLedCt.java
        if(ref.reset.reset(time, 20)) {          // interface access to assigned here unknown reset module
          this.ct = ref.cfg.time_BlinkingLed();

This is a snippet from the BlinkingLedCt PROCESS in Java: A reset information is requested. If reset is given, then the counter ct is set to its reload value.

But it is not designated here from where reset is coming and how it is built, and also from where the reload value is coming and how it is built. This should be clarified outside of the module. The module only needs connection for it.

The translated VHDL design looks like:

VHDL: generated code of interface usage
      IF (res_Q.res)='1' THEN
          ct_Q.ct <= TO_STDLOGICVECTOR(BlinkingLed_Fpga_time_BlinkingLed);

Here the relations to the reset signal and the reload value is full clarified: A signal from another RECORD instance (another module) is used for reset, and the reload value is defined as constant in the VHDL code above:

VHDL: constant definition due to interface operations
CONSTANT BlinkingLed_Fpga_onDuration_BlinkingLed : INTEGER := 10;
CONSTANT BlinkingLed_Fpga_time_BlinkingLed : BIT_VECTOR(7 DOWNTO 0) := x"64";

But in another usage configuration of the same module, without change of the Java code of the module, the reload value can for example come from another Signal vector as for example

VHDL: other result of interface usage
          ct_Q.ct <= otherRecord.reloadVal;

4.4.2. References with interface type

Explained from hardware view: A simple signal or variable in Java with a dedicated type, BIT or STD_LOGIC_VECTOR or such in VHDL or boolean or int in Java is like a cable with a standard plug, which’s properties are general defined, not specialized. Whereas an interface is a cable with a plug of a certain design which can only be plugged into the corresponding counterpart. The conditions on the interface are well defined. But it is not defined how the implementing device behind the plug works.

The invocation of the interface relation uses the referenced modules ref.reset and ref.cfg for this both values from the chapter above.

Look on the definition of all references for input values from outside of the BlinkingLedCt module:

Java: Reference class definition
//fpga/exmpl/modules/BlinkingLedCt.java
  private static class Ref {

    /**Common module for save creation of a reset signal. */
    final Reset_ifc reset;
    
    final BlinkingLedCfg_ifc cfg;
    
    /**Specific module for clock pre-division. */
    final ClockDivider clkDiv;
    
    Ref(Reset_ifc reset, BlinkingLedCfg_ifc cfg, ClockDivider clkDiv) {
      this.reset = reset;
      this.cfg = cfg;
      this.clkDiv = clkDiv;
    }
  }
  
  private Ref ref;

As you see, the type of the ref.reset is Reset_ifc, as also the type of ref.cfg is BlinkingLedCfg_ifc. Look firstly to the defintion of the Reset_ifc:

Java: Definition of Reset_ifc:
//fpga/exmpl/stdmodules/Reset_ifc.java
package org.vishia.fpga.stdmodules;

public interface Reset_ifc {
  
  /**Returns true for reset. false for normal operation.
   * @param time current time for the access
   * @param max check whether the time of the accessed signal was latest set to (time - max).
   * @return true then reset active, false: normal operation.
   */
  public boolean reset ( int time, int max);
  
}

Using of interfaces is very proven in Java (as also another Object Orientated Languages), and it is not so complex to translate it to VHDL, if some sensible simple style guides are additional regarded.

It means, to get an information about the reset state as boolean value in Java or BIT in VHDL, the reset() operation is called in the implementing module. That is the formal form of the plug. How this function is implemented - depends on the plugged module.

The association for ref.reset is set on construction. For this example the init(…​) operation is responsible to plug:

Java: Reference class definition
//fpga/exmpl/modules/BlinkingLedCt.java
  /**The init operation should be used instead the parameterized constructor with arguments if there are circular dependencies:
   * The modules should be known each other. Then only one module can be instantiated with the parameterized constructor.
   * The other module can only be instantiated firstly without aggregations, then get the aggregation via this init operation.
   * <br>
   * Note: The arguments should have the exact same name and type as in the {@link Ref#Ref(Reset_ifc, ClockDivider)} inner class.
   * @param reset aggregation to the reset module.
   * @param cfg aggregation to the configuration
   * @param clkDiv aggregation to the clock divider.
   */
  public void init ( Reset_ifc reset, BlinkingLedCfg_ifc cfg, ClockDivider clkDiv) {
    this.ref = new Ref(reset, cfg, clkDiv);
  }

This operation creates the Ref instance and sets the references with the outside given reference to the implementor, the supplier for this signal with the interface type. Alternatively this can be also done also with the constructor of the module.

The value for the supplier of the reset signal in form of the Reset_ifc comes from another module. The connection is done in the Moduls class on top level:

Java: Module definition and relations
//fpga/exmpl/modules/BlinkingLedCt.java
  /**The modules which are part of this Fpga for test. */
  public class Modules {
    
    /**The i/o pins of the top level FPGA should have exact this name ioPins. */
    public BlinkingLed_FpgaInOutput ioPins = new BlinkingLed_FpgaInOutput();
    
    /**Build a reset signal high active for reset. Initial or also with the reset_Pin. 
     * This module is immediately connected to one of the inputFpga pins
     * via specific interface, see constructor argument type.
     */
    public final Reset res = new Reset(this.ioPins.reset_Inpin);
    
    public final Test_Combinatoric_BlinkingLed vhdl_Combinatoric = new Test_Combinatoric_BlinkingLed();
    
    public final BlinkingLedCt ct = new BlinkingLedCt(this.res, BlinkingLed_Fpga.this.blinkingLedCfg, this.ce);    //cfg implemented in extra class in this file.
    
    public final ClockDivider ce = new ClockDivider(this.res, this.ct);
    
    Modules ( ) {
      //aggregate the module afterwards
      this.ct.init(this.res, BlinkingLed_Fpga.this.blinkingLedCfg, this.ce);    //cfg implemented in extra class in this file.
    }
  }

  public final Modules modules;

Here you see the invocation of the init(…​) operation with the first argument `this.res. It is the reference to the Reset module which supplies the interface. Adequate it is done for the others. The implementor of the BlinkingLedCfg_ifc is here assembled in the environment class denoted by BlinkingLed_Fpga.this and their in blinkingLedCfg, see following chapter Interface access instances for stubs (replacement for non existing module outputs). You see also here that the references are fulfilled for example for the Reset module by construction, here with reference to the pin of the Input interface on the FPGA.

4.4.3. Implementation of an Interface from the whole module

Modul1Plug If you have a simple module, as here the Reset which has only one task: Deliver the reset signal, this module can/should immediately implement this interface. The image right shows a module, it has only one task, supply voltage 5 V, one interface, an USB plug, no more.

The Reset module in Java implements the interface on module level:

Java: Implementation of Reset.reset():
//fpga/exmpl/stdmodules/Reset.java
public class Reset implements FpgaModule_ifc, Reset_ifc {
  .....
  @Override public boolean reset ( int time, int max) { return this.q.res; }

It’s very simple: It accesses the variable res in its own inner class q which is the PROCESS class for the reset functionality. This is the detail of this Reset module.

The Java2Vhdl translator evaluates this term in the context of this module which is given as reference. Hence the result is the already above shown VHDL code:

VHDL: generated code of interface usage
      IF (res_Q.res)='1' THEN
          ct_Q.ct <= TO_STDLOGICVECTOR(BlinkingLed_Fpga_time_BlinkingLed);

Generally, only the term of the return statement is evaluated. If the interface operation contains more statements, it is usable for simulation on Java level. For the ordenary software execution of interfaces concepts in Java as also other programming languages the interface operation can also change data and do anywhich else. But this "full freedom to do whatever you want" is also criticized in some software writing guidelines. Normally it is a good style to prevent or forbid changes in another software module on only access operations. Changes should be done with (also maybe interface-) operations which are named set…​, do…​, process…​ or exec…​ or such else. For that things the processes in VHDL are responsible. It means execution routines cannot be invoked in the Java context for VHDL hardware descriptions.

But the return expression can be more complex, for example can contain logical combinations, access to bit ranges, comparison and all what is possible also in other expressions. This expression is generated inside the accessing PROCESS. If the same interface operation is used more as one time in different contexts, this operations are generated on any access. It is similar as execution of the operation in the Java runtime. Also there any access executes the statements again.

It means, the PROCESS of the module can prepare proper signals for simple access if it is expectable that this signals are used more as one time. On the other hand the optimizer while routing can accomplish the aggregation of multiple equal accesses. How these accesses are to be designed is up to the Java description of the VHDL developers.

Now, any module can implement this Reset_ifc. It means for testing, or for another design, you can use another module for the reset signal without change of the inner structure of the using module. The Java2Vhdl translator gets the correct access.

4.4.4. Interface agents or access instances

Modul5Plugs If a module has more interface connections, especially connections of the same type more as one, then it is not optimal or not possible for the same interface type to implement the interface with the module as a whole. For that Java offers an interesting possibility: Implementing the interface in instances of anonymous inner classes. The inner class has only the task to implement the interface with the possibility to access to the environment (outer) class which is the module. For that it can be seen as agent which is responsible to access to inner details of the module from outside, without the necessity to regard this access in the module itself. On the other hand it can be designated as access point.

Java: Example for interface access or agent
//fpga/exmplBlinkingLed/fpgatop/BlinkingLed_FpgaInOutput
package org.vishia.fpga.exmplBlinkingLed.fpgatop;

import org.vishia.fpga.Fpga;
import org.vishia.fpga.stdmodules.Reset_Inpin_ifc;

public class BlinkingLed_FpgaInOutput {
.....
  /**Get the reset pin as referenced interface access from a module.
   * Using the {@link org.vishia.fpga.stdmodules.Reset} may be seen as recommended because it clarifies a longer reset signal.
   */
  @Fpga.IfcAccess public Reset_Inpin_ifc reset_Inpin = new Reset_Inpin_ifc () {
    @Override public boolean reset_Pin() { return BlinkingLed_FpgaInOutput.this.input.reset_Pin; }
  };

Such an anonymous class instance or agent is shown here for the access to the reset pin itself. The pin is located in a static class Input definition in this module with a final Input input = new Input() instance. This is used to generate the PORT definition in VHDL, but this is not the point of interest here.

For the port definition, especially, there may be a lot of pins. A flexibility is necessary because the same FPGA functionality may be necessary to implement in different environments, different card types etc. For that the interface access to the port pins is a very good idea and reduces adaption effort and error possibilities because of pin confusion.

Hence, the port definition is associated to an own module (which can be replaced for different applications without replacing and adaption of other modules). This module contains:

public class MyPortsVersionXY_FPGA {
  public static class Input {
    //.... input pins
  }

  public static class Output {
    //.... output pins
  }

  public final Input input = new Input();     //instantiation
  public final Output output = new Output();

  //some access operations:
  @Fpga.IfcAccess public Type_Inpin_ifc access_Inpin = new Type_Inpin_ifc () {
    @Override public boolean access_Pin() { .... }
  };

Now this module can be connected with dedicated access instances to any module which needs an input pin. More as that: A module which needs primary an input pin can also be connected not immediately to the pin, but to another module may be with a filter functionality. This other module should only offer also such an interface access agent with the same interface type.

Of course this Port module is only an example. The interface access agents can be used anywhere for more complex applications.

4.4.5. Interface access instances for stubs (replacement for non existing module outputs)

Especially for test, but also for variants of a FPGA design sometimes a functionality is not necessary. Without adaption of the module, the inputs can be connected to constant signals driving '0' or '1'. The place and route will be remove this unnecessary parts. But on source level an adaption should not be necessary.

Adequate can be done if some constant values are necessary. The constant values should not be set in a module itself to preserve flexibility, it should be delivered from outside. It’s the same story: a module should not be modified and adapted for a specific use, but the functionality should be defined externally with appropriate connections.

For the Blinking Led example this is done with timing parameters:

Java: interface usage
//fpga/exmpl/modules/BlinkingLedCt.java
        if(ref.reset.reset(time, 20)) {          // interface access to assigned here unknown reset module
          this.ct = ref.cfg.time_BlinkingLed();

The request is adequate, a value should be accesses. It is not first intendet that this is a constant value. It can be also a signal which contains this reload value as dynamic information.

Especially for tests sometimes signals are terminated by fix values, if this functionality is not in focus yet. The signals should be fulfilled, of course, but inputs may be always false or '0' for this test condition or for more simple usage of a module. The last hint is also important: A module can contain more functionality as necessary for the amount of usages. If inputs are terminated with constants and outputs are not used, then the routing process will remove this unnecessary parts. The sources can contain it, without disadvantages. It means also that a module should not be prepared or adapted for specific usage situations. It can be uses "as is", and the outer signals and connections determine which is really implemented in the FPGA.

For that all reasons a second use cases and translation goal for interface access is supported by the Java2Vhdl converter: If the return expression delivers a constant value, then a CONSTANT definition in VHDL is created and used.

Follow the example: The interface to access the time_BlinkingLed() is implemented here in the main or top level file also with an access instance:

Java: interface implementation for a constant
//fpga/exmpl/modules/Fpga_BlinkingLed.java

  /**Provides the used possibility for configuration values.
   */
  @Fpga.IfcAccess BlinkingLedCfg_ifc blinkingLedCfg = new BlinkingLedCfg_ifc ( ) {
    
    @Override @Fpga.BITVECTOR(8) public int time_BlinkingLed() {
      return 0x64;
    }

    @Override public int onDuration_BlinkingLed() {
      return 10;
    }

    @Override
    public int time() { return 0; }  // set from beginning

  };

The essence is: The return …​ expression contains only one term, it is a constant. If this situation is detected, this constant value is generated in VHDL with the name of the module and the name of interface operation, it`s unique. The result is (already shown above):

VHDL: constant definition due to interface operations
CONSTANT BlinkingLed_Fpga_onDuration_BlinkingLed : INTEGER := 10;
CONSTANT BlinkingLed_Fpga_time_BlinkingLed : BIT_VECTOR(7 DOWNTO 0) := x"64";

The advantage of building a constant element instead the immediately constant value is: It is obvious in VHDL. Elsewhere only the constant value will be written there as evaluation of the expression, and the back tracking to the Java code is difficult.

If for instance inputs are terminated by a constant to prevent usage, the generated VHDL code contains for instance:

VHDL: constants for input termination example
CONSTANT Toplevel_Fpga_inputX : BIT := '0';
 ....
PROCESS ...
  IF Toplevel_Fpga_inputX = '1' THEN
    ...

In VHDL you see that the input of the module is used, but the expression is never true. Hence, the router removes this part, proper for the use case.

The idea to implement such termination interfaces on the top level comes from the idea, that the top level, or the whole design decides about usages of details of some modules. Another module as stub is not necessary. It simplifies the design. But of course a specific module can also be used for that.

4.4.6. How does the interface technology works for Java2Vhdl

As you have seen in the chapters above: The references and interface operations are full resolved to simple accesses to the internals of another module. Here a question can be asked: Why is it so complicated to run the programs with virtual operations? The interface usage in Java is the same as virtual or overridden operations with late linkage in C++.

The answer is: If references are not full clarified from beginning, the mechanism of the virtual table is necessary. This is the common approach for software execution. But if references are clarified from beginning in the construction phase, and they are never changed, then the execution level can use the simple immediate access. It may be also in the same kind for a Java just-in-time (JIT) compiler: This is the translation from Java byte code to machine code on loading a class. After loading and preparing the immediate machine code is executed. Thats why Java execution needs more startup time, but then it runs fast. If references are organized as final in the constructor, the JIT can optimize it. Whereas, C++ has not such an JIT compiler, and should use the virtual operation call anyway.

Now, for the FPGA usage, the references are also clarified from beginning, with the adequate specific constructs also using the init(…​). Hence it can be resolved on translation time.

There are two things to considerate:

  • The refernced module

  • the interface operation.

On translation of each module the interface operations (only in the first level classes of each file, not in inner classes) are gathered. The return statement is searched and evaluated.

Either it is a simple constant, then the constant is stored in the idxConstDef:

and the const definition is stored in

Both alternatives are then stored module-type-related in

On translation this table is reported in the -rep:reportfile.txt for this example as:

== J2Vhdl_ModuleType: Reset
  ifcOperation()    | access    {@link J2Vhdl_ModuleType#idxIfcOperation}
--------------------+----------------
  reset()           | this.q..??refres @;
--------------------+----------------

If an interface operation is detected as part of an expression, then the reference is dissolved, detecting the type of the reference, and the interface operation is searched (binary search) in that module type related index.

Then either the constant definition is immediately used, or the given expression is evaluated as any other expression too, whereby the module context is switched.

4.5. Overview modularity

The next image should show the variants in a common way (unrelated to the example), also with public immediately access in the modularity:

Modularity in Java

  • The Module_A_In in this image is an inner In class adequate to the VHDL approach using PORT. It is possible.

  • Left side an immediately access to a inner variable in Java is shown. The variable should be public. The disadvantage for that is, that the white Module_A should know the defintiion of Module_X. That is possible and also familiar in Java. It is bad for module test, because on testing also an instance of the ModuleX should be present in the test environment, exact this module or a replacement but with the same package path, which is terrible for source maintaining (version management).

  • But also for that immediately usage the inner aggregation refX is translated to the ref1 on usage. It means the type should be known, but the instance is determined outside. This is important if more as one instance for one type is existing.

  • Right side the interface usage is shown. The interface operation should have the same name as the accessed variable. That is not necessary from the view point of Java. It is necessary for the Java2Vhdl translation. But it is possible.

  • You can connect different implementation modules also on FPGA level in VHDL. It means the variable stateY can exist in different records types. Only the name is determined.

  • Now the advantage for testing is shown. That is only done in Java, not on VHDL level. That’s why the test implementation of the interface_K can use any implementation, should not access exact one variable.

  • Note that also for the module implementation for Vhdl translation the access operation here for stateY can contain more as the simple access to the variable, for example log informations, adaption, a debug break point. That is all possible on Java level, not translated to VHDL. For VHDL anytime the simple access to the record signal with the same name as the access operation is translated.

5. Including existing VHDL files

There are two reasons to include a given VHDL file in the generated sources:

  • a) Existing reused modules

  • b) Often the tools for FPGA design have features to generate VHDL files for specific features, for example for a RAM block.

VHDL is the standard for FPGA design (beside Verilog), hence VHDL is necessary to include.

For simulation on Java level, the functionality of the VHDL code should be emulated. For example for a RAM module this should be simple. You may also only simulate only the basically functionality to fulfill the interface to the module.

5.1. Java class which emulates the given VHDL file

The Java class for the given VHDL file to include it in the Java sources should start with the annotation:

Java: class definition for given VHDL file
@Fpga.VHDL_MODULE(vhdlEntity = "Vhdl_filename") public class EmulationClass implements FpgaModule_ifc {

The class emulates the behavior of the included VHDL file, maybe only in basically features. The name of the EmulationClass and the name of the Vhdl_filename should be the same or similar to find the coherence. But because that are different name approaches it can be slightly different.

The port definition in the Emulation class should be written following the port definition in the orginal VHDL file, for example:

Java: port definition for given VHDL file
  public static class Input {
    public @Fpga.STD_LOGIC boolean Clock;
    public @Fpga.STD_LOGIC boolean ClockEn;
    public @Fpga.STD_LOGIC boolean Reset;
    public @Fpga.STD_LOGIC boolean WE;
    public @Fpga.STDVECTOR(7) int Address;
    public @Fpga.STDVECTOR(8) int Data;
  }

  public static class Output {
    public @Fpga.STDVECTOR(8) int Q;
  }

  public Input input = new Input();
  public Output output = new Output();

This is for the example with the RAM block VHDL. See next chapter for VHDL result:

The rest of the class file contains the emulation of the functionality in any desired kind, for your own and responsible, without influence to the Java2VHDL translation. The step(…​) and the update() operation should be overridden from the FpgaModule_ifc as given in the class definition and due to the general approaches for simulation.

5.2. Definition of the module

The external VHDL file should be included with its emulation class as module in the :

Java: instance definition for given VHDL file inside the Modules as sub module:
  class Modules {
    ....
    public final EmulationClass vhdlModuleName = new EmulationClass();

Because of the annotation @Fpga.VHDL_MODULE(vhdlEntity = "Vhdl_filename") the definition of the VHDL component is included in the generated VHDL file, due to the example above:

VHDL: component definition in the given VHDL file
-- The external VHDL file RAM_SpiRamSel is defined here.
--  Its file and entity name comes from annotation "@Fpga.VHDL_MODULE ( vhdlEntity = <name> in the Java implementation file.
--  The PORT variables comes from the implementation Java file, which should match to the VHDL file.
COMPONENT Vhdl_filename
PORT (
  Clock: IN STD_LOGIC ;
  ClockEn: IN STD_LOGIC ;
  Reset: IN STD_LOGIC ;
  WE: IN STD_LOGIC ;
  Address: IN STD_LOGIC_VECTOR(6 DOWNTO 0) ;
  Data: IN STD_LOGIC_VECTOR(7 DOWNTO 0) ;
  Q: OUT STD_LOGIC_VECTOR(7 DOWNTO 0)
 );
END COMPONENT;

5.3. Usage of the module

For usage of the module in a Java2VHDL generated Java source it needs an inner class with annotation:

Java: usage of given VHDL file in Java code for generation, inner class
  @Fpga.LINK_VHDL_MODULE private static final class Vhdlink_vhdlModuleName {

The class name should follow the instance name of the Vhdl module in the class Modules { …​. The instance of this class (with same name with lower case first char) is responsible to exact this vhdl module, hence this naming convention is acceptable. But the class name should start with Vhdlink_ and hence the two instances for step() and update() with vhdlink_.

This class can have some interim variables to organize the mapping, for example:

Java: usage of given VHDL file in Java code for generation, interim variables
    @Fpga.STDVECTOR(8) final int ramData;
    @Fpga.STDVECTOR(7) final int ramAddr;

    Vhdlink_vhdlModuleName() {
      this.ramData = 0;
      this.ramAddr = 0;
    }

The default constructor is for simulation only. It is the 0-set adequate "reset" in the hardware.

The here defined variables are assembled in a record and SIGNAL record definition, adequate to the example:

VHDL: component definition in the given VHDL file
TYPE Module_Vhdlink_vhdlModuleName_REC IS RECORD
  ramData : STD_LOGIC_VECTOR(7 DOWNTO 0);
  ramAddr : STD_LOGIC_VECTOR(6 DOWNTO 0);
END RECORD Module_Vhdlink_vhdlModuleName_REC;

  .....

SIGNAL module_Vhdlink_vhdlModuleName : Module_Vhdlink_vhdlModuleName_REC;

That are combinatoric values.

Inside the class the constructor should be written in the following kind (example):

Java: usage of given VHDL file in Java code for generation, constructor
    @Fpga.LINK_VHDL_MODULE Vhdlink_vhdlModuleName(int time, EmulationClass vhdlMdl, Modules modules, Ref ref, Module thism) {
      this.ramAddr = Fpga.getBit(ref.dcmd.wrSelectSpeSlaveToTx(), 15) ? Fpga.getBits(ref.dcmd.wrSelectSpeSlaveToTx(), 14, 8) : Fpga.getBits(thism.frameSelect.ctData,9,3);
      vhdlMdl.input.Clock = Fpga.clk;
      vhdlMdl.input.ClockEn = ref.ce.ce0();
      vhdlMdl.input.Reset = ref.res.reset(0, 0);
      vhdlMdl.input.Address = this.ramAddr;
      vhdlMdl.input.Data = Fpga.getBits(ref.dcmd.wrSelectSpeSlaveToTx(), 7, 0);
      vhdlMdl.input.WE = Fpga.getBit(ref.dcmd.wrSelectSpeSlaveToTx(), 15);
      vhdlMdl.step(time);
      vhdlMdl.update();
      this.ramData = vhdlMdl.output.Q;
    }

The constructor works in the same time as in other processes with annotation @Fpga.VHDL_PROCESS: In the step(…​) routine of the module class a this.ram_d = new Ram(…​) will be created, and in the update the this.ram = this.ram_d; new instance is established for usage in the next step.

As one argument of the constructor EmulationClass vhdlMdl with the name vhdlModule the type of the emulation class is necessary for construction. This type is used from the Java2Vhdl translator to search the emulation class, and get its entity and file name of the VHDL file from its annotation @Fpga.VHDL_MODULE ( vhdlEntity = "<name>" ) (see above). That is used as type name of the PORT MAP.

For simulation the step(…​) and update() of the @Fpga.VHDL_MODULE public class EmulationClass…​ is called one after another. The isolation from step() and update() for a correct simulation execution is done by storing the result value of the VHDL module here in the ramData internal data, which is accessible only after the update() from the wrapping @Fpga.LINK_VHDL_MODULE("ram").

Be aware that assignments are only regarded till the step() operation (following by update()). After step() assignments to the local variables of this class stores the result of the linked modules. That is because in VHDL in PORT MAP only an assignment between the in/out variables of the called module to local variables can be associated, not expressions. Necessary expressions to build the correct input values are before step, necessary expressions to work with the output variables are only possible as access to the intermediate variables.

The translation to VHDL code produces the following for this example:

VHDL: component call
-- The external VHDL file RAM_SpiRamSel is included here.

-- Assignments for VHDL instance inputs:
txSpe_Ram.ramAddr  <= TO_STDLOGICVECTOR(data_Data.wrSelectSpeSlaveToTx(14 DOWNTO 8))  WHEN data_Data.wrSelectSpeSlaveToTx(15)='1' ELSE txSpe_FrameSelect.ctData(9 DOWNTO 3) ;

-- <name>: <type>
--   ... <name> is build from <moduleName>_<className> with annotation "@Fpga.LINK_VHDL_MODULE"
--   ... <type> is gotten from the Java class implementation annotation "Fpga.VHDL_MODULE ( vhdlEntity = <type> )"
module_Vhdlink_vhdlModuleName: Vhdl_filename
PORT MAP(
  Clock =>  TO_STDULOGIC(clk) ,
  ClockEn =>  TO_STDULOGIC(rxClkSync_Q.ce0) ,
  Reset =>  TO_STDULOGIC(reset_Q.res) ,
  Address =>   txSpe_Ram.ramAddr ,
  Data =>  TO_STDLOGICVECTOR(data_Data.wrSelectSpeSlaveToTx(7 DOWNTO 0)) ,
  WE =>  TO_STDULOGIC(data_Data.wrSelectSpeSlaveToTx(15)) ,
  Q => txSpe_Ram.ramData
 );  --PORT MAP Vhdl_filename

The comments in the VHDL file should show the coherence to the Java sources. The Assignments are only generated if there are intermediate variables.

The Vhdlink_…​ class should be instantiated and used for step(…​) and update(…​) in the same kind as a process class:

Java: instances for linked Vhdl module, step and update
public Vhdlink_vhdlModuleName vhdlink_vhdlModuleName = new Vhdlink_vhdlModuleName();
private Vhdlink_vhdlModuleName vhdlink_vhdlModuleName_d;

  ....
public void step() { //step from the module
  ....
  this.vhdlink_vhdlModuleName_d = new Vhdlink_vhdlModuleName(this.vhdlink_vhdlModuleName, modules.vhdlModuleName, ....)
....
public void update() { //update from the module
  ....
  this.vhdlink_vhdlModuleName = this.vhdlink_vhdlModuleName_d;

The update assures as for processes that the Java simulation uses the new data in the next step only (clocked).

It is obviously that the instance of the vhdlModuleName is associated here to the vhdlMdl calling argument. But this is not obviously by the Java2Vhdl translation process, only for simulation. That’s why it needs the naming convention, the organization class Vhdlink_…​ should follow the vhdl module name.

6. State machines, enum

State machines are familiar both in software and in hardware. In software solutions often the so named "Harel-Statecharts" are used. (https://en.wikipedia.org/wiki/David_Harel), which defines nested and parallel states with history. That’s a part of the "Unified Modeling Language" (UML): https://en.wikipedia.org/wiki/UML_state_machine, definition by omg.org: http://www.omg.org/spec/UML/. For Software state machines often events are responsible to switch the states.

But all these practical things are not substantiated in the Java core definition. It is possible in Java, but with specific classes.

Hardware state machines are often a little bit simpler. Especially there are not event driven, the system clock switches because of conditions. But the idea of nesting and parallel states is and should also possible and practicable. Also the idea of the event may be possible, presented by an additional condition (the event bit). This means that thinking in terms of states should not distinguish too much between software and hardware.

A basis for this is the enum definition for states and the enum type for the state variable. Exactly this is implemented in the current version of Java2Vhdl.

6.1. State variable with 1-of-n decoding

A state variable is basically a numeric value. For 5 states you need 3 bits to code numbers between 1 to 5 as presentation of the state. But another coding is better:

Use 5 bits if you have 5 states, the state variable is 1-to-5 decoded (one bit from 5 is set).

00000 invalid state
00001 state A
00010 state B
00100 state C
01000 state D
10000 state E

With this coding schema only one bit is need to detect a specific state, and this is a lesser effort in routing, for the FPGA hardware resources. The number of FlipFlops in a FPGA are usual enough, the scare resource is often the lines for routing and the look up tables (LUT) for combinatoric.

Only the quest of the invalid state (after reset) needs testing of all 5 bits, or more for more states. In the current version of the Java2VHDL translator, the set of a state influences all state bits. Most of them are set from 0 to 0, only the elapsed state bit is set from 1 to 0 and the new state bit is set from 0 to 1. Maybe the router can improve this situation by optimizing. Setting all state bits clarifies a possible mistake if more as one bit is set.

6.2. enum definition in Java

Known from C/++ language, a enum is only a definition of an integer constant value by a symbol, and the enum type assures only acceptance of these enum constant definitions. In Java the enum definition is a little bit more powerful:

  • Java creates an enum Object with some values as constant.

  • The enum value in an enum variable is the reference to one of these enum constant objects. This needs 8 byte for a pointer, in opposite to C/++ where for example 2 Bytes for an int16_t enum base type are sufficient. But Java runs only on powerful processors, not an disadvantage.

The first point offers usage of some more properties of the enum constant.

Java: enum definition
  /**States for xyz*/
  public enum MyState {
    Undef  (0b00000, -1, '0'),
    /**State A */
    A  (0b00001, 0, 'A'),
    B  (0b00010, 1, 'B'),
    C  (0b00100, 2, 'C'),
    D  (0b01000, 3, 'D'),
    E  (0b10000, 4, 'E'),
    ;
    public final int _val_;
    public final char show;
    final int bit;
    /**Constructor for the enum value
     * @param value The internal used Vhdl value
     * @param bit the bit number for VHDL translation, following the value
     * @param show character to show the state in println
     */
    MyState(int value, int bit, char show) {
      this._val_ = value; this.bit = bit; this.show = show;
    }
  }

This is a strikingly example. The state names A..E are of course any identifier, but you have a namespace inside the enum definition (clashs are prevented, better than in C/++). The fields value and bit are required. The field show is proper usable for output of test results, see some programming examples. The state Undef is the undefined state after reset.

6.3. state variable as enum

The state variable in Java has this enum type. Concluding, Java assures only set with a valid state value. For the VHDL conversion it should be a bit vector (not a STD_LOGIC_VECTOR, not supported in the current version). A BIT_VECTOR is sufficient for usage.

Java: state variable definition inside a process class:
    @Fpga.BITVECTOR(5) final MyState state;

The number of bits should follow the enum definition, elsewhere mistakes in the generated VHDL code are resulting.

6.4. query state variables

The query is usal written as:

Java: state variable query:
  if( z.state == MyState.Undef ) {
    //.....
  else if( z.state == MyState.A ) {
    //.....
  else if( z.state == MyState.B && otherSignal ) {
    //.....

As you see, the content of the state variable is compared to the given state constant value. Usage of the identifier of the enum type definition is necessary in Java, it clarifies name clashing and increases obviousness and readability. Usage of switch …​ case is possible and may be also recommended in Java, but it is not yet supported in the Java2Vhdl translator. As given in the third query line the query of the state can be also combined with the logic relation to other signals (variables), which are the conditions for state usage. This is better possible in an if (…​ construct.

The Java2Vhdl translator detects a definition of a state constant with a bit value >=0, then only the query of the state bit is produced in VHDL. For this example the result in VHDL is:

VHDL: state variable query:
  IF(module_Prc.state = Module_MyState_Undef) THEN
    --.....
  ELSE IF ( module_Prc.state(0)='1') THEN
    --....
  ELSE IF ( module_Prc.otherSignal AND module_Prc.state(1))='1' THEN
    --....

The first comparison of state used the state value 0b00000 which is not marked with a valid bit, hence the Java2VHDL translator generates a full comparison of the state vector with the constant value which is defined as

VHDL: state constant definition
CONSTANT Module_MyState_Undef : BIT_VECTOR(4 DOWNTO 0) :=  "00000";

The second comparison knows from the Java enum definition, bit 0 is associated, and generates the simple access to this bit value. The third query combines the BIT access to the state BIT_VECTOR bit with the BIT variable otherSignal as BIT-AND and converts outside of the paranthesis to the necessary boolean value for the IF query.

Java: state variable query:
  myBitVariable = (z.state == MyState.C) & otherBitVariable;

This is a combinatoric from a state query and a boolean, it is translated very simple to:

VHDL: state variable query:
  module_Prc.myBitVariable <= module_Prc.state(2) AND module_Prc.otherBitVariable;

It means it is only a BIT logical combination.

6.5. set state variables

To switch the state only the new state should be set:

Java: state variable set:
    if(condition){
      this.state = MyState.C;

This is immediately translated to

VHDL: state variable query:
    IF condition THEN
      module.Prc.state <= Module_MyState_C;

It means all state bits are determined. The optimizer of the FPGA routing process may detect not changed bits.

6.6. Nested and parallel states

As mentioned in the introduction, Harel-Statemachines have nested and/or parallel states. Especially nested states helps get overview, it follows the practical requirements.

In Java level for VHDL any sub state in a state should have its own state variable. Entry in a nested state needs set two state variables. Query of the composite state (the outer of nesting) queries and set only the outer state variable, the inner state remains its value. This is helpful for the entry to the "history state", to the given remained inner state back again.

These things are all able to do on user level. A translation between UML diagrams with given Harel state charts to Java and then to VHDL may be nice and possible, but not presented here. See links in german:

7. Timing constrains for place and route

The next examples are related to the Lattice Diamond tool. Whether constrains should be given in the same syntax form for other tools (Xilinx & co) is not yet clarified.

See also basic considerations in the chapter Timing constraints for CE and steadyState

Simple, the whole FPGA design should have the same clock. That is a basic mentioned in chapter Principle of functional simulation of synchronous state machines, and it makes sometimes easier to manage timing and functionality. But this is precisely why a higher clock frequency is usually used as the base clock, even though certain functions can operate in slower cycles.

The basic in VHDL for this use case is:

--From example: BlinkingLed_Fpga.vhd
ct_Q_PRC: PROCESS ( clk )
BEGIN IF(clk'event AND clK='1') THEN

  IF ce_Q.ce='1' THEN
  --.... logic working only if ce is set
  END IF;

It is a clock enable strategy. The Flipflops remains unchanged if ce='0'.

The adequate form in the Java src is similar:

Java: constructor for a PROCESS in VHDL using ce
//fpga/exmpl/modules/BlinkingLedCt.java
    @Fpga.VHDL_PROCESS Q(int time, Q z, Ref ref, Modules modules) {
      Fpga.checkTime(time, ref.clkDiv.q.time, 1);  // for the ce signal, constraint with 1 clock delay.
      if(modules.ct_clkDiv.q.ce) {
        Fpga.checkTime(time, z.time, 20);        // check whether all own process signals are persistent since 20 time steps.
        Fpga.checkTime(time, ref.cfg.time(), 20);// check all signals from the referenced module. 
        this.time = time;                        // all variables are declared as possible set with this time stamp.
      //.... logic working only if ce is set
    }

Supposed, this ce signal comes with a cycle of 200 ns as in the example, you can write a constrain in the lpf file of the lattice Diamond tool:

PERIOD PORT "clk" 10.000000 ns ;
MULTICYCLE FROM GROUP "ct_q" TO GROUP "ct_q" 20.0 X;

It means that the routing regards the basic clock frequency of 100 MHz or 10 ns cycle, but between the members of the GROUP "ct_q" the timing path can have till 16 times slower, 160 ns. With this information the place and route tool can route firstly the fast paths, then with the remaining resources the slower paths. The place and route tool is specialized for that requirement. In result you have a proper routing result without timing violations if your requirements to timings are not too hard. But it is precisely because slower paths are allowed, because the possible cycle is slower, that this is possible.

But the remaining quest is: How to build the groups?

Here the Java notification and the test at Java level helps. The Fpga.checkTime(…​) statements are evaluated. The second argument is the time variable in the group, it designates the source group. The destination group is the from the own process. The constrain is the third argument, as mulitply of clock cycles or steps in the simulation.

If the same constraints are detected with different Fpga.checkTime(…​) statements, then of course these groups can be combined.

Also using an interface access (see chapter Interface technology in Java for VHDL allows the assertion with the access time. This is the importance for module interconnection:

Java: time constrain using an interface access
//fpga/exmpl/modules/BlinkingLedCt.java
        if(ref.reset.reset(time, 20)) {          // interface access to assigned here unknown reset module
          this.ct = ref.cfg.time_BlinkingLed();

The first argument is the current time, the second one the maximal delay. The check itself is (should be) done in the interface implementing operation, for the simulation. The constrain generator does not recognize it. It does regocnize the interface access with this two arguments. The source time group is the referenced module (left of interface call, here ref.reset with the resolving of the reference to the real module. The desination time group is the own, the constrain is the second argument.

In the version 2022-05 the time constrains are not yet automatically generated, this is todo in the nearer future. But the constrains are used for simulation, manuall written than for the place and route.

8. Test

The Test of the logic is a very important part.

General a test should be done in two categories:

  • Test under exact defined conditions expecting dedicated results. This is important for two situations:

    • Test of features which are assumed in requirements.

    • Repeated tests after changes to fast and automatic clarify, all is ok.

  • Test under accidently conditions. This tests are important to study the behavior independent of planned tests and exactly defined requirements.

    • It is possible that some conditions are not exactly defined, but that definitions should be intrinsinc necessary.

Furthermore, the tests should be done in three situations:

  • Test of the ready to use logic in the routed FPGA under several conditions (several input signals), of course in both categories as above presented, planned tests and accident tests (sometimes denoted by "white noise tests").

  • Test of the whole logic on Java level with dedicated test cases, or maybe also for accident inputs. Whereas a random value generator may be used in software. But of course the accident situations follows only the programmed randomizations, this is not a "white noise test".

  • Test of modules ("unit test"). Usual for that only planned tests are determined. The behavior of a module should follow exactly situations, this follows the denotation "design by contract". The contract of the behavior of a module should be well defined. A module should be manageable (rather than a complex system).

These are pure basics about tests that are generally applicable.

8.1. step and update operations

The content of the step(int time) and update() operations are not used for the VHDL translation. Essential for the VHDL translation are only the existence of the inner classes designated by the annotation @Fpga.VHDL_PROCESS in the modules and the instantiation of the modules in a class Modules in the top level file and also possible in modules for sub modules.

It means the (manually programmed) content of this routines should follow the existing module instances and process classes. Then only the behavior of the test is identically with the original FPGA behavior. It means, intrinsic, this operations should be generated also for Java level. But this is not done yet.

The step routine prepares the states before the next clock. The update routine is the clock, it is the manifestation for the next state.

8.2. Input signals for test simulation in Java

If you are testing the whole FPGA design usual the top level file has a Input and Output inner class with the designated input and output instance which presents the pins of the FPGA. Hence should only set the elements of the input with the appropriate values. This input values can come from simple test bed algorithm, or from more complex algorithm maybe also fet from values in tables, the test cases.

If you are testing a module usual the module has interface connections. You should satisfy this interface requirements by implementations in the test bed, for the appropriate input signals.

8.3. Output signals for manually evaluation of the test results

With given tools of FPGA simulation a graphical output is usual for instance:

ExampleUsual wave

Of course an adequate approach is possible for Java simulation because Java has graphical possibilities, it can be programmed in a specific way, or given routines in libraries of from other tools are usable. But is this the best one?

The very simple solution presented below may be also satisfactory:

ct.ctLow__________:0001 0000 ffff 61a7 ...0001 0000 ffff 61a7 ...0001 0000 ffff 61a7 ...0001 0000 ffff 61a7 ...0001
ct.ct_____________:63   63   63   62   62      62   62   61   61      61   61   60   60      60   60   5f   5f
ct.time___________:                    '5.001.20              '10.001.40             '15.001.60             '20.001.
io.ledA___________:_____________________________________________________________AAAAAAAAAAAAAAAAAAAAAAA_____________
io.ledB___________:_________________________________________________________________________________________________
=========================================================================
ok Test_BlinkingLed

This is the content of a simple text file, able to view with any normal stupid or better a powerful text editor, for example https://notepad-plus-plus.org/ or http://www.jedit.org/. The original lines produces by the Blinking_Led example has a length of ~5000 character, no problem for viewing, shifting and copy also a part to the clipboard using the "rectangular selection" mode in Jedit or the "column selection mode" on Nodepad++.

The lines are time lines. Each column position in each line presents the same time, more exact the start column of a longer information. Of course you should use a monospace font in the editor.

The resolution of such an output can be one system clock per column or character, or also condensed, for example a dedicated CE (clock enable) occurrence per column.

Because you can use proper characters to show a state, this is more obviously as only simple lines. Especially you can assemble more signals in one line using proper characters. For that look on a snippet of another example:

rx2.rxCE___________:____________----f---------f---------f--=======e=========e=========e==++++e++++++e++++++e++++++e++++________________
Time.time__________:^``8.54      |      |      |      ^``9.00       |      |      |      ^      |      |      |      |      ^``10.00

This first line shows three signals: * The 'f' or 'e' is a special clock enable signal, always one clock period wide. * The _ is used for the idle state, then also no ce should be occur. * The --- is one specific state, in this state a ce may occur as shown for the one clock period. * The === and + is each another state, also with occurrence of ce. The letter for the occurrence of the ce signal is also changed for better visibility.

To build this output, you should override the operation addSignals in the derived class of org.vishia.fpga.testutil.TestSignalRecorder in the following form:

Java: Example to build a character for output
char ce;
if(thism.qrx.ce) { ce = thism.qrx.dataState ? 'e' : 'f'; }
else if(thism.qrx.stateA && thism.qrx.stateB) { ce = '='; }
else if(thism.qrx.stateA) { ce = '-'; }
else if(thism.qrx.stateC) { ce = '+'; }  //(not all is full decoded)
else { ce = '_';}
this.sbCe.append(ce);

You can use more mnemonic character for the states, and also for short signals in the state. The only one challenge is, a good documentation of the used characters. You get a very compressed representation with more expressiveness than many lines.

The second line shows the time, which is equidistant here. This signal is created using a instance of

Using this class, the time is always written on start of line, and then in a proper distance with 5- and 10 divisions.

The time in the line may not be equidistant, depending of the output routines. This is an advantage. You can show an incident in detail zoomed in time, whereas non interesting time spreads are condensed. The example shows it. The trigger for writing details is here the ctLow with the value 0x0001. It is shortly before zero crossing. The ct, the higher count is shown, its value changes if the ctLow reaches ffff and the reload value is also set if ffff is reached before, and not as expectable, if 0000 is reached before. This is the written logic. The time is also presented, only on end of this zoomed spread because it needs upto 13 character positions. The time is a millisecond value, proper readable till 10 ns resolution. As you can see the period of one count step of ct is not 5.00000 ms as expected than 5.00020 ms, 200 ns longer. This is an effect which is better able to see with this numeric time information than in a diagram with measurement.

The algorithm in Java to sort the output for this information is not complicated:

Java: Output routine for signals
//fpga/exmpl/modules/BlinkingLedCt.java
    @Override public int addSignals ( int time, int lenCurr, boolean bAdd ) throws IOException {
      BlinkingLedCt thism = BlinkingLedCt.this;
      int zCurr = this.sbCt.length(); // current length for this time
      int zAdd = 0;                   // >0 then position of new length for this time
      if(thism.ref.clkDiv.q.ce) {       // because the own states switches only with this ce, the signals should also recorded only then.
        if(thism.q.ctLow == 1) {        // on this condition
          this.wrCt = 5;              // switch on, write 5 steps info
        }
        if(--this.wrCt >0) {          // if one of the 5 infos shouls be written:
          StringFunctions_C.appendHex(this.sbCtLow, thism.q.ctLow,4).append(' ');    //append info
          StringFunctions_C.appendHex(this.sbCt, thism.q.ct,2);                      //append info
          if(checkLen(this.sbtime, zCurr)) {      // add the time information if here is space.
            StringFunctions_C.appendIntPict(this.sbtime, time, "33'331.111.11");   // append time info
          }
          zAdd = this.sbCtLow.length();  //length of buffers for new time determined by the sbCtLow, the longest entry.
        } 
        else if(this.wrCt ==0) {         // end of the 5 steps, append .... as separation
          this.sbCtLow.append("..... ");
          zAdd = this.sbCtLow.length();  //length of buffers for new time determined by the sbCtLow, the longest entry.
        }
      }// if ce
      return zAdd;       // will be used in TestSignalRecorderSet.addSignals(zAdd) to set all lines to this length
    }//addSignals

Generally for this example, adding a signal is only done if clkDiv.q.ce is true, because: This module changes only its state with this ce clock enable. It saves calculation time if this is quest firstly. Then the trigger for the output is mdl.q.ctLow == 1. With this condition the next 5 ce times are presented.

Because 5 characters are appended to sbCtLow, this is the longest appendix, all lines are elongated to this legth, as you can see in the ouput. It means this time uses 5 column positions in the output, in all output lines.

You should not see the effort to program the output, you should see the advantage to design your necessary output for complex signals and test cases. Programming in Java is much easier and safer than, for example, in the C++ language or in certain scripting languages often used for simulation tools. You can make extensive use of auto-completion during editing, you will get immediately writing mistake messages (syntax errors) and hints for correction.

This horizontal output as also a vertical output, the time is continued in the lines than in columns, is organized by two classes in the package org.vishia.fpga.testutil. It is explained in detail in Java2Vhdl_TestOutput.html and for the Blinking Led example in chapter Java2Vhdl_ToolsAndExample.html#JavaSrcTest

8.4. Test of modules or the whole design on Java level

In Java programming also the environment, the "test bed" should be written. In this test bed the inputs are determined and the outputs are gathered maybe in lists appropriate to states and times. This results can be compared with expected results in a very more proper way because Java is a safe and familiar language.

You can use several test environment approaches. It is recommended to use the simple org.vishia.util.TestOrg, see ../../../Java/docuSrcJava_vishiaBase/org/vishia/util/TestOrg.html

Last not least the link to the so named StimuliSelector should be also offered here:

This tool is not used yet for the FPGA test, but it is possible. It is used yet for Simulink tests, for algorithm tests in C/++, see ../../../emc/html/TestOrg/testStrategie_en.html#testStrategies and more.

9. Writing style of logic - data assignment versus situation thinking

It is a general question, orientation to data assignment or situation evaluation. What is meant by this?

  • Situation evaluation: Programming is a familiar idea of program flow. The typical construct is if …​ then …​ else. "If a situation is given, then do this and that". This is also the prevailing mindset for VHDL and for the programming style of the 1980th (before Object Orientation became really familiar).

  • Orientation to data assign: The real truth of programming is: Influencing data. That is also one of the ideas of Object Orientation. The objects are the data. Program flow and operation is only a way to influence the data. The quest is the state of data. The flow is only a mediator for action.

In graphical models such as Simulink or Labview immediately a data flow is modeled. This is a data flow between function blocks, another level, nor related here. This data flow concept seems to be in opposite to the modeling with UML, which shows not a flow but data relations especially in Class- and Object Diagrams.

Another topic is functional programming. This is more oriented to data assign because all is result of a function, and a function is the result of the input data. This is mentioned here only as additional information.

Look on examples for Situation evaluation versus Orientation to data assign:

9.1. Style: Situation evaluation, program flow

 if(condition) {
   data = changed;
 } else if(other_condition) {
   data = other_input;
   other_data = changed;
 }

Here it is not clarified whether the data are changed anywhere other too, as well as the other_data are changed in one of the given situation. The change of data are not well described. But the reaction of situations (test a condition) is well described. Usual the condition is only tested one time.

9.2. Style data assignment orientation (data flow)

 if(condition) {
   data = changed;
 else if(other_condition) {
   data = other_input;
 } else {
   //comment: data remain unchanged.
 }
 if(other_condition) {
   other_data = changed;
 } else if(specific_condition) {
   other_data = specific;
 } else {
   //other_data unchanged in all other situation
 }

Here we have two program blocks, because two different data are handled. The data are not related, except one other_condition influences both. But the conditions are secondary. Each program block for one data should be complete for this data.

The disadvantage of data assign orientation: Usual it needs more code. The test of conditions are programmed more as one time. For all data extra. That needs also more runtime for the program in a controller (if the compiler does not optimize).

The advantage of data assign orientation is: The program clearly shows, how data are changed. If one looks on the part for one data specification, it is complete. The data will not be changed anywhere else.

In practice, both approaches are often combined. Fundamental situation as "clear", "reset" are programmed with the situation approach: "what’s happen on clear:…​". But for details the data orientation should be better.

The argument of longer execution time is not applicable for the hardware design if complex combinatoric are stored in intermediate variable. If that is not done, the router may optimize the combinatoric too. Only the readability of the code is decisive.

9.3. Ternary or condition operator in Java: condition ? a : b

Now, for thinking to data orientation, in Java (as also in C/++ the so named ternary or condition operator can be used:

data = condition ? changed : other_input;

This is the simplest form with two variants. It can be more complex:

data = condition ? changed
     : other_condition ? other_input
     : data;

This is the same effect as the if in the examples above. But it is well obviously. Also the else branch is exactly determined. The compiler of software languages will remove the unnecessary assignment for the unchanged data. For this situation it is well documented that the data are used unchanged.

In Java you can use a final keyword for such data:

final int data = condition ? changed
               : other_condition ? other_input
               : old_data;

This is similar as https://en.wikipedia.org/wiki/Functional_programming functional programming. With the final keyword the compiler has more capabilities to optimize.

And now, for hardware design:

The new state of a FlipFlop or a FF group is complete determined by such an final functional construct. The logic is obvious at a glance.

9.4. Solutions for pure VHDL

As mentioned above, VHDL was created in a time (1980th) where structured programming was familiar. Additional to the known if …​ then …​ else …​ end if; VHDL knows:

  • 'case 'selection is when choice statements
    This is also typical for situation thinking. The statements can contain any assignment. The selection describes which situation is checked.

  • assignment value1 when condition1
    value2 when condition2
    else valueelse;
    This is exactly the behavior for the data assignment thinking. Only one variable is under consideration. It is set under several conditions.

This when …​ else statement was introduced also for using in processes (behavioral programming) with VHDL-2008. It is ideal for this case. But unfortunately VHDL-2008 doesn’t seem to be considerate by all tools and all thinking:

4. Try to only use when else outside a process even though it’s supported in VHDL-2008. Why? Because in 2020, some synthesis tools still have some bugs compiling VHDL-2008. Perhaps in the future this won’t be an issue. Plus, if you have to support an older FPGA using older tools, you won’t have 2008 as an option. Keep these issues in mind when you code. There are better options than when else if you need to have it in a process, like using case instead.

It means it is not able to use for a tool independent translation from Java.

9.5. Java2VHDL for condition operator

Hence, conclusion, in processes only IF can be used for translation of the Java condition operator.

 //Java:
 data = condition ? changed
      : other_condition ? other_input
      : data;

is translated to:

 IF condition THEN data <= changed;
 ELSIF other_conditon THEN data <= other_input;
 ELSE data <= data;
 END IF;

whereas the last line can be removed. This is the same behavior as written with the conditional operator in Java. What is obviously: The same left side term for the data assignment (data ⇐) is written for each branch. But this is not an disadvantage. It is readable, and the router and simulation tools with VHDL can proper deal with it. Only the readability of the source is a little bit not optimal because one line may contain a writing mistake. But because it is generated code - no problem.

Unfortunately outside of a process IF THEN ELSE cannot be used, here the WHEN construct is used for translation of such an conditional expression.

The same problem occurs on conversion of a bit in BIT logic from STD_LOGIC:

  -- in a PROCESS:
  IF myBitValue = '1' THEN myStdValue <= '1';
  ELSE myStdValue <= '0';
  END IF;

and outside of a PROCESS

  myStdValue <= '1' WHEN myBitValue = '1' ELSE '0';

9.6. Multiplexer in hardware design, problem of WHEN ELSE

In Java it is very simple to write in an expression:

boolean q = a & (b ? c : d) | e;
In this case `c` and `d` is multiplexed by selecting with `b`. The rest is boolean logic.
In VHDL theoretically a
c WHEN b='1' ELSE d

is existing. It works outside of a process. But this construct doesn’t seem to be considerate by all tools and all thinking:

4. Try to only use when else outside a process even though it’s supported in VHDL-2008. Why? Because in 2020, some synthesis tools still have some bugs compiling VHDL-2008. Perhaps in the future this won’t be an issue. Plus, if you have to support an older FPGA using older tools, you won’t have 2008 as an option. Keep these issues in mind when you code. There are better options than when else if you need to have it in a process, like using case instead.

It means, the simple expression for a multiplexer, which is familiar in hardware, is not possible for VHDL.

But instead, VHDL likes to see IF constructs:

IF b='1' THEN t = c; ELSE t = d; END IF;

This works also in processes, it is a basic, supported, known. But what is the disadvantage: As part of an expression we need a temporary variable. And this part of expression should be extracted as extra statement.

  PROCESS
  -- Java: boolean q = a & (b ? c : d) | e;

  SIGNAL b_sel : BIT;  --process variable
  BEGIN
  IF b = '1' THEN b_sel := c;
  ELSE b_sel := d; END IF;
  q <= (a AND b_sel ) OR e;

This complicates the readability. But we have VHDL, the best and safe language for hardware.

9.7. Programming in loops

Also VHDL knows loops. If you following https://vhdlguide.readthedocs.io/en/latest/vhdl/behave.html#problem-with-loops then loops should not be used.

In software two types of loops should be well distinguished:

  • Classic loops for repeated execution till a condition is met. It is typical a while loop, or repeat - until.

  • Loops only to execute an algorithm for all given instances. That is not really a execution in the loop, it is only written as loop in software to process all given instances. This can be done also parallel and also sequentially. But because the instances are contained in a so named container the loop works for all member of the container. This is typically a foreach loop. In Java it is written as:

 //Java
 for( Type element : container ) {
   //do for all element.
 }

Such constructs are parallelizable also in software, for example distributed on several cores of a processor. Why: Because all operations which are executed in a loop one after another are independent. It means the order of execution is not important.

This scheme can now be used for hardware designs for parallelization. Of course the FPGA should have enough resources for the task.

It is also interesting that each element in a container can have a different derived type. It means really, different operations are executed (via virtual operations). Now, thinking in hardware: You have a planned container with elements, the elements are a little bit different and you need a design for this elements:

  • a) You write code for each element extra.

  • b) You use a for-each loop. But because the type of the elements are only known in runtime (depends on other program parts which may be not in focus) such a VHDL code as generated parallel code can be built also especially in run time. The executed statements are not executed, instead they produce VHDL code.

This is another approach, currently not supported by the Java2VHDL concept, maybe done in future.

10. Literature, links