Skip to content

Commit ddba890

Browse files
committed
Provide template for an efficient private RAM
1 parent 3df0823 commit ddba890

File tree

3 files changed

+193
-0
lines changed

3 files changed

+193
-0
lines changed

lib/1.4/utility.dml

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import "simics/devs/signal.dml";
99
import "simics/devs/ram.dml";
1010
import "simics/devs/memory-space.dml";
1111
import "simics/devs/translator.dml";
12+
import "simics/model-iface/direct-memory.dml";
1213
import "simics/model-iface/transaction.dml";
14+
import "simics/simulator/conf-object.dml";
1315

1416
template _reg_or_field {
1517
param is_register : bool;
@@ -1393,6 +1395,157 @@ template map_target is (connect, _qname) {
13931395
}
13941396
}
13951397

1398+
/**
1399+
### ram
1400+
1401+
This template can be instantiated on a `port`, `device`, `bank` or
1402+
`subdevice` object for an efficient implementation of a private RAM
1403+
area. This is implemented by automatically creating a separate Simics object
1404+
of class `ram`, with an image attached to it, and using the `direct_memory`
1405+
interface to access the image efficiently. This gives a small memory
1406+
footprint and efficient checkpointing; this can make a visible impact for
1407+
internal memories that are several megabytes in size.
1408+
1409+
This template adds an implementation of the `direct_memory_update` interface
1410+
to the instantiating object. Because of current limitations, the `queue`
1411+
attribute of the corresponding Simics object must be configured before the
1412+
first RAM access.
1413+
1414+
The template requires a single integer parameter `size`, which must be set
1415+
to a multiple of 8192.
1416+
1417+
The template provides the following methods:
1418+
*/
1419+
typedef struct {
1420+
direct_memory_handle_t handle;
1421+
uint8 *data;
1422+
} _ram_page_t;
1423+
template ram {
1424+
param size;
1425+
connect ram is (init_as_subobj, destroy) {
1426+
param documentation = "dm";
1427+
interface direct_memory;
1428+
interface ram;
1429+
param page_size = 8192;
1430+
param size = parent.size;
1431+
#if (size % page_size != 0) { error "size must be a multiple of 8192"; }
1432+
param classname = "ram";
1433+
session _ram_page_t pages[size / page_size];
1434+
method init() {
1435+
default();
1436+
SIM_set_attribute_default(this.obj, "image", SIM_make_attr_nil());
1437+
SIM_set_attribute_default(
1438+
this.obj, "self_allocated_image_size", SIM_make_attr_uint64(size));
1439+
}
1440+
1441+
method invalidate(direct_memory_handle_t handle) {
1442+
local int page = cast(direct_memory.get_user_data(handle), uintptr_t);
1443+
pages[page].data = NULL;
1444+
}
1445+
method request(uint64 offs) -> (uint8 *) {
1446+
local int page = offs / page_size;
1447+
if (pages[page].data == NULL) {
1448+
if (pages[page].handle == NULL) {
1449+
pages[page].handle = direct_memory.get_handle(
1450+
parent.obj, 0, page * page_size, page_size);
1451+
direct_memory.set_user_data(
1452+
pages[page].handle, cast(cast(page, uintptr_t), void *));
1453+
}
1454+
local direct_memory_t mem = direct_memory.request(
1455+
pages[page].handle, Sim_Access_Read | Sim_Access_Write,
1456+
Sim_Access_Read | Sim_Access_Write | Sim_Access_Execute);
1457+
pages[page].data = mem.data;
1458+
}
1459+
return pages[page].data + offs % page_size;
1460+
}
1461+
1462+
method destroy() {
1463+
for (local int i = 0; i < pages.len; i++) {
1464+
if (pages[i].handle != NULL) {
1465+
direct_memory.release(pages[i].handle);
1466+
}
1467+
}
1468+
}
1469+
1470+
method read(int64 offs, buffer_t buf) {
1471+
assert offs + buf.len <= size;
1472+
local int page_mask = page_size - 1;
1473+
if (buf.len <= page_size - (offs & page_mask)) {
1474+
memcpy(buf.data, request(offs), buf.len);
1475+
} else {
1476+
local size_t copied = page_size - (offs & page_mask);
1477+
memcpy(buf.data, request(offs), copied);
1478+
while (buf.len - copied > page_size) {
1479+
memcpy(buf.data + copied, request(offs + copied), page_size);
1480+
copied += page_size;
1481+
}
1482+
memcpy(buf.data + copied, request(offs + copied), buf.len - copied);
1483+
}
1484+
}
1485+
}
1486+
1487+
/**
1488+
* `get_u8(int64 offs) -> (uint8)`
1489+
1490+
Return a single byte from a given offset.
1491+
*/
1492+
method get_u8(uint64 offs) -> (uint8) {
1493+
assert offs < ram.size;
1494+
return *ram.request(offs);
1495+
}
1496+
1497+
/**
1498+
* `get_u8(int64 offs) -> (uint8)`
1499+
1500+
Read a 64-bit little-endian integer from a given offset
1501+
*/
1502+
method get_u64_le(uint64 offs) -> (uint64) {
1503+
local uint64_le_t ret;
1504+
ram.read(offs, {.data=cast(&ret, uint8 *), .len=8});
1505+
return ret;
1506+
}
1507+
/**
1508+
* `set_u8(int64 offs) -> (uint8)`
1509+
1510+
Update a single byte at a given offset.
1511+
*/
1512+
method set_u8(uint64 offs, uint8 val) {
1513+
assert offs < ram.size;
1514+
*ram.request(offs) = val;
1515+
}
1516+
1517+
implement direct_memory_update {
1518+
method release(conf_object_t *target, direct_memory_handle_t handle,
1519+
direct_memory_ack_id_t id) {
1520+
assert target == ram.obj;
1521+
ram.invalidate(handle);
1522+
local int page = cast(
1523+
ram.direct_memory.get_user_data(handle), uintptr_t);
1524+
ram.pages[page].handle = NULL;
1525+
ram.direct_memory.ack(id);
1526+
}
1527+
method update_permission(conf_object_t *target,
1528+
direct_memory_handle_t handle,
1529+
access_t lost_access,
1530+
access_t lost_permission,
1531+
access_t lost_inhibit,
1532+
direct_memory_ack_id_t id) {
1533+
assert target == ram.obj;
1534+
ram.invalidate(handle);
1535+
ram.direct_memory.ack(id);
1536+
}
1537+
method conflicting_access(conf_object_t *target,
1538+
direct_memory_handle_t handle,
1539+
access_t conflicting_permission,
1540+
direct_memory_ack_id_t id) {
1541+
assert target == ram.obj;
1542+
ram.invalidate(handle);
1543+
ram.direct_memory.ack(id);
1544+
}
1545+
}
1546+
}
1547+
1548+
13961549
/**
13971550
## Signal related templates
13981551

test/1.4/lib/T_ram.dml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
© 2025 Intel Corporation
3+
SPDX-License-Identifier: MPL-2.0
4+
*/
5+
dml 1.4;
6+
7+
device test;
8+
9+
import "utility.dml";
10+
11+
subdevice ram {
12+
is ram;
13+
param size = 65536;
14+
}
15+
16+
attribute trigger_test is bool_attr {
17+
// cannot run from init: RAM access requires queue to be set
18+
method set(attr_value_t _) throws {
19+
// integer across page boundary
20+
assert ram.ram.page_size == 0x2000;
21+
ram.set_u8(0x1ffe, 0x11);
22+
ram.set_u8(0x1fff, 0x22);
23+
ram.set_u8(0x2000, 0x33);
24+
ram.set_u8(0x2001, 0x44);
25+
assert ram.get_u64_le(0x1ff8) == 0x2211_0000_0000_0000;
26+
assert ram.get_u64_le(0x2000) == 0x4433;
27+
assert ram.get_u64_le(0x1ffe) == 0x44332211;
28+
}
29+
}

test/1.4/lib/T_ram.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# © 2025 Intel Corporation
2+
# SPDX-License-Identifier: MPL-2.0
3+
4+
clock = SIM_create_object('clock', 'clock', freq_mhz=1)
5+
# implementer of direct_memory_update must have a clock
6+
obj.ram.queue = clock
7+
obj.trigger_test = True
8+
9+
b=simics.buffer_t(100)
10+
obj.ram.ram.iface.ram.read(None, 0, b, 0)
11+
print(bytes(b))

0 commit comments

Comments
 (0)