quic/qbox
Loading...
Searching...
No Matches
gs_memory.h
1/*
2 * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
3 * Author: GreenSocs 2022
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7
8#ifndef _GREENSOCS_BASE_COMPONENTS_MEMORY_H
9#define _GREENSOCS_BASE_COMPONENTS_MEMORY_H
10
11#include <fstream>
12#include <memory>
13
14#include <cci_configuration>
15#include <systemc>
16#include <tlm>
17#include <tlm_utils/multi_passthrough_target_socket.h>
18#include <scp/report.h>
19#include <scp/helpers.h>
20
21#include <loader.h>
22#include <memory_services.h>
23
24#include <tlm-extensions/shmem_extension.h>
25#include <module_factory_registery.h>
26#include <tlm_sockets_buswidth.h>
27#include <unordered_map>
28
29#ifndef _WIN32
30#include <fcntl.h>
31#include <sys/mman.h>
32#include <sys/stat.h>
33#endif
34
35namespace gs {
36
52#define ALIGNEDBITS 12
53
54template <unsigned int BUSWIDTH = DEFAULT_TLM_BUSWIDTH>
55class gs_memory : public sc_core::sc_module
56{
57 uint64_t m_size = 0;
58 uint64_t m_address;
59 bool m_address_valid = false;
60 bool m_relative_addresses;
61
62 SCP_LOGGER(());
63
64 // Templated on the power of 2 to use to divide the blocks up
65 template <unsigned int N = 2>
66 class SubBlock
67 {
68 SCP_LOGGER();
69 uint64_t m_len; // size of the bloc
70 uint64_t m_address; // this is an absolute address and this is the
71 // beginning of the bloc/Memory
73
74 uint8_t* m_ptr = nullptr;
75 std::array<std::unique_ptr<SubBlock>, (1 << N)> m_sub_blocks;
76 bool m_use_sub_blocks = false;
77
78 bool m_mapped = false;
79 ShmemIDExtension m_shmemID;
80
81 public:
82 SubBlock(uint64_t address, uint64_t len, gs_memory& mem): m_len(len), m_address(address), m_mem(mem)
83 {
84 SCP_TRACE(())("Init");
85 }
86
87 void doreset()
88 {
89 SCP_WARN((), m_mem.name())("Reset (block at offset {:x})", m_address);
90 for (unsigned int i = 0; i < N; i++) {
91 if (m_sub_blocks[i]) m_sub_blocks[i]->doreset();
92 }
93 if (m_mem.p_init_mem && m_ptr) {
94 memset(m_ptr, m_mem.p_init_mem_val, m_len);
95 }
96 }
97 SubBlock& access(uint64_t address)
98 {
99 // address is the address of where we want to write/read
100 // len is the size of the data
101
102 if (m_ptr && !m_use_sub_blocks) {
103 assert(address >= m_address);
104 return *this;
105 }
106 if (m_len > m_mem.p_max_block_size) {
107 m_use_sub_blocks = true;
108 }
109
110 if (!m_use_sub_blocks) {
111 if (!((std::string)m_mem.p_mapfile).empty()) {
112 if ((m_ptr = MemoryServices::get().map_file(((std::string)(m_mem.p_mapfile)).c_str(), m_len,
113 m_address)) != nullptr) {
114 m_mapped = true;
115 return *this;
116 }
117 }
118 if (m_mem.p_shmem) {
119 std::stringstream shmname_stream;
120 if (m_mem.p_shmem_prefix.get_value() != "")
121 shmname_stream << "/" << m_mem.p_shmem_prefix.get_value() << std::hex << getpid();
122 else
123 shmname_stream << "/" << std::hex << getpid() << "-" << std::hex
124 << MemoryServices::get().get_shmem_seg_num();
125 if (shmname_stream.str().size() > 31) { /*PSHMNAMLEN in Mac OS*/
126 size_t hash = std::hash<std::string>{}(
127 shmname_stream.str()); // hash length is 16 hex digits in 64 bit machines.
128 shmname_stream.str(""); // clear
129 shmname_stream << "/" << std::hex << hash;
130 }
131 std::string shmname = shmname_stream.str();
132 int shm_fd = -1;
133 if ((m_ptr = MemoryServices::get().map_mem_create(shmname.c_str(), m_len, &shm_fd)) != nullptr) {
134 m_mapped = true;
135 m_shmemID = ShmemIDExtension(shmname, (uint64_t)m_ptr, m_len, shm_fd);
136 return *this;
137 }
138 }
139 if ((m_ptr = MemoryServices::get().alloc(m_len)) != nullptr) {
140 if (m_mem.p_init_mem) memset(m_ptr, m_mem.p_init_mem_val, m_len);
141 return *this;
142 }
143
144 // else we failed to allocate, try with a smaller sub_block size.
145 m_use_sub_blocks = true;
146 }
147
148 if (m_len < m_mem.p_min_block_size) {
149 SCP_FATAL(m_mem.name()) << "Unable to allocate memory!"; // out of memory!
150 }
151
152 // return a index of sub_bloc
153 assert((m_len & ~(-1llu << N)) == 0);
154 uint64_t m_sub_size = m_len >> N;
155 int i = (address - m_address) / (m_sub_size);
156
157 if (!m_sub_blocks[i]) {
158 m_sub_blocks[i] = std::make_unique<SubBlock<N>>((i * m_sub_size) + m_address, m_sub_size, m_mem);
159 }
160 return m_sub_blocks[i]->access(address);
161 }
162
163 uint64_t read_sub_blocks(uint8_t* data, uint64_t offset, uint64_t len)
164 {
165 uint64_t block_offset = offset - m_address;
168
169 memcpy(data, &m_ptr[block_offset], remain_len);
170
171 return remain_len;
172 }
173
174 uint64_t write_sub_blocks(const uint8_t* data, uint64_t offset, uint64_t len)
175 {
176 uint64_t block_offset = offset - m_address;
179
180 memcpy(&m_ptr[block_offset], data, remain_len);
181
182 return remain_len;
183 }
184
185 uint8_t* get_ptr() { return m_ptr; }
186
187 uint64_t get_len() { return m_len; }
188
189 uint64_t get_address() { return m_address; }
190
191 ShmemIDExtension* get_extension()
192 {
193 if (m_shmemID.empty()) return nullptr;
194 return &m_shmemID;
195 }
196
197 ~SubBlock()
198 {
199 if (m_mapped) {
200 munmap(m_ptr, m_len);
201 } else {
202 if (m_ptr) free(m_ptr);
203 }
204 }
205 };
206
207private:
208 std::unique_ptr<gs_memory<BUSWIDTH>::SubBlock<>> m_sub_block;
209 cci::cci_broker_handle m_broker;
210
211protected:
212 virtual bool get_direct_mem_ptr(int id, tlm::tlm_generic_payload& txn, tlm::tlm_dmi& dmi_data)
213 {
214 if (!p_dmi) return false;
215 sc_dt::uint64 addr = txn.get_address();
216
217 if (!m_relative_addresses) {
218 if (addr < m_address) {
219 txn.set_response_status(tlm::TLM_ADDRESS_ERROR_RESPONSE);
220 return false;
221 }
222 addr -= m_address;
223 }
224 if (addr >= m_size) {
225 txn.set_response_status(tlm::TLM_ADDRESS_ERROR_RESPONSE);
226 return false;
227 }
228
229 SCP_TRACE(()) << " : DMI access to address "
230 << "0x" << std::hex << addr;
231
232 if (p_rom)
233 dmi_data.allow_read();
234 else
235 dmi_data.allow_read_write();
236
237 SubBlock<>& blk = m_sub_block->access(addr);
238
239 uint8_t* ptr = blk.get_ptr();
240 uint64_t size = blk.get_len();
241 uint64_t block_address = blk.get_address();
242
243 dmi_data.set_dmi_ptr(reinterpret_cast<unsigned char*>(ptr));
244 if (!m_relative_addresses) {
245 dmi_data.set_start_address(block_address + m_address);
246 dmi_data.set_end_address((block_address + m_address + size) - 1);
247 } else {
248 dmi_data.set_start_address(block_address);
249 dmi_data.set_end_address((block_address + size) - 1);
250 }
251 dmi_data.set_read_latency(p_latency);
252 dmi_data.set_write_latency(p_latency);
253
254 ShmemIDExtension* ext = blk.get_extension();
255 if (ext) {
256 txn.set_extension(ext);
257 }
258
259 return true;
260 }
261
262 virtual void b_transport(int id, tlm::tlm_generic_payload& txn, sc_core::sc_time& delay)
263 {
264 unsigned int len = txn.get_data_length();
265 unsigned char* ptr = txn.get_data_ptr();
266 sc_dt::uint64 addr = txn.get_address();
267 unsigned char* byt = txn.get_byte_enable_ptr();
268 unsigned int bel = txn.get_byte_enable_length();
269
270 if (txn.get_streaming_width() < len) {
271 SCP_FATAL(()) << "using streaming width: 0x" << std::hex << txn.get_streaming_width()
272 << " less than data length: 0x" << std::hex << len << " is not supported.";
273 }
274 SCP_INFO(()) << "Start b_transport :" << get_txn_command_str(txn) << " to address: 0x" << std::hex << addr
275 << " len: 0x" << std::hex << len;
276
277 if (!m_relative_addresses) {
278 if (addr < m_address) {
279 txn.set_response_status(tlm::TLM_ADDRESS_ERROR_RESPONSE);
280 return;
281 }
282 addr -= m_address;
283 }
284 if ((addr + len) > m_size) {
285 txn.set_response_status(tlm::TLM_ADDRESS_ERROR_RESPONSE);
286 return;
287 }
288
289 switch (txn.get_command()) {
290 case tlm::TLM_READ_COMMAND:
291 if (byt) {
292 for (unsigned int i = 0; i < len; i++)
293 if (byt[i % bel] == TLM_BYTE_ENABLED) {
294 if (!read(&(ptr[i]), (addr + i), 1)) {
295 SCP_FATAL(()) << "Address + length is out of range of the memory size";
296 }
297 }
298 } else {
299 if (!read(ptr, addr, len)) {
300 SCP_FATAL(()) << "Address + length is out of range of the memory size";
301 }
302 }
303 break;
304 case tlm::TLM_WRITE_COMMAND:
305 if (p_rom) {
306 txn.set_response_status(tlm::TLM_COMMAND_ERROR_RESPONSE);
307 return;
308 }
309 if (byt) {
310 for (unsigned int i = 0; i < len; i++)
311 if (byt[i % bel] == TLM_BYTE_ENABLED) {
312 if (!write(&(ptr[i]), (addr + i), 1)) {
313 SCP_FATAL(()) << "Address + length is out of range of the memory size";
314 }
315 }
316 } else {
317 if (!write(ptr, addr, len)) {
318 SCP_FATAL(()) << "Address + length is out of range of the memory size";
319 }
320 }
321 break;
322 default:
323 SCP_FATAL(()) << "TLM command not supported";
324 break;
325 }
326 delay += p_latency;
327 txn.set_response_status(tlm::TLM_OK_RESPONSE);
328 SCP_INFO(()) << "Completed b_transport :" << scp::scp_txn_tostring(txn);
329
330 if (p_dmi) txn.set_dmi_allowed(true);
331 }
332
333 const char* get_txn_command_str(const tlm::tlm_generic_payload& trans)
334 {
335 switch (trans.get_command()) {
336 case tlm::TLM_READ_COMMAND:
337 return "READ";
338 case tlm::TLM_WRITE_COMMAND:
339 return "WRITE";
340 case tlm::TLM_IGNORE_COMMAND:
341 return "IGNORE";
342 default:
343 break;
344 }
345 return "UNKNOWN";
346 }
347
348 virtual unsigned int transport_dbg(int id, tlm::tlm_generic_payload& txn)
349 {
350 unsigned int len = txn.get_data_length();
351 sc_dt::uint64 addr = txn.get_address();
352 sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
353 SCP_INFO(()) << "Start b_transport dbg :" << get_txn_command_str(txn) << " to address: 0x" << std::hex << addr
354 << " len: 0x" << std::hex << len;
355 b_transport(id, txn, delay);
356 SCP_INFO(()) << "Completed b_transport dbg :" << get_txn_command_str(txn) << " to address: 0x" << std::hex
357 << addr << " len: 0x" << std::hex << len << " status: " << txn.get_response_string();
358 if (txn.get_response_status() == tlm::TLM_OK_RESPONSE)
359 return len;
360 else
361 return 0;
362 }
363
364 bool read(uint8_t* data, uint64_t offset, uint64_t len)
365 {
366 // force end of elaboration to ensure we fix the sizes
367 // this may happen if another model descides to load data into memory as
368 // part of it's initialization during before_end_of_elaboration.
369
370 if (!m_sub_block) before_end_of_elaboration();
371
374
375 if (offset + len > m_size) {
376 return false;
377 }
378
379 while (len > 0) {
380 SubBlock<>& blk = m_sub_block->access(offset + data_ptr_offset);
381
382 remain_len = blk.read_sub_blocks(&data[data_ptr_offset], offset + data_ptr_offset, len);
383
385 len -= remain_len;
386 }
387
388 return true;
389 }
390 bool write(const uint8_t* data, uint64_t offset, uint64_t len)
391 {
392 if (!m_sub_block) before_end_of_elaboration();
393
396
397 if (offset + len > m_size) {
398 return false;
399 }
400
401 while (len > 0) {
402 SubBlock<>& blk = m_sub_block->access(offset + data_ptr_offset);
403
404 remain_len = blk.write_sub_blocks(&data[data_ptr_offset], offset + data_ptr_offset, len);
405
407 len -= remain_len;
408 }
409
410 return true;
411 }
412
413public:
414 tlm_utils::multi_passthrough_target_socket<gs_memory<BUSWIDTH>, BUSWIDTH> socket;
416 cci::cci_param<bool> p_rom;
417 cci::cci_param<bool> p_dmi;
418 cci::cci_param<sc_core::sc_time> p_latency;
419 cci::cci_param<std::string> p_mapfile;
420 cci::cci_param<uint64_t> p_max_block_size;
421 cci::cci_param<uint64_t> p_min_block_size;
422 cci::cci_param<bool> p_shmem;
423 cci::cci_param<std::string> p_shmem_prefix;
424 cci::cci_param<bool> p_init_mem;
425 cci::cci_param<int> p_init_mem_val; // to match the signature of memset
426
427 gs::loader<> load;
428
429 // NB
430 // A size given by a config will always take precedence
431 // A size set on the constructor will take precedence over
432 // the size given on an e.g. 'add_target' in the router
433
434 gs_memory(sc_core::sc_module_name name, uint64_t _size = 0)
435 : m_sub_block(nullptr)
436 , m_broker(cci::cci_get_broker())
437 , socket("target_socket")
438 , reset("reset")
439 , p_rom("read_only", false, "Read Only memory (default false)")
440 , p_dmi("dmi_allow", true, "DMI allowed (default true)")
441 , p_latency("latency", sc_core::sc_time(10, sc_core::SC_NS), "Latency reported for DMI access")
442 , p_mapfile("map_file", "", "(optional) file to map this memory")
443 , p_max_block_size("max_block_size", 0x100000000, "Maximum size of the sub bloc")
444 , p_min_block_size("min_block_size", sysconf(_SC_PAGE_SIZE), "Minimum size of the sub bloc")
445 , p_shmem("shared_memory", false, "Allocate using shared memory")
446 , p_shmem_prefix("shared_memory_prefix", "", "(optional) prefix_for shared memory file")
447 , p_init_mem("init_mem", false, "Initialize allocated memory")
448 , p_init_mem_val("init_mem_val", 0, "Value to initialize memory to")
449 , load("load", [&](const uint8_t* data, uint64_t offset, uint64_t len) -> void {
450 if (!write(data, offset, len)) {
451 SCP_WARN(()) << " Offset : 0x" << std::hex << offset << " of the out of range";
452 }
453 })
454 {
455 SCP_DEBUG(()) << "Memory constructor";
456 MemoryServices::get().init(); // allow any init required
457 if (_size) {
458 std::string ts_name = std::string(sc_module::name()) + ".target_socket";
459 if (!m_broker.has_preset_value(ts_name + ".size")) {
460 m_broker.set_preset_cci_value(ts_name + ".size", cci::cci_value(_size));
461 }
462 }
463
464 socket.register_b_transport(this, &gs_memory::b_transport);
465 socket.register_transport_dbg(this, &gs_memory::transport_dbg);
466 socket.register_get_direct_mem_ptr(this, &gs_memory::get_direct_mem_ptr);
467
468 reset.register_value_changed_cb([&](bool value) {
469 if (value) {
470 SCP_WARN(()) << "Reset";
471 m_sub_block->doreset();
472 load.doreset(value);
473 }
474 });
475 }
476
477 void before_end_of_elaboration()
478 {
479 if (m_sub_block) {
480 return;
481 }
482
483 std::string ts_name = std::string(sc_module::name()) + ".target_socket";
484
485 m_address = base();
486 m_size = size();
487
488 m_sub_block = std::make_unique<gs_memory<BUSWIDTH>::SubBlock<>>(0, m_size, *this);
489
490 SCP_DEBUG(()) << "m_address: " << m_address;
491 SCP_DEBUG(()) << "m_size: " << m_size;
492
493 m_relative_addresses = true;
494 if (gs::cci_get<bool>(m_broker, ts_name + ".relative_addresses", m_relative_addresses)) {
495 m_broker.lock_preset_value(ts_name + ".relative_addresses");
496 }
497 }
498
499 gs_memory() = delete;
500 gs_memory(const gs_memory&) = delete;
501
502 ~gs_memory() {}
503
510 {
511 if (!m_size) {
512 std::string ts_name = std::string(sc_module::name()) + ".target_socket";
513 std::string ts_name_alias;
514 if (gs::cci_get<std::string>(m_broker, ts_name + ".0", ts_name_alias)) {
515 // deal with an alias
516 if (m_broker.has_preset_value(ts_name + ".size") &&
517 m_broker.get_preset_cci_value(ts_name + ".size").is_number()) {
518 SCP_WARN(())
519 ("The configuration alias provided ({}) will be ignored as a valid size is "
520 "also provided.",
521 ts_name);
522 } else {
524 if (ts_name[0] == '&') ts_name = (ts_name.erase(0, 1)) + ".target_socket";
525 }
526 }
527 m_size = gs::cci_get<uint64_t>(m_broker, ts_name + ".size");
528 }
529 return m_size;
530 }
531
538 {
539 if (!m_address_valid) {
540 std::string ts_name = std::string(sc_module::name()) + ".target_socket";
541 std::string ts_name_alias;
542 if (gs::cci_get<std::string>(m_broker, ts_name + ".0", ts_name_alias)) {
543 // deal with an alias
544 if (gs::cci_get<uint64_t>(m_broker, ts_name + ".address", m_address)) {
545 SCP_WARN(())
546 ("The configuration alias provided ({}) will be ignored as a valid address is "
547 "also provided.",
548 ts_name);
549 } else {
551 if (ts_name[0] == '&') ts_name = (ts_name.erase(0, 1)) + ".target_socket";
552 }
553 }
554 if (!m_broker.has_preset_value(ts_name + ".address")) {
555 m_address = 0; // fine for relative addressing
556 SCP_WARN(()) << "Can't find " << ts_name << ".address";
557 } else {
558 m_address = gs::cci_get<uint64_t>(m_broker, ts_name + ".address");
559 }
560 m_broker.lock_preset_value(ts_name + ".address");
561 m_address_valid = true;
562 }
563 return m_address;
564 }
565};
566} // namespace gs
567
568extern "C" void module_register();
569#endif
Definition target.h:160
Definition shmem_extension.h:24
A gs_memory component that can add memory to a virtual platform project.
Definition gs_memory.h:56
uint64_t base()
this function returns the base address of the memory
Definition gs_memory.h:537
uint64_t size()
this function returns the size of the memory
Definition gs_memory.h:509
Definition loader.h:90
Tool which reads a Lua configuration file and sets parameters.
Definition biflow.cc:10