Qspice State machine logic in C++

Hi,

I am new to Qspice, and i am trying to implement a simple statemachine in C++ (which i want to implement later on a uC). I tried some simple things in C++, and it works well until i want to store states. I found some great examples on the net that i used as a starting point, but unfortunately i couldn’t get my implementation to work.

Thanks for helping me out :slight_smile:

This is the statemachine i try to implement:

This is the code i got so far. The code passes the compiler phase, but nothing is moving on the outputs. Does anyone see what i am doing wrong?


// Automatically generated C++ file on Sun Dec  8 12:42:46 2024
//
// To build with Digital Mars C++ Compiler:
//
//    dmc -mn -WD rectifier.cpp kernel32.lib

#include <cstdio>
#include <cstdlib>
#include <cmath>

union uData
{
   bool b;
   char c;
   unsigned char uc;
   short s;
   unsigned short us;
   int i;
   unsigned int ui;
   float f;
   double d;
   long long int i64;
   unsigned long long int ui64;
   char *str;
   unsigned char *bytes;
};

// int DllMain() must exist and return 1 for a process to load the .DLL
int __stdcall DllMain(void *module, unsigned int reason, void *reserved) { return 1; }

// #undef pin names lest they collide with names in any header file(s) you might include.
#undef AC1
#undef AC2
#undef OUT1
#undef OUT2
#undef EN1
#undef EN2
#undef clk
#undef state_diag

// Structure to store instance-specific data
struct InstData {
   int rectifier_state; // Represents the rectifier state
   bool clk_n1;         // Previous clock state

   InstData() : rectifier_state(0.0), clk_n1(0.0) {}
};

extern "C" __declspec(dllexport) void rectifier(InstData **opaque, double t, union uData *data)
{
   double  AC1  = data[0].d; // input
   double  AC2  = data[1].d; // input
   double &OUT1 = data[2].d; // output
   double &OUT2 = data[3].d; // output
   double &EN1  = data[4].d; // output
   double &EN2  = data[5].d; // output
   bool   clk   = data[6].b; // input
   double &state_diag  = data[7].d; // state diagnostics

   // Allocate and initialize instance data if not already done
   if (!*opaque) {
      *opaque = new InstData;

      if (!*opaque) {
         printf("rectifier_controller error. Unable to allocate memory. Terminating simulation.\n");
         std::exit(1);
      }
   }

   InstData *inst = *opaque;

   double V_trafo = AC2 - AC1;

   if (clk && !inst->clk_n1) // Rising edge
   {
      switch (inst->rectifier_state) {
         case 0:
            EN1 = 0;
            EN2 = 0;
            OUT1 = 0;
            OUT2 = 0;
            if (V_trafo > 2.0)
               {inst->rectifier_state = 1;}
            else if (V_trafo < -2.0)
               {inst->rectifier_state = 3;}
            state_diag = inst->rectifier_state;
            break;

         case 1:
            EN1 = 0;
            EN2 = 1;
            OUT1 = 0;
            OUT2 = 1;
            if (V_trafo > 2.0)
               {inst->rectifier_state = 2;}
            else if (V_trafo < 1.0)
               {inst->rectifier_state = 0;}
            state_diag = inst->rectifier_state;
            break;

         case 2:
            EN1 = 1;
            EN2 = 1;
            OUT1 = 0;
            OUT2 = 1;
            if (V_trafo < 1.0)
               {inst->rectifier_state = 1;}
            state_diag = inst->rectifier_state;
            break;

         case 3:
            EN1 = 0;
            EN2 = 1;
            OUT1 = 0;
            OUT2 = 1;
            if (V_trafo < -2.0)
               {inst->rectifier_state = 4;}
            else if (V_trafo > -1.0)
               {inst->rectifier_state = 0;}
            state_diag = inst->rectifier_state;
            break;

         case 4:
            EN1 = 1;
            EN2 = 1;
            OUT1 = 0;
            OUT2 = 1;
            if (V_trafo > -1.0)
               {inst->rectifier_state = 3;}
            state_diag = inst->rectifier_state;
            break;

         default:
            inst->rectifier_state = 0;
            EN1 = 0;
            EN2 = 0;
            OUT1 = 0;
            OUT2 = 0;
            state_diag = inst->rectifier_state;
      }
   }

   // Save clock state and rectifier state for the next step
   inst->clk_n1 = clk;
}

extern "C" __declspec(dllexport) void Trunc(
    InstData *inst, double t, union uData *data, double *timestep)
{
   double clk = data[6].d; // input clock
   const double ttol = 1e-9;

   // If not rising clock edge, we're done
   if (clk == inst->clk_n1 || !clk) { return; }

   *timestep = ttol;
}

extern "C" __declspec(dllexport) void Destroy(InstData *inst)
{
   // Free instance memory
   delete inst;
}

Hi,

Small update … i managed to fix it by creating a new symbol block in the schematic, creating the new ports and pasting the code in it again. Now it works perfect.

Anyone has a clue what could have been wrong?

Thanks again…

Hi, Joris.

My guess is that you changed the schematic component without regenerating the template code. That is, whenever the schematic component is changed – add/change ports or attributes – and the offsets into the passed *data parameter change. Regenerating the code from the schematic fixed the offsets and data types. Bottom line: Always use the QSpice template generator to create a new template whenever you change the schematic component.

You might want to check out my GitHub QSpice repository for C-Block component tutorials and examples.

–robert

Hi again, Joris. Just a quick tip…

Regenerating the C++ code will, of course, overwrite the existing *.cpp that contains your carefully crafted code. So, temporarily change the name of the component (1st String Attribute in Symbol Properties). Generate the code. Change the component name back.

Finally, open the temporary *.cpp and copy the data array stuff in the evaluation function from the temporary to the original *.cpp.

Note that there’s a project, QCodeGen, in my repository that generates the QSpice code without overwriting the existing *.cpp (in addition to other features). However, I think that Mike has updated the code generator and I probably need to update that project.

–robert