Add examples/xspice/d_process.

This commit is contained in:
Brian Taylor 2023-10-11 15:03:57 -07:00
parent dee0500140
commit fcb61e5c3e
11 changed files with 624 additions and 0 deletions

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,53 @@
/**
* \file
* \brief NgSpice D_PROCESS Interface
* \author Uros Platise <uros@isotel.eu>
*/
#ifndef __NGSPICE_D_PROCESS__
#define __NGSPICE_D_PROCESS__
#include <stdio.h>
#if !defined(_MSC_VER)
#include <unistd.h>
#endif
#include <stdint.h>
#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

View File

@ -0,0 +1,131 @@
/*
The organization of this file is modelled after the motorforce example
developed by Uros Platise.
*/
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#if !defined(_MSC_VER) && !defined(__MINGW64__)
#include <unistd.h>
#endif
#if defined(_MSC_VER)
#include <io.h>
#include <process.h>
#include <Windows.h>
#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<argc; i++) {
fprintf(stderr, "[%d] %s\n", i, argv[i]);
if (strcmp(argv[i],"--pipe")==0) {
#if defined(_MSC_VER) || defined(__MINGW64__)
if ((pipein = _open("graycode_in", O_RDONLY)) < 0 || (pipeout = _open("graycode_out", O_WRONLY)) < 0)
#else
if ((pipein = open("graycode_in", O_RDONLY)) < 0 || (pipeout = open("graycode_out", O_WRONLY)) < 0)
#endif
{
fprintf(stderr, "Cannot open %s named pipes\n", argv[0]);
return -1;
}
}
}
if (d_process_init(pipein, pipeout, DIGITAL_IN, DIGITAL_OUT) ) {
#if defined(_MSC_VER) || defined(__MINGW64__)
while(_read(pipein, &in, sizeof(in)) == sizeof(in)) {
#else
while(read(pipein, &in, sizeof(in)) == sizeof(in)) {
#endif
if (!compute(out.dout, outlen, in.time)) {
return 1;
}
#if defined(_MSC_VER) || defined(__MINGW64__)
_write(pipeout, &out, sizeof(out));
#else
write(pipeout, &out, sizeof(out));
#endif
}
return 0;
}
return -1;
}
static int compute(uint8_t *dataout, int outsz, double time)
{
static uint8_t count = 0;
if (count < 15) {
count++;
} else {
count = 0;
}
dataout[0] = (count ^ (count >> 1)) & 0x0F;
return 1;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,136 @@
/*
The organization of this file is modelled after the motorforce example
developed by Uros Platise.
*/
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#if !defined(_MSC_VER) && !defined(__MINGW64__)
#include <unistd.h>
#endif
#if defined(_MSC_VER)
#include <io.h>
#include <process.h>
#include <Windows.h>
#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<argc; i++) {
fprintf(stderr, "[%d] %s\n", i, argv[i]);
}
if (d_process_init(pipein, pipeout, DIGITAL_IN, DIGITAL_OUT) ) {
#if defined(_MSC_VER) || defined(__MINGW64__)
while(_read(pipein, &in, sizeof(in)) == sizeof(in)) {
#else
while(read(pipein, &in, sizeof(in)) == sizeof(in)) {
#endif
if (!compute(in.din, inlen, out.dout, outlen, in.time)) {
return 1;
}
#if defined(_MSC_VER) || defined(__MINGW64__)
_write(pipeout, &out, sizeof(out));
#else
write(pipeout, &out, sizeof(out));
#endif
}
return 0;
}
return -1;
}
static int compute(
uint8_t *datain, int insz, uint8_t *dataout, int outsz, double time
)
{
static uint8_t next[2][16] = {
{15, 14, 0, 1, 13, 12, 2, 3, 11, 10, 4, 5, 9, 8, 6, 7},
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0}
};
uint8_t inbit0 = datain[0] & 1;
static uint8_t count = 0;
if (time < 0.0) {
fprintf(stderr, "Reset prog1in4out at time %g\n", -time);
count = 15;
}
if (count < 15) {
count++;
} else {
count = 0;
}
dataout[0] = (next[inbit0][count]) & 0x0F;
return 1;
}

View File

@ -0,0 +1,137 @@
/*
The organization of this file is modelled after the motorforce example
developed by Uros Platise.
*/
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#if !defined(_MSC_VER) && !defined(__MINGW64__)
#include <unistd.h>
#endif
#if defined(_MSC_VER)
#include <io.h>
#include <process.h>
#include <Windows.h>
#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<argc; i++) {
fprintf(stderr, "[%d] %s\n", i, argv[i]);
}
if (d_process_init(pipein, pipeout, DIGITAL_IN, DIGITAL_OUT) ) {
#if defined(_MSC_VER) || defined(__MINGW64__)
while(_read(pipein, &in, sizeof(in)) == sizeof(in)) {
#else
while(read(pipein, &in, sizeof(in)) == sizeof(in)) {
#endif
if (!compute(in.din, inlen, out.dout, outlen, in.time)) {
return 1;
}
#if defined(_MSC_VER) || defined(__MINGW64__)
_write(pipeout, &out, sizeof(out));
#else
write(pipeout, &out, sizeof(out));
#endif
}
return 0;
}
return -1;
}
static int compute(
uint8_t *datain, int insz, uint8_t *dataout, int outsz, double time
)
{
uint8_t i0 = 0, zeros = 0;
uint8_t inbyte = datain[0];
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++;
if (zeros == 2 || zeros == 4) {
dataout[0] = 0x01;
} else {
dataout[0] = 0x00;
}
return 1;
}