view ueda/sverp/elaborate.c @ 83:88cdef7e6b1b

BOM tallying code factored out of ueda-mkbom
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 23 Feb 2017 19:27:14 +0000
parents 7b4f78fcca08
children
line wrap: on
line source

/*
 * Here we elaborate the hierarchy and create our flat netlist.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "struct.h"
#include "lexer.h"	/* for MAXDIGITS */

struct output_net *output_net_head;
struct output_element *output_element_head;
int total_good_nets, total_singular_nets, total_null_nets;
int total_output_elements;

static struct output_net **output_net_tail = &output_net_head;
static struct output_element **output_element_tail = &output_element_head;

static struct output_net **
alloc_mod_net_array(mod)
	struct module_def *mod;
{
	int total_wires;
	unsigned alloc_size;
	register struct output_net **array;

	total_wires = mod->nwires_ports + mod->nwires_internal;
	alloc_size = sizeof(struct output_net *) * total_wires;
	array = (struct output_net **) malloc(alloc_size);
	if (!array) {
		perror("malloc");
		exit(1);
	}
	return(array);
}

static struct output_net **
alloc_connect_net_array(mod)
	struct module_def *mod;
{
	register unsigned alloc_size;
	register struct output_net **array;

	alloc_size = sizeof(struct output_net *) * mod->nwires_ports;
	array = (struct output_net **) malloc(alloc_size);
	if (!array) {
		perror("malloc");
		exit(1);
	}
	bzero(array, alloc_size);
	return(array);
}

static struct output_net *
create_real_net(hier_prefix, base_name, is_bus, bus_pos)
	char *hier_prefix, *base_name;
{
	int len;
	register char *buf;
	register struct output_net *net;

	len = sizeof(struct output_net) + strlen(hier_prefix) +
		strlen(base_name) + 1;
	if (is_bus)
		len += MAXDIGITS + 2;
	buf = malloc(len);
	if (!buf) {
		perror("malloc");
		exit(1);
	}
	net = (struct output_net *) buf;
	bzero(net, sizeof(struct output_net));
	buf += sizeof(struct output_net);
	if (is_bus)
		sprintf(buf, "%s%s[%d]", hier_prefix, base_name, bus_pos);
	else
		sprintf(buf, "%s%s", hier_prefix, base_name);
	net->name = buf;
	*output_net_tail = net;
	output_net_tail = &net->next;
	return(net);
}

static void
create_module_nets(mod, hier_prefix, fillp)
	struct module_def *mod;
	char *hier_prefix;
	register struct output_net **fillp;
{
	register struct module_net_def *nd;
	register int i, incr;

	for (nd = mod->nets; nd; nd = nd->next) {
		if (nd->is_port)
			continue;
		if (!nd->is_bus) {
			*fillp++ = create_real_net(hier_prefix, nd->name, 0);
			continue;
		}
		if (nd->bus_msb < nd->bus_lsb)
			incr = 1;
		else
			incr = -1;
		for (i = nd->bus_msb; i != nd->bus_lsb + incr; i += incr)
			*fillp++ = create_real_net(hier_prefix, nd->name, 1, i);
	}
}

elaborate_module(mod, inst_name, hier_prefix, in_conn_array)
	register struct module_def *mod;
	char *inst_name, *hier_prefix;
	struct output_net **in_conn_array;
{
	register struct output_net **mod_net_array;
	register struct module_def_subinst *sub;

	if (mod->nwires_internal) {
		mod_net_array = alloc_mod_net_array(mod);
		if (mod->nwires_ports)
			bcopy(in_conn_array, mod_net_array,
			      sizeof(struct output_net *) * mod->nwires_ports);
		create_module_nets(mod, hier_prefix,
				   mod_net_array + mod->nwires_ports);
	} else
		mod_net_array = in_conn_array;

	for (sub = mod->subinst; sub; sub = sub->next)
		elaborate_subinst(mod, inst_name, hier_prefix, mod_net_array,
				  sub);

	if (mod->nwires_internal)
		free(mod_net_array);
}

static void
report_connect_conflict(new_inst, ce, offset)
	char *new_inst;
	struct connect_entry *ce;
	int offset;
{
	register struct module_net_def *port = ce->down_portdef;
	int bus_pos;

	if (port->is_bus) {
		offset += ce->down_offset;
		if (port->bus_msb > port->bus_lsb)
			bus_pos = port->bus_msb - offset;
		else
			bus_pos = port->bus_msb + offset;
		fprintf(stderr,
			"elaborating %s: multiple connections to port %s[%d]\n",
			new_inst, port->name, bus_pos);
	} else
		fprintf(stderr,
			"elaborating %s: multiple connections to port %s\n",
			new_inst, port->name);
	exit(1);
}

static void
check_connect_conflict(new_inst, ce, conn_array)
	char *new_inst;
	register struct connect_entry *ce;
	register struct output_net **conn_array;
{
	register int start, i;

	start = ce->down_portdef->array_index + ce->down_offset;
	for (i = 0; i < ce->down_width; i++)
		if (conn_array[start + i])
			report_connect_conflict(new_inst, ce, i);
}

static struct output_net *
create_nc_net(mup, old_inst, ce)
	struct module_def *mup;
	char *old_inst;
	struct connect_entry *ce;
{
	register struct output_net *net;

	net = (struct output_net *) malloc(sizeof(struct output_net));
	if (!net) {
		perror("malloc");
		exit(1);
	}
	bzero(net, sizeof(struct output_net));
	net->nc_module_name = mup->name;
	net->nc_module_lineno = ce->src_lineno;
	net->nc_module_inst = old_inst;
	return(net);
}

static struct output_net *
process_subinst_connect(mup, sub, old_inst, new_inst, mod_net_array, conn_array)
	struct module_def *mup;
	struct module_def_subinst *sub;
	char *old_inst, *new_inst;
	struct output_net **mod_net_array;
	register struct output_net **conn_array;
{
	struct output_net *nc_head = 0, **nc_tail = &nc_head;
	register struct connect_entry *ce;
	int start;
	register int i;
	register struct output_net *nc_net;

	for (ce = sub->connections; ce; ce = ce->next) {
		check_connect_conflict(new_inst, ce, conn_array);
		if (ce->up_netdef) {
			bcopy(mod_net_array + ce->up_netdef->array_index +
				ce->up_offset,
			      conn_array + ce->down_portdef->array_index +
				ce->down_offset,
			      sizeof(struct output_net *) * ce->up_width);
			continue;
		}
		/* it's a no-connect */
		start = ce->down_portdef->array_index + ce->down_offset;
		for (i = 0; i < ce->down_width; i++) {
			nc_net = create_nc_net(mup, old_inst, ce);
			conn_array[start + i] = nc_net;
			*nc_tail = nc_net;
			nc_tail = &nc_net->next;
		}
	}
	return(nc_head);
}

static void
report_missing_connect(new_inst, port, offset)
	char *new_inst;
	register struct module_net_def *port;
	int offset;
{
	int bus_pos;

	if (port->is_bus) {
		if (port->bus_msb > port->bus_lsb)
			bus_pos = port->bus_msb - offset;
		else
			bus_pos = port->bus_msb + offset;
		fprintf(stderr,
			"elaborating %s: missing connection to port %s[%d]\n",
			new_inst, port->name, bus_pos);
	} else
		fprintf(stderr,
			"elaborating %s: missing connection to port %s\n",
			new_inst, port->name);
	exit(1);
}

static void
check_missing_connect(new_inst, mdown, conn_array)
	char *new_inst;
	struct module_def *mdown;
	struct output_net **conn_array;
{
	register struct module_net_def *port;
	register struct output_net **connp = conn_array;
	register int i;

	for (port = mdown->nets; port; port = port->next) {
		if (!port->is_port)
			return;		/* all ports are upfront */
		if (!port->is_bus) {
			if (!*connp++)
				report_missing_connect(new_inst, port, 0);
			continue;
		}
		for (i = 0; i < port->bus_width; i++)
			if (!*connp++)
				report_missing_connect(new_inst, port, i);
	}
}

static void
report_bad_nc_net(net)
	register struct output_net *net;
{
	fprintf(stderr,
"error: NC pseudo-net created at module %s line %d inst %s goes to more than one pin\n",
		net->nc_module_name, net->nc_module_lineno,
		net->nc_module_inst);
	exit(1);
}

static void
check_nc_nets(head)
	struct output_net *head;
{
	register struct output_net *net;

	for (net = head; net; net = net->next)
		if (net->npoints > 1)
			report_bad_nc_net(net);
}

elaborate_subinst(mup, inst_name, hier_prefix, mod_net_array, sub)
	struct module_def *mup;
	char *inst_name, *hier_prefix;
	struct output_net **mod_net_array;
	struct module_def_subinst *sub;
{
	char *new_inst, *new_prefix;
	struct module_def *mdown = sub->submod_def;
	struct output_net **conn_array, *nc_nets;

	new_inst = malloc(strlen(hier_prefix) + strlen(sub->inst_name) + 1);
	if (!new_inst) {
		perror("malloc");
		exit(1);
	}
	sprintf(new_inst, "%s%s", hier_prefix, sub->inst_name);
	if (mdown->nports) {
		conn_array = alloc_connect_net_array(mdown);
		nc_nets = process_subinst_connect(mup, sub, inst_name, new_inst,
						  mod_net_array, conn_array);
		check_missing_connect(new_inst, mdown, conn_array);
	} else {
		conn_array = 0;
		nc_nets = 0;
	}

	if (mdown->is_primitive)
		elaborate_primitive(mdown, new_inst, conn_array);
	else {
		new_prefix = malloc(strlen(hier_prefix) +
					strlen(sub->inst_name) + 2);
		if (!new_prefix) {
			perror("malloc");
			exit(1);
		}
		sprintf(new_prefix, "%s%s.", hier_prefix, sub->inst_name);
		elaborate_module(mdown, new_inst, new_prefix, conn_array);
		free(new_prefix);
		if (conn_array)
			free(conn_array);
	}

	if (nc_nets)
		check_nc_nets(nc_nets);
}

static void
incr_net_points_for_output_element(elem)
	struct output_element *elem;
{
	register struct output_net **p, **endp;

	p = elem->connections;
	endp = p + elem->prim_def->nports;
	for (; p < endp; p++)
		(*p)->npoints++;
}

elaborate_primitive(mod, inst_name, in_conn_array)
	struct module_def *mod;
	char *inst_name;
	struct output_net **in_conn_array;
{
	register struct output_element *elem;

	elem = (struct output_element *) malloc(sizeof(struct output_element));
	if (!elem) {
		perror("malloc");
		exit(1);
	}
	elem->prim_def = mod;
	elem->hier_inst_name = inst_name;
	elem->connections = in_conn_array;
	elem->next = 0;
	*output_element_tail = elem;
	output_element_tail = &elem->next;
	total_output_elements++;
	incr_net_points_for_output_element(elem);
}

tally_output_nets()
{
	register struct output_net *net;

	for (net = output_net_head; net; net = net->next) {
		if (net->npoints > 1)
			total_good_nets++;
		else if (net->npoints == 1)
			total_singular_nets++;
		else
			total_null_nets++;
	}
}