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.
This commit is contained in:
couriersud 2022-05-05 17:37:22 +02:00 committed by GitHub
parent 0818eafc2f
commit d20a1982da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 188 additions and 29 deletions

151
src/lib/netlist/FAQ.md Normal file
View File

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

View File

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

View File

@ -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<NT>(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<int>(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<pstring> &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;

View File

@ -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}>")

View File

@ -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<nl_fptype>(); }
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<nl_fptype>(); }
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()
{

View File

@ -328,7 +328,7 @@ namespace solver
}
m_cnt++;
if (false)
if (false) // NOLINT
for (unsigned i=0; i<iN; i++)
{
float_type tmp = plib::constants<FT>::zero();

View File

@ -362,6 +362,7 @@ namespace devices
auto &pt = dynamic_cast<terminal_t &>(*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();