From 8b32c743aef6e5dd8812e49ec7b6e7d08c166968 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 22 May 2026 17:32:46 +0200 Subject: [PATCH] Update to the SEEgenerator as describes in README.SEEgenerator --- README.SEEgenerator | 36 ++++ src/xspice/icm/xtradev/seegenerator/cfunc.mod | 202 +++++++++++++----- .../icm/xtradev/seegenerator/ifspec.ifs | 19 ++ 3 files changed, 199 insertions(+), 58 deletions(-) diff --git a/README.SEEgenerator b/README.SEEgenerator index ae2753945..74606b3b6 100644 --- a/README.SEEgenerator +++ b/README.SEEgenerator @@ -39,6 +39,23 @@ As literature please see for example Single-Event Effects, from Space to Accelerator Environments Springer 2025 +There is an alternative pulse variant possible by setting the parameters scdelay and scaling. +In advanced process nodes a single particle hit with its electron/hole pairs generated may +influence not only a single node, but several neighboring nodes as well, may be a little less +and with a small delay. This behaviour may be emulated by connecting SEEgen to a central node +and some surrounding nodes. These will get a portion of the charge carriers, with a possible +(small) delay. The .model line + +.model seemod1 seegen (tdelay = 400p tperiod=500p tfall='tfall' trise='trise' let='let' cdepth='d' ++ perlim=TRUE ctrlthres=0 scaling=[1, 0.5, 0.25, 0.125] scdelay=[0, 10p, 20p, 30p]) + +now contain the arrays scaling and scdelay. The nummber of array elements has to equal the number +of ports. Scaling will distribute the LET onto the different nodes with each weight given. +Scdelay will delay each port relative to the parameter tdelay. Parameters tperiod, perlim, and +ctrlthres are ignored for now. As the netlist does not contain information on the location of the +nodes in the layout, the user has to provide these data be selecting the proper nodes and pulse +scaling. + Detailed description (will be added to the manual): NAME_TABLE: @@ -131,6 +148,25 @@ Vector: no no Vector_Bounds: - - Null_Allowed: yes yes + +PARAMETER_TABLE: + +Parameter_Name: scaling scdelay +Description: "scale the current" "delay scaled pulses individually" +Data_Type: real real +Default_Value: 1 0 +Limits: [0 1] [0, -] +Vector: yes yes +Vector_Bounds: - - +Null_Allowed: yes yes + +STATIC_VAR_TABLE: + +Static_Var_Name: pulses +Data_Type: pointer +Vector: no +Description: "info on pulse for each port" + STATIC_VAR_TABLE: Static_Var_Name: last_t_value diff --git a/src/xspice/icm/xtradev/seegenerator/cfunc.mod b/src/xspice/icm/xtradev/seegenerator/cfunc.mod index fb088b895..6b4f0ad8e 100644 --- a/src/xspice/icm/xtradev/seegenerator/cfunc.mod +++ b/src/xspice/icm/xtradev/seegenerator/cfunc.mod @@ -55,7 +55,17 @@ NON-STANDARD FEATURES -/*=== LOCAL VARIABLES & TYPEDEFS =======*/ +/*=== LOCAL VARIABLES & TYPEDEFS =======*/ + +typedef struct pulse_info +{ + double iscaled; /* scaled current pulse for this port */ + double start_time; /* pulse start time for this port */ + double next_start_time; /* next pulse start time for this port */ +} pulse_info_t; + + +/*=== FUNCTION PROTOTYPE DEFINITIONS ===*/ static void cm_seegen_callback(ARGS, Mif_Callback_Reason_t reason) @@ -74,18 +84,14 @@ cm_seegen_callback(ARGS, Mif_Callback_Reason_t reason) if (last_ctrl) free(last_ctrl); STATIC_VAR (last_ctrl) = NULL; + pulse_info_t *pulses = STATIC_VAR (pulses); + if (pulses) + free(pulses); break; } } } - - -/*=== FUNCTION PROTOTYPE DEFINITIONS ===*/ - - - - /*============================================================================== FUNCTION void cm_seegen() @@ -144,6 +150,10 @@ void cm_seegen(ARGS) /* structure holding parms, double *last_ctrl; /* static storage of last ctrl value */ double tcurr = TIME; /* current simulation time */ + int ports; /* number of output ports */ + pulse_info_t *allpulses; /* info for pulse on each port */ + bool have_scaled = FALSE;/* TRUE if we want to use scaled pulses */ + if (ANALYSIS == MIF_AC) { return; } @@ -160,6 +170,10 @@ void cm_seegen(ARGS) /* structure holding parms, angle = PARAM(angle); ctrlthres = PARAM(ctrlthres); + have_scaled = !PARAM_NULL(scaling) && !PARAM_NULL(scdelay); + + ports = PORT_SIZE(out); + if (PORT_NULL(ctrl)) ctrl = 1; else @@ -167,73 +181,145 @@ void cm_seegen(ARGS) /* structure holding parms, if (INIT==1) { + int i; + double sum = 0; + + if (have_scaled && PARAM_SIZE(scaling) != ports) { + cm_message_send("Error: Number of Output ports and Scaling don't match\n"); + cm_cexit(1); + } + + if (have_scaled && PARAM_SIZE(scdelay) != ports) { + cm_message_send("Error: Number of Output ports and SCdelay don't match\n"); + cm_cexit(1); + } + + if (5 * (trise + tfall) > tperiod) { + cm_message_send("\nError: tperiod should be at least 5 times the sum of trise and tfall\n"); + cm_cexit(1); + } + CALLBACK = cm_seegen_callback; - /* Allocate storage for last_t_value */ - STATIC_VAR(last_t_value) = (double *) malloc(sizeof(double)); - last_t_value = (double *) STATIC_VAR(last_t_value); - /* no start if ctrl is set */ - if (PORT_NULL(ctrl)) - *last_t_value = tdelay; - else - *last_t_value = 1e12; - STATIC_VAR(pulse_number) = (int *) malloc(sizeof(int)); - pulse_number = (int *) STATIC_VAR(pulse_number); - *pulse_number = 1; - STATIC_VAR(last_ctrl) = (double *) malloc(sizeof(double)); - last_ctrl = (double *) STATIC_VAR(last_ctrl); - *last_ctrl = ctrl; - /* set breakpoints at first pulse start and pulse maximum times */ - double tatmax = *last_t_value + tfall * trise * log(trise/tfall) / (trise - tfall); - cm_analog_set_perm_bkpt(*last_t_value); - cm_analog_set_perm_bkpt(tatmax); - } - else { + if (have_scaled) { + int j; + double del = 1e12; - last_t_value = (double *) STATIC_VAR(last_t_value); - pulse_number = (int *) STATIC_VAR(pulse_number); - last_ctrl = (double *) STATIC_VAR(last_ctrl); + cm_message_send("Use the scaling option\n"); - /* reset the pulse sequence, to start anew upon a rising ctrl */ - if (*last_ctrl < ctrlthres && ctrl >= ctrlthres) { - *last_t_value = tcurr + tdelay; - *pulse_number = 1; - } - *last_ctrl = ctrl; + allpulses = STATIC_VAR(pulses) = (pulse_info_t *) malloc(ports * sizeof(pulse_info_t)); - /* the double exponential current pulse function */ - if (tcurr < *last_t_value) - out = 0; - else { + /* parameter inull not specified, calculate it */ if (inull == 0) { double LETeff = let/cos(angle); double Qc = 1.035e-14 * LETeff * cdepth; inull = Qc / (tfall - trise); } - out = inull * (exp(-(tcurr-*last_t_value)/tfall) - exp(-(tcurr-*last_t_value)/trise)); + + /* pulse currents are scaled, and find minimum time delay */ + for (i = 0; i < ports; i++){ + sum += PARAM(scaling[i]); + } + if (sum == 0.) { + cm_message_send("Error: Scaling parameters are zero\n"); + cm_cexit(1); + } + + for (i = 0; i < ports; i++){ + allpulses[i].iscaled = PARAM(scaling[i]) / sum * inull; + allpulses[i].start_time = tdelay + PARAM(scdelay[i]); + double tatmax = allpulses[i].start_time + tfall * trise * log(trise/tfall) / (trise - tfall); + cm_analog_set_perm_bkpt(allpulses[i].start_time); + cm_analog_set_perm_bkpt(tatmax); + } } - if (tcurr > *last_t_value + tperiod * 0.9) { - /* return some info */ - cm_message_printf("port name: out, node pair no.: %d, \nnode names: %s, %s, pulse time: %e", - *pulse_number, cm_get_node_name("out", *pulse_number - 1), - cm_get_neg_node_name("out", *pulse_number - 1), *last_t_value); - /* set the time for the next pulse */ - *last_t_value = *last_t_value + tperiod; - /* set breakpoints at new pulse start and pulse maximum times */ + else { + /* Allocate storage for last_t_value */ + STATIC_VAR(last_t_value) = (double *) malloc(sizeof(double)); + last_t_value = (double *) STATIC_VAR(last_t_value); + /* no start if ctrl is set */ + if (PORT_NULL(ctrl)) + *last_t_value = tdelay; + else + *last_t_value = 1e12; + STATIC_VAR(last_ctrl) = (double *) malloc(sizeof(double)); + last_ctrl = (double *) STATIC_VAR(last_ctrl); + *last_ctrl = ctrl; + STATIC_VAR(pulse_number) = (int *) malloc(sizeof(int)); + pulse_number = (int *) STATIC_VAR(pulse_number); + *pulse_number = 1; + + /* set breakpoints at first pulse start and pulse maximum times */ double tatmax = *last_t_value + tfall * trise * log(trise/tfall) / (trise - tfall); cm_analog_set_perm_bkpt(*last_t_value); cm_analog_set_perm_bkpt(tatmax); - (*pulse_number)++; - if (*pulse_number > PORT_SIZE(out)) { - if (PARAM(perlim) == FALSE) - *pulse_number = 1; + } + } + /* after initialization */ + else { + /* individual scaling and delay */ + + if (have_scaled) { + /* */ + int i; + allpulses = (pulse_info_t *) STATIC_VAR(pulses); + for (i = 0; i < ports; i++){ + double tst = allpulses[i].start_time; + if (tcurr < tst) + out = 0; else - *last_t_value = 1e12; /* stop any output */ + out = allpulses[i].iscaled * (exp(-(tcurr-tst)/tfall) - exp(-(tcurr-tst)/trise)); + OUTPUT(out[i]) = out; + OUTPUT(mon) = out; } } - if (*pulse_number - 1 < PORT_SIZE(out)) { - OUTPUT(out[*pulse_number - 1]) = out; - OUTPUT(mon) = out; + /* equal pulses, period, and repetition */ + else { + last_t_value = (double *) STATIC_VAR(last_t_value); + pulse_number = (int *) STATIC_VAR(pulse_number); + last_ctrl = (double *) STATIC_VAR(last_ctrl); + + /* reset the pulse sequence, to start anew upon a rising ctrl */ + if (*last_ctrl < ctrlthres && ctrl >= ctrlthres) { + *last_t_value = tcurr + tdelay; + *pulse_number = 1; + } + *last_ctrl = ctrl; + + /* the double exponential current pulse function */ + if (tcurr < *last_t_value) + out = 0; + else { + if (inull == 0) { + double LETeff = let/cos(angle); + double Qc = 1.035e-14 * LETeff * cdepth; + inull = Qc / (tfall - trise); + } + out = inull * (exp(-(tcurr-*last_t_value)/tfall) - exp(-(tcurr-*last_t_value)/trise)); + } + if (tcurr > *last_t_value + tperiod * 0.9) { + /* return some info */ + cm_message_printf("port name: out, node pair no.: %d, \nnode names: %s, %s, pulse time: %e", + *pulse_number, cm_get_node_name("out", *pulse_number - 1), + cm_get_neg_node_name("out", *pulse_number - 1), *last_t_value); + /* set the time for the next pulse */ + *last_t_value = *last_t_value + tperiod; + /* set breakpoints at new pulse start and pulse maximum times */ + double tatmax = *last_t_value + tfall * trise * log(trise/tfall) / (trise - tfall); + cm_analog_set_perm_bkpt(*last_t_value); + cm_analog_set_perm_bkpt(tatmax); + (*pulse_number)++; + if (*pulse_number > PORT_SIZE(out)) { + if (PARAM(perlim) == FALSE) + *pulse_number = 1; + else + *last_t_value = 1e12; /* stop any output */ + } + } + if (*pulse_number - 1 < PORT_SIZE(out)) { + OUTPUT(out[*pulse_number - 1]) = out; + OUTPUT(mon) = out; + } } } } diff --git a/src/xspice/icm/xtradev/seegenerator/ifspec.ifs b/src/xspice/icm/xtradev/seegenerator/ifspec.ifs index cee53b78f..a46ca30c1 100644 --- a/src/xspice/icm/xtradev/seegenerator/ifspec.ifs +++ b/src/xspice/icm/xtradev/seegenerator/ifspec.ifs @@ -110,6 +110,25 @@ Vector: no no Vector_Bounds: - - Null_Allowed: yes yes + +PARAMETER_TABLE: + +Parameter_Name: scaling scdelay +Description: "scale the current" "delay scaled pulses individually" +Data_Type: real real +Default_Value: 1 0 +Limits: [0 1] [0, -] +Vector: yes yes +Vector_Bounds: - - +Null_Allowed: yes yes + +STATIC_VAR_TABLE: + +Static_Var_Name: pulses +Data_Type: pointer +Vector: no +Description: "info on pulse for each port" + STATIC_VAR_TABLE: Static_Var_Name: last_t_value