orca-sim
Memory.cpp
Go to the documentation of this file.
1 /******************************************************************************
2  * This file is part of project ORCA. More information on the project
3  * can be found at the following repositories at GitHub's website.
4  *
5  * http://https://github.com/andersondomingues/orca-sim
6  * http://https://github.com/andersondomingues/orca-software
7  * http://https://github.com/andersondomingues/orca-mpsoc
8  * http://https://github.com/andersondomingues/orca-tools
9  *
10  * Copyright (C) 2018-2020 Anderson Domingues, <ti.andersondomingues@gmail.com>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License along
23  * with this program; if not, write to the Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 ******************************************************************************/
26 #include <inttypes.h>
27 
28 #include <string>
29 #include <sstream>
30 
31 #include "Memory.hpp"
32 #include "Signal.hpp"
33 
35 
36 Memory::Memory(std::string name, MemoryAddr size, MemoryAddr sram_base,
37  bool wipe, std::string binname) : UntimedModel(name) {
38  // creates a new array of MemoryType elements (must be disposed by dctor.)
39  _mem = new MemoryType[size];
40 
41  // set internal fields
42  _length = size;
43  _base = sram_base;
44 
45  // if wipe is true, write zeroes to the whole addressable space
46  if (wipe) this->Wipe();
47 
48  // if a binary file is provided, load that file into the base address
49  if (binname != "")
50  this->LoadBin(binname, _base, _length);
51 
52  #ifdef MEMORY_ENABLE_COUNTERS
53  // if counters are enabled, initiate the respective signals
54  // please note that these signals are not mapped to anywhere yet
55  _counter_nstore = new Signal<uint32_t>(GetName() + ".counters.store");
56  _counter_nload = new Signal<uint32_t>(GetName() + ".counters.load");
57 
58  // reset counters (must!)
59  _counter_nstore->Write(0);
60  _counter_nload->Write(0);
61  #endif
62 }
63 
64 #ifdef MEMORY_ENABLE_COUNTERS
65 // getters and setters for the counters' signals
66 Signal<uint32_t>* Memory::GetSignalCounterStore() {
67  return _counter_nstore;
68 }
69 
70 Signal<uint32_t>* Memory::GetSignalCounterLoad() {
71  return _counter_nload;
72 }
73 #endif
74 
75 void Memory::Write(MemoryAddr addr, MemoryType* data, uint32_t length) {
76  #ifdef MEMORY_WRITE_ADDRESS_CHECKING
77  // check whether we're trying to access an address
78  // lower than the base address
79  if (addr < _sram_base) {
80  stringstream s;
81  s << this->GetName() << ": unable to write to addr (0x" << std::hex
82  << addr << ") lower than sram base.";
83 
84  throw std::runtime_error(s.str());
85  }
86 
87  // check whether we're trying to access and address
88  // higher than the last address
89  if ((addr + length -1) > GetLastAddr()) {
90  stringstream s;
91  s << this->GetName() << ": unable to write to addr (0x" << std::hex
92  << addr << ") higher than last mapped address of (0x" << std::hex
93  << GetLastAddr() << ").";
94  throw std::runtime_error(s.str());
95  }
96  #endif
97 
98  #ifdef MEMORY_ENABLE_COUNTERS
99  // increment number of stores into nstore counter
100  _counter_nstore->Inc(1);
101  #endif
102 
103  // write data to the memory
104  // @TODO(ad): check whether this has the best performance
105  for (uint32_t i = 0; i < length; i++) {
106  _mem[addr - _base] = data[i];
107  addr++;
108  }
109 }
110 
111 void Memory::Read(MemoryAddr addr, MemoryType* buffer, uint32_t length) {
112  #ifdef MEMORY_READ_ADDRESS_CHECKING
113  // check whether we'are trying to read from an address
114  // lower than the base address
115  if (addr < _sram_base) {
116  stringstream s;
117  s << this->GetName() << ": unable to read from addr (0x" << std::hex
118  << addr << ") lower than sram base.";
119  throw std::runtime_error(s.str());
120  }
121 
122  // check whether we're trying to read from an address
123  // higher than the last address
124  if ((addr + length) - 1 > GetLastAddr()) {
125  stringstream s;
126  s << this->GetName() << ": unable to read from addr (0x" << std::hex
127  << addr << ") higher than last mapped address of (0x" << std::hex
128  << GetLastAddr() << ").";
129  throw std::runtime_error(s.str());
130  }
131  #endif
132 
133  // read data from memory
134  // @TODO(ad): check whether this has the best performance
135  for (uint32_t i = 0; i < length; i++)
136  buffer[i] = _mem[(addr - _base) + i];
137 
138  #ifdef MEMORY_ENABLE_COUNTERS
139  // increment number of loades into nstore counter
140  _counter_nload->Inc(1);
141  #endif
142 }
143 
145  #ifdef MEMORY_MAP_ADDRESS_CHECKING
146  // check whether we're trying to get the pointer
147  // to a cell whose address is lower than the base
148  // address
149  if (addr < _sram_base) {
150  stringstream s;
151  s << this->GetName() << ": unable to map from to addr (0x" << std::hex
152  << addr << ") lower than sram base.";
153  throw std::runtime_error(s.str());
154  }
155 
156  // check whether we're trying to get the pointer
157  // to a cell whose address is higher than the last
158  // address
159  if (addr > GetLastAddr()) {
160  stringstream s;
161  s << this->GetName() << ": unable to map from to addr (0x" << std::hex
162  << addr << ") higher than last mapped address of (0x" << std::hex
163  << GetLastAddr() << ").";
164  throw std::runtime_error(s.str());
165  }
166  #endif
167 
168  // return the address to the requested cell
169  return &(_mem[addr - _base]);
170 }
171 
172 // return the base address (getter)
174  return _base;
175 }
176 
177 // return the memory size (getter)
178 uint32_t Memory::GetSize() {
179  return _length;
180 }
181 
182 // return the address of the last addressable cell
184  return (_base + _length) - 1;
185 }
186 
187 // wipe without parameters is a shortcut to wipeing
188 // the whole memory
189 void Memory::Wipe() {
190  this->Wipe(_base, _length);
191 }
192 
193 void Memory::Wipe(MemoryAddr base, uint32_t size) {
194  #ifdef MEMORY_WRITE_ADDRESS_CHECKING
195  // base must be less than the last address
196  if (base < _base)
197  throw std::runtime_error(this->GetName() +
198  ": unable to wipe from base (" + std::to_string(base) +
199  ") lower than sram base .");
200 
201  // base must be less than the last address
202  if (base > GetLastAddr())
203  throw std::runtime_error(this->GetName() +
204  ": unable to wipe from base (" + std::to_string(base) +
205  ") higher than sram base .");
206 
207  // base + size cannot go further than the last address
208  if (base + size > GetLastAddr())
209  throw std::runtime_error(this->GetName() +
210  ": unable to wipe that much length (" + std::to_string(size) +
211  ") higher than sram base + length.");
212  #endif
213 
214  // get the pointer to the desired base
215  MemoryType* range_ptr = _mem + (base - _base);
216 
217  // write zeros to the data to up to <size> cells.
218  for (uint32_t i = 0; i < size; i++)
219  range_ptr[i] = 0;
220 }
221 
222 void Memory::LoadBin(std::string filename, uint32_t base, uint32_t size) {
223  // try to open the file
224  std::ifstream f(filename, std::ios::binary | std::ios::in | std::ios::out);
225 
226  // if we could open the file properly, load it at the base address
227  if (f.is_open()) {
228  f.read(reinterpret_cast<char*>(&_mem[base -_base]),
229  sizeof(_mem[0]) * size);
230  f.close();
231 
232  // if could not open the file, throw exception
233  } else {
234  std::string err_msg = this->GetName() + ": unable to load '"
235  + filename + "'.";
236  throw std::runtime_error(err_msg);
237  }
238 }
239 
240 void Memory::SaveBin(std::string filename, uint32_t base, uint32_t size) {
241  std::ofstream f(filename, std::ifstream::binary);
242 
243  if (f.is_open()) {
244  f.write(reinterpret_cast<char*>(&_mem[base -_base]),
245  sizeof(_mem[0]) * size);
246  f.close();
247  } else {
248  std::string err_msg = this->GetName() +
249  ": unable to save'" + filename + "'.";
250  throw std::runtime_error(err_msg);
251  }
252 }
253 
254 // Dump is a shorthand for Dump(x, y)
255 void Memory::Dump() {
256  this->Dump(0, _length);
257 }
258 
259 void Memory::Dump(uint32_t base, uint32_t length) {
260  uint32_t k, l;
261 
262  // mask is necessary to correct a bug(?) when printing
263  // negative hexas.
264  uint32_t mask = 0x000000FF;
265  int8_t ch;
266 
267  // print in lines containing 16 bytes
268  for (k = 0; k < length; k += 16) {
269  // print the address
270  printf("\n%08x ", base + k);
271  for (l = 0; l < 16; l++) {
272  printf("%02x ", _mem[k + l + (base - _base)] & mask);
273  if (l == 7) putchar(' ');
274  }
275 
276  // print the data in ascii ("." is printed when no ascii char exists)
277  printf(" |");
278  for (l = 0; l < 16; l++) {
279  ch = _mem[k + l + (base - _base)];
280  if ((ch >= 32) && (ch <= 126))
281  putchar(ch);
282  else
283  putchar('.');
284  }
285  putchar('|');
286  }
287 }
288 
290  // delete all cells
291  delete [] _mem;
292 
293  #ifdef MEMORY_ENABLE_COUNTERS
294  // delete signals that hold counters
295  delete(_counter_nstore);
296  delete(_counter_nload);
297  #endif
298 }
MemoryType * GetMap(MemoryAddr addr)
Gets a pointer of MemoryType type that points to the cell in address <addr>.
Definition: Memory.cpp:144
void Write(uint32_t addr, MemoryType *data, uint32_t length)
Writes data to the memory.
Definition: Memory.cpp:75
MemoryAddr _length
The _length attribute denotes the number of cells of the memory module.
Definition: Memory.hpp:71
MemoryType * _mem
The _mem attribute is an array of MemoryType elements, where MemoryType is the smalled memory unit th...
Definition: Memory.hpp:63
void Wipe()
Write zeroes to the whole addressable memory space.
Definition: Memory.cpp:189
#define MemoryAddr
Definition: MemoryType.hpp:34
void Dump()
Display the contents of the whole memory on the output.
Definition: Memory.cpp:255
void SaveBin(std::string filename, MemoryAddr base, uint32_t size)
Write the contents of memory cells into a binary file.
Definition: Memory.cpp:240
void LoadBin(std::string filename, MemoryAddr base, uint32_t size)
Loads the content of a given file to the memory.
Definition: Memory.cpp:222
void Write(T val)
Writes some value to the bus.
Definition: Signal.cpp:127
MemoryAddr GetLastAddr()
Get the address of the last addressable memory cell.
Definition: Memory.cpp:183
MemoryAddr _base
The _base attribute indicates the first address of the memory module, an offset.
Definition: Memory.hpp:80
std::string GetName()
Getter method for the <_name> field.
Definition: Model.cpp:34
MemoryAddr GetBase()
(getter) Gets the base address, which is the first addressable memory cell in the module...
Definition: Memory.cpp:173
void Read(uint32_t addr, MemoryType *buffer, uint32_t length)
Reads data from a given memory location.
Definition: Memory.cpp:111
Untimed models represent hardware models whose clock period is irrelevant for the simulation...
This class models a memory module.
Definition: Memory.hpp:55
~Memory()
Destroy the Memory object.
Definition: Memory.cpp:289
MemoryAddr GetSize()
(getter) Gets the size (alternatively, length) of the memory module, representing the number of addre...
Definition: Memory.cpp:178
#define MemoryType
Definition: MemoryType.hpp:33