Version: 2022-06-04

See also:

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

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

1. Approach

This document describes the tools for translation Java to VHDL, test at Java level using an example.

2. Working tree organization for sources and tools

Look in the given example Example1_BlinkingLed.zip. The content of this file may be but need not be the template for the file tree organization.

Generally the idea of the SwEng/srcFileTree.html is used, a file tree similar as the familiar used maven or gradle file tree, whereas maven or gradle itself is not used here.

Path/to/myWorkingTree
 +-src                all sources should be versioned
 +-tools              tools loadable from internet
 +-build              output directory for build outputs (may be in RAM disk, or temp location)

The example contains some more files and directories on root level, but this files are really only for the simple example. It should be assembled with adequate content in a user project inside the src tree.

Path/to/myWorkingTree
 +-src
 +-genVHDL_cmp              directory creates the generated files for compare with build
 +- +clean.bat              batch helper file to clean all
 +- +clean_mkLinkBuild.bat  batch helper file to create the build directory
 +- +gen_Vhdl_Example1.bat  batch file for start generation

The tools and build sub directory are created with batch files (Windows-oriented) or adequate shell scripts. The empty build directory will be created and removed by the above shown +clean.bat and +clean_mkLinkBuild.bat. This files may be assembled in your user source tree at adequate positions in the src/…​ with adequate content.

The following files are given, to load the tools from internet, see also ../../../SwEng/html/srcFileTree.html#libsTools and the following chapter Tools necessary for Java to Vhdl translation and test support

Path/to/myWorkingTree
 +-src/load_tools
        +-+loadTools.bat   script to create the tools directory and load tools
        +-tools.bom        so named "bill of material" to determine which tools from where
        +-vishiaMinisys.jar  a simple java executable to execute the load

The src working tree is organized in the following form:

src
 +-main              generally for the application (the main path, maven-like)
 |  +-java
 |  +-vhdl
 +-test              support for test (maven like)
 |  +-java
 |  +-fpga...
 +-load_tools        helper for the tools, not in maven concept.

main and test separates between the product relevant files (in main) and test files. The next level below main and test is the kind of source files, here Java files or some VHDL files, or other if necessary. C and C++ files, maybe necessary for other parts of the whole project, are usual stored in a cpp subdir. fpga…​ is a hint to any other test files, user specific.

The level below should be determined by the components directory, see also ../../../SwEng/html/srcFileTree.html#components. For our example and tools we have:

src
 +-main
 |  +-java
 |     +-srcJava_vishiaFpga              common necessary Java sources from vishia
 |     +-srcJava_FpgaExmplBlinkingLed    the specific user sources, here the example
 +-test
    +-java
       +-testJava_FpgaExmplBlinkingLed   the specific test sources, here the example

It means, if you want to use Eclipse as environment for Java development (recommended), you need three linked folders to work. For organization of eclipse projects, see also ../../../SwEng/html/EclipseWrk.de.html (unfortunately yet only in German).

Inside the Java components you have the familiar Java package tree, starting in this case all with org/vishia. The Java package tree is familiar since the first Java development in the 1990th. It is a world wide unique deterministic of packages using the revers internet address as first members. Hence all parts which are developed related to the https://www.vishia.org web page (Java related parts) are denoted in the org/vishia/…​ package tree. For your own you should use your web presence as start path such as com/siemens/department/…​ if you are from the Siemens company or com/bosch/department/…​ if you are working in the Bosch company or whatever else, as usual in your company. This should be only understand as hint or notice, may or may not be important.

3. Tools necessary for Java to Vhdl translation and test support

The necessary tools for Java to VHDL translation are really less. It is only jar files to work with Java.

Java itself should be familiar for usage. This examples and tool files are related to the long term provided Java-8 version from Oracle, but also some open source Java may usable.

After loading the Java files from the internet via clicking on src/load_tools/+loadTools.bat you get the following files to work:

2022-05-23  14:14               502 +loadTools.bat
2022-05-23  14:48               584 tools.bom
2022-05-23  14:49         1.500.128 vishiaBase.jar
2022-01-24  20:25            81.128 vishiaMinisys.jar
2022-05-23  14:49            56.282 vishiaVhdlConv.jar
               5 Datei(en),      1.638.624 Bytes

If you look on src/load_tools/tools.bom you see the following:

#Format: filename.jar@URL ?!MD5=checksum

#The minisys is part of the git archive because it is need to load the other jars, MD5 check
vishiaMinisys.jar@https://www.vishia.org/Java/deploy/vishiaMinisys-2022-05-31.jar  ?!MD5=6c111b696267b9a4292e8d15bad7c8b2;

#It is need for the organization of the generation.
vishiaBase.jar@https://www.vishia.org/Java/deploy/vishiaBase-2022-05-31.jar  ?!MD5=aa6d0b2cbbd8973eb127e3ccc25683bb;

##Special tool for Java2Vhdl
vishiaVhdlConv.jar@https://www.vishia.org/Java/deploy/vishiaVhdlConv-2022-05-31.jar  ?!MD5=39bcbd8f65940b770f51800465bcc1d4;

This textual file is executed by the Java class org.vishia.minisys.GetWebfile which is contained in the here also registered vishiaMinisys.jar. It contains the internet location for the jar file, the destination file name and a MD5 checksum. You can do this actions also manually, build and compare the check sum. The files are able to view and load in the given location, this is https://www.vishia.org/Java/deploy. You find also the source files beside the jar files with the same name, only with the extension -source.zip All is open source, you can study the algorithm, and also compile it newly. The source-zip archive contains a _make directory. You should only place all depending jar files or sources (that is here srcJava_vishiaBase) side beside. Depending jar files should be placed in a tools directory beside:

After newly translation you get the same jar files with exactly the same binary content and hence the same check sum. This is the approach of reproducible build, see also ../../../Java/html5/source+build/reproducibleJar.html and also https://reproducible-builds.org/reports/2020-03/. It means you can both check the correctness of the MD5 check sum and check whether the sources are really valid for the given binary.

As you see, the VhdlConv itself is only a small file consist of a few Java classes. No more is necessary. But the basics, independent of the VHDL approach, the Parser, text generator etc. are all contained in the vishiaBase.jar. But this file has also only 1.5 MByte. The other used tools are only the Java-8 system from Oracle. No other tools and executables are used. Nothing is stored in any temporary or home/user locations. Getting the core of the job done usually doesn’t require sprawling tools.

4. The platform to edit the Java sources for VHDL

It is recommended to use Eclipse, but also another IDE is possible as your choice.

You can also use any text editor to view the sources.

To compile and run the example independent of an IDE you can use the batch file compilation. But you need he javac compiler (part of JDK, Java Development Kit, https://www.oracle.com/java/technologies/downloads/.

Look at src/main/java/srcJava_FpgaExmplBlinkingLed/_make/+makejar_exmplBlinkingLedFpga.sh.

The compile result will be written in the build folder. From there it can be run starting src/test/bat/test_Example1_BlinkingLed.bat. This is for your experience.

5. The translation Java to VHDL

This is only the start of a command line execution, for the example:

 java -cp tools/vishiaBase.jar;tools/vishiaVhdlConv.jar org.vishia.java2Vhdl.Java2Vhdl -sdir:src/main/java/srcJava_FpgaExmplBlinkingLed -sdir:src/main/java/srcJava_vishiaFpga org.vishia.fpga.exmplBlinkingLed.fpgatop.BlinkingLed_Fpga -o:build/BlinkingLed_Fpga.vhd -tmp:build/ -rep:build/BlinkingLed2Vhdl_report.txt

This is a very long line because of the arguments, not obviosly. Therefore a better solution is possible, given in the example:

java -cp tools/vishiaBase.jar;tools/vishiaVhdlConv.jar org.vishia.java2Vhdl.Java2Vhdl --@%0:convArgs
REM info: one space after the label, then trim all trailing spaces also without comment
::convArgs ##
::-sdir:src/main/java/srcJava_FpgaExmplBlinkingLed            ##source dirs from current
::-sdir:src/main/java/srcJava_vishiaFpga
::-top:org.vishia.fpga.exmplBlinkingLed.fpgatop.BlinkingLed_Fpga   ##top level file to translate in first source dir
::-o:build/BlinkingLed_Fpga.vhd                               ##output
::-tmp:build/
::-parseData                                                  ## The java data tree to view
::-parseResult                                                ## The parse result list
::---parseLog                                                 ## an elaborately parse log
::-rep:build/BlinkingLed2Vhdl_report.txt                      ##report with meta information
pause

General with the argument --@path/to/argfile some arguments can be read from a file. Whereas each line of the file is one argument. That makes it also possible to use white spaces in arguments without quotation marks. But often an extra file for that is not nice. With a label after the argfile path after colon the argument processor searches this label in the argument file. The label should be places on beginning of the line, but after maximal 5 comment characters. The comment characters then must be written also leading before the arguments in the line. This helps to separate comment lines from other content in any file, in this case in the batch file itself. The first line without these comment character, here the pause line is then the termination of argument lines. Additionally the arguments can be commented with the comment character given after the label. One space between label and argument comment characters forces removing trailing spaces in the line, which is often sensible but not at all. Hence it can be controlled here.

With this argument designation the arguments are well readable.

A short explanation of the arguments comes if the converter is started without arguments:

Java2Vhdl made by HSchorrig, 2022-02-16 - 2022-05-31
 see www.vishia.org/Fpga/html/Vhdl/Java2Vhdl_ToolsAndExample.html
-i:path/to/template.vhd  ...optional, if given, read this file to insert
-o:path/to/output.vhd
-top:pkg.path.VhdlTopModule ... the top level java file (without .java, as class path)
-sdir:path/to/srcJava  ... able to use more as one
-sl ... optional, if given, remark src and line
-parseData ... optional, if given, writes the parser java data tree
-pd ... optional, same as -parseData
-parseResult ... optional, if given, writes the parser result
-pr ... optional, same as -parseResult
-parseLog ... optional only with -parseResult, writes an elaborately parser log file
-pl ... optional, same as -parseLog
-tmp:path/to/dirTmp for log and result
-rep:path/to/fileReport.txt   ... optional

This is of course only a short description, with the link to this document.

  • The -i:path/to/template.vhd can be used if only a part of the VHDL file should be generated, the frame is given with this file. The generated parts are firstly the TYPE …​ RECORD definitions and the SIGNAL …​.:_REC instances, ` and secondly the PROCESS . The given file should contain labels in the following form:

  ...start of the file, with heading, ENTITY, Ports
ARCHITECTURE BEHAVIORAL OF ....

-- INSERT Java2Vhdl
  ... This parts are replaced by the new generated one TYPE ... RECORD definitions
  ... and SIGNAL ....:_REC` instances
-- END Java2Vhdl
  ... further content, SIGNAL and COMPONENT definiton, especially the
BEGIN
  ... and more given content
-- INSERT Java2Vhdl
  ... This parts are replaced by the new generated processes
-- END Java2Vhdl
  ... finishing content
  • -o:path/to/output.vhd is also used if -i:…​ is given. It means the -i:…​ file will not be replaced, only read. It may be recommended to generate a new file first to a temporary location in the file system, and then compare because of changes, at least replace.

  • -top:pkg.path.VhdlTopModule This is the class path with package path of the top level Java class for the FPGA design. Usual this class contains a class Modules inner class to determine all other sub modules.

  • -sdir:path/to/srcJava This argument can be given more as one (usual) as search path for the Java files. It contains the directory where the Java package path starts (with org/…​), not the directory of the Java file itself.

  • -sl means "source line". If given then in the generated -o:…​ file the source file and the line of the Java source for the appropriate generated VHDL line is written as ---path/to/src: line. This helps to associate generated lines and Java source lines. However, using this feature makes it a little bit difficult to compare a newly created file with the previous version because often the lines are shifted in the source, hence only all the line numbers are changed. It makes really changes lesser obviously. It may be recommended to generate both versions, with and without this option, and store both as second source, without line numbers for a simple version comparison and with line numbers to search assiciations with the Java sources.

  • -tmp:path/to/dirTmp It is possible to output intermediate files for parsing results etc. especially during development, not used in the compiled version.

  • -rep:path/to/fileReport.txt This is an interesting report file about modules, interfaces, variables and should be stored beside the VHDL output file.

6. The component srcJava_vishiaFpga

This component contains some Java files. They are necessary in a user’s project for test and for using annotations and call specific operations. It means this component should be used as source file tree. It is located for the example.zip in:

src
 +-main
   +-java
      +-srcJava_vishiaFpga
         +-org/vishia/fpga
                       +-stdmodules/*.Java     useable in the design
                       +-testutil/*.Java       useable for test on Java level
                       +-Fpga.java             define some standard operations and annotations
                       +-FpgaModule_ifc.java   the essential module interface

You don’t need (must not) change the content of these files, only use it. It is also versioned (yet TODO Github)

7. The example Blinking LED, view to Java sources in respect to the FPGA description

This is a study example or template for your own. The sources are located for the exmaple.zip in:

src
 +-main
   +-java
      +-srcJava_FpgaExmplBlinkingLed
         +-org/vishia/fpga/exmplBlinkingLed    This should be your own package path for other projects
            +-fpgatop/*.java
            +-modules/*.java
            +-test/*.java                      only for test on Java level, may be in test folder, see next chapter

The content of the files are partially described already in the approach document regarding interface concepts etc.

This chapter describes it from the view of the template for your own.

7.1. The top level FPGA java file

All following code snippets comes from the main/java/srcJava_FpgaExmpl…​/fpgatop/BlinkingLed_Fpga.java.

7.1.1. Package and class definition, import

In Java always the name and path of the file itself should match to the package declaration and name of the only one public class inside the file: The Java file starts with the package declaration. The package names and also the appropriate directories on the file system must be written starting with a lower case character.

Java: top level class definition
package org.vishia.fpga.exmplBlinkingLed.fpgatop;

import org.vishia.fpga.stdmodules.Reset;
import org.vishia.fpga.Fpga;
import org.vishia.fpga.FpgaModule_ifc;
//Note: Do not use a package.path.*, not yet supported by Java2Vhdl
import org.vishia.fpga.exmplBlinkingLed.modules.BlinkingLedCfg_ifc;
import org.vishia.fpga.exmplBlinkingLed.modules.BlinkingLedCt;
import org.vishia.fpga.exmplBlinkingLed.modules.ClockDivider;

public class BlinkingLed_Fpga implements FpgaModule_ifc {

The import statements name all used classes from other packages. It is possible to use an asterisk to select all Files in the package. But then the dependencies are not well documented. In this case anyway all files in the 'exmplBlinkingLed.modules' package should be part of, then it is ok.

The module class should implement the FpgaModule_ifc. That is all necessary. This interface defines:

Java: FpgaModule_ifc definition as basic interface
public interface FpgaModule_ifc {
  void step(int time);
  
  void update();
  
  
}

7.1.2. The modules in the top level

The class definition continues with the

Java: used modules in the top level
  /**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 ClockDivider ce = new ClockDivider();
    
    public final BlinkingLedCt ct = new BlinkingLedCt(this.res, BlinkingLed_Fpga.this.blinkingLedCfg, this.ce);    //cfg implemented in extra class in this file.
    
    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 ref;

Because you can generate a Javadoc it is recommended to comment all modules in the given style (here not all are commented). But also a non commented style is sufficient because you can use cross referencing in the IDE.

The modules should be connected immediately here, either on instantiation with a parameterized constructor, or with the init(…​) operation in the constructor of modules. It depends on circular referencing whether the immediately referenced instantiation can be done. That is more simple. But using init(…​) has more flexibility.

Firstly in this class the ioPins are defined. This is done in an extra class, see next chapter.

The writing style with the explicitly this is recommended. this is also implicitly accept (can be omitted), but then the relations are worse documented. The BlinkingLed_Fpga.this writing style is necessary for the translator. BlinkingLed_Fpga.this is the reference to the environment class. Java can automatically detect this relation, it checks whether the following identifier is able to find either locally, or in the own class, or in all environment classes. This is more error prone because of confusion in identifier usage. Hence the dedicated writing style EnvironmentClass.this prevents the confusion. For the Java2Vhdl translator it is also more simple.

7.1.3. step(…​) and update() operations

Following the both routines are defined:

Java: top level class step update
  @Override
  public void step(int time) {
    this.ref.res.step(time);
    this.ref.ce.step(time);
    this.ref.ct.step(time);
    
  }


  @Override
  public void update() {
    this.ref.res.update();
    this.ref.ce.update();
    this.ref.ct.update();
    this.ref.ioPins.output.led1 = this.ref.ct.ledBlinking();
    this.ref.ioPins.output.led2 = this.ref.ct.getLedFast.getBit();
    this.ref.ioPins.output.led3 = Fpga.getBits(this.ref.ct.ct(), 2,0) != 0b000;
  }

Both routines should call all step(time) and update() of all sub modules. The time comes from the simulation environment useble for time checking, see Java2Vhdl_Approaches.html#timeCnstrn and for signal output, see Java2Vhdl_Approaches.html#testOutp.

7.1.4. interface agents in the top level

The so named interface agents are anonymous class definitions as interface implementation to access data in this module. They are usable for referencing.

Java: top level class interface agent/access
  /**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

  };

In the top level they should be used either for stubs instead not implemented modules, for test designs or variations, but also for parameter of modules which should be determined in the top level. This is shown above. The interface access implements a …​Cfg…​ interface for configuration parameters. It is used as reference for the ct(…​, BlinkingLed_Fpga.this.blinkingLedCfg,…​ module, see chapter The modules in the top level

7.1.5. test output in the top level

The top level does not need test output if it has no own PROCESS sub classes. The output preparation for the main level can be done immediately in the test environment, see chapter Test output preparation for the main level. This has no meaning for the Java to VHDL translation, writing this stuff to the test java file unburdens this file for translation.

7.2. The FPGA pin description file

It is a good idea to separate the Pin description source file from the top level file, because different inner FPGA designs can use the same pinning. This is typical if you have a hardware board with a given layout, but the content of the FPGA should be varied. Then you need define the pinning only one time for the given layout.

Furthermore it is also possible to have more as one top level FPGA file for different parts, but you should have only one file for the pinning, because this is board layout related.

7.2.1. How to designate the ioPins file

The pin description file should be designated as ioPins in the Modules class of a top level:

Java: ioPins definition in the top level
  /**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();

See also chapter The modules in the top level. The designated class file is searched and translated especially for IO pinning.

The ioPins file starts due to Java conventions with

Java: BlinkingLed_FpgaInOutput class definition head
package org.vishia.fpga.exmplBlinkingLed.fpgatop;

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

public class BlinkingLed_FpgaInOutput {

7.2.2. Input and Output inner classes

For this example the pin classes are short, for more pins it is a little bit more, very simple:

Java: BlinkingLed_FpgaInOutput Input and Output
  public static class Input {

    /**A low active reset pin, usual also the PROGR pin. 
     * Because of the specific function the access should only be done with the here defined access operation, hold it package private, not public. 
     * For test it can be accessed in a test access class in the same package.*/
    boolean reset_Pin;
  }

  
  public static class Output {
    
    /**Ordinary output pins, use public in responsible to top level design sources. */
    public boolean led1, led2, led3;
  }
  
  /**This instances are final and public accessible. 
   * The inputs should be used in the step operations as given on start of D-calculation.
   */
  public final Input input = new Input(); 
  
  /**The outputs should be set in the update() operation of the top level with the Q-Values of FlipFlops. */
  public final Output output = new Output(); 
  • If the same pin should be used both as input and output, then the same pin name should be defined in the Input and in the Output inner class.

  • If a pin should have tristate character (often necessary on input/output switch), it should be designated as char type and set with '0', '1' and 'Z'. (TODO for the 2022-05 version).

  • The Input pins can be declared as package private (without designation) if an interface access is defined here also to set the pins. See next chapter. Elsewhere they can be designated as public for simple set accesses for tests.

  • Output pins should be declared as public for immediately access for test and to set from the different top level FPGA java file maybe in different packages.

The output pins are written immediately in the update() operation on the top level:

Java: top level class using interface implementation and simple access operation for output
    this.ref.ioPins.output.led1 = this.ref.ct.ledBlinking();
    this.ref.ioPins.output.led2 = this.ref.ct.getLedFast.getBit();
    this.ref.ioPins.output.led3 = Fpga.getBits(this.ref.ct.ct(), 2,0) != 0b000;

7.2.3. Interface access to the Input pins

The input pins are usual accessed via interface references from view of a module. Hence the Java2Vhdl_Approaches.html#IfcAccess should be defined in a proper way for all input pins. For specific pins a specific interface also with more as one bit can be used. For common ordinary pins the following simple interface is sufficient:

Java: Bit_ifc
package org.vishia.fpga.stdmodules;

import org.vishia.fpga.Fpga;

/**This is a very common interface only for one bit for any usage.
 * For some reason an input is necessary and an output is offered,
 * but the connection from the input to the output is determined not by a module,
 * It is determined by the interconnection of modules.
 * In such cases a specialized interface should not be used.
 * The connection should be proper for any plug.
 * The responsibility of the correctness of the connection is associated to the interconnection design only.
 * <br>
 * The implementor should have the annotation <code>@Fpga.IfcAccess</code>, example:
 * <pre>
  public @Fpga.IfcAccess Bit_ifc txReqMaster = new Bit_ifc ( ) {
    @Override public boolean getBit() { 
      return SpiMaster.this.spiM.stateCmd;  
    }
  };
 * </pre>
 * @author Hartmut Schorrig
 *
 */
public interface Bit_ifc {

  boolean getBit ( );
}

In this example only the access to the only one reset_Pin is necessary

Java: BlinkingLed_FpgaInOutput Interface access
  /**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; }
  };

This uses the Reset_Inpin_ifc as used in the accessing module org.vishia.fpga.stdmodules.Reset, see the Modules definition in the top level file.

To set this inpin a set operation is necessary because of the non public property of the pin variable. It is a good decision for overview (divide and conquer) to write this set operation in an extra file, but in the same package:

Java: BlinkingLed_IoAcc for test, the whole file:
package org.vishia.fpga.exmplBlinkingLed.fpgatop;

/**This class is only for test to access Pins in {@link BlinkingLed_FpgaInOutput}
 * @author Hartmut Schorrig
 *
 */
public class BlinkingLed_IoAcc {

  /**Operation to set the reset Inpin, with the hint that this pin is low active.
   * @param val the immediately pin value, true for high, inactive.  */
  public static void setLowactive_reset_Inpin(BlinkingLed_Fpga fpga, boolean val) { 
    fpga.ref.ioPins.input.reset_Pin = val; 
  }
  
}

7.3. A module file

All following code snippets comes from the main/java/srcJava_FpgaExmplBlinkingLed/ org/vishia/fpga/modules/BlinkingLedCt.java.

7.3.1. Package and class definition, import and module interface

The principles in Java are already explained in Package and class definition, import.

Java: module class definition
package org.vishia.fpga.exmplBlinkingLed.modules;

import org.vishia.fpga.Fpga;
import org.vishia.fpga.FpgaModule_ifc;
import org.vishia.fpga.stdmodules.Bit_ifc;
import org.vishia.fpga.stdmodules.Reset_ifc;
import org.vishia.fpga.testutil.TestSignalRecorder;
import org.vishia.util.StringFunctions_C;

import java.io.IOException;

public class BlinkingLedCt implements FpgaModule_ifc, BlinkingLed_ifc {

Here the advantage of dependency documentation of the import statements are shown. In opposite to C/++ programming where the dependencies are documented with included headers, the used modules are immediately obviously. In C/++ maybe hidden dependencies may be existing because an header can include other headers, and the association between header and implementation files is weak. In Java it is strong, clarified and obviously.

You see that external dependencies exists to one of standard modules, to test utils and to a special class for String preparation, which is used for test output.

The module class implements the BlinkingLed_ifc which is a module interface, see Java2Vhdl_Approaches.html#IfcModule, the implementation is shown in the chapter interface implementation of the module

7.3.2. The references and sub modules of the module

The class definition continues with the

Java: module class references
  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;

The meaning and the writing style of the references is also explained in Java2Vhdl_Approaches.html#aggr.

Also there is explained how the references are set. It is the same example.

A module can also have sub modules to build a deeper tree of modules. It is adequate to the top level. (TODO 2022-05 not tested, should work)

7.3.3. Inner static classes in a module which builds a TYPE RECORD and PROCESS in VHDL

The following code snippet shows one PROCESS as a whole.

Java: module PROCESS class and instances
  @Fpga.VHDL_PROCESS private static final class Q{

    @Fpga.STDVECTOR(16) final int ctLow;
    @Fpga.STDVECTOR(8) final int ct;
    final boolean led;
    int time; 
    
    Q() {
      this.ctLow = 0;
      this.ct = 0;
      this.led = false;
      this.time = 0;
    }
    
    @Fpga.VHDL_PROCESS Q(int time, Q z, Ref ref) {
      Fpga.checkTime(time, ref.clkDiv.q.time, 1);  // for the ce signal, constraint with 1 clock delay.
      if(ref.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.
        if(ref.reset.reset(time, 20)) {          // interface access to assigned here unknown reset module
          this.ct = ref.cfg.time_BlinkingLed();
          this.ctLow = 0x0000;    
        }                                     // underflow detection to 111.... as simple hardware-saving solution. The counter itself can use a carry-logic. 
        else if(Fpga.getBits(z.ctLow, 15, 13)==0b111) {    // check only 3 bits and not all bits =0
          this.ctLow = 0x61a7;  // 24999;                  // Period 25 ms, hint cannot use the range 0xe000..0xffff to prevent immediately underflow detection
          //                    // TODO should convert automatically also a given INTEGER constant to STD_LOGIC_VECTOR
          if(z.ct == 0x00) {                     // here a full 0 test with 8 bit is done. effort in hardware.
            this.ct = ref.cfg.time_BlinkingLed();// interface access to the reload value
          } else {
            this.ct = z.ct -1;                   // high counter normally count down automatically proper implemented in FPGA
          }
        }
        else {
          this.ctLow = z.ctLow -1;               // count down automatically proper implemented in FPGA
          this.ct = z.ct;                        // high counter copy the state (not generated to VHDL, it is implicitely there)
        }
        this.led = z.ct < ref.cfg.onDuration_BlinkingLed(); //set FF after comparison with 8 bit.
      }
      else {                                     // clock enable ce == false
        this.ct = z.ct;                          // copy the state (not generated in VHDL)
        this.ctLow = z.ctLow;
        this.led = z.led;
        this.time = z.time;
      }
    }
  }
  
  private Q q = new Q();
  private Q d_q;     

This is the core functionality for VHDL and hence explained elaborately:

  • The inner class should be marked with the @Fpga.VHDL_PROCESS annotation to detect as such from the Java2Vhdl translator.

  • Variables in the inner class are generated in a type record with the class name:

TYPE BlinkingLedCt_Q_REC IS RECORD
  ctLow : STD_LOGIC_VECTOR(15 DOWNTO 0);
  ct : STD_LOGIC_VECTOR(7 DOWNTO 0);
  led : BIT;
END RECORD BlinkingLedCt_Q_REC;
  • Variables started with time are ignored for VHDL conversion, there are for timing test on Java level.

  • SIGNAL definitions in VHDL with the TYPE RECORD (instances for this RECORD) are not created because of the module PROCESS class definitions. Instead, they are defined only if the module class is used, and then with the module(s) name(s). It is possible to have a module more as one time, then also more SIGNAL …​ RECORD variable are defined. Here for the usage in the fpgatop/BlinkingLed_Fpga.java:

Java: top level part of Modules definition:
  public class Modules {
    ....
    public final BlinkingLedCt ct = new BlinkingLedCt(....
Vhdl: SIGNAL definition for this PROCESS of a module:
SIGNAL ce_Q : ClockDivider_Q_REC;
SIGNAL ct_Q : BlinkingLedCt_Q_REC;
SIGNAL res_Q : Reset_Q_REC;
  • You can see in the mid of this three signals the module name ct from the module definition in Modules combined with the name of the Process Q. If you have more PROCESS classes in a module, of course you have more TYPE RECORD definitions and also more appropriate SIGNAL definitions.

  • The constructor Q() {…​} is only for Java test. It is (should be) related to the reset behavior of the FPGA: Signals are reset to 0-level. A specific reset behavior is not provided. It is also not full clarified in VHDL, depends on FPGA types. The 0-initialization is anyway applicable. For specific reset behavior the following process should use a reset signal functionality.

  • The constructor @Fpga.VHDL_PROCESS Q(Q z, Ref ref) { …​ now describes the process for VHDL translation and also for simulation. It should/could have the following arguments with fixed naming conventions:

    • int time reference to a time value used for timing test in Java, not used for VHDL

    • z of the same type: It is the previous state for calculation, comes from the current state in step.

    • ref reference to the Ref class of this module, to access referenced modules.

    • mdl reference to the whole module class (set with this on call, see next chapter).

    • in reference of a Input class of the module for a simple wiring without references.

    • out reference to an Output class of this module, more exact to the _d instance (prepared values). This allows the simple wiring and public access in Java.

  • The PROCESS inner class is private, should be used only in this module and not accessed from outside. The elements are package private (without designation in Java). It can be accessed anyway only in this module because of the private nature of the class.

  • The content of this constructor is immediately translated to the VHDL PROCESS. To see it for this example look on the translated code:

Vhdl: PROCESS due to the BlinkingLedCt.Q constructor:
ct_Q_PRC: PROCESS ( clk )
BEGIN IF(clk'event AND clK='1') THEN

  IF ce_Q.ce='1' THEN
      IF (res_Q.res)='1' THEN
          ct_Q.ct <= TO_STDLOGICVECTOR(BlinkingLed_Fpga_time_BlinkingLed);
      ELSE
        IF ct_Q.ctLow(15 DOWNTO 13) =  "111"  THEN
            ct_Q.ctLow <=  x"61a7";
            IF ct_Q.ct = x"00"  THEN
                ct_Q.ct <= TO_STDLOGICVECTOR(BlinkingLed_Fpga_time_BlinkingLed);
        ELSE
                ct_Q.ct <=  ct_Q.ct - 1 ;
            END IF;
        ELSE
            ct_Q.ctLow <=  ct_Q.ctLow - 1 ;
        END IF;
      END IF;
      IF ct_Q.ct < BlinkingLed_Fpga_onDuration_BlinkingLed  THEN ct_Q.led  <=  '1'; ELSE ct_Q.led  <=  '0'; END IF;
  ELSE
  END IF;
END IF; END PROCESS;
  • Variable with time…​ are ignored for VHDL, they are for timing checks in Java.

  • Variable which are set with the same z. variable are ignored for VHDL because this is the standard behavior for VHDL: Not assigned variables preserve there values. But that assigments are necessary for Java.

  • Because all variables should be final a complete unique assignment of all variables is necessary for Java. This helps preventing errors on forgotten not clarified functionality:

  • Because you should write this.var = z.var; in Java it is clarified in the Java source that this is an unchanged value. The place and route for the FPGA can use either the own Q output of the FF for the logic, or can work with the CE (clock enable) input for the FF groups.

  • You cannot forget variables, because Java checks setting of all final variables.

  • Tip for writing sources with final variables: You can firstly remove all final keywords, set it one after another. Then the error messages for missing final are obviously step by step. You can set an assignment this.var = z.var; in all branches, then set the variable to final, then you get an error message on this positions where the variable is defined twice. Remove it and think about correct assignments due to the requested logic. At least you have all variables final and no errors.

  • If you use already assigned this. variables on the right side for an assignment, this would be the value of the D-input of an FF. VHDL suggests using an internal variable for that. In the moment (2022-05) this is not regarded, but can be implemented in the Java2Vhdl translator.

How statements and expressions are translated, see Java2Vhdl_TranslatorInternals.html.

  • At last in the Java example with private Q q = new Q(); and private Q q_d; two references for instances are defined.

    • The q reference must have the same name as the PROCESS class, only start with a lower case character. Here it is only one character. This reference is used to access to the RECORD instances in VHDL both on public immediately definition of this reference (possible) and also for interface accesses.

    • In this example the reference q is private. This means that you can be sure that there is no direct access from outside, both for the Java source level and for the VHDL record type.

    • The _d instance is not used for translation and should only accessed in the step(time) and update() operations. It holds the prepared D-input values of the FlipFlops. It should be anytime private

    • Note that Java knows for class instances only references (other than in C/++). All is a reference. The instances are organized in the heap.

7.3.4. step(…​) and update() operations

The step and update in a module should call the process execution. It is not used for the VHDL translation, but for the test.

Java: module class step and update
  @Override
  public void step(int time) {
    if(this.ref.clkDiv.q.ce) {             // speed up simulation, only on ce the data are calculated new.
      this.d_q = new Q(time, this.q, this.ref);
    } else {
      this.d_q = this.q;
    }
  }

  @Override
  public void update() {
    this.q = this.d_q;
  }

You see here how does it work:

  • step(time) creates new instances of all process variables with new combinatoric values and stores it in the private …​_d instance. Therefore, it is blocked for third-party access. Other modules should use only the Q-state of the FlipFlops to preserve the exact timing. All step(time) routines of all modules are executed firstly before update()

  • update() stores all prepared D-values in the real used instances for all immediately and interface accesses.

It means, any step in Java creates a new instance. The instance is allocated in the heap. If you have 120000000 steps for the BlinkingLed example, the Java simulation creates 120000000 instance of any PROCESS class type for any used module. The simulation runs on a Notebook with normal modern equipment approximately 5 seconds, no more. It needs approximately 0.3 GByte RAM (measured with the Task Manager on Windows-10). It means Java can proper deal with this request.

If sub modules are present, of course this should be also called here.

7.3.5. interface implementation of the module

The next code snippet shows two accesses:

Java: module class interface implementation and simple access operation
  /**Implementation of a given interface which is also fulfilled by this module. 
   * @return value for a led which should be blinking.
   */
  @Override public boolean ledBlinking() { return this.q.led; }

  /**This is an example for an access operation only for this module,
   * without abstraction of using an interface.
   * Hence it is only defined for this module, not with a universal interface,
   * it should be used especially for necessary test outputs wich have only meaning using this module,
   * not for input interfaces for other modules.
   * @return the counter.
   */
  @Fpga.GetterVhdl public int ct() { return this.q.ct; }
  //@Fpga.BITVECTOR(8) //TODO evaluate it 

The first operation with the annotation @Override overrides the declaration in the BlinkingLed_ifc, see chapter Package and class definition, import and module interface. It is used on the top level of the FPGA for the outputs in the update() operation of the top level in the first line, see code snippet below.

The second operation with the annotation @Fpga.GetterVhdl is an access operation without an interface. It means it cannot be used as univeral access, only for this module. Hence it is not proper for an input reference of another module, because then another implementation and therefore access via an interface is necessary. But this operation is usable both for specific test outputs on the FPGA and to build output values only for the module specific implementation. It is used on the top level of the FPGA for the outputs in the update() operation of the top level in the last line:

Java: top level class using interface implementation and simple access operation for output
    this.ref.ioPins.output.led1 = this.ref.ct.ledBlinking();
    this.ref.ioPins.output.led2 = this.ref.ct.getLedFast.getBit();
    this.ref.ioPins.output.led3 = Fpga.getBits(this.ref.ct.ct(), 2,0) != 0b000;

7.3.6. interface agents or access in a module

The so named interface agents are anonymous class definitions as interface implementation to access data in this module. They are usable for referencing.

Java: module class interface agents/access
  public @Fpga.IfcAccess Bit_ifc getLedFast = new Bit_ifc ( ) {
    @Override public boolean getBit() {
      return Fpga.getBits(BlinkingLedCt.this.q.ct, 2,0) == 0b000;
    }
  };

See the example in the top level, chapter interface agents in the top level In this case the interface access is used for the output, see the last code snippet in the chapter above: 'top level class using interface implementation and simple access operation for output'.

The implementation accesses the ct variable in the q instance of the module class. The writing style BlinkingLedCt.this. means in Java the access to the (named) environment class. For debugging this reference is shown as this$0 whereas this$1 etc. are higher level environment classes. In Java this can be omitted, but then it is lesser obviously. For the Java2Vhdl translator it is (yet 2022-05) necessary to write.

7.3.7. test output

The module source.java is continued with

TestSignalRecorderHead

Java: module class TestSignalRecorder class definition
  public class TestSignals extends TestSignalRecorder {
    ....
  }

This class is not used for the VHDL generation, but used for test. It is explained in the next main chapter.

8. The example Blinking LED, view to Java sources in respect to test on Java level

If you want to separate test files (with a lot of complicated test cases) from the sources, you can also place the test Java files in

src
 +-test                                        use the test sub folder
   +-java
      +-testJava_YourComponent
         +-package/path/your/component         This should be your own package path for other projects
            +-test/*.java                      only for test on Java level.

But for this simple example the test class is part of the same Java source tree:

src
 +-main
   +-java
      +-srcJava_FpgaExmplBlinkingLed
         +-org/vishia/fpga/exmplBlinkingLed
                            +-test/*,java    only for test on Java level

The test classes offers test cases/pattern and checks.

The source code of the modules offers Test output signal generation functionality which are universal usable for the tests.

8.1. The main test source

8.1.1. Class definition and instances to test and used for test

Look firstly to that org/vishia/fpga/exmplBlinkingLed/test/Test_BlinkingLed.java which contains the main start routine:

Java: main test routine class definition
package org.vishia.fpga.exmplBlinkingLed.test;

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

import org.vishia.fpga.exmplBlinkingLed.fpgatop.BlinkingLed_Fpga;
import org.vishia.fpga.exmplBlinkingLed.fpgatop.BlinkingLed_IoAcc;
import org.vishia.fpga.testutil.CheckOper;
import org.vishia.fpga.testutil.TestSignalRecorder;
import org.vishia.fpga.testutil.TestSignalRecorderSet;
import org.vishia.util.TestOrg;

public class Test_BlinkingLed {

  BlinkingLed_Fpga fpga = new BlinkingLed_Fpga();

You see on the depedencies (import statements) that the test does not need knowledge about the modules. This is because the test signals are built independently in the modules, the access is via the TestSignalRecorder interface.

On top of the test class the BlinkingLed_Fpga fpga = new BlinkingLed_Fpga(); FPGA top level is instantiated one time. It is also possible to instantiate more as one FPGA to check interaction. It is also possible to instantiate here some simulation replacements for environment hardware, all what’s necessary.

If this is a module test class, then the module(s) should be instantiated here with their test environment (test bed).

8.1.2. Instantiate a horizontal output recorder

Java: output recorder instance and definiton in start of a test routine
  TestSignalRecorderSet outH;
  
  
  void test_All ( TestOrg testParent) throws IOException {
    this.outH = new TestSignalRecorderSet();
    this.outH.registerRecorder(this.fpga.ref.ct.new TestSignals("ct"));
    this.outH.registerRecorder(this.new TestSignals("io"));
    this.outH.clean();

The instance should be created new in any test routine for that test outputs. After them the desired TestSignalRecorder instances of the modules should be added. One module can have more as one instances of TestSignalRecorder. They are used to compile the desired output information. For different test cases you can get just different output information.

The detailed content of the output information is determined in the modules itself, see the chapter Determination of information to record for output horizontal.

8.1.3. Organization of a checked test

The next line shows only the creation of a test check support class, see org.vishia.util.TestOrg.

Java: Instance of TestOrg in the test routine
    TestOrg test = new TestOrg("testTxSpe", 6, testParent);

This class helps to organize some tests with a concise output for automatic test case evaluation.

8.1.4. Initialize stimuli (signals) for the simulation

This are the initials settings of all input pins. It can be set either immediately or via interface operations.

Java: Initialization stimulis / signals
    BlinkingLed_IoAcc.setLowactive_reset_Inpin( this.fpga, true);           // reset inactive high as only one input

For this example only the reset pin is used. For the test it is hold to inactive high during the whole operation. The functionality of the reset pin itself can be tested with another programmed test, or is already tested using the reset module in other contexts. Hence, it is not important here.

8.1.5. Run the simulation for this test case

In the simulation loop all FPGA simulations and also conditions of inputs to the FPGA which are not constant (depends on outputs, own stimuli signals) should be calculated. The basic step time is the clock.

Java: statements to run the test
    int time = 0;
    while(++time < 120000000) {
      this.fpga.step(++time);
      this.fpga.update();
      if(this.fpga.ref.ce.q.ce) {             // speed up simulation, only on ce the data are calculated new.
        this.outH.addSignals(time);
      }
    }

To simulate the FPGA(s) the appropriate step(time) one after another and after them all update() should be called. After update() the preparation of inputs to the test bed can be gotten from FPGA outputs of this step time. The test bed calculations can be done then in the next loop as first before the fpga.step(). Here nothing else is to do.

Also after all updates the outH.addSignals(time) is called to record all signals in all modules, organized in the outH of type TestSignalGeneratorSet, see sub chapters above.

Here it is shown that the addSignals(…​) is conditionally called only if …​ce. This means that the output signals are not resolved down to the individual clock level. The ce is the central clock enable for the most module functions, and only outputs regarded to this ce are in focus. This may be important for longer runinng tests, such as here for more as 100 million steps. Usual, if the signals on any clock edge are interesting, the simulation time is lesser and focused on module functionality. This is not a principle approach, it is sensible.

8.1.6. Output recorded signals

The output of the recorded signals is only a simple writing of all StringBuilder registered in the TestSignalRecorder in the order of registering. The output written to a text file can be edited for presentation also afterwards.

Java: output signals of horizontal recording usual for manually elaboration
    this.outH.output(System.out);
    Writer fout = new FileWriter("build/test_BlinkingLed_Signals.txt");
    this.outH.output(fout);
    fout.close();

It is also possible to produce output signals in a file with vertical recording (one line is one time stamp, signals in the line). This is not shown here yet. Especially this format can be converted to a signal graphic similar as the output of FPGA simulation tools, with the possibility of zoom etc.

8.1.7. Automatically evaluation of test results

In the next code snippet two output lines of the horizontal signal recording are checked. The special function checkOutput(…​) as specific implementation for the check tests the pattern in the line: The signals should be a minimal and a maximal number written one after another. The evaluation starts after the first change, not from beginning, because elsewhere the minimal number of characters may be violated.

Java: evaluation of test results for automatically test
    CheckOper.CharMinMax[] checkLedA = { new CheckOper.CharMinMax('_', 200, 9999), new CheckOper.CharMinMax('A', 200, 300)};
    //Note: a shorter low phase of LedB occurs on overflow of the high bit counter.
    CheckOper.CharMinMax[] checkLedB = { new CheckOper.CharMinMax('_', 26, 200 ), new CheckOper.CharMinMax('B', 26,26)};
    String error = CheckOper.checkOutput(this.outH.getLine("io.ledA"), 20, checkLedA);
    test.expect(error == null, 5, "LedA off 200.. chars, on 200..300 chars" + (error ==null ? "" : " ERROR: " + error) );
    error = CheckOper.checkOutput(this.outH.getLine("io.ledB"), 20, checkLedB);
    test.expect(error == null, 5, "LedB off 180..200 chars, on 26 chars" + (error ==null ? "" : " ERROR: " + error));
    
    test.finish();

This test is programmed done. It produces with the following test.finish() a concise state "Test ok or ERROR" which can be simple automatically evaluated. It means this and more tests runs, and the result should be "ok" for all tests.

With this approach it is possible to test whether the functionality is proper (for the test cases) after some changes in the sources. May be specific changes were done, and the results were checked manually. But now the question arises: Has the change side effects? Is everything else still running?

To answer this, such tests, which can be elaborately, are important.

Of course, the test cases, the stimuli and the evaluation can also be faulty or incomplete. In conclusion, the effort for the test cases is often higher than the effort for the functionality itself. It depends on the type of use (long-term or intermittent) whether this expense is appropriate.

8.1.8. The main routine for test

The main routine is called immediately from command line level. It creates the own class, and starts some tests.

The top level TestOrg assembles all children TestOrg for more tests and a summarized evaluation.

Java: TestSignalGenerator implementation complete for conditional output
  public static void main(String[] args) {
    Test_BlinkingLed thiz = new Test_BlinkingLed();
    TestOrg test = new TestOrg("Test_BlinkingLed", 3, args);
    try {
      thiz.test_All(test);
    }
    catch(Exception exc) {
      System.out.println(exc.getMessage());
      exc.printStackTrace();
      test.exception(exc);
    }
    test.finish();
  }

Generally a try …​ catch should be present to catch non expected exceptions. With the stack trace the reason may be able to find also in non debugging environments. For example any uninitialized stuff may cause a null pointer exception which is not necessarily a flaw in the logic, only a small programming mistake. In general, Java’s try-catch capability is well proven.

8.1.9. Test output preparation for the main level

This is the contribution of test signal output from the main test or the whole fpga to view and also for automatically check the results. This top level output test results is not implemented in the top level FPGA java file (here BlinkingLed_Fpga.java), instead in the test routine, to unburden the top level java file. The access to the Pin signals is anytime possible in a public way, in opposite to the situation in the modules, see next chapter.

The code snippet shows the complete TestSignalRecorder

Java: TestSignalRecorder implementation complete for conditional output
  class TestSignals extends TestSignalRecorder {

    StringBuilder sbLedA = new StringBuilder(500);
    StringBuilder sbLedB = new StringBuilder(500);
    
    private char cLedA, cLedB;
    
    /**This instance should be added on end using {@link TestSignalRecorderSet#registerRecorder(TestSignalRecorder)}
     * because it decides adding an information only depending of other SignalRecorders.
     * @param sModule name, first part of line identifier
     */
    public TestSignals(String sModule) {
      super(sModule);
    }

    /**cleans all StringBuilder line and registered it. */
    @Override public void registerLines ( ) {
      super.clean();
      super.registerLine(this.sbLedA, "ledA");
      super.registerLine(this.sbLedB, "ledB");
    }
    
    @Override public int addSignals ( int time, int lenCurr, boolean bAdd) throws IOException {
      BlinkingLed_Fpga fpga = Test_BlinkingLed.this.fpga;
      if(bAdd) {                                  //only calculate state if another line has additional information.
        if(fpga.ref.ioPins.output.led1) {
          if(fpga.ref.ioPins.output.led1) {
          }  
        }
        this.cLedA = fpga.ref.ioPins.output.led1 ? 'A': '_';
        this.cLedB = fpga.ref.ioPins.output.led2 ? 'B': '_';
        this.sbLedA.append(this.cLedA);
        this.sbLedB.append(this.cLedB);
        return this.sbLedA.length();
      } else {
        return 0;  // no own contribution to length, regard add, sub ordinate.
      }
    }
    
    
    /**This operation is here overridden to add the character of the led state instead adding spaces as separator. 
     * It will be called in {@link TestSignalRecorderSet#addSignals(int)} after info to all lines are added.
     * Which character is added, this is determined by addSignals above in this class. */
    @Override protected void endSignals ( int pos) {
      while(this.sbLedA.length() < pos) { this.sbLedA.append(this.cLedA); }
      while(this.sbLedB.length() < pos) { this.sbLedB.append(this.cLedB); }
    }
    
  }
  • The Recorder should define StringBuilder for each test signal for store and output, here two lines.

  • The char cLed…​ are only locally, it stores the values for complete the lines in endSignals(…​).

  • The constructor has the name of the module as parameter. The hint in the comment is for the order, see chapter Instantiate a horizontal output recorder.

  • The clean() operation should be overridden in the shown kind, should register the locally StringBuilder calling registerLine(…​).

  • The addSignals(…​) operation is subordinate here, it writes the state of the Led output pins, but only if another module, here it is especially BlinkingLedCt has written something. This is sufficient, because the Led signal is slow and long. The other module produces enough time stamps to see what happens. But such decisions depends heavily on the test case. Hence the output should tuned to the test case. On the other hand the test cases are often similar in output requirements.

  • The endSignals(…​) is here overridden to produce the repeated character for the LED. The standard implementation writes a space to separate hexa values, which is often the requested use case.

8.2. Test support in modules

The decision which signals are to be output for the display of the test results can only be made in a module itself, since only the module knows its own signals. This is often seen in a different way, namely that the test case itself should determine which signals should be displayed. However, this assumes that the signals are known and not changed. It also violates the private/public encapsulation of content in modules.

But of course the decision which signals to display may depend on the particular test case. For such possibilities more as one TestSignalRecorder inner class can be implemented. The implementation can be done due to a specialized test case, but should be designed in a more universal way. The question is which signals are interesting, for a detailed look at the module - or to get an overview. The structure of the module, not the specific opinion of the tester, should be determinative.

8.2.1. Determination of information to record for output horizontal

For this example a specific detail is programmed in the next code snippet: It is the elaborately view to the time spread where the low-bit counter overflows and triggers the high-bit counter. All other occurrences are irrelevant, they are clarified. This is the interesting point in the module, and may be also the interesting point for a global test view.

Java: TestSignalGenerator addSignals(…​) with condition building
    @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

Here the output is triggered if the counter reaches the value 1, back counting before zero-crossing. Details of this are also used to present the common approach in Java2Vhdl_Approaches.html#testOutp.

8.2.2. Store and restore the state of modules as well as the whole simulation state

For the example the following Store class is able to find in the common offered Reset module:

Java: Store class in a module
  /**Stores the state for special tests.
   * You can use this implementation as template for your modules.
   */
  public static class Store extends StateStoreFpga < Reset > {
    final Q q;

    /**Creates a Store instance, which refers the data from the {@link Reset#q} instance,
     * it is the PROCESS data, able to call after a defined simulation procedure,
     * to resume later exact from this state.
     * @param time The time stamp of the simulation
     * @param src The reference to the module.
     */
    public Store(int time, Reset src) {
      super(time, src);
      this.q = src.q;
    }

    /**Restore the state to the same module, which is used on creation.
     * It is presumed that the {@link Reset#q} instance was not changed meanwhile.
     * However, this is guaranteed if the Application Pattern Style Guide is followed,
     * and also because all members in {@link Reset.Q} are <code>final</code>.
     */
    @Override public int restore() {
      super.dst.q = this.q;
      return super.time;
    }
  }

If you look to the description of the base class:

you find also the pattern for application. This is not done in the simple Example.

The storage of the whole state of the simulation (all modules in the FPGA and also the environment, test bed state) is helpfully if you want to simulate variants starting from a dedicated state. You save the effort to reach the start state again from beginning.