Skip to content
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

Transform include header files from C++ to C. #1676

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

m-kru
Copy link
Contributor

@m-kru m-kru commented Mar 8, 2021

This is a draft. There are still a few things that must be resolved.

  1. Should typedef be used for compound types?
    This is invalid C code.
  struct Name_Id { unsigned int id; };
  inline const char *get_cstr(Name_Id n) {
    return name_table__get_address (n.id);
  }

There are 2 ways to make it valid:

  typedef struct Name_Id { unsigned int id; } Name_Id;
  inline const char *get_cstr(Name_Id n) {
    return name_table__get_address (n.id);
  }
  struct Name_Id { unsigned int id; };
  inline const char *get_cstr(struct Name_Id n) {
    return name_table__get_address (n.id);
  }
  1. As in C symbols are global there must be some prefix policy. Normally it is {lib}_{module}_{name}. So for example logic_32 would become ghdl_synth_logic_32. However, the libghdl.a already uses ghdlsynth prefix. Using 2 styles will be confusing, so it is possible to adopt {lib}{module}_{name} style or rename the functions from libghdl.a.
  2. Is it necessary to name types In_This_Way in header files? Wouldn't ghdl_synth_name_id look better then ghdlsynth_Name_Id? Functions are already typed with snake case. Or is there policy This_Case for types, and this_case for functions?

@tgingold
Copy link
Member

tgingold commented Mar 8, 2021 via email

@m-kru
Copy link
Contributor Author

m-kru commented Mar 8, 2021

For 3: It was a matter of consistency. Name_Id in Ada, so Name_Id in C. But as it is now prefixed, we can convert to lower cases. I would use the ghdl_ prefix for names defined in the non-synth part (like Name_Id) and ghdlsynth_ for named defined in synth part (all the other ones).

I guess this will be in another PR, as some stuff needs to be moved out from the synth.
For example, I doubt

  //  Disp ghdl configuration.
  void ghdlcomp__disp_config (void);

  // Initialize the whole library.
  void ghdlsynth__init_for_ghdl_synth (void);

is related only with synth.

@tgingold
Copy link
Member

tgingold commented Mar 8, 2021 via email

@m-kru
Copy link
Contributor Author

m-kru commented Mar 9, 2021

@tgingold Following issues need to be resolved:

  1. There is a clash with is_valid function names as C does not support function overloading.
    All of them looks like that:
  inline bool is_valid(struct Sname l) { return l.id != 0; }

Do we really need a function in this case? If the check is limited to checking the id field (and if it is not going to change soon) I would remove the is_valid functions and implement that check directory in the code. I would not follow the C++ schizophrenia for adding getters and setters for everything, even if there is literally no reason to do so.

  1. Clash with get_id names. This is the same story as with is_valid. The only difference is that this is one is implemented with macro.
/usr/local/include/ghdl/synth.h:152:3: error: conflicting types for 'get_id'
  152 |   GHDLSYNTH_ADA_WRAPPER_DW(get_id, enum Module_Id, struct Instance);
      |   ^~~~~~~~~~~~~~~~~~~~~~~~
/usr/local/include/ghdl/synth.h:112:3: note: previous definition of 'get_id' was here
  112 |   GHDLSYNTH_ADA_WRAPPER_DW(get_id, enum Module_Id, struct Module);
#define GHDLSYNTH_ADA_WRAPPER_DW(NAME, RESTYPE, ARGTYPE)	\
  RESTYPE GHDLSYNTH_ADA_PREFIX(NAME) (unsigned int); \
  inline RESTYPE NAME(ARGTYPE arg) { \
    return GHDLSYNTH_ADA_PREFIX(NAME) (arg.id); \
  }
  1. Which ones of following types are related strictly with synth and which ones are rather related with GHDL globally?
    logic_32, Name_Id, Sname, Sname_Kind, Width, Port_Idx, Param_Idx, Pval, Module, Net, Instance, Input, Attribute?

@tgingold
Copy link
Member

tgingold commented Mar 9, 2021 via email

@tgingold
Copy link
Member

tgingold commented Mar 9, 2021 via email

@m-kru
Copy link
Contributor Author

m-kru commented Mar 9, 2021

For 1: I think it is a useful abstraction: the name of the (simple) function clarifies the intent. I would keep it but add a prefix. Or keep it only for C++.

The same abstraction can be achieve by introducing macro

#define SNAME_INVALID 0

and comparing against it.

@m-kru
Copy link
Contributor Author

m-kru commented Mar 9, 2021

You can write an alternate C header without all these extra features. It would then be the common interface.
And also rewrite the existing C++ header (using the C header) so that C++ users would be happier and so that you don't have to modify the plugin.

This is an option. however I feel like it would result with more code to maintain in the future.

@m-kru
Copy link
Contributor Author

m-kru commented Mar 9, 2021

@tgingold I get it to compile with C. I have also skimmed through Ada sources and how they are represented in the libghdl.a. I have an idea how to structure the include code.

Functions in libghdl.a are automatically prefixed with the package name. Why not to resemble this structure in the include files? Each distinct package would have a corresponding header file. Functions are already automatically prefixed with the package name. We would need to add types from that package to the header file. However, this task can be quite easily automated (at least to some degree). We would also keep the Naming_Convention. The whole structure would be really clear and readable.

Lets consider an example.
Currently in the synth.h there is following type declaration

  struct logic_32 {
    unsigned int va;
    unsigned int zx;
  };

This corresponds to the following type from the Ada types.ads file:

   type Logic_32 is record
      Val : Uns32;  --  AKA aval
      Zx  : Uns32;  --  AKA bval
   end record;

The drawbacks are quite obvious. The names differ, looking into the include file it is not clear where the type is defined within the GHDL repo.

With the new approach there would be types.h header file with following type declaration

  struct types_Logic_32 {
    unsigned int Val;
    unsigned int Zx;
  };

Names are the same, it is clear that Logic_32 is defined within Types package in the Ada sources.

Yet another advantage is, that if we would like to introduce some type that is not a "mirror" of a type defined in Ada it would be simply named with_this_convention. So again, it would be clear which types come directly from the Ada sources.

One approach is to prepare input files containing list of symbols which should be declared in the header file for given package. Then we could write Python script to automatically generated headers.

Another idea is to mark things that should be put into the C header file with some tag in the comment associated with this type.
Something like @include/[include].

@tgingold
Copy link
Member

tgingold commented Mar 10, 2021 via email

@tgingold
Copy link
Member

tgingold commented Mar 10, 2021 via email

@tgingold
Copy link
Member

tgingold commented Mar 10, 2021 via email

@m-kru
Copy link
Contributor Author

m-kru commented Mar 10, 2021

It's a trade-off. Removing the C++ features also means modifying the yosys plugin.

Indeed, however the question is whether you want to keep the includes to be C++ or to make automatically generated C header files to be the facade for the GHDL shared library. In my humble opinion, "more" advanced features for languages such as C++ or Go should be built on top of the raw C headers, not next to them.

@tgingold
Copy link
Member

tgingold commented Mar 11, 2021 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants