scc 2026.07
SystemC components library
memory.h
1/*******************************************************************************
2 * Copyright 2016, 2018 MINRES Technologies GmbH
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *******************************************************************************/
16
17#ifndef _SYSC_MEMORY_H_
18#define _SYSC_MEMORY_H_
19
20// Needed for the simple_target_socket
21#include <nonstd/span.h>
22#include <tlm/scc/memory_map_collector.h>
23#include <tlm_core/tlm_2/tlm_generic_payload/tlm_gp.h>
24#include <util/ities.h>
25#ifndef SC_INCLUDE_DYNAMIC_PROCESSES
26#define SC_INCLUDE_DYNAMIC_PROCESSES
27#endif
28
29#include "clock_if_mixins.h"
30#include <cci_configuration>
31#include <cstdint>
32#include <numeric>
33#include <scc/mt19937_rng.h>
34#include <scc/report.h>
35#include <scc/signal_opt_ports.h>
36#include <scc/utilities.h>
37#include <tlm.h>
38#include <tlm/scc/target_mixin.h>
39#include <type_traits>
40#include <util/range_lut.h>
41#include <util/sparse_array.h>
42
43namespace scc {
44template <bool USE_CYCLES> struct delay_spec_type;
45
46template <> struct delay_spec_type<true> {
47 using type = unsigned;
48 static inline unsigned get_default_val() { return 0; };
49 static inline sc_core::sc_time get_effective_value(unsigned cycles, sc_core::sc_time period) { return cycles * period; }
50};
51
52template <> struct delay_spec_type<false> {
53 using type = sc_core::sc_time;
54 static inline sc_core::sc_time get_default_val() { return sc_core::SC_ZERO_TIME; };
55 static inline sc_core::sc_time get_effective_value(sc_core::sc_time delay, sc_core::sc_time period) { return delay; }
56};
57
72template <unsigned long long SIZE, unsigned BUSWIDTH = LT, unsigned PAGE_ADDR_BITS = 24, bool USE_CYCLES = false>
73class memory : public sc_core::sc_module {
74public:
75 using delay_type = typename delay_spec_type<USE_CYCLES>::type;
77
85 memory(const sc_core::sc_module_name& nm);
91 static constexpr unsigned long long getPageSize() { return 1 << PAGE_ADDR_BITS; }
97 static constexpr unsigned long long getSize() { return SIZE; }
104 void set_operation_callback(std::function<int(memory<SIZE, BUSWIDTH>&, tlm::tlm_generic_payload&, sc_core::sc_time& delay)> cb) {
105 operation_cb = cb;
106 }
107
113 void set_dmi_callback(std::function<bool(memory<SIZE, BUSWIDTH>&, tlm::tlm_generic_payload&, tlm::tlm_dmi&)> cb) { dmi_cb = cb; }
118 cci::cci_param<bool> allow_dmi{"allow_dmi", true, "Allow DMI accesses to this memory if set"};
122 cci::cci_param<delay_type> rd_resp_delay{"rd_resp_delay", delay_spec_type<USE_CYCLES>::get_default_val()};
126 cci::cci_param<delay_type> wr_resp_delay{"wr_resp_delay", delay_spec_type<USE_CYCLES>::get_default_val()};
135 void map_host_memory(uint64_t base, uint64_t size, uint8_t* ptr) {
136 try {
137 host_mem_lut.addEntry(host_map_entry{ptr, base, size}, base, size);
138 } catch(std::runtime_error& e) {
139 SCCERR(SCMOD) << "Cannot map memory to address=0x" << std::hex << base << " with size=0x" << size << " because: " << e.what();
140 }
141 }
142
149 void unmap_host_memory(uint64_t base, uint64_t size) {
150 if(!host_mem_lut.removeEntry(host_map_entry{nullptr, base, size})) {
151 SCCERR(SCMOD) << "Cannot unmap memory at address=0x" << std::hex << base << " with size=0x" << size;
152 }
153 }
154
155protected:
159 uint8_t* ptr;
160 uint64_t base;
161 uint64_t size;
162 bool operator==(host_map_entry const& o) { return base == o.base && size == o.size; }
163 bool operator!=(host_map_entry const& o) { return !operator==(o); }
164 };
165 util::range_lut<host_map_entry> host_mem_lut{host_map_entry{nullptr, 0, 0}};
166
167 void set_clock_period(sc_core::sc_time period) { clk_period = period; }
168 sc_core::sc_time clk_period;
169
170public:
172 int handle_operation(tlm::tlm_generic_payload& trans, sc_core::sc_time& delay);
174 bool handle_dmi(tlm::tlm_generic_payload& gp, tlm::tlm_dmi& dmi_data);
175 std::function<int(this_class&, tlm::tlm_generic_payload&, sc_core::sc_time& delay)> operation_cb;
176 std::function<bool(this_class&, tlm::tlm_generic_payload&, tlm::tlm_dmi&)> dmi_cb;
177};
178
179template <unsigned long long SIZE, unsigned BUSWIDTH = LT, unsigned PAGE_ADDR_BITS = 24>
181template <unsigned long long SIZE, unsigned BUSWIDTH = LT, unsigned PAGE_ADDR_BITS = 24>
183
184template <unsigned long long SIZE, unsigned BUSWIDTH = LT>
185int handle_operation(memory<SIZE, BUSWIDTH>&, tlm::tlm_generic_payload&, sc_core::sc_time& delay);
186
187struct host_mem_map_extension : public tlm::tlm_extension<host_mem_map_extension> {
188 tlm_extension_base* clone() const override { return nullptr; }
189 void copy_from(tlm_extension_base const& ext) override {}
190 host_mem_map_extension(uint8_t* data, size_t size)
191 : range(data, data + size) {}
192 host_mem_map_extension(size_t size)
193 : range(nullptr, size) {}
194 host_mem_map_extension() = default;
195 ~host_mem_map_extension() {}
196 nonstd::span<uint8_t> range;
197};
198
202template <unsigned long long SIZE, unsigned BUSWIDTH, unsigned PAGE_ADDR_BITS, bool USE_CYCLES>
204: sc_module(nm) {
205 // Register callback for incoming b_transport interface method call
206 target.register_b_transport([this](tlm::tlm_generic_payload& gp, sc_core::sc_time& delay) -> void {
207 operation_cb ? operation_cb(*this, gp, delay) : handle_operation(gp, delay);
208 });
209 target.register_transport_dbg([this](tlm::tlm_generic_payload& gp) -> unsigned {
210 if(gp.get_command() == tlm::TLM_IGNORE_COMMAND)
211 if(auto ext = gp.get_extension<tlm::scc::memory_map_extension>()) {
212 ext->node.name = name();
213 return 0;
214 }
215 sc_core::sc_time z = sc_core::SC_ZERO_TIME;
216 if(auto ext = gp.get_extension<host_mem_map_extension>()) {
217 if(ext->range.data())
218 map_host_memory(gp.get_address(), ext->range.size(), ext->range.data());
219 else
220 unmap_host_memory(gp.get_address(), ext->range.size());
221 if(gp.get_command() == tlm::TLM_IGNORE_COMMAND)
222 return 0;
223 }
224 return operation_cb ? operation_cb(*this, gp, z) : handle_operation(gp, z);
225 });
226 target.register_get_direct_mem_ptr([this](tlm::tlm_generic_payload& gp, tlm::tlm_dmi& dmi_data) -> bool {
227 return dmi_cb ? dmi_cb(*this, gp, dmi_data) : handle_dmi(gp, dmi_data);
228 });
229}
230
231template <unsigned long long SIZE, unsigned BUSWIDTH, unsigned PAGE_ADDR_BITS, bool USE_CYCLES>
232int memory<SIZE, BUSWIDTH, PAGE_ADDR_BITS, USE_CYCLES>::handle_operation(tlm::tlm_generic_payload& trans, sc_core::sc_time& delay) {
233 uint64_t adr = trans.get_address();
234 uint8_t* ptr = trans.get_data_ptr();
235 unsigned len = trans.get_data_length();
236 uint8_t* byt = trans.get_byte_enable_ptr();
237 unsigned wid = trans.get_streaming_width();
238 // check address range and check for unsupported features,
239 // i.e. byte enables, streaming, and bursts
240 // Can ignore DMI hint and extensions
241 if(adr + len > ::sc_dt::uint64(SIZE)) {
242 SC_REPORT_ERROR("TLM-2", "generic payload transaction exceeeds memory size");
243 trans.set_response_status(tlm::TLM_ADDRESS_ERROR_RESPONSE);
244 return 0;
245 }
246 if(wid < len) {
247 SCCERR(SCMOD) << "Streaming width: " << wid << ", data length: " << len;
248 SC_REPORT_ERROR("TLM-2", "generic payload transaction not supported");
249 trans.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
250 return 0;
251 }
252 auto scattered =
253 byt ? std::accumulate(byt, byt + trans.get_byte_enable_length(), 0xff, [](uint8_t a, uint8_t b) { return a & b; }) != 0xff : false;
254 tlm::tlm_command cmd = trans.get_command();
255 SCCTRACE(SCMOD) << (cmd == tlm::TLM_READ_COMMAND ? "read" : "write") << " access to addr 0x" << std::hex << adr;
256 trans.set_dmi_allowed(allow_dmi.get_value());
257 trans.set_response_status(tlm::TLM_OK_RESPONSE);
258 auto hm_entry = host_mem_lut.getEntry(adr);
259 if(cmd == tlm::TLM_READ_COMMAND) {
261 if(hm_entry.ptr) {
262 auto hm_start_offs = adr - hm_entry.base;
263 auto hm_end_offs = hm_start_offs + len;
264 auto hm_ptr = hm_entry.ptr + hm_start_offs;
265 if(hm_end_offs < hm_entry.size) {
266 std::copy(hm_ptr, hm_ptr + len, ptr);
267 } else {
268 auto transfer_length = hm_end_offs - hm_start_offs;
269 std::copy(hm_ptr, hm_ptr + transfer_length, ptr);
270 for(size_t i = transfer_length; i < len; i++)
271 ptr[i] = scc::MT19937::uniform() % 256;
272 }
273 } else {
274 if(mem.is_allocated(adr)) {
275 const auto& p = mem(adr / mem.page_size);
276 auto offs = adr & mem.page_addr_mask;
277 if((offs + len) > mem.page_size) {
278 auto first_part = mem.page_size - offs;
279 std::copy(p.data() + offs, p.data() + offs + first_part, ptr);
280 const auto& p2 = mem((adr / mem.page_size) + 1);
281 auto second_part = len - first_part;
282 std::copy(p2.data(), p2.data() + second_part, ptr + first_part);
283 } else {
284 std::copy(p.data() + offs, p.data() + offs + len, ptr);
285 }
286 } else {
287 // no allocated page so return randomized data
288 for(size_t i = 0; i < len; i++)
289 ptr[i] = scc::MT19937::uniform() % 256;
290 }
291 }
292 } else if(cmd == tlm::TLM_WRITE_COMMAND) {
294 if(UNLIKELY(hm_entry.ptr)) {
295 auto hm_start_offs = adr - hm_entry.base;
296 auto hm_end_offs = adr + len - hm_entry.base;
297 auto hm_ptr = hm_entry.ptr + hm_start_offs;
298 auto transfer_length = hm_end_offs < hm_entry.size ? len : hm_end_offs - hm_start_offs;
299 if(scattered) {
300 for(size_t i = 0; i < transfer_length; ++i)
301 if(byt[i] == 0xff)
302 *(hm_ptr + i) = *(ptr + i);
303 } else
304 std::copy(ptr, ptr + transfer_length, hm_ptr);
305 } else {
306 auto& p = mem(adr / mem.page_size);
307 auto offs = adr & mem.page_addr_mask;
308 if(UNLIKELY((offs + len) > mem.page_size)) { // we cross a page of the memory
309 auto first_part = mem.page_size - offs;
310 if(scattered) {
311 for(size_t i = 0; i < first_part; ++i)
312 if(byt[i] == 0xff)
313 *(p.data() + offs + i) = *(ptr + i);
314 } else
315 std::copy(ptr, ptr + first_part, p.data() + offs);
316 auto& p2 = mem((adr / mem.page_size) + 1);
317 if(scattered) {
318 for(size_t i = 0, j = first_part; j < len; ++i, ++j)
319 if(byt[j] == 0xff)
320 *(p2.data() + i) = *(ptr + j);
321 } else
322 std::copy(ptr + first_part, ptr + len, p2.data());
323 } else { // we stay within a page of the memory
324 if(scattered) {
325 for(size_t i = 0; i < len; ++i)
326 if(byt[i] == 0xff)
327 *(p.data() + offs + i) = *(ptr + i);
328 } else
329 std::copy(ptr, ptr + len, p.data() + offs);
330 }
331 }
332 }
333 return len;
334}
335
336template <unsigned long long SIZE, unsigned BUSWIDTH, unsigned PAGE_ADDR_BITS, bool USE_CYCLES>
337inline bool memory<SIZE, BUSWIDTH, PAGE_ADDR_BITS, USE_CYCLES>::handle_dmi(tlm::tlm_generic_payload& gp, tlm::tlm_dmi& dmi_data) {
338 if(allow_dmi.get_value()) {
339 auto hm_entry = host_mem_lut.getEntry(gp.get_address());
340 if(hm_entry.ptr) {
341 dmi_data.set_start_address(hm_entry.base);
342 dmi_data.set_end_address(hm_entry.base + hm_entry.size - 1);
343 dmi_data.set_dmi_ptr(hm_entry.ptr);
344 } else {
345 auto& p = mem(gp.get_address() / mem.page_size);
346 auto start_address = gp.get_address() & ~mem.page_addr_mask;
347 auto end_address = std::min<uint64_t>(start_address + mem.page_size, SIZE);
348 dmi_data.set_start_address(start_address);
349 dmi_data.set_end_address(end_address - 1);
350 dmi_data.set_dmi_ptr(p.data());
351 }
352 dmi_data.set_granted_access(tlm::tlm_dmi::DMI_ACCESS_READ_WRITE);
353 dmi_data.set_read_latency(delay_spec_type<USE_CYCLES>::get_effective_value(clk_period, rd_resp_delay));
354 dmi_data.set_write_latency(delay_spec_type<USE_CYCLES>::get_effective_value(clk_period, wr_resp_delay));
355 }
356 return allow_dmi.get_value();
357}
358
359} // namespace scc
360
361#endif /* _SYSC_MEMORY_H_ */
static uint64_t uniform()
Definition mt19937_rng.h:60
simple TLM2.0 LT memory model
Definition memory.h:73
void set_dmi_callback(std::function< bool(memory< SIZE, BUSWIDTH > &, tlm::tlm_generic_payload &, tlm::tlm_dmi &)> cb)
allows to register a callback or functor being invoked upon a direct memory access (DMI) to the memor...
Definition memory.h:113
int handle_operation(tlm::tlm_generic_payload &trans, sc_core::sc_time &delay)
! handle the memory operation independent on interface function used
Definition memory.h:232
static constexpr unsigned long long getSize()
return the size of the array
Definition memory.h:97
util::sparse_array< uint8_t, SIZE, PAGE_ADDR_BITS > mem
Definition memory.h:157
tlm::scc::target_mixin< tlm::tlm_target_socket< BUSWIDTH > > target
Definition memory.h:79
void unmap_host_memory(uint64_t base, uint64_t size)
unmpas a host memory mapped into the address range of the TLM memory
Definition memory.h:149
void set_operation_callback(std::function< int(memory< SIZE, BUSWIDTH > &, tlm::tlm_generic_payload &, sc_core::sc_time &delay)> cb)
allows to register a callback or functor being invoked upon an access to the memory
Definition memory.h:104
void map_host_memory(uint64_t base, uint64_t size, uint8_t *ptr)
maps a given memory into the address range of the TLM memory
Definition memory.h:135
bool handle_dmi(tlm::tlm_generic_payload &gp, tlm::tlm_dmi &dmi_data)
handle the dmi functionality
Definition memory.h:337
memory(const sc_core::sc_module_name &nm)
Definition memory.h:203
The ticking_clock class is a mixin that provides ticking clock functionality.
The tickless_clock class is a mixin that provides tickless clock functionality.
range based lookup table
Definition range_lut.h:37
a sparse array suitable for large sizes
SCC TLM utilities.
Definition memory.h:158