trunk/src/emu/netlist/nl_base.h
| r26546 | r26547 | |
| 117 | 117 | * |
| 118 | 118 | * These equations represent a linear Matrix equation (with more math). |
| 119 | 119 | * |
| 120 | | * In the context of this exercise, in a first step, we will not solve it. Instead, |
| 121 | | * we calculate the net voltage V(l) by using a mirror of the above device on each terminal. |
| 120 | * In the end the solution of the analog subsystem boils down to |
| 122 | 121 | * |
| 122 | * (G - D) * V = I |
| 123 | * |
| 124 | * with G being the conductance matrix, D a diagonal matrix with the total conductance |
| 125 | * on the diagonal elements, V the net voltage vector and I the current vector. |
| 126 | * |
| 127 | * By using solely two terminal devices, we can simplify the whole calculation |
| 128 | * significantly. A BJT now is a four terminal device with two terminals being |
| 129 | * connected internally. |
| 130 | * |
| 131 | * The system is solved using an iterative approach: |
| 132 | * |
| 133 | * G * V - D * V = I |
| 134 | * |
| 135 | * assuming V=Vn=Vo |
| 136 | * |
| 137 | * Vn = D-1 * (I - G * Vo) |
| 138 | * |
| 123 | 139 | * Each terminal thus has three properties: |
| 124 | 140 | * |
| 125 | 141 | * a) Resistance |
| 126 | 142 | * b) Voltage source |
| 127 | 143 | * c) Current source/sink |
| 128 | 144 | * |
| 129 | | * Going forward, approach can be extended to use a linear equation solver. |
| 145 | * Going forward, the approach can be extended e.g. to use a linear equation solver |
| 130 | 146 | * |
| 131 | 147 | * The formal representation of the circuit will stay the same, thus scales. |
| 132 | 148 | * |
| r26546 | r26547 | |
| 225 | 241 | class netlist_param_t; |
| 226 | 242 | class netlist_setup_t; |
| 227 | 243 | class netlist_base_t; |
| 244 | class netlist_matrix_solver_t; |
| 228 | 245 | |
| 229 | 246 | |
| 230 | 247 | // ---------------------------------------------------------------------------------------- |
| r26546 | r26547 | |
| 438 | 455 | { |
| 439 | 456 | public: |
| 440 | 457 | |
| 458 | typedef netlist_list_t<netlist_net_t *> list_t; |
| 459 | |
| 441 | 460 | friend class NETLIB_NAME(mainclock); |
| 442 | 461 | friend class NETLIB_NAME(solver); |
| 462 | friend class netlist_matrix_solver_t; |
| 443 | 463 | friend class netlist_output_t; |
| 444 | 464 | friend class netlist_input_t; |
| 445 | 465 | friend class netlist_logic_output_t; |
| r26546 | r26547 | |
| 493 | 513 | // m_terms is only used by analog subsystem |
| 494 | 514 | typedef netlist_list_t<netlist_terminal_t *> terminal_list_t; |
| 495 | 515 | terminal_list_t m_terms; |
| 516 | netlist_matrix_solver_t *m_solver; |
| 496 | 517 | |
| 518 | netlist_core_terminal_t *m_head; |
| 519 | |
| 497 | 520 | protected: |
| 498 | 521 | |
| 499 | 522 | /* prohibit use in device functions |
| r26546 | r26547 | |
| 510 | 533 | hybrid_t m_cur; |
| 511 | 534 | hybrid_t m_new; |
| 512 | 535 | |
| 513 | | netlist_core_terminal_t *m_head; |
| 514 | 536 | UINT32 m_num_cons; |
| 515 | 537 | |
| 516 | 538 | private: |
| r26546 | r26547 | |
| 717 | 739 | { |
| 718 | 740 | public: |
| 719 | 741 | |
| 742 | typedef netlist_list_t<netlist_core_device_t *> list_t; |
| 743 | |
| 720 | 744 | ATTR_COLD netlist_core_device_t(); |
| 721 | 745 | ATTR_COLD netlist_core_device_t(const family_t afamily); |
| 722 | 746 | |
trunk/src/emu/netlist/devices/nld_system.c
| r26546 | r26547 | |
| 78 | 78 | } |
| 79 | 79 | |
| 80 | 80 | // ---------------------------------------------------------------------------------------- |
| 81 | // netlist_matrix_solver |
| 82 | // ---------------------------------------------------------------------------------------- |
| 83 | |
| 84 | |
| 85 | ATTR_COLD void netlist_matrix_solver_t::setup(netlist_net_t::list_t &nets) |
| 86 | { |
| 87 | for (netlist_net_t::list_t::entry_t *pn = nets.first(); pn != NULL; pn = nets.next(pn)) |
| 88 | { |
| 89 | NL_VERBOSE_OUT(("setting up net\n")); |
| 90 | |
| 91 | m_nets.add(pn->object()); |
| 92 | pn->object()->m_solver = this; |
| 93 | |
| 94 | for (netlist_core_terminal_t *p = pn->object()->m_head; p != NULL; p = p->m_update_list_next) |
| 95 | { |
| 96 | switch (p->type()) |
| 97 | { |
| 98 | case netlist_terminal_t::TERMINAL: |
| 99 | switch (p->netdev().family()) |
| 100 | { |
| 101 | case netlist_device_t::CAPACITOR: |
| 102 | if (!m_steps.contains(&p->netdev())) |
| 103 | m_steps.add(&p->netdev()); |
| 104 | break; |
| 105 | case netlist_device_t::DIODE: |
| 106 | case netlist_device_t::BJT_SWITCH: |
| 107 | if (!m_dynamic.contains(&p->netdev())) |
| 108 | m_dynamic.add(&p->netdev()); |
| 109 | break; |
| 110 | default: |
| 111 | break; |
| 112 | } |
| 113 | pn->object()->m_terms.add(static_cast<netlist_terminal_t *>(p)); |
| 114 | NL_VERBOSE_OUT(("Added terminal\n")); |
| 115 | break; |
| 116 | case netlist_terminal_t::INPUT: |
| 117 | if (!m_inps.contains(&p->netdev())) |
| 118 | m_inps.add(&p->netdev()); |
| 119 | NL_VERBOSE_OUT(("Added input\n")); |
| 120 | break; |
| 121 | default: |
| 122 | fatalerror("unhandled element found\n"); |
| 123 | break; |
| 124 | } |
| 125 | } |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | ATTR_HOT inline void netlist_matrix_solver_t::step(const netlist_time delta) |
| 130 | { |
| 131 | const double dd = delta.as_double(); |
| 132 | for (dev_list_t::entry_t *p = m_steps.first(); p != NULL; p = m_steps.next(p)) |
| 133 | p->object()->step_time(dd); |
| 134 | } |
| 135 | |
| 136 | ATTR_HOT inline void netlist_matrix_solver_t::update_inputs() |
| 137 | { |
| 138 | for (dev_list_t::entry_t *p = m_inps.first(); p != NULL; p = m_inps.next(p)) |
| 139 | p->object()->update_dev(); |
| 140 | } |
| 141 | |
| 142 | |
| 143 | ATTR_HOT inline bool netlist_matrix_solver_t::solve() |
| 144 | { |
| 145 | bool resched = false; |
| 146 | |
| 147 | /* update all non-linear devices */ |
| 148 | for (dev_list_t::entry_t *p = m_dynamic.first(); p != NULL; p = m_dynamic.next(p)) |
| 149 | p->object()->update_terminals(); |
| 150 | |
| 151 | for (netlist_net_t::list_t::entry_t *pn = m_nets.first(); pn != NULL; pn = m_nets.next(pn)) |
| 152 | { |
| 153 | netlist_net_t *net = pn->object(); |
| 154 | |
| 155 | double gtot = 0; |
| 156 | double iIdr = 0; |
| 157 | |
| 158 | for (netlist_net_t::terminal_list_t::entry_t *e = net->m_terms.first(); e != NULL; e = net->m_terms.next(e)) |
| 159 | { |
| 160 | netlist_terminal_t *pt = e->object(); |
| 161 | gtot += pt->m_g; |
| 162 | iIdr += pt->m_Idr + pt->m_g * pt->m_otherterm->net().Q_Analog(); |
| 163 | } |
| 164 | |
| 165 | double new_val = iIdr / gtot; |
| 166 | if (fabs(new_val - net->m_cur.Analog) > m_accuracy) |
| 167 | resched = true; |
| 168 | net->m_cur.Analog = net->m_new.Analog = new_val; |
| 169 | |
| 170 | NL_VERBOSE_OUT(("Info: %d\n", pn->object()->m_num_cons)); |
| 171 | NL_VERBOSE_OUT(("New: %lld %f %f\n", netlist().time().as_raw(), netlist().time().as_double(), new_val)); |
| 172 | } |
| 173 | return resched; |
| 174 | } |
| 175 | |
| 176 | // ---------------------------------------------------------------------------------------- |
| 81 | 177 | // solver |
| 82 | 178 | // ---------------------------------------------------------------------------------------- |
| 83 | 179 | |
| 180 | typedef netlist_net_t::list_t *net_groups_t; |
| 181 | |
| 182 | static bool already_processed(net_groups_t groups, int &cur_group, netlist_net_t *net) |
| 183 | { |
| 184 | if (net->isRailNet()) |
| 185 | return true; |
| 186 | for (int i = 0; i <= cur_group; i++) |
| 187 | { |
| 188 | if (groups[i].contains(net)) |
| 189 | return true; |
| 190 | } |
| 191 | return false; |
| 192 | } |
| 193 | |
| 194 | static void process_net(net_groups_t groups, int &cur_group, netlist_net_t *net) |
| 195 | { |
| 196 | /* add the net */ |
| 197 | groups[cur_group].add(net); |
| 198 | for (netlist_core_terminal_t *p = net->m_head; p != NULL; p = p->m_update_list_next) |
| 199 | { |
| 200 | if (p->isType(netlist_terminal_t::TERMINAL)) |
| 201 | { |
| 202 | netlist_terminal_t *pt = static_cast<netlist_terminal_t *>(p); |
| 203 | netlist_net_t *nnet = &pt->m_otherterm->net(); |
| 204 | if (!already_processed(groups, cur_group, nnet)) |
| 205 | process_net(groups, cur_group, nnet); |
| 206 | } |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | |
| 84 | 211 | NETLIB_START(solver) |
| 85 | 212 | { |
| 86 | 213 | register_output("Q_sync", m_Q_sync); |
| r26546 | r26547 | |
| 108 | 235 | |
| 109 | 236 | NETLIB_NAME(solver)::~NETLIB_NAME(solver)() |
| 110 | 237 | { |
| 111 | | net_list_t::entry_t *p = m_nets.first(); |
| 238 | netlist_matrix_solver_t::list_t::entry_t *e = m_mat_solvers.first(); |
| 239 | while (e != NULL) |
| 240 | { |
| 241 | netlist_matrix_solver_t::list_t::entry_t *en = m_mat_solvers.next(e); |
| 242 | delete e->object(); |
| 243 | e = en; |
| 244 | } |
| 245 | |
| 246 | netlist_net_t::list_t::entry_t *p = m_nets.first(); |
| 112 | 247 | while (p != NULL) |
| 113 | 248 | { |
| 114 | | net_list_t::entry_t *pn = m_nets.next(p); |
| 249 | netlist_net_t::list_t::entry_t *pn = m_nets.next(p); |
| 115 | 250 | delete p->object(); |
| 116 | 251 | p = pn; |
| 117 | 252 | } |
| r26546 | r26547 | |
| 119 | 254 | |
| 120 | 255 | NETLIB_FUNC_VOID(solver, post_start, ()) |
| 121 | 256 | { |
| 257 | netlist_net_t::list_t groups[100]; |
| 258 | int cur_group = -1; |
| 122 | 259 | |
| 123 | 260 | NL_VERBOSE_OUT(("post start solver ...\n")); |
| 124 | | for (net_list_t::entry_t *pn = m_nets.first(); pn != NULL; pn = m_nets.next(pn)) |
| 261 | |
| 262 | // delete empty nets ... |
| 263 | for (netlist_net_t::list_t::entry_t *pn = m_nets.first(); pn != NULL; pn = m_nets.next(pn)) |
| 125 | 264 | { |
| 126 | | NL_VERBOSE_OUT(("setting up net\n")); |
| 127 | | for (netlist_core_terminal_t *p = pn->object()->m_head; p != NULL; p = p->m_update_list_next) |
| 128 | | { |
| 129 | | switch (p->type()) |
| 130 | | { |
| 131 | | case netlist_terminal_t::TERMINAL: |
| 132 | | switch (p->netdev().family()) |
| 133 | | { |
| 134 | | case CAPACITOR: |
| 135 | | if (!m_steps.contains(&p->netdev())) |
| 136 | | m_steps.add(&p->netdev()); |
| 137 | | break; |
| 138 | | case DIODE: |
| 139 | | case BJT_SWITCH: |
| 140 | | if (!m_dynamic.contains(&p->netdev())) |
| 141 | | m_dynamic.add(&p->netdev()); |
| 142 | | break; |
| 143 | | default: |
| 144 | | break; |
| 145 | | } |
| 146 | | pn->object()->m_terms.add(static_cast<netlist_terminal_t *>(p)); |
| 147 | | NL_VERBOSE_OUT(("Added terminal\n")); |
| 148 | | break; |
| 149 | | case netlist_terminal_t::INPUT: |
| 150 | | if (!m_inps.contains(&p->netdev())) |
| 151 | | m_inps.add(&p->netdev()); |
| 152 | | NL_VERBOSE_OUT(("Added input\n")); |
| 153 | | break; |
| 154 | | default: |
| 155 | | fatalerror("unhandled element found\n"); |
| 156 | | break; |
| 157 | | } |
| 158 | | } |
| 159 | 265 | if (pn->object()->m_head == NULL) |
| 160 | 266 | { |
| 161 | 267 | NL_VERBOSE_OUT(("Deleting net ...\n")); |
| r26546 | r26547 | |
| 165 | 271 | pn--; |
| 166 | 272 | } |
| 167 | 273 | } |
| 274 | |
| 275 | printf("Scanning net groups ...\n"); |
| 276 | // determine net groups |
| 277 | for (netlist_net_t::list_t::entry_t *pn = m_nets.first(); pn != NULL; pn = m_nets.next(pn)) |
| 278 | { |
| 279 | if (!already_processed(groups, cur_group, pn->object())) |
| 280 | { |
| 281 | cur_group++; |
| 282 | process_net(groups, cur_group, pn->object()); |
| 283 | } |
| 284 | } |
| 285 | printf("Found %d net groups in %d nets\n", cur_group + 1, m_nets.count()); |
| 286 | for (int i = 0; i <= cur_group; i++) |
| 287 | { |
| 288 | printf("%d ==> %d nets %s\n", i, groups[i].count(), groups[i].first()->object()->m_head->name().cstr()); |
| 289 | } |
| 290 | |
| 291 | |
| 292 | // setup the solvers |
| 293 | for (int i = 0; i <= cur_group; i++) |
| 294 | { |
| 295 | netlist_matrix_solver_t *ms = new netlist_matrix_solver_t; |
| 296 | ms->m_accuracy = m_accuracy.Value(); |
| 297 | ms->setup(groups[i]); |
| 298 | m_mat_solvers.add(ms); |
| 299 | } |
| 300 | |
| 168 | 301 | } |
| 169 | 302 | |
| 170 | 303 | NETLIB_UPDATE(solver) |
| r26546 | r26547 | |
| 182 | 315 | NL_VERBOSE_OUT(("Step!\n")); |
| 183 | 316 | /* update all terminals for new time step */ |
| 184 | 317 | m_last_step = now; |
| 185 | | for (dev_list_t::entry_t *p = m_steps.first(); p != NULL; p = m_steps.next(p)) |
| 186 | | p->object()->step_time(delta.as_double()); |
| 187 | | } |
| 188 | | do { |
| 189 | | /* update all non-linear devices */ |
| 190 | | for (dev_list_t::entry_t *p = m_dynamic.first(); p != NULL; p = m_dynamic.next(p)) |
| 191 | | p->object()->update_terminals(); |
| 192 | | |
| 193 | | resched = false; |
| 194 | | |
| 195 | | for (net_list_t::entry_t *pn = m_nets.first(); pn != NULL; pn = m_nets.next(pn)) |
| 318 | for (netlist_matrix_solver_t::list_t::entry_t *e = m_mat_solvers.first(); e != NULL; e = m_mat_solvers.next(e)) |
| 196 | 319 | { |
| 197 | | netlist_net_t *net = pn->object(); |
| 198 | | |
| 199 | | double gtot = 0; |
| 200 | | double iIdr = 0; |
| 201 | | |
| 202 | | for (netlist_net_t::terminal_list_t::entry_t *e = net->m_terms.first(); e != NULL; e = net->m_terms.next(e)) |
| 203 | | { |
| 204 | | netlist_terminal_t *pt = e->object(); |
| 205 | | gtot += pt->m_g; |
| 206 | | iIdr += pt->m_Idr + pt->m_g * pt->m_otherterm->net().Q_Analog(); |
| 207 | | } |
| 208 | | |
| 209 | | double new_val = iIdr / gtot; |
| 210 | | if (fabs(new_val - net->m_cur.Analog) > m_accuracy.Value()) |
| 211 | | resched = true; |
| 212 | | resched_cnt++; |
| 213 | | net->m_cur.Analog = net->m_new.Analog = new_val; |
| 214 | | |
| 215 | | NL_VERBOSE_OUT(("Info: %d\n", pn->object()->m_num_cons)); |
| 216 | | NL_VERBOSE_OUT(("New: %lld %f %f\n", netlist().time().as_raw(), netlist().time().as_double(), new_val)); |
| 320 | e->object()->step(delta); |
| 217 | 321 | } |
| 218 | | } while (resched && (resched_cnt < 5)); |
| 219 | | //if (resched_cnt >= 5) |
| 220 | | // printf("rescheduled\n"); |
| 221 | | if (resched) |
| 322 | } |
| 323 | bool global_resched = false; |
| 324 | for (netlist_matrix_solver_t::list_t::entry_t *e = m_mat_solvers.first(); e != NULL; e = m_mat_solvers.next(e)) |
| 222 | 325 | { |
| 326 | resched_cnt = 0; |
| 327 | do { |
| 328 | resched = e->object()->solve(); |
| 329 | resched_cnt++; |
| 330 | } while (resched && (resched_cnt < 5)); |
| 331 | global_resched = global_resched || resched; |
| 332 | } |
| 333 | if (global_resched) |
| 334 | printf("rescheduled\n"); |
| 335 | if (global_resched) |
| 336 | { |
| 223 | 337 | schedule(); |
| 224 | 338 | } |
| 225 | 339 | else |
| 226 | 340 | { |
| 227 | 341 | /* update all inputs connected */ |
| 228 | | for (dev_list_t::entry_t *p = m_inps.first(); p != NULL; p = m_inps.next(p)) |
| 229 | | p->object()->update_dev(); |
| 342 | for (netlist_matrix_solver_t::list_t::entry_t *e = m_mat_solvers.first(); e != NULL; e = m_mat_solvers.next(e)) |
| 343 | { |
| 344 | e->object()->update_inputs(); |
| 345 | } |
| 230 | 346 | |
| 231 | 347 | /* step circuit */ |
| 232 | 348 | if (!m_Q_step.net().is_queued()) |
| 233 | 349 | m_Q_step.net().push_to_queue(m_inc); |
| 234 | 350 | } |
| 235 | 351 | |
| 236 | | /* only inputs and terminals connected |
| 237 | | * approach: |
| 238 | | * |
| 239 | | * a) Update voltage on this net |
| 240 | | * b) Update devices |
| 241 | | * c) If difference old - new > trigger schedule immediate update |
| 242 | | * of number of updates < max_update_count |
| 243 | | * else clear number of updates |
| 244 | | */ |
| 245 | | |
| 246 | 352 | } |