Version: 2022-06-04
See also:
-
Java2Vhdl_Approaches.html - Basically information about the concept
-
Java2Vhdl_TranslatorInternals.html - Internals about the implementation
-
../../deploy/Example1_BlinkingLed-2022-05-26.zip A zip file containing an example and link to the tool base.
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. 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 theTYPE … RECORD
definitions and theSIGNAL ….:_REC
instances, ` and secondly thePROCESS
. 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 aclass 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 (withorg/…
), 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.
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:
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
/**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;
The modules are aggregated together as described in Java2Vhdl Approaches, chapter More possibilities with Java2VHDL: References (aggregations) in Object Orientation kind.
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:
@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;
}
See also Java2Vhdl_Approaches.html#stepupd
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.
/**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:
/**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
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:
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 theOutput
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:
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:
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
/**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:
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.
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
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.
@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 theTYPE RECORD
(instances for thisRECORD
) 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 thefpgatop/BlinkingLed_Fpga.java
:
public class Modules {
....
public final BlinkingLedCt ct = new BlinkingLedCt(....
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 inModules
combined with the name of the ProcessQ
. 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 theRef
class of this module, to access referenced modules. -
mdl
reference to the whole module class (set withthis
on call, see next chapter). -
in
reference of aInput
class of the module for a simple wiring without references. -
out
reference to anOutput
class of this module, more exact to the_d
instance (prepared values). This allows the simple wiring andpublic
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 theprivate
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:
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 assignmentthis.var = z.var;
in all branches, then set the variable tofinal
, 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 variablesfinal
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();
andprivate 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 onpublic
immediately definition of this reference (possible) and also for interface accesses. -
In this example the reference
q
isprivate
. 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 thestep(time)
andupdate()
operations. It holds the prepared D-input values of the FlipFlops. It should be anytimeprivate
-
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.
@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;
}
See also Java2Vhdl_Approaches.html#stepupd
You see here how does it work:
-
step(time)
creates new instances of all process variables with new combinatoric values and stores it in theprivate …_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. Allstep(time)
routines of all modules are executed firstly beforeupdate()
-
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:
/**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:
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.
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
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:
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
The horizontal output recorder is an instance of ../../docuSrcJava_vishiaFpga/org/vishia/fpga/testutil/TestSignalRecorderSet.html.
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.
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.
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.
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.
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.
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.
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
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 inendSignals(…)
. -
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 locallyStringBuilder
callingregisterLine(…)
. -
The
addSignals(…)
operation is subordinate here, it writes the state of the Led output pins, but only if another module, here it is especiallyBlinkingLedCt
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.
@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:
/**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.