From d20a1982da6a87642dd96973ea7f94d3ca37a5c4 Mon Sep 17 00:00:00 2001 From: couriersud Date: Thu, 5 May 2022 17:37:22 +0200 Subject: [PATCH] Netlist lint, first version of a FAQ and small code changes. (#9684) - Added a first version of a FAQ. - Use better error messages in pfunction. - Made member functions static where appropriate in nld_solver. --- src/lib/netlist/FAQ.md | 151 +++++++++++++++++++++ src/lib/netlist/devices/nld_9321.cpp | 4 +- src/lib/netlist/plib/pfunction.cpp | 19 ++- src/lib/netlist/plib/ptokenizer.cpp | 2 +- src/lib/netlist/solver/nld_matrix_solver.h | 38 +++--- src/lib/netlist/solver/nld_ms_w.h | 2 +- src/lib/netlist/solver/nld_solver.cpp | 1 + 7 files changed, 188 insertions(+), 29 deletions(-) create mode 100644 src/lib/netlist/FAQ.md diff --git a/src/lib/netlist/FAQ.md b/src/lib/netlist/FAQ.md new file mode 100644 index 00000000000..44bcf7c924b --- /dev/null +++ b/src/lib/netlist/FAQ.md @@ -0,0 +1,151 @@ +# The netlist FAQ + +## Glossary + +BTANB: Bugs That Aren't Bugs. Bugs in the hardware (or software) that are +faithfully emulated, therefore not a bug in the emulator. + +## General + +### Have you considered maybe supporting part randomization in the netlist core? + +Yes I did. And decided against it. Which features? Forced randomness - will this +be different at every restart? And it would require another parameter per device +(specification, e.g. for resistors 5%, 10%, 20%). This would cause more cache +usage. + +And I am convinced we would see more BTANBs. + +### How do frontiers work? + +Frontiers divides a netlist into sub netlists, i.e. a number of smaller netlists. +Frontiers work best if you have a low impedance to high impedance transition. +This is best illustrated by an example. Consider the following mixing stage + + R1 + S1 >-----1RRRR2---------+ + | + R2 | + S2 >-----1RRRR2---------+----------> Out + | + R + R3 R + R + | + GND + +With `OPTIMIZE_FRONTIER(R2.2, R3, R2)` this becomes: + + R1 + S1 >-----1RRRR2--------------------------------+ + | + ########################## | + R2 # R2 # | + S2 >-----1RRRR2-----+-->AnIn AnOut>--RRRR------+----------> Out + # | # | + # R # R + # R R3 # R3 R + # R # R + # | # | + # GND Frontier # GND + # # + ########################## + +As a result, provided there are no other connections between the parts +generating S1 and S2 the "S2 part" will now have a separate solver. + +The size (aka number of nets) of the solver for S1 will be smaller. +The size of the solver for S2 and the rest of the circuit will be smaller +as well. + +Frontiers assume that there is little to no feedback from the "out" terminal to the "in" +terminal. This is a safe assumption if the "in" terminal e.g. is connected to an +op-amp output (typically < 500 Ohm) and R2 in the example above is in the 10 KOhm range. + +## MAME specific + +### Is there an example of a netlist that takes input from an AY-8910/2 and handles it? + +There are quite a few: + +- nl_kidniki.cpp +- nl_konami.cpp +- 1942.cpp + +Basically the AY8910 driver has to be configured to push resistor values to the +input stream. The konami drivers (scramble, frogger and friends) do that and +should be used as a reference. 1942 connects outputs and may be an even better example. + +## Models + +### Is there are JFET model? + +No, there is currently no JFET model in netlist. They are close to depletion +mode MOSFETs with very low gate capacitance so you may try a generic n-mosfet +with a negative trigger voltage. Example: + + MOSFET(Q21, "NMOS(VTO=-1.0)") + +Writing an analog model is a very time-consuming task. The simplified model above should +deliver reasonable results. + +## Operational Amplifiers + +### What's UGF? Would like to understand this black magic a little better. :) + +UGF = Unity Gain Frequency, usually identical to gain-bandwidth product for +op-amps. The idea is that the an op-amp's open-loop gain (its amplification +factor when used without any negative feedback) is proportional to the +frequency of its input signal. For slowly-varying, near-DC signals, this gain +will be extremely high (in this case, a gain of 10000 at 1 Hz), which is part +of what makes an op-amp effective. In practice the op-amp's actual gain will +be limited by whatever negative feedback is applied, but its open-loop gain +is a key quantity in computing its actual response. Because the op-amp is +limited in how fast it can respond to its input, its open-loop gain will +drop as the frequency of the input signal rises, eventually falling to a +gain of 1 (output = input) at the unity gain frequency. In this case +couriersud set the unity gain frequency is 10 kHz, which is much lower than +reality, in order to make the op-amp response in the oscillators slower and +easier for the netlist solver to handle without using very small time steps. +According to Texas Instruments, the TL084's actual gain-bandwidth product +(remember, this is the same as the UGF) is typically 3 MHz. + +## Documentation + +### What is the preferred documentation format? + +The preferred documentation for devices is to use the netlist documentation format. +This will ensure that the devices are properly documented in the Doxygen +documentation created by `make doc` + +An example entry is given here: + + //- Identifier: SN74LS629_DIP + //- Title: SN74LS629 VOLTAGE-CONTROLLED OSCILLATORS + //- Description: Please add a detailed description + //- FIXME: Missing description + //- + //- Pinalias: 2FC,1FC,1RNG,1CX1,1CX2,1ENQ,1Y,OSC_GND,GND,2Y,2ENQ,2CX2,2CX1,2RNG,OSC_VCC,VCC + //- Package: DIP + //- Param: A.CAP + //- Capacitor value of capacitor connected to 1CX1 and 1CX2 pins + //- Param: B.CAP + //- Capacitor value of capacitor connected to 2CX1 and 2CX2 pins + //- Limitations: + //- The capacitor inputs are NC. Capacitor values need to be specified as + //- ``` + //- SN74LS629_DIP(X) + //- PARAM(X.A.CAP, CAP_U(1)) + //- PARAM(X.B.CAP, CAP_U(2)) + //- ``` + //- + //- Example: 74ls629.cpp,74ls629_example + //- + //- FunctionTable: + //- http://pdf.datasheetcatalog.com/datasheets/400/335051_DS.pdf + //- + static NETLIST_START(SN74LS629_DIP) + +If you add an example in the examples folder this will be included in the +documentation as well. + diff --git a/src/lib/netlist/devices/nld_9321.cpp b/src/lib/netlist/devices/nld_9321.cpp index 8adaaad0f6b..479b83d497e 100644 --- a/src/lib/netlist/devices/nld_9321.cpp +++ b/src/lib/netlist/devices/nld_9321.cpp @@ -39,7 +39,7 @@ namespace netlist::devices { private: NETLIB_HANDLERI(in) { - m_enable = m_E() ? false : true; + m_enable = m_E() ? false : true; // NOLINT m_o = (m_A[1]() << 1) | m_A[0](); for (std::size_t i=0; i<4; i++) m_D[i].push((i == m_o && m_enable) ? 0 : 1, NLTIME_FROM_NS(21)); @@ -47,7 +47,7 @@ namespace netlist::devices { NETLIB_HANDLERI(e) { - m_enable = m_E() ? false : true; + m_enable = m_E() ? false : true; // NOLINT m_o = (m_A[1]() << 1) | m_A[0](); for (std::size_t i=0; i<4; i++) m_D[i].push((i == m_o && m_enable) ? 0 : 1, NLTIME_FROM_NS(18)); diff --git a/src/lib/netlist/plib/pfunction.cpp b/src/lib/netlist/plib/pfunction.cpp index 362615468e5..6d295f48a73 100644 --- a/src/lib/netlist/plib/pfunction.cpp +++ b/src/lib/netlist/plib/pfunction.cpp @@ -17,6 +17,13 @@ namespace plib { + PERRMSGV(MF_FUNCTION_UNKNOWN_TOKEN, 2, "pfunction: unknown/misformatted token <{1}> in <{2}>") + PERRMSGV(MF_FUNCTION_STACK_UNDERFLOW, 2, "pfunction: stack underflow on token <{1}> in <{2}>") + PERRMSGV(MF_FUNCTION_STACK_OVERFLOW, 2, "pfunction: stack overflow on token <{1}> in <{2}>") + PERRMSGV(MF_FUNCTION_PARENTHESIS_INEQUALITY, 2, "pfunction: parenthesis inequality on token <{1}> in <{2}>") + PERRMSGV(MF_FUNCTION_STACK_UNEQUAL_ONE, 2, "pfunction: stack count {1} different to one on <{2}>") + PERRMSGV(MF_FUNCTION_STACK_UNDERFLOW_INFIX, 1, "pfunction: stack underflow during infix parsing of: <{1}>") + static constexpr const std::size_t MAX_STACK = 32; struct pcmd_t @@ -141,20 +148,20 @@ namespace plib { else rc = rpn_inst(plib::pstonum_ne(plib::left(cmd, cmd.length()-1), err) * r->second); if (err) - throw pexception(plib::pfmt("pfunction: unknown/misformatted token <{1}> in <{2}>")(cmd)(expr)); + throw pexception(MF_FUNCTION_UNKNOWN_TOKEN(cmd, expr)); stk += 1; } } if (stk < 1) - throw pexception(plib::pfmt("pfunction: stack underflow on token <{1}> in <{2}>")(cmd)(expr)); + throw pexception(MF_FUNCTION_STACK_UNDERFLOW(cmd, expr)); if (stk >= narrow_cast(MAX_STACK)) - throw pexception(plib::pfmt("pfunction: stack overflow on token <{1}> in <{2}>")(cmd)(expr)); + throw pexception(MF_FUNCTION_STACK_OVERFLOW(cmd, expr)); if (rc.cmd() == LP || rc.cmd() == RP) - throw pexception(plib::pfmt("pfunction: parenthesis inequality on token <{1}> in <{2}>")(cmd)(expr)); + throw pexception(MF_FUNCTION_PARENTHESIS_INEQUALITY(cmd, expr)); m_precompiled.push_back(rc); } if (stk != 1) - throw pexception(plib::pfmt("pfunction: stack count {1} different to one on <{2}>")(stk, expr)); + throw pexception(MF_FUNCTION_STACK_UNEQUAL_ONE(stk, expr)); compress(); } @@ -187,7 +194,7 @@ namespace plib { static pstring pop_check(std::stack &stk, const pstring &expr) noexcept(false) { if (stk.empty()) - throw pexception(plib::pfmt("pfunction: stack underflow during infix parsing of: <{1}>")(expr)); + throw pexception(MF_FUNCTION_STACK_UNDERFLOW_INFIX(expr)); pstring res = stk.top(); stk.pop(); return res; diff --git a/src/lib/netlist/plib/ptokenizer.cpp b/src/lib/netlist/plib/ptokenizer.cpp index fed0e2add31..80db2d0663a 100644 --- a/src/lib/netlist/plib/ptokenizer.cpp +++ b/src/lib/netlist/plib/ptokenizer.cpp @@ -13,7 +13,7 @@ namespace plib { PERRMSGV(MF_EXPECTED_IDENTIFIER_GOT_1, 1, "Expected an identifier, got <{1}>") PERRMSGV(MF_EXPECTED_ID_OR_NUM_GOT_1, 1, "Expected an identifier or number, got <{1}>") PERRMSGV(MF_EXPECTED_NUMBER_GOT_1, 1, "Expected a number, got <{1}>") - PERRMSGV(MF_EXPECTED_LONGINT_GOT_1, 1, "Expected a logn int, got <{1}>") + PERRMSGV(MF_EXPECTED_LONGINT_GOT_1, 1, "Expected a long int, got <{1}>") PERRMSGV(MF_EXPECTED_LINENUM_GOT_1, 1, "Expected line number after line marker but got <{1}>") PERRMSGV(MF_EXPECTED_FILENAME_GOT_1, 1, "Expected file name after line marker but got <{1}>") diff --git a/src/lib/netlist/solver/nld_matrix_solver.h b/src/lib/netlist/solver/nld_matrix_solver.h index 2c0057a11b9..57d56880a90 100644 --- a/src/lib/netlist/solver/nld_matrix_solver.h +++ b/src/lib/netlist/solver/nld_matrix_solver.h @@ -64,35 +64,35 @@ namespace solver struct solver_parameter_defaults { - constexpr nl_fptype m_freq() { return nlconst::magic(48000.0); } + static constexpr nl_fptype m_freq() { return nlconst::magic(48000.0); } // iteration parameters - constexpr nl_fptype m_gs_sor() { return nlconst::magic(1.059); } - constexpr matrix_type_e m_method() { return matrix_type_e::MAT_CR; } - constexpr matrix_fp_type_e m_fp_type() { return matrix_fp_type_e::DOUBLE; } - constexpr nl_fptype m_reltol() { return nlconst::magic(1e-3); } - constexpr nl_fptype m_vntol() { return nlconst::magic(1e-7); } - constexpr nl_fptype m_accuracy() { return nlconst::magic(1e-7); } - constexpr std::size_t m_nr_loops() { return 250; } - constexpr std::size_t m_gs_loops() { return 50; } + static constexpr nl_fptype m_gs_sor() { return nlconst::magic(1.059); } + static constexpr matrix_type_e m_method() { return matrix_type_e::MAT_CR; } + static constexpr matrix_fp_type_e m_fp_type() { return matrix_fp_type_e::DOUBLE; } + static constexpr nl_fptype m_reltol() { return nlconst::magic(1e-3); } + static constexpr nl_fptype m_vntol() { return nlconst::magic(1e-7); } + static constexpr nl_fptype m_accuracy() { return nlconst::magic(1e-7); } + static constexpr std::size_t m_nr_loops() { return 250; } + static constexpr std::size_t m_gs_loops() { return 50; } // general parameters - constexpr nl_fptype m_gmin() { return nlconst::magic(1e-9); } - constexpr bool m_pivot() { return false; } - constexpr nl_fptype m_nr_recalc_delay(){ return netlist_time::quantum().as_fp(); } - constexpr int m_parallel() { return 0; } + static constexpr nl_fptype m_gmin() { return nlconst::magic(1e-9); } + static constexpr bool m_pivot() { return false; } + static constexpr nl_fptype m_nr_recalc_delay(){ return netlist_time::quantum().as_fp(); } + static constexpr int m_parallel() { return 0; } - constexpr nl_fptype m_min_ts_ts() { return nlconst::magic(1e-9); } + static constexpr nl_fptype m_min_ts_ts() { return nlconst::magic(1e-9); } // automatic time step - constexpr bool m_dynamic_ts() { return false; } - constexpr nl_fptype m_dynamic_lte() { return nlconst::magic(1e-5); } - constexpr nl_fptype m_dynamic_min_ts() { return nlconst::magic(1e-6); } + static constexpr bool m_dynamic_ts() { return false; } + static constexpr nl_fptype m_dynamic_lte() { return nlconst::magic(1e-5); } + static constexpr nl_fptype m_dynamic_min_ts() { return nlconst::magic(1e-6); } // matrix sorting - constexpr matrix_sort_type_e m_sort_type() { return matrix_sort_type_e::PREFER_IDENTITY_TOP_LEFT; } + static constexpr matrix_sort_type_e m_sort_type() { return matrix_sort_type_e::PREFER_IDENTITY_TOP_LEFT; } // special - constexpr bool m_use_gabs() { return true; } + static constexpr bool m_use_gabs() { return true; } static solver_parameter_defaults &get_instance() { diff --git a/src/lib/netlist/solver/nld_ms_w.h b/src/lib/netlist/solver/nld_ms_w.h index da7a2c75a7a..49cb9dfa097 100644 --- a/src/lib/netlist/solver/nld_ms_w.h +++ b/src/lib/netlist/solver/nld_ms_w.h @@ -328,7 +328,7 @@ namespace solver } m_cnt++; - if (false) + if (false) // NOLINT for (unsigned i=0; i::zero(); diff --git a/src/lib/netlist/solver/nld_solver.cpp b/src/lib/netlist/solver/nld_solver.cpp index 9dee5a37f8b..89c2aa82c19 100644 --- a/src/lib/netlist/solver/nld_solver.cpp +++ b/src/lib/netlist/solver/nld_solver.cpp @@ -362,6 +362,7 @@ namespace devices auto &pt = dynamic_cast(*term); // check the connected terminal const auto *const connected_terminals = nlstate.setup().get_connected_terminals(pt); + // NOLINTNEXTLINE proposal does not work for VS for (auto ct = connected_terminals->begin(); *ct != nullptr; ct++) { analog_net_t &connected_net = (*ct)->net();