https://github.com/jhallen/joes-sandbox/tree/master/verilog-...
My solution allows you to define registers as module instantiations throughout the design hierarchy. Parameters give the address and bit positions of each register. A tool extracts the registers and generates documentation and a C header file for the chip. The tool has to elaborate the design to determine the parameter values- so it also handles derived addresses by handling math involving parameters. It generates C names for the registers which match the register instantiation path in the design hierarchy (and tries to simplify it by providing just enough of the path to make the names unique). Field names are derived from the names of the wires connected to the register bits.
I've seen other tools which work the other way: the master is a C header file, and the Verilog is derived. I'm not a fan of this, since it means you are have to plumb signals to a central register file module, which creates a big mess.
I like to use an OR-tree for the on-chip register bus- I think it's the optimal design for FPGAs. I have a library which includes simple registers and modules which allow the internal bus to cross clock domains. Look at bus*.v here:
https://github.com/jhallen/joes-sandbox/tree/master/fpga/hdl...
You have to plumb just two identifiers throughout the design for the registers: bus_in and bus_out.
- reduce the number of possible errors as a result of the manual translation and
- make this (somewhat hand-crafted) converter be able to synchronize future changes in Verilog code to the C code that uses it, so five years from now it will be easy to add functionality to the system.
And thanks again for the links - I'll look more into your code.
Again, I don't have Verilog experience before and I might be totally wrong.
Other than that - DRY is always in our agenda, and there is only one place that holds the parameters/ preprocessor macros that define the hardware and its software interface. Python code reads Verilog files and extracts all the parameter names and values (names/types are automatically added to the Python source to allow checking). That Python code allows to run tests on the target system, do some calibration and save results in the same Verilog parameters format (same as here https://github.com/Elphel/x393/blob/master/includes/x393_cur... , just no header). Same program uses Verilog data to export C files, it uses hand-crafted code to allow flexibility - it is not always possible to fit everything in the same format, but here the focus is again on the DRY - new code to do what is different, and use common code that processes multiple data items as much as possible. And when it is not - provide checks to catch differences when you do have to repeat yourself.
Only part of the Python code is a "translation" of the Verilog tests (and this we plan to eliminate in the future), the rest is just a continuation of the project, and when writing (not just running) that code I'm re-using data exported from the Verilog (and re-exported when there are changes that influence data). Same with C - code continues the project, re-using Verilog+Python data.