Compare commits

...

222 Commits
master ... xc7

Author SHA1 Message Date
Eddie Hung
7125961e51
Merge pull request #207 from eddiehung/xc7
[xc7] clangformat and more cleanup
2019-01-17 16:03:45 -08:00
Eddie Hung
04c08b7bc6 clangformat and more cleanup 2019-01-10 15:04:51 -08:00
Eddie Hung
3f0ad5915a
Merge pull request #202 from eddiehung/xc7
[xc7] WIP: cirrus CI
2019-01-10 12:50:12 -08:00
Eddie Hung
74e84cf65e [xc7] Try again 2019-01-10 12:23:12 -08:00
Eddie Hung
940843f7f3 [xc7] And again 2019-01-06 21:16:31 -08:00
Eddie Hung
2c4fd3ce81 [xc7] Retry TORC_ROOT 2019-01-06 21:04:00 -08:00
Eddie Hung
ef35b41e0f [xc7] Remove hardcoded Torc path 2019-01-06 12:29:37 -08:00
Eddie Hung
1a9b2d9803 [xc7] Docker's torc not to make install 2019-01-01 13:50:35 -08:00
Eddie Hung
efd35a2dd8 [xc7] Install torc in Docker image 2019-01-01 12:38:17 -08:00
Eddie Hung
e393187d68 [xc7] Remove superseded include_directories() 2019-01-01 12:16:36 -08:00
Eddie Hung
1e2a7c9fca [xc7] target_include_directories for gui_xc7 target too 2019-01-01 12:07:07 -08:00
Eddie Hung
3d79eee05a [xc7] Use target_include_directories not include_directories 2018-12-30 14:04:35 -08:00
Eddie Hung
ae1bbbdbdd [xc7] Cope with BUILD_TESTS=off 2018-12-30 14:02:19 -08:00
Eddie Hung
36edbdcbbd [xc7] Update torc submodule 2018-12-30 13:47:16 -08:00
Eddie Hung
078285fe7f [xc7] Fix include_directories path 2018-12-30 13:16:10 -08:00
Eddie Hung
55ac41046d [xc7] Update torc submodule 2018-12-30 13:05:29 -08:00
Eddie Hung
0bc1587407 [xc7] Fix test deps 2018-12-29 12:28:26 -08:00
Eddie Hung
af0bc8ab4c Fix submodule path 2018-12-29 12:10:04 -08:00
Eddie Hung
c28b1ee6bd cmake to do a git submodule update 2018-12-29 00:21:32 -08:00
Eddie Hung
57f8c216b5 Fix compiler warning 2018-12-29 00:21:18 -08:00
Eddie Hung
c51d89f2af
Merge pull request #201 from eddiehung/xc7
Remove some more ice40 stuff
2018-12-29 00:12:53 -08:00
Eddie Hung
39a5019f88 And some more 2018-12-28 15:22:03 -08:00
Eddie Hung
84485152cc Remove some more ice40 stuff 2018-12-28 15:20:51 -08:00
Clifford Wolf
bbef0182f8
Merge pull request #199 from eddiehung/xc7
Unofficial xc7 support
2018-12-28 13:44:47 +01:00
Eddie Hung
0514fb9042 Merge branch 'xc7' into xc7_gui 2018-12-28 00:03:01 -08:00
Eddie Hung
ba646ac8c4 Update README.md 2018-12-27 23:49:16 -08:00
Eddie Hung
4c9f62e1e0 Add attosoc_sim.sh and fimrware_fast.hex 2018-12-27 23:47:16 -08:00
Eddie Hung
592022c180 Add *.nlf and *.sdf to .gitignore 2018-12-27 23:29:35 -08:00
Eddie Hung
cb6ea8ba84 Remove PLL from attosoc, allow errors in pnr 2018-12-27 23:28:33 -08:00
Eddie Hung
04757865fe Remove PLL from picorv32, allow errors in pnr 2018-12-27 23:28:12 -08:00
Eddie Hung
84d038360d Add implicit wire 2018-12-27 23:27:50 -08:00
Eddie Hung
e15aa8d3f4 Add blinky_sim.sh 2018-12-27 23:26:47 -08:00
Eddie Hung
ada9076114 Remove stuff copied over from ice40 2018-12-27 23:26:31 -08:00
Eddie Hung
5cf0c559fa Remove files from tree 2018-12-27 23:12:57 -08:00
Eddie Hung
e00d6000d7 Fix blinky.sh and blinky_sim.sh 2018-12-27 23:02:33 -08:00
Eddie Hung
88e7267510 Remove ps7.vh 2018-12-27 23:02:22 -08:00
Eddie Hung
afc89a60e8 Write SYNC_ATTR to xdl too 2018-12-27 21:56:15 -08:00
Eddie Hung
5b438116e5 Always set SYNC_ATTR otherwise netgen fails 2018-12-27 21:53:11 -08:00
Eddie Hung
9790cea186 Revert "Revert "Do not add "$iob" suffix""
This reverts commit cbc01f0f71.
2018-12-27 21:43:10 -08:00
Eddie Hung
cbc01f0f71 Revert "Do not add "$iob" suffix"
This reverts commit 10d6db6d94.
2018-12-27 21:39:41 -08:00
Eddie Hung
ab35983000 Use std::regex not boost::regex (even though Torc still depends on latter) 2018-12-27 21:25:22 -08:00
Eddie Hung
64f3f25270 Update xc7/family.cmake to use submodule torc 2018-12-27 21:25:07 -08:00
Eddie Hung
60fc4735bf Add torc as submodule 2018-12-27 21:24:52 -08:00
Eddie Hung
5072d184ab One more thing 2018-12-27 21:10:12 -08:00
Eddie Hung
ede0e93206 Merge branch 'xc7' into xc7_gui 2018-12-27 20:53:15 -08:00
Eddie Hung
a0a6281d0d Remove HACK for BUFG routing 2018-12-26 18:15:01 -08:00
Eddie Hung
a630758ca7 Cleanup 2018-12-26 18:14:23 -08:00
Eddie Hung
463d9a6920 Merge branch 'master' into xc7 2018-12-26 17:11:58 -08:00
Eddie Hung
6a09f2a856 Remove fasm writer 2018-12-26 17:10:52 -08:00
Eddie Hung
f2b6e525d5 Add imux_wire to guide router 2018-12-18 18:23:00 -08:00
Eddie Hung
a8a00ff3fb Disable entering HROW from INT_[LR].CLK[01] 2018-12-18 18:20:23 -08:00
Eddie Hung
b2b02c9c3b Prefer INT/CLB tile as "anchor" tilewire 2018-12-18 16:42:18 -08:00
Eddie Hung
8d069c0115 Merge http://github.com/YosysHQ/nextpnr into xc7 2018-12-18 11:40:10 -08:00
Eddie Hung
e87b96fe4a Add xc7 specific hack for global network routing 2018-12-08 22:49:55 -08:00
Eddie Hung
097062c5cb Remove pip_to_dst_wire lookup 2018-12-08 22:49:39 -08:00
Eddie Hung
d37a57800b Tune predictDelay 2018-12-07 20:22:30 -08:00
Eddie Hung
9c0fd6816e Add X back into predictDelay() for global resources 2018-12-07 14:48:53 -08:00
Eddie Hung
16e001b679 Merge https://github.com/YosysHQ/nextpnr into xc7 2018-12-07 14:37:53 -08:00
Eddie Hung
0146312707 Fixes 2018-12-07 14:31:24 -08:00
Eddie Hung
cadafffbec Missing files 2018-12-07 22:23:15 +00:00
Eddie Hung
550271b338 Cleanup XDL generation 2018-12-07 22:22:30 +00:00
Eddie Hung
236ca15b49 Place blinky BUFG in dedicated location for input pin 2018-12-06 18:02:10 -08:00
Eddie Hung
8c44888466 Fix delay prediction 2018-12-06 17:40:15 -08:00
Eddie Hung
904860b2b4 Add comment 2018-12-06 16:53:48 -08:00
Eddie Hung
66f22150b1 Improve estimateDelay for global clocks 2018-12-06 16:49:35 -08:00
Eddie Hung
c708a4e0d3 Fix leftover divide by 2 in Arch::estimateDelay() 2018-12-06 16:11:15 -08:00
Eddie Hung
557011cfe6 Have a go at generating FASM from Torc XDL 2018-12-06 14:51:24 -08:00
Eddie Hung
5f75a8447f Merge in vx980t support 2018-12-06 20:07:51 +00:00
Eddie Hung
20f0353f76 Add report to attosoc_tb.vhd 2018-12-02 15:41:30 -08:00
Eddie Hung
b1b8183967 Re-enable PLL in attosoc.v 2018-12-02 15:41:03 -08:00
Eddie Hung
8f0e888815 nextpnr now writes to log, netgen to overwrite 2018-12-02 15:40:20 -08:00
Eddie Hung
5aff7bbbc4 Fix INIT of pass-thru LUT to be "2" not "1" 2018-12-02 15:32:29 -08:00
Eddie Hung
5ddfc32c75 Add attosoc.sh and attosoc_tb.vhd 2018-11-30 17:06:55 -08:00
Eddie Hung
4574a57efc Add attosoc 2018-11-30 15:24:32 -08:00
Eddie Hung
fdca3d6d77 firmware.hex with delay loop 2018-11-30 15:23:09 -08:00
Eddie Hung
cac7ce2747 Cleanup 2018-11-29 17:20:51 -08:00
Eddie Hung
f4e7f4e690 Use wholesale attrs from ISE for MMCME2_ADV 2018-11-29 17:20:35 -08:00
Eddie Hung
2fdf937259 Assignment LUT inputs from fastest down 2018-11-29 17:08:05 -08:00
Eddie Hung
0327fa554a Revert "Ahead of LUT input swapping, assign LUT<6 from A6 downwards"
This reverts commit ec96897c1d.
2018-11-29 16:54:33 -08:00
Eddie Hung
c5165f7830 Duplicate arcs.clear() 2018-11-29 16:32:33 -08:00
Eddie Hung
d7dd945f55 Overwrite COMPENSATION attribute on MMCME2_ADV to "INTERNAL" 2018-11-29 16:32:08 -08:00
Eddie Hung
d8b6b231de Move required attributes to pack 2018-11-29 15:38:28 -08:00
Eddie Hung
6985e80c01 Merge branch 'xc7' of gitlab.com:eddiehung/nextpnr into xc7 2018-11-29 13:32:32 -08:00
Miodrag Milanovic
535fc953d4 Use site x location to determine if it is one block or other 2018-11-29 12:44:02 -08:00
Eddie Hung
9f03d9eed3 Add PLL to bring 125MHz clock to 60MHz for picorv32 2018-11-29 12:25:39 -08:00
Miodrag Milanovic
1f387d44fb Use site x location to determine if it is one block or other 2018-11-29 21:12:56 +01:00
Miodrag Milanovic
b7a06a02c4 Display slices 2018-11-29 20:54:46 +01:00
Eddie Hung
4161856d49 Add support for MMCME2_ADV 2018-11-28 22:34:22 -08:00
Miodrag Milanovic
105c148848 Made Pip and Wires trees work 2018-11-28 19:49:28 +01:00
Miodrag Milanovic
bfa2157ae6 compile fix for gui and proper size 2018-11-28 17:59:58 +01:00
Miodrag Milanovic
f2fecc3c69 make gui run 2018-11-28 17:04:26 +01:00
Eddie Hung
13e7798b34 Fix #endif placement 2018-11-27 18:11:19 -08:00
Eddie Hung
212b03999b Gzip the torc_info data 2018-11-27 18:08:03 -08:00
Eddie Hung
440802bf9d Add support for serialization of torc_info 2018-11-27 17:55:31 -08:00
Eddie Hung
662733c171 Remove methods 2018-11-27 14:12:25 -08:00
Eddie Hung
a0b6d3b19b clangformat 2018-11-27 12:28:48 -08:00
Eddie Hung
ae9ccfa5ad Refactor torc_info constructor 2018-11-27 12:28:21 -08:00
Eddie Hung
664c48f5e4 Merge https://github.com/YosysHQ/nextpnr into xc7 2018-11-27 09:45:35 -08:00
Eddie Hung
fc015d28d3 Fix pip indexing, do not allow fabric to connect to CLK (only global network can) 2018-11-20 17:41:46 -08:00
Eddie Hung
c3dc8696eb Fix getDelayFromNS() 2018-11-20 15:00:09 -08:00
Eddie Hung
ab9cb99f52 Arch::getPipDelay() returns delay of dst wire; Arch::getWireDelay() to return nothing 2018-11-20 14:52:01 -08:00
Eddie Hung
fa3e390e5f Add TODO 2018-11-20 14:43:21 -08:00
Eddie Hung
18cee5d279 More changes for upstream 2018-11-20 14:26:29 -08:00
Eddie Hung
75b48dfe1e Merge branch 'master' into xc7 2018-11-20 14:26:26 -08:00
Eddie Hung
92ea676fc4 Merge http://github.com/YosysHQ/nextpnr into xc7 2018-11-14 18:37:08 -08:00
Eddie Hung
934c5d5f8b Merge http://github.com/YosysHQ/nextpnr into xc7 2018-11-13 17:24:44 -08:00
Eddie Hung
e19ad23ef4 Merge branch 'xc7-router_improve' into xc7 2018-11-13 17:21:46 -08:00
Eddie Hung
9a498c26d1 Update picorv32.sh 2018-11-13 11:07:43 -08:00
Eddie Hung
bccf95296c Add picorv32.pcf 2018-11-13 11:07:08 -08:00
Eddie Hung
ca94aa1915 Reduce blinky.sh freq to 150MHz 2018-11-13 10:11:27 -08:00
Eddie Hung
fa3d366ddb Add missing operator needed by router_improve 2018-11-13 10:10:57 -08:00
Eddie Hung
d5ca744ca0 Fix typo 2018-11-13 09:10:14 -08:00
Eddie Hung
97f1901fcf Merge branch 'router_improve' of http://github.com/YosysHQ/nextpnr into xc7-router_improve 2018-11-13 09:05:35 -08:00
Eddie Hung
72a16004e1 Divide columns by 2 to get better X estimate for tiles 2018-11-13 09:01:24 -08:00
Eddie Hung
ec96897c1d Ahead of LUT input swapping, assign LUT<6 from A6 downwards 2018-11-11 14:21:06 -08:00
Eddie Hung
75654a69f0 Fix LUT input delays, speedup construct_wire_to_delay? 2018-11-11 14:15:11 -08:00
Eddie Hung
53f025c03f Improved delay estimator 2018-11-11 12:56:52 -08:00
Eddie Hung
dd85e11abb [timing] More verbosity for crit path report 2018-11-11 11:55:27 -08:00
Eddie Hung
83117bef66 Add missing APIs needed for router_improve 2018-11-11 10:19:17 -08:00
Eddie Hung
19561fde52 Merge branch 'router_improve' of https://github.com/YosysHQ/nextpnr into xc7-router_improve 2018-11-11 10:13:13 -08:00
Eddie Hung
99f5836b0e Add some cell delays 2018-11-10 19:55:22 -08:00
Eddie Hung
a0c6c64be7 blinky.v to not instantiate PS7, blink more slowly/predictably 2018-11-10 18:50:43 -08:00
Eddie Hung
533c730418 Update without $iob suffix 2018-11-10 18:49:58 -08:00
Eddie Hung
10d6db6d94 Do not add "$iob" suffix 2018-11-10 18:48:46 -08:00
Eddie Hung
f6bdd92640 Implement Arch::getPipName() and remove debug/verbose before routing 2018-11-10 16:11:38 -08:00
Eddie Hung
540765a14e Merge branch 'router_improve' of https://github.com/YosysHQ/nextpnr into xc7-router_improve 2018-11-10 12:54:09 -08:00
Eddie Hung
3d8b88deef blinky to instantiate PS7 so that reconfiguring from Linux doesn't kill PS 2018-11-10 12:53:41 -08:00
Eddie Hung
2b2254f514 Add support for PS7 blocks 2018-11-10 12:53:41 -08:00
Eddie Hung
3b86c3a381 blinky to instantiate PS7 so that reconfiguring from Linux doesn't kill PS 2018-11-10 12:52:52 -08:00
Eddie Hung
3576509684 Add support for PS7 blocks 2018-11-10 12:51:56 -08:00
Eddie Hung
4619b2d82f Disable debug/verbose flag before routing 2018-11-09 17:09:25 -08:00
Eddie Hung
c67f1091ca Merge https://github.com/YosysHQ/nextpnr into xc7 2018-11-09 17:07:58 -08:00
Eddie Hung
31ae470414 Adapt blinky for xc7 2018-11-09 17:07:23 -08:00
Eddie Hung
c6f66b4468 Adapt blinky for xc7 2018-11-09 17:05:55 -08:00
Eddie Hung
11b1309e62 Merge branch 'router_improve' of https://github.com/YosysHQ/nextpnr into xc7-router_improve 2018-11-09 17:05:48 -08:00
Eddie Hung
2711ff9cdc Merge branch 'xc7' into xc7-router_improve 2018-11-09 16:59:38 -08:00
Eddie Hung
a5be1bbe6e [xc7] Fix LUT mask logic; also add attributes for IGNORE0 and {CE,S,IGNORE}1 2018-11-05 08:21:32 -08:00
Eddie Hung
e53f8364ec [xc7] Also ignore PIN token lines for PCF 2018-11-05 08:21:14 -08:00
Eddie Hung
1ee041e63f Add more ISE stuff to .gitignore 2018-11-04 22:15:06 -08:00
Eddie Hung
f67cf32d0d [xc7] PCF reader to ignore lines with NET token 2018-11-04 22:14:10 -08:00
Eddie Hung
61939dca11 [xc7] blinky.sh to set freq 125 MHz and run trce 2018-11-04 22:13:51 -08:00
Eddie Hung
f1b454b96c [xc7] blinky.v to use led0-3, and just to be safe, set BUFGCTRL control
inputs too
2018-11-04 22:13:24 -08:00
Eddie Hung
42a1b1a750 [xc7] Add NET PERIOD constraint to blinky.pcf (for trce) 2018-11-04 22:12:43 -08:00
Eddie Hung
e137a9c507 [xc7] Add xdl and bitgen to blinky.sh 2018-11-03 16:12:11 -07:00
Eddie Hung
1e41183c5e Update .gitignore with *_fpga_editor.log 2018-11-03 16:11:56 -07:00
Eddie Hung
5c56fab0ab [xc7] Add torc_info->site_index_to_bel lookup; also fix Arch::getBelByName() 2018-11-03 15:56:06 -07:00
Eddie Hung
aa7f7d6a97 clangformat 2018-11-03 15:18:26 -07:00
Eddie Hung
d80b63cc55 [xc7] Re-enable PCF reading 2018-11-03 15:17:53 -07:00
Eddie Hung
4239e3668a [xc7] Make clg400 the default package (Zybo) 2018-11-03 15:02:09 -07:00
Eddie Hung
ac919421f0 Add more ISE output to .gitignore 2018-11-03 14:25:20 -07:00
Eddie Hung
c6bf8aff43 [xc7] Fix timing analysis for constant drivers 2018-11-03 14:24:29 -07:00
Eddie Hung
8f1c91151b Revert "[xc7] Fix getPortTimingClass for IOBs"
This reverts commit 5d9019994e.
2018-11-03 13:30:58 -07:00
Eddie Hung
0e1c23a07b [xc7] blinky.v to only have 4 LEDs 2018-11-03 13:27:39 -07:00
Eddie Hung
e758d8befe Add *.xdl and *.bit to .gitignore 2018-11-03 12:56:46 -07:00
Eddie Hung
5d9019994e [xc7] Fix getPortTimingClass for IOBs 2018-11-03 12:55:36 -07:00
Eddie Hung
324ff41f13 Fix Torc path 2018-11-03 12:10:21 -07:00
Eddie Hung
af4f9680e2 Merge branch 'master' into xc7 2018-11-03 11:42:19 -07:00
Eddie Hung
735c7c7c9c torc now expected to be /opt/torc 2018-11-03 11:41:36 -07:00
Eddie Hung
382b52fc88 Merge branch 'xc7' into xc7-router_improve 2018-09-05 22:39:28 -07:00
Eddie Hung
834f5f58c2 Fix wire delays, disable BUFG I->O routethrough 2018-09-05 22:24:46 -07:00
Eddie Hung
6e2d215e6a Merge branch 'xc7' into xc7-router_improve 2018-09-04 19:23:27 -07:00
Eddie Hung
5214d1dbb5 Segment anchors may not be beginning of wires 2018-09-04 11:05:03 -07:00
Eddie Hung
e0e5604958 Merge branch 'xc7' into xc7-router_improve 2018-09-04 10:42:03 -07:00
Eddie Hung
d0916943c5 Extend delays to cover BYP and FAN 2018-09-04 10:41:32 -07:00
Eddie Hung
0721a15c33 Convert path delay to NS 2018-09-04 10:35:30 -07:00
Eddie Hung
c7f0bdfc1b Move DelayInfo into loop 2018-09-04 10:35:12 -07:00
Eddie Hung
beb533b005 Merge branch 'xc7' into xc7-router_improve 2018-09-04 10:17:53 -07:00
Eddie Hung
db6e81d6c3 Populate Arch::getWireDelay() 2018-09-04 10:11:10 -07:00
Eddie Hung
7da5e2b525 Reduce predictDelay/estimateDelay to 100ps per tile 2018-09-04 10:10:27 -07:00
Eddie Hung
6c2247b4f6 Merge branch 'router_improve' of github.com:YosysHQ/nextpnr into xc7-router_improve
Conflicts:
	common/router1.cc
2018-09-04 09:16:09 -07:00
Eddie Hung
24d702d0be Save a lookup for router1 2018-09-04 09:05:53 -07:00
Eddie Hung
d78f5a1d5b Build a pip_to_dst_wire lookup to speedup routing 2018-09-03 22:59:34 -07:00
Eddie Hung
30fe1f229a Set CE0INV and S0INV for BUFGCTRL; PRESELECT_I0 to be TRUE if not set 2018-09-03 22:25:05 -07:00
Eddie Hung
2eeb59d9f1 Re-enable routing 2018-09-03 22:24:59 -07:00
Eddie Hung
3a5665c1cb Speedup placement slightly using bel_to_loc 2018-09-03 21:00:11 -07:00
Eddie Hung
4f61d2dae7 Add yosys script 2018-09-03 19:23:36 -07:00
Eddie Hung
86fa032b63 picorv32_top to instantiate BUFGCTRL, and picorv32.sh to use picorv32.ys script 2018-09-03 19:23:00 -07:00
Eddie Hung
7f1c1ecaf0 blinky.v to instantiate BUFGCTRL correctly 2018-09-03 19:22:39 -07:00
Eddie Hung
bf5a4717f5 Add pips to XDL output 2018-09-03 13:40:52 -07:00
Eddie Hung
6d17810dde Merge fixes 2018-09-03 13:20:19 -07:00
Eddie Hung
001806f317 Merge github.com:YosysHQ/nextpnr into xc7 2018-09-03 13:18:06 -07:00
Eddie Hung
c128df127b Do not consider route-through for CLB tiles 2018-09-03 13:17:16 -07:00
Eddie Hung
d2597bcd8d Fix segments 2018-09-03 00:10:16 -07:00
Eddie Hung
3f865f9049 Fix Arch::estimateDelay() 2018-09-02 23:38:53 -07:00
Eddie Hung
7e693ff27d Precompute pips too 2018-09-02 19:06:20 -07:00
Eddie Hung
ca7eef26ac Wires now encapsulate segments 2018-09-02 16:57:11 -07:00
Eddie Hung
df2f295545 Apparently netgen needs SYNC_ATTR to be set 2018-09-02 13:09:28 -07:00
Eddie Hung
82fbc551f8 Fix DRC errors 2018-08-21 22:58:20 -07:00
Eddie Hung
3a177c72c6 Preserve packed LUT name as LUT_NAME parameter 2018-08-21 22:24:14 -07:00
Eddie Hung
b658a39d73 IOB -> IOB33; preserve FF init as DFF_INIT, use BUFGCTRL with PRESELECT_I0 in blinky 2018-08-21 22:18:00 -07:00
Eddie Hung
5b6255abf1 Fix LUT masks, add speedgrade, fix IOB type 2018-08-20 21:50:06 -07:00
Eddie Hung
0a16e24c82 create_ice_cell -> create_xc7_cell 2018-08-20 19:29:04 -07:00
Eddie Hung
3e1085ecb5 Combine IOB33S and IOB33M 2018-08-20 19:25:54 -07:00
Eddie Hung
f7be783a32 Escape flop names as well 2018-08-20 19:21:53 -07:00
Eddie Hung
718f5b81f0 Escape colons in config names 2018-08-20 19:19:45 -07:00
Eddie Hung
699bd3ef5a Fix for leading '+', and use An for LUT masks 2018-08-19 22:31:50 -07:00
Eddie Hung
a87f26b254 Update comment 2018-08-19 19:41:24 -07:00
Eddie Hung
dcc08b27cc Output unrouted nets into XDL 2018-08-19 19:41:11 -07:00
Eddie Hung
07fb4702ce Populate LUT masks 2018-08-19 19:16:24 -07:00
Eddie Hung
a7ccc01c45 Use getBelType() 2018-08-19 17:38:55 -07:00
Eddie Hung
17918b5992 Fix for multiple id_SLICE_LUT6 per actual SLICE 2018-08-17 23:05:12 -07:00
Eddie Hung
d05ac75fda Add basics for XDL exporter 2018-08-17 21:52:34 -07:00
Eddie Hung
b8b9813056 id_QUARTER_SLICE -> id_SLICE_LUT6, fix getBelLocation() 2018-08-14 08:42:27 -07:00
Eddie Hung
72c785db0e Convert to use torc_info 2018-08-12 22:09:16 -07:00
Eddie Hung
7b15569c69 Use general pin names for QUARTER_SLICE 2018-08-12 20:29:04 -07:00
Eddie Hung
56b7299cca {SLICEL,SLICEM} -> QUARTER_SLICE 2018-08-12 20:21:03 -07:00
Eddie Hung
f6f20dce0c Rename ddb to torc 2018-08-12 19:20:13 -07:00
Eddie Hung
8dedd7a83c Add stub for XDL output 2018-08-12 19:07:33 -07:00
Eddie Hung
13e30a4eb1 Add nextpnr-xc7 to gitignore 2018-08-12 18:56:36 -07:00
Eddie Hung
0fe579f046 Add torc symlink 2018-08-12 18:56:25 -07:00
Eddie Hung
57c273898c Finishes placement now 2018-08-11 22:24:13 -07:00
Eddie Hung
32f5346378 Hacked blinky.ys yosys script 2018-08-11 22:23:52 -07:00
Eddie Hung
67a0fa11e6 Enable timing 2018-08-11 21:36:23 -07:00
Eddie Hung
2bc7ffc2ea WIP 2018-08-11 21:13:49 -07:00
Eddie Hung
8cddc49abc Starts placement onto all Xilinx sites 2018-08-11 18:52:48 -07:00
Eddie Hung
45009ac09d Remove timing, remove wires 2018-08-11 17:08:50 -07:00
Eddie Hung
74ff630922 Tweak blinky.sh for xc7 2018-08-11 16:01:15 -07:00
Eddie Hung
fbeb039f39 Enable -pcf option but ignore 2018-08-11 16:01:08 -07:00
Eddie Hung
8357417787 Load Torc DDB 2018-08-11 15:53:55 -07:00
Eddie Hung
6425032ec4 Rip out ice40 stuff, put xc7z020 in 2018-08-11 15:00:31 -07:00
Eddie Hung
a0d72a6f8e Add xc7 to supported arches 2018-08-11 14:37:56 -07:00
Eddie Hung
d53658a079 Copy ice40 into xc7 2018-08-11 14:35:49 -07:00
59 changed files with 5526 additions and 10 deletions

View File

@ -5,8 +5,9 @@ task:
memory: 16
dockerfile: .cirrus/Dockerfile.ubuntu16.04
build_script: mkdir build && cd build && cmake .. -DARCH=all -DTRELLIS_ROOT=/usr/local/src/prjtrellis -DBUILD_TESTS=on && make -j $(nproc)
build_script: mkdir build && cd build && cmake .. -DARCH=all -DTRELLIS_ROOT=/usr/local/src/prjtrellis -DTORC_ROOT=/usr/local/src/torc -DBUILD_TESTS=on && make -j $(nproc)
test_generic_script: cd build && ./nextpnr-generic-test
test_ice40_script: cd build && ./nextpnr-ice40-test
smoketest_ice40_script: export NEXTPNR=$(pwd)/build/nextpnr-ice40 && cd ice40/smoketest/attosoc && ./smoketest.sh
test_ecp5_script: cd build && ./nextpnr-ecp5-test
test_ecp5_script: cd build && ./nextpnr-ecp5-test
test_xc7_script: cd build && ./nextpnr-xc7-test

View File

@ -52,4 +52,10 @@ RUN set -e -x ;\
make -j $(nproc) ;\
make install
RUN set -e -x ;\
mkdir -p /usr/local/src ;\
cd /usr/local/src ;\
git clone --recursive https://github.com/eddiehung/torc.git ;\
cd torc ;\
git reset --hard 90874d1293fbeba77bae41b5c38168cd91e1bf00 ;\
make -j $(nproc) -C src/torc

20
.gitignore vendored
View File

@ -3,6 +3,7 @@
/nextpnr-generic*
/nextpnr-ice40*
/nextpnr-ecp5*
/nextpnr-xc7*
cmake-build-*/
Makefile
cmake_install.cmake
@ -29,3 +30,22 @@ install_manifest.txt
/ImportExecutables.cmake
*-coverage/
*-coverage.info
# nextpnr-xc7
*.xdl
*.bit
# ise
_xmsgs/
*.bgn
*.drc
*.ncd
*.xwbt
usage_statistics_webtalk.html
webtalk.log
xilinx_device_details.xml
*_fpga_editor.*
*.twr
*.twx
*.nlf
*.sdf

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "torc"]
path = torc
url = https://github.com/eddiehung/torc

View File

@ -17,7 +17,7 @@ if (STATIC_BUILD)
endif()
# List of families to build
set(FAMILIES generic ice40 ecp5)
set(FAMILIES generic ice40 ecp5 xc7)
set(ARCH "" CACHE STRING "Architecture family for nextpnr build")
set_property(CACHE ARCH PROPERTY STRINGS ${FAMILIES})
@ -120,9 +120,10 @@ if (BUILD_PYTHON)
# Original source: https://github.com/BVLC/caffe/blob/master/cmake/Dependencies.cmake#L148
set(version ${PYTHONLIBS_VERSION_STRING})
STRING(REGEX REPLACE "[^0-9]" "" boost_py_version ${version})
STRING(REGEX REPLACE "[^0-9]" "" boost_py_version ${version})
find_package(Boost QUIET COMPONENTS "python-py${boost_py_version}" ${boost_libs})
set(Boost_PYTHON_FOUND ${Boost_PYTHON-PY${boost_py_version}_FOUND})
set(boost_python_lib "python-py${boost_py_version}")
while (NOT "${version}" STREQUAL "" AND NOT Boost_PYTHON_FOUND)
STRING(REGEX REPLACE "([0-9.]+).[0-9]+" "\\1" version ${version})
@ -130,6 +131,7 @@ if (BUILD_PYTHON)
STRING(REGEX REPLACE "[^0-9]" "" boost_py_version ${version})
find_package(Boost QUIET COMPONENTS "python-py${boost_py_version}" ${boost_libs})
set(Boost_PYTHON_FOUND ${Boost_PYTHON-PY${boost_py_version}_FOUND})
set(boost_python_lib "python-py${boost_py_version}")
STRING(REGEX MATCHALL "([0-9.]+).[0-9]+" has_more_version ${version})
if ("${has_more_version}" STREQUAL "")
@ -139,6 +141,7 @@ if (BUILD_PYTHON)
if (NOT Boost_PYTHON_FOUND)
find_package(Boost QUIET COMPONENTS python3 ${boost_libs})
set(boost_python_lib python3)
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
set(Boost_PYTHON_FOUND TRUE)
endif ()
@ -146,6 +149,7 @@ if (BUILD_PYTHON)
if (NOT Boost_PYTHON_FOUND)
find_package(Boost QUIET COMPONENTS python36 ${boost_libs})
set(boost_python_lib python36)
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
set(Boost_PYTHON_FOUND TRUE)
endif ()
@ -153,6 +157,7 @@ if (BUILD_PYTHON)
if (NOT Boost_PYTHON_FOUND)
find_package(Boost QUIET COMPONENTS python37 ${boost_libs})
set(boost_python_lib python37)
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
set(Boost_PYTHON_FOUND TRUE)
endif ()
@ -161,6 +166,7 @@ if (BUILD_PYTHON)
if (NOT Boost_PYTHON_FOUND)
STRING(REGEX REPLACE "([0-9]+\\.[0-9]+).*" "\\1" gentoo_version ${PYTHONLIBS_VERSION_STRING})
find_package(Boost QUIET COMPONENTS python-${gentoo_version} ${boost_libs})
set(boost_python_lib python-${gentoo_version})
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
set(Boost_PYTHON_FOUND TRUE)
endif ()

View File

@ -1,6 +1,19 @@
nextpnr -- a portable FPGA place and route tool
===============================================
***
### NB: This nextpnr-xc7 branch is *unofficial*, *very* proof-of-concept, *very* experimental, *very* unoptimised, and is provided with *no support whatsoever*. Use at your own risk!
#### It leverages a [torc](https://github.com/torc-isi/torc) fork with minimal changes (those necessary to support building on later versions of gcc) to target XDL-compatible devices.
### Note that torc is licensed under GPLv3 which differs from nextpnr's ISC license, thus please respect the limitations imposed by both licenses.
Currently, only LUT1-6, IOB, BUFGCTRL, MMCME2_ADV are supported for xc7z020 and xc7vx680t (but trivial to add others).
The following example shell scripts are available:
* blinky.sh -- generates blinky.bit that flashes (with a delay) the 4 LEDs on a ZYBO Z7
* blinky_sim.sh -- post place-and-route simulation, without any delays (requires [GHDL](https://github.com/ghdl/ghdl))
* picorv32.sh -- just places-and-routes picorv32.ncd (no testbench)
* attosoc.sh -- generates attosoc.bit of a self-stimulating picorv32 device that displays (with a delay) prime numbers to the LEDs -- when testing on hardware, consider using a PLL (MMCM) to meet timing
* attosoc_sim.sh -- post place-and-route simulation of a self-stimulating picorv32 device, without any delays (requires [GHDL](https://github.com/ghdl/ghdl))
***
nextpnr aims to be a vendor neutral, timing driven, FOSS FPGA place and route
tool.

View File

@ -37,6 +37,8 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type
if (driver_gb)
return 0;
int clock_count;
if (ctx->getPortTimingClass(driver_cell, net->driver.port, clock_count) == TMG_IGNORE)
return 0;
bool timing_driven = ctx->timing_driven && type == MetricType::COST &&
ctx->getPortTimingClass(driver_cell, net->driver.port, clock_count) != TMG_IGNORE;
delay_t negative_slack = 0;

View File

@ -512,6 +512,21 @@ struct Router1
WireId next_wire = ctx->getPipDstWire(pip);
next_delay += ctx->getWireDelay(next_wire).maxDelay();
#ifdef ARCH_XC7
// For BUFG routing, do not exit the global network until the destination tile is reached
if (ctx->isGlobalNet(net_info)) {
if (torc_info->wire_is_global[src_wire.index] && !torc_info->wire_is_global[next_wire.index]) {
const auto &arc = torc_info->pip_to_arc[pip.index];
const auto &next_tw = arc.getSinkTilewire();
const auto &next_loc = torc_info->tile_to_xy[next_tw.getTileIndex()];
const auto &dst_tw = torc_info->wire_to_tilewire[dst_wire.index];
const auto &dst_loc = torc_info->tile_to_xy[dst_tw.getTileIndex()];
if (next_loc.second != dst_loc.second || next_loc.first != dst_loc.first)
continue;
}
}
#endif
WireId conflictWireWire = WireId(), conflictPipWire = WireId();
NetInfo *conflictWireNet = nullptr, *conflictPipNet = nullptr;

View File

@ -51,9 +51,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
bool Application::notify(QObject *receiver, QEvent *event)
{
bool retVal = true;
try {
retVal = QApplication::notify(receiver, event);
} catch (assertion_failure ex) {
// try {
retVal = QApplication::notify(receiver, event);
/*} catch (assertion_failure ex) {
QString msg;
QTextStream out(&msg);
out << ex.filename.c_str() << " at " << ex.line << "\n";
@ -61,7 +61,7 @@ bool Application::notify(QObject *receiver, QEvent *event)
QMessageBox::critical(0, "Error", msg);
} catch (...) {
QMessageBox::critical(0, "Error", "Fatal error !!!");
}
}*/
return retVal;
}

View File

@ -317,6 +317,12 @@ void DesignWidget::newContext(Context *ctx)
for (const auto &wire : ctx->getWires()) {
wireMap[std::pair<int, int>(wire.location.x, wire.location.y)].push_back(wire);
}
#endif
#ifdef ARCH_XC7
for (const auto &wire : ctx->getWires()) {
const auto loc = torc_info->wire_to_loc(wire.index);
wireMap[std::pair<int, int>(loc.x, loc.y)].push_back(wire);
}
#endif
auto wireGetter = [](Context *ctx, WireId id) { return ctx->getWireName(id); };
getTreeByElementType(ElementType::WIRE)
@ -706,8 +712,9 @@ void DesignWidget::onSelectionChanged(int num, const QItemSelection &, const QIt
addProperty(topItem, QVariant::String, "Type", ctx->getPipType(pip).c_str(ctx));
addProperty(topItem, QVariant::Bool, "Available", ctx->checkPipAvail(pip));
addProperty(topItem, QVariant::String, "Bound Net", ctx->nameOf(ctx->getBoundPipNet(pip)), ElementType::NET);
WireId conflict = ctx->getConflictingPipWire(pip);
addProperty(topItem, QVariant::String, "Conflicting Wire",
ctx->getWireName(ctx->getConflictingPipWire(pip)).c_str(ctx), ElementType::WIRE);
(conflict != WireId() ? ctx->getWireName(conflict).c_str(ctx) : ""), ElementType::WIRE);
addProperty(topItem, QVariant::String, "Conflicting Net", ctx->nameOf(ctx->getConflictingPipNet(pip)),
ElementType::NET);
addProperty(topItem, QVariant::String, "Src Wire", ctx->getWireName(ctx->getPipSrcWire(pip)).c_str(ctx),

0
gui/xc7/family.cmake Normal file
View File

51
gui/xc7/mainwindow.cc Normal file
View File

@ -0,0 +1,51 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
*
* 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.
*
*/
#include "mainwindow.h"
static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
NEXTPNR_NAMESPACE_BEGIN
MainWindow::MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent)
: BaseMainWindow(std::move(context), args, parent)
{
initMainResource();
std::string title = "nextpnr-xc7 - [EMPTY]";
setWindowTitle(title.c_str());
connect(this, &BaseMainWindow::contextChanged, this, &MainWindow::newContext);
createMenu();
}
MainWindow::~MainWindow() {}
void MainWindow::newContext(Context *ctx)
{
std::string title = "nextpnr-xc7 - " + ctx->getChipName();
setWindowTitle(title.c_str());
}
void MainWindow::createMenu() {}
void MainWindow::new_proj() {}
NEXTPNR_NAMESPACE_END

45
gui/xc7/mainwindow.h Normal file
View File

@ -0,0 +1,45 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
*
* 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 MAINWINDOW_H
#define MAINWINDOW_H
#include "../basewindow.h"
NEXTPNR_NAMESPACE_BEGIN
class MainWindow : public BaseMainWindow
{
Q_OBJECT
public:
explicit MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent = 0);
virtual ~MainWindow();
public:
void createMenu();
protected Q_SLOTS:
void new_proj() override;
void newContext(Context *ctx);
};
NEXTPNR_NAMESPACE_END
#endif // MAINWINDOW_H

2
gui/xc7/nextpnr.qrc Normal file
View File

@ -0,0 +1,2 @@
<RCC>
</RCC>

1
torc Submodule

@ -0,0 +1 @@
Subproject commit 90874d1293fbeba77bae41b5c38168cd91e1bf00

879
xc7/arch.cc Normal file
View File

@ -0,0 +1,879 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
*
* 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.
*
*/
#include <algorithm>
#include <cmath>
#include <regex>
#include "cells.h"
#include "gfx.h"
#include "log.h"
#include "nextpnr.h"
#include "placer1.h"
#include "router1.h"
#include "util.h"
#include "torc/common/DirectoryTree.hpp"
NEXTPNR_NAMESPACE_BEGIN
std::unique_ptr<const TorcInfo> torc_info;
TorcInfo::TorcInfo(BaseCtx *ctx, const std::string &inDeviceName, const std::string &inPackageName)
: TorcInfo(inDeviceName, inPackageName)
{
static const std::regex re_loc(".+_X(\\d+)Y(\\d+)");
std::cmatch what;
tile_to_xy.resize(tiles.getTileCount());
for (TileIndex tileIndex(0); tileIndex < tiles.getTileCount(); tileIndex++) {
const auto &tileInfo = tiles.getTileInfo(tileIndex);
if (!std::regex_match(tileInfo.getName(), what, re_loc))
throw;
const auto x = boost::lexical_cast<int>(what.str(1));
const auto y = boost::lexical_cast<int>(what.str(2));
tile_to_xy[tileIndex] = std::make_pair(x, y);
}
bel_to_site_index.reserve(sites.getSiteCount() * 4);
bel_to_loc.reserve(sites.getSiteCount() * 4);
site_index_to_bel.resize(sites.getSiteCount());
site_index_to_type.resize(sites.getSiteCount());
BelId b;
b.index = 0;
for (SiteIndex i(0); i < sites.getSiteCount(); ++i) {
const auto &site = sites.getSite(i);
const auto &pd = site.getPrimitiveDefPtr();
const auto &type = pd->getName();
int x, y;
std::tie(x, y) = tile_to_xy[site.getTileIndex()];
if (type == "SLICEL" || type == "SLICEM") {
bel_to_site_index.push_back(i);
bel_to_site_index.push_back(i);
bel_to_site_index.push_back(i);
bel_to_site_index.push_back(i);
site_index_to_type[i] = id_SLICE_LUT6;
const auto site_name = site.getName();
if (!std::regex_match(site_name.c_str(), what, re_loc))
throw;
const auto sx = boost::lexical_cast<int>(what.str(1));
if ((sx & 1) == 0) {
bel_to_loc.emplace_back(x, y, 0);
bel_to_loc.emplace_back(x, y, 1);
bel_to_loc.emplace_back(x, y, 2);
bel_to_loc.emplace_back(x, y, 3);
} else {
bel_to_loc.emplace_back(x, y, 4);
bel_to_loc.emplace_back(x, y, 5);
bel_to_loc.emplace_back(x, y, 6);
bel_to_loc.emplace_back(x, y, 7);
}
site_index_to_bel[i] = b;
b.index += 4;
} else if (type == "IOB33S" || type == "IOB33M") {
bel_to_site_index.push_back(i);
site_index_to_type[i] = id_IOB33;
// TODO: Fix z when two IOBs on same tile
bel_to_loc.emplace_back(x, y, 0);
site_index_to_bel[i] = b;
++b.index;
} else if (type == "IOB18S" || type == "IOB18M") {
bel_to_site_index.push_back(i);
site_index_to_type[i] = id_IOB18;
// TODO: Fix z when two IOBs on same tile
bel_to_loc.emplace_back(x, y, 0);
site_index_to_bel[i] = b;
++b.index;
} else {
bel_to_site_index.push_back(i);
site_index_to_type[i] = ctx->id(type);
bel_to_loc.emplace_back(x, y, 0);
site_index_to_bel[i] = b;
++b.index;
}
}
num_bels = bel_to_site_index.size();
bel_to_site_index.shrink_to_fit();
bel_to_loc.shrink_to_fit();
const std::regex re_124("(.+_)?[NESW][NESWLR](\\d)((BEG(_[NS])?)|(END(_[NS])?)|[A-E])?\\d(_\\d)?");
const std::regex re_L("(.+_)?L(H|V|VB)(_L)?\\d+(_\\d)?");
const std::regex re_BYP("BYP(_ALT)?\\d");
const std::regex re_BYP_B("BYP_[BL]\\d");
const std::regex re_BOUNCE_NS("(BYP|FAN)_BOUNCE_[NS]3_\\d");
const std::regex re_FAN("FAN(_ALT)?\\d");
const std::regex re_CLB_I1_6("CLBL[LM]_(L|LL|M)_[A-D]([1-6])");
const std::regex bufg_i("CLK_BUFG_BUFGCTRL\\d+_I0");
const std::regex bufg_o("CLK_BUFG_BUFGCTRL\\d+_O");
const std::regex hrow("CLK_HROW_CLK[01]_[34]");
std::unordered_map</*TileTypeIndex*/ unsigned, std::vector<delay_t>> delay_lookup;
std::unordered_map<Segments::SegmentReference, TileIndex> segment_to_anchor;
Tilewire currentTilewire;
WireId w;
w.index = 0;
for (TileIndex tileIndex(0); tileIndex < tiles.getTileCount(); tileIndex++) {
// iterate over every wire in the tile
const auto &tileInfo = tiles.getTileInfo(tileIndex);
auto tileTypeIndex = tileInfo.getTypeIndex();
auto wireCount = tiles.getWireCount(tileTypeIndex);
currentTilewire.setTileIndex(tileIndex);
for (WireIndex wireIndex(0); wireIndex < wireCount; wireIndex++) {
currentTilewire.setWireIndex(wireIndex);
const auto &currentSegment = segments.getTilewireSegment(currentTilewire);
if (!currentSegment.isTrivial()) {
auto r = segment_to_anchor.emplace(currentSegment, currentSegment.getAnchorTileIndex());
if (r.second) {
TilewireVector segment;
const_cast<DDB &>(*ddb).expandSegment(currentTilewire, segment, DDB::eExpandDirectionNone);
// expand all of the arcs
TilewireVector::const_iterator sep = segment.begin();
TilewireVector::const_iterator see = segment.end();
while (sep < see) {
// expand the tilewire sinks
const Tilewire &tilewire = *sep++;
const auto &tileInfo = tiles.getTileInfo(tilewire.getTileIndex());
const auto &tileTypeName = tiles.getTileTypeName(tileInfo.getTypeIndex());
if (boost::starts_with(tileTypeName, "INT") || boost::starts_with(tileTypeName, "CLB")) {
r.first->second = tilewire.getTileIndex();
break;
}
}
}
if (r.first->second != tileIndex)
continue;
segment_to_wire.emplace(currentSegment, w);
} else
trivial_to_wire.emplace(currentTilewire, w);
wire_to_tilewire.push_back(currentTilewire);
auto it = delay_lookup.find(tileTypeIndex);
if (it == delay_lookup.end()) {
auto wireCount = tiles.getWireCount(tileTypeIndex);
std::vector<delay_t> tile_delays(wireCount);
for (WireIndex wireIndex(0); wireIndex < wireCount; wireIndex++) {
const WireInfo &wireInfo = tiles.getWireInfo(tileTypeIndex, wireIndex);
auto wire_name = wireInfo.getName();
if (std::regex_match(wire_name, what, re_124)) {
switch (what.str(2)[0]) {
case '1':
tile_delays[wireIndex] = 150;
break;
case '2':
tile_delays[wireIndex] = 170;
break;
case '4':
tile_delays[wireIndex] = 210;
break;
case '6':
tile_delays[wireIndex] = 210;
break;
default:
throw;
}
} else if (std::regex_match(wire_name, what, re_L)) {
std::string l(what[2]);
if (l == "H")
tile_delays[wireIndex] = 360;
else if (l == "VB")
tile_delays[wireIndex] = 300;
else if (l == "V")
tile_delays[wireIndex] = 350;
else
throw;
} else if (std::regex_match(wire_name, what, re_BYP)) {
tile_delays[wireIndex] = 190;
} else if (std::regex_match(wire_name, what, re_BYP_B)) {
} else if (std::regex_match(wire_name, what, re_FAN)) {
tile_delays[wireIndex] = 190;
} else if (std::regex_match(wire_name, what, re_CLB_I1_6)) {
switch (what.str(2)[0]) {
case '1':
tile_delays[wireIndex] = 280;
break;
case '2':
tile_delays[wireIndex] = 280;
break;
case '3':
tile_delays[wireIndex] = 180;
break;
case '4':
tile_delays[wireIndex] = 180;
break;
case '5':
tile_delays[wireIndex] = 80;
break;
case '6':
tile_delays[wireIndex] = 40;
break;
default:
throw;
}
}
}
it = delay_lookup.emplace(tileTypeIndex, std::move(tile_delays)).first;
}
assert(it != delay_lookup.end());
DelayInfo d;
d.delay = it->second[currentTilewire.getWireIndex()];
wire_to_delay.emplace_back(std::move(d));
++w.index;
}
}
segment_to_anchor.clear();
wire_to_tilewire.shrink_to_fit();
wire_to_delay.shrink_to_fit();
num_wires = wire_to_tilewire.size();
wire_is_global.resize(num_wires);
wire_to_pips_downhill.resize(num_wires);
// std::unordered_map<Arc, int> arc_to_pip;
ArcVector arcs;
ExtendedWireInfo ewi(*ddb);
PipId p;
p.index = 0;
for (w.index = 0; w.index < num_wires; ++w.index) {
const auto &currentTilewire = wire_to_tilewire[w.index];
if (currentTilewire.isUndefined())
continue;
const auto &tileInfo = tiles.getTileInfo(currentTilewire.getTileIndex());
const auto tileTypeName = tiles.getTileTypeName(tileInfo.getTypeIndex());
const bool clb = boost::starts_with(
tileTypeName, "CLB"); // Disable all CLB route-throughs (i.e. LUT in->out, LUT A->AMUX, for now)
auto &pips = wire_to_pips_downhill[w.index];
const bool clk_tile = boost::starts_with(tileTypeName, "CLK");
bool global_tile = false;
arcs.clear();
// const_cast<DDB &>(*ddb).expandSegmentSinks(currentTilewire, arcs, DDB::eExpandDirectionNone,
// false /* inUseTied */, true /*inUseRegular */,
// true /* inUseIrregular */, !clb /* inUseRoutethrough */);
{
// expand the segment
TilewireVector segment;
const_cast<DDB &>(*ddb).expandSegment(currentTilewire, segment, DDB::eExpandDirectionNone);
// expand all of the arcs
TilewireVector::const_iterator sep = segment.begin();
TilewireVector::const_iterator see = segment.end();
while (sep < see) {
// expand the tilewire sinks
const Tilewire &tilewire = *sep++;
const auto &tileInfo = tiles.getTileInfo(tilewire.getTileIndex());
const auto &tileTypeName = tiles.getTileTypeName(tileInfo.getTypeIndex());
global_tile = global_tile || boost::starts_with(tileTypeName, "CLK") ||
boost::starts_with(tileTypeName, "HCLK") || boost::starts_with(tileTypeName, "CFG");
TilewireVector sinks;
const_cast<DDB &>(*ddb).expandTilewireSinks(tilewire, sinks, false /*inUseTied*/, true /*inUseRegular*/,
true /*inUseIrregular*/, !clb /* inUseRoutethrough */);
// rewrite the sinks as arcs
TilewireVector::const_iterator sip = sinks.begin();
TilewireVector::const_iterator sie = sinks.end();
while (sip < sie) {
Arc a(tilewire, *sip++);
// Disable BUFG I0 -> O routethrough
if (clk_tile) {
ewi.set(a.getSourceTilewire());
if (std::regex_match(ewi.mWireName, bufg_i)) {
ewi.set(a.getSinkTilewire());
if (std::regex_match(ewi.mWireName, bufg_o))
continue;
}
}
// Disable entering HROW from INT_[LR].CLK[01]
if (boost::starts_with(tileTypeName, "CLK_HROW")) {
ewi.set(a.getSourceTilewire());
if (std::regex_match(ewi.mWireName, hrow))
continue;
}
pips.emplace_back(p);
pip_to_arc.emplace_back(a);
// arc_to_pip.emplace(a, p.index);
++p.index;
}
}
}
pips.shrink_to_fit();
if (global_tile)
wire_is_global[w.index] = true;
}
pip_to_arc.shrink_to_fit();
num_pips = pip_to_arc.size();
height = (int)tiles.getRowCount();
width = (int)tiles.getColCount();
}
TorcInfo::TorcInfo(const std::string &inDeviceName, const std::string &inPackageName)
: ddb(new DDB(inDeviceName, inPackageName)), sites(ddb->getSites()), tiles(ddb->getTiles()),
segments(ddb->getSegments())
{
}
// -----------------------------------------------------------------------
void IdString::initialize_arch(const BaseCtx *ctx)
{
#define X(t) initialize_add(ctx, #t, ID_##t);
#include "constids.inc"
#undef X
}
// -----------------------------------------------------------------------
Arch::Arch(ArchArgs args) : args(args)
{
std::stringstream ss;
ss << TORC_ROOT << "/src/torc";
torc::common::DirectoryTree directoryTree(ss.str().c_str());
if (args.type == ArchArgs::Z020) {
torc_info = std::unique_ptr<TorcInfo>(new TorcInfo(this, "xc7z020", args.package));
} else if (args.type == ArchArgs::VX980) {
torc_info = std::unique_ptr<TorcInfo>(new TorcInfo(this, "xc7vx980t", args.package));
} else {
log_error("Unsupported XC7 chip type.\n");
}
// TODO: FIXME
width = torc_info->width;
height = torc_info->height;
/*if (getCtx()->verbose)*/ {
log_info("Number of bels: %d\n", torc_info->num_bels);
log_info("Number of wires: %d\n", torc_info->num_wires);
log_info("Number of pips: %d\n", torc_info->num_pips);
}
bel_to_cell.resize(torc_info->num_bels);
wire_to_net.resize(torc_info->num_wires);
pip_to_net.resize(torc_info->num_pips);
}
// -----------------------------------------------------------------------
std::string Arch::getChipName() const
{
if (args.type == ArchArgs::Z020) {
return "z020";
} else if (args.type == ArchArgs::VX980) {
return "vx980";
} else {
log_error("Unsupported XC7 chip type.\n");
}
}
// -----------------------------------------------------------------------
IdString Arch::archArgsToId(ArchArgs args) const
{
if (args.type == ArchArgs::Z020)
return id("z020");
if (args.type == ArchArgs::VX980)
return id("vx980");
return IdString();
}
// -----------------------------------------------------------------------
static bool endsWith(const std::string &str, const std::string &suffix)
{
return str.size() >= suffix.size() && 0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix);
}
BelId Arch::getBelByName(IdString name) const
{
std::string n = name.str(this);
int ndx = 0;
if (endsWith(n, "_A") || endsWith(n, "_B") || endsWith(n, "_C") || endsWith(n, "_D")) {
ndx = (int)(n.back() - 'A');
n = n.substr(0, n.size() - 2);
}
auto it = torc_info->sites.findSiteIndex(n);
if (it != SiteIndex(-1)) {
BelId id = torc_info->site_index_to_bel.at(it);
id.index += ndx;
return id;
}
return BelId();
}
BelId Arch::getBelByLocation(Loc loc) const
{
BelId bel;
if (bel_by_loc.empty()) {
for (int i = 0; i < torc_info->num_bels; i++) {
BelId b;
b.index = i;
bel_by_loc[getBelLocation(b)] = b;
}
}
auto it = bel_by_loc.find(loc);
if (it != bel_by_loc.end())
bel = it->second;
return bel;
}
BelRange Arch::getBelsByTile(int x, int y) const
{
BelRange br;
br.b.cursor = 0;
br.e.cursor = 0;
NPNR_ASSERT("TODO");
return br;
}
PortType Arch::getBelPinType(BelId bel, IdString pin) const
{
NPNR_ASSERT(bel != BelId());
NPNR_ASSERT("TODO");
return PORT_INOUT;
}
std::vector<std::pair<IdString, std::string>> Arch::getBelAttrs(BelId bel) const
{
std::vector<std::pair<IdString, std::string>> ret;
return ret;
}
WireId Arch::getBelPinWire(BelId bel, IdString pin) const
{
auto pin_name = pin.str(this);
auto bel_type = getBelType(bel);
if (bel_type == id_SLICE_LUT6) {
// For all LUT based inputs and outputs (I1-I6,O,OQ,OMUX) then change the I/O into the LUT
if (pin_name[0] == 'I' || pin_name[0] == 'O') {
switch (torc_info->bel_to_loc[bel.index].z) {
case 0:
case 4:
pin_name[0] = 'A';
break;
case 1:
case 5:
pin_name[0] = 'B';
break;
case 2:
case 6:
pin_name[0] = 'C';
break;
case 3:
case 7:
pin_name[0] = 'D';
break;
default:
throw;
}
}
} else if (bel_type == id_PS7 || bel_type == id_MMCME2_ADV) {
// e.g. Convert DDRARB[0] -> DDRARB0
pin_name.erase(std::remove_if(pin_name.begin(), pin_name.end(), boost::is_any_of("[]")), pin_name.end());
}
auto site_index = torc_info->bel_to_site_index[bel.index];
const auto &site = torc_info->sites.getSite(site_index);
auto &tw = site.getPinTilewire(pin_name);
if (tw.isUndefined())
log_error("no wire found for site '%s' pin '%s' \n", torc_info->bel_to_name(bel.index).c_str(),
pin_name.c_str());
return torc_info->tilewire_to_wire(tw);
}
std::vector<IdString> Arch::getBelPins(BelId bel) const
{
std::vector<IdString> ret;
NPNR_ASSERT("TODO");
return ret;
}
// -----------------------------------------------------------------------
WireId Arch::getWireByName(IdString name) const
{
WireId ret;
if (wire_by_name.empty()) {
for (int i = 0; i < torc_info->num_wires; i++)
wire_by_name[id(torc_info->wire_to_name(i))] = i;
}
auto it = wire_by_name.find(name);
if (it != wire_by_name.end())
ret.index = it->second;
return ret;
}
IdString Arch::getWireType(WireId wire) const
{
NPNR_ASSERT(wire != WireId());
NPNR_ASSERT("TODO");
return IdString();
}
// -----------------------------------------------------------------------
std::vector<std::pair<IdString, std::string>> Arch::getWireAttrs(WireId wire) const
{
std::vector<std::pair<IdString, std::string>> ret;
NPNR_ASSERT("TODO");
return ret;
}
// -----------------------------------------------------------------------
PipId Arch::getPipByName(IdString name) const
{
PipId ret;
if (pip_by_name.empty()) {
for (int i = 0; i < torc_info->num_pips; i++) {
PipId pip;
pip.index = i;
pip_by_name[getPipName(pip)] = i;
}
}
auto it = pip_by_name.find(name);
if (it != pip_by_name.end())
ret.index = it->second;
return ret;
}
IdString Arch::getPipName(PipId pip) const
{
NPNR_ASSERT(pip != PipId());
ExtendedWireInfo ewi_src(*torc_info->ddb, torc_info->pip_to_arc[pip.index].getSourceTilewire());
ExtendedWireInfo ewi_dst(*torc_info->ddb, torc_info->pip_to_arc[pip.index].getSinkTilewire());
std::stringstream pip_name;
pip_name << ewi_src.mTileName << "." << ewi_src.mWireName << ".->." << ewi_dst.mWireName;
return id(pip_name.str());
}
std::vector<std::pair<IdString, std::string>> Arch::getPipAttrs(PipId pip) const
{
std::vector<std::pair<IdString, std::string>> ret;
NPNR_ASSERT("TODO");
return ret;
}
// -----------------------------------------------------------------------
BelId Arch::getPackagePinBel(const std::string &pin) const { return getBelByName(id(pin)); }
std::string Arch::getBelPackagePin(BelId bel) const
{
NPNR_ASSERT("TODO");
return "";
}
// -----------------------------------------------------------------------
GroupId Arch::getGroupByName(IdString name) const
{
for (auto g : getGroups())
if (getGroupName(g) == name)
return g;
return GroupId();
}
IdString Arch::getGroupName(GroupId group) const
{
std::string suffix;
switch (group.type) {
NPNR_ASSERT("TODO");
default:
return IdString();
}
return id("X" + std::to_string(group.x) + "/Y" + std::to_string(group.y) + "/" + suffix);
}
std::vector<GroupId> Arch::getGroups() const
{
std::vector<GroupId> ret;
NPNR_ASSERT("TODO");
return ret;
}
std::vector<BelId> Arch::getGroupBels(GroupId group) const
{
std::vector<BelId> ret;
return ret;
}
std::vector<WireId> Arch::getGroupWires(GroupId group) const
{
std::vector<WireId> ret;
return ret;
}
std::vector<PipId> Arch::getGroupPips(GroupId group) const
{
std::vector<PipId> ret;
NPNR_ASSERT("TODO");
return ret;
}
std::vector<GroupId> Arch::getGroupGroups(GroupId group) const
{
std::vector<GroupId> ret;
NPNR_ASSERT("TODO");
return ret;
}
// -----------------------------------------------------------------------
bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; }
// -----------------------------------------------------------------------
bool Arch::place() { return placer1(getCtx(), Placer1Cfg(getCtx())); }
bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); }
// -----------------------------------------------------------------------
DecalXY Arch::getBelDecal(BelId bel) const
{
DecalXY decalxy;
decalxy.decal.type = DecalId::TYPE_BEL;
decalxy.decal.index = bel.index;
decalxy.decal.active = bel_to_cell.at(bel.index) != nullptr;
return decalxy;
}
DecalXY Arch::getWireDecal(WireId wire) const
{
DecalXY decalxy;
decalxy.decal.type = DecalId::TYPE_WIRE;
decalxy.decal.index = wire.index;
decalxy.decal.active = wire_to_net.at(wire.index) != nullptr;
return decalxy;
}
DecalXY Arch::getPipDecal(PipId pip) const
{
DecalXY decalxy;
decalxy.decal.type = DecalId::TYPE_PIP;
decalxy.decal.index = pip.index;
decalxy.decal.active = pip_to_net.at(pip.index) != nullptr;
return decalxy;
};
DecalXY Arch::getGroupDecal(GroupId group) const
{
DecalXY decalxy;
decalxy.decal.type = DecalId::TYPE_GROUP;
decalxy.decal.index = (group.type << 16) | (group.x << 8) | (group.y);
decalxy.decal.active = true;
return decalxy;
};
std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
{
std::vector<GraphicElement> ret;
if (decal.type == DecalId::TYPE_BEL) {
BelId bel;
bel.index = decal.index;
auto bel_type = getBelType(bel);
int x = torc_info->bel_to_loc[bel.index].x;
int y = torc_info->bel_to_loc[bel.index].y;
int z = torc_info->bel_to_loc[bel.index].z;
if (bel_type == id_SLICE_LUT6) {
GraphicElement el;
/*if (z>3) {
z = z - 4;
x -= logic_cell_x2- logic_cell_x1;
}*/
el.type = GraphicElement::TYPE_BOX;
el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
el.x1 = x + logic_cell_x1;
el.x2 = x + logic_cell_x2;
el.y1 = y + logic_cell_y1 + (z)*logic_cell_pitch;
el.y2 = y + logic_cell_y2 + (z)*logic_cell_pitch;
ret.push_back(el);
}
}
return ret;
}
// -----------------------------------------------------------------------
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const
{
if (cell->type == id_SLICE_LUT6) {
if (fromPort.index >= id_I1.index && fromPort.index <= id_I6.index) {
if (toPort == id_O) {
delay.delay = 124; // Tilo
return true;
}
if (toPort == id_OQ) {
delay.delay = 95; // Tas
return true;
}
}
if (fromPort == id_CLK) {
if (toPort == id_OQ) {
delay.delay = 456; // Tcko
return true;
}
}
} else if (cell->type == id_BUFGCTRL) {
return true;
}
return false;
}
// Get the port class, also setting clockPort to associated clock if applicable
TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const
{
if (cell->type == id_SLICE_LUT6) {
if (port == id_CLK)
return TMG_CLOCK_INPUT;
if (port == id_CIN)
return TMG_COMB_INPUT;
if (port == id_COUT)
return TMG_COMB_OUTPUT;
if (port == id_O) {
// LCs with no inputs are constant drivers
if (cell->lcInfo.inputCount == 0)
return TMG_IGNORE;
return TMG_COMB_OUTPUT;
}
if (cell->lcInfo.dffEnable) {
clockInfoCount = 1;
if (port == id_OQ)
return TMG_REGISTER_OUTPUT;
return TMG_REGISTER_INPUT;
} else {
return TMG_COMB_INPUT;
}
// TODO
// if (port == id_OMUX)
} else if (cell->type == id_IOB33 || cell->type == id_IOB18) {
if (port == id_I)
return TMG_STARTPOINT;
else if (port == id_O)
return TMG_ENDPOINT;
} else if (cell->type == id_BUFGCTRL) {
if (port == id_O)
return TMG_COMB_OUTPUT;
return TMG_COMB_INPUT;
} else if (cell->type == id_PS7) {
// TODO
return TMG_IGNORE;
} else if (cell->type == id_MMCME2_ADV) {
return TMG_IGNORE;
}
log_error("no timing info for port '%s' of cell type '%s'\n", port.c_str(this), cell->type.c_str(this));
}
TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const
{
TimingClockingInfo info;
if (cell->type == id_SLICE_LUT6) {
info.clock_port = id_CLK;
info.edge = cell->lcInfo.negClk ? FALLING_EDGE : RISING_EDGE;
if (port == id_OQ) {
bool has_clktoq = getCellDelay(cell, id_CLK, id_OQ, info.clockToQ);
NPNR_ASSERT(has_clktoq);
} else {
info.setup.delay = 124; // Tilo
info.hold.delay = 0;
}
} else {
NPNR_ASSERT_FALSE("unhandled cell type in getPortClockingInfo");
}
return info;
}
bool Arch::isGlobalNet(const NetInfo *net) const
{
if (net == nullptr)
return false;
return net->driver.cell != nullptr && net->driver.cell->type == id_BUFGCTRL && net->driver.port == id_O;
}
// Assign arch arg info
void Arch::assignArchInfo()
{
for (auto &net : getCtx()->nets) {
NetInfo *ni = net.second.get();
if (isGlobalNet(ni))
ni->is_global = true;
ni->is_enable = false;
ni->is_reset = false;
for (auto usr : ni->users) {
if (is_enable_port(this, usr))
ni->is_enable = true;
if (is_reset_port(this, usr))
ni->is_reset = true;
}
}
for (auto &cell : getCtx()->cells) {
CellInfo *ci = cell.second.get();
assignCellInfo(ci);
}
}
void Arch::assignCellInfo(CellInfo *cell)
{
cell->belType = cell->type;
if (cell->type == id_SLICE_LUT6) {
cell->lcInfo.dffEnable = bool_or_default(cell->params, id_DFF_ENABLE);
cell->lcInfo.carryEnable = bool_or_default(cell->params, id_CARRY_ENABLE);
cell->lcInfo.negClk = bool_or_default(cell->params, id_NEG_CLK);
cell->lcInfo.clk = get_net_or_empty(cell, id_CLK);
cell->lcInfo.cen = get_net_or_empty(cell, id_CEN);
cell->lcInfo.sr = get_net_or_empty(cell, id_SR);
cell->lcInfo.inputCount = 0;
if (get_net_or_empty(cell, id_I1))
cell->lcInfo.inputCount++;
if (get_net_or_empty(cell, id_I2))
cell->lcInfo.inputCount++;
if (get_net_or_empty(cell, id_I3))
cell->lcInfo.inputCount++;
if (get_net_or_empty(cell, id_I4))
cell->lcInfo.inputCount++;
if (get_net_or_empty(cell, id_I5))
cell->lcInfo.inputCount++;
if (get_net_or_empty(cell, id_I6))
cell->lcInfo.inputCount++;
}
}
NEXTPNR_NAMESPACE_END

722
xc7/arch.h Normal file
View File

@ -0,0 +1,722 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
*
* 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 NEXTPNR_H
#error Include "arch.h" via "nextpnr.h" only.
#endif
#include "torc/Architecture.hpp"
#include "torc/Common.hpp"
using namespace torc::architecture;
using namespace torc::architecture::xilinx;
namespace std {
template <> struct hash<Segments::SegmentReference>
{
size_t operator()(const Segments::SegmentReference &s) const
{
size_t seed = 0;
boost::hash_combine(seed, hash<unsigned>()(s.getCompactSegmentIndex()));
boost::hash_combine(seed, hash<unsigned>()(s.getAnchorTileIndex()));
return seed;
}
};
template <> struct equal_to<Segments::SegmentReference>
{
bool operator()(const Segments::SegmentReference &lhs, const Segments::SegmentReference &rhs) const
{
return lhs.getAnchorTileIndex() == rhs.getAnchorTileIndex() &&
lhs.getCompactSegmentIndex() == rhs.getCompactSegmentIndex();
}
};
template <> struct hash<Tilewire>
{
size_t operator()(const Tilewire &t) const { return hash_value(t); }
};
template <> struct hash<Arc>
{
size_t operator()(const Arc &a) const
{
size_t seed = 0;
boost::hash_combine(seed, hash_value(a.getSourceTilewire()));
boost::hash_combine(seed, hash_value(a.getSinkTilewire()));
return seed;
}
};
} // namespace std
NEXTPNR_NAMESPACE_BEGIN
struct TorcInfo
{
TorcInfo(BaseCtx *ctx, const std::string &inDeviceName, const std::string &inPackageName);
TorcInfo() = delete;
std::unique_ptr<const DDB> ddb;
const Sites &sites;
const Tiles &tiles;
const Segments &segments;
const TileInfo &bel_to_tile_info(int32_t index) const
{
auto si = bel_to_site_index[index];
const auto &site = sites.getSite(si);
return tiles.getTileInfo(site.getTileIndex());
}
const std::string &bel_to_name(int32_t index) const
{
auto si = bel_to_site_index[index];
return sites.getSite(si).getName();
}
std::string wire_to_name(int32_t index) const
{
const auto &tw = wire_to_tilewire[index];
ExtendedWireInfo ewi(*ddb, tw);
std::stringstream ss;
ss << ewi.mTileName << "/" << ewi.mWireName;
ss << "(" << tw.getWireIndex() << "@" << tw.getTileIndex() << ")";
return ss.str();
}
Loc wire_to_loc(int32_t index) const
{
const auto &tw = wire_to_tilewire[index];
ExtendedWireInfo ewi(*ddb, tw);
Loc l;
l.x = (int)ewi.mTileCol;
l.y = (int)ewi.mTileRow;
return l;
}
WireId tilewire_to_wire(const Tilewire &tw) const
{
const auto &segment = segments.getTilewireSegment(tw);
if (!segment.isTrivial())
return segment_to_wire.at(segment);
return trivial_to_wire.at(tw);
}
std::vector<SiteIndex> bel_to_site_index;
int num_bels;
std::vector<BelId> site_index_to_bel;
std::vector<IdString> site_index_to_type;
std::vector<Loc> bel_to_loc;
std::unordered_map<Segments::SegmentReference, WireId> segment_to_wire;
std::unordered_map<Tilewire, WireId> trivial_to_wire;
std::vector<Tilewire> wire_to_tilewire;
int num_wires;
std::vector<DelayInfo> wire_to_delay;
// std::vector<std::vector<int>> wire_to_pips_uphill;
std::vector<std::vector<PipId>> wire_to_pips_downhill;
std::vector<Arc> pip_to_arc;
int num_pips;
int width;
int height;
std::vector<bool> wire_is_global;
std::vector<std::pair<int, int>> tile_to_xy;
TorcInfo(const std::string &inDeviceName, const std::string &inPackageName);
};
extern std::unique_ptr<const TorcInfo> torc_info;
struct BelIterator
{
int cursor;
BelIterator operator++()
{
cursor++;
return *this;
}
BelIterator operator++(int)
{
BelIterator prior(*this);
cursor++;
return prior;
}
bool operator!=(const BelIterator &other) const { return cursor != other.cursor; }
bool operator==(const BelIterator &other) const { return cursor == other.cursor; }
BelId operator*() const
{
BelId ret;
ret.index = cursor;
return ret;
}
};
struct BelRange
{
BelIterator b, e;
BelIterator begin() const { return b; }
BelIterator end() const { return e; }
};
// -----------------------------------------------------------------------
struct BelPinIterator
{
const BelId bel;
Array<const WireIndex>::iterator it;
void operator++() { it++; }
bool operator!=(const BelPinIterator &other) const { return it != other.it && bel != other.bel; }
BelPin operator*() const
{
BelPin ret;
ret.bel = bel;
ret.pin = IdString();
return ret;
}
};
struct BelPinRange
{
BelPinIterator b, e;
BelPinIterator begin() const { return b; }
BelPinIterator end() const { return e; }
};
// -----------------------------------------------------------------------
struct WireIterator
{
int cursor = -1;
void operator++() { cursor++; }
bool operator!=(const WireIterator &other) const { return cursor != other.cursor; }
WireId operator*() const
{
WireId ret;
ret.index = cursor;
return ret;
}
};
struct WireRange
{
WireIterator b, e;
WireIterator begin() const { return b; }
WireIterator end() const { return e; }
};
// -----------------------------------------------------------------------
struct AllPipIterator
{
int cursor = -1;
void operator++() { cursor++; }
bool operator!=(const AllPipIterator &other) const { return cursor != other.cursor; }
PipId operator*() const
{
PipId ret;
ret.index = cursor;
return ret;
}
};
struct AllPipRange
{
AllPipIterator b, e;
AllPipIterator begin() const { return b; }
AllPipIterator end() const { return e; }
};
// -----------------------------------------------------------------------
struct PipIterator
{
const PipId *cursor = nullptr;
void operator++() { cursor++; }
bool operator!=(const PipIterator &other) const { return cursor != other.cursor; }
PipId operator*() const { return *cursor; }
};
struct PipRange
{
PipIterator b, e;
PipIterator begin() const { return b; }
PipIterator end() const { return e; }
};
struct ArchArgs
{
enum ArchArgsTypes
{
NONE,
Z020,
VX980
} type = NONE;
std::string package;
};
struct Arch : BaseCtx
{
int width;
int height;
mutable std::unordered_map<IdString, int> wire_by_name;
mutable std::unordered_map<IdString, int> pip_by_name;
mutable std::unordered_map<Loc, BelId> bel_by_loc;
// std::vector<bool> bel_carry;
std::vector<CellInfo *> bel_to_cell;
std::vector<NetInfo *> wire_to_net;
std::vector<NetInfo *> pip_to_net;
// std::vector<NetInfo *> switches_locked;
ArchArgs args;
Arch(ArchArgs args);
std::string getChipName() const;
IdString archId() const { return id("xc7"); }
ArchArgs archArgs() const { return args; }
IdString archArgsToId(ArchArgs args) const;
// -------------------------------------------------
int getGridDimX() const { return width; }
int getGridDimY() const { return height; }
int getTileBelDimZ(int, int) const { return 8; }
int getTilePipDimZ(int, int) const { return 1; }
// -------------------------------------------------
BelId getBelByName(IdString name) const;
IdString getBelName(BelId bel) const
{
NPNR_ASSERT(bel != BelId());
auto name = torc_info->bel_to_name(bel.index);
if (getBelType(bel) == id_SLICE_LUT6) {
// Append LUT name to name
name.reserve(name.size() + 2);
name += "_";
switch (torc_info->bel_to_loc[bel.index].z) {
case 0:
case 4:
name += 'A';
break;
case 1:
case 5:
name += 'B';
break;
case 2:
case 6:
name += 'C';
break;
case 3:
case 7:
name += 'D';
break;
default:
throw;
}
}
return id(name);
}
uint32_t getBelChecksum(BelId bel) const { return bel.index; }
void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength)
{
NPNR_ASSERT(bel != BelId());
NPNR_ASSERT(bel_to_cell[bel.index] == nullptr);
bel_to_cell[bel.index] = cell;
// bel_carry[bel.index] = (cell->type == id_ICESTORM_LC && cell->lcInfo.carryEnable);
cell->bel = bel;
cell->belStrength = strength;
refreshUiBel(bel);
}
void unbindBel(BelId bel)
{
NPNR_ASSERT(bel != BelId());
NPNR_ASSERT(bel_to_cell[bel.index] != nullptr);
bel_to_cell[bel.index]->bel = BelId();
bel_to_cell[bel.index]->belStrength = STRENGTH_NONE;
bel_to_cell[bel.index] = nullptr;
// bel_carry[bel.index] = false;
refreshUiBel(bel);
}
bool checkBelAvail(BelId bel) const
{
NPNR_ASSERT(bel != BelId());
return bel_to_cell[bel.index] == nullptr;
}
CellInfo *getBoundBelCell(BelId bel) const
{
NPNR_ASSERT(bel != BelId());
return bel_to_cell[bel.index];
}
CellInfo *getConflictingBelCell(BelId bel) const
{
NPNR_ASSERT(bel != BelId());
return bel_to_cell[bel.index];
}
BelRange getBels() const
{
BelRange range;
range.b.cursor = 0;
range.e.cursor = torc_info->num_bels;
return range;
}
Loc getBelLocation(BelId bel) const { return torc_info->bel_to_loc[bel.index]; }
BelId getBelByLocation(Loc loc) const;
BelRange getBelsByTile(int x, int y) const;
bool getBelGlobalBuf(BelId bel) const { return getBelType(bel) == id_BUFGCTRL; }
IdString getBelType(BelId bel) const
{
NPNR_ASSERT(bel != BelId());
auto site_index = torc_info->bel_to_site_index[bel.index];
return torc_info->site_index_to_type[site_index];
}
std::vector<std::pair<IdString, std::string>> getBelAttrs(BelId bel) const;
WireId getBelPinWire(BelId bel, IdString pin) const;
PortType getBelPinType(BelId bel, IdString pin) const;
std::vector<IdString> getBelPins(BelId bel) const;
// -------------------------------------------------
WireId getWireByName(IdString name) const;
IdString getWireName(WireId wire) const
{
NPNR_ASSERT(wire != WireId());
return id(torc_info->wire_to_name(wire.index));
}
IdString getWireType(WireId wire) const;
std::vector<std::pair<IdString, std::string>> getWireAttrs(WireId wire) const;
uint32_t getWireChecksum(WireId wire) const { return wire.index; }
void bindWire(WireId wire, NetInfo *net, PlaceStrength strength)
{
NPNR_ASSERT(wire != WireId());
NPNR_ASSERT(wire_to_net[wire.index] == nullptr);
wire_to_net[wire.index] = net;
net->wires[wire].pip = PipId();
net->wires[wire].strength = strength;
refreshUiWire(wire);
}
void unbindWire(WireId wire)
{
NPNR_ASSERT(wire != WireId());
NPNR_ASSERT(wire_to_net[wire.index] != nullptr);
auto &net_wires = wire_to_net[wire.index]->wires;
auto it = net_wires.find(wire);
NPNR_ASSERT(it != net_wires.end());
auto pip = it->second.pip;
if (pip != PipId()) {
pip_to_net[pip.index] = nullptr;
}
net_wires.erase(it);
wire_to_net[wire.index] = nullptr;
refreshUiWire(wire);
}
bool checkWireAvail(WireId wire) const
{
NPNR_ASSERT(wire != WireId());
return wire_to_net[wire.index] == nullptr;
}
NetInfo *getBoundWireNet(WireId wire) const
{
NPNR_ASSERT(wire != WireId());
return wire_to_net[wire.index];
}
WireId getConflictingWireWire(WireId wire) const { return wire; }
NetInfo *getConflictingWireNet(WireId wire) const
{
NPNR_ASSERT(wire != WireId());
return wire_to_net[wire.index];
}
DelayInfo getWireDelay(WireId wire) const { return {}; }
BelPinRange getWireBelPins(WireId wire) const
{
BelPinRange range;
// TODO
return range;
}
WireRange getWires() const
{
WireRange range;
range.b.cursor = 0;
range.e.cursor = torc_info->num_wires;
return range;
}
// -------------------------------------------------
PipId getPipByName(IdString name) const;
void bindPip(PipId pip, NetInfo *net, PlaceStrength strength)
{
NPNR_ASSERT(pip != PipId());
NPNR_ASSERT(pip_to_net[pip.index] == nullptr);
pip_to_net[pip.index] = net;
WireId dst = getPipDstWire(pip);
NPNR_ASSERT(wire_to_net[dst.index] == nullptr);
wire_to_net[dst.index] = net;
net->wires[dst].pip = pip;
net->wires[dst].strength = strength;
refreshUiPip(pip);
refreshUiWire(dst);
}
void unbindPip(PipId pip)
{
NPNR_ASSERT(pip != PipId());
NPNR_ASSERT(pip_to_net[pip.index] != nullptr);
WireId dst = getPipDstWire(pip);
NPNR_ASSERT(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] = nullptr;
refreshUiPip(pip);
refreshUiWire(dst);
}
bool checkPipAvail(PipId pip) const
{
NPNR_ASSERT(pip != PipId());
return pip_to_net[pip.index] == nullptr;
}
NetInfo *getBoundPipNet(PipId pip) const
{
NPNR_ASSERT(pip != PipId());
return pip_to_net[pip.index];
}
WireId getConflictingPipWire(PipId pip) const { return WireId(); }
NetInfo *getConflictingPipNet(PipId pip) const
{
NPNR_ASSERT(pip != PipId());
return pip_to_net[pip.index];
}
AllPipRange getPips() const
{
AllPipRange range;
range.b.cursor = 0;
range.e.cursor = torc_info->num_pips;
return range;
}
Loc getPipLocation(PipId pip) const
{
Loc loc;
NPNR_ASSERT("TODO");
return loc;
}
IdString getPipName(PipId pip) const;
IdString getPipType(PipId pip) const { return IdString(); }
std::vector<std::pair<IdString, std::string>> getPipAttrs(PipId pip) const;
uint32_t getPipChecksum(PipId pip) const { return pip.index; }
WireId getPipSrcWire(PipId pip) const
{
NPNR_ASSERT(pip != PipId());
const auto &arc = torc_info->pip_to_arc[pip.index];
const auto &tw = arc.getSourceTilewire();
return torc_info->tilewire_to_wire(tw);
}
WireId getPipDstWire(PipId pip) const
{
NPNR_ASSERT(pip != PipId());
const auto &arc = torc_info->pip_to_arc[pip.index];
const auto &tw = arc.getSinkTilewire();
return torc_info->tilewire_to_wire(tw);
}
DelayInfo getPipDelay(PipId pip) const
{
NPNR_ASSERT(pip != PipId());
auto wire = getPipDstWire(pip);
return torc_info->wire_to_delay[wire.index];
}
PipRange getPipsDownhill(WireId wire) const
{
PipRange range;
NPNR_ASSERT(wire != WireId());
const auto &pips = torc_info->wire_to_pips_downhill[wire.index];
range.b.cursor = pips.data();
range.e.cursor = range.b.cursor + pips.size();
return range;
}
PipRange getPipsUphill(WireId wire) const
{
PipRange range;
// NPNR_ASSERT(wire != WireId());
// const auto &pips = torc_info->wire_to_pips_uphill[wire.index];
// range.b.cursor = pips.data();
// range.e.cursor = range.b.cursor + pips.size();
return range;
}
PipRange getWireAliases(WireId wire) const
{
PipRange range;
NPNR_ASSERT(wire != WireId());
range.b.cursor = nullptr;
range.e.cursor = nullptr;
return range;
}
BelId getPackagePinBel(const std::string &pin) const;
std::string getBelPackagePin(BelId bel) const;
// -------------------------------------------------
GroupId getGroupByName(IdString name) const;
IdString getGroupName(GroupId group) const;
std::vector<GroupId> getGroups() const;
std::vector<BelId> getGroupBels(GroupId group) const;
std::vector<WireId> getGroupWires(GroupId group) const;
std::vector<PipId> getGroupPips(GroupId group) const;
std::vector<GroupId> getGroupGroups(GroupId group) const;
// -------------------------------------------------
delay_t estimateDelay(WireId src, WireId dst) const;
delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const;
delay_t getDelayEpsilon() const { return 20; }
delay_t getRipupDelayPenalty() const { return 200; }
float getDelayNS(delay_t v) const { return v * 0.001; }
DelayInfo getDelayFromNS(float ns) const
{
DelayInfo del;
del.delay = delay_t(ns * 1000);
return del;
}
uint32_t getDelayChecksum(delay_t v) const { return v; }
bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const;
// -------------------------------------------------
bool pack();
bool place();
bool route();
// -------------------------------------------------
std::vector<GraphicElement> getDecalGraphics(DecalId decal) const;
DecalXY getBelDecal(BelId bel) const;
DecalXY getWireDecal(WireId wire) const;
DecalXY getPipDecal(PipId pip) const;
DecalXY getGroupDecal(GroupId group) const;
// -------------------------------------------------
// Get the delay through a cell from one port to another, returning false
// if no path exists
bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const;
// Get the port class, also setting clockDomain if applicable
TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const;
// Get the TimingClockingInfo of a port
TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const;
// Return true if a port is a net
bool isGlobalNet(const NetInfo *net) const;
// -------------------------------------------------
// Perform placement validity checks, returning false on failure (all
// implemented in arch_place.cc)
// Whether or not a given cell can be placed at a given Bel
// This is not intended for Bel type checks, but finer-grained constraints
// such as conflicting set/reset signals, etc
bool isValidBelForCell(CellInfo *cell, BelId bel) const;
// Return true whether all Bels at a given location are valid
bool isBelLocationValid(BelId bel) const;
// Helper function for above
bool logicCellsCompatible(const CellInfo **it, const size_t size) const;
// -------------------------------------------------
// Assign architecure-specific arguments to nets and cells, which must be
// called between packing or further
// netlist modifications, and validity checks
void assignArchInfo();
void assignCellInfo(CellInfo *cell);
// -------------------------------------------------
BelPin getIOBSharingPLLPin(BelId pll, IdString pll_pin) const
{
auto wire = getBelPinWire(pll, pll_pin);
for (auto src_bel : getWireBelPins(wire)) {
if (getBelType(src_bel.bel) == id_SB_IO && src_bel.pin == id_D_IN_0) {
return src_bel;
}
}
NPNR_ASSERT_FALSE("Expected PLL pin to share an output with an SB_IO D_IN_{0,1}");
}
float placer_constraintWeight = 10;
};
NEXTPNR_NAMESPACE_END

77
xc7/arch_place.cc Normal file
View File

@ -0,0 +1,77 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
*
* 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.
*
*/
#include "cells.h"
#include "nextpnr.h"
#include "util.h"
#include <boost/range/iterator_range.hpp>
NEXTPNR_NAMESPACE_BEGIN
bool Arch::logicCellsCompatible(const CellInfo **it, const size_t size) const
{
// TODO: Check clock, clock-enable, and set-reset compatiility
return true;
}
bool Arch::isBelLocationValid(BelId bel) const
{
if (getBelType(bel) == id("XC7_LC")) {
std::array<const CellInfo *, 4> bel_cells;
size_t num_cells = 0;
Loc bel_loc = getBelLocation(bel);
for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
CellInfo *ci_other = getBoundBelCell(bel_other);
if (ci_other != nullptr)
bel_cells[num_cells++] = ci_other;
}
return logicCellsCompatible(bel_cells.data(), num_cells);
} else {
CellInfo *ci = getBoundBelCell(bel);
if (ci == nullptr)
return true;
else
return isValidBelForCell(ci, bel);
}
}
bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
{
if (cell->type == id("XC7_LC")) {
std::array<const CellInfo *, 4> bel_cells;
size_t num_cells = 0;
Loc bel_loc = getBelLocation(bel);
for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
CellInfo *ci_other = getBoundBelCell(bel_other);
if (ci_other != nullptr && bel_other != bel)
bel_cells[num_cells++] = ci_other;
}
bel_cells[num_cells++] = cell;
return logicCellsCompatible(bel_cells.data(), num_cells);
} else {
return true;
}
}
NEXTPNR_NAMESPACE_END

144
xc7/arch_pybindings.cc Normal file
View File

@ -0,0 +1,144 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* 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 NO_PYTHON
#include "arch_pybindings.h"
#include "nextpnr.h"
#include "pybindings.h"
NEXTPNR_NAMESPACE_BEGIN
void arch_wrap_python()
{
using namespace PythonConversion;
class_<ArchArgs>("ArchArgs").def_readwrite("type", &ArchArgs::type);
class_<BelId>("BelId").def_readwrite("index", &BelId::index);
class_<WireId>("WireId").def_readwrite("index", &WireId::index);
class_<PipId>("PipId").def_readwrite("index", &PipId::index);
class_<BelPin>("BelPin").def_readwrite("bel", &BelPin::bel).def_readwrite("pin", &BelPin::pin);
auto arch_cls = class_<Arch, Arch *, bases<BaseCtx>, boost::noncopyable>("Arch", init<ArchArgs>());
auto ctx_cls = class_<Context, Context *, bases<Arch>, boost::noncopyable>("Context", no_init)
.def("checksum", &Context::checksum)
.def("pack", &Context::pack)
.def("place", &Context::place)
.def("route", &Context::route);
fn_wrapper_1a<Context, decltype(&Context::getBelType), &Context::getBelType, conv_to_str<IdString>,
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelType");
fn_wrapper_1a<Context, decltype(&Context::checkBelAvail), &Context::checkBelAvail, pass_through<bool>,
conv_from_str<BelId>>::def_wrap(ctx_cls, "checkBelAvail");
fn_wrapper_1a<Context, decltype(&Context::getBelChecksum), &Context::getBelChecksum, pass_through<uint32_t>,
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelChecksum");
fn_wrapper_3a_v<Context, decltype(&Context::bindBel), &Context::bindBel, conv_from_str<BelId>,
addr_and_unwrap<CellInfo>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindBel");
fn_wrapper_1a_v<Context, decltype(&Context::unbindBel), &Context::unbindBel, conv_from_str<BelId>>::def_wrap(
ctx_cls, "unbindBel");
fn_wrapper_1a<Context, decltype(&Context::getBoundBelCell), &Context::getBoundBelCell, deref_and_wrap<CellInfo>,
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBoundBelCell");
fn_wrapper_1a<Context, decltype(&Context::getConflictingBelCell), &Context::getConflictingBelCell,
deref_and_wrap<CellInfo>, conv_from_str<BelId>>::def_wrap(ctx_cls, "getConflictingBelCell");
fn_wrapper_0a<Context, decltype(&Context::getBels), &Context::getBels, wrap_context<BelRange>>::def_wrap(ctx_cls,
"getBels");
fn_wrapper_2a<Context, decltype(&Context::getBelPinWire), &Context::getBelPinWire, conv_to_str<WireId>,
conv_from_str<BelId>, conv_from_str<IdString>>::def_wrap(ctx_cls, "getBelPinWire");
fn_wrapper_1a<Context, decltype(&Context::getWireBelPins), &Context::getWireBelPins, wrap_context<BelPinRange>,
conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireBelPins");
fn_wrapper_1a<Context, decltype(&Context::getWireChecksum), &Context::getWireChecksum, pass_through<uint32_t>,
conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireChecksum");
fn_wrapper_3a_v<Context, decltype(&Context::bindWire), &Context::bindWire, conv_from_str<WireId>,
addr_and_unwrap<NetInfo>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindWire");
fn_wrapper_1a_v<Context, decltype(&Context::unbindWire), &Context::unbindWire, conv_from_str<WireId>>::def_wrap(
ctx_cls, "unbindWire");
fn_wrapper_1a<Context, decltype(&Context::checkWireAvail), &Context::checkWireAvail, pass_through<bool>,
conv_from_str<WireId>>::def_wrap(ctx_cls, "checkWireAvail");
fn_wrapper_1a<Context, decltype(&Context::getBoundWireNet), &Context::getBoundWireNet, deref_and_wrap<NetInfo>,
conv_from_str<WireId>>::def_wrap(ctx_cls, "getBoundWireNet");
fn_wrapper_1a<Context, decltype(&Context::getConflictingWireNet), &Context::getConflictingWireNet,
deref_and_wrap<NetInfo>, conv_from_str<WireId>>::def_wrap(ctx_cls, "getConflictingWireNet");
fn_wrapper_0a<Context, decltype(&Context::getWires), &Context::getWires, wrap_context<WireRange>>::def_wrap(
ctx_cls, "getWires");
fn_wrapper_0a<Context, decltype(&Context::getPips), &Context::getPips, wrap_context<AllPipRange>>::def_wrap(
ctx_cls, "getPips");
fn_wrapper_1a<Context, decltype(&Context::getPipChecksum), &Context::getPipChecksum, pass_through<uint32_t>,
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipChecksum");
fn_wrapper_3a_v<Context, decltype(&Context::bindPip), &Context::bindPip, conv_from_str<PipId>,
addr_and_unwrap<NetInfo>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindPip");
fn_wrapper_1a_v<Context, decltype(&Context::unbindPip), &Context::unbindPip, conv_from_str<PipId>>::def_wrap(
ctx_cls, "unbindPip");
fn_wrapper_1a<Context, decltype(&Context::checkPipAvail), &Context::checkPipAvail, pass_through<bool>,
conv_from_str<PipId>>::def_wrap(ctx_cls, "checkPipAvail");
fn_wrapper_1a<Context, decltype(&Context::getBoundPipNet), &Context::getBoundPipNet, deref_and_wrap<NetInfo>,
conv_from_str<PipId>>::def_wrap(ctx_cls, "getBoundPipNet");
fn_wrapper_1a<Context, decltype(&Context::getConflictingPipNet), &Context::getConflictingPipNet,
deref_and_wrap<NetInfo>, conv_from_str<PipId>>::def_wrap(ctx_cls, "getConflictingPipNet");
fn_wrapper_1a<Context, decltype(&Context::getPipsDownhill), &Context::getPipsDownhill, wrap_context<PipRange>,
conv_from_str<WireId>>::def_wrap(ctx_cls, "getPipsDownhill");
fn_wrapper_1a<Context, decltype(&Context::getPipsUphill), &Context::getPipsUphill, wrap_context<PipRange>,
conv_from_str<WireId>>::def_wrap(ctx_cls, "getPipsUphill");
fn_wrapper_1a<Context, decltype(&Context::getWireAliases), &Context::getWireAliases, wrap_context<PipRange>,
conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireAliases");
fn_wrapper_1a<Context, decltype(&Context::getPipSrcWire), &Context::getPipSrcWire, conv_to_str<WireId>,
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipSrcWire");
fn_wrapper_1a<Context, decltype(&Context::getPipDstWire), &Context::getPipDstWire, conv_to_str<WireId>,
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipDstWire");
fn_wrapper_1a<Context, decltype(&Context::getPipDelay), &Context::getPipDelay, pass_through<DelayInfo>,
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipDelay");
fn_wrapper_1a<Context, decltype(&Context::getPackagePinBel), &Context::getPackagePinBel, conv_to_str<BelId>,
pass_through<std::string>>::def_wrap(ctx_cls, "getPackagePinBel");
fn_wrapper_1a<Context, decltype(&Context::getBelPackagePin), &Context::getBelPackagePin, pass_through<std::string>,
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelPackagePin");
fn_wrapper_0a<Context, decltype(&Context::getChipName), &Context::getChipName, pass_through<std::string>>::def_wrap(
ctx_cls, "getChipName");
fn_wrapper_0a<Context, decltype(&Context::archId), &Context::archId, conv_to_str<IdString>>::def_wrap(ctx_cls,
"archId");
typedef std::unordered_map<IdString, std::unique_ptr<CellInfo>> CellMap;
typedef std::unordered_map<IdString, std::unique_ptr<NetInfo>> NetMap;
readonly_wrapper<Context, decltype(&Context::cells), &Context::cells, wrap_context<CellMap &>>::def_wrap(ctx_cls,
"cells");
readonly_wrapper<Context, decltype(&Context::nets), &Context::nets, wrap_context<NetMap &>>::def_wrap(ctx_cls,
"nets");
WRAP_RANGE(Bel, conv_to_str<BelId>);
WRAP_RANGE(Wire, conv_to_str<WireId>);
WRAP_RANGE(AllPip, conv_to_str<PipId>);
WRAP_RANGE(Pip, conv_to_str<PipId>);
WRAP_MAP_UPTR(CellMap, "IdCellMap");
WRAP_MAP_UPTR(NetMap, "IdNetMap");
}
NEXTPNR_NAMESPACE_END
#endif // NO_PYTHON

69
xc7/arch_pybindings.h Normal file
View File

@ -0,0 +1,69 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* 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 ARCH_PYBINDINGS_H
#define ARCH_PYBINDINGS_H
#ifndef NO_PYTHON
#include "nextpnr.h"
#include "pybindings.h"
#include "pywrappers.h"
NEXTPNR_NAMESPACE_BEGIN
namespace PythonConversion {
template <> struct string_converter<BelId>
{
BelId from_str(Context *ctx, std::string name) { return ctx->getBelByName(ctx->id(name)); }
std::string to_str(Context *ctx, BelId id)
{
if (id == BelId())
throw bad_wrap();
return ctx->getBelName(id).str(ctx);
}
};
template <> struct string_converter<WireId>
{
WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(ctx->id(name)); }
std::string to_str(Context *ctx, WireId id) { return ctx->getWireName(id).str(ctx); }
};
template <> struct string_converter<const WireId>
{
WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(ctx->id(name)); }
std::string to_str(Context *ctx, WireId id) { return ctx->getWireName(id).str(ctx); }
};
template <> struct string_converter<PipId>
{
PipId from_str(Context *ctx, std::string name) { return ctx->getPipByName(ctx->id(name)); }
std::string to_str(Context *ctx, PipId id) { return ctx->getPipName(id).str(ctx); }
};
} // namespace PythonConversion
NEXTPNR_NAMESPACE_END
#endif
#endif

190
xc7/archdefs.h Normal file
View File

@ -0,0 +1,190 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
*
* 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 NEXTPNR_H
#error Include "archdefs.h" via "nextpnr.h" only.
#endif
#include "torc/Architecture.hpp"
using namespace torc::architecture;
using namespace torc::architecture::xilinx;
NEXTPNR_NAMESPACE_BEGIN
typedef int delay_t;
struct DelayInfo
{
delay_t delay = 0;
delay_t minRaiseDelay() const { return delay; }
delay_t maxRaiseDelay() const { return delay; }
delay_t minFallDelay() const { return delay; }
delay_t maxFallDelay() const { return delay; }
delay_t minDelay() const { return delay; }
delay_t maxDelay() const { return delay; }
DelayInfo operator+(const DelayInfo &other) const
{
DelayInfo ret;
ret.delay = this->delay + other.delay;
return ret;
}
};
// -----------------------------------------------------------------------
enum ConstIds
{
ID_NONE
#define X(t) , ID_##t
#include "constids.inc"
#undef X
};
#define X(t) static constexpr auto id_##t = IdString(ID_##t);
#include "constids.inc"
#undef X
struct BelId
{
int32_t index = -1;
bool operator==(const BelId &other) const { return index == other.index; }
bool operator!=(const BelId &other) const { return index != other.index; }
bool operator<(const BelId &other) const { return index < other.index; }
};
struct WireId
{
int32_t index = -1;
bool operator==(const WireId &other) const { return index == other.index; }
bool operator!=(const WireId &other) const { return index != other.index; }
bool operator<(const WireId &other) const { return index < other.index; }
};
struct PipId
{
int32_t index = -1;
bool operator==(const PipId &other) const { return index == other.index; }
bool operator!=(const PipId &other) const { return index != other.index; }
bool operator<(const PipId &other) const { return index < other.index; }
};
struct GroupId
{
enum : int8_t
{
TYPE_NONE,
} type = TYPE_NONE;
int8_t x = 0, y = 0;
bool operator==(const GroupId &other) const { return (type == other.type) && (x == other.x) && (y == other.y); }
bool operator!=(const GroupId &other) const { return (type != other.type) || (x != other.x) || (y == other.y); }
};
struct DecalId
{
enum : int8_t
{
TYPE_NONE,
TYPE_BEL,
TYPE_WIRE,
TYPE_PIP,
TYPE_GROUP
} type = TYPE_NONE;
int32_t index = -1;
bool active = false;
bool operator==(const DecalId &other) const { return (type == other.type) && (index == other.index); }
bool operator!=(const DecalId &other) const { return (type != other.type) || (index != other.index); }
};
struct ArchNetInfo
{
bool is_global = false;
bool is_reset = false, is_enable = false;
};
struct NetInfo;
struct ArchCellInfo
{
IdString belType;
union
{
struct
{
bool dffEnable;
bool carryEnable;
bool negClk;
int inputCount;
const NetInfo *clk, *cen, *sr;
} lcInfo;
};
};
NEXTPNR_NAMESPACE_END
namespace std {
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX BelId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX BelId &bel) const noexcept { return hash<int>()(bel.index); }
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX WireId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX WireId &wire) const noexcept
{
return hash<int>()(wire.index);
}
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX PipId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX PipId &pip) const noexcept { return hash<int>()(pip.index); }
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX GroupId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX GroupId &group) const noexcept
{
std::size_t seed = 0;
boost::hash_combine(seed, hash<int>()(group.type));
boost::hash_combine(seed, hash<int>()(group.x));
boost::hash_combine(seed, hash<int>()(group.y));
return seed;
}
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX DecalId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX DecalId &decal) const noexcept
{
std::size_t seed = 0;
boost::hash_combine(seed, hash<int>()(decal.type));
boost::hash_combine(seed, hash<int>()(decal.index));
return seed;
}
};
} // namespace std

8
xc7/attosoc.pcf Normal file
View File

@ -0,0 +1,8 @@
COMP "led[0]" LOCATE = SITE "M14" LEVEL 1;
COMP "led[1]" LOCATE = SITE "M15" LEVEL 1;
COMP "led[2]" LOCATE = SITE "G14" LEVEL 1;
COMP "led[3]" LOCATE = SITE "D18" LEVEL 1;
COMP "clki" LOCATE = SITE "K17" LEVEL 1;
NET "pll.clkin1" PERIOD = 8 nS ;
PIN "clki_pin" = BEL "clki.PAD" PINNAME PAD;
PIN "clki_pin" CLOCK_DEDICATED_ROUTE = FALSE;

13
xc7/attosoc.sh Executable file
View File

@ -0,0 +1,13 @@
#!/bin/bash
set -ex
rm -f picorv32.v attosoc.v
wget https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v
wget https://raw.githubusercontent.com/SymbiFlow/prjtrellis/master/examples/picorv32_versa5g/attosoc.v
ln -sf firmware_slow.hex firmware.hex
yosys attosoc.ys
set +e
../nextpnr-xc7 --json attosoc.json --xdl attosoc.xdl --pcf attosoc.pcf --freq 125
set -e
xdl -xdl2ncd attosoc.xdl
bitgen -w attosoc.ncd -g UnconstrainedPins:Allow
trce attosoc.ncd -v 10

55
xc7/attosoc.ys Normal file
View File

@ -0,0 +1,55 @@
read_verilog attosoc_top.v
read_verilog attosoc.v
read_verilog picorv32.v
#synth_xilinx -top picorv32
#begin:
read_verilog -lib +/xilinx/cells_sim.v
read_verilog -lib +/xilinx/cells_xtra.v
# read_verilog -lib +/xilinx/brams_bb.v
# read_verilog -lib +/xilinx/drams_bb.v
hierarchy -check -top top
#flatten: (only if -flatten)
proc
flatten
#coarse:
synth -run coarse
#bram:
# memory_bram -rules +/xilinx/brams.txt
# techmap -map +/xilinx/brams_map.v
#
#dram:
# memory_bram -rules +/xilinx/drams.txt
# techmap -map +/xilinx/drams_map.v
fine:
opt -fast -full
memory_map
dffsr2dff
# dff2dffe
opt -full
techmap -map +/techmap.v #-map +/xilinx/arith_map.v
opt -fast
map_luts:
abc -luts 2:2,3,6:5 #,10,20 [-dff]
clean
map_cells:
techmap -map +/xilinx/cells_map.v
dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT
clean
check:
hierarchy -check
stat
check -noinit
#edif: (only if -edif)
# write_edif <file-name>
write_json attosoc.json

16
xc7/attosoc_sim.sh Executable file
View File

@ -0,0 +1,16 @@
#!/bin/bash
set -ex
rm -f picorv32.v attosoc.v
wget https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v
wget https://raw.githubusercontent.com/SymbiFlow/prjtrellis/master/examples/picorv32_versa5g/attosoc.v
ln -sf firmware_fast.hex firmware.hex
yosys attosoc.ys
set +e
../nextpnr-xc7 --json attosoc.json --xdl attosoc.xdl --pcf attosoc.pcf --freq 125
set -e
xdl -xdl2ncd attosoc.xdl
#bitgen -w attosoc.ncd -g UnconstrainedPins:Allow
trce attosoc.ncd -v 10
netgen -sim -ofmt vhdl attosoc.ncd -w attosoc_pnr.vhd
ghdl -c -fexplicit --no-vital-checks --ieee=synopsys -Pxilinx-ise attosoc_tb.vhd attosoc_pnr.vhd -r testbench

25
xc7/attosoc_tb.vhd Normal file
View File

@ -0,0 +1,25 @@
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity testbench is
end entity;
architecture rtl of testbench is
signal clk : STD_LOGIC;
signal led : STD_LOGIC_VECTOR(3 downto 0);
begin
process begin
clk <= '0';
wait for 4 ns;
clk <= '1';
wait for 4 ns;
end process;
uut: entity work.name port map(clki_PAD_PAD => clk, led_0_OUTBUF_OUT => led(0), led_1_OUTBUF_OUT => led(1), led_2_OUTBUF_OUT => led(2), led_3_OUTBUF_OUT => led(3));
process
begin
report "led = " & std_logic'image(led(3)) & std_logic'image(led(2)) & std_logic'image(led(1)) & std_logic'image(led(0));
wait on led;
end process;
end rtl;

24
xc7/attosoc_top.v Normal file
View File

@ -0,0 +1,24 @@
module top (
input clki,
output [3:0] led
);
(* keep *)
wire led_unused;
wire clk;
BUFGCTRL clk_gb (
.I0(clki),
.CE0(1'b1),
.CE1(1'b0),
.S0(1'b1),
.S1(1'b0),
.IGNORE0(1'b0),
.IGNORE1(1'b0),
.O(clk)
);
attosoc soc(.clk(clk), .led({led_unused, led}));
endmodule

9
xc7/blinky.pcf Normal file
View File

@ -0,0 +1,9 @@
COMP "led0" LOCATE = SITE "M14" LEVEL 1;
COMP "led1" LOCATE = SITE "M15" LEVEL 1;
COMP "led2" LOCATE = SITE "G14" LEVEL 1;
COMP "led3" LOCATE = SITE "D18" LEVEL 1;
COMP "clki" LOCATE = SITE "K17" LEVEL 1;
COMP "clk_gb" LOCATE = SITE "BUFGCTRL_X0Y31" LEVEL 1;
NET "clki" PERIOD = 8 nS ;
PIN "clki_pin" = BEL "clki.PAD" PINNAME PAD;
PIN "clki_pin" CLOCK_DEDICATED_ROUTE = FALSE;

15
xc7/blinky.proj Normal file
View File

@ -0,0 +1,15 @@
{
"project": {
"version": "1",
"name": "blinky",
"arch": {
"name": "ice40",
"type": "hx1k",
"package": "tq144"
},
"input": {
"json": "blinky.json",
"pcf": "blinky.pcf"
}
}
}

6
xc7/blinky.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/bash
set -ex
yosys blinky.ys
../nextpnr-xc7 --json blinky.json --pcf blinky.pcf --xdl blinky.xdl --freq 125
xdl -xdl2ncd blinky.xdl
bitgen -w blinky.ncd -g UnconstrainedPins:Allow

32
xc7/blinky.v Normal file
View File

@ -0,0 +1,32 @@
module blinky (
input clki,
output led0,
output led1,
output led2,
output led3
);
wire clk;
BUFGCTRL clk_gb (
.I0(clki),
.CE0(1'b1),
.CE1(1'b0),
.S0(1'b1),
.S1(1'b0),
.IGNORE0(1'b0),
.IGNORE1(1'b0),
.O(clk)
);
localparam BITS = 4;
parameter LOG2DELAY = 23;
reg [BITS+LOG2DELAY-1:0] counter = 0;
reg [BITS-1:0] outcnt;
always @(posedge clk) begin
counter <= counter + 1;
outcnt <= counter >> LOG2DELAY;
end
assign {led0, led1, led2, led3} = outcnt ^ (outcnt >> 1);
endmodule

53
xc7/blinky.ys Normal file
View File

@ -0,0 +1,53 @@
read_verilog blinky.v
#synth_xilinx -top blinky
#begin:
read_verilog -lib +/xilinx/cells_sim.v
read_verilog -lib +/xilinx/cells_xtra.v
# read_verilog -lib +/xilinx/brams_bb.v
# read_verilog -lib +/xilinx/drams_bb.v
hierarchy -check -top blinky
#flatten: (only if -flatten)
proc
flatten
#coarse:
synth -run coarse
#bram:
# memory_bram -rules +/xilinx/brams.txt
# techmap -map +/xilinx/brams_map.v
#
#dram:
# memory_bram -rules +/xilinx/drams.txt
# techmap -map +/xilinx/drams_map.v
fine:
opt -fast -full
memory_map
dffsr2dff
# dff2dffe
opt -full
techmap -map +/techmap.v #-map +/xilinx/arith_map.v
opt -fast
map_luts:
abc -luts 2:2,3,6:5 #,10,20 [-dff]
clean
map_cells:
techmap -map +/xilinx/cells_map.v
dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT
clean
check:
hierarchy -check
stat
check -noinit
#edif: (only if -edif)
# write_edif <file-name>
write_json blinky.json

8
xc7/blinky_sim.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/bash
set -ex
yosys blinky_sim.ys
../nextpnr-xc7 --json blinky.json --pcf blinky.pcf --xdl blinky.xdl --freq 125
xdl -xdl2ncd blinky.xdl
trce blinky.ncd -v 10
netgen -sim -ofmt vhdl blinky.ncd -w blinky_pnr.vhd
ghdl -c -fexplicit --no-vital-checks --ieee=synopsys -Pxilinx-ise blinky_tb.vhd blinky_pnr.vhd -r testbench

54
xc7/blinky_sim.ys Normal file
View File

@ -0,0 +1,54 @@
read_verilog blinky.v
chparam -set LOG2DELAY 0
#synth_xilinx -top blinky
#begin:
read_verilog -lib +/xilinx/cells_sim.v
read_verilog -lib +/xilinx/cells_xtra.v
# read_verilog -lib +/xilinx/brams_bb.v
# read_verilog -lib +/xilinx/drams_bb.v
hierarchy -check -top blinky
#flatten: (only if -flatten)
proc
flatten
#coarse:
synth -run coarse
#bram:
# memory_bram -rules +/xilinx/brams.txt
# techmap -map +/xilinx/brams_map.v
#
#dram:
# memory_bram -rules +/xilinx/drams.txt
# techmap -map +/xilinx/drams_map.v
fine:
opt -fast -full
memory_map
dffsr2dff
# dff2dffe
opt -full
techmap -map +/techmap.v #-map +/xilinx/arith_map.v
opt -fast
map_luts:
abc -luts 2:2,3,6:5 #,10,20 [-dff]
clean
map_cells:
techmap -map +/xilinx/cells_map.v
dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT
clean
check:
hierarchy -check
stat
check -noinit
#edif: (only if -edif)
# write_edif <file-name>
write_json blinky.json

25
xc7/blinky_tb.vhd Normal file
View File

@ -0,0 +1,25 @@
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity testbench is
end entity;
architecture rtl of testbench is
signal clk : STD_LOGIC;
signal led : STD_LOGIC_VECTOR(3 downto 0);
begin
process begin
clk <= '0';
wait for 4 ns;
clk <= '1';
wait for 4 ns;
end process;
uut: entity work.name port map(clki_PAD_PAD => clk, led0_OUTBUF_OUT => led(0), led1_OUTBUF_OUT => led(1), led2_OUTBUF_OUT => led(2), led3_OUTBUF_OUT => led(3));
process
begin
report std_logic'image(led(3)) & std_logic'image(led(2)) & std_logic'image(led(1)) & std_logic'image(led(0));
wait on led;
end process;
end rtl;

238
xc7/cells.cc Normal file
View File

@ -0,0 +1,238 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
*
* 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.
*
*/
#include "cells.h"
#include "design_utils.h"
#include "log.h"
#include "util.h"
NEXTPNR_NAMESPACE_BEGIN
void add_port(const Context *ctx, CellInfo *cell, std::string name, PortType dir)
{
IdString id = ctx->id(name);
cell->ports[id] = PortInfo{id, nullptr, dir};
}
std::unique_ptr<CellInfo> create_xc7_cell(Context *ctx, IdString type, std::string name)
{
static int auto_idx = 0;
std::unique_ptr<CellInfo> new_cell = std::unique_ptr<CellInfo>(new CellInfo());
if (name.empty()) {
new_cell->name = ctx->id("$nextpnr_" + type.str(ctx) + "_" + std::to_string(auto_idx++));
} else {
new_cell->name = ctx->id(name);
}
new_cell->type = type;
if (type == ctx->id("XC7_LC")) {
new_cell->type = id_SLICE_LUT6;
new_cell->params[ctx->id("INIT")] = "0";
new_cell->params[ctx->id("NEG_CLK")] = "0";
new_cell->params[ctx->id("CARRY_ENABLE")] = "0";
new_cell->params[ctx->id("DFF_ENABLE")] = "0";
new_cell->params[ctx->id("CIN_CONST")] = "0";
new_cell->params[ctx->id("CIN_SET")] = "0";
add_port(ctx, new_cell.get(), "I1", PORT_IN);
add_port(ctx, new_cell.get(), "I2", PORT_IN);
add_port(ctx, new_cell.get(), "I3", PORT_IN);
add_port(ctx, new_cell.get(), "I4", PORT_IN);
add_port(ctx, new_cell.get(), "I5", PORT_IN);
add_port(ctx, new_cell.get(), "I6", PORT_IN);
add_port(ctx, new_cell.get(), "CIN", PORT_IN);
add_port(ctx, new_cell.get(), "CLK", PORT_IN);
add_port(ctx, new_cell.get(), "CE", PORT_IN);
add_port(ctx, new_cell.get(), "SR", PORT_IN);
add_port(ctx, new_cell.get(), "O", PORT_OUT);
add_port(ctx, new_cell.get(), "OQ", PORT_OUT);
add_port(ctx, new_cell.get(), "OMUX", PORT_OUT);
add_port(ctx, new_cell.get(), "COUT", PORT_OUT);
} else if (type == ctx->id("IOBUF")) {
if (ctx->args.type == ArchArgs::Z020)
new_cell->type = id_IOB33;
else
new_cell->type = id_IOB18;
add_port(ctx, new_cell.get(), "I", PORT_OUT);
add_port(ctx, new_cell.get(), "O", PORT_IN);
} else if (type == id_BUFGCTRL) {
add_port(ctx, new_cell.get(), "I0", PORT_IN);
add_port(ctx, new_cell.get(), "O", PORT_OUT);
} else {
log_error("unable to create XC7 cell of type %s\n", type.c_str(ctx));
}
return new_cell;
}
void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff)
{
lc->params[ctx->id("INIT")] = lut->params[ctx->id("INIT")];
int i = 6;
if (get_net_or_empty(lut, id_I5))
replace_port(lut, id_I5, lc, ctx->id("I" + std::to_string(i--)));
if (get_net_or_empty(lut, id_I4))
replace_port(lut, id_I4, lc, ctx->id("I" + std::to_string(i--)));
if (get_net_or_empty(lut, id_I3))
replace_port(lut, id_I3, lc, ctx->id("I" + std::to_string(i--)));
if (get_net_or_empty(lut, id_I2))
replace_port(lut, id_I2, lc, ctx->id("I" + std::to_string(i--)));
if (get_net_or_empty(lut, id_I1))
replace_port(lut, id_I1, lc, ctx->id("I" + std::to_string(i--)));
replace_port(lut, ctx->id("I0"), lc, ctx->id("I" + std::to_string(i--)));
if (no_dff) {
replace_port(lut, id_O, lc, id_O);
lc->params[ctx->id("DFF_ENABLE")] = "0";
}
lc->params[ctx->id("LUT_NAME")] = lut->name.str(ctx);
}
void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_lut)
{
lc->params[ctx->id("DFF_ENABLE")] = "1";
std::string config = dff->type.str(ctx).substr(2);
auto citer = config.begin();
replace_port(dff, ctx->id("C"), lc, id_CLK);
if (citer != config.end()) {
auto gnd_net = ctx->nets.at(ctx->id("$PACKER_GND_NET")).get();
if (*citer == 'S') {
citer++;
if (get_net_or_empty(dff, id_S) != gnd_net) {
lc->params[id_SR] = "SRHIGH";
replace_port(dff, id_S, lc, id_SR);
} else
disconnect_port(ctx, dff, id_S);
lc->params[ctx->id("SYNC_ATTR")] = "SYNC";
} else if (*citer == 'R') {
citer++;
if (get_net_or_empty(dff, id_R) != gnd_net) {
lc->params[id_SR] = "SRLOW";
replace_port(dff, id_R, lc, id_SR);
} else
disconnect_port(ctx, dff, id_R);
lc->params[ctx->id("SYNC_ATTR")] = "SYNC";
} else if (*citer == 'C') {
citer++;
if (get_net_or_empty(dff, id_CLR) != gnd_net) {
lc->params[id_SR] = "SRLOW";
replace_port(dff, id_CLR, lc, id_SR);
} else
disconnect_port(ctx, dff, id_CLR);
lc->params[ctx->id("SYNC_ATTR")] = "ASYNC";
} else {
NPNR_ASSERT(*citer == 'P');
citer++;
if (get_net_or_empty(dff, id_PRE) != gnd_net) {
lc->params[id_SR] = "SRHIGH";
replace_port(dff, id_PRE, lc, id_SR);
} else
disconnect_port(ctx, dff, id_PRE);
lc->params[ctx->id("SYNC_ATTR")] = "ASYNC";
}
}
if (citer != config.end() && *citer == 'E') {
auto vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC_NET")).get();
++citer;
if (get_net_or_empty(dff, ctx->id("CE")) != vcc_net)
replace_port(dff, ctx->id("CE"), lc, ctx->id("CE"));
else
disconnect_port(ctx, dff, ctx->id("CE"));
}
NPNR_ASSERT(citer == config.end());
if (pass_thru_lut) {
lc->params[ctx->id("INIT")] = "2";
replace_port(dff, ctx->id("D"), lc, id_I1);
}
replace_port(dff, ctx->id("Q"), lc, id_OQ);
auto it = dff->params.find(ctx->id("INIT"));
if (it != dff->params.end())
lc->params[ctx->id("FFINIT")] = it->second == "1" ? "INIT1" : "INIT0";
}
void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio)
{
if (nxio->type == ctx->id("$nextpnr_ibuf")) {
sbio->params[ctx->id("PIN_TYPE")] = "1";
auto pu_attr = nxio->attrs.find(ctx->id("PULLUP"));
if (pu_attr != nxio->attrs.end())
sbio->params[ctx->id("PULLUP")] = pu_attr->second;
replace_port(nxio, id_O, sbio, id_I);
} else if (nxio->type == ctx->id("$nextpnr_obuf")) {
sbio->params[ctx->id("PIN_TYPE")] = "25";
replace_port(nxio, id_I, sbio, id_O);
} else if (nxio->type == ctx->id("$nextpnr_iobuf")) {
// N.B. tristate will be dealt with below
sbio->params[ctx->id("PIN_TYPE")] = "25";
replace_port(nxio, id_I, sbio, id_O);
replace_port(nxio, id_O, sbio, id_I);
} else {
NPNR_ASSERT(false);
}
NetInfo *donet = sbio->ports.at(id_O).net;
CellInfo *tbuf = net_driven_by(
ctx, donet, [](const Context *ctx, const CellInfo *cell) { return cell->type == ctx->id("$_TBUF_"); },
ctx->id("Y"));
if (tbuf) {
sbio->params[ctx->id("PIN_TYPE")] = "41";
replace_port(tbuf, ctx->id("A"), sbio, id_O);
replace_port(tbuf, ctx->id("E"), sbio, ctx->id("OUTPUT_ENABLE"));
ctx->nets.erase(donet->name);
if (!donet->users.empty())
log_error("unsupported tristate IO pattern for IO buffer '%s', "
"instantiate SB_IO manually to ensure correct behaviour\n",
nxio->name.c_str(ctx));
ctx->cells.erase(tbuf->name);
}
}
bool is_clock_port(const BaseCtx *ctx, const PortRef &port)
{
if (port.cell == nullptr)
return false;
NPNR_ASSERT("TODO");
return false;
}
bool is_reset_port(const BaseCtx *ctx, const PortRef &port)
{
if (port.cell == nullptr)
return false;
NPNR_ASSERT("TODO");
return false;
}
bool is_enable_port(const BaseCtx *ctx, const PortRef &port)
{
if (port.cell == nullptr)
return false;
NPNR_ASSERT("TODO");
return false;
}
NEXTPNR_NAMESPACE_END

110
xc7/cells.h Normal file
View File

@ -0,0 +1,110 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* 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.
*
*/
#include "nextpnr.h"
#ifndef ICE40_CELLS_H
#define ICE40_CELLS_H
NEXTPNR_NAMESPACE_BEGIN
// Create a standard xc7 cell and return it
// Name will be automatically assigned if not specified
std::unique_ptr<CellInfo> create_xc7_cell(Context *ctx, IdString type, std::string name = "");
// Return true if a cell is a LUT
inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell)
{
return cell->type == id_LUT1 || cell->type == id_LUT2 || cell->type == id_LUT3 || cell->type == id_LUT4 ||
cell->type == id_LUT5 || cell->type == id_LUT6;
}
// Return true if a cell is a flipflop
inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell)
{
return cell->type == id_FDRE || cell->type == id_FDSE || cell->type == id_FDCE || cell->type == id_FDPE;
}
inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_CARRY"); }
inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("XC7_LC"); }
// Return true if a cell is a SB_IO
inline bool is_sb_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_IO"); }
// Return true if a cell is a global buffer
inline bool is_gbuf(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_BUFGCTRL; }
// Return true if a cell is a RAM
inline bool is_ram(const BaseCtx *ctx, const CellInfo *cell)
{
return cell->type == ctx->id("SB_RAM40_4K") || cell->type == ctx->id("SB_RAM40_4KNR") ||
cell->type == ctx->id("SB_RAM40_4KNW") || cell->type == ctx->id("SB_RAM40_4KNRNW");
}
inline bool is_sb_lfosc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_LFOSC"); }
inline bool is_sb_hfosc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_HFOSC"); }
inline bool is_sb_spram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_SPRAM256KA"); }
inline bool is_sb_mac16(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_MAC16"); }
inline bool is_sb_pll40(const BaseCtx *ctx, const CellInfo *cell)
{
return cell->type == ctx->id("SB_PLL40_PAD") || cell->type == ctx->id("SB_PLL40_2_PAD") ||
cell->type == ctx->id("SB_PLL40_2F_PAD") || cell->type == ctx->id("SB_PLL40_CORE") ||
cell->type == ctx->id("SB_PLL40_2F_CORE");
}
inline bool is_sb_pll40_pad(const BaseCtx *ctx, const CellInfo *cell)
{
return cell->type == ctx->id("SB_PLL40_PAD") || cell->type == ctx->id("SB_PLL40_2_PAD") ||
cell->type == ctx->id("SB_PLL40_2F_PAD");
}
uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell);
// Convert a SB_LUT primitive to (part of) an ICESTORM_LC, swapping ports
// as needed. Set no_dff if a DFF is not being used, so that the output
// can be reconnected
void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff = true);
// Convert a SB_DFFx primitive to (part of) an ICESTORM_LC, setting parameters
// and reconnecting signals as necessary. If pass_thru_lut is True, the LUT will
// be configured as pass through and D connected to I0, otherwise D will be
// ignored
void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_lut = false);
// Convert a nextpnr IO buffer to a SB_IO
void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio);
// Return true if a port is a clock port
bool is_clock_port(const BaseCtx *ctx, const PortRef &port);
// Return true if a port is a reset port
bool is_reset_port(const BaseCtx *ctx, const PortRef &port);
// Return true if a port is a clock enable port
bool is_enable_port(const BaseCtx *ctx, const PortRef &port);
NEXTPNR_NAMESPACE_END
#endif

288
xc7/chains.cc Normal file
View File

@ -0,0 +1,288 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* 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.
*
*/
#include "chains.h"
#include <algorithm>
#include <vector>
#include "cells.h"
#include "design_utils.h"
#include "log.h"
#include "place_common.h"
#include "util.h"
NEXTPNR_NAMESPACE_BEGIN
struct CellChain
{
std::vector<CellInfo *> cells;
};
// Generic chain finder
template <typename F1, typename F2, typename F3>
std::vector<CellChain> find_chains(const Context *ctx, F1 cell_type_predicate, F2 get_previous, F3 get_next,
size_t min_length = 2)
{
std::set<IdString> chained;
std::vector<CellChain> chains;
for (auto cell : sorted(ctx->cells)) {
if (chained.find(cell.first) != chained.end())
continue;
CellInfo *ci = cell.second;
if (cell_type_predicate(ctx, ci)) {
CellInfo *start = ci;
CellInfo *prev_start = ci;
while (prev_start != nullptr) {
start = prev_start;
prev_start = get_previous(ctx, start);
}
CellChain chain;
CellInfo *end = start;
while (end != nullptr) {
chain.cells.push_back(end);
end = get_next(ctx, end);
}
if (chain.cells.size() >= min_length) {
chains.push_back(chain);
for (auto c : chain.cells)
chained.insert(c->name);
}
}
}
return chains;
}
class ChainConstrainer
{
private:
Context *ctx;
// Split a carry chain into multiple legal chains
std::vector<CellChain> split_carry_chain(CellChain &carryc)
{
bool start_of_chain = true;
std::vector<CellChain> chains;
std::vector<const CellInfo *> tile;
const int max_length = (torc_info->height - 2) * 8 - 2;
auto curr_cell = carryc.cells.begin();
while (curr_cell != carryc.cells.end()) {
CellInfo *cell = *curr_cell;
if (tile.size() >= 8) {
tile.clear();
}
if (start_of_chain) {
tile.clear();
chains.emplace_back();
start_of_chain = false;
if (cell->ports.at(ctx->id("CIN")).net) {
// CIN is not constant and not part of a chain. Must feed in from fabric
CellInfo *feedin = make_carry_feed_in(cell, cell->ports.at(ctx->id("CIN")));
chains.back().cells.push_back(feedin);
tile.push_back(feedin);
}
}
tile.push_back(cell);
chains.back().cells.push_back(cell);
bool split_chain = (!ctx->logicCellsCompatible(tile.data(), tile.size())) ||
(int(chains.back().cells.size()) > max_length);
if (split_chain) {
CellInfo *passout = make_carry_pass_out(cell->ports.at(ctx->id("COUT")));
tile.pop_back();
chains.back().cells.back() = passout;
start_of_chain = true;
} else {
NetInfo *carry_net = cell->ports.at(ctx->id("COUT")).net;
bool at_end = (curr_cell == carryc.cells.end() - 1);
if (carry_net != nullptr && (carry_net->users.size() > 1 || at_end)) {
if (carry_net->users.size() > 2 ||
(net_only_drives(ctx, carry_net, is_lc, ctx->id("I3"), false) !=
net_only_drives(ctx, carry_net, is_lc, ctx->id("CIN"), false)) ||
(at_end && !net_only_drives(ctx, carry_net, is_lc, ctx->id("I3"), true))) {
CellInfo *passout = make_carry_pass_out(cell->ports.at(ctx->id("COUT")));
chains.back().cells.push_back(passout);
tile.push_back(passout);
start_of_chain = true;
}
}
++curr_cell;
}
}
return chains;
}
// Insert a logic cell to legalise a COUT->fabric connection
CellInfo *make_carry_pass_out(PortInfo &cout_port)
{
NPNR_ASSERT(cout_port.net != nullptr);
std::unique_ptr<CellInfo> lc = create_xc7_cell(ctx, ctx->id("ICESTORM_LC"));
lc->params[ctx->id("LUT_INIT")] = "65280"; // 0xff00: O = I3
lc->params[ctx->id("CARRY_ENABLE")] = "1";
lc->ports.at(ctx->id("O")).net = cout_port.net;
std::unique_ptr<NetInfo> co_i3_net(new NetInfo());
co_i3_net->name = ctx->id(lc->name.str(ctx) + "$I3");
co_i3_net->driver = cout_port.net->driver;
PortRef i3_r;
i3_r.port = ctx->id("I3");
i3_r.cell = lc.get();
co_i3_net->users.push_back(i3_r);
PortRef o_r;
o_r.port = ctx->id("O");
o_r.cell = lc.get();
cout_port.net->driver = o_r;
lc->ports.at(ctx->id("I3")).net = co_i3_net.get();
cout_port.net = co_i3_net.get();
IdString co_i3_name = co_i3_net->name;
NPNR_ASSERT(ctx->nets.find(co_i3_name) == ctx->nets.end());
ctx->nets[co_i3_name] = std::move(co_i3_net);
IdString name = lc->name;
ctx->assignCellInfo(lc.get());
ctx->cells[lc->name] = std::move(lc);
return ctx->cells[name].get();
}
// Insert a logic cell to legalise a CIN->fabric connection
CellInfo *make_carry_feed_in(CellInfo *cin_cell, PortInfo &cin_port)
{
NPNR_ASSERT(cin_port.net != nullptr);
std::unique_ptr<CellInfo> lc = create_xc7_cell(ctx, ctx->id("ICESTORM_LC"));
lc->params[ctx->id("CARRY_ENABLE")] = "1";
lc->params[ctx->id("CIN_CONST")] = "1";
lc->params[ctx->id("CIN_SET")] = "1";
lc->ports.at(ctx->id("I1")).net = cin_port.net;
cin_port.net->users.erase(std::remove_if(cin_port.net->users.begin(), cin_port.net->users.end(),
[cin_cell, cin_port](const PortRef &usr) {
return usr.cell == cin_cell && usr.port == cin_port.name;
}));
PortRef i1_ref;
i1_ref.cell = lc.get();
i1_ref.port = ctx->id("I1");
lc->ports.at(ctx->id("I1")).net->users.push_back(i1_ref);
std::unique_ptr<NetInfo> out_net(new NetInfo());
out_net->name = ctx->id(lc->name.str(ctx) + "$O");
PortRef drv_ref;
drv_ref.port = ctx->id("COUT");
drv_ref.cell = lc.get();
out_net->driver = drv_ref;
lc->ports.at(ctx->id("COUT")).net = out_net.get();
PortRef usr_ref;
usr_ref.port = cin_port.name;
usr_ref.cell = cin_cell;
out_net->users.push_back(usr_ref);
cin_cell->ports.at(cin_port.name).net = out_net.get();
IdString out_net_name = out_net->name;
NPNR_ASSERT(ctx->nets.find(out_net_name) == ctx->nets.end());
ctx->nets[out_net_name] = std::move(out_net);
IdString name = lc->name;
ctx->assignCellInfo(lc.get());
ctx->cells[lc->name] = std::move(lc);
return ctx->cells[name].get();
}
void process_carries()
{
std::vector<CellChain> carry_chains =
find_chains(ctx, [](const Context *ctx, const CellInfo *cell) { return is_lc(ctx, cell); },
[](const Context *ctx, const
CellInfo *cell) {
CellInfo *carry_prev =
net_driven_by(ctx, cell->ports.at(ctx->id("CIN")).net, is_lc, ctx->id("COUT"));
if (carry_prev != nullptr)
return carry_prev;
/*CellInfo *i3_prev = net_driven_by(ctx, cell->ports.at(ctx->id("I3")).net, is_lc,
ctx->id("COUT")); if (i3_prev != nullptr) return i3_prev;*/
return (CellInfo *)nullptr;
},
[](const Context *ctx, const CellInfo *cell) {
CellInfo *carry_next = net_only_drives(ctx, cell->ports.at(ctx->id("COUT")).net, is_lc,
ctx->id("CIN"), false);
if (carry_next != nullptr)
return carry_next;
/*CellInfo *i3_next =
net_only_drives(ctx, cell->ports.at(ctx->id("COUT")).net, is_lc, ctx->id("I3"),
false); if (i3_next != nullptr) return i3_next;*/
return (CellInfo *)nullptr;
});
std::unordered_set<IdString> chained;
for (auto &base_chain : carry_chains) {
for (auto c : base_chain.cells)
chained.insert(c->name);
}
// Any cells not in chains, but with carry enabled, must also be put in a single-carry chain
// for correct processing
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (chained.find(cell.first) == chained.end() && is_lc(ctx, ci) &&
bool_or_default(ci->params, ctx->id("CARRY_ENABLE"))) {
CellChain sChain;
sChain.cells.push_back(ci);
chained.insert(cell.first);
carry_chains.push_back(sChain);
}
}
std::vector<CellChain> all_chains;
// Chain splitting
for (auto &base_chain : carry_chains) {
if (ctx->verbose) {
log_info("Found carry chain: \n");
for (auto entry : base_chain.cells)
log_info(" %s\n", entry->name.c_str(ctx));
log_info("\n");
}
std::vector<CellChain> split_chains = split_carry_chain(base_chain);
for (auto &chain : split_chains) {
all_chains.push_back(chain);
}
}
// Actual chain placement
for (auto &chain : all_chains) {
if (ctx->verbose)
log_info("Placing carry chain starting at '%s'\n", chain.cells.front()->name.c_str(ctx));
// Place carry chain
chain.cells.at(0)->constr_abs_z = true;
chain.cells.at(0)->constr_z = 0;
for (int i = 1; i < int(chain.cells.size()); i++) {
chain.cells.at(i)->constr_x = 0;
chain.cells.at(i)->constr_y = (i / 8);
chain.cells.at(i)->constr_z = i % 8;
chain.cells.at(i)->constr_abs_z = true;
chain.cells.at(i)->constr_parent = chain.cells.at(0);
chain.cells.at(0)->constr_children.push_back(chain.cells.at(i));
}
}
}
public:
ChainConstrainer(Context *ctx) : ctx(ctx){};
void constrain_chains() { process_carries(); }
};
void constrain_chains(Context *ctx)
{
log_info("Constraining chains...\n");
ChainConstrainer(ctx).constrain_chains();
}
NEXTPNR_NAMESPACE_END

27
xc7/chains.h Normal file
View File

@ -0,0 +1,27 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* 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.
*
*/
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
// This finds chains, inserts LCs to legalise them as needed, and sets relative constraints as appropriate
void constrain_chains(Context *ctx);
NEXTPNR_NAMESPACE_END

467
xc7/constids.inc Normal file
View File

@ -0,0 +1,467 @@
// pin and port names
//X(I0)
X(I1)
X(I2)
X(I3)
X(I4)
X(I5)
X(I6)
X(O)
X(OQ)
X(OMUX)
X(CIN)
X(COUT)
X(CEN)
X(CLK)
X(SR)
X(S)
X(R)
X(PRE)
X(CLR)
X(MASK_0)
X(MASK_1)
X(MASK_2)
X(MASK_3)
X(MASK_4)
X(MASK_5)
X(MASK_6)
X(MASK_7)
X(MASK_8)
X(MASK_9)
X(MASK_10)
X(MASK_11)
X(MASK_12)
X(MASK_13)
X(MASK_14)
X(MASK_15)
X(RDATA_0)
X(RDATA_1)
X(RDATA_2)
X(RDATA_3)
X(RDATA_4)
X(RDATA_5)
X(RDATA_6)
X(RDATA_7)
X(RDATA_8)
X(RDATA_9)
X(RDATA_10)
X(RDATA_11)
X(RDATA_12)
X(RDATA_13)
X(RDATA_14)
X(RDATA_15)
X(WDATA_0)
X(WDATA_1)
X(WDATA_2)
X(WDATA_3)
X(WDATA_4)
X(WDATA_5)
X(WDATA_6)
X(WDATA_7)
X(WDATA_8)
X(WDATA_9)
X(WDATA_10)
X(WDATA_11)
X(WDATA_12)
X(WDATA_13)
X(WDATA_14)
X(WDATA_15)
X(WADDR_0)
X(WADDR_1)
X(WADDR_2)
X(WADDR_3)
X(WADDR_4)
X(WADDR_5)
X(WADDR_6)
X(WADDR_7)
X(WADDR_8)
X(WADDR_9)
X(WADDR_10)
X(RADDR_0)
X(RADDR_1)
X(RADDR_2)
X(RADDR_3)
X(RADDR_4)
X(RADDR_5)
X(RADDR_6)
X(RADDR_7)
X(RADDR_8)
X(RADDR_9)
X(RADDR_10)
X(WCLK)
X(WCLKE)
X(WE)
X(RCLK)
X(RCLKE)
X(RE)
X(PACKAGE_PIN)
X(LATCH_INPUT_VALUE)
X(CLOCK_ENABLE)
X(INPUT_CLK)
X(OUTPUT_CLK)
X(OUTPUT_ENABLE)
X(D_OUT_0)
X(D_OUT_1)
X(D_IN_0)
X(D_IN_1)
X(USER_SIGNAL_TO_GLOBAL_BUFFER)
X(GLOBAL_BUFFER_OUTPUT)
X(REFERENCECLK)
X(EXTFEEDBACK)
X(DYNAMICDELAY_0)
X(DYNAMICDELAY_1)
X(DYNAMICDELAY_2)
X(DYNAMICDELAY_3)
X(DYNAMICDELAY_4)
X(DYNAMICDELAY_5)
X(DYNAMICDELAY_6)
X(DYNAMICDELAY_7)
X(LOCK)
X(PLLOUT_A)
X(PLLOUT_B)
X(BYPASS)
X(RESETB)
X(LATCHINPUTVALUE)
X(SDO)
X(SDI)
X(SCLK)
X(BOOT)
X(S0)
X(S1)
X(ADDSUBBOT)
X(ADDSUBTOP)
X(AHOLD)
X(A_0)
X(A_1)
X(A_10)
X(A_11)
X(A_12)
X(A_13)
X(A_14)
X(A_15)
X(A_2)
X(A_3)
X(A_4)
X(A_5)
X(A_6)
X(A_7)
X(A_8)
X(A_9)
X(BHOLD)
X(B_0)
X(B_1)
X(B_10)
X(B_11)
X(B_12)
X(B_13)
X(B_14)
X(B_15)
X(B_2)
X(B_3)
X(B_4)
X(B_5)
X(B_6)
X(B_7)
X(B_8)
X(B_9)
X(CE)
X(CHOLD)
X(CI)
X(CO)
X(C_0)
X(C_1)
X(C_10)
X(C_11)
X(C_12)
X(C_13)
X(C_14)
X(C_15)
X(C_2)
X(C_3)
X(C_4)
X(C_5)
X(C_6)
X(C_7)
X(C_8)
X(C_9)
X(DHOLD)
X(D_0)
X(D_1)
X(D_10)
X(D_11)
X(D_12)
X(D_13)
X(D_14)
X(D_15)
X(D_2)
X(D_3)
X(D_4)
X(D_5)
X(D_6)
X(D_7)
X(D_8)
X(D_9)
X(IRSTBOT)
X(IRSTTOP)
X(OHOLDBOT)
X(OHOLDTOP)
X(OLOADBOT)
X(OLOADTOP)
X(ORSTBOT)
X(ORSTTOP)
X(O_0)
X(O_1)
X(O_10)
X(O_11)
X(O_12)
X(O_13)
X(O_14)
X(O_15)
X(O_16)
X(O_17)
X(O_18)
X(O_19)
X(O_2)
X(O_20)
X(O_21)
X(O_22)
X(O_23)
X(O_24)
X(O_25)
X(O_26)
X(O_27)
X(O_28)
X(O_29)
X(O_3)
X(O_30)
X(O_31)
X(O_4)
X(O_5)
X(O_6)
X(O_7)
X(O_8)
X(O_9)
X(CLKHF)
X(CLKHFEN)
X(CLKHFPU)
X(CLKHF_FABRIC)
X(TRIM0)
X(TRIM1)
X(TRIM2)
X(TRIM3)
X(TRIM4)
X(TRIM5)
X(TRIM6)
X(TRIM7)
X(TRIM8)
X(TRIM9)
X(CLKLF)
X(CLKLFEN)
X(CLKLFPU)
X(CLKLF_FABRIC)
X(I2CIRQ)
X(I2CWKUP)
X(SBACKO)
X(SBADRI0)
X(SBADRI1)
X(SBADRI2)
X(SBADRI3)
X(SBADRI4)
X(SBADRI5)
X(SBADRI6)
X(SBADRI7)
X(SBCLKI)
X(SBDATI0)
X(SBDATI1)
X(SBDATI2)
X(SBDATI3)
X(SBDATI4)
X(SBDATI5)
X(SBDATI6)
X(SBDATI7)
X(SBDATO0)
X(SBDATO1)
X(SBDATO2)
X(SBDATO3)
X(SBDATO4)
X(SBDATO5)
X(SBDATO6)
X(SBDATO7)
X(SBRWI)
X(SBSTBI)
X(SCLI)
X(SCLO)
X(SCLOE)
X(SDAI)
X(SDAO)
X(SDAOE)
X(MCSNO0)
X(MCSNO1)
X(MCSNO2)
X(MCSNO3)
X(MCSNOE0)
X(MCSNOE1)
X(MCSNOE2)
X(MCSNOE3)
X(MI)
X(MO)
X(MOE)
X(SCKI)
X(SCKO)
X(SCKOE)
X(SCSNI)
X(SI)
X(SO)
X(SOE)
X(SPIIRQ)
X(SPIWKUP)
X(PU_ENB)
X(WEAK_PU_ENB)
X(LEDDADDR0)
X(LEDDADDR1)
X(LEDDADDR2)
X(LEDDADDR3)
X(LEDDCLK)
X(LEDDCS)
X(LEDDDAT0)
X(LEDDDAT1)
X(LEDDDAT2)
X(LEDDDAT3)
X(LEDDDAT4)
X(LEDDDAT5)
X(LEDDDAT6)
X(LEDDDAT7)
X(LEDDDEN)
X(LEDDEXE)
X(LEDDON)
X(PWMOUT0)
X(PWMOUT1)
X(PWMOUT2)
X(CURREN)
X(RGB0PWM)
X(RGB1PWM)
X(RGB2PWM)
X(RGBLEDEN)
X(RGB0)
X(RGB1)
X(RGB2)
X(ADDRESS_0)
X(ADDRESS_1)
X(ADDRESS_10)
X(ADDRESS_11)
X(ADDRESS_12)
X(ADDRESS_13)
X(ADDRESS_2)
X(ADDRESS_3)
X(ADDRESS_4)
X(ADDRESS_5)
X(ADDRESS_6)
X(ADDRESS_7)
X(ADDRESS_8)
X(ADDRESS_9)
X(CHIPSELECT)
X(CLOCK)
X(DATAIN_0)
X(DATAIN_1)
X(DATAIN_10)
X(DATAIN_11)
X(DATAIN_12)
X(DATAIN_13)
X(DATAIN_14)
X(DATAIN_15)
X(DATAIN_2)
X(DATAIN_3)
X(DATAIN_4)
X(DATAIN_5)
X(DATAIN_6)
X(DATAIN_7)
X(DATAIN_8)
X(DATAIN_9)
X(DATAOUT_0)
X(DATAOUT_1)
X(DATAOUT_10)
X(DATAOUT_11)
X(DATAOUT_12)
X(DATAOUT_13)
X(DATAOUT_14)
X(DATAOUT_15)
X(DATAOUT_2)
X(DATAOUT_3)
X(DATAOUT_4)
X(DATAOUT_5)
X(DATAOUT_6)
X(DATAOUT_7)
X(DATAOUT_8)
X(DATAOUT_9)
X(MASKWREN_0)
X(MASKWREN_1)
X(MASKWREN_2)
X(MASKWREN_3)
X(POWEROFF)
X(SLEEP)
X(STANDBY)
X(WREN)
// cell and bel types
X(ICESTORM_LC)
X(ICESTORM_RAM)
X(SB_IO)
X(SB_GB)
X(ICESTORM_PLL)
X(SB_WARMBOOT)
X(ICESTORM_DSP)
X(ICESTORM_HFOSC)
X(ICESTORM_LFOSC)
X(SB_I2C)
X(SB_SPI)
X(IO_I3C)
X(SB_LEDDA_IP)
X(SB_RGBA_DRV)
X(ICESTORM_SPRAM)
// cell parameters
X(DFF_ENABLE)
X(CARRY_ENABLE)
X(NEG_CLK)
// XC7
X(I)
X(LUT1)
X(LUT2)
X(LUT3)
X(LUT4)
X(LUT5)
X(LUT6)
X(FDRE)
X(FDSE)
X(FDCE)
X(FDPE)
X(BUFGCTRL)
X(SLICE_LUT6)
X(IOB33)
X(IOB18)
X(PS7)
X(MMCME2_ADV)

84
xc7/delay.cc Normal file
View File

@ -0,0 +1,84 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
*
* 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.
*
*/
#include "nextpnr.h"
#include "router1.h"
NEXTPNR_NAMESPACE_BEGIN
#define NUM_FUZZ_ROUTES 100000
delay_t Arch::estimateDelay(WireId src, WireId dst) const
{
const auto &src_tw = torc_info->wire_to_tilewire[src.index];
const auto &src_loc = torc_info->tile_to_xy[src_tw.getTileIndex()];
const auto &dst_tw = torc_info->wire_to_tilewire[dst.index];
const auto &dst_loc = torc_info->tile_to_xy[dst_tw.getTileIndex()];
if (!torc_info->wire_is_global[src.index]) {
auto abs_delta_x = abs(dst_loc.first - src_loc.first);
auto abs_delta_y = abs(dst_loc.second - src_loc.second);
auto div_LH = std::div(abs_delta_x, 12);
auto div_LV = std::div(abs_delta_y, 18);
auto div_LVB = std::div(div_LV.rem, 12);
auto div_H6 = std::div(div_LH.rem, 6);
auto div_V6 = std::div(div_LVB.rem, 6);
auto div_H4 = std::div(div_H6.rem, 4);
auto div_V4 = std::div(div_V6.rem, 4);
auto div_H2 = std::div(div_H4.rem, 2);
auto div_V2 = std::div(div_V4.rem, 2);
auto num_H1 = div_H2.rem;
auto num_V1 = div_V2.rem;
return div_LH.quot * 360 + div_LVB.quot * 300 + div_LV.quot * 350 +
(div_H6.quot + div_H4.quot + div_V6.quot + div_V4.quot) * 210 + (div_H2.quot + div_V2.quot) * 170 +
(num_H1 + num_V1) * 150;
} else {
auto src_y = src_loc.second;
auto dst_y = dst_loc.second;
auto div_src_y = std::div(src_y, 52);
auto div_dst_y = std::div(dst_y, 52);
return abs(div_dst_y.quot - div_src_y.quot) * 52 + abs(div_dst_y.rem - div_src_y.rem);
}
}
delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
{
const auto &driver = net_info->driver;
auto driver_loc = getBelLocation(driver.cell->bel);
auto sink_loc = getBelLocation(sink.cell->bel);
auto abs_delta_x = abs(driver_loc.x - sink_loc.x);
auto abs_delta_y = abs(driver_loc.y - sink_loc.y);
auto div_LH = std::div(abs_delta_x, 12);
auto div_LV = std::div(abs_delta_y, 18);
auto div_LVB = std::div(div_LV.rem, 12);
auto div_H6 = std::div(div_LH.rem, 6);
auto div_V6 = std::div(div_LVB.rem, 6);
auto div_H4 = std::div(div_H6.rem, 4);
auto div_V4 = std::div(div_V6.rem, 4);
auto div_H2 = std::div(div_H4.rem, 2);
auto div_V2 = std::div(div_V4.rem, 2);
auto num_H1 = div_H2.rem;
auto num_V1 = div_V2.rem;
return div_LH.quot * 360 + div_LVB.quot * 300 + div_LV.quot * 350 +
(div_H6.quot + div_H4.quot + div_V6.quot + div_V4.quot) * 210 + (div_H2.quot + div_V2.quot) * 170 +
(num_H1 + num_V1) * 150;
}
NEXTPNR_NAMESPACE_END

108
xc7/family.cmake Normal file
View File

@ -0,0 +1,108 @@
if (NOT DEFINED TORC_ROOT)
# Adapted from https://cliutils.gitlab.io/modern-cmake/chapters/projects/submodule.html
find_package(Git QUIET)
if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.gitmodules")
# Update submodules as needed
option(GIT_SUBMODULE "Check submodules during build" ON)
if(GIT_SUBMODULE)
message(STATUS "Submodule update")
execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE GIT_SUBMOD_RESULT)
if(NOT GIT_SUBMOD_RESULT EQUAL "0")
message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules")
endif()
endif()
endif()
add_dependencies(nextpnr-${family} torc)
if (BUILD_TESTS)
add_dependencies(nextpnr-${family}-test torc)
endif()
add_custom_target(torc ALL
COMMAND $(MAKE) > /dev/null 2> /dev/null
COMMENT "Building torc (may take some time...)"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/torc/src)
set(TORC_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/torc)
endif()
find_package(Boost REQUIRED COMPONENTS regex ${boost_libs} ${boost_python_lib})
target_compile_definitions(nextpnr-${family} PRIVATE -DTORC_ROOT="${TORC_ROOT}")
target_include_directories(nextpnr-${family} PUBLIC ${TORC_ROOT}/src)
if (BUILD_TESTS)
target_compile_definitions(nextpnr-${family}-test PRIVATE -DTORC_ROOT="${TORC_ROOT}")
target_include_directories(nextpnr-${family}-test PUBLIC ${TORC_ROOT}/src)
endif()
if (BUILD_GUI)
target_include_directories(gui_${family} PUBLIC ${TORC_ROOT}/src)
endif()
set(TORC_OBJS
${TORC_ROOT}/src/torc/architecture/Arc.o
${TORC_ROOT}/src/torc/architecture/ArcUsage.o
${TORC_ROOT}/src/torc/architecture/Array.o
${TORC_ROOT}/src/torc/architecture/DDB.o
${TORC_ROOT}/src/torc/architecture/DDBConsoleStreams.o
${TORC_ROOT}/src/torc/architecture/DDBStreamHelper.o
${TORC_ROOT}/src/torc/architecture/DigestStream.o
${TORC_ROOT}/src/torc/architecture/ExtendedWireInfo.o
${TORC_ROOT}/src/torc/architecture/InstancePin.o
${TORC_ROOT}/src/torc/architecture/OutputStreamHelpers.o
${TORC_ROOT}/src/torc/architecture/Package.o
${TORC_ROOT}/src/torc/architecture/Pad.o
${TORC_ROOT}/src/torc/architecture/PrimitiveConn.o
${TORC_ROOT}/src/torc/architecture/PrimitiveDef.o
${TORC_ROOT}/src/torc/architecture/PrimitiveElement.o
${TORC_ROOT}/src/torc/architecture/PrimitiveElementPin.o
${TORC_ROOT}/src/torc/architecture/PrimitivePin.o
${TORC_ROOT}/src/torc/architecture/Segments.o
${TORC_ROOT}/src/torc/architecture/Site.o
${TORC_ROOT}/src/torc/architecture/Sites.o
${TORC_ROOT}/src/torc/architecture/Tiles.o
${TORC_ROOT}/src/torc/architecture/TileInfo.o
${TORC_ROOT}/src/torc/architecture/Tilewire.o
${TORC_ROOT}/src/torc/architecture/Versions.o
${TORC_ROOT}/src/torc/architecture/VprExporter.o
${TORC_ROOT}/src/torc/architecture/WireInfo.o
${TORC_ROOT}/src/torc/architecture/WireUsage.o
${TORC_ROOT}/src/torc/architecture/XdlImporter.o
${TORC_ROOT}/src/torc/architecture/XilinxDatabaseTypes.o
${TORC_ROOT}/src/torc/common/Annotated.o
${TORC_ROOT}/src/torc/common/DeviceDesignator.o
${TORC_ROOT}/src/torc/common/Devices.o
${TORC_ROOT}/src/torc/common/DirectoryTree.o
${TORC_ROOT}/src/torc/common/DottedVersion.o
${TORC_ROOT}/src/torc/common/NullOutputStream.o
${TORC_ROOT}/src/torc/externals/zlib/zfstream.o
z
${TORC_ROOT}/src/torc/physical/Circuit.o
${TORC_ROOT}/src/torc/physical/ConfigMap.o
${TORC_ROOT}/src/torc/physical/Config.o
${TORC_ROOT}/src/torc/physical/Design.o
${TORC_ROOT}/src/torc/physical/Factory.o
${TORC_ROOT}/src/torc/physical/Instance.o
${TORC_ROOT}/src/torc/physical/InstancePin.o
${TORC_ROOT}/src/torc/physical/InstanceReference.o
${TORC_ROOT}/src/torc/physical/Module.o
${TORC_ROOT}/src/torc/physical/ModuleTransformer.o
${TORC_ROOT}/src/torc/physical/Named.o
${TORC_ROOT}/src/torc/physical/Net.o
${TORC_ROOT}/src/torc/physical/OutputStreamHelpers.o
${TORC_ROOT}/src/torc/physical/Pip.o
${TORC_ROOT}/src/torc/physical/Port.o
${TORC_ROOT}/src/torc/physical/Progenitor.o
${TORC_ROOT}/src/torc/physical/Progeny.o
${TORC_ROOT}/src/torc/physical/Renamable.o
${TORC_ROOT}/src/torc/physical/Routethrough.o
${TORC_ROOT}/src/torc/physical/TilewirePlaceholder.o
${TORC_ROOT}/src/torc/physical/XdlExporter.o
)
target_link_libraries(nextpnr-${family} PRIVATE ${TORC_OBJS})
if (BUILD_TESTS)
target_link_libraries(nextpnr-${family}-test PRIVATE ${TORC_OBJS})
endif()

6
xc7/firmware_fast.hex Normal file
View File

@ -0,0 +1,6 @@
@00000000
13 04 20 00 B7 04 00 02 13 04 14 00 13 74 F4 0F
13 09 20 00 63 5E 89 00 13 05 04 00 93 05 09 00
EF 00 80 01 63 08 05 00 13 09 19 00 6F F0 9F FE
23 A0 84 00 6F F0 5F FD 93 02 10 00 33 05 B5 40
E3 5E 55 FE 67 80 00 00

8
xc7/firmware_slow.hex Normal file
View File

@ -0,0 +1,8 @@
@00000000
13 04 20 00 B7 04 00 02 93 09 00 10 13 04 14 00
63 44 34 01 13 04 20 00 13 09 20 00 63 5E 89 00
13 05 04 00 93 05 09 00 EF 00 C0 01 63 0A 05 00
13 09 19 00 6F F0 9F FE 23 A0 84 00 EF 00 80 01
6F F0 DF FC 93 02 10 00 33 05 B5 40 E3 5E 55 FE
67 80 00 00 B7 82 05 00 93 82 02 E4 93 82 F2 FF
E3 9E 02 FE 67 80 00 00

24
xc7/gfx.cc Normal file
View File

@ -0,0 +1,24 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
*
* 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.
*
*/
#include "gfx.h"
NEXTPNR_NAMESPACE_BEGIN
NEXTPNR_NAMESPACE_END

48
xc7/gfx.h Normal file
View File

@ -0,0 +1,48 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
*
* 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 GFX_H
#define GFX_H
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
const float main_swbox_x1 = 0.35;
const float main_swbox_x2 = 0.60;
const float main_swbox_y1 = 0.05;
const float main_swbox_y2 = 0.73;
const float local_swbox_x1 = 0.63;
const float local_swbox_x2 = 0.73;
const float local_swbox_y1 = 0.05;
const float local_swbox_y2 = 0.55;
const float lut_swbox_x1 = 0.76;
const float lut_swbox_x2 = 0.80;
const float logic_cell_x1 = 0.83;
const float logic_cell_x2 = 0.95;
const float logic_cell_y1 = 0.05;
const float logic_cell_y2 = 0.10;
const float logic_cell_pitch = 0.0625;
NEXTPNR_NAMESPACE_END
#endif // GFX_H

119
xc7/main.cc Normal file
View File

@ -0,0 +1,119 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
*
* 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.
*
*/
#ifdef MAIN_EXECUTABLE
#include <fstream>
#include "command.h"
#include "design_utils.h"
#include "jsonparse.h"
#include "log.h"
#include "pcf.h"
#include "timing.h"
#include "xdl.h"
USING_NEXTPNR_NAMESPACE
class Xc7CommandHandler : public CommandHandler
{
public:
Xc7CommandHandler(int argc, char **argv);
virtual ~Xc7CommandHandler(){};
std::unique_ptr<Context> createContext() override;
void setupArchContext(Context *ctx) override;
void validate() override;
void customAfterLoad(Context *ctx) override;
void customBitstream(Context *ctx) override;
protected:
po::options_description getArchOptions();
};
Xc7CommandHandler::Xc7CommandHandler(int argc, char **argv) : CommandHandler(argc, argv) {}
po::options_description Xc7CommandHandler::getArchOptions()
{
po::options_description specific("Architecture specific options");
specific.add_options()("z020", "set device type to xc7z020");
specific.add_options()("vx980", "set device type to xc7v980");
specific.add_options()("package", po::value<std::string>(), "set device package");
specific.add_options()("pcf", po::value<std::string>(), "PCF constraints file to ingest");
specific.add_options()("xdl", po::value<std::string>(), "XDL file to write");
// specific.add_options()("tmfuzz", "run path delay estimate fuzzer");
return specific;
}
void Xc7CommandHandler::validate()
{
conflicting_options(vm, "read", "json");
// if ((vm.count("lp384") + vm.count("lp1k") + vm.count("lp8k") + vm.count("hx1k") + vm.count("hx8k") +
// vm.count("up5k")) > 1)
// log_error("Only one device type can be set\n");
}
void Xc7CommandHandler::customAfterLoad(Context *ctx)
{
if (vm.count("pcf")) {
std::string filename = vm["pcf"].as<std::string>();
std::ifstream pcf(filename);
if (!apply_pcf(ctx, filename, pcf))
log_error("Loading PCF failed.\n");
}
}
void Xc7CommandHandler::customBitstream(Context *ctx)
{
if (vm.count("xdl")) {
std::string filename = vm["xdl"].as<std::string>();
std::ofstream f(filename);
write_xdl(ctx, f);
}
}
void Xc7CommandHandler::setupArchContext(Context *ctx) {}
std::unique_ptr<Context> Xc7CommandHandler::createContext()
{
if (vm.count("z020")) {
chipArgs.type = ArchArgs::Z020;
chipArgs.package = "clg400";
}
if (vm.count("vx980")) {
chipArgs.type = ArchArgs::VX980;
chipArgs.package = "ffg1926";
}
if (chipArgs.type == ArchArgs::NONE) {
chipArgs.type = ArchArgs::Z020;
chipArgs.package = "clg400";
}
if (vm.count("package"))
chipArgs.package = vm["package"].as<std::string>();
return std::unique_ptr<Context>(new Context(chipArgs));
}
int main(int argc, char *argv[])
{
Xc7CommandHandler handler(argc, argv);
return handler.exec();
}
#endif

728
xc7/pack.cc Normal file
View File

@ -0,0 +1,728 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
*
* 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.
*
*/
#include <algorithm>
#include <iterator>
#include <unordered_set>
#include "cells.h"
#include "chains.h"
#include "design_utils.h"
#include "log.h"
#include "util.h"
NEXTPNR_NAMESPACE_BEGIN
// Pack LUTs and LUT-FF pairs
static void pack_lut_lutffs(Context *ctx)
{
log_info("Packing LUT-FFs..\n");
std::unordered_set<IdString> packed_cells;
std::vector<std::unique_ptr<CellInfo>> new_cells;
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (ctx->verbose)
log_info("cell '%s' is of type '%s'\n", ci->name.c_str(ctx), ci->type.c_str(ctx));
if (is_lut(ctx, ci)) {
std::unique_ptr<CellInfo> packed = create_xc7_cell(ctx, ctx->id("XC7_LC"), ci->name.str(ctx) + "_LC");
std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(packed->attrs, packed->attrs.begin()));
packed_cells.insert(ci->name);
if (ctx->verbose)
log_info("packed cell %s into %s\n", ci->name.c_str(ctx), packed->name.c_str(ctx));
// See if we can pack into a DFF
// TODO: LUT cascade
NetInfo *o = ci->ports.at(ctx->id("O")).net;
CellInfo *dff = net_only_drives(ctx, o, is_ff, ctx->id("D"), true);
auto lut_bel = ci->attrs.find(ctx->id("BEL"));
bool packed_dff = false;
if (dff) {
if (ctx->verbose)
log_info("found attached dff %s\n", dff->name.c_str(ctx));
auto dff_bel = dff->attrs.find(ctx->id("BEL"));
if (lut_bel != ci->attrs.end() && dff_bel != dff->attrs.end() && lut_bel->second != dff_bel->second) {
// Locations don't match, can't pack
} else {
lut_to_lc(ctx, ci, packed.get(), false);
dff_to_lc(ctx, dff, packed.get(), false);
ctx->nets.erase(o->name);
if (dff_bel != dff->attrs.end())
packed->attrs[ctx->id("BEL")] = dff_bel->second;
packed_cells.insert(dff->name);
if (ctx->verbose)
log_info("packed cell %s into %s\n", dff->name.c_str(ctx), packed->name.c_str(ctx));
packed_dff = true;
}
}
if (!packed_dff) {
lut_to_lc(ctx, ci, packed.get(), true);
}
new_cells.push_back(std::move(packed));
}
}
for (auto pcell : packed_cells) {
ctx->cells.erase(pcell);
}
for (auto &ncell : new_cells) {
ctx->cells[ncell->name] = std::move(ncell);
}
}
// Pack FFs not packed as LUTFFs
static void pack_nonlut_ffs(Context *ctx)
{
log_info("Packing non-LUT FFs..\n");
std::unordered_set<IdString> packed_cells;
std::vector<std::unique_ptr<CellInfo>> new_cells;
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (is_ff(ctx, ci)) {
std::unique_ptr<CellInfo> packed = create_xc7_cell(ctx, ctx->id("XC7_LC"), ci->name.str(ctx) + "_DFFLC");
std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(packed->attrs, packed->attrs.begin()));
if (ctx->verbose)
log_info("packed cell %s into %s\n", ci->name.c_str(ctx), packed->name.c_str(ctx));
packed_cells.insert(ci->name);
dff_to_lc(ctx, ci, packed.get(), true);
new_cells.push_back(std::move(packed));
}
}
for (auto pcell : packed_cells) {
ctx->cells.erase(pcell);
}
for (auto &ncell : new_cells) {
ctx->cells[ncell->name] = std::move(ncell);
}
}
static bool net_is_constant(const Context *ctx, NetInfo *net, bool &value)
{
if (net == nullptr)
return false;
if (net->name == ctx->id("$PACKER_GND_NET") || net->name == ctx->id("$PACKER_VCC_NET")) {
value = (net->name == ctx->id("$PACKER_VCC_NET"));
return true;
} else {
return false;
}
}
// Pack carry logic
static void pack_carries(Context *ctx)
{
// log_info("Packing carries..\n");
// TODO
}
// "Pack" RAMs
static void pack_ram(Context *ctx)
{
// log_info("Packing RAMs..\n");
// TODO
}
// Merge a net into a constant net
static void set_net_constant(const Context *ctx, NetInfo *orig, NetInfo *constnet, bool constval)
{
orig->driver.cell = nullptr;
for (auto user : orig->users) {
if (user.cell != nullptr) {
CellInfo *uc = user.cell;
if (ctx->verbose)
log_info("%s user %s\n", orig->name.c_str(ctx), uc->name.c_str(ctx));
if ((is_lut(ctx, uc) || is_lc(ctx, uc) || is_carry(ctx, uc)) && (user.port.str(ctx).at(0) == 'I') &&
!constval) {
uc->ports[user.port].net = nullptr;
} else {
uc->ports[user.port].net = constnet;
constnet->users.push_back(user);
}
}
}
orig->users.clear();
}
// Pack constants (simple implementation)
static void pack_constants(Context *ctx)
{
log_info("Packing constants..\n");
std::unique_ptr<CellInfo> gnd_cell = create_xc7_cell(ctx, ctx->id("XC7_LC"), "$PACKER_GND");
gnd_cell->params[ctx->id("INIT")] = "0";
std::unique_ptr<NetInfo> gnd_net = std::unique_ptr<NetInfo>(new NetInfo);
gnd_net->name = ctx->id("$PACKER_GND_NET");
gnd_net->driver.cell = gnd_cell.get();
gnd_net->driver.port = id_O;
gnd_cell->ports.at(id_O).net = gnd_net.get();
std::unique_ptr<CellInfo> vcc_cell = create_xc7_cell(ctx, ctx->id("XC7_LC"), "$PACKER_VCC");
vcc_cell->params[ctx->id("INIT")] = "1";
std::unique_ptr<NetInfo> vcc_net = std::unique_ptr<NetInfo>(new NetInfo);
vcc_net->name = ctx->id("$PACKER_VCC_NET");
vcc_net->driver.cell = vcc_cell.get();
vcc_net->driver.port = id_O;
vcc_cell->ports.at(id_O).net = vcc_net.get();
std::vector<IdString> dead_nets;
bool gnd_used = false;
for (auto net : sorted(ctx->nets)) {
NetInfo *ni = net.second;
if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("GND")) {
IdString drv_cell = ni->driver.cell->name;
set_net_constant(ctx, ni, gnd_net.get(), false);
gnd_used = true;
dead_nets.push_back(net.first);
ctx->cells.erase(drv_cell);
} else if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("VCC")) {
IdString drv_cell = ni->driver.cell->name;
set_net_constant(ctx, ni, vcc_net.get(), true);
dead_nets.push_back(net.first);
ctx->cells.erase(drv_cell);
}
}
if (gnd_used) {
ctx->cells[gnd_cell->name] = std::move(gnd_cell);
ctx->nets[gnd_net->name] = std::move(gnd_net);
}
// Vcc cell always inserted for now, as it may be needed during carry legalisation (TODO: trim later if actually
// never used?)
ctx->cells[vcc_cell->name] = std::move(vcc_cell);
ctx->nets[vcc_net->name] = std::move(vcc_net);
for (auto dn : dead_nets) {
ctx->nets.erase(dn);
}
}
static bool is_nextpnr_iob(Context *ctx, CellInfo *cell)
{
return cell->type == ctx->id("$nextpnr_ibuf") || cell->type == ctx->id("$nextpnr_obuf") ||
cell->type == ctx->id("$nextpnr_iobuf");
}
// Pack IO buffers
static void pack_io(Context *ctx)
{
std::unordered_set<IdString> packed_cells;
std::vector<std::unique_ptr<CellInfo>> new_cells;
log_info("Packing IOs..\n");
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (is_nextpnr_iob(ctx, ci)) {
CellInfo *sb = nullptr;
if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) {
sb = net_only_drives(ctx, ci->ports.at(ctx->id("O")).net, is_sb_io, ctx->id("PACKAGE_PIN"), true, ci);
} else if (ci->type == ctx->id("$nextpnr_obuf")) {
sb = net_only_drives(ctx, ci->ports.at(ctx->id("I")).net, is_sb_io, ctx->id("PACKAGE_PIN"), true, ci);
}
if (sb != nullptr) {
// Trivial case, IOBUF used. Just destroy the net and the
// iobuf
log_info("%s feeds IOBUF %s, removing %s %s.\n", ci->name.c_str(ctx), sb->name.c_str(ctx),
ci->type.c_str(ctx), ci->name.c_str(ctx));
NetInfo *net = sb->ports.at(ctx->id("PACKAGE_PIN")).net;
if (net != nullptr) {
ctx->nets.erase(net->name);
sb->ports.at(ctx->id("PACKAGE_PIN")).net = nullptr;
}
if (ci->type == ctx->id("$nextpnr_iobuf")) {
NetInfo *net2 = ci->ports.at(ctx->id("I")).net;
if (net2 != nullptr) {
ctx->nets.erase(net2->name);
}
}
} else {
// Create a IOBUF buffer
std::unique_ptr<CellInfo> xc7_cell = create_xc7_cell(ctx, ctx->id("IOBUF"), ci->name.str(ctx));
nxio_to_sb(ctx, ci, xc7_cell.get());
new_cells.push_back(std::move(xc7_cell));
sb = new_cells.back().get();
}
packed_cells.insert(ci->name);
std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(sb->attrs, sb->attrs.begin()));
}
}
for (auto pcell : packed_cells) {
ctx->cells.erase(pcell);
}
for (auto &ncell : new_cells) {
ctx->cells[ncell->name] = std::move(ncell);
}
}
// Return true if a port counts as "logic" for global promotion
static bool is_logic_port(BaseCtx *ctx, const PortRef &port)
{
if (is_clock_port(ctx, port) || is_reset_port(ctx, port) || is_enable_port(ctx, port))
return false;
return !is_sb_io(ctx, port.cell) && port.cell->type != id_BUFGCTRL;
}
static void insert_global(Context *ctx, NetInfo *net, bool is_reset, bool is_cen, bool is_logic)
{
std::string glb_name = net->name.str(ctx) + std::string("_$glb_") + (is_reset ? "sr" : (is_cen ? "ce" : "clk"));
std::unique_ptr<CellInfo> gb = create_xc7_cell(ctx, id_BUFGCTRL, "$bufg_" + glb_name);
gb->ports[ctx->id("I0")].net = net;
PortRef pr;
pr.cell = gb.get();
pr.port = ctx->id("I0");
net->users.push_back(pr);
pr.cell = gb.get();
pr.port = ctx->id("O");
std::unique_ptr<NetInfo> glbnet = std::unique_ptr<NetInfo>(new NetInfo());
glbnet->name = ctx->id(glb_name);
glbnet->driver = pr;
gb->ports[ctx->id("O")].net = glbnet.get();
std::vector<PortRef> keep_users;
for (auto user : net->users) {
if (is_clock_port(ctx, user) || (is_reset && is_reset_port(ctx, user)) ||
(is_cen && is_enable_port(ctx, user)) || (is_logic && is_logic_port(ctx, user))) {
user.cell->ports[user.port].net = glbnet.get();
glbnet->users.push_back(user);
} else {
keep_users.push_back(user);
}
}
net->users = keep_users;
ctx->nets[glbnet->name] = std::move(glbnet);
ctx->cells[gb->name] = std::move(gb);
}
// Simple global promoter (clock only)
static void promote_globals(Context *ctx)
{
log_info("Promoting globals..\n");
const int logic_fanout_thresh = 15;
const int enable_fanout_thresh = 5;
std::map<IdString, int> clock_count, reset_count, cen_count, logic_count;
for (auto net : sorted(ctx->nets)) {
NetInfo *ni = net.second;
if (ni->driver.cell != nullptr && !ctx->isGlobalNet(ni)) {
clock_count[net.first] = 0;
reset_count[net.first] = 0;
cen_count[net.first] = 0;
for (auto user : ni->users) {
if (is_clock_port(ctx, user))
clock_count[net.first]++;
if (is_reset_port(ctx, user))
reset_count[net.first]++;
if (is_enable_port(ctx, user))
cen_count[net.first]++;
if (is_logic_port(ctx, user))
logic_count[net.first]++;
}
}
}
int prom_globals = 0, prom_resets = 0, prom_cens = 0, prom_logics = 0;
int gbs_available = 8;
for (auto &cell : ctx->cells)
if (is_gbuf(ctx, cell.second.get()))
--gbs_available;
while (prom_globals < gbs_available) {
auto global_clock = std::max_element(clock_count.begin(), clock_count.end(),
[](const std::pair<IdString, int> &a, const std::pair<IdString, int> &b) {
return a.second < b.second;
});
auto global_reset = std::max_element(reset_count.begin(), reset_count.end(),
[](const std::pair<IdString, int> &a, const std::pair<IdString, int> &b) {
return a.second < b.second;
});
auto global_cen = std::max_element(cen_count.begin(), cen_count.end(),
[](const std::pair<IdString, int> &a, const std::pair<IdString, int> &b) {
return a.second < b.second;
});
auto global_logic = std::max_element(logic_count.begin(), logic_count.end(),
[](const std::pair<IdString, int> &a, const std::pair<IdString, int> &b) {
return a.second < b.second;
});
if (global_clock->second == 0 && prom_logics < 4 && global_logic->second > logic_fanout_thresh &&
(global_logic->second > global_cen->second || prom_cens >= 4) &&
(global_logic->second > global_reset->second || prom_resets >= 4)) {
NetInfo *logicnet = ctx->nets[global_logic->first].get();
insert_global(ctx, logicnet, false, false, true);
++prom_globals;
++prom_logics;
clock_count.erase(logicnet->name);
reset_count.erase(logicnet->name);
cen_count.erase(logicnet->name);
logic_count.erase(logicnet->name);
} else if (global_reset->second > global_clock->second && prom_resets < 4) {
NetInfo *rstnet = ctx->nets[global_reset->first].get();
insert_global(ctx, rstnet, true, false, false);
++prom_globals;
++prom_resets;
clock_count.erase(rstnet->name);
reset_count.erase(rstnet->name);
cen_count.erase(rstnet->name);
logic_count.erase(rstnet->name);
} else if (global_cen->second > global_clock->second && prom_cens < 4 &&
global_cen->second > enable_fanout_thresh) {
NetInfo *cennet = ctx->nets[global_cen->first].get();
insert_global(ctx, cennet, false, true, false);
++prom_globals;
++prom_cens;
clock_count.erase(cennet->name);
reset_count.erase(cennet->name);
cen_count.erase(cennet->name);
logic_count.erase(cennet->name);
} else if (global_clock->second != 0) {
NetInfo *clknet = ctx->nets[global_clock->first].get();
insert_global(ctx, clknet, false, false, false);
++prom_globals;
clock_count.erase(clknet->name);
reset_count.erase(clknet->name);
cen_count.erase(clknet->name);
logic_count.erase(clknet->name);
} else {
break;
}
}
}
// spliceLUT adds a pass-through LUT LC between the given cell's output port
// and either all users or only non_LUT users.
static std::unique_ptr<CellInfo> spliceLUT(Context *ctx, CellInfo *ci, IdString portId, bool onlyNonLUTs)
{
auto port = ci->ports[portId];
NPNR_ASSERT(port.net != nullptr);
// Create pass-through LUT.
std::unique_ptr<CellInfo> pt =
create_xc7_cell(ctx, ctx->id("XC7_LC"), ci->name.str(ctx) + "$nextpnr_" + portId.str(ctx) + "_lut_through");
pt->params[ctx->id("INIT")] = "65280"; // output is always I3
// Create LUT output net.
std::unique_ptr<NetInfo> out_net = std::unique_ptr<NetInfo>(new NetInfo);
out_net->name = ctx->id(ci->name.str(ctx) + "$nextnr_" + portId.str(ctx) + "_lut_through_net");
out_net->driver.cell = pt.get();
out_net->driver.port = ctx->id("O");
pt->ports.at(ctx->id("O")).net = out_net.get();
// New users of the original cell's port
std::vector<PortRef> new_users;
for (const auto &user : port.net->users) {
if (onlyNonLUTs && user.cell->type == ctx->id("XC7_LC")) {
new_users.push_back(user);
continue;
}
// Rewrite pointer into net in user.
user.cell->ports[user.port].net = out_net.get();
// Add user to net.
PortRef pr;
pr.cell = user.cell;
pr.port = user.port;
out_net->users.push_back(pr);
}
// Add LUT to new users.
PortRef pr;
pr.cell = pt.get();
pr.port = ctx->id("I3");
new_users.push_back(pr);
pt->ports.at(ctx->id("I3")).net = port.net;
// Replace users of the original net.
port.net->users = new_users;
ctx->nets[out_net->name] = std::move(out_net);
return pt;
}
// Pack special functions
static void pack_special(Context *ctx)
{
log_info("Packing special functions..\n");
std::unordered_set<IdString> packed_cells;
std::vector<std::unique_ptr<CellInfo>> new_cells;
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (ci->type == id_BUFGCTRL) {
ci->params.emplace(ctx->id("PRESELECT_I0"), "FALSE");
ci->params.emplace(ctx->id("CE0INV"), "CE0");
ci->params.emplace(ctx->id("S0INV"), "S0");
ci->params.emplace(ctx->id("IGNORE0INV"), "IGNORE0");
ci->params.emplace(ctx->id("CE1INV"), "CE1");
ci->params.emplace(ctx->id("S1INV"), "S1");
ci->params.emplace(ctx->id("IGNORE1INV"), "IGNORE1");
} else if (ci->type == id_MMCME2_ADV) {
ci->params.emplace(ctx->id("BANDWIDTH"), "OPTIMIZED");
ci->params.emplace(ctx->id("CLKBURST_ENABLE"), "FALSE");
ci->params.emplace(ctx->id("CLKBURST_REPEAT"), "FALSE");
ci->params.emplace(ctx->id("CLKFBIN_EDGE"), "FALSE");
ci->params.emplace(ctx->id("CLKFBIN_NOCOUNT"), "TRUE");
ci->params.emplace(ctx->id("CLKFBOUT_EDGE"), "FALSE");
ci->params.emplace(ctx->id("CLKFBOUT_EN"), "TRUE");
ci->params.emplace(ctx->id("CLKFBOUT_FRAC_EN"), "FALSE");
ci->params.emplace(ctx->id("CLKFBOUT_FRAC_WF_FALL"), "FALSE");
ci->params.emplace(ctx->id("CLKFBOUT_FRAC_WF_RISE"), "FALSE");
ci->params.emplace(ctx->id("CLKFBOUT_NOCOUNT"), "TRUE");
ci->params.emplace(ctx->id("CLKFBOUT_USE_FINE_PS"), "FALSE");
ci->params.emplace(ctx->id("CLKINSELINV"), "CLKINSEL");
ci->params.emplace(ctx->id("CLKOUT0_EDGE"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT0_EN"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT0_FRAC_EN"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT0_FRAC_WF_FALL"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT0_FRAC_WF_RISE"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT0_NOCOUNT"), "TRUE");
ci->params.emplace(ctx->id("CLKOUT0_USE_FINE_PS"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT1_EDGE"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT1_EN"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT1_NOCOUNT"), "TRUE");
ci->params.emplace(ctx->id("CLKOUT1_USE_FINE_PS"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT2_EDGE"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT2_EN"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT2_NOCOUNT"), "TRUE");
ci->params.emplace(ctx->id("CLKOUT2_USE_FINE_PS"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT3_EDGE"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT3_EN"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT3_NOCOUNT"), "TRUE");
ci->params.emplace(ctx->id("CLKOUT3_USE_FINE_PS"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT4_CASCADE"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT4_EDGE"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT4_EN"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT4_NOCOUNT"), "TRUE");
ci->params.emplace(ctx->id("CLKOUT4_USE_FINE_PS"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT5_EDGE"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT5_EN"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT5_NOCOUNT"), "TRUE");
ci->params.emplace(ctx->id("CLKOUT5_USE_FINE_PS"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT6_EDGE"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT6_EN"), "FALSE");
ci->params.emplace(ctx->id("CLKOUT6_NOCOUNT"), "TRUE");
ci->params.emplace(ctx->id("CLKOUT6_USE_FINE_PS"), "FALSE");
ci->params.emplace(ctx->id("COMPENSATION"), "INTERNAL");
ci->params.emplace(ctx->id("DIRECT_PATH_CNTRL"), "FALSE");
ci->params.emplace(ctx->id("DIVCLK_EDGE"), "FALSE");
ci->params.emplace(ctx->id("DIVCLK_NOCOUNT"), "TRUE");
ci->params.emplace(ctx->id("EN_VCO_DIV1"), "FALSE");
ci->params.emplace(ctx->id("EN_VCO_DIV6"), "FALSE");
ci->params.emplace(ctx->id("GTS_WAIT"), "FALSE");
ci->params.emplace(ctx->id("HVLF_CNT_TEST_EN"), "FALSE");
ci->params.emplace(ctx->id("INTERP_TEST"), "FALSE");
ci->params.emplace(ctx->id("IN_DLY_EN"), "TRUE");
ci->params.emplace(ctx->id("LF_LOW_SEL"), "FALSE");
ci->params.emplace(ctx->id("MMCM_EN"), "TRUE");
ci->params.emplace(ctx->id("PERF0_USE_CLK"), "FALSE");
ci->params.emplace(ctx->id("PERF1_USE_CLK"), "FALSE");
ci->params.emplace(ctx->id("PERF2_USE_CLK"), "FALSE");
ci->params.emplace(ctx->id("PERF3_USE_CLK"), "FALSE");
ci->params.emplace(ctx->id("PSENINV"), "PSEN");
ci->params.emplace(ctx->id("PSINCDECINV"), "PSINCDEC");
ci->params.emplace(ctx->id("PWRDWNINV"), "PWRDWN");
ci->params.emplace(ctx->id("RSTINV"), "RST");
ci->params.emplace(ctx->id("SEL_HV_NMOS"), "FALSE");
ci->params.emplace(ctx->id("SEL_LV_NMOS"), "FALSE");
ci->params.emplace(ctx->id("SEL_SLIPD"), "FALSE");
ci->params.emplace(ctx->id("SS_EN"), "FALSE");
ci->params.emplace(ctx->id("SS_MODE"), "CENTER_HIGH");
ci->params.emplace(ctx->id("STARTUP_WAIT"), "FALSE");
ci->params.emplace(ctx->id("SUP_SEL_AREG"), "FALSE");
ci->params.emplace(ctx->id("SUP_SEL_DREG"), "FALSE");
ci->params.emplace(ctx->id("TMUX_MUX_SEL"), "00");
ci->params.emplace(ctx->id("VLF_HIGH_DIS_B"), "TRUE");
ci->params.emplace(ctx->id("VLF_HIGH_PWDN_B"), "TRUE");
// ci->params.emplace(ctx->id("MMCME2_ADV:mmcm_adv_inst:");
ci->params.emplace(ctx->id("ANALOG_MISC"), "0000");
ci->params.emplace(ctx->id("AVDD_COMP_SET"), "011");
ci->params.emplace(ctx->id("AVDD_VBG_PD"), "110");
ci->params.emplace(ctx->id("AVDD_VBG_SEL"), "1001");
ci->params.emplace(ctx->id("CLKBURST_CNT"), "1");
ci->params.emplace(ctx->id("CLKFBIN_HT"), "1");
ci->params.emplace(ctx->id("CLKFBIN_LT"), "1");
ci->params.emplace(ctx->id("CLKFBIN_MULT"), "1");
ci->params.emplace(ctx->id("CLKFBOUT_DT"), "0");
ci->params.emplace(ctx->id("CLKFBOUT_FRAC"), "0");
ci->params.emplace(ctx->id("CLKFBOUT_HT"), "1");
ci->params.emplace(ctx->id("CLKFBOUT_LT"), "1");
ci->params.emplace(ctx->id("CLKFBOUT_MULT_F"), "40.5");
ci->params.emplace(ctx->id("CLKFBOUT_MX"), "00");
ci->params.emplace(ctx->id("CLKFBOUT_PHASE"), "0.0");
ci->params.emplace(ctx->id("CLKFBOUT_PM_FALL"), "000");
ci->params.emplace(ctx->id("CLKFBOUT_PM_RISE"), "000");
ci->params.emplace(ctx->id("CLKFB_MUX_SEL"), "000");
ci->params.emplace(ctx->id("CLKIN1_MUX_SEL"), "000");
ci->params.emplace(ctx->id("CLKIN1_PERIOD"), "8");
ci->params.emplace(ctx->id("CLKIN2_MUX_SEL"), "000");
ci->params.emplace(ctx->id("CLKIN2_PERIOD"), "0");
ci->params.emplace(ctx->id("CLKOUT0_DIVIDE_F"), "16.875");
ci->params.emplace(ctx->id("CLKOUT0_DT"), "0");
ci->params.emplace(ctx->id("CLKOUT0_DUTY_CYCLE"), "0.5");
ci->params.emplace(ctx->id("CLKOUT0_FRAC"), "0");
ci->params.emplace(ctx->id("CLKOUT0_HT"), "1");
ci->params.emplace(ctx->id("CLKOUT0_LT"), "1");
ci->params.emplace(ctx->id("CLKOUT0_MX"), "00");
ci->params.emplace(ctx->id("CLKOUT0_PHASE"), "0.0");
ci->params.emplace(ctx->id("CLKOUT0_PM_FALL"), "000");
ci->params.emplace(ctx->id("CLKOUT0_PM_RISE"), "000");
ci->params.emplace(ctx->id("CLKOUT1_DIVIDE"), "1");
ci->params.emplace(ctx->id("CLKOUT1_DT"), "0");
ci->params.emplace(ctx->id("CLKOUT1_DUTY_CYCLE"), "0.5");
ci->params.emplace(ctx->id("CLKOUT1_HT"), "1");
ci->params.emplace(ctx->id("CLKOUT1_LT"), "1");
ci->params.emplace(ctx->id("CLKOUT1_MX"), "00");
ci->params.emplace(ctx->id("CLKOUT1_PHASE"), "0.0");
ci->params.emplace(ctx->id("CLKOUT1_PM"), "000");
ci->params.emplace(ctx->id("CLKOUT2_DIVIDE"), "1");
ci->params.emplace(ctx->id("CLKOUT2_DT"), "0");
ci->params.emplace(ctx->id("CLKOUT2_DUTY_CYCLE"), "0.5");
ci->params.emplace(ctx->id("CLKOUT2_HT"), "1");
ci->params.emplace(ctx->id("CLKOUT2_LT"), "1");
ci->params.emplace(ctx->id("CLKOUT2_MX"), "00");
ci->params.emplace(ctx->id("CLKOUT2_PHASE"), "0.0");
ci->params.emplace(ctx->id("CLKOUT2_PM"), "000");
ci->params.emplace(ctx->id("CLKOUT3_DIVIDE"), "1");
ci->params.emplace(ctx->id("CLKOUT3_DT"), "0");
ci->params.emplace(ctx->id("CLKOUT3_DUTY_CYCLE"), "0.5");
ci->params.emplace(ctx->id("CLKOUT3_HT"), "1");
ci->params.emplace(ctx->id("CLKOUT3_LT"), "1");
ci->params.emplace(ctx->id("CLKOUT3_MX"), "00");
ci->params.emplace(ctx->id("CLKOUT3_PHASE"), "0.0");
ci->params.emplace(ctx->id("CLKOUT3_PM"), "000");
ci->params.emplace(ctx->id("CLKOUT4_DIVIDE"), "1");
ci->params.emplace(ctx->id("CLKOUT4_DT"), "0");
ci->params.emplace(ctx->id("CLKOUT4_DUTY_CYCLE"), "0.5");
ci->params.emplace(ctx->id("CLKOUT4_HT"), "1");
ci->params.emplace(ctx->id("CLKOUT4_LT"), "1");
ci->params.emplace(ctx->id("CLKOUT4_MX"), "00");
ci->params.emplace(ctx->id("CLKOUT4_PHASE"), "0.0");
ci->params.emplace(ctx->id("CLKOUT4_PM"), "000");
ci->params.emplace(ctx->id("CLKOUT5_DIVIDE"), "1");
ci->params.emplace(ctx->id("CLKOUT5_DT"), "0");
ci->params.emplace(ctx->id("CLKOUT5_DUTY_CYCLE"), "0.5");
ci->params.emplace(ctx->id("CLKOUT5_HT"), "1");
ci->params.emplace(ctx->id("CLKOUT5_LT"), "1");
ci->params.emplace(ctx->id("CLKOUT5_MX"), "00");
ci->params.emplace(ctx->id("CLKOUT5_PHASE"), "0.0");
ci->params.emplace(ctx->id("CLKOUT5_PM"), "000");
ci->params.emplace(ctx->id("CLKOUT6_DIVIDE"), "1");
ci->params.emplace(ctx->id("CLKOUT6_DT"), "0");
ci->params.emplace(ctx->id("CLKOUT6_DUTY_CYCLE"), "0.5");
ci->params.emplace(ctx->id("CLKOUT6_HT"), "1");
ci->params.emplace(ctx->id("CLKOUT6_LT"), "1");
ci->params.emplace(ctx->id("CLKOUT6_MX"), "00");
ci->params.emplace(ctx->id("CLKOUT6_PHASE"), "0.0");
ci->params.emplace(ctx->id("CLKOUT6_PM"), "000");
ci->params.emplace(ctx->id("CONTROL_0"), "1111001101111100");
ci->params.emplace(ctx->id("CONTROL_1"), "0111110101001101");
ci->params.emplace(ctx->id("CONTROL_2"), "0101000001000010");
ci->params.emplace(ctx->id("CONTROL_3"), "1110101111001000");
ci->params.emplace(ctx->id("CONTROL_4"), "1101010011011111");
ci->params.emplace(ctx->id("CONTROL_5"), "1010110111111011");
ci->params.emplace(ctx->id("CONTROL_6"), "1011001011000011");
ci->params.emplace(ctx->id("CONTROL_7"), "0100110000101110");
ci->params.emplace(ctx->id("CP"), "0000");
ci->params.emplace(ctx->id("CP_BIAS_TRIP_SET"), "0");
ci->params.emplace(ctx->id("CP_RES"), "01");
ci->params.emplace(ctx->id("DIVCLK_DIVIDE"), "5");
ci->params.emplace(ctx->id("DIVCLK_HT"), "1");
ci->params.emplace(ctx->id("DIVCLK_LT"), "1");
ci->params.emplace(ctx->id("DVDD_COMP_SET"), "011");
ci->params.emplace(ctx->id("DVDD_VBG_PD"), "110");
ci->params.emplace(ctx->id("DVDD_VBG_SEL"), "1001");
ci->params.emplace(ctx->id("EN_CURR_SINK"), "11");
ci->params.emplace(ctx->id("FINE_PS_FRAC"), "0");
ci->params.emplace(ctx->id("FREQ_BB_USE_CLK0"), "0");
ci->params.emplace(ctx->id("FREQ_BB_USE_CLK1"), "0");
ci->params.emplace(ctx->id("FREQ_BB_USE_CLK2"), "0");
ci->params.emplace(ctx->id("FREQ_BB_USE_CLK3"), "0");
ci->params.emplace(ctx->id("FREQ_COMP"), "01");
ci->params.emplace(ctx->id("HROW_DLY_SET"), "0");
ci->params.emplace(ctx->id("HVLF_CNT_TEST"), "0");
ci->params.emplace(ctx->id("INTERP_EN"), "00010000");
ci->params.emplace(ctx->id("IN_DLY_MX_CVDD"), "011000");
ci->params.emplace(ctx->id("IN_DLY_MX_DVDD"), "000001");
ci->params.emplace(ctx->id("IN_DLY_SET"), "38");
ci->params.emplace(ctx->id("LFHF"), "11");
ci->params.emplace(ctx->id("LF_NEN"), "10");
ci->params.emplace(ctx->id("LF_PEN"), "00");
ci->params.emplace(ctx->id("LOCK_CNT"), "128");
ci->params.emplace(ctx->id("LOCK_FB_DLY"), "3");
ci->params.emplace(ctx->id("LOCK_REF_DLY"), "3");
ci->params.emplace(ctx->id("LOCK_SAT_HIGH"), "160");
ci->params.emplace(ctx->id("MAN_LF"), "000");
ci->params.emplace(ctx->id("MVDD_SEL"), "11");
ci->params.emplace(ctx->id("PERF0_MUX_SEL"), "000");
ci->params.emplace(ctx->id("PERF1_MUX_SEL"), "000");
ci->params.emplace(ctx->id("PERF2_MUX_SEL"), "000");
ci->params.emplace(ctx->id("PERF3_MUX_SEL"), "000");
ci->params.emplace(ctx->id("PFD"), "0100001");
ci->params.emplace(ctx->id("REF_JITTER1"), "0.01");
ci->params.emplace(ctx->id("REF_JITTER2"), "0.01");
ci->params.emplace(ctx->id("RES"), "0000");
ci->params.emplace(ctx->id("SKEW_FLOP_INV"), "0000");
ci->params.emplace(ctx->id("SPARE_ANALOG"), "00000");
ci->params.emplace(ctx->id("SPARE_DIGITAL"), "00000");
ci->params.emplace(ctx->id("SS_MOD_PERIOD"), "10000");
ci->params.emplace(ctx->id("SS_STEPS"), "011");
ci->params.emplace(ctx->id("SS_STEPS_INIT"), "010");
ci->params.emplace(ctx->id("SYNTH_CLK_DIV"), "11");
ci->params.emplace(ctx->id("UNLOCK_CNT"), "64");
ci->params.emplace(ctx->id("VREF_START"), "01");
ci->params[ctx->id("COMPENSATION")] = "INTERNAL";
}
}
for (auto pcell : packed_cells) {
ctx->cells.erase(pcell);
}
for (auto &ncell : new_cells) {
ctx->cells[ncell->name] = std::move(ncell);
}
}
// Main pack function
bool Arch::pack()
{
Context *ctx = getCtx();
try {
log_break();
pack_constants(ctx);
// TODO
// promote_globals(ctx);
pack_io(ctx);
pack_lut_lutffs(ctx);
pack_nonlut_ffs(ctx);
pack_carries(ctx);
pack_ram(ctx);
pack_special(ctx);
ctx->assignArchInfo();
constrain_chains(ctx);
ctx->assignArchInfo();
log_info("Checksum: 0x%08x\n", ctx->checksum());
return true;
} catch (log_execution_error_exception) {
return false;
}
}
NEXTPNR_NAMESPACE_END

84
xc7/pcf.cc Normal file
View File

@ -0,0 +1,84 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* 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.
*
*/
#include "pcf.h"
#include <sstream>
#include "log.h"
#include <boost/algorithm/string.hpp>
NEXTPNR_NAMESPACE_BEGIN
// Read a w
// Apply PCF constraints to a pre-packing design
bool apply_pcf(Context *ctx, std::string filename, std::istream &in)
{
try {
if (!in)
log_error("failed to open PCF file\n");
std::string line;
while (std::getline(in, line)) {
size_t cstart = line.find("#");
if (cstart != std::string::npos)
line = line.substr(0, cstart);
std::stringstream ss(line);
std::vector<std::string> words;
std::string tmp;
while (ss >> tmp)
words.push_back(tmp);
if (words.size() == 0)
continue;
std::string cmd = words.at(0);
if (cmd == "COMP") {
size_t args_end = 1;
while (args_end < words.size() && words.at(args_end).at(0) == '-')
args_end++;
std::string cell = words.at(args_end);
boost::trim_if(cell, boost::is_any_of("\""));
std::string pin = words.at(args_end + 4);
boost::trim_if(pin, boost::is_any_of("\""));
auto fnd_cell = ctx->cells.find(ctx->id(cell));
if (fnd_cell == ctx->cells.end()) {
log_warning("unmatched pcf constraint %s\n", cell.c_str());
} else {
BelId pin_bel = ctx->getPackagePinBel(pin);
if (pin_bel == BelId())
log_error("package does not have a pin named %s\n", pin.c_str());
fnd_cell->second->attrs[ctx->id("BEL")] = ctx->getBelName(pin_bel).str(ctx);
log_info("constrained '%s' to bel '%s'\n", cell.c_str(),
fnd_cell->second->attrs[ctx->id("BEL")].c_str());
}
} else if (cmd == "NET") {
// TODO
} else if (cmd == "PIN") {
// TODO
} else {
log_error("unsupported pcf command '%s'\n", cmd.c_str());
}
}
ctx->settings.emplace(ctx->id("project/input/pcf"), filename);
return true;
} catch (log_execution_error_exception) {
return false;
}
}
NEXTPNR_NAMESPACE_END

34
xc7/pcf.h Normal file
View File

@ -0,0 +1,34 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* 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 PCF_H
#define PCF_H
#include <iostream>
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
// Apply PCF constraints to a pre-packing design
bool apply_pcf(Context *ctx, std::string filename, std::istream &in);
NEXTPNR_NAMESPACE_END
#endif // ROUTE_H

3
xc7/picorv32.pcf Normal file
View File

@ -0,0 +1,3 @@
NET "clki" PERIOD = 8 nS ;
PIN "clki_pin" = BEL "clki.PAD" PINNAME PAD;
PIN "clki_pin" CLOCK_DEDICATED_ROUTE = FALSE;

15
xc7/picorv32.proj Normal file
View File

@ -0,0 +1,15 @@
{
"project": {
"version": "1",
"name": "picorv32",
"arch": {
"name": "ice40",
"type": "hx8k",
"package": "ct256"
},
"input": {
"json": "picorv32.json",
"pcf": "icebreaker.pcf"
}
}
}

11
xc7/picorv32.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/bash
set -ex
rm -f picorv32.v
wget https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v
yosys picorv32.ys
set +e
../nextpnr-xc7 --json picorv32.json --xdl picorv32.xdl --pcf picorv32.pcf --freq 125
set -e
xdl -xdl2ncd picorv32.xdl
#bitgen -w blinky.ncd -g UnconstrainedPins:Allow
trce picorv32.ncd -v 10

55
xc7/picorv32.ys Normal file
View File

@ -0,0 +1,55 @@
read_verilog picorv32.v
read_verilog picorv32_top.v
read_verilog 125MHz_to_60MHz.v
#synth_xilinx -top picorv32
#begin:
read_verilog -lib +/xilinx/cells_sim.v
read_verilog -lib +/xilinx/cells_xtra.v
# read_verilog -lib +/xilinx/brams_bb.v
# read_verilog -lib +/xilinx/drams_bb.v
hierarchy -check -top top
#flatten: (only if -flatten)
proc
flatten
#coarse:
synth -run coarse
#bram:
# memory_bram -rules +/xilinx/brams.txt
# techmap -map +/xilinx/brams_map.v
#
#dram:
# memory_bram -rules +/xilinx/drams.txt
# techmap -map +/xilinx/drams_map.v
fine:
opt -fast -full
memory_map
dffsr2dff
# dff2dffe
opt -full
techmap -map +/techmap.v #-map +/xilinx/arith_map.v
opt -fast
map_luts:
abc -luts 2:2,3,6:5 #,10,20 [-dff]
clean
map_cells:
techmap -map +/xilinx/cells_map.v
dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT
clean
check:
hierarchy -check
stat
check -noinit
#edif: (only if -edif)
# write_edif <file-name>
write_json picorv32.json

42
xc7/picorv32_benchmark.py Executable file
View File

@ -0,0 +1,42 @@
#!/usr/bin/env python3
import os, sys, threading
from os import path
import subprocess
import re
num_runs = 8
if not path.exists("picorv32.json"):
subprocess.run(["wget", "https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v"], check=True)
subprocess.run(["yosys", "-q", "-p", "synth_ice40 -json picorv32.json -top top", "picorv32.v", "picorv32_top.v"], check=True)
fmax = {}
if not path.exists("picorv32_work"):
os.mkdir("picorv32_work")
threads = []
for i in range(num_runs):
def runner(run):
ascfile = "picorv32_work/picorv32_s{}.asc".format(run)
if path.exists(ascfile):
os.remove(ascfile)
result = subprocess.run(["../nextpnr-ice40", "--hx8k", "--seed", str(run), "--json", "picorv32.json", "--asc", ascfile, "--freq", "70"], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
if result.returncode != 0:
print("Run {} failed!".format(run))
else:
icetime_res = subprocess.check_output(["icetime", "-d", "hx8k", ascfile])
fmax_m = re.search(r'\(([0-9.]+) MHz\)', icetime_res.decode('utf-8'))
fmax[run] = float(fmax_m.group(1))
threads.append(threading.Thread(target=runner, args=[i+1]))
for t in threads: t.start()
for t in threads: t.join()
fmax_min = min(fmax.values())
fmax_max = max(fmax.values())
fmax_avg = sum(fmax.values()) / len(fmax)
print("{}/{} runs passed".format(len(fmax), num_runs))
print("icetime: min = {} MHz, avg = {} MHz, max = {} MHz".format(fmax_min, fmax_avg, fmax_max))

44
xc7/picorv32_top.v Normal file
View File

@ -0,0 +1,44 @@
module top (
input clki, resetn,
output trap,
output mem_valid,
output mem_instr,
input mem_ready,
output [31:0] mem_addr,
output [31:0] mem_wdata,
output [ 3:0] mem_wstrb,
input [31:0] mem_rdata
);
wire clk;
BUFGCTRL clk_gb (
.I0(clki),
.CE0(1'b1),
.CE1(1'b0),
.S0(1'b1),
.S1(1'b0),
.IGNORE0(1'b0),
.IGNORE1(1'b0),
.O(clk)
);
picorv32 #(
.ENABLE_COUNTERS(0),
.TWO_STAGE_SHIFT(0),
.CATCH_MISALIGN(0),
.CATCH_ILLINSN(0)
) cpu (
.clk (clk ),
.resetn (resetn ),
.trap (trap ),
.mem_valid(mem_valid),
.mem_instr(mem_instr),
.mem_ready(mem_ready),
.mem_addr (mem_addr ),
.mem_wdata(mem_wdata),
.mem_wstrb(mem_wstrb),
.mem_rdata(mem_rdata)
);
endmodule

58
xc7/project.cc Normal file
View File

@ -0,0 +1,58 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
*
* 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.
*
*/
#include "project.h"
#include <boost/filesystem/convenience.hpp>
#include <fstream>
#include "log.h"
#include "pcf.h"
NEXTPNR_NAMESPACE_BEGIN
void ProjectHandler::saveArch(Context *ctx, pt::ptree &root, std::string path)
{
root.put("project.arch.package", ctx->archArgs().package);
if (ctx->settings.find(ctx->id("project/input/pcf")) != ctx->settings.end()) {
std::string fn = ctx->settings[ctx->id("project/input/pcf")];
root.put("project.input.pcf", make_relative(fn, path).string());
}
}
std::unique_ptr<Context> ProjectHandler::createContext(pt::ptree &root)
{
ArchArgs chipArgs;
std::string arch_type = root.get<std::string>("project.arch.type");
if (arch_type == "z020") {
chipArgs.type = ArchArgs::Z020;
}
chipArgs.package = root.get<std::string>("project.arch.package");
return std::unique_ptr<Context>(new Context(chipArgs));
}
void ProjectHandler::loadArch(Context *ctx, pt::ptree &root, std::string path)
{
auto input = root.get_child("project").get_child("input");
boost::filesystem::path pcf = boost::filesystem::path(path) / input.get<std::string>("pcf");
std::ifstream f(pcf.string());
if (!apply_pcf(ctx, input.get<std::string>("pcf"), f))
log_error("Loading PCF failed.\n");
}
NEXTPNR_NAMESPACE_END

286
xc7/xdl.cc Normal file
View File

@ -0,0 +1,286 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
*
* 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.
*
*/
#include "xdl.h"
#include <boost/range/adaptor/reversed.hpp>
#include <cctype>
#include <vector>
#include "cells.h"
#include "log.h"
#include "nextpnr.h"
#include "util.h"
#include "torc/Physical.hpp"
using namespace torc::architecture::xilinx;
using namespace torc::physical;
NEXTPNR_NAMESPACE_BEGIN
DesignSharedPtr create_torc_design(const Context *ctx)
{
auto designPtr = Factory::newDesignPtr("name", torc_info->ddb->getDeviceName(), ctx->args.package, "-1", "");
std::unordered_map<int32_t, InstanceSharedPtr> site_to_instance;
std::vector<std::pair<std::string, std::string>> lut_inputs;
lut_inputs.reserve(6);
auto bel_to_lut = [](const BelId bel) {
switch (torc_info->bel_to_loc[bel.index].z) {
case 0:
case 4:
return "A";
break;
case 1:
case 5:
return "B";
break;
case 2:
case 6:
return "C";
break;
case 3:
case 7:
return "D";
break;
default:
throw;
}
};
for (const auto &cell : ctx->cells) {
const char *type;
if (cell.second->type == id_SLICE_LUT6)
type = "SLICEL";
else if (cell.second->type == id_IOB33 || cell.second->type == id_IOB18 || cell.second->type == id_BUFGCTRL ||
cell.second->type == id_PS7 || cell.second->type == id_MMCME2_ADV)
type = cell.second->type.c_str(ctx);
else
log_error("Unsupported cell type '%s'.\n", cell.second->type.c_str(ctx));
auto site_index = torc_info->bel_to_site_index[cell.second->bel.index];
auto ret = site_to_instance.emplace(site_index, nullptr);
InstanceSharedPtr instPtr;
if (ret.second) {
instPtr = Factory::newInstancePtr(cell.second->name.str(ctx), type, "", "");
auto b = designPtr->addInstance(instPtr);
assert(b);
ret.first->second = instPtr;
const auto &tile_info = torc_info->bel_to_tile_info(cell.second->bel.index);
instPtr->setTile(tile_info.getName());
instPtr->setSite(torc_info->bel_to_name(cell.second->bel.index));
} else
instPtr = ret.first->second;
if (cell.second->type == id_SLICE_LUT6) {
std::string setting, name, value;
const std::string lut = bel_to_lut(cell.second->bel);
setting = lut + "6LUT";
value = "#LUT:O6=";
lut_inputs.clear();
if (get_net_or_empty(cell.second.get(), id_I1))
lut_inputs.emplace_back("A1", "~A1");
if (get_net_or_empty(cell.second.get(), id_I2))
lut_inputs.emplace_back("A2", "~A2");
if (get_net_or_empty(cell.second.get(), id_I3))
lut_inputs.emplace_back("A3", "~A3");
if (get_net_or_empty(cell.second.get(), id_I4))
lut_inputs.emplace_back("A4", "~A4");
if (get_net_or_empty(cell.second.get(), id_I5))
lut_inputs.emplace_back("A5", "~A5");
if (get_net_or_empty(cell.second.get(), id_I6))
lut_inputs.emplace_back("A6", "~A6");
const auto &init = cell.second->params[ctx->id("INIT")];
// Assume from Yosys that INIT masks of less than 32 bits are output as uint32_t
if (lut_inputs.size() < 6) {
auto init_as_uint = boost::lexical_cast<uint32_t>(init);
NPNR_ASSERT(init_as_uint <= ((1ull << (1u << lut_inputs.size())) - 1));
if (lut_inputs.empty())
value += init;
else {
unsigned n = 0;
for (unsigned o = 0; o < (1u << lut_inputs.size()); ++o) {
if (!((init_as_uint >> o) & 1))
continue;
if (n++ > 0)
value += "+";
value += "(";
value += (o & 1) ? lut_inputs[0].first : lut_inputs[0].second;
for (unsigned i = 1; i < lut_inputs.size(); ++i) {
value += "*";
value += o & (1 << i) ? lut_inputs[i].first : lut_inputs[i].second;
}
value += ")";
}
}
}
// Otherwise as a bit string
else {
NPNR_ASSERT(init.size() == (1u << lut_inputs.size()));
unsigned n = 0;
for (unsigned i = 0; i < init.size(); ++i) {
if (init[init.size() - 1 - i] == '0')
continue;
if (n++ > 0)
value += "+";
value += "(";
value += (i & 1) ? lut_inputs[0].first : lut_inputs[0].second;
for (unsigned j = 1; j < lut_inputs.size(); ++j) {
value += "*";
value += i & (1 << j) ? lut_inputs[j].first : lut_inputs[j].second;
}
value += ")";
}
}
auto it = cell.second->params.find(ctx->id("LUT_NAME"));
if (it != cell.second->params.end())
name = it->second;
else
name = cell.second->name.str(ctx);
boost::replace_all(name, ":", "\\:");
instPtr->setConfig(setting, name, value);
auto O = get_net_or_empty(cell.second.get(), id_O);
if (O) {
setting = lut;
setting += "USED";
instPtr->setConfig(setting, "", "0");
}
auto OQ = get_net_or_empty(cell.second.get(), id_OQ);
if (OQ) {
setting = lut;
setting += "FF";
name = OQ->name.str(ctx);
boost::replace_all(name, ":", "\\:");
instPtr->setConfig(setting, name, "#FF");
instPtr->setConfig(setting + "MUX", "", "O6");
instPtr->setConfig(setting + "INIT", "", cell.second->params.at(ctx->id("FFINIT")));
if (cell.second->lcInfo.negClk)
instPtr->setConfig("CLKINV", "", "CLK_B");
else
instPtr->setConfig("CLKINV", "", "CLK");
if (get_net_or_empty(cell.second.get(), id_SR)) {
instPtr->setConfig(setting + "SR", "", cell.second->params.at(id_SR));
instPtr->setConfig("SRUSEDMUX", "", "IN");
}
instPtr->setConfig("SYNC_ATTR", "", cell.second->params.at(ctx->id("SYNC_ATTR")));
if (get_net_or_empty(cell.second.get(), ctx->id("CE")))
instPtr->setConfig("CEUSEDMUX", "", "IN");
}
} else if (cell.second->type == id_IOB33) {
if (get_net_or_empty(cell.second.get(), id_I)) {
instPtr->setConfig("IUSED", "", "0");
instPtr->setConfig("IBUF_LOW_PWR", "", "TRUE");
instPtr->setConfig("ISTANDARD", "", "LVCMOS33");
} else {
instPtr->setConfig("OUSED", "", "0");
instPtr->setConfig("OSTANDARD", "", "LVCMOS33");
instPtr->setConfig("DRIVE", "", "12");
instPtr->setConfig("SLEW", "", "SLOW");
}
} else if (cell.second->type == id_IOB18) {
if (get_net_or_empty(cell.second.get(), id_I)) {
instPtr->setConfig("IUSED", "", "0");
instPtr->setConfig("IBUF_LOW_PWR", "", "TRUE");
instPtr->setConfig("ISTANDARD", "", "LVCMOS18");
} else {
instPtr->setConfig("OUSED", "", "0");
instPtr->setConfig("OSTANDARD", "", "LVCMOS18");
instPtr->setConfig("DRIVE", "", "12");
instPtr->setConfig("SLEW", "", "SLOW");
}
} else if (cell.second->type == id_BUFGCTRL || cell.second->type == id_PS7 ||
cell.second->type == id_MMCME2_ADV) {
for (const auto &i : cell.second->params)
instPtr->setConfig(i.first.str(ctx), "", i.second);
} else
log_error("Unsupported cell type '%s'.\n", cell.second->type.c_str(ctx));
}
for (const auto &net : ctx->nets) {
const auto &driver = net.second->driver;
auto site_index = torc_info->bel_to_site_index[driver.cell->bel.index];
auto instPtr = site_to_instance.at(site_index);
auto netPtr = Factory::newNetPtr(net.second->name.str(ctx));
auto pin_name = driver.port.str(ctx);
// For all LUT based inputs and outputs (I1-I6,O,OQ,OMUX) then change the I/O into the LUT
if (driver.cell->type == id_SLICE_LUT6 && (pin_name[0] == 'I' || pin_name[0] == 'O')) {
const auto lut = bel_to_lut(driver.cell->bel);
pin_name[0] = lut[0];
}
// e.g. Convert DDRARB[0] -> DDRARB0
pin_name.erase(std::remove_if(pin_name.begin(), pin_name.end(), boost::is_any_of("[]")), pin_name.end());
auto pinPtr = Factory::newInstancePinPtr(instPtr, pin_name);
netPtr->addSource(pinPtr);
if (!net.second->users.empty()) {
for (const auto &user : net.second->users) {
site_index = torc_info->bel_to_site_index[user.cell->bel.index];
instPtr = site_to_instance.at(site_index);
pin_name = user.port.str(ctx);
// For all LUT based inputs and outputs (I1-I6,O,OQ,OMUX) then change the I/O into the LUT
if (user.cell->type == id_SLICE_LUT6 && (pin_name[0] == 'I' || pin_name[0] == 'O')) {
const auto lut = bel_to_lut(user.cell->bel);
pin_name[0] = lut[0];
} else {
// e.g. Convert DDRARB[0] -> DDRARB0
pin_name.erase(std::remove_if(pin_name.begin(), pin_name.end(), boost::is_any_of("[]")),
pin_name.end());
}
pinPtr = Factory::newInstancePinPtr(instPtr, pin_name);
netPtr->addSink(pinPtr);
}
auto b = designPtr->addNet(netPtr);
assert(b);
for (const auto &i : net.second->wires) {
const auto &pip_map = i.second;
if (pip_map.pip == PipId())
continue;
ExtendedWireInfo ewi_src(*torc_info->ddb, torc_info->pip_to_arc[pip_map.pip.index].getSourceTilewire());
ExtendedWireInfo ewi_dst(*torc_info->ddb, torc_info->pip_to_arc[pip_map.pip.index].getSinkTilewire());
auto p = Factory::newPip(ewi_src.mTileName, ewi_src.mWireName, ewi_dst.mWireName,
ePipUnidirectionalBuffered);
netPtr->addPip(p);
}
}
}
return designPtr;
}
void write_xdl(const Context *ctx, std::ostream &out)
{
XdlExporter exporter(out);
auto designPtr = create_torc_design(ctx);
exporter(designPtr);
}
NEXTPNR_NAMESPACE_END

33
xc7/xdl.h Normal file
View File

@ -0,0 +1,33 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* 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 XC7_BITSTREAM_H
#define XC7_BITSTREAM_H
#include <iostream>
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
void write_xdl(const Context *ctx, std::ostream &out);
NEXTPNR_NAMESPACE_END
#endif