aboutsummaryrefslogtreecommitdiffstats
path: root/fpga_interchange/site_arch.impl.h
blob: 88df6c0f89d7cd67d26e05e20aad910d3c81865a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
/*
 *  nextpnr -- Next Generation Place and Route
 *
 *  Copyright (C) 2021  Symbiflow Authors
 *
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#ifndef SITE_ARCH_IMPL_H
#define SITE_ARCH_IMPL_H

#include "context.h"
#include "site_arch.h"

NEXTPNR_NAMESPACE_BEGIN

inline const ChipInfoPOD &SiteInformation::chip_info() const { return *ctx->chip_info; }

inline bool SiteInformation::is_wire_in_site(WireId wire) const
{
    if (wire.tile != tile) {
        return false;
    }

    return ctx->wire_info(wire).site == site;
}

inline bool SiteInformation::is_bel_in_site(BelId bel) const
{
    if (bel.tile != tile) {
        return false;
    }

    return bel_info(ctx->chip_info, bel).site == site;
}

inline bool SiteInformation::is_pip_part_of_site(PipId pip) const
{
    if (pip.tile != tile) {
        return false;
    }

    const auto &tile_type_data = ctx->chip_info->tile_types[tile_type];
    const auto &pip_data = tile_type_data.pip_data[pip.index];
    return pip_data.site == site;
}

inline bool SiteInformation::is_site_port(PipId pip) const
{
    const auto &tile_type_data = ctx->chip_info->tile_types[tile_type];
    const auto &pip_data = tile_type_data.pip_data[pip.index];
    if (pip_data.site == -1) {
        return false;
    }
    auto &bel_data = tile_type_data.bel_data[pip_data.bel];
    return bel_data.category == BEL_CATEGORY_SITE_PORT;
}

inline SiteWire SiteWire::make(const SiteInformation *site_info, WireId site_wire)
{
    NPNR_ASSERT(site_info->is_wire_in_site(site_wire));
    SiteWire out;
    out.type = SITE_WIRE;
    out.wire = site_wire;
    return out;
}

inline SiteWire SiteWire::make_site_port(const SiteInformation *site_info, PipId pip, bool dst_wire)
{
    const auto &tile_type_data = site_info->chip_info().tile_types[site_info->tile_type];
    const auto &pip_data = tile_type_data.pip_data[pip.index];

    // This pip should definitely be part of this site
    NPNR_ASSERT(pip_data.site == site_info->site);

    SiteWire out;

    const auto &src_data = tile_type_data.wire_data[pip_data.src_index];
    const auto &dst_data = tile_type_data.wire_data[pip_data.dst_index];

    if (dst_wire) {
        if (src_data.site == site_info->site) {
            NPNR_ASSERT(dst_data.site == -1);
            out.type = SITE_PORT_SINK;
            out.pip = pip;
            out.wire = canonical_wire(&site_info->chip_info(), pip.tile, pip_data.dst_index);
        } else {
            NPNR_ASSERT(src_data.site == -1);
            NPNR_ASSERT(dst_data.site == site_info->site);
            out.type = SITE_WIRE;
            out.wire.tile = pip.tile;
            out.wire.index = pip_data.dst_index;
        }
    } else {
        if (src_data.site == site_info->site) {
            NPNR_ASSERT(dst_data.site == -1);
            out.type = SITE_WIRE;
            out.wire.tile = pip.tile;
            out.wire.index = pip_data.src_index;
        } else {
            NPNR_ASSERT(src_data.site == -1);
            NPNR_ASSERT(dst_data.site == site_info->site);
            out.type = SITE_PORT_SOURCE;
            out.pip = pip;
            out.wire = canonical_wire(&site_info->chip_info(), pip.tile, pip_data.src_index);
        }
    }

    return out;
}

inline SitePip SitePip::make(const SiteInformation *site_info, PipId pip)
{
    SitePip out;
    out.pip = pip;

    if (site_info->is_site_port(pip)) {
        out.type = SITE_PORT;
    } else {
        out.type = SITE_PIP;
    }
    return out;
}

inline SiteWire SiteArch::getPipSrcWire(const SitePip &site_pip) const
{
    SiteWire site_wire;
    switch (site_pip.type) {
    case SitePip::Type::SITE_PIP:
        return SiteWire::make(site_info, ctx->getPipSrcWire(site_pip.pip));
    case SitePip::Type::SITE_PORT:
        return SiteWire::make_site_port(site_info, site_pip.pip, /*dst_wire=*/false);
    case SitePip::Type::SOURCE_TO_SITE_PORT:
        NPNR_ASSERT(site_pip.wire.type == SiteWire::OUT_OF_SITE_SOURCE);
        return site_pip.wire;
    case SitePip::Type::SITE_PORT_TO_SINK:
        site_wire = SiteWire::make_site_port(site_info, site_pip.pip, /*dst_wire=*/true);
        NPNR_ASSERT(site_wire.type == SiteWire::SITE_PORT_SINK);
        return site_wire;
    case SitePip::Type::SITE_PORT_TO_SITE_PORT:
        site_wire = SiteWire::make_site_port(site_info, site_pip.pip, /*dst_wire=*/true);
        NPNR_ASSERT(site_wire.type == SiteWire::SITE_PORT_SINK);
        return site_wire;
    default:
        // Unreachable!
        NPNR_ASSERT(false);
    }
}

inline SiteWire SiteArch::getPipDstWire(const SitePip &site_pip) const
{
    switch (site_pip.type) {
    case SitePip::Type::SITE_PIP:
        return SiteWire::make(site_info, ctx->getPipDstWire(site_pip.pip));
    case SitePip::Type::SITE_PORT:
        return SiteWire::make_site_port(site_info, site_pip.pip, /*dst_wire=*/true);
    case SitePip::Type::SOURCE_TO_SITE_PORT: {
        SiteWire site_wire = SiteWire::make_site_port(site_info, site_pip.pip, /*dst_wire=*/false);
        NPNR_ASSERT(site_wire.type == SiteWire::SITE_PORT_SOURCE);
        return site_wire;
    }
    case SitePip::Type::SITE_PORT_TO_SINK:
        NPNR_ASSERT(site_pip.wire.type == SiteWire::OUT_OF_SITE_SINK);
        return site_pip.wire;
    case SitePip::Type::SITE_PORT_TO_SITE_PORT: {
        SiteWire site_wire = SiteWire::make_site_port(site_info, site_pip.other_pip, /*dst_wire=*/false);
        NPNR_ASSERT(site_wire.type == SiteWire::SITE_PORT_SOURCE);
        return site_wire;
    }
    default:
        // Unreachable!
        NPNR_ASSERT(false);
    }
}

inline bool SiteArch::is_pip_synthetic(const SitePip &pip) const
{
    if (pip.type != SitePip::SITE_PORT) {
        // This isn't a site port, so its valid!
        return false;
    }

    auto &tile_type = ctx->chip_info->tile_types[site_info->tile_type];
    auto &pip_data = tile_type.pip_data[pip.pip.index];
    if (pip_data.site == -1) {
        return pip_data.extra_data == -1;
    } else {
        auto &bel_data = tile_type.bel_data[pip_data.bel];
        return bel_data.synthetic != 0;
    }
}

inline SyntheticType SiteArch::pip_synthetic_type(const SitePip &pip) const
{
    if (pip.type != SitePip::SITE_PORT) {
        // This isn't a site port, so its valid!
        return NOT_SYNTH;
    }

    auto &tile_type = ctx->chip_info->tile_types[site_info->tile_type];
    auto &pip_data = tile_type.pip_data[pip.pip.index];
    NPNR_ASSERT(pip_data.site != -1);
    auto &bel_data = tile_type.bel_data[pip_data.bel];
    return SyntheticType(bel_data.synthetic);
}

inline SitePip SitePipDownhillIterator::operator*() const
{
    switch (state) {
    case NORMAL_PIPS: {
        PipId pip;
        pip.tile = site_arch->site_info->tile;
        pip.index = (*pips_downhill)[cursor];
        return SitePip::make(site_arch->site_info, pip);
    }
    case PORT_SINK_TO_PORT_SRC:
        return SitePip::make(site_arch->site_info, site_wire.pip, site_arch->input_site_ports.at(cursor));
    case OUT_OF_SITE_SINKS:
        return SitePip::make(site_arch->site_info, site_wire.pip, site_arch->out_of_site_sinks.at(cursor));
    case OUT_OF_SITE_SOURCE_TO_PORT_SRC:
        return SitePip::make(site_arch->site_info, site_wire, site_arch->input_site_ports.at(cursor));
    case SITE_PORT:
        return SitePip::make(site_arch->site_info, site_wire.pip);
    default:
        // Unreachable!
        NPNR_ASSERT(false);
    }
}

inline const RelSlice<int32_t> *SitePipDownhillRange::init_pip_range() const
{
    NPNR_ASSERT(site_wire.type == SiteWire::SITE_WIRE);
    NPNR_ASSERT(site_wire.wire.tile == site_arch->site_info->tile);
    return &site_arch->ctx->chip_info->tile_types[site_arch->site_info->tile_type]
                    .wire_data[site_wire.wire.index]
                    .pips_downhill;
}

inline SitePipDownhillIterator SitePipDownhillRange::begin() const
{
    SitePipDownhillIterator b;
    b.state = SitePipDownhillIterator::BEGIN;
    b.site_arch = site_arch;
    b.site_wire = site_wire;
    b.cursor = 0;
    if (site_wire.type == SiteWire::SITE_WIRE) {
        b.pips_downhill = init_pip_range();
    }

    ++b;

    return b;
}

inline bool SiteArch::isInverting(const SitePip &site_pip) const
{
    if (site_pip.type != SitePip::SITE_PIP) {
        return false;
    }

    auto &tile_type = ctx->chip_info->tile_types[site_info->tile_type];
    auto &pip_data = tile_type.pip_data[site_pip.pip.index];
    NPNR_ASSERT(pip_data.site != -1);
    auto &bel_data = tile_type.bel_data[pip_data.bel];

    // Is a fixed inverter if the non_inverting_pin is another pin.
    return bel_data.non_inverting_pin != pip_data.extra_data && bel_data.inverting_pin == pip_data.extra_data;
}

inline bool SiteArch::canInvert(const SitePip &site_pip) const
{
    if (site_pip.type != SitePip::SITE_PIP) {
        return false;
    }

    auto &tile_type = ctx->chip_info->tile_types[site_info->tile_type];
    auto &pip_data = tile_type.pip_data[site_pip.pip.index];
    NPNR_ASSERT(pip_data.site != -1);
    auto &bel_data = tile_type.bel_data[pip_data.bel];

    // Can optionally invert if this pip is both the non_inverting_pin and
    // inverting pin.
    return bel_data.non_inverting_pin == pip_data.extra_data && bel_data.inverting_pin == pip_data.extra_data;
}

inline PhysicalNetlist::PhysNetlist::NetType SiteArch::prefered_constant_net_type(const SitePip &site_pip) const
{
    // FIXME: Implement site port overrides from chipdb once available.
    IdString prefered_constant_net(ctx->chip_info->constants->best_constant_net);
    IdString gnd_net_name(ctx->chip_info->constants->gnd_net_name);
    IdString vcc_net_name(ctx->chip_info->constants->vcc_net_name);

    if (prefered_constant_net == IdString()) {
        return PhysicalNetlist::PhysNetlist::NetType::SIGNAL;
    } else if (prefered_constant_net == gnd_net_name) {
        return PhysicalNetlist::PhysNetlist::NetType::GND;
    } else if (prefered_constant_net == vcc_net_name) {
        return PhysicalNetlist::PhysNetlist::NetType::VCC;
    } else {
        log_error("prefered_constant_net %s is not the GND (%s) or VCC(%s) net?\n", prefered_constant_net.c_str(ctx),
                  gnd_net_name.c_str(ctx), vcc_net_name.c_str(ctx));
    }
}

inline SiteWire SiteArch::getBelPinWire(BelId bel, IdString pin) const
{
    WireId wire = ctx->getBelPinWire(bel, pin);
    return SiteWire::make(site_info, wire);
}

inline PortType SiteArch::getBelPinType(BelId bel, IdString pin) const { return ctx->getBelPinType(bel, pin); }

NEXTPNR_NAMESPACE_END

#endif /* SITE_ARCH_H */
">) f2 = PythFactory7(tag.shared_ptr, tag.base, 12) assert f1.get() == 11 assert f2.get() == 112 assert not f1.has_alias() assert f2.has_alias() g1 = TestFactory7(tag.shared_ptr, tag.invalid_base, 13) assert g1.get() == 13 assert not g1.has_alias() with pytest.raises(TypeError) as excinfo: PythFactory7(tag.shared_ptr, tag.invalid_base, 14) assert ( str(excinfo.value) == "pybind11::init(): construction failed: returned holder-wrapped instance is not an " "alias instance" ) assert [i.alive() for i in cstats] == [13, 7] assert ConstructorStats.detail_reg_inst() == n_inst + 13 del a1, a2, b1, d1, e1, e2 assert [i.alive() for i in cstats] == [7, 4] assert ConstructorStats.detail_reg_inst() == n_inst + 7 del b2, c1, c2, d2, f1, f2, g1 assert [i.alive() for i in cstats] == [0, 0] assert ConstructorStats.detail_reg_inst() == n_inst assert [i.values() for i in cstats] == [ ["1", "2", "3", "4", "5", "6", "7", "8", "9", "100", "11", "12", "13", "14"], ["2", "4", "6", "8", "9", "100", "12"], ] def test_no_placement_new(capture): """Prior to 2.2, `py::init<...>` relied on the type supporting placement new; this tests a class without placement new support.""" with capture: a = m.NoPlacementNew(123) found = re.search(r"^operator new called, returning (\d+)\n$", str(capture)) assert found assert a.i == 123 with capture: del a pytest.gc_collect() assert capture == "operator delete called on " + found.group(1) with capture: b = m.NoPlacementNew() found = re.search(r"^operator new called, returning (\d+)\n$", str(capture)) assert found assert b.i == 100 with capture: del b pytest.gc_collect() assert capture == "operator delete called on " + found.group(1) def test_multiple_inheritance(): class MITest(m.TestFactory1, m.TestFactory2): def __init__(self): m.TestFactory1.__init__(self, tag.unique_ptr, 33) m.TestFactory2.__init__(self, tag.move) a = MITest() assert m.TestFactory1.value.fget(a) == "33" assert m.TestFactory2.value.fget(a) == "(empty2)" def create_and_destroy(*args): a = m.NoisyAlloc(*args) print("---") del a pytest.gc_collect() def strip_comments(s): return re.sub(r"\s+#.*", "", s) def test_reallocation_a(capture, msg): """When the constructor is overloaded, previous overloads can require a preallocated value. This test makes sure that such preallocated values only happen when they might be necessary, and that they are deallocated properly.""" pytest.gc_collect() with capture: create_and_destroy(1) assert ( msg(capture) == """ noisy new noisy placement new NoisyAlloc(int 1) --- ~NoisyAlloc() noisy delete """ ) def test_reallocation_b(capture, msg): with capture: create_and_destroy(1.5) assert msg(capture) == strip_comments( """ noisy new # allocation required to attempt first overload noisy delete # have to dealloc before considering factory init overload noisy new # pointer factory calling "new", part 1: allocation NoisyAlloc(double 1.5) # ... part two, invoking constructor --- ~NoisyAlloc() # Destructor noisy delete # operator delete """ ) def test_reallocation_c(capture, msg): with capture: create_and_destroy(2, 3) assert msg(capture) == strip_comments( """ noisy new # pointer factory calling "new", allocation NoisyAlloc(int 2) # constructor --- ~NoisyAlloc() # Destructor noisy delete # operator delete """ ) def test_reallocation_d(capture, msg): with capture: create_and_destroy(2.5, 3) assert msg(capture) == strip_comments( """ NoisyAlloc(double 2.5) # construction (local func variable: operator_new not called) noisy new # return-by-value "new" part 1: allocation ~NoisyAlloc() # moved-away local func variable destruction --- ~NoisyAlloc() # Destructor noisy delete # operator delete """ ) def test_reallocation_e(capture, msg): with capture: create_and_destroy(3.5, 4.5) assert msg(capture) == strip_comments( """ noisy new # preallocation needed before invoking placement-new overload noisy placement new # Placement new NoisyAlloc(double 3.5) # construction --- ~NoisyAlloc() # Destructor noisy delete # operator delete """ ) def test_reallocation_f(capture, msg): with capture: create_and_destroy(4, 0.5) assert msg(capture) == strip_comments( """ noisy new # preallocation needed before invoking placement-new overload noisy delete # deallocation of preallocated storage noisy new # Factory pointer allocation NoisyAlloc(int 4) # factory pointer construction --- ~NoisyAlloc() # Destructor noisy delete # operator delete """ ) def test_reallocation_g(capture, msg): with capture: create_and_destroy(5, "hi") assert msg(capture) == strip_comments( """ noisy new # preallocation needed before invoking first placement new noisy delete # delete before considering new-style constructor noisy new # preallocation for second placement new noisy placement new # Placement new in the second placement new overload NoisyAlloc(int 5) # construction --- ~NoisyAlloc() # Destructor noisy delete # operator delete """ ) @pytest.mark.skipif("env.PY2") def test_invalid_self(): """Tests invocation of the pybind-registered base class with an invalid `self` argument. You can only actually do this on Python 3: Python 2 raises an exception itself if you try.""" class NotPybindDerived(object): pass # Attempts to initialize with an invalid type passed as `self`: class BrokenTF1(m.TestFactory1): def __init__(self, bad): if bad == 1: a = m.TestFactory2(tag.pointer, 1) m.TestFactory1.__init__(a, tag.pointer) elif bad == 2: a = NotPybindDerived() m.TestFactory1.__init__(a, tag.pointer) # Same as above, but for a class with an alias: class BrokenTF6(m.TestFactory6): def __init__(self, bad): if bad == 1: a = m.TestFactory2(tag.pointer, 1) m.TestFactory6.__init__(a, tag.base, 1) elif bad == 2: a = m.TestFactory2(tag.pointer, 1) m.TestFactory6.__init__(a, tag.alias, 1) elif bad == 3: m.TestFactory6.__init__( NotPybindDerived.__new__(NotPybindDerived), tag.base, 1 ) elif bad == 4: m.TestFactory6.__init__( NotPybindDerived.__new__(NotPybindDerived), tag.alias, 1 ) for arg in (1, 2): with pytest.raises(TypeError) as excinfo: BrokenTF1(arg) assert ( str(excinfo.value) == "__init__(self, ...) called with invalid `self` argument" ) for arg in (1, 2, 3, 4): with pytest.raises(TypeError) as excinfo: BrokenTF6(arg) assert ( str(excinfo.value) == "__init__(self, ...) called with invalid `self` argument" )