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