1 /*
2  *  nextpnr -- Next Generation Place and Route
3  *
4  *  Copyright (C) 2018  David Shah <david@symbioticeda.com>
5  *
6  *  Permission to use, copy, modify, and/or distribute this software for any
7  *  purpose with or without fee is hereby granted, provided that the above
8  *  copyright notice and this permission notice appear in all copies.
9  *
10  *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  *
18  */
19 
20 #include "cells.h"
21 #include <algorithm>
22 #include "design_utils.h"
23 #include "log.h"
24 #include "util.h"
25 
26 NEXTPNR_NAMESPACE_BEGIN
27 
add_port(const Context * ctx,CellInfo * cell,std::string name,PortType dir)28 void add_port(const Context *ctx, CellInfo *cell, std::string name, PortType dir)
29 {
30     IdString id = ctx->id(name);
31     cell->ports[id] = PortInfo{id, nullptr, dir};
32 }
33 
create_ecp5_cell(Context * ctx,IdString type,std::string name)34 std::unique_ptr<CellInfo> create_ecp5_cell(Context *ctx, IdString type, std::string name)
35 {
36     static int auto_idx = 0;
37     std::unique_ptr<CellInfo> new_cell = std::unique_ptr<CellInfo>(new CellInfo());
38     if (name.empty()) {
39         new_cell->name = ctx->id("$nextpnr_" + type.str(ctx) + "_" + std::to_string(auto_idx++));
40     } else {
41         new_cell->name = ctx->id(name);
42     }
43     new_cell->type = type;
44 
45     auto copy_bel_ports = [&]() {
46         // First find a Bel of the target type
47         BelId tgt;
48         for (auto bel : ctx->getBels()) {
49             if (ctx->getBelType(bel) == type) {
50                 tgt = bel;
51                 break;
52             }
53         }
54         NPNR_ASSERT(tgt != BelId());
55         for (auto port : ctx->getBelPins(tgt)) {
56             add_port(ctx, new_cell.get(), port.str(ctx), ctx->getBelPinType(tgt, port));
57         }
58     };
59 
60     if (type == ctx->id("TRELLIS_SLICE")) {
61         new_cell->params[ctx->id("MODE")] = std::string("LOGIC");
62         new_cell->params[ctx->id("GSR")] = std::string("DISABLED");
63         new_cell->params[ctx->id("SRMODE")] = std::string("LSR_OVER_CE");
64         new_cell->params[ctx->id("CEMUX")] = std::string("1");
65         new_cell->params[ctx->id("CLKMUX")] = std::string("CLK");
66         new_cell->params[ctx->id("LSRMUX")] = std::string("LSR");
67         new_cell->params[ctx->id("LUT0_INITVAL")] = Property(0, 16);
68         new_cell->params[ctx->id("LUT1_INITVAL")] = Property(0, 16);
69         new_cell->params[ctx->id("REG0_SD")] = std::string("0");
70         new_cell->params[ctx->id("REG1_SD")] = std::string("0");
71         new_cell->params[ctx->id("REG0_REGSET")] = std::string("RESET");
72         new_cell->params[ctx->id("REG1_REGSET")] = std::string("RESET");
73         new_cell->params[ctx->id("CCU2_INJECT1_0")] = std::string("NO");
74         new_cell->params[ctx->id("CCU2_INJECT1_1")] = std::string("NO");
75         new_cell->params[ctx->id("WREMUX")] = std::string("WRE");
76 
77         add_port(ctx, new_cell.get(), "A0", PORT_IN);
78         add_port(ctx, new_cell.get(), "B0", PORT_IN);
79         add_port(ctx, new_cell.get(), "C0", PORT_IN);
80         add_port(ctx, new_cell.get(), "D0", PORT_IN);
81 
82         add_port(ctx, new_cell.get(), "A1", PORT_IN);
83         add_port(ctx, new_cell.get(), "B1", PORT_IN);
84         add_port(ctx, new_cell.get(), "C1", PORT_IN);
85         add_port(ctx, new_cell.get(), "D1", PORT_IN);
86 
87         add_port(ctx, new_cell.get(), "M0", PORT_IN);
88         add_port(ctx, new_cell.get(), "M1", PORT_IN);
89 
90         add_port(ctx, new_cell.get(), "FCI", PORT_IN);
91         add_port(ctx, new_cell.get(), "FXA", PORT_IN);
92         add_port(ctx, new_cell.get(), "FXB", PORT_IN);
93 
94         add_port(ctx, new_cell.get(), "CLK", PORT_IN);
95         add_port(ctx, new_cell.get(), "LSR", PORT_IN);
96         add_port(ctx, new_cell.get(), "CE", PORT_IN);
97 
98         add_port(ctx, new_cell.get(), "DI0", PORT_IN);
99         add_port(ctx, new_cell.get(), "DI1", PORT_IN);
100 
101         add_port(ctx, new_cell.get(), "WD0", PORT_IN);
102         add_port(ctx, new_cell.get(), "WD1", PORT_IN);
103         add_port(ctx, new_cell.get(), "WAD0", PORT_IN);
104         add_port(ctx, new_cell.get(), "WAD1", PORT_IN);
105         add_port(ctx, new_cell.get(), "WAD2", PORT_IN);
106         add_port(ctx, new_cell.get(), "WAD3", PORT_IN);
107         add_port(ctx, new_cell.get(), "WRE", PORT_IN);
108         add_port(ctx, new_cell.get(), "WCK", PORT_IN);
109 
110         add_port(ctx, new_cell.get(), "F0", PORT_OUT);
111         add_port(ctx, new_cell.get(), "Q0", PORT_OUT);
112         add_port(ctx, new_cell.get(), "F1", PORT_OUT);
113         add_port(ctx, new_cell.get(), "Q1", PORT_OUT);
114 
115         add_port(ctx, new_cell.get(), "FCO", PORT_OUT);
116         add_port(ctx, new_cell.get(), "OFX0", PORT_OUT);
117         add_port(ctx, new_cell.get(), "OFX1", PORT_OUT);
118 
119         add_port(ctx, new_cell.get(), "WDO0", PORT_OUT);
120         add_port(ctx, new_cell.get(), "WDO1", PORT_OUT);
121         add_port(ctx, new_cell.get(), "WDO2", PORT_OUT);
122         add_port(ctx, new_cell.get(), "WDO3", PORT_OUT);
123         add_port(ctx, new_cell.get(), "WADO0", PORT_OUT);
124         add_port(ctx, new_cell.get(), "WADO1", PORT_OUT);
125         add_port(ctx, new_cell.get(), "WADO2", PORT_OUT);
126         add_port(ctx, new_cell.get(), "WADO3", PORT_OUT);
127     } else if (type == ctx->id("TRELLIS_IO")) {
128         new_cell->params[ctx->id("DIR")] = std::string("INPUT");
129         new_cell->attrs[ctx->id("IO_TYPE")] = std::string("LVCMOS33");
130         new_cell->params[ctx->id("DATAMUX_ODDR")] = std::string("PADDO");
131         new_cell->params[ctx->id("DATAMUX_MDDR")] = std::string("PADDO");
132 
133         add_port(ctx, new_cell.get(), "B", PORT_INOUT);
134         add_port(ctx, new_cell.get(), "I", PORT_IN);
135         add_port(ctx, new_cell.get(), "T", PORT_IN);
136         add_port(ctx, new_cell.get(), "O", PORT_OUT);
137 
138         add_port(ctx, new_cell.get(), "IOLDO", PORT_IN);
139         add_port(ctx, new_cell.get(), "IOLTO", PORT_IN);
140 
141     } else if (type == ctx->id("LUT4")) {
142         new_cell->params[ctx->id("INIT")] = Property(0, 16);
143 
144         add_port(ctx, new_cell.get(), "A", PORT_IN);
145         add_port(ctx, new_cell.get(), "B", PORT_IN);
146         add_port(ctx, new_cell.get(), "C", PORT_IN);
147         add_port(ctx, new_cell.get(), "D", PORT_IN);
148         add_port(ctx, new_cell.get(), "Z", PORT_OUT);
149     } else if (type == ctx->id("CCU2C")) {
150         new_cell->params[ctx->id("INIT0")] = Property(0, 16);
151         new_cell->params[ctx->id("INIT1")] = Property(0, 16);
152         new_cell->params[ctx->id("INJECT1_0")] = std::string("YES");
153         new_cell->params[ctx->id("INJECT1_1")] = std::string("YES");
154 
155         add_port(ctx, new_cell.get(), "CIN", PORT_IN);
156 
157         add_port(ctx, new_cell.get(), "A0", PORT_IN);
158         add_port(ctx, new_cell.get(), "B0", PORT_IN);
159         add_port(ctx, new_cell.get(), "C0", PORT_IN);
160         add_port(ctx, new_cell.get(), "D0", PORT_IN);
161 
162         add_port(ctx, new_cell.get(), "A1", PORT_IN);
163         add_port(ctx, new_cell.get(), "B1", PORT_IN);
164         add_port(ctx, new_cell.get(), "C1", PORT_IN);
165         add_port(ctx, new_cell.get(), "D1", PORT_IN);
166 
167         add_port(ctx, new_cell.get(), "S0", PORT_OUT);
168         add_port(ctx, new_cell.get(), "S1", PORT_OUT);
169         add_port(ctx, new_cell.get(), "COUT", PORT_OUT);
170 
171     } else if (type == ctx->id("DCCA")) {
172         add_port(ctx, new_cell.get(), "CLKI", PORT_IN);
173         add_port(ctx, new_cell.get(), "CLKO", PORT_OUT);
174         add_port(ctx, new_cell.get(), "CE", PORT_IN);
175     } else if (type == id_IOLOGIC || type == id_SIOLOGIC) {
176         new_cell->params[ctx->id("MODE")] = std::string("NONE");
177         new_cell->params[ctx->id("GSR")] = std::string("DISABLED");
178         new_cell->params[ctx->id("CLKIMUX")] = std::string("CLK");
179         new_cell->params[ctx->id("CLKOMUX")] = std::string("CLK");
180         new_cell->params[ctx->id("LSRIMUX")] = std::string("0");
181         new_cell->params[ctx->id("LSROMUX")] = std::string("0");
182         new_cell->params[ctx->id("LSRMUX")] = std::string("LSR");
183 
184         new_cell->params[ctx->id("DELAY.OUTDEL")] = std::string("DISABLED");
185         new_cell->params[ctx->id("DELAY.DEL_VALUE")] = Property(0, 7);
186         new_cell->params[ctx->id("DELAY.WAIT_FOR_EDGE")] = std::string("DISABLED");
187 
188         if (type == id_IOLOGIC) {
189             new_cell->params[ctx->id("IDDRXN.MODE")] = std::string("NONE");
190             new_cell->params[ctx->id("ODDRXN.MODE")] = std::string("NONE");
191 
192             new_cell->params[ctx->id("MIDDRX.MODE")] = std::string("NONE");
193             new_cell->params[ctx->id("MODDRX.MODE")] = std::string("NONE");
194             new_cell->params[ctx->id("MTDDRX.MODE")] = std::string("NONE");
195 
196             new_cell->params[ctx->id("IOLTOMUX")] = std::string("NONE");
197             new_cell->params[ctx->id("MTDDRX.DQSW_INVERT")] = std::string("DISABLED");
198             new_cell->params[ctx->id("MTDDRX.REGSET")] = std::string("RESET");
199 
200             new_cell->params[ctx->id("MIDDRX_MODDRX.WRCLKMUX")] = std::string("NONE");
201         }
202         // Just copy ports from the Bel
203         copy_bel_ports();
204     } else if (type == id_TRELLIS_ECLKBUF) {
205         add_port(ctx, new_cell.get(), "ECLKI", PORT_IN);
206         add_port(ctx, new_cell.get(), "ECLKO", PORT_OUT);
207     } else {
208         log_error("unable to create ECP5 cell of type %s", type.c_str(ctx));
209     }
210     return new_cell;
211 }
212 
set_param_safe(bool has_ff,CellInfo * lc,IdString name,const std::string & value)213 static void set_param_safe(bool has_ff, CellInfo *lc, IdString name, const std::string &value)
214 {
215     NPNR_ASSERT(!has_ff || lc->params.at(name) == value);
216     lc->params[name] = value;
217 }
218 
replace_port_safe(bool has_ff,CellInfo * ff,IdString ff_port,CellInfo * lc,IdString lc_port)219 static void replace_port_safe(bool has_ff, CellInfo *ff, IdString ff_port, CellInfo *lc, IdString lc_port)
220 {
221     if (has_ff) {
222         NPNR_ASSERT(lc->ports.at(lc_port).net == ff->ports.at(ff_port).net);
223         NetInfo *ffnet = ff->ports.at(ff_port).net;
224         if (ffnet != nullptr)
225             ffnet->users.erase(
226                     std::remove_if(ffnet->users.begin(), ffnet->users.end(),
227                                    [ff, ff_port](PortRef port) { return port.cell == ff && port.port == ff_port; }),
228                     ffnet->users.end());
229     } else {
230         replace_port(ff, ff_port, lc, lc_port);
231     }
232 }
233 
ff_to_slice(Context * ctx,CellInfo * ff,CellInfo * lc,int index,bool driven_by_lut)234 void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool driven_by_lut)
235 {
236     if (lc->hierpath == IdString())
237         lc->hierpath = ff->hierpath;
238     bool has_ff = lc->ports.at(ctx->id("Q0")).net != nullptr || lc->ports.at(ctx->id("Q1")).net != nullptr;
239     std::string reg = "REG" + std::to_string(index);
240     set_param_safe(has_ff, lc, ctx->id("SRMODE"), str_or_default(ff->params, ctx->id("SRMODE"), "LSR_OVER_CE"));
241     set_param_safe(has_ff, lc, ctx->id("GSR"), str_or_default(ff->params, ctx->id("GSR"), "DISABLED"));
242     set_param_safe(has_ff, lc, ctx->id("CEMUX"), str_or_default(ff->params, ctx->id("CEMUX"), "1"));
243     set_param_safe(has_ff, lc, ctx->id("LSRMUX"), str_or_default(ff->params, ctx->id("LSRMUX"), "LSR"));
244     set_param_safe(has_ff, lc, ctx->id("CLKMUX"), str_or_default(ff->params, ctx->id("CLKMUX"), "CLK"));
245 
246     lc->params[ctx->id(reg + "_SD")] = std::string(driven_by_lut ? "1" : "0");
247     lc->params[ctx->id(reg + "_REGSET")] = str_or_default(ff->params, ctx->id("REGSET"), "RESET");
248     lc->params[ctx->id(reg + "_LSRMODE")] = str_or_default(ff->params, ctx->id("LSRMODE"), "LSR");
249     replace_port_safe(has_ff, ff, ctx->id("CLK"), lc, ctx->id("CLK"));
250     if (ff->ports.find(ctx->id("LSR")) != ff->ports.end())
251         replace_port_safe(has_ff, ff, ctx->id("LSR"), lc, ctx->id("LSR"));
252     if (ff->ports.find(ctx->id("CE")) != ff->ports.end())
253         replace_port_safe(has_ff, ff, ctx->id("CE"), lc, ctx->id("CE"));
254 
255     replace_port(ff, ctx->id("Q"), lc, ctx->id("Q" + std::to_string(index)));
256     if (get_net_or_empty(ff, ctx->id("M")) != nullptr) {
257         // PRLD FFs that use both M and DI
258         NPNR_ASSERT(!driven_by_lut);
259         // As M is used; must route DI through a new LUT
260         lc->params[ctx->id(reg + "_SD")] = std::string("1");
261         lc->params[ctx->id("LUT" + std::to_string(index) + "_INITVAL")] = Property(0xFF00, 16);
262         replace_port(ff, ctx->id("DI"), lc, ctx->id("D" + std::to_string(index)));
263         replace_port(ff, ctx->id("M"), lc, ctx->id("M" + std::to_string(index)));
264         connect_ports(ctx, lc, ctx->id("F" + std::to_string(index)), lc, ctx->id("DI" + std::to_string(index)));
265     } else {
266         if (driven_by_lut) {
267             replace_port(ff, ctx->id("DI"), lc, ctx->id("DI" + std::to_string(index)));
268         } else {
269             replace_port(ff, ctx->id("DI"), lc, ctx->id("M" + std::to_string(index)));
270         }
271     }
272 }
273 
lut_to_slice(Context * ctx,CellInfo * lut,CellInfo * lc,int index)274 void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index)
275 {
276     if (lc->hierpath == IdString())
277         lc->hierpath = lut->hierpath;
278     lc->params[ctx->id("LUT" + std::to_string(index) + "_INITVAL")] =
279             get_or_default(lut->params, ctx->id("INIT"), Property(0, 16));
280     replace_port(lut, ctx->id("A"), lc, ctx->id("A" + std::to_string(index)));
281     replace_port(lut, ctx->id("B"), lc, ctx->id("B" + std::to_string(index)));
282     replace_port(lut, ctx->id("C"), lc, ctx->id("C" + std::to_string(index)));
283     replace_port(lut, ctx->id("D"), lc, ctx->id("D" + std::to_string(index)));
284     replace_port(lut, ctx->id("Z"), lc, ctx->id("F" + std::to_string(index)));
285 }
286 
ccu2c_to_slice(Context * ctx,CellInfo * ccu,CellInfo * lc)287 void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc)
288 {
289     if (lc->hierpath == IdString())
290         lc->hierpath = ccu->hierpath;
291     lc->params[ctx->id("MODE")] = std::string("CCU2");
292     lc->params[ctx->id("LUT0_INITVAL")] = get_or_default(ccu->params, ctx->id("INIT0"), Property(0, 16));
293     lc->params[ctx->id("LUT1_INITVAL")] = get_or_default(ccu->params, ctx->id("INIT1"), Property(0, 16));
294 
295     lc->params[ctx->id("CCU2_INJECT1_0")] = str_or_default(ccu->params, ctx->id("INJECT1_0"), "YES");
296     lc->params[ctx->id("CCU2_INJECT1_1")] = str_or_default(ccu->params, ctx->id("INJECT1_1"), "YES");
297 
298     replace_port(ccu, ctx->id("CIN"), lc, ctx->id("FCI"));
299 
300     replace_port(ccu, ctx->id("A0"), lc, ctx->id("A0"));
301     replace_port(ccu, ctx->id("B0"), lc, ctx->id("B0"));
302     replace_port(ccu, ctx->id("C0"), lc, ctx->id("C0"));
303     replace_port(ccu, ctx->id("D0"), lc, ctx->id("D0"));
304 
305     replace_port(ccu, ctx->id("A1"), lc, ctx->id("A1"));
306     replace_port(ccu, ctx->id("B1"), lc, ctx->id("B1"));
307     replace_port(ccu, ctx->id("C1"), lc, ctx->id("C1"));
308     replace_port(ccu, ctx->id("D1"), lc, ctx->id("D1"));
309 
310     replace_port(ccu, ctx->id("S0"), lc, ctx->id("F0"));
311     replace_port(ccu, ctx->id("S1"), lc, ctx->id("F1"));
312 
313     replace_port(ccu, ctx->id("COUT"), lc, ctx->id("FCO"));
314 }
315 
dram_to_ramw(Context * ctx,CellInfo * ram,CellInfo * lc)316 void dram_to_ramw(Context *ctx, CellInfo *ram, CellInfo *lc)
317 {
318     if (lc->hierpath == IdString())
319         lc->hierpath = ram->hierpath;
320     lc->params[ctx->id("MODE")] = std::string("RAMW");
321     replace_port(ram, ctx->id("WAD[0]"), lc, ctx->id("D0"));
322     replace_port(ram, ctx->id("WAD[1]"), lc, ctx->id("B0"));
323     replace_port(ram, ctx->id("WAD[2]"), lc, ctx->id("C0"));
324     replace_port(ram, ctx->id("WAD[3]"), lc, ctx->id("A0"));
325 
326     replace_port(ram, ctx->id("DI[0]"), lc, ctx->id("C1"));
327     replace_port(ram, ctx->id("DI[1]"), lc, ctx->id("A1"));
328     replace_port(ram, ctx->id("DI[2]"), lc, ctx->id("D1"));
329     replace_port(ram, ctx->id("DI[3]"), lc, ctx->id("B1"));
330 }
331 
get_dram_init(const Context * ctx,const CellInfo * ram,int bit)332 static unsigned get_dram_init(const Context *ctx, const CellInfo *ram, int bit)
333 {
334     auto init_prop = get_or_default(ram->params, ctx->id("INITVAL"), Property(0, 64));
335     NPNR_ASSERT(!init_prop.is_string);
336     const std::string &idata = init_prop.str;
337     NPNR_ASSERT(idata.length() == 64);
338     unsigned value = 0;
339     for (int i = 0; i < 16; i++) {
340         char c = idata.at(4 * i + bit);
341         if (c == '1')
342             value |= (1 << i);
343         else
344             NPNR_ASSERT(c == '0' || c == 'x');
345     }
346     return value;
347 }
348 
dram_to_ram_slice(Context * ctx,CellInfo * ram,CellInfo * lc,CellInfo * ramw,int index)349 void dram_to_ram_slice(Context *ctx, CellInfo *ram, CellInfo *lc, CellInfo *ramw, int index)
350 {
351     if (lc->hierpath == IdString())
352         lc->hierpath = ram->hierpath;
353     lc->params[ctx->id("MODE")] = std::string("DPRAM");
354     lc->params[ctx->id("WREMUX")] = str_or_default(ram->params, ctx->id("WREMUX"), "WRE");
355     lc->params[ctx->id("WCKMUX")] = str_or_default(ram->params, ctx->id("WCKMUX"), "WCK");
356 
357     unsigned permuted_init0 = 0, permuted_init1 = 0;
358     unsigned init0 = get_dram_init(ctx, ram, index * 2), init1 = get_dram_init(ctx, ram, index * 2 + 1);
359 
360     for (int i = 0; i < 16; i++) {
361         int permuted_addr = 0;
362         if (i & 1)
363             permuted_addr |= 8;
364         if (i & 2)
365             permuted_addr |= 2;
366         if (i & 4)
367             permuted_addr |= 4;
368         if (i & 8)
369             permuted_addr |= 1;
370         if (init0 & (1 << permuted_addr))
371             permuted_init0 |= (1 << i);
372         if (init1 & (1 << permuted_addr))
373             permuted_init1 |= (1 << i);
374     }
375 
376     lc->params[ctx->id("LUT0_INITVAL")] = Property(permuted_init0, 16);
377     lc->params[ctx->id("LUT1_INITVAL")] = Property(permuted_init1, 16);
378 
379     if (ram->ports.count(ctx->id("RAD[0]"))) {
380         connect_port(ctx, ram->ports.at(ctx->id("RAD[0]")).net, lc, ctx->id("D0"));
381         connect_port(ctx, ram->ports.at(ctx->id("RAD[0]")).net, lc, ctx->id("D1"));
382     }
383     if (ram->ports.count(ctx->id("RAD[1]"))) {
384         connect_port(ctx, ram->ports.at(ctx->id("RAD[1]")).net, lc, ctx->id("B0"));
385         connect_port(ctx, ram->ports.at(ctx->id("RAD[1]")).net, lc, ctx->id("B1"));
386     }
387     if (ram->ports.count(ctx->id("RAD[2]"))) {
388         connect_port(ctx, ram->ports.at(ctx->id("RAD[2]")).net, lc, ctx->id("C0"));
389         connect_port(ctx, ram->ports.at(ctx->id("RAD[2]")).net, lc, ctx->id("C1"));
390     }
391     if (ram->ports.count(ctx->id("RAD[3]"))) {
392         connect_port(ctx, ram->ports.at(ctx->id("RAD[3]")).net, lc, ctx->id("A0"));
393         connect_port(ctx, ram->ports.at(ctx->id("RAD[3]")).net, lc, ctx->id("A1"));
394     }
395 
396     if (ram->ports.count(ctx->id("WRE")))
397         connect_port(ctx, ram->ports.at(ctx->id("WRE")).net, lc, ctx->id("WRE"));
398     if (ram->ports.count(ctx->id("WCK")))
399         connect_port(ctx, ram->ports.at(ctx->id("WCK")).net, lc, ctx->id("WCK"));
400 
401     connect_ports(ctx, ramw, id_WADO0, lc, id_WAD0);
402     connect_ports(ctx, ramw, id_WADO1, lc, id_WAD1);
403     connect_ports(ctx, ramw, id_WADO2, lc, id_WAD2);
404     connect_ports(ctx, ramw, id_WADO3, lc, id_WAD3);
405 
406     if (index == 0) {
407         connect_ports(ctx, ramw, id_WDO0, lc, id_WD0);
408         connect_ports(ctx, ramw, id_WDO1, lc, id_WD1);
409 
410         replace_port(ram, ctx->id("DO[0]"), lc, id_F0);
411         replace_port(ram, ctx->id("DO[1]"), lc, id_F1);
412 
413     } else if (index == 1) {
414         connect_ports(ctx, ramw, id_WDO2, lc, id_WD0);
415         connect_ports(ctx, ramw, id_WDO3, lc, id_WD1);
416 
417         replace_port(ram, ctx->id("DO[2]"), lc, id_F0);
418         replace_port(ram, ctx->id("DO[3]"), lc, id_F1);
419     } else {
420         NPNR_ASSERT_FALSE("bad DPRAM index");
421     }
422 }
423 
nxio_to_tr(Context * ctx,CellInfo * nxio,CellInfo * trio,std::vector<std::unique_ptr<CellInfo>> & created_cells,std::unordered_set<IdString> & todelete_cells)424 void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vector<std::unique_ptr<CellInfo>> &created_cells,
425                 std::unordered_set<IdString> &todelete_cells)
426 {
427     if (nxio->type == ctx->id("$nextpnr_ibuf")) {
428         trio->params[ctx->id("DIR")] = std::string("INPUT");
429         replace_port(nxio, ctx->id("O"), trio, ctx->id("O"));
430     } else if (nxio->type == ctx->id("$nextpnr_obuf")) {
431         trio->params[ctx->id("DIR")] = std::string("OUTPUT");
432         replace_port(nxio, ctx->id("I"), trio, ctx->id("I"));
433     } else if (nxio->type == ctx->id("$nextpnr_iobuf")) {
434         // N.B. tristate will be dealt with below
435         NetInfo *i = get_net_or_empty(nxio, ctx->id("I"));
436         if (i == nullptr || i->driver.cell == nullptr)
437             trio->params[ctx->id("DIR")] = std::string("INPUT");
438         else {
439             log_info("%s: %s.%s\n", ctx->nameOf(i), ctx->nameOf(i->driver.cell), ctx->nameOf(i->driver.port));
440             trio->params[ctx->id("DIR")] = std::string("BIDIR");
441         }
442         replace_port(nxio, ctx->id("I"), trio, ctx->id("I"));
443         replace_port(nxio, ctx->id("O"), trio, ctx->id("O"));
444     } else {
445         NPNR_ASSERT(false);
446     }
447     NetInfo *donet = trio->ports.at(ctx->id("I")).net, *dinet = trio->ports.at(ctx->id("O")).net;
448 
449     // Rename I/O nets to avoid conflicts
450     if (donet != nullptr && donet->name == nxio->name)
451         rename_net(ctx, donet, ctx->id(donet->name.str(ctx) + "$TRELLIS_IO_OUT"));
452     if (dinet != nullptr && dinet->name == nxio->name)
453         rename_net(ctx, dinet, ctx->id(dinet->name.str(ctx) + "$TRELLIS_IO_IN"));
454 
455     if (ctx->nets.count(nxio->name)) {
456         int i = 0;
457         IdString new_name;
458         do {
459             new_name = ctx->id(nxio->name.str(ctx) + "$rename$" + std::to_string(i++));
460         } while (ctx->nets.count(new_name));
461         rename_net(ctx, ctx->nets.at(nxio->name).get(), new_name);
462     }
463 
464     // Create a new top port net for accurate IO timing analysis and simulation netlists
465     if (ctx->ports.count(nxio->name)) {
466         IdString tn_netname = nxio->name;
467         NPNR_ASSERT(!ctx->nets.count(tn_netname));
468         std::unique_ptr<NetInfo> toplevel_net{new NetInfo};
469         toplevel_net->name = tn_netname;
470         connect_port(ctx, toplevel_net.get(), trio, ctx->id("B"));
471         ctx->ports[nxio->name].net = toplevel_net.get();
472         ctx->nets[tn_netname] = std::move(toplevel_net);
473     }
474 
475     CellInfo *tbuf = net_driven_by(
476             ctx, donet, [](const Context *ctx, const CellInfo *cell) { return cell->type == ctx->id("$_TBUF_"); },
477             ctx->id("Y"));
478     if (tbuf) {
479         replace_port(tbuf, ctx->id("A"), trio, ctx->id("I"));
480         // Need to invert E to form T
481         std::unique_ptr<CellInfo> inv_lut = create_ecp5_cell(ctx, ctx->id("LUT4"), trio->name.str(ctx) + "$invert_T");
482         replace_port(tbuf, ctx->id("E"), inv_lut.get(), ctx->id("A"));
483         inv_lut->params[ctx->id("INIT")] = Property(21845, 16);
484         connect_ports(ctx, inv_lut.get(), ctx->id("Z"), trio, ctx->id("T"));
485         created_cells.push_back(std::move(inv_lut));
486 
487         if (donet->users.size() > 1) {
488             for (auto user : donet->users)
489                 log_info("     remaining tristate user: %s.%s\n", user.cell->name.c_str(ctx), user.port.c_str(ctx));
490             log_error("unsupported tristate IO pattern for IO buffer '%s', "
491                       "instantiate SB_IO manually to ensure correct behaviour\n",
492                       nxio->name.c_str(ctx));
493         }
494         ctx->nets.erase(donet->name);
495         todelete_cells.insert(tbuf->name);
496     }
497 }
498 
499 NEXTPNR_NAMESPACE_END
500