Skip to content

Commit 8466808

Browse files
no load_core or load_core_cp without load_pdi + restuct
Signed-off-by: Himanshu Choudhary <Himanshu.Choudhary@amd.com>
1 parent 2fac2c1 commit 8466808

13 files changed

Lines changed: 230 additions & 122 deletions

src/cpp/preprocessor/aie2ps/aie2ps_preprocessor.h

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -115,18 +115,25 @@ class aie2ps_preprocessor: public preprocessor
115115
if (!ok)
116116
throw error(error::error_code::invalid_asm,
117117
"load_pdi opcode count mismatch: " + std::to_string(exp) +
118-
" for col 0, but " + std::to_string(got) +
119-
" for col " + std::to_string(col) + "\n");
118+
" for column 0, but " + std::to_string(got) +
119+
" for column " + std::to_string(col) + "\n");
120120
}
121121

122+
// Verify that load_cores / load_cores_cp are not used without load_pdi.
123+
// cert needs load_pdi as the recovery anchor; load_cores / load_cores_cp
124+
// alone are not sufficient to reconstruct hw state.
125+
if (!parser->verify_load_cores_requires_load_pdi())
126+
throw error(error::error_code::invalid_asm,
127+
"load_cores / load_cores_cp opcode requires at least one load_pdi in the same control code elf\n");
128+
122129
// Verify load_cores opcode count is equal across all columns.
123130
{
124131
auto [ok, exp, col, got] = parser->verify_load_cores_count();
125132
if (!ok)
126133
throw error(error::error_code::invalid_asm,
127134
"load_cores opcode count mismatch: " + std::to_string(exp) +
128-
" for col 0, but " + std::to_string(got) +
129-
" for col " + std::to_string(col) + "\n");
135+
" for column 0, but " + std::to_string(got) +
136+
" for column " + std::to_string(col) + "\n");
130137
}
131138

132139
// Verify load_cores_cp opcode count is equal across all columns.
@@ -135,8 +142,8 @@ class aie2ps_preprocessor: public preprocessor
135142
if (!ok)
136143
throw error(error::error_code::invalid_asm,
137144
"load_cores_cp opcode count mismatch: " + std::to_string(exp) +
138-
" for col 0, but " + std::to_string(got) +
139-
" for col " + std::to_string(col) + "\n");
145+
" for column 0, but " + std::to_string(got) +
146+
" for column " + std::to_string(col) + "\n");
140147
}
141148

142149
// Verify start_cond_job_preempt opcode count is equal across all columns.
@@ -145,8 +152,8 @@ class aie2ps_preprocessor: public preprocessor
145152
if (!ok)
146153
throw error(error::error_code::invalid_asm,
147154
"start_cond_job_preempt opcode count mismatch: " + std::to_string(exp) +
148-
" for col 0, but " + std::to_string(got) +
149-
" for col " + std::to_string(col) + "\n");
155+
" for column 0, but " + std::to_string(got) +
156+
" for column " + std::to_string(col) + "\n");
150157
}
151158

152159
// Verify .target directive matches the -t command line option

src/cpp/preprocessor/asm/asm_parser.cpp

Lines changed: 116 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,120 @@ parse_lines()
217217
finalize_preempt();
218218
}
219219

220+
void
221+
asm_parser::
222+
handle_preempt_opcode(std::string& arg_str, std::string& line)
223+
{
224+
if (get_target_type() == "aie2ps")
225+
throw error(error::error_code::internal_error, "PREEMPT opcode is not supported for aie2ps target");
226+
227+
int current_group = current_col();
228+
record_preempt_label(current_group);
229+
230+
std::vector<std::string> args;
231+
if (!arg_str.empty()) {
232+
std::stringstream ss(arg_str);
233+
std::string arg;
234+
while (std::getline(ss, arg, ','))
235+
args.push_back(trim(arg));
236+
}
237+
238+
if (args.empty())
239+
throw error(error::error_code::internal_error, "PREEMPT opcode has no arguments");
240+
241+
std::string first_arg = args[0];
242+
if (first_arg.empty())
243+
throw error(error::error_code::internal_error, "PREEMPT opcode has empty first argument");
244+
245+
std::string hintmap_label;
246+
if (args.size() >= 4) {
247+
hintmap_label = args[3];
248+
if (!hintmap_label.empty() && hintmap_label[0] == '@')
249+
hintmap_label = hintmap_label.substr(1);
250+
if (!hintmap_label.empty()) {
251+
std::transform(hintmap_label.begin(), hintmap_label.end(), hintmap_label.begin(),
252+
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
253+
}
254+
}
255+
256+
std::pair<std::string, std::string> labels;
257+
if (!hintmap_label.empty()) {
258+
// Qualify the hintmap key with its label scope so that the same label
259+
// name in different scopes (e.g. "default" vs "default:pdi") is treated
260+
// as a distinct hintmap.
261+
std::string qualified_key = m_current_label + ":" + hintmap_label;
262+
// Get unique save/restore labels for this hintmap BEFORE adding to vector
263+
// (so the index calculation is correct)
264+
labels = get_hintmap_save_restore_labels(hintmap_label, current_group);
265+
// Store qualified key for later processing (after getting labels)
266+
m_preempt_hintmaps[current_group].push_back(qualified_key);
267+
} else {
268+
// No hintmap, use group-level labels
269+
// Track that this group has PREEMPT opcodes without hintmaps
270+
m_preempt_without_hintmap.insert(current_group);
271+
labels = m_preempt_labels[current_group];
272+
}
273+
274+
if (!hintmap_label.empty())
275+
arg_str = first_arg + ", @" + labels.first + ", @" + labels.second + ", @" + hintmap_label;
276+
else
277+
arg_str = first_arg + ", @" + labels.first + ", @" + labels.second;
278+
279+
log_info() << "PREEMPT opcode: updated arg_str to '" << arg_str
280+
<< "' (hintmap: '" << hintmap_label << "', labels: @" << labels.first
281+
<< " / @" << labels.second << ")" << std::endl;
282+
283+
line = "preempt\t" + arg_str;
284+
}
285+
286+
void
287+
asm_parser::
288+
handle_load_or_preempt_cond(const std::string& op_name, const std::string& arg_str, const smatch& sm)
289+
{
290+
// col is common to all four opcodes; compute once.
291+
int col = current_col();
292+
293+
if (sm[1].matched || sm[3].matched) { // load_pdi or load_cores
294+
// operator[] default-constructs col_data if the key is absent (single lookup).
295+
auto& cdata = m_col[col];
296+
bool is_load_pdi = sm[1].matched;
297+
298+
// Extract the @label at argument index 1 (<id>, @<label>).
299+
// Compiled once (static); regex_search finds the first ", @<label>" token.
300+
static const regex LOAD_LABEL_RE(",\\s*(@[a-zA-Z0-9_]+)");
301+
std::string label_arg;
302+
smatch lm;
303+
if (regex_search(arg_str, lm, LOAD_LABEL_RE))
304+
label_arg = lm[1].str();
305+
// Enforce uniqueness of the PDI / core-elf address within this column.
306+
// label_arg will never be empty
307+
bool inserted = is_load_pdi
308+
? cdata.try_add_load_pdi_label(label_arg)
309+
: cdata.try_add_load_cores_label(label_arg);
310+
if (!inserted)
311+
throw error(error::error_code::invalid_asm,
312+
op_name + " location '" + label_arg + "' is not unique in column " +
313+
std::to_string(col) + "; each " + op_name +
314+
" in a control code elf must use a distinct address\n");
315+
316+
if (is_load_pdi)
317+
cdata.increment_load_pdi_count();
318+
else
319+
cdata.increment_load_cores_count();
320+
} else if (sm[2].matched) { // load_cores_cp
321+
m_col[col].increment_load_cores_cp_count();
322+
} else {
323+
// start_cond_job_preempt must only appear after at least one preempt in
324+
// the same column (cert uses the preceding preempt point for recovery).
325+
auto col_it = m_col.find(col);
326+
if (col_it == m_col.end() || col_it->second.get_preempt_count() == 0)
327+
throw error(error::error_code::invalid_asm,
328+
"start_cond_job_preempt found in column " + std::to_string(col) +
329+
" before any preempt opcode; it must follow a preempt opcode\n");
330+
col_it->second.increment_start_cond_job_preempt_count();
331+
}
332+
}
333+
220334
void
221335
asm_parser::
222336
parse_lines(const std::vector<char>& data, std::string& file)
@@ -317,125 +431,13 @@ parse_lines(const std::vector<char>& data, std::string& file)
317431

318432
// Handle PREEMPT opcode - record label for current group
319433
if (!op_name.compare("preempt")) {
320-
if (get_target_type() == "aie2ps")
321-
throw error(error::error_code::internal_error, "PREEMPT opcode is not supported for aie2ps target");
322-
323-
// Get current group (default to 0 if no attach_to_group yet)
324-
int current_group = current_col();
325-
326-
// Record preempt label for this group (save_N/restore_N) - for backward compatibility
327-
record_preempt_label(current_group);
328-
329-
// Parse arguments: id, @save, @restore, [@hintmap]
330-
std::vector<std::string> args;
331-
if (!arg_str.empty()) {
332-
std::stringstream ss(arg_str);
333-
std::string arg;
334-
while (std::getline(ss, arg, ',')) {
335-
args.push_back(trim(arg));
336-
}
337-
}
338-
339-
// Extract first argument (id)
340-
std::string first_arg;
341-
if (args.empty())
342-
throw error(error::error_code::internal_error, "PREEMPT opcode has no arguments");
343-
344-
first_arg = args[0];
345-
if (first_arg.empty())
346-
throw error(error::error_code::internal_error, "PREEMPT opcode has empty first argument");
347-
348-
// Extract hintmap label if present (4th argument, optional)
349-
std::string hintmap_label;
350-
if (args.size() >= 4) {
351-
hintmap_label = args[3];
352-
// Remove @ prefix if present
353-
if (!hintmap_label.empty() && hintmap_label[0] == '@') {
354-
hintmap_label = hintmap_label.substr(1);
355-
}
356-
if (!hintmap_label.empty()) {
357-
std::transform(hintmap_label.begin(), hintmap_label.end(), hintmap_label.begin(),
358-
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
359-
}
360-
}
361-
362-
// Get save/restore labels - specific to hintmap if present, otherwise use group labels
363-
std::pair<std::string, std::string> labels;
364-
if (!hintmap_label.empty()) {
365-
// Qualify the hintmap key with its label scope so that the same label
366-
// name in different scopes (e.g. "default" vs "default:pdi") is treated
367-
// as a distinct hintmap.
368-
std::string qualified_key = m_current_label + ":" + hintmap_label;
369-
// Get unique save/restore labels for this hintmap BEFORE adding to vector
370-
// (so the index calculation is correct)
371-
labels = get_hintmap_save_restore_labels(hintmap_label, current_group);
372-
// Store qualified key for later processing (after getting labels)
373-
m_preempt_hintmaps[current_group].push_back(qualified_key);
374-
} else {
375-
// No hintmap, use group-level labels
376-
// Track that this group has PREEMPT opcodes without hintmaps
377-
m_preempt_without_hintmap.insert(current_group);
378-
labels = m_preempt_labels[current_group];
379-
}
380-
381-
// Build new arg_str: <first_arg>, @<save_label>, @<restore_label>[, @<hintmap_label>]
382-
if (!hintmap_label.empty())
383-
arg_str = first_arg + ", @" + labels.first + ", @" + labels.second + ", @" + hintmap_label;
384-
else
385-
arg_str = first_arg + ", @" + labels.first + ", @" + labels.second;
386-
387-
log_info() << "PREEMPT opcode: updated arg_str to '" << arg_str
388-
<< "' (hintmap: '" << hintmap_label << "', labels: @" << labels.first
389-
<< " / @" << labels.second << ")" << std::endl;
390-
391-
line = op_name + "\t" + arg_str;
434+
handle_preempt_opcode(arg_str, line);
392435
}
393436
// Per-opcode parse-time checks and counter updates.
394437
// These run after the preempt block so that m_preempt_count is already
395438
// incremented when we reach start_cond_job_preempt.
396439
else if (regex_match(op_name, sm, LOAD_OR_PREEMPT_COND_RE)) {
397-
// col is common to all four opcodes; compute once.
398-
int col = current_col();
399-
400-
if (sm[1].matched || sm[3].matched) { // load_pdi or load_cores
401-
// operator[] default-constructs col_data if the key is absent (single lookup).
402-
auto& cdata = m_col[col];
403-
bool is_load_pdi = sm[1].matched; // if load_pdi
404-
405-
// Extract the @label at argument index 1 (<id>, @<label>).
406-
// Compiled once (static); regex_search finds the first ", @<label>" token.
407-
static const regex LOAD_LABEL_RE(",\\s*(@[a-zA-Z0-9_]+)");
408-
std::string label_arg;
409-
smatch lm;
410-
if (regex_search(arg_str, lm, LOAD_LABEL_RE))
411-
label_arg = lm[1].str();
412-
// Enforce uniqueness of the PDI / core-elf address within this column.
413-
if (!label_arg.empty()) {
414-
bool inserted = is_load_pdi
415-
? cdata.try_add_load_pdi_label(label_arg)
416-
: cdata.try_add_load_cores_label(label_arg);
417-
if (!inserted)
418-
throw error(error::error_code::invalid_asm,
419-
op_name + " location '" + label_arg + "' is not unique in column " +
420-
std::to_string(col) + "; each " + op_name +
421-
" in a control code elf must use a distinct address\n");
422-
}
423-
if (is_load_pdi)
424-
cdata.increment_load_pdi_count();
425-
else
426-
cdata.increment_load_cores_count();
427-
} else if (sm[2].matched) { // load_cores_cp
428-
m_col[col].increment_load_cores_cp_count();
429-
} else {
430-
// start_cond_job_preempt must only appear after at least one preempt in
431-
// the same column (cert uses the preceding preempt point for recovery).
432-
auto col_it = m_col.find(col);
433-
if (col_it == m_col.end() || col_it->second.get_preempt_count() == 0)
434-
throw error(error::error_code::invalid_asm,
435-
"start_cond_job_preempt found in column " + std::to_string(col) +
436-
" before any preempt opcode; it must follow a preempt opcode\n");
437-
col_it->second.increment_start_cond_job_preempt_count();
438-
}
440+
handle_load_or_preempt_cond(op_name, arg_str, sm);
439441
}
440442
insert_col_asmdata(std::make_shared<asm_data>(operation(op_name, arg_str), operation_type::op,
441443
code_section::unknown, 0, (uint32_t)-1, linenumber,

src/cpp/preprocessor/asm/asm_parser.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,18 @@ class asm_parser: public std::enable_shared_from_this<asm_parser>
720720
return true;
721721
}
722722

723+
// Verify that every column containing load_cores or load_cores_cp also has at
724+
// least one load_pdi. cert needs load_pdi as the anchor to recover the full
725+
// hw context; load_cores / load_cores_cp are meaningless without it.
726+
bool verify_load_cores_requires_load_pdi() const {
727+
for (const auto& [col, data] : m_col) {
728+
if ((data.get_load_cores_count() > 0 || data.get_load_cores_cp_count() > 0) &&
729+
data.get_load_pdi_count() == 0)
730+
return false;
731+
}
732+
return true;
733+
}
734+
723735
std::pair<std::vector<uint8_t>, std::vector<uint8_t>> get_preempt_save_restore(uint32_t key) const;
724736
std::pair<std::vector<std::string>, std::vector<std::string>> get_preempt_save_restore_bd(uint32_t key) const;
725737
std::pair<std::vector<std::string>, std::vector<std::string>> get_preempt_save_restore_shimbd(uint32_t key) const;
@@ -811,6 +823,13 @@ class asm_parser: public std::enable_shared_from_this<asm_parser>
811823

812824
void parse_lines(const std::vector<char>& data, std::string& file);
813825

826+
// Rewrites arg_str and line in-place for a PREEMPT opcode.
827+
void handle_preempt_opcode(std::string& arg_str, std::string& line);
828+
829+
// Runs parse-time counter updates and checks for load_pdi / load_cores /
830+
// load_cores_cp / start_cond_job_preempt (matched via LOAD_OR_PREEMPT_COND_RE).
831+
void handle_load_or_preempt_cond(const std::string& op_name, const std::string& arg_str, const smatch& sm);
832+
814833
std::vector<char> get_asm_data(const std::string& name);
815834
// Switch active column for subsequent opcodes. Do not replace existing col_data:
816835
// the same column may be selected again after .include; wiping

test/aie4-ctrlcode/opcode_checks/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ add_aie4_config_neg_test("aie4_check_load_cores_cp_count_mismatch"
4646
add_aie4_config_neg_test("aie4_check_preempt_no_load_pdi"
4747
"config_preempt_no_load_pdi.json")
4848

49+
# load_cores: used without any load_pdi in the same control code elf
50+
add_aie4_config_neg_test("aie4_check_load_cores_no_load_pdi"
51+
"config_load_cores_no_load_pdi.json")
52+
53+
# load_cores_cp: used without any load_pdi in the same control code elf
54+
add_aie4_config_neg_test("aie4_check_load_cores_cp_no_load_pdi"
55+
"config_load_cores_cp_no_load_pdi.json")
56+
4957
# start_cond_job_preempt: appears before any preempt in the same column
5058
add_aie4_config_neg_test("aie4_check_start_cond_before_preempt"
5159
"config_start_cond_before_preempt.json")
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"xrt-kernels": [
3+
{
4+
"name" : "DPU",
5+
"arguments" : [],
6+
"instance" : [
7+
{
8+
"id" : "dpu",
9+
"ctrl_code_file" : "./load_cores_cp_no_load_pdi.asm"
10+
}
11+
]
12+
}
13+
]
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"xrt-kernels": [
3+
{
4+
"name" : "DPU",
5+
"arguments" : [],
6+
"instance" : [
7+
{
8+
"id" : "dpu",
9+
"ctrl_code_file" : "./load_cores_no_load_pdi.asm"
10+
}
11+
]
12+
}
13+
]
14+
}

test/aie4-ctrlcode/opcode_checks/dup_load_cores_addr.asm

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@
77
.attach_to_group 0
88
START_JOB 0
99
LOAD_PDI 0, @pdi0
10+
END_JOB
11+
12+
START_JOB 1
1013
LOAD_CORES 0, @cores0
14+
END_JOB
15+
16+
START_JOB 2
1117
LOAD_CORES 1, @cores0
1218
END_JOB
1319

0 commit comments

Comments
 (0)