reimplement memory ports.

This commit is contained in:
steve 2005-03-09 04:52:40 +00:00
parent 72240c460e
commit 789c95b1c1
6 changed files with 133 additions and 387 deletions

View File

@ -1,7 +1,7 @@
/*
* Copyright (c) 2001 Stephen Williams (steve@icarus.com)
*
* $Id: README.txt,v 1.58 2005/03/03 04:33:10 steve Exp $
* $Id: README.txt,v 1.59 2005/03/09 04:52:40 steve Exp $
*/
VVP SIMULATION ENGINE
@ -306,42 +306,36 @@ allowed for multidimensional indexing. This statement creates the
memory array and makes it available to procedural code.
Procedural access to the memory references the memory as single array
of words.
of words, with the base address==0, and the last address the size (in
words) of the memory -1. It is up to the compiler to convert Verilog
index sets to a cannonical address. The multi-dimensional index set is
available for VPI use.
Structural read access is implemented in terms of address and data
ports. The addresses applied to the address port are expected to be
within the ranges specified, not based at zero.
in cannonical form.
A read port is a vector of functors that is wide enough to accept all
provided address bits and at least as wide as the requested subset of
the data port.
A read port is a functor that takes a single input, the read address,
and outputs the word value at the given (cannonical) address.
<label> .mem/port <memid>, <msb>,<lsb>, <aw>, <addr_bits> ;
<label> .mem/port <memid>, <address> ;
<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 is here
referred to as 0, regardless to the range specified in the memory
definition. <addr_bits> is a list of symbols, which connect to the
address inputs of this port. There are <aw> address bits (<aw> may
become a list of numbers, when multi-index memory ports become
supported).
to the data output. <memid> is the label of the memory.
Any address change, or any change in the addressed memory contents is
immediately propagated to the port outputs.
Any address inputchange, or any change in the addressed memory
contents, is immediately propagated to the port output.
A write port is a superset of a read port. It is a vector of functors
that is wide enough to accept the address bits, an event input, a
write enable input, and the data inputs.
A write port is a superset of a read port. It is a 4-input functor
that accepts the word address, an event input, a write enable input,
and the data input.
<label> .mem/port <memid>, <msb>,<lsb>, <aw>, <addr_bits>,
<event>, <we>, <data> ;
<label> .mem/port <memid>, <address>, <event>, <we>, <data> ;
<event> is an event functor that triggers a write, if the <we> input
is true. <data> is a list of symbols that connect to the data input
is true. <data> is the input that connect to the data input
port. For asynchronous transparent write operation, connect
<event> to C<z>, the RAM will transparently follow any changes on
<event> to C4<z>, the RAM will transparently follow any changes on
address and data lines, while <we> is true.
There is no Verilog construct that calls for a structural write port

View File

@ -17,7 +17,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CVS_IDENT
#ident "$Id: compile.cc,v 1.189 2005/03/03 04:33:10 steve Exp $"
#ident "$Id: compile.cc,v 1.190 2005/03/09 04:52:40 steve Exp $"
#endif
# include "arith.h"
@ -1273,33 +1273,21 @@ void compile_memory(char *label, char *name, int msb, int lsb,
}
void compile_memory_port(char *label, char *memid,
unsigned msb, unsigned lsb,
unsigned naddr,
unsigned argc, struct symb_s *argv)
{
#if 0
vvp_memory_t mem = memory_find(memid);
free(memid);
assert(mem);
vvp_memory_t mem = memory_find(memid);
free(memid);
assert(mem);
// This is not a Verilog bit range.
// This is a data port bit range.
assert (lsb >= 0 && lsb<=msb);
assert (msb < memory_data_width(mem));
unsigned nbits = msb-lsb+1;
vvp_net_t*ptr = new vvp_net_t;
vvp_fun_memport*fun = new vvp_fun_memport(mem, ptr);
ptr->fun = fun;
bool writable = argc >= (naddr + 2 + nbits);
define_functor_symbol(label, ptr);
free(label);
vvp_ipoint_t ix = memory_port_new(mem, nbits, lsb, naddr, writable);
define_functor_symbol(label, ix);
free(label);
inputs_connect(ix, argc, argv);
free(argv);
#else
fprintf(stderr, "XXXX compile_memory_port not implemented.\n");
#endif
inputs_connect(ptr, argc, argv);
free(argv);
}
/*
@ -1657,6 +1645,9 @@ void compile_param_string(char*label, char*name, char*str, char*value)
/*
* $Log: compile.cc,v $
* Revision 1.190 2005/03/09 04:52:40 steve
* reimplement memory ports.
*
* Revision 1.189 2005/03/03 04:33:10 steve
* Rearrange how memories are supported as vvp_vector4 arrays.
*

View File

@ -19,7 +19,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CVS_IDENT
#ident "$Id: compile.h,v 1.63 2005/03/03 04:33:10 steve Exp $"
#ident "$Id: compile.h,v 1.64 2005/03/09 04:52:40 steve Exp $"
#endif
# include <stdio.h>
@ -200,8 +200,6 @@ 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 naddr,
unsigned argc, struct symb_s *argv);
extern void compile_memory_init(char *memid, unsigned idx, long val);
@ -297,6 +295,9 @@ extern void compile_net(char*label, char*name,
/*
* $Log: compile.h,v $
* Revision 1.64 2005/03/09 04:52:40 steve
* reimplement memory ports.
*
* Revision 1.63 2005/03/03 04:33:10 steve
* Rearrange how memories are supported as vvp_vector4 arrays.
*

View File

@ -19,7 +19,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CVS_IDENT
#ident "$Id: memory.cc,v 1.25 2005/03/06 17:07:48 steve Exp $"
#ident "$Id: memory.cc,v 1.26 2005/03/09 04:52:40 steve Exp $"
#endif
#include "memory.h"
@ -51,31 +51,11 @@ struct vvp_memory_s
vvp_vector4_t*words;
// List of ports into this memory.
vvp_memory_port_t port_list;
class vvp_fun_memport* port_list;
};
#define VVP_MEMORY_NO_ADDR ((int)0x80000000)
#if 0
struct vvp_memory_port_s : public functor_s
{
void set(vvp_ipoint_t i, bool push, unsigned val, unsigned str);
vvp_memory_t mem;
vvp_ipoint_t ix;
unsigned naddr;
vvp_memory_port_t next;
int cur_addr;
vvp_memory_bits_t cur_bits;
unsigned bitoff;
unsigned nbits;
bool writable;
};
#endif
// Compilation
static symbol_table_t memory_table = 0;
@ -209,51 +189,11 @@ void memory_set_word(vvp_memory_t mem, unsigned addr, vvp_vector4_t val)
{
memory_init_word(mem, addr, val);
if (mem->port_list)
fprintf(stderr, "XXXX memory_set_word(%u, ...)"
" not fully implemented\n", addr);
}
#if 0
vvp_ipoint_t memory_port_new(vvp_memory_t mem,
unsigned nbits, unsigned bitoff,
unsigned naddr, bool writable)
{
unsigned nfun = naddr;
if (writable)
nfun += 2 + nbits;
nfun = (nfun+3)/4;
if (nfun < nbits)
nfun = nbits;
vvp_memory_port_t a = new struct vvp_memory_port_s;
a->mem = mem;
a->naddr = naddr;
a->writable = writable;
a->nbits = nbits;
a->bitoff = bitoff;
a->next = mem->addr_root;
mem->addr_root = a;
a->ix = functor_allocate(nfun);
functor_define(a->ix, a);
if (nfun > 1)
{
extra_ports_functor_s *fu = new extra_ports_functor_s[nfun-1];
for (unsigned i = 0; i< nfun - 1; i++) {
fu[i].base_ = a->ix;
functor_define(ipoint_index(a->ix, i+1), fu+i);
for (vvp_fun_memport*cur = mem->port_list
; cur ; cur = cur->next_) {
cur->check_word_change(addr);
}
}
a->cur_addr = VVP_MEMORY_NO_ADDR;
a->cur_bits = 0x0;
return a->ix;
}
#endif
void schedule_memory(vvp_memory_t mem, unsigned addr,
vvp_vector4_t val, unsigned long delay)
@ -261,279 +201,58 @@ void schedule_memory(vvp_memory_t mem, unsigned addr,
fprintf(stderr, "XXXX Forgot how to schedule memory write.\n");
}
// Utilities
#if 0
inline static
vvp_memory_bits_t get_word_ix(vvp_memory_t mem, unsigned idx)
vvp_fun_memport::vvp_fun_memport(vvp_memory_t mem, vvp_net_t*net)
: mem_(mem), net_(net)
{
return mem->bits + idx*mem->fwidth;
}
#endif
#if 0
inline static
vvp_memory_bits_t get_word(vvp_memory_t mem, int addr)
{
assert(mem->a_idxs==1);
unsigned waddr = addr - mem->a_idx[0].first;
if (waddr >= mem->size)
return 0x0;
return get_word_ix(mem, waddr);
}
#endif
#if 0
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;
}
#endif
#if 0
inline static
unsigned char get_nibble(vvp_memory_bits_t bits, int bit)
{
if (!bits)
return 0xaa;
int ix = bit/4;
return bits[ix];
}
#endif
#if 0
inline static
unsigned char get_bit(vvp_memory_bits_t bits, int bit)
{
return (get_nibble(bits, bit) >> (2*(bit&3))) & 3;
}
#endif
#if 0
inline static
unsigned char functor_get_inputs(vvp_ipoint_t ip)
{
functor_t fp = functor_index(ip);
assert(fp);
return fp->ival;
}
#endif
#if 0
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;
}
#endif
#if 0
static
bool update_addr_bit(vvp_memory_port_t addr, vvp_ipoint_t ip)
{
unsigned abit = ip - addr->ix;
assert(abit >= 0 && abit < addr->naddr);
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);
addr->cur_bits = get_word(addr->mem, addr->cur_addr);
return addr->cur_addr != old;
}
#endif
#if 0
static
void update_addr(vvp_memory_port_t addr)
{
addr->cur_addr = 0;
for (unsigned i=0; i < addr->naddr; i++)
{
update_addr_bit(addr, addr->ix+i);
if (addr->cur_addr == VVP_MEMORY_NO_ADDR)
break;
}
}
#endif
#if 0
inline static
void update_data(vvp_memory_port_t data)
{
assert(data);
for (unsigned 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(data->cur_bits, i + data->bitoff);
df->put_oval(out, true);
}
}
#endif
#if 0
static
void update_data_ports(vvp_memory_t mem, vvp_memory_bits_t bits, int bit,
unsigned char val)
{
if (!bits)
return;
vvp_memory_port_t a = mem->addr_root;
while (a)
{
if (bits == a->cur_bits)
{
unsigned i = bit - a->bitoff;
if (i < a->nbits)
{
vvp_ipoint_t ix = ipoint_index(a->ix, i);
functor_t df = functor_index(ix);
df->put_oval(val, true);
}
}
a = a->next;
}
}
#endif
#if 0
static inline
void write_event(vvp_memory_port_t p)
{
if (!p->cur_bits)
return;
unsigned we = functor_get_input(p->ix + p->naddr + 1);
if (!we)
return;
for (unsigned i=0; i < p->nbits; i++)
{
unsigned val = functor_get_input(p->ix + p->naddr + 2 + i);
if (set_bit(p->cur_bits, i + p->bitoff, val))
{
// if a write would change the memory bit, but <we> is
// undefined (x or z), set the bit to x.
if (we > 1)
{
set_bit(p->cur_bits, i + p->bitoff, 2);
val = 2;
}
update_data_ports(p->mem, p->cur_bits, i + p->bitoff, val);
}
}
}
#endif
#if 0
void vvp_memory_port_s::set(vvp_ipoint_t i, bool, unsigned val, unsigned)
{
// !attention! "i" may not correspond to "this"
functor_t ifu = functor_index(i);
ifu->put(i, val);
if (i < ix+naddr)
{
if (update_addr_bit(this, i))
update_data(this);
}
// port ix+naddr is the write clock. If its input value is
// undefined, we do asynchronous write. Else any event on ix+naddr
// is a valid write clock edge. Connect an appropriate edge event
// functor.
if (i == ix+naddr
|| (writable && functor_get_input(ix+naddr) == 3))
{
assert(writable);
write_event(this);
}
}
#endif
// %set/mem
#if 0
void memory_set(vvp_memory_t mem, unsigned idx, unsigned char val)
{
if (idx/4 >= (mem->size * mem->fwidth))
return;
if (!set_bit(mem->bits, idx, val))
return;
unsigned widx = idx/(4*mem->fwidth);
unsigned bidx = idx%(4*mem->fwidth);
update_data_ports(mem, get_word_ix(mem, widx), bidx, val);
}
#endif
// %load/mem
#if 0
unsigned memory_get(vvp_memory_t mem, unsigned idx)
{
if (idx/4 >= (mem->size * mem->fwidth))
return 2;
return get_bit(mem->bits, idx);
}
#endif
// %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;
addr_ = 0;
next_ = mem_->port_list;
mem_->port_list = this;
}
inline static void ma_free(struct mem_assign_s* cur)
vvp_fun_memport::~vvp_fun_memport()
{
cur->next = ma_free_list;
ma_free_list = cur;
}
#if 0
static void run_mem_assign(vvp_gen_event_t obj, unsigned char val)
void vvp_fun_memport::recv_vec4(vvp_net_ptr_t port, vvp_vector4_t bit)
{
struct mem_assign_s *e = (struct mem_assign_s *) obj;
memory_set(e->mem, e->idx, val);
ma_free(e);
bool addr_valid_flag;
switch (port.port()) {
case 0: // Address input
addr_valid_flag = vector4_to_value(bit, addr_);
if (! addr_valid_flag)
addr_ = memory_word_count(mem_);
bit = memory_get_word(mem_, addr_);
vvp_send_vec4(port.ptr()->out, bit);
break;
default:
fprintf(stdout, "XXXX write ports not implemented.\n");
assert(0);
}
}
#endif
/*
* This function is called by the memory itself to tell this port that
* the given address had a content change. The device itself figures
* out what to do with that information.
*/
void vvp_fun_memport::check_word_change(unsigned long addr)
{
if (addr != addr_)
return;
vvp_vector4_t bit = memory_get_word(mem_, addr_);
vvp_send_vec4(net_->out, bit);
}
/*
* $Log: memory.cc,v $
* Revision 1.26 2005/03/09 04:52:40 steve
* reimplement memory ports.
*
* Revision 1.25 2005/03/06 17:07:48 steve
* Non blocking assign to memory words.
*

View File

@ -20,7 +20,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CVS_IDENT
#ident "$Id: memory.h,v 1.8 2005/03/03 04:33:10 steve Exp $"
#ident "$Id: memory.h,v 1.9 2005/03/09 04:52:40 steve Exp $"
#endif
#include "vvp_net.h"
@ -84,12 +84,6 @@ void schedule_memory(vvp_memory_t mem, unsigned addr,
*/
extern vvp_vector4_t memory_get_word(vvp_memory_t mem, unsigned idx);
#if 0
vvp_ipoint_t memory_port_new(vvp_memory_t mem,
unsigned nbits, unsigned bitoff,
unsigned naddr, bool writable);
#endif
/* Number of words in the memory. */
unsigned memory_word_count(vvp_memory_t mem);
@ -103,6 +97,47 @@ long memory_right_range(vvp_memory_t mem, unsigned ix);
long memory_word_left_range(vvp_memory_t mem);
long memory_word_right_range(vvp_memory_t mem);
/* vvp_fun_memport
* The vvp_fum_memport is a structural port into a vvp_memory_t
* object. The output is the word that is read from the addressed
* memory, and the inputs are the address and optional write controls.
*
* 0 -- Address
* This addresses the word in the memory. The output follows this
* address as it changes, and also follows the value of the addressed
* word.
*
* 1 -- Write event
*
* 2 -- Write enable
*
* 3 -- Write data
*
* NOTE: This functor is unique in that it needs to store the
* vvp_net_t pointer associated with it. It needs this because it can
* received input from other then its ports. Notably, the memory
* itself reports word changes.
*/
class vvp_fun_memport : public vvp_net_fun_t {
public:
explicit vvp_fun_memport(vvp_memory_t mem, vvp_net_t*net);
~vvp_fun_memport();
void recv_vec4(vvp_net_ptr_t port, vvp_vector4_t bit);
private:
vvp_memory_t mem_;
friend void memory_set_word(vvp_memory_t, unsigned, vvp_vector4_t);
void check_word_change(unsigned long address);
class vvp_fun_memport*next_;
unsigned long addr_;
vvp_net_t*net_;
};
/*
** Access to the memory symbol table.
**
@ -117,6 +152,9 @@ vvp_memory_t memory_create(char *label);
/*
* $Log: memory.h,v $
* Revision 1.9 2005/03/09 04:52:40 steve
* reimplement memory ports.
*
* Revision 1.8 2005/03/03 04:33:10 steve
* Rearrange how memories are supported as vvp_vector4 arrays.
*

View File

@ -19,7 +19,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CVS_IDENT
#ident "$Id: parse.y,v 1.67 2005/03/03 04:33:10 steve Exp $"
#ident "$Id: parse.y,v 1.68 2005/03/09 04:52:40 steve Exp $"
#endif
# include "parse_misc.h"
@ -159,8 +159,8 @@ statement
| 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 ',' T_NUMBER ',' symbols ';'
{ compile_memory_port($1, $3, $5, $7, $9, $11.cnt, $11.vect); }
| T_LABEL K_MEM_P T_SYMBOL ',' symbols ';'
{ compile_memory_port($1, $3, $5.cnt, $5.vect); }
| mem_init_stmt
@ -673,6 +673,9 @@ int compile_design(const char*path)
/*
* $Log: parse.y,v $
* Revision 1.68 2005/03/09 04:52:40 steve
* reimplement memory ports.
*
* Revision 1.67 2005/03/03 04:33:10 steve
* Rearrange how memories are supported as vvp_vector4 arrays.
*