mirror of https://github.com/YosysHQ/yosys.git
memory_libmap: print additional debug messages when no valid mapping is found
This commit is contained in:
parent
a6be7b4751
commit
57de249881
|
@ -183,6 +183,7 @@ struct MemMapping {
|
|||
dict<std::pair<int, int>, bool> wr_implies_rd_cache;
|
||||
dict<std::pair<int, int>, bool> wr_excludes_rd_cache;
|
||||
dict<std::pair<int, int>, bool> wr_excludes_srst_cache;
|
||||
std::string rejected_cfg_debug_msgs;
|
||||
|
||||
MemMapping(MapWorker &worker, Mem &mem, const Library &lib, const PassOptions &opts) : worker(worker), qcsat(worker.modwalker), mem(mem), lib(lib), opts(opts) {
|
||||
determine_style();
|
||||
|
@ -202,7 +203,7 @@ struct MemMapping {
|
|||
if (!check_init(rdef))
|
||||
continue;
|
||||
if (rdef.prune_rom && mem.wr_ports.empty()) {
|
||||
log_debug("memory %s.%s: rejecting mapping to %s: ROM not allowed\n", log_id(mem.module->name), log_id(mem.memid), log_id(rdef.id));
|
||||
log_debug("memory %s.%s: rejecting mapping to %s: ROM mapping disabled (prune_rom set)\n", log_id(mem.module->name), log_id(mem.memid), log_id(rdef.id));
|
||||
continue;
|
||||
}
|
||||
MemConfig cfg;
|
||||
|
@ -311,6 +312,59 @@ struct MemMapping {
|
|||
void prune_post_geom();
|
||||
void emit_port(const MemConfig &cfg, std::vector<Cell*> &cells, const PortVariant &pdef, const char *name, int wpidx, int rpidx, const std::vector<int> &hw_addr_swizzle);
|
||||
void emit(const MemConfig &cfg);
|
||||
|
||||
void log_reject(std::string message){
|
||||
if(ys_debug(1)) {
|
||||
rejected_cfg_debug_msgs += message;
|
||||
rejected_cfg_debug_msgs += "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void log_reject(const Ram &ram, std::string message) {
|
||||
if(ys_debug(1)) {
|
||||
rejected_cfg_debug_msgs += stringf("can't map to to %s: ", log_id(ram.id));
|
||||
rejected_cfg_debug_msgs += message;
|
||||
rejected_cfg_debug_msgs += "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void log_reject(const Ram &ram, const PortGroup &pg, std::string message) {
|
||||
if(ys_debug(1)) {
|
||||
rejected_cfg_debug_msgs += stringf("can't map to port group [");
|
||||
bool first = true;
|
||||
for (std::string portname : pg.names){
|
||||
if (!first) rejected_cfg_debug_msgs += ", ";
|
||||
rejected_cfg_debug_msgs += portname;
|
||||
first = false;
|
||||
}
|
||||
rejected_cfg_debug_msgs += stringf("] of %s: ", log_id(ram.id));
|
||||
rejected_cfg_debug_msgs += message;
|
||||
rejected_cfg_debug_msgs += "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void log_reject(const Ram &ram, const PortGroup &pg, int pvi, std::string message) {
|
||||
if(ys_debug(1)) {
|
||||
rejected_cfg_debug_msgs += stringf("can't map to option selection [");
|
||||
bool first = true;
|
||||
for(auto opt : pg.variants[pvi].options){
|
||||
if (!first) rejected_cfg_debug_msgs += ", ";
|
||||
rejected_cfg_debug_msgs += opt.first;
|
||||
rejected_cfg_debug_msgs += stringf(" = %s", log_const(opt.second));
|
||||
first = false;
|
||||
}
|
||||
rejected_cfg_debug_msgs += "] of port group [";
|
||||
first = true;
|
||||
for (std::string portname : pg.names){
|
||||
if (!first) rejected_cfg_debug_msgs += ", ";
|
||||
rejected_cfg_debug_msgs += portname;
|
||||
first = false;
|
||||
}
|
||||
rejected_cfg_debug_msgs += stringf("] of %s: ", log_id(ram.id));
|
||||
rejected_cfg_debug_msgs += message;
|
||||
rejected_cfg_debug_msgs += "\n";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void MemMapping::dump_configs(int stage) {
|
||||
|
@ -479,7 +533,7 @@ void MemMapping::determine_style() {
|
|||
// Determine whether the memory can be mapped entirely to soft logic.
|
||||
bool MemMapping::determine_logic_ok() {
|
||||
if (kind != RamKind::Auto && kind != RamKind::Logic) {
|
||||
log_debug("memory %s.%s: rejecting mapping to logic: RAM kind conflicts with attribute\n", log_id(mem.module->name), log_id(mem.memid));
|
||||
log_reject("can't map to logic: RAM kind conflicts with attribute");
|
||||
return false;
|
||||
}
|
||||
// Memory is mappable entirely to soft logic iff all its write ports are in the same clock domain.
|
||||
|
@ -487,15 +541,15 @@ bool MemMapping::determine_logic_ok() {
|
|||
return true;
|
||||
for (auto &port: mem.wr_ports) {
|
||||
if (!port.clk_enable){
|
||||
log_debug("memory %s.%s: rejecting mapping to logic: unclocked port\n", log_id(mem.module->name), log_id(mem.memid));
|
||||
log_reject("can't map to logic: unclocked port");
|
||||
return false;
|
||||
}
|
||||
if (port.clk != mem.wr_ports[0].clk) {
|
||||
log_debug("memory %s.%s: rejecting mapping to logic: ports have different write clock domains\n", log_id(mem.module->name), log_id(mem.memid));
|
||||
log_reject("can't map to logic: ports have different write clock domains");
|
||||
return false;
|
||||
}
|
||||
if (port.clk_polarity != mem.wr_ports[0].clk_polarity) {
|
||||
log_debug("memory %s.%s: rejecting mapping to logic: ports have different write clock polarity\n", log_id(mem.module->name), log_id(mem.memid));
|
||||
log_reject("can't map to logic: ports have different write clock polarity");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -510,20 +564,20 @@ bool MemMapping::check_ram_kind(const Ram &ram) {
|
|||
return true;
|
||||
if (kind == RamKind::Auto || kind == RamKind::NotLogic) {
|
||||
if (ram.kind == RamKind::Distributed && opts.no_auto_distributed) {
|
||||
log_debug("memory %s.%s: rejecting mapping to %s: option -no-auto-distributed given\n", log_id(mem.module->name), log_id(mem.memid), log_id(ram.id));
|
||||
log_reject(ram, "option -no-auto-distributed given");
|
||||
return false;
|
||||
}
|
||||
if (ram.kind == RamKind::Block && opts.no_auto_block) {
|
||||
log_debug("memory %s.%s: rejecting mapping to %s: option -no-auto-block given\n", log_id(mem.module->name), log_id(mem.memid), log_id(ram.id));
|
||||
log_reject(ram, "option -no-auto-block given");
|
||||
return false;
|
||||
}
|
||||
if (ram.kind == RamKind::Huge && opts.no_auto_huge) {
|
||||
log_debug("memory %s.%s: rejecting mapping to %s: option -no-auto-huge given\n", log_id(mem.module->name), log_id(mem.memid), log_id(ram.id));
|
||||
log_reject(ram, "option -no-auto-huge given");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
log_debug("memory %s.%s: rejecting mapping to %s: RAM kind conflicts with attribute\n", log_id(mem.module->name), log_id(mem.memid), log_id(ram.id));
|
||||
log_reject(ram, "RAM kind conflicts with attribute");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -534,7 +588,7 @@ bool MemMapping::check_ram_style(const Ram &ram) {
|
|||
for (auto &s: ram.style)
|
||||
if (s == style)
|
||||
return true;
|
||||
log_debug("memory %s.%s: rejecting mapping to %s: RAM style conflicts with attribute\n", log_id(mem.module->name), log_id(mem.memid), log_id(ram.id));
|
||||
log_reject(ram, "RAM style conflicts with attribute");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -554,10 +608,10 @@ bool MemMapping::check_init(const Ram &ram) {
|
|||
|
||||
switch (ram.init) {
|
||||
case MemoryInitKind::None:
|
||||
if(has_nonx) log_debug("memory %s.%s: rejecting mapping to %s: does not support initialization\n", log_id(mem.module->name), log_id(mem.memid), log_id(ram.id));
|
||||
if(has_nonx) log_reject(ram, "does not support initialization");
|
||||
return !has_nonx;
|
||||
case MemoryInitKind::Zero:
|
||||
if(has_one) log_debug("memory %s.%s: rejecting mapping to %s: does not support non-zero initialization\n", log_id(mem.module->name), log_id(mem.memid), log_id(ram.id));
|
||||
if(has_one) log_reject(ram, "does not support non-zero initialization");
|
||||
return !has_one;
|
||||
default:
|
||||
return true;
|
||||
|
@ -593,11 +647,12 @@ bool apply_clock(MemConfig &cfg, const PortVariant &def, SigBit clk, bool clk_po
|
|||
|
||||
// Perform write port assignment, validating clock options as we go.
|
||||
void MemMapping::assign_wr_ports() {
|
||||
log_reject(stringf("Assigning write ports... (candidate configs: %lu)", cfgs.size()));
|
||||
for (auto &port: mem.wr_ports) {
|
||||
if (!port.clk_enable) {
|
||||
// Async write ports not supported.
|
||||
cfgs.clear();
|
||||
log_debug("memory %s.%s: rejecting mapping: async write port\n", log_id(mem.module->name), log_id(mem.memid));
|
||||
log_reject("can't map at all: async write port");
|
||||
return;
|
||||
}
|
||||
MemConfigs new_cfgs;
|
||||
|
@ -609,21 +664,27 @@ void MemMapping::assign_wr_ports() {
|
|||
for (auto &oport: cfg.wr_ports)
|
||||
if (oport.port_group == pgi)
|
||||
used++;
|
||||
if (used >= GetSize(pg.names))
|
||||
if (used >= GetSize(pg.names)) {
|
||||
log_reject(*cfg.def, pg, "not enough unassigned ports remaining");
|
||||
continue;
|
||||
}
|
||||
for (int pvi = 0; pvi < GetSize(pg.variants); pvi++) {
|
||||
auto &def = pg.variants[pvi];
|
||||
// Make sure the target is a write port.
|
||||
if (def.kind == PortKind::Ar || def.kind == PortKind::Sr)
|
||||
if (def.kind == PortKind::Ar || def.kind == PortKind::Sr) {
|
||||
log_reject(*cfg.def, pg, pvi, "not a write port");
|
||||
continue;
|
||||
}
|
||||
MemConfig new_cfg = cfg;
|
||||
WrPortConfig pcfg;
|
||||
pcfg.rd_port = -1;
|
||||
pcfg.port_group = pgi;
|
||||
pcfg.port_variant = pvi;
|
||||
pcfg.def = &def;
|
||||
if (!apply_clock(new_cfg, def, port.clk, port.clk_polarity))
|
||||
if (!apply_clock(new_cfg, def, port.clk, port.clk_polarity)) {
|
||||
log_reject(*cfg.def, pg, pvi, "incompatible clock polarity");
|
||||
continue;
|
||||
}
|
||||
new_cfg.wr_ports.push_back(pcfg);
|
||||
new_cfgs.push_back(new_cfg);
|
||||
}
|
||||
|
@ -635,6 +696,7 @@ void MemMapping::assign_wr_ports() {
|
|||
|
||||
// Perform read port assignment, validating clock and rden options as we go.
|
||||
void MemMapping::assign_rd_ports() {
|
||||
log_reject(stringf("Assigning read ports... (candidate configs: %lu)", cfgs.size()));
|
||||
for (int pidx = 0; pidx < GetSize(mem.rd_ports); pidx++) {
|
||||
auto &port = mem.rd_ports[pidx];
|
||||
MemConfigs new_cfgs;
|
||||
|
@ -649,17 +711,23 @@ void MemMapping::assign_rd_ports() {
|
|||
for (auto &oport: cfg.wr_ports)
|
||||
if (oport.port_group == pgi)
|
||||
used++;
|
||||
if (used >= GetSize(pg.names))
|
||||
if (used >= GetSize(pg.names)) {
|
||||
log_reject(*cfg.def, pg, "not enough unassigned ports remaining");
|
||||
continue;
|
||||
}
|
||||
for (int pvi = 0; pvi < GetSize(pg.variants); pvi++) {
|
||||
auto &def = pg.variants[pvi];
|
||||
// Make sure the target is a read port.
|
||||
if (def.kind == PortKind::Sw)
|
||||
if (def.kind == PortKind::Sw) {
|
||||
log_reject(*cfg.def, pg, pvi, "not a read port");
|
||||
continue;
|
||||
}
|
||||
// If mapping an async port, accept only async defs.
|
||||
if (!port.clk_enable) {
|
||||
if (def.kind == PortKind::Sr || def.kind == PortKind::Srsw)
|
||||
if (def.kind == PortKind::Sr || def.kind == PortKind::Srsw) {
|
||||
log_reject(*cfg.def, pg, pvi, "not an asynchronous read port");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
MemConfig new_cfg = cfg;
|
||||
RdPortConfig pcfg;
|
||||
|
@ -669,8 +737,10 @@ void MemMapping::assign_rd_ports() {
|
|||
pcfg.def = &def;
|
||||
if (def.kind == PortKind::Sr || def.kind == PortKind::Srsw) {
|
||||
pcfg.emu_sync = false;
|
||||
if (!apply_clock(new_cfg, def, port.clk, port.clk_polarity))
|
||||
if (!apply_clock(new_cfg, def, port.clk, port.clk_polarity)) {
|
||||
log_reject(*cfg.def, pg, pvi, "incompatible clock polarity");
|
||||
continue;
|
||||
}
|
||||
// Decide if rden is usable.
|
||||
if (port.en != State::S1) {
|
||||
if (def.clk_en) {
|
||||
|
@ -692,22 +762,34 @@ void MemMapping::assign_rd_ports() {
|
|||
auto &wpcfg = cfg.wr_ports[wpidx];
|
||||
auto &def = *wpcfg.def;
|
||||
// Make sure the write port is not yet shared.
|
||||
if (wpcfg.rd_port != -1)
|
||||
if (wpcfg.rd_port != -1) {
|
||||
log_reject(stringf("can't share write port %d: already shared by a different read port", wpidx));
|
||||
continue;
|
||||
}
|
||||
// Make sure the target is a read port.
|
||||
if (def.kind == PortKind::Sw)
|
||||
if (def.kind == PortKind::Sw) {
|
||||
log_reject(stringf("can't share write port %d: not a read-write port", wpidx));
|
||||
continue;
|
||||
}
|
||||
// Validate address compatibility.
|
||||
if (!addr_compatible(wpidx, pidx))
|
||||
if (!addr_compatible(wpidx, pidx)) {
|
||||
log_reject(stringf("can't share write port %d: addresses are not compatible", wpidx));
|
||||
continue;
|
||||
}
|
||||
// Validate clock compatibility, if needed.
|
||||
if (def.kind == PortKind::Srsw) {
|
||||
if (!port.clk_enable)
|
||||
if (!port.clk_enable) {
|
||||
log_reject(stringf("can't share write port %d: incompatible enable", wpidx));
|
||||
continue;
|
||||
if (port.clk != wport.clk)
|
||||
}
|
||||
if (port.clk != wport.clk) {
|
||||
log_reject(stringf("can't share write port %d: different clock signal", wpidx));
|
||||
continue;
|
||||
if (port.clk_polarity != wport.clk_polarity)
|
||||
}
|
||||
if (port.clk_polarity != wport.clk_polarity) {
|
||||
log_reject(stringf("can't share write port %d: incompatible clock polarity", wpidx));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Okay, let's fill it in.
|
||||
MemConfig new_cfg = cfg;
|
||||
|
@ -724,8 +806,10 @@ void MemMapping::assign_rd_ports() {
|
|||
bool col_x = port.collision_x_mask[wpidx];
|
||||
if (def.rdwr == RdWrKind::NoChange) {
|
||||
if (!get_wr_excludes_rd(wpidx, pidx)) {
|
||||
if (!trans && !col_x)
|
||||
if (!trans && !col_x) {
|
||||
log_reject(stringf("can't share write port %d: conflict in simultaneous read and write operations", wpidx));
|
||||
continue;
|
||||
}
|
||||
if (trans)
|
||||
pcfg.emu_trans.push_back(wpidx);
|
||||
new_cfg.wr_ports[wpidx].force_uniform = true;
|
||||
|
@ -738,8 +822,10 @@ void MemMapping::assign_rd_ports() {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if (!col_x && !trans && def.rdwr != RdWrKind::Old)
|
||||
if (!col_x && !trans && def.rdwr != RdWrKind::Old) {
|
||||
log_reject(stringf("can't share write port %d: simultaneous read and write operations should result in new value but port reads old", wpidx));
|
||||
continue;
|
||||
}
|
||||
if (trans) {
|
||||
if (def.rdwr != RdWrKind::New && def.rdwr != RdWrKind::NewOnly)
|
||||
pcfg.emu_trans.push_back(wpidx);
|
||||
|
@ -771,6 +857,7 @@ void MemMapping::assign_rd_ports() {
|
|||
|
||||
// Validate transparency restrictions, determine where to add soft transparency logic.
|
||||
void MemMapping::handle_trans() {
|
||||
log_reject(stringf("Handling transparency... (candidate configs: %lu)", cfgs.size()));
|
||||
if (mem.emulate_read_first_ok()) {
|
||||
MemConfigs new_cfgs;
|
||||
for (auto &cfg: cfgs) {
|
||||
|
@ -829,15 +916,21 @@ void MemMapping::handle_trans() {
|
|||
bool found = false;
|
||||
for (auto &tdef: wpcfg.def->wrtrans) {
|
||||
// Check if the target matches.
|
||||
if (tdef.target_kind == WrTransTargetKind::Group && rpcfg.port_group != tdef.target_group)
|
||||
if (tdef.target_kind == WrTransTargetKind::Group && rpcfg.port_group != tdef.target_group) {
|
||||
log_reject(*cfg.def, stringf("transparency with target port group %d not supported", tdef.target_group));
|
||||
continue;
|
||||
}
|
||||
// Check if the transparency kind is acceptable.
|
||||
if (transparent) {
|
||||
if (tdef.kind == WrTransKind::Old)
|
||||
if (tdef.kind == WrTransKind::Old) {
|
||||
log_reject(*cfg.def, stringf("target %d has wrong transparency kind: new value required", tdef.target_group));
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (tdef.kind != WrTransKind::Old)
|
||||
if (tdef.kind != WrTransKind::Old) {
|
||||
log_reject(*cfg.def, stringf("target %d has wrong transparency kind: old value required", tdef.target_group));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Okay, we can use this cap.
|
||||
new_cfgs.push_back(cfg);
|
||||
|
@ -2101,8 +2194,11 @@ struct MemoryLibMapPass : public Pass {
|
|||
int idx = -1;
|
||||
int best = map.logic_cost;
|
||||
if (!map.logic_ok) {
|
||||
if (map.cfgs.empty())
|
||||
if (map.cfgs.empty()) {
|
||||
log_debug("Rejected candidates for mapping memory %s.%s:\n", log_id(module->name), log_id(mem.memid));
|
||||
log_debug("%s", map.rejected_cfg_debug_msgs.c_str());
|
||||
log_error("no valid mapping found for memory %s.%s\n", log_id(module->name), log_id(mem.memid));
|
||||
}
|
||||
idx = 0;
|
||||
best = map.cfgs[0].cost;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue