def step_resp(vpk_pk): vmax = vpk_pk / 2 shutil.copyfile('PA13.LIB', '/tmp/PA13.LIB') shutil.copyfile('op27.cir', '/tmp/op27.cir') circuit = Circuit('Step Response') circuit.include('/tmp/PA13.LIB') circuit.include('/tmp/op27.cir') circuit.subcircuit(PA13Amplifier(Z_1, Z_2)) circuit.subcircuit(OP27Amplifier(R_4, C_4)) # V_Ir input circuit.PulseVoltageSource( 'input', 'vir', circuit.gnd, -vmax, vmax, pulse_width=0.05, # 0.05s period=0.1, # 0.1s, 10Hz rise_time=1e-9, fall_time=1e-9) # From lab provided schematic circuit.R('r3', 'vir', 'vir2', Z_3) # Current controller stage circuit.X('curr', 'op27_amplifier', 'vir2', 'vr') # Voltage stage circuit.X('volt', 'pa13_amplifier', 'vr', 'vo') # Motor stage circuit.R('rm', 'vo', 'vm1', R_m) circuit.L('lm', 'vm1', 'vio', L_m) circuit.R('rs', 'vio', circuit.gnd, R_s) circuit.R('r5', 'vio', 'vir2', Z_5) simulator = circuit.simulator() # Force ngspice to have shorter time steps near sharp transitions simulator.options(trtol=0.00001) import pdb pdb.set_trace() analysis = simulator.transient( step_time=0.1, # Lab schematic suggests 0.1s, this seems too slow end_time=0.045 ) # Lab schematic stops at 1s, we only need the first step return analysis
import PySpice.Logging.Logging as Logging logger = Logging.setup_logging() #################################################################################################### from PySpice.Probe.Plot import plot from PySpice.Spice.Netlist import Circuit from PySpice.Unit import * #################################################################################################### #r# We will drive the transmission line with a pulse source and use a standard 50 Ω load. circuit = Circuit('Transmission Line') circuit.PulseVoltageSource('pulse', 'input', circuit.gnd, 0 @ u_V, 1 @ u_V, 1 @ u_ns, 1 @ u_us) circuit.LosslessTransmissionLine('delay', 'output', circuit.gnd, 'input', circuit.gnd, impedance=50, time_delay=40e-9) circuit.R('load', 'output', circuit.gnd, 50 @ u_Ω) simulator = circuit.simulator(temperature=25, nominal_temperature=25) analysis = simulator.transient(step_time=1e-11, end_time=100e-9) #################################################################################################### figure = plt.figure(None, (20, 6)) plot(analysis['input']) plot(analysis['output']) plt.xlabel('Time [s]') plt.ylabel('Voltage (V)') plt.grid()
def do_monte_carlo_sim(tech, put, step_time, num_sims, plot_result, cvs, logger): # initialisar el generador alatoria usando el tiempo del sistema random.seed() # Abrir el archivo de .cvs if (cvs != None): try: cvs_handle = open(cvs,"w+") except: logger.error("Failed to open %s for writing, aborting", cvs) return logger.info("Running Monte Carlo simulation with path %s, tech %s, with step_time %e, num_sims %d", put.name(), tech.NAME, step_time, num_sims) # ================== # Generar el netlist # ================== libraries_path = find_libraries() circuit = Circuit("Monte_Carlo_Sim_"+put.name()+"_"+tech.NAME) circuit.include(libraries_path + "/" + tech.LIB_NAME) #.inclcude "foo.lib" vdd = circuit.V('dd', 'Vdd', circuit.gnd, tech.VDD) # Fuente de tensión Vdd # Fuente de tensión de pulses que usamos como la entrada de la ruta vin = circuit.PulseVoltageSource("In", "In", circuit.gnd, initial_value=0, pulsed_value=tech.VDD, pulse_width=1e-9, period=2e-9, delay_time=10e-12, rise_time=20e-12, fall_time=20e-12) # La ruta que queremos probar put.add_to_circuit(circuit, 'Vdd', 'In', 'Out') # ====================== # Hacer las simulaciones # ====================== # Comienza simulando por 100ps sim_time = 100e-12 ts = time.time(); # if simulation times out without seeing the transition, increase sim time by ... sim_time_step = 100e-12 # Once we've seen a succesfull transisition (sim time was long enough) # we reduce simulation time to bestResult.tp + 50ps succesfull_run = False # Hay varios resultados con el mismo (o muy parecido) Tp # Si una simulación da un Tp un poco peor (1%?) peor usa menos area, deberíamos # usar esto resultado cómo el mejor. TP_FLEX_PERCENT = 1.0 # guardamos el mejor resultado bestResult = Result(100.0, put.get_widths()) # Y guardamos un array de todos los resultados allResults = [] for l in range(1,num_sims+1): widths = put.get_widths() totalWidth = sum(widths) logger.debug("Running simulation with widths: [%s], sim_time %e, step_time %e", _get_widths_str(widths), sim_time, step_time) tp = _get_tp(circuit, tech, put, sim_time, step_time, logger) if (tp < 0): # La duración de simulación no estuvo suficiente largo # Si vimos una transición antes, entonces es claro que esto no puede ser mejor. # Pero si nunca vimos una transición antes, incrementamos la duración if (not succesfull_run): sim_time += sim_time_step logger.verbose("Out never transititons, increasing simulation time to %e", sim_time) else: logger.debug("Out never transititons") else: succesfull_run = True logger.verbose("tp: %e, widths: [%s], totalWidth: %e", tp, _get_widths_str(widths), totalWidth) allResults.append(Result(tp, widths)) # este resultado tiene menor tp que el corriente mejor? if (tp < bestResult.tp): logger.verbose(" New Best Tp") bestResult = Result(tp, widths) # reducir la duración de la simulación a tp + 50ps sim_time = tp + 50e-12 # ===================== # Actualizar los anchos # ===================== # Usamos los mejores anchos que encontramos como base widths = list(bestResult.widths) # tomar una copia #------------------------------------------------------------------- # Método 1: Cambiar todos los ancho aleatoriamente #------------------------------------------------------------------- #for idx in range(1, len(widths)): # width = random.uniform(tech.W_MIN, put.get_max_width()) # widths[idx] = width #---------------------------------------------------------------- # Método 2: Cambiar un ancho aleatoriamente adentro todo el rango #---------------------------------------------------------------- # Generar un ancho aleatoriamente entre tech.W_MIN y put.get_max_width() #width = random.uniform(tech.W_MIN, put.get_max_width()) # Elegir cual ancho cambiar (no elegimos el primero) #idx = random.randint(1, len(widths)-1) #widths[idx] = width #---------------------------------------------------------------------- # Método 3: Cambiar todos los ancho aleatoriamente en el rango de 0.5w # a 2w. Dónde w es el ancho actual. #---------------------------------------------------------------------- for idx in range(1, len(widths)): minWidth = max(tech.W_MIN, widths[idx]/2) maxWidth = min(put.get_max_width(), widths[idx]*2) width = random.uniform(minWidth, maxWidth) logger.debug("curr_width %e, minWidth %e, maxWidth %e, new width %e", widths[idx], minWidth, maxWidth, width) widths[idx] = width # Hazlo put.set_widths(widths) if (l % 100 == 0): logger.info("Ran %d / %d (%.1f%%)", l, num_sims, (100.0 * l)/num_sims) te = time.time(); # ====================== # Mostrar los resultados # ====================== logger.info("Took %ds to run %d simulations", int(te - ts), num_sims); logger.info("Best Tp %e, Widths [%s]", bestResult.tp, _get_widths_str(bestResult.widths)) # ============================================ # Encontrar el Tp optimo usando logical effort # ============================================ LEwidths= put.get_logical_effort_optimal_widths() if (LEwidths == None): logger.warning("Optimal case not supported by this path") else: logger.info("Running test with optimal widths calculated via logical effort: [%s]", _get_widths_str(LEwidths)) put.set_widths(LEwidths) LEtp = _get_tp(circuit, tech, put, sim_time, step_time, logger) if (LEtp <= 0): logger.info("Optimal Case: Out never transititons") else: logger.info("Optimal Case: %e, widths: [%s]", LEtp, _get_widths_str(LEwidths)) # ================================================ # Escribir los resultados al .cvs # ================================================ if (cvs != None): # Comenzamos con un comentario cvs_handle.write("#%s: Step time %.1e, Num sims %d, path: %s, tech: %s, load: %.2f\n" % (datetime.now().strftime("%Y/%m/%d %H:%M:%S"), step_time, num_sims, put.name(), tech.NAME, put.get_load())) # La simulación de LE (comentario) if (LEwidths == None): cvs_handle.write("#Logical Effort not supported by this path\n") elif (LEtp <= 0): cvs_handle.write("#Logical Effort: out never transititons\n") else: cvs_handle.write("#Logical Effort: %e, widths: [%s]\n" % (LEtp, _get_widths_str(LEwidths))) # El titulo de los columnos: widthHeadings = "" for i in range(len(widths)): widthHeadings += "width[%d], " % i ratioHeadings = "" for i in range(len(widths) - 1): ratioHeadings += "ratio %d to %d, " % (i, i+1) ratioHeadings += "ratio %d to load, " % (i+1) cvs_handle.write("Tp, " + widthHeadings + "load width, Total width, " + ratioHeadings + "Average ratio (not including load), Average ratio (including load)\n") # Y los resultados for r in allResults: ratios = [] for i in range(len(r.widths) - 1): ratios.append(r.widths[i+1] / r.widths[i]) avgWithoutLoad = sum(ratios)/len(ratios) ratios.append(put.get_load() * tech.W_MIN / r.widths[i+1]) avgWithLoad = sum(ratios)/len(ratios) ratiosStr = "" for i, ratio in enumerate(ratios): if (i != 0): ratiosStr += ", " ratiosStr += "%.2f" % ratio cvs_handle.write("%e, %s, %e, %e, %s, %.2f, %.2f\n" % (r.tp, _get_widths_str(r.widths), put.get_load() * tech.W_MIN, sum(r.widths), ratiosStr, avgWithoutLoad, avgWithLoad)) cvs_handle.close() # ================================================ # Finalmente simula una vez más con anchos mejores # que encontramos y plotear el resultado # ================================================ if (plot_result and (bestResult.tp > 0.0) and (bestResult.tp < 1.0)): put.set_widths(bestResult.widths) simulator = circuit.simulator(temperature=27, nominal_temperature=27) analysis = simulator.transient(end_time=sim_time*1.5, step_time=step_time) put.plot(analysis, 'In', 'Out')
print('Cout =', Cout) print('Cint =', Cin) circuit.V('in', 'in', circuit.gnd, Vin) circuit.C('in', 'in', circuit.gnd, Cin) # Fixme: out drop from 12V to 4V # circuit.VCS('switch', 'gate', circuit.gnd, 'in', 'source', model='Switch', initial_state='off') # circuit.PulseVoltageSource('pulse', 'gate', circuit.gnd, 0@u_V, Vin, duty_cycle, period) # circuit.model('Switch', 'SW', ron=1@u_mΩ, roff=10@u_MΩ) # Fixme: Vgate => Vout ??? circuit.X('Q', 'irf150', 'in', 'gate', 'source') # circuit.PulseVoltageSource('pulse', 'gate', 'source', 0@u_V, Vin, duty_cycle, period) circuit.R('gate', 'gate', 'clock', 1@u_Ohm) circuit.PulseVoltageSource('pulse', 'clock', circuit.gnd, 0@u_V, 2.*Vin, duty_cycle, period) circuit.X('D', '1N5822', circuit.gnd, 'source') circuit.L(1, 'source', 1, L) circuit.R('L', 1, 'out', RL) circuit.C(1, 'out', circuit.gnd, Cout) # , initial_condition=0@u_V circuit.R('load', 'out', circuit.gnd, Rload) ##### # Simulation parameters ##### simulator = circuit.simulator(temperature=25, nominal_temperature=25) analysis = simulator.transient(step_time=period/300, end_time=period*150)
#r# We will now drive the diode with a pulse generator and perform a transient analysis. #f# circuit_macros('diode-characteristic-curve-circuit-pulse.m4') frequency = 1 @ u_MHz circuit = Circuit('Diode') circuit.include(spice_library['BAV21']) # source = circuit.SinusoidalVoltageSource('input', 'in', circuit.gnd, # dc_offset=dc_offset, offset=dc_offset, # amplitude=ac_amplitude, # frequency=frequency) source = circuit.PulseVoltageSource('input', 'in', circuit.gnd, initial_value=dc_offset - ac_amplitude, pulsed_value=dc_offset + ac_amplitude, pulse_width=frequency.period / 2, period=frequency.period) circuit.R(1, 'in', 'out', 1 @ u_kΩ) circuit.D('1', 'out', circuit.gnd, model='BAV21') simulator = circuit.simulator(temperature=25, nominal_temperature=25) analysis = simulator.transient(step_time=source.period / 1e3, end_time=source.period * 4) axe = plt.subplot(313) # Fixme: axis, x scale # plot(analysis['in'] - dc_offset + quiescent_points[0]['quiescent_voltage'], axis=axe) # plot(analysis.out, axis=axe) axe.plot(analysis.out.abscissa * 1e6, analysis.out)
# r# We will fit from the simulation output the time constant of each circuit and compare it to the #r# theoretical value. figure = plt.figure(1, (20, 10)) element_types = ('capacitor', 'inductor') for element_type in ('capacitor', 'inductor'): circuit = Circuit(element_type.title()) # Fixme: compute value source = circuit.PulseVoltageSource('input', 'in', circuit.gnd, initial_value=0 @ u_V, pulsed_value=10 @ u_V, pulse_width=10 @ u_ms, period=20 @ u_ms) circuit.R(1, 'in', 'out', 1 @ u_kΩ) if element_type == 'capacitor': element = circuit.C value = 1 @ u_uF # tau = RC = 1 ms else: element = circuit.L # Fixme: force component value to an Unit instance ? value = 1 @ u_H # tau = L/R = 1 ms element(1, 'out', circuit.gnd, value) # circuit.R(2, 'out', circuit.gnd, kilo(1)) # for debug
#################################################################################################### libraries_path = find_libraries() spice_library = SpiceLibrary(libraries_path) #################################################################################################### #?# #cm# relay.m4 period = 50@u_ms pulse_width = period / 2 circuit = Circuit('Relay') # circuit.V('digital', 'Vdigital', circuit.gnd, 5@u_V) circuit.PulseVoltageSource('clock', 'clock', circuit.gnd, 0@u_V, 5@u_V, pulse_width, period, rise_time=5@u_ms, fall_time=5@u_ms) circuit.R('base', 'clock', 'base', 100@u_Ω) circuit.BJT(1, 'collector', 'base', circuit.gnd, model='bjt') # Q is mapped to BJT ! circuit.model('bjt', 'npn', bf=80, cjc=pico(5), rb=100) circuit.V('analog', 'VccAnalog', circuit.gnd, 8@u_V) circuit.R('relay', 'VccAnalog', 1, 50@u_Ω) circuit.L('relay', 1, 'collector', 100@u_mH) circuit.include(spice_library['1N5822']) # Schottky diode diode = circuit.X('D', '1N5822', 'collector', 'VccAnalog') # Fixme: subcircuit node # diode.minus.add_current_probe(circuit) #################################################################################################### figure = plt.figure(1, (20, 10))
from PySpice.Unit import * #################################################################################################### from PySpice.Spice.NgSpice.Shared import NgSpiceShared #################################################################################################### libraries_path = find_libraries() spice_library = SpiceLibrary(libraries_path) #################################################################################################### circuit = Circuit('Basic Switch') circuit.PulseVoltageSource('pulse', 'sw_drive', circuit.gnd, 0@u_V, 10@u_V, 1@u_ms, 2@u_ms,) circuit.V('input', 'input', circuit.gnd, 20@u_V) circuit.R('load', circuit.gnd, 'sw_node', 5@u_Ohm) # circuit.VoltageControlledSwitch('input', 'sw_node', 'sw_drive', circuit.gnd, 'sw1', model=None) # circuit.VoltageControlledSwitch('sw1', 'sw_node', 'sw_drive', circuit.gnd, 'sw1', model=None, initial_state=True) # circuit.VoltageControlledSwitch('sw2', 'sw_node', 'sw_drive', circuit.gnd, 'sw1', model=None, initial_state=False) # circuit.VoltageControlledSwitch('sw3', input_plus='sw_drive', input_minus=circuit.gnd, output_minus='sw_node', output_plus='input', model='SW') circuit.VoltageControlledSwitch('input', 'sw_node', 'sw_drive', circuit.gnd, 'sw1', model='switch1') circuit.model('switch1', 'SW', Ron=.002@u_Ohm, Roff=1@u_MOhm, Vt=3.0@u_V) print(circuit)
##### # Buck LC output and diode ##### circuit.X('D', '1N5822', circuit.gnd, 'pch_drain') circuit.L(1, 'pch_drain', 1, L) circuit.R('L', 1, 'out', RL) circuit.C(1, 'out', circuit.gnd, Cout) # , initial_condition=0@u_V circuit.R('load', 'out', circuit.gnd, Rload) # This clock is used for NGspice mixed signal simulation. # The python code runs every clock edge, both positive and negative # clock speed: 20MHz # clock cycle length: 50ns circuit.PulseVoltageSource('clk', 'clk', circuit.gnd, 0@u_V, 1@u_V, 0.05@u_us, 0.1@u_us) ##### # Add a step load ##### # ~ circuit.PulseVoltageSource('name', n1, n2, v_low, v_high, t_high, t_period, t_delay,t_rise,t_fall) circuit.PulseVoltageSource('load_sw', 'gate_drive2', circuit.gnd, 0@u_V, 10@u_V, 1@u_s,1@u_s,0.8@u_ms,10@u_ns) # load switch circuit.X('Q2', 'irf150', 'out', 'gate2', 'source2') circuit.R('gate2', 'gate2', 'gate_drive2', 1@u_Ohm) circuit.R('load_on','source2', circuit.gnd,Rload) ##### # Simulation parameters
# Cout = 22@u_uF # ESR = 20@u_mOhm # ESL = 0 # parameters on input to buck # circuit.V('input', 'input', circuit.gnd, Vin) # circuit.C('input', 'input', circuit.gnd, Cin) circuit.C(1, 'esr', circuit.gnd, 22 @ u_uF) # , initial_condition=0@u_V circuit.R(1, 'sense_net_2', 'esr', 10 @ u_Ohm) # This clock is used for NGspice mixed signal simulation. # The python code runs every clock edge, both positive and negative # clock speed: 2.5MHz # clock cycle length: 40ns circuit.PulseVoltageSource('clk', 'clk', circuit.gnd, 0 @ u_V, 1 @ u_V, 20 @ u_ns, 40 @ u_ns) ##### # Simulation parameters ##### # Python block input constants amplitude = 10 @ u_V # Call the MyNgSpiceShared ngspice_shared = MyNgSpiceShared(amplitude=amplitude, send_data=True) simulator = circuit.simulator(temperature=25, nominal_temperature=25, simulator='ngspice-shared', ngspice_shared=ngspice_shared)