CDL Support libraries

Gavin J Stark
Sept 11th 2007


This module defines a set of functions for creating and accessing a hierarchical memory. This is a potentially sparse memory of very large size, which can be read and written at any location and appropriate memory allocated for it at such times. Large unused portions of the memory are not allocated, and so a large address space can be simulated with ease.

The address space supported is 64-bits. This means that every address supplied to the module is t_sl_uint64.

Header file (including documentation)

/*a  Copyright 
  This file 'sl_hier_mem.h' copyright Gavin J Stark 2003, 2004, 2007
  This is free software; you can redistribute it and/or modify it however you wish,
  with no obligations
  This software is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even implied warranty of MERCHANTABILITY

/*a  Wrapper 
#ifdef __INC_SL_HIER_MEM
#define __INC_SL_HIER_MEM

/*a  Includes 
#include "sl_general.h"
#include "sl_mif.h"
#include "c_sl_error.h"

/*a  Defines 
//d This define specifies the maximum number of levels of hierarchy
//d permissible in a memory. 8 is way overkill.
#define SL_HIER_MAX_LEVEL (8)

/*a  Types 
/*t  t_sl_hier_mem_unwritten_data -- enumeration of how uninitilialized data should be read 
//d An enumeration of how an unwritten data word should be read back
//d when it is accessed. Currently no warnings are returned for such
//d accesses, but this should perhaps be added. The available types are
//d currently sl_hier_mem_unwritten_data_zero and
//d sl_hier_mem_unwritten_data_ones, which return all 0's or all
//d 1's for uninitialized data when it is read.
typedef enum
} t_sl_hier_mem_unwritten_data;

/*t  t_sl_hier_mem_ptr -- internal type, see main code for use 
typedef union t_sl_hier_mem_ptr *t_sl_hier_mem_submem_ptr;

/*t  t_sl_hier_mem_data_ptr -- basic type of pointer to data in the memory 
//d A pointer to the data in the memory. A value of this type is
//d returned, for example, when you request the location of a particular
//d address in the hierarchical memory. It is defined as a pointer to
//d unsigned char, as the memory is assumed to be (at the smallest) an
//d array of bytes; the actual size of each address location is specified
//d at creation, though.
typedef unsigned char *t_sl_hier_mem_data_ptr;

/*t  t_sl_hier_mem_range -- a range in the memory return 
//d When an iter is set up over a hierarchical memory instance this
//d structure is returned. It provides a start address and length of a
//d block of memory, and a pointer to the first entry in that memory.
typedef struct t_sl_hier_mem_range
    t_sl_uint64 start_address;
    int length; // Number of addresses
    t_sl_hier_mem_data_ptr data; // Pointer to data
} t_sl_hier_mem_range;

/*t  t_sl_hier_mem_ptr -- internal union of possible pointers 
typedef union t_sl_hier_mem_ptr
    t_sl_hier_mem_submem_ptr submem;
    t_sl_hier_mem_data_ptr   data; // NULL or points to mallocked area of size bytes_per_memory*page_size
} t_sl_hier_mem_ptr;

/*t  t_sl_hier_mem -- The actual external type 
//d  Single mallocked block, allocated using sl_hier_mem_create call
//d  Each level in the memory points to a t_sl_hier_submem_ptr array,
//d  unless page_shift[level+1] is 0, in which case it is t_sl_hier_mem_data_ptr
//d  However, if page_shift[0] is 0 then the top level has only one pointer in array[0]
//d  which is a t_sl_hier_mem_data_ptr - i.e. there is no hierarchy
//d  Always use sl_hier_mem_free to free this structure
typedef struct t_sl_hier_mem
    t_sl_uint64 total_size;
    int bytes_per_memory; // 1, 2, 4, or 4n bytes
    int num_levels;
    int page_size[SL_HIER_MAX_LEVEL];  //d Number of address in each level (mask has size bits set, shift[n-1]-shift[n] = size[n])
    int page_shift[SL_HIER_MAX_LEVEL]; //d address shift right that each level's masked address required (bottom level has 0)
    t_sl_uint64 page_mask[SL_HIER_MAX_LEVEL]; //d address mask that each level is for, post-shift (bottom level if shift is 0)
    t_sl_hier_mem_unwritten_data unwritten_data_method;

    int bit_start; // Used internally for MIF file reading

    t_sl_hier_mem_ptr array[1];
} t_sl_hier_mem;

/*a  Hierarchical memory functions 
/*f  sl_hier_mem_create 
//d Create a hierarchical memory of a given total size, with a hierarchy
//d layout specified. Each memory location will consume bytes_per_memory
//d bytes, and any access to unwrittend data will return data specified by
//d the unwritten_data_method.
//d The log2_page_sizes is a null-terminated array of integers specifying
//d how the total size should be broken up in to hierarchical page
//d tables. The last non-null entry specifies how large the actual memory
//d blocks should be (bytes_per_memory<<log2_page_size) - these are the
//d leaves, as it were, of the page table tree. The entry before
//d that specifies how many leaves should be gathered together at the next
//d higher level, and so on back to the top of the tree. There should be
//d enough levels/bits in each level to accomodate the total_size
//d given. If there are too many, then the leading entries in the
//d log2_page_sizes will be reduced or ignored as necessary, so that the
//d leaf size is always that specified last in the array. A good array,
//d then, for any memory from 20 bits up in address size, could be
//d {14,10,10,10,10,10,0}; the entries are consumed from the right, so
//d leaves will have 1024 (2^10) memory addresses, and there will be 1024
//d of these lumped together, an 1024 of those above up to the required amount.
//d If the total size is less than the size of the leaf block, then no
//d hierarchy will be imposed, and a simple memory block will be used. So,
//d there is very little penalty for using the hierarchical memories in
//d pretty much every reasonable sized simulation memory, say from 16kB upwards.
//d The function returns NULL on failure or a pointer to the memory on
//d success. The memory must be freed with sl_hier_mem_free.
extern t_sl_hier_mem *sl_hier_mem_create( t_sl_uint64 total_size,
                                          int *log2_page_sizes,
                                          t_sl_hier_mem_unwritten_data unwritten_data_method,
                                          int bytes_per_memory );

/*f  sl_hier_mem_free 
//d This call frees all the memory used by the hierarchical memory,
//d including the complete hierarchy.
extern void sl_hier_mem_free( t_sl_hier_mem *shm );

/*f  sl_hier_mem_read_mif 
//d This file reads a MIF file in to the memory given
//d The MIF file reader uses a c_sl_error to handle errors, with a corresponding 'user' string
//d A MIF file can specify an arbitrary address space, and only those addresses in the MIF file
//d starting at address_offset will be loaded in to the t_sl_hier_mem. Basically, the address
//d in the t_sl_hier_mem is MIF address - address_offset
//d Data specified in the MIF file that is outside the address range of the t_sl_hier_mem will be
//d ignored silently if error_on_out_of_range is 0, and it will generate a serious error if
//d error_on_out_of_range is 1.
//d Additionally, only a particular bit range of each MIF file data can be written to the t_sl_hier_mem
//d This is so that a t_sl_hier_mem can contain, for example, just bytes 2 and 3 of a DRAM memory
//d and the MIF file to initialize the memory does not need to be split up.
//d For this, bit_start would be 16, and the t_sl_hier_mem would be 2 bytes wide (at creation).
//d Finally, a callback is provided for each word written to the t_sl_hier_mem from the MIF file
//d This callback has probably got some vital purpose, but it is not known what uses it as yet.
extern t_sl_error_level sl_hier_mem_read_mif( t_sl_hier_mem *shm,
                                              c_sl_error *error,
                                              const char *filename,
                                              const char *user,
                                              t_sl_uint64 address_offset,
                                              int error_on_out_of_range,
                                              int bit_start,
                                              t_sl_mif_file_callback_fn callback,
                                              void *handle );
/*f  sl_hier_mem_find 
//d This call returns a pointer to where a particular memory location in
//d the hierarchical memory is stored. It returns NULL if the location is
//d outside the address range of the memory, or if the location has not
//d been allocated. However, if the 'alloc' arg is set to 1 then the
//d location will be allocated if possible and a pointer to this newly
//d allocated space returned.
extern t_sl_hier_mem_data_ptr sl_hier_mem_find( t_sl_hier_mem *shm, t_sl_uint64 address, int alloc );

/*f  sl_hier_mem_read 
//d This call returns the value of a location in a hierarchical memory,
//d for anything hierarchical memoryp to 32-bits wide. If the location
//d accessed is outside the range of the memory then 0 is returned. If the
//d address has not yet been allocated and alloc is 0 then 0 is currently
//d returned; if alloc is 1 then the address will be allocated, and the
//d correct uninitialized value returned.
extern unsigned int sl_hier_mem_read( t_sl_hier_mem *shm, t_sl_uint64 address, int alloc );

/*f  sl_hier_mem_write 
//d This call writes to a particular memory location in the hierarchical
//d memory. It will not write the location if it is outside the initially
//d specified memory size. It works for any number of bytes per memory
//d location. It will allocate the memory location if it has not already
//d been allocated.
extern void sl_hier_mem_write( t_sl_hier_mem *shm, t_sl_uint64 address, t_sl_hier_mem_data_ptr data );

/*f  sl_hier_mem_iter 
//d This call iterates through a hierarchical memory giving the allocated
//d ranges of the memory complete with where their data contents are.
//d If on entry range.length is <=0 then the search will start at address
//d 0, otherwise it will start at range.address+1. This permits a full
//d enumeration of the allocated spaces of the memory, with the initial
//d call having range.length=-1, and subsequent calls requiring no change
//d to range from a previous call. The complete contents of range are
//d filled on return: start_address, length, and pointer to data. The
//d length will currently always be the same, as it is the size of the
//d leaf block of the hierarchy.
//d The call returns 0 if no more blocks have been allocated after the
//d specified range (e.g. it returns 0 if no blocks are allocated at all
//d and range.length was -1 on entry); it returns 1 if a range was filled
//d out with a block after the specified input range.address.
extern int sl_hier_mem_iter( t_sl_hier_mem *shm, t_sl_hier_mem_range *range );

/*a  Wrapper 

/*a  Editor preferences and notes 
mode: c ***
c-basic-offset: 4 ***
c-default-style: (quote ((c-mode . "k&r") (c++-mode . "k&r"))) ***
outline-regexp: "/\\\*a\\\|[\t ]*\/\\\*[b-z][\t ]" ***
Main links Logo

Site map