-
Notifications
You must be signed in to change notification settings - Fork 68
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Preprocessor - expose complete parser? #165
Comments
There is no python API to handle preprocessor stuff. Your usecase is not clear to me. I would suggest to reuse directly the antlr grammar to handle verilog and systemVerilog preprocessor (https://github.com/Nic30/hdlConvertor/blob/master/grammars/verilogPreprocLexer.g4, https://github.com/Nic30/hdlConvertor/blob/master/grammars/verilogPreprocParser.g4) to access an AST. Thomas |
I do have some notes which you may find useful:
|
Thanks for the extra info. I am sorry to be vague about the use case, I am writing python to generate synthisable SV code from a menu of components. Each component needs to be checked for syntax then the final generated output checked. The issue is ifdefs and includes which need careful attention. (expecially includes inside ifdefs) Multiple identical includes need optimising. So I am really only operating in the preprocessor. I would rather avoid C++ to prevent scope creep on the project. |
Usually user don't like to read generated code. It end up to be very repetitive, with no so interesting comments inside. Base of this observation you could just print the code and avoid as much as possible usage of preprocessor directive. Code beauty is a human personal satisfaction but efficiency most of time is what pay-off at the end of the day. |
Hi, |
Only real dependency are the definitions of enums for language version. Things like https://github.com/Nic30/hdlConvertor/blob/master/grammars/verilogPreprocLexer.g4#L9
Can you please post some example of your input and output code and mark what you need to detect/translate? So please provide some examples so I can give a better advice. |
Our company is very pedantic about sharing code IP, even fragments. I would probably lose my job if I were to share even a tiny piece of code here. It goes so far as to scanning public repos for possible leaks. But I can paraphrase it.... Generally it is module instantiation of pre-built IP blocks, e.g. /// higher level file
`resetall
`timescale 1ns/10ps
`include "define_stuff"
<<== Other includes as required
<<== Collected includes deduplicated here
/*
This module is generated by a code generator. The generator is controlled by a build database.
We have a menu of dozens of code blocks, each module tested every day by Jenkins.
The generator knows about module interoperability and supplies a list of valid combinations.
Any given FPGA RTL is built from a subset of those modules and tested as an integrated block.
The test bench is automatically generated to match the top level build in the same way as this.
as well as those modules being include / excluded by the processing system.
*/
// <<== Means generated code possibly inserted here after parser has processed this module.
// RTL and testbench code is generated from human written HDL like templates.
module outer #(
parameter PARAM1 = 1,
PARAM2 = 3
// <<== Additional params as required
) (
input IN_PINS,
output OUT_PINS,
some_interface.controller interface_instance,
`ifdef SOME_BUILD_PARAM
// BUILD_SPECIFIC code
// <<== Other build specific pins as required
'endif
// <<== Other pins as required (perhaps with ifdefs)
);
`include "RTL_stuff"
// <<== Other includes as required, e.g. interface definitions
interface_def inst_interface1();
// <<== Other interfaces as required
SOME_STATIC_MODULE static_module_inst1
(
.MODULE SIGNAL1 (INSTANCE_SIGNAL1),
.MODULE SIGNAL2 (INSTANCE_SIGNAL2),
// <<== Other pin connections as required
);
// Static assignments
assign INSTANCE_SIGNAL1 = IN_PINS;
assign OUT_PINS = INSTANCE_SIGNAL2;
// <<== Other module instances as required, generated from templates.
// <<== Module assignments as required by generator
endmodule //end of outer
////// lower level file
// NOTE: Dynamic module templates
// Pins, parameters and preprocessor symbols required to insert the module are detected by the generator
// Code between braces {like_this} is completed by the generator before insertion
// Code validity is verified by the parser to compare declared pins (after rendering template)
// with those pins present in the higher level module instantiation.
`include "required_include"
module xyz_wrapper #(
parameter staic_params = static_value,
{template_params = template_value}
) (
input sub_module_pin1,
output sub_module_pin2,
{template_pins)
);
xyz_module wrapped_xyz (
.pin1 (sub_module_pin1),
.pin2 (sub_module_pin2)
);
`ifdef MODULE_INCLUDED
// do that
`else
// do something else
`endif
endmodule
So here the parser must detect parameters, pins and preprocessor symbols that must be defined should the sub-module be included in the build. |
Can you point me to any documentation that explains the code structure? I am trying to work out what direction to go to achieve the above task. I have half a mind to return to my attempts using PEG, other other to work out how to produce a similar wrapper for the hdlConverter preprocessor. I think I'm strugging to understand what code is machine generated (e.g. from ANTLR) what is boilerplate and what is original code. My understanding, correct me where I am wrong. |
Yes, cmake is "makefile generator" but you can also use ninja-build or visual studio project etc. as an output from cmake
Yes it is linked there https://github.com/Nic30/hdlConvertor/blob/master/src/CMakeLists.txt#L55
All generated code is from antlr which translates .g4 files or from cython which transpiles the .pyx files
These are internal libraries which are linked together to create hdlConvertor.so
setup.py is common python package installation script, this one is based on https://scikit-build.readthedocs.io/en/latest/usage.html#example-of-setup-py-cmakelists-txt-and-pyproject-toml
Yes, it seems to me as most comfort option in other project I am using SWIG or pybind11 but for this library Cython seems to be good enough. However I was considering moving from scikit-build + cmake + Cython -> meson + Cython or pybind11. |
If I understand your example you need to generate "tops" based on components used somewhere in the hierarchy.
Because of this and many other issues in our team we decided to generate tops entirely and instead of templates in SV like string we are using python AST object to generate the code directly and import SV/VHDL modules as black boxes. We are using HWT, e.g. in this project of superscalar packet classifier with DDR4. But there are other tools which can do the same. I do not want to say that this is a better way, nor that it is compatible with your company ideology. I am just saying that if you start using system verilog templates there will always be some obscure restriction or complicated config but and if you use python as a preprocessor and code generator it has some advantages. |
Great, thanks. It seems my understanding of code structure is reasonably accurate. I will continue to look at what I have to do to get AST out of the preprocessor in the same way the existing parser works. A bit more background: You are correct; Computed changes are mostly towards the outside. They also affect module level verification and top level functional verification test benches. Qualcomm, being esentially an IP company, means we are re-using propretary IP from many sources across the organisation. I like the sound of HWT and I will spend some time looking at it. It may be something I am able to use. However, one fundamental requirement of this project is that the wrappers, adapters and modules are written by Verilog designers who we don't mandate have any Python ability. I originally thought use Python to describe the whole thing but that would put a requirement on the sort of skills that the developer who produces targets from this system has. Another requirement is that I have to use YAML to define/serialise each build. That way it is compatable with other systems and existing knowledge. Currently the YAML is being used to create globaldefs though a simple build script. My predecessor (now retired) has done this using pure SV, resulting in a lot of global parameters, includes and ifdefs. My goal is to move away from that over time as it's difficult to maintain, review and grow, and gradualy replace it with a template basd system that produces human readable and commented RTL as well as testbench code that builds without any further human input but is still human readable. I went with a simple text based template system as that seemed apropriate, especially as I am are using Verilog like syntax for the templates. Something recognisable by Verilog engineers, allowing them to quickley adopt, but is non-synthisisable so can't 'escape' via accidental source tree merges etc into other projects. |
Hello You can parse the code with hdlConvertor several time, depending of the number of combination of macro definition you need to support. Then you analyze and recombine all result to build a single data model with all require detail of existence of each port. |
I can see that the way hdlConverter works is that it first passes the code through a preprocessor.
Then produces and AST from the resulting text.
This preprocessor 'flattens' the code, replacing
defines and
ifdef/else blocks, etc.I am finding a problem with this as I need to know what is inside those backtick blocks.
My use case is more complicated than this, but I can give a sort of practical example.
e.g.
I want to merge two code blocks programmatically.
Both code blocks include the same files.
I want to move that include to the top of the generated file so that it's not included twice as that would cause redefinition.
But in one file that include may (or may not) be inside an undefined `ifdef, in that case it should not be moved.
This might be because depending on that ifdef a different include is made.
So I need to know where the `ifdef starts and ends so that I can resolve the ifdef, but that parsing operation is not exposed to the user, it's transparent.
Is there a way to see the AST that the preprocessor uses, before flattening, rather than the output from hdlAst?
The text was updated successfully, but these errors were encountered: