view ueda/sverp/vparse.c @ 139:bf188727e606

donl-rename-parts reader: no tEDAx-style escapes
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 07 Sep 2020 04:25:11 +0000
parents 7b4f78fcca08
children
line wrap: on
line source

/*
 * Parser for the minimal subset of Verilog we grok
 */

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

extern struct module_def *glob_module_list;

extern char *parser_filename;
extern FILE *parser_readF;
extern int parser_lineno;
extern char parser_read_word[];
extern int parser_read_number;
extern int pushback_token;

static struct module_def *curmod;
static struct module_def_subinst *subinst;
static struct connect_entry *curconn, **subinst_nextconn;

/* internal (to the parser) structure for passing range info */
struct range_info {
	int	range_given;
	int	range_msb;
	int	range_lsb;
};

static int
capture_range(spec)
	struct range_info *spec;
{
	register int t;

	t = get_token();
	if (t != '[') {
		pushback_token = t;
		bzero(spec, sizeof(struct range_info));
		return(0);
	}
	spec->range_given = 1;
	t = get_token();
	if (t != NUMBER)
		parse_error("expected number after '['");
	spec->range_msb = parser_read_number;
	t = get_token();
	if (t == ']') {
		spec->range_lsb = spec->range_msb;
		return(1);
	}
	if (t != ':')
		parse_error("expected ':' or ']' after MSB number");
	t = get_token();
	if (t != NUMBER)
		parse_error("expected number after ':'");
	spec->range_lsb = parser_read_number;
	t = get_token();
	if (t != ']')
		parse_error("expected ']' after LSB number");
	return(1);
}

static void
create_module()
{
	register struct module_def *mod, **modp;
	char *buf;

	for (modp = &glob_module_list; mod = *modp; modp = &mod->next) {
		if (!strcmp(mod->name, parser_read_word))
			parse_error("duplicate module name");
	}
	buf = malloc(sizeof(struct module_def) + strlen(parser_read_word) + 1);
	if (!buf) {
		perror("malloc");
		exit(1);
	}
	mod = (struct module_def *) buf;
	bzero(mod, sizeof(struct module_def));
	buf += sizeof(struct module_def);
	strcpy(buf, parser_read_word);
	mod->name = buf;
	*modp = mod;
	curmod = mod;
}

static void
add_port()
{
	register struct module_net_def *n, **np;
	char *buf;

	for (np = &curmod->nets; n = *np; np = &n->next) {
		if (!strcmp(n->name, parser_read_word))
			parse_error("duplicate port name in module line");
	}
	buf = malloc(sizeof(struct module_net_def) + strlen(parser_read_word)
			+ 1);
	if (!buf) {
		perror("malloc");
		exit(1);
	}
	n = (struct module_net_def *) buf;
	bzero(n, sizeof(struct module_net_def));
	buf += sizeof(struct module_net_def);
	strcpy(buf, parser_read_word);
	n->name = buf;
	n->is_port = 1;
	*np = n;
	curmod->nports++;
}

static void
process_port_list()
{
	register int t;

	for (;;) {
		t = get_token();
		if (t == ')')
			return;
		if (t != WORD)
			parse_error("expected port name in module line");
		add_port();
		t = get_token();
		if (t == ')')
			return;
		if (t != ',')
			parse_error("expected ',' or ')' in module line");
	}
}

static void
netdef_set_businfo(n, range)
	register struct module_net_def *n;
	register struct range_info *range;
{
	if (!range->range_given) {
		n->is_bus = 0;
		n->bus_width = 1;
		return;
	}
	if (range->range_msb == range->range_lsb)
		parse_error("bus declaration with MSB==LSB is meaningless");
	n->is_bus = 1;
	n->bus_msb = range->range_msb;
	n->bus_lsb = range->range_lsb;
	if (n->bus_msb > n->bus_lsb)
		n->bus_width = n->bus_msb - n->bus_lsb + 1;
	else
		n->bus_width = n->bus_lsb - n->bus_msb + 1;
}

static void
complete_port_def(range)
	struct range_info *range;
{
	register struct module_net_def *n;

	for (n = curmod->nets; n; n = n->next)
		if (!strcmp(n->name, parser_read_word))
			break;
	if (!n) {
		fprintf(stderr, "%s line %d: no port named %s\n",
			parser_filename, parser_lineno, parser_read_word);
		exit(1);
	}
	if (n->def_complete) {
		fprintf(stderr,
			"%s line %d: inout decl for %s is a redefinition\n",
			parser_filename, parser_lineno, parser_read_word);
		exit(1);
	}
	netdef_set_businfo(n, range);
	n->def_complete = 1;
}

static void
add_wire(range)
	struct range_info *range;
{
	register struct module_net_def *n, **np;
	char *buf;

	for (np = &curmod->nets; n = *np; np = &n->next)
		if (!strcmp(n->name, parser_read_word)) {
			fprintf(stderr,
				"%s line %d: wire %s is a redefinition\n",
				parser_filename, parser_lineno,
				parser_read_word);
			exit(1);
		}
	buf = malloc(sizeof(struct module_net_def) + strlen(parser_read_word)
			+ 1);
	if (!buf) {
		perror("malloc");
		exit(1);
	}
	n = (struct module_net_def *) buf;
	bzero(n, sizeof(struct module_net_def));
	buf += sizeof(struct module_net_def);
	strcpy(buf, parser_read_word);
	n->name = buf;
	*np = n;
	netdef_set_businfo(n, range);
	n->def_complete = 1;
}

static void
process_netdef_line(is_port)
{
	struct range_info range;
	register int t;

	capture_range(&range);
	for (;;) {
		t = get_token();
		if (t == ';')
			return;
		if (t != WORD)
			parse_error("expected ident on wire def line");
		if (is_port)
			complete_port_def(&range);
		else
			add_wire(&range);
		t = get_token();
		if (t == ';')
			return;
		if (t != ',')
			parse_error("expected ',' or ';' in wire def line");
	}
}

static void
create_subinst(submod_name, inst_name)
	char *submod_name, *inst_name;
{
	register struct module_def_subinst *s, **sp;
	char *buf;

	for (sp = &curmod->subinst; s = *sp; sp = &s->next)
		if (!strcmp(s->inst_name, inst_name))
			parse_error("duplicate subinstance name");
	buf = malloc(sizeof(struct module_def_subinst) + strlen(submod_name) +
			strlen(inst_name) + 2);
	if (!buf) {
		perror("malloc");
		exit(1);
	}
	s = (struct module_def_subinst *) buf;
	bzero(s, sizeof(struct module_def_subinst));
	buf += sizeof(struct module_def_subinst);
	strcpy(buf, submod_name);
	s->submod_name = buf;
	buf += strlen(submod_name) + 1;
	strcpy(buf, inst_name);
	s->inst_name = buf;
	*sp = s;
	subinst = s;
	subinst_nextconn = &s->connections;
}

static void
alloc_connect_entry(downport)
	char *downport;
{
	struct connect_entry *s;
	int len;
	char *buf;

	if (downport)
		len = sizeof(struct connect_entry) + strlen(downport) + 1;
	else
		len = sizeof(struct connect_entry);
	s = (struct connect_entry *) malloc(len);
	if (!s) {
		perror("malloc");
		exit(1);
	}
	bzero(s, sizeof(struct connect_entry));
	if (downport) {
		buf = (char *)(s + 1);
		strcpy(buf, downport);
		s->down_portname = buf;
	}
	curconn = s;
	*subinst_nextconn = s;
	subinst_nextconn = &s->next;
}

static void
connect_upper()
{
	struct range_info range;
	register struct module_net_def *n;

	for (n = curmod->nets; n; n = n->next)
		if (!strcmp(n->name, parser_read_word))
			break;
	if (!n) {
		fprintf(stderr, "%s line %d: no net named %s\n",
			parser_filename, parser_lineno, parser_read_word);
		exit(1);
	}
	curconn->up_netdef = n;
	if (!capture_range(&range)) {
		curconn->up_offset = 0;
		curconn->up_width = n->bus_width;
		return;
	}
	if (!n->is_bus) {
		fprintf(stderr,
		"%s line %d: net %s is not a bus, range spec is meaningless\n",
			parser_filename, parser_lineno, n->name);
		exit(1);
	}
	if (n->bus_msb > n->bus_lsb) {
		if (range.range_msb < range.range_lsb) {
error_reversed:		fprintf(stderr,
				"%s line %d: reversed range on bus %s\n",
				parser_filename, parser_lineno, n->name);
			exit(1);
		}
		if (range.range_msb > n->bus_msb) {
error_outofrange:	fprintf(stderr,
	"%s line %d: subrange of bus %s to connect exceeds full bus range\n",
				parser_filename, parser_lineno, n->name);
			exit(1);
		}
		if (range.range_lsb < n->bus_lsb)
			goto error_outofrange;
		curconn->up_offset = n->bus_msb - range.range_msb;
		curconn->up_width = range.range_msb - range.range_lsb + 1;
	} else {
		if (range.range_msb > range.range_lsb)
			goto error_reversed;
		if (range.range_msb < n->bus_msb)
			goto error_outofrange;
		if (range.range_lsb > n->bus_lsb)
			goto error_outofrange;
		curconn->up_offset = range.range_msb - n->bus_msb;
		curconn->up_width = range.range_lsb - range.range_msb + 1;
	}
}

static void
connect_by_name()
{
	struct range_info range;
	register int t;

	for (;;) {
		t = get_token();
		if (t == ')')
			return;
		if (t != '.')
			parse_error("expected '.' for downward port");
		t = get_token();
		if (t != WORD)
			parse_error("expected downward port name after '.'");
		alloc_connect_entry(parser_read_word);
		curconn->src_lineno = parser_lineno;
		if (capture_range(&range)) {
			curconn->down_range_given = 1;
			curconn->down_range_msb = range.range_msb;
			curconn->down_range_lsb = range.range_lsb;
		}
		t = get_token();
		if (t != '(')
			parse_error("expected '(' for connect-by-name");
		t = get_token();
		if (t != ')') {
			if (t != WORD)
				parse_error(
				"expected in-module net name to connect");
			connect_upper();
			t = get_token();
			if (t != ')')
				parse_error("expected ')' for connect-by-name");
		}
		t = get_token();
		if (t == ')')
			return;
		if (t != ',')
			parse_error("expected ',' or ')' in connection list");
	}
}

static void
connect_by_order()
{
	register int t;

	for (;;) {
		t = get_token();
		if (t == ')')
			return;
		if (t != WORD)
			parse_error("expected in-module net name to connect");
		alloc_connect_entry(0);
		curconn->src_lineno = parser_lineno;
		connect_upper();
		subinst->connect_by_order++;
		t = get_token();
		if (t == ')')
			return;
		if (t != ',')
			parse_error("expected ',' or ')' in connection list");
	}
}

static void
process_subinst()
{
	char submod_name[MAXWORD+1];
	register int t;

	strcpy(submod_name, parser_read_word);
	t = get_token();
	if (t != WORD)
		parse_error("expected instance name after submodule name");
	create_subinst(submod_name, parser_read_word);
	t = get_token();
	if (t != '(')
		parse_error("expected '(' after <module> <instance>");
	t = get_token();
	pushback_token = t;
	if (t == '.')
		connect_by_name();
	else
		connect_by_order();
	t = get_token();
	if (t != ';')
		parse_error("expected ';' at the end of instantiation");
}

static void
preen_module_nets()
{
	register struct module_net_def *n;
	register int idx;

	idx = 0;
	for (n = curmod->nets; n; n = n->next) {
		if (!n->def_complete) {
			fprintf(stderr,
		"error: definition of port %s in module %s is incomplete\n",
				n->name, curmod->name);
			exit(1);
		}
		n->array_index = idx;
		idx += n->bus_width;
		if (n->is_port)
			curmod->nwires_ports += n->bus_width;
		else
			curmod->nwires_internal += n->bus_width;
	}
}

read_verilog_file(filename_arg)
	char *filename_arg;
{
	register int t;

	parser_filename = filename_arg;
	parser_readF = fopen(parser_filename, "r");
	if (!parser_readF) {
		perror(parser_filename);
		exit(1);
	}
	parser_lineno = 1;
	pushback_token = 0;

	t = get_token();
	if (t != WORD || strcmp(parser_read_word, "module"))
		parse_error("first token is expected to be \"module\"");
	t = get_token();
	if (t != WORD)
		parse_error("module name expected after \"module\"");
	create_module();
	t = get_token();
	if (t != '(')
		parse_error("expected '(' after module <modname>");
	process_port_list();
	t = get_token();
	if (t != ';')
		parse_error("expected ';' at the end of module line");

	for (;;) {
		t = get_token();
		if (t != WORD)
			parse_error(
			"expected word token at the beginning of module item");
		if (!strcmp(parser_read_word, "endmodule"))
			break;
		if (!strcmp(parser_read_word, "input") ||
		    !strcmp(parser_read_word, "output") ||
		    !strcmp(parser_read_word, "inout"))
			process_netdef_line(1);
		else if (!strcmp(parser_read_word, "wire"))
			process_netdef_line(0);
		else
			process_subinst();
	}

	fclose(parser_readF);
	preen_module_nets();
	return(0);
}