mirror of https://github.com/YosysHQ/yosys.git
Merge branch 'sim' of github.com:silimate/yosys into sim
This commit is contained in:
commit
a45eaad9a7
|
|
@ -114,13 +114,54 @@ void FstData::extractVarNames()
|
|||
struct fstHier *h;
|
||||
std::string fst_scope_name;
|
||||
|
||||
// Track nested fork scopes using a stack to handle nested packed structs
|
||||
// Begins with outmost scope and ends with innermost scope
|
||||
// Scopes are not normalized on the stack
|
||||
std::vector<std::string> fork_scope_stack;
|
||||
|
||||
// Start fork handles after the maximum real handle from FST file to avoid collisions
|
||||
fstHandle next_fork_handle = fstReaderGetMaxHandle(ctx) + 1;
|
||||
|
||||
// Map of fork scopes to their members, which are all normalized
|
||||
std::map<std::string, std::vector<fstHandle>> fork_scopes;
|
||||
|
||||
while ((h = fstReaderIterateHier(ctx))) {
|
||||
switch (h->htyp) {
|
||||
case FST_HT_SCOPE: {
|
||||
fst_scope_name = fstReaderPushScope(ctx, h->u.scope.name, NULL);
|
||||
|
||||
// Fork scopes are identified by FST_ST_VCD_FORK and are pushed onto the stack
|
||||
if (h->u.scope.typ == FST_ST_VCD_FORK) {
|
||||
fork_scope_stack.push_back(fst_scope_name);
|
||||
// Create new vector that contains struct members
|
||||
normalize_brackets(fst_scope_name);
|
||||
fork_scopes[fst_scope_name] = std::vector<fstHandle>();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FST_HT_UPSCOPE: {
|
||||
if (!fork_scope_stack.empty() && fork_scope_stack.back() == fst_scope_name) {
|
||||
// Assign a unique handle to this fork scope and increment for future forks
|
||||
fstHandle fork_handle = next_fork_handle++;
|
||||
|
||||
// Map normalized scope name to the handle for future lookups via getHandle()
|
||||
normalize_brackets(fst_scope_name);
|
||||
name_to_handle[fst_scope_name] = fork_handle;
|
||||
|
||||
// Copy the extracted members of the fork scope to the fork scope members map
|
||||
// for value lookups in valueOf()
|
||||
fork_scope_members[fork_handle] = fork_scopes[fst_scope_name];
|
||||
|
||||
// If this is a nested fork scope, add its handle to the parent fork scope
|
||||
if (fork_scope_stack.size() > 1) {
|
||||
std::string parent_fork = fork_scope_stack[fork_scope_stack.size() - 2];
|
||||
normalize_brackets(parent_fork);
|
||||
fork_scopes[parent_fork].push_back(fork_handle);
|
||||
}
|
||||
|
||||
// Pop this fork scope from the stack
|
||||
fork_scope_stack.pop_back();
|
||||
}
|
||||
fst_scope_name = fstReaderPopScope(ctx);
|
||||
break;
|
||||
}
|
||||
|
|
@ -136,6 +177,14 @@ void FstData::extractVarNames()
|
|||
vars.push_back(var);
|
||||
if (!var.is_alias)
|
||||
handle_to_var[h->u.var.handle] = var;
|
||||
|
||||
// Add variable to the innermost fork scope in the fork scope stack
|
||||
if (!fork_scope_stack.empty()) {
|
||||
std::string current_fork = fork_scope_stack.back();
|
||||
normalize_brackets(current_fork);
|
||||
fork_scopes[current_fork].push_back(h->u.var.handle);
|
||||
}
|
||||
|
||||
std::string clean_name;
|
||||
bool has_space = false;
|
||||
for(size_t i=0;i<strlen(h->u.var.name);i++)
|
||||
|
|
@ -275,8 +324,137 @@ void FstData::reconstructAllAtTimes(std::vector<fstHandle> &signal, uint64_t sta
|
|||
|
||||
std::string FstData::valueOf(fstHandle signal)
|
||||
{
|
||||
// Check if this is a fork scope (struct)
|
||||
auto it = fork_scope_members.find(signal);
|
||||
if (it != fork_scope_members.end()) {
|
||||
std::string result;
|
||||
const std::vector<fstHandle>& members = it->second;
|
||||
|
||||
// Iterate over members of the struct to get concatenated value.
|
||||
// The first declared member is MSB in SystemVerilog packed structs
|
||||
for (auto m = members.begin(); m != members.end(); m++) {
|
||||
fstHandle member = *m;
|
||||
std::string member_val;
|
||||
|
||||
// Check if this member is itself a nested fork scope (struct)
|
||||
if (fork_scope_members.find(member) != fork_scope_members.end()) {
|
||||
// Recursively get the value of the nested struct
|
||||
member_val = valueOf(member);
|
||||
} else {
|
||||
// Regular variable - look up in past_data
|
||||
int expected_width = 0;
|
||||
|
||||
// Get the declared width of this member
|
||||
if (handle_to_var.find(member) != handle_to_var.end()) {
|
||||
expected_width = handle_to_var[member].width;
|
||||
}
|
||||
// Get the current value of the member
|
||||
if (past_data.find(member) != past_data.end()) {
|
||||
member_val = past_data[member];
|
||||
// Pad with zeros to the expected width of the member
|
||||
if (expected_width > 0 && (int)member_val.length() < expected_width) {
|
||||
member_val = std::string(expected_width - member_val.length(), '0') + member_val;
|
||||
}
|
||||
} else if (expected_width > 0) {
|
||||
// No value yet, use X to pad
|
||||
member_val = std::string(expected_width, 'x');
|
||||
} else { // fallback to X
|
||||
member_val = "x";
|
||||
}
|
||||
}
|
||||
// Concatenate the member value to the overall struct value
|
||||
result += member_val;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Normal signal handling
|
||||
if (past_data.find(signal) == past_data.end()) {
|
||||
return std::string(handle_to_var[signal].width, 'x');
|
||||
}
|
||||
return past_data[signal];
|
||||
}
|
||||
|
||||
// Auto-discover scope from FST by finding the top module
|
||||
std::string FstData::autoScope(Module *topmod) {
|
||||
|
||||
log("Auto-discovering scope from file...\n");
|
||||
std::string top = RTLIL::unescape_id(topmod->name);
|
||||
|
||||
log("Available scopes:\n");
|
||||
std::set<std::string> unique_scopes;
|
||||
for (const auto& var : vars) {
|
||||
unique_scopes.insert(var.scope);
|
||||
}
|
||||
for (const auto& scope : unique_scopes) {
|
||||
log(" %s\n", scope.c_str());
|
||||
}
|
||||
|
||||
// Option 1 - Instance based scope matching
|
||||
// Will fail if the DUT instance name != the top module name
|
||||
log("Trying instance-based scope matching...\n");
|
||||
for (const auto& var : vars) {
|
||||
// Check if this scope ends with our top module
|
||||
log_debug("Checking scope: %s\n", var.scope.c_str());
|
||||
if (var.scope == top ||
|
||||
var.scope.find("." + top) != std::string::npos) {
|
||||
// Extract the full path up to (and including) the top module
|
||||
size_t pos = var.scope.find(top);
|
||||
if (pos != std::string::npos) {
|
||||
std::string scope = var.scope.substr(0, pos + top.length());
|
||||
return scope;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Option 2 - Port based scope matching
|
||||
// Matches based on exact port name matching of the top module
|
||||
log("Trying port-based scope matching...\n");
|
||||
|
||||
// Map top module port name to their bit widths (RTL reference point)
|
||||
dict<std::string, int> top2widths;
|
||||
for (auto wire : topmod->wires()) {
|
||||
if (wire->port_input || wire->port_output) {
|
||||
top2widths[RTLIL::unescape_id(wire->name)] = wire->width;
|
||||
}
|
||||
}
|
||||
log("Extracted %d ports from top module\n", GetSize(top2widths));
|
||||
|
||||
// For each scope, track the number of matching ports
|
||||
dict<std::string, int> scopes2matches;
|
||||
for (const auto& var : vars) {
|
||||
|
||||
// Strip array '[]' notation from variable name
|
||||
std::string var_name = var.name;
|
||||
size_t bracket = var_name.find('[');
|
||||
if (bracket != std::string::npos) {
|
||||
var_name = var_name.substr(0, bracket);
|
||||
}
|
||||
|
||||
// Check if this variable name matches one of our top module port names and width
|
||||
if (top2widths.count(var_name) && top2widths[var_name] == var.width) {
|
||||
scopes2matches[var.scope] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Find scopes with exact matches
|
||||
// If there is a tie, return the longest scope
|
||||
std::string result = "";
|
||||
for (const auto& entry : scopes2matches) {
|
||||
int num_matches = entry.second;
|
||||
if (num_matches == GetSize(top2widths)) {
|
||||
std::string scope = entry.first;
|
||||
if (result.empty() || scope.length() > result.length()) {
|
||||
result = scope;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!result.empty()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// No match found
|
||||
log_warning("Could not auto-discover scope for module '%s'...\n",
|
||||
RTLIL::unescape_id(topmod->name).c_str());
|
||||
return "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ class FstData
|
|||
dict<int,fstHandle> getMemoryHandles(std::string name);
|
||||
double getTimescale() { return timescale; }
|
||||
const char *getTimescaleString() { return timescale_str.c_str(); }
|
||||
std::string autoScope(Module *topmod);
|
||||
private:
|
||||
void extractVarNames();
|
||||
|
||||
|
|
@ -69,6 +70,7 @@ private:
|
|||
uint64_t last_time;
|
||||
std::map<fstHandle, std::string> past_data;
|
||||
uint64_t past_time;
|
||||
std::map<fstHandle, std::vector<fstHandle>> fork_scope_members;
|
||||
double timescale;
|
||||
std::string timescale_str;
|
||||
uint64_t start_time;
|
||||
|
|
|
|||
|
|
@ -1474,8 +1474,14 @@ struct SimWorker : SimShared
|
|||
log_assert(top == nullptr);
|
||||
fst = new FstData(sim_filename);
|
||||
timescale = fst->getTimescaleString();
|
||||
if (scope.empty())
|
||||
log_error("Scope must be defined for co-simulation.\n");
|
||||
if (scope.empty()) {
|
||||
scope = fst->autoScope(topmod);
|
||||
if (scope.empty()) {
|
||||
log_error("No scope found for module '%s'. Please specify -scope explicitly.\n",
|
||||
RTLIL::unescape_id(topmod->name).c_str());
|
||||
}
|
||||
}
|
||||
log("Using scope: \"%s\"\n", scope.c_str());
|
||||
|
||||
top = new SimInstance(this, scope, topmod);
|
||||
register_signals();
|
||||
|
|
|
|||
|
|
@ -85,16 +85,29 @@ struct NegoptPass : public Pass {
|
|||
|
||||
for (auto module : design->selected_modules()) {
|
||||
if (run_pre) {
|
||||
// manual2sub and sub2neg only need to run once: no downstream
|
||||
// pre-subpass creates the patterns they match
|
||||
// separate pm instances so sub2neg sees the $sub cells manual2sub creates.
|
||||
{
|
||||
peepopt_pm pm(module);
|
||||
pm.setup(module->selected_cells());
|
||||
pm.run_manual2sub();
|
||||
log_flush();
|
||||
}
|
||||
{
|
||||
peepopt_pm pm(module);
|
||||
pm.setup(module->selected_cells());
|
||||
pm.run_sub2neg();
|
||||
log_flush();
|
||||
}
|
||||
|
||||
// negexpand/negneg/negmux can feed each other.
|
||||
did_something = true;
|
||||
for (int iter = 0; iter < max_iterations && did_something; iter++) {
|
||||
did_something = false;
|
||||
peepopt_pm pm(module);
|
||||
pm.setup(module->selected_cells());
|
||||
|
||||
pm.run_manual2sub(); // Reduce manual 2's complement to subtraction first
|
||||
log_flush();
|
||||
pm.run_sub2neg();
|
||||
log_flush();
|
||||
pm.run_negexpand();
|
||||
log_flush();
|
||||
pm.run_negneg();
|
||||
|
|
|
|||
|
|
@ -12,9 +12,8 @@ pattern manual2sub
|
|||
|
||||
state <SigSpec> minuend subtrahend result_sig root_a root_b
|
||||
state <bool> is_signed
|
||||
state <SigSpec> inner_y
|
||||
state <SigSpec> inner_y inner_port_sig_A root_port_sig inner_port_sig_B
|
||||
|
||||
// 1. Match the "root" add (the one that produces the final result)
|
||||
match root_add
|
||||
select root_add->type == $add
|
||||
set root_a port(root_add, \A)
|
||||
|
|
@ -23,8 +22,7 @@ match root_add
|
|||
set is_signed root_add->getParam(ID::A_SIGNED).as_bool()
|
||||
endmatch
|
||||
|
||||
// 2. Case A: (a + ~b) + 1
|
||||
// Check if root_add has a constant 1
|
||||
// Case A: (a + ~b) + 1
|
||||
code root_add inner_y
|
||||
{
|
||||
SigSpec pa = root_a;
|
||||
|
|
@ -51,20 +49,28 @@ code root_add inner_y
|
|||
}
|
||||
endcode
|
||||
|
||||
// Find the inner add
|
||||
// inner_y is discovered in code so gating happens in the code block
|
||||
// Find the inner add whose Y feeds the non-constant port of root_add
|
||||
match inner_add_A
|
||||
select inner_add_A->type == $add
|
||||
index <SigSpec> port(inner_add_A, \Y) === inner_y
|
||||
filter nusers(port(inner_add_A, \Y)) == 2
|
||||
endmatch
|
||||
|
||||
// Find the NOT gate on one of the ports of inner_add_A
|
||||
// Branch over both ports of inner_add_A to find the NOT gate
|
||||
code inner_port_sig_A
|
||||
inner_port_sig_A = port(inner_add_A, \A);
|
||||
branch;
|
||||
inner_port_sig_A = port(inner_add_A, \B);
|
||||
endcode
|
||||
|
||||
match not_gate_A
|
||||
select not_gate_A->type == $not
|
||||
index <SigSpec> port(not_gate_A, \Y) === inner_port_sig_A
|
||||
endmatch
|
||||
|
||||
code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed
|
||||
{
|
||||
// Require consistent signedness on the root add.
|
||||
// Require consistent signedness across the chain
|
||||
if (root_add->getParam(ID::B_SIGNED).as_bool() != is_signed)
|
||||
reject;
|
||||
|
||||
|
|
@ -73,11 +79,7 @@ code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed
|
|||
if (inner_add_A->getParam(ID::B_SIGNED).as_bool() != is_signed)
|
||||
reject;
|
||||
|
||||
if (port(inner_add_A, \Y) != inner_y)
|
||||
reject;
|
||||
if (nusers(port(inner_add_A, \Y)) != 2)
|
||||
reject;
|
||||
|
||||
// Determine which port is the NOT output and which is the minuend
|
||||
SigSpec not_y = port(not_gate_A, \Y);
|
||||
SigSpec add_a = port(inner_add_A, \A);
|
||||
SigSpec add_b = port(inner_add_A, \B);
|
||||
|
|
@ -92,7 +94,6 @@ code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed
|
|||
|
||||
subtrahend = port(not_gate_A, \A);
|
||||
|
||||
// Create the subtraction cell
|
||||
log("manual2sub in %s: Found (a + ~b) + 1 pattern, creating $sub for %s\n", log_id(module), log_signal(result_sig));
|
||||
Cell *cell = root_add;
|
||||
int width = GetSize(result_sig);
|
||||
|
|
@ -100,27 +101,25 @@ code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed
|
|||
// Reject if the +1 wrap boundary is narrower than the final result
|
||||
if (inner_width < width)
|
||||
reject;
|
||||
// Anchor both operands to the inner add width to preserve carry behavior
|
||||
SigSpec minuend_rs = minuend;
|
||||
SigSpec subtrahend_rs = subtrahend;
|
||||
// Anchor both operands to the inner add width to preserve carry behavior
|
||||
minuend_rs.extend_u0(inner_width, is_signed);
|
||||
subtrahend_rs.extend_u0(inner_width, is_signed);
|
||||
SigSpec sub_y = result_sig;
|
||||
// Extend the sub result back to the root width when needed
|
||||
if (inner_width != width) {
|
||||
sub_y = module->addWire(NEW_ID2_SUFFIX("sub_y"), inner_width);
|
||||
}
|
||||
Cell *sub = module->addSub(NEW_ID2_SUFFIX("sub"), minuend_rs, subtrahend_rs, sub_y, is_signed);
|
||||
// Extend the sub result back to root width when inner is wider
|
||||
if (inner_width != width) {
|
||||
SigSpec sub_y_rs = sub_y;
|
||||
sub_y_rs.extend_u0(width, is_signed);
|
||||
module->connect(result_sig, sub_y_rs);
|
||||
}
|
||||
|
||||
// Let fixup_parameters handle width adjustments
|
||||
sub->fixup_parameters();
|
||||
|
||||
// Remove old cells
|
||||
autoremove(root_add);
|
||||
autoremove(inner_add_A);
|
||||
autoremove(not_gate_A);
|
||||
|
|
@ -130,23 +129,36 @@ code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed
|
|||
}
|
||||
endcode
|
||||
|
||||
// 3. Case B: a + (~b + 1)
|
||||
// Case B: a + (~b + 1)
|
||||
|
||||
// Branch over both ports of root_add to find the inner (~b + 1) add
|
||||
code root_port_sig
|
||||
root_port_sig = port(root_add, \A);
|
||||
branch;
|
||||
root_port_sig = port(root_add, \B);
|
||||
endcode
|
||||
|
||||
// Find the inner add on either port of root_add
|
||||
match inner_add_B
|
||||
select inner_add_B->type == $add
|
||||
filter port(inner_add_B, \Y) == root_a || port(inner_add_B, \Y) == root_b
|
||||
select nusers(port(inner_add_B, \Y)) == 2
|
||||
index <SigSpec> port(inner_add_B, \Y) === root_port_sig
|
||||
filter nusers(port(inner_add_B, \Y)) == 2
|
||||
endmatch
|
||||
|
||||
// Check if inner_add_B has a constant 1 and a NOT gate
|
||||
// Branch over both ports of inner_add_B to find the NOT gate
|
||||
code inner_port_sig_B
|
||||
inner_port_sig_B = port(inner_add_B, \A);
|
||||
branch;
|
||||
inner_port_sig_B = port(inner_add_B, \B);
|
||||
endcode
|
||||
|
||||
match not_gate_B
|
||||
select not_gate_B->type == $not
|
||||
index <SigSpec> port(not_gate_B, \Y) === inner_port_sig_B
|
||||
endmatch
|
||||
|
||||
code root_add inner_add_B not_gate_B minuend subtrahend result_sig is_signed
|
||||
{
|
||||
// Require consistent signedness on the root add.
|
||||
// Require consistent signedness across the chain
|
||||
if (root_add->getParam(ID::B_SIGNED).as_bool() != is_signed)
|
||||
reject;
|
||||
|
||||
|
|
@ -155,6 +167,8 @@ code root_add inner_add_B not_gate_B minuend subtrahend result_sig is_signed
|
|||
if (inner_add_B->getParam(ID::B_SIGNED).as_bool() != is_signed)
|
||||
reject;
|
||||
|
||||
// Verify inner_add_B has the form (~b + 1): one port is constant 1,
|
||||
// the other is the NOT output
|
||||
SigSpec pa = inner_add_B->getPort(ID::A);
|
||||
SigSpec pb = inner_add_B->getPort(ID::B);
|
||||
SigSpec not_y = port(not_gate_B, \Y);
|
||||
|
|
@ -175,13 +189,13 @@ code root_add inner_add_B not_gate_B minuend subtrahend result_sig is_signed
|
|||
|
||||
if (!valid) reject;
|
||||
|
||||
// The minuend is whichever root_add port is NOT the inner_add_B output
|
||||
subtrahend = port(not_gate_B, \A);
|
||||
if (inner_add_B->getPort(ID::Y) == root_add->getPort(ID::A))
|
||||
minuend = root_b;
|
||||
else
|
||||
minuend = root_a;
|
||||
|
||||
// Create the subtraction cell
|
||||
log("manual2sub in %s: Found a + (~b + 1) pattern, creating $sub for %s\n", log_id(module), log_signal(result_sig));
|
||||
Cell *cell = root_add;
|
||||
int width = GetSize(result_sig);
|
||||
|
|
@ -190,32 +204,30 @@ code root_add inner_add_B not_gate_B minuend subtrahend result_sig is_signed
|
|||
// Reject if the +1 wrap boundary is narrower than the final result
|
||||
if (inner_width < width)
|
||||
reject;
|
||||
|
||||
|
||||
// Anchor both operands to the inner add width to preserve carry behavior
|
||||
SigSpec minuend_rs = minuend;
|
||||
SigSpec subtrahend_rs = subtrahend;
|
||||
|
||||
// Anchor both operands to the inner add width to preserve carry behavior
|
||||
minuend_rs.extend_u0(inner_width, is_signed);
|
||||
subtrahend_rs.extend_u0(inner_width, is_signed);
|
||||
SigSpec sub_y = result_sig;
|
||||
|
||||
// Extend the sub result back to the root width when needed
|
||||
if (inner_width != width) {
|
||||
sub_y = module->addWire(NEW_ID2_SUFFIX("sub_y"), inner_width);
|
||||
}
|
||||
|
||||
Cell *sub = module->addSub(NEW_ID2_SUFFIX("sub"), minuend_rs, subtrahend_rs, sub_y, is_signed);
|
||||
|
||||
// Extend the sub result back to root width when inner is wider
|
||||
if (inner_width != width) {
|
||||
SigSpec sub_y_rs = sub_y;
|
||||
sub_y_rs.extend_u0(width, is_signed);
|
||||
module->connect(result_sig, sub_y_rs);
|
||||
}
|
||||
|
||||
// Let fixup_parameters handle width adjustments
|
||||
sub->fixup_parameters();
|
||||
|
||||
// Remove old cells
|
||||
autoremove(root_add);
|
||||
autoremove(inner_add_B);
|
||||
autoremove(not_gate_B);
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ endmatch
|
|||
|
||||
match neg_a
|
||||
select neg_a->type == $neg
|
||||
select nusers(port(neg_a, \Y)) == 2
|
||||
filter nusers(port(neg_a, \Y)) == 2
|
||||
index <SigSpec> port(neg_a, \Y) === mux_a
|
||||
set neg_a_in port(neg_a, \A)
|
||||
set neg_a_y port(neg_a, \Y)
|
||||
|
|
@ -29,7 +29,7 @@ endmatch
|
|||
|
||||
match neg_b
|
||||
select neg_b->type == $neg
|
||||
select nusers(port(neg_b, \Y)) == 2
|
||||
filter nusers(port(neg_b, \Y)) == 2
|
||||
index <SigSpec> port(neg_b, \Y) === mux_b
|
||||
set neg_b_in port(neg_b, \A)
|
||||
set neg_b_y port(neg_b, \Y)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ endcode
|
|||
|
||||
match neg
|
||||
select neg->type == $neg
|
||||
select nusers(port(neg, \Y)) == 2
|
||||
filter nusers(port(neg, \Y)) == 2
|
||||
index <SigSpec> port(neg, \Y) === (neg_on_a ? add_a : add_b)
|
||||
set neg_a port(neg, \A)
|
||||
set neg_y port(neg, \Y)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ endmatch
|
|||
match add
|
||||
select add->type == $add
|
||||
index <SigSpec> port(add, \Y) === neg_a
|
||||
select nusers(port(add, \Y)) == 2
|
||||
filter nusers(port(add, \Y)) == 2
|
||||
set add_a port(add, \A)
|
||||
set add_b port(add, \B)
|
||||
endmatch
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ match neg
|
|||
endmatch
|
||||
|
||||
match mux
|
||||
select mux->type == $mux
|
||||
select nusers(port(mux, \Y)) == 2
|
||||
select mux->type == $mux
|
||||
select nusers(port(mux, \Y)) == 2
|
||||
set mux_a port(mux, \A)
|
||||
set mux_b port(mux, \B)
|
||||
set mux_s port(mux, \S)
|
||||
|
|
@ -43,7 +43,7 @@ code neg_a neg_y mux_a mux_b mux_s mux_y a_signed
|
|||
}
|
||||
|
||||
{
|
||||
// Anchor negations to the original negation width
|
||||
// Anchor negations to the original neg output width
|
||||
int width = GetSize(neg_y);
|
||||
|
||||
Cell *cell = neg;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ endmatch
|
|||
match neg2
|
||||
select neg2->type == $neg
|
||||
index <SigSpec> port(neg2, \Y) === neg1_a
|
||||
select nusers(port(neg2, \Y)) == 2
|
||||
filter nusers(port(neg2, \Y)) == 2
|
||||
set neg2_a port(neg2, \A)
|
||||
endmatch
|
||||
|
||||
|
|
|
|||
|
|
@ -39,16 +39,16 @@ code add_a add_b add_y neg1_a neg1_y neg2_a neg2_y add_signed add_b_signed neg1_
|
|||
if (add_signed != add_b_signed)
|
||||
reject;
|
||||
|
||||
// Require negations to share the add signedness
|
||||
// Require both negations share the add's signedness
|
||||
if (neg1_signed != add_signed || neg2_signed != add_signed)
|
||||
reject;
|
||||
|
||||
{
|
||||
// Avoid matching the same neg cell twice
|
||||
// Avoid matching the same neg cell for both inputs
|
||||
if (neg1 == neg2)
|
||||
reject;
|
||||
|
||||
// Require a single wrap boundary for both negations and the sum
|
||||
// Require a single wrap boundary across both negations and the sum
|
||||
if (GetSize(neg1_y) != GetSize(neg2_y))
|
||||
reject;
|
||||
if (GetSize(add_y) != GetSize(neg1_y))
|
||||
|
|
@ -89,11 +89,13 @@ code add_a add_b add_y neg1_a neg1_y neg2_a neg2_y add_signed add_b_signed neg1_
|
|||
neg1_a_rs.extend_u0(width, add_signed);
|
||||
neg2_a_rs.extend_u0(width, add_signed);
|
||||
|
||||
// Build -(a + b) to replace (-a) + (-b)
|
||||
SigSpec sum = module->addWire(NEW_ID2_SUFFIX("sum"), width);
|
||||
Cell *new_add = module->addAdd(NEW_ID2_SUFFIX("add"), neg1_a_rs, neg2_a_rs, sum, add_signed);
|
||||
SigSpec neg_out = module->addWire(NEW_ID2_SUFFIX("neg_y"), width);
|
||||
Cell *new_neg = module->addNeg(NEW_ID2_SUFFIX("neg"), sum, neg_out, add_signed);
|
||||
|
||||
// Extend result back to original add output width
|
||||
SigSpec neg_out_rs = neg_out;
|
||||
neg_out_rs.extend_u0(GetSize(add_y), add_signed);
|
||||
module->connect(add_y, neg_out_rs);
|
||||
|
|
|
|||
|
|
@ -188,15 +188,25 @@ struct RegRenamePass : public Pass {
|
|||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
// Extract top module
|
||||
Module *topmod = design->top_module();
|
||||
if (!topmod)
|
||||
log_error("No top module found!\n");
|
||||
|
||||
// Extract pre-optimization register widths from VCD file
|
||||
dict<std::pair<std::string, std::string>, int> vcd_reg_widths;
|
||||
if (!vcd_filename.empty()) {
|
||||
if (scope.empty()) {
|
||||
log_error("No scope provided. Use -scope option.\n");
|
||||
}
|
||||
log("Reading VCD file: %s\n", vcd_filename.c_str());
|
||||
try {
|
||||
FstData fst(vcd_filename);
|
||||
if (scope.empty()) {
|
||||
scope = fst.autoScope(topmod);
|
||||
if (scope.empty()) {
|
||||
log_error("No scope found for module '%s'. Please specify -scope explicitly.\n",
|
||||
RTLIL::unescape_id(topmod->name).c_str());
|
||||
}
|
||||
}
|
||||
log("Using scope: \"%s\"\n", scope.c_str());
|
||||
for (auto &var : fst.getVars()) {
|
||||
if (var.is_reg) {
|
||||
std::string reg_vcd_scope = var.scope;
|
||||
|
|
@ -223,9 +233,6 @@ struct RegRenamePass : public Pass {
|
|||
}
|
||||
|
||||
// STEP 2: Build hierarchy and process
|
||||
Module *topmod = design->top_module();
|
||||
if (!topmod)
|
||||
log_error("No top module found!\n");
|
||||
log("Building hierarchy from scope: %s\n", scope.c_str());
|
||||
|
||||
// Build hierarchy and process register renamings
|
||||
|
|
|
|||
Loading…
Reference in New Issue