Skip to content

QEP: Verilog AMS interface

Guilherme Brondani Torri edited this page Jan 29, 2015 · 10 revisions

QEP: Verilog AMS interface

Author: Guilherme
Status: active, beta in Qucs 0.0.18
Created: 13/02/2014

Proposed workflow

This is proposal about the usage of dynamically compiled Verilog-A devices into Qucs. It reuses some of the features already available since 0.0.16 (see Mike Notes). However the changes presented here greatly improve matters on the side of compiling and loading Verilog-A modules dynamically. There will be no need to recompile Qucs and QucsAtor to add new models. Model symbols and properties for Qucs as well as the compiled modules for QucsAtor will be loaded dynamically during runtime. I will enable users to independently add models to Qucs, either their own creations or Verilog-A models/modules available from elsewhere (provided ADMS is capable of handling them).


TODO

The fist alpha release works fine, but many other issues were identified in the process.

These are a few of the issues that need to be addressed.

  • cleanup the ucs.cpp
  • cleanup the vacomponent on the UI.
  • check that the verilog-a module and file have the same name
  • check that the module name is not already in use on qucsator
  • move from JSON to XML for dynamic loader icon and parameters. Qt4 series has no JSON writer
  • add a netlist component for building the model, similar to include hdl somemodel.va that
    • kick in the model builder
    • instruct the simulator to load the model
  • add a netlist component to load list of model parameters
  • add a UI component or method to load a list of model parameters
  • decouple UI and simulator
  • investigate auto-diff for the derivatives

1 Start

  • The user has a working has created a nice little project in Qucs
  • The user wrote a module in Verilog-A (VA) and wants to include in the simulation.

2 Qucs - load symbols and properties

The following is supported since 0.0.16 (see Mike Notes). Se below for proposal to allow dynamic load

  • The VA is added to the project
    • syntax highlight is active
  • Press F9-Edit Text Symbol, Qucs generate symbol
  • User edit symbol, save
    • save symbol emits C++ code ([module].dat) with Lines, Texts, Ports,…, to be integrated into Qucs

Current approach to include models to Qucs (see Mike notes):

  • the VA is passed thru qucsMODULEgui.xml to emit C++ code
  • the generated code (.gui.h, .gui.cpp)h as to be modified to include the [module].dat symbol, symbol bounding box and device name
  • files are added to the build system
  • new device is added to via REGISTER_VERILOGA_ in qucs/qucs/module.cpp
  • compile Qucs

Proposed approach for dynamic loading of symbols and properties:

  • modify the F9 function

    • Schematic::saveDocument() calls saveSymbolCpp ()

    • SymbolPaints is appended as the symbol is drawn.

    • does it loadind [module].sym appends to SymbolPaints??

    • modify Schematic::saveSymbolCpp in schematic_file.cpp

      • it streams the symbol SymbolPaintings to C++, terminals, bounding box, texts
      • each painting has a method to emit c++ ex. Arrow::saveCpp()
      • TODO emit an easy to parse symbol file with Lines, Texts…
    • let us call the modified output [module]_symbol.dat

  • create a modified qucsMODULEguiLoad.xml to

    • process VA and generate an easy to parse file containing Description, Properties,…
    • let us call it [module]_props.dat
  • add a generic component place holder vadynamic derived from base class Component

Finally, to the actual loading of dynamic modules symbols and properties can take place

  • a function scan the project dir for [module]_symbol.dat, [module]_props.dat

  • modify Module::registerComponent

    • create a new vadynamic object
    • assign content of [module]_symbol.dat, [module]_props.dat to new component
    • register new component into
      • Q3Dict<Module> Module::Modules
      • Q3PtrList<Category> Category::Categories
  • the netlister should be able to seamlessly read the above Dict and PtrList and emit a valid netlist with the correct device signature (name and properties)

  • the new component should show up in the Component Tab for Verilog-A or User defined models

  • user is able to save the schematic, generate netlist

  • for simulation the steps outlined on the next section must be performed.


3 QucsAtor - compile VA module as dynamic library

The dynamic library [module].{so,dylib,dll} has to be compiled before launching QucsAtor

  • The VA is run through admsXml with adequate .xml files
    • va2cpp.makefile generates C++
    • TODO Report error messages, minimal checking
    • TODO raise error messages all the way up to Qucs
  • The C++ is compiled to a dynamic library
    • cpp2lib.makefile compiles dynamic library [module].{so,dylib,dll}
    • TODO Report error messages, minimal checking
    • TODO raise messages up to Qucs

Inspecting the cpp2lib.makefile you notice that the dynamic library links to libqucs and leaves the exportedfactorycreate and factorydef undefined. These symbols will be defined at load time.


4 QucsAtor - dynamic loader

Summary of file changes and new functions that load and release dynamic modules/libraries.

qucs-core/src/circuit.h

The circuit base class defines two external factories. Factories will be populated during the load time of the dynamic libraries containing de derived class objects (VA modules) with function pointers which return constructors and definition.

// typedef to make it easier to set up our factory
typedef qucs::circuit *maker_t();
// function typdefs to make it easier to set up our factories
typedef qucs::circuit *creator_t();
typedef struct define_t *defs_t();

// our global factories defined in module.cpp
extern "C" {
 extern std::map<std::string, creator_t *, std::less<std::string> > factorycreate;
 extern std::map<std::string, defs_t *, std::less<std::string> > factorydef;
}

qucs-core/src/module.{h.cpp}

The global factories (stc::map) to store function pointers from dynamic libraries are defined in module.cpp:

  • factorycreate
  • factorydef

registerDynamicModules (void)

  • very similar to the static registration via registerModule
  • Scans the project directory for dynamic libraries
  • Load any found library
  • Create new modules out of the factory
  • Register dynamic modules into the global hash
  • Once modules are registered into the hash the simulator does not distinct between static and loaded modules.
  • TODO error checking/handling

closeDynamicLibs (void)

  • Closes any previously opened library

qucs-core/src/ucs.cpp

Main QucsAtor source. It makes use of the functions described above:

  • module::closeDynamicLibs()
  • module::registerDynamicModules ()

qucsMODULEcore.xml

The file qucsMODULEcore.xml was modified to emit the loader interface. It just appends the following snippet to the .core.cpp file (properly replacing the module name mypotentiometer in this case). This piece of code is needed to add the dynamic module function pointers into their respective factories during library load.

Note the last argument -A dylib which is parsed and used to emit the dynamic loader code.

amdsXml ... -e qucsMODULEcore.xml -A dylib

// loader constructor interface
extern "C" {
  class va_proxy { public:
    va_proxy(){
      // register the constructor and the definitions with the global factories
      // Note that ::create and ::definition are handled by expanding the CREATOR define constructor
      factorycreate["mypotentiometer"] = mypotentiometer::create;
      factorydef   ["mypotentiometer"] = mypotentiometer::definition;
    }
  };
  // our one instance of the proxy
  va_proxy p;
}

Note on QucsAtor from command line

It is rather difficult to create and use modules directly from the command line. It is very hard to figure out the correct parameters, ports and to connect circuit elements. Further tools could be created to enable introspection into the VA and C++ sources and dynamic libraries. Automatic documentation or a help mechanism could document the component netlist signature enabling users to manually create and edit netlists. Better support should be offered in the future.

Dynamic Loader Reference

The proposed Qucs dynamic loader is inspired after:

Dynamic Class Loading for C++ on Linux, May 01, 2000 By James Norton. http://www.linuxjournal.com/article/3687

The above code recently tested on Linux, Mac OS and Windows can be found at https://github.com/guitorri/draw

Most of the dynamic loader boiler-place code and build system is already in place for Qucs. What remains it so produce the import library to enable the dynamic library compilation with undefined factory symbols (see 5 Caveats).

The loader is capable of loading a dynamic library and instantiate an object out of it. Support makefiles are provided to manage the compilation steps of the source files and the final library.

What remains is the proper registration of loaded classes into the QucsAtor internal machinery so that they can be used for simulation of netlists.


5 Caveats

  • Linux, OSX:

    • Working fine, uses dlopen, dlclose to handle the dynamic libraries
  • Windows

    • Compiles fine, usesLoadLibrary, FreeLibrary to handle the dynamic libraries
    • The library links against libqucs, which has the factories defined.

6 How to test

Code merged on release 0.0.18.

TODO

Tested on:

  • Debian 6.0.7, g++ 4.4.5
  • Debian 7.1.0, g++ 4.7.2, bison 2.5, flex 2.5.35
  • Mac OSX 10.8.5, clang++ LLVM version 4.2 (clang-425.0.28)
  • Windows XP, MinGW 4.7 (loader not working, see 5 Caveat)

Example va_loader_inverter_prj

Loading multiple modules dynamically

Qucsator currently rely on a simple scanner for dynamic modules in work dir.

How it is supposed to work:

  1. It will list (ls) the working directory contents looking for libraries
  2. It will try to open each found library and put its handle into a list
    • Just by opening the lib it will already populate the factorycreate and factorydef
  3. The factory is then iterated to create new modules and to add them to the global hash

TODO:

  • Add destructor for loaded objects
  • Add other methods (besides search on work dir) to find the libraries

Running the example

Go to the test project to:

  • convert Verilog-A to C++
  • compile C++ into shared library
  • run qucsator to demo the loading and creation of the dynamic objects

This example handles 3 VA modules loaded dynamically. The results are compared agains the original statically compiled VA modules

  • see the inverter.pdf for a screenshot
  • the original Qucs potentiometer and bsim4v30{n,p}MOS modules and files where just renamed to differentiate them from the statically models already compiled into Qucsator.
  • run build_run.sh (please adjust PREFIX inside the script), it will:
    • 'elaborate' the VA to C++
    • 'compile' the C++ to dynamic libraries
    • run the original netlist with original Qucs static models
    • run the modified netlist with dynamically loaded models
      • qucsator will load any dynamic lib available in the working dir
    • run diff on the original and modified resulting datasets

Notes

  • qucsMODULEcoreload.xml is a slight modification from qucsMODULEcore.xml. It appends a few lines of code which allow the dynamic loading of the modules. If anyone knows how admst can be used to append to an existing file, we can keep the original core and add another xml for the load part.

The simulation output looks like

Sorry, this is an old output with just one dynamic component, mypotentiometer:

~/git/qucs/va_loader_prj $ sh run_mypotentiometer.sh /Users/guilherme/local/qucs-dylib/bin/
/Users/guilherme/local/qucs-dylib/bin//qucsator -i netlist_mypot.txt -o data_pot.dat
The current working directory is: /Users/guilherme/git/qucs/va_loader_prj
/Users/guilherme/git/qucs/va_loader_prj/mypotentiometer.dylib
Try loading: /Users/guilherme/git/qucs/va_loader_prj/mypotentiometer.dylib
factory.size() is 1
factory contains: mypotentiometer

 ==> quick inspection of definitions...
type: mypotentiometer
nodes: 3
action: 0
required->key: R_pot
parsing netlist...
checking netlist...
subcircuit root
  mypotentiometer:POT1 Vin Vout gnd R_pot="10000" Rotation="120" Taper_Coeff="0" LEVEL="1" Max_Rotation="240" Conformity="0.2" Linearity="0.2" Contact_Res="1" Temp_Coeff="100" Tnom="26.85" Temp="26.85"
  Vac:V1 Vin gnd U="1V" f="100Hz" Phase="0" Theta="0"
  TR:TR1 Type="lin" Start="0" Stop="0.01s" Points="101" IntegrationMethod="Trapezoidal" Order="2" InitialStep="1e-09s" MinStep="1e-16" MaxIter="150" reltol="0.001" abstol="1e-12A" vntol="1e-06V" Temp="26.85" LTEreltol="0.001" LTEabstol="1e-06" LTEfactor="1" Solver="CroutLU" relaxTSR="no" initialDC="yes" MaxStep="0"
netlist content
      1 mypotentiometer instances
      1 Vac instances
      1 TR instances
creating netlist...
input::createCircuit, got: mypotentiometer
input::createCircuit, got: Vac
NOTIFY: TR1: creating node list for initial DC analysis
NOTIFY: TR1: solving initial DC netlist
NOTIFY: TR1: creating node list for transient analysis
NOTIFY: TR1: solving transient netlist
NOTIFY: TR1: average time-step 1.72117e-05, 147 rejections
NOTIFY: TR1: average NR-iterations 2.50602, 0 non-convergences

TODO

  • install XML files to /include/qucs-core
  • install headers to /include/qucs-core
  • install admsXml into the system
  • makefile va2cpp
    • should be generated by qucsator
    • or find a way to pass VA model as variable
  • patch the C++ core.cpp to include the unmangled interface
    • Can it be handled by the XML files during code generation?
  • makefile cpp2lib
    • either generated by qucsator
    • or find a way to pass VA model as variable
  • modify circuit.h to
    • add typedef for factory
    • define extern factory symbol
  • modify ucs.cpp to
  • add support for OSX, Windows

Issues/Changes to compiler flags

  • add libdl (-ldl) to the build (or ../configure LIBS="-ldl" / LDFLAGS='-ldl' make install)
  • add -rdynamic/-Wl,-export-dynamic to enable libs back-linking the 'factory' symbol
  • removed the -fno-rtti
    • the dyn. lib could not be loaded, undefined symbol: _ZTI7circuit

Handy tools

  • otool -L
  • ldd
  • nm
  • readelf -d
  • objdump -x
  • c++filt
Clone this wiki locally