You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
466 lines
14 KiB
466 lines
14 KiB
/*.......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 <uros@isotel.eu>
|
|
|
|
|
|
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 <uros@isotel.eu>
|
|
- Initial design, ready for use with projects
|
|
|
|
4 April 2018 Uros Platse <uros@isotel.eu>
|
|
- Tested and polished ready to be published
|
|
|
|
7 April 2018 Uros Platse <uros@isotel.eu>
|
|
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.
|
|
|
|
==============================================================================*/
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#if !defined(_MSC_VER)
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#endif
|
|
#include <fcntl.h>
|
|
|
|
#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) || defined(__MINGW64__)
|
|
#include <io.h>
|
|
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) || defined(__MINGW64__)
|
|
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) || 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)) {
|
|
#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) || 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);
|
|
#endif
|
|
if (wlen == -1) {
|
|
fprintf(stderr, "Error: d_process when writing exchange data\n");
|
|
exit(1);
|
|
}
|
|
|
|
#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);
|
|
#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) && !defined(__MINGW64__)
|
|
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_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);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
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
|
|
|
|
|
|
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 */
|
|
|
|
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);
|
|
CALLBACK = cm_d_process_callback;
|
|
|
|
if (!PARAM_NULL(process_params)) {
|
|
for (i=0; i<PARAM_SIZE(process_params); i++) {
|
|
c_argv[c_argc++] = PARAM(process_params[i]);
|
|
}
|
|
}
|
|
c_argv[0] = PARAM(process_file);
|
|
c_argv[c_argc] = NULL;
|
|
|
|
#if defined(_MSC_VER) || defined(__MINGW64__)
|
|
w_start(c_argv[0], c_argv, local_process);
|
|
#else
|
|
start(c_argv[0], c_argv, local_process);
|
|
#endif
|
|
sendheader(local_process, PORT_SIZE(in), PORT_SIZE(out));
|
|
|
|
for (i=0; i<PORT_SIZE(in); i++) {
|
|
LOAD(in[i]) = PARAM(input_load);
|
|
}
|
|
|
|
LOAD(clk) = PARAM(clk_load);
|
|
|
|
if ( !PORT_NULL(reset) ) {
|
|
LOAD(reset) = PARAM(reset_load);
|
|
}
|
|
}
|
|
else {
|
|
local_process = STATIC_VAR(process);
|
|
|
|
clk = (Digital_State_t *) cm_event_get_ptr(0,0);
|
|
clk_old = (Digital_State_t *) cm_event_get_ptr(0,1);
|
|
|
|
reset = (Digital_State_t *) cm_event_get_ptr(1,0);
|
|
reset_old = (Digital_State_t *) cm_event_get_ptr(1,1);
|
|
}
|
|
|
|
|
|
if ( 0.0 == TIME ) { /****** DC analysis...output w/o delays ******/
|
|
for (i=0; i<PORT_SIZE(out); i++) {
|
|
local_process->dout_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 = (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<PORT_SIZE(in); i++) {
|
|
switch(INPUT_STATE(in[i])) {
|
|
case ZERO: b = 0; break;
|
|
case ONE: b = 1; break;
|
|
#if defined(_MSC_VER) || defined(__MINGW64__)
|
|
default: b = rand() & 1; break;
|
|
#else
|
|
default: b = random() & 1; break;
|
|
#endif
|
|
}
|
|
din[i >> 3] |= (uint8_t)(b << (i & 7));
|
|
}
|
|
|
|
dprocess_exchangedata(local_process, (ONE != *reset) ? TIME : -TIME, din, dout);
|
|
|
|
for (i=0; i<PORT_SIZE(out); i++) {
|
|
Digital_State_t new_state = ((dout[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<PORT_SIZE(out); i++) {
|
|
OUTPUT_CHANGED(out[i]) = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(_MSC_VER) || defined(__MINGW64__)
|
|
|
|
#include <process.h>
|
|
#include <io.h>
|
|
|
|
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;
|
|
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 (_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
|