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