From 492bb64d9213fdec249580e6767f6e8b1f693281 Mon Sep 17 00:00:00 2001 From: Brian Taylor Date: Sun, 3 Sep 2023 13:16:44 -0700 Subject: [PATCH] By default, use the shortest typical delay estimate. This makes the digi_74LS90_74LS42.cir testcase for bug641 behave almost the same as MicroCap 12. In ngspice and MicroCap, the only signal with a glitch is not_y8. The other not_* signals look the same. Setting ps_use_mntymx in .spiceinit will change the delay estimates. See the function set_u_devices_info in src/frontend/udevices.c for the various settings of ps_use_mntymx. --- src/frontend/logicexp.c | 80 +++++++++++--- src/frontend/udevices.c | 185 +++++++++++++++++++++++---------- src/include/ngspice/udevices.h | 6 ++ 3 files changed, 201 insertions(+), 70 deletions(-) diff --git a/src/frontend/logicexp.c b/src/frontend/logicexp.c index 5911da317..32626be0e 100644 --- a/src/frontend/logicexp.c +++ b/src/frontend/logicexp.c @@ -1912,7 +1912,38 @@ static char *get_typ_estimate(char *min, char *typ, char *max, DSTRING *pds) return NULL; } -static char *typical_estimate(char *delay_str, DSTRING *pds) +static char *get_one_estimate(char *s, DSTRING *pds) +{ + ds_clear(pds); + if (s && strlen(s) > 0 && s[0] != '-') { + ds_cat_str(pds, s); + return ds_get_buf(pds); + } else { + return NULL; + } +} + +static char *get_delay_estimate(char *min, char *typ, char *max, DSTRING *pds) +{ + char *one = NULL; + struct udevices_info info = u_get_udevices_info(); + int delay_type = info.mntymx; + if (delay_type == 1) { // min + one = get_one_estimate(min, pds); + if (one) { + return one; + } + } else if (delay_type == 2) { // max + one = get_one_estimate(max, pds); + if (one) { + return one; + } + } + // typ + return get_typ_estimate(min, typ, max, pds); +} + +static char *mntymx_estimate(char *delay_str, DSTRING *pds) { /* Input string (t1,t2,t2) */ int which = 0; @@ -1945,7 +1976,7 @@ static char *typical_estimate(char *delay_str, DSTRING *pds) break; } } - s = get_typ_estimate(ds_get_buf(&dmin), ds_get_buf(&dtyp), + s = get_delay_estimate(ds_get_buf(&dmin), ds_get_buf(&dtyp), ds_get_buf(&dmax), pds); ds_free(&dmin); ds_free(&dtyp); @@ -1967,22 +1998,25 @@ static BOOL extract_delay( BOOL in_delay = FALSE, ret_val = TRUE; int i; BOOL prit = PRINT_ALL; - float typ_max_val = 0.0, typ_val = 0.0; + BOOL shorter = FALSE, update_val = FALSE; + struct udevices_info info = u_get_udevices_info(); + float del_max_val = 0.0, del_val = 0.0, del_min_val = FLT_MAX; char *units; + shorter = info.shorter_delays; DS_CREATE(dly, 64); - DS_CREATE(dtyp_max_str, 16); + DS_CREATE(ddel_str, 16); DS_CREATE(tmp_ds, 128); if (val != '=') { ds_free(&dly); - ds_free(&dtyp_max_str); + ds_free(&ddel_str); ds_free(&tmp_ds); return FALSE; } val = lexer_scan(lx); if (val != '{') { ds_free(&dly); - ds_free(&dtyp_max_str); + ds_free(&ddel_str); ds_free(&tmp_ds); return FALSE; } @@ -2005,7 +2039,7 @@ static BOOL extract_delay( char *tmps; ds_clear(&tmp_ds); in_delay = FALSE; - tmps = typical_estimate(ds_get_buf(&dly), &tmp_ds); + tmps = mntymx_estimate(ds_get_buf(&dly), &tmp_ds); if (!tmps) { ret_val = FALSE; ds_clear(&tmp_ds); @@ -2015,22 +2049,34 @@ static BOOL extract_delay( printf("%s\n", ds_get_buf(&dly)); printf("estimate \"%s\"\n", tmps); } - typ_val = strtof(tmps, &units); - if (typ_val > typ_max_val) { + del_val = strtof(tmps, &units); + update_val = FALSE; + if (shorter) { + if (del_val < del_min_val) { + update_val = TRUE; + } + } else if (del_val > del_max_val) { + update_val = TRUE; + } + if (update_val) { ds_clear(&delay_string); - ds_clear(&dtyp_max_str); - ds_cat_str(&dtyp_max_str, tmps); - typ_max_val = typ_val; - if (ds_get_length(&dtyp_max_str) > 0) { + ds_clear(&ddel_str); + ds_cat_str(&ddel_str, tmps); + if (shorter) { + del_min_val = del_val; + } else { + del_max_val = del_val; + } + if (ds_get_length(&ddel_str) > 0) { if (tri) { ds_cat_printf(&delay_string, "(inertial_delay=true delay=%s)", - ds_get_buf(&dtyp_max_str)); + ds_get_buf(&ddel_str)); } else { ds_cat_printf(&delay_string, "(inertial_delay=true rise_delay=%s fall_delay=%s)", - ds_get_buf(&dtyp_max_str), - ds_get_buf(&dtyp_max_str)); + ds_get_buf(&ddel_str), + ds_get_buf(&ddel_str)); } } else { printf("WARNING pindly DELAY not found\n"); @@ -2055,7 +2101,7 @@ static BOOL extract_delay( val = lexer_scan(lx); } // end while != '}' ds_free(&dly); - ds_free(&dtyp_max_str); + ds_free(&ddel_str); ds_free(&tmp_ds); return ret_val; } diff --git a/src/frontend/udevices.c b/src/frontend/udevices.c index c06184a01..f4433e70a 100644 --- a/src/frontend/udevices.c +++ b/src/frontend/udevices.c @@ -307,6 +307,10 @@ static int ps_udevice_exit = 0; static int ps_tpz_delays = 0; // For tristate delays static int ps_with_inverters = 0; // For ff/latch control inputs static int ps_with_tri_inverters = 0; // For inv3/inv3a data inputs + +static int ps_use_mntymx = 0; +static struct udevices_info mntymx_shorter = {0, FALSE}; + static NAME_ENTRY new_names_list = NULL; static NAME_ENTRY input_names_list = NULL; static NAME_ENTRY output_names_list = NULL; @@ -319,6 +323,46 @@ static BOOL add_drive_hilo = FALSE; static char *current_subckt = NULL; static unsigned int subckt_msg_count = 0; +static void set_u_devices_info(int mntymx_duration) +{ + switch (mntymx_duration) { + case 0: // typ + longer delays + mntymx_shorter.mntymx = 0; + mntymx_shorter.shorter_delays = FALSE; + break; + case 1: // min + longer delays + mntymx_shorter.mntymx = 1; + mntymx_shorter.shorter_delays = FALSE; + break; + case 2: // max + longer delays + mntymx_shorter.mntymx = 2; + mntymx_shorter.shorter_delays = FALSE; + break; + case 4: // typ + shorter delays + mntymx_shorter.mntymx = 0; + mntymx_shorter.shorter_delays = TRUE; + break; + case 5: // min + shorter delays + mntymx_shorter.mntymx = 1; + mntymx_shorter.shorter_delays = TRUE; + break; + case 6: // max + shorter delays + mntymx_shorter.mntymx = 2; + mntymx_shorter.shorter_delays = TRUE; + break; + default: + mntymx_shorter.mntymx = 0; + mntymx_shorter.shorter_delays = FALSE; + break; + } + return; +} + +struct udevices_info u_get_udevices_info(void) +{ + return mntymx_shorter; +} + static void check_name_unused(char *name) { if (find_name_entry(name, new_names_list)) { @@ -897,7 +941,7 @@ void initialize_udevice(char *subckt_line) if they are the only delays. */ if (!cp_getvar("ps_tpz_delays", CP_NUM, &ps_tpz_delays, 0)) { - ps_tpz_delays = 0; + ps_tpz_delays = 1; // default: use tpz... delays if necessary } /* If non-zero use inverters with ff/latch control inputs */ if (!cp_getvar("ps_with_inverters", CP_NUM, &ps_with_inverters, 0)) { @@ -907,6 +951,11 @@ void initialize_udevice(char *subckt_line) if (!cp_getvar("ps_with_tri_inverters", CP_NUM, &ps_with_tri_inverters, 0)) { ps_with_tri_inverters = 0; } + if (!cp_getvar("ps_use_mntymx", CP_NUM, &ps_use_mntymx, 0)) { + ps_use_mntymx = 4; // default: typ + shorter delays + } + set_u_devices_info(ps_use_mntymx); + if (subckt_line && strncmp(subckt_line, ".subckt", 7) == 0) { add_all_port_names(subckt_line); current_subckt = TMALLOC(char, strlen(subckt_line) + 1); @@ -2759,10 +2808,32 @@ static void estimate_typ(struct timing_data *tdp) return; } +static void estimate_delay(struct timing_data *tdp) +{ + char *del = NULL; + if (!tdp) { return; } + if (mntymx_shorter.mntymx == 1) { // use min delay + del = tdp->min; + if (del && strlen(del) > 0 && del[0] != '-') { + tdp->estimate = EST_MIN; + return; + } + } else if (mntymx_shorter.mntymx == 2) { // use max delay + del = tdp->max; + if (del && strlen(del) > 0 && del[0] != '-') { + tdp->estimate = EST_MAX; + return; + } + } + // use typ delay + estimate_typ(tdp); + return; +} + static char *get_estimate(struct timing_data *tdp) { /* - Call after estimate_typ. + Call after estimate_delay. Don't call delete_timing_data until you have copied or finished with this return value. */ @@ -2777,8 +2848,11 @@ static char *get_estimate(struct timing_data *tdp) return NULL; } -static char *larger_delay(char *delay1, char *delay2) +static char *select_delay(char *delay1, char *delay2) { + /* Return the shorter or longer delay + depending on the mntymx_shorter.shorter_delays setting + */ float val1, val2; char *units1, *units2; @@ -2787,11 +2861,16 @@ static char *larger_delay(char *delay1, char *delay2) if (!eq(units1, units2)) { printf("WARNING units do not match\n"); } - if (val1 >= val2) { - return delay1; + if (mntymx_shorter.shorter_delays) { + if (val1 <= val2) { + return delay1; + } } else { - return delay2; + if (val1 >= val2) { + return delay1; + } } + return delay2; } /* NOTE @@ -2813,10 +2892,10 @@ static char *get_delays_ugate(char *rem) BOOL has_rising = FALSE, has_falling = FALSE; tdp1 = create_min_typ_max("tplh", rem); - estimate_typ(tdp1); + estimate_delay(tdp1); rising = get_estimate(tdp1); tdp2 = create_min_typ_max("tphl", rem); - estimate_typ(tdp2); + estimate_delay(tdp2); falling = get_estimate(tdp2); has_rising = (rising && strlen(rising) > 0); has_falling = (falling && strlen(falling) > 0); @@ -2845,7 +2924,7 @@ static char *get_delays_udly(char *rem) struct timing_data *tdp1; tdp1 = create_min_typ_max("dly", rem); - estimate_typ(tdp1); + estimate_delay(tdp1); udelay = get_estimate(tdp1); if (udelay) { delays = tprintf( @@ -2865,22 +2944,22 @@ static char *get_delays_utgate(char *rem) char *rising, *falling, *delays = NULL; struct timing_data *tdp1, *tdp2; struct timing_data *tdp3, *tdp4, *tdp5, *tdp6; - char *tplz, *tphz, *tpzl, *tpzh, *larger, *larger1, *larger2, *larger3; + char *tplz, *tphz, *tpzl, *tpzh, *select0, *select1, *select2, *select3; BOOL use_zdelays = FALSE; BOOL has_rising = FALSE, has_falling = FALSE; tdp1 = create_min_typ_max("tplh", rem); - estimate_typ(tdp1); + estimate_delay(tdp1); rising = get_estimate(tdp1); tdp2 = create_min_typ_max("tphl", rem); - estimate_typ(tdp2); + estimate_delay(tdp2); falling = get_estimate(tdp2); has_rising = (rising && strlen(rising) > 0); has_falling = (falling && strlen(falling) > 0); if (has_rising) { if (has_falling) { - larger = larger_delay(rising, falling); - delays = tprintf("(inertial_delay=true delay = %s)", larger); + select0 = select_delay(rising, falling); + delays = tprintf("(inertial_delay=true delay = %s)", select0); } else { delays = tprintf("(inertial_delay=true delay = %s)", rising); } @@ -2889,49 +2968,49 @@ static char *get_delays_utgate(char *rem) } else if (use_zdelays || (ps_tpz_delays & 1)) { /* No lh/hl delays, so try the largest lz/hz/zl/zh delay */ tdp3 = create_min_typ_max("tplz", rem); - estimate_typ(tdp3); + estimate_delay(tdp3); tplz = get_estimate(tdp3); tdp4 = create_min_typ_max("tphz", rem); - estimate_typ(tdp4); + estimate_delay(tdp4); tphz = get_estimate(tdp4); - larger1 = NULL; + select1 = NULL; if (tplz && strlen(tplz) > 0) { if (tphz && strlen(tphz) > 0) { - larger1 = larger_delay(tplz, tphz); + select1 = select_delay(tplz, tphz); } else { - larger1 = tplz; + select1 = tplz; } } else if (tphz && strlen(tphz) > 0) { - larger1 = tphz; + select1 = tphz; } tdp5 = create_min_typ_max("tpzl", rem); - estimate_typ(tdp5); + estimate_delay(tdp5); tpzl = get_estimate(tdp5); tdp6 = create_min_typ_max("tpzh", rem); - estimate_typ(tdp6); + estimate_delay(tdp6); tpzh = get_estimate(tdp6); - larger2 = NULL; + select2 = NULL; if (tpzl && strlen(tpzl) > 0) { if (tpzh && strlen(tpzh) > 0) { - larger2 = larger_delay(tpzl, tpzh); + select2 = select_delay(tpzl, tpzh); } else { - larger2 = tpzl; + select2 = tpzl; } } else if (tpzh && strlen(tpzh) > 0) { - larger2 = tpzh; + select2 = tpzh; } - larger3 = NULL; - if (larger1) { - if (larger2) { - larger3 = larger_delay(larger1, larger2); + select3 = NULL; + if (select1) { + if (select2) { + select3 = select_delay(select1, select2); } else { - larger3 = larger1; + select3 = select1; } - } else if (larger2) { - larger3 = larger2; + } else if (select2) { + select3 = select2; } - if (larger3) { - delays = tprintf("(inertial_delay=true delay = %s)", larger3); + if (select3) { + delays = tprintf("(inertial_delay=true delay = %s)", select3); } else { delays = tprintf("(inertial_delay=true delay=1.0e-12)"); } @@ -2951,26 +3030,26 @@ static char *get_delays_ueff(char *rem) { char *delays = NULL; char *clkqrise, *clkqfall, *pcqrise, *pcqfall; - char *clkd, *setd, *resetd, *larger; + char *clkd, *setd, *resetd, *select; struct timing_data *tdp1, *tdp2, *tdp3, *tdp4; tdp1 = create_min_typ_max("tpclkqlh", rem); - estimate_typ(tdp1); + estimate_delay(tdp1); clkqrise = get_estimate(tdp1); tdp2 = create_min_typ_max("tpclkqhl", rem); - estimate_typ(tdp2); + estimate_delay(tdp2); clkqfall = get_estimate(tdp2); tdp3 = create_min_typ_max("tppcqlh", rem); - estimate_typ(tdp3); + estimate_delay(tdp3); pcqrise = get_estimate(tdp3); tdp4 = create_min_typ_max("tppcqhl", rem); - estimate_typ(tdp4); + estimate_delay(tdp4); pcqfall = get_estimate(tdp4); clkd = NULL; if (clkqrise && strlen(clkqrise) > 0) { if (clkqfall && strlen(clkqfall) > 0) { - larger = larger_delay(clkqrise, clkqfall); - clkd = larger; + select = select_delay(clkqrise, clkqfall); + clkd = select; } else { clkd = clkqrise; } @@ -3018,7 +3097,7 @@ static char *get_delays_ugff(char *rem, char *d_name) char *delays = NULL, *dname; char *tpdqlh, *tpdqhl, *tpgqlh, *tpgqhl, *tppcqlh, *tppcqhl; char *d_delay, *enab, *setd, *resetd; - char *s1, *s2, *larger; + char *s1, *s2, *select; struct timing_data *tdp1, *tdp2, *tdp3, *tdp4, *tdp5, *tdp6; if (eq(d_name, "d_dlatch")) { @@ -3029,28 +3108,28 @@ static char *get_delays_ugff(char *rem, char *d_name) return NULL; } tdp1 = create_min_typ_max("tpdqlh", rem); - estimate_typ(tdp1); + estimate_delay(tdp1); tpdqlh = get_estimate(tdp1); tdp2 = create_min_typ_max("tpdqhl", rem); - estimate_typ(tdp2); + estimate_delay(tdp2); tpdqhl = get_estimate(tdp2); tdp3 = create_min_typ_max("tpgqlh", rem); - estimate_typ(tdp3); + estimate_delay(tdp3); tpgqlh = get_estimate(tdp3); tdp4 = create_min_typ_max("tpgqhl", rem); - estimate_typ(tdp4); + estimate_delay(tdp4); tpgqhl = get_estimate(tdp4); tdp5 = create_min_typ_max("tppcqlh", rem); - estimate_typ(tdp5); + estimate_delay(tdp5); tppcqlh = get_estimate(tdp5); tdp6 = create_min_typ_max("tppcqhl", rem); - estimate_typ(tdp6); + estimate_delay(tdp6); tppcqhl = get_estimate(tdp6); d_delay = NULL; if (tpdqlh && strlen(tpdqlh) > 0) { if (tpdqhl && strlen(tpdqhl) > 0) { - larger = larger_delay(tpdqlh, tpdqhl); - d_delay = larger; + select = select_delay(tpdqlh, tpdqhl); + d_delay = select; } else { d_delay = tpdqlh; } @@ -3060,8 +3139,8 @@ static char *get_delays_ugff(char *rem, char *d_name) enab = NULL; if (tpgqlh && strlen(tpgqlh) > 0) { if (tpgqhl && strlen(tpgqhl) > 0) { - larger = larger_delay(tpgqlh, tpgqhl); - enab = larger; + select = select_delay(tpgqlh, tpgqhl); + enab = select; } else { enab = tpgqlh; } diff --git a/src/include/ngspice/udevices.h b/src/include/ngspice/udevices.h index 5ee5a5fb1..7fe19a881 100644 --- a/src/include/ngspice/udevices.h +++ b/src/include/ngspice/udevices.h @@ -1,6 +1,11 @@ #ifndef INCLUDED_UDEVICES #define INCLUDED_UDEVICES +struct udevices_info { + int mntymx; + BOOL shorter_delays; +}; + BOOL u_process_instance(char *line); BOOL u_process_model_line(char *line); BOOL u_check_instance(char *line); @@ -10,5 +15,6 @@ void cleanup_udevice(void); void u_add_instance(char *str); void u_add_logicexp_model(char *tmodel, char *xspice_gate, char *model_name); void u_remember_pin(char *name, int type); +struct udevices_info u_get_udevices_info(void); #endif