Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions src/cpp/preprocessor/aie2ps/aie2ps_preprocessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,59 @@ class aie2ps_preprocessor: public preprocessor
" has " + std::to_string(mismatch_count) + " preempt opcodes\n");
}
log_info() << "Ctrlcode has " << expected_count << " preemption points\n";

// cert relies on load_pdi (and possible load_cores / load_cores_cp) to recover
// the last loaded PDI and cores at each preemption point.
if (!parser->verify_preempt_requires_load_pdi())
throw error(error::error_code::invalid_asm,
"preempt opcode requires at least one load_pdi in the same control code elf\n");
}

// Verify load_pdi opcode count is equal across all columns in multi-UC control code.
{
auto [ok, exp, col, got] = parser->verify_load_pdi_count();
if (!ok)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: statement should be inside braces [hicpp-braces-around-statements]

Suggested change
if (!ok)
if (!ok) {

src/cpp/preprocessor/aie2ps/aie2ps_preprocessor.h:118:

-           " has " + std::to_string(got) + " load_pdi opcodes\n");
+           " has " + std::to_string(got) + " load_pdi opcodes\n");
+ }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: statement should be inside braces [hicpp-braces-around-statements]

Suggested change
if (!ok)
if (!ok) {

src/cpp/preprocessor/aie2ps/aie2ps_preprocessor.h:118:

-           " for col " + std::to_string(col) + "\n");
+           " for col " + std::to_string(col) + "\n");
+ }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: statement should be inside braces [hicpp-braces-around-statements]

Suggested change
if (!ok)
if (!ok) {

src/cpp/preprocessor/aie2ps/aie2ps_preprocessor.h:118:

-           " for column " + std::to_string(col) + "\n");
+           " for column " + std::to_string(col) + "\n");
+ }

throw error(error::error_code::invalid_asm,
"load_pdi opcode count mismatch: " + std::to_string(exp) +
" for column 0, but " + std::to_string(got) +
" for column " + std::to_string(col) + "\n");
}
Comment thread
HimanshuChoudhary-Xilinx marked this conversation as resolved.

// Verify that load_cores / load_cores_cp are not used without load_pdi.
// cert needs load_pdi as the recovery anchor; load_cores / load_cores_cp
// alone are not sufficient to reconstruct hw state.
if (!parser->verify_load_cores_requires_load_pdi())
throw error(error::error_code::invalid_asm,
"load_cores / load_cores_cp opcode requires at least one load_pdi in the same control code elf\n");

// Verify load_cores opcode count is equal across all columns.
{
auto [ok, exp, col, got] = parser->verify_load_cores_count();
if (!ok)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: statement should be inside braces [hicpp-braces-around-statements]

Suggested change
if (!ok)
if (!ok) {

src/cpp/preprocessor/aie2ps/aie2ps_preprocessor.h:128:

-           " has " + std::to_string(got) + " load_cores opcodes\n");
+           " has " + std::to_string(got) + " load_cores opcodes\n");
+ }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: statement should be inside braces [hicpp-braces-around-statements]

Suggested change
if (!ok)
if (!ok) {

src/cpp/preprocessor/aie2ps/aie2ps_preprocessor.h:128:

-           " for col " + std::to_string(col) + "\n");
+           " for col " + std::to_string(col) + "\n");
+ }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: statement should be inside braces [hicpp-braces-around-statements]

Suggested change
if (!ok)
if (!ok) {

src/cpp/preprocessor/aie2ps/aie2ps_preprocessor.h:135:

-           " for column " + std::to_string(col) + "\n");
+           " for column " + std::to_string(col) + "\n");
+ }

throw error(error::error_code::invalid_asm,
"load_cores opcode count mismatch: " + std::to_string(exp) +
" for column 0, but " + std::to_string(got) +
" for column " + std::to_string(col) + "\n");
}

// Verify load_cores_cp opcode count is equal across all columns.
{
auto [ok, exp, col, got] = parser->verify_load_cores_cp_count();
if (!ok)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: statement should be inside braces [hicpp-braces-around-statements]

Suggested change
if (!ok)
if (!ok) {

src/cpp/preprocessor/aie2ps/aie2ps_preprocessor.h:138:

-           " has " + std::to_string(got) + " load_cores_cp opcodes\n");
+           " has " + std::to_string(got) + " load_cores_cp opcodes\n");
+ }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: statement should be inside braces [hicpp-braces-around-statements]

Suggested change
if (!ok)
if (!ok) {

src/cpp/preprocessor/aie2ps/aie2ps_preprocessor.h:138:

-           " for col " + std::to_string(col) + "\n");
+           " for col " + std::to_string(col) + "\n");
+ }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: statement should be inside braces [hicpp-braces-around-statements]

Suggested change
if (!ok)
if (!ok) {

src/cpp/preprocessor/aie2ps/aie2ps_preprocessor.h:145:

-           " for column " + std::to_string(col) + "\n");
+           " for column " + std::to_string(col) + "\n");
+ }

throw error(error::error_code::invalid_asm,
"load_cores_cp opcode count mismatch: " + std::to_string(exp) +
" for column 0, but " + std::to_string(got) +
" for column " + std::to_string(col) + "\n");
}

// Verify start_cond_job_preempt opcode count is equal across all columns.
{
auto [ok, exp, col, got] = parser->verify_start_cond_job_preempt_count();
if (!ok)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: statement should be inside braces [hicpp-braces-around-statements]

Suggested change
if (!ok)
if (!ok) {

src/cpp/preprocessor/aie2ps/aie2ps_preprocessor.h:148:

-           " has " + std::to_string(got) + " start_cond_job_preempt opcodes\n");
+           " has " + std::to_string(got) + " start_cond_job_preempt opcodes\n");
+ }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: statement should be inside braces [hicpp-braces-around-statements]

Suggested change
if (!ok)
if (!ok) {

src/cpp/preprocessor/aie2ps/aie2ps_preprocessor.h:148:

-           " for col " + std::to_string(col) + "\n");
+           " for col " + std::to_string(col) + "\n");
+ }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: statement should be inside braces [hicpp-braces-around-statements]

Suggested change
if (!ok)
if (!ok) {

src/cpp/preprocessor/aie2ps/aie2ps_preprocessor.h:155:

-           " for column " + std::to_string(col) + "\n");
+           " for column " + std::to_string(col) + "\n");
+ }

throw error(error::error_code::invalid_asm,
"start_cond_job_preempt opcode count mismatch: " + std::to_string(exp) +
" for column 0, but " + std::to_string(got) +
" for column " + std::to_string(col) + "\n");
}
Comment thread
HimanshuChoudhary-Xilinx marked this conversation as resolved.

// Verify .target directive matches the -t command line option
Expand Down
196 changes: 124 additions & 72 deletions src/cpp/preprocessor/asm/asm_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <fstream>
#include <iomanip>
#include <optional>
#include "common/regex_wrapper.h"
#include <sstream>

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -217,6 +218,120 @@ parse_lines()
finalize_preempt();
}

void
asm_parser::
handle_preempt_opcode(std::string& arg_str, std::string& line)
{
if (get_target_type() == "aie2ps")
throw error(error::error_code::internal_error, "PREEMPT opcode is not supported for aie2ps target");

int current_group = current_col();
record_preempt_label(current_group);

std::vector<std::string> args;
if (!arg_str.empty()) {
Comment thread
HimanshuChoudhary-Xilinx marked this conversation as resolved.
static const regex token_re("[^,]+");
for (aiebu::sregex_iterator it(arg_str.begin(), arg_str.end(), token_re), end;
it != end; ++it)
args.push_back(trim(it->str()));
}

if (args.empty())
throw error(error::error_code::internal_error, "PREEMPT opcode has no arguments");

std::string first_arg = args[0];
if (first_arg.empty())
throw error(error::error_code::internal_error, "PREEMPT opcode has empty first argument");

std::string hintmap_label;
if (args.size() >= 4) {
hintmap_label = args[3];
if (!hintmap_label.empty() && hintmap_label[0] == '@')
hintmap_label = hintmap_label.substr(1);
if (!hintmap_label.empty()) {
std::transform(hintmap_label.begin(), hintmap_label.end(), hintmap_label.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
}
}

std::pair<std::string, std::string> labels;
if (!hintmap_label.empty()) {
// Qualify the hintmap key with its label scope so that the same label
// name in different scopes (e.g. "default" vs "default:pdi") is treated
// as a distinct hintmap.
std::string qualified_key = m_current_label + ":" + hintmap_label;
// Get unique save/restore labels for this hintmap BEFORE adding to vector
// (so the index calculation is correct)
labels = get_hintmap_save_restore_labels(hintmap_label, current_group);
// Store qualified key for later processing (after getting labels)
m_preempt_hintmaps[current_group].push_back(qualified_key);
} else {
// No hintmap, use group-level labels
// Track that this group has PREEMPT opcodes without hintmaps
m_preempt_without_hintmap.insert(current_group);
labels = m_preempt_labels[current_group];
}

if (!hintmap_label.empty())
arg_str = first_arg + ", @" + labels.first + ", @" + labels.second + ", @" + hintmap_label;
else
arg_str = first_arg + ", @" + labels.first + ", @" + labels.second;

log_info() << "PREEMPT opcode: updated arg_str to '" << arg_str
<< "' (hintmap: '" << hintmap_label << "', labels: @" << labels.first
<< " / @" << labels.second << ")" << std::endl;

line = "preempt\t" + arg_str;
}

void
asm_parser::
handle_load_or_preempt_cond(const std::string& op_name, const std::string& arg_str, const smatch& sm)
{
// col is common to all four opcodes; compute once.
int col = current_col();

if (sm[1].matched || sm[3].matched) { // load_pdi or load_cores
// operator[] default-constructs col_data if the key is absent (single lookup).
auto& cdata = m_col[col];
bool is_load_pdi = sm[1].matched;

// Extract the @label at argument index 1 (<id>, @<label>).
// Compiled once (static); regex_search finds the first ", @<label>" token.
static const regex LOAD_LABEL_RE(",\\s*(@[a-zA-Z0-9_]+)");
std::string label_arg;
smatch lm;
if (regex_search(arg_str, lm, LOAD_LABEL_RE))
label_arg = lm[1].str();
// Enforce uniqueness of the PDI / core-elf address within this column.
// label_arg will never be empty
bool inserted = is_load_pdi
? cdata.try_add_load_pdi_label(label_arg)
: cdata.try_add_load_cores_label(label_arg);
if (!inserted)
throw error(error::error_code::invalid_asm,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: statement should be inside braces [hicpp-braces-around-statements]

Suggested change
throw error(error::error_code::invalid_asm,
rted) {

src/cpp/preprocessor/asm/asm_parser.cpp:315:

- n");
+ n");
+ }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: statement should be inside braces [hicpp-braces-around-statements]

Suggested change
throw error(error::error_code::invalid_asm,
rted) {

src/cpp/preprocessor/asm/asm_parser.cpp:316:

- n");
+ n");
+ }

op_name + " location '" + label_arg + "' is not unique in column " +
std::to_string(col) + "; each " + op_name +
" in a control code elf must use a distinct address\n");

if (is_load_pdi)
cdata.increment_load_pdi_count();
else
cdata.increment_load_cores_count();
} else if (sm[2].matched) { // load_cores_cp
m_col[col].increment_load_cores_cp_count();
} else {
// start_cond_job_preempt must only appear after at least one preempt in
// the same column (cert uses the preceding preempt point for recovery).
auto col_it = m_col.find(col);
if (col_it == m_col.end() || col_it->second.get_preempt_count() == 0)
throw error(error::error_code::invalid_asm,
"start_cond_job_preempt found in column " + std::to_string(col) +
" before any preempt opcode; it must follow a preempt opcode\n");
col_it->second.increment_start_cond_job_preempt_count();
}
}

void
asm_parser::
parse_lines(const std::vector<char>& data, std::string& file)
Expand All @@ -225,6 +340,8 @@ parse_lines(const std::vector<char>& data, std::string& file)
const static regex COMMENT_REGEX("^;(.*)$");
const static regex LABEL_REGEX("^([a-zA-Z0-9_]+):$");
const static regex OP_REGEX("^([.a-zA-Z0-9_]+)(?:[ \\t]+(.+))?$");
// Groups: [1]=load_pdi [2]=load_cores_cp [3]=load_cores [4]=start_cond_job_preempt
const static regex LOAD_OR_PREEMPT_COND_RE("^(?:(load_pdi)|(load_cores_cp)|(load_cores)|(start_cond_job_preempt))$");

// Sanitize input data: filter out non-printable characters except newline, tab, and carriage return
// This prevents corrupted operation names during parsing
Expand Down Expand Up @@ -315,78 +432,13 @@ parse_lines(const std::vector<char>& data, std::string& file)

// Handle PREEMPT opcode - record label for current group
if (!op_name.compare("preempt")) {
if (get_target_type() == "aie2ps")
throw error(error::error_code::internal_error, "PREEMPT opcode is not supported for aie2ps target");

// Get current group (default to 0 if no attach_to_group yet)
int current_group = (m_current_col >= 0) ? m_current_col : 0;

// Record preempt label for this group (save_N/restore_N) - for backward compatibility
record_preempt_label(current_group);

// Parse arguments: id, @save, @restore, [@hintmap]
std::vector<std::string> args;
if (!arg_str.empty()) {
std::stringstream ss(arg_str);
std::string arg;
while (std::getline(ss, arg, ',')) {
args.push_back(trim(arg));
}
}

// Extract first argument (id)
std::string first_arg;
if (args.empty())
throw error(error::error_code::internal_error, "PREEMPT opcode has no arguments");

first_arg = args[0];
if (first_arg.empty())
throw error(error::error_code::internal_error, "PREEMPT opcode has empty first argument");

// Extract hintmap label if present (4th argument, optional)
std::string hintmap_label;
if (args.size() >= 4) {
hintmap_label = args[3];
// Remove @ prefix if present
if (!hintmap_label.empty() && hintmap_label[0] == '@') {
hintmap_label = hintmap_label.substr(1);
}
if (!hintmap_label.empty()) {
std::transform(hintmap_label.begin(), hintmap_label.end(), hintmap_label.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
}
}

// Get save/restore labels - specific to hintmap if present, otherwise use group labels
std::pair<std::string, std::string> labels;
if (!hintmap_label.empty()) {
// Qualify the hintmap key with its label scope so that the same label
// name in different scopes (e.g. "default" vs "default:pdi") is treated
// as a distinct hintmap.
std::string qualified_key = m_current_label + ":" + hintmap_label;
// Get unique save/restore labels for this hintmap BEFORE adding to vector
// (so the index calculation is correct)
labels = get_hintmap_save_restore_labels(hintmap_label, current_group);
// Store qualified key for later processing (after getting labels)
m_preempt_hintmaps[current_group].push_back(qualified_key);
} else {
// No hintmap, use group-level labels
// Track that this group has PREEMPT opcodes without hintmaps
m_preempt_without_hintmap.insert(current_group);
labels = m_preempt_labels[current_group];
}

// Build new arg_str: <first_arg>, @<save_label>, @<restore_label>[, @<hintmap_label>]
if (!hintmap_label.empty())
arg_str = first_arg + ", @" + labels.first + ", @" + labels.second + ", @" + hintmap_label;
else
arg_str = first_arg + ", @" + labels.first + ", @" + labels.second;

log_info() << "PREEMPT opcode: updated arg_str to '" << arg_str
<< "' (hintmap: '" << hintmap_label << "', labels: @" << labels.first
<< " / @" << labels.second << ")" << std::endl;

line = op_name + "\t" + arg_str;
handle_preempt_opcode(arg_str, line);
}
// Per-opcode parse-time checks and counter updates.
// These run after the preempt block so that m_preempt_count is already
// incremented when we reach start_cond_job_preempt.
else if (regex_match(op_name, sm, LOAD_OR_PREEMPT_COND_RE)) {
Comment thread
HimanshuChoudhary-Xilinx marked this conversation as resolved.
handle_load_or_preempt_cond(op_name, arg_str, sm);
}
insert_col_asmdata(std::make_shared<asm_data>(operation(op_name, arg_str), operation_type::op,
code_section::unknown, 0, (uint32_t)-1, linenumber,
Expand Down
Loading
Loading