Precompute pips too
This commit is contained in:
parent
ca7eef26ac
commit
7e693ff27d
53
xc7/arch.cc
53
xc7/arch.cc
@ -35,7 +35,7 @@ NEXTPNR_NAMESPACE_BEGIN
|
|||||||
|
|
||||||
std::unique_ptr<const TorcInfo> torc_info;
|
std::unique_ptr<const TorcInfo> torc_info;
|
||||||
TorcInfo::TorcInfo(Arch *ctx, const std::string &inDeviceName, const std::string &inPackageName)
|
TorcInfo::TorcInfo(Arch *ctx, const std::string &inDeviceName, const std::string &inPackageName)
|
||||||
: ddb(new DDB(inDeviceName, inPackageName)), sites(ddb->getSites()), tiles(ddb->getTiles()), segments(ddb->getSegments()), bel_to_site_index(construct_bel_to_site_index(ctx, sites)), num_bels(bel_to_site_index.size()), site_index_to_type(construct_site_index_to_type(ctx, sites)), bel_to_z(construct_bel_to_z(sites, num_bels, site_index_to_type)), wire_to_tilewire(construct_wire_to_tilewire(segments, tiles)), num_wires(wire_to_tilewire.size())
|
: ddb(new DDB(inDeviceName, inPackageName)), sites(ddb->getSites()), tiles(ddb->getTiles()), segments(ddb->getSegments()), bel_to_site_index(construct_bel_to_site_index(ctx, sites)), num_bels(bel_to_site_index.size()), site_index_to_type(construct_site_index_to_type(ctx, sites)), bel_to_z(construct_bel_to_z(sites, num_bels, site_index_to_type)), wire_to_tilewire(construct_wire_to_tilewire(segments, tiles)), num_wires(wire_to_tilewire.size()), pip_to_arc(construct_pip_to_arc(wire_to_tilewire, *ddb, wire_to_pips_uphill, wire_to_pips_downhill)), num_pips(pip_to_arc.size())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
std::vector<SiteIndex> TorcInfo::construct_bel_to_site_index(Arch* ctx, const Sites &sites)
|
std::vector<SiteIndex> TorcInfo::construct_bel_to_site_index(Arch* ctx, const Sites &sites)
|
||||||
@ -125,6 +125,55 @@ std::vector<Tilewire> TorcInfo::construct_wire_to_tilewire(const Segments& segme
|
|||||||
|
|
||||||
return wire_to_tilewire;
|
return wire_to_tilewire;
|
||||||
}
|
}
|
||||||
|
std::vector<Arc> TorcInfo::construct_pip_to_arc(const std::vector<Tilewire>& wire_to_tilewire, const DDB& ddb, std::vector<std::vector<int>> &wire_to_pips_uphill, std::vector<std::vector<int>> &wire_to_pips_downhill)
|
||||||
|
{
|
||||||
|
std::vector<Arc> pip_to_arc;
|
||||||
|
wire_to_pips_downhill.resize(wire_to_tilewire.size());
|
||||||
|
|
||||||
|
auto arc_hash = [](const Arc& arc) {
|
||||||
|
size_t seed = 0;
|
||||||
|
boost::hash_combine(seed, hash_value(arc.getSourceTilewire()));
|
||||||
|
boost::hash_combine(seed, hash_value(arc.getSinkTilewire()));
|
||||||
|
return seed;
|
||||||
|
};
|
||||||
|
std::unordered_map<Arc, int, decltype(arc_hash)> arc_to_pip(0, arc_hash);
|
||||||
|
|
||||||
|
ArcVector arcs;
|
||||||
|
for (auto i = 0u; i < wire_to_tilewire.size(); ++i) {
|
||||||
|
const auto &tw = wire_to_tilewire[i];
|
||||||
|
if (tw.isUndefined()) continue;
|
||||||
|
arcs.clear();
|
||||||
|
const_cast<DDB&>(ddb).expandSegmentSinks(tw, arcs);
|
||||||
|
|
||||||
|
auto index = pip_to_arc.size();
|
||||||
|
pip_to_arc.insert(pip_to_arc.end(), arcs.begin(), arcs.end());
|
||||||
|
|
||||||
|
auto &pips = wire_to_pips_downhill[i];
|
||||||
|
pips.reserve(arcs.size());
|
||||||
|
for (const auto& a : arcs) {
|
||||||
|
pips.push_back(index);
|
||||||
|
arc_to_pip.emplace(a, index);
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pip_to_arc.shrink_to_fit();
|
||||||
|
|
||||||
|
wire_to_pips_uphill.resize(wire_to_tilewire.size());
|
||||||
|
for (auto i = 0u; i < wire_to_tilewire.size(); ++i) {
|
||||||
|
const auto &tw = wire_to_tilewire[i];
|
||||||
|
if (tw.isUndefined()) continue;
|
||||||
|
arcs.clear();
|
||||||
|
const_cast<DDB&>(ddb).expandSegmentSinks(tw, arcs);
|
||||||
|
|
||||||
|
auto &pips = wire_to_pips_uphill[i];
|
||||||
|
pips.reserve(arcs.size());
|
||||||
|
for (const auto& a : arcs)
|
||||||
|
pips.push_back(arc_to_pip.at(a));
|
||||||
|
}
|
||||||
|
|
||||||
|
return pip_to_arc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
@ -160,7 +209,7 @@ Arch::Arch(ArchArgs args) : args(args)
|
|||||||
//bel_carry.resize(chip_info->num_bels);
|
//bel_carry.resize(chip_info->num_bels);
|
||||||
bel_to_cell.resize(torc_info->num_bels);
|
bel_to_cell.resize(torc_info->num_bels);
|
||||||
wire_to_net.resize(torc_info->num_wires);
|
wire_to_net.resize(torc_info->num_wires);
|
||||||
//pip_to_net.resize(chip_info->num_pips);
|
pip_to_net.resize(torc_info->num_pips);
|
||||||
//switches_locked.resize(chip_info->num_switches);
|
//switches_locked.resize(chip_info->num_switches);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
126
xc7/arch.h
126
xc7/arch.h
@ -252,7 +252,7 @@ struct TorcInfo {
|
|||||||
}
|
}
|
||||||
std::string wire_to_name(int32_t index) const
|
std::string wire_to_name(int32_t index) const
|
||||||
{
|
{
|
||||||
const auto tw = wire_to_tilewire[index];
|
const auto &tw = wire_to_tilewire[index];
|
||||||
ExtendedWireInfo ewi(*ddb, tw);
|
ExtendedWireInfo ewi(*ddb, tw);
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << ewi.mTileName << "/" << ewi.mWireName;
|
ss << ewi.mTileName << "/" << ewi.mWireName;
|
||||||
@ -260,9 +260,16 @@ struct TorcInfo {
|
|||||||
}
|
}
|
||||||
int32_t tilewire_to_wire(const Tilewire &tw) const
|
int32_t tilewire_to_wire(const Tilewire &tw) const
|
||||||
{
|
{
|
||||||
const auto& segment = segments.getTilewireSegment(tw);
|
const auto &segment = segments.getTilewireSegment(tw);
|
||||||
return segment.getCompactSegmentIndex();
|
return segment.getCompactSegmentIndex();
|
||||||
}
|
}
|
||||||
|
int32_t pip_to_wire(int32_t index) const
|
||||||
|
{
|
||||||
|
const auto &arc = pip_to_arc[index];
|
||||||
|
const auto &segment = segments.getTilewireSegment(arc.getSourceTilewire());
|
||||||
|
return segment.getCompactSegmentIndex();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
const std::vector<SiteIndex> bel_to_site_index;
|
const std::vector<SiteIndex> bel_to_site_index;
|
||||||
const int num_bels;
|
const int num_bels;
|
||||||
@ -270,12 +277,18 @@ struct TorcInfo {
|
|||||||
const std::vector<int8_t> bel_to_z;
|
const std::vector<int8_t> bel_to_z;
|
||||||
const std::vector<Tilewire> wire_to_tilewire;
|
const std::vector<Tilewire> wire_to_tilewire;
|
||||||
const int num_wires;
|
const int num_wires;
|
||||||
|
std::vector<std::vector<int>> wire_to_pips_uphill;
|
||||||
|
std::vector<std::vector<int>> wire_to_pips_downhill;
|
||||||
|
const std::vector<Arc> pip_to_arc;
|
||||||
|
const int num_pips;
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::vector<SiteIndex> construct_bel_to_site_index(Arch *ctx, const Sites &sites);
|
static std::vector<SiteIndex> construct_bel_to_site_index(Arch *ctx, const Sites &sites);
|
||||||
static std::vector<IdString> construct_site_index_to_type(Arch *ctx, const Sites &sites);
|
static std::vector<IdString> construct_site_index_to_type(Arch *ctx, const Sites &sites);
|
||||||
static std::vector<int8_t> construct_bel_to_z(const Sites &sites, const int num_bels, const std::vector<IdString> &site_index_to_type);
|
static std::vector<int8_t> construct_bel_to_z(const Sites &sites, const int num_bels, const std::vector<IdString> &site_index_to_type);
|
||||||
static std::vector<Tilewire> construct_wire_to_tilewire(const Segments &segments, const Tiles &tiles);
|
static std::vector<Tilewire> construct_wire_to_tilewire(const Segments &segments, const Tiles &tiles);
|
||||||
|
static std::vector<Arc> construct_pip_to_arc(const std::vector<Tilewire>& wire_to_tilewire, const DDB& ddb, std::vector<std::vector<int>> &wire_to_pips_uphill, std::vector<std::vector<int>> &wire_to_pips_downhill);
|
||||||
};
|
};
|
||||||
extern std::unique_ptr<const TorcInfo> torc_info;
|
extern std::unique_ptr<const TorcInfo> torc_info;
|
||||||
|
|
||||||
@ -435,11 +448,11 @@ struct Arch : BaseCtx
|
|||||||
mutable std::unordered_map<IdString, int> pip_by_name;
|
mutable std::unordered_map<IdString, int> pip_by_name;
|
||||||
mutable std::unordered_map<Loc, BelId> bel_by_loc;
|
mutable std::unordered_map<Loc, BelId> bel_by_loc;
|
||||||
|
|
||||||
std::vector<bool> bel_carry;
|
//std::vector<bool> bel_carry;
|
||||||
std::vector<CellInfo *> bel_to_cell;
|
std::vector<CellInfo *> bel_to_cell;
|
||||||
std::vector<NetInfo *> wire_to_net;
|
std::vector<NetInfo *> wire_to_net;
|
||||||
std::vector<NetInfo *> pip_to_net;
|
std::vector<NetInfo *> pip_to_net;
|
||||||
std::vector<NetInfo *> switches_locked;
|
//std::vector<NetInfo *> switches_locked;
|
||||||
|
|
||||||
ArchArgs args;
|
ArchArgs args;
|
||||||
Arch(ArchArgs args);
|
Arch(ArchArgs args);
|
||||||
@ -533,7 +546,7 @@ struct Arch : BaseCtx
|
|||||||
|
|
||||||
Loc getBelLocation(BelId bel) const
|
Loc getBelLocation(BelId bel) const
|
||||||
{
|
{
|
||||||
auto &tile_info = torc_info->bel_to_tile_info(bel.index);
|
const auto &tile_info = torc_info->bel_to_tile_info(bel.index);
|
||||||
|
|
||||||
Loc loc;
|
Loc loc;
|
||||||
loc.x = tile_info.getCol();
|
loc.x = tile_info.getCol();
|
||||||
@ -591,11 +604,11 @@ struct Arch : BaseCtx
|
|||||||
auto it = net_wires.find(wire);
|
auto it = net_wires.find(wire);
|
||||||
NPNR_ASSERT(it != net_wires.end());
|
NPNR_ASSERT(it != net_wires.end());
|
||||||
|
|
||||||
//auto pip = it->second.pip;
|
auto pip = it->second.pip;
|
||||||
//if (pip != PipId()) {
|
if (pip != PipId()) {
|
||||||
// pip_to_net[pip.index] = nullptr;
|
pip_to_net[pip.index] = nullptr;
|
||||||
// switches_locked[chip_info->pip_data[pip.index].switch_index] = nullptr;
|
//switches_locked[chip_info->pip_data[pip.index].switch_index] = nullptr;
|
||||||
//}
|
}
|
||||||
|
|
||||||
net_wires.erase(it);
|
net_wires.erase(it);
|
||||||
wire_to_net[wire.index] = nullptr;
|
wire_to_net[wire.index] = nullptr;
|
||||||
@ -640,6 +653,7 @@ struct Arch : BaseCtx
|
|||||||
//NPNR_ASSERT(wire != WireId());
|
//NPNR_ASSERT(wire != WireId());
|
||||||
//range.b.ptr = chip_info->wire_data[wire.index].bel_pins.get();
|
//range.b.ptr = chip_info->wire_data[wire.index].bel_pins.get();
|
||||||
//range.e.ptr = range.b.ptr + chip_info->wire_data[wire.index].num_bel_pins;
|
//range.e.ptr = range.b.ptr + chip_info->wire_data[wire.index].num_bel_pins;
|
||||||
|
throw;
|
||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -659,17 +673,16 @@ struct Arch : BaseCtx
|
|||||||
{
|
{
|
||||||
NPNR_ASSERT(pip != PipId());
|
NPNR_ASSERT(pip != PipId());
|
||||||
NPNR_ASSERT(pip_to_net[pip.index] == nullptr);
|
NPNR_ASSERT(pip_to_net[pip.index] == nullptr);
|
||||||
NPNR_ASSERT(switches_locked[chip_info->pip_data[pip.index].switch_index] == nullptr);
|
//NPNR_ASSERT(switches_locked[chip_info->pip_data[pip.index].switch_index] == nullptr);
|
||||||
|
|
||||||
pip_to_net[pip.index] = net;
|
pip_to_net[pip.index] = net;
|
||||||
switches_locked[chip_info->pip_data[pip.index].switch_index] = net;
|
//switches_locked[chip_info->pip_data[pip.index].switch_index] = net;
|
||||||
|
|
||||||
WireId dst;
|
WireId dst = getPipDstWire(pip);
|
||||||
//dst.index = chip_info->pip_data[pip.index].dst;
|
|
||||||
NPNR_ASSERT(wire_to_net[dst.index] == nullptr);
|
NPNR_ASSERT(wire_to_net[dst.index] == nullptr);
|
||||||
wire_to_net[dst.index] = net;
|
wire_to_net[dst.index] = net;
|
||||||
//net->wires[dst].pip = pip;
|
net->wires[dst].pip = pip;
|
||||||
//net->wires[dst].strength = strength;
|
net->wires[dst].strength = strength;
|
||||||
refreshUiPip(pip);
|
refreshUiPip(pip);
|
||||||
refreshUiWire(dst);
|
refreshUiWire(dst);
|
||||||
}
|
}
|
||||||
@ -678,16 +691,15 @@ struct Arch : BaseCtx
|
|||||||
{
|
{
|
||||||
NPNR_ASSERT(pip != PipId());
|
NPNR_ASSERT(pip != PipId());
|
||||||
NPNR_ASSERT(pip_to_net[pip.index] != nullptr);
|
NPNR_ASSERT(pip_to_net[pip.index] != nullptr);
|
||||||
NPNR_ASSERT(switches_locked[chip_info->pip_data[pip.index].switch_index] != nullptr);
|
//NPNR_ASSERT(switches_locked[chip_info->pip_data[pip.index].switch_index] != nullptr);
|
||||||
|
|
||||||
WireId dst;
|
WireId dst = getPipDstWire(pip);
|
||||||
//dst.index = chip_info->pip_data[pip.index].dst;
|
|
||||||
NPNR_ASSERT(wire_to_net[dst.index] != nullptr);
|
NPNR_ASSERT(wire_to_net[dst.index] != nullptr);
|
||||||
wire_to_net[dst.index] = nullptr;
|
wire_to_net[dst.index] = nullptr;
|
||||||
pip_to_net[pip.index]->wires.erase(dst);
|
pip_to_net[pip.index]->wires.erase(dst);
|
||||||
|
|
||||||
pip_to_net[pip.index] = nullptr;
|
pip_to_net[pip.index] = nullptr;
|
||||||
switches_locked[chip_info->pip_data[pip.index].switch_index] = nullptr;
|
//switches_locked[chip_info->pip_data[pip.index].switch_index] = nullptr;
|
||||||
refreshUiPip(pip);
|
refreshUiPip(pip);
|
||||||
refreshUiWire(dst);
|
refreshUiWire(dst);
|
||||||
}
|
}
|
||||||
@ -695,25 +707,26 @@ struct Arch : BaseCtx
|
|||||||
bool checkPipAvail(PipId pip) const
|
bool checkPipAvail(PipId pip) const
|
||||||
{
|
{
|
||||||
NPNR_ASSERT(pip != PipId());
|
NPNR_ASSERT(pip != PipId());
|
||||||
auto &pi = chip_info->pip_data[pip.index];
|
//auto &pi = chip_info->pip_data[pip.index];
|
||||||
auto &si = chip_info->bits_info->switches[pi.switch_index];
|
//auto &si = chip_info->bits_info->switches[pi.switch_index];
|
||||||
|
|
||||||
if (switches_locked[pi.switch_index] != nullptr)
|
//if (switches_locked[pi.switch_index] != nullptr)
|
||||||
return false;
|
// return false;
|
||||||
|
|
||||||
if (pi.flags & PipInfoPOD::FLAG_ROUTETHRU) {
|
//if (pi.flags & PipInfoPOD::FLAG_ROUTETHRU) {
|
||||||
NPNR_ASSERT(si.bel >= 0);
|
// NPNR_ASSERT(si.bel >= 0);
|
||||||
if (bel_to_cell[si.bel] != nullptr)
|
// if (bel_to_cell[si.bel] != nullptr)
|
||||||
return false;
|
// return false;
|
||||||
}
|
//}
|
||||||
|
|
||||||
if (pi.flags & PipInfoPOD::FLAG_NOCARRY) {
|
//if (pi.flags & PipInfoPOD::FLAG_NOCARRY) {
|
||||||
NPNR_ASSERT(si.bel >= 0);
|
// NPNR_ASSERT(si.bel >= 0);
|
||||||
if (bel_carry[si.bel])
|
// if (bel_carry[si.bel])
|
||||||
return false;
|
// return false;
|
||||||
}
|
//}
|
||||||
|
|
||||||
return true;
|
//return true;
|
||||||
|
return getConflictingPipNet(pip);
|
||||||
}
|
}
|
||||||
|
|
||||||
NetInfo *getBoundPipNet(PipId pip) const
|
NetInfo *getBoundPipNet(PipId pip) const
|
||||||
@ -725,22 +738,27 @@ struct Arch : BaseCtx
|
|||||||
NetInfo *getConflictingPipNet(PipId pip) const
|
NetInfo *getConflictingPipNet(PipId pip) const
|
||||||
{
|
{
|
||||||
NPNR_ASSERT(pip != PipId());
|
NPNR_ASSERT(pip != PipId());
|
||||||
return switches_locked[chip_info->pip_data[pip.index].switch_index];
|
//return switches_locked[chip_info->pip_data[pip.index].switch_index];
|
||||||
|
return pip_to_net[pip.index];
|
||||||
}
|
}
|
||||||
|
|
||||||
AllPipRange getPips() const
|
AllPipRange getPips() const
|
||||||
{
|
{
|
||||||
AllPipRange range;
|
AllPipRange range;
|
||||||
range.b.cursor = 0;
|
range.b.cursor = 0;
|
||||||
range.e.cursor = chip_info->num_pips;
|
range.e.cursor = torc_info->num_pips;
|
||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
Loc getPipLocation(PipId pip) const
|
Loc getPipLocation(PipId pip) const
|
||||||
{
|
{
|
||||||
|
const auto &arc = torc_info->pip_to_arc[pip.index];
|
||||||
|
const auto &tw = arc.getSourceTilewire();
|
||||||
|
const auto &tile_info = torc_info->tiles.getTileInfo(tw.getTileIndex());
|
||||||
|
|
||||||
Loc loc;
|
Loc loc;
|
||||||
loc.x = chip_info->pip_data[pip.index].x;
|
loc.x = tile_info.getCol();
|
||||||
loc.y = chip_info->pip_data[pip.index].y;
|
loc.y = tile_info.getRow();
|
||||||
loc.z = 0;
|
loc.z = 0;
|
||||||
return loc;
|
return loc;
|
||||||
}
|
}
|
||||||
@ -755,7 +773,11 @@ struct Arch : BaseCtx
|
|||||||
{
|
{
|
||||||
WireId wire;
|
WireId wire;
|
||||||
NPNR_ASSERT(pip != PipId());
|
NPNR_ASSERT(pip != PipId());
|
||||||
//wire.index = chip_info->pip_data[pip.index].src;
|
|
||||||
|
const auto &arc = torc_info->pip_to_arc[pip.index];
|
||||||
|
const auto &tw = arc.getSourceTilewire();
|
||||||
|
wire.index = torc_info->tilewire_to_wire(tw);
|
||||||
|
|
||||||
return wire;
|
return wire;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -763,7 +785,11 @@ struct Arch : BaseCtx
|
|||||||
{
|
{
|
||||||
WireId wire;
|
WireId wire;
|
||||||
NPNR_ASSERT(pip != PipId());
|
NPNR_ASSERT(pip != PipId());
|
||||||
//wire.index = chip_info->pip_data[pip.index].dst;
|
|
||||||
|
const auto &arc = torc_info->pip_to_arc[pip.index];
|
||||||
|
const auto &tw = arc.getSinkTilewire();
|
||||||
|
wire.index = torc_info->tilewire_to_wire(tw);
|
||||||
|
|
||||||
return wire;
|
return wire;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -771,10 +797,10 @@ struct Arch : BaseCtx
|
|||||||
{
|
{
|
||||||
DelayInfo delay;
|
DelayInfo delay;
|
||||||
NPNR_ASSERT(pip != PipId());
|
NPNR_ASSERT(pip != PipId());
|
||||||
if (fast_part)
|
//if (fast_part)
|
||||||
delay.delay = chip_info->pip_data[pip.index].fast_delay;
|
// delay.delay = chip_info->pip_data[pip.index].fast_delay;
|
||||||
else
|
//else
|
||||||
delay.delay = chip_info->pip_data[pip.index].slow_delay;
|
// delay.delay = chip_info->pip_data[pip.index].slow_delay;
|
||||||
return delay;
|
return delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -782,8 +808,9 @@ struct Arch : BaseCtx
|
|||||||
{
|
{
|
||||||
PipRange range;
|
PipRange range;
|
||||||
NPNR_ASSERT(wire != WireId());
|
NPNR_ASSERT(wire != WireId());
|
||||||
//range.b.cursor = chip_info->wire_data[wire.index].pips_downhill.get();
|
const auto &pips = torc_info->wire_to_pips_downhill[wire.index];
|
||||||
//range.e.cursor = range.b.cursor + chip_info->wire_data[wire.index].num_downhill;
|
range.b.cursor = pips.data();
|
||||||
|
range.e.cursor = range.b.cursor + pips.size();
|
||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -791,8 +818,9 @@ struct Arch : BaseCtx
|
|||||||
{
|
{
|
||||||
PipRange range;
|
PipRange range;
|
||||||
NPNR_ASSERT(wire != WireId());
|
NPNR_ASSERT(wire != WireId());
|
||||||
//range.b.cursor = chip_info->wire_data[wire.index].pips_uphill.get();
|
const auto &pips = torc_info->wire_to_pips_uphill[wire.index];
|
||||||
//range.e.cursor = range.b.cursor + chip_info->wire_data[wire.index].num_uphill;
|
range.b.cursor = pips.data();
|
||||||
|
range.e.cursor = range.b.cursor + pips.size();
|
||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user