From 10d72977983c3e3c9d95c70bb4eb162fc40cb015 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 21 Oct 2023 15:52:10 +0200 Subject: [PATCH 01/25] Add cm_cecit(). If the input file could not be opened, stop ngspice. --- src/xspice/icm/analog/file_source/cfunc.mod | 1 + src/xspice/icm/table/table2D/cfunc.mod | 1 + src/xspice/icm/table/table3D/cfunc.mod | 1 + 3 files changed, 3 insertions(+) diff --git a/src/xspice/icm/analog/file_source/cfunc.mod b/src/xspice/icm/analog/file_source/cfunc.mod index f140831a5..4e5765b8d 100644 --- a/src/xspice/icm/analog/file_source/cfunc.mod +++ b/src/xspice/icm/analog/file_source/cfunc.mod @@ -233,6 +233,7 @@ void cm_filesource(ARGS) /* structure holding parms, inputs, outputs, etc. if (!loc->state->fp) { cm_message_printf("cannot open file %s", PARAM(file)); loc->state->atend = 1; + cm_cexit(1); } } /* read, preprocess and store the data */ diff --git a/src/xspice/icm/table/table2D/cfunc.mod b/src/xspice/icm/table/table2D/cfunc.mod index a5375c3e7..ff43938b4 100644 --- a/src/xspice/icm/table/table2D/cfunc.mod +++ b/src/xspice/icm/table/table2D/cfunc.mod @@ -732,6 +732,7 @@ EXITPOINT: free_local_data(loc); loc = (Table2_Data_t *) NULL; } + cm_cexit(1); } return loc; } /* end of function init_local_data */ diff --git a/src/xspice/icm/table/table3D/cfunc.mod b/src/xspice/icm/table/table3D/cfunc.mod index 2792cfdcc..606dc1dc6 100644 --- a/src/xspice/icm/table/table3D/cfunc.mod +++ b/src/xspice/icm/table/table3D/cfunc.mod @@ -802,6 +802,7 @@ EXITPOINT: free_local_data(loc); loc = (Table3_Data_t *) NULL; } + cm_cexit(1); } return loc; } /* end of function init_local_data */ From ce82c839e3d46f46049e193b047f6ed47e6cd233 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Mon, 23 Oct 2023 16:50:49 +0200 Subject: [PATCH 02/25] dependent project klu_complex added --- visualc/sharedspice.sln | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/visualc/sharedspice.sln b/visualc/sharedspice.sln index 9006b79bf..b63b8234c 100644 --- a/visualc/sharedspice.sln +++ b/visualc/sharedspice.sln @@ -1,9 +1,14 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29709.97 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34202.233 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sharedspice", "sharedspice.vcxproj", "{83E315C7-EDD3-4F6B-AF28-87A92A4FA49A}" + ProjectSection(ProjectDependencies) = postProject + {B8DD41C5-70C6-4183-9018-C4B518E8D36A} = {B8DD41C5-70C6-4183-9018-C4B518E8D36A} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KLU_COMPLEX", "KLU\KLU_COMPLEX.vcxproj", "{B8DD41C5-70C6-4183-9018-C4B518E8D36A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -27,6 +32,18 @@ Global {83E315C7-EDD3-4F6B-AF28-87A92A4FA49A}.ReleaseOMP|x64.Build.0 = ReleaseOMP|x64 {83E315C7-EDD3-4F6B-AF28-87A92A4FA49A}.ReleaseOMP|x86.ActiveCfg = ReleaseOMP|Win32 {83E315C7-EDD3-4F6B-AF28-87A92A4FA49A}.ReleaseOMP|x86.Build.0 = ReleaseOMP|Win32 + {B8DD41C5-70C6-4183-9018-C4B518E8D36A}.Debug|x64.ActiveCfg = Debug|x64 + {B8DD41C5-70C6-4183-9018-C4B518E8D36A}.Debug|x64.Build.0 = Debug|x64 + {B8DD41C5-70C6-4183-9018-C4B518E8D36A}.Debug|x86.ActiveCfg = Debug|Win32 + {B8DD41C5-70C6-4183-9018-C4B518E8D36A}.Debug|x86.Build.0 = Debug|Win32 + {B8DD41C5-70C6-4183-9018-C4B518E8D36A}.Release|x64.ActiveCfg = Release|x64 + {B8DD41C5-70C6-4183-9018-C4B518E8D36A}.Release|x64.Build.0 = Release|x64 + {B8DD41C5-70C6-4183-9018-C4B518E8D36A}.Release|x86.ActiveCfg = Release|Win32 + {B8DD41C5-70C6-4183-9018-C4B518E8D36A}.Release|x86.Build.0 = Release|Win32 + {B8DD41C5-70C6-4183-9018-C4B518E8D36A}.ReleaseOMP|x64.ActiveCfg = Release|x64 + {B8DD41C5-70C6-4183-9018-C4B518E8D36A}.ReleaseOMP|x64.Build.0 = Release|x64 + {B8DD41C5-70C6-4183-9018-C4B518E8D36A}.ReleaseOMP|x86.ActiveCfg = Release|Win32 + {B8DD41C5-70C6-4183-9018-C4B518E8D36A}.ReleaseOMP|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From efdf332061448824567b1530d822ac412e195227 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 27 Oct 2023 23:13:41 +0200 Subject: [PATCH 03/25] Prevent a crash if hash model table was not generated, e.g. due to missing valid models. --- src/spicelib/parser/inpgmod.c | 42 ++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/spicelib/parser/inpgmod.c b/src/spicelib/parser/inpgmod.c index 44dc93989..9d902dc81 100644 --- a/src/spicelib/parser/inpgmod.c +++ b/src/spicelib/parser/inpgmod.c @@ -343,31 +343,33 @@ INPgetMod(CKTcircuit *ckt, char *name, INPmodel **model, INPtables *tab) printf("In INPgetMod, examining model %s ...\n", name); #endif - modtmp = nghash_find(modtabhash, name); - if (modtmp) { - /* found the model in question - now instantiate if necessary */ - /* and return an appropriate pointer to it */ + if (modtabhash) { + modtmp = nghash_find(modtabhash, name); + if (modtmp) { + /* found the model in question - now instantiate if necessary */ + /* and return an appropriate pointer to it */ -/* if illegal device type */ - if (modtmp->INPmodType < 0) { + /* if illegal device type */ + if (modtmp->INPmodType < 0) { #ifdef TRACE - printf("In INPgetMod, illegal device type for model %s ...\n", name); + printf("In INPgetMod, illegal device type for model %s ...\n", name); #endif - * model = NULL; - return tprintf("Unknown device type for model %s\n", name); - } - - /* create unless model is already defined */ - if (!modtmp->INPmodfast) { - int error = create_model(ckt, modtmp, tab); - if (error) { - *model = NULL; - return INPerror(error); + * model = NULL; + return tprintf("Unknown device type for model %s\n", name); } - } - *model = modtmp; - return NULL; + /* create unless model is already defined */ + if (!modtmp->INPmodfast) { + int error = create_model(ckt, modtmp, tab); + if (error) { + *model = NULL; + return INPerror(error); + } + } + + *model = modtmp; + return NULL; + } } #if (0) for (modtmp = modtab; modtmp; modtmp = modtmp->INPnextModel) { From 046be0cdc8074b174282d5d751840b84e2a31551 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 27 Oct 2023 23:25:42 +0200 Subject: [PATCH 04/25] Use the PSP103 model pspnqs103va (including nqs option) as this is the standard in the IHP Open PDK. --- examples/osdi/psp103/Modelcards/psp103_nmos-2.mod | 2 +- examples/osdi/psp103/Modelcards/psp103_pmos-2.mod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/osdi/psp103/Modelcards/psp103_nmos-2.mod b/examples/osdi/psp103/Modelcards/psp103_nmos-2.mod index 9ce1e38e3..6f5ec9b1b 100644 --- a/examples/osdi/psp103/Modelcards/psp103_nmos-2.mod +++ b/examples/osdi/psp103/Modelcards/psp103_nmos-2.mod @@ -1,7 +1,7 @@ * psp_VA_and_CMC_ref_data 103.3.0 asym_nmos_t * LEVEL=103.0 * https://www.cea.fr/cea-tech/leti/pspsupport/Documents/Level%20103.3.3/psp_VA_and_CMC_ref_data.tar.gz -.model nch psp103va level=69 +.model nch pspnqs103va level=69 +type=1 +TR=27.0 +DTA=0 diff --git a/examples/osdi/psp103/Modelcards/psp103_pmos-2.mod b/examples/osdi/psp103/Modelcards/psp103_pmos-2.mod index b7604d3e2..0ed02bd04 100644 --- a/examples/osdi/psp103/Modelcards/psp103_pmos-2.mod +++ b/examples/osdi/psp103/Modelcards/psp103_pmos-2.mod @@ -1,7 +1,7 @@ * psp_VA_and_CMC_ref_data 103.3.0 asym_pmos_t * LEVEL=103.0 * https://www.cea.fr/cea-tech/leti/pspsupport/Documents/Level%20103.3.3/psp_VA_and_CMC_ref_data.tar.gz -.model pch psp103va level=69 +.model pch pspnqs103va level=69 +TYPE=-1 +TR=27.0 +DTA=0 From 3bb6e8601522e21d6bfdf42a798d664981d3d227 Mon Sep 17 00:00:00 2001 From: Brian Taylor Date: Thu, 28 Sep 2023 16:31:02 -0700 Subject: [PATCH 05/25] Add Windows Visual Studio support for the Isotel d_process xspice digital model. The Isotel mixedsim-master/examples/embedded/motorforce C code needs to have minor changes for _MSC_VER defines, and are not included with this commit. The changes relate to using #pragma pack(push, 1)...#pragma pack(pop) around some structs, and setting stdin/stdout in binary mode. It is doubtful that the cfunc.mod will compile on mingw or cygwin since I don't know if they support pipe/fork/exec/spawn calls. The code builds and runs on Windows, Linux, and MacOS. In the future, Giles Atkinson is looking at allowing Xspice models to load shared libraries (.dll, .so) which will avoid creating and communicating with an external program. --- src/xspice/icm/digital/d_process/cfunc.mod | 436 ++++++++++++++++++++ src/xspice/icm/digital/d_process/ifspec.ifs | 147 +++++++ src/xspice/icm/digital/modpath.lst | 1 + visualc/xspice/digital.vcxproj | 8 +- 4 files changed, 591 insertions(+), 1 deletion(-) create mode 100644 src/xspice/icm/digital/d_process/cfunc.mod create mode 100644 src/xspice/icm/digital/d_process/ifspec.ifs diff --git a/src/xspice/icm/digital/d_process/cfunc.mod b/src/xspice/icm/digital/d_process/cfunc.mod new file mode 100644 index 000000000..6e0174346 --- /dev/null +++ b/src/xspice/icm/digital/d_process/cfunc.mod @@ -0,0 +1,436 @@ +/*.......1.........2.........3.........4.........5.........6.........7.........8 +================================================================================ + +FILE d_process/cfunc.mod + +Copyright 2017-2018 Isotel d.o.o. http://www.isotel.eu +PROJECT http://isotel.eu/mixedsim + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +AUTHORS + + 2017-2018 Uros Platse + + +SUMMARY + + This module provides an interface to an external process via + standard unix stdin/stdout pipe to extend ngspice functionality + to the level of embedded systems. + + If a process ends with | character then rather than invoking + a process it opens named pipe, process_in which is input to the + process and pipe process_out for reading back from process. + + Communication between this code model and a process is in 8-bit + binary format. On start-up + + 0x01: version + 0x00-0xFF: number of inputs, max 255, 0 means none + 0x00-0xFF: number of outputs, max 255, 0 means none + + On start: + + outputs are set to uknown state and high impedance + + On each rising edge of a clock and reset being low + + double (8-byte): positive value of TIME if reset is low otherwise -TIME + [inputs array ]: input bytes, each byte packs up to 8 inputs + ooutputs are defined by returning process + + and process must return: + + [output array]: output bytes, each byte packs up to 8 outputs + + For example project please see: http://isotel.eu/mixedsim + + +MODIFICATIONS + + 9 November 2017 Uros Platse + - Initial design, ready for use with projects + + 4 April 2018 Uros Platse + - Tested and polished ready to be published + + 7 April 2018 Uros Platse + Removed async reset and converted it to synchronous reset only. + Code cleanup. + + +REFERENCED FILES + + Inputs from and outputs to ARGS structure. + +===============================================================================*/ + +#include +#include +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif +#include + +#define D_PROCESS_FORMAT_VERSION 0x01 +#define DLEN(x) (uint8_t)( ((x)==0) ? 0 : (((x)-1)/8 + 1) ) +#define DIN_SIZE_MAX 256 // also represents a theoretical maximum for 8-bit input length specifier + +typedef unsigned char uint8_t; + +typedef struct { + int pipe_to_child; + int pipe_from_child; + uint8_t N_din, N_dout; // number of inputs/outputs bytes + Digital_State_t dout_old[256]; // max possible storage to track output changes +} Process_t; + +#if defined(_MSC_VER) +#include +static void w_start(char *system_command, char * c_argv[], Process_t * process); +#endif + +static void sendheader(Process_t * process, int N_din, int N_dout) +{ +#if defined(_MSC_VER) +#pragma pack(push, 1) + struct header_s { + uint8_t version, N_din, N_dout; + } header = {D_PROCESS_FORMAT_VERSION, (uint8_t)N_din, (uint8_t)N_dout}; +#pragma pack(pop) +#else + struct header_s { + uint8_t version, N_din, N_dout; + } __attribute__((packed)) header = {D_PROCESS_FORMAT_VERSION, (uint8_t)N_din, (uint8_t)N_dout}; +#endif + + if (N_din > 255 || N_dout > 255) { + fprintf(stderr, "Error: d_process supports max 255 input and output and 255 output signals\n"); + exit(1); + } + +#if defined(_MSC_VER) + if (_write(process->pipe_to_child, &header, sizeof(header)) == -1) { +#else + if (write(process->pipe_to_child, &header, sizeof(header)) == -1) { +#endif + fprintf(stderr, "Error: d_process when sending header\n"); + exit(1); + } + + // Wait for echo which must return the same header to ack transfer +#if defined(_MSC_VER) + if (_read(process->pipe_from_child, &header, sizeof(header)) != sizeof(header)) { +#else + if (read(process->pipe_from_child, &header, sizeof(header)) != sizeof(header)) { +#endif + fprintf(stderr, "Error: d_process didn't respond to the header\n"); + exit(1); + } + if (header.version != D_PROCESS_FORMAT_VERSION) { + fprintf(stderr, "Error: d_process returned invalid version: %d\n", header.version); + exit(1); + } + if (header.N_din != N_din || header.N_dout != N_dout) { + fprintf(stderr, "Error: d_process I/O mismatch: in %d vs. returned %d, out %d vs. returned %d\n", + N_din, header.N_din, N_dout, header.N_dout); + exit(1); + } + + process->N_din = (uint8_t)DLEN(N_din); + process->N_dout = (uint8_t)DLEN(N_dout); +} + + +static void dprocess_exchangedata(Process_t * process, double time, uint8_t din[], uint8_t dout[]) +{ +#if defined(_MSC_VER) +#pragma pack(push, 1) + typedef struct { + double time; + uint8_t din[DIN_SIZE_MAX]; + } packet_t; +#pragma pack(pop) +#else + typedef struct { + double time; + uint8_t din[DIN_SIZE_MAX]; + } __attribute__((packed)) packet_t; +#endif + + size_t dlen = 0; + int wlen = 0; + packet_t packet; + packet.time = time; + memcpy(packet.din, din, process->N_din); + +#if defined(_MSC_VER) + wlen = _write(process->pipe_to_child, &packet, sizeof(double) + process->N_din); +#else + wlen = write(process->pipe_to_child, &packet, sizeof(double) + process->N_din); +#endif + if (wlen == -1) { + fprintf(stderr, "Error: d_process when writing exchange data\n"); + exit(1); + } + +#if defined(_MSC_VER) + dlen = _read(process->pipe_from_child, dout, process->N_dout); +#else + dlen = read(process->pipe_from_child, dout, process->N_dout); +#endif + if (dlen != (size_t)process->N_dout) { + fprintf(stderr, + "Error: d_process received invalid dout count, read %lu expected %d\n", + dlen, process->N_dout); + exit(1); + } +} + + +#if !defined(_MSC_VER) +static void start(char *system_command, char * c_argv[], Process_t * process) +{ + int pipe_to_child[2]; + int pipe_from_child[2]; + int pid; + size_t syscmd_len = strlen(system_command); + + if (syscmd_len == 0) { + fprintf(stderr, "Error: d_process process_file argument is not given"); + exit(1); + } + if (system_command[syscmd_len-1] == '|') { + char filename[syscmd_len+5]; + strncpy(filename, system_command, syscmd_len-1); + strcpy(&filename[syscmd_len-1], "_in"); + if ( (process->pipe_to_child = open(filename, O_WRONLY)) < 0) { + perror(filename); + exit(1); + } + strncpy(filename, system_command, syscmd_len-1); + strcpy(&filename[syscmd_len-1], "_out"); + if ( (process->pipe_from_child = open(filename, O_RDONLY)) < 0) { + perror(filename); + exit(1); + } + } + else { + if (pipe(pipe_to_child) || pipe(pipe_from_child) || (pid=fork()) ==-1) { + perror("Error: d_process cannot create pipes and fork"); + exit(1); + } + if (pid == 0) { + dup2(pipe_to_child[0],0); + dup2(pipe_from_child[1],1); + close(pipe_to_child[1]); + close(pipe_from_child[0]); + + if (execv(system_command, c_argv) == -1) { + perror(system_command); + exit(1); + } + } + else { + process->pipe_to_child = pipe_to_child[1]; + process->pipe_from_child = pipe_from_child[0]; + close(pipe_to_child[0]); + close(pipe_from_child[1]); + } + } +} +#endif + + +void cm_d_process(ARGS) +{ + int i; /* generic loop counter index */ + + Digital_State_t *reset, /* storage for clock value */ + *reset_old; /* previous clock value */ + + Digital_State_t *clk, /* storage for clock value */ + *clk_old; /* previous clock value */ + + Process_t *local_process; /* Structure containing process vars */ + + + if (INIT) { + char * c_argv[1024]; + int c_argc = 1; + cm_event_alloc(0,sizeof(Digital_State_t)); + cm_event_alloc(1,sizeof(Digital_State_t)); + + clk = clk_old = (Digital_State_t *) cm_event_get_ptr(0,0); + reset = reset_old = (Digital_State_t *) cm_event_get_ptr(1,0); + + STATIC_VAR(process) = malloc(sizeof(Process_t)); + local_process = STATIC_VAR(process); + + if (!PARAM_NULL(process_params)) { + for (i=0; idout_old[i] = UNKNOWN; + OUTPUT_STATE(out[i]) = UNKNOWN; + OUTPUT_STRENGTH(out[i]) = HI_IMPEDANCE; + } + } + else { /****** Transient Analysis ******/ + *clk = INPUT_STATE(clk); + + if ( PORT_NULL(reset) ) { + *reset = *reset_old = ZERO; + } + else { + *reset = INPUT_STATE(reset); + } + + if (*clk != *clk_old && ONE == *clk) { + uint8_t *dout, *din; + uint8_t b; + dout = malloc(local_process->N_dout * sizeof(uint8_t)); + din = malloc(local_process->N_din * sizeof(uint8_t)); + memset(din, 0, local_process->N_din); + + for (i=0; i> 3] |= (uint8_t)(b << (i & 7)); + } + + dprocess_exchangedata(local_process, (ONE != *reset) ? TIME : -TIME, din, dout); + + for (i=0; i> 3] >> (i & 7)) & 0x01) ? ONE : ZERO; + + if (new_state != local_process->dout_old[i]) { + OUTPUT_STATE(out[i]) = new_state; + OUTPUT_STRENGTH(out[i]) = STRONG; + OUTPUT_DELAY(out[i]) = PARAM(clk_delay); + local_process->dout_old[i] = new_state; + } + else { + OUTPUT_CHANGED(out[i]) = FALSE; + } + } + free(din); + free(dout); + } + else { + for (i=0; i +#include + +static void w_start(char *system_command, char * c_argv[], Process_t * process) +{ + int pipe_to_child[2]; + int pipe_from_child[2]; + int pid = 0; +#define USE_BINARY_MODE +#ifdef USE_BINARY_MODE + int mode = _O_BINARY; +#else + int mode = _O_TEXT; +#endif + size_t syscmd_len = strlen(system_command); + + if (syscmd_len == 0) { + fprintf(stderr, "Error: d_process process_file argument is not given"); + exit(1); + } + + if (_pipe(pipe_to_child, 1024, mode) == -1) { + perror("pipe_to_child"); + fprintf(stderr, "Failed to open pipe_to_child\n"); + exit(1); + } + if (_pipe(pipe_from_child, 1024, mode) == -1) { + perror("pipe_from_child"); + fprintf(stderr, "Failed to open pipe_from_child\n"); + exit(1); + } + + _dup2(pipe_to_child[0],0); + _dup2(pipe_from_child[1],1); + _close(pipe_to_child[0]); + _close(pipe_from_child[1]); + + _flushall(); + pid = _spawnvp(_P_NOWAIT, system_command, c_argv); + if (pid == -1) { + perror("spawn from d_process"); + fprintf(stderr, "Failed to spawn %s\n", system_command); + exit(1); + } + + process->pipe_to_child = pipe_to_child[1]; + process->pipe_from_child = pipe_from_child[0]; +} +#endif diff --git a/src/xspice/icm/digital/d_process/ifspec.ifs b/src/xspice/icm/digital/d_process/ifspec.ifs new file mode 100644 index 000000000..d002b97b6 --- /dev/null +++ b/src/xspice/icm/digital/d_process/ifspec.ifs @@ -0,0 +1,147 @@ +/*.......1.........2.........3.........4.........5.........6.........7.........8 +================================================================================ +Copyright 2017-2018 Isotel d.o.o. http://www.isotel.eu + +AUTHORS + + 5 November 2017 Uros Platse + + + +SUMMARY + + This file contains the interface specification file for the + digital d_process (extends to a system process) code model. + +===============================================================================*/ + + +NAME_TABLE: + + +C_Function_Name: cm_d_process +Spice_Model_Name: d_process +Description: "digital process" + + +PORT_TABLE: + + +Port_Name: in clk +Description: "input" "clock" +Direction: in in +Default_Type: d d +Allowed_Types: [d] [d] +Vector: yes no +Vector_Bounds: - - +Null_Allowed: yes no + + +PORT_TABLE: + + +Port_Name: reset out +Description: "reset" "output" +Direction: in out +Default_Type: d d +Allowed_Types: [d] [d] +Vector: no yes +Vector_Bounds: - [1 -] +Null_Allowed: yes yes + + + +PARAMETER_TABLE: + + +Parameter_Name: clk_delay reset_delay +Description: "delay from CLK" "delay from reset" +Data_Type: real real +Default_Value: 1.0e-9 1.0e-9 +Limits: - - +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes + + +PARAMETER_TABLE: + + +Parameter_Name: process_file +Description: "file name of the executable process" +Data_Type: string +Default_Value: "process" +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: no + + +PARAMETER_TABLE: + + +Parameter_Name: process_params +Description: "parameters to be passed to an executable process" +Data_Type: string +Default_Value: - +Limits: - +Vector: yes +Vector_Bounds: - +Null_Allowed: yes + + +PARAMETER_TABLE: + + +Parameter_Name: reset_state +Description: "default state on RESET & at DC" +Data_Type: int +Default_Value: 0 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: no + + +PARAMETER_TABLE: + + +Parameter_Name: input_load +Description: "input loading capacitance (F)" +Data_Type: real +Default_Value: 1.0e-12 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: no + + +PARAMETER_TABLE: + + +Parameter_Name: clk_load +Description: "clock loading capacitance (F)" +Data_Type: real +Default_Value: 1.0e-12 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: no +PARAMETER_TABLE: + + +Parameter_Name: reset_load +Description: "reset loading capacitance (F)" +Data_Type: real +Default_Value: 1.0e-12 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: no + + +STATIC_VAR_TABLE: + +Static_Var_Name: process +Description: "local data about process" +Data_Type: pointer diff --git a/src/xspice/icm/digital/modpath.lst b/src/xspice/icm/digital/modpath.lst index 3b5ee09f5..323c94c19 100644 --- a/src/xspice/icm/digital/modpath.lst +++ b/src/xspice/icm/digital/modpath.lst @@ -16,6 +16,7 @@ d_open_c d_open_e d_or d_osc +d_process d_pulldown d_pullup d_pwm diff --git a/visualc/xspice/digital.vcxproj b/visualc/xspice/digital.vcxproj index 18907b26d..47e7cb7d7 100644 --- a/visualc/xspice/digital.vcxproj +++ b/visualc/xspice/digital.vcxproj @@ -270,6 +270,10 @@ ..\..\src\xspice\%(RelativeDir);%(AdditionalIncludeDirectories) + + ..\..\src\xspice\%(RelativeDir);%(AdditionalIncludeDirectories) + + ..\..\src\xspice\%(RelativeDir);%(AdditionalIncludeDirectories) @@ -357,6 +361,8 @@ + + @@ -388,4 +394,4 @@ - \ No newline at end of file + From 5c4352d02fccca1ac59d1e6d1ca18086776b60e7 Mon Sep 17 00:00:00 2001 From: Brian Taylor Date: Fri, 29 Sep 2023 18:14:11 -0700 Subject: [PATCH 06/25] Add __MINGW64__ defines for builds on mingw with msys. --- src/xspice/icm/digital/d_process/cfunc.mod | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/xspice/icm/digital/d_process/cfunc.mod b/src/xspice/icm/digital/d_process/cfunc.mod index 6e0174346..1cf0a2f83 100644 --- a/src/xspice/icm/digital/d_process/cfunc.mod +++ b/src/xspice/icm/digital/d_process/cfunc.mod @@ -102,7 +102,7 @@ typedef struct { Digital_State_t dout_old[256]; // max possible storage to track output changes } Process_t; -#if defined(_MSC_VER) +#if defined(_MSC_VER) || defined(__MINGW64__) #include static void w_start(char *system_command, char * c_argv[], Process_t * process); #endif @@ -126,7 +126,7 @@ static void sendheader(Process_t * process, int N_din, int N_dout) exit(1); } -#if defined(_MSC_VER) +#if defined(_MSC_VER) || defined(__MINGW64__) if (_write(process->pipe_to_child, &header, sizeof(header)) == -1) { #else if (write(process->pipe_to_child, &header, sizeof(header)) == -1) { @@ -136,7 +136,7 @@ static void sendheader(Process_t * process, int N_din, int N_dout) } // Wait for echo which must return the same header to ack transfer -#if defined(_MSC_VER) +#if defined(_MSC_VER) || defined(__MINGW64__) if (_read(process->pipe_from_child, &header, sizeof(header)) != sizeof(header)) { #else if (read(process->pipe_from_child, &header, sizeof(header)) != sizeof(header)) { @@ -181,7 +181,7 @@ static void dprocess_exchangedata(Process_t * process, double time, uint8_t din[ packet.time = time; memcpy(packet.din, din, process->N_din); -#if defined(_MSC_VER) +#if defined(_MSC_VER) || defined(__MINGW64__) wlen = _write(process->pipe_to_child, &packet, sizeof(double) + process->N_din); #else wlen = write(process->pipe_to_child, &packet, sizeof(double) + process->N_din); @@ -191,7 +191,7 @@ static void dprocess_exchangedata(Process_t * process, double time, uint8_t din[ exit(1); } -#if defined(_MSC_VER) +#if defined(_MSC_VER) || defined(__MINGW64__) dlen = _read(process->pipe_from_child, dout, process->N_dout); #else dlen = read(process->pipe_from_child, dout, process->N_dout); @@ -205,7 +205,7 @@ static void dprocess_exchangedata(Process_t * process, double time, uint8_t din[ } -#if !defined(_MSC_VER) +#if !defined(_MSC_VER) && !defined(__MINGW64__) static void start(char *system_command, char * c_argv[], Process_t * process) { int pipe_to_child[2]; @@ -292,7 +292,7 @@ void cm_d_process(ARGS) c_argv[0] = PARAM(process_file); c_argv[c_argc] = NULL; -#if defined(_MSC_VER) +#if defined(_MSC_VER) || defined(__MINGW64__) w_start(c_argv[0], c_argv, local_process); #else start(c_argv[0], c_argv, local_process); @@ -348,7 +348,7 @@ void cm_d_process(ARGS) switch(INPUT_STATE(in[i])) { case ZERO: b = 0; break; case ONE: b = 1; break; -#if defined(_MSC_VER) +#if defined(_MSC_VER) || defined(__MINGW64__) default: b = rand() & 1; break; #else default: b = random() & 1; break; @@ -383,7 +383,7 @@ void cm_d_process(ARGS) } } -#if defined(_MSC_VER) +#if defined(_MSC_VER) || defined(__MINGW64__) #include #include From 33780055ee6e7940f19c7e6763905b18cd5e9bf0 Mon Sep 17 00:00:00 2001 From: Brian Taylor Date: Wed, 4 Oct 2023 14:46:42 -0700 Subject: [PATCH 07/25] Add MODIFICATIONS note, cleanup white space. --- src/xspice/icm/digital/d_process/cfunc.mod | 128 +++++++++++--------- src/xspice/icm/digital/d_process/ifspec.ifs | 104 ++++++++-------- 2 files changed, 123 insertions(+), 109 deletions(-) diff --git a/src/xspice/icm/digital/d_process/cfunc.mod b/src/xspice/icm/digital/d_process/cfunc.mod index 1cf0a2f83..1d6f0374b 100644 --- a/src/xspice/icm/digital/d_process/cfunc.mod +++ b/src/xspice/icm/digital/d_process/cfunc.mod @@ -21,10 +21,10 @@ PROJECT http://isotel.eu/mixedsim Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -AUTHORS +AUTHORS 2017-2018 Uros Platse - + SUMMARY @@ -32,13 +32,13 @@ SUMMARY standard unix stdin/stdout pipe to extend ngspice functionality to the level of embedded systems. - If a process ends with | character then rather than invoking + If a process ends with | character then rather than invoking a process it opens named pipe, process_in which is input to the process and pipe process_out for reading back from process. - + Communication between this code model and a process is in 8-bit binary format. On start-up - + 0x01: version 0x00-0xFF: number of inputs, max 255, 0 means none 0x00-0xFF: number of outputs, max 255, 0 means none @@ -46,7 +46,7 @@ SUMMARY On start: outputs are set to uknown state and high impedance - + On each rising edge of a clock and reset being low double (8-byte): positive value of TIME if reset is low otherwise -TIME @@ -54,7 +54,7 @@ SUMMARY ooutputs are defined by returning process and process must return: - + [output array]: output bytes, each byte packs up to 8 outputs For example project please see: http://isotel.eu/mixedsim @@ -69,13 +69,21 @@ MODIFICATIONS - Tested and polished ready to be published 7 April 2018 Uros Platse - Removed async reset and converted it to synchronous reset only. + Removed async reset and converted it to synchronous reset only. Code cleanup. + 30 September 2023 Brian Taylor + Modify the code for Windows VisualC and Mingw. + In VisualC, #pragma pack(...) compiler directives are needed for + the struct declarations using __attribute__(packed). + Use Microsoft CRT library functions _pipe, _read, _write, _spawn. + For Windows VisualC and Mingw the pipes use binary mode, and + named pipes or fifos are not supported. + REFERENCED FILES - Inputs from and outputs to ARGS structure. + Inputs from and outputs to ARGS structure. ===============================================================================*/ @@ -86,9 +94,11 @@ REFERENCED FILES #include #if !defined(_MSC_VER) #include +#include +#include #endif #include - + #define D_PROCESS_FORMAT_VERSION 0x01 #define DLEN(x) (uint8_t)( ((x)==0) ? 0 : (((x)-1)/8 + 1) ) #define DIN_SIZE_MAX 256 // also represents a theoretical maximum for 8-bit input length specifier @@ -98,7 +108,7 @@ typedef unsigned char uint8_t; typedef struct { int pipe_to_child; int pipe_from_child; - uint8_t N_din, N_dout; // number of inputs/outputs bytes + uint8_t N_din, N_dout; // number of inputs/outputs bytes Digital_State_t dout_old[256]; // max possible storage to track output changes } Process_t; @@ -120,10 +130,10 @@ static void sendheader(Process_t * process, int N_din, int N_dout) uint8_t version, N_din, N_dout; } __attribute__((packed)) header = {D_PROCESS_FORMAT_VERSION, (uint8_t)N_din, (uint8_t)N_dout}; #endif - + if (N_din > 255 || N_dout > 255) { fprintf(stderr, "Error: d_process supports max 255 input and output and 255 output signals\n"); - exit(1); + exit(1); } #if defined(_MSC_VER) || defined(__MINGW64__) @@ -134,7 +144,7 @@ static void sendheader(Process_t * process, int N_din, int N_dout) fprintf(stderr, "Error: d_process when sending header\n"); exit(1); } - + // Wait for echo which must return the same header to ack transfer #if defined(_MSC_VER) || defined(__MINGW64__) if (_read(process->pipe_from_child, &header, sizeof(header)) != sizeof(header)) { @@ -143,17 +153,17 @@ static void sendheader(Process_t * process, int N_din, int N_dout) #endif fprintf(stderr, "Error: d_process didn't respond to the header\n"); exit(1); - } + } if (header.version != D_PROCESS_FORMAT_VERSION) { fprintf(stderr, "Error: d_process returned invalid version: %d\n", header.version); - exit(1); - } - if (header.N_din != N_din || header.N_dout != N_dout) { - fprintf(stderr, "Error: d_process I/O mismatch: in %d vs. returned %d, out %d vs. returned %d\n", - N_din, header.N_din, N_dout, header.N_dout); - exit(1); + exit(1); } - + if (header.N_din != N_din || header.N_dout != N_dout) { + fprintf(stderr, "Error: d_process I/O mismatch: in %d vs. returned %d, out %d vs. returned %d\n", + N_din, header.N_din, N_dout, header.N_dout); + exit(1); + } + process->N_din = (uint8_t)DLEN(N_din); process->N_dout = (uint8_t)DLEN(N_dout); } @@ -174,7 +184,7 @@ static void dprocess_exchangedata(Process_t * process, double time, uint8_t din[ uint8_t din[DIN_SIZE_MAX]; } __attribute__((packed)) packet_t; #endif - + size_t dlen = 0; int wlen = 0; packet_t packet; @@ -218,31 +228,37 @@ static void start(char *system_command, char * c_argv[], Process_t * process) exit(1); } if (system_command[syscmd_len-1] == '|') { - char filename[syscmd_len+5]; - strncpy(filename, system_command, syscmd_len-1); - strcpy(&filename[syscmd_len-1], "_in"); - if ( (process->pipe_to_child = open(filename, O_WRONLY)) < 0) { - perror(filename); + char *filename_in = NULL, *filename_out = NULL; + filename_in = (char *) malloc(syscmd_len + 5); + filename_out = (char *) malloc(syscmd_len + 5); + filename_in[0] = '\0'; + filename_out[0] = '\0'; + strncpy(filename_in, system_command, syscmd_len-1); + strcpy(&filename_in[syscmd_len-1], "_in"); + strncpy(filename_out, system_command, syscmd_len-1); + strcpy(&filename_out[syscmd_len-1], "_out"); + if ((process->pipe_to_child = open(filename_in, O_WRONLY)) == -1) { + perror("open in file"); exit(1); } - strncpy(filename, system_command, syscmd_len-1); - strcpy(&filename[syscmd_len-1], "_out"); - if ( (process->pipe_from_child = open(filename, O_RDONLY)) < 0) { - perror(filename); + if ((process->pipe_from_child = open(filename_out, O_RDONLY)) == -1) { + perror("open out file"); exit(1); } + if (filename_in) free(filename_in); + if (filename_out) free(filename_out); } else { if (pipe(pipe_to_child) || pipe(pipe_from_child) || (pid=fork()) ==-1) { perror("Error: d_process cannot create pipes and fork"); exit(1); - } + } if (pid == 0) { dup2(pipe_to_child[0],0); dup2(pipe_from_child[1],1); close(pipe_to_child[1]); close(pipe_from_child[0]); - + if (execv(system_command, c_argv) == -1) { perror(system_command); exit(1); @@ -259,10 +275,10 @@ static void start(char *system_command, char * c_argv[], Process_t * process) #endif -void cm_d_process(ARGS) +void cm_d_process(ARGS) { int i; /* generic loop counter index */ - + Digital_State_t *reset, /* storage for clock value */ *reset_old; /* previous clock value */ @@ -272,18 +288,18 @@ void cm_d_process(ARGS) Process_t *local_process; /* Structure containing process vars */ - if (INIT) { + if (INIT) { char * c_argv[1024]; int c_argc = 1; cm_event_alloc(0,sizeof(Digital_State_t)); cm_event_alloc(1,sizeof(Digital_State_t)); - + clk = clk_old = (Digital_State_t *) cm_event_get_ptr(0,0); reset = reset_old = (Digital_State_t *) cm_event_get_ptr(1,0); - STATIC_VAR(process) = malloc(sizeof(Process_t)); + STATIC_VAR(process) = malloc(sizeof(Process_t)); local_process = STATIC_VAR(process); - + if (!PARAM_NULL(process_params)) { for (i=0; iN_dout * sizeof(uint8_t)); - din = malloc(local_process->N_din * sizeof(uint8_t)); + dout = (uint8_t *) malloc(local_process->N_dout * sizeof(uint8_t)); + din = (uint8_t *) malloc(local_process->N_din * sizeof(uint8_t)); memset(din, 0, local_process->N_din); for (i=0; i> 3] >> (i & 7)) & 0x01) ? ONE : ZERO; - + if (new_state != local_process->dout_old[i]) { OUTPUT_STATE(out[i]) = new_state; OUTPUT_STRENGTH(out[i]) = STRONG; @@ -370,7 +386,7 @@ void cm_d_process(ARGS) } else { OUTPUT_CHANGED(out[i]) = FALSE; - } + } } free(din); free(dout); @@ -393,19 +409,17 @@ static void w_start(char *system_command, char * c_argv[], Process_t * process) int pipe_to_child[2]; int pipe_from_child[2]; int pid = 0; -#define USE_BINARY_MODE -#ifdef USE_BINARY_MODE int mode = _O_BINARY; -#else - int mode = _O_TEXT; -#endif size_t syscmd_len = strlen(system_command); if (syscmd_len == 0) { fprintf(stderr, "Error: d_process process_file argument is not given"); exit(1); } - + if (system_command[syscmd_len-1] == '|') { + fprintf(stderr, "Error: d_process named pipe/fifo not supported\n"); + exit(1); + } if (_pipe(pipe_to_child, 1024, mode) == -1) { perror("pipe_to_child"); fprintf(stderr, "Failed to open pipe_to_child\n"); diff --git a/src/xspice/icm/digital/d_process/ifspec.ifs b/src/xspice/icm/digital/d_process/ifspec.ifs index d002b97b6..4531f12d4 100644 --- a/src/xspice/icm/digital/d_process/ifspec.ifs +++ b/src/xspice/icm/digital/d_process/ifspec.ifs @@ -2,20 +2,20 @@ ================================================================================ Copyright 2017-2018 Isotel d.o.o. http://www.isotel.eu -AUTHORS +AUTHORS 5 November 2017 Uros Platse - + SUMMARY - This file contains the interface specification file for the + This file contains the interface specification file for the digital d_process (extends to a system process) code model. ===============================================================================*/ - + NAME_TABLE: @@ -32,8 +32,8 @@ Description: "input" "clock" Direction: in in Default_Type: d d Allowed_Types: [d] [d] -Vector: yes no -Vector_Bounds: - - +Vector: yes no +Vector_Bounds: - - Null_Allowed: yes no @@ -45,8 +45,8 @@ Description: "reset" "output" Direction: in out Default_Type: d d Allowed_Types: [d] [d] -Vector: no yes -Vector_Bounds: - [1 -] +Vector: no yes +Vector_Bounds: - [1 -] Null_Allowed: yes yes @@ -58,35 +58,35 @@ Parameter_Name: clk_delay reset_delay Description: "delay from CLK" "delay from reset" Data_Type: real real Default_Value: 1.0e-9 1.0e-9 -Limits: - - -Vector: no no -Vector_Bounds: - - -Null_Allowed: yes yes +Limits: - - +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes PARAMETER_TABLE: Parameter_Name: process_file -Description: "file name of the executable process" -Data_Type: string -Default_Value: "process" -Limits: - -Vector: no -Vector_Bounds: - -Null_Allowed: no +Description: "file name of the executable process" +Data_Type: string +Default_Value: "process" +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: no PARAMETER_TABLE: Parameter_Name: process_params -Description: "parameters to be passed to an executable process" -Data_Type: string +Description: "parameters to be passed to an executable process" +Data_Type: string Default_Value: - -Limits: - -Vector: yes -Vector_Bounds: - +Limits: - +Vector: yes +Vector_Bounds: - Null_Allowed: yes @@ -94,50 +94,50 @@ PARAMETER_TABLE: Parameter_Name: reset_state -Description: "default state on RESET & at DC" -Data_Type: int -Default_Value: 0 -Limits: - -Vector: no -Vector_Bounds: - -Null_Allowed: no +Description: "default state on RESET & at DC" +Data_Type: int +Default_Value: 0 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: no PARAMETER_TABLE: Parameter_Name: input_load -Description: "input loading capacitance (F)" -Data_Type: real -Default_Value: 1.0e-12 -Limits: - -Vector: no -Vector_Bounds: - -Null_Allowed: no +Description: "input loading capacitance (F)" +Data_Type: real +Default_Value: 1.0e-12 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: no PARAMETER_TABLE: Parameter_Name: clk_load -Description: "clock loading capacitance (F)" -Data_Type: real -Default_Value: 1.0e-12 -Limits: - -Vector: no -Vector_Bounds: - -Null_Allowed: no +Description: "clock loading capacitance (F)" +Data_Type: real +Default_Value: 1.0e-12 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: no PARAMETER_TABLE: Parameter_Name: reset_load -Description: "reset loading capacitance (F)" -Data_Type: real -Default_Value: 1.0e-12 -Limits: - -Vector: no -Vector_Bounds: - -Null_Allowed: no +Description: "reset loading capacitance (F)" +Data_Type: real +Default_Value: 1.0e-12 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: no STATIC_VAR_TABLE: From cccb4a1559467c48908c09213e855eb07e960ef1 Mon Sep 17 00:00:00 2001 From: Brian Taylor Date: Thu, 5 Oct 2023 15:13:26 -0700 Subject: [PATCH 08/25] Add callback to free process memory. --- src/xspice/icm/digital/d_process/cfunc.mod | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/xspice/icm/digital/d_process/cfunc.mod b/src/xspice/icm/digital/d_process/cfunc.mod index 1d6f0374b..8b51a7c19 100644 --- a/src/xspice/icm/digital/d_process/cfunc.mod +++ b/src/xspice/icm/digital/d_process/cfunc.mod @@ -85,7 +85,7 @@ REFERENCED FILES Inputs from and outputs to ARGS structure. -===============================================================================*/ +==============================================================================*/ #include #include @@ -275,6 +275,21 @@ static void start(char *system_command, char * c_argv[], Process_t * process) #endif +static void cm_d_process_callback(ARGS, Mif_Callback_Reason_t reason) +{ + switch (reason) { + case MIF_CB_DESTROY: { + Process_t *proc = STATIC_VAR(process); + if (proc) { + free(proc); + STATIC_VAR(process) = NULL; + } + break; + } + } +} + + void cm_d_process(ARGS) { int i; /* generic loop counter index */ @@ -299,6 +314,7 @@ void cm_d_process(ARGS) STATIC_VAR(process) = malloc(sizeof(Process_t)); local_process = STATIC_VAR(process); + CALLBACK = cm_d_process_callback; if (!PARAM_NULL(process_params)) { for (i=0; i Date: Fri, 6 Oct 2023 14:58:53 -0700 Subject: [PATCH 09/25] Fix VisualC and Mingw compiler warnings. --- src/xspice/icm/digital/d_process/cfunc.mod | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/xspice/icm/digital/d_process/cfunc.mod b/src/xspice/icm/digital/d_process/cfunc.mod index 8b51a7c19..7dbbbd7fd 100644 --- a/src/xspice/icm/digital/d_process/cfunc.mod +++ b/src/xspice/icm/digital/d_process/cfunc.mod @@ -114,7 +114,7 @@ typedef struct { #if defined(_MSC_VER) || defined(__MINGW64__) #include -static void w_start(char *system_command, char * c_argv[], Process_t * process); +static void w_start(char *system_command, const char *const *argv, Process_t * process); #endif static void sendheader(Process_t * process, int N_din, int N_dout) @@ -185,7 +185,6 @@ static void dprocess_exchangedata(Process_t * process, double time, uint8_t din[ } __attribute__((packed)) packet_t; #endif - size_t dlen = 0; int wlen = 0; packet_t packet; packet.time = time; @@ -202,14 +201,13 @@ static void dprocess_exchangedata(Process_t * process, double time, uint8_t din[ } #if defined(_MSC_VER) || defined(__MINGW64__) - dlen = _read(process->pipe_from_child, dout, process->N_dout); + if (_read(process->pipe_from_child, dout, process->N_dout) != process->N_dout) { #else - dlen = read(process->pipe_from_child, dout, process->N_dout); + if (read(process->pipe_from_child, dout, process->N_dout) != process->N_dout) { #endif - if (dlen != (size_t)process->N_dout) { fprintf(stderr, - "Error: d_process received invalid dout count, read %lu expected %d\n", - dlen, process->N_dout); + "Error: d_process received invalid dout count, expected %d\n", + process->N_dout); exit(1); } } @@ -325,7 +323,7 @@ void cm_d_process(ARGS) c_argv[c_argc] = NULL; #if defined(_MSC_VER) || defined(__MINGW64__) - w_start(c_argv[0], c_argv, local_process); + w_start(c_argv[0], (const char *const *)c_argv, local_process); #else start(c_argv[0], c_argv, local_process); #endif @@ -420,11 +418,11 @@ void cm_d_process(ARGS) #include #include -static void w_start(char *system_command, char * c_argv[], Process_t * process) +static void w_start(char *system_command, const char *const *argv, Process_t * process) { int pipe_to_child[2]; int pipe_from_child[2]; - int pid = 0; + intptr_t pid = 0; int mode = _O_BINARY; size_t syscmd_len = strlen(system_command); @@ -453,7 +451,7 @@ static void w_start(char *system_command, char * c_argv[], Process_t * process) _close(pipe_from_child[1]); _flushall(); - pid = _spawnvp(_P_NOWAIT, system_command, c_argv); + pid = _spawnvp(_P_NOWAIT, system_command, argv); if (pid == -1) { perror("spawn from d_process"); fprintf(stderr, "Failed to spawn %s\n", system_command); From e762047fc28c009309018687e7486b980e1aaff1 Mon Sep 17 00:00:00 2001 From: Brian Taylor Date: Sun, 8 Oct 2023 14:57:43 -0700 Subject: [PATCH 10/25] Add array bounds check, fix typos. --- src/xspice/icm/digital/d_process/cfunc.mod | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/xspice/icm/digital/d_process/cfunc.mod b/src/xspice/icm/digital/d_process/cfunc.mod index 7dbbbd7fd..218c08d02 100644 --- a/src/xspice/icm/digital/d_process/cfunc.mod +++ b/src/xspice/icm/digital/d_process/cfunc.mod @@ -51,7 +51,7 @@ SUMMARY double (8-byte): positive value of TIME if reset is low otherwise -TIME [inputs array ]: input bytes, each byte packs up to 8 inputs - ooutputs are defined by returning process + outputs are defined by returning process and process must return: @@ -292,8 +292,8 @@ void cm_d_process(ARGS) { int i; /* generic loop counter index */ - Digital_State_t *reset, /* storage for clock value */ - *reset_old; /* previous clock value */ + Digital_State_t *reset, /* storage for reset value */ + *reset_old; /* previous reset value */ Digital_State_t *clk, /* storage for clock value */ *clk_old; /* previous clock value */ @@ -302,7 +302,8 @@ void cm_d_process(ARGS) if (INIT) { - char * c_argv[1024]; +#define C_ARGV_SIZE 1024 + char * c_argv[C_ARGV_SIZE]; int c_argc = 1; cm_event_alloc(0,sizeof(Digital_State_t)); cm_event_alloc(1,sizeof(Digital_State_t)); @@ -315,12 +316,17 @@ void cm_d_process(ARGS) CALLBACK = cm_d_process_callback; if (!PARAM_NULL(process_params)) { + if (PARAM_SIZE(process_params) > (C_ARGV_SIZE - 2)) { + fprintf(stderr, "Error: too many process_parameters\n"); + exit(1); + } for (i=0; i Date: Sun, 8 Oct 2023 20:22:00 -0700 Subject: [PATCH 11/25] Remove the unused reset_delay param. --- src/xspice/icm/digital/d_process/cfunc.mod | 3 +++ src/xspice/icm/digital/d_process/ifspec.ifs | 16 ++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/xspice/icm/digital/d_process/cfunc.mod b/src/xspice/icm/digital/d_process/cfunc.mod index 218c08d02..10c0c22cd 100644 --- a/src/xspice/icm/digital/d_process/cfunc.mod +++ b/src/xspice/icm/digital/d_process/cfunc.mod @@ -80,6 +80,9 @@ MODIFICATIONS For Windows VisualC and Mingw the pipes use binary mode, and named pipes or fifos are not supported. + 8 October 2023 Brian Taylor + Remove reset_delay param from ifspec.ifs since the clk_delay + is used with synchronous reset. REFERENCED FILES diff --git a/src/xspice/icm/digital/d_process/ifspec.ifs b/src/xspice/icm/digital/d_process/ifspec.ifs index 4531f12d4..faf317c60 100644 --- a/src/xspice/icm/digital/d_process/ifspec.ifs +++ b/src/xspice/icm/digital/d_process/ifspec.ifs @@ -54,14 +54,14 @@ Null_Allowed: yes yes PARAMETER_TABLE: -Parameter_Name: clk_delay reset_delay -Description: "delay from CLK" "delay from reset" -Data_Type: real real -Default_Value: 1.0e-9 1.0e-9 -Limits: - - -Vector: no no -Vector_Bounds: - - -Null_Allowed: yes yes +Parameter_Name: clk_delay +Description: "delay from CLK" +Data_Type: real +Default_Value: 1.0e-9 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes PARAMETER_TABLE: From 307ba1985b655f24843d2cce329a4e0882c2f163 Mon Sep 17 00:00:00 2001 From: Brian Taylor Date: Mon, 9 Oct 2023 21:52:32 -0700 Subject: [PATCH 12/25] Remove unused reset_state param; do not allow null in and out, so there is at least 1 input and 1 output. --- src/xspice/icm/digital/d_process/ifspec.ifs | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/xspice/icm/digital/d_process/ifspec.ifs b/src/xspice/icm/digital/d_process/ifspec.ifs index faf317c60..696eb8064 100644 --- a/src/xspice/icm/digital/d_process/ifspec.ifs +++ b/src/xspice/icm/digital/d_process/ifspec.ifs @@ -34,7 +34,7 @@ Default_Type: d d Allowed_Types: [d] [d] Vector: yes no Vector_Bounds: - - -Null_Allowed: yes no +Null_Allowed: no no PORT_TABLE: @@ -47,7 +47,7 @@ Default_Type: d d Allowed_Types: [d] [d] Vector: no yes Vector_Bounds: - [1 -] -Null_Allowed: yes yes +Null_Allowed: yes no @@ -93,19 +93,6 @@ Null_Allowed: yes PARAMETER_TABLE: -Parameter_Name: reset_state -Description: "default state on RESET & at DC" -Data_Type: int -Default_Value: 0 -Limits: - -Vector: no -Vector_Bounds: - -Null_Allowed: no - - -PARAMETER_TABLE: - - Parameter_Name: input_load Description: "input loading capacitance (F)" Data_Type: real @@ -127,6 +114,7 @@ Limits: - Vector: no Vector_Bounds: - Null_Allowed: no + PARAMETER_TABLE: From df28fabc42c2ba6e38f6f80a5fb77becb0af5417 Mon Sep 17 00:00:00 2001 From: Brian Taylor Date: Tue, 10 Oct 2023 15:41:06 -0700 Subject: [PATCH 13/25] Restore the deleted unused params to be backword compatible. Handle the case where the d_process instance has a null in vector, so that N_din is zero. This allows the model to be just a pattern generator. Add include to cmproto.h to avoid a forward ref. to an enum type. --- src/include/ngspice/cmproto.h | 1 + src/xspice/icm/digital/d_process/cfunc.mod | 29 +++++++++++------- src/xspice/icm/digital/d_process/ifspec.ifs | 33 ++++++++++++++------- 3 files changed, 43 insertions(+), 20 deletions(-) diff --git a/src/include/ngspice/cmproto.h b/src/include/ngspice/cmproto.h index 5f7fc4c11..7ab6ffbd5 100644 --- a/src/include/ngspice/cmproto.h +++ b/src/include/ngspice/cmproto.h @@ -49,6 +49,7 @@ NON-STANDARD FEATURES #include #include "ngspice/cmtypes.h" #include "ngspice/cktdefs.h" +#include "ngspice/cpextern.h" void cm_climit_fcn(double in, double in_offset, double cntl_upper, diff --git a/src/xspice/icm/digital/d_process/cfunc.mod b/src/xspice/icm/digital/d_process/cfunc.mod index 10c0c22cd..7a72919a8 100644 --- a/src/xspice/icm/digital/d_process/cfunc.mod +++ b/src/xspice/icm/digital/d_process/cfunc.mod @@ -80,9 +80,6 @@ MODIFICATIONS For Windows VisualC and Mingw the pipes use binary mode, and named pipes or fifos are not supported. - 8 October 2023 Brian Taylor - Remove reset_delay param from ifspec.ifs since the clk_delay - is used with synchronous reset. REFERENCED FILES @@ -191,7 +188,11 @@ static void dprocess_exchangedata(Process_t * process, double time, uint8_t din[ int wlen = 0; packet_t packet; packet.time = time; - memcpy(packet.din, din, process->N_din); + if (process->N_din > 0) { + memcpy(packet.din, din, process->N_din); + } else { + packet.din[0] = 0; + } #if defined(_MSC_VER) || defined(__MINGW64__) wlen = _write(process->pipe_to_child, &packet, sizeof(double) + process->N_din); @@ -338,6 +339,12 @@ void cm_d_process(ARGS) #endif sendheader(local_process, PORT_SIZE(in), PORT_SIZE(out)); + if (PORT_SIZE(in) == 0) { + if (local_process->N_din != 0) { + fprintf(stderr, "Error: in port size mismatch\n"); + exit(1); + } + } for (i=0; iN_dout * sizeof(uint8_t)); - din = (uint8_t *) malloc(local_process->N_din * sizeof(uint8_t)); - memset(din, 0, local_process->N_din); + dout = (uint8_t *)malloc(local_process->N_dout * sizeof(uint8_t)); + if (local_process->N_din > 0) { + din = (uint8_t *)malloc(local_process->N_din * sizeof(uint8_t)); + memset(din, 0, local_process->N_din); + } for (i=0; i Date: Wed, 11 Oct 2023 15:03:57 -0700 Subject: [PATCH 14/25] Add examples/xspice/d_process. --- examples/xspice/d_process/README | 55 +++++++++ examples/xspice/d_process/clean.sh | 7 ++ examples/xspice/d_process/compile.bat.txt | 4 + examples/xspice/d_process/compile.sh | 5 + examples/xspice/d_process/d_process.h | 53 +++++++++ examples/xspice/d_process/graycode.c | 131 +++++++++++++++++++++ examples/xspice/d_process/nggtk.tcl | 10 ++ examples/xspice/d_process/prog-pipes.cir | 40 +++++++ examples/xspice/d_process/prog1-4.cir | 46 ++++++++ examples/xspice/d_process/prog1in4out.c | 136 +++++++++++++++++++++ examples/xspice/d_process/prog4in1out.c | 137 ++++++++++++++++++++++ 11 files changed, 624 insertions(+) create mode 100644 examples/xspice/d_process/README create mode 100755 examples/xspice/d_process/clean.sh create mode 100644 examples/xspice/d_process/compile.bat.txt create mode 100755 examples/xspice/d_process/compile.sh create mode 100644 examples/xspice/d_process/d_process.h create mode 100644 examples/xspice/d_process/graycode.c create mode 100644 examples/xspice/d_process/nggtk.tcl create mode 100644 examples/xspice/d_process/prog-pipes.cir create mode 100644 examples/xspice/d_process/prog1-4.cir create mode 100644 examples/xspice/d_process/prog1in4out.c create mode 100644 examples/xspice/d_process/prog4in1out.c diff --git a/examples/xspice/d_process/README b/examples/xspice/d_process/README new file mode 100644 index 000000000..1be54064a --- /dev/null +++ b/examples/xspice/d_process/README @@ -0,0 +1,55 @@ +The d_process Xspice model was created by Uros Platise. + +A complete, non-trivial example is located at: + https://www.isotel.eu/mixedsim/embedded/motorforce/index.html + +This directory contains a simple test of the d_process model. + +Compile the programs that are called from within d_process: + + ./compile.sh (on Linux or Cygwin on Windows) + +In a Windows Powershell using VisualC, copy compile.bat.txt to compile.bat, +then use it. In a Mingw Msys shell, you can use the VisualC compiled programs, +or use compile.sh. In both these environments the programs need to be compiled +this way to use binary mode pipes. + +Run the test case (comment out the gtkwave lines or use plot if you like): + + ngspice prog1-4.cir + +To clean up: + + ./clean.sh + +NOTE that the prog-pipes.cir test needs fifos created on Linux or Cygwin +on Windows. This is not available on Windows VisualC or Mingw builds. + + mkfifo graycode_in + mkfifo graycode_out + +before ngspice prog-pipes.cir, and in another shell: + + ./graycode --pipe + +needs to be started. + +NOTE on debugging. On Linux or Cygwin on Windows, gdb can be attached to +the running d_process programs, or in --pipe mode with fifos, the graycode +example can be run when invoked from gdb. + +From a Windows Powershell, WinDbg can be attached to a running d_process +program. + +Each program prints (to stderr) its process id when started. This makes +it easier to know the process when attaching a debugger. + +All the programs (graycode.c, prog1in4out.c, prog4in1out.c) contain a call +to sleep to give you time to attach a debugger. This is enabled by: + + export GO_TO_SLEEP=1 (on Linux, Cygwin) + $env:go_to_sleep = '1' (on Windows Powershell) + +Don't forget to remove the environment variables after use, or the +prog1-4.cir will sleep (for up to 1 minute) each time you rerun it. + diff --git a/examples/xspice/d_process/clean.sh b/examples/xspice/d_process/clean.sh new file mode 100755 index 000000000..45c7e3558 --- /dev/null +++ b/examples/xspice/d_process/clean.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -v +rm -fv *.vcd *.exe *.obj +rm -fv prog1in4out +rm -fv prog4in1out +rm -fv graycode +rm -fv graycode_in graycode_out diff --git a/examples/xspice/d_process/compile.bat.txt b/examples/xspice/d_process/compile.bat.txt new file mode 100644 index 000000000..ef8470602 --- /dev/null +++ b/examples/xspice/d_process/compile.bat.txt @@ -0,0 +1,4 @@ +# Add /Zi for debug info +cl /Fe: prog1in4out.exe prog1in4out.c +cl /Fe: prog4in1out.exe prog4in1out.c +cl /Fe: graycode.exe graycode.c diff --git a/examples/xspice/d_process/compile.sh b/examples/xspice/d_process/compile.sh new file mode 100755 index 000000000..50d1dcd2d --- /dev/null +++ b/examples/xspice/d_process/compile.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -v +gcc -Wall -Wpedantic -g -o prog1in4out prog1in4out.c +gcc -Wall -Wpedantic -g -o prog4in1out prog4in1out.c +gcc -Wall -Wpedantic -g -o graycode graycode.c diff --git a/examples/xspice/d_process/d_process.h b/examples/xspice/d_process/d_process.h new file mode 100644 index 000000000..face10ab1 --- /dev/null +++ b/examples/xspice/d_process/d_process.h @@ -0,0 +1,53 @@ +/** + * \file + * \brief NgSpice D_PROCESS Interface + * \author Uros Platise + */ + +#ifndef __NGSPICE_D_PROCESS__ +#define __NGSPICE_D_PROCESS__ + +#include +#if !defined(_MSC_VER) +#include +#endif +#include + +#define D_PROCESS_FORMAT_VERSION 0x01 +#define D_PROCESS_DLEN(x) (uint8_t)( ((x)==0) ? 0 : (((x)-1)/8 + 1) ) + +static inline int d_process_init(int pipein, int pipeout, uint8_t N_din, uint8_t N_dout) { +#if defined(_MSC_VER) || defined(__MINGW64__) +#pragma pack(push, 1) + struct header_s { + uint8_t version, N_din, N_dout; + } header; +#pragma pack(pop) +#else + struct header_s { + uint8_t version, N_din, N_dout; + } __attribute__((packed))header; +#endif + +#if defined(_MSC_VER) || defined(__MINGW64__) + if (_read(pipein, &header, sizeof(header)) != sizeof(header)) { +#else + if (read(pipein, &header, sizeof(header)) != sizeof(header)) { +#endif + fprintf(stderr, "Error: Incompatible ngspice d_process header size, requiring version %d\n", header.version); + return 0; + } + if (header.version != D_PROCESS_FORMAT_VERSION || header.N_din != N_din || header.N_dout != N_dout) { + fprintf(stderr, "Error: Incompatible ngspice d_process requiring version %d, number of inputs %d expected %d, and outputs %d expected %d.\n", + header.version, header.N_din, N_din, header.N_dout, N_dout); + return 0; + } +#if defined(_MSC_VER) || defined(__MINGW64__) + _write(pipeout, &header, sizeof(header)); // acknowledge connection by returning back the same header +#else + write(pipeout, &header, sizeof(header)); // acknowledge connection by returning back the same header +#endif + return 1; +} + +#endif diff --git a/examples/xspice/d_process/graycode.c b/examples/xspice/d_process/graycode.c new file mode 100644 index 000000000..19216e088 --- /dev/null +++ b/examples/xspice/d_process/graycode.c @@ -0,0 +1,131 @@ +/* + The organization of this file is modelled after the motorforce example + developed by Uros Platise. +*/ +#include +#include +#include +#if !defined(_MSC_VER) && !defined(__MINGW64__) +#include +#endif +#if defined(_MSC_VER) +#include +#include +#include +#endif +#include "d_process.h" + +#define DIGITAL_IN 0 +#define DIGITAL_OUT 4 + +static int compute(uint8_t *dataout, int outsz, double time); + +static int known_bp(int iargc) +{ + return iargc; +} + +int main(int argc, char *argv[]) { + int i; + int outlen = D_PROCESS_DLEN(DIGITAL_OUT); + +#if defined(_MSC_VER) || defined(__MINGW64__) +#pragma pack(push, 1) + struct in_s { + double time; + } in; + + struct out_s { + uint8_t dout[D_PROCESS_DLEN(DIGITAL_OUT)]; + } out; +#pragma pack(pop) +#else + struct in_s { + double time; + } __attribute__((packed)) in; + + struct out_s { + uint8_t dout[D_PROCESS_DLEN(DIGITAL_OUT)]; + } __attribute__((packed)) out; +#endif + + int pipein = 0; // default stdin to recv from ngspice + int pipeout= 1; // default stdout to send to ngspice +#if defined(_MSC_VER) || defined(__MINGW64__) + _setmode(0, _O_BINARY); + _setmode(1, _O_BINARY); +#endif + +#if defined(_MSC_VER) || defined(__MINGW64__) + fprintf(stderr, "%s pid %d\n", argv[0], _getpid()); +#else + fprintf(stderr, "%s pid %d\n", argv[0], getpid()); +#endif + +#if !defined(_MSC_VER) && !defined(__MINGW64__) + if (getenv("GO_TO_SLEEP")) { + sleep(40); + } +#endif +#if defined(__MINGW64__) + if (getenv("GO_TO_SLEEP")) { + sleep(40); + } +#endif +#if defined(_MSC_VER) + if (getenv("GO_TO_SLEEP")) { + Sleep(60000); + } +#endif + + (void)known_bp(argc); + + for (i=0; i> 1)) & 0x0F; + return 1; +} + diff --git a/examples/xspice/d_process/nggtk.tcl b/examples/xspice/d_process/nggtk.tcl new file mode 100644 index 000000000..a8669f6f8 --- /dev/null +++ b/examples/xspice/d_process/nggtk.tcl @@ -0,0 +1,10 @@ +# tcl script for gtkwave: show vcd file data created by ngspice +set nfacs [ gtkwave::getNumFacs ] + +for {set i 0} {$i < $nfacs } {incr i} { + set facname [ gtkwave::getFacName $i ] + set num_added [ gtkwave::addSignalsFromList $facname ] +} + +gtkwave::/Edit/UnHighlight_All +gtkwave::/Time/Zoom/Zoom_Full diff --git a/examples/xspice/d_process/prog-pipes.cir b/examples/xspice/d_process/prog-pipes.cir new file mode 100644 index 000000000..2ca3caa62 --- /dev/null +++ b/examples/xspice/d_process/prog-pipes.cir @@ -0,0 +1,40 @@ +prog-pipes.cir + +*** analysis type *** +.tran .01us 10us +v1 1 0 DC 1.0 +v2 2 0 DC 0.0 + +.model d_osc1 d_osc (cntl_array=[-1.0 0.0 1.0 2.0] ++ freq_array=[1.0e6 1.0e6 4.0e6 4.0e6] ++ rise_delay=1.0e-6 fall_delay=2.0e-6) + +a1 1 clk1 d_osc1 +a2 2 clk2 d_osc1 + +ap0_4 null clk1 null [q1 q2 q3 q4] proc0 +.model proc0 d_process (process_file="graycode|" process_params=["--pipe"]) + +ap1_4 [clk2] clk1 null [o1 o2 o3 o4] proc1 +.model proc1 d_process (process_file="prog1in4out" process_params=["opt1", "qwerty"]) + +ap4_1 [o1 o2 o3 o4] clk1 null [zeros] proc2 +.model proc2 d_process (process_file="prog4in1out" process_params=["abc", "99"]) + +ap4_1a [q1 q2 q3 q4] clk1 null [qzeros] proc3 +.model proc3 d_process (process_file="prog4in1out") + +.control +run +edisplay +eprvcd clk1 clk2 o1 o2 o3 o4 q1 q2 q3 q4 zeros qzeros > prog1-4.vcd +shell gtkwave prog1-4.vcd --script nggtk.tcl & +* quit +.endc +.end + + + + + + diff --git a/examples/xspice/d_process/prog1-4.cir b/examples/xspice/d_process/prog1-4.cir new file mode 100644 index 000000000..032a71ba8 --- /dev/null +++ b/examples/xspice/d_process/prog1-4.cir @@ -0,0 +1,46 @@ +prog1-4.cir + +*** analysis type *** +.tran .01us 10us +v1 1 0 DC 1.0 +v2 2 0 DC 0.0 + +.model d_osc1 d_osc (cntl_array=[-1.0 0.0 1.0 2.0] ++ freq_array=[1.0e6 1.0e6 4.0e6 4.0e6] ++ rise_delay=1.0e-6 fall_delay=2.0e-6) + +a1 1 clk1 d_osc1 +a2 2 clk2 d_osc1 + +ap0_4 null clk1 null [q1 q2 q3 q4] proc0 +.model proc0 d_process (process_file="graycode" process_params=["none"]) + +ap1_4 [clk2] clk1 null [o1 o2 o3 o4] proc1 +.model proc1 d_process (process_file="prog1in4out" process_params=["opt1", "qwerty"]) + +ap4_1 [o1 o2 o3 o4] clk1 null [zeros] proc2 +.model proc2 d_process (process_file="prog4in1out" process_params=["abc", "99"]) + +ap4_1a [q1 q2 q3 q4] clk1 null [qzeros] proc3 +.model proc3 d_process (process_file="prog4in1out") + +an1 [o1 ~o2 o3] reseto dand1 +.model dand1 d_and(inertial_delay=true rise_delay=1ns fall_delay=50ns) + +ap1_4a [clk2] clk1 reseto [b1 b2 b3 b4] proc4 +.model proc4 d_process (process_file="prog1in4out") + +.control +run +edisplay +eprvcd clk1 clk2 o1 o2 o3 o4 q1 q2 q3 q4 b1 b2 b3 b4 zeros qzeros reseto > prog1-4.vcd +shell gtkwave prog1-4.vcd --script nggtk.tcl & +quit +.endc +.end + + + + + + diff --git a/examples/xspice/d_process/prog1in4out.c b/examples/xspice/d_process/prog1in4out.c new file mode 100644 index 000000000..da8964114 --- /dev/null +++ b/examples/xspice/d_process/prog1in4out.c @@ -0,0 +1,136 @@ +/* + The organization of this file is modelled after the motorforce example + developed by Uros Platise. +*/ +#include +#include +#include +#if !defined(_MSC_VER) && !defined(__MINGW64__) +#include +#endif +#if defined(_MSC_VER) +#include +#include +#include +#endif +#include "d_process.h" + +#define DIGITAL_IN 1 +#define DIGITAL_OUT 4 + +static int compute( + uint8_t *datain, int insz, uint8_t *dataout, int outsz, double time +); + +static int known_bp(int iargc) +{ + return iargc; +} + +int main(int argc, char *argv[]) { + int i; + int inlen = D_PROCESS_DLEN(DIGITAL_IN); + int outlen = D_PROCESS_DLEN(DIGITAL_OUT); + +#if defined(_MSC_VER) || defined(__MINGW64__) +#pragma pack(push, 1) + struct in_s { + double time; + uint8_t din[D_PROCESS_DLEN(DIGITAL_IN)]; + } in; + + struct out_s { + uint8_t dout[D_PROCESS_DLEN(DIGITAL_OUT)]; + } out; +#pragma pack(pop) +#else + struct in_s { + double time; + uint8_t din[D_PROCESS_DLEN(DIGITAL_IN)]; + } __attribute__((packed)) in; + + struct out_s { + uint8_t dout[D_PROCESS_DLEN(DIGITAL_OUT)]; + } __attribute__((packed)) out; +#endif + + int pipein = 0; // default stdin to recv from ngspice + int pipeout= 1; // default stdout to send to ngspice +#if defined(_MSC_VER) || defined(__MINGW64__) + _setmode(0, _O_BINARY); + _setmode(1, _O_BINARY); +#endif + +#if defined(_MSC_VER) || defined(__MINGW64__) + fprintf(stderr, "%s pid %d\n", argv[0], _getpid()); +#else + fprintf(stderr, "%s pid %d\n", argv[0], getpid()); +#endif + +#if !defined(_MSC_VER) && !defined(__MINGW64__) + if (getenv("GO_TO_SLEEP")) { + sleep(40); + } +#endif +#if defined(__MINGW64__) + if (getenv("GO_TO_SLEEP")) { + sleep(40); + } +#endif +#if defined(_MSC_VER) + if (getenv("GO_TO_SLEEP")) { + Sleep(60000); + } +#endif + + (void)known_bp(argc); + + for (i=0; i +#include +#include +#if !defined(_MSC_VER) && !defined(__MINGW64__) +#include +#endif +#if defined(_MSC_VER) +#include +#include +#include +#endif +#include "d_process.h" + +#define DIGITAL_IN 4 +#define DIGITAL_OUT 1 + +static int compute( + uint8_t *datain, int insz, uint8_t *dataout, int outsz, double time +); + +static int known_bp(int iargc) +{ + return iargc; +} + +int main(int argc, char *argv[]) { + int i; + int inlen = D_PROCESS_DLEN(DIGITAL_IN); + int outlen = D_PROCESS_DLEN(DIGITAL_OUT); + +#if defined(_MSC_VER) || defined(__MINGW64__) +#pragma pack(push, 1) + struct in_s { + double time; + uint8_t din[D_PROCESS_DLEN(DIGITAL_IN)]; + } in; + + struct out_s { + uint8_t dout[D_PROCESS_DLEN(DIGITAL_OUT)]; + } out; +#pragma pack(pop) +#else + struct in_s { + double time; + uint8_t din[D_PROCESS_DLEN(DIGITAL_IN)]; + } __attribute__((packed)) in; + + struct out_s { + uint8_t dout[D_PROCESS_DLEN(DIGITAL_OUT)]; + } __attribute__((packed)) out; +#endif + + int pipein = 0; // default stdin to recv from ngspice + int pipeout= 1; // default stdout to send to ngspice +#if defined(_MSC_VER) || defined(__MINGW64__) + _setmode(0, _O_BINARY); + _setmode(1, _O_BINARY); +#endif + +#if defined(_MSC_VER) || defined(__MINGW64__) + fprintf(stderr, "%s pid %d\n", argv[0], _getpid()); +#else + fprintf(stderr, "%s pid %d\n", argv[0], getpid()); +#endif + +#if !defined(_MSC_VER) && !defined(__MINGW64__) + if (getenv("GO_TO_SLEEP")) { + sleep(40); + } +#endif +#if defined(__MINGW64__) + if (getenv("GO_TO_SLEEP")) { + sleep(40); + } +#endif +#if defined(_MSC_VER) + if (getenv("GO_TO_SLEEP")) { + Sleep(60000); + } +#endif + + (void)known_bp(argc); + + for (i=0; i Date: Fri, 13 Oct 2023 18:15:38 -0700 Subject: [PATCH 15/25] Use Xspice cm_message_send to report errors rathen than printing to stderr and calling exit. When a d_process model has errors found in start(), sendheader(), and dprocess_exchangedata() these are reported, but if the model is run a SIGINT is raised. There must be a better way of stopping the simulator. --- examples/xspice/d_process/README | 3 + examples/xspice/d_process/graycode.c | 13 +- examples/xspice/d_process/prog1in4out.c | 16 +- examples/xspice/d_process/prog4in1out.c | 14 +- src/xspice/icm/digital/d_process/cfunc.mod | 177 +++++++++++++-------- 5 files changed, 142 insertions(+), 81 deletions(-) diff --git a/examples/xspice/d_process/README b/examples/xspice/d_process/README index 1be54064a..9031425c6 100644 --- a/examples/xspice/d_process/README +++ b/examples/xspice/d_process/README @@ -41,6 +41,9 @@ example can be run when invoked from gdb. From a Windows Powershell, WinDbg can be attached to a running d_process program. +To enable the debugging hooks in the programs (graycode.c, prog1in4out.c, +prog4in1out.c), edit them to #define ENABLE_DEBUGGING. + Each program prints (to stderr) its process id when started. This makes it easier to know the process when attaching a debugger. diff --git a/examples/xspice/d_process/graycode.c b/examples/xspice/d_process/graycode.c index 19216e088..835552ef2 100644 --- a/examples/xspice/d_process/graycode.c +++ b/examples/xspice/d_process/graycode.c @@ -20,10 +20,13 @@ static int compute(uint8_t *dataout, int outsz, double time); +//#define ENABLE_DEBUGGING 1 +#ifdef ENABLE_DEBUGGING static int known_bp(int iargc) { return iargc; } +#endif int main(int argc, char *argv[]) { int i; @@ -56,6 +59,7 @@ int main(int argc, char *argv[]) { _setmode(1, _O_BINARY); #endif +#ifdef ENABLE_DEBUGGING #if defined(_MSC_VER) || defined(__MINGW64__) fprintf(stderr, "%s pid %d\n", argv[0], _getpid()); #else @@ -79,9 +83,10 @@ int main(int argc, char *argv[]) { #endif (void)known_bp(argc); +#endif for (i=0; i #include #include +#include #endif #include @@ -108,16 +109,53 @@ typedef unsigned char uint8_t; typedef struct { int pipe_to_child; int pipe_from_child; + int start_failed; + int header_failed; + int exchange_failed; uint8_t N_din, N_dout; // number of inputs/outputs bytes Digital_State_t dout_old[256]; // max possible storage to track output changes } Process_t; #if defined(_MSC_VER) || defined(__MINGW64__) #include -static void w_start(char *system_command, const char *const *argv, Process_t * process); +static unsigned int w_start(char *system_command, const char *const *argv, Process_t * process); #endif -static void sendheader(Process_t * process, int N_din, int N_dout) +static void report_sendheader_message(unsigned int msg_num) +{ + static char *messages[] = { + "", + "ERROR: d_process supports max 255 input and max 255 output signals", + "ERROR: d_process when sending header", + "ERROR: d_process didn't respond to the header", + "ERROR: d_process returned invalid header version", + "ERROR: d_process header I/O mismatch N_din and N_dout counts" + }; + cm_message_send(messages[msg_num]); +} + +static void report_exchangedata_message(unsigned int msg_num) +{ + static char *messages[] = { + "", + "ERROR: d_process when writing exchange data", + "ERROR: d_process received invalid dout count when reading exchange data" + }; + cm_message_send(messages[msg_num]); +} + +static void report_start_message(unsigned int msg_num) +{ + static char *messages[] = { + "", + "ERROR: d_process process_file argument is invalid or not given", + "ERROR: d_process failed to open pipe", + "ERROR: d_process failed to fork or start process" + }; + cm_message_send(messages[msg_num]); +} + +static unsigned int sendheader(Process_t * process, int N_din, int N_dout) { #if defined(_MSC_VER) #pragma pack(push, 1) @@ -132,8 +170,7 @@ static void sendheader(Process_t * process, int N_din, int N_dout) #endif if (N_din > 255 || N_dout > 255) { - fprintf(stderr, "Error: d_process supports max 255 input and output and 255 output signals\n"); - exit(1); + return 1; } #if defined(_MSC_VER) || defined(__MINGW64__) @@ -141,8 +178,7 @@ static void sendheader(Process_t * process, int N_din, int N_dout) #else if (write(process->pipe_to_child, &header, sizeof(header)) == -1) { #endif - fprintf(stderr, "Error: d_process when sending header\n"); - exit(1); + return 2; } // Wait for echo which must return the same header to ack transfer @@ -151,25 +187,22 @@ static void sendheader(Process_t * process, int N_din, int N_dout) #else if (read(process->pipe_from_child, &header, sizeof(header)) != sizeof(header)) { #endif - fprintf(stderr, "Error: d_process didn't respond to the header\n"); - exit(1); + return 3; } if (header.version != D_PROCESS_FORMAT_VERSION) { - fprintf(stderr, "Error: d_process returned invalid version: %d\n", header.version); - exit(1); + return 4; } if (header.N_din != N_din || header.N_dout != N_dout) { - fprintf(stderr, "Error: d_process I/O mismatch: in %d vs. returned %d, out %d vs. returned %d\n", - N_din, header.N_din, N_dout, header.N_dout); - exit(1); + return 5; } process->N_din = (uint8_t)DLEN(N_din); process->N_dout = (uint8_t)DLEN(N_dout); + return 0; } -static void dprocess_exchangedata(Process_t * process, double time, uint8_t din[], uint8_t dout[]) +static unsigned int dprocess_exchangedata(Process_t * process, double time, uint8_t din[], uint8_t dout[]) { #if defined(_MSC_VER) #pragma pack(push, 1) @@ -200,8 +233,7 @@ static void dprocess_exchangedata(Process_t * process, double time, uint8_t din[ wlen = write(process->pipe_to_child, &packet, sizeof(double) + process->N_din); #endif if (wlen == -1) { - fprintf(stderr, "Error: d_process when writing exchange data\n"); - exit(1); + return 1; } #if defined(_MSC_VER) || defined(__MINGW64__) @@ -209,16 +241,14 @@ static void dprocess_exchangedata(Process_t * process, double time, uint8_t din[ #else if (read(process->pipe_from_child, dout, process->N_dout) != process->N_dout) { #endif - fprintf(stderr, - "Error: d_process received invalid dout count, expected %d\n", - process->N_dout); - exit(1); + return 2; } + return 0; } #if !defined(_MSC_VER) && !defined(__MINGW64__) -static void start(char *system_command, char * c_argv[], Process_t * process) +static unsigned int start(char *system_command, char * c_argv[], Process_t * process) { int pipe_to_child[2]; int pipe_from_child[2]; @@ -226,8 +256,7 @@ static void start(char *system_command, char * c_argv[], Process_t * process) size_t syscmd_len = strlen(system_command); if (syscmd_len == 0) { - fprintf(stderr, "Error: d_process process_file argument is not given"); - exit(1); + return 1; } if (system_command[syscmd_len-1] == '|') { char *filename_in = NULL, *filename_out = NULL; @@ -240,20 +269,20 @@ static void start(char *system_command, char * c_argv[], Process_t * process) strncpy(filename_out, system_command, syscmd_len-1); strcpy(&filename_out[syscmd_len-1], "_out"); if ((process->pipe_to_child = open(filename_in, O_WRONLY)) == -1) { - perror("open in file"); - exit(1); + return 2; } if ((process->pipe_from_child = open(filename_out, O_RDONLY)) == -1) { - perror("open out file"); - exit(1); + return 2; } if (filename_in) free(filename_in); if (filename_out) free(filename_out); } else { - if (pipe(pipe_to_child) || pipe(pipe_from_child) || (pid=fork()) ==-1) { - perror("Error: d_process cannot create pipes and fork"); - exit(1); + if (pipe(pipe_to_child) || pipe(pipe_from_child)) { + return 2; + } + if ((pid=fork()) ==-1) { + return 3; } if (pid == 0) { dup2(pipe_to_child[0],0); @@ -262,17 +291,19 @@ static void start(char *system_command, char * c_argv[], Process_t * process) close(pipe_from_child[0]); if (execv(system_command, c_argv) == -1) { - perror(system_command); - exit(1); + fprintf(stderr, + "ERROR: d_process failed to fork or start process %s\n", + system_command); + return 3; } - } - else { + } else { process->pipe_to_child = pipe_to_child[1]; process->pipe_from_child = pipe_from_child[0]; close(pipe_to_child[0]); close(pipe_from_child[1]); } } + return 0; } #endif @@ -291,10 +322,19 @@ static void cm_d_process_callback(ARGS, Mif_Callback_Reason_t reason) } } +static int any_failed(Process_t *p) +{ + if (p->start_failed || p->header_failed || p->exchange_failed) { + return 1; + } else { + return 0; + } +} void cm_d_process(ARGS) { int i; /* generic loop counter index */ + unsigned int errnum; Digital_State_t *reset, /* storage for reset value */ *reset_old; /* previous reset value */ @@ -315,16 +355,18 @@ void cm_d_process(ARGS) clk = clk_old = (Digital_State_t *) cm_event_get_ptr(0,0); reset = reset_old = (Digital_State_t *) cm_event_get_ptr(1,0); - STATIC_VAR(process) = malloc(sizeof(Process_t)); + STATIC_VAR(process) = calloc(1, sizeof(Process_t)); local_process = STATIC_VAR(process); CALLBACK = cm_d_process_callback; if (!PARAM_NULL(process_params)) { + int upper_limit; if (PARAM_SIZE(process_params) > (C_ARGV_SIZE - 2)) { - fprintf(stderr, "Error: too many process_parameters\n"); - exit(1); + upper_limit = C_ARGV_SIZE - 2; + } else { + upper_limit = PARAM_SIZE(process_params); } - for (i=0; iN_din != 0) { - fprintf(stderr, "Error: in port size mismatch\n"); - exit(1); - } + if (errnum) { + report_start_message(errnum); + local_process->start_failed = 1; + return; } + if (any_failed(local_process)) return; + errnum = sendheader(local_process, PORT_SIZE(in), PORT_SIZE(out)); + if (errnum) { + report_sendheader_message(errnum); + local_process->header_failed = 1; + return; + } + if (any_failed(local_process)) return; + for (i=0; i> 3] |= (uint8_t)(b << (i & 7)); } - dprocess_exchangedata(local_process, (ONE != *reset) ? TIME : -TIME, din, dout); + errnum = dprocess_exchangedata(local_process, (ONE != *reset) ? TIME : -TIME, din, dout); + if (errnum) { + report_exchangedata_message(errnum); + local_process->exchange_failed = 1; + return; + } for (i=0; i> 3] >> (i & 7)) & 0x01) ? ONE : ZERO; @@ -436,7 +491,7 @@ void cm_d_process(ARGS) #include #include -static void w_start(char *system_command, const char *const *argv, Process_t * process) +static unsigned int w_start(char *system_command, const char *const *argv, Process_t * process) { int pipe_to_child[2]; int pipe_from_child[2]; @@ -444,23 +499,14 @@ static void w_start(char *system_command, const char *const *argv, Process_t * p int mode = _O_BINARY; size_t syscmd_len = strlen(system_command); - if (syscmd_len == 0) { - fprintf(stderr, "Error: d_process process_file argument is not given"); - exit(1); - } - if (system_command[syscmd_len-1] == '|') { - fprintf(stderr, "Error: d_process named pipe/fifo not supported\n"); - exit(1); + if (syscmd_len == 0 || system_command[syscmd_len-1] == '|') { + return 1; } if (_pipe(pipe_to_child, 1024, mode) == -1) { - perror("pipe_to_child"); - fprintf(stderr, "Failed to open pipe_to_child\n"); - exit(1); + return 2; } if (_pipe(pipe_from_child, 1024, mode) == -1) { - perror("pipe_from_child"); - fprintf(stderr, "Failed to open pipe_from_child\n"); - exit(1); + return 2; } _dup2(pipe_to_child[0],0); @@ -471,12 +517,11 @@ static void w_start(char *system_command, const char *const *argv, Process_t * p _flushall(); pid = _spawnvp(_P_NOWAIT, system_command, argv); if (pid == -1) { - perror("spawn from d_process"); - fprintf(stderr, "Failed to spawn %s\n", system_command); - exit(1); + return 3; } process->pipe_to_child = pipe_to_child[1]; process->pipe_from_child = pipe_from_child[0]; + return 0; } #endif From 4eae039779d5f5fabb0f558d60fb1a0f6210c461 Mon Sep 17 00:00:00 2001 From: Brian Taylor Date: Fri, 13 Oct 2023 19:23:38 -0700 Subject: [PATCH 16/25] Add modifications note. --- src/xspice/icm/digital/d_process/cfunc.mod | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/xspice/icm/digital/d_process/cfunc.mod b/src/xspice/icm/digital/d_process/cfunc.mod index cc72e6d6a..91c467f19 100644 --- a/src/xspice/icm/digital/d_process/cfunc.mod +++ b/src/xspice/icm/digital/d_process/cfunc.mod @@ -80,6 +80,9 @@ MODIFICATIONS For Windows VisualC and Mingw the pipes use binary mode, and named pipes or fifos are not supported. + 13 October 2023 Brian Taylor + Avoid calling exit(1) after reporting an error to stderr. Use + cm_message_send. REFERENCED FILES From 8c72a76844100c6fcedcfd303480224014975a08 Mon Sep 17 00:00:00 2001 From: Brian Taylor Date: Sat, 14 Oct 2023 16:01:39 -0700 Subject: [PATCH 17/25] Another attempt at clean up of error handling and reporting. It's OK on limux and cygwin, but Windows VisualC is being stubborn. I would prefer not to have to call exit(1) just for Windows, since then nothing is reported and the gui disappears in a puff of smoke. Even without the exit calls nothing gets reported with the Windows gui which will hang when there are errors detected by d_process. --- src/xspice/icm/digital/d_process/cfunc.mod | 150 +++++++-------------- 1 file changed, 52 insertions(+), 98 deletions(-) diff --git a/src/xspice/icm/digital/d_process/cfunc.mod b/src/xspice/icm/digital/d_process/cfunc.mod index 91c467f19..7fe08d9ae 100644 --- a/src/xspice/icm/digital/d_process/cfunc.mod +++ b/src/xspice/icm/digital/d_process/cfunc.mod @@ -80,9 +80,9 @@ MODIFICATIONS For Windows VisualC and Mingw the pipes use binary mode, and named pipes or fifos are not supported. - 13 October 2023 Brian Taylor - Avoid calling exit(1) after reporting an error to stderr. Use - cm_message_send. + 14 October 2023 Brian Taylor + Use cm_message_send() to report errors, avoid exit(1) calls. + REFERENCED FILES @@ -99,7 +99,6 @@ REFERENCED FILES #include #include #include -#include #endif #include @@ -112,53 +111,16 @@ typedef unsigned char uint8_t; typedef struct { int pipe_to_child; int pipe_from_child; - int start_failed; - int header_failed; - int exchange_failed; uint8_t N_din, N_dout; // number of inputs/outputs bytes Digital_State_t dout_old[256]; // max possible storage to track output changes } Process_t; #if defined(_MSC_VER) || defined(__MINGW64__) #include -static unsigned int w_start(char *system_command, const char *const *argv, Process_t * process); +static int w_start(char *system_command, const char *const *argv, Process_t * process); #endif -static void report_sendheader_message(unsigned int msg_num) -{ - static char *messages[] = { - "", - "ERROR: d_process supports max 255 input and max 255 output signals", - "ERROR: d_process when sending header", - "ERROR: d_process didn't respond to the header", - "ERROR: d_process returned invalid header version", - "ERROR: d_process header I/O mismatch N_din and N_dout counts" - }; - cm_message_send(messages[msg_num]); -} - -static void report_exchangedata_message(unsigned int msg_num) -{ - static char *messages[] = { - "", - "ERROR: d_process when writing exchange data", - "ERROR: d_process received invalid dout count when reading exchange data" - }; - cm_message_send(messages[msg_num]); -} - -static void report_start_message(unsigned int msg_num) -{ - static char *messages[] = { - "", - "ERROR: d_process process_file argument is invalid or not given", - "ERROR: d_process failed to open pipe", - "ERROR: d_process failed to fork or start process" - }; - cm_message_send(messages[msg_num]); -} - -static unsigned int sendheader(Process_t * process, int N_din, int N_dout) +static int sendheader(Process_t * process, int N_din, int N_dout) { #if defined(_MSC_VER) #pragma pack(push, 1) @@ -173,6 +135,7 @@ static unsigned int sendheader(Process_t * process, int N_din, int N_dout) #endif if (N_din > 255 || N_dout > 255) { + cm_message_send("Error: d_process supports max 255 input and output and 255 output signals"); return 1; } @@ -181,7 +144,8 @@ static unsigned int sendheader(Process_t * process, int N_din, int N_dout) #else if (write(process->pipe_to_child, &header, sizeof(header)) == -1) { #endif - return 2; + cm_message_send("Error: d_process when sending header"); + return 1; } // Wait for echo which must return the same header to ack transfer @@ -190,13 +154,17 @@ static unsigned int sendheader(Process_t * process, int N_din, int N_dout) #else if (read(process->pipe_from_child, &header, sizeof(header)) != sizeof(header)) { #endif - return 3; + cm_message_send("Error: d_process didn't respond to the header"); + return 1; } if (header.version != D_PROCESS_FORMAT_VERSION) { - return 4; + cm_message_printf("Error: d_process returned invalid version: %d", header.version); + return 1; } if (header.N_din != N_din || header.N_dout != N_dout) { - return 5; + cm_message_printf("Error: d_process I/O mismatch: in %d vs. returned %d, out %d vs. returned %d", + N_din, header.N_din, N_dout, header.N_dout); + return 1; } process->N_din = (uint8_t)DLEN(N_din); @@ -205,7 +173,7 @@ static unsigned int sendheader(Process_t * process, int N_din, int N_dout) } -static unsigned int dprocess_exchangedata(Process_t * process, double time, uint8_t din[], uint8_t dout[]) +static int dprocess_exchangedata(Process_t * process, double time, uint8_t din[], uint8_t dout[]) { #if defined(_MSC_VER) #pragma pack(push, 1) @@ -236,6 +204,7 @@ static unsigned int dprocess_exchangedata(Process_t * process, double time, uint wlen = write(process->pipe_to_child, &packet, sizeof(double) + process->N_din); #endif if (wlen == -1) { + cm_message_send("Error: d_process when writing exchange data"); return 1; } @@ -244,14 +213,17 @@ static unsigned int dprocess_exchangedata(Process_t * process, double time, uint #else if (read(process->pipe_from_child, dout, process->N_dout) != process->N_dout) { #endif - return 2; + cm_message_printf( + "Error: d_process received invalid dout count, expected %d", + process->N_dout); + return 1; } return 0; } #if !defined(_MSC_VER) && !defined(__MINGW64__) -static unsigned int start(char *system_command, char * c_argv[], Process_t * process) +static int start(char *system_command, char * c_argv[], Process_t * process) { int pipe_to_child[2]; int pipe_from_child[2]; @@ -259,6 +231,7 @@ static unsigned int start(char *system_command, char * c_argv[], Process_t * pro size_t syscmd_len = strlen(system_command); if (syscmd_len == 0) { + cm_message_send("Error: d_process process_file argument is not given"); return 1; } if (system_command[syscmd_len-1] == '|') { @@ -272,20 +245,20 @@ static unsigned int start(char *system_command, char * c_argv[], Process_t * pro strncpy(filename_out, system_command, syscmd_len-1); strcpy(&filename_out[syscmd_len-1], "_out"); if ((process->pipe_to_child = open(filename_in, O_WRONLY)) == -1) { - return 2; + cm_message_send("Error: d_process failed to open in fifo"); + return 1; } if ((process->pipe_from_child = open(filename_out, O_RDONLY)) == -1) { - return 2; + cm_message_send("Error: d_process failed to open out fifo"); + return 1; } if (filename_in) free(filename_in); if (filename_out) free(filename_out); } else { - if (pipe(pipe_to_child) || pipe(pipe_from_child)) { - return 2; - } - if ((pid=fork()) ==-1) { - return 3; + if (pipe(pipe_to_child) || pipe(pipe_from_child) || (pid=fork()) ==-1) { + cm_message_send("Error: d_process cannot create pipes and fork"); + return 1; } if (pid == 0) { dup2(pipe_to_child[0],0); @@ -295,11 +268,12 @@ static unsigned int start(char *system_command, char * c_argv[], Process_t * pro if (execv(system_command, c_argv) == -1) { fprintf(stderr, - "ERROR: d_process failed to fork or start process %s\n", + "Error: d_process failed to fork or start process %s\n", system_command); - return 3; + exit(1); } - } else { + } + else { process->pipe_to_child = pipe_to_child[1]; process->pipe_from_child = pipe_from_child[0]; close(pipe_to_child[0]); @@ -325,19 +299,10 @@ static void cm_d_process_callback(ARGS, Mif_Callback_Reason_t reason) } } -static int any_failed(Process_t *p) -{ - if (p->start_failed || p->header_failed || p->exchange_failed) { - return 1; - } else { - return 0; - } -} void cm_d_process(ARGS) { int i; /* generic loop counter index */ - unsigned int errnum; Digital_State_t *reset, /* storage for reset value */ *reset_old; /* previous reset value */ @@ -378,23 +343,11 @@ void cm_d_process(ARGS) #undef C_ARGV_SIZE #if defined(_MSC_VER) || defined(__MINGW64__) - errnum = w_start(c_argv[0], (const char *const *)c_argv, local_process); + (void) w_start(c_argv[0], (const char *const *)c_argv, local_process); #else - errnum = start(c_argv[0], c_argv, local_process); + (void) start(c_argv[0], c_argv, local_process); #endif - if (errnum) { - report_start_message(errnum); - local_process->start_failed = 1; - return; - } - if (any_failed(local_process)) return; - errnum = sendheader(local_process, PORT_SIZE(in), PORT_SIZE(out)); - if (errnum) { - report_sendheader_message(errnum); - local_process->header_failed = 1; - return; - } - if (any_failed(local_process)) return; + (void) sendheader(local_process, PORT_SIZE(in), PORT_SIZE(out)); for (i=0; i> 3] |= (uint8_t)(b << (i & 7)); } - errnum = dprocess_exchangedata(local_process, (ONE != *reset) ? TIME : -TIME, din, dout); - if (errnum) { - report_exchangedata_message(errnum); - local_process->exchange_failed = 1; - return; - } + (void) dprocess_exchangedata(local_process, (ONE != *reset) ? TIME : -TIME, din, dout); for (i=0; i> 3] >> (i & 7)) & 0x01) ? ONE : ZERO; @@ -494,7 +440,7 @@ void cm_d_process(ARGS) #include #include -static unsigned int w_start(char *system_command, const char *const *argv, Process_t * process) +static int w_start(char *system_command, const char *const *argv, Process_t * process) { int pipe_to_child[2]; int pipe_from_child[2]; @@ -502,14 +448,21 @@ static unsigned int w_start(char *system_command, const char *const *argv, Proce int mode = _O_BINARY; size_t syscmd_len = strlen(system_command); - if (syscmd_len == 0 || system_command[syscmd_len-1] == '|') { + if (syscmd_len == 0) { + cm_message_send("Error: d_process process_file argument is not given"); + return 1; + } + if (system_command[syscmd_len-1] == '|') { + cm_message_send("Error: d_process named pipe/fifo not supported"); return 1; } if (_pipe(pipe_to_child, 1024, mode) == -1) { - return 2; + cm_message_send("Error: d_process failed to open pipe_to_child"); + return 1; } if (_pipe(pipe_from_child, 1024, mode) == -1) { - return 2; + cm_message_send("Error: d_process failed to open pipe_from_child"); + return 1; } _dup2(pipe_to_child[0],0); @@ -520,7 +473,8 @@ static unsigned int w_start(char *system_command, const char *const *argv, Proce _flushall(); pid = _spawnvp(_P_NOWAIT, system_command, argv); if (pid == -1) { - return 3; + cm_message_printf("Error: d_process failed to spawn %s", system_command); + return 1; } process->pipe_to_child = pipe_to_child[1]; From 09f070f5823d8698d30830832d8cfed35ecd2ccb Mon Sep 17 00:00:00 2001 From: Brian Taylor Date: Mon, 16 Oct 2023 13:19:43 -0700 Subject: [PATCH 18/25] Error handling improvements in cfunc.mod. Ensure that d_process.h wiil always respond to version and interface checks sent from sendheader. This is needed so that the pipe reads in sendheader do not cause Windows to hang when the interface version and in/out counts do not match. This hang was the cause of errors not being reported and the Windows gui hanging. Startup and header checks are now detected in cm_d_process, and the simulator will run but with runtime errors since a d_process model cannot be completely instantiated after initial errors. It would be good to find a means of gracefully halting the simulation run. --- examples/xspice/d_process/d_process.h | 30 ++++++++-- examples/xspice/d_process/prog1-4.cir | 6 +- src/xspice/icm/digital/d_process/cfunc.mod | 68 ++++++++++++++-------- 3 files changed, 73 insertions(+), 31 deletions(-) diff --git a/examples/xspice/d_process/d_process.h b/examples/xspice/d_process/d_process.h index face10ab1..fa624de3a 100644 --- a/examples/xspice/d_process/d_process.h +++ b/examples/xspice/d_process/d_process.h @@ -4,6 +4,15 @@ * \author Uros Platise */ +/* +MODIFICATIONS + + 16 October 2023 Brian Taylor + Always send an acknowledgement to cm_d_process to + avoid the pipe read hanging waiting for a response. + +*/ + #ifndef __NGSPICE_D_PROCESS__ #define __NGSPICE_D_PROCESS__ @@ -28,6 +37,7 @@ static inline int d_process_init(int pipein, int pipeout, uint8_t N_din, uint8_t uint8_t version, N_din, N_dout; } __attribute__((packed))header; #endif + int retval = 1; #if defined(_MSC_VER) || defined(__MINGW64__) if (_read(pipein, &header, sizeof(header)) != sizeof(header)) { @@ -35,19 +45,29 @@ static inline int d_process_init(int pipein, int pipeout, uint8_t N_din, uint8_t if (read(pipein, &header, sizeof(header)) != sizeof(header)) { #endif fprintf(stderr, "Error: Incompatible ngspice d_process header size, requiring version %d\n", header.version); - return 0; + header.version = 0; + header.N_din = 0; + header.N_dout = 0; + retval = 0; + } + if (retval == 0) { + goto respond; } if (header.version != D_PROCESS_FORMAT_VERSION || header.N_din != N_din || header.N_dout != N_dout) { fprintf(stderr, "Error: Incompatible ngspice d_process requiring version %d, number of inputs %d expected %d, and outputs %d expected %d.\n", header.version, header.N_din, N_din, header.N_dout, N_dout); - return 0; + header.version = D_PROCESS_FORMAT_VERSION; + header.N_din = N_din; + header.N_dout = N_dout; + retval = 0; } +respond: #if defined(_MSC_VER) || defined(__MINGW64__) - _write(pipeout, &header, sizeof(header)); // acknowledge connection by returning back the same header + _write(pipeout, &header, sizeof(header)); // acknowledge connection by returning back the header #else - write(pipeout, &header, sizeof(header)); // acknowledge connection by returning back the same header + write(pipeout, &header, sizeof(header)); // acknowledge connection by returning back the header #endif - return 1; + return retval; } #endif diff --git a/examples/xspice/d_process/prog1-4.cir b/examples/xspice/d_process/prog1-4.cir index 032a71ba8..4d1e7d25b 100644 --- a/examples/xspice/d_process/prog1-4.cir +++ b/examples/xspice/d_process/prog1-4.cir @@ -34,7 +34,11 @@ ap1_4a [clk2] clk1 reseto [b1 b2 b3 b4] proc4 run edisplay eprvcd clk1 clk2 o1 o2 o3 o4 q1 q2 q3 q4 b1 b2 b3 b4 zeros qzeros reseto > prog1-4.vcd -shell gtkwave prog1-4.vcd --script nggtk.tcl & +if $oscompiled = 1 | $oscompiled = 8 ; MS Windows + shell start gtkwave prog1-4.vcd --script nggtk.tcl +else + shell gtkwave prog1-4.vcd --script nggtk.tcl & +end quit .endc .end diff --git a/src/xspice/icm/digital/d_process/cfunc.mod b/src/xspice/icm/digital/d_process/cfunc.mod index 7fe08d9ae..79cfc06ff 100644 --- a/src/xspice/icm/digital/d_process/cfunc.mod +++ b/src/xspice/icm/digital/d_process/cfunc.mod @@ -111,6 +111,7 @@ typedef unsigned char uint8_t; typedef struct { int pipe_to_child; int pipe_from_child; + unsigned int error_count; uint8_t N_din, N_dout; // number of inputs/outputs bytes Digital_State_t dout_old[256]; // max possible storage to track output changes } Process_t; @@ -135,7 +136,7 @@ static int sendheader(Process_t * process, int N_din, int N_dout) #endif if (N_din > 255 || N_dout > 255) { - cm_message_send("Error: d_process supports max 255 input and output and 255 output signals"); + cm_message_send("ERROR: d_process supports max 255 input and output and 255 output signals"); return 1; } @@ -144,7 +145,7 @@ static int sendheader(Process_t * process, int N_din, int N_dout) #else if (write(process->pipe_to_child, &header, sizeof(header)) == -1) { #endif - cm_message_send("Error: d_process when sending header"); + cm_message_send("ERROR: d_process when sending header"); return 1; } @@ -154,15 +155,15 @@ static int sendheader(Process_t * process, int N_din, int N_dout) #else if (read(process->pipe_from_child, &header, sizeof(header)) != sizeof(header)) { #endif - cm_message_send("Error: d_process didn't respond to the header"); + cm_message_send("ERROR: d_process didn't respond to the header"); return 1; } if (header.version != D_PROCESS_FORMAT_VERSION) { - cm_message_printf("Error: d_process returned invalid version: %d", header.version); + cm_message_printf("ERROR: d_process returned invalid version: %d", header.version); return 1; } if (header.N_din != N_din || header.N_dout != N_dout) { - cm_message_printf("Error: d_process I/O mismatch: in %d vs. returned %d, out %d vs. returned %d", + cm_message_printf("ERROR: d_process I/O mismatch: in %d vs. returned %d, out %d vs. returned %d", N_din, header.N_din, N_dout, header.N_dout); return 1; } @@ -204,7 +205,7 @@ static int dprocess_exchangedata(Process_t * process, double time, uint8_t din[] wlen = write(process->pipe_to_child, &packet, sizeof(double) + process->N_din); #endif if (wlen == -1) { - cm_message_send("Error: d_process when writing exchange data"); + cm_message_send("ERROR: d_process when writing exchange data"); return 1; } @@ -214,7 +215,7 @@ static int dprocess_exchangedata(Process_t * process, double time, uint8_t din[] if (read(process->pipe_from_child, dout, process->N_dout) != process->N_dout) { #endif cm_message_printf( - "Error: d_process received invalid dout count, expected %d", + "ERROR: d_process received invalid dout count, expected %d", process->N_dout); return 1; } @@ -228,10 +229,13 @@ static int start(char *system_command, char * c_argv[], Process_t * process) int pipe_to_child[2]; int pipe_from_child[2]; int pid; - size_t syscmd_len = strlen(system_command); + size_t syscmd_len = 0; + if (system_command) { + syscmd_len = strlen(system_command); + } - if (syscmd_len == 0) { - cm_message_send("Error: d_process process_file argument is not given"); + if (!system_command || syscmd_len == 0) { + cm_message_send("ERROR: d_process process_file argument is not given"); return 1; } if (system_command[syscmd_len-1] == '|') { @@ -245,11 +249,11 @@ static int start(char *system_command, char * c_argv[], Process_t * process) strncpy(filename_out, system_command, syscmd_len-1); strcpy(&filename_out[syscmd_len-1], "_out"); if ((process->pipe_to_child = open(filename_in, O_WRONLY)) == -1) { - cm_message_send("Error: d_process failed to open in fifo"); + cm_message_send("ERROR: d_process failed to open in fifo"); return 1; } if ((process->pipe_from_child = open(filename_out, O_RDONLY)) == -1) { - cm_message_send("Error: d_process failed to open out fifo"); + cm_message_send("ERROR: d_process failed to open out fifo"); return 1; } if (filename_in) free(filename_in); @@ -257,7 +261,7 @@ static int start(char *system_command, char * c_argv[], Process_t * process) } else { if (pipe(pipe_to_child) || pipe(pipe_from_child) || (pid=fork()) ==-1) { - cm_message_send("Error: d_process cannot create pipes and fork"); + cm_message_send("ERROR: d_process cannot create pipes and fork"); return 1; } if (pid == 0) { @@ -268,7 +272,7 @@ static int start(char *system_command, char * c_argv[], Process_t * process) if (execv(system_command, c_argv) == -1) { fprintf(stderr, - "Error: d_process failed to fork or start process %s\n", + "\nERROR: d_process failed to fork or start process %s\n", system_command); exit(1); } @@ -303,6 +307,7 @@ static void cm_d_process_callback(ARGS, Mif_Callback_Reason_t reason) void cm_d_process(ARGS) { int i; /* generic loop counter index */ + int err; Digital_State_t *reset, /* storage for reset value */ *reset_old; /* previous reset value */ @@ -343,11 +348,19 @@ void cm_d_process(ARGS) #undef C_ARGV_SIZE #if defined(_MSC_VER) || defined(__MINGW64__) - (void) w_start(c_argv[0], (const char *const *)c_argv, local_process); + err = w_start(c_argv[0], (const char *const *)c_argv, local_process); #else - (void) start(c_argv[0], c_argv, local_process); + err = start(c_argv[0], c_argv, local_process); #endif - (void) sendheader(local_process, PORT_SIZE(in), PORT_SIZE(out)); + if (err) { + local_process->error_count++; + return; + } + err = sendheader(local_process, PORT_SIZE(in), PORT_SIZE(out)); + if (err) { + local_process->error_count++; + return; + } for (i=0; ierror_count > 0) { + return; + } clk = (Digital_State_t *) cm_event_get_ptr(0,0); clk_old = (Digital_State_t *) cm_event_get_ptr(0,1); @@ -446,22 +461,25 @@ static int w_start(char *system_command, const char *const *argv, Process_t * pr int pipe_from_child[2]; intptr_t pid = 0; int mode = _O_BINARY; - size_t syscmd_len = strlen(system_command); + size_t syscmd_len = 0; + if (system_command) { + syscmd_len = strlen(system_command); + } - if (syscmd_len == 0) { - cm_message_send("Error: d_process process_file argument is not given"); + if (!system_command || syscmd_len == 0) { + cm_message_send("ERROR: d_process process_file argument is not given"); return 1; } if (system_command[syscmd_len-1] == '|') { - cm_message_send("Error: d_process named pipe/fifo not supported"); + cm_message_send("ERROR: d_process named pipe/fifo not supported"); return 1; } if (_pipe(pipe_to_child, 1024, mode) == -1) { - cm_message_send("Error: d_process failed to open pipe_to_child"); + cm_message_send("ERROR: d_process failed to open pipe_to_child"); return 1; } if (_pipe(pipe_from_child, 1024, mode) == -1) { - cm_message_send("Error: d_process failed to open pipe_from_child"); + cm_message_send("ERROR: d_process failed to open pipe_from_child"); return 1; } @@ -473,7 +491,7 @@ static int w_start(char *system_command, const char *const *argv, Process_t * pr _flushall(); pid = _spawnvp(_P_NOWAIT, system_command, argv); if (pid == -1) { - cm_message_printf("Error: d_process failed to spawn %s", system_command); + cm_message_printf("ERROR: d_process failed to spawn %s", system_command); return 1; } From 21761207bc3f91ca39d0ecb7ed02500f1bc89eea Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 28 Oct 2023 14:55:13 +0200 Subject: [PATCH 19/25] Change license to 3-clause BSD (see https://sourceforge.net/p/ngspice/ngspice/merge-requests/6/?limit=25#fd4f/73e6) --- src/xspice/icm/digital/d_process/cfunc.mod | 32 ++++++++++++++-------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/xspice/icm/digital/d_process/cfunc.mod b/src/xspice/icm/digital/d_process/cfunc.mod index 79cfc06ff..8dca85dce 100644 --- a/src/xspice/icm/digital/d_process/cfunc.mod +++ b/src/xspice/icm/digital/d_process/cfunc.mod @@ -6,19 +6,29 @@ FILE d_process/cfunc.mod Copyright 2017-2018 Isotel d.o.o. http://www.isotel.eu PROJECT http://isotel.eu/mixedsim - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. +License: 3-clause BSD - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. AUTHORS From c83d11c9c0e5b5796d6fd86ff4bf0bab612c80df Mon Sep 17 00:00:00 2001 From: Brian Taylor Date: Thu, 19 Oct 2023 08:31:37 -0700 Subject: [PATCH 20/25] Call cm_cexit() to prevent the simulator running when d_process initialization errors occur. --- src/xspice/icm/digital/d_process/cfunc.mod | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/xspice/icm/digital/d_process/cfunc.mod b/src/xspice/icm/digital/d_process/cfunc.mod index 8dca85dce..5b1aae287 100644 --- a/src/xspice/icm/digital/d_process/cfunc.mod +++ b/src/xspice/icm/digital/d_process/cfunc.mod @@ -93,6 +93,10 @@ MODIFICATIONS 14 October 2023 Brian Taylor Use cm_message_send() to report errors, avoid exit(1) calls. + 18 October 2023 Brian Taylor + Use cm_cexit() to halt simulation after fatal errors. + Cleanup (terminate) Windows child processes. + REFERENCED FILES @@ -385,6 +389,7 @@ void cm_d_process(ARGS) else { local_process = STATIC_VAR(process); if (local_process->error_count > 0) { + cm_cexit(1); return; } clk = (Digital_State_t *) cm_event_get_ptr(0,0); From 1f5f7ae439c105a103db3b1a2660cff450cf4e61 Mon Sep 17 00:00:00 2001 From: Brian Taylor Date: Fri, 20 Oct 2023 11:38:21 -0700 Subject: [PATCH 21/25] Update d_process examples. --- examples/xspice/d_process/README | 13 +++++-- examples/xspice/d_process/checks.cir | 44 ++++++++++++++++++++++++ examples/xspice/d_process/debugging.h | 40 +++++++++++++++++++++ examples/xspice/d_process/graycode.c | 32 ++--------------- examples/xspice/d_process/prog-pipes.cir | 5 --- examples/xspice/d_process/prog1-4.cir | 11 +++--- examples/xspice/d_process/prog1in4out.c | 35 ++----------------- examples/xspice/d_process/prog4in1out.c | 33 ++---------------- 8 files changed, 107 insertions(+), 106 deletions(-) create mode 100644 examples/xspice/d_process/checks.cir create mode 100644 examples/xspice/d_process/debugging.h diff --git a/examples/xspice/d_process/README b/examples/xspice/d_process/README index 9031425c6..4405737c4 100644 --- a/examples/xspice/d_process/README +++ b/examples/xspice/d_process/README @@ -34,7 +34,11 @@ before ngspice prog-pipes.cir, and in another shell: needs to be started. -NOTE on debugging. On Linux or Cygwin on Windows, gdb can be attached to +The file checks.cir can be run to test the error handling in d_process. + +============================================================================== + +NOTE ON DEBUGGING. On Linux or Cygwin on Windows, gdb can be attached to the running d_process programs, or in --pipe mode with fifos, the graycode example can be run when invoked from gdb. @@ -42,13 +46,16 @@ From a Windows Powershell, WinDbg can be attached to a running d_process program. To enable the debugging hooks in the programs (graycode.c, prog1in4out.c, -prog4in1out.c), edit them to #define ENABLE_DEBUGGING. +prog4in1out.c), edit them to #define ENABLE_DEBUGGING. The debugging +hooks are in the debugging.h include file. Each program prints (to stderr) its process id when started. This makes it easier to know the process when attaching a debugger. All the programs (graycode.c, prog1in4out.c, prog4in1out.c) contain a call -to sleep to give you time to attach a debugger. This is enabled by: +to debug_info() which calls sleep() to give you time to attach a debugger. +Just after the sleep() call, there is a call to known_bp() on which you can +set a breakpoint. The sleep() is enabled by: export GO_TO_SLEEP=1 (on Linux, Cygwin) $env:go_to_sleep = '1' (on Windows Powershell) diff --git a/examples/xspice/d_process/checks.cir b/examples/xspice/d_process/checks.cir new file mode 100644 index 000000000..5953d8ab8 --- /dev/null +++ b/examples/xspice/d_process/checks.cir @@ -0,0 +1,44 @@ +checks.cir test error handling. + +*** analysis type *** +.tran .01us 10us +v1 1 0 DC 1.0 +v2 2 0 DC 0.0 + +.model d_osc1 d_osc (cntl_array=[-1.0 0.0 1.0 2.0] ++ freq_array=[1.0e6 1.0e6 4.0e6 4.0e6] ++ rise_delay=1.0e-6 fall_delay=2.0e-6) + +a1 1 clk1 d_osc1 +a2 2 clk2 d_osc1 + +** Too few outputs, 4 required +ap0 null clk1 null [q2 q3 q4] proc0 +.model proc0 d_process (process_file="graycode" process_params=["none"]) + +ap1 [clk2] clk1 null [o1 o2 o3 o4] proc1 +.model proc1 d_process (process_file="prog1in4out" process_params=["opt1", "qwerty"]) + +ap2 [o1 o2 o3 o4] clk1 null [zeros] proc2 +** Non existent process_file or missing fifos +** Unsupported fifo on Windows VisualC +.model proc2 d_process (process_file="badprog4in1out|" process_params=["abc", "99"]) + +ap3 [q1 q2 q3 q4] clk1 null [qzeros] proc3 +** Non existent process_file +.model proc3 d_process (process_file="prog4in1outxxx") + +an1 [o1 ~o2 o3] reseto dand1 +.model dand1 d_and(inertial_delay=true rise_delay=1ns fall_delay=50ns) + +ap4 [clk2] clk1 reseto [b1 b2 b3 b4] proc4 +** Empty process_file name +.model proc4 d_process (process_file="") + +.control +run +edisplay +quit +.endc +.end + diff --git a/examples/xspice/d_process/debugging.h b/examples/xspice/d_process/debugging.h new file mode 100644 index 000000000..44c3aa18b --- /dev/null +++ b/examples/xspice/d_process/debugging.h @@ -0,0 +1,40 @@ +#ifndef INCLUDED_DEBUGGING_H +#define INCLUDED_DEBUGGING_H + +static int known_bp(int iargc) +{ + return iargc; +} + +void debug_info(int argc, char **argv) +{ +#if defined(_MSC_VER) || defined(__MINGW64__) + fprintf(stderr, "%s pid %d\n", argv[0], _getpid()); +#else + fprintf(stderr, "%s pid %d\n", argv[0], getpid()); +#endif + +#if !defined(_MSC_VER) && !defined(__MINGW64__) + if (getenv("GO_TO_SLEEP")) { + sleep(40); + } +#endif +#if defined(__MINGW64__) + if (getenv("GO_TO_SLEEP")) { + sleep(40); + } +#endif +#if defined(_MSC_VER) + if (getenv("GO_TO_SLEEP")) { + Sleep(60000); + } +#endif + + (void)known_bp(argc); + + for (int i=0; i prog if $oscompiled = 1 | $oscompiled = 8 ; MS Windows shell start gtkwave prog1-4.vcd --script nggtk.tcl else - shell gtkwave prog1-4.vcd --script nggtk.tcl & + if $oscompiled = 7 ; macOS, manual tweaking required (mark, insert, Zoom Fit) + shell open -a gtkwave prog1-4.vcd + else ; Linux and others + shell gtkwave prog1-4.vcd --script nggtk.tcl & + end end quit .endc .end - - - - - diff --git a/examples/xspice/d_process/prog1in4out.c b/examples/xspice/d_process/prog1in4out.c index bb384cc6c..50c49d5aa 100644 --- a/examples/xspice/d_process/prog1in4out.c +++ b/examples/xspice/d_process/prog1in4out.c @@ -24,10 +24,7 @@ static int compute( //#define ENABLE_DEBUGGING 1 #ifdef ENABLE_DEBUGGING -static int known_bp(int iargc) -{ - return iargc; -} +#include "debugging.h" #endif int main(int argc, char *argv[]) { @@ -64,35 +61,9 @@ int main(int argc, char *argv[]) { #endif #ifdef ENABLE_DEBUGGING -#if defined(_MSC_VER) || defined(__MINGW64__) - fprintf(stderr, "%s pid %d\n", argv[0], _getpid()); -#else - fprintf(stderr, "%s pid %d\n", argv[0], getpid()); + debug_info(argc, argv); #endif -#if !defined(_MSC_VER) && !defined(__MINGW64__) - if (getenv("GO_TO_SLEEP")) { - sleep(40); - } -#endif -#if defined(__MINGW64__) - if (getenv("GO_TO_SLEEP")) { - sleep(40); - } -#endif -#if defined(_MSC_VER) - if (getenv("GO_TO_SLEEP")) { - Sleep(60000); - } -#endif - - (void)known_bp(argc); - - for (int i=0; i Date: Sun, 22 Oct 2023 20:47:59 -0700 Subject: [PATCH 22/25] Fix the zero count. --- examples/xspice/d_process/prog1-4.cir | 10 +++++----- examples/xspice/d_process/prog4in1out.c | 20 +++++++++++--------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/examples/xspice/d_process/prog1-4.cir b/examples/xspice/d_process/prog1-4.cir index 1d5f87bea..7f3a8ca91 100644 --- a/examples/xspice/d_process/prog1-4.cir +++ b/examples/xspice/d_process/prog1-4.cir @@ -12,22 +12,22 @@ v2 2 0 DC 0.0 a1 1 clk1 d_osc1 a2 2 clk2 d_osc1 -ap0_4 null clk1 null [q1 q2 q3 q4] proc0 +ap0 null clk1 null [q1 q2 q3 q4] proc0 .model proc0 d_process (process_file="graycode" process_params=["none"]) -ap1_4 [clk2] clk1 null [o1 o2 o3 o4] proc1 +ap1 [clk2] clk1 null [o1 o2 o3 o4] proc1 .model proc1 d_process (process_file="prog1in4out" process_params=["opt1", "qwerty"]) -ap4_1 [o1 o2 o3 o4] clk1 null [zeros] proc2 +ap2 [o1 o2 o3 o4] clk1 null [zeros] proc2 .model proc2 d_process (process_file="prog4in1out" process_params=["abc", "99"]) -ap4_1a [q1 q2 q3 q4] clk1 null [qzeros] proc3 +ap3 [q1 q2 q3 q4] clk1 null [qzeros] proc3 .model proc3 d_process (process_file="prog4in1out") an1 [o1 ~o2 o3] reseto dand1 .model dand1 d_and(inertial_delay=true rise_delay=1ns fall_delay=50ns) -ap1_4a [clk2] clk1 reseto [b1 b2 b3 b4] proc4 +ap4 [clk2] clk1 reseto [b1 b2 b3 b4] proc4 .model proc4 d_process (process_file="prog1in4out") .control diff --git a/examples/xspice/d_process/prog4in1out.c b/examples/xspice/d_process/prog4in1out.c index dc0fc993c..28ebdd6c4 100644 --- a/examples/xspice/d_process/prog4in1out.c +++ b/examples/xspice/d_process/prog4in1out.c @@ -91,21 +91,23 @@ static int compute( ) { uint8_t i0 = 0, zeros = 0; - uint8_t inbyte = datain[0]; + uint8_t inbyte = datain[0] & 0x0F; dataout[0] = 0; - i0 = inbyte ^ 0x01; - if (i0) zeros++; - i0 = inbyte ^ 0x02; - if (i0) zeros++; - i0 = inbyte ^ 0x04; - if (i0) zeros++; - i0 = inbyte ^ 0x08; - if (i0) zeros++; + i0 = inbyte & 0x01; + if (i0 == 0) zeros++; + i0 = inbyte & 0x02; + if (i0 == 0) zeros++; + i0 = inbyte & 0x04; + if (i0 == 0) zeros++; + i0 = inbyte & 0x08; + if (i0 == 0) zeros++; if (zeros == 2 || zeros == 4) { dataout[0] = 0x01; } else { dataout[0] = 0x00; } + fprintf(stderr, "datain %X zeros %d dataout %X time %g\n", + datain[0], zeros, dataout[0], time); return 1; } From 4219efb5d5f87f8a5a4b8223a298ec4701f81294 Mon Sep 17 00:00:00 2001 From: Brian Taylor Date: Mon, 23 Oct 2023 10:36:27 -0700 Subject: [PATCH 23/25] Make it explicit that null output ports are not allowed. The output port vector bounds are always >= 1. --- src/xspice/icm/digital/d_process/ifspec.ifs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xspice/icm/digital/d_process/ifspec.ifs b/src/xspice/icm/digital/d_process/ifspec.ifs index d1502a8d7..07da5123c 100644 --- a/src/xspice/icm/digital/d_process/ifspec.ifs +++ b/src/xspice/icm/digital/d_process/ifspec.ifs @@ -47,7 +47,7 @@ Default_Type: d d Allowed_Types: [d] [d] Vector: no yes Vector_Bounds: - [1 -] -Null_Allowed: yes yes +Null_Allowed: yes no From 864ef7925cc82e996caf177d5fe538bbd8a19e08 Mon Sep 17 00:00:00 2001 From: Brian Taylor Date: Mon, 23 Oct 2023 17:15:46 -0700 Subject: [PATCH 24/25] Add notes on the structure and organization of an external d_process program. --- examples/xspice/d_process/README | 47 ++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/examples/xspice/d_process/README b/examples/xspice/d_process/README index 4405737c4..5166e0920 100644 --- a/examples/xspice/d_process/README +++ b/examples/xspice/d_process/README @@ -1,6 +1,6 @@ -The d_process Xspice model was created by Uros Platise. -A complete, non-trivial example is located at: +The d_process XSPICE model was developed by Uros Platise and he has +provided a non-trivial example and detailed descriptions at: https://www.isotel.eu/mixedsim/embedded/motorforce/index.html This directory contains a simple test of the d_process model. @@ -18,6 +18,12 @@ Run the test case (comment out the gtkwave lines or use plot if you like): ngspice prog1-4.cir +A typical instance/.model pair Looks like: + + ap1 [clk2] clk1 null [o1 o2 o3 o4] proc1 + .model proc1 d_process (process_file="prog1in4out" + + process_params=["opt1", "qwerty"]) + To clean up: ./clean.sh @@ -38,6 +44,43 @@ The file checks.cir can be run to test the error handling in d_process. ============================================================================== +NOTES ON THE PROGRAM STRUCTURE. + +In the d_process .model statement the process_file parameter specifies +the name of the external program which is started by fork/exec or spawn, +and connections are established using pipes. The external program is written +in C, and first of all, in main() the argc, argv parameters can be read. +These command line parameters are those specified in the process_params field +of the d_process .model statement. + +Notice that the input pipe defaults to stdin and the output pipe defaults +to stdout. On Windows VisualC and Mingw the _setmode(.., _O_BINARY) function +must be called to ensure binary data transfer on the pipes. The fifo pipes +are not used on those Windows builds. Since stdin and stdout are generally +used, if you need to write any messages, use stderr or write to some other +file. + +A header is sent from ngspice to the external program which acknowledges +that the number of inputs and outputs match the instance statement in the +SPICE circuit file. Header d_process.h defines the call to d_process_init() +for the header response. + +Thereafter, the external program executes a loop: + while (read data from the input pipe and if it is OK) { + compute output data for that input + write the output data to the output pipe + } +Data is passed in packets of struct in_s {..} and struct out_s {..}. The +input struct contains the execution time in ngspice, and if it is negative, +it signifies a reset has occurred. The compute(..) function can decide how +to handle the reset. + +In the meantime the cm_d_process code in ngspice is writing data to its +output pipe at each clock change to ONE, then reading on its input pipe +the response from the external program. + +============================================================================== + NOTE ON DEBUGGING. On Linux or Cygwin on Windows, gdb can be attached to the running d_process programs, or in --pipe mode with fifos, the graycode example can be run when invoked from gdb. From 04c68d5f30f5fd7bbf64e7431ad6e8c2adcb64ce Mon Sep 17 00:00:00 2001 From: Brian Taylor Date: Wed, 18 Oct 2023 14:48:16 -0700 Subject: [PATCH 25/25] Cleanup child processes on Windows VisualC and Mingw. On Linux those processes quit when ngspice quits. --- src/xspice/icm/digital/d_process/cfunc.mod | 34 +++++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/xspice/icm/digital/d_process/cfunc.mod b/src/xspice/icm/digital/d_process/cfunc.mod index 5b1aae287..22ae17cae 100644 --- a/src/xspice/icm/digital/d_process/cfunc.mod +++ b/src/xspice/icm/digital/d_process/cfunc.mod @@ -126,6 +126,7 @@ typedef struct { int pipe_to_child; int pipe_from_child; unsigned int error_count; + int pid_of_child; uint8_t N_din, N_dout; // number of inputs/outputs bytes Digital_State_t dout_old[256]; // max possible storage to track output changes } Process_t; @@ -133,6 +134,7 @@ typedef struct { #if defined(_MSC_VER) || defined(__MINGW64__) #include static int w_start(char *system_command, const char *const *argv, Process_t * process); +static void w_cleanup_child_process(Process_t *process); #endif static int sendheader(Process_t * process, int N_din, int N_dout) @@ -292,6 +294,7 @@ static int start(char *system_command, char * c_argv[], Process_t * process) } } else { + process->pid_of_child = pid; process->pipe_to_child = pipe_to_child[1]; process->pipe_from_child = pipe_from_child[0]; close(pipe_to_child[0]); @@ -302,13 +305,15 @@ static int start(char *system_command, char * c_argv[], Process_t * process) } #endif - static void cm_d_process_callback(ARGS, Mif_Callback_Reason_t reason) { switch (reason) { case MIF_CB_DESTROY: { Process_t *proc = STATIC_VAR(process); if (proc) { +#if defined(_MSC_VER) || defined(__MINGW64__) + w_cleanup_child_process(proc); +#endif free(proc); STATIC_VAR(process) = NULL; } @@ -467,6 +472,9 @@ void cm_d_process(ARGS) #if defined(_MSC_VER) || defined(__MINGW64__) +#undef BYTE +#undef BOOLEAN +#include #include #include @@ -474,7 +482,8 @@ static int w_start(char *system_command, const char *const *argv, Process_t * pr { int pipe_to_child[2]; int pipe_from_child[2]; - intptr_t pid = 0; + int pid; + intptr_t sp_result; int mode = _O_BINARY; size_t syscmd_len = 0; if (system_command) { @@ -504,14 +513,31 @@ static int w_start(char *system_command, const char *const *argv, Process_t * pr _close(pipe_from_child[1]); _flushall(); - pid = _spawnvp(_P_NOWAIT, system_command, argv); - if (pid == -1) { + sp_result = _spawnvp(_P_NOWAIT, system_command, argv); + if (sp_result == -1) { cm_message_printf("ERROR: d_process failed to spawn %s", system_command); return 1; } + pid = GetProcessId((HANDLE)sp_result); + process->pid_of_child = pid; + cm_message_printf("Note: Process %s has pid %u", system_command, pid); process->pipe_to_child = pipe_to_child[1]; process->pipe_from_child = pipe_from_child[0]; return 0; } + +static void w_cleanup_child_process(Process_t *process) +{ + HANDLE phand; + if (process && process->pid_of_child) { + phand = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process->pid_of_child); + if (phand != NULL) { + (void) TerminateProcess(phand, 0); + CloseHandle(phand); + } + process->pid_of_child = 0; + } +} + #endif