Add support for memory objects. (Stephan Boettcher)

This commit is contained in:
steve 2001-05-01 01:09:39 +00:00
parent 972e9c866b
commit 8b4befd4fc
14 changed files with 894 additions and 25 deletions

View File

@ -16,7 +16,7 @@
# 59 Temple Place - Suite 330
# Boston, MA 02111-1307, USA
#
#ident "$Id: Makefile.in,v 1.14 2001/04/24 02:23:59 steve Exp $"
#ident "$Id: Makefile.in,v 1.15 2001/05/01 01:09:39 steve Exp $"
#
#
SHELL = /bin/sh
@ -61,7 +61,7 @@ V = vpi_modules.o vpi_const.o vpi_iter.o vpi_mcd.o vpi_priv.o \
vpi_scope.o vpi_signal.o vpi_tasks.o vpi_time.o
O = main.o parse.o parse_misc.o lexor.o compile.o functor.o symbols.o \
codes.o vthread.o schedule.o tables.o udp.o $V
codes.o vthread.o schedule.o tables.o udp.o memory.o $V
vvp: $O
$(CXX) $(rdynamic) $(CXXFLAGS) $(LDFLAGS) -o vvp $O $(dllib)

View File

@ -1,7 +1,7 @@
/*
* Copyright (c) 2001 Stephen Williams (steve@icarus.com)
*
* $Id: README.txt,v 1.20 2001/04/30 04:51:27 steve Exp $
* $Id: README.txt,v 1.21 2001/05/01 01:09:39 steve Exp $
*/
VVP SIMULATION ENGINE
@ -281,6 +281,77 @@ This prevents any real functor being created and connected, and
instead leaves the input unconnected and initializes the wire with the
specified value, instead of the default z.
MEMORY STATEMENTS:
Three types of memory stetement perform (1) creation of a memory, (2)
connecting a read port to an existing memory, and (3) initializing the
memory's contents.
<label> .mem "name", <msb>,<lsb>, <last>,<first> ... ;
The pair of numbers <msb>,<lsb> defines the data port width. The pair
<last>,<first> defines the address range. Multiple address ranges are
allowed for multidimensional indexing.
Procedural access to the memory references the memory as single array
of bits. For this purpose, the number of bits in a memory word is
rounded up to the next multiple of four. That is, for an 18 bit wide
data bus, bit 0 is the lsb of the first word, bit 20 is the lsb of the
second word.
Structural read access is implemented in terms of address and data
ports. An address port is the concatinations of the smallest number of
address bits required to express each address range. The addresses
applied to the address port are expected to be withing the ranges
specified.
An port is a vector of functors that is wide enough to accept all
address bits and at least as wide as the requested subset of the data
port.
<label> .mem/port <memid>, <msb>,<lsb>, <symbols_list> ;
<label> identifies the vector of output functors, to allow connections
to the data output. <memid> is the label of the memory. <msb>,<lsb>
select a part of the data width. These are not relative to the data
port range defined for the memory. The LSB of the data word if here
referred to as 0, regardless to the range specified in the memory
definition. The <symbols_list> connects to the address inputs of this
port.
Any address change, or any change in the addressed memory contents is
imediately propagated to the port outputs.
To initialize a memory, use:
.mem/init <memid>[<start>],
val val val ...
;
<memid> is the lavbel of the memory. [<start>] is optional,
identifying the bits locattion where the first value is loaded.
<start> must be a multiple of four, and defaults to zero, if omitted.
The values are decimal or hex numbers (0x prefix), which may be
optionally separated by commata ','. Each number in the range 0..256
initializes four memory bits. Two bits form each byte for each memory
bit, in the ususal encoding.
Procedural access to the memory employs an index register to address a
bit location in the mememory, via the commands:
%load/mem <bit>, <memid>[<ix>] ;
%set/mem <memid>[<ix>], <bit> ;
%assign/mem <memid>[<ix>], <delay>, <bit> ;
??? is "/mem" required, or shall functors and memories share a
namespace? ???
<ix> identifies one of the four index registers of the current
thread. The value of register <ix> is the index in the memories bit
space, where each data word occupies a multiple of four bits.
EVENT STATEMENTS
Threads need to interact with the functors of a netlist synchronously,

View File

@ -19,11 +19,12 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#if !defined(WINNT)
#ident "$Id: codes.h,v 1.20 2001/04/18 04:21:23 steve Exp $"
#ident "$Id: codes.h,v 1.21 2001/05/01 01:09:39 steve Exp $"
#endif
# include "pointers.h"
# include "memory.h"
# include "vthread.h"
# include <stdio.h>
@ -38,6 +39,7 @@ typedef bool (*vvp_code_fun)(vthread_t thr, vvp_code_t code);
extern bool of_ADD(vthread_t thr, vvp_code_t code);
extern bool of_AND(vthread_t thr, vvp_code_t code);
extern bool of_ASSIGN(vthread_t thr, vvp_code_t code);
extern bool of_ASSIGN_MEM(vthread_t thr, vvp_code_t code);
extern bool of_CMPS(vthread_t thr, vvp_code_t code);
extern bool of_CMPU(vthread_t thr, vvp_code_t code);
extern bool of_CMPX(vthread_t thr, vvp_code_t code);
@ -47,17 +49,22 @@ extern bool of_DISABLE(vthread_t thr, vvp_code_t code);
extern bool of_END(vthread_t thr, vvp_code_t code);
extern bool of_FORK(vthread_t thr, vvp_code_t code);
extern bool of_INV(vthread_t thr, vvp_code_t code);
extern bool of_IX_ADD(vthread_t thr, vvp_code_t code);
extern bool of_IX_LOAD(vthread_t thr, vvp_code_t code);
extern bool of_IX_MUL(vthread_t thr, vvp_code_t code);
extern bool of_JMP(vthread_t thr, vvp_code_t code);
extern bool of_JMP0(vthread_t thr, vvp_code_t code);
extern bool of_JMP0XZ(vthread_t thr, vvp_code_t code);
extern bool of_JMP1(vthread_t thr, vvp_code_t code);
extern bool of_JOIN(vthread_t thr, vvp_code_t code);
extern bool of_LOAD(vthread_t thr, vvp_code_t code);
extern bool of_LOAD_MEM(vthread_t thr, vvp_code_t code);
extern bool of_MOV(vthread_t thr, vvp_code_t code);
extern bool of_NOOP(vthread_t thr, vvp_code_t code);
extern bool of_NORR(vthread_t thr, vvp_code_t code);
extern bool of_OR(vthread_t thr, vvp_code_t code);
extern bool of_SET(vthread_t thr, vvp_code_t code);
extern bool of_SET_MEM(vthread_t thr, vvp_code_t code);
extern bool of_VPI_CALL(vthread_t thr, vvp_code_t code);
extern bool of_WAIT(vthread_t thr, vvp_code_t code);
extern bool of_XNOR(vthread_t thr, vvp_code_t code);
@ -75,6 +82,7 @@ struct vvp_code_s {
unsigned number;
vvp_ipoint_t iptr;
vvp_cpoint_t cptr;
vvp_memory_t mem;
struct __vpiHandle*handle;
struct fork_extend*fork;
};
@ -117,6 +125,9 @@ extern void codespace_dump(FILE*fd);
/*
* $Log: codes.h,v $
* Revision 1.21 2001/05/01 01:09:39 steve
* Add support for memory objects. (Stephan Boettcher)
*
* Revision 1.20 2001/04/18 04:21:23 steve
* Put threads into scopes.
*

View File

@ -17,12 +17,13 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#if !defined(WINNT)
#ident "$Id: compile.cc,v 1.47 2001/04/30 03:53:19 steve Exp $"
#ident "$Id: compile.cc,v 1.48 2001/05/01 01:09:39 steve Exp $"
#endif
# include "compile.h"
# include "functor.h"
# include "udp.h"
# include "memory.h"
# include "symbols.h"
# include "codes.h"
# include "schedule.h"
@ -54,7 +55,10 @@ enum operand_e {
/* The operand is a pointer to code space */
OA_CODE_PTR,
/* The operand is a variable or net pointer */
OA_FUNC_PTR
OA_FUNC_PTR,
/* The operand is a memory, with index ... */
OA_MEM_X3, /* ... hardwired index register 3 */
OA_MEM_I1 /* ... index register in bit1 */
};
struct opcode_table_s {
@ -69,6 +73,7 @@ const static struct opcode_table_s opcode_table[] = {
{ "%add", of_ADD, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
{ "%and", of_AND, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
{ "%assign", of_ASSIGN, 3, {OA_FUNC_PTR, OA_BIT1, OA_BIT2} },
{ "%assign/m",of_ASSIGN_MEM,3,{OA_MEM_X3, OA_BIT1, OA_BIT2} },
{ "%cmp/s", of_CMPS, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
{ "%cmp/u", of_CMPU, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
{ "%cmp/x", of_CMPX, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
@ -76,17 +81,22 @@ const static struct opcode_table_s opcode_table[] = {
{ "%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} },
{ "%ix/add", of_IX_ADD, 2, {OA_BIT1, OA_NUMBER, OA_NONE} },
{ "%ix/load",of_IX_LOAD,3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
{ "%ix/mul", of_IX_MUL, 2, {OA_BIT1, OA_NUMBER, OA_NONE} },
{ "%jmp", of_JMP, 1, {OA_CODE_PTR, OA_NONE, OA_NONE} },
{ "%jmp/0", of_JMP0, 2, {OA_CODE_PTR, OA_BIT1, OA_NONE} },
{ "%jmp/0xz",of_JMP0XZ, 2, {OA_CODE_PTR, OA_BIT1, OA_NONE} },
{ "%jmp/1", of_JMP1, 2, {OA_CODE_PTR, OA_BIT1, OA_NONE} },
{ "%join", of_JOIN, 0, {OA_NONE, OA_NONE, OA_NONE} },
{ "%load", of_LOAD, 2, {OA_BIT1, OA_FUNC_PTR, OA_NONE} },
{ "%load/m", of_LOAD_MEM,2, {OA_BIT2, OA_MEM_I1, OA_NONE} },
{ "%mov", of_MOV, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
{ "%noop", of_NOOP, 0, {OA_NONE, OA_NONE, OA_NONE} },
{ "%nor/r", of_NORR, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
{ "%or", of_OR, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
{ "%set", of_SET, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} },
{ "%set/m", of_SET_MEM,2, {OA_MEM_I1, OA_BIT1, OA_NONE} },
{ "%wait", of_WAIT, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} },
{ "%xnor", of_XNOR, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
{ "%xor", of_XOR, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
@ -94,6 +104,9 @@ const static struct opcode_table_s opcode_table[] = {
};
static unsigned opcode_count = 0;
//static const unsigned opcode_count
// = sizeof(opcode_table)/sizeof(*opcode_table) - 1;
// No?
static int opcode_compare(const void*k, const void*r)
{
@ -406,6 +419,60 @@ void compile_udp_functor(char*label, char*type,
}
void compile_memory(char *label, char *name, int msb, int lsb,
unsigned idxs, long *idx)
{
vvp_memory_t mem = memory_create(label);
free(label);
memory_new(mem, name, lsb, msb, idxs, idx);
}
void compile_memory_port(char *label, char *memid,
unsigned msb, unsigned lsb,
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;
unsigned awidth = memory_addr_width(mem);
unsigned nfun = (awidth + 3)/4;
if (nfun < nbits)
nfun = nbits;
vvp_ipoint_t ix = functor_allocate(nfun);
assert(argc == awidth);
define_functor_symbol(label, ix);
free(label);
inputs_connect(ix, argc, argv);
memory_port_new(mem, ix, nbits, lsb);
}
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++;
}
void compile_event(char*label, char*type,
unsigned argc, struct symb_s*argv)
{
@ -625,6 +692,40 @@ void compile_code(char*label, char*mnem, comp_operands_t opa)
code->number = opa->argv[idx].numb;
break;
case OA_MEM_I1:
case OA_MEM_X3:
if (opa->argv[idx].ltype != L_SYMB) {
yyerror("operand format");
break;
}
code->mem = memory_find(opa->argv[idx].symb.text);
if (code->mem == 0) {
yyerror("functor undefined");
break;
}
if (opa->argv[idx].symb.idx >= 4) {
yyerror("index operand out of range (0..3)");
break;
}
switch(op->argt[idx]) {
case OA_MEM_I1:
code->bit_idx1 = (unsigned short)
opa->argv[idx].symb.idx;
break;
case OA_MEM_X3:
if (opa->argv[idx].symb.idx != 3)
yyerror("index operand must be 3");
break;
default:
break;
}
free(opa->argv[idx].symb.text);
break;
}
}
@ -963,6 +1064,9 @@ void compile_dump(FILE*fd)
/*
* $Log: compile.cc,v $
* Revision 1.48 2001/05/01 01:09:39 steve
* Add support for memory objects. (Stephan Boettcher)
*
* Revision 1.47 2001/04/30 03:53:19 steve
* Fix up functor inputs to support C<?> values.
*

View File

@ -19,7 +19,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#if !defined(WINNT)
#ident "$Id: compile.h,v 1.18 2001/04/25 04:35:05 steve Exp $"
#ident "$Id: compile.h,v 1.19 2001/05/01 01:09:39 steve Exp $"
#endif
# include <stdio.h>
@ -83,6 +83,20 @@ extern void compile_udp_functor(char*label, char*type,
extern char **compile_udp_table(char **table, char *row);
/*
* Memory Instances, Ports, and Initialization
*/
extern void compile_memory(char *label, char *name, int lsb, int msb,
unsigned idxs, long *idx);
extern void compile_memory_port(char *label, char *memid,
unsigned lsb, unsigned msb,
unsigned argc, struct symb_s *argv);
extern void compile_memory_init(char *memid, unsigned idx, unsigned char val);
/*
* The compile_event function takes the parts of the event statement
* and makes the various objects needed to simulate it. This includes
@ -160,6 +174,9 @@ extern void compile_dump(FILE*fd);
/*
* $Log: compile.h,v $
* Revision 1.19 2001/05/01 01:09:39 steve
* Add support for memory objects. (Stephan Boettcher)
*
* Revision 1.18 2001/04/25 04:35:05 steve
* Document the UDP implementation.
*

View File

@ -19,7 +19,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#if !defined(WINNT)
#ident "$Id: lexor.lex,v 1.14 2001/04/24 02:23:59 steve Exp $"
#ident "$Id: lexor.lex,v 1.15 2001/05/01 01:09:39 steve Exp $"
#endif
# include "parse_misc.h"
@ -77,7 +77,9 @@
".udp" { return K_UDP; }
".udp/c"(omb)? { return K_UDP_C; }
".udp/s"(equ)? { return K_UDP_S; }
".mem" { return K_MEM; }
".mem/p"(ort)? { return K_MEM_P; }
".mem/i"(nit)? { return K_MEM_I; }
/* instructions start with a % character. The compiler decides what
kind of instruction this really is. The few exceptions (that have
@ -130,6 +132,9 @@ int yywrap()
/*
* $Log: lexor.lex,v $
* Revision 1.15 2001/05/01 01:09:39 steve
* Add support for memory objects. (Stephan Boettcher)
*
* Revision 1.14 2001/04/24 02:23:59 steve
* Support for UDP devices in VVP (Stephen Boettcher)
*

422
vvp/memory.cc Normal file
View File

@ -0,0 +1,422 @@
/*
* Copyright (c) 2000 Stephen Williams (steve@icarus.com)
* Copyright (c) 2001 Stephan Boettcher <stephan@nevis.columbia.edu>
*
* 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)
#ident "$Id: memory.cc,v 1.1 2001/05/01 01:09:39 steve Exp $"
#endif
#include "memory.h"
#include "symbols.h"
#include "schedule.h"
#include <assert.h>
#include <malloc.h>
#include <string.h>
typedef struct vvp_memory_port_s *vvp_memory_port_t;
struct vvp_memory_s
{
char *name; // VPI scope.name
// Address port properties:
unsigned size; // total number of data words
unsigned awidth; // total number of address bits
unsigned a_idxs; // number of address indices
vvp_memory_index_t a_idx; // vector of address indices
// Data port properties:
unsigned width; // number of data bits
unsigned fwidth; // number of bytes (4bits) per data word
int msb, lsb; // Most/Least Significant data bit (VPI)
vvp_memory_bits_t bits; // Array of bits
vvp_memory_port_t addr_root; // Port list root;
};
unsigned memory_data_width(vvp_memory_t mem)
{
return mem->width;
}
unsigned memory_addr_width(vvp_memory_t mem)
{
return mem->awidth;
}
#define VVP_MEMORY_NO_ADDR ((int)0x80000000)
struct vvp_memory_index_s
{
int first; // first memory address
unsigned size; // number of valid addresses
unsigned awidth; // width of address port
};
struct vvp_memory_port_s : public vvp_fobj_s
{
vvp_memory_t mem;
vvp_ipoint_t ix;
vvp_memory_port_t next;
int cur_addr;
vvp_memory_bits_t cur_bits;
int bitoff;
int nbits;
};
// Compilation
static symbol_table_t memory_table;
vvp_memory_t memory_find(char *label)
{
symbol_value_t v = sym_get_value(memory_table, label);
return (vvp_memory_t)v.ptr;
}
vvp_memory_t memory_create(char *label)
{
if (!memory_table)
memory_table = new_symbol_table();
assert(!memory_find(label));
vvp_memory_t mem = new struct vvp_memory_s;
symbol_value_t v;
v.ptr = mem;
sym_set_value(memory_table, label, v);
return mem;
}
void memory_new(vvp_memory_t mem, char *name, int msb, int lsb,
unsigned idxs, long *idx)
{
mem->width = msb > lsb ? msb-lsb+1 : lsb-msb+1;
mem->msb = msb;
mem->lsb = lsb;
mem->fwidth = (mem->width+3)/4;
assert((idxs&1) == 0);
mem->a_idxs = idxs/2;
mem->a_idx = (vvp_memory_index_t)
malloc(mem->a_idxs*sizeof(struct vvp_memory_index_s));
assert(mem->a_idxs);
mem->size = 1;
mem->awidth = 0;
for (unsigned i=0; i < mem->a_idxs; i++)
{
vvp_memory_index_t x = mem->a_idx + i;
int msw = *(idx++);
int lsw = *(idx++);
x->size = msw - lsw + 1;
x->first = lsw;
int m = lsw ^ msw;
x->awidth = 0;
if (m < 0)
{
if (lsw < 0)
m = ~lsw | msw;
else
m = lsw | ~msw;
x->awidth++;
}
for (int c=1; c<m; c<<=1)
x->awidth ++;
mem->awidth += x->awidth;
mem->size *= x->size;
}
mem->bits = (vvp_memory_bits_t) malloc(mem->size * mem->fwidth);
assert(mem->bits);
memset(mem->bits, 0xaa, mem->size * mem->fwidth);
mem->addr_root = 0x0;
mem->name = name;
}
static void port_functor_set(vvp_ipoint_t i, functor_t f, bool push);
void memory_port_new(vvp_memory_t mem, vvp_ipoint_t ix,
unsigned nbits, unsigned bitoff)
{
vvp_memory_port_t a = new struct vvp_memory_port_s;
a->set = port_functor_set;
a->get = 0x0;
a->mem = mem;
a->ix = ix;
a->nbits = nbits;
a->bitoff = bitoff;
a->next = mem->addr_root;
a->mem->addr_root = a;
unsigned nfun = (a->mem->awidth+3)/4;
if (nfun < nbits)
nfun = nbits;
for (unsigned idx = 0; idx < nfun; idx ++)
{
vvp_ipoint_t ifdx = ipoint_index(ix, idx);
functor_t iobj = functor_index(ifdx);
iobj->ival = 0xaa;
iobj->oval = 0x02;
iobj->mode = M42;
iobj->out = 0;
iobj->obj = a;
}
a->cur_addr = VVP_MEMORY_NO_ADDR;
a->cur_bits = 0x0;
}
void memory_init_nibble(vvp_memory_t mem, unsigned idx, unsigned char val)
{
assert(idx < mem->size*mem->fwidth);
mem->bits[idx] = val;
}
// Utilities
inline static
vvp_memory_bits_t get_word(vvp_memory_t mem, int addr)
{
if (addr == VVP_MEMORY_NO_ADDR)
return 0x0;
// Compute the word index into the bits array.
unsigned waddr = 0;
for (unsigned i = 0; i<mem->a_idxs; i++)
{
vvp_memory_index_t x = mem->a_idx + i;
unsigned iwaddr = (addr - x->first) & ((1<<x->awidth) - 1);
waddr *= x->size;
// This fails for negative
if (iwaddr >= x->size)
return 0x0;
waddr += iwaddr;
addr >>= x->awidth;
}
assert(waddr < mem->size);
return mem->bits + waddr*mem->fwidth;
}
inline static
bool set_bit(vvp_memory_bits_t bits, int bit, unsigned char val)
{
int ix = bit/4;
int ip = 2*(bit%4);
bool r = ((bits[ix] >> ip) & 3) != val;
bits[ix] = (bits[ix] &~ (3<<ip)) | ((val&3) << ip);
return r;
}
inline static
unsigned char get_nibble(vvp_memory_bits_t bits, int bit)
{
if (!bits)
return 0xaa;
int ix = bit/4;
return bits[ix];
}
inline static
unsigned char get_bit(vvp_memory_bits_t bits, int bit)
{
return (get_nibble(bits, bit) >> (2*(bit&3))) & 3;
}
inline static
unsigned char functor_get_inputs(vvp_ipoint_t ip)
{
functor_t fp = functor_index(ip);
assert(fp);
return fp->ival;
}
inline static
unsigned char functor_get_input(vvp_ipoint_t ip)
{
unsigned char bits = functor_get_inputs(ip);
return (bits >> (2*ipoint_port(ip))) & 3;
}
static void update_addr(vvp_memory_port_t addr);
static
bool update_addr_bit(vvp_memory_port_t addr, vvp_ipoint_t ip)
{
unsigned abit = ip - addr->ix;
assert(abit >= 0 && abit < addr->mem->awidth);
int old = addr->cur_addr;
int abval = functor_get_input(ip);
if (abval>1)
addr->cur_addr = VVP_MEMORY_NO_ADDR;
else if (addr->cur_addr == VVP_MEMORY_NO_ADDR)
update_addr(addr);
else if (abval)
addr->cur_addr |= (1<<abit);
else
addr->cur_addr &=~ (1<<abit);
bool r = addr->cur_addr != old;
if (r)
addr->cur_bits = get_word(addr->mem, addr->cur_addr);
return r;
}
static
void update_addr(vvp_memory_port_t addr)
{
addr->cur_addr = 2;
for (unsigned i=0; i < addr->mem->awidth; i++)
{
update_addr_bit(addr, addr->ix+i);
if (addr->cur_addr == VVP_MEMORY_NO_ADDR)
break;
}
}
inline static
void update_data(vvp_memory_port_t data,
vvp_memory_bits_t bits)
{
assert(data);
for (int i=0; i < data->nbits; i++)
{
vvp_ipoint_t dx = ipoint_index(data->ix, i);
functor_t df = functor_index(dx);
unsigned char out = get_bit(bits, i + data->bitoff);
if (out != df->oval)
{
df->oval = out;
functor_propagate(dx);
}
}
}
static
void port_functor_set(vvp_ipoint_t i, functor_t f, bool push)
{
vvp_memory_port_t addr = (vvp_memory_port_t)f->obj;
if (update_addr_bit(addr, i))
update_data(addr, addr->cur_bits);
return;
}
static
void update_data_ports(vvp_memory_t mem, int addr, int bit,
unsigned char val)
{
vvp_memory_port_t a = mem->addr_root;
while (a)
{
if (addr == a->cur_addr)
{
vvp_memory_port_t d = a; // historic
int i = bit - d->bitoff;
if (i >= 0 && i < d->nbits)
{
vvp_ipoint_t ix = ipoint_index(d->ix, i);
functor_t df = functor_index(ix);
if (df->oval != val)
{
df->oval = val;
functor_propagate(ix);
}
}
}
a = a->next;
}
}
// %set/mem
void memory_set(vvp_memory_t mem, unsigned idx, unsigned char val)
{
if (!set_bit(mem->bits, idx, val))
return;
update_data_ports(mem, idx/(4*mem->fwidth), idx%(4*mem->fwidth), val);
}
// %load/mem
unsigned memory_get(vvp_memory_t mem, unsigned idx)
{
return get_bit(mem->bits, idx);
}
// %assign/mem event scheduling
struct mem_assign_s: public vvp_gen_event_s
{
union
{
vvp_memory_t mem;
struct mem_assign_s *next;
};
unsigned long idx;
};
static struct mem_assign_s* ma_free_list = 0;
inline static struct mem_assign_s* ma_alloc()
{
struct mem_assign_s* cur = ma_free_list;
if (!cur)
cur = (struct mem_assign_s*) malloc(sizeof(struct mem_assign_s));
else
ma_free_list = cur->next;
return cur;
}
inline static void ma_free(struct mem_assign_s* cur)
{
cur->next = ma_free_list;
ma_free_list = cur;
}
static void run_mem_assign(vvp_gen_event_t obj, unsigned char val)
{
struct mem_assign_s *e = (struct mem_assign_s *) obj;
memory_set(e->mem, e->idx, val);
ma_free(e);
}
void schedule_memory(vvp_memory_t mem, unsigned idx,
unsigned char val, unsigned delay)
{
struct mem_assign_s *e = ma_alloc();
e->run = run_mem_assign;
e->mem = mem;
e->idx = idx;
schedule_generic(e, val, delay);
}

57
vvp/memory.h Normal file
View File

@ -0,0 +1,57 @@
#ifndef __memory_H // -*- c++ -*-
#define __memory_H
/*
* Copyright (c) 2001 Stephen Williams (steve@icarus.com)
* Copyright (c) 2001 Stephan Boettcher <stephan@nevis.columbia.edu>
*
* 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)
#ident "$Id: memory.h,v 1.1 2001/05/01 01:09:39 steve Exp $"
#endif
#include "pointers.h"
#include "functor.h"
typedef struct vvp_memory_s *vvp_memory_t;
typedef unsigned char *vvp_memory_bits_t;
typedef struct vvp_memory_index_s *vvp_memory_index_t;
void memory_new(vvp_memory_t mem, char *name, int lsb, int msb,
unsigned idxs, long *idx);
void memory_port_new(vvp_memory_t mem, vvp_ipoint_t ix,
unsigned nbits, unsigned bitoff);
void memory_init_nibble(vvp_memory_t mem, unsigned idx, unsigned char val);
void memory_set(vvp_memory_t mem, unsigned idx, unsigned char val);
unsigned memory_get(vvp_memory_t mem, unsigned idx);
void schedule_memory(vvp_memory_t mem, unsigned idx,
unsigned char val, unsigned delay);
unsigned memory_addr_width(vvp_memory_t mem);
unsigned memory_data_width(vvp_memory_t mem);
vvp_memory_t memory_find(char *label);
vvp_memory_t memory_create(char *label);
/*
* $Log: memory.h,v $
* Revision 1.1 2001/05/01 01:09:39 steve
* Add support for memory objects. (Stephan Boettcher)
*
*/
#endif

View File

@ -19,7 +19,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#if !defined(WINNT)
#ident "$Id: parse.y,v 1.22 2001/04/24 02:23:59 steve Exp $"
#ident "$Id: parse.y,v 1.23 2001/05/01 01:09:39 steve Exp $"
#endif
# include "parse_misc.h"
@ -54,6 +54,7 @@ extern FILE*yyin;
%token K_EVENT K_EVENT_OR K_FUNCTOR K_NET K_NET_S K_SCOPE K_THREAD
%token K_UDP K_UDP_C K_UDP_S
%token K_MEM K_MEM_P K_MEM_I
%token K_VAR K_VAR_S K_vpi_call K_disable K_fork
%token K_vpi_module
@ -65,7 +66,7 @@ extern FILE*yyin;
%token <vect> T_VECTOR
%type <symb> symbol symbol_opt
%type <symbv> symbols symbols_net
%type <symbv> symbols symbols_net numbers
%type <text> label_opt
%type <opa> operand operands operands_opt
%type <table> udp_table
@ -128,6 +129,17 @@ statement
{ compile_udp_functor($1, $3, $5.cnt, $5.vect); }
/* Memory. Definition, port, initialization */
| T_LABEL K_MEM T_STRING ',' T_NUMBER ',' T_NUMBER ',' numbers ';'
{ compile_memory($1, $3, $5, $7, $9.cnt, $9.nvec); }
| T_LABEL K_MEM_P T_SYMBOL ',' T_NUMBER ',' T_NUMBER ',' symbols ';'
{ compile_memory_port($1, $3, $5, $7, $9.cnt, $9.vect); }
| mem_init_stmt
/* Event statements take a label, a type (the first T_SYMBOL) and a
list of inputs. If the type is instead a string, then we have a
named event instead. */
@ -323,6 +335,21 @@ symbols
;
numbers
: T_NUMBER
{ struct numbv_s obj;
numbv_init(&obj);
numbv_add(&obj, $1);
$$ = obj;
}
| numbers ',' T_NUMBER
{ struct numbv_s obj = $1;
numbv_add(&obj, $3);
$$ = obj;
}
;
symbols_net
: symbol_opt
{ struct symbv_s obj;
@ -358,6 +385,7 @@ symbol_opt
{ $$.text = 0;
$$.idx = 0;
}
;
udp_table
: T_STRING
@ -366,6 +394,18 @@ udp_table
{ $$ = compile_udp_table($1, $3); }
;
mem_init_stmt
: K_MEM_I symbol ',' T_NUMBER o_komma
{ compile_memory_init($2.text, $2.idx, $4); }
| mem_init_stmt T_NUMBER o_komma
{ compile_memory_init(0x0, 0, $2); }
;
o_komma
: /* empty */
| ','
;
%%
int compile_design(const char*path)
@ -385,6 +425,9 @@ int compile_design(const char*path)
/*
* $Log: parse.y,v $
* Revision 1.23 2001/05/01 01:09:39 steve
* Add support for memory objects. (Stephan Boettcher)
*
* Revision 1.22 2001/04/24 02:23:59 steve
* Support for UDP devices in VVP (Stephen Boettcher)
*

View File

@ -17,7 +17,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#if !defined(WINNT)
#ident "$Id: parse_misc.cc,v 1.3 2001/03/20 06:16:24 steve Exp $"
#ident "$Id: parse_misc.cc,v 1.4 2001/05/01 01:09:39 steve Exp $"
#endif
# include "parse_misc.h"
@ -46,6 +46,13 @@ void symbv_add(struct symbv_s*obj, struct symb_s item)
obj->cnt += 1;
}
void numbv_add(struct numbv_s*obj, long item)
{
obj->nvec = (long*) realloc(obj->nvec, (obj->cnt+1) * sizeof(long));
obj->nvec[obj->cnt] = item;
obj->cnt += 1;
}
void argv_init(struct argv_s*obj)
{
obj->argc = 0;
@ -62,6 +69,9 @@ void argv_add(struct argv_s*obj, vpiHandle item)
/*
* $Log: parse_misc.cc,v $
* Revision 1.4 2001/05/01 01:09:39 steve
* Add support for memory objects. (Stephan Boettcher)
*
* Revision 1.3 2001/03/20 06:16:24 steve
* Add support for variable vectors.
*

View File

@ -19,7 +19,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#if !defined(WINNT)
#ident "$Id: parse_misc.h,v 1.3 2001/03/20 06:16:24 steve Exp $"
#ident "$Id: parse_misc.h,v 1.4 2001/05/01 01:09:39 steve Exp $"
#endif
@ -50,12 +50,19 @@ struct symb_s {
struct symbv_s {
unsigned cnt;
struct symb_s*vect;
union {
struct symb_s*vect;
long*nvec;
};
};
extern void symbv_init(struct symbv_s*obj);
extern void symbv_add(struct symbv_s*obj, struct symb_s item);
#define numbv_s symbv_s
#define numbv_init symbv_init
extern void numbv_add(struct numbv_s*obj, long item);
struct argv_s {
@ -68,6 +75,9 @@ extern void argv_add(struct argv_s*obj, vpiHandle);
/*
* $Log: parse_misc.h,v $
* Revision 1.4 2001/05/01 01:09:39 steve
* Add support for memory objects. (Stephan Boettcher)
*
* Revision 1.3 2001/03/20 06:16:24 steve
* Add support for variable vectors.
*

View File

@ -17,11 +17,12 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#if !defined(WINNT)
#ident "$Id: schedule.cc,v 1.6 2001/04/21 00:34:39 steve Exp $"
#ident "$Id: schedule.cc,v 1.7 2001/05/01 01:09:39 steve Exp $"
#endif
# include "schedule.h"
# include "functor.h"
# include "memory.h"
# include "vthread.h"
# include <malloc.h>
# include <assert.h>
@ -32,6 +33,7 @@ struct event_s {
union {
vthread_t thr;
vvp_ipoint_t fun;
vvp_gen_event_t obj;
};
unsigned val :2;
unsigned type :2;
@ -39,11 +41,40 @@ struct event_s {
struct event_s*next;
struct event_s*last;
};
const unsigned TYPE_NOOP = 0;
const unsigned TYPE_GEN = 0;
const unsigned TYPE_THREAD = 1;
const unsigned TYPE_PROP = 2;
const unsigned TYPE_ASSIGN = 3;
/*
** These event_s will be required a lot, at high frequency.
** Once allocated, we never free them, but stash them away for next time.
*/
static struct event_s* free_list = 0;
inline static struct event_s* e_alloc()
{
struct event_s* cur = free_list;
if (!cur)
{
cur = (struct event_s*) malloc(sizeof(struct event_s));
// cur = (struct event_s*) calloc(1, sizeof(struct event_s));
}
else
{
free_list = cur->next;
// memset(cur, 0, sizeof(struct event_s));
}
return cur;
}
inline static void e_free(struct event_s* cur)
{
cur->next = free_list;
free_list = cur;
}
/*
* This is the head of the list of pending events.
*/
@ -114,8 +145,7 @@ static void schedule_event_(struct event_s*cur)
void schedule_vthread(vthread_t thr, unsigned delay)
{
struct event_s*cur = (struct event_s*)
calloc(1, sizeof(struct event_s));
struct event_s*cur = e_alloc();
cur->delay = delay;
cur->thr = thr;
@ -127,8 +157,7 @@ void schedule_vthread(vthread_t thr, unsigned delay)
void schedule_functor(vvp_ipoint_t fun, unsigned delay)
{
struct event_s*cur = (struct event_s*)
calloc(1, sizeof(struct event_s));
struct event_s*cur = e_alloc();
cur->delay = delay;
cur->fun = fun;
@ -139,8 +168,7 @@ void schedule_functor(vvp_ipoint_t fun, unsigned delay)
void schedule_assign(vvp_ipoint_t fun, unsigned char val, unsigned delay)
{
struct event_s*cur = (struct event_s*)
calloc(1, sizeof(struct event_s));
struct event_s*cur = e_alloc();
cur->delay = delay;
cur->fun = fun;
@ -148,7 +176,18 @@ void schedule_assign(vvp_ipoint_t fun, unsigned char val, unsigned delay)
cur->type= TYPE_ASSIGN;
schedule_event_(cur);
}
void schedule_generic(vvp_gen_event_t obj, unsigned char val, unsigned delay)
{
struct event_s*cur = e_alloc();
cur->delay = delay;
cur->obj = obj;
cur->val = val;
cur->type= TYPE_GEN;
schedule_event_(cur);
}
static unsigned long schedule_time;
@ -188,14 +227,22 @@ void schedule_simulate(void)
functor_set(cur->fun, cur->val);
break;
case TYPE_GEN:
if (cur->obj && cur->obj->run)
cur->obj->run(cur->obj, cur->val);
break;
}
free(cur);
e_free(cur);
}
}
/*
* $Log: schedule.cc,v $
* Revision 1.7 2001/05/01 01:09:39 steve
* Add support for memory objects. (Stephan Boettcher)
*
* Revision 1.6 2001/04/21 00:34:39 steve
* Working %disable and reap handling references from scheduler.
*

View File

@ -19,7 +19,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#if !defined(WINNT)
#ident "$Id: schedule.h,v 1.4 2001/03/31 19:00:43 steve Exp $"
#ident "$Id: schedule.h,v 1.5 2001/05/01 01:09:39 steve Exp $"
#endif
# include "vthread.h"
@ -46,6 +46,20 @@ extern void schedule_functor(vvp_ipoint_t fun, unsigned delay);
extern void schedule_assign(vvp_ipoint_t fun, unsigned char val,
unsigned delay);
/*
* Create an abstract event.
*/
typedef struct vvp_gen_event_s *vvp_gen_event_t;
extern void schedule_generic(vvp_gen_event_t obj, unsigned char val,
unsigned delay);
struct vvp_gen_event_s
{
void (*run)(vvp_gen_event_t obj, unsigned char val);
};
/*
* This runs the simulator. It runs until all the functors run out or
* the simulation is otherwise finished.
@ -74,6 +88,9 @@ extern bool schedule_finished(void);
/*
* $Log: schedule.h,v $
* Revision 1.5 2001/05/01 01:09:39 steve
* Add support for memory objects. (Stephan Boettcher)
*
* Revision 1.4 2001/03/31 19:00:43 steve
* Add VPI support for the simulation time.
*

View File

@ -17,7 +17,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#if !defined(WINNT)
#ident "$Id: vthread.cc,v 1.29 2001/04/21 00:34:39 steve Exp $"
#ident "$Id: vthread.cc,v 1.30 2001/05/01 01:09:39 steve Exp $"
#endif
# include "vthread.h"
@ -82,6 +82,7 @@ struct vthread_s {
unsigned long pc;
/* These hold the private thread bits. */
unsigned char *bits;
long index[4];
unsigned nbits :16;
/* My parent sets this when it wants me to wake it up. */
unsigned schedule_parent_on_end :1;
@ -328,6 +329,13 @@ bool of_ASSIGN(vthread_t thr, vvp_code_t cp)
return true;
}
bool of_ASSIGN_MEM(vthread_t thr, vvp_code_t cp)
{
unsigned char bit_val = thr_get_bit(thr, cp->bit_idx2);
schedule_memory(cp->mem, thr->index[3], bit_val, cp->bit_idx1);
return true;
}
bool of_CMPS(vthread_t thr, vvp_code_t cp)
{
unsigned eq = 1;
@ -615,6 +623,32 @@ bool of_INV(vthread_t thr, vvp_code_t cp)
return true;
}
/*
** Index registers, signed arithmetic.
*/
bool of_IX_ADD(vthread_t thr, vvp_code_t cp)
{
int number = (int)cp->number; // signed
thr->index[cp->bit_idx1 & 3] += number;
return true;
}
bool of_IX_MUL(vthread_t thr, vvp_code_t cp)
{
int number = (int)cp->number; // signed
thr->index[cp->bit_idx1 & 3] *= number;
return true;
}
bool of_IX_LOAD(vthread_t thr, vvp_code_t cp)
{
// TODO
return true;
}
/*
* The various JMP instruction work simply by pulling the new program
* counter from the instruction and resuming. If the jump is
@ -676,6 +710,15 @@ bool of_LOAD(vthread_t thr, vvp_code_t cp)
return true;
}
bool of_LOAD_MEM(vthread_t thr, vvp_code_t cp)
{
assert(cp->bit_idx1 >= 4);
assert(cp->bit_idx2 < 4);
unsigned char val = memory_get(cp->mem, thr->index[cp->bit_idx1]);
thr_put_bit(thr, cp->bit_idx2, val);
return true;
}
bool of_MOV(vthread_t thr, vvp_code_t cp)
{
assert(cp->bit_idx1 >= 4);
@ -763,6 +806,15 @@ bool of_SET(vthread_t thr, vvp_code_t cp)
return true;
}
bool of_SET_MEM(vthread_t thr, vvp_code_t cp)
{
assert(cp->bit_idx2 < 4);
unsigned char val = thr_get_bit(thr, cp->bit_idx2);
memory_set(cp->mem, thr->index[cp->bit_idx1], val);
return true;
}
bool of_VPI_CALL(vthread_t thr, vvp_code_t cp)
{
// printf("thread %p: %%vpi_call\n", thr);
@ -873,6 +925,9 @@ bool of_ZOMBIE(vthread_t thr, vvp_code_t)
/*
* $Log: vthread.cc,v $
* Revision 1.30 2001/05/01 01:09:39 steve
* Add support for memory objects. (Stephan Boettcher)
*
* Revision 1.29 2001/04/21 00:34:39 steve
* Working %disable and reap handling references from scheduler.
*