1 /*
2 * Copyright (c) 2008-2018 Stephen Williams (steve@icarus.com)
3 *
4 * This source code is free software; you can redistribute it
5 * and/or modify it in source code form under the terms of the GNU
6 * General Public License as published by the Free Software
7 * Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 # include "vvp_island.h"
21 # include "compile.h"
22 # include "symbols.h"
23 # include "schedule.h"
24 # include <list>
25
26 # include <iostream>
27
28 using namespace std;
29
30 class vvp_island_tran : public vvp_island {
31
32 public:
33 void run_island();
34 void count_drivers(vvp_island_port*port, unsigned bit_idx,
35 unsigned counts[3]);
36 };
37
38 enum tran_state_t {
39 tran_disabled,
40 tran_enabled,
41 tran_unknown
42 };
43
44 struct vvp_island_branch_tran : public vvp_island_branch {
45
46 vvp_island_branch_tran(vvp_net_t*en__, bool active_high__,
47 unsigned width__, unsigned part__,
48 unsigned offset__, bool resistive__);
49 bool run_test_enabled();
50 void run_resolution();
51 void run_output();
52
53 vvp_net_t*en;
54 unsigned width, part, offset;
55 bool active_high, resistive;
56 tran_state_t state;
57 };
58
vvp_island_branch_tran(vvp_net_t * en__,bool active_high__,unsigned width__,unsigned part__,unsigned offset__,bool resistive__)59 vvp_island_branch_tran::vvp_island_branch_tran(vvp_net_t*en__,
60 bool active_high__,
61 unsigned width__,
62 unsigned part__,
63 unsigned offset__,
64 bool resistive__)
65 : en(en__), width(width__), part(part__), offset(offset__),
66 active_high(active_high__), resistive(resistive__)
67 {
68 state = en__ ? tran_disabled : tran_enabled;
69 }
70
BRANCH_TRAN(vvp_island_branch * tmp)71 static inline vvp_island_branch_tran* BRANCH_TRAN(vvp_island_branch*tmp)
72 {
73 vvp_island_branch_tran*res = dynamic_cast<vvp_island_branch_tran*>(tmp);
74 assert(res);
75 return res;
76 }
77
78 /*
79 * The run_island() method is called by the scheduler to run the
80 * entire island. We run the island by calling run_resolution() for
81 * all the branches in the island.
82 */
run_island()83 void vvp_island_tran::run_island()
84 {
85 // Test to see if any of the branches are enabled. This loop
86 // tests the enabled inputs for all the branches and caches
87 // the results in the state for each branch.
88 bool runnable = false;
89 for (vvp_island_branch*cur = branches_ ; cur ; cur = cur->next_branch) {
90 vvp_island_branch_tran*tmp = dynamic_cast<vvp_island_branch_tran*>(cur);
91 assert(tmp);
92 runnable |= tmp->run_test_enabled();
93 }
94
95 // Now resolve all the branches in the island.
96 for (vvp_island_branch*cur = branches_ ; cur ; cur = cur->next_branch) {
97 vvp_island_branch_tran*tmp = dynamic_cast<vvp_island_branch_tran*>(cur);
98 assert(tmp);
99 tmp->run_resolution();
100 }
101
102 // Now output the resolved values.
103 for (vvp_island_branch*cur = branches_ ; cur ; cur = cur->next_branch) {
104 vvp_island_branch_tran*tmp = dynamic_cast<vvp_island_branch_tran*>(cur);
105 assert(tmp);
106 tmp->run_output();
107 }
108 }
109
count_drivers_(vvp_branch_ptr_t cur,bool other_side_visited,unsigned bit_idx,unsigned counts[3])110 static void count_drivers_(vvp_branch_ptr_t cur, bool other_side_visited,
111 unsigned bit_idx, unsigned counts[3])
112 {
113 // First count any value driven into the port associated with
114 // the current endpoint.
115 vvp_net_t*net = cur.port() ? cur.ptr()->b : cur.ptr()->a;
116 vvp_scalar_t bit = island_get_value(net).value(bit_idx);
117 update_driver_counts(bit.value(), counts);
118
119 // Now handle all the branches attached to that port.
120 vvp_branch_ptr_t idx = cur;
121 do {
122 vvp_island_branch_tran*tmp = BRANCH_TRAN(idx.ptr());
123
124 // If this branch represents a tran gate, we count the
125 // value on the other side of the tran (providing it is
126 // enabled) as a single driver.
127 if (tmp->width == 0) {
128 if (tmp->state == tran_enabled) {
129 net = idx.port() ? idx.ptr()->a : idx.ptr()->b;
130 bit = island_get_sent_value(net).value(bit_idx);
131 update_driver_counts(bit.value(), counts);
132 }
133 continue;
134 }
135
136 // If we get here, this branch is a part select. If we've
137 // just come from the other end of the branch, we're done.
138 if ((idx == cur) && other_side_visited)
139 continue;
140
141 // If this is the narrow end of the part select, the other
142 // end must include the bit we are interested in. Follow
143 // the branch to collect any drivers on the other side.
144 if (idx.port() == 1) {
145 vvp_branch_ptr_t a_side(tmp, 0);
146 count_drivers_(a_side, true, tmp->offset + bit_idx, counts);
147 continue;
148 }
149
150 // If we get here, this branch is the wide end of a part
151 // select. If the bit we are interested in is within the
152 // selected part, follow the branch to collect any drivers
153 // on the other side.
154 if ((bit_idx >= tmp->offset) && (bit_idx < tmp->offset+tmp->part)) {
155 vvp_branch_ptr_t b_side(tmp, 1);
156 count_drivers_(b_side, true, bit_idx - tmp->offset, counts);
157 continue;
158 }
159 } while ((idx = next(idx)) != cur);
160 }
161
count_drivers(vvp_island_port * port,unsigned bit_idx,unsigned counts[3])162 void vvp_island_tran::count_drivers(vvp_island_port*port, unsigned bit_idx,
163 unsigned counts[3])
164 {
165 // First we need to find a branch that is attached to the specified
166 // port. Unfortunately there's no quick way to do this.
167 vvp_island_branch*branch = branches_;
168 unsigned side = 0;
169 while (branch) {
170 if (branch->a->fun == port) {
171 side = 0;
172 break;
173 }
174 if (branch->b->fun == port) {
175 side = 1;
176 break;
177 }
178 branch = branch->next_branch;
179 }
180 assert(branch);
181
182 // Now count the drivers, pushing through the network as necessary.
183 vvp_branch_ptr_t endpoint(branch, side);
184 count_drivers_(endpoint, false, bit_idx, counts);
185 }
186
run_test_enabled()187 bool vvp_island_branch_tran::run_test_enabled()
188 {
189 vvp_island_port*ep = en? dynamic_cast<vvp_island_port*> (en->fun) : 0;
190
191 // If there is no ep port (no "enabled" input) then this is a
192 // tran branch. Assume it is always enabled.
193 if (ep == 0) {
194 state = tran_enabled;
195 return true;
196 }
197
198 // Get the input that is driving this enable.
199 // SPECIAL NOTE: Try to get the input value from the
200 // *outvalue* of the port. If the enable is connected to a
201 // .port (instead of a .import) then there may be feedback
202 // going on, and we need to be looking at the resolved input,
203 // not the event input. For example:
204 //
205 // tranif1 (pin, X, pin);
206 //
207 // In this case, when we test the value for "pin", we need to
208 // look at the value that is resolved from this
209 // island. Reading the outvalue will do the trick.
210 //
211 // If the outvalue is nil, then we know that this port is a
212 // .import after all, so just read the invalue.
213 vvp_bit4_t enable_val;
214 if (ep->outvalue.size() != 0)
215 enable_val = ep->outvalue.value(0).value();
216 else if (ep->invalue.size() == 0)
217 enable_val = BIT4_Z;
218 else
219 enable_val = ep->invalue.value(0).value();
220
221 switch (enable_val) {
222 case BIT4_0:
223 state = active_high ? tran_disabled : tran_enabled;
224 break;
225 case BIT4_1:
226 state = active_high ? tran_enabled : tran_disabled;
227 break;
228 default:
229 state = tran_unknown;
230 break;
231 }
232 return (state != tran_disabled);
233 }
234
235 // The IEEE standard does not specify the behaviour when a tranif control
236 // input is 'x' or 'z'. We use the rules that are given for MOS switches.
resolve_ambiguous(const vvp_vector8_t & a,const vvp_vector8_t & b,tran_state_t state,unsigned str_map[8])237 inline vvp_vector8_t resolve_ambiguous(const vvp_vector8_t&a,
238 const vvp_vector8_t&b,
239 tran_state_t state,
240 unsigned str_map[8])
241 {
242 assert(a.size() == b.size());
243 vvp_vector8_t out (a.size());
244
245 for (unsigned idx = 0 ; idx < out.size() ; idx += 1) {
246 vvp_scalar_t a_bit = a.value(idx);
247 vvp_scalar_t b_bit = b.value(idx);
248 b_bit = vvp_scalar_t(b_bit.value(),
249 str_map[b_bit.strength0()],
250 str_map[b_bit.strength1()]);
251 if (state == tran_unknown) {
252 switch (b_bit.value()) {
253 case BIT4_0:
254 b_bit = vvp_scalar_t(BIT4_X, b_bit.strength0(), 0);
255 break;
256 case BIT4_1:
257 b_bit = vvp_scalar_t(BIT4_X, 0, b_bit.strength1());
258 break;
259 default:
260 break;
261 }
262 }
263 out.set_bit(idx, resolve(a_bit, b_bit));
264 }
265 return out;
266 }
267
268 static void push_value_through_branches(const vvp_vector8_t&val,
269 list<vvp_branch_ptr_t>&connections);
270
push_value_through_branch(const vvp_vector8_t & val,vvp_branch_ptr_t cur)271 static void push_value_through_branch(const vvp_vector8_t&val,
272 vvp_branch_ptr_t cur)
273 {
274 vvp_island_branch_tran*branch = BRANCH_TRAN(cur.ptr());
275
276 // If the branch is disabled, skip.
277 if (branch->state == tran_disabled)
278 return;
279
280 unsigned src_ab = cur.port();
281 unsigned dst_ab = src_ab^1;
282
283 vvp_net_t*dst_net = dst_ab? branch->b : branch->a;
284 vvp_island_port*dst_port = dynamic_cast<vvp_island_port*>(dst_net->fun);
285
286 vvp_vector8_t old_val = dst_port->value;
287
288 // If the port on the other side has not yet been visited,
289 // get its input value.
290 if (dst_port->value.size() == 0)
291 dst_port->value = island_get_value(dst_net);
292
293 // If we don't yet have an initial value for the port, skip.
294 if (dst_port->value.size() == 0)
295 return;
296
297 // Now resolve the pushed value with whatever values we have
298 // previously collected (and resolved) for the port.
299 if (branch->width == 0) {
300 // There are no part selects.
301 dst_port->value = resolve_ambiguous(dst_port->value, val, branch->state,
302 vvp_switch_strength_map[branch->resistive]);
303
304 } else if (dst_ab == 1) {
305 // The other side is a strict subset (part select)
306 // of this side.
307 vvp_vector8_t tmp = val.subvalue(branch->offset, branch->part);
308 dst_port->value = resolve(dst_port->value, tmp);
309
310 } else {
311 // The other side is a superset of this side.
312 vvp_vector8_t tmp = part_expand(val, branch->width, branch->offset);
313 dst_port->value = resolve(dst_port->value, tmp);
314 }
315
316 // If the resolved value for the port has changed, push the new
317 // value back into the network.
318 if (! dst_port->value.eeq(old_val)) {
319 list<vvp_branch_ptr_t> connections;
320
321 vvp_branch_ptr_t dst_side(branch, dst_ab);
322 island_collect_node(connections, dst_side);
323
324 push_value_through_branches(dst_port->value, connections);
325 }
326 }
327
push_value_through_branches(const vvp_vector8_t & val,list<vvp_branch_ptr_t> & connections)328 static void push_value_through_branches(const vvp_vector8_t&val,
329 list<vvp_branch_ptr_t>&connections)
330 {
331 for (list<vvp_branch_ptr_t>::iterator idx = connections.begin()
332 ; idx != connections.end() ; ++ idx ) {
333
334 push_value_through_branch(val, *idx);
335 }
336 }
337
338 /*
339 * This method resolves the value for a branch recursively. It uses
340 * recursive descent to span the graph of branches, pushing values
341 * through the network until a stable state is reached.
342 */
run_resolution()343 void vvp_island_branch_tran::run_resolution()
344 {
345 list<vvp_branch_ptr_t> connections;
346 vvp_island_port*port;
347
348 // If the A side port hasn't already been visited, then push
349 // its input value through all the branches connected to it.
350 port = dynamic_cast<vvp_island_port*>(a->fun);
351 if (port->value.size() == 0) {
352 vvp_branch_ptr_t a_side(this, 0);
353 island_collect_node(connections, a_side);
354
355 port->value = island_get_value(a);
356 if (port->value.size() != 0)
357 push_value_through_branches(port->value, connections);
358
359 connections.clear();
360 }
361
362 // Do the same for the B side port. Note that if the branch
363 // is enabled, the B side port will have already been visited
364 // when we resolved the A side port.
365 port = dynamic_cast<vvp_island_port*>(b->fun);
366 if (port->value.size() == 0) {
367 vvp_branch_ptr_t b_side(this, 1);
368 island_collect_node(connections, b_side);
369
370 port->value = island_get_value(b);
371 if (port->value.size() != 0)
372 push_value_through_branches(port->value, connections);
373
374 connections.clear();
375 }
376 }
377
run_output()378 void vvp_island_branch_tran::run_output()
379 {
380 vvp_island_port*port;
381
382 // If the A side port hasn't already been updated, send the
383 // resolved value to the output.
384 port = dynamic_cast<vvp_island_port*>(a->fun);
385 if (port->value.size() != 0) {
386 island_send_value(a, port->value);
387 port->value = vvp_vector8_t::nil;
388 }
389
390 // Do the same for the B side port.
391 port = dynamic_cast<vvp_island_port*>(b->fun);
392 if (port->value.size() != 0) {
393 island_send_value(b, port->value);
394 port->value = vvp_vector8_t::nil;
395 }
396 }
397
compile_island_tran(char * label)398 void compile_island_tran(char*label)
399 {
400 vvp_island*use_island = new vvp_island_tran;
401 compile_island_base(label, use_island);
402 }
403
compile_island_tranif(int sense,char * island,char * pa,char * pb,char * pe,bool resistive)404 void compile_island_tranif(int sense, char*island, char*pa, char*pb, char*pe,
405 bool resistive)
406 {
407 vvp_island*use_island = compile_find_island(island);
408 assert(use_island);
409 free(island);
410
411 vvp_net_t*en = NULL;
412
413 if (pe) {
414 en = use_island->find_port(pe);
415 assert(en);
416 free(pe);
417 }
418
419 vvp_island_branch_tran*br = new vvp_island_branch_tran(en,
420 sense ? true :
421 false,
422 0, 0, 0, resistive);
423
424 use_island->add_branch(br, pa, pb);
425
426 free(pa);
427 free(pb);
428 }
429
430
compile_island_tranvp(char * island,char * pa,char * pb,unsigned wid,unsigned par,unsigned off)431 void compile_island_tranvp(char*island, char*pa, char*pb,
432 unsigned wid, unsigned par, unsigned off)
433 {
434 vvp_island*use_island = compile_find_island(island);
435 assert(use_island);
436 free(island);
437
438 vvp_island_branch_tran*br = new vvp_island_branch_tran(NULL, false,
439 wid, par, off, false);
440
441 use_island->add_branch(br, pa, pb);
442
443 free(pa);
444 free(pb);
445 }
446