scc 2025.09
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 <tlm_core/tlm_2/tlm_generic_payload/tlm_gp.h>
22#ifndef SC_INCLUDE_DYNAMIC_PROCESSES
23#define SC_INCLUDE_DYNAMIC_PROCESSES
24#endif
25
26#include "clock_if_mixins.h"
27#include <cci_configuration>
28#include <cstdint>
29#include <numeric>
30#include <scc/mt19937_rng.h>
31#include <scc/report.h>
32#include <scc/signal_opt_ports.h>
33#include <scc/utilities.h>
34#include <tlm.h>
35#include <tlm/scc/target_mixin.h>
36#include <type_traits>
37#include <util/range_lut.h>
38#include <util/sparse_array.h>
39
40namespace scc {
41template <bool USE_CYCLES> struct delay_spec_type;
42
43template <> struct delay_spec_type<true> {
44 using type = unsigned;
45 static inline unsigned get_default_val() { return 0; };
46 static inline sc_core::sc_time get_effective_value(unsigned cycles, sc_core::sc_time period) { return cycles * period; }
47};
48
49template <> struct delay_spec_type<false> {
50 using type = sc_core::sc_time;
51 static inline sc_core::sc_time get_default_val() { return sc_core::SC_ZERO_TIME; };
52 static inline sc_core::sc_time get_effective_value(sc_core::sc_time delay, sc_core::sc_time period) { return delay; }
53};
54
69template <unsigned long long SIZE, unsigned BUSWIDTH = LT, unsigned PAGE_ADDR_BITS = 24, bool USE_CYCLES = false>
70class memory : public sc_core::sc_module {
71public:
72 using delay_type = typename delay_spec_type<USE_CYCLES>::type;
73
81 memory(const sc_core::sc_module_name& nm);
87 static constexpr unsigned long long getPageSize() { return 1 << PAGE_ADDR_BITS; }
93 static constexpr unsigned long long getSize() { return SIZE; }
100 void set_operation_callback(std::function<int(memory<SIZE, BUSWIDTH>&, tlm::tlm_generic_payload&, sc_core::sc_time& delay)> cb) {
101 operation_cb = cb;
102 }
103
109 void set_dmi_callback(std::function<bool(memory<SIZE, BUSWIDTH>&, tlm::tlm_generic_payload&, tlm::tlm_dmi&)> cb) { dmi_cb = cb; }
114 cci::cci_param<bool> allow_dmi{"allow_dmi", true, "Allow DMI accesses to this memory if set"};
118 cci::cci_param<delay_type> rd_resp_delay{"rd_resp_delay", delay_spec_type<USE_CYCLES>::get_default_val()};
122 cci::cci_param<delay_type> wr_resp_delay{"wr_resp_delay", delay_spec_type<USE_CYCLES>::get_default_val()};
131 void map_host_memory(uint64_t base, uint64_t size, uint8_t* ptr) {
132 try {
133 host_mem_lut.addEntry(host_map_entry{ptr, base, size}, base, size);
134 } catch(std::runtime_error& e) {
135 SCCERR(SCMOD) << "Cannot map memory to address=0x" << std::hex << base << " with size=0x" << size << " because: " << e.what();
136 }
137 }
138
145 void unmap_host_memory(uint64_t base, uint64_t size) {
146 if(!host_mem_lut.removeEntry(host_map_entry{nullptr, base, size})) {
147 SCCERR(SCMOD) << "Cannot unmap memory at address=0x" << std::hex << base << " with size=0x" << size;
148 }
149 }
150
151protected:
155 uint8_t* ptr;
156 uint64_t base;
157 uint64_t size;
158 bool operator==(host_map_entry const& o) { return base == o.base && size == o.size; }
159 bool operator!=(host_map_entry const& o) { return !operator==(o); }
160 };
161 util::range_lut<host_map_entry> host_mem_lut{host_map_entry{nullptr, 0, 0}};
162
163 void set_clock_period(sc_core::sc_time period) { clk_period = period; }
164 sc_core::sc_time clk_period;
165
166public:
168 int handle_operation(tlm::tlm_generic_payload& trans, sc_core::sc_time& delay);
170 bool handle_dmi(tlm::tlm_generic_payload& gp, tlm::tlm_dmi& dmi_data);
171 std::function<int(memory<SIZE, BUSWIDTH>&, tlm::tlm_generic_payload&, sc_core::sc_time& delay)> operation_cb;
172 std::function<bool(memory<SIZE, BUSWIDTH>&, tlm::tlm_generic_payload&, tlm::tlm_dmi&)> dmi_cb;
173};
174
175template <unsigned long long SIZE, unsigned BUSWIDTH = LT, unsigned PAGE_ADDR_BITS = 24>
177template <unsigned long long SIZE, unsigned BUSWIDTH = LT, unsigned PAGE_ADDR_BITS = 24>
179
180template <unsigned long long SIZE, unsigned BUSWIDTH = LT>
181int handle_operation(memory<SIZE, BUSWIDTH>&, tlm::tlm_generic_payload&, sc_core::sc_time& delay);
182
183struct host_mem_map_extension : public tlm::tlm_extension<host_mem_map_extension> {
184 tlm_extension_base* clone() const override { return nullptr; }
185 void copy_from(tlm_extension_base const& ext) override {}
186 host_mem_map_extension(uint8_t* ptr)
187 : ptr(ptr) {}
188 host_mem_map_extension() = default;
189 ~host_mem_map_extension() {}
190 uint8_t* ptr{nullptr};
191};
192
196template <unsigned long long SIZE, unsigned BUSWIDTH, unsigned PAGE_ADDR_BITS, bool USE_CYCLES>
198: sc_module(nm) {
199 // Register callback for incoming b_transport interface method call
200 target.register_b_transport([this](tlm::tlm_generic_payload& gp, sc_core::sc_time& delay) -> void {
201 operation_cb ? operation_cb(*this, gp, delay) : handle_operation(gp, delay);
202 });
203 target.register_transport_dbg([this](tlm::tlm_generic_payload& gp) -> unsigned {
204 sc_core::sc_time z = sc_core::SC_ZERO_TIME;
205 if(auto ext = gp.get_extension<host_mem_map_extension>()) {
206 if(ext->ptr)
207 map_host_memory(gp.get_address(), gp.get_data_length(), ext->ptr);
208 else
209 unmap_host_memory(gp.get_address(), gp.get_data_length());
210 if(gp.get_command() == tlm::TLM_IGNORE_COMMAND)
211 return 0;
212 }
213 return operation_cb ? operation_cb(*this, gp, z) : handle_operation(gp, z);
214 });
215 target.register_get_direct_mem_ptr([this](tlm::tlm_generic_payload& gp, tlm::tlm_dmi& dmi_data) -> bool {
216 return dmi_cb ? dmi_cb(*this, gp, dmi_data) : handle_dmi(gp, dmi_data);
217 });
218}
219
220template <unsigned long long SIZE, unsigned BUSWIDTH, unsigned PAGE_ADDR_BITS, bool USE_CYCLES>
221int memory<SIZE, BUSWIDTH, PAGE_ADDR_BITS, USE_CYCLES>::handle_operation(tlm::tlm_generic_payload& trans, sc_core::sc_time& delay) {
222 uint64_t adr = trans.get_address();
223 uint8_t* ptr = trans.get_data_ptr();
224 unsigned len = trans.get_data_length();
225 uint8_t* byt = trans.get_byte_enable_ptr();
226 unsigned wid = trans.get_streaming_width();
227 // check address range and check for unsupported features,
228 // i.e. byte enables, streaming, and bursts
229 // Can ignore DMI hint and extensions
230 if(adr + len > ::sc_dt::uint64(SIZE)) {
231 SC_REPORT_ERROR("TLM-2", "generic payload transaction exceeeds memory size");
232 trans.set_response_status(tlm::TLM_ADDRESS_ERROR_RESPONSE);
233 return 0;
234 }
235 if(wid < len) {
236 SCCERR(SCMOD) << "Streaming width: " << wid << ", data length: " << len;
237 SC_REPORT_ERROR("TLM-2", "generic payload transaction not supported");
238 trans.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
239 return 0;
240 }
241 if(byt) {
242 auto res = std::accumulate(byt, byt + trans.get_byte_enable_length(), 0xff, [](uint8_t a, uint8_t b) { return a | b; });
243 if(trans.get_byte_enable_length() != len || res != 0xff) {
244 SC_REPORT_ERROR("TLM-2", "generic payload transaction with scattered byte enable not supported");
245 trans.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
246 return 0;
247 }
248 }
249 tlm::tlm_command cmd = trans.get_command();
250 SCCTRACE(SCMOD) << (cmd == tlm::TLM_READ_COMMAND ? "read" : "write") << " access to addr 0x" << std::hex << adr;
251 trans.set_response_status(tlm::TLM_OK_RESPONSE);
252 auto hm_entry = host_mem_lut.getEntry(adr);
253 if(cmd == tlm::TLM_READ_COMMAND) {
255 if(hm_entry.ptr) {
256 auto hm_start_offs = adr - hm_entry.base;
257 auto hm_end_offs = hm_start_offs + len;
258 auto hm_ptr = hm_entry.ptr + hm_start_offs;
259 if(hm_end_offs < hm_entry.size) {
260 std::copy(hm_ptr, hm_ptr + len, ptr);
261 } else {
262 auto transfer_length = hm_end_offs - hm_start_offs;
263 std::copy(hm_ptr, hm_ptr + transfer_length, ptr);
264 for(size_t i = transfer_length; i < len; i++)
265 ptr[i] = scc::MT19937::uniform() % 256;
266 }
267 } else {
268 if(mem.is_allocated(adr)) {
269 const auto& p = mem(adr / mem.page_size);
270 auto offs = adr & mem.page_addr_mask;
271 if((offs + len) > mem.page_size) {
272 auto first_part = mem.page_size - offs;
273 std::copy(p.data() + offs, p.data() + offs + first_part, ptr);
274 const auto& p2 = mem((adr / mem.page_size) + 1);
275 auto second_part = len - first_part;
276 std::copy(p2.data(), p2.data() + second_part, ptr + first_part);
277 } else {
278 std::copy(p.data() + offs, p.data() + offs + len, ptr);
279 }
280 } else {
281 // no allocated page so return randomized data
282 for(size_t i = 0; i < len; i++)
283 ptr[i] = scc::MT19937::uniform() % 256;
284 }
285 }
286 } else if(cmd == tlm::TLM_WRITE_COMMAND) {
288 if(hm_entry.ptr) {
289 auto hm_start_offs = adr - hm_entry.base;
290 auto hm_end_offs = adr + len - hm_entry.base;
291 auto hm_ptr = hm_entry.ptr + hm_start_offs;
292 auto transfer_length = hm_end_offs < hm_entry.size ? len : hm_end_offs - hm_start_offs;
293 std::copy(ptr, ptr + transfer_length, hm_ptr);
294 } else {
295 auto& p = mem(adr / mem.page_size);
296 auto offs = adr & mem.page_addr_mask;
297 if((offs + len) > mem.page_size) {
298 auto first_part = mem.page_size - offs;
299 std::copy(ptr, ptr + first_part, p.data() + offs);
300 auto& p2 = mem((adr / mem.page_size) + 1);
301 std::copy(ptr + first_part, ptr + len, p2.data());
302 } else {
303 std::copy(ptr, ptr + len, p.data() + offs);
304 }
305 }
306 }
307 trans.set_dmi_allowed(allow_dmi.get_value());
308 return len;
309}
310
311template <unsigned long long SIZE, unsigned BUSWIDTH, unsigned PAGE_ADDR_BITS, bool USE_CYCLES>
312inline bool memory<SIZE, BUSWIDTH, PAGE_ADDR_BITS, USE_CYCLES>::handle_dmi(tlm::tlm_generic_payload& gp, tlm::tlm_dmi& dmi_data) {
313 if(allow_dmi.get_value()) {
314 auto hm_entry = host_mem_lut.getEntry(gp.get_address());
315 if(hm_entry.ptr) {
316 dmi_data.set_start_address(hm_entry.base);
317 dmi_data.set_end_address(hm_entry.base + hm_entry.size - 1);
318 dmi_data.set_dmi_ptr(hm_entry.ptr);
319 } else {
320 auto& p = mem(gp.get_address() / mem.page_size);
321 auto start_address = gp.get_address() & ~mem.page_addr_mask;
322 auto end_address = std::min<uint64_t>(start_address + mem.page_size, SIZE);
323 dmi_data.set_start_address(start_address);
324 dmi_data.set_end_address(end_address - 1);
325 dmi_data.set_dmi_ptr(p.data());
326 }
327 dmi_data.set_granted_access(tlm::tlm_dmi::DMI_ACCESS_READ_WRITE);
328 dmi_data.set_read_latency(delay_spec_type<USE_CYCLES>::get_effective_value(clk_period, rd_resp_delay));
329 dmi_data.set_write_latency(delay_spec_type<USE_CYCLES>::get_effective_value(clk_period, wr_resp_delay));
330 }
331 return allow_dmi.get_value();
332}
333
334} // namespace scc
335
336#endif /* _SYSC_MEMORY_H_ */
static uint64_t uniform()
Definition mt19937_rng.h:60
simple TLM2.0 LT memory model
Definition memory.h:70
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:109
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:221
static constexpr unsigned long long getSize()
return the size of the array
Definition memory.h:93
cci::cci_param< bool > allow_dmi
allow DMI accesses
Definition memory.h:114
util::sparse_array< uint8_t, SIZE, PAGE_ADDR_BITS > mem
the real memory structure
Definition memory.h:153
tlm::scc::target_mixin< tlm::tlm_target_socket< BUSWIDTH > > target
the target socket to connect to TLM
Definition memory.h:75
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:145
cci::cci_param< delay_type > rd_resp_delay
read response delay in cycles if USE_CYCLES==true else in sc_core::sc_time
Definition memory.h:118
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:100
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:131
cci::cci_param< delay_type > wr_resp_delay
write response delay in cycles if USE_CYCLES==true else in sc_core::sc_time
Definition memory.h:122
bool handle_dmi(tlm::tlm_generic_payload &gp, tlm::tlm_dmi &dmi_data)
handle the dmi functionality
Definition memory.h:312
memory(const sc_core::sc_module_name &nm)
Definition memory.h:197
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 with compile time constants for performance
SCC TLM utilities.
Definition memory.h:154