view ueda/sverp/link.c @ 4:7b4f78fcca08

ueda/sverp Linuxified
author Space Falcon <falcon@ivan.Harhan.ORG>
date Mon, 20 Jul 2015 00:53:19 +0000
parents cd92449fdb51
children
line wrap: on
line source

/*
 * ueda-sverp link pass
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "struct.h"

extern struct module_def *glob_module_list, *top_module_def;
extern int top_module_candidates;

extern struct module_def *find_module_by_name();

static void
toplevel_candidate(mod)
	struct module_def *mod;
{
	top_module_candidates++;
	if (!top_module_def)
		top_module_def = mod;
}

static struct module_net_def *
find_port_by_name(mod, portname)
	struct module_def *mod;
	register char *portname;
{
	register struct module_net_def *n;

	for (n = mod->nets; n; n = n->next)
		if (!strcmp(n->name, portname))
			break;
	if (n && n->is_port)
		return(n);
	else
		return(0);
}

static void
handle_downport_range(mup, ins, conn)
	struct module_def *mup;
	struct module_def_subinst *ins;
	register struct connect_entry *conn;
{
	register struct module_net_def *port;

	port = conn->down_portdef;
	if (!port->is_bus) {
		fprintf(stderr,
"module %s line %d: mod %s port %s is not a bus, range spec is meaningless\n",
			mup->name, conn->src_lineno, ins->submod_name,
			port->name);
		exit(1);
	}
	if (port->bus_msb > port->bus_lsb) {
		if (conn->down_range_msb < conn->down_range_lsb) {
error_reversed:		fprintf(stderr,
			"module %s line %d: reversed range on bus port %s\n",
				mup->name, conn->src_lineno, port->name);
			exit(1);
		}
		if (conn->down_range_msb > port->bus_msb) {
error_outofrange:	fprintf(stderr,
	"module %s line %d: subrange on bus port %s exceeds full bus range\n",
				mup->name, conn->src_lineno, port->name);
			exit(1);
		}
		if (conn->down_range_lsb < port->bus_lsb)
			goto error_outofrange;
		conn->down_offset = port->bus_msb - conn->down_range_msb;
		conn->down_width = conn->down_range_msb - conn->down_range_lsb
					+ 1;
	} else {
		if (conn->down_range_msb > conn->down_range_lsb)
			goto error_reversed;
		if (conn->down_range_msb < port->bus_msb)
			goto error_outofrange;
		if (conn->down_range_lsb > port->bus_lsb)
			goto error_outofrange;
		conn->down_offset = conn->down_range_msb - port->bus_msb;
		conn->down_width = conn->down_range_lsb - conn->down_range_msb
					+ 1;
	}
}

static void
connect_by_name(mup, ins)
	struct module_def *mup;
	register struct module_def_subinst *ins;
{
	register struct connect_entry *conn;
	register struct module_net_def *port;

	for (conn = ins->connections; conn; conn = conn->next) {
		port = find_port_by_name(ins->submod_def, conn->down_portname);
		if (!port) {
			fprintf(stderr,
	"instance %s in module %s: lower module %s has no port named \"%s\"\n",
				ins->inst_name, mup->name,
				ins->submod_name, conn->down_portname);
			exit(1);
		}
		conn->down_portdef = port;
		if (conn->down_range_given)
			handle_downport_range(mup, ins, conn);
		else {
			conn->down_offset = 0;
			conn->down_width = port->bus_width;
		}
	}
}

static void
connect_by_order(mup, ins)
	struct module_def *mup;
	register struct module_def_subinst *ins;
{
	struct module_def *mdown;
	register struct module_net_def *port;
	register struct connect_entry *conn;

	mdown = ins->submod_def;
	if (ins->connect_by_order != mdown->nports) {
		fprintf(stderr,
"instance %s in module %s: connect by order port count mismatch (%d ports, %d connections)\n",
			ins->inst_name, mup->name, mdown->nports,
			ins->connect_by_order);
		exit(1);
	}
	for (conn = ins->connections, port = mdown->nets; conn;
	     conn = conn->next, port = port->next) {
		conn->down_portdef = port;
		conn->down_offset = 0;
		conn->down_width = port->bus_width;
	}
}

static void
check_width_match(mup, ins)
	struct module_def *mup;
	struct module_def_subinst *ins;
{
	register struct connect_entry *conn;

	for (conn = ins->connections; conn; conn = conn->next)
		if (conn->up_netdef && conn->up_width != conn->down_width) {
			fprintf(stderr,
		"module %s line %d: width mismatch on connection to port %s\n",
				mup->name, conn->src_lineno,
				conn->down_portdef->name);
			exit(1);
		}
}

static void
linkpass_process_inst(mup, ins)
	struct module_def *mup;
	register struct module_def_subinst *ins;
{
	register struct module_def *mdown;

	mdown = find_module_by_name(ins->submod_name);
	if (!mdown) {
		fprintf(stderr,
		    "instance %s in module %s: lower module \"%s\" not found\n",
			ins->inst_name, mup->name, ins->submod_name);
		exit(1);
	}
	ins->submod_def = mdown;
	if (ins->connect_by_order)
		connect_by_order(mup, ins);
	else
		connect_by_name(mup, ins);
	check_width_match(mup, ins);
}

static void
linkpass_process_mod(mod)
	struct module_def *mod;
{
	register struct module_def_subinst *ins;

	for (ins = mod->subinst; ins; ins = ins->next)
		linkpass_process_inst(mod, ins);
}

link_pass()
{
	register struct module_def *m;

	for (m = glob_module_list; m; m = m->next) {
		if (m->is_primitive)
			continue;
		if (!m->nports)
			toplevel_candidate(m);
		linkpass_process_mod(m);
	}
}