netlist: include file work (#10096)

- move code to more appropriate locations
- apply clang-format to modified files
- fixed some cspell errors
- Applied emu.h rule.
This commit is contained in:
couriersud 2022-07-16 22:48:22 +02:00 committed by GitHub
parent 20d6ea06f4
commit 4ecc148240
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 1221 additions and 954 deletions

View File

@ -11,14 +11,11 @@
#include "emu.h" #include "emu.h"
#include "netlist.h" #include "netlist.h"
#include "netlist/nl_base.h"
#include "netlist/nl_setup.h" #include "netlist/nl_setup.h"
#include "netlist/nl_factory.h" #include "netlist/nl_factory.h"
#include "netlist/nl_parser.h" #include "netlist/nl_parser.h"
#include "netlist/nl_interface.h" #include "netlist/nl_interface.h"
#include "netlist/plib/palloc.h"
#include "netlist/plib/pmempool.h"
#include "netlist/plib/pdynlib.h" #include "netlist/plib/pdynlib.h"
#include "netlist/plib/pstonum.h" #include "netlist/plib/pstonum.h"

View File

@ -2,24 +2,26 @@
// copyright-holders:Couriersud // copyright-holders:Couriersud
/*************************************************************************** /***************************************************************************
fixfreq.h fixfreq.h
2013-2021 Couriersud 2013-2021 Couriersud
Fixed frequency monochrome monitor emulation Fixed frequency monochrome monitor emulation
The driver is intended for drivers which provide an analog video signal. The driver is intended for drivers which provide an analog video signal.
VSYNC and HSYNC levels are used to create the bitmap. VSYNC and HSYNC levels are used to create the bitmap.
***************************************************************************/ ***************************************************************************/
// emu.h must be first to be included
#include "emu.h" #include "emu.h"
#include "fixfreq.h" #include "fixfreq.h"
#include "render.h" #include "render.h"
#include "ui/uimain.h" #include "ui/uimain.h"
#include <cstdio> #include <iostream>
// for quick and dirty debugging // for quick and dirty debugging
#define VERBOSE 0 #define VERBOSE 0
@ -36,20 +38,24 @@
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// device type definition // device type definition
DEFINE_DEVICE_TYPE(FIXFREQ, fixedfreq_device, "fixfreq", "Fixed-Frequency Monochrome Monitor") DEFINE_DEVICE_TYPE(FIXFREQ, fixedfreq_device, "fixfreq",
"Fixed-Frequency Monochrome Monitor")
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// Port adjuster support // Port adjuster support
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
#define PORT_ADJUSTERX(_id, _name, _min, _max) \ #define PORT_ADJUSTERX(_id, _name, _min, _max) \
PORT_START(# _id) \ PORT_START(#_id) \
configurer.field_alloc(IPT_ADJUSTER, (static_cast<fixedfreq_device &>(owner).monitor_val(_id)), 0xffff, ("Monitor - " _name)); \ configurer.field_alloc( \
PORT_MINMAX(_min, _max) \ IPT_ADJUSTER, \
PORT_CHANGED_MEMBER(DEVICE_SELF, fixedfreq_device, port_changed, _id) \ (static_cast<fixedfreq_device &>(owner).monitor_val(_id)), 0xffff, \
("Monitor - " _name)); \
PORT_MINMAX(_min, _max) \
PORT_CHANGED_MEMBER(DEVICE_SELF, fixedfreq_device, port_changed, _id) \
PORT_CONDITION("ENABLE", 0x01, EQUALS, 0x01) PORT_CONDITION("ENABLE", 0x01, EQUALS, 0x01)
#define IOPORT_ID(_id) ioport(# _id) #define IOPORT_ID(_id) ioport(#_id)
enum fixedfreq_tag_id_e enum fixedfreq_tag_id_e
{ {
@ -67,30 +73,36 @@ enum fixedfreq_tag_id_e
SCANLINE_HEIGHT SCANLINE_HEIGHT
}; };
void fixedfreq_monitor_state::update_sync_channel(const time_type &time, const double newval) void fixedfreq_monitor_state::update_sync_channel(const time_type &time,
const double newval)
{ {
const time_type delta_time = time - m_last_sync_time; const time_type delta_time = time - m_last_sync_time;
const int last_vsync = m_sig_vsync; const int last_vsync = m_sig_vsync;
const int last_comp = m_sig_composite; const int last_comp = m_sig_composite;
m_vsync_filter += ((double) last_comp - m_vsync_filter) * (1.0 - exp(-delta_time * m_desc.vsync_filter_timeconst())); m_vsync_filter += ((double)last_comp - m_vsync_filter)
m_sig_composite = (newval < m_desc.m_sync_threshold) ? 1 : 0 ; * (1.0
- exp(-delta_time * m_desc.vsync_filter_timeconst()));
m_sig_composite = (newval < m_desc.m_sync_threshold) ? 1 : 0;
m_sig_vsync = (m_vsync_filter > m_desc.m_vsync_threshold) ? 1 : 0; m_sig_vsync = (m_vsync_filter > m_desc.m_vsync_threshold) ? 1 : 0;
if (!last_vsync && m_sig_vsync) if (!last_vsync && m_sig_vsync)
{ {
LOG("VSYNC UP %f %d\n", m_last_x, m_last_y); LOG("VSYNC UP %f %d\n", m_last_x, m_last_y);
const int has_fields = (m_desc.m_fieldcount > 1) ? 1: 0; const int has_fields = (m_desc.m_fieldcount > 1) ? 1 : 0;
// FIXME: add modes: true interlaced, overlayed, false progressive (see popeye video) // FIXME: add modes: true interlaced, overlayed, false progressive (see
// popeye video)
if (has_fields) if (has_fields)
{ {
const auto avg_line_dur = (time - m_last_field_time) * m_desc.m_fieldcount / (m_last_y + 1); const auto avg_line_dur = (time - m_last_field_time)
* m_desc.m_fieldcount / (m_last_y + 1);
m_last_field_time = time; m_last_field_time = time;
m_sig_field = avg_line_dur * 0.75 > m_last_line_duration; m_sig_field = avg_line_dur * 0.75 > m_last_line_duration;
LOG("%d %f %f %f\n", m_sig_field, m_last_line_duration, avg_line_dur, time); LOG("%d %f %f %f\n", m_sig_field, m_last_line_duration,
avg_line_dur, time);
} }
// notify the controlling device about the vsync and the field. // notify the controlling device about the vsync and the field.
@ -108,12 +120,13 @@ void fixedfreq_monitor_state::update_sync_channel(const time_type &time, const d
{ {
if (m_sig_vsync) if (m_sig_vsync)
LOG("Hsync in vsync\n"); LOG("Hsync in vsync\n");
//LOG("HSYNC up %d\n", m_last_x); // LOG("HSYNC up %d\n", m_last_x);
// FIXME: pixels > 0 filters some spurious hysnc on line 23/24 in breakout // FIXME: pixels > 0 filters some spurious hysnc on line
// The hsync signal transition from high to low is 7 pixels too // 23/24 in breakout
// early, goes up again after 6.8 pix and down after 7.2 pix. // The hsync signal transition from high to low is 7 pixels too
// Therefore we need to filter early low to high transitions // early, goes up again after 6.8 pix and down after 7.2 pix.
// and base hsync on the start of the hsync signal. // Therefore we need to filter early low to high transitions
// and base hsync on the start of the hsync signal.
if (!m_sig_vsync && (m_last_x > 0)) if (!m_sig_vsync && (m_last_x > 0))
{ {
m_last_y += m_desc.m_fieldcount; m_last_y += m_desc.m_fieldcount;
@ -122,13 +135,12 @@ void fixedfreq_monitor_state::update_sync_channel(const time_type &time, const d
m_last_line_duration = time - m_last_hsync_time; m_last_line_duration = time - m_last_hsync_time;
m_last_hsync_time = time; m_last_hsync_time = time;
} }
} }
else if (last_comp && !m_sig_composite) else if (last_comp && !m_sig_composite)
{ {
/* falling composite */ /* falling composite */
//LOG("HSYNC down %f %d %f\n", time * 1e6, m_last_x, m_sync_signal); // LOG("HSYNC down %f %d %f\n", time * 1e6, m_last_x, m_sync_signal);
} }
m_last_sync_val = newval; m_last_sync_val = newval;
m_last_sync_time = time; m_last_sync_time = time;
@ -136,99 +148,109 @@ void fixedfreq_monitor_state::update_sync_channel(const time_type &time, const d
void fixedfreq_monitor_state::update_bm(const time_type &time) void fixedfreq_monitor_state::update_bm(const time_type &time)
{ {
const float pixels = (time - m_line_time) * (double) m_desc.monitor_clock(); const float pixels = (time - m_line_time) * (double)m_desc.monitor_clock();
const int has_fields = (m_desc.m_fieldcount > 1) ? 1: 0; const int has_fields = (m_desc.m_fieldcount > 1) ? 1 : 0;
const float fhscale(static_cast<float>(m_desc.m_hscale)); const float fhscale(static_cast<float>(m_desc.m_hscale));
//uint32_t col(0xffff0000); // Mark sync areas // uint32_t col(0xffff0000); // Mark sync areas
//if (m_last_sync >= m_desc.m_sync_threshold) // if (m_last_sync >= m_desc.m_sync_threshold)
// col = m_col; // col = m_col;
if (!m_sig_vsync && !m_sig_composite) if (!m_sig_vsync && !m_sig_composite)
{ {
//uint32_t mask = m_sig_field ? 0xffffffff : 0xffff0000; // uint32_t mask = m_sig_field ? 0xffffffff : 0xffff0000;
m_fragments.push_back({static_cast<float>(m_last_y + m_sig_field * has_fields), m_fragments.push_back(
m_last_x * fhscale, pixels * fhscale, m_col}); // & mask}); {static_cast<float>(m_last_y + m_sig_field * has_fields),
m_last_x * fhscale, pixels * fhscale, m_col}); // & mask});
} }
//m_intf.plot_hline(m_last_x, m_last_y + m_sig_field * has_fields, pixels, col); // m_intf.plot_hline(m_last_x, m_last_y + m_sig_field * has_fields, pixels,
// col);
m_last_x = pixels; m_last_x = pixels;
} }
void fixedfreq_monitor_state::update_composite_monochrome(const time_type &time, const double data) void fixedfreq_monitor_state::update_composite_monochrome(const time_type &time,
const double data)
{ {
update_bm(time); update_bm(time);
update_sync_channel(time, data); update_sync_channel(time, data);
//int colv = (int) ((data - m_desc.m_sync_threshold) * m_desc.m_gain * 255.0); //#int colv = (int) ((data - m_desc.m_sync_threshold) * m_desc.m_gain * 255.0);
int colv = (int) ((data - 1.5) * m_desc.m_gain * 255.0); int colv = (int)((data - 1.5) * m_desc.m_gain * 255.0);
if (colv > 255) if (colv > 255)
colv = 255; colv = 255;
if (colv < 0) if (colv < 0)
//m_col = 0xffff0000; // m_col = 0xffff0000;
m_col = 0x0000000; m_col = 0x0000000;
else else
m_col = 0xff000000 | (colv<<16) | (colv<<8) | colv; m_col = 0xff000000 | (colv << 16) | (colv << 8) | colv;
} }
void fixedfreq_monitor_state::update_red(const time_type &time, const double data) void fixedfreq_monitor_state::update_red(const time_type &time,
const double data)
{ {
update_bm(time); update_bm(time);
int colv = (int) ((data - m_desc.m_sync_threshold) * m_desc.m_gain * 255.0); int colv = (int)((data - m_desc.m_sync_threshold) * m_desc.m_gain * 255.0);
if (colv > 255) if (colv > 255)
colv = 255; colv = 255;
if (colv < 0) if (colv < 0)
colv = 0; colv = 0;
m_col = (m_col & 0xff00ffff) | (colv<<16); m_col = (m_col & 0xff00ffff) | (colv << 16);
} }
void fixedfreq_monitor_state::update_green(const time_type &time, const double data) void fixedfreq_monitor_state::update_green(const time_type &time,
const double data)
{ {
update_bm(time); update_bm(time);
//update_sync_channel(ctime, data); // update_sync_channel(ctime, data);
int colv = (int) ((data - m_desc.m_sync_threshold) * m_desc.m_gain * 255.0); int colv = (int)((data - m_desc.m_sync_threshold) * m_desc.m_gain * 255.0);
if (colv > 255) if (colv > 255)
colv = 255; colv = 255;
if (colv < 0) if (colv < 0)
colv = 0; colv = 0;
m_col = (m_col & 0xffff00ff) | (colv<<8); m_col = (m_col & 0xffff00ff) | (colv << 8);
} }
void fixedfreq_monitor_state::update_blue(const time_type &time, const double data) void fixedfreq_monitor_state::update_blue(const time_type &time,
const double data)
{ {
update_bm(time); update_bm(time);
//update_sync_channel(ctime, data); // update_sync_channel(ctime, data);
int colv = (int) ((data - m_desc.m_sync_threshold) * m_desc.m_gain * 255.0); int colv = (int)((data - m_desc.m_sync_threshold) * m_desc.m_gain * 255.0);
if (colv > 255) if (colv > 255)
colv = 255; colv = 255;
if (colv < 0) if (colv < 0)
colv = 0; colv = 0;
m_col = (m_col & 0xffffff00) | colv; m_col = (m_col & 0xffffff00) | colv;
} }
void fixedfreq_monitor_state::update_sync(const time_type &time, const double data) void fixedfreq_monitor_state::update_sync(const time_type &time,
const double data)
{ {
update_bm(time); update_bm(time);
update_sync_channel(time, data); update_sync_channel(time, data);
} }
fixedfreq_device::fixedfreq_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) fixedfreq_device::fixedfreq_device(const machine_config &mconfig,
: device_t(mconfig, type, tag, owner, clock), device_type type, const char *tag,
device_video_interface(mconfig, *this, false), device_t *owner, uint32_t clock)
m_enable(*this, "ENABLE"), : device_t(mconfig, type, tag, owner, clock)
m_vector(*this, "VECTOR"), , device_video_interface(mconfig, *this, false)
m_scanline_height(1.0), , m_enable(*this, "ENABLE")
m_last_rt(0.0), , m_vector(*this, "VECTOR")
m_monitor(), , m_scanline_height(1.0)
m_state(m_monitor, *this) , m_last_rt(0.0)
, m_monitor()
, m_state(m_monitor, *this)
{ {
} }
fixedfreq_device::fixedfreq_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) fixedfreq_device::fixedfreq_device(const machine_config &mconfig,
: fixedfreq_device(mconfig, FIXFREQ, tag, owner, clock) const char *tag, device_t *owner,
uint32_t clock)
: fixedfreq_device(mconfig, FIXFREQ, tag, owner, clock)
{ {
} }
@ -251,10 +273,11 @@ void fixedfreq_device::device_config_complete()
// to specify the window size. // to specify the window size.
if (!screen().refresh_attoseconds()) if (!screen().refresh_attoseconds())
screen().set_raw(m_monitor.m_monitor_clock, m_monitor.htotal(), 0, screen().set_raw(m_monitor.m_monitor_clock, m_monitor.htotal(), 0,
m_monitor.htotal(), m_monitor.vtotal(), 0, m_monitor.htotal(), m_monitor.vtotal(), 0,
m_monitor.vtotal()); m_monitor.vtotal());
if (!screen().has_screen_update()) if (!screen().has_screen_update())
screen().set_screen_update(*this, FUNC(fixedfreq_device::screen_update)); screen().set_screen_update(*this,
FUNC(fixedfreq_device::screen_update));
LOG("config complete\n"); LOG("config complete\n");
} }
@ -288,7 +311,7 @@ void fixedfreq_device::device_reset()
{ {
m_state.reset(); m_state.reset();
LOG("Reset\n"); LOG("Reset\n");
//ioport("YYY")->field(0xffff)->live().value = 20; // ioport("YYY")->field(0xffff)->live().value = 20;
#if 0 #if 0
//IOPORT_ID(HVISIBLE)->field(~0)->set_value(m_monitor.m_hvisible); //IOPORT_ID(HVISIBLE)->field(~0)->set_value(m_monitor.m_hvisible);
//IOPORT_ID(HVISIBLE)->update_defvalue(false); //IOPORT_ID(HVISIBLE)->update_defvalue(false);
@ -307,36 +330,39 @@ void fixedfreq_device::device_reset()
void fixedfreq_device::device_post_load() void fixedfreq_device::device_post_load()
{ {
//recompute_parameters(); // recompute_parameters();
LOG("post load\n"); LOG("post load\n");
} }
static uint32_t nom_col(uint32_t col) static uint32_t nom_col(uint32_t col)
{ {
float r = ((col >> 16) & 0xff); float r = ((col >> 16) & 0xff);
float g = ((col >> 8) & 0xff); float g = ((col >> 8) & 0xff);
float b = ((col >> 0) & 0xff); float b = ((col >> 0) & 0xff);
float m = std::max(r, std::max(g,b)); float m = std::max(r, std::max(g, b));
if (m == 0.0f) if (m == 0.0f)
return 0; return 0;
return (((uint32_t) m ) << 24) | (((uint32_t) (r/m*255.0f) ) << 16) return (((uint32_t)m) << 24) | (((uint32_t)(r / m * 255.0f)) << 16)
| (((uint32_t) (g/m*255.0f) ) << 8) | (((uint32_t) (b/m*255.0f) ) << 0); | (((uint32_t)(g / m * 255.0f)) << 8)
| (((uint32_t)(b / m * 255.0f)) << 0);
} }
static void draw_testpat(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) static void draw_testpat(screen_device &screen, bitmap_rgb32 &bitmap,
const rectangle &cliprect)
{ {
// Test pattern Grey scale // Test pattern Grey scale
const int stripes = 255; const int stripes = 255;
//auto va(screen.visible_area()); // auto va(screen.visible_area());
auto &va(cliprect); auto &va(cliprect);
for (int i = 0; i < stripes; i++) for (int i = 0; i < stripes; i++)
{ {
int l = va.left() + (i * va.width() / stripes); int l = va.left() + (i * va.width() / stripes);
int w = (va.left() + (i+1) * va.width() / stripes) - l; int w = (va.left() + (i + 1) * va.width() / stripes) - l;
int v = (255 * i) / stripes; int v = (255 * i) / stripes;
bitmap.plot_box(l, va.top()+20, w, va.height()/2-20, rgb_t(0xff, v, v, v)); bitmap.plot_box(l, va.top() + 20, w, va.height() / 2 - 20,
rgb_t(0xff, v, v, v));
} }
int l(va.left() + va.width() / 4); int l(va.left() + va.width() / 4);
@ -353,14 +379,20 @@ static void draw_testpat(screen_device &screen, bitmap_rgb32 &bitmap, const rect
bitmap.plot_box(l, t, w, h, rgb_t(0xff, 0xc3, 0xc3, 0xc3)); // 195 bitmap.plot_box(l, t, w, h, rgb_t(0xff, 0xc3, 0xc3, 0xc3)); // 195
} }
uint32_t fixedfreq_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) uint32_t
fixedfreq_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap,
const rectangle &cliprect)
{ {
//printf("%f\n", machine().time().as_double()); // printf("%f\n", machine().time().as_double());
//printf("%d %lu %f %f\n", m_state.m_sig_vsync, m_state.m_fragments.size(), m_state.m_fragments[0].y, m_state.m_fragments[m_state.m_fragments.size()-1].y); // printf("%d %lu %f %f\n", m_state.m_sig_vsync, m_state.m_fragments.size(),
bool force_vector = screen.screen_type() == SCREEN_TYPE_VECTOR || (m_vector->read() & 1); // m_state.m_fragments[0].y,
bool debug_timing = (m_enable->read() & 2) == 2; // m_state.m_fragments[m_state.m_fragments.size()-1].y);
bool test_pat = (m_enable->read() & 4) == 4; bool force_vector = screen.screen_type() == SCREEN_TYPE_VECTOR
rgb_t backcol = debug_timing ? rgb_t(0xff, 0xff, 0x00, 0x00) : rgb_t(0xff, 0x00, 0x00, 0x00); || (m_vector->read() & 1);
bool debug_timing = (m_enable->read() & 2) == 2;
bool test_pat = (m_enable->read() & 4) == 4;
rgb_t backcol = debug_timing ? rgb_t(0xff, 0xff, 0x00, 0x00)
: rgb_t(0xff, 0x00, 0x00, 0x00);
if (!force_vector) if (!force_vector)
{ {
@ -377,18 +409,22 @@ uint32_t fixedfreq_device::screen_update(screen_device &screen, bitmap_rgb32 &bi
{ {
screen.set_video_attributes(VIDEO_SELF_RENDER); screen.set_video_attributes(VIDEO_SELF_RENDER);
const uint32_t flags(PRIMFLAG_ANTIALIAS(1) const uint32_t flags(
| PRIMFLAG_BLENDMODE(BLENDMODE_ADD) PRIMFLAG_ANTIALIAS(1) | PRIMFLAG_BLENDMODE(BLENDMODE_ADD)
| (screen.screen_type() == SCREEN_TYPE_VECTOR ? PRIMFLAG_VECTOR(1) : 0)); | (screen.screen_type() == SCREEN_TYPE_VECTOR ? PRIMFLAG_VECTOR(1)
: 0));
const rectangle &visarea = screen.visible_area(); const rectangle &visarea = screen.visible_area();
float xscale = 1.0f / (float)visarea.width(); float xscale = 1.0f / (float)visarea.width();
float yscale = 1.0f / (float)visarea.height(); float yscale = 1.0f / (float)visarea.height();
float xoffs = (float)visarea.min_x; float xoffs = (float)visarea.min_x;
float yoffs = (float)visarea.min_y; float yoffs = (float)visarea.min_y;
screen.container().empty(); screen.container().empty();
screen.container().add_rect(0.0f, 0.0f, 1.0f, 1.0f, rgb_t(0xff,0x00,0x00,0x00), screen.container().add_rect(
0.0f, 0.0f, 1.0f, 1.0f, rgb_t(0xff, 0x00, 0x00, 0x00),
PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)
| (screen.screen_type() == SCREEN_TYPE_VECTOR ? PRIMFLAG_VECTORBUF(1) : 0)); | (screen.screen_type() == SCREEN_TYPE_VECTOR
? PRIMFLAG_VECTORBUF(1)
: 0));
float last_y = -1e6; float last_y = -1e6;
for (auto &f : m_state.m_fragments) for (auto &f : m_state.m_fragments)
@ -397,7 +433,7 @@ uint32_t fixedfreq_device::screen_update(screen_device &screen, bitmap_rgb32 &bi
const float y0((f.y - yoffs) * yscale); const float y0((f.y - yoffs) * yscale);
const float x1((f.xr - xoffs) * xscale); const float x1((f.xr - xoffs) * xscale);
rgb_t col = (debug_timing && f.y < last_y) ? backcol : (rgb_t) f.col; rgb_t col = (debug_timing && f.y < last_y) ? backcol : (rgb_t)f.col;
// FIXME: Debug check for proper vsync timing // FIXME: Debug check for proper vsync timing
#if 0 #if 0
auto w = m_scanline_height * xscale * 0.5; auto w = m_scanline_height * xscale * 0.5;
@ -407,21 +443,18 @@ uint32_t fixedfreq_device::screen_update(screen_device &screen, bitmap_rgb32 &bi
// (0xff << 24) | (f.col & 0xffffff), // (0xff << 24) | (f.col & 0xffffff),
flags); flags);
#elif 1 #elif 1
const float y1((f.y + m_scanline_height - yoffs) * yscale); const float y1((f.y + m_scanline_height - yoffs) * yscale);
screen.container().add_rect( screen.container().add_rect(x0, y0, x1, y1, nom_col(col),
x0, y0, x1, y1, // (0xaf << 24) |
nom_col(col), // (f.col & 0xffffff),
// (0xaf << 24) | (f.col & 0xffffff), flags);
flags);
#else #else
const float y1((f.y + m_scanline_height - yoffs) * yscale); const float y1((f.y + m_scanline_height - yoffs) * yscale);
// Crashes with bgfx // Crashes with bgfx
screen.container().add_quad( screen.container().add_quad(x0, y0, x1, y1, rgb_t(nom_col(f.col)),
x0, y0, x1, y1, // (0xaf << 24) |
rgb_t(nom_col(f.col)), // (f.col & 0xffffff),
// (0xaf << 24) | (f.col & 0xffffff), m_texture, flags);
m_texture,
flags);
#endif #endif
last_y = f.y; last_y = f.y;
} }
@ -432,8 +465,9 @@ uint32_t fixedfreq_device::screen_update(screen_device &screen, bitmap_rgb32 &bi
void fixedfreq_device::vsync_end_cb(double refresh_time, uint32_t field) void fixedfreq_device::vsync_end_cb(double refresh_time, uint32_t field)
{ {
const auto expected_frame_period(m_monitor.clock_period() * m_monitor.vtotal() * m_monitor.htotal()); const auto expected_frame_period(m_monitor.clock_period()
bool progressive = (m_enable->read() & 8) == 8; * m_monitor.vtotal() * m_monitor.htotal());
bool progressive = (m_enable->read() & 8) == 8;
double mult = 0.5; double mult = 0.5;
@ -448,15 +482,21 @@ void fixedfreq_device::vsync_end_cb(double refresh_time, uint32_t field)
mult = 1.0; mult = 1.0;
} }
const auto refresh_limited(std::min(4.0 * expected_frame_period, const auto refresh_limited(std::min(
std::max((refresh_time + m_last_rt) * mult, 0.25 * expected_frame_period))); 4.0 * expected_frame_period, std::max((refresh_time + m_last_rt) * mult,
0.25 * expected_frame_period)));
m_last_rt = refresh_time; m_last_rt = refresh_time;
rectangle visarea(m_monitor.minh(), m_monitor.maxh(), m_monitor.minv(), m_monitor.maxv()); rectangle visarea(m_monitor.minh(), m_monitor.maxh(), m_monitor.minv(),
m_monitor.maxv());
// reset_origin must be called first. // reset_origin must be called first.
screen().reset_origin(m_state.m_last_y-(m_monitor.vsync_width() + m_monitor.vbackporch_width()), 0); screen().reset_origin(
screen().configure(m_monitor.htotal_scaled(), m_monitor.vtotal(), visarea, DOUBLE_TO_ATTOSECONDS(refresh_limited)); m_state.m_last_y
- (m_monitor.vsync_width() + m_monitor.vbackporch_width()),
0);
screen().configure(m_monitor.htotal_scaled(), m_monitor.vtotal(), visarea,
DOUBLE_TO_ATTOSECONDS(refresh_limited));
} }
NETDEV_ANALOG_CALLBACK_MEMBER(fixedfreq_device::update_composite_monochrome) NETDEV_ANALOG_CALLBACK_MEMBER(fixedfreq_device::update_composite_monochrome)
@ -501,6 +541,8 @@ NETDEV_ANALOG_CALLBACK_MEMBER(fixedfreq_device::update_sync)
/***************************************************************************/ /***************************************************************************/
// clang-format off
static INPUT_PORTS_START(fixedfreq_base_ports) static INPUT_PORTS_START(fixedfreq_base_ports)
PORT_START("ENABLE") PORT_START("ENABLE")
PORT_CONFNAME( 0x01, 0x00, "Display Monitor sliders" ) PORT_CONFNAME( 0x01, 0x00, "Display Monitor sliders" )
@ -549,6 +591,9 @@ static INPUT_PORTS_START(fixedfreq_vector_ports)
PORT_ADJUSTERX(SCANLINE_HEIGHT, "Scanline Height", 10, 300) PORT_ADJUSTERX(SCANLINE_HEIGHT, "Scanline Height", 10, 300)
INPUT_PORTS_END INPUT_PORTS_END
//
// clang-format on
ioport_constructor fixedfreq_device::device_input_ports() const ioport_constructor fixedfreq_device::device_input_ports() const
{ {
LOG("input ports\n"); LOG("input ports\n");
@ -567,30 +612,18 @@ unsigned fixedfreq_device::monitor_val(unsigned param) const
{ {
switch (param) switch (param)
{ {
case HVISIBLE: case HVISIBLE: return m_monitor.hvisible_width();
return m_monitor.hvisible_width(); case HFRONTPORCH: return m_monitor.hfrontporch_width();
case HFRONTPORCH: case HSYNC: return m_monitor.hsync_width();
return m_monitor.hfrontporch_width(); case HBACKPORCH: return m_monitor.hbackporch_width();
case HSYNC: case VVISIBLE: return m_monitor.vvisible_width();
return m_monitor.hsync_width(); case VFRONTPORCH: return m_monitor.vfrontporch_width();
case HBACKPORCH: case VSYNC: return m_monitor.vsync_width();
return m_monitor.hbackporch_width(); case VBACKPORCH: return m_monitor.vbackporch_width();
case VVISIBLE: case SYNCTHRESHOLD: return m_monitor.m_sync_threshold * 1000.0;
return m_monitor.vvisible_width(); case VSYNCTHRESHOLD: return m_monitor.m_vsync_threshold * 1000.0;
case VFRONTPORCH: case GAIN: return m_monitor.m_gain * 100.0;
return m_monitor.vfrontporch_width(); case SCANLINE_HEIGHT: return m_scanline_height * 100.0;
case VSYNC:
return m_monitor.vsync_width();
case VBACKPORCH:
return m_monitor.vbackporch_width();
case SYNCTHRESHOLD:
return m_monitor.m_sync_threshold * 1000.0;
case VSYNCTHRESHOLD:
return m_monitor.m_vsync_threshold * 1000.0;
case GAIN:
return m_monitor.m_gain * 100.0;
case SCANLINE_HEIGHT:
return m_scanline_height * 100.0;
} }
return 0; return 0;
} }
@ -603,28 +636,36 @@ INPUT_CHANGED_MEMBER(fixedfreq_device::port_changed)
switch (param) switch (param)
{ {
case HVISIBLE: case HVISIBLE:
m.set_h_rel(newval, m.hfrontporch_width(), m.hsync_width(), m.hbackporch_width()); m.set_h_rel(newval, m.hfrontporch_width(), m.hsync_width(),
m.hbackporch_width());
break; break;
case HFRONTPORCH: case HFRONTPORCH:
m.set_h_rel(m.hvisible_width(), newval, m.hsync_width(), m.hbackporch_width()); m.set_h_rel(m.hvisible_width(), newval, m.hsync_width(),
m.hbackporch_width());
break; break;
case HSYNC: case HSYNC:
m.set_h_rel(m.hvisible_width(), m.hfrontporch_width(), newval, m.hbackporch_width()); m.set_h_rel(m.hvisible_width(), m.hfrontporch_width(), newval,
m.hbackporch_width());
break; break;
case HBACKPORCH: case HBACKPORCH:
m.set_h_rel(m.hvisible_width(), m.hfrontporch_width(), m.hsync_width(), newval); m.set_h_rel(m.hvisible_width(), m.hfrontporch_width(),
m.hsync_width(), newval);
break; break;
case VVISIBLE: case VVISIBLE:
m.set_v_rel(newval, m.vfrontporch_width(), m.vsync_width(), m.vbackporch_width()); m.set_v_rel(newval, m.vfrontporch_width(), m.vsync_width(),
m.vbackporch_width());
break; break;
case VFRONTPORCH: case VFRONTPORCH:
m.set_v_rel(m.vvisible_width(), newval, m.vsync_width(), m.vbackporch_width()); m.set_v_rel(m.vvisible_width(), newval, m.vsync_width(),
m.vbackporch_width());
break; break;
case VSYNC: case VSYNC:
m.set_v_rel(m.vvisible_width(), m.vfrontporch_width(), newval, m.vbackporch_width()); m.set_v_rel(m.vvisible_width(), m.vfrontporch_width(), newval,
m.vbackporch_width());
break; break;
case VBACKPORCH: case VBACKPORCH:
m.set_v_rel(m.vvisible_width(), m.vfrontporch_width(), m.vsync_width(), newval); m.set_v_rel(m.vvisible_width(), m.vfrontporch_width(),
m.vsync_width(), newval);
break; break;
case SYNCTHRESHOLD: case SYNCTHRESHOLD:
m.m_sync_threshold = static_cast<double>(newval) / 1000.0; m.m_sync_threshold = static_cast<double>(newval) / 1000.0;
@ -632,13 +673,12 @@ INPUT_CHANGED_MEMBER(fixedfreq_device::port_changed)
case VSYNCTHRESHOLD: case VSYNCTHRESHOLD:
m.m_vsync_threshold = static_cast<double>(newval) / 1000.0; m.m_vsync_threshold = static_cast<double>(newval) / 1000.0;
break; break;
case GAIN: case GAIN: m.m_gain = static_cast<double>(newval) / 100.0; break;
m.m_gain = static_cast<double>(newval) / 100.0;
break;
case SCANLINE_HEIGHT: case SCANLINE_HEIGHT:
m_scanline_height = static_cast<double>(newval) / 100.0; m_scanline_height = static_cast<double>(newval) / 100.0;
break; break;
} }
machine().ui().popup_time(5, "Screen Dim %d x %d\n", m.htotal(), m.vtotal()); machine().ui().popup_time(5, "Screen Dim %d x %d\n", m.htotal(),
//ioport("YYY")->update_defvalue(true); m.vtotal());
// ioport("YYY")->update_defvalue(true);
} }

View File

@ -2,12 +2,12 @@
// copyright-holders:Couriersud // copyright-holders:Couriersud
/*************************************************************************** /***************************************************************************
fixfreq.h fixfreq.h
Fixed frequency monochrome monitor emulation Fixed frequency monochrome monitor emulation
The driver is intended for drivers which provide an analog video signal. The driver is intended for drivers which provide an analog video signal.
VSYNC and HSYNC levels are used to create the bitmap. VSYNC and HSYNC levels are used to create the bitmap.
***************************************************************************/ ***************************************************************************/
@ -21,41 +21,58 @@ struct fixedfreq_monitor_desc
{ {
fixedfreq_monitor_desc() fixedfreq_monitor_desc()
// default to NTSC "704x480@30i" // default to NTSC "704x480@30i"
: m_monitor_clock(13500000), : m_monitor_clock(13500000)
m_fieldcount(2), , m_fieldcount(2)
m_sync_threshold(0.3), , m_sync_threshold(0.3)
m_gain(1.0 / 3.7), , m_gain(1.0 / 3.7)
m_hscale(1), , m_hscale(1)
m_vsync_threshold(0.600), // trigger at 91% of vsync length 1-exp(-0.6) , m_vsync_threshold(0.600)
m_hvisible(704), , // trigger at 91% of vsync length 1-exp(-0.6)
m_hfrontporch(728), m_hvisible(704)
m_hsync(791), , m_hfrontporch(728)
m_hbackporch(858), , m_hsync(791)
m_vvisible(480), , m_hbackporch(858)
m_vfrontporch(486), , m_vvisible(480)
m_vsync(492), , m_vfrontporch(486)
m_vbackporch(525) , m_vsync(492)
{} , m_vbackporch(525)
{
}
uint32_t monitor_clock() const noexcept { return m_monitor_clock; } uint32_t monitor_clock() const noexcept { return m_monitor_clock; }
double clock_period() const noexcept { return 1.0 / (double) m_monitor_clock; } double clock_period() const noexcept
{
return 1.0 / (double)m_monitor_clock;
}
int minh() const noexcept { return (m_hbackporch - m_hsync) * m_hscale; } int minh() const noexcept { return (m_hbackporch - m_hsync) * m_hscale; }
int maxh() const noexcept { return (m_hbackporch - m_hsync + m_hvisible) * m_hscale - 1; } int maxh() const noexcept
{
return (m_hbackporch - m_hsync + m_hvisible) * m_hscale - 1;
}
int minv() const noexcept { return m_vbackporch - m_vsync; } int minv() const noexcept { return m_vbackporch - m_vsync; }
int maxv() const noexcept { return m_vbackporch - m_vsync + m_vvisible - 1; } int maxv() const noexcept
{
return m_vbackporch - m_vsync + m_vvisible - 1;
}
int htotal_scaled() const noexcept { return m_hbackporch * m_hscale; } int htotal_scaled() const noexcept { return m_hbackporch * m_hscale; }
int vbackporch_width() const noexcept { return m_vbackporch - m_vsync; } int vbackporch_width() const noexcept { return m_vbackporch - m_vsync; }
int vsync_width() const noexcept { return m_vsync - m_vfrontporch; } int vsync_width() const noexcept { return m_vsync - m_vfrontporch; }
int vfrontporch_width() const noexcept { return m_vfrontporch - m_vvisible; } int vfrontporch_width() const noexcept
{
return m_vfrontporch - m_vvisible;
}
int vvisible_width() const noexcept { return m_vvisible; } int vvisible_width() const noexcept { return m_vvisible; }
int vtotal() const noexcept { return m_vbackporch; } int vtotal() const noexcept { return m_vbackporch; }
int hbackporch_width() const noexcept { return m_hbackporch - m_hsync; } int hbackporch_width() const noexcept { return m_hbackporch - m_hsync; }
int hsync_width() const noexcept { return m_hsync - m_hfrontporch; } int hsync_width() const noexcept { return m_hsync - m_hfrontporch; }
int hfrontporch_width() const noexcept { return m_hfrontporch - m_hvisible; } int hfrontporch_width() const noexcept
{
return m_hfrontporch - m_hvisible;
}
int hvisible_width() const noexcept { return m_hvisible; } int hvisible_width() const noexcept { return m_hvisible; }
int htotal() const noexcept { return m_hbackporch; } int htotal() const noexcept { return m_hbackporch; }
@ -77,20 +94,22 @@ struct fixedfreq_monitor_desc
double vsync_filter_timeconst() const noexcept double vsync_filter_timeconst() const noexcept
{ {
return (double) (m_monitor_clock) / ((double) m_hbackporch * vsync_width()); return (double)(m_monitor_clock)
/ ((double)m_hbackporch * vsync_width());
} }
double hsync_filter_timeconst() const noexcept double hsync_filter_timeconst() const noexcept
{ {
return (double) m_monitor_clock / (double) hsync_width(); return (double)m_monitor_clock / (double)hsync_width();
} }
uint32_t m_monitor_clock; uint32_t m_monitor_clock;
int m_fieldcount; int m_fieldcount;
double m_sync_threshold; double m_sync_threshold;
double m_gain; double m_gain;
int m_hscale; int m_hscale;
double m_vsync_threshold; double m_vsync_threshold;
private: private:
int m_hvisible; int m_hvisible;
int m_hfrontporch; int m_hfrontporch;
@ -110,9 +129,9 @@ struct fixedfreq_monitor_intf
struct fixedfreq_monitor_line struct fixedfreq_monitor_line
{ {
float y; float y;
float x; float x;
float xr; float xr;
uint32_t col; uint32_t col;
}; };
@ -120,24 +139,26 @@ struct fixedfreq_monitor_state
{ {
using time_type = double; using time_type = double;
fixedfreq_monitor_state(fixedfreq_monitor_desc &desc, fixedfreq_monitor_intf &intf) fixedfreq_monitor_state(fixedfreq_monitor_desc &desc,
: m_desc(desc), fixedfreq_monitor_intf &intf)
m_intf(intf), : m_desc(desc)
m_last_sync_val(0), , m_intf(intf)
m_col(0), , m_last_sync_val(0)
m_last_x(0), , m_col(0)
m_last_y(0), , m_last_x(0)
m_last_sync_time(time_type(0)), , m_last_y(0)
m_line_time(time_type(0)), , m_last_sync_time(time_type(0))
m_last_hsync_time(time_type(0)), , m_line_time(time_type(0))
m_last_vsync_time(time_type(0)), , m_last_hsync_time(time_type(0))
m_last_line_duration(time_type(0)), , m_last_vsync_time(time_type(0))
m_last_field_time(time_type(0)), , m_last_line_duration(time_type(0))
m_vsync_filter(0), , m_last_field_time(time_type(0))
m_sig_vsync(0), , m_vsync_filter(0)
m_sig_composite(0), , m_sig_vsync(0)
m_sig_field(0) , m_sig_composite(0)
{} , m_sig_field(0)
{
}
/*** /***
* \brief To be called after monitor parameters are set * \brief To be called after monitor parameters are set
@ -148,7 +169,7 @@ struct fixedfreq_monitor_state
// Only copies constructor init // Only copies constructor init
m_last_sync_val = 0.0; m_last_sync_val = 0.0;
m_col = rgb_t(0,0,0); m_col = rgb_t(0, 0, 0);
m_last_x = 0; m_last_x = 0;
m_last_y = 0; m_last_y = 0;
m_last_sync_time = time_type(0); m_last_sync_time = time_type(0);
@ -168,13 +189,15 @@ struct fixedfreq_monitor_state
/* sync separator */ /* sync separator */
//m_vsync_threshold = (exp(- 3.0/(3.0+3.0))) - exp(-1.0); // m_vsync_threshold = (exp(- 3.0/(3.0+3.0))) - exp(-1.0);
//printf("trigger %f with len %f\n", m_vsync_threshold, 1e6 / m_vsync_filter_timeconst); // printf("trigger %f with len %f\n", m_vsync_threshold, 1e6 /
// Minimum frame period to be passed to video system ? // m_vsync_filter_timeconst);
// Minimum frame period to be passed to video system ?
m_fragments.clear(); m_fragments.clear();
//m_intf.vsync_end_cb(m_desc.clock_period() * m_desc.vtotal() * m_desc.htotal(), 0); // m_intf.vsync_end_cb(m_desc.clock_period() * m_desc.vtotal() *
// m_desc.htotal(), 0);
} }
void reset() void reset()
@ -192,19 +215,20 @@ struct fixedfreq_monitor_state
void update_sync_channel(const time_type &time, const double newval); void update_sync_channel(const time_type &time, const double newval);
void update_bm(const time_type &time); void update_bm(const time_type &time);
void update_composite_monochrome(const time_type &time, const double newval); void
update_composite_monochrome(const time_type &time, const double newval);
void update_red(const time_type &time, const double data); void update_red(const time_type &time, const double data);
void update_green(const time_type &time, const double data); void update_green(const time_type &time, const double data);
void update_blue(const time_type &time, const double data); void update_blue(const time_type &time, const double data);
void update_sync(const time_type &time, const double data); void update_sync(const time_type &time, const double data);
const fixedfreq_monitor_desc &m_desc; const fixedfreq_monitor_desc &m_desc;
fixedfreq_monitor_intf &m_intf; fixedfreq_monitor_intf &m_intf;
double m_last_sync_val; double m_last_sync_val;
uint32_t m_col; uint32_t m_col;
float m_last_x; float m_last_x;
int m_last_y; int m_last_y;
time_type m_last_sync_time; time_type m_last_sync_time;
time_type m_line_time; time_type m_line_time;
time_type m_last_hsync_time; time_type m_last_hsync_time;
@ -216,52 +240,76 @@ struct fixedfreq_monitor_state
/* sync separator */ /* sync separator */
double m_vsync_filter; double m_vsync_filter;
int m_sig_vsync; int m_sig_vsync;
int m_sig_composite; int m_sig_composite;
int m_sig_field; int m_sig_field;
std::vector<fixedfreq_monitor_line> m_fragments; std::vector<fixedfreq_monitor_line> m_fragments;
}; };
// ======================> fixedfreq_device // ======================> fixedfreq_device
class fixedfreq_device : public device_t, public device_video_interface, class fixedfreq_device
public fixedfreq_monitor_intf : public device_t
, public device_video_interface
, public fixedfreq_monitor_intf
{ {
public: public:
using time_type = fixedfreq_monitor_state::time_type; using time_type = fixedfreq_monitor_state::time_type;
// construction/destruction // construction/destruction
fixedfreq_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); fixedfreq_device(const machine_config &mconfig, const char *tag,
device_t *owner, uint32_t clock = 0);
// inline configuration helpers // inline configuration helpers
fixedfreq_device &set_monitor_clock(uint32_t clock) { m_monitor.m_monitor_clock = clock; return *this;} fixedfreq_device &set_monitor_clock(uint32_t clock)
fixedfreq_device &set_fieldcount(int count) { m_monitor.m_fieldcount = count; return *this; }
fixedfreq_device &set_threshold(double threshold) { m_monitor.m_sync_threshold = threshold; return *this; }
fixedfreq_device &set_vsync_threshold(double threshold) { m_monitor.m_vsync_threshold = threshold; return *this; }
fixedfreq_device &set_gain(double gain) { m_monitor.m_gain = gain; return *this; }
fixedfreq_device &set_horz_params(int visible, int frontporch, int sync, int backporch)
{ {
m_monitor.set_h_rel( m_monitor.m_monitor_clock = clock;
visible,
frontporch - visible,
sync - frontporch,
backporch - sync);
return *this; return *this;
} }
fixedfreq_device &set_vert_params(int visible, int frontporch, int sync, int backporch) fixedfreq_device &set_fieldcount(int count)
{ {
m_monitor.set_v_rel( m_monitor.m_fieldcount = count;
visible, return *this;
frontporch - visible, }
sync - frontporch, fixedfreq_device &set_threshold(double threshold)
backporch - sync); {
m_monitor.m_sync_threshold = threshold;
return *this;
}
fixedfreq_device &set_vsync_threshold(double threshold)
{
m_monitor.m_vsync_threshold = threshold;
return *this;
}
fixedfreq_device &set_gain(double gain)
{
m_monitor.m_gain = gain;
return *this;
}
fixedfreq_device &
set_horz_params(int visible, int frontporch, int sync, int backporch)
{
m_monitor.set_h_rel(visible, frontporch - visible, sync - frontporch,
backporch - sync);
return *this;
}
fixedfreq_device &
set_vert_params(int visible, int frontporch, int sync, int backporch)
{
m_monitor.set_v_rel(visible, frontporch - visible, sync - frontporch,
backporch - sync);
return *this;
}
fixedfreq_device &set_horz_scale(int hscale)
{
m_monitor.m_hscale = hscale;
return *this; return *this;
} }
fixedfreq_device &set_horz_scale(int hscale) { m_monitor.m_hscale = hscale; return *this;}
// pre-defined configurations // pre-defined configurations
fixedfreq_device &set_mode_ntsc720() //ModeLine "720x480@30i" 13.5 720 736 799 858 480 486 492 525 interlace -hsync -vsync fixedfreq_device &set_mode_ntsc720() // ModeLine "720x480@30i" 13.5 720 736
// 799 858 480 486 492 525 interlace
// -hsync -vsync
{ {
set_monitor_clock(13500000); set_monitor_clock(13500000);
set_horz_params(720, 736, 799, 858); set_horz_params(720, 736, 799, 858);
@ -270,7 +318,8 @@ public:
set_threshold(0.3); set_threshold(0.3);
return *this; return *this;
} }
fixedfreq_device &set_mode_ntsc704() //ModeLine "704x480@30i" 13.5 704 728 791 858 480 486 492 525 fixedfreq_device &set_mode_ntsc704() // ModeLine "704x480@30i" 13.5 704 728
// 791 858 480 486 492 525
{ {
set_monitor_clock(13500000); set_monitor_clock(13500000);
set_horz_params(704, 728, 791, 858); set_horz_params(704, 728, 791, 858);
@ -280,7 +329,8 @@ public:
return *this; return *this;
} }
virtual uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); virtual uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap,
const rectangle &cliprect);
NETDEV_ANALOG_CALLBACK_MEMBER(update_composite_monochrome); NETDEV_ANALOG_CALLBACK_MEMBER(update_composite_monochrome);
NETDEV_ANALOG_CALLBACK_MEMBER(update_red); NETDEV_ANALOG_CALLBACK_MEMBER(update_red);
@ -293,8 +343,8 @@ public:
unsigned monitor_val(unsigned param) const; unsigned monitor_val(unsigned param) const;
protected: protected:
fixedfreq_device(const machine_config &mconfig, device_type type,
fixedfreq_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); const char *tag, device_t *owner, uint32_t clock);
// device-level overrides // device-level overrides
virtual void device_config_complete() override; virtual void device_config_complete() override;
@ -309,16 +359,14 @@ protected:
private: private:
required_ioport m_enable; required_ioport m_enable;
required_ioport m_vector; required_ioport m_vector;
float m_scanline_height; float m_scanline_height;
double m_last_rt; double m_last_rt;
/* adjustable by drivers */ /* adjustable by drivers */
fixedfreq_monitor_desc m_monitor; fixedfreq_monitor_desc m_monitor;
fixedfreq_monitor_state m_state; fixedfreq_monitor_state m_state;
}; };
// device type definition // device type definition
DECLARE_DEVICE_TYPE(FIXFREQ, fixedfreq_device) DECLARE_DEVICE_TYPE(FIXFREQ, fixedfreq_device)

View File

@ -353,8 +353,6 @@ namespace netlist::analog
nld_two_terminal m_P0_P1; // 0, -gec, -gcc, 0 | 0 nld_two_terminal m_P0_P1; // 0, -gec, -gcc, 0 | 0
}; };
#define USE_THREE (1)
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// nld_QBJT_EB // nld_QBJT_EB
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@ -21,14 +21,16 @@
/// http://jaco.ec.t.kanazawa-u.ac.jp/edu/mix/pdf/3.pdf /// http://jaco.ec.t.kanazawa-u.ac.jp/edu/mix/pdf/3.pdf
/// ///
/// Farid N. Naim, Circuit Simulation (Wiley-IEEE Press, 2010). /// Farid N. Naim, Circuit Simulation (Wiley-IEEE Press, 2010).
/// Stefan Jahn, Michael Margraf, Vincent Habchi and Raimund Jacob, "Qucs Technical Papers" (2007) /// Stefan Jahn, Michael Margraf, Vincent Habchi and Raimund Jacob, "Qucs
/// Technical Papers" (2007)
/// ///
#include "solver/nld_solver.h"
#include "../nl_setup.h" #include "../nl_setup.h"
#include "nlid_twoterm.h" #include "nlid_twoterm.h"
#define BODY_CONNECTED_TO_SOURCE (1) #include "solver/nld_solver.h"
#define BODY_CONNECTED_TO_SOURCE (1)
namespace netlist::analog namespace netlist::analog
{ {
@ -99,11 +101,11 @@ namespace netlist::analog
{ {
public: public:
fet_model_t(param_model_t &model) fet_model_t(param_model_t &model)
: m_VTO(model, "VTO") : m_VTO(model, "VTO")
, m_N(model, "N") , m_N(model, "N")
, m_ISS(model, "IS") // Haven't seen a model using ISS / ISD , m_ISS(model, "IS") // Haven't seen a model using ISS / ISD
, m_ISD(model, "IS") , m_ISD(model, "IS")
, m_LD(model, "LD") , m_LD(model, "LD")
, m_L(model, "L") , m_L(model, "L")
, m_W(model, "W") , m_W(model, "W")
, m_TOX(model, "TOX") , m_TOX(model, "TOX")
@ -119,28 +121,33 @@ namespace netlist::analog
, m_CGDO(model, "CGDO") , m_CGDO(model, "CGDO")
, m_CGBO(model, "CGBO") , m_CGBO(model, "CGBO")
, m_CAPMOD(model, "CAPMOD") , m_CAPMOD(model, "CAPMOD")
{} {
}
param_model_t::value_t m_VTO; //!< Threshold voltage [V] param_model_t::value_t m_VTO; //!< Threshold voltage [V]
param_model_t::value_t m_N; //!< Bulk diode emission coefficient param_model_t::value_t m_N; //!< Bulk diode emission coefficient
param_model_t::value_t m_ISS; //!< Body diode saturation current param_model_t::value_t m_ISS; //!< Body diode saturation current
param_model_t::value_t m_ISD; //!< Body diode saturation current param_model_t::value_t m_ISD; //!< Body diode saturation current
param_model_t::value_t m_LD; //!< Lateral diffusion [m] param_model_t::value_t m_LD; //!< Lateral diffusion [m]
param_model_t::value_t m_L; //!< Length scaling param_model_t::value_t m_L; //!< Length scaling
param_model_t::value_t m_W; //!< Width scaling param_model_t::value_t m_W; //!< Width scaling
param_model_t::value_t m_TOX; //!< Oxide thickness param_model_t::value_t m_TOX; //!< Oxide thickness
param_model_t::value_t m_KP; //!< Transconductance parameter [A/V²] param_model_t::value_t m_KP; //!< Transconductance parameter [A/V²]
param_model_t::value_t m_UO; //!< Surface mobility [cm²/V/s] param_model_t::value_t m_UO; //!< Surface mobility [cm²/V/s]
param_model_t::value_t m_PHI; //!< Surface inversion potential [V] param_model_t::value_t m_PHI; //!< Surface inversion potential [V]
param_model_t::value_t m_NSUB; //!< Substrate doping [1/cm³] param_model_t::value_t m_NSUB; //!< Substrate doping [1/cm³]
param_model_t::value_t m_GAMMA; //!< Bulk threshold parameter [V^½] param_model_t::value_t m_GAMMA; //!< Bulk threshold parameter [V^½]
param_model_t::value_t m_LAMBDA; //!< Channel-length modulation [1/V] param_model_t::value_t m_LAMBDA; //!< Channel-length modulation [1/V]
param_model_t::value_t m_RD; //!< Drain ohmic resistance param_model_t::value_t m_RD; //!< Drain ohmic resistance
param_model_t::value_t m_RS; //!< Source ohmic resistance param_model_t::value_t m_RS; //!< Source ohmic resistance
param_model_t::value_t m_CGSO; //!< Gate-source overlap capacitance per meter channel width param_model_t::value_t m_CGSO; //!< Gate-source overlap capacitance per
param_model_t::value_t m_CGDO; //!< Gate-drain overlap capacitance per meter channel width //!< meter channel width
param_model_t::value_t m_CGBO; //!< Gate-bulk overlap capacitance per meter channel width param_model_t::value_t m_CGDO; //!< Gate-drain overlap capacitance per
param_model_t::value_base_t<int> m_CAPMOD; //!< Capacitance model (0=no model 2=Meyer) //!< meter channel width
param_model_t::value_t m_CGBO; //!< Gate-bulk overlap capacitance per
//!< meter channel width
param_model_t::value_base_t<int> m_CAPMOD; //!< Capacitance model (0=no
//!< model 2=Meyer)
}; };
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -150,7 +157,6 @@ namespace netlist::analog
class nld_MOSFET : public base_device_t class nld_MOSFET : public base_device_t
{ {
public: public:
public: \
nld_MOSFET(constructor_param_t data) nld_MOSFET(constructor_param_t data)
: base_device_t(data) : base_device_t(data)
, m_model(*this, "MODEL", "NMOS") , m_model(*this, "MODEL", "NMOS")
@ -171,7 +177,7 @@ namespace netlist::analog
, m_lambda(nlconst::zero()) , m_lambda(nlconst::zero())
, m_Leff(nlconst::zero()) , m_Leff(nlconst::zero())
, m_CoxWL(nlconst::zero()) , m_CoxWL(nlconst::zero())
//S, m_polarity(qtype() == FET_NMOS ? nlconst::one() : -nlconst::one()) // S, m_polarity(qtype() == FET_NMOS ? nlconst::one() : -nlconst::one())
, m_Cgb(nlconst::zero()) , m_Cgb(nlconst::zero())
, m_Cgs(nlconst::zero()) , m_Cgs(nlconst::zero())
, m_Cgd(nlconst::zero()) , m_Cgd(nlconst::zero())
@ -179,28 +185,30 @@ namespace netlist::analog
, m_Vgs(*this, "m_Vgs", nlconst::zero()) , m_Vgs(*this, "m_Vgs", nlconst::zero())
, m_Vgd(*this, "m_Vgd", nlconst::zero()) , m_Vgd(*this, "m_Vgd", nlconst::zero())
, m_model_acc(m_model) , m_model_acc(m_model)
{ {
register_sub_alias("S", m_SG.P()); // Source register_sub_alias("S", m_SG.P()); // Source
register_sub_alias("G", m_SG.N()); // Gate register_sub_alias("G", m_SG.N()); // Gate
register_sub_alias("D", m_DG.P()); // Drain register_sub_alias("D", m_DG.P()); // Drain
connect(m_SG.P(), m_SD.P()); connect(m_SG.P(), m_SD.P());
connect(m_SG.N(), m_DG.N()); connect(m_SG.N(), m_DG.N());
connect(m_DG.P(), m_SD.N()); connect(m_DG.P(), m_SD.N());
m_polarity = (m_model.type() == "NMOS_DEFAULT" ? nlconst::one() : -nlconst::one()); m_polarity = (m_model.type() == "NMOS_DEFAULT" ? nlconst::one()
: -nlconst::one());
m_capacitor_model = m_model_acc.m_CAPMOD; m_capacitor_model = m_model_acc.m_CAPMOD;
//# printf("capmod %d %g %g\n", m_capacitor_model, (nl_fptype)m_model_acc.m_VTO, m_polarity); //# printf("capmod %d %g %g\n", m_capacitor_model, (nl_fptype)m_model_acc.m_VTO, m_polarity);
nl_assert_always(m_capacitor_model == 0 || m_capacitor_model == 2, "Error: CAPMODEL invalid value"); nl_assert_always(m_capacitor_model == 0 || m_capacitor_model == 2,
"Error: CAPMODEL invalid value");
// //
// From http://ltwiki.org/LTspiceHelp/LTspiceHelp/M_MOSFET.htm : // From http://ltwiki.org/LTspiceHelp/LTspiceHelp/M_MOSFET.htm :
// //
// VTO, KP, LAMBDA, PHI and GAMMA. These parameters are computed // VTO, KP, LAMBDA, PHI and GAMMA. These parameters are
// if the process parameters(NSUB, TOX,...) are given, but // computed if the process parameters(NSUB, TOX,...) are given,
// user-specified values always override. // but user-specified values always override.
// //
// But couldn't find a formula for lambda anywhere // But couldn't find a formula for lambda anywhere
// //
@ -209,20 +217,28 @@ namespace netlist::analog
// calculate effective channel length // calculate effective channel length
m_Leff = m_model_acc.m_L - 2 * m_model_acc.m_LD; m_Leff = m_model_acc.m_L - 2 * m_model_acc.m_LD;
nl_assert_always(m_Leff > nlconst::zero(), "Effective Lateral diffusion would be negative for model"); nl_assert_always(
m_Leff > nlconst::zero(),
"Effective Lateral diffusion would be negative for model");
nl_fptype Cox = (m_model_acc.m_TOX > nlconst::zero()) ? (constants::eps_SiO2() * constants::eps_0() / m_model_acc.m_TOX) : nlconst::zero(); nl_fptype Cox = (m_model_acc.m_TOX > nlconst::zero())
? (constants::eps_SiO2() * constants::eps_0()
/ m_model_acc.m_TOX)
: nlconst::zero();
// calculate DC transconductance coefficient // calculate DC transconductance coefficient
if (m_model_acc.m_KP > nlconst::zero()) if (m_model_acc.m_KP > nlconst::zero())
m_beta = m_model_acc.m_KP * m_model_acc.m_W / m_Leff; m_beta = m_model_acc.m_KP * m_model_acc.m_W / m_Leff;
else if (Cox > nlconst::zero() && m_model_acc.m_UO > nlconst::zero()) else if (Cox > nlconst::zero()
m_beta = m_model_acc.m_UO * nlconst::magic(1e-4) * Cox * m_model_acc.m_W / m_Leff; && m_model_acc.m_UO > nlconst::zero())
m_beta = m_model_acc.m_UO * nlconst::magic(1e-4) * Cox
* m_model_acc.m_W / m_Leff;
else else
m_beta = nlconst::magic(2e-5) * m_model_acc.m_W / m_Leff; m_beta = nlconst::magic(2e-5) * m_model_acc.m_W / m_Leff;
//FIXME::UT can disappear // FIXME::UT can disappear
const nl_fptype Vt = constants::T0() * constants::k_b() / constants::Q_e(); const nl_fptype Vt = constants::T0() * constants::k_b()
/ constants::Q_e();
// calculate surface potential if not given // calculate surface potential if not given
@ -230,8 +246,12 @@ namespace netlist::analog
m_phi = m_model_acc.m_PHI; m_phi = m_model_acc.m_PHI;
else if (m_model_acc.m_NSUB > nlconst::zero()) else if (m_model_acc.m_NSUB > nlconst::zero())
{ {
nl_assert_always(m_model_acc.m_NSUB * nlconst::magic(1e6) >= constants::NiSi(), "Error calculating phi for model"); nl_assert_always(m_model_acc.m_NSUB * nlconst::magic(1e6)
m_phi = nlconst::two() * Vt * plib::log (m_model_acc.m_NSUB * nlconst::magic(1e6) / constants::NiSi()); >= constants::NiSi(),
"Error calculating phi for model");
m_phi = nlconst::two() * Vt
* plib::log(m_model_acc.m_NSUB * nlconst::magic(1e6)
/ constants::NiSi());
} }
else else
m_phi = nlconst::magic(0.6); m_phi = nlconst::magic(0.6);
@ -241,21 +261,24 @@ namespace netlist::analog
m_gamma = m_model_acc.m_GAMMA; m_gamma = m_model_acc.m_GAMMA;
else else
{ {
if (Cox > nlconst::zero() && m_model_acc.m_NSUB > nlconst::zero()) if (Cox > nlconst::zero()
m_gamma = plib::sqrt (nlconst::two() && m_model_acc.m_NSUB > nlconst::zero())
* constants::Q_e() * constants::eps_Si() * constants::eps_0() m_gamma = plib::sqrt(
* m_model_acc.m_NSUB * nlconst::magic(1e6)) / Cox; nlconst::two() * constants::Q_e()
* constants::eps_Si() * constants::eps_0()
* m_model_acc.m_NSUB * nlconst::magic(1e6))
/ Cox;
else else
m_gamma = nlconst::zero(); m_gamma = nlconst::zero();
} }
m_vto = m_model_acc.m_VTO; m_vto = m_model_acc.m_VTO;
// FIXME zero conversion // FIXME zero conversion
if(m_vto == nlconst::zero()) if (m_vto == nlconst::zero())
log().warning(MW_MOSFET_THRESHOLD_VOLTAGE(m_model.name())); log().warning(MW_MOSFET_THRESHOLD_VOLTAGE(m_model.name()));
// FIXME: VTO if missing may be calculated from TPG, NSS and temperature. Usually models // FIXME: VTO if missing may be calculated from TPG, NSS and
// specify VTO so skip this here. // temperature. Usually models specify VTO so skip this here.
m_CoxWL = Cox * m_model_acc.m_W * m_Leff; m_CoxWL = Cox * m_model_acc.m_W * m_Leff;
@ -269,13 +292,14 @@ namespace netlist::analog
{ {
if (m_capacitor_model != 0) if (m_capacitor_model != 0)
{ {
if (ts_type == time_step_type::FORWARD) if (ts_type == detail::time_step_type::FORWARD)
{ {
//#const nl_nl_fptype Ugd = -m_DG.deltaV() * m_polarity; // Gate - Drain //#const nl_nl_fptype Ugd = -m_DG.deltaV() * m_polarity; // Gate - Drain
//#const nl_nl_fptype Ugs = -m_SG.deltaV() * m_polarity; // Gate - Source //#const nl_nl_fptype Ugs = -m_SG.deltaV() * m_polarity; // Gate - Source
const nl_fptype Ugd = m_Vgd; // Gate - Drain const nl_fptype Ugd = m_Vgd; // Gate - Drain
const nl_fptype Ugs = m_Vgs; // Gate - Source const nl_fptype Ugs = m_Vgs; // Gate - Source
const nl_fptype Ubs = nlconst::zero(); // Bulk - Source == 0 if connected const nl_fptype Ubs = nlconst::zero(); // Bulk - Source == 0
// if connected
const nl_fptype Ugb = Ugs - Ubs; const nl_fptype Ugb = Ugs - Ubs;
m_cap_gb.time_step(m_Cgb, Ugb, step); m_cap_gb.time_step(m_Cgb, Ugb, step);
@ -292,20 +316,22 @@ namespace netlist::analog
} }
protected: protected:
NETLIB_RESETI() NETLIB_RESETI()
{ {
// Bulk diodes // Bulk diodes
m_D_BD.set_param(m_model_acc.m_ISD, m_model_acc.m_N, exec().gmin(), constants::T0()); m_D_BD.set_param(m_model_acc.m_ISD, m_model_acc.m_N, exec().gmin(),
#if (!BODY_CONNECTED_TO_SOURCE) constants::T0());
m_D_BS.set_param(m_model_acc.m_ISS, m_model_acc.m_N, exec().gmin(), constants::T0()); #if (!BODY_CONNECTED_TO_SOURCE)
#endif m_D_BS.set_param(m_model_acc.m_ISS, m_model_acc.m_N, exec().gmin(),
constants::T0());
#endif
} }
NETLIB_HANDLERI(terminal_handler) NETLIB_HANDLERI(terminal_handler)
{ {
// only called if connected to a rail net ==> notify the solver to recalculate // only called if connected to a rail net ==> notify the solver to
// recalculate
auto *solv(m_SG.solver()); auto *solv(m_SG.solver());
if (solv != nullptr) if (solv != nullptr)
solv->solve_now(); solv->solve_now();
@ -316,7 +342,6 @@ namespace netlist::analog
NETLIB_UPDATE_TERMINALSI(); NETLIB_UPDATE_TERMINALSI();
private: private:
param_model_t m_model; param_model_t m_model;
NETLIB_NAME(two_terminal) m_DG; NETLIB_NAME(two_terminal) m_DG;
@ -349,25 +374,30 @@ namespace netlist::analog
nl_fptype m_Cgs; nl_fptype m_Cgs;
nl_fptype m_Cgd; nl_fptype m_Cgd;
int m_capacitor_model; int m_capacitor_model;
state_var<nl_fptype> m_Vgs; state_var<nl_fptype> m_Vgs;
state_var<nl_fptype> m_Vgd; state_var<nl_fptype> m_Vgd;
fet_model_t m_model_acc; fet_model_t m_model_acc;
void set_cap(generic_capacitor<capacitor_e::VARIABLE_CAPACITY> &cap, void set_cap(generic_capacitor<capacitor_e::VARIABLE_CAPACITY> &cap,
nl_fptype capval, nl_fptype V, nl_fptype capval, nl_fptype V, nl_fptype &g11,
nl_fptype &g11, nl_fptype &g12, nl_fptype &g21, nl_fptype &g22, nl_fptype &g12, nl_fptype &g21, nl_fptype &g22,
nl_fptype &I1, nl_fptype &I2) const nl_fptype &I1, nl_fptype &I2) const
{ {
const nl_fptype I = cap.Ieq(capval, V) * m_polarity; const nl_fptype I = cap.Ieq(capval, V) * m_polarity;
const nl_fptype G = cap.G(capval); const nl_fptype G = cap.G(capval);
g11 += G; g12 -= G; g21 -= G; g22 += G; g11 += G;
I1 -= I; I2 += I; g12 -= G;
//printf("Cap: %g\n", capval); g21 -= G;
g22 += G;
I1 -= I;
I2 += I;
// printf("Cap: %g\n", capval);
} }
void calculate_caps(nl_fptype Vgs, nl_fptype Vgd, nl_fptype Vth, void
nl_fptype &Cgs, nl_fptype &Cgd, nl_fptype &Cgb) const calculate_caps(nl_fptype Vgs, nl_fptype Vgd, nl_fptype Vth,
nl_fptype &Cgs, nl_fptype &Cgd, nl_fptype &Cgb) const
{ {
nl_fptype Vctrl = Vgs - Vth * m_polarity; nl_fptype Vctrl = Vgs - Vth * m_polarity;
// Cut off - now further differentiated into 3 different formulas // Cut off - now further differentiated into 3 different formulas
@ -388,7 +418,8 @@ namespace netlist::analog
else if (Vctrl <= 0) else if (Vctrl <= 0)
{ {
Cgb = -Vctrl * m_CoxWL / m_phi; Cgb = -Vctrl * m_CoxWL / m_phi;
Cgs = Vctrl * m_CoxWL * nlconst::fraction(4.0, 3.0) / m_phi + nlconst::two_thirds() * m_CoxWL; Cgs = Vctrl * m_CoxWL * nlconst::fraction(4.0, 3.0) / m_phi
+ nlconst::two_thirds() * m_CoxWL;
Cgd = nlconst::zero(); Cgd = nlconst::zero();
} }
else else
@ -405,11 +436,15 @@ namespace netlist::analog
else else
{ {
// linear // linear
const auto Sqr1(plib::narrow_cast<nl_fptype>(plib::pow(Vdsat - Vds, 2))); const auto Sqr1(plib::narrow_cast<nl_fptype>(
const auto Sqr2(plib::narrow_cast<nl_fptype>(plib::pow(nlconst::two() * Vdsat - Vds, 2))); plib::pow(Vdsat - Vds, 2)));
const auto Sqr2(plib::narrow_cast<nl_fptype>(
plib::pow(nlconst::two() * Vdsat - Vds, 2)));
Cgb = 0; Cgb = 0;
Cgs = m_CoxWL * (nlconst::one() - Sqr1 / Sqr2) * nlconst::two_thirds(); Cgs = m_CoxWL * (nlconst::one() - Sqr1 / Sqr2)
Cgd = m_CoxWL * (nlconst::one() - Vdsat * Vdsat / Sqr2) * nlconst::two_thirds(); * nlconst::two_thirds();
Cgd = m_CoxWL * (nlconst::one() - Vdsat * Vdsat / Sqr2)
* nlconst::two_thirds();
} }
} }
} }
@ -426,19 +461,26 @@ namespace netlist::analog
// limit step sizes // limit step sizes
const nl_fptype k = nlconst::magic(3.5); // see "Circuit Simulation", page 185 const nl_fptype k = nlconst::magic(3.5); // see "Circuit Simulation",
// page 185
nl_fptype d = (Vgs - m_Vgs); nl_fptype d = (Vgs - m_Vgs);
Vgs = m_Vgs + plib::reciprocal(k) * plib::signum(d) * plib::log1p(k * plib::abs(d)); Vgs = m_Vgs
+ plib::reciprocal(k) * plib::signum(d)
* plib::log1p(k * plib::abs(d));
d = (Vgd - m_Vgd); d = (Vgd - m_Vgd);
Vgd = m_Vgd + plib::reciprocal(k) * plib::signum(d) * plib::log1p(k * plib::abs(d)); Vgd = m_Vgd
+ plib::reciprocal(k) * plib::signum(d)
* plib::log1p(k * plib::abs(d));
m_Vgs = Vgs; m_Vgs = Vgs;
m_Vgd = Vgd; m_Vgd = Vgd;
const nl_fptype Vbs = nlconst::zero(); // Bulk - Source == 0 if connected const nl_fptype Vbs = nlconst::zero(); // Bulk - Source == 0 if
//const nl_nl_fptype Vbd = m_SD.deltaV() * m_polarity; // Bulk - Drain = Source - Drain // connected
// const nl_nl_fptype Vbd = m_SD.deltaV() * m_polarity; // Bulk - Drain
// = Source - Drain
const nl_fptype Vds = Vgs - Vgd; const nl_fptype Vds = Vgs - Vgd;
const nl_fptype Vbd = -Vds; // Bulk - Drain = Source - Drain const nl_fptype Vbd = -Vds; // Bulk - Drain = Source - Drain
#if (!BODY_CONNECTED_TO_SOURCE) #if (!BODY_CONNECTED_TO_SOURCE)
m_D_BS.update_diode(Vbs); m_D_BS.update_diode(Vbs);
@ -451,8 +493,11 @@ namespace netlist::analog
// calculate Vth // calculate Vth
const nl_fptype Vbulk = is_forward ? Vbs : Vbd; const nl_fptype Vbulk = is_forward ? Vbs : Vbd;
const nl_fptype phi_m_Vbulk = (m_phi > Vbulk) ? plib::sqrt(m_phi - Vbulk) : nlconst::zero(); const nl_fptype phi_m_Vbulk = (m_phi > Vbulk)
const nl_fptype Vth = m_vto * m_polarity + m_gamma * (phi_m_Vbulk - plib::sqrt(m_phi)); ? plib::sqrt(m_phi - Vbulk)
: nlconst::zero();
const nl_fptype Vth = m_vto * m_polarity
+ m_gamma * (phi_m_Vbulk - plib::sqrt(m_phi));
const nl_fptype Vctrl = (is_forward ? Vgs : Vgd) - Vth; const nl_fptype Vctrl = (is_forward ? Vgs : Vgd) - Vth;
@ -467,30 +512,36 @@ namespace netlist::analog
{ {
// cutoff region // cutoff region
Ids = nlconst::zero(); Ids = nlconst::zero();
gm = nlconst::zero(); gm = nlconst::zero();
gds = nlconst::zero(); gds = nlconst::zero();
gmb = nlconst::zero(); gmb = nlconst::zero();
} }
else else
{ {
const nl_fptype beta = m_beta * (nlconst::one() + m_lambda * absVds); const nl_fptype beta = m_beta
* (nlconst::one() + m_lambda * absVds);
if (Vctrl <= absVds) if (Vctrl <= absVds)
{ {
// saturation region // saturation region
Ids = beta * Vctrl * Vctrl / nlconst::two(); Ids = beta * Vctrl * Vctrl / nlconst::two();
gm = beta * Vctrl; gm = beta * Vctrl;
gds = m_lambda * m_beta * Vctrl * Vctrl / nlconst::two(); gds = m_lambda * m_beta * Vctrl * Vctrl / nlconst::two();
} }
else else
{ {
// linear region // linear region
Ids = beta * absVds * (Vctrl - absVds / nlconst::two()); Ids = beta * absVds * (Vctrl - absVds / nlconst::two());
gm = beta * absVds; gm = beta * absVds;
gds = beta * (Vctrl - absVds) + m_lambda * m_beta * absVds * (Vctrl - absVds / nlconst::two()); gds = beta * (Vctrl - absVds)
+ m_lambda * m_beta * absVds
* (Vctrl - absVds / nlconst::two());
} }
// back gate transconductance // back gate transconductance
const nl_fptype bgtc = (phi_m_Vbulk != nlconst::zero()) ? (m_gamma / phi_m_Vbulk / nlconst::two()) : nlconst::zero(); const nl_fptype bgtc = (phi_m_Vbulk != nlconst::zero())
? (m_gamma / phi_m_Vbulk
/ nlconst::two())
: nlconst::zero();
gmb = gm * bgtc; gmb = gm * bgtc;
} }
@ -512,11 +563,11 @@ namespace netlist::analog
#endif #endif
// exchange controlling nodes if necessary // exchange controlling nodes if necessary
const nl_fptype gate_source = is_forward ? (gm + gmb) : nlconst::zero(); const nl_fptype gate_source = is_forward ? (gm + gmb) : nlconst::zero();
const nl_fptype gate_drain = is_forward ? nlconst::zero() : (gm + gmb); const nl_fptype gate_drain = is_forward ? nlconst::zero() : (gm + gmb);
const nl_fptype IeqDS = (is_forward) ? const nl_fptype IeqDS = (is_forward)
Ids - gm * Vgs - gmb * Vbs - gds * Vds ? Ids - gm * Vgs - gmb * Vbs - gds * Vds
: -Ids - gm * Vgd - gmb * Vbd - gds * Vds; : -Ids - gm * Vgd - gmb * Vbd - gds * Vds;
// IG = 0 // IG = 0
nl_fptype IG = nlconst::zero(); nl_fptype IG = nlconst::zero();
@ -529,20 +580,20 @@ namespace netlist::analog
nl_fptype gGS = nlconst::zero(); nl_fptype gGS = nlconst::zero();
nl_fptype gGB = nlconst::zero(); nl_fptype gGB = nlconst::zero();
nl_fptype gDG = gm; nl_fptype gDG = gm;
nl_fptype gDD = gds + gbd - gate_drain; nl_fptype gDD = gds + gbd - gate_drain;
const nl_fptype gDS = -gds - gate_source; const nl_fptype gDS = -gds - gate_source;
const nl_fptype gDB = gmb - gbd; const nl_fptype gDB = gmb - gbd;
nl_fptype gSG = -gm; nl_fptype gSG = -gm;
const nl_fptype gSD = -gds + gate_drain; const nl_fptype gSD = -gds + gate_drain;
nl_fptype gSS = gbs + gds + gate_source; nl_fptype gSS = gbs + gds + gate_source;
const nl_fptype gSB = -gbs - gmb; const nl_fptype gSB = -gbs - gmb;
nl_fptype gBG = nlconst::zero(); nl_fptype gBG = nlconst::zero();
const nl_fptype gBD = -gbd; const nl_fptype gBD = -gbd;
const nl_fptype gBS = -gbs; const nl_fptype gBS = -gbs;
nl_fptype gBB = gbs + gbd; nl_fptype gBB = gbs + gbd;
if (m_capacitor_model != 0) if (m_capacitor_model != 0)
{ {
@ -553,23 +604,26 @@ namespace netlist::analog
else else
calculate_caps(Vgd, Vgs, Vth, m_Cgd, m_Cgs, m_Cgb); calculate_caps(Vgd, Vgs, Vth, m_Cgd, m_Cgs, m_Cgb);
set_cap(m_cap_gb, m_Cgb + m_model_acc.m_CGBO * m_Leff, Vgb, gGG, gGB, gBG, gBB, IG, IB); set_cap(m_cap_gb, m_Cgb + m_model_acc.m_CGBO * m_Leff, Vgb, gGG,
set_cap(m_cap_gs, m_Cgs + m_model_acc.m_CGSO * m_model_acc.m_W, Vgs, gGG, gGS, gSG, gSS, IG, IS); gGB, gBG, gBB, IG, IB);
set_cap(m_cap_gd, m_Cgd + m_model_acc.m_CGDO * m_model_acc.m_W, Vgd, gGG, gGD, gDG, gDD, IG, ID); set_cap(m_cap_gs, m_Cgs + m_model_acc.m_CGSO * m_model_acc.m_W, Vgs,
gGG, gGS, gSG, gSS, IG, IS);
set_cap(m_cap_gd, m_Cgd + m_model_acc.m_CGDO * m_model_acc.m_W, Vgd,
gGG, gGD, gDG, gDD, IG, ID);
} }
// Source connected to body, Diode S-B shorted! // Source connected to body, Diode S-B shorted!
const nl_fptype gSSBB = gSS + gBB + gBS + gSB; const nl_fptype gSSBB = gSS + gBB + gBS + gSB;
const auto zero(nlconst::zero()); const auto zero(nlconst::zero());
// S G // S G
m_SG.set_mat( gSSBB, gSG + gBG, +(IS + IB), // S m_SG.set_mat(gSSBB, gSG + gBG, +(IS + IB), // S
gGS + gGB, gGG, IG ); // G gGS + gGB, gGG, IG); // G
// D G // D G
m_DG.set_mat( gDD, gDG, +ID, // D m_DG.set_mat(gDD, gDG, +ID, // D
gGD, zero, zero ); // G gGD, zero, zero); // G
// S D // S D
m_SD.set_mat( zero, gSD + gBD, zero, // S m_SD.set_mat(zero, gSD + gBD, zero, // S
gDS + gDB, zero, zero ); // D gDS + gDB, zero, zero); // D
/// | /// |
/// | D S G I /// | D S G I
@ -600,13 +654,11 @@ namespace netlist::analog
/// | /// |
} }
NETLIB_UPDATE_PARAM(MOSFET) NETLIB_UPDATE_PARAM(MOSFET) {}
{
}
} // namespace netlist::analog } // namespace netlist::analog
namespace netlist::devices { namespace netlist::devices
{
NETLIB_DEVICE_IMPL_NS(analog, MOSFET, "MOSFET", "MODEL") NETLIB_DEVICE_IMPL_NS(analog, MOSFET, "MOSFET", "MODEL")
} // namespace netlist::devices } // namespace netlist::devices

View File

@ -125,7 +125,7 @@ namespace netlist::analog
NETLIB_TIMESTEP(L) NETLIB_TIMESTEP(L)
{ {
if (ts_type == time_step_type::FORWARD) if (ts_type == detail::time_step_type::FORWARD)
{ {
m_last_I = m_I; m_last_I = m_I;
m_last_G = m_G; m_last_G = m_G;

View File

@ -312,7 +312,7 @@ namespace netlist::analog
NETLIB_IS_TIMESTEP(true) NETLIB_IS_TIMESTEP(true)
NETLIB_TIMESTEPI() NETLIB_TIMESTEPI()
{ {
if (ts_type == time_step_type::FORWARD) if (ts_type == detail::time_step_type::FORWARD)
{ {
// G, Ieq // G, Ieq
const auto res(m_cap.time_step(m_C(), deltaV(), step)); const auto res(m_cap.time_step(m_C(), deltaV(), step));
@ -584,7 +584,7 @@ namespace netlist::analog
NETLIB_TIMESTEPI() NETLIB_TIMESTEPI()
{ {
if (ts_type == time_step_type::FORWARD) if (ts_type == detail::time_step_type::FORWARD)
{ {
m_t += step; m_t += step;
m_funcparam[0] = m_t; m_funcparam[0] = m_t;
@ -637,7 +637,7 @@ namespace netlist::analog
NETLIB_IS_TIMESTEP(!m_func().empty()) NETLIB_IS_TIMESTEP(!m_func().empty())
NETLIB_TIMESTEPI() NETLIB_TIMESTEPI()
{ {
if (ts_type == time_step_type::FORWARD) if (ts_type == detail::time_step_type::FORWARD)
{ {
m_t += step; m_t += step;
m_funcparam[0] = m_t; m_funcparam[0] = m_t;

View File

@ -25,13 +25,14 @@ BreakStringLiterals: false
AlwaysBreakTemplateDeclarations: Yes AlwaysBreakTemplateDeclarations: Yes
# AfterComma does not work <= 13 # AfterComma does not work <= 13
#BreakInheritanceList: AfterComma #BreakInheritanceList: AfterComma
#BreakInheritanceList: BeforeComma BreakInheritanceList: BeforeComma
#BreakInheritanceList: false #BreakInheritanceList: false
SpaceBeforeInheritanceColon: true SpaceBeforeInheritanceColon: true
#AlignAfterOpenBracket: DontAlign #AlignAfterOpenBracket: DontAlign
AlignAfterOpenBracket: Align AlignAfterOpenBracket: Align
PointerAlignment: Right PointerAlignment: Right
ReferenceAlignment: Right
SpacesInAngles: false SpacesInAngles: false
SpaceBeforeAssignmentOperators: true SpaceBeforeAssignmentOperators: true
AlignConsecutiveDeclarations: true AlignConsecutiveDeclarations: true
@ -90,16 +91,17 @@ StatementMacros:
TypenameMacros: TypenameMacros:
- "NETLIST_NAME" - "NETLIST_NAME"
- "NETLIB_NAME" - "NETLIB_NAME"
- "PENUM"
WhitespaceSensitiveMacros: WhitespaceSensitiveMacros:
- "ALIAS" - "ALIAS"
- "NET_C"
- "DIPPINS" - "DIPPINS"
- "NET_C"
- "PENUM"
IndentPPDirectives: BeforeHash IndentPPDirectives: BeforeHash
MacroBlockBegin: "^static NETLIST_START\\(.+\\)|static TRUTHTABLE_START\\(.*\\)$" MacroBlockBegin: "^static NETLIST_START\\(.+\\)|static TRUTHTABLE_START\\(.*\\)$"
MacroBlockEnd: "^NETLIST_END\\(\\)|TRUTHTABLE_END\\(\\)$" MacroBlockEnd: "^NETLIST_END\\(\\)|TRUTHTABLE_END\\(\\)$"
# ReferenceAlignment: Middle
# Avoid formatting # Avoid formatting
# -- clang-tidy # -- clang-tidy

View File

@ -19,6 +19,7 @@
"Schmitt", "Schmitt",
"Schottky", "Schottky",
"Zener", "Zener",
"Thevenin",
// Company names // Company names
"Fairchild", "Fairchild",
"Signetics", "Signetics",
@ -69,6 +70,7 @@
// FIXME: Remove everything below here again // FIXME: Remove everything below here again
// Excluded for now ... Still over 1000 in the log // Excluded for now ... Still over 1000 in the log
"plib", // namespace "plib", // namespace
"isnull",
"pstring", "pstring",
"passert", "passert",
"putf", "putf",
@ -79,6 +81,8 @@
"idrn", "idrn",
"preprocessor", "preprocessor",
"ppreprocessor", "ppreprocessor",
"psource",
"psemaphore",
"modacc", "modacc",
"Ainv", "Ainv",
"anetlist", "anetlist",

View File

@ -114,7 +114,7 @@ TIDY_DB = $(OBJ)/compile_commands.json
#LTO decreases performance :-( #LTO decreases performance :-(
#LTO = -flto=4 -fuse-linker-plugin -Wodr #LTO = -flto=4 -fuse-linker-plugin -Wodr
CCOREFLAGS = -g -O3 -std=c++17 -I$(SRC) -I$(SRC)/.. -I$(SRC)/../.. CCOREFLAGS = -g -O3 -std=c++17 -I$(SRC)
CFLAGS = $(LTO) $(CCOREFLAGS) $(CEXTRAFLAGS) CFLAGS = $(LTO) $(CCOREFLAGS) $(CEXTRAFLAGS)
LDFLAGS = $(LTO) -g -O3 -std=c++17 $(LDEXTRAFLAGS) LDFLAGS = $(LTO) -g -O3 -std=c++17 $(LDEXTRAFLAGS)

View File

@ -17,9 +17,19 @@
#include "../plib/pexception.h" #include "../plib/pexception.h"
#include "../plib/plists.h" #include "../plib/plists.h"
#include "../plib/pmempool.h" #include "../plib/pmempool.h"
#include "../plib/ppmf.h"
#include <unordered_map> #include <unordered_map>
namespace netlist
{
/// \brief Delegate type for device notification.
///
using nl_delegate = plib::pmfp<void()>;
using nl_delegate_ts = plib::pmfp<void(detail::time_step_type, nl_fptype)>;
using nl_delegate_dyn = plib::pmfp<void()>;
} // namespace netlist
namespace netlist::detail namespace netlist::detail
{ {
@ -131,16 +141,16 @@ namespace netlist::detail
PCOPYASSIGNMOVE(netlist_object_t, delete) PCOPYASSIGNMOVE(netlist_object_t, delete)
netlist_state_t & state() noexcept; netlist_state_t &state() noexcept;
const netlist_state_t &state() const noexcept; const netlist_state_t &state() const noexcept;
constexpr netlist_t & exec() noexcept { return m_netlist; } constexpr netlist_t &exec() noexcept { return m_netlist; }
constexpr const netlist_t &exec() const noexcept { return m_netlist; } constexpr const netlist_t &exec() const noexcept { return m_netlist; }
// to ease template design // to ease template design
template <typename T, typename... Args> template <typename T, typename... Args>
device_arena::unique_ptr<T> make_pool_object(Args &&...args) noexcept( device_arena::unique_ptr<T>
false) make_pool_object(Args &&...args) noexcept(false)
{ {
return state().make_pool_object<T>(std::forward<Args>(args)...); return state().make_pool_object<T>(std::forward<Args>(args)...);
} }
@ -174,13 +184,13 @@ namespace netlist::detail
/// \brief returns reference to owning device. /// \brief returns reference to owning device.
/// \returns reference to owning device. /// \returns reference to owning device.
core_device_t & device() noexcept { return *m_device; } core_device_t &device() noexcept { return *m_device; }
const core_device_t &device() const noexcept { return *m_device; } const core_device_t &device() const noexcept { return *m_device; }
/// \brief The netlist owning the owner of this object. /// \brief The netlist owning the owner of this object.
/// \returns reference to netlist object. /// \returns reference to netlist object.
netlist_state_t & state() noexcept; netlist_state_t &state() noexcept;
const netlist_state_t &state() const noexcept; const netlist_state_t &state() const noexcept;
private: private:
@ -218,7 +228,7 @@ namespace netlist::detail
static constexpr netlist_sig_t OUT_TRISTATE() { return INP_MASK; } static constexpr netlist_sig_t OUT_TRISTATE() { return INP_MASK; }
static_assert(INP_BITS * 2 <= sizeof(netlist_sig_t) * 8, static_assert(INP_BITS * 2 <= sizeof(netlist_sig_t) * 8,
"netlist_sig_t size not sufficient"); "netlist_sig_t size not sufficient");
enum state_e enum state_e
{ {
@ -231,7 +241,7 @@ namespace netlist::detail
}; };
core_terminal_t(core_device_t &dev, const pstring &aname, state_e state, core_terminal_t(core_device_t &dev, const pstring &aname, state_e state,
nl_delegate delegate); nl_delegate delegate);
virtual ~core_terminal_t() noexcept = default; virtual ~core_terminal_t() noexcept = default;
PCOPYASSIGNMOVE(core_terminal_t, delete) PCOPYASSIGNMOVE(core_terminal_t, delete)
@ -271,12 +281,12 @@ namespace netlist::detail
void reset() noexcept void reset() noexcept
{ {
set_state( set_state(is_type(terminal_type::OUTPUT) ? STATE_OUT
is_type(terminal_type::OUTPUT) ? STATE_OUT : STATE_INP_ACTIVE); : STATE_INP_ACTIVE);
} }
constexpr void set_copied_input( constexpr void
[[maybe_unused]] netlist_sig_t val) noexcept set_copied_input([[maybe_unused]] netlist_sig_t val) noexcept
{ {
if constexpr (config::use_copy_instead_of_reference::value) if constexpr (config::use_copy_instead_of_reference::value)
{ {
@ -298,7 +308,7 @@ namespace netlist::detail
private: private:
nl_delegate m_delegate; nl_delegate m_delegate;
net_t * m_net; net_t *m_net;
state_var<state_e> m_state; state_var<state_e> m_state;
}; };

View File

@ -33,7 +33,7 @@ namespace netlist
friend class factory::device_element_t; friend class factory::device_element_t;
friend class factory::library_element_t; friend class factory::library_element_t;
template <typename CX> template <typename DEVICE>
friend struct sub_device_wrapper; friend struct sub_device_wrapper;
friend class solver::matrix_solver_t; friend class solver::matrix_solver_t;
@ -103,7 +103,7 @@ namespace netlist
log_type &log(); log_type &log();
public: public:
virtual void time_step([[maybe_unused]] time_step_type ts_type, virtual void time_step([[maybe_unused]] detail::time_step_type ts_type,
[[maybe_unused]] nl_fptype st) noexcept [[maybe_unused]] nl_fptype st) noexcept
{ {
} }

View File

@ -73,40 +73,42 @@ namespace netlist
// FIXME: Rename // FIXME: Rename
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
template <typename CX> template <typename DEVICE>
struct sub_device_wrapper struct sub_device_wrapper
{ {
using constructor_data_t = typename CX::constructor_data_t; using constructor_data_t = typename DEVICE::constructor_data_t;
using constructor_param_t = typename CX::constructor_param_t; using constructor_param_t = typename DEVICE::constructor_param_t;
template <typename... Args> template <typename... Args>
sub_device_wrapper(base_device_t &owner, const pstring &name, sub_device_wrapper(base_device_t &owner, const pstring &name,
Args &&...args) Args &&...args)
{ {
// m_dev = owner.state().make_pool_object<CX>(owner, name, // m_dev = owner.state().make_pool_object<DEVICE>(owner, name,
// std::forward<Args>(args)...); // std::forward<Args>(args)...);
m_dev = owner.state().make_pool_object<CX>( m_dev = owner.state().make_pool_object<DEVICE>(
constructor_data_t{owner.state(), owner.name() + "." + name}, constructor_data_t{owner.state(), owner.name() + "." + name},
std::forward<Args>(args)...); std::forward<Args>(args)...);
owner.state().register_device(m_dev->name(), owner.state().register_device(
m_dev->name(),
device_arena::owned_ptr<core_device_t>(m_dev.get(), false)); device_arena::owned_ptr<core_device_t>(m_dev.get(), false));
} }
template <typename... Args> template <typename... Args>
sub_device_wrapper(device_t &owner, const pstring &name, Args &&...args) sub_device_wrapper(device_t &owner, const pstring &name, Args &&...args)
{ {
// m_dev = owner.state().make_pool_object<CX>(owner, name, // m_dev = owner.state().make_pool_object<DEVICE>(owner, name,
// std::forward<Args>(args)...); // std::forward<Args>(args)...);
m_dev = owner.state().make_pool_object<CX>( m_dev = owner.state().make_pool_object<DEVICE>(
constructor_data_t{owner.state(), owner.name() + "." + name}, constructor_data_t{owner.state(), owner.name() + "." + name},
std::forward<Args>(args)...); std::forward<Args>(args)...);
owner.state().register_device(m_dev->name(), owner.state().register_device(
m_dev->name(),
device_arena::owned_ptr<core_device_t>(m_dev.get(), false)); device_arena::owned_ptr<core_device_t>(m_dev.get(), false));
} }
CX & operator()() { return *m_dev; } DEVICE & operator()() { return *m_dev; }
const CX &operator()() const { return *m_dev; } const DEVICE &operator()() const { return *m_dev; }
private: private:
device_arena::unique_ptr<CX> m_dev; device_arena::unique_ptr<DEVICE> m_dev;
}; };
} // namespace netlist } // namespace netlist

View File

@ -107,7 +107,7 @@ public: \
#define NETLIB_TIMESTEPI() \ #define NETLIB_TIMESTEPI() \
public: \ public: \
virtual void time_step(time_step_type ts_type, \ virtual void time_step(detail::time_step_type ts_type, \
nl_fptype step) noexcept override nl_fptype step) noexcept override
/// \brief Used to implement the body of the time stepping code. /// \brief Used to implement the body of the time stepping code.
@ -119,7 +119,7 @@ public: \
/// \param cname Name of object as given to \ref NETLIB_OBJECT /// \param cname Name of object as given to \ref NETLIB_OBJECT
/// ///
#define NETLIB_TIMESTEP(cname) \ #define NETLIB_TIMESTEP(cname) \
void NETLIB_NAME(cname)::time_step(time_step_type ts_type, \ void NETLIB_NAME(cname)::time_step(detail::time_step_type ts_type, \
nl_fptype step) noexcept nl_fptype step) noexcept
//#define NETLIB_DELEGATE(name) nl_delegate(&this_type :: name, this) //#define NETLIB_DELEGATE(name) nl_delegate(&this_type :: name, this)

View File

@ -10,6 +10,8 @@
#include "../nltypes.h" #include "../nltypes.h"
#include "../plib/palloc.h"
#include "../plib/pmempool.h"
#include "../plib/pstring.h" #include "../plib/pstring.h"
namespace netlist namespace netlist

View File

@ -12,6 +12,7 @@
#include "queue.h" #include "queue.h"
#include "../plib/plists.h" #include "../plib/plists.h"
#include "../plib/pmempool.h"
#include "../plib/pstate.h" #include "../plib/pstate.h"
#include "../plib/pstring.h" #include "../plib/pstring.h"
@ -31,8 +32,8 @@ namespace netlist
public: public:
using nets_collection_type = std::vector< using nets_collection_type = std::vector<
device_arena::owned_ptr<detail::net_t>>; device_arena::owned_ptr<detail::net_t>>;
using family_collection_type = std::unordered_map<pstring, using family_collection_type = std::unordered_map<
host_arena::unique_ptr<logic_family_desc_t>>; pstring, host_arena::unique_ptr<logic_family_desc_t>>;
// need to preserve order of device creation ... // need to preserve order of device creation ...
using devices_collection_type = std::vector< using devices_collection_type = std::vector<
@ -55,8 +56,9 @@ namespace netlist
return bool(plib::dynamic_downcast<C *>(p)); return bool(plib::dynamic_downcast<C *>(p));
} }
core_device_t *get_single_device(const pstring &classname, core_device_t *
bool (*cc)(core_device_t *)) const noexcept(false); get_single_device(const pstring &classname,
bool (*cc)(core_device_t *)) const noexcept(false);
/// \brief Get single device filtered by class and name /// \brief Get single device filtered by class and name
/// ///
@ -91,7 +93,7 @@ namespace netlist
// logging // logging
log_type & log() noexcept { return m_log; } log_type &log() noexcept { return m_log; }
const log_type &log() const noexcept { return m_log; } const log_type &log() const noexcept { return m_log; }
plib::dynamic_library_base &static_solver_lib() const noexcept plib::dynamic_library_base &static_solver_lib() const noexcept
@ -108,23 +110,23 @@ namespace netlist
void set_static_solver_lib( void set_static_solver_lib(
std::unique_ptr<plib::dynamic_library_base> &&lib); std::unique_ptr<plib::dynamic_library_base> &&lib);
netlist_t & exec() noexcept { return *m_netlist; } netlist_t &exec() noexcept { return *m_netlist; }
const netlist_t &exec() const noexcept { return *m_netlist; } const netlist_t &exec() const noexcept { return *m_netlist; }
// state handling // state handling
plib::state_manager_t &run_state_manager() noexcept { return m_state; } plib::state_manager_t &run_state_manager() noexcept { return m_state; }
template <typename O, typename C> template <typename O, typename C>
void save(O &owner, C &state, const pstring &module, void
const pstring &stname) save(O &owner, C &state, const pstring &module, const pstring &stname)
{ {
this->run_state_manager().save_item(plib::void_ptr_cast(&owner), this->run_state_manager().save_item(plib::void_ptr_cast(&owner),
state, module + "." + stname); state, module + "." + stname);
} }
template <typename O, typename C> template <typename O, typename C>
void save(O &owner, C *state, const pstring &module, void save(O &owner, C *state, const pstring &module,
const pstring &stname, const std::size_t count) const pstring &stname, const std::size_t count)
{ {
this->run_state_manager().save_state_ptr( this->run_state_manager().save_state_ptr(
plib::void_ptr_cast(&owner), module + "." + stname, plib::void_ptr_cast(&owner), module + "." + stname,
@ -165,8 +167,8 @@ namespace netlist
/// \param dev Device to be registered /// \param dev Device to be registered
template <typename T> template <typename T>
void register_device(const pstring &name, void register_device(const pstring &name,
device_arena::owned_ptr<T> && dev) noexcept(false) device_arena::owned_ptr<T> &&dev) noexcept(false)
{ {
for (auto &d : m_devices) for (auto &d : m_devices)
if (d.first == name) if (d.first == name)
@ -187,11 +189,11 @@ namespace netlist
/// \param dev Device to be registered /// \param dev Device to be registered
template <typename T> template <typename T>
void register_device(const pstring &name, void
device_arena::unique_ptr<T> && dev) register_device(const pstring &name, device_arena::unique_ptr<T> &&dev)
{ {
register_device(name, device_arena::owned_ptr<T>(dev.release(), register_device(name, device_arena::owned_ptr<T>(
true, dev.get_deleter())); dev.release(), true, dev.get_deleter()));
} }
/// \brief Remove device /// \brief Remove device
@ -203,24 +205,24 @@ namespace netlist
void remove_device(core_device_t *dev); void remove_device(core_device_t *dev);
setup_t & setup() noexcept { return *m_setup; } setup_t &setup() noexcept { return *m_setup; }
const setup_t &setup() const noexcept { return *m_setup; } const setup_t &setup() const noexcept { return *m_setup; }
nlparse_t & parser() noexcept; nlparse_t &parser() noexcept;
const nlparse_t &parser() const noexcept; const nlparse_t &parser() const noexcept;
// FIXME: make a post load member and include code there // FIXME: make a post load member and include code there
void rebuild_lists(); // must be called after post_load ! void rebuild_lists(); // must be called after post_load !
static void compile_defines( static void
std::vector<std::pair<pstring, pstring>> &defs); compile_defines(std::vector<std::pair<pstring, pstring>> &defs);
static pstring version(); static pstring version();
static pstring version_patchlevel(); static pstring version_patchlevel();
nets_collection_type & nets() noexcept { return m_nets; } nets_collection_type &nets() noexcept { return m_nets; }
const nets_collection_type &nets() const noexcept { return m_nets; } const nets_collection_type &nets() const noexcept { return m_nets; }
devices_collection_type & devices() noexcept { return m_devices; } devices_collection_type &devices() noexcept { return m_devices; }
const devices_collection_type &devices() const noexcept const devices_collection_type &devices() const noexcept
{ {
return m_devices; return m_devices;
@ -234,13 +236,13 @@ namespace netlist
return plib::make_unique<T>(m_pool, std::forward<Args>(args)...); return plib::make_unique<T>(m_pool, std::forward<Args>(args)...);
} }
// memory pool - still needed in some places // memory pool - still needed in some places
device_arena & pool() noexcept { return m_pool; } device_arena &pool() noexcept { return m_pool; }
const device_arena &pool() const noexcept { return m_pool; } const device_arena &pool() const noexcept { return m_pool; }
struct stats_info struct stats_info
{ {
const detail::queue_t & m_queue; // performance const detail::queue_t &m_queue; // performance
const plib::pperftime_t<true> & m_stat_mainloop; const plib::pperftime_t<true> &m_stat_mainloop;
const plib::pperfcount_t<true> &m_perf_out_processed; const plib::pperfcount_t<true> &m_perf_out_processed;
}; };
@ -256,8 +258,8 @@ namespace netlist
/// ///
void free_setup_resources(); void free_setup_resources();
#if !(NL_USE_INPLACE_CORE_TERMS) #if !(NL_USE_INPLACE_CORE_TERMS)
std::vector<detail::core_terminal_t *> &core_terms( std::vector<detail::core_terminal_t *> &
const detail::net_t &net) core_terms(const detail::net_t &net)
{ {
return m_core_terms[&net]; return m_core_terms[&net];
} }
@ -281,7 +283,7 @@ namespace netlist
#if !(NL_USE_INPLACE_CORE_TERMS) #if !(NL_USE_INPLACE_CORE_TERMS)
// all terms for a net // all terms for a net
std::unordered_map<const detail::net_t *, std::unordered_map<const detail::net_t *,
std::vector<detail::core_terminal_t *>> std::vector<detail::core_terminal_t *>>
m_core_terms; m_core_terms;
#endif #endif
// dummy version // dummy version

View File

@ -8,8 +8,12 @@
#ifndef NL_CORE_OBJECT_ARRAY_H_ #ifndef NL_CORE_OBJECT_ARRAY_H_
#define NL_CORE_OBJECT_ARRAY_H_ #define NL_CORE_OBJECT_ARRAY_H_
#include "base_objects.h"
#include "logic.h"
#include "../nltypes.h" #include "../nltypes.h"
#include "../plib/pfmtlog.h"
#include "../plib/plists.h" #include "../plib/plists.h"
#include "../plib/pstring.h" #include "../plib/pstring.h"

View File

@ -10,6 +10,7 @@
#include "../nltypes.h" #include "../nltypes.h"
#include "../plib/pfmtlog.h"
#include "../plib/pstring.h" #include "../plib/pstring.h"
namespace netlist namespace netlist

View File

@ -78,7 +78,7 @@ namespace netlist::devices {
} }
else else
{ {
newstate_setreset(set, reset); set_reset(set, reset);
m_CLK.inactivate(); m_CLK.inactivate();
m_D.inactivate(); m_D.inactivate();
} }
@ -86,7 +86,7 @@ namespace netlist::devices {
NETLIB_HANDLERI(clk) NETLIB_HANDLERI(clk)
{ {
newstate_clk(m_nextD); set_output(m_nextD);
m_CLK.inactivate(); m_CLK.inactivate();
} }
@ -101,14 +101,14 @@ namespace netlist::devices {
nld_power_pins m_power_pins; nld_power_pins m_power_pins;
void newstate_clk(const netlist_sig_t stateQ) void set_output(const netlist_sig_t stateQ)
{ {
static constexpr auto delay = NLTIME_FROM_NS(150); static constexpr const auto delay = NLTIME_FROM_NS(150);
m_Q.push(stateQ, delay); m_Q.push(stateQ, delay);
m_QQ.push(!stateQ, delay); m_QQ.push(!stateQ, delay);
} }
void newstate_setreset(const netlist_sig_t stateQ, const netlist_sig_t stateQQ) void set_reset(const netlist_sig_t stateQ, const netlist_sig_t stateQQ)
{ {
// Q: 150 ns, QQ: 200 ns // Q: 150 ns, QQ: 200 ns
static constexpr const std::array<netlist_time, 2> delay = { NLTIME_FROM_NS(150), NLTIME_FROM_NS(200) }; static constexpr const std::array<netlist_time, 2> delay = { NLTIME_FROM_NS(150), NLTIME_FROM_NS(200) };

View File

@ -17,6 +17,7 @@
namespace netlist namespace netlist
{ {
// nothing in here. This should not change. // nothing in here. This should not change.
} // namespace netlist } // namespace netlist
#endif // NLBASE_H_ #endif // NLBASE_H_

View File

@ -134,7 +134,7 @@ namespace netlist
/// brief default minimum alignment of mempool_arena /// brief default minimum alignment of mempool_arena
/// ///
/// 256 is the best compromise between logic applications like MAME /// 256 is the best compromise between logic applications like MAME
/// TTL games (e.g. pong) and analog applications like e.g. kidnikik /// TTL games (e.g. pong) and analog applications like e.g. kidniki
/// sound. /// sound.
/// ///
/// Best performance for pong is achieved with a value of 16, but this /// Best performance for pong is achieved with a value of 16, but this
@ -178,7 +178,7 @@ namespace netlist
/// | 63 | 1,000,000,000,000 | 9,223,372 | 107| 0.3 | /// | 63 | 1,000,000,000,000 | 9,223,372 | 107| 0.3 |
/// ///
using INTERNAL_RES = std::integral_constant<long long int, using INTERNAL_RES = std::integral_constant<long long int,
10'000'000'000LL>; // NOLINT 10'000'000'000LL>; // NOLINT
/// \brief Recommended clock to be used /// \brief Recommended clock to be used
/// ///
@ -187,7 +187,7 @@ namespace netlist
/// contains code illustrating how to deal with remainders if \ref /// contains code illustrating how to deal with remainders if \ref
/// INTERNAL_RES is bigger than NETLIST_CLOCK. /// INTERNAL_RES is bigger than NETLIST_CLOCK.
using DEFAULT_CLOCK = std::integral_constant<int, using DEFAULT_CLOCK = std::integral_constant<int,
1'000'000'000>; // NOLINT 1'000'000'000>; // NOLINT
/// \brief Default logic family /// \brief Default logic family
/// ///
@ -196,27 +196,27 @@ namespace netlist
/// \brief Maximum queue size /// \brief Maximum queue size
/// ///
using max_queue_size = std::integral_constant<std::size_t, using max_queue_size = std::integral_constant<std::size_t,
1024>; // NOLINT 1024>; // NOLINT
/// \brief Maximum queue size for solvers /// \brief Maximum queue size for solvers
/// ///
using max_solver_queue_size = std::integral_constant<std::size_t, using max_solver_queue_size = std::integral_constant<std::size_t,
512>; // NOLINT 512>; // NOLINT
/// \brief Support float type for matrix calculations. /// \brief Support float type for matrix calculations.
/// ///
/// Defaults to NL_USE_ACADEMIC_SOLVERS to provide faster build times /// Defaults to NL_USE_ACADEMIC_SOLVERS to provide faster build times
using use_float_matrix = std::integral_constant<bool, using use_float_matrix = std::integral_constant<
NL_USE_ACADEMIC_SOLVERS>; bool, NL_USE_ACADEMIC_SOLVERS>;
/// \brief Support long double type for matrix calculations. /// \brief Support long double type for matrix calculations.
/// ///
/// Defaults to NL_USE_ACADEMIC_SOLVERS to provide faster build times /// Defaults to NL_USE_ACADEMIC_SOLVERS to provide faster build times
using use_long_double_matrix = std::integral_constant<bool, using use_long_double_matrix = std::integral_constant<
NL_USE_ACADEMIC_SOLVERS>; bool, NL_USE_ACADEMIC_SOLVERS>;
using use_float128_matrix = std::integral_constant<bool, using use_float128_matrix = std::integral_constant<bool,
NL_USE_FLOAT128>; NL_USE_FLOAT128>;
/// \brief Floating point types used /// \brief Floating point types used
/// ///
@ -242,7 +242,7 @@ namespace netlist
/// the default approach. It is ~20% slower. /// the default approach. It is ~20% slower.
/// ///
using use_copy_instead_of_reference = std::integral_constant<bool, using use_copy_instead_of_reference = std::integral_constant<bool,
false>; false>;
/// \brief Avoid unnecessary queue pushes /// \brief Avoid unnecessary queue pushes
/// ///
@ -416,16 +416,4 @@ namespace netlist
#endif #endif
} // namespace netlist } // namespace netlist
//============================================================
// Asserts
//============================================================
#define nl_assert(x) \
do \
{ \
if (NL_DEBUG) \
passert_always(x); \
} while (0)
#define nl_assert_always(x, msg) passert_always_msg(x, msg)
#endif // NLCONFIG_H_ #endif // NLCONFIG_H_

View File

@ -8,13 +8,53 @@
#ifndef NL_ERRSTR_H_ #ifndef NL_ERRSTR_H_
#define NL_ERRSTR_H_ #define NL_ERRSTR_H_
#include "plib/pexception.h"
#include "plib/pfmtlog.h" #include "plib/pfmtlog.h"
namespace netlist namespace netlist
{ {
static constexpr const char sHINT_NO_DEACTIVATE[] = ".HINT_NO_DEACTIVATE"; // NOLINT(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) static constexpr const char sHINT_NO_DEACTIVATE[]
static constexpr const char sHINT_NC[] = ".HINT_NC"; // NOLINT(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) = ".HINT_NO_DEACTIVATE"; // NOLINT(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
static constexpr const char sHINT_NC[]
= ".HINT_NC"; // NOLINT(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
// -------------------------------------------------------------------------
// Exceptions
// -------------------------------------------------------------------------
/// \brief Generic netlist exception.
/// The exception is used in all events which are considered fatal.
class nl_exception : public plib::pexception
{
public:
/// \brief Constructor.
/// Allows a descriptive text to be passed to the exception
explicit nl_exception(const pstring &text //!< text to be passed
)
: plib::pexception(text)
{
}
/// \brief Constructor.
/// Allows to use \ref plib::pfmt logic to be used in exception
template <typename... Args>
explicit nl_exception(const pstring &fmt, //!< format to be used
Args &&...args //!< arguments to be passed
)
: plib::pexception(plib::pfmt(fmt)(std::forward<Args>(args)...))
{
}
};
// -------------------------------------------------------------------------
// Error messages
// -------------------------------------------------------------------------
// clang-format off
// nl_base.cpp // nl_base.cpp
@ -187,9 +227,19 @@ namespace netlist
PERRMSGV(MF_FILE_OPEN_ERROR, 1, "Error opening file: {1}") PERRMSGV(MF_FILE_OPEN_ERROR, 1, "Error opening file: {1}")
// clang-format on
} // namespace netlist } // namespace netlist
// -------------------------------------------------------------------------
// Asserts
// -------------------------------------------------------------------------
#define nl_assert(x) \
do \
{ \
if (NL_DEBUG) \
passert_always(x); \
} while (0)
#define nl_assert_always(x, msg) passert_always_msg(x, msg)
#endif // NL_ERRSTR_H_ #endif // NL_ERRSTR_H_

View File

@ -8,14 +8,14 @@
#ifndef NLSETUP_H_ #ifndef NLSETUP_H_
#define NLSETUP_H_ #define NLSETUP_H_
#include "nl_config.h"
#include "nltypes.h"
#include "plib/ppreprocessor.h" #include "plib/ppreprocessor.h"
#include "plib/psource.h" #include "plib/psource.h"
#include "plib/pstream.h" #include "plib/pstream.h"
#include "plib/pstring.h" #include "plib/pstring.h"
#include "nl_config.h"
#include "nltypes.h"
#include <initializer_list> #include <initializer_list>
#include <memory> #include <memory>
#include <stack> #include <stack>
@ -26,118 +26,95 @@
// MACROS - netlist definitions // MACROS - netlist definitions
//============================================================ //============================================================
#define NET_STR(x) # x #define NET_STR(x) #x
#define NET_MODEL(model) \ #define NET_MODEL(model) setup.register_model(model);
setup.register_model(model);
#define ALIAS(alias, name) \ #define ALIAS(alias, name) setup.register_alias(#alias, #name);
setup.register_alias(# alias, # name);
#define DIPPINS(pin1, ...) \ #define DIPPINS(pin1, ...) \
setup.register_dip_alias_arr( # pin1 ", " # __VA_ARGS__); setup.register_dip_alias_arr(#pin1 ", " #__VA_ARGS__);
// to be used to reference new library truth table devices // to be used to reference new library truth table devices
#define NET_REGISTER_DEV(type, name) \ #define NET_REGISTER_DEV(type, name) setup.register_dev(#type, #name);
setup.register_dev(# type, # name);
// name is first element so that __VA_ARGS__ always has one element // name is first element so that __VA_ARGS__ always has one element
#define NET_REGISTER_DEVEXT(type, ...) \ #define NET_REGISTER_DEVEXT(type, ...) \
setup.register_dev(# type, { PSTRINGIFY_VA(__VA_ARGS__) }); setup.register_dev(#type, {PSTRINGIFY_VA(__VA_ARGS__)});
#define NET_CONNECT(name, input, output) \ #define NET_CONNECT(name, input, output) \
setup.register_link(# name "." # input, # output); setup.register_link(#name "." #input, #output);
#define NET_C(term1, ...) \ #define NET_C(term1, ...) \
setup.register_connection_arr( # term1 ", " # __VA_ARGS__); setup.register_connection_arr(#term1 ", " #__VA_ARGS__);
#define PARAM(name, val) \ #define PARAM(name, val) setup.register_param(NET_STR(name), NET_STR(val));
setup.register_param(NET_STR(name), NET_STR(val));
#define DEFPARAM(name, val) \ #define DEFPARAM(name, val) \
setup.register_default_param(NET_STR(name), NET_STR(val)); setup.register_default_param(NET_STR(name), NET_STR(val));
#define HINT(name, val) \ #define HINT(name, val) setup.register_hint(#name, ".HINT_" #val);
setup.register_hint(# name , ".HINT_" # val);
#define NETDEV_PARAMI(name, param, val) \ #define NETDEV_PARAMI(name, param, val) \
setup.register_param(# name "." # param, val); setup.register_param(#name "." #param, val);
#define NETLIST_NAME(name) netlist ## _ ## name #define NETLIST_NAME(name) netlist##_##name
#define NETLIST_EXTERNAL(name) \ #define NETLIST_EXTERNAL(name) \
void NETLIST_NAME(name)(netlist::nlparse_t &setup); void NETLIST_NAME(name)(netlist::nlparse_t & setup);
#define NETLIST_START(name) \ #define NETLIST_START(name) \
void NETLIST_NAME(name)([[maybe_unused]] netlist::nlparse_t &setup) \ void NETLIST_NAME(name)([[maybe_unused]] netlist::nlparse_t & setup)
#define LOCAL_SOURCE(name) \ #define LOCAL_SOURCE(name) \
setup.register_source_proc(# name, &NETLIST_NAME(name)); setup.register_source_proc(#name, &NETLIST_NAME(name));
#define EXTERNAL_SOURCE(name) \ #define EXTERNAL_SOURCE(name) \
setup.register_source_proc(# name, &NETLIST_NAME(name)); setup.register_source_proc(#name, &NETLIST_NAME(name));
#define LOCAL_LIB_ENTRY_2(type, name) \ #define LOCAL_LIB_ENTRY_2(type, name) \
type ## _SOURCE(name) \ type##_SOURCE(name) setup.register_lib_entry(#name, "", PSOURCELOC());
setup.register_lib_entry(# name, "", PSOURCELOC());
#define LOCAL_LIB_ENTRY_3(type, name, param_spec) \ #define LOCAL_LIB_ENTRY_3(type, name, param_spec) \
type ## _SOURCE(name) \ type##_SOURCE(name) \
setup.register_lib_entry(# name, param_spec, PSOURCELOC()); setup.register_lib_entry(#name, param_spec, PSOURCELOC());
#define LOCAL_LIB_ENTRY(...) PCALLVARARG(LOCAL_LIB_ENTRY_, LOCAL, __VA_ARGS__) #define LOCAL_LIB_ENTRY(...) PCALLVARARG(LOCAL_LIB_ENTRY_, LOCAL, __VA_ARGS__)
#define EXTERNAL_LIB_ENTRY(...) PCALLVARARG(LOCAL_LIB_ENTRY_, EXTERNAL, __VA_ARGS__) #define EXTERNAL_LIB_ENTRY(...) \
PCALLVARARG(LOCAL_LIB_ENTRY_, EXTERNAL, __VA_ARGS__)
#define INCLUDE(name) \ #define INCLUDE(name) setup.include(#name);
setup.include(# name);
#define SUBMODEL(model, name) \ #define SUBMODEL(model, name) \
setup.namespace_push(# name); \ setup.namespace_push(#name); \
setup.include(# model); \ setup.include(#model); \
setup.namespace_pop(); setup.namespace_pop();
#define OPTIMIZE_FRONTIER(attach, r_in, r_out) \ #define OPTIMIZE_FRONTIER(attach, r_in, r_out) \
setup.register_frontier(# attach, PSTRINGIFY_VA(r_in), PSTRINGIFY_VA(r_out)); setup.register_frontier(#attach, PSTRINGIFY_VA(r_in), PSTRINGIFY_VA(r_out));
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// truth table defines // truth table defines
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#if 0 #define TRUTH_TABLE(cname, in, out, params) \
#define TRUTHTABLE_START(cname, in, out, pdef_params) \ void NETLIST_NAME(cname##_impl)(netlist::nlparse_t & setup, \
void NETLIST_NAME(cname ## _impl)(netlist::tt_desc &desc); \ netlist::tt_desc & desc); \
static NETLIST_START(cname) static void NETLIST_NAME(cname)(netlist::nlparse_t & setup) \
{ \ { \
netlist::tt_desc xdesc{ #cname, in, out, "" }; \ netlist::tt_desc desc{#cname, in, out, "", {}}; \
auto sloc = PSOURCELOC(); \ NETLIST_NAME(cname##_impl)(setup, desc); \
const pstring def_params = pdef_params; \ setup.truth_table_create(desc, params, PSOURCELOC()); \
NETLIST_NAME(cname ## _impl)(xdesc); \ } \
setup.truth_table_create(xdesc, def_params, std::move(sloc)); \ static void NETLIST_NAME(cname##_impl)( \
} \ [[maybe_unused]] netlist::nlparse_t & setup, netlist::tt_desc & desc)
static void NETLIST_NAME(cname ## _impl)(netlist::tt_desc &desc) \
{
#else
#define TRUTH_TABLE(cname, in, out, pdef_params) \
void NETLIST_NAME(cname ## _impl)(netlist::nlparse_t &setup, netlist::tt_desc &desc); \
static void NETLIST_NAME(cname)(netlist::nlparse_t &setup) \
{ \
netlist::tt_desc desc{ #cname, in, out, "", {} }; \
NETLIST_NAME(cname ## _impl)(setup, desc); \
setup.truth_table_create(desc, pdef_params, PSOURCELOC()); \
} \
static void NETLIST_NAME(cname ## _impl)([[maybe_unused]] netlist::nlparse_t &setup, netlist::tt_desc &desc) \
#endif #define TT_HEAD(x) desc.desc.emplace_back(x);
#define TT_HEAD(x) \ #define TT_LINE(x) desc.desc.emplace_back(x);
desc.desc.emplace_back(x);
#define TT_LINE(x) \ #define TT_FAMILY(x) desc.family = x;
desc.desc.emplace_back(x);
#define TT_FAMILY(x) \
desc.family = x;
#define TRUTHTABLE_ENTRY(name) \ #define TRUTHTABLE_ENTRY(name) \
LOCAL_SOURCE(name) \ LOCAL_SOURCE(name) \
@ -152,10 +129,10 @@ namespace netlist
struct tt_desc struct tt_desc
{ {
pstring name; pstring name;
unsigned long ni; unsigned long ni;
unsigned long no; unsigned long no;
pstring family; pstring family;
std::vector<pstring> desc; std::vector<pstring> desc;
}; };
@ -193,25 +170,29 @@ namespace netlist
/// \param type the alias type see \ref alias_type /// \param type the alias type see \ref alias_type
/// \param alias the alias to be qualified /// \param alias the alias to be qualified
/// \param points_to the pin aliased /// \param points_to the pin aliased
void register_alias(detail::alias_type type, const pstring &alias, const pstring &points_to); void register_alias(detail::alias_type type, const pstring &alias,
const pstring &points_to);
/// \brief Register an aliases where alias and references are fully qualified names /// \brief Register an aliases where alias and references are fully qualified names
/// \param type the alias type see \ref alias_type /// \param type the alias type see \ref alias_type
/// \param alias the alias to be qualified /// \param alias the alias to be qualified
/// \param points_to the pin aliased /// \param points_to the pin aliased
void register_fqn_alias(detail::alias_type type, const pstring &alias, const pstring &points_to); void register_fqn_alias(detail::alias_type type, const pstring &alias,
const pstring &points_to);
void register_dip_alias_arr(const pstring &terms); void register_dip_alias_arr(const pstring &terms);
// last argument only needed by nltool // last argument only needed by nltool
void register_dev(const pstring &classname, const pstring &name, void register_dev(const pstring &classname, const pstring &name,
const std::vector<pstring> &params_and_connections, const std::vector<pstring> &params_and_connections,
factory::element_t **factory_element = nullptr); factory::element_t **factory_element = nullptr);
void register_dev(const pstring &classname, std::initializer_list<const char *> more_parameters); void register_dev(const pstring &classname,
std::initializer_list<const char *> more_parameters);
void register_dev(const pstring &classname, const pstring &name) void register_dev(const pstring &classname, const pstring &name)
{ {
register_dev(classname, name, std::vector<pstring>()); register_dev(classname, name, std::vector<pstring>());
} }
void register_hint(const pstring &object_name, const pstring &hint_name); void
register_hint(const pstring &object_name, const pstring &hint_name);
void register_connection(const pstring &sin, const pstring &sout); void register_connection(const pstring &sin, const pstring &sout);
void register_connection_arr(const pstring &terms); void register_connection_arr(const pstring &terms);
@ -230,20 +211,23 @@ namespace netlist
register_param_fp(param, plib::narrow_cast<nl_fptype>(value)); register_param_fp(param, plib::narrow_cast<nl_fptype>(value));
} }
void register_lib_entry(const pstring &name, const pstring &def_params, plib::source_location &&loc); void register_lib_entry(const pstring &name, const pstring &def_params,
plib::source_location &&loc);
void register_frontier(const pstring &attach, const pstring &r_IN, const pstring &r_OUT); void register_frontier(const pstring &attach, const pstring &r_IN,
const pstring &r_OUT);
// register a source // register a source
template <typename S, typename... Args> template <typename S, typename... Args>
void register_source(Args&&... args) void register_source(Args &&...args)
{ {
m_sources.add_source<S>(std::forward<Args>(args)...); m_sources.add_source<S>(std::forward<Args>(args)...);
} }
void register_source_proc(const pstring &name, nlsetup_func func); void register_source_proc(const pstring &name, nlsetup_func func);
void truth_table_create(tt_desc &desc, const pstring &def_params, plib::source_location &&loc); void truth_table_create(tt_desc &desc, const pstring &def_params,
plib::source_location &&loc);
// include other files // include other files
@ -256,17 +240,18 @@ namespace netlist
// FIXME: used by source_t - need a different approach at some time // FIXME: used by source_t - need a different approach at some time
bool parse_stream(plib::istream_uptr &&in_stream, const pstring &name); bool parse_stream(plib::istream_uptr &&in_stream, const pstring &name);
bool parse_tokens(const plib::detail::token_store_t &tokens, const pstring &name); bool parse_tokens(const plib::detail::token_store_t &tokens,
const pstring &name);
template <typename S, typename... Args> template <typename S, typename... Args>
void add_include(Args&&... args) void add_include(Args &&...args)
{ {
m_includes.add_source<S>(std::forward<Args>(args)...); m_includes.add_source<S>(std::forward<Args>(args)...);
} }
void add_define(const pstring &def, const pstring &val) void add_define(const pstring &def, const pstring &val)
{ {
m_defines.insert({ def, plib::ppreprocessor::define_t(def, val)}); m_defines.insert({def, plib::ppreprocessor::define_t(def, val)});
} }
void add_define(const pstring &define); void add_define(const pstring &define);
@ -274,10 +259,10 @@ namespace netlist
// register a list of logs // register a list of logs
void register_dynamic_log_devices(const std::vector<pstring> &log_list); void register_dynamic_log_devices(const std::vector<pstring> &log_list);
factory::list_t &factory() noexcept; factory::list_t &factory() noexcept;
const factory::list_t &factory() const noexcept; const factory::list_t &factory() const noexcept;
log_type &log() noexcept { return m_log; } log_type &log() noexcept { return m_log; }
const log_type &log() const noexcept { return m_log; } const log_type &log() const noexcept { return m_log; }
plib::istream_uptr get_data_stream(const pstring &name); plib::istream_uptr get_data_stream(const pstring &name);
@ -285,23 +270,22 @@ namespace netlist
private: private:
pstring namespace_prefix() const; pstring namespace_prefix() const;
pstring build_fqn(const pstring &obj_name) const; pstring build_fqn(const pstring &obj_name) const;
void register_param_fp(const pstring &param, nl_fptype value); void register_param_fp(const pstring &param, nl_fptype value);
bool device_exists(const pstring &name) const; bool device_exists(const pstring &name) const;
// FIXME: stale? - remove later // FIXME: stale? - remove later
void remove_connections(const pstring &pin); void remove_connections(const pstring &pin);
plib::ppreprocessor::defines_map_type m_defines; plib::ppreprocessor::defines_map_type m_defines;
plib::psource_collection_t m_includes; plib::psource_collection_t m_includes;
std::stack<pstring> m_namespace_stack; std::stack<pstring> m_namespace_stack;
plib::psource_collection_t m_sources; plib::psource_collection_t m_sources;
detail::abstract_t & m_abstract; detail::abstract_t &m_abstract;
log_type &m_log; log_type &m_log;
unsigned m_frontier_cnt; unsigned m_frontier_cnt;
}; };
} // namespace netlist } // namespace netlist
#endif // NLSETUP_H_ #endif // NLSETUP_H_

View File

@ -14,9 +14,6 @@
#include "nl_config.h" #include "nl_config.h"
#include "plib/pmempool.h"
#include "plib/ppmf.h"
#include "plib/pstring.h"
#include "plib/ptime.h" #include "plib/ptime.h"
#include "plib/ptypes.h" #include "plib/ptypes.h"
@ -170,12 +167,12 @@ namespace netlist
/// ///
/// \note This is not the right location yet. /// \note This is not the right location yet.
/// ///
using device_arena = std::conditional_t< using device_arena = std::conditional_t<
config::use_mempool::value, config::use_mempool::value,
plib::mempool_arena<plib::aligned_arena<>, plib::mempool_arena<plib::aligned_arena<>,
config::mempool_align::value>, config::mempool_align::value>,
plib::aligned_arena<>>; plib::aligned_arena<>>;
using host_arena = plib::aligned_arena<>; using host_arena = plib::aligned_arena<>;
using log_type = plib::plog_base<NL_DEBUG>; using log_type = plib::plog_base<NL_DEBUG>;
@ -184,25 +181,19 @@ namespace netlist
// Types needed by various includes // Types needed by various includes
//============================================================ //============================================================
/// \brief Time step type.
///
/// May be either FORWARD or RESTORE
///
enum class time_step_type
{
FORWARD, //!< forward time
RESTORE //!< restore state before last forward
};
/// \brief Delegate type for device notification.
///
using nl_delegate = plib::pmfp<void()>;
using nl_delegate_ts = plib::pmfp<void(time_step_type, nl_fptype)>;
using nl_delegate_dyn = plib::pmfp<void()>;
namespace detail namespace detail
{ {
/// \brief Time step type.
///
/// May be either FORWARD or RESTORE
///
enum class time_step_type
{
FORWARD, //!< forward time
RESTORE //!< restore state before last forward
};
/// \brief Enum specifying the type of object /// \brief Enum specifying the type of object
/// ///
enum class terminal_type enum class terminal_type
@ -220,10 +211,10 @@ namespace netlist
/// ///
enum class alias_type enum class alias_type
{ {
UNKNOWN, //!< Used as a placeholder during code changes UNKNOWN, //!< Used as a placeholder during code changes
INTERNAL, //!< the alias references a internal pin INTERNAL, //!< the alias references a internal pin
FUNCTIONAL, //!< Used for aliases e.g. in BJTs : ALIAS("B", FUNCTIONAL, //!< Used for aliases e.g. in BJTs : ALIAS("B",
//!< somesub.p()) //!< somesub.p())
PACKAGE_PIN, //!< the alias references a package pin, e.g. ALIAS(13, PACKAGE_PIN, //!< the alias references a package pin, e.g. ALIAS(13,
//!< B.CLK) //!< B.CLK)
READABILITY, //!< the alias is used to improved readability, e.g. READABILITY, //!< the alias is used to improved readability, e.g.
@ -317,37 +308,6 @@ namespace netlist
using desc_const_t = std::integral_constant<const T, V>; using desc_const_t = std::integral_constant<const T, V>;
}; };
//============================================================
// Exceptions
//============================================================
/// \brief Generic netlist exception.
/// The exception is used in all events which are considered fatal.
class nl_exception : public plib::pexception
{
public:
/// \brief Constructor.
/// Allows a descriptive text to be passed to the exception
explicit nl_exception(const pstring &text //!< text to be passed
)
: plib::pexception(text)
{
}
/// \brief Constructor.
/// Allows to use \ref plib::pfmt logic to be used in exception
template <typename... Args>
explicit nl_exception(const pstring &fmt, //!< format to be used
Args &&...args //!< arguments to be passed
)
: plib::pexception(plib::pfmt(fmt)(std::forward<Args>(args)...))
{
}
};
} // namespace netlist } // namespace netlist
#endif // NLTYPES_H_ #endif // NLTYPES_H_

View File

@ -9,6 +9,7 @@
/// ///
#include "pconfig.h" #include "pconfig.h"
#include "pgsl.h"
#include "ptypes.h" #include "ptypes.h"
#include <algorithm> #include <algorithm>
@ -271,8 +272,7 @@ namespace plib
/// FIXME: limited implementation /// FIXME: limited implementation
/// ///
template <typename T1, typename T2> template <typename T1, typename T2>
static inline static inline auto pow(T1 v, T2 p) noexcept -> decltype(std::pow(v, p))
auto pow(T1 v, T2 p) noexcept -> decltype(std::pow(v, p))
{ {
return std::pow(v, p); return std::pow(v, p);
} }
@ -283,60 +283,30 @@ namespace plib
return constants<FLOAT128>::one() / v; return constants<FLOAT128>::one() / v;
} }
static FLOAT128 abs(FLOAT128 v) noexcept static FLOAT128 abs(FLOAT128 v) noexcept { return fabsq(v); }
{
return fabsq(v);
}
static FLOAT128 sqrt(FLOAT128 v) noexcept static FLOAT128 sqrt(FLOAT128 v) noexcept { return sqrtq(v); }
{
return sqrtq(v);
}
static FLOAT128 hypot(FLOAT128 v1, FLOAT128 v2) noexcept static FLOAT128 hypot(FLOAT128 v1, FLOAT128 v2) noexcept
{ {
return hypotq(v1, v2); return hypotq(v1, v2);
} }
static FLOAT128 exp(FLOAT128 v) noexcept static FLOAT128 exp(FLOAT128 v) noexcept { return expq(v); }
{
return expq(v);
}
static FLOAT128 log(FLOAT128 v) noexcept static FLOAT128 log(FLOAT128 v) noexcept { return logq(v); }
{
return logq(v);
}
static FLOAT128 tanh(FLOAT128 v) noexcept static FLOAT128 tanh(FLOAT128 v) noexcept { return tanhq(v); }
{
return tanhq(v);
}
static FLOAT128 floor(FLOAT128 v) noexcept static FLOAT128 floor(FLOAT128 v) noexcept { return floorq(v); }
{
return floorq(v);
}
static FLOAT128 log1p(FLOAT128 v) noexcept static FLOAT128 log1p(FLOAT128 v) noexcept { return log1pq(v); }
{
return log1pq(v);
}
static FLOAT128 sin(FLOAT128 v) noexcept static FLOAT128 sin(FLOAT128 v) noexcept { return sinq(v); }
{
return sinq(v);
}
static FLOAT128 cos(FLOAT128 v) noexcept static FLOAT128 cos(FLOAT128 v) noexcept { return cosq(v); }
{
return cosq(v);
}
static FLOAT128 trunc(FLOAT128 v) noexcept static FLOAT128 trunc(FLOAT128 v) noexcept { return truncq(v); }
{
return truncq(v);
}
template <typename T> template <typename T>
static FLOAT128 pow(FLOAT128 v, T p) noexcept static FLOAT128 pow(FLOAT128 v, T p) noexcept
@ -346,8 +316,8 @@ namespace plib
static FLOAT128 pow(FLOAT128 v, int p) noexcept static FLOAT128 pow(FLOAT128 v, int p) noexcept
{ {
if (p==2) if (p == 2)
return v*v; return v * v;
else else
return powq(v, static_cast<FLOAT128>(p)); return powq(v, static_cast<FLOAT128>(p));
} }
@ -364,7 +334,7 @@ namespace plib
constexpr bool is_pow2(T v) noexcept constexpr bool is_pow2(T v) noexcept
{ {
static_assert(is_integral<T>::value, "is_pow2 needs integer arguments"); static_assert(is_integral<T>::value, "is_pow2 needs integer arguments");
return !(v & (v-1)); return !(v & (v - 1));
} }
/// \brief return absolute value of signed argument /// \brief return absolute value of signed argument
@ -373,9 +343,9 @@ namespace plib
/// \param v argument /// \param v argument
/// \return absolute value of argument /// \return absolute value of argument
/// ///
template<typename T> template <typename T>
constexpr constexpr std::enable_if_t<
std::enable_if_t<plib::is_integral<T>::value && plib::is_signed<T>::value, T> plib::is_integral<T>::value && plib::is_signed<T>::value, T>
abs(T v) noexcept abs(T v) noexcept
{ {
return v < 0 ? -v : v; return v < 0 ? -v : v;
@ -387,9 +357,9 @@ namespace plib
/// \param v argument /// \param v argument
/// \return argument since it has no sign /// \return argument since it has no sign
/// ///
template<typename T> template <typename T>
constexpr constexpr std::enable_if_t<
std::enable_if_t<plib::is_integral<T>::value && plib::is_unsigned<T>::value, T> plib::is_integral<T>::value && plib::is_unsigned<T>::value, T>
abs(T v) noexcept abs(T v) noexcept
{ {
return v; return v;
@ -406,16 +376,14 @@ namespace plib
/// \param n first argument /// \param n first argument
/// \return greatest common denominator of m and n /// \return greatest common denominator of m and n
/// ///
template<typename M, typename N> template <typename M, typename N>
constexpr typename std::common_type<M, N>::type constexpr typename std::common_type<M, N>::type
gcd(M m, N n) noexcept //NOLINT(misc-no-recursion) gcd(M m, N n) noexcept // NOLINT(misc-no-recursion)
{ {
static_assert(plib::is_integral<M>::value, "gcd: M must be an integer"); static_assert(plib::is_integral<M>::value, "gcd: M must be an integer");
static_assert(plib::is_integral<N>::value, "gcd: N must be an integer"); static_assert(plib::is_integral<N>::value, "gcd: N must be an integer");
return m == 0 ? plib::abs(n) return m == 0 ? plib::abs(n) : n == 0 ? plib::abs(m) : gcd(n, m % n);
: n == 0 ? plib::abs(m)
: gcd(n, m % n);
} }
/// \brief return least common multiple /// \brief return least common multiple
@ -429,24 +397,25 @@ namespace plib
/// \param n first argument /// \param n first argument
/// \return least common multiple of m and n /// \return least common multiple of m and n
/// ///
template<typename M, typename N> template <typename M, typename N>
constexpr typename std::common_type<M, N>::type constexpr typename std::common_type<M, N>::type lcm(M m, N n) noexcept
lcm(M m, N n) noexcept
{ {
static_assert(plib::is_integral<M>::value, "lcm: M must be an integer"); static_assert(plib::is_integral<M>::value, "lcm: M must be an integer");
static_assert(plib::is_integral<N>::value, "lcm: N must be an integer"); static_assert(plib::is_integral<N>::value, "lcm: N must be an integer");
return (m != 0 && n != 0) ? (plib::abs(m) / gcd(m, n)) * plib::abs(n) : 0; return (m != 0 && n != 0) ? (plib::abs(m) / gcd(m, n)) * plib::abs(n)
: 0;
} }
template<class T> template <class T>
constexpr const T& clamp( const T& v, const T& low, const T& high) constexpr const T &clamp(const T &v, const T &low, const T &high)
{ {
gsl_Expects(high >= low); gsl_Expects(high >= low);
return (v < low) ? low : (high < v) ? high : v; return (v < low) ? low : (high < v) ? high : v;
} }
static_assert(noexcept(constants<double>::one()), "Not evaluated as constexpr"); static_assert(noexcept(constants<double>::one()),
"Not evaluated as constexpr");
} // namespace plib } // namespace plib

View File

@ -7,35 +7,40 @@
// Specific technical terms // Specific technical terms
// spell-checker: words vsolver // spell-checker: words vsolver
#include "nld_solver.h"
#include "core/setup.h"
#include "nl_setup.h"
#include "nld_matrix_solver.h" #include "nld_matrix_solver.h"
#include "nl_setup.h"
#include "nld_solver.h"
#include "core/setup.h"
#include "plib/putil.h" #include "plib/putil.h"
namespace netlist::solver namespace netlist::solver
{ {
terms_for_net_t::terms_for_net_t(arena_type &arena, analog_net_t * net) terms_for_net_t::terms_for_net_t(arena_type &arena, analog_net_t *net)
: m_nz(arena) : m_nz(arena)
, m_nzrd(arena) , m_nzrd(arena)
, m_nzbd(arena) , m_nzbd(arena)
, m_connected_net_idx(arena) , m_connected_net_idx(arena)
, m_terms(arena) , m_terms(arena)
, m_net(net) , m_net(net)
, m_rail_start(0) , m_rail_start(0)
{ {
} }
void terms_for_net_t::add_terminal(terminal_t *term, int net_other, bool sorted) void
terms_for_net_t::add_terminal(terminal_t *term, int net_other, bool sorted)
{ {
if (sorted) if (sorted)
for (std::size_t i=0; i < m_connected_net_idx.size(); i++) for (std::size_t i = 0; i < m_connected_net_idx.size(); i++)
{ {
if (m_connected_net_idx[i] > net_other) if (m_connected_net_idx[i] > net_other)
{ {
plib::container::insert_at(m_terms, i, term); plib::container::insert_at(m_terms, i, term);
plib::container::insert_at(m_connected_net_idx, i, net_other); plib::container::insert_at(m_connected_net_idx, i,
net_other);
return; return;
} }
} }
@ -47,34 +52,36 @@ namespace netlist::solver
// matrix_solver // matrix_solver
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
matrix_solver_t::matrix_solver_t(devices::nld_solver &main_solver, const pstring &name, matrix_solver_t::matrix_solver_t(devices::nld_solver &main_solver,
const net_list_t &nets, const pstring &name,
const solver::solver_parameters_t *params) const net_list_t &nets,
//: device_t(static_cast<device_t &>(main_solver), name) const solver::solver_parameters_t *params)
: device_t(device_data_t{main_solver.state(), main_solver.name() + "." + name}) //: device_t(static_cast<device_t &>(main_solver), name)
, m_params(*params) : device_t(
, m_gonn(m_arena) device_data_t{main_solver.state(), main_solver.name() + "." + name})
, m_gtn(m_arena) , m_params(*params)
, m_Idrn(m_arena) , m_gonn(m_arena)
, m_connected_net_Vn(m_arena) , m_gtn(m_arena)
, m_iterative_fail(*this, "m_iterative_fail", 0) , m_Idrn(m_arena)
, m_iterative_total(*this, "m_iterative_total", 0) , m_connected_net_Vn(m_arena)
, m_main_solver(main_solver) , m_iterative_fail(*this, "m_iterative_fail", 0)
, m_stat_calculations(*this, "m_stat_calculations", 0) , m_iterative_total(*this, "m_iterative_total", 0)
, m_stat_newton_raphson(*this, "m_stat_newton_raphson", 0) , m_main_solver(main_solver)
, m_stat_newton_raphson_fail(*this, "m_stat_newton_raphson_fail", 0) , m_stat_calculations(*this, "m_stat_calculations", 0)
, m_stat_vsolver_calls(*this, "m_stat_vsolver_calls", 0) , m_stat_newton_raphson(*this, "m_stat_newton_raphson", 0)
, m_last_step(*this, "m_last_step", netlist_time_ext::zero()) , m_stat_newton_raphson_fail(*this, "m_stat_newton_raphson_fail", 0)
, m_step_funcs(m_arena) , m_stat_vsolver_calls(*this, "m_stat_vsolver_calls", 0)
, m_dynamic_funcs(m_arena) , m_last_step(*this, "m_last_step", netlist_time_ext::zero())
, m_inputs(m_arena) , m_step_funcs(m_arena)
, m_ops(0) , m_dynamic_funcs(m_arena)
, m_inputs(m_arena)
, m_ops(0)
{ {
setup_base(this->state().setup(), nets); setup_base(this->state().setup(), nets);
// now setup the matrix // now setup the matrix
setup_matrix(); setup_matrix();
//printf("Freq: %f\n", m_params.m_freq()); // printf("Freq: %f\n", m_params.m_freq());
} }
analog_net_t *matrix_solver_t::get_connected_net(terminal_t *term) analog_net_t *matrix_solver_t::get_connected_net(terminal_t *term)
@ -87,7 +94,8 @@ namespace netlist::solver
m_main_solver.reschedule(this, ts); m_main_solver.reschedule(this, ts);
} }
void matrix_solver_t::setup_base([[maybe_unused]] setup_t &setup, const net_list_t &nets) void matrix_solver_t::setup_base([[maybe_unused]] setup_t &setup,
const net_list_t &nets)
{ {
log().debug("New solver setup\n"); log().debug("New solver setup\n");
std::vector<core_device_t *> step_devices; std::vector<core_device_t *> step_devices;
@ -95,7 +103,7 @@ namespace netlist::solver
m_terms.clear(); m_terms.clear();
for (const auto & net : nets) for (const auto &net : nets)
{ {
m_terms.emplace_back(m_arena, net); m_terms.emplace_back(m_arena, net);
m_rails_temp.emplace_back(m_arena); m_rails_temp.emplace_back(m_arena);
@ -108,68 +116,87 @@ namespace netlist::solver
analog_net_t &net = *nets[k]; analog_net_t &net = *nets[k];
// FIXME: add size() to list // FIXME: add size() to list
// log().debug("adding net with {1} populated connections\n", net.core_terms().size()); // log().debug("adding net with {1} populated connections\n",
// net.core_terms().size());
net.set_solver(this); net.set_solver(this);
for (detail::core_terminal_t * p : net.core_terms_copy()) for (detail::core_terminal_t *p : net.core_terms_copy())
{ {
nl_assert_always(&p->net() == &net, "Net integrity violated"); nl_assert_always(&p->net() == &net, "Net integrity violated");
log().debug("{1} {2} {3}\n", p->name(), net.name(), net.is_rail_net()); log().debug("{1} {2} {3}\n", p->name(), net.name(),
net.is_rail_net());
switch (p->type()) switch (p->type())
{ {
case detail::terminal_type::TERMINAL: case detail::terminal_type::TERMINAL:
if (p->device().is_time_step()) if (p->device().is_time_step())
if (!plib::container::contains(step_devices, &p->device())) if (!plib::container::contains(step_devices,
&p->device()))
step_devices.push_back(&p->device()); step_devices.push_back(&p->device());
if (p->device().is_dynamic()) if (p->device().is_dynamic())
if (!plib::container::contains(dynamic_devices, &p->device())) if (!plib::container::contains(dynamic_devices,
&p->device()))
dynamic_devices.push_back(&p->device()); dynamic_devices.push_back(&p->device());
{ {
auto pterm = plib::dynamic_downcast<terminal_t *>(p); auto pterm = plib::dynamic_downcast<terminal_t *>(
nl_assert_always(bool(pterm), "cast to terminal_t * failed"); p);
nl_assert_always(bool(pterm),
"cast to terminal_t * failed");
add_term(k, *pterm); add_term(k, *pterm);
} }
log().debug("Added terminal {1}\n", p->name()); log().debug("Added terminal {1}\n", p->name());
break; break;
case detail::terminal_type::INPUT: case detail::terminal_type::INPUT:
{ {
proxied_analog_output_t *net_proxy_output = nullptr; proxied_analog_output_t *net_proxy_output = nullptr;
for (auto & input : m_inputs) for (auto &input : m_inputs)
if (input->proxied_net() == &p->net()) if (input->proxied_net() == &p->net())
{
net_proxy_output = input.get();
break;
}
if (net_proxy_output == nullptr)
{ {
pstring new_name(this->name() + "." + pstring(plib::pfmt("m{1}")(m_inputs.size()))); net_proxy_output = input.get();
auto proxied_net = plib::dynamic_downcast<analog_net_t *>(p->net()); break;
nl_assert_always(proxied_net, "Net is not an analog net");
auto net_proxy_output_u = state().make_pool_object<proxied_analog_output_t>(*this, new_name, *proxied_net);
net_proxy_output = net_proxy_output_u.get();
m_inputs.emplace_back(std::move(net_proxy_output_u));
} }
net.remove_terminal(*p);
net_proxy_output->net().add_terminal(*p); if (net_proxy_output == nullptr)
// FIXME: repeated calling - kind of brute force {
net_proxy_output->net().rebuild_list(); pstring new_name(
log().debug("Added input {1}", net_proxy_output->name()); this->name() + "."
+ pstring(plib::pfmt("m{1}")(m_inputs.size())));
auto proxied_net = plib::dynamic_downcast<
analog_net_t *>(p->net());
nl_assert_always(proxied_net,
"Net is not an analog net");
auto net_proxy_output_u
= state()
.make_pool_object<
proxied_analog_output_t>(
*this, new_name, *proxied_net);
net_proxy_output = net_proxy_output_u.get();
m_inputs.emplace_back(
std::move(net_proxy_output_u));
} }
break; net.remove_terminal(*p);
net_proxy_output->net().add_terminal(*p);
// FIXME: repeated calling - kind of brute force
net_proxy_output->net().rebuild_list();
log().debug("Added input {1}",
net_proxy_output->name());
}
break;
case detail::terminal_type::OUTPUT: case detail::terminal_type::OUTPUT:
log().fatal(MF_UNHANDLED_ELEMENT_1_FOUND(p->name())); log().fatal(MF_UNHANDLED_ELEMENT_1_FOUND(p->name()));
throw nl_exception(MF_UNHANDLED_ELEMENT_1_FOUND(p->name())); throw nl_exception(
MF_UNHANDLED_ELEMENT_1_FOUND(p->name()));
} }
} }
net.rebuild_list(); net.rebuild_list();
} }
for (auto &d : step_devices) for (auto &d : step_devices)
m_step_funcs.emplace_back(nl_delegate_ts(&core_device_t::time_step, d)); m_step_funcs.emplace_back(
nl_delegate_ts(&core_device_t::time_step, d));
for (auto &d : dynamic_devices) for (auto &d : dynamic_devices)
m_dynamic_funcs.emplace_back(nl_delegate_dyn(&core_device_t::update_terminals, d)); m_dynamic_funcs.emplace_back(
nl_delegate_dyn(&core_device_t::update_terminals, d));
} }
/// \brief Sort terminals /// \brief Sort terminals
@ -192,7 +219,8 @@ namespace netlist::solver
/// literature but I have found no articles about Gauss Seidel. /// literature but I have found no articles about Gauss Seidel.
/// ///
/// For Gaussian Elimination however increasing order is better suited. /// For Gaussian Elimination however increasing order is better suited.
/// NOTE: Even better would be to sort on elements right of the matrix diagonal. /// NOTE: Even better would be to sort on elements right of the matrix
/// diagonal.
/// FIXME: This entry needs an update. /// FIXME: This entry needs an update.
/// ///
void matrix_solver_t::sort_terms(matrix_sort_type_e sort) void matrix_solver_t::sort_terms(matrix_sort_type_e sort)
@ -202,65 +230,69 @@ namespace netlist::solver
switch (sort) switch (sort)
{ {
case matrix_sort_type_e::PREFER_BAND_MATRIX: case matrix_sort_type_e::PREFER_BAND_MATRIX:
{
for (std::size_t k = 0; k < iN - 1; k++)
{ {
for (std::size_t k = 0; k < iN - 1; k++) auto pk = get_weight_around_diagonal(k, k);
for (std::size_t i = k + 1; i < iN; i++)
{ {
auto pk = get_weight_around_diagonal(k,k); auto pi = get_weight_around_diagonal(i, k);
for (std::size_t i = k+1; i < iN; i++) if (pi < pk)
{ {
auto pi = get_weight_around_diagonal(i,k); std::swap(m_terms[i], m_terms[k]);
if (pi < pk) pk = get_weight_around_diagonal(k, k);
{
std::swap(m_terms[i], m_terms[k]);
pk = get_weight_around_diagonal(k,k);
}
} }
} }
} }
break; }
break;
case matrix_sort_type_e::PREFER_IDENTITY_TOP_LEFT: case matrix_sort_type_e::PREFER_IDENTITY_TOP_LEFT:
{
for (std::size_t k = 0; k < iN - 1; k++)
{ {
for (std::size_t k = 0; k < iN - 1; k++) auto pk = get_left_right_of_diagonal(k, k);
for (std::size_t i = k + 1; i < iN; i++)
{ {
auto pk = get_left_right_of_diagonal(k,k); auto pi = get_left_right_of_diagonal(i, k);
for (std::size_t i = k+1; i < iN; i++) if (pi.first <= pk.first && pi.second >= pk.second)
{ {
auto pi = get_left_right_of_diagonal(i,k); std::swap(m_terms[i], m_terms[k]);
if (pi.first <= pk.first && pi.second >= pk.second) pk = get_left_right_of_diagonal(k, k);
{
std::swap(m_terms[i], m_terms[k]);
pk = get_left_right_of_diagonal(k,k);
}
} }
} }
} }
break; }
break;
case matrix_sort_type_e::ASCENDING: case matrix_sort_type_e::ASCENDING:
case matrix_sort_type_e::DESCENDING: case matrix_sort_type_e::DESCENDING:
{ {
int sort_order = (sort == matrix_sort_type_e::DESCENDING ? 1 : -1); int sort_order = (sort == matrix_sort_type_e::DESCENDING ? 1
: -1);
for (std::size_t k = 0; k < iN - 1; k++) for (std::size_t k = 0; k < iN - 1; k++)
for (std::size_t i = k+1; i < iN; i++) for (std::size_t i = k + 1; i < iN; i++)
{
if ((static_cast<int>(m_terms[k].rail_start())
- static_cast<int>(m_terms[i].rail_start()))
* sort_order
< 0)
{ {
if ((static_cast<int>(m_terms[k].rail_start()) - static_cast<int>(m_terms[i].rail_start())) * sort_order < 0) std::swap(m_terms[i], m_terms[k]);
{
std::swap(m_terms[i], m_terms[k]);
}
} }
} }
break; }
case matrix_sort_type_e::NOSORT: break;
break; case matrix_sort_type_e::NOSORT: break;
} }
// rebuild // rebuild
for (auto &term : m_terms) for (auto &term : m_terms)
{ {
//int *other = term.m_connected_net_idx.data(); // int *other = term.m_connected_net_idx.data();
for (std::size_t i = 0; i < term.count(); i++) for (std::size_t i = 0; i < term.count(); i++)
//FIXME: this is weird // FIXME: this is weird
if (term.m_connected_net_idx[i] != -1) if (term.m_connected_net_idx[i] != -1)
term.m_connected_net_idx[i] = get_net_idx(get_connected_net(term.terms()[i])); term.m_connected_net_idx[i] = get_net_idx(
get_connected_net(term.terms()[i]));
} }
} }
@ -272,7 +304,9 @@ namespace netlist::solver
{ {
m_terms[k].set_rail_start(m_terms[k].count()); m_terms[k].set_rail_start(m_terms[k].count());
for (std::size_t i = 0; i < m_rails_temp[k].count(); i++) for (std::size_t i = 0; i < m_rails_temp[k].count(); i++)
this->m_terms[k].add_terminal(m_rails_temp[k].terms()[i], m_rails_temp[k].m_connected_net_idx.data()[i], false); this->m_terms[k].add_terminal(
m_rails_temp[k].terms()[i],
m_rails_temp[k].m_connected_net_idx.data()[i], false);
} }
// free all - no longer needed // free all - no longer needed
@ -285,17 +319,18 @@ namespace netlist::solver
// create a list of non zero elements. // create a list of non zero elements.
for (unsigned k = 0; k < iN; k++) for (unsigned k = 0; k < iN; k++)
{ {
terms_for_net_t & t = m_terms[k]; terms_for_net_t &t = m_terms[k];
// pretty brutal // pretty brutal
int *other = t.m_connected_net_idx.data(); int *other = t.m_connected_net_idx.data();
t.m_nz.clear(); t.m_nz.clear();
for (std::size_t i = 0; i < t.rail_start(); i++) for (std::size_t i = 0; i < t.rail_start(); i++)
if (!plib::container::contains(t.m_nz, static_cast<unsigned>(other[i]))) if (!plib::container::contains(t.m_nz,
static_cast<unsigned>(other[i])))
t.m_nz.push_back(static_cast<unsigned>(other[i])); t.m_nz.push_back(static_cast<unsigned>(other[i]));
t.m_nz.push_back(k); // add diagonal t.m_nz.push_back(k); // add diagonal
// and sort // and sort
std::sort(t.m_nz.begin(), t.m_nz.end()); std::sort(t.m_nz.begin(), t.m_nz.end());
@ -307,16 +342,16 @@ namespace netlist::solver
for (std::size_t k = 0; k < iN; k++) for (std::size_t k = 0; k < iN; k++)
{ {
terms_for_net_t & t = m_terms[k]; terms_for_net_t &t = m_terms[k];
// pretty brutal // pretty brutal
int *other = t.m_connected_net_idx.data(); int *other = t.m_connected_net_idx.data();
if (k==0) if (k == 0)
t.m_nzrd.clear(); t.m_nzrd.clear();
else else
{ {
t.m_nzrd = m_terms[k-1].m_nzrd; t.m_nzrd = m_terms[k - 1].m_nzrd;
for (auto j = t.m_nzrd.begin(); j != t.m_nzrd.end(); ) for (auto j = t.m_nzrd.begin(); j != t.m_nzrd.end();)
{ {
if (*j < k + 1) if (*j < k + 1)
j = t.m_nzrd.erase(j); j = t.m_nzrd.erase(j);
@ -326,7 +361,9 @@ namespace netlist::solver
} }
for (std::size_t i = 0; i < t.rail_start(); i++) for (std::size_t i = 0; i < t.rail_start(); i++)
if (!plib::container::contains(t.m_nzrd, static_cast<unsigned>(other[i])) && other[i] >= static_cast<int>(k + 1)) if (!plib::container::contains(t.m_nzrd,
static_cast<unsigned>(other[i]))
&& other[i] >= static_cast<int>(k + 1))
t.m_nzrd.push_back(static_cast<unsigned>(other[i])); t.m_nzrd.push_back(static_cast<unsigned>(other[i]));
// and sort // and sort
@ -366,7 +403,8 @@ namespace netlist::solver
} }
} }
} }
log().verbose("Number of multiplications/additions for {1}: {2}", name(), m_ops); log().verbose("Number of multiplications/additions for {1}: {2}",
name(), m_ops);
// Dumps non zero elements right of diagonal -> to much output, disabled // Dumps non zero elements right of diagonal -> to much output, disabled
// NOLINTNEXTLINE(readability-simplify-boolean-expr) // NOLINTNEXTLINE(readability-simplify-boolean-expr)
@ -374,7 +412,7 @@ namespace netlist::solver
for (std::size_t k = 0; k < iN; k++) for (std::size_t k = 0; k < iN; k++)
{ {
pstring line = plib::pfmt("{1:3}")(k); pstring line = plib::pfmt("{1:3}")(k);
for (const auto & nzrd : m_terms[k].m_nzrd) for (const auto &nzrd : m_terms[k].m_nzrd)
line += plib::pfmt(" {1:3}")(nzrd); line += plib::pfmt(" {1:3}")(nzrd);
log().verbose("{1}", line); log().verbose("{1}", line);
} }
@ -387,9 +425,12 @@ namespace netlist::solver
{ {
pstring num = plib::pfmt("{1}")(k); pstring num = plib::pfmt("{1}")(k);
state().save(*this, m_gonn[k],"GO" + num, this->name(), m_terms[k].count()); state().save(*this, m_gonn[k], "GO" + num, this->name(),
state().save(*this, m_gtn[k],"GT" + num, this->name(), m_terms[k].count()); m_terms[k].count());
state().save(*this, m_Idrn[k],"IDR" + num, this->name(), m_terms[k].count()); state().save(*this, m_gtn[k], "GT" + num, this->name(),
m_terms[k].count());
state().save(*this, m_Idrn[k], "IDR" + num, this->name(),
m_terms[k].count());
} }
} }
@ -414,20 +455,22 @@ namespace netlist::solver
for (std::size_t k = 0; k < iN; k++) for (std::size_t k = 0; k < iN; k++)
for (std::size_t j = 0; j < m_terms[k].count(); j++) for (std::size_t j = 0; j < m_terms[k].count(); j++)
{ {
m_gtn.set(k,j, nlconst::zero()); m_gtn.set(k, j, nlconst::zero());
m_gonn.set(k,j, nlconst::zero()); m_gonn.set(k, j, nlconst::zero());
m_Idrn.set(k,j, nlconst::zero()); m_Idrn.set(k, j, nlconst::zero());
m_connected_net_Vn.set(k, j, nullptr); m_connected_net_Vn.set(k, j, nullptr);
} }
for (std::size_t k = 0; k < iN; k++) for (std::size_t k = 0; k < iN; k++)
{ {
auto count = m_terms[k].count(); auto count = m_terms[k].count();
for (std::size_t i = 0; i < count; i++) for (std::size_t i = 0; i < count; i++)
{ {
m_terms[k].terms()[i]->set_ptrs(&m_gtn[k][i], &m_gonn[k][i], &m_Idrn[k][i]); m_terms[k].terms()[i]->set_ptrs(&m_gtn[k][i], &m_gonn[k][i],
m_connected_net_Vn[k][i] = get_connected_net(m_terms[k].terms()[i])->Q_Analog_state_ptr(); &m_Idrn[k][i]);
m_connected_net_Vn[k][i] = get_connected_net(
m_terms[k].terms()[i])
->Q_Analog_state_ptr();
} }
} }
} }
@ -448,10 +491,11 @@ namespace netlist::solver
void matrix_solver_t::reset() void matrix_solver_t::reset()
{ {
//m_last_step = netlist_time_ext::zero(); // m_last_step = netlist_time_ext::zero();
} }
void matrix_solver_t::step(time_step_type ts_type, netlist_time delta) noexcept void matrix_solver_t::step(detail::time_step_type ts_type,
netlist_time delta) noexcept
{ {
const auto dd(delta.as_fp<fptype>()); const auto dd(delta.as_fp<fptype>());
for (auto &d : m_step_funcs) for (auto &d : m_step_funcs)
@ -460,12 +504,13 @@ namespace netlist::solver
bool matrix_solver_t::solve_nr_base() bool matrix_solver_t::solve_nr_base()
{ {
bool this_resched(false); bool this_resched(false);
std::size_t newton_loops = 0; std::size_t newton_loops = 0;
do do
{ {
update_dynamic(); update_dynamic();
// Gauss-Seidel will revert to Gaussian elimination if steps exceeded. // Gauss-Seidel will revert to Gaussian elimination if steps
// exceeded.
this->m_stat_calculations++; this->m_stat_calculations++;
this->upstream_solve_non_dynamic(); this->upstream_solve_non_dynamic();
this_resched = this->check_err(); this_resched = this->check_err();
@ -482,18 +527,21 @@ namespace netlist::solver
netlist_time matrix_solver_t::newton_loops_exceeded(netlist_time delta) netlist_time matrix_solver_t::newton_loops_exceeded(netlist_time delta)
{ {
netlist_time next_time_step; netlist_time next_time_step;
bool resched(false); bool resched(false);
restore(); restore();
step(time_step_type::RESTORE, delta); step(detail::time_step_type::RESTORE, delta);
for (std::size_t i=0; i< 10; i++) for (std::size_t i = 0; i < 10; i++)
{ {
backup(); backup();
step(time_step_type::FORWARD, netlist_time::from_fp(m_params.m_min_ts_ts())); step(detail::time_step_type::FORWARD,
netlist_time::from_fp(m_params.m_min_ts_ts()));
resched = solve_nr_base(); resched = solve_nr_base();
// update time step calculation // update time step calculation
next_time_step = compute_next_time_step(m_params.m_min_ts_ts(), m_params.m_min_ts_ts(), m_params.m_max_time_step); next_time_step = compute_next_time_step(m_params.m_min_ts_ts(),
m_params.m_min_ts_ts(),
m_params.m_max_time_step);
delta -= netlist_time::from_fp(m_params.m_min_ts_ts()); delta -= netlist_time::from_fp(m_params.m_min_ts_ts());
} }
// try remaining time using compute_next_time step // try remaining time using compute_next_time step
@ -502,19 +550,23 @@ namespace netlist::solver
if (next_time_step > delta) if (next_time_step > delta)
next_time_step = delta; next_time_step = delta;
backup(); backup();
step(time_step_type::FORWARD, next_time_step); step(detail::time_step_type::FORWARD, next_time_step);
delta -= next_time_step; delta -= next_time_step;
resched = solve_nr_base(); resched = solve_nr_base();
next_time_step = compute_next_time_step(next_time_step.as_fp<nl_fptype>(), m_params.m_min_ts_ts(), m_params.m_max_time_step); next_time_step = compute_next_time_step(
next_time_step.as_fp<nl_fptype>(), m_params.m_min_ts_ts(),
m_params.m_max_time_step);
} }
if (m_stat_newton_raphson % 100 == 0) if (m_stat_newton_raphson % 100 == 0)
log().warning(MW_NEWTON_LOOPS_EXCEEDED_INVOCATION_3(100, this->name(), exec().time().as_double() * 1e6)); log().warning(MW_NEWTON_LOOPS_EXCEEDED_INVOCATION_3(
100, this->name(), exec().time().as_double() * 1e6));
if (resched) if (resched)
{ {
// reschedule .... // reschedule ....
log().warning(MW_NEWTON_LOOPS_EXCEEDED_ON_NET_2(this->name(), exec().time().as_double() * 1e6)); log().warning(MW_NEWTON_LOOPS_EXCEEDED_ON_NET_2(
this->name(), exec().time().as_double() * 1e6));
return netlist_time::from_fp(m_params.m_nr_recalc_delay()); return netlist_time::from_fp(m_params.m_nr_recalc_delay());
} }
if (m_params.m_dynamic_ts) if (m_params.m_dynamic_ts)
@ -523,7 +575,8 @@ namespace netlist::solver
return netlist_time::from_fp(m_params.m_max_time_step); return netlist_time::from_fp(m_params.m_max_time_step);
} }
netlist_time matrix_solver_t::solve(netlist_time_ext now, [[maybe_unused]] const char *source) netlist_time matrix_solver_t::solve(netlist_time_ext now,
[[maybe_unused]] const char *source)
{ {
auto delta = static_cast<netlist_time>(now - m_last_step()); auto delta = static_cast<netlist_time>(now - m_last_step());
PFDEBUG(printf("solve %.10f\n", delta.as_double());) PFDEBUG(printf("solve %.10f\n", delta.as_double());)
@ -532,8 +585,10 @@ namespace netlist::solver
// FIXME: Make this a parameter! // FIXME: Make this a parameter!
if (delta < netlist_time::quantum()) if (delta < netlist_time::quantum())
{ {
//printf("solve return %s at %f\n", source, now.as_double()); // printf("solve return %s at %f\n", source, now.as_double());
return time_step_device_count() > 0 ? netlist_time::from_fp(m_params.m_min_time_step) : netlist_time::zero(); return time_step_device_count() > 0
? netlist_time::from_fp(m_params.m_min_time_step)
: netlist_time::zero();
} }
backup(); // save voltages for backup and time step calculation backup(); // save voltages for backup and time step calculation
@ -543,7 +598,7 @@ namespace netlist::solver
++m_stat_vsolver_calls; ++m_stat_vsolver_calls;
if (dynamic_device_count() != 0) if (dynamic_device_count() != 0)
{ {
step(time_step_type::FORWARD, delta); step(detail::time_step_type::FORWARD, delta);
const auto resched = solve_nr_base(); const auto resched = solve_nr_base();
if (resched) if (resched)
@ -551,7 +606,7 @@ namespace netlist::solver
} }
else else
{ {
step(time_step_type::FORWARD, delta); step(detail::time_step_type::FORWARD, delta);
this->m_stat_calculations++; this->m_stat_calculations++;
this->upstream_solve_non_dynamic(); this->upstream_solve_non_dynamic();
this->store(); this->store();
@ -560,14 +615,15 @@ namespace netlist::solver
if (m_params.m_dynamic_ts) if (m_params.m_dynamic_ts)
{ {
if (time_step_device_count() > 0) if (time_step_device_count() > 0)
return compute_next_time_step(delta.as_fp<nl_fptype>(), m_params.m_min_time_step, m_params.m_max_time_step); return compute_next_time_step(delta.as_fp<nl_fptype>(),
m_params.m_min_time_step,
m_params.m_max_time_step);
} }
if (time_step_device_count() > 0) if (time_step_device_count() > 0)
return netlist_time::from_fp(m_params.m_max_time_step); return netlist_time::from_fp(m_params.m_max_time_step);
return netlist_time::zero(); return netlist_time::zero();
} }
int matrix_solver_t::get_net_idx(const analog_net_t *net) const noexcept int matrix_solver_t::get_net_idx(const analog_net_t *net) const noexcept
@ -578,11 +634,14 @@ namespace netlist::solver
return -1; return -1;
} }
std::pair<int, int> matrix_solver_t::get_left_right_of_diagonal(std::size_t irow, std::size_t idiag) std::pair<int, int>
matrix_solver_t::get_left_right_of_diagonal(std::size_t irow,
std::size_t idiag)
{ {
// //
// return the maximum column left of the diagonal (-1 if no cols found) // return the maximum column left of the diagonal (-1 if no cols found)
// return the minimum column right of the diagonal (999999 if no cols found) // return the minimum column right of the diagonal (999999 if no cols
// found)
// //
const auto row = static_cast<int>(irow); const auto row = static_cast<int>(irow);
@ -598,8 +657,10 @@ namespace netlist::solver
auto col = get_net_idx(get_connected_net(term.terms()[i])); auto col = get_net_idx(get_connected_net(term.terms()[i]));
if (col != -1) if (col != -1)
{ {
if (col==row) col = diag; if (col == row)
else if (col==diag) col = row; col = diag;
else if (col == diag)
col = row;
if (col > diag && col < colmin) if (col > diag && col < colmin)
colmin = col; colmin = col;
@ -610,7 +671,9 @@ namespace netlist::solver
return {colmax, colmin}; return {colmax, colmin};
} }
matrix_solver_t::fptype matrix_solver_t::get_weight_around_diagonal(std::size_t row, std::size_t diag) matrix_solver_t::fptype
matrix_solver_t::get_weight_around_diagonal(std::size_t row,
std::size_t diag)
{ {
{ {
// //
@ -620,7 +683,7 @@ namespace netlist::solver
std::vector<bool> touched(1024, false); // FIXME! std::vector<bool> touched(1024, false); // FIXME!
fptype weight = nlconst::zero(); fptype weight = nlconst::zero();
auto &term = m_terms[row]; auto &term = m_terms[row];
for (std::size_t i = 0; i < term.count(); i++) for (std::size_t i = 0; i < term.count(); i++)
{ {
auto col = get_net_idx(get_connected_net(term.terms()[i])); auto col = get_net_idx(get_connected_net(term.terms()[i]));
@ -629,10 +692,14 @@ namespace netlist::solver
auto colu = static_cast<std::size_t>(col); auto colu = static_cast<std::size_t>(col);
if (!touched[colu]) if (!touched[colu])
{ {
if (colu==row) colu = static_cast<unsigned>(diag); if (colu == row)
else if (colu==diag) colu = static_cast<unsigned>(row); colu = static_cast<unsigned>(diag);
else if (colu == diag)
colu = static_cast<unsigned>(row);
weight = weight + plib::abs(static_cast<fptype>(colu) - static_cast<fptype>(diag)); weight = weight
+ plib::abs(static_cast<fptype>(colu)
- static_cast<fptype>(diag));
touched[colu] = true; touched[colu] = true;
} }
} }
@ -650,38 +717,46 @@ namespace netlist::solver
else else
{ {
int ot = get_net_idx(get_connected_net(term)); int ot = get_net_idx(get_connected_net(term));
if (ot>=0) if (ot >= 0)
{ {
m_terms[net_idx].add_terminal(term, ot, true); m_terms[net_idx].add_terminal(term, ot, true);
} }
else else
{ {
log().fatal(MF_FOUND_TERM_WITH_MISSING_OTHERNET(term->name())); log().fatal(MF_FOUND_TERM_WITH_MISSING_OTHERNET(term->name()));
throw nl_exception(MF_FOUND_TERM_WITH_MISSING_OTHERNET(term->name())); throw nl_exception(
MF_FOUND_TERM_WITH_MISSING_OTHERNET(term->name()));
} }
} }
} }
void matrix_solver_t::log_stats() void matrix_solver_t::log_stats()
{ {
if (this->m_stat_calculations != 0 && this->m_stat_vsolver_calls && log().verbose.is_enabled()) if (this->m_stat_calculations != 0 && this->m_stat_vsolver_calls
&& log().verbose.is_enabled())
{ {
log().verbose("=============================================="); log().verbose("==============================================");
log().verbose("Solver {1}", this->name()); log().verbose("Solver {1}", this->name());
log().verbose(" ==> {1} nets", this->m_terms.size()); log().verbose(" ==> {1} nets", this->m_terms.size());
log().verbose(" has {1} dynamic elements", this->dynamic_device_count()); log().verbose(" has {1} dynamic elements",
log().verbose(" has {1} time step elements", this->time_step_device_count()); this->dynamic_device_count());
log().verbose(" {1:6.3} average newton raphson loops", log().verbose(" has {1} time step elements",
static_cast<fptype>(this->m_stat_newton_raphson) / static_cast<fptype>(this->m_stat_vsolver_calls)); this->time_step_device_count());
log().verbose(" {1:10} invocations ({2:6.0} Hz) {3:10} gs fails ({4:6.2} %) {5:6.3} average", log().verbose(
this->m_stat_calculations, " {1:6.3} average newton raphson loops",
static_cast<fptype>(this->m_stat_calculations) / this->exec().time().as_fp<fptype>(), static_cast<fptype>(this->m_stat_newton_raphson)
this->m_iterative_fail, / static_cast<fptype>(this->m_stat_vsolver_calls));
nlconst::hundred() * static_cast<fptype>(this->m_iterative_fail) log().verbose(
/ static_cast<fptype>(this->m_stat_calculations), " {1:10} invocations ({2:6.0} Hz) {3:10} gs fails ({4:6.2} %) {5:6.3} average",
static_cast<fptype>(this->m_iterative_total) / static_cast<fptype>(this->m_stat_calculations)); this->m_stat_calculations,
static_cast<fptype>(this->m_stat_calculations)
/ this->exec().time().as_fp<fptype>(),
this->m_iterative_fail,
nlconst::hundred() * static_cast<fptype>(this->m_iterative_fail)
/ static_cast<fptype>(this->m_stat_calculations),
static_cast<fptype>(this->m_iterative_total)
/ static_cast<fptype>(this->m_stat_calculations));
} }
} }
} // namespace netlist::solver } // namespace netlist::solver

View File

@ -11,14 +11,14 @@
/// \file nld_matrix_solver.h /// \file nld_matrix_solver.h
/// ///
#include "nl_errstr.h"
#include "nltypes.h"
#include "../core/analog.h" #include "../core/analog.h"
#include "../core/device.h" #include "../core/device.h"
#include "../core/device_macros.h" #include "../core/device_macros.h"
#include "../core/param.h" #include "../core/param.h"
#include "nl_errstr.h"
#include "nltypes.h"
#include "plib/palloc.h" #include "plib/palloc.h"
#include "plib/penum.h" #include "plib/penum.h"
#include "plib/pmatrix2d.h" #include "plib/pmatrix2d.h"
@ -29,7 +29,7 @@
#include <numeric> #include <numeric>
//FIXME: remove again // FIXME: remove again
#define PFDEBUG(x) #define PFDEBUG(x)
@ -42,6 +42,8 @@ namespace netlist::solver
CXX_STATIC CXX_STATIC
}; };
// clang-format off
PENUM(matrix_sort_type_e, PENUM(matrix_sort_type_e,
NOSORT, NOSORT,
ASCENDING, ASCENDING,
@ -67,40 +69,63 @@ namespace netlist::solver
, FLOATQ128 , FLOATQ128
) )
// clang-format on
using arena_type = plib::mempool_arena<plib::aligned_arena<>, 1024>; using arena_type = plib::mempool_arena<plib::aligned_arena<>, 1024>;
using static_compile_container = std::vector<std::pair<pstring, pstring>>; using static_compile_container = std::vector<std::pair<pstring, pstring>>;
struct solver_parameter_defaults struct solver_parameter_defaults
{ {
static constexpr nl_fptype m_freq() { return nlconst::magic(48000.0); } static constexpr nl_fptype m_freq() { return nlconst::magic(48000.0); }
// iteration parameters // iteration parameters
static constexpr nl_fptype m_gs_sor() { return nlconst::magic(1.059); } 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_type_e m_method()
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); } return matrix_type_e::MAT_CR;
static constexpr nl_fptype m_vntol() { return nlconst::magic(1e-7); } }
static constexpr nl_fptype m_accuracy() { return nlconst::magic(1e-7); } static constexpr matrix_fp_type_e m_fp_type()
static constexpr std::size_t m_nr_loops() { return 250; } {
static constexpr std::size_t m_gs_loops() { return 50; } 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 // general parameters
static constexpr nl_fptype m_gmin() { return nlconst::magic(1e-9); } static constexpr nl_fptype m_gmin() { return nlconst::magic(1e-9); }
static constexpr bool m_pivot() { return false; } 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 nl_fptype m_nr_recalc_delay()
static constexpr int m_parallel() { return 0; } {
return netlist_time::quantum().as_fp<nl_fptype>();
}
static constexpr int m_parallel() { return 0; }
static 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 // automatic time step
static constexpr bool m_dynamic_ts() { return false; } 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_lte()
static constexpr nl_fptype m_dynamic_min_ts() { return nlconst::magic(1e-6); } {
return nlconst::magic(1e-5);
}
static constexpr nl_fptype m_dynamic_min_ts()
{
return nlconst::magic(1e-6);
}
// matrix sorting // matrix sorting
static 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 // special
static constexpr bool m_use_gabs() { return true; } static constexpr bool m_use_gabs() { return true; }
static solver_parameter_defaults &get_instance() static solver_parameter_defaults &get_instance()
{ {
@ -112,30 +137,52 @@ namespace netlist::solver
struct solver_parameters_t struct solver_parameters_t
{ {
template <typename D> template <typename D>
solver_parameters_t(device_t &parent, const pstring &prefix, D &defaults) solver_parameters_t(device_t &parent, const pstring &prefix,
D &defaults)
: m_freq(parent, prefix + "FREQ", defaults.m_freq()) : m_freq(parent, prefix + "FREQ", defaults.m_freq())
// iteration parameters // iteration parameters
, m_gs_sor(parent, prefix + "SOR_FACTOR", defaults.m_gs_sor()) , m_gs_sor(parent, prefix + "SOR_FACTOR", defaults.m_gs_sor())
, m_method(parent, prefix + "METHOD", defaults.m_method()) , m_method(parent, prefix + "METHOD", defaults.m_method())
, m_fp_type(parent, prefix + "FPTYPE", defaults.m_fp_type()) , m_fp_type(parent, prefix + "FPTYPE", defaults.m_fp_type())
, m_reltol(parent, prefix + "RELTOL", defaults.m_reltol()) //!< SPICE RELTOL parameter , m_reltol(parent, prefix + "RELTOL",
, m_vntol(parent, prefix + "VNTOL", defaults.m_vntol()) //!< SPICE VNTOL parameter defaults.m_reltol()) //!< SPICE RELTOL parameter
, m_accuracy(parent, prefix + "ACCURACY", defaults.m_accuracy()) //!< Iterative solver accuracy , m_vntol(parent, prefix + "VNTOL", defaults.m_vntol()) //!< SPICE VNTOL
, m_nr_loops(parent, prefix + "NR_LOOPS", defaults.m_nr_loops()) //!< Maximum number of Newton-Raphson loops //!< parameter
, m_gs_loops(parent, prefix + "GS_LOOPS", defaults.m_gs_loops()) //!< Maximum number of Gauss-Seidel loops , m_accuracy(parent, prefix + "ACCURACY",
defaults.m_accuracy()) //!< Iterative solver accuracy
, m_nr_loops(parent, prefix + "NR_LOOPS",
defaults.m_nr_loops()) //!< Maximum number of
//!< Newton-Raphson loops
, m_gs_loops(parent, prefix + "GS_LOOPS",
defaults.m_gs_loops()) //!< Maximum number of Gauss-Seidel
//!< loops
// general parameters // general parameters
, m_gmin(parent, prefix + "GMIN", defaults.m_gmin()) , m_gmin(parent, prefix + "GMIN", defaults.m_gmin())
, m_pivot(parent, prefix + "PIVOT", defaults.m_pivot()) //!< use pivoting on supported solvers , m_pivot(parent, prefix + "PIVOT", defaults.m_pivot()) //!< use
, m_nr_recalc_delay(parent, prefix + "NR_RECALC_DELAY", defaults.m_nr_recalc_delay()) //!< Delay to next solve attempt if nr loops exceeded //!< pivoting on
//!< supported
//!< solvers
, m_nr_recalc_delay(parent, prefix + "NR_RECALC_DELAY",
defaults.m_nr_recalc_delay()) //!< Delay to next
//!< solve attempt if
//!< nr loops exceeded
, m_parallel(parent, prefix + "PARALLEL", defaults.m_parallel()) , m_parallel(parent, prefix + "PARALLEL", defaults.m_parallel())
, m_min_ts_ts(parent, prefix + "MIN_TS_TS", defaults.m_min_ts_ts()) //!< The minimum time step for solvers with time stepping devices. , m_min_ts_ts(parent, prefix + "MIN_TS_TS",
defaults.m_min_ts_ts()) //!< The minimum time step for
//!< solvers with time stepping
//!< devices.
// automatic time step // automatic time step
, m_dynamic_ts(parent, prefix + "DYNAMIC_TS", defaults.m_dynamic_ts()) //!< Use dynamic time stepping , m_dynamic_ts(parent, prefix + "DYNAMIC_TS",
, m_dynamic_lte(parent, prefix + "DYNAMIC_LTE", defaults.m_dynamic_lte()) //!< dynamic time stepping slope defaults.m_dynamic_ts()) //!< Use dynamic time stepping
, m_dynamic_min_ts(parent, prefix + "DYNAMIC_MIN_TIMESTEP", defaults.m_dynamic_min_ts()) //!< smallest time step allowed , m_dynamic_lte(parent, prefix + "DYNAMIC_LTE",
defaults.m_dynamic_lte()) //!< dynamic time stepping
//!< slope
, m_dynamic_min_ts(parent, prefix + "DYNAMIC_MIN_TIMESTEP",
defaults.m_dynamic_min_ts()) //!< smallest time step
//!< allowed
// matrix sorting // matrix sorting
, m_sort_type(parent, prefix + "SORT_TYPE", defaults.m_sort_type()) , m_sort_type(parent, prefix + "SORT_TYPE", defaults.m_sort_type())
@ -144,11 +191,12 @@ namespace netlist::solver
, m_use_gabs(parent, prefix + "USE_GABS", defaults.m_use_gabs()) , m_use_gabs(parent, prefix + "USE_GABS", defaults.m_use_gabs())
, m_min_time_step(m_dynamic_min_ts()) , m_min_time_step(m_dynamic_min_ts())
{ {
m_max_time_step = netlist_time::from_fp(plib::reciprocal(m_freq())).as_fp<decltype(m_max_time_step)>(); m_max_time_step = netlist_time::from_fp(plib::reciprocal(m_freq()))
.as_fp<decltype(m_max_time_step)>();
if (m_dynamic_ts) if (m_dynamic_ts)
{ {
m_max_time_step *= 1;//NL_FCONST(1000.0); m_max_time_step *= 1; // NL_FCONST(1000.0);
} }
else else
{ {
@ -156,23 +204,23 @@ namespace netlist::solver
} }
} }
param_fp_t m_freq; param_fp_t m_freq;
param_fp_t m_gs_sor; param_fp_t m_gs_sor;
param_enum_t<matrix_type_e> m_method; param_enum_t<matrix_type_e> m_method;
param_enum_t<matrix_fp_type_e> m_fp_type; param_enum_t<matrix_fp_type_e> m_fp_type;
param_fp_t m_reltol; param_fp_t m_reltol;
param_fp_t m_vntol; param_fp_t m_vntol;
param_fp_t m_accuracy; param_fp_t m_accuracy;
param_num_t<std::size_t> m_nr_loops; param_num_t<std::size_t> m_nr_loops;
param_num_t<std::size_t> m_gs_loops; param_num_t<std::size_t> m_gs_loops;
param_fp_t m_gmin; param_fp_t m_gmin;
param_logic_t m_pivot; param_logic_t m_pivot;
param_fp_t m_nr_recalc_delay; param_fp_t m_nr_recalc_delay;
param_int_t m_parallel; param_int_t m_parallel;
param_fp_t m_min_ts_ts; param_fp_t m_min_ts_ts;
param_logic_t m_dynamic_ts; param_logic_t m_dynamic_ts;
param_fp_t m_dynamic_lte; param_fp_t m_dynamic_lte;
param_fp_t m_dynamic_min_ts; param_fp_t m_dynamic_min_ts;
param_enum_t<matrix_sort_type_e> m_sort_type; param_enum_t<matrix_sort_type_e> m_sort_type;
param_logic_t m_use_gabs; param_logic_t m_use_gabs;
@ -181,11 +229,10 @@ namespace netlist::solver
nl_fptype m_max_time_step; nl_fptype m_max_time_step;
}; };
class terms_for_net_t class terms_for_net_t
{ {
public: public:
terms_for_net_t(arena_type &arena, analog_net_t * net = nullptr); terms_for_net_t(arena_type &arena, analog_net_t *net = nullptr);
void clear(); void clear();
@ -201,35 +248,49 @@ namespace netlist::solver
void setV(nl_fptype v) noexcept { m_net->set_Q_Analog(v); } void setV(nl_fptype v) noexcept { m_net->set_Q_Analog(v); }
bool is_net(const analog_net_t * net) const noexcept { return net == m_net; } bool is_net(const analog_net_t *net) const noexcept
{
return net == m_net;
}
void set_rail_start(std::size_t val) noexcept { m_rail_start = val; } void set_rail_start(std::size_t val) noexcept { m_rail_start = val; }
PALIGNAS_VECTOROPT() PALIGNAS_VECTOROPT()
plib::arena_vector<arena_type, unsigned> m_nz; //!< all non zero for multiplication plib::arena_vector<arena_type, unsigned> m_nz; //!< all non zero for
plib::arena_vector<arena_type, unsigned> m_nzrd; //!< non zero right of the diagonal for elimination, may include RHS element //!< multiplication
plib::arena_vector<arena_type, unsigned> m_nzbd; //!< non zero below of the diagonal for elimination plib::arena_vector<arena_type, unsigned> m_nzrd; //!< non zero right of
//!< the diagonal for
//!< elimination, may
//!< include RHS
//!< element
plib::arena_vector<arena_type, unsigned> m_nzbd; //!< non zero below of
//!< the diagonal for
//!< elimination
plib::arena_vector<arena_type, int> m_connected_net_idx; plib::arena_vector<arena_type, int> m_connected_net_idx;
private: private:
plib::arena_vector<arena_type, terminal_t *> m_terms; plib::arena_vector<arena_type, terminal_t *> m_terms;
analog_net_t * m_net; analog_net_t *m_net;
std::size_t m_rail_start; std::size_t m_rail_start;
}; };
class proxied_analog_output_t : public analog_output_t class proxied_analog_output_t : public analog_output_t
{ {
public: public:
proxied_analog_output_t(core_device_t &dev, const pstring &aname,
proxied_analog_output_t(core_device_t &dev, const pstring &aname, analog_net_t *pnet) analog_net_t *pnet)
: analog_output_t(dev, aname) : analog_output_t(dev, aname)
, m_proxied_net(pnet) , m_proxied_net(pnet)
{ } {
}
analog_net_t *proxied_net() const { return m_proxied_net; }
analog_net_t *proxied_net() const { return m_proxied_net;}
private: private:
analog_net_t *m_proxied_net; // only for proxy nets in analog input logic analog_net_t *m_proxied_net; // only for proxy nets in analog input
// logic
}; };
class matrix_solver_t : public device_t class matrix_solver_t : public device_t
@ -237,16 +298,22 @@ namespace netlist::solver
public: public:
using list_t = std::vector<matrix_solver_t *>; using list_t = std::vector<matrix_solver_t *>;
using fptype = nl_fptype; using fptype = nl_fptype;
using net_list_t = std::vector<analog_net_t *>; using net_list_t = std::vector<analog_net_t *>;
// after every call to solve, update inputs must be called. // after every call to solve, update inputs must be called.
// this can be done as well as a batch to ease parallel processing. // this can be done as well as a batch to ease parallel processing.
netlist_time solve(netlist_time_ext now, const char *source); netlist_time solve(netlist_time_ext now, const char *source);
void update_inputs(); void update_inputs();
std::size_t dynamic_device_count() const noexcept { return m_dynamic_funcs.size(); } std::size_t dynamic_device_count() const noexcept
std::size_t time_step_device_count() const noexcept { return m_step_funcs.size(); } {
return m_dynamic_funcs.size();
}
std::size_t time_step_device_count() const noexcept
{
return m_step_funcs.size();
}
/// \brief reschedule solver execution /// \brief reschedule solver execution
/// ///
@ -264,23 +331,28 @@ namespace netlist::solver
// this should only occur outside of execution and thus // this should only occur outside of execution and thus
// using time should be safe. // using time should be safe.
[[maybe_unused]] const netlist_time new_time_step = solve(exec().time(), "solve_now"); [[maybe_unused]] const netlist_time new_time_step = solve(
exec().time(), "solve_now");
update_inputs(); update_inputs();
if (time_step_device_count() > 0) if (time_step_device_count() > 0)
{ {
this->reschedule(netlist_time::from_fp(m_params.m_dynamic_ts ? m_params.m_min_time_step : m_params.m_max_time_step)); this->reschedule(netlist_time::from_fp(
m_params.m_dynamic_ts ? m_params.m_min_time_step
: m_params.m_max_time_step));
} }
} }
template <typename F> template <typename F>
void change_state(F f) void change_state(F f)
{ {
// We only need to update the net first if this is a time stepping net // We only need to update the net first if this is a time stepping
// net
if (time_step_device_count() > 0) if (time_step_device_count() > 0)
{ {
[[maybe_unused]] const netlist_time new_time_step = solve(exec().time(), "change_state"); [[maybe_unused]] const netlist_time new_time_step = solve(
exec().time(), "change_state");
update_inputs(); update_inputs();
} }
f(); f();
@ -297,9 +369,11 @@ namespace netlist::solver
virtual void log_stats(); virtual void log_stats();
virtual std::pair<pstring, pstring> create_solver_code([[maybe_unused]] solver::static_compile_target target) virtual std::pair<pstring, pstring> create_solver_code(
[[maybe_unused]] solver::static_compile_target target)
{ {
return { "", plib::pfmt("// solver doesn't support static compile\n\n") }; return {"",
plib::pfmt("// solver doesn't support static compile\n\n")};
} }
// return number of floating point operations for solve // return number of floating point operations for solve
@ -307,11 +381,13 @@ namespace netlist::solver
protected: protected:
matrix_solver_t(devices::nld_solver &main_solver, const pstring &name, matrix_solver_t(devices::nld_solver &main_solver, const pstring &name,
const net_list_t &nets, const net_list_t &nets,
const solver_parameters_t *params); const solver_parameters_t *params);
virtual void upstream_solve_non_dynamic() = 0; virtual void upstream_solve_non_dynamic() = 0;
virtual netlist_time compute_next_time_step(fptype cur_ts, fptype min_ts, fptype max_ts) = 0; virtual netlist_time
compute_next_time_step(fptype cur_ts, fptype min_ts, fptype max_ts)
= 0;
virtual bool check_err() const = 0; virtual bool check_err() const = 0;
virtual void store() = 0; virtual void store() = 0;
virtual void backup() = 0; virtual void backup() = 0;
@ -326,7 +402,7 @@ namespace netlist::solver
} }
const solver_parameters_t &m_params; const solver_parameters_t &m_params;
arena_type m_arena; arena_type m_arena;
plib::pmatrix2d_vrl<arena_type, fptype> m_gonn; plib::pmatrix2d_vrl<arena_type, fptype> m_gonn;
plib::pmatrix2d_vrl<arena_type, fptype> m_gtn; plib::pmatrix2d_vrl<arena_type, fptype> m_gtn;
@ -339,20 +415,20 @@ namespace netlist::solver
std::vector<terms_for_net_t> m_terms; // setup only std::vector<terms_for_net_t> m_terms; // setup only
private: private:
// base setup - called from constructor // base setup - called from constructor
void setup_base(setup_t &setup, const net_list_t &nets) noexcept(false); void setup_base(setup_t &setup, const net_list_t &nets) noexcept(false);
bool solve_nr_base(); bool solve_nr_base();
netlist_time newton_loops_exceeded(netlist_time delta); netlist_time newton_loops_exceeded(netlist_time delta);
void sort_terms(matrix_sort_type_e sort); void sort_terms(matrix_sort_type_e sort);
void update_dynamic() noexcept; void update_dynamic() noexcept;
void step(time_step_type ts_type, netlist_time delta) noexcept; void step(detail::time_step_type ts_type, netlist_time delta) noexcept;
int get_net_idx(const analog_net_t *net) const noexcept; int get_net_idx(const analog_net_t *net) const noexcept;
std::pair<int, int> get_left_right_of_diagonal(std::size_t irow, std::size_t idiag); std::pair<int, int>
get_left_right_of_diagonal(std::size_t irow, std::size_t idiag);
fptype get_weight_around_diagonal(std::size_t row, std::size_t diag); fptype get_weight_around_diagonal(std::size_t row, std::size_t diag);
void add_term(std::size_t net_idx, terminal_t *term) noexcept(false); void add_term(std::size_t net_idx, terminal_t *term) noexcept(false);
@ -371,10 +447,12 @@ namespace netlist::solver
state_var<std::size_t> m_stat_newton_raphson_fail; state_var<std::size_t> m_stat_newton_raphson_fail;
state_var<std::size_t> m_stat_vsolver_calls; state_var<std::size_t> m_stat_vsolver_calls;
state_var<netlist_time_ext> m_last_step; state_var<netlist_time_ext> m_last_step;
plib::arena_vector<arena_type, nl_delegate_ts> m_step_funcs; plib::arena_vector<arena_type, nl_delegate_ts> m_step_funcs;
plib::arena_vector<arena_type, nl_delegate_dyn> m_dynamic_funcs; plib::arena_vector<arena_type, nl_delegate_dyn> m_dynamic_funcs;
plib::arena_vector<arena_type, device_arena::unique_ptr<proxied_analog_output_t>> m_inputs; plib::arena_vector<arena_type,
device_arena::unique_ptr<proxied_analog_output_t>>
m_inputs;
std::size_t m_ops; std::size_t m_ops;

View File

@ -2,12 +2,12 @@
// copyright-holders:Couriersud // copyright-holders:Couriersud
/// ///
/// \file test_pmfp.cpp /// \file test_precommit.cpp
/// ///
/// tests for `plib::pmfp` /// tests to check for experimental code before commit
/// ///
#include "netlist/nl_config.h" #include "nl_config.h"
#include "plib/pconfig.h" #include "plib/pconfig.h"
#include "plib/ppmf.h" #include "plib/ppmf.h"