aboutsummaryrefslogtreecommitdiffstats
path: root/include/mips_mem.h
blob: 9a62422dc8507af08d46deed67125cc7a26902d9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/*! \file mips_mem.h
    Defines the functions used to interact with simulated memory.
    
    Note that the notions of "memory/address space" and "RAM" are actually
    two related but distinct things (we will explore this more later).
    A memory space is some kind of addressable space that the CPU can
    read and write to, where addressable locations are identified by
    integers. For the moment we will only deal with one address space,
    but later on we'll see others. In this API, abstract memory spaces
    are accessed using the functions in \ref mips_mem_core, but they
    must be intially created using a device specific API from \ref mips_mem_devices.
    
    RAM is a particular kind of memory device, which maps reads and
    writes transactions at particular addresses to corresponding
    storage locations. ROM is another kind of memory device that you
    saw earlier. It is extremely common for multiple types of memory
    device to exist in one address space, but for now we will stick
    with the simple idea of having one RAM, which is created using mips_mem_create_ram.
*/
#ifndef mips_mem_header
#define mips_mem_header

#include "mips_core.h"

/* This allows the header to be used from both C and C++, so
programs can be written in either (or both) languages. */
#ifdef __cplusplus
extern "C"{
#endif
    
/*! \defgroup mips_mem Memory
    \addtogroup mips_mem
    @{
    
    \defgroup mips_mem_core Abstract Memory Interface
    \addtogroup mips_mem_core
    @{
*/

/*! Represents some sort of memory, but without saying
anything about how it is represented. See \ref mips_mem_h.

\struct mips_mem_provider    
*/
struct mips_mem_provider;

/*! An opaque handle to a memory space.

    We can pass this around without knowing who or what provides the
    memory. This is an example of an "opaque data type" http://en.wikipedia.org/wiki/Opaque_data_type
    and is commonly used in embedded systems and operating
    systems. An example you might have come across includes the
    FILE data-type used by fopen and fprintf in the C standard
    library.

    Because this is a pointer, we can safely give it the
    known value of 0 or NULL in order to get a known empty
    state. For example:
        
        mips_mem_h myMem=0; // Declare an empty handle
        
        if(some_condition)
            myMem=get_a_handle();
        
        if(myMem)
            do_something_with mem(myMem);
    
    So even without knowing what the data-structure is, we can still
    tell whether or not a handle is currently pointing at a
    data-structure.
*/
typedef struct mips_mem_provider *mips_mem_h;


/*! Perform a read transaction on the memory
    
    The implementation is expected to check that the transaction
    matches the alignment and block size requirements, and return an
    error if they are violated.
*/
mips_error mips_mem_read(
    mips_mem_h mem,		//!< Handle to target memory
    uint32_t address,	//!< Byte address to start transaction at
    uint32_t length,	//!< Number of bytes to transfer
    uint8_t *dataOut	//!< Receives the target bytes
);

/*! Perform a write transaction on the memory
    
    The implementation is expected to check that the transaction
    matches the alignment and block size requirements, and return an
    error if they are violated.
*/
mips_error mips_mem_write(
    mips_mem_h mem,	        //!< Handle to target memory
    uint32_t address,	    //!< Byte address to start transaction at
    uint32_t length,	    //!< Number of bytes to transfer
    const uint8_t *dataIn	//!< Receives the target bytes
);


/*! Release all resources associated with memory. The caller doesn't
    really know what is being released (it could be memory, it could
    be file handles), and shouldn't care. Calling mips_mem_free on an
    empty (zero) handle is legal. Calling mips_mem_free twice on the
    same non-empty handle is illegal, and the resulting behaviour is undefined
    (most likely a crash).
    
    A pattern that can be useful is:
    
        mips_mem_h h=0; // Initialise it to empty
        ...
        h=some_creator_function(...);
        ...
        use_memory_somehow(h);
        ...
        if(h){
            mips_mem_free(h);
            h=0; // Make sure it is never freed again
        }
*/
void mips_mem_free(mips_mem_h mem);

/*! @} */


/*! \defgroup mips_mem_devices Concrete Memory Devices
    \ingroup mips_mem_devices
    @{
*/

/*! Initialise a new RAM of the given size.

    The RAM will expect transactions to be at the granularity
    of the blockSize. This means any reads or writes must be aligned
    to the correct blockSize, and should consist of an integer number
    of blocks. For example, choosing blockSize=4 would result in a RAM
    that only supports aligned 32-bit reads and writes.
*/
mips_mem_h mips_mem_create_ram(
    uint32_t cbMem,	//!< Total number of bytes of ram
    uint32_t blockSize	//!< Granularity of transactions supported by RAM
);

/*!
    @}
    @}
*/


#ifdef __cplusplus
};
#endif

#endif