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 +