Skip to content

Commit 7ce8717

Browse files
committed
thread_aware now internal & experimental; many fixes; test PoC
Gonna keep the documentation for now
1 parent 6837b15 commit 7ce8717

File tree

8 files changed

+325
-156
lines changed

8 files changed

+325
-156
lines changed

Diff for: lib/1.4/dml-builtins.dml

+94-75
Large diffs are not rendered by default.

Diff for: lib/1.4/utility.dml

+11-11
Original file line numberDiff line numberDiff line change
@@ -1128,15 +1128,15 @@ template function_io_memory {
11281128
foreach b in (each function_mapped_bank in (dev)) {
11291129
if (b.function == map_info.function) {
11301130
local domain_lock_t *lock;
1131-
#if (dev.thread_aware) SIM_ACQUIRE_CELL(dev.obj, &lock);
1131+
#if (dev._thread_aware) SIM_ACQUIRE_CELL(dev.obj, &lock);
11321132
if (!b.use_io_memory) {
11331133
local map_target_t *mt = SIM_new_map_target(b._bank_obj(),
11341134
NULL, NULL);
11351135
if (!mt) {
11361136
local exception_type_t _exc = SIM_clear_exception();
11371137
log error: "failed to create map target for %s: %s",
11381138
SIM_object_name(b._bank_obj()), SIM_last_error();
1139-
#if (dev.thread_aware) SIM_RELEASE_CELL(dev.obj, &lock);
1139+
#if (dev._thread_aware) SIM_RELEASE_CELL(dev.obj, &lock);
11401140
return Sim_PE_IO_Not_Taken;
11411141
}
11421142
// hack: VT_map_target_access doesn't accept a map_info
@@ -1149,17 +1149,17 @@ template function_io_memory {
11491149
= VT_map_target_access(mt, mem_op);
11501150
SIM_set_mem_op_physical_address(mem_op, before);
11511151
SIM_free_map_target(mt);
1152-
#if (dev.thread_aware) SIM_RELEASE_CELL(dev.obj, &lock);
1152+
#if (dev._thread_aware) SIM_RELEASE_CELL(dev.obj, &lock);
11531153
return ret;
11541154
}
11551155
if (b.io_memory_access(
11561156
mem_op,
11571157
SIM_get_mem_op_physical_address(mem_op) + offset,
11581158
NULL)) {
1159-
#if (dev.thread_aware) SIM_RELEASE_CELL(dev.obj, &lock);
1159+
#if (dev._thread_aware) SIM_RELEASE_CELL(dev.obj, &lock);
11601160
return Sim_PE_No_Exception;
11611161
} else {
1162-
#if (dev.thread_aware) SIM_RELEASE_CELL(dev.obj, &lock);
1162+
#if (dev._thread_aware) SIM_RELEASE_CELL(dev.obj, &lock);
11631163
return Sim_PE_IO_Not_Taken;
11641164
}
11651165
}
@@ -1380,14 +1380,14 @@ template map_target is (connect, _qname) {
13801380
}
13811381
}
13821382
local domain_lock_t *lock;
1383-
#if (dev.thread_aware) SIM_ACQUIRE_TARGET(obj, &lock);
1383+
#if (dev._thread_aware) SIM_ACQUIRE_CELL(obj, &lock);
13841384
local exception_type_t exc = SIM_issue_transaction(map_target, t, addr);
13851385
local bool fail = exc != Sim_PE_No_Exception;
13861386
log info, (fail ? 2 : 4) : "%s%s %d bytes @ 0x%x in %s",
13871387
fail ? "failed to " : "",
13881388
SIM_transaction_is_read(t) ? "read" : fail ? "write" : "wrote",
13891389
SIM_transaction_size(t), addr, SIM_object_name(obj);
1390-
#if (dev.thread_aware) SIM_RELEASE_TARGET(obj, &lock);
1390+
#if (dev._thread_aware) SIM_RELEASE_CELL(obj, &lock);
13911391
return exc;
13921392
}
13931393
}
@@ -1451,10 +1451,10 @@ template signal_connect is (connect, post_init) {
14511451

14521452
method post_init() default {
14531453
local domain_lock_t *lock;
1454-
#if (dev.thread_aware) SIM_ACQUIRE_CELL(dev.obj, &lock);
1454+
#if (dev._thread_aware) SIM_ACQUIRE_CELL(dev.obj, &lock);
14551455
if (!SIM_is_restoring_state(dev.obj) && signal.val && signal.high)
14561456
signal.signal_raise();
1457-
#if (dev.thread_aware) SIM_RELEASE_CELL(dev.obj, &lock);
1457+
#if (dev._thread_aware) SIM_RELEASE_CELL(dev.obj, &lock);
14581458
}
14591459

14601460
method set(conf_object_t *obj) default {
@@ -1469,10 +1469,10 @@ template signal_connect is (connect, post_init) {
14691469
// currently high but the effects of the hotplug has already taken
14701470
// place so we should NOT treat it as a hotplug.
14711471
local domain_lock_t *lock;
1472-
#if (dev.thread_aware) SIM_ACQUIRE_CELL(dev.obj, &lock);
1472+
#if (dev._thread_aware) SIM_ACQUIRE_CELL(dev.obj, &lock);
14731473
local bool hotplug = SIM_object_is_configured(dev.obj)
14741474
&& !SIM_is_restoring_state(dev.obj);
1475-
#if (dev.thread_aware) SIM_RELEASE_CELL(dev.obj, &lock);
1475+
#if (dev._thread_aware) SIM_RELEASE_CELL(dev.obj, &lock);
14761476
if (hotplug && signal.val && signal.high)
14771477
signal.signal_lower();
14781478
default(obj);

Diff for: py/dml/c_backend.py

+18-15
Original file line numberDiff line numberDiff line change
@@ -1269,13 +1269,14 @@ def generate_simple_events_control_methods(device):
12691269
out('{\n', postindent = 1)
12701270
out(crep.structtype(device) + ' *_dev UNUSED = ('
12711271
+ crep.structtype(device) + '*)_obj;\n')
1272-
output_domain_lock_decl('_lock')
1273-
output_acquire_cell('_obj', '_lock')
1274-
for key in dml.globals.after_delay_infos:
1275-
out('SIM_event_cancel_time('
1276-
+ f'SIM_object_clock(_obj), {crep.get_evclass(key)}, _obj, '
1277-
+ '_simple_event_predicate, (lang_void *) &domain);\n')
1278-
output_release_cell('_obj', '_lock')
1272+
if dml.globals.after_delay_infos:
1273+
output_domain_lock_decl('_lock')
1274+
output_acquire_cell('_obj', '_lock')
1275+
for key in dml.globals.after_delay_infos:
1276+
out('SIM_event_cancel_time('
1277+
+ f'SIM_object_clock(_obj), {crep.get_evclass(key)}, _obj, '
1278+
+ '_simple_event_predicate, (lang_void *) &domain);\n')
1279+
output_release_cell('_obj', '_lock')
12791280

12801281
site = logging.SimpleSite('<_cancel_simple_events>')
12811282
by_dims = {}
@@ -1336,25 +1337,27 @@ def generate_register_events(device):
13361337
if not events and not dml.globals.after_delay_infos:
13371338
out('return;\n')
13381339
else:
1340+
flags = 'Sim_EC_No_Serialize' if dml.globals.thread_aware else '0'
13391341
for event in events:
13401342
if (dml.globals.dml_version == (1, 2)
13411343
and param_str(event, 'timebase') == 'stacked'
13421344
and event.dimensions > 0):
13431345
raise ICE(event, "stacked event array not supported")
13441346
for indices in event.all_indices():
1345-
out('%s%s = SIM_register_event("%s", class, 0, %s);\n'
1347+
out('%s%s = SIM_register_event("%s", class, %s, %s);\n'
13461348
% (crep.get_evclass(event),
1347-
''.join('[' + str(i) + ']' for i in indices),
1348-
event.logname_anonymized(indices),
1349-
', '.join(
1350-
cname
1351-
for (_, cname) in event_callbacks(event,
1349+
''.join('[' + str(i) + ']' for i in indices),
1350+
event.logname_anonymized(indices),
1351+
flags,
1352+
', '.join(
1353+
cname
1354+
for (_, cname) in event_callbacks(event,
13521355
indices))))
13531356
for (key, info) in dml.globals.after_delay_infos.items():
1354-
out(('%s = SIM_register_event(%s, class, 0, %s, %s, %s, %s, '
1357+
out(('%s = SIM_register_event(%s, class, %s, %s, %s, %s, %s, '
13551358
+ 'NULL);\n')
13561359
% (crep.get_evclass(key), string_literal(info.string_key),
1357-
info.cident_callback, '_destroy_simple_event_data',
1360+
flags, info.cident_callback, '_destroy_simple_event_data',
13581361
info.cident_get_value, info.cident_set_value))
13591362
out('}\n\n', preindent = -1)
13601363
splitting_point()

Diff for: py/dml/structure.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -1491,6 +1491,13 @@ def mkobj2(obj, obj_specs, params, each_stmts):
14911491
for (issite, tpl) in obj_spec.templates:
14921492
if tpl.trait:
14931493
obj_traits.append((issite, tpl.trait))
1494+
# TODO remove once thread-awareness support is public.
1495+
# Determining thread-awareness via the _thread_aware param is
1496+
# cleaner, but doesn't provide is-site info.
1497+
if (obj.objtype == 'device'
1498+
and tpl.name == '_thread_aware'
1499+
and dml.globals.dml_version != (1, 2)):
1500+
report(WEXPERIMENTAL(issite, 'thread-aware device model'))
14941501

14951502
for obj_spec in obj_specs:
14961503
for (templates, spec) in obj_spec.in_eachs:
@@ -1956,8 +1963,7 @@ def mkobj2(obj, obj_specs, params, each_stmts):
19561963
mark_method_exported(func, name, export.site)
19571964

19581965
if dml.globals.dml_version != (1, 2):
1959-
dml.globals.thread_aware = param_bool_fixup(
1960-
obj, 'thread_aware', False)
1966+
dml.globals.thread_aware = param_bool(obj, '_thread_aware')
19611967

19621968
elif obj.objtype == 'bank':
19631969
set_confidential_object(obj)

Diff for: test/1.4/misc/T_thread_aware.py

-49
This file was deleted.

Diff for: test/1.4/misc/T_thread_aware.dml renamed to test/1.4/misc/thread_aware.dml

+113-2
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,112 @@ dml 1.4;
77
device test;
88

99
import "utility.dml";
10+
import "simics/util/os.dml";
1011

1112
saved int s;
1213

13-
is thread_aware;
14+
// This test should accomplish two things:
15+
// 1. Only pass if the device is actually considered thread-aware, and is
16+
// entered without the cell domain being taken.
17+
// 2. Only pass if the device acquires and releases locks correctly
18+
//
19+
// Accomplished by two cooperative devices that have a piece of shared state:
20+
// 1. is accomplished by manipulating the shared state without locking,
21+
// relying on sleeps for one thread to observe the changes made by the
22+
// other, and performing tests on that state that can only succeed if
23+
// the two devices are entered concurrently.
24+
// 2. is accomplished by manipulations on the shared state while holding
25+
// an object lock; then having a device entry to inspect the
26+
// state be blocked by an existing entry that modifies it. This blocking is
27+
// ensured by also leveraging sleeps. The test is practically guaranteed to
28+
// succeed only if object locking is done correctly.
29+
30+
/// WARNING WEXPERIMENTAL
31+
is _thread_aware;
1432

15-
param init_concurrency_mode = Sim_Concurrency_Mode_Serialized;
33+
header %{
34+
static volatile int32 shared_state = 0;
35+
%}
36+
37+
extern int32 shared_state;
38+
39+
connect buddy {
40+
session int32 *state;
41+
param configuration = "optional";
42+
interface pulse;
43+
}
44+
45+
attribute setup_main is write_only_attr {
46+
param type = "n";
47+
method set(attr_value_t val) throws default {
48+
after 1 cycles: event();
49+
}
50+
51+
method event() {
52+
os_millisleep(50);
53+
// Second resolved (due to the sleep in setup_buddy.event() being
54+
// longer)
55+
56+
// This essentially serves as a check that setup_buddy.event() is
57+
// ongoing; and thus the buddy is holding its own lock
58+
assert shared_state == 1;
59+
// Modify shared state without acquiring lock.
60+
shared_state = 2;
61+
// Then try to enter the buddy, and become blocked
62+
buddy.pulse.pulse();
63+
}
64+
}
65+
66+
attribute setup_buddy is write_only_attr {
67+
param type = "n";
68+
method set(attr_value_t val) throws default {
69+
after 1 cycles: event();
70+
}
71+
72+
method event() {
73+
// First resolved (due to the sleep in setup_main.event())
74+
shared_state = 1;
75+
os_millisleep(100);
76+
// Third resolved.
77+
assert shared_state == 2;
78+
shared_state = 3;
79+
// Once this is left, the object lock of the buddy will be released
80+
}
81+
}
82+
83+
implement pulse {
84+
method pulse() {
85+
// Last resolved, when setup_main.event() becomes unblocked and may
86+
// enter the buddy
87+
assert shared_state == 3;
88+
}
89+
}
90+
91+
attribute shared_state_attr {
92+
param type = "i";
93+
method get() -> (attr_value_t) {
94+
return SIM_make_attr_int64(shared_state);
95+
}
96+
method set(attr_value_t val) throws {
97+
shared_state = SIM_attr_integer(val);
98+
}
99+
}
100+
101+
bank b;
102+
port p;
103+
subdevice sd {
104+
subdevice sd {
105+
bank b;
106+
port p;
107+
}
108+
bank b;
109+
port p;
110+
}
111+
112+
// Remainder is skeleton code stolen from 1.4/misc/notify_state.
113+
// We have to do similar tests that object locking is done for the various ways
114+
// the device may be entered.
115+
// ... That, or write those tests via ctree_tests.py
16116

17117
header %{
18118
#include <assert.h>
@@ -127,4 +227,15 @@ implement signal {
127227
}
128228

129229

230+
// port insig is signal_port;
231+
// connect outsig is signal_connect;
130232

233+
// attribute test_outsig is (pseudo_attr, bool_attr) {
234+
// method set(attr_value_t value) throws {
235+
// default(value);
236+
// if (this.val)
237+
// outsig.set_level(1);
238+
// else
239+
// outsig.set_level(0);
240+
// }
241+
// }

0 commit comments

Comments
 (0)