2001-03-11 01:29:38 +01:00
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2001 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
|
|
|
|
|
* General Public License as published by the Free Software
|
|
|
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
|
|
|
* any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
|
|
|
|
*/
|
|
|
|
|
#if !defined(WINNT)
|
2001-06-30 23:07:26 +02:00
|
|
|
#ident "$Id: compile.cc,v 1.82 2001/06/30 21:07:26 steve Exp $"
|
2001-03-11 01:29:38 +01:00
|
|
|
#endif
|
|
|
|
|
|
2001-06-05 05:05:41 +02:00
|
|
|
# include "arith.h"
|
2001-05-31 06:12:43 +02:00
|
|
|
# include "bufif.h"
|
2001-03-11 01:29:38 +01:00
|
|
|
# include "compile.h"
|
|
|
|
|
# include "functor.h"
|
2001-05-09 04:53:25 +02:00
|
|
|
# include "resolv.h"
|
2001-04-24 04:23:58 +02:00
|
|
|
# include "udp.h"
|
2001-05-01 03:09:39 +02:00
|
|
|
# include "memory.h"
|
2001-03-11 01:29:38 +01:00
|
|
|
# include "symbols.h"
|
|
|
|
|
# include "codes.h"
|
|
|
|
|
# include "schedule.h"
|
2001-03-16 02:44:34 +01:00
|
|
|
# include "vpi_priv.h"
|
2001-03-11 01:29:38 +01:00
|
|
|
# include "vthread.h"
|
|
|
|
|
# include "parse_misc.h"
|
|
|
|
|
# include <malloc.h>
|
|
|
|
|
# include <stdlib.h>
|
2001-03-28 19:24:32 +02:00
|
|
|
# include <string.h>
|
2001-03-11 01:29:38 +01:00
|
|
|
# include <assert.h>
|
|
|
|
|
|
2001-05-22 04:14:47 +02:00
|
|
|
#ifdef __MINGW32__
|
|
|
|
|
#include <windows.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2001-03-22 23:38:13 +01:00
|
|
|
unsigned compile_errors = 0;
|
2001-03-11 01:29:38 +01:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The opcode table lists all the code mnemonics, along with their
|
|
|
|
|
* opcode and operand types. The table is written sorted by mnemonic
|
|
|
|
|
* so that it can be searched by binary search. The opcode_compare
|
|
|
|
|
* function is a helper function for that lookup.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
enum operand_e {
|
|
|
|
|
/* Place holder for unused operand */
|
|
|
|
|
OA_NONE,
|
2001-03-12 00:06:49 +01:00
|
|
|
/* The operand is a number, an immediate unsigned integer */
|
2001-03-11 01:29:38 +01:00
|
|
|
OA_NUMBER,
|
|
|
|
|
/* The operand is a thread bit index */
|
2001-03-12 00:06:49 +01:00
|
|
|
OA_BIT1,
|
|
|
|
|
OA_BIT2,
|
2001-03-11 01:29:38 +01:00
|
|
|
/* The operand is a pointer to code space */
|
|
|
|
|
OA_CODE_PTR,
|
|
|
|
|
/* The operand is a variable or net pointer */
|
2001-05-01 03:09:39 +02:00
|
|
|
OA_FUNC_PTR,
|
2001-05-03 01:16:50 +02:00
|
|
|
/* The operand is a pointer to a memory */
|
|
|
|
|
OA_MEM_PTR,
|
2001-03-11 01:29:38 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct opcode_table_s {
|
|
|
|
|
const char*mnemonic;
|
|
|
|
|
vvp_code_fun opcode;
|
|
|
|
|
|
|
|
|
|
unsigned argc;
|
|
|
|
|
enum operand_e argt[OPERAND_MAX];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const static struct opcode_table_s opcode_table[] = {
|
2001-03-31 03:59:58 +02:00
|
|
|
{ "%add", of_ADD, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
|
2001-04-01 08:12:13 +02:00
|
|
|
{ "%and", of_AND, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
|
2001-06-18 03:09:32 +02:00
|
|
|
{ "%and/r", of_ANDR, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
|
2001-03-22 06:08:00 +01:00
|
|
|
{ "%assign", of_ASSIGN, 3, {OA_FUNC_PTR, OA_BIT1, OA_BIT2} },
|
2001-05-03 01:16:50 +02:00
|
|
|
{ "%assign/m",of_ASSIGN_MEM,3,{OA_MEM_PTR,OA_BIT1, OA_BIT2} },
|
2001-05-06 01:55:46 +02:00
|
|
|
{ "%breakpoint", of_BREAKPOINT, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
2001-04-05 03:12:27 +02:00
|
|
|
{ "%cmp/s", of_CMPS, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
|
2001-03-22 06:08:00 +01:00
|
|
|
{ "%cmp/u", of_CMPU, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
|
2001-04-01 06:34:28 +02:00
|
|
|
{ "%cmp/x", of_CMPX, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
|
|
|
|
|
{ "%cmp/z", of_CMPZ, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
|
2001-03-22 06:08:00 +01:00
|
|
|
{ "%delay", of_DELAY, 1, {OA_NUMBER, OA_NONE, OA_NONE} },
|
|
|
|
|
{ "%end", of_END, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
|
|
|
|
{ "%inv", of_INV, 2, {OA_BIT1, OA_BIT2, OA_NONE} },
|
2001-05-01 03:09:39 +02:00
|
|
|
{ "%ix/add", of_IX_ADD, 2, {OA_BIT1, OA_NUMBER, OA_NONE} },
|
2001-05-10 02:26:53 +02:00
|
|
|
{ "%ix/get", of_IX_GET, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
|
2001-05-01 07:00:02 +02:00
|
|
|
{ "%ix/load",of_IX_LOAD,2, {OA_BIT1, OA_NUMBER, OA_NONE} },
|
2001-05-01 03:09:39 +02:00
|
|
|
{ "%ix/mul", of_IX_MUL, 2, {OA_BIT1, OA_NUMBER, OA_NONE} },
|
2001-05-03 01:16:50 +02:00
|
|
|
{ "%ix/sub", of_IX_SUB, 2, {OA_BIT1, OA_NUMBER, OA_NONE} },
|
2001-03-22 06:08:00 +01:00
|
|
|
{ "%jmp", of_JMP, 1, {OA_CODE_PTR, OA_NONE, OA_NONE} },
|
|
|
|
|
{ "%jmp/0", of_JMP0, 2, {OA_CODE_PTR, OA_BIT1, OA_NONE} },
|
2001-03-25 05:54:26 +02:00
|
|
|
{ "%jmp/0xz",of_JMP0XZ, 2, {OA_CODE_PTR, OA_BIT1, OA_NONE} },
|
2001-03-31 19:36:02 +02:00
|
|
|
{ "%jmp/1", of_JMP1, 2, {OA_CODE_PTR, OA_BIT1, OA_NONE} },
|
2001-03-30 06:55:22 +02:00
|
|
|
{ "%join", of_JOIN, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
2001-03-22 06:08:00 +01:00
|
|
|
{ "%load", of_LOAD, 2, {OA_BIT1, OA_FUNC_PTR, OA_NONE} },
|
2001-05-03 01:16:50 +02:00
|
|
|
{ "%load/m", of_LOAD_MEM,2, {OA_BIT1, OA_MEM_PTR, OA_NONE} },
|
2001-05-24 06:20:10 +02:00
|
|
|
{ "%mod", of_MOD, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
|
2001-03-22 06:08:00 +01:00
|
|
|
{ "%mov", of_MOV, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
|
2001-06-17 01:45:05 +02:00
|
|
|
{ "%mul", of_MUL, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
|
2001-06-18 03:09:32 +02:00
|
|
|
{ "%nand/r", of_NANDR, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
|
2001-04-01 08:12:13 +02:00
|
|
|
{ "%noop", of_NOOP, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
2001-04-02 00:25:33 +02:00
|
|
|
{ "%nor/r", of_NORR, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
|
2001-04-01 09:22:08 +02:00
|
|
|
{ "%or", of_OR, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
|
2001-06-18 03:09:32 +02:00
|
|
|
{ "%or/r", of_ORR, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
|
2001-03-22 06:08:00 +01:00
|
|
|
{ "%set", of_SET, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} },
|
2001-05-03 01:16:50 +02:00
|
|
|
{ "%set/m", of_SET_MEM,2, {OA_MEM_PTR, OA_BIT1, OA_NONE} },
|
2001-06-23 20:26:26 +02:00
|
|
|
{ "%shiftl/i0", of_SHIFTL_I0, 2, {OA_BIT1,OA_NUMBER, OA_NONE} },
|
2001-06-30 23:07:26 +02:00
|
|
|
{ "%shiftr/i0", of_SHIFTR_I0, 2, {OA_BIT1,OA_NUMBER, OA_NONE} },
|
2001-05-02 03:57:25 +02:00
|
|
|
{ "%sub", of_SUB, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
|
2001-03-26 06:00:39 +02:00
|
|
|
{ "%wait", of_WAIT, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} },
|
2001-04-15 06:07:56 +02:00
|
|
|
{ "%xnor", of_XNOR, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
|
2001-06-18 03:09:32 +02:00
|
|
|
{ "%xnor/r", of_XNORR, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
|
2001-04-15 18:37:48 +02:00
|
|
|
{ "%xor", of_XOR, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
|
2001-06-18 03:09:32 +02:00
|
|
|
{ "%xor/r", of_XORR, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
|
2001-03-11 01:29:38 +01:00
|
|
|
{ 0, of_NOOP, 0, {OA_NONE, OA_NONE, OA_NONE} }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static unsigned opcode_count = 0;
|
2001-05-01 03:09:39 +02:00
|
|
|
//static const unsigned opcode_count
|
|
|
|
|
// = sizeof(opcode_table)/sizeof(*opcode_table) - 1;
|
|
|
|
|
// No?
|
2001-03-11 01:29:38 +01:00
|
|
|
|
|
|
|
|
static int opcode_compare(const void*k, const void*r)
|
|
|
|
|
{
|
|
|
|
|
const char*kp = (const char*)k;
|
|
|
|
|
const struct opcode_table_s*rp = (const struct opcode_table_s*)r;
|
|
|
|
|
return strcmp(kp, rp->mnemonic);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Keep a symbol table of addresses within code space. Labels on
|
|
|
|
|
* executable opcodes are mapped to their address here.
|
|
|
|
|
*/
|
|
|
|
|
static symbol_table_t sym_codespace = 0;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Keep a symbol table of functors mentioned in the source. This table
|
|
|
|
|
* is used to resolve references as they come.
|
|
|
|
|
*/
|
|
|
|
|
static symbol_table_t sym_functors = 0;
|
|
|
|
|
|
2001-03-21 06:13:03 +01:00
|
|
|
/*
|
|
|
|
|
* VPI objects are indexed during compile time so that they can be
|
|
|
|
|
* linked together as they are created. This symbol table matches
|
|
|
|
|
* labels to vpiHandles.
|
|
|
|
|
*/
|
|
|
|
|
static symbol_table_t sym_vpi = 0;
|
|
|
|
|
|
|
|
|
|
|
2001-03-11 01:29:38 +01:00
|
|
|
/*
|
|
|
|
|
* If a functor parameter makes a forward reference to a functor, then
|
|
|
|
|
* I need to save that reference and resolve it after the functors are
|
|
|
|
|
* created. Use this structure to keep the unresolved references in an
|
|
|
|
|
* unsorted singly linked list.
|
2001-03-25 05:54:26 +02:00
|
|
|
*
|
|
|
|
|
* The postpone_functor_input arranges for a functor input to be
|
|
|
|
|
* resolved and connected at cleanup. This is used if the symbol is
|
|
|
|
|
* defined after its use in a functor. The ptr parameter is the
|
|
|
|
|
* complete vvp_input_t for the input port.
|
2001-03-11 01:29:38 +01:00
|
|
|
*/
|
|
|
|
|
struct resolv_list_s {
|
|
|
|
|
struct resolv_list_s*next;
|
|
|
|
|
vvp_ipoint_t port;
|
2001-03-20 07:16:23 +01:00
|
|
|
|
2001-03-11 01:29:38 +01:00
|
|
|
char*source;
|
2001-03-20 07:16:23 +01:00
|
|
|
unsigned idx;
|
2001-03-11 01:29:38 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct resolv_list_s*resolv_list = 0;
|
|
|
|
|
|
2001-03-25 05:54:26 +02:00
|
|
|
static void postpone_functor_input(vvp_ipoint_t ptr, char*lab, unsigned idx)
|
|
|
|
|
{
|
|
|
|
|
struct resolv_list_s*res = (struct resolv_list_s*)
|
|
|
|
|
calloc(1, sizeof(struct resolv_list_s));
|
|
|
|
|
|
|
|
|
|
res->port = ptr;
|
|
|
|
|
res->source = lab;
|
|
|
|
|
res->idx = idx;
|
|
|
|
|
res->next = resolv_list;
|
|
|
|
|
resolv_list = res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2001-03-22 06:28:41 +01:00
|
|
|
/*
|
2001-06-10 19:12:51 +02:00
|
|
|
* Instructions may make forward references to labels or functors. In
|
|
|
|
|
* this case, the reference is short is a llist or flist (depending on
|
|
|
|
|
* the type) and resolved during cleanup.
|
2001-03-22 06:28:41 +01:00
|
|
|
*/
|
|
|
|
|
struct cresolv_list_s {
|
|
|
|
|
struct cresolv_list_s*next;
|
|
|
|
|
struct vvp_code_s*cp;
|
|
|
|
|
char*lab;
|
2001-06-10 19:12:51 +02:00
|
|
|
unsigned idx;
|
2001-03-22 06:28:41 +01:00
|
|
|
};
|
|
|
|
|
|
2001-06-10 19:12:51 +02:00
|
|
|
static struct cresolv_list_s*cresolv_llist = 0;
|
|
|
|
|
static struct cresolv_list_s*cresolv_flist = 0;
|
2001-03-11 01:29:38 +01:00
|
|
|
|
2001-03-31 21:00:43 +02:00
|
|
|
void compile_vpi_symbol(const char*label, vpiHandle obj)
|
|
|
|
|
{
|
|
|
|
|
symbol_value_t val;
|
|
|
|
|
val.ptr = obj;
|
|
|
|
|
sym_set_value(sym_vpi, label, val);
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-11 01:29:38 +01:00
|
|
|
/*
|
|
|
|
|
* Initialize the compiler by allocation empty symbol tables and
|
|
|
|
|
* initializing the various address spaces.
|
|
|
|
|
*/
|
|
|
|
|
void compile_init(void)
|
|
|
|
|
{
|
2001-03-21 06:13:03 +01:00
|
|
|
sym_vpi = new_symbol_table();
|
2001-03-31 21:00:43 +02:00
|
|
|
compile_vpi_symbol("$time", vpip_sim_time());
|
|
|
|
|
|
2001-03-11 01:29:38 +01:00
|
|
|
sym_functors = new_symbol_table();
|
|
|
|
|
functor_init();
|
2001-03-31 21:00:43 +02:00
|
|
|
|
2001-03-11 01:29:38 +01:00
|
|
|
sym_codespace = new_symbol_table();
|
|
|
|
|
codespace_init();
|
|
|
|
|
|
|
|
|
|
opcode_count = 0;
|
|
|
|
|
while (opcode_table[opcode_count].mnemonic)
|
|
|
|
|
opcode_count += 1;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-23 03:40:22 +01:00
|
|
|
void compile_load_vpi_module(char*name)
|
|
|
|
|
{
|
2001-05-22 04:14:47 +02:00
|
|
|
#ifdef __MINGW32__
|
|
|
|
|
char module_path[4096], *s;
|
|
|
|
|
GetModuleFileName(NULL,module_path,1024);
|
|
|
|
|
/* Get to the end. Search back twice for backslashes */
|
|
|
|
|
s = module_path + strlen(module_path);
|
|
|
|
|
while (*s != '\\') s--; s--;
|
|
|
|
|
while (*s != '\\') s--;
|
|
|
|
|
strcpy(s,"\\lib\\ivl");
|
|
|
|
|
#endif
|
2001-03-23 03:40:22 +01:00
|
|
|
vpip_load_module(name, module_path);
|
|
|
|
|
free(name);
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-28 22:24:03 +02:00
|
|
|
/*
|
|
|
|
|
* Add a functor to the symbol table
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static void define_functor_symbol(char*label, vvp_ipoint_t fdx)
|
|
|
|
|
{
|
|
|
|
|
symbol_value_t val;
|
|
|
|
|
val.num = fdx;
|
|
|
|
|
sym_set_value(sym_functors, label, val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Run through the arguments looking for the functors that are
|
|
|
|
|
* connected to my input ports. For each source functor that I
|
|
|
|
|
* find, connect the output of that functor to the indexed
|
|
|
|
|
* input by inserting myself (complete with the port number in
|
|
|
|
|
* the vvp_ipoint_t) into the list that the source heads.
|
|
|
|
|
*
|
|
|
|
|
* If the source functor is not declared yet, then don't do
|
2001-04-30 05:53:19 +02:00
|
|
|
* the link yet. Save the reference to be resolved later.
|
|
|
|
|
*
|
|
|
|
|
* If the source is a constant value, then set the ival of the functor
|
|
|
|
|
* and skip the symbol lookup.
|
|
|
|
|
*/
|
2001-04-28 22:24:03 +02:00
|
|
|
|
|
|
|
|
static void inputs_connect(vvp_ipoint_t fdx, unsigned argc, struct symb_s*argv)
|
|
|
|
|
{
|
|
|
|
|
|
2001-04-30 05:53:19 +02:00
|
|
|
for (unsigned idx = 0; idx < argc; idx += 1) {
|
|
|
|
|
|
2001-05-01 04:18:15 +02:00
|
|
|
/* Find the functor for this input. This assumes that
|
|
|
|
|
wide (more then 4 inputs) gates are consecutive
|
|
|
|
|
functors. */
|
2001-04-30 05:53:19 +02:00
|
|
|
vvp_ipoint_t ifdx = ipoint_input_index(fdx, idx);
|
|
|
|
|
functor_t iobj = functor_index(ifdx);
|
|
|
|
|
|
|
|
|
|
if (strcmp(argv[idx].text, "C<0>") == 0) {
|
|
|
|
|
free(argv[idx].text);
|
2001-05-30 05:02:35 +02:00
|
|
|
functor_put_input(iobj, idx, 0, St0);
|
2001-05-12 22:38:06 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp(argv[idx].text, "C<pu0>") == 0) {
|
|
|
|
|
free(argv[idx].text);
|
2001-05-30 05:02:35 +02:00
|
|
|
functor_put_input(iobj, idx, 0, Pu0);
|
2001-04-30 05:53:19 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp(argv[idx].text, "C<1>") == 0) {
|
|
|
|
|
free(argv[idx].text);
|
2001-05-30 05:02:35 +02:00
|
|
|
functor_put_input(iobj, idx, 1, St1);
|
2001-05-12 22:38:06 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp(argv[idx].text, "C<pu1>") == 0) {
|
|
|
|
|
free(argv[idx].text);
|
2001-05-30 05:02:35 +02:00
|
|
|
functor_put_input(iobj, idx, 1, Pu1);
|
2001-04-30 05:53:19 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2001-05-09 04:53:25 +02:00
|
|
|
if (strcmp(argv[idx].text, "C<x>") == 0) {
|
|
|
|
|
free(argv[idx].text);
|
2001-05-30 05:02:35 +02:00
|
|
|
functor_put_input(iobj, idx, 2, StX);
|
2001-05-09 04:53:25 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp(argv[idx].text, "C<z>") == 0) {
|
|
|
|
|
free(argv[idx].text);
|
2001-05-30 05:02:35 +02:00
|
|
|
functor_put_input(iobj, idx, 3, HiZ);
|
2001-05-09 04:53:25 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-30 05:53:19 +02:00
|
|
|
symbol_value_t val = sym_get_value(sym_functors, argv[idx].text);
|
|
|
|
|
vvp_ipoint_t tmp = val.num;
|
2001-04-28 22:24:03 +02:00
|
|
|
|
2001-04-30 05:53:19 +02:00
|
|
|
if (tmp) {
|
|
|
|
|
tmp = ipoint_index(tmp, argv[idx].idx);
|
|
|
|
|
functor_t fport = functor_index(tmp);
|
|
|
|
|
iobj->port[ipoint_port(ifdx)] = fport->out;
|
|
|
|
|
fport->out = ifdx;
|
|
|
|
|
free(argv[idx].text);
|
|
|
|
|
} else {
|
|
|
|
|
postpone_functor_input(ifdx, argv[idx].text, argv[idx].idx);
|
|
|
|
|
}
|
|
|
|
|
}
|
2001-04-28 22:24:03 +02:00
|
|
|
}
|
|
|
|
|
|
2001-03-11 01:29:38 +01:00
|
|
|
/*
|
|
|
|
|
* The parser calls this function to create a functor. I allocate a
|
|
|
|
|
* functor, and map the name to the vvp_ipoint_t address for the
|
|
|
|
|
* functor. Also resolve the inputs to the functor.
|
|
|
|
|
*/
|
2001-05-02 06:05:16 +02:00
|
|
|
void compile_functor(char*label, char*type, unsigned argc, struct symb_s*argv)
|
2001-03-11 01:29:38 +01:00
|
|
|
{
|
2001-03-20 07:16:23 +01:00
|
|
|
vvp_ipoint_t fdx = functor_allocate(1);
|
2001-03-11 01:29:38 +01:00
|
|
|
functor_t obj = functor_index(fdx);
|
2001-03-18 01:37:55 +01:00
|
|
|
|
2001-04-28 22:24:03 +02:00
|
|
|
define_functor_symbol(label, fdx);
|
2001-03-11 01:29:38 +01:00
|
|
|
|
|
|
|
|
assert(argc <= 4);
|
|
|
|
|
|
2001-05-22 06:08:16 +02:00
|
|
|
obj->ival = 0xaa;
|
2001-03-11 23:42:11 +01:00
|
|
|
obj->oval = 2;
|
2001-05-09 01:32:26 +02:00
|
|
|
obj->odrive0 = 6;
|
|
|
|
|
obj->odrive1 = 6;
|
2001-03-26 06:00:39 +02:00
|
|
|
obj->mode = 0;
|
2001-05-09 04:53:25 +02:00
|
|
|
#if defined(WITH_DEBUG)
|
2001-05-09 01:32:26 +02:00
|
|
|
obj->breakpoint = 0;
|
|
|
|
|
#endif
|
2001-03-11 23:42:11 +01:00
|
|
|
|
|
|
|
|
if (strcmp(type, "OR") == 0) {
|
|
|
|
|
obj->table = ft_OR;
|
|
|
|
|
|
|
|
|
|
} else if (strcmp(type, "AND") == 0) {
|
|
|
|
|
obj->table = ft_AND;
|
|
|
|
|
|
2001-04-01 23:31:46 +02:00
|
|
|
} else if (strcmp(type, "BUF") == 0) {
|
|
|
|
|
obj->table = ft_BUF;
|
|
|
|
|
|
2001-04-30 01:13:33 +02:00
|
|
|
} else if (strcmp(type, "BUFIF0") == 0) {
|
2001-05-31 06:12:43 +02:00
|
|
|
obj->obj = new vvp_bufif0_s;
|
|
|
|
|
obj->mode = M42;
|
2001-04-30 01:13:33 +02:00
|
|
|
|
|
|
|
|
} else if (strcmp(type, "BUFIF1") == 0) {
|
2001-05-31 06:12:43 +02:00
|
|
|
obj->obj = new vvp_bufif1_s;
|
|
|
|
|
obj->mode = M42;
|
2001-04-30 01:13:33 +02:00
|
|
|
|
2001-04-26 07:12:02 +02:00
|
|
|
} else if (strcmp(type, "MUXZ") == 0) {
|
|
|
|
|
obj->table = ft_MUXZ;
|
|
|
|
|
|
2001-06-19 05:01:10 +02:00
|
|
|
} else if (strcmp(type, "EEQ") == 0) {
|
|
|
|
|
obj->table = ft_EEQ;
|
|
|
|
|
|
2001-04-21 04:04:01 +02:00
|
|
|
} else if (strcmp(type, "NAND") == 0) {
|
|
|
|
|
obj->table = ft_NAND;
|
|
|
|
|
|
2001-03-25 21:38:23 +02:00
|
|
|
} else if (strcmp(type, "NOR") == 0) {
|
|
|
|
|
obj->table = ft_NOR;
|
|
|
|
|
|
|
|
|
|
} else if (strcmp(type, "NOT") == 0) {
|
|
|
|
|
obj->table = ft_NOT;
|
|
|
|
|
|
2001-04-21 04:04:01 +02:00
|
|
|
} else if (strcmp(type, "XNOR") == 0) {
|
|
|
|
|
obj->table = ft_XNOR;
|
|
|
|
|
|
2001-04-15 18:37:48 +02:00
|
|
|
} else if (strcmp(type, "XOR") == 0) {
|
|
|
|
|
obj->table = ft_XOR;
|
|
|
|
|
|
2001-03-11 23:42:11 +01:00
|
|
|
} else {
|
|
|
|
|
yyerror("invalid functor type.");
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-30 05:53:19 +02:00
|
|
|
/* Connect the inputs of this functor to the given symbols. If
|
|
|
|
|
there are C<X> inputs, set the ival appropriately. */
|
|
|
|
|
inputs_connect(fdx, argc, argv);
|
2001-06-05 05:05:41 +02:00
|
|
|
free(argv);
|
2001-04-30 05:53:19 +02:00
|
|
|
|
|
|
|
|
/* Recalculate the output based on the given ival. if the oval
|
|
|
|
|
turns out to *not* be x, then schedule the functor so that
|
|
|
|
|
the value gets propagated. */
|
|
|
|
|
unsigned char out = obj->table[obj->ival >> 2];
|
|
|
|
|
obj->oval = 3 & (out >> 2 * (obj->ival&3));
|
|
|
|
|
if (obj->oval != 2)
|
|
|
|
|
schedule_functor(fdx, 0);
|
|
|
|
|
|
2001-03-11 01:29:38 +01:00
|
|
|
free(label);
|
|
|
|
|
free(type);
|
|
|
|
|
}
|
|
|
|
|
|
2001-06-07 05:09:03 +02:00
|
|
|
static void connect_arith_inputs(vvp_ipoint_t fdx, long wid,
|
|
|
|
|
vvp_arith_* arith,
|
|
|
|
|
unsigned argc, struct symb_s*argv)
|
|
|
|
|
{
|
|
|
|
|
unsigned opcount = argc / wid;
|
|
|
|
|
|
|
|
|
|
struct symb_s tmp_argv[4];
|
|
|
|
|
for (unsigned idx = 0 ; idx < wid ; idx += 1) {
|
|
|
|
|
vvp_ipoint_t ptr = ipoint_index(fdx,idx);
|
|
|
|
|
functor_t obj = functor_index(ptr);
|
|
|
|
|
|
|
|
|
|
obj->ival = 0xaa >> 2*(4 - opcount);
|
|
|
|
|
obj->oval = 2;
|
|
|
|
|
obj->odrive0 = 6;
|
|
|
|
|
obj->odrive1 = 6;
|
|
|
|
|
obj->mode = M42;
|
|
|
|
|
obj->obj = arith;
|
|
|
|
|
#if defined(WITH_DEBUG)
|
|
|
|
|
obj->breakpoint = 0;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
for (unsigned cdx = 0 ; cdx < opcount ; cdx += 1)
|
|
|
|
|
tmp_argv[cdx] = argv[idx + wid*cdx];
|
|
|
|
|
|
|
|
|
|
inputs_connect(ptr, opcount, tmp_argv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(argv);
|
|
|
|
|
}
|
|
|
|
|
|
2001-06-17 01:45:05 +02:00
|
|
|
void compile_arith_mult(char*label, long wid,
|
|
|
|
|
unsigned argc, struct symb_s*argv)
|
|
|
|
|
{
|
|
|
|
|
assert( wid > 0 );
|
|
|
|
|
|
|
|
|
|
if (argc != 2*wid) {
|
|
|
|
|
fprintf(stderr, "%s; .arith has wrong number of symbols\n", label);
|
|
|
|
|
compile_errors += 1;
|
|
|
|
|
free(label);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vvp_ipoint_t fdx = functor_allocate(wid);
|
|
|
|
|
define_functor_symbol(label, fdx);
|
|
|
|
|
|
|
|
|
|
vvp_arith_mult*arith = new vvp_arith_mult(fdx, wid);
|
|
|
|
|
|
|
|
|
|
connect_arith_inputs(fdx, wid, arith, argc, argv);
|
|
|
|
|
}
|
|
|
|
|
|
2001-06-07 05:09:03 +02:00
|
|
|
void compile_arith_sub(char*label, long wid, unsigned argc, struct symb_s*argv)
|
2001-06-05 05:05:41 +02:00
|
|
|
{
|
|
|
|
|
assert( wid > 0 );
|
|
|
|
|
|
|
|
|
|
if ((argc % wid) != 0) {
|
|
|
|
|
fprintf(stderr, "%s; .arith has wrong number of symbols\n", label);
|
|
|
|
|
compile_errors += 1;
|
|
|
|
|
free(label);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned opcount = argc / wid;
|
|
|
|
|
if (opcount > 4) {
|
|
|
|
|
fprintf(stderr, "%s; .arith has too many operands.\n", label);
|
|
|
|
|
compile_errors += 1;
|
|
|
|
|
free(label);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vvp_ipoint_t fdx = functor_allocate(wid);
|
|
|
|
|
define_functor_symbol(label, fdx);
|
|
|
|
|
|
2001-06-07 05:09:03 +02:00
|
|
|
vvp_arith_sub*arith = new vvp_arith_sub(fdx, wid);
|
2001-06-05 05:05:41 +02:00
|
|
|
|
2001-06-07 05:09:03 +02:00
|
|
|
connect_arith_inputs(fdx, wid, arith, argc, argv);
|
|
|
|
|
}
|
2001-06-05 05:05:41 +02:00
|
|
|
|
2001-06-07 05:09:03 +02:00
|
|
|
void compile_arith_sum(char*label, long wid, unsigned argc, struct symb_s*argv)
|
|
|
|
|
{
|
|
|
|
|
assert( wid > 0 );
|
2001-06-05 05:05:41 +02:00
|
|
|
|
2001-06-07 05:09:03 +02:00
|
|
|
if ((argc % wid) != 0) {
|
|
|
|
|
fprintf(stderr, "%s; .arith has wrong number of symbols\n", label);
|
|
|
|
|
compile_errors += 1;
|
|
|
|
|
free(label);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2001-06-05 05:05:41 +02:00
|
|
|
|
2001-06-07 05:09:03 +02:00
|
|
|
unsigned opcount = argc / wid;
|
|
|
|
|
if (opcount > 4) {
|
|
|
|
|
fprintf(stderr, "%s; .arith has too many operands.\n", label);
|
|
|
|
|
compile_errors += 1;
|
|
|
|
|
free(label);
|
|
|
|
|
return;
|
2001-06-05 05:05:41 +02:00
|
|
|
}
|
|
|
|
|
|
2001-06-07 05:09:03 +02:00
|
|
|
vvp_ipoint_t fdx = functor_allocate(wid);
|
|
|
|
|
define_functor_symbol(label, fdx);
|
|
|
|
|
|
|
|
|
|
vvp_arith_sum*arith = new vvp_arith_sum(fdx, wid);
|
|
|
|
|
|
|
|
|
|
connect_arith_inputs(fdx, wid, arith, argc, argv);
|
|
|
|
|
|
2001-06-05 05:05:41 +02:00
|
|
|
}
|
|
|
|
|
|
2001-06-15 06:07:57 +02:00
|
|
|
void compile_cmp_ge(char*label, long wid, unsigned argc, struct symb_s*argv)
|
|
|
|
|
{
|
|
|
|
|
assert( wid > 0 );
|
|
|
|
|
|
|
|
|
|
if (argc != 2*wid) {
|
|
|
|
|
fprintf(stderr, "%s; .cmp has wrong number of symbols\n", label);
|
|
|
|
|
compile_errors += 1;
|
|
|
|
|
free(label);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vvp_ipoint_t fdx = functor_allocate(wid);
|
|
|
|
|
define_functor_symbol(label, fdx);
|
|
|
|
|
|
|
|
|
|
vvp_cmp_ge*cmp = new vvp_cmp_ge(fdx, wid);
|
|
|
|
|
|
|
|
|
|
connect_arith_inputs(fdx, wid, cmp, argc, argv);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void compile_cmp_gt(char*label, long wid, unsigned argc, struct symb_s*argv)
|
|
|
|
|
{
|
|
|
|
|
assert( wid > 0 );
|
|
|
|
|
|
|
|
|
|
if (argc != 2*wid) {
|
|
|
|
|
fprintf(stderr, "%s; .cmp has wrong number of symbols\n", label);
|
|
|
|
|
compile_errors += 1;
|
|
|
|
|
free(label);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vvp_ipoint_t fdx = functor_allocate(wid);
|
|
|
|
|
define_functor_symbol(label, fdx);
|
|
|
|
|
|
|
|
|
|
vvp_cmp_gt*cmp = new vvp_cmp_gt(fdx, wid);
|
|
|
|
|
|
|
|
|
|
connect_arith_inputs(fdx, wid, cmp, argc, argv);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2001-05-09 04:53:25 +02:00
|
|
|
void compile_resolver(char*label, char*type, unsigned argc, struct symb_s*argv)
|
|
|
|
|
{
|
|
|
|
|
vvp_ipoint_t fdx = functor_allocate(1);
|
|
|
|
|
functor_t obj = functor_index(fdx);
|
|
|
|
|
|
|
|
|
|
define_functor_symbol(label, fdx);
|
|
|
|
|
|
|
|
|
|
assert(argc <= 4);
|
|
|
|
|
|
|
|
|
|
obj->ival = 0xaa;
|
|
|
|
|
obj->oval = 2;
|
|
|
|
|
obj->odrive0 = 6;
|
|
|
|
|
obj->odrive1 = 6;
|
|
|
|
|
obj->mode = M42;
|
|
|
|
|
#if defined(WITH_DEBUG)
|
|
|
|
|
obj->breakpoint = 0;
|
|
|
|
|
#endif
|
|
|
|
|
|
2001-05-12 22:38:06 +02:00
|
|
|
if (strcmp(type,"tri") == 0) {
|
|
|
|
|
obj->obj = new vvp_resolv_s;
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(stderr, "invalid resolver type: %s\n", type);
|
|
|
|
|
compile_errors += 1;
|
|
|
|
|
}
|
2001-05-09 04:53:25 +02:00
|
|
|
|
|
|
|
|
/* Connect the inputs of this functor to the given symbols. If
|
|
|
|
|
there are C<X> inputs, set the ival appropriately. */
|
|
|
|
|
inputs_connect(fdx, argc, argv);
|
2001-06-05 05:05:41 +02:00
|
|
|
free(argv);
|
2001-05-09 04:53:25 +02:00
|
|
|
|
2001-05-13 23:05:06 +02:00
|
|
|
/* This causes the output value to be set from the existing
|
|
|
|
|
inputs, and if the output is not x, a propagation event is
|
|
|
|
|
created. */
|
|
|
|
|
obj->obj->set(fdx, obj, false);
|
|
|
|
|
|
2001-05-09 04:53:25 +02:00
|
|
|
free(label);
|
|
|
|
|
free(type);
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-24 04:23:58 +02:00
|
|
|
void compile_udp_def(int sequ, char *label, char *name,
|
|
|
|
|
unsigned nin, unsigned init, char **table)
|
|
|
|
|
{
|
|
|
|
|
struct vvp_udp_s *u = udp_create(label);
|
|
|
|
|
u->name = name;
|
|
|
|
|
u->sequ = sequ;
|
|
|
|
|
u->nin = nin;
|
|
|
|
|
u->init = init;
|
|
|
|
|
u->table = table;
|
2001-04-28 22:24:03 +02:00
|
|
|
free(label);
|
2001-04-24 04:23:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char **compile_udp_table(char **table, char *row)
|
|
|
|
|
{
|
|
|
|
|
if (table)
|
|
|
|
|
assert(strlen(*table)==strlen(row));
|
|
|
|
|
|
|
|
|
|
char **tt;
|
|
|
|
|
for (tt = table; tt && *tt; tt++);
|
|
|
|
|
int n = (tt-table) + 2;
|
|
|
|
|
|
|
|
|
|
table = (char**)realloc(table, n*sizeof(char*));
|
|
|
|
|
table[n-2] = row;
|
|
|
|
|
table[n-1] = 0x0;
|
|
|
|
|
|
|
|
|
|
return table;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void compile_udp_functor(char*label, char*type,
|
|
|
|
|
unsigned argc, struct symb_s*argv)
|
|
|
|
|
{
|
|
|
|
|
struct vvp_udp_s *u = udp_find(type);
|
|
|
|
|
assert (argc == u->nin);
|
|
|
|
|
|
2001-04-26 05:10:55 +02:00
|
|
|
int nfun = (argc+3)/4;
|
2001-04-24 04:23:58 +02:00
|
|
|
|
|
|
|
|
vvp_ipoint_t fdx = functor_allocate(nfun);
|
|
|
|
|
functor_t obj = functor_index(fdx);
|
|
|
|
|
|
2001-04-28 22:24:03 +02:00
|
|
|
define_functor_symbol(label, fdx);
|
|
|
|
|
free(label);
|
2001-04-24 04:23:58 +02:00
|
|
|
|
2001-04-26 05:10:55 +02:00
|
|
|
for (unsigned idx = 0; idx < argc; idx += 4)
|
|
|
|
|
{
|
|
|
|
|
vvp_ipoint_t ifdx = ipoint_input_index(fdx, idx);
|
|
|
|
|
functor_t iobj = functor_index(ifdx);
|
|
|
|
|
|
|
|
|
|
iobj->ival = 0xaa;
|
|
|
|
|
iobj->old_ival = obj->ival;
|
|
|
|
|
iobj->oval = u->init;
|
2001-04-26 17:52:22 +02:00
|
|
|
iobj->mode = M42;
|
2001-05-09 01:32:26 +02:00
|
|
|
#if defined(WITH_DEBUG)
|
|
|
|
|
iobj->breakpoint = 0;
|
|
|
|
|
#endif
|
2001-04-26 05:10:55 +02:00
|
|
|
if (idx)
|
|
|
|
|
{
|
|
|
|
|
iobj->out = fdx;
|
2001-05-06 05:51:37 +02:00
|
|
|
iobj->obj = 0;
|
2001-04-26 05:10:55 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2001-05-06 05:51:37 +02:00
|
|
|
iobj->obj = u;
|
2001-04-26 05:10:55 +02:00
|
|
|
}
|
|
|
|
|
}
|
2001-04-24 04:23:58 +02:00
|
|
|
|
2001-04-28 22:24:03 +02:00
|
|
|
inputs_connect(fdx, argc, argv);
|
2001-06-05 05:05:41 +02:00
|
|
|
free(argv);
|
2001-04-24 04:23:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2001-05-01 03:09:39 +02:00
|
|
|
void compile_memory(char *label, char *name, int msb, int lsb,
|
|
|
|
|
unsigned idxs, long *idx)
|
|
|
|
|
{
|
|
|
|
|
vvp_memory_t mem = memory_create(label);
|
|
|
|
|
memory_new(mem, name, lsb, msb, idxs, idx);
|
2001-05-09 01:59:33 +02:00
|
|
|
|
|
|
|
|
vpiHandle obj = vpip_make_memory(mem);
|
|
|
|
|
compile_vpi_symbol(label, obj);
|
|
|
|
|
|
|
|
|
|
free(label);
|
2001-05-01 03:09:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void compile_memory_port(char *label, char *memid,
|
|
|
|
|
unsigned msb, unsigned lsb,
|
2001-06-15 05:28:30 +02:00
|
|
|
unsigned naddr,
|
2001-05-01 03:09:39 +02:00
|
|
|
unsigned argc, struct symb_s *argv)
|
|
|
|
|
{
|
|
|
|
|
vvp_memory_t mem = memory_find(memid);
|
|
|
|
|
free(memid);
|
|
|
|
|
assert(mem);
|
|
|
|
|
|
|
|
|
|
// These is not a Verilog bit range.
|
|
|
|
|
// These is a data port bit range.
|
|
|
|
|
assert (lsb >= 0 && lsb<=msb);
|
|
|
|
|
assert (msb < memory_data_width(mem));
|
|
|
|
|
unsigned nbits = msb-lsb+1;
|
|
|
|
|
|
2001-06-15 05:28:30 +02:00
|
|
|
bool writable = argc >= (naddr + 2 + nbits);
|
|
|
|
|
|
|
|
|
|
unsigned nfun = naddr;
|
|
|
|
|
if (writable)
|
|
|
|
|
nfun += 2 + nbits;
|
|
|
|
|
assert(nfun == argc);
|
|
|
|
|
nfun = (nfun+3)/4;
|
2001-05-01 03:09:39 +02:00
|
|
|
if (nfun < nbits)
|
|
|
|
|
nfun = nbits;
|
|
|
|
|
|
|
|
|
|
vvp_ipoint_t ix = functor_allocate(nfun);
|
|
|
|
|
|
|
|
|
|
define_functor_symbol(label, ix);
|
|
|
|
|
free(label);
|
|
|
|
|
|
|
|
|
|
inputs_connect(ix, argc, argv);
|
2001-06-05 05:05:41 +02:00
|
|
|
free(argv);
|
2001-05-01 03:09:39 +02:00
|
|
|
|
2001-06-15 05:28:30 +02:00
|
|
|
memory_port_new(mem, ix, nbits, lsb, naddr, writable);
|
2001-05-01 03:09:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void compile_memory_init(char *memid, unsigned i, unsigned char val)
|
|
|
|
|
{
|
|
|
|
|
static vvp_memory_t mem = 0x0;
|
|
|
|
|
static unsigned idx;
|
|
|
|
|
if (memid)
|
|
|
|
|
{
|
|
|
|
|
mem = memory_find(memid);
|
|
|
|
|
free(memid);
|
|
|
|
|
idx = i/4;
|
|
|
|
|
}
|
|
|
|
|
assert(mem);
|
|
|
|
|
memory_init_nibble(mem, idx, val);
|
|
|
|
|
idx++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2001-03-26 06:00:39 +02:00
|
|
|
void compile_event(char*label, char*type,
|
|
|
|
|
unsigned argc, struct symb_s*argv)
|
|
|
|
|
{
|
|
|
|
|
vvp_ipoint_t fdx = functor_allocate(1);
|
|
|
|
|
functor_t obj = functor_index(fdx);
|
|
|
|
|
|
2001-04-28 22:24:03 +02:00
|
|
|
define_functor_symbol(label, fdx);
|
2001-03-26 06:00:39 +02:00
|
|
|
|
|
|
|
|
assert(argc <= 4);
|
|
|
|
|
|
|
|
|
|
/* Run through the arguments looking for the functors that are
|
|
|
|
|
connected to my input ports. For each source functor that I
|
|
|
|
|
find, connect the output of that functor to the indexed
|
|
|
|
|
input by inserting myself (complete with the port number in
|
|
|
|
|
the vvp_ipoint_t) into the list that the source heads.
|
|
|
|
|
|
|
|
|
|
If the source functor is not declared yet, then don't do
|
|
|
|
|
the link yet. Save the reference to be resolved later. */
|
|
|
|
|
|
2001-04-28 22:24:03 +02:00
|
|
|
inputs_connect(fdx, argc, argv);
|
2001-06-05 05:05:41 +02:00
|
|
|
free(argv);
|
2001-03-26 06:00:39 +02:00
|
|
|
|
|
|
|
|
obj->ival = 0xaa;
|
|
|
|
|
obj->oval = 2;
|
2001-05-09 01:32:26 +02:00
|
|
|
obj->odrive0 = 6;
|
|
|
|
|
obj->odrive0 = 6;
|
2001-03-26 06:00:39 +02:00
|
|
|
obj->mode = 1;
|
2001-04-14 07:10:56 +02:00
|
|
|
obj->out = 0;
|
2001-05-09 01:32:26 +02:00
|
|
|
#if defined(WITH_DEBUG)
|
|
|
|
|
obj->breakpoint = 0;
|
|
|
|
|
#endif
|
2001-03-26 06:00:39 +02:00
|
|
|
|
|
|
|
|
obj->event = (struct vvp_event_s*) malloc(sizeof (struct vvp_event_s));
|
|
|
|
|
obj->event->threads = 0;
|
|
|
|
|
obj->event->ival = obj->ival;
|
|
|
|
|
|
|
|
|
|
if (strcmp(type,"posedge") == 0)
|
|
|
|
|
obj->event->vvp_edge_tab = vvp_edge_posedge;
|
|
|
|
|
else if (strcmp(type,"negedge") == 0)
|
|
|
|
|
obj->event->vvp_edge_tab = vvp_edge_negedge;
|
|
|
|
|
else if (strcmp(type,"edge") == 0)
|
|
|
|
|
obj->event->vvp_edge_tab = vvp_edge_anyedge;
|
|
|
|
|
else
|
|
|
|
|
obj->event->vvp_edge_tab = 0;
|
|
|
|
|
|
|
|
|
|
free(type);
|
|
|
|
|
free(label);
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-29 05:46:36 +02:00
|
|
|
void compile_named_event(char*label, char*name)
|
|
|
|
|
{
|
|
|
|
|
vvp_ipoint_t fdx = functor_allocate(1);
|
|
|
|
|
functor_t obj = functor_index(fdx);
|
|
|
|
|
|
2001-04-28 22:24:03 +02:00
|
|
|
define_functor_symbol(label, fdx);
|
2001-03-29 05:46:36 +02:00
|
|
|
|
|
|
|
|
obj->ival = 0xaa;
|
|
|
|
|
obj->oval = 2;
|
2001-05-09 01:32:26 +02:00
|
|
|
obj->odrive0 = 6;
|
|
|
|
|
obj->odrive1 = 6;
|
2001-03-29 05:46:36 +02:00
|
|
|
obj->mode = 2;
|
2001-04-14 07:10:56 +02:00
|
|
|
obj->out = 0;
|
2001-05-09 01:32:26 +02:00
|
|
|
#if defined(WITH_DEBUG)
|
|
|
|
|
obj->breakpoint = 0;
|
|
|
|
|
#endif
|
2001-03-29 05:46:36 +02:00
|
|
|
|
|
|
|
|
obj->event = (struct vvp_event_s*) malloc(sizeof (struct vvp_event_s));
|
|
|
|
|
obj->event->threads = 0;
|
|
|
|
|
obj->event->ival = obj->ival;
|
|
|
|
|
|
|
|
|
|
free(label);
|
|
|
|
|
free(name);
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-14 07:10:56 +02:00
|
|
|
void compile_event_or(char*label, unsigned argc, struct symb_s*argv)
|
|
|
|
|
{
|
|
|
|
|
vvp_ipoint_t fdx = functor_allocate(1);
|
|
|
|
|
functor_t obj = functor_index(fdx);
|
|
|
|
|
|
2001-04-28 22:24:03 +02:00
|
|
|
define_functor_symbol(label, fdx);
|
2001-04-14 07:10:56 +02:00
|
|
|
|
|
|
|
|
obj->ival = 0xaa;
|
|
|
|
|
obj->oval = 2;
|
2001-05-09 01:32:26 +02:00
|
|
|
obj->odrive0 = 6;
|
|
|
|
|
obj->odrive1 = 6;
|
2001-04-14 07:10:56 +02:00
|
|
|
obj->mode = 2;
|
|
|
|
|
obj->out = 0;
|
2001-05-09 01:32:26 +02:00
|
|
|
#if defined(WITH_DEBUG)
|
|
|
|
|
obj->breakpoint = 0;
|
|
|
|
|
#endif
|
2001-04-14 07:10:56 +02:00
|
|
|
|
|
|
|
|
obj->event = new struct vvp_event_s;
|
|
|
|
|
obj->event->threads = 0;
|
|
|
|
|
obj->event->ival = obj->ival;
|
|
|
|
|
|
|
|
|
|
/* Link the outputs of the named events to me. */
|
|
|
|
|
|
|
|
|
|
for (unsigned idx = 0 ; idx < argc ; idx += 1) {
|
|
|
|
|
symbol_value_t val = sym_get_value(sym_functors, argv[idx].text);
|
|
|
|
|
vvp_ipoint_t tmp = val.num;
|
|
|
|
|
|
|
|
|
|
assert(tmp);
|
|
|
|
|
|
|
|
|
|
tmp = ipoint_index(tmp, argv[idx].idx);
|
|
|
|
|
|
|
|
|
|
functor_t fport = functor_index(tmp);
|
|
|
|
|
assert(fport->out == 0);
|
|
|
|
|
fport->out = fdx;
|
|
|
|
|
|
|
|
|
|
free(argv[idx].text);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(argv);
|
|
|
|
|
free(label);
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-11 01:29:38 +01:00
|
|
|
/*
|
|
|
|
|
* The parser uses this function to compile an link an executable
|
|
|
|
|
* opcode. I do this by looking up the opcode in the opcode_table. The
|
|
|
|
|
* table gives the operand structure that is acceptible, so I can
|
|
|
|
|
* process the operands here as well.
|
|
|
|
|
*/
|
|
|
|
|
void compile_code(char*label, char*mnem, comp_operands_t opa)
|
|
|
|
|
{
|
|
|
|
|
vvp_cpoint_t ptr = codespace_allocate();
|
|
|
|
|
|
|
|
|
|
/* First, I can give the label a value that is the current
|
|
|
|
|
codespace pointer. Don't need the text of the label after
|
|
|
|
|
this is done. */
|
|
|
|
|
if (label) {
|
2001-03-18 01:37:55 +01:00
|
|
|
symbol_value_t val;
|
|
|
|
|
val.num = ptr;
|
|
|
|
|
sym_set_value(sym_codespace, label, val);
|
2001-03-11 01:29:38 +01:00
|
|
|
free(label);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Lookup the opcode in the opcode table. */
|
|
|
|
|
struct opcode_table_s*op = (struct opcode_table_s*)
|
|
|
|
|
bsearch(mnem, opcode_table, opcode_count,
|
|
|
|
|
sizeof(struct opcode_table_s), &opcode_compare);
|
|
|
|
|
if (op == 0) {
|
|
|
|
|
yyerror("Invalid opcode");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(op);
|
|
|
|
|
|
|
|
|
|
/* Build up the code from the information about the opcode and
|
|
|
|
|
the information from the comiler. */
|
|
|
|
|
vvp_code_t code = codespace_index(ptr);
|
|
|
|
|
code->opcode = op->opcode;
|
|
|
|
|
|
|
|
|
|
if (op->argc != (opa? opa->argc : 0)) {
|
|
|
|
|
yyerror("operand count");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Pull the operands that the instruction expects from the
|
|
|
|
|
list that the parser supplied. */
|
|
|
|
|
|
|
|
|
|
for (unsigned idx = 0 ; idx < op->argc ; idx += 1) {
|
2001-03-18 01:37:55 +01:00
|
|
|
symbol_value_t tmp;
|
|
|
|
|
|
2001-03-11 01:29:38 +01:00
|
|
|
switch (op->argt[idx]) {
|
|
|
|
|
case OA_NONE:
|
|
|
|
|
break;
|
|
|
|
|
|
2001-03-12 00:06:49 +01:00
|
|
|
case OA_BIT1:
|
2001-03-11 01:29:38 +01:00
|
|
|
if (opa->argv[idx].ltype != L_NUMB) {
|
|
|
|
|
yyerror("operand format");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-12 00:06:49 +01:00
|
|
|
code->bit_idx1 = opa->argv[idx].numb;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OA_BIT2:
|
|
|
|
|
if (opa->argv[idx].ltype != L_NUMB) {
|
|
|
|
|
yyerror("operand format");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
code->bit_idx2 = opa->argv[idx].numb;
|
2001-03-11 01:29:38 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OA_CODE_PTR:
|
2001-03-20 07:16:23 +01:00
|
|
|
if (opa->argv[idx].ltype != L_SYMB) {
|
2001-03-11 01:29:38 +01:00
|
|
|
yyerror("operand format");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-20 07:16:23 +01:00
|
|
|
assert(opa->argv[idx].symb.idx == 0);
|
|
|
|
|
tmp = sym_get_value(sym_codespace, opa->argv[idx].symb.text);
|
2001-03-18 01:37:55 +01:00
|
|
|
code->cptr = tmp.num;
|
2001-03-11 01:29:38 +01:00
|
|
|
if (code->cptr == 0) {
|
2001-03-22 06:28:41 +01:00
|
|
|
struct cresolv_list_s*res = (struct cresolv_list_s*)
|
|
|
|
|
calloc(1, sizeof(struct cresolv_list_s));
|
|
|
|
|
res->cp = code;
|
|
|
|
|
res->lab = opa->argv[idx].symb.text;
|
2001-06-10 19:12:51 +02:00
|
|
|
res->next = cresolv_llist;
|
|
|
|
|
cresolv_llist = res;
|
2001-03-22 06:28:41 +01:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
free(opa->argv[idx].symb.text);
|
2001-03-11 01:29:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OA_FUNC_PTR:
|
2001-06-10 19:12:51 +02:00
|
|
|
/* The operand is a functor. Resolve the label to
|
|
|
|
|
a functor pointer, or postpone the resolution
|
|
|
|
|
if it is not defined yet. */
|
2001-03-20 07:16:23 +01:00
|
|
|
if (opa->argv[idx].ltype != L_SYMB) {
|
2001-03-11 01:29:38 +01:00
|
|
|
yyerror("operand format");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-20 07:16:23 +01:00
|
|
|
tmp = sym_get_value(sym_functors, opa->argv[idx].symb.text);
|
|
|
|
|
if (tmp.num == 0) {
|
2001-06-10 19:12:51 +02:00
|
|
|
struct cresolv_list_s*res = new struct cresolv_list_s;
|
|
|
|
|
res->cp = code;
|
|
|
|
|
res->lab = opa->argv[idx].symb.text;
|
|
|
|
|
res->idx = opa->argv[idx].symb.idx;
|
|
|
|
|
res->next = cresolv_flist;
|
|
|
|
|
cresolv_flist = res;
|
2001-03-11 01:29:38 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2001-06-10 19:12:51 +02:00
|
|
|
|
2001-03-20 07:16:23 +01:00
|
|
|
code->iptr = ipoint_index(tmp.num, opa->argv[idx].symb.idx);
|
2001-03-11 01:29:38 +01:00
|
|
|
|
2001-03-20 07:16:23 +01:00
|
|
|
free(opa->argv[idx].symb.text);
|
2001-03-11 01:29:38 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OA_NUMBER:
|
|
|
|
|
if (opa->argv[idx].ltype != L_NUMB) {
|
|
|
|
|
yyerror("operand format");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
code->number = opa->argv[idx].numb;
|
|
|
|
|
break;
|
|
|
|
|
|
2001-05-03 01:16:50 +02:00
|
|
|
case OA_MEM_PTR:
|
2001-05-01 03:09:39 +02:00
|
|
|
if (opa->argv[idx].ltype != L_SYMB) {
|
|
|
|
|
yyerror("operand format");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
code->mem = memory_find(opa->argv[idx].symb.text);
|
|
|
|
|
if (code->mem == 0) {
|
2001-05-03 01:16:50 +02:00
|
|
|
yyerror("memory undefined");
|
2001-05-01 03:09:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(opa->argv[idx].symb.text);
|
|
|
|
|
break;
|
|
|
|
|
|
2001-03-11 01:29:38 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (opa) free(opa);
|
|
|
|
|
|
|
|
|
|
free(mnem);
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-01 08:40:44 +02:00
|
|
|
void compile_codelabel(char*label)
|
|
|
|
|
{
|
|
|
|
|
symbol_value_t val;
|
|
|
|
|
vvp_cpoint_t ptr = codespace_next();
|
|
|
|
|
|
|
|
|
|
val.num = ptr;
|
|
|
|
|
sym_set_value(sym_codespace, label, val);
|
|
|
|
|
|
|
|
|
|
free(label);
|
|
|
|
|
}
|
|
|
|
|
|
2001-06-23 03:04:07 +02:00
|
|
|
|
|
|
|
|
struct postponed_handles_list_s {
|
|
|
|
|
struct postponed_handles_list_s*next;
|
|
|
|
|
vpiHandle *handle;
|
|
|
|
|
char*name;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct postponed_handles_list_s *late_handles;
|
|
|
|
|
|
|
|
|
|
static vpiHandle postpone_vpi_symbol_lookup(vpiHandle *handle,
|
|
|
|
|
char*name)
|
|
|
|
|
{
|
|
|
|
|
*handle = compile_vpi_lookup(name);
|
|
|
|
|
if (*handle)
|
|
|
|
|
free(name);
|
|
|
|
|
else {
|
|
|
|
|
struct postponed_handles_list_s*res =
|
|
|
|
|
(struct postponed_handles_list_s*)
|
|
|
|
|
malloc(sizeof(struct postponed_handles_list_s));
|
|
|
|
|
|
|
|
|
|
res->handle = handle;
|
|
|
|
|
res->name = name;
|
|
|
|
|
res->next = late_handles;
|
|
|
|
|
late_handles = res;
|
|
|
|
|
}
|
|
|
|
|
return *handle;
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-18 06:21:23 +02:00
|
|
|
void compile_disable(char*label, struct symb_s symb)
|
|
|
|
|
{
|
|
|
|
|
vvp_cpoint_t ptr = codespace_allocate();
|
|
|
|
|
|
|
|
|
|
/* First, I can give the label a value that is the current
|
|
|
|
|
codespace pointer. Don't need the text of the label after
|
|
|
|
|
this is done. */
|
|
|
|
|
if (label) {
|
|
|
|
|
symbol_value_t val;
|
|
|
|
|
val.num = ptr;
|
|
|
|
|
sym_set_value(sym_codespace, label, val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Fill in the basics of the %disable in the instruction. */
|
|
|
|
|
vvp_code_t code = codespace_index(ptr);
|
|
|
|
|
code->opcode = of_DISABLE;
|
|
|
|
|
|
2001-06-23 03:04:07 +02:00
|
|
|
postpone_vpi_symbol_lookup(&code->handle, symb.text);
|
2001-04-18 06:21:23 +02:00
|
|
|
|
|
|
|
|
free(label);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The %fork instruction is a little different from other instructions
|
|
|
|
|
* in that it has an extended field that holds the information needed
|
|
|
|
|
* to create the new thread. This includes the target PC and scope.
|
|
|
|
|
* I get these from the parser in the form of symbols.
|
|
|
|
|
*/
|
|
|
|
|
void compile_fork(char*label, struct symb_s dest, struct symb_s scope)
|
|
|
|
|
{
|
|
|
|
|
symbol_value_t tmp;
|
|
|
|
|
vvp_cpoint_t ptr = codespace_allocate();
|
|
|
|
|
|
|
|
|
|
/* First, I can give the label a value that is the current
|
|
|
|
|
codespace pointer. Don't need the text of the label after
|
|
|
|
|
this is done. */
|
|
|
|
|
if (label) {
|
|
|
|
|
symbol_value_t val;
|
|
|
|
|
val.num = ptr;
|
|
|
|
|
sym_set_value(sym_codespace, label, val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Fill in the basics of the %fork in the instruction. */
|
|
|
|
|
vvp_code_t code = codespace_index(ptr);
|
|
|
|
|
code->opcode = of_FORK;
|
|
|
|
|
code->fork = new struct fork_extend;
|
|
|
|
|
|
|
|
|
|
/* Figure out the target PC. */
|
|
|
|
|
tmp = sym_get_value(sym_codespace, dest.text);
|
|
|
|
|
code->fork->cptr = tmp.num;
|
2001-04-18 07:03:49 +02:00
|
|
|
if (code->fork->cptr == 0) {
|
|
|
|
|
struct cresolv_list_s*res = new cresolv_list_s;
|
|
|
|
|
res->cp = code;
|
|
|
|
|
res->lab = dest.text;
|
2001-06-10 19:12:51 +02:00
|
|
|
res->next = cresolv_llist;
|
|
|
|
|
cresolv_llist = res;
|
2001-04-18 07:03:49 +02:00
|
|
|
dest.text = 0;
|
|
|
|
|
}
|
2001-04-18 06:21:23 +02:00
|
|
|
|
|
|
|
|
/* Figure out the target SCOPE. */
|
2001-06-23 03:04:07 +02:00
|
|
|
postpone_vpi_symbol_lookup((vpiHandle*)&code->fork->scope, scope.text);
|
|
|
|
|
|
2001-04-18 06:21:23 +02:00
|
|
|
free(label);
|
|
|
|
|
free(dest.text);
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-18 05:35:18 +01:00
|
|
|
void compile_vpi_call(char*label, char*name, unsigned argc, vpiHandle*argv)
|
2001-03-16 02:44:34 +01:00
|
|
|
{
|
|
|
|
|
vvp_cpoint_t ptr = codespace_allocate();
|
|
|
|
|
|
|
|
|
|
/* First, I can give the label a value that is the current
|
|
|
|
|
codespace pointer. Don't need the text of the label after
|
|
|
|
|
this is done. */
|
|
|
|
|
if (label) {
|
2001-03-18 01:37:55 +01:00
|
|
|
symbol_value_t val;
|
|
|
|
|
val.num = ptr;
|
|
|
|
|
sym_set_value(sym_codespace, label, val);
|
2001-03-16 02:44:34 +01:00
|
|
|
free(label);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create an instruction in the code space. */
|
|
|
|
|
vvp_code_t code = codespace_index(ptr);
|
|
|
|
|
code->opcode = &of_VPI_CALL;
|
|
|
|
|
|
|
|
|
|
/* Create a vpiHandle that bundles the call information, and
|
|
|
|
|
store that handle in the instruction. */
|
2001-05-20 02:46:12 +02:00
|
|
|
code->handle = vpip_build_vpi_call(name, 0, 0, argc, argv);
|
|
|
|
|
if (code->handle == 0)
|
|
|
|
|
compile_errors += 1;
|
|
|
|
|
|
|
|
|
|
/* Done with the lexor-allocated name string. */
|
|
|
|
|
free(name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void compile_vpi_func_call(char*label, char*name,
|
|
|
|
|
unsigned vbit, unsigned vwid,
|
|
|
|
|
unsigned argc, vpiHandle*argv)
|
|
|
|
|
{
|
|
|
|
|
vvp_cpoint_t ptr = codespace_allocate();
|
|
|
|
|
|
|
|
|
|
/* First, I can give the label a value that is the current
|
|
|
|
|
codespace pointer. Don't need the text of the label after
|
|
|
|
|
this is done. */
|
|
|
|
|
if (label) {
|
|
|
|
|
symbol_value_t val;
|
|
|
|
|
val.num = ptr;
|
|
|
|
|
sym_set_value(sym_codespace, label, val);
|
|
|
|
|
free(label);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create an instruction in the code space. */
|
|
|
|
|
vvp_code_t code = codespace_index(ptr);
|
|
|
|
|
code->opcode = &of_VPI_CALL;
|
|
|
|
|
|
|
|
|
|
/* Create a vpiHandle that bundles the call information, and
|
|
|
|
|
store that handle in the instruction. */
|
|
|
|
|
code->handle = vpip_build_vpi_call(name, vbit, vwid, argc, argv);
|
2001-03-22 23:38:13 +01:00
|
|
|
if (code->handle == 0)
|
|
|
|
|
compile_errors += 1;
|
2001-03-16 02:44:34 +01:00
|
|
|
|
|
|
|
|
/* Done with the lexor-allocated name string. */
|
|
|
|
|
free(name);
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-11 01:29:38 +01:00
|
|
|
/*
|
|
|
|
|
* When the parser finds a thread statement, I create a new thread
|
|
|
|
|
* with the start address referenced by the program symbol passed to
|
|
|
|
|
* me.
|
|
|
|
|
*/
|
|
|
|
|
void compile_thread(char*start_sym)
|
|
|
|
|
{
|
2001-03-18 01:37:55 +01:00
|
|
|
symbol_value_t tmp = sym_get_value(sym_codespace, start_sym);
|
|
|
|
|
vvp_cpoint_t pc = tmp.num;
|
2001-03-11 01:29:38 +01:00
|
|
|
if (pc == 0) {
|
|
|
|
|
yyerror("unresolved address");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-18 06:21:23 +02:00
|
|
|
vthread_t thr = vthread_new(pc, vpip_peek_current_scope());
|
2001-03-11 01:29:38 +01:00
|
|
|
schedule_vthread(thr, 0);
|
|
|
|
|
free(start_sym);
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-21 06:13:03 +01:00
|
|
|
vpiHandle compile_vpi_lookup(const char*label)
|
|
|
|
|
{
|
|
|
|
|
symbol_value_t val;
|
|
|
|
|
|
|
|
|
|
val = sym_get_value(sym_vpi, label);
|
2001-05-10 02:26:53 +02:00
|
|
|
if (!val.ptr) {
|
|
|
|
|
// check for thread vector T<base,wid>
|
|
|
|
|
unsigned base, wid;
|
|
|
|
|
unsigned n;
|
|
|
|
|
if (2 <= sscanf(label, "T<%u,%u>%n", &base, &wid, &n)
|
|
|
|
|
&& n == strlen(label)) {
|
|
|
|
|
val.ptr = vpip_make_vthr_vector(base, wid);
|
|
|
|
|
sym_set_value(sym_vpi, label, val);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!val.ptr) {
|
|
|
|
|
// check for memory word M<mem,base,wid>
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-21 06:13:03 +01:00
|
|
|
return (vpiHandle) val.ptr;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-11 01:29:38 +01:00
|
|
|
/*
|
|
|
|
|
* A variable is a special functor, so we allocate that functor and
|
|
|
|
|
* write the label into the symbol table.
|
|
|
|
|
*/
|
2001-04-05 03:34:26 +02:00
|
|
|
void compile_variable(char*label, char*name, int msb, int lsb,
|
|
|
|
|
bool signed_flag)
|
2001-03-11 01:29:38 +01:00
|
|
|
{
|
2001-03-20 07:16:23 +01:00
|
|
|
unsigned wid = ((msb > lsb)? msb-lsb : lsb-msb) + 1;
|
|
|
|
|
vvp_ipoint_t fdx = functor_allocate(wid);
|
2001-04-28 22:24:03 +02:00
|
|
|
|
|
|
|
|
define_functor_symbol(label, fdx);
|
2001-03-11 01:29:38 +01:00
|
|
|
|
2001-03-20 07:16:23 +01:00
|
|
|
for (unsigned idx = 0 ; idx < wid ; idx += 1) {
|
|
|
|
|
functor_t obj = functor_index(ipoint_index(fdx,idx));
|
|
|
|
|
obj->table = ft_var;
|
|
|
|
|
obj->ival = 0x22;
|
|
|
|
|
obj->oval = 0x02;
|
2001-05-09 01:32:26 +02:00
|
|
|
obj->odrive0 = 6;
|
|
|
|
|
obj->odrive1 = 6;
|
2001-03-26 06:00:39 +02:00
|
|
|
obj->mode = 0;
|
2001-05-09 01:32:26 +02:00
|
|
|
#if defined(WITH_DEBUG)
|
|
|
|
|
obj->breakpoint = 0;
|
|
|
|
|
#endif
|
2001-03-20 07:16:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make the vpiHandle for the reg. */
|
2001-04-05 03:34:26 +02:00
|
|
|
vpiHandle obj = vpip_make_reg(name, msb, lsb, signed_flag, fdx);
|
2001-03-21 06:13:03 +01:00
|
|
|
compile_vpi_symbol(label, obj);
|
2001-06-10 18:47:49 +02:00
|
|
|
vpip_attach_to_current_scope(obj);
|
2001-03-21 06:13:03 +01:00
|
|
|
|
|
|
|
|
free(label);
|
2001-03-11 01:29:38 +01:00
|
|
|
}
|
|
|
|
|
|
2001-04-05 03:34:26 +02:00
|
|
|
void compile_net(char*label, char*name, int msb, int lsb, bool signed_flag,
|
2001-03-25 01:35:35 +01:00
|
|
|
unsigned argc, struct symb_s*argv)
|
|
|
|
|
{
|
|
|
|
|
unsigned wid = ((msb > lsb)? msb-lsb : lsb-msb) + 1;
|
|
|
|
|
vvp_ipoint_t fdx = functor_allocate(wid);
|
2001-04-28 22:24:03 +02:00
|
|
|
|
|
|
|
|
define_functor_symbol(label, fdx);
|
2001-03-25 01:35:35 +01:00
|
|
|
|
|
|
|
|
/* Allocate all the functors for the net itself. */
|
|
|
|
|
for (unsigned idx = 0 ; idx < wid ; idx += 1) {
|
|
|
|
|
functor_t obj = functor_index(ipoint_index(fdx,idx));
|
|
|
|
|
obj->table = ft_var;
|
2001-05-09 01:32:26 +02:00
|
|
|
obj->ival = 0x02;
|
2001-03-25 01:35:35 +01:00
|
|
|
obj->oval = 0x02;
|
2001-05-09 01:32:26 +02:00
|
|
|
obj->odrive0 = 6;
|
|
|
|
|
obj->odrive1 = 6;
|
2001-03-26 06:00:39 +02:00
|
|
|
obj->mode = 0;
|
2001-05-09 01:32:26 +02:00
|
|
|
#if defined(WITH_DEBUG)
|
|
|
|
|
obj->breakpoint = 0;
|
|
|
|
|
#endif
|
2001-03-25 01:35:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(argc == wid);
|
|
|
|
|
|
|
|
|
|
/* Connect port[0] of each of the net functors to the output
|
|
|
|
|
of the addressed object. */
|
|
|
|
|
for (unsigned idx = 0 ; idx < wid ; idx += 1) {
|
|
|
|
|
vvp_ipoint_t ptr = ipoint_index(fdx,idx);
|
|
|
|
|
functor_t obj = functor_index(ptr);
|
|
|
|
|
|
2001-04-23 02:37:58 +02:00
|
|
|
/* Skip unconnected nets. */
|
|
|
|
|
if (argv[idx].text == 0) {
|
|
|
|
|
obj->oval = 3;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-30 00:59:46 +02:00
|
|
|
if (strcmp(argv[idx].text, "C<0>") == 0) {
|
|
|
|
|
obj->oval = 0;
|
2001-05-06 02:18:13 +02:00
|
|
|
schedule_functor(ptr, 0);
|
2001-04-30 00:59:46 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp(argv[idx].text, "C<1>") == 0) {
|
|
|
|
|
obj->oval = 1;
|
2001-05-06 02:18:13 +02:00
|
|
|
schedule_functor(ptr, 0);
|
2001-04-30 00:59:46 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp(argv[idx].text, "C<x>") == 0) {
|
|
|
|
|
obj->oval = 2;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp(argv[idx].text, "C<z>") == 0) {
|
|
|
|
|
obj->oval = 3;
|
2001-05-06 02:18:13 +02:00
|
|
|
schedule_functor(ptr, 0);
|
2001-04-30 00:59:46 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-28 22:24:03 +02:00
|
|
|
symbol_value_t val = sym_get_value(sym_functors, argv[idx].text);
|
2001-03-25 05:54:26 +02:00
|
|
|
if (val.num) {
|
|
|
|
|
|
|
|
|
|
functor_t src = functor_index(ipoint_index(val.num,
|
|
|
|
|
argv[idx].idx));
|
|
|
|
|
obj->port[0] = src->out;
|
|
|
|
|
src->out = ptr;
|
2001-03-25 01:35:35 +01:00
|
|
|
|
2001-03-25 05:54:26 +02:00
|
|
|
} else {
|
|
|
|
|
postpone_functor_input(ipoint_make(ptr, 0),
|
|
|
|
|
argv[idx].text,
|
|
|
|
|
argv[idx].idx);
|
|
|
|
|
}
|
2001-03-25 01:35:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make the vpiHandle for the reg. */
|
2001-04-05 03:34:26 +02:00
|
|
|
vpiHandle obj = vpip_make_net(name, msb, lsb, signed_flag, fdx);
|
2001-03-25 01:35:35 +01:00
|
|
|
compile_vpi_symbol(label, obj);
|
2001-06-10 18:47:49 +02:00
|
|
|
vpip_attach_to_current_scope(obj);
|
2001-03-25 01:35:35 +01:00
|
|
|
|
|
|
|
|
free(label);
|
|
|
|
|
free(argv);
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-11 23:42:11 +01:00
|
|
|
/*
|
|
|
|
|
* When parsing is otherwise complete, this function is called to do
|
|
|
|
|
* the final stuff. Clean up deferred linking here.
|
|
|
|
|
*/
|
2001-03-11 01:29:38 +01:00
|
|
|
void compile_cleanup(void)
|
|
|
|
|
{
|
|
|
|
|
struct resolv_list_s*tmp_list = resolv_list;
|
|
|
|
|
resolv_list = 0;
|
|
|
|
|
|
|
|
|
|
while (tmp_list) {
|
|
|
|
|
struct resolv_list_s*res = tmp_list;
|
|
|
|
|
tmp_list = res->next;
|
|
|
|
|
|
2001-03-11 23:42:11 +01:00
|
|
|
/* Get the addressed functor object and select the input
|
|
|
|
|
port that needs resolution. */
|
2001-03-11 01:29:38 +01:00
|
|
|
functor_t obj = functor_index(res->port);
|
|
|
|
|
unsigned idx = ipoint_port(res->port);
|
|
|
|
|
|
2001-03-11 23:42:11 +01:00
|
|
|
/* Try again to look up the symbol that was not defined
|
|
|
|
|
the first time around. */
|
2001-03-18 01:37:55 +01:00
|
|
|
symbol_value_t val = sym_get_value(sym_functors, res->source);
|
|
|
|
|
vvp_ipoint_t tmp = val.num;
|
2001-03-11 01:29:38 +01:00
|
|
|
|
|
|
|
|
if (tmp != 0) {
|
2001-03-11 23:42:11 +01:00
|
|
|
/* The symbol is defined, link the functor input
|
|
|
|
|
to the resolved output. */
|
2001-03-20 07:16:23 +01:00
|
|
|
|
|
|
|
|
tmp = ipoint_index(tmp, res->idx);
|
2001-03-11 01:29:38 +01:00
|
|
|
functor_t fport = functor_index(tmp);
|
|
|
|
|
obj->port[idx] = fport->out;
|
|
|
|
|
fport->out = res->port;
|
|
|
|
|
|
|
|
|
|
free(res->source);
|
|
|
|
|
free(res);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
/* Still not resolved. put back into the list. */
|
2001-04-30 00:59:46 +02:00
|
|
|
fprintf(stderr, "unresolved functor reference: %s\n",
|
|
|
|
|
res->source);
|
2001-03-11 01:29:38 +01:00
|
|
|
res->next = resolv_list;
|
|
|
|
|
resolv_list = res;
|
2001-05-12 22:38:06 +02:00
|
|
|
compile_errors += 1;
|
2001-03-11 01:29:38 +01:00
|
|
|
}
|
|
|
|
|
}
|
2001-03-18 01:37:55 +01:00
|
|
|
|
2001-06-10 19:12:51 +02:00
|
|
|
struct cresolv_list_s*tmp_clist = cresolv_llist;
|
|
|
|
|
cresolv_llist = 0;
|
2001-03-22 06:28:41 +01:00
|
|
|
|
|
|
|
|
while (tmp_clist) {
|
|
|
|
|
struct cresolv_list_s*res = tmp_clist;
|
|
|
|
|
tmp_clist = res->next;
|
|
|
|
|
|
|
|
|
|
symbol_value_t val = sym_get_value(sym_codespace, res->lab);
|
|
|
|
|
vvp_cpoint_t tmp = val.num;
|
|
|
|
|
|
|
|
|
|
if (tmp != 0) {
|
2001-04-18 07:03:49 +02:00
|
|
|
/* Resolved the reference. If this is a %fork,
|
|
|
|
|
then handle it slightly differently. */
|
|
|
|
|
if (res->cp->opcode == of_FORK)
|
|
|
|
|
res->cp->fork->cptr = tmp;
|
|
|
|
|
else
|
|
|
|
|
res->cp->cptr = tmp;
|
2001-03-22 06:28:41 +01:00
|
|
|
free(res->lab);
|
|
|
|
|
|
|
|
|
|
} else {
|
2001-05-12 22:38:06 +02:00
|
|
|
compile_errors += 1;
|
2001-03-22 06:28:41 +01:00
|
|
|
fprintf(stderr, "unresolved code label: %s\n", res->lab);
|
2001-06-10 19:12:51 +02:00
|
|
|
res->next = cresolv_llist;
|
|
|
|
|
cresolv_llist = res;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tmp_clist = cresolv_flist;
|
|
|
|
|
cresolv_flist = 0;
|
|
|
|
|
|
|
|
|
|
while (tmp_clist) {
|
|
|
|
|
struct cresolv_list_s*res = tmp_clist;
|
|
|
|
|
tmp_clist = res->next;
|
|
|
|
|
|
|
|
|
|
symbol_value_t val = sym_get_value(sym_functors, res->lab);
|
|
|
|
|
vvp_ipoint_t tmp = val.num;
|
|
|
|
|
|
|
|
|
|
if (tmp != 0) {
|
|
|
|
|
res->cp->iptr = ipoint_index(tmp, res->idx);
|
|
|
|
|
free(res->lab);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
compile_errors += 1;
|
|
|
|
|
fprintf(stderr, "unresolved code reference "
|
|
|
|
|
"to functor: %s\n", res->lab);
|
|
|
|
|
res->next = cresolv_llist;
|
|
|
|
|
cresolv_flist = res;
|
2001-03-22 06:28:41 +01:00
|
|
|
}
|
|
|
|
|
}
|
2001-06-23 03:04:07 +02:00
|
|
|
|
|
|
|
|
struct postponed_handles_list_s *lhandle = late_handles;
|
|
|
|
|
late_handles = 0x0;
|
|
|
|
|
while (lhandle) {
|
|
|
|
|
struct postponed_handles_list_s *tmp = lhandle;
|
|
|
|
|
lhandle = lhandle->next;
|
|
|
|
|
postpone_vpi_symbol_lookup(tmp->handle, tmp->name);
|
|
|
|
|
free(tmp);
|
|
|
|
|
}
|
|
|
|
|
lhandle = late_handles;
|
|
|
|
|
while (lhandle) {
|
|
|
|
|
compile_errors += 1;
|
|
|
|
|
fprintf(stderr,
|
|
|
|
|
"unresolved vpi name lookup: %s\n",
|
|
|
|
|
lhandle->name);
|
|
|
|
|
lhandle = lhandle->next;
|
|
|
|
|
}
|
2001-03-11 01:29:38 +01:00
|
|
|
}
|
|
|
|
|
|
2001-05-09 01:32:26 +02:00
|
|
|
/*
|
|
|
|
|
* These functions are in support of the debugger.
|
|
|
|
|
*
|
|
|
|
|
* debug_lookup_functor
|
|
|
|
|
* Use a name to locate a functor address. This only gets the LSB
|
|
|
|
|
* of a vector, but it is enough to locate the object.
|
|
|
|
|
*/
|
|
|
|
|
vvp_ipoint_t debug_lookup_functor(const char*name)
|
|
|
|
|
{
|
|
|
|
|
symbol_value_t val = sym_get_value(sym_functors, name);
|
|
|
|
|
return val.num;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2001-03-11 01:29:38 +01:00
|
|
|
/*
|
|
|
|
|
* $Log: compile.cc,v $
|
2001-06-30 23:07:26 +02:00
|
|
|
* Revision 1.82 2001/06/30 21:07:26 steve
|
|
|
|
|
* Support non-const right shift (unsigned).
|
|
|
|
|
*
|
2001-06-23 20:26:26 +02:00
|
|
|
* Revision 1.81 2001/06/23 18:26:26 steve
|
|
|
|
|
* Add the %shiftl/i0 instruction.
|
|
|
|
|
*
|
2001-06-23 03:04:07 +02:00
|
|
|
* Revision 1.80 2001/06/23 01:04:07 steve
|
|
|
|
|
* Allow forward references of task scopes. (Stephan Boettcher)
|
|
|
|
|
*
|
2001-06-19 05:01:10 +02:00
|
|
|
* Revision 1.79 2001/06/19 03:01:10 steve
|
|
|
|
|
* Add structural EEQ gates (Stephan Boettcher)
|
|
|
|
|
*
|
2001-06-18 03:09:32 +02:00
|
|
|
* Revision 1.78 2001/06/18 01:09:32 steve
|
|
|
|
|
* More behavioral unary reduction operators.
|
|
|
|
|
* (Stephan Boettcher)
|
|
|
|
|
*
|
2001-06-17 01:45:05 +02:00
|
|
|
* Revision 1.77 2001/06/16 23:45:05 steve
|
|
|
|
|
* Add support for structural multiply in t-dll.
|
|
|
|
|
* Add code generators and vvp support for both
|
|
|
|
|
* structural and behavioral multiply.
|
|
|
|
|
*
|
2001-06-15 06:07:57 +02:00
|
|
|
* Revision 1.76 2001/06/15 04:07:58 steve
|
|
|
|
|
* Add .cmp statements for structural comparison.
|
|
|
|
|
*
|
2001-06-15 05:28:30 +02:00
|
|
|
* Revision 1.75 2001/06/15 03:28:31 steve
|
|
|
|
|
* Change the VPI call process so that loaded .vpi modules
|
|
|
|
|
* use a function table instead of implicit binding.
|
|
|
|
|
*
|
2001-06-10 19:12:51 +02:00
|
|
|
* Revision 1.74 2001/06/10 17:12:51 steve
|
|
|
|
|
* Instructions can forward reference functors.
|
|
|
|
|
*
|
2001-06-10 18:47:49 +02:00
|
|
|
* Revision 1.73 2001/06/10 16:47:49 steve
|
|
|
|
|
* support scan of scope from VPI.
|
|
|
|
|
*
|
2001-06-07 05:09:03 +02:00
|
|
|
* Revision 1.72 2001/06/07 03:09:03 steve
|
|
|
|
|
* Implement .arith/sub subtraction.
|
|
|
|
|
*
|
2001-06-05 05:05:41 +02:00
|
|
|
* Revision 1.71 2001/06/05 03:05:41 steve
|
|
|
|
|
* Add structural addition.
|
|
|
|
|
*
|
2001-05-31 06:12:43 +02:00
|
|
|
* Revision 1.70 2001/05/31 04:12:43 steve
|
|
|
|
|
* Make the bufif0 and bufif1 gates strength aware,
|
|
|
|
|
* and accurately propagate strengths of outputs.
|
|
|
|
|
*
|
2001-05-30 05:02:35 +02:00
|
|
|
* Revision 1.69 2001/05/30 03:02:35 steve
|
|
|
|
|
* Propagate strength-values instead of drive strengths.
|
|
|
|
|
*
|
2001-05-24 06:20:10 +02:00
|
|
|
* Revision 1.68 2001/05/24 04:20:10 steve
|
|
|
|
|
* Add behavioral modulus.
|
|
|
|
|
*
|
2001-05-22 06:08:16 +02:00
|
|
|
* Revision 1.67 2001/05/22 04:08:16 steve
|
|
|
|
|
* Get the initial inputs to functors set at xxxx.
|
|
|
|
|
*
|
2001-05-22 04:14:47 +02:00
|
|
|
* Revision 1.66 2001/05/22 02:14:47 steve
|
|
|
|
|
* Update the mingw build to not require cygwin files.
|
|
|
|
|
*
|
2001-05-20 02:46:12 +02:00
|
|
|
* Revision 1.65 2001/05/20 00:46:12 steve
|
|
|
|
|
* Add support for system function calls.
|
|
|
|
|
*
|
2001-05-13 23:05:06 +02:00
|
|
|
* Revision 1.64 2001/05/13 21:05:06 steve
|
|
|
|
|
* calculate the output of resolvers.
|
|
|
|
|
*
|
2001-05-12 22:38:06 +02:00
|
|
|
* Revision 1.63 2001/05/12 20:38:06 steve
|
|
|
|
|
* A resolver that understands some simple strengths.
|
|
|
|
|
*
|
2001-05-10 02:26:53 +02:00
|
|
|
* Revision 1.62 2001/05/10 00:26:53 steve
|
|
|
|
|
* VVP support for memories in expressions,
|
|
|
|
|
* including general support for thread bit
|
|
|
|
|
* vectors as system task parameters.
|
|
|
|
|
* (Stephan Boettcher)
|
|
|
|
|
*
|
2001-05-09 06:23:18 +02:00
|
|
|
* Revision 1.61 2001/05/09 04:23:18 steve
|
|
|
|
|
* Now that the interactive debugger exists,
|
|
|
|
|
* there is no use for the output dump.
|
|
|
|
|
*
|
2001-05-09 04:53:25 +02:00
|
|
|
* Revision 1.60 2001/05/09 02:53:25 steve
|
|
|
|
|
* Implement the .resolv syntax.
|
|
|
|
|
*
|
2001-05-09 01:59:33 +02:00
|
|
|
* Revision 1.59 2001/05/08 23:59:33 steve
|
|
|
|
|
* Add ivl and vvp.tgt support for memories in
|
|
|
|
|
* expressions and l-values. (Stephan Boettcher)
|
|
|
|
|
*
|
2001-05-09 01:32:26 +02:00
|
|
|
* Revision 1.58 2001/05/08 23:32:26 steve
|
|
|
|
|
* Add to the debugger the ability to view and
|
|
|
|
|
* break on functors.
|
|
|
|
|
*
|
|
|
|
|
* Add strengths to functors at compile time,
|
|
|
|
|
* and Make functors pass their strengths as they
|
|
|
|
|
* propagate their output.
|
|
|
|
|
*
|
2001-05-06 19:42:22 +02:00
|
|
|
* Revision 1.57 2001/05/06 17:42:22 steve
|
|
|
|
|
* Add the %ix/get instruction. (Stephan Boettcher)
|
|
|
|
|
*
|
2001-05-06 05:51:37 +02:00
|
|
|
* Revision 1.56 2001/05/06 03:51:37 steve
|
|
|
|
|
* Regularize the mode-42 functor handling.
|
|
|
|
|
*
|
2001-05-06 02:18:13 +02:00
|
|
|
* Revision 1.55 2001/05/06 00:18:13 steve
|
|
|
|
|
* Propagate non-x constant net values.
|
|
|
|
|
*
|
2001-05-06 01:55:46 +02:00
|
|
|
* Revision 1.54 2001/05/05 23:55:46 steve
|
|
|
|
|
* Add the beginnings of an interactive debugger.
|
|
|
|
|
*
|
2001-05-03 01:16:50 +02:00
|
|
|
* Revision 1.53 2001/05/02 23:16:50 steve
|
|
|
|
|
* Document memory related opcodes,
|
|
|
|
|
* parser uses numbv_s structures instead of the
|
|
|
|
|
* symbv_s and a mess of unions,
|
|
|
|
|
* Add the %is/sub instruction.
|
|
|
|
|
* (Stephan Boettcher)
|
|
|
|
|
*
|
2001-05-02 06:05:16 +02:00
|
|
|
* Revision 1.52 2001/05/02 04:05:17 steve
|
|
|
|
|
* Remove the init parameter of functors, and instead use
|
|
|
|
|
* the special C<?> symbols to initialize inputs. This is
|
|
|
|
|
* clearer and more regular.
|
|
|
|
|
*
|
2001-05-02 03:57:25 +02:00
|
|
|
* Revision 1.51 2001/05/02 01:57:25 steve
|
|
|
|
|
* Support behavioral subtraction.
|
|
|
|
|
*
|
2001-05-01 07:00:02 +02:00
|
|
|
* Revision 1.50 2001/05/01 05:00:02 steve
|
|
|
|
|
* Implement %ix/load.
|
|
|
|
|
*
|
2001-05-01 04:18:15 +02:00
|
|
|
* Revision 1.49 2001/05/01 02:18:15 steve
|
|
|
|
|
* Account for ipoint_input_index behavior in inputs_connect.
|
|
|
|
|
*
|
2001-05-01 03:09:39 +02:00
|
|
|
* Revision 1.48 2001/05/01 01:09:39 steve
|
|
|
|
|
* Add support for memory objects. (Stephan Boettcher)
|
2001-03-11 01:29:38 +01:00
|
|
|
*/
|
|
|
|
|
|