Added truthtable implementations to a number of devices.

The combination of device activation, usage of GCC PMF extensions
and truthtables brings breakout to 95% speed up from about 75%.
Fixed NE555 initial state. [Couriersud]
This commit is contained in:
couriersud 2015-05-14 17:47:50 +02:00
parent 1ef199200c
commit e8acdc9b96
28 changed files with 363 additions and 59 deletions

View File

@ -9,16 +9,6 @@
#if (USE_TRUTHTABLE)
nld_7400::truthtable_t nld_7400::m_ttbl;
#if 0
const char *nld_7400::m_desc[] = {
"A,B,PQ,PA,PB|Q",
"0,X,X,X,X|1|22",
"X,0,X,X,X|1|22",
"1,1,X,X,X|0|15",
""
};
#else
const char *nld_7400::m_desc[] = {
"A , B | Q ",
"0,X|1|22",
@ -26,7 +16,6 @@ const char *nld_7400::m_desc[] = {
"1,1|0|15",
""
};
#endif
#endif

View File

@ -7,6 +7,18 @@
#include "nld_7402.h"
#if (USE_TRUTHTABLE)
nld_7402::truthtable_t nld_7402::m_ttbl;
const char *nld_7402::m_desc[] = {
"A , B | Q ",
"0,0|1|22",
"X,1|0|15",
"1,X|0|15",
""
};
#endif
NETLIB_START(7402_dip)
{
register_sub("1", m_1);

View File

@ -43,7 +43,12 @@
NET_REGISTER_DEV(7402_dip, _name)
#if (USE_TRUTHTABLE)
#include "nld_truthtable.h"
NETLIB_TRUTHTABLE(7402, 2, 1, 0);
#else
NETLIB_SIGNAL(7402, 2, 1, 0);
#endif
NETLIB_DEVICE(7402_dip,

View File

@ -7,6 +7,18 @@
#include "nld_7408.h"
#if (USE_TRUTHTABLE)
nld_7408::truthtable_t nld_7408::m_ttbl;
const char *nld_7408::m_desc[] = {
"A , B | Q ",
"X,0|0|15",
"0,X|0|15",
"1,1|1|22",
""
};
#endif
NETLIB_START(7408_dip)
{
register_sub("1", m_1);

View File

@ -39,7 +39,12 @@
NET_CONNECT(_name, A, _A) \
NET_CONNECT(_name, B, _B)
#if (USE_TRUTHTABLE)
#include "nld_truthtable.h"
NETLIB_TRUTHTABLE(7408, 2, 1, 0);
#else
NETLIB_SIGNAL(7408, 2, 0, 1);
#endif
#define TTL_7408_DIP(_name) \
NET_REGISTER_DEV(7408_dip, _name)

View File

@ -7,6 +7,20 @@
#include "nld_7410.h"
#if (USE_TRUTHTABLE)
nld_7410::truthtable_t nld_7410::m_ttbl;
const char *nld_7410::m_desc[] = {
"A,B,C|Q",
"0,X,X|1|22",
"X,0,X|1|22",
"X,X,0|1|22",
"1,1,1|0|15",
""
};
#endif
NETLIB_START(7410_dip)
{
register_sub("1", m_1);

View File

@ -40,7 +40,12 @@
NET_CONNECT(_name, B, _I2) \
NET_CONNECT(_name, C, _I3)
#if (USE_TRUTHTABLE)
#include "nld_truthtable.h"
NETLIB_TRUTHTABLE(7410, 3, 1, 0);
#else
NETLIB_SIGNAL(7410, 3, 0, 0);
#endif
#define TTL_7410_DIP(_name) \
NET_REGISTER_DEV(7410_dip, _name)

View File

@ -7,6 +7,18 @@
#include "nld_7411.h"
#if (USE_TRUTHTABLE)
nld_7411::truthtable_t nld_7411::m_ttbl;
const char *nld_7411::m_desc[] = {
"A,B,C|Q",
"0,X,X|0|15",
"X,0,X|0|15",
"X,X,0|0|15",
"1,1,1|1|22",
""
};
#endif
NETLIB_START(7411_dip)
{
register_sub("1", m_1);

View File

@ -40,7 +40,12 @@
NET_CONNECT(_name, B, _I2) \
NET_CONNECT(_name, C, _I3)
#if (USE_TRUTHTABLE)
#include "nld_truthtable.h"
NETLIB_TRUTHTABLE(7411, 3, 1, 0);
#else
NETLIB_SIGNAL(7411, 3, 0, 1);
#endif
#define TTL_7411_DIP(_name) \
NET_REGISTER_DEV(7411_dip, _name)

View File

@ -7,6 +7,19 @@
#include "nld_7420.h"
#if (USE_TRUTHTABLE)
nld_7420::truthtable_t nld_7420::m_ttbl;
const char *nld_7420::m_desc[] = {
"A,B,C,D|Q",
"0,X,X,X|1|22",
"X,0,X,X|1|22",
"X,X,0,X|1|22",
"X,X,X,0|1|22",
"1,1,1,1|0|15",
""
};
#endif
NETLIB_START(7420_dip)
{
register_sub("1", m_1);

View File

@ -43,7 +43,12 @@
NET_CONNECT(_name, D, _I4)
#if (USE_TRUTHTABLE)
#include "nld_truthtable.h"
NETLIB_TRUTHTABLE(7420, 4, 1, 0);
#else
NETLIB_SIGNAL(7420, 4, 0, 0);
#endif
#define TTL_7420_DIP(_name) \
NET_REGISTER_DEV(7420_dip, _name)

View File

@ -7,6 +7,20 @@
#include "nld_7427.h"
#include "nld_7402.h"
#if (USE_TRUTHTABLE)
nld_7427::truthtable_t nld_7427::m_ttbl;
const char *nld_7427::m_desc[] = {
"A,B,C|Q ",
"0,0,0|1|22",
"1,X,X|0|15",
"X,1,X|0|15",
"X,X,1|0|15",
""
};
#endif
NETLIB_START(7427_dip)
{
register_sub("1", m_1);

View File

@ -40,7 +40,12 @@
NET_CONNECT(_name, B, _I2) \
NET_CONNECT(_name, C, _I3)
#if (USE_TRUTHTABLE)
#include "nld_truthtable.h"
NETLIB_TRUTHTABLE(7427, 3, 1, 0);
#else
NETLIB_SIGNAL(7427, 3, 1, 0);
#endif
#define TTL_7427_DIP(_name) \
NET_REGISTER_DEV(7427_dip, _name)

View File

@ -8,7 +8,9 @@
#include "nld_74279.h"
nld_74279A::truthtable_t nld_74279A::m_ttbl;
nld_74279B::truthtable_t nld_74279B::m_ttbl;
#if 0
const char *nld_74279A::m_desc[] = {
"S,R,PQ,PS,PR|Q",
"0,0,X,X,X|1|22",
@ -19,7 +21,6 @@ const char *nld_74279A::m_desc[] = {
""
};
nld_74279B::truthtable_t nld_74279B::m_ttbl;
const char *nld_74279B::m_desc[] = {
"S1,S2,R,PQ,PS1,PS2,PR|Q",
@ -32,6 +33,29 @@ const char *nld_74279B::m_desc[] = {
"1,1,1,1,X,X,X|1|22",
""
};
#else
const char *nld_74279A::m_desc[] = {
"S,R,_Q|Q",
"0,X,X|1|22",
"1,0,X|0|27",
"1,1,0|0|27", //15
"1,1,1|1|22",
""
};
const char *nld_74279B::m_desc[] = {
"S1,S2,R,_Q|Q",
"0,X,X,X|1|22",
"X,0,X,X|1|22",
"1,1,0,X|0|27",
"1,1,1,0|0|27", // 15
"1,1,1,1|1|22",
""
};
#endif
NETLIB_START(74279_dip)
{

View File

@ -3,7 +3,7 @@
/*
* nld_74279.h
*
* DM74279: Triple 3-Input NAND Gates
* DM74279: Quad S-R Latch
*
* +--------------+
* 1R |1 ++ 16| VCC
@ -38,9 +38,13 @@
#include "nld_truthtable.h"
#if 0
NETLIB_TRUTHTABLE(74279A, 2, 1, 1);
NETLIB_TRUTHTABLE(74279B, 3, 1, 1);
#else
NETLIB_TRUTHTABLE(74279A, 3, 1, 0);
NETLIB_TRUTHTABLE(74279B, 4, 1, 0);
#endif
#define TTL_74279_DIP(_name) \
NET_REGISTER_DEV(74279_dip, _name)

View File

@ -7,6 +7,25 @@
#include "nld_7430.h"
#if (USE_TRUTHTABLE)
nld_7430::truthtable_t nld_7430::m_ttbl;
const char *nld_7430::m_desc[] = {
"A,B,C,D,E,F,G,H|Q ",
"0,X,X,X,X,X,X,X|1|22",
"X,0,X,X,X,X,X,X|1|22",
"X,X,0,X,X,X,X,X|1|22",
"X,X,X,0,X,X,X,X|1|22",
"X,X,X,X,0,X,X,X|1|22",
"X,X,X,X,X,0,X,X|1|22",
"X,X,X,X,X,X,0,X|1|22",
"X,X,X,X,X,X,X,0|1|22",
"1,1,1,1,1,1,1,1|0|15",
""
};
#endif
NETLIB_START(7430_dip)
{
register_sub("1", m_1);

View File

@ -51,7 +51,12 @@
NET_CONNECT(_name, H, _I8)
#if (USE_TRUTHTABLE)
#include "nld_truthtable.h"
NETLIB_TRUTHTABLE(7430, 8, 1, 0);
#else
NETLIB_SIGNAL(7430, 8, 0, 0);
#endif
#define TTL_7430_DIP(_name) \
NET_REGISTER_DEV(7430_dip, _name)

View File

@ -7,6 +7,18 @@
#include "nld_7432.h"
#if (USE_TRUTHTABLE)
nld_7432::truthtable_t nld_7432::m_ttbl;
const char *nld_7432::m_desc[] = {
"A,B|Q ",
"1,X|1|22",
"0,1|1|22",
"0,0|0|15",
""
};
#endif
NETLIB_START(7432_dip)
{
register_sub("1", m_1);

View File

@ -43,7 +43,12 @@
NET_REGISTER_DEV(7432_dip, _name)
#if (USE_TRUTHTABLE)
#include "nld_truthtable.h"
NETLIB_TRUTHTABLE(7432, 2, 1, 0);
#else
NETLIB_SIGNAL(7432, 2, 1, 1);
#endif
NETLIB_DEVICE(7432_dip,

View File

@ -22,7 +22,7 @@
*/
#include "nld_9312.h"
#if (USE_TRUTHTABLE)
#if (1 && USE_TRUTHTABLE)
nld_9312::truthtable_t nld_9312::m_ttbl;
/* FIXME: Data changes are propagating faster than changing selects A,B,C
@ -57,25 +57,26 @@ const char *nld_9312::m_desc[] = {
NETLIB_UPDATE(9312)
{
const UINT8 G = INPLOGIC(m_G);
if ((m_last_G ^ G) == 1)
if (G)
{
static const netlist_time delay[2] = { NLTIME_FROM_NS(33), NLTIME_FROM_NS(19) };
OUTLOGIC(m_Y, !G, delay[!G]);
OUTLOGIC(m_YQ, G, delay[G]);
if (G)
OUTLOGIC(m_Y, 0, delay[0]);
OUTLOGIC(m_YQ, 1, delay[1]);
m_A.inactivate();
m_B.inactivate();
m_C.inactivate();
m_last_G = G;
}
else
{
if (m_last_G)
{
m_A.inactivate();
m_B.inactivate();
m_C.inactivate();
} else {
m_last_G = G;
m_A.activate();
m_B.activate();
m_C.activate();
}
m_last_G = G;
}
if (!G)
{
static const netlist_time delay[2] = { NLTIME_FROM_NS(33), NLTIME_FROM_NS(28) };
const UINT8 chan = INPLOGIC(m_A) | (INPLOGIC(m_B)<<1) | (INPLOGIC(m_C)<<2);
if (m_last_chan != chan)
@ -125,7 +126,7 @@ NETLIB_START(9312_dip)
{
register_sub("1", m_sub);
#if (USE_TRUTHTABLE)
#if (1 && USE_TRUTHTABLE)
register_subalias("13", m_sub.m_i[0]);
register_subalias("12", m_sub.m_i[1]);

View File

@ -43,6 +43,10 @@
NET_REGISTER_DEV(9312, _name)
#if (USE_TRUTHTABLE)
#include "nld_truthtable.h"
/* The truthtable implementation is a lot faster than
* the carefully crafted code :-(
*/
NETLIB_TRUTHTABLE(9312, 12, 2, 0);
#else

View File

@ -60,7 +60,7 @@ NETLIB_RESET(NE555)
m_R3.set_R(5000);
m_RDIS.set_R(R_OFF);
m_last_out = false;
m_last_out = true;
}
NETLIB_UPDATE(NE555)

View File

@ -24,6 +24,35 @@
}
static inline UINT32 remove_first_bit(UINT32 v)
{
for (int i=0; i<32; i++)
if (v & (1<<i))
return v & ~(1<<i);
return v;
}
static inline int count_bits(UINT32 v)
{
int ret = 0;
for (int i=0; i<32; i++)
if (v & (1<<i))
ret++;
return ret;
}
static inline UINT32 set_bits(UINT32 v, UINT32 b)
{
UINT32 ret = 0;
for (int i=0; i<32; i++)
if (v & (1<<i))
{
ret |= ((b&1)<<i);
b = b >> 1;
}
return ret;
}
template<int m_NI, int m_NO, int has_state>
class nld_truthtable_t : public netlist_device_t
{
@ -36,7 +65,7 @@ public:
{
truthtable_t() : m_initialized(false) {}
UINT32 m_outs[m_size];
UINT8 m_timing[m_size][m_NO];
UINT8 m_timing[m_size * m_NO];
netlist_time m_timing_nt[16];
bool m_initialized;
};
@ -49,22 +78,37 @@ public:
ATTR_COLD virtual void start()
{
pstring ttline = pstring(m_desc[0]);
{
nl_util::pstring_list io = nl_util::split(ttline,"|");
// checks
nl_assert_always(io.count() == 2, "too many '|'");
nl_util::pstring_list inout = nl_util::split(io[0], ",");
nl_assert_always(inout.count() == m_num_bits, "bitcount wrong");
nl_util::pstring_list out = nl_util::split(io[1], ",");
nl_assert_always(out.count() == m_NO, "output count wrong");
for (int i=0; i < m_NI; i++)
{
register_input(inout[i].trim(), m_i[i]);
}
nl_util::pstring_list io = nl_util::split(ttline,"|");
// checks
nl_assert_always(io.count() == 2, "too many '|'");
nl_util::pstring_list inout = nl_util::split(io[0], ",");
nl_assert_always(inout.count() == m_num_bits, "bitcount wrong");
nl_util::pstring_list out = nl_util::split(io[1], ",");
nl_assert_always(out.count() == m_NO, "output count wrong");
for (int i=0; i < m_NI; i++)
{
inout[i] = inout[i].trim();
register_input(inout[i], m_i[i]);
}
for (int i=0; i < m_NO; i++)
{
out[i] = out[i].trim();
register_output(out[i], m_Q[i]);
}
{
// Connect output "Q" to input "_Q" if this exists
// This enables timed state without having explicit state ....
for (int i=0; i < m_NO; i++)
{
register_output(out[i].trim(), m_Q[i]);
pstring tmp = "_" + out[i];
const int idx = inout.indexof(tmp);
if (idx>=0)
{
//printf("connecting %s %d\n", out[i].cstr(), idx);
connect(m_Q[i], m_i[idx]);
}
}
}
m_ign = 0;
@ -73,12 +117,11 @@ public:
}
ATTR_COLD void help(int cur, nl_util::pstring_list list,
UINT64 state, UINT64 ignore, UINT16 val, UINT8 timing_index[m_NO])
UINT64 state,UINT16 val, UINT8 timing_index[m_NO])
{
pstring elem = list[cur].trim();
int start = 0;
int end = 0;
int ign = 0;
if (elem.equals("0"))
{
@ -94,25 +137,23 @@ public:
{
start = 0;
end = 1;
ign = 1;
}
else
nl_assert_always(false, "unknown input value (not 0, 1, or X)");
for (int i = start; i <= end; i++)
{
const UINT64 nstate = state | (i << cur);
const UINT64 nignore = ignore | (ign << cur);
if (cur < m_num_bits - 1)
{
help(cur + 1, list, nstate, nignore, val, timing_index);
help(cur + 1, list, nstate, val, timing_index);
}
else
{
// cutoff previous inputs and outputs for ignore
m_ttp->m_outs[nstate] = val | ((nignore & ((1 << m_NI)-1)) << m_NO);
m_ttp->m_outs[nstate] = val;
for (int j=0; j<m_NO; j++)
m_ttp->m_timing[nstate][j] = timing_index[j];
m_ttp->m_timing[nstate * m_NO + j] = timing_index[j];
}
}
}
@ -163,11 +204,72 @@ public:
tindex[j] = k;
}
help(0, inout, 0 , 0 , val, tindex);
help(0, inout, 0 , val, tindex);
ttline = pstring(truthtable[0]);
truthtable++;
}
// ((nignore & ((1 << m_NI)-1)) << m_NO)
// determine ignore
UINT32 ign[m_size];
for (int i=0; i<m_size; i++)
{
#if 1
UINT32 m_enable = 0;
for (int j=0; j<m_size; j++)
{
if (m_ttp->m_outs[j] != m_ttp->m_outs[i])
{
m_enable |= (i ^ j);
}
}
ign[i] = m_enable ^ (m_size - 1);
#else
// this doesn't work in all cases and is dog slow
UINT32 nign = 0;
for (int j=0; j<m_NI; j++)
{
if (m_ttp->m_outs[i] == m_ttp->m_outs[i ^ (1 << j)])
nign |= (1<<j);
}
/* Check all permutations of ign
*/
int t[1<<count_bits(nign)];
for (UINT32 j=0; j<(1<<count_bits(nign)); j++)
{
UINT32 tign = set_bits(nign, j);
t[j] = 0;
for (UINT32 k=0; k<(1<<count_bits(tign)); k++)
{
UINT32 b=set_bits(tign, k);
if (m_ttp->m_outs[i] != m_ttp->m_outs[(i & ~tign) | b])
t[j] = 1;
}
//printf("x %d %d\n", j, tign);
}
UINT32 jb=0;
UINT32 jm=0;
for (UINT32 j=0; j<(1<<count_bits(nign)); j++)
{
int nb = count_bits(j);
if ((t[j] == 0)&& (nb>jb))
{
jb = nb;
jm = j;
}
}
ign[i] = set_bits(nign, jm);
#endif
}
for (int i=0; i<m_size; i++)
{
m_ttp->m_outs[i] |= (ign[i] << m_NO);
}
#if 0
printf("%s\n", name().cstr());
for (int j=0; j < m_size; j++)
printf("%05x %04x %04x %04x\n", j, m_ttp->m_outs[j] & ((1 << m_NO)-1),
m_ttp->m_outs[j] >> m_NO, m_ttp->m_timing[j][0]);
@ -217,14 +319,15 @@ public:
if (m_ign & (1 << i))
m_i[i].inactivate();
const UINT32 timebase = nstate * m_NO;
if (doOUT)
{
for (int i = 0; i < m_NO; i++)
OUTLOGIC(m_Q[i], (out >> i) & 1, m_ttp->m_timing_nt[m_ttp->m_timing[nstate][i]]);
OUTLOGIC(m_Q[i], (out >> i) & 1, m_ttp->m_timing_nt[m_ttp->m_timing[timebase + i]]);
}
else
for (int i = 0; i < m_NO; i++)
m_Q[i].net().set_Q_time((out >> i) & 1, mt + m_ttp->m_timing_nt[m_ttp->m_timing[nstate][i]]);
m_Q[i].net().set_Q_time((out >> i) & 1, mt + m_ttp->m_timing_nt[m_ttp->m_timing[timebase + i]]);
}

View File

@ -386,8 +386,12 @@ ATTR_COLD void netlist_core_device_t::init(netlist_base_t &anetlist, const pstri
#if USE_PMFDELEGATES
void (netlist_core_device_t::* pFunc)() = &netlist_core_device_t::update;
#if NO_USE_PMFCONVERSION
static_update = pFunc;
#else
static_update = reinterpret_cast<net_update_delegate>((this->*pFunc));
#endif
#endif
}
@ -1019,7 +1023,10 @@ ATTR_COLD nl_double netlist_param_model_t::model_value(const pstring &entity, co
return atof(tmp.cstr()) * factor;
}
else
{
netlist().log("Entity %s not found in model %s\n", entity.cstr(), tmp.cstr());
return defval;
}
}

View File

@ -173,8 +173,12 @@
class netlist_core_device_t;
#if USE_PMFDELEGATES
#if NO_USE_PMFCONVERSION
typedef void (netlist_core_device_t::*net_update_delegate)();
#else
typedef void (*net_update_delegate)(netlist_core_device_t *);
#endif
#endif
//============================================================
// MACROS / netlist devices
@ -972,7 +976,11 @@ public:
begin_timing(stat_total_time);
inc_stat(stat_update_count);
#if USE_PMFDELEGATES
#if NO_USE_PMFCONVERSION
(this->*static_update)();
#else
static_update(this);
#endif
#else
update();
#endif

View File

@ -23,13 +23,28 @@
/*
* The next options needs -Wno-pmf-conversions to compile and gcc
* This is intended for non-mame usage.
* There is quite some significant speed-up of up to 20% involved.
* NO_USE_PMFCONVERSION is for illustrative purposes only. Using PMFs
* has some overhead in comparison to calling a virtual function.
*
* To get a performance increase we need the GCC extension.
*
*/
#define USE_PMFDELEGATES (0)
#if (USE_PMFDELEGATES)
#pragma GCC diagnostic ignored "-Wpmf-conversions"
#ifndef USE_PMFDELEGATES
#if defined(__clang__)
#define USE_PMFDELEGATES (0)
#define NO_USE_PMFCONVERSION (1)
#else
#if defined(__GNUC__)
#define USE_PMFDELEGATES (1)
#define NO_USE_PMFCONVERSION (0)
#pragma GCC diagnostic ignored "-Wpmf-conversions"
#else
#define USE_PMFDELEGATES (0)
#define NO_USE_PMFCONVERSION (1)
#endif
#endif
#endif
/*
@ -44,7 +59,7 @@
// moved to parameter NETLIST.USE_DEACTIVATE
// #define USE_DEACTIVE_DEVICE (0)
#define USE_TRUTHTABLE (0)
#define USE_TRUTHTABLE (1)
// The following adds about 10% performance ...

View File

@ -114,7 +114,7 @@ CIRCUIT_LAYOUT( breakout )
// FIXME: PARALLEL Doesn't work in breakout.
PARAM(Solver.PARALLEL, 0)
#endif
//PARAM(NETLIST.USE_DEACTIVATE, 1)
PARAM(NETLIST.USE_DEACTIVATE, 1)
// DIPSWITCH - Free game
SWITCH(S1_1)

View File

@ -14,6 +14,7 @@ NETLIST_START(pong_fast)
PARAM(Solver.PARALLEL, 0) // Don't do parallel solvers
PARAM(Solver.ACCURACY, 1e-4) // works and is sufficient
PARAM(Solver.LTE, 1e-4) // Default is not enough for paddle control if using LTE
PARAM(NETLIST.USE_DEACTIVATE, 1)
ANALOG_INPUT(V5, 5)