Rework of vvp resolver networks to support $countdrivers.

To implement the $countdrivers system function, we need to be able to
find all the driver values for a given wire. Currently, if a wire has
has more than four drivers, the compiler builds a resolver tree out
of 4-input nodes to resolve the driven values, and there is no way at
run time to work back from the output node to the original driver
values. This patch moves the implementation of the resolver tree into
a single vvp functor (using a mechanism similar to wide functors to
support more than 4 inputs), thus gathering all the driver values into
a single place.
This commit is contained in:
Martin Whitaker 2012-07-10 21:15:34 +01:00 committed by Stephen Williams
parent ea420d94ac
commit a290c58a2e
4 changed files with 340 additions and 174 deletions

View File

@ -85,17 +85,6 @@ static ivl_variable_type_t signal_data_type_of_nexus(ivl_nexus_t nex)
return out;
}
static void draw_C4_repeated_constant(char bit_char, unsigned width)
{
unsigned idx;
fprintf(vvp_out, "C4<");
for (idx = 0 ; idx < width ; idx += 1)
fprintf(vvp_out, "%c", bit_char);
fprintf(vvp_out, ">");
}
static char* draw_C4_to_string(ivl_net_const_t cptr)
{
const char*bits = ivl_const_bits(cptr);
@ -648,10 +637,10 @@ static void draw_net_input_x(ivl_nexus_t nex,
ivl_signal_type_t res;
char result[512];
unsigned idx;
int level;
char**driver_labels;
unsigned ndrivers = 0;
const char*resolv_type, *branch_type;
const char*resolv_type;
char*nex_private = 0;
@ -663,31 +652,25 @@ static void draw_net_input_x(ivl_nexus_t nex,
case IVL_SIT_TRI:
case IVL_SIT_UWIRE:
resolv_type = "tri";
branch_type = "tri";
break;
case IVL_SIT_TRI0:
resolv_type = "tri0";
branch_type = "tri";
nex_flags |= VVP_NEXUS_DATA_STR;
break;
case IVL_SIT_TRI1:
resolv_type = "tri1";
branch_type = "tri";
nex_flags |= VVP_NEXUS_DATA_STR;
break;
case IVL_SIT_TRIAND:
resolv_type = "triand";
branch_type = "triand";
break;
case IVL_SIT_TRIOR:
resolv_type = "trior";
branch_type = "trior";
break;
default:
fprintf(stderr, "vvp.tgt: Unsupported signal type: %u\n", res);
assert(0);
resolv_type = "tri";
branch_type = "tri";
break;
}
@ -835,46 +818,17 @@ static void draw_net_input_x(ivl_nexus_t nex,
display_multi_driver_error(nex, ndrivers, MDRV_REAL);
}
level = 0;
while (ndrivers) {
unsigned int inst;
for (inst = 0; inst < ndrivers; inst += 4) {
char*drive[4];
if (level == 0) {
for (idx = inst; idx < ndrivers && idx < inst+4; idx += 1) {
drive[idx-inst] = draw_net_input_drive(nex, drivers[idx]);
driver_labels = malloc(ndrivers * sizeof(char*));
for (idx = 0; idx < ndrivers; idx += 1) {
driver_labels[idx] = draw_net_input_drive(nex, drivers[idx]);
}
fprintf(vvp_out, "RS_%p .resolv %s", nex, resolv_type);
for (idx = 0; idx < ndrivers; idx += 1) {
fprintf(vvp_out, ", %s", driver_labels[idx]);
free(driver_labels[idx]);
}
if (ndrivers > 4)
fprintf(vvp_out, "RS_%p/%d/%d .resolv %s",
nex, level, inst, branch_type);
else
fprintf(vvp_out, "RS_%p .resolv %s",
nex, resolv_type);
for (idx = inst; idx < ndrivers && idx < inst+4; idx += 1) {
if (level) {
fprintf(vvp_out, ", RS_%p/%d/%d",
nex, level - 1, idx*4);
} else {
fprintf(vvp_out, ", %s", drive[idx-inst]);
free(drive[idx-inst]);
}
}
for ( ; idx < inst+4 ; idx += 1) {
fprintf(vvp_out, ", ");
draw_C4_repeated_constant('z',width_of_nexus(nex));
}
fprintf(vvp_out, ";\n");
}
if (ndrivers > 4)
ndrivers = (ndrivers+3) / 4;
else
ndrivers = 0;
level += 1;
}
free(driver_labels);
snprintf(result, sizeof result, "RS_%p", nex);

View File

@ -1462,37 +1462,46 @@ void compile_shiftr(char*label, long wid, bool signed_flag,
void compile_resolver(char*label, char*type, unsigned argc, struct symb_s*argv)
{
assert(argc <= 4);
vvp_net_fun_t* obj = 0;
vvp_net_t*net = new vvp_net_t;
resolv_core*core = 0;
if (strcmp(type,"tri") == 0) {
obj = new resolv_functor(vvp_scalar_t(BIT4_Z, 0,0));
} else if (strncmp(type,"tri$",4) == 0) {
obj = new resolv_functor(vvp_scalar_t(BIT4_Z, 0,0), strdup(type+4));
core = new resolv_tri(argc, net, vvp_scalar_t(BIT4_Z, 0,0));
} else if (strcmp(type,"tri0") == 0) {
obj = new resolv_functor(vvp_scalar_t(BIT4_0, 5,5));
core = new resolv_tri(argc, net, vvp_scalar_t(BIT4_0, 5,5));
} else if (strcmp(type,"tri1") == 0) {
obj = new resolv_functor(vvp_scalar_t(BIT4_1, 5,5));
core = new resolv_tri(argc, net, vvp_scalar_t(BIT4_1, 5,5));
} else if (strcmp(type,"triand") == 0) {
obj = new resolv_triand;
core = new resolv_triand(argc, net);
} else if (strcmp(type,"trior") == 0) {
obj = new resolv_trior;
core = new resolv_trior(argc, net);
} else {
fprintf(stderr, "invalid resolver type: %s\n", type);
compile_errors += 1;
free(net);
}
if (obj) {
vvp_net_t*net = new vvp_net_t;
net->fun = obj;
if (core) {
net->fun = core;
define_functor_symbol(label, net);
inputs_connect(net, argc, argv);
for (unsigned base = 0 ; base < argc ; base += 4) {
unsigned nports = argc - base;
if (nports > 4)
nports = 4;
if (base > 0) {
net = new vvp_net_t;
net->fun = new resolv_extend(core, base);
}
inputs_connect(net, nports, argv+base);
}
}
free(type);
free(label);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com)
* Copyright (c) 2001-2010,2012 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -22,28 +22,36 @@
# include "compile.h"
# include "statistics.h"
# include <iostream>
# include <algorithm>
# include <cassert>
resolv_functor::resolv_functor(vvp_scalar_t hiz_value, const char*debug_l)
: hiz_(hiz_value), debug_label_(debug_l)
/*
* The core functor for a resolver node stores all the input values
* received by that node. This provides the necessary information
* for implementing the $countdrivers system call. For efficency,
* the resolver is implemented using a balanced quaternary tree, so
* the core functor also stores the current value for each branch
* of the tree, to eliminate the need to re-evaluate branches whose
* inputs haven't changed. The tree values are flattened into a linear
* array, with the input values stored at the start of the array, then
* the next level of branch values, and so on, down to the final array
* array element which stores the current output value.
*/
resolv_core::resolv_core(unsigned nports, vvp_net_t*net)
: nports_(nports), net_(net)
{
count_functors_resolv += 1;
}
resolv_functor::~resolv_functor()
resolv_core::~resolv_core()
{
}
void resolv_functor::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t)
{
recv_vec8(port, vvp_vector8_t(bit, 6,6 /* STRONG */));
}
void resolv_functor::recv_vec4_pv(vvp_net_ptr_t port, const vvp_vector4_t&bit,
unsigned base, unsigned wid, unsigned vwid,
vvp_context_t)
void resolv_core::recv_vec4_pv_(unsigned port, const vvp_vector4_t&bit,
unsigned base, unsigned wid, unsigned vwid)
{
assert(bit.size() == wid);
vvp_vector4_t res (vwid);
@ -57,49 +65,10 @@ void resolv_functor::recv_vec4_pv(vvp_net_ptr_t port, const vvp_vector4_t&bit,
for (unsigned idx = base+wid ; idx < vwid ; idx += 1)
res.set_bit(idx, BIT4_Z);
recv_vec4(port, res, 0);
recv_vec4_(port, res);
}
void resolv_functor::recv_vec8(vvp_net_ptr_t port, const vvp_vector8_t&bit)
{
unsigned pdx = port.port();
vvp_net_t*ptr = port.ptr();
if (val_[pdx].eeq(bit))
return;
val_[pdx] = bit;
vvp_vector8_t out (bit);
for (unsigned idx = 0 ; idx < 4 ; idx += 1) {
if (idx == pdx)
continue;
if (val_[idx].size() == 0)
continue;
if (out.size()==0)
out = val_[idx];
else
out = resolve(out, val_[idx]);
}
if (! hiz_.is_hiz()) {
for (unsigned idx = 0 ; idx < out.size() ; idx += 1) {
if (out.value(idx).is_hiz())
out.set_bit(idx, hiz_);
}
}
if (debug_label_ && debug_file.is_open())
debug_file << "[" << schedule_simtime() << "] "
<< debug_label_ << ": Resolv out=" << out
<< " in=" << val_[0] << ", " << val_[1]
<< ", " << val_[2] << ", " << val_[3] << endl;
ptr->send_vec8(out);
}
void resolv_functor::recv_vec8_pv(vvp_net_ptr_t port, const vvp_vector8_t&bit,
void resolv_core::recv_vec8_pv_(unsigned port, const vvp_vector8_t&bit,
unsigned base, unsigned wid, unsigned vwid)
{
assert(bit.size() == wid);
@ -114,39 +83,184 @@ void resolv_functor::recv_vec8_pv(vvp_net_ptr_t port, const vvp_vector8_t&bit,
for (unsigned idx = base+wid ; idx < vwid ; idx += 1)
res.set_bit(idx, vvp_scalar_t());
recv_vec8(port, res);
recv_vec8_(port, res);
}
resolv_wired_logic::resolv_wired_logic()
resolv_extend::resolv_extend(resolv_core*core, unsigned port_base)
: core_(core), port_base_(port_base)
{
}
resolv_extend::~resolv_extend()
{
}
resolv_tri::resolv_tri(unsigned nports, vvp_net_t*net, vvp_scalar_t hiz_value)
: resolv_core(nports, net), hiz_value_(hiz_value)
{
// count the input (leaf) nodes
unsigned nnodes = nports;
// add in the intermediate branch nodes
while (nports > 4) {
nports = (nports + 3) / 4;
nnodes += nports;
}
// add one more node for storing the output value
// (not needed if there is only one input)
if (nnodes > 1)
nnodes += 1;
val_ = new vvp_vector8_t [nnodes];
}
resolv_tri::~resolv_tri()
{
delete[] val_;
}
void resolv_tri::recv_vec4_(unsigned port, const vvp_vector4_t&bit)
{
recv_vec8_(port, vvp_vector8_t(bit, 6,6 /* STRONG */));
}
void resolv_tri::recv_vec8_(unsigned port, const vvp_vector8_t&bit)
{
assert(port < nports_);
if (val_[port].eeq(bit))
return;
val_[port] = bit;
// Starting at the leaf level, work down the tree, resolving
// the changed values. base is the first node in the current
// level and span is the number of nodes at that level. ip
// is the first node in the group of four nodes at that level
// that include the node that has changed, and op is the node
// at the next level that stores the resolved value from that
// group.
unsigned base = 0;
unsigned span = nports_;
while (span > 1) {
unsigned next_base = base + span;
unsigned ip = base + (port & ~0x3);
unsigned op = next_base + (port / 4);
unsigned ll = min(ip + 4, next_base);
vvp_vector8_t out = val_[ip];
for (ip = ip + 1; ip < ll; ip += 1) {
if (val_[ip].size() == 0)
continue;
if (out.size() == 0)
out = val_[ip];
else
out = resolve(out, val_[ip]);
}
if (val_[op].eeq(out))
return;
val_[op] = out;
base = next_base;
span = (span + 3) / 4;
port = port / 4;
}
if (! hiz_value_.is_hiz()) {
for (unsigned idx = 0 ; idx < val_[base].size() ; idx += 1) {
if (val_[base].value(idx).is_hiz())
val_[base].set_bit(idx, hiz_value_);
}
}
net_->send_vec8(val_[base]);
}
resolv_wired_logic::resolv_wired_logic(unsigned nports, vvp_net_t*net)
: resolv_core(nports, net)
{
// count the input (leaf) nodes
unsigned nnodes = nports;
// add in the intermediate branch nodes
while (nports > 4) {
nports = (nports + 3) / 4;
nnodes += nports;
}
// add one more node for storing the output value
// (not needed if there is only one input)
if (nnodes > 1)
nnodes += 1;
val_ = new vvp_vector4_t [nnodes];
}
resolv_wired_logic::~resolv_wired_logic()
{
delete[] val_;
}
void resolv_wired_logic::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t)
void resolv_wired_logic::recv_vec4_(unsigned port, const vvp_vector4_t&bit)
{
unsigned pdx = port.port();
vvp_net_t*ptr = port.ptr();
assert(port < nports_);
if (val_[pdx].eeq(bit))
if (val_[port].eeq(bit))
return;
val_[pdx] = bit;
val_[port] = bit;
vvp_vector4_t out (bit);
for (unsigned idx = 0 ; idx < 4 ; idx += 1) {
if (idx == pdx)
continue;
if (val_[idx].size() == 0)
continue;
// Starting at the leaf level, work down the tree, resolving
// the changed values. base is the first node in the current
// level and span is the number of nodes at that level. ip
// is the first node in the group of four nodes at that level
// that include the node that has changed, and op is the node
// at the next level that stores the resolved value from that
// group.
unsigned base = 0;
unsigned span = nports_;
while (span > 1) {
unsigned next_base = base + span;
unsigned ip = base + (port & ~0x3);
unsigned op = next_base + (port / 4);
unsigned ll = min(ip + 4, next_base);
out = wired_logic_math_(out, val_[idx]);
vvp_vector4_t out = val_[ip];
for (ip = ip + 1; ip < ll; ip += 1) {
if (val_[ip].size() == 0)
continue;
if (out.size() == 0)
out = val_[ip];
else
out = wired_logic_math_(out, val_[ip]);
}
if (val_[op].eeq(out))
return;
val_[op] = out;
base = next_base;
span = (span + 3) / 4;
port = port / 4;
}
ptr->send_vec4(out, 0);
net_->send_vec4(val_[base], 0);
}
void resolv_wired_logic::recv_vec8_(unsigned port, const vvp_vector8_t&bit)
{
recv_vec4_(port, reduce4(bit));
}
resolv_triand::resolv_triand(unsigned nports, vvp_net_t*net)
: resolv_wired_logic(nports, net)
{
}
resolv_triand::~resolv_triand()
{
}
vvp_vector4_t resolv_triand::wired_logic_math_(vvp_vector4_t&a, vvp_vector4_t&b)
@ -174,6 +288,16 @@ vvp_vector4_t resolv_triand::wired_logic_math_(vvp_vector4_t&a, vvp_vector4_t&b)
return out;
}
resolv_trior::resolv_trior(unsigned nports, vvp_net_t*net)
: resolv_wired_logic(nports, net)
{
}
resolv_trior::~resolv_trior()
{
}
vvp_vector4_t resolv_trior::wired_logic_math_(vvp_vector4_t&a, vvp_vector4_t&b)
{
assert(a.size() == b.size());

View File

@ -1,7 +1,7 @@
#ifndef __resolv_H
#define __resolv_H
/*
* Copyright (c) 2001-2008 Stephen Williams (steve@icarus.com)
* Copyright (c) 2001-2008,2012 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -22,6 +22,80 @@
# include "config.h"
# include "vvp_net.h"
/*
* Resolver nodes are similar to wide functors, in that they may have
* more than 4 inputs. Unlike wide functors, the functor that provides
* the core functionality and delivers the output is also used to handle
* the first 4 inputs, thus each resolver node is implemented by N/4
* actual functors.
*/
class resolv_core : public vvp_net_fun_t {
public:
explicit resolv_core(unsigned nports, vvp_net_t*net);
virtual ~resolv_core();
void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t)
{ recv_vec4_(port.port(), bit); }
void recv_vec8(vvp_net_ptr_t port, const vvp_vector8_t&bit)
{ recv_vec8_(port.port(), bit); }
void recv_vec4_pv(vvp_net_ptr_t port, const vvp_vector4_t&bit,
unsigned base, unsigned wid, unsigned vwid,
vvp_context_t)
{ recv_vec4_pv_(port.port(), bit, base, wid, vwid); }
void recv_vec8_pv(vvp_net_ptr_t port, const vvp_vector8_t&bit,
unsigned base, unsigned wid, unsigned vwid)
{ recv_vec8_pv_(port.port(), bit, base, wid, vwid); }
private:
friend class resolv_extend;
virtual void recv_vec4_(unsigned port, const vvp_vector4_t&bit) =0;
virtual void recv_vec8_(unsigned port, const vvp_vector8_t&bit) =0;
void recv_vec4_pv_(unsigned port, const vvp_vector4_t&bit,
unsigned base, unsigned wid, unsigned vwid);
void recv_vec8_pv_(unsigned port, const vvp_vector8_t&bit,
unsigned base, unsigned wid, unsigned vwid);
protected:
unsigned nports_;
vvp_net_t*net_;
};
class resolv_extend : public vvp_net_fun_t {
public:
resolv_extend(resolv_core*core, unsigned port_base);
~resolv_extend();
void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t)
{ core_->recv_vec4_(port_base_ + port.port(), bit); }
void recv_vec8(vvp_net_ptr_t port, const vvp_vector8_t&bit)
{ core_->recv_vec8_(port_base_ + port.port(), bit); }
void recv_vec4_pv(vvp_net_ptr_t port, const vvp_vector4_t&bit,
unsigned base, unsigned wid, unsigned vwid,
vvp_context_t)
{ core_->recv_vec4_pv_(port_base_ + port.port(), bit,
base, wid, vwid); }
void recv_vec8_pv(vvp_net_ptr_t port, const vvp_vector8_t&bit,
unsigned base, unsigned wid, unsigned vwid)
{ core_->recv_vec8_pv_(port_base_ + port.port(), bit,
base, wid, vwid); }
private:
resolv_core*core_;
unsigned port_base_;
};
/*
* This functor type resolves its inputs using the Verilog method of
* combining signals, and outputs that resolved value. The puller
@ -34,51 +108,56 @@
* strong values (or HiZ) for the sake of resolution. In any case, the
* propagated value is a vvp_vector8_t value.
*/
class resolv_functor : public vvp_net_fun_t {
class resolv_tri : public resolv_core {
public:
explicit resolv_functor(vvp_scalar_t hiz_value, const char* debug =0);
~resolv_functor();
void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t);
void recv_vec8(vvp_net_ptr_t port, const vvp_vector8_t&bit);
void recv_vec4_pv(vvp_net_ptr_t port, const vvp_vector4_t&bit,
unsigned base, unsigned wid, unsigned vwid,
vvp_context_t);
void recv_vec8_pv(vvp_net_ptr_t port, const vvp_vector8_t&bit,
unsigned base, unsigned wid, unsigned vwid);
explicit resolv_tri(unsigned nports, vvp_net_t*net,
vvp_scalar_t hiz_value);
~resolv_tri();
private:
vvp_vector8_t val_[4];
// Bit value to emit for HiZ bits.
vvp_scalar_t hiz_;
// True if debugging is enabled
const char* debug_label_;
void recv_vec4_(unsigned port, const vvp_vector4_t&bit);
void recv_vec8_(unsigned port, const vvp_vector8_t&bit);
private:
// The puller value to be used when a bit is not driven.
vvp_scalar_t hiz_value_;
// The array of input values.
vvp_vector8_t*val_;
};
class resolv_wired_logic : public vvp_net_fun_t {
/*
* This functor type resolves its inputs using the wired_logic_math_
* method implemented by each derived class, and outputs that resolved
* value.
*
* This node takes in vvp_vector4_t values, and emits a vvp_vector4_t
* value. It also takes in vvp_vector8_t values, which it reduces to
* 4-state values for the sake of resolution.
*/
class resolv_wired_logic : public resolv_core {
public:
explicit resolv_wired_logic(void);
~resolv_wired_logic();
void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t);
explicit resolv_wired_logic(unsigned nports, vvp_net_t*net);
virtual ~resolv_wired_logic();
protected:
virtual vvp_vector4_t wired_logic_math_(vvp_vector4_t&a, vvp_vector4_t&b) =0;
private:
vvp_vector4_t val_[4];
void recv_vec4_(unsigned port, const vvp_vector4_t&bit);
void recv_vec8_(unsigned port, const vvp_vector8_t&bit);
private:
// The array of input values.
vvp_vector4_t*val_;
};
class resolv_triand : public resolv_wired_logic {
public:
explicit resolv_triand(void) { }
~resolv_triand() { }
explicit resolv_triand(unsigned nports, vvp_net_t*net);
~resolv_triand();
private:
virtual vvp_vector4_t wired_logic_math_(vvp_vector4_t&a, vvp_vector4_t&b);
@ -87,8 +166,8 @@ class resolv_triand : public resolv_wired_logic {
class resolv_trior : public resolv_wired_logic {
public:
explicit resolv_trior(void) { }
~resolv_trior() { }
explicit resolv_trior(unsigned nports, vvp_net_t*net);
~resolv_trior();
private:
virtual vvp_vector4_t wired_logic_math_(vvp_vector4_t&a, vvp_vector4_t&b);